Files
notebook-ai-extension/HANDOVER.md
T
tao.chenandClaude f9d52f7b8e docs: add HANDOVER.md for next session context
The current session's context window is nearly full. This document
captures the project state, the key design decisions that future
maintainers should NOT re-derive, and the known minor issues that
were intentionally left for a follow-up session.

Contents:
- Project overview + current state (tag v0.1.1, branch main @ 310d507)
- Architecture diagram
- Critical code locations
- All 11 route endpoints in one table
- 5 key design decisions (SSE filter, no cell auto-inject, multi-session
  model, no partial-render markdown, real interactive permission/question
  UI) - written so the next session doesn't accidentally undo them
- How to run the tests
- Known minor issues / TODO list

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-28 18:10:20 +08:00

7.1 KiB

opencode_bridge 交接文档

项目概览

JupyterLab extension,把本地 OpenCode Serve 进程的能力桥接到 notebook 单元。 v0.1.1 (tag 在远端,当前 main 已经是 310d507)

仓库

/Users/taochen/llm/notebook-extension/opencode-bridge

  • 主分支:main(已切回)
  • 远端:http://101.43.40.124:3000/tao.chen/notebook-ai-extension.git
  • Tag:v0.1.1 (310d507)

架构

Frontend (JupyterLab TS)              Server ext (Python tornado)         OpenCode Serve
─────────────────────────────────────  ─────────────────────────────────  ─────────────
opencode_cell_actions.ts     ─POST /opencode-bridge/edit──>  EditHandler
  per-cell AI 按钮 + inline panel     _build_request_body(prompt)  ──>  /session/:id/prompt_async
                                     (只发 prompt,不再自动注入 cell)  ──>  /session/:id/abort
opencode_inline_prompt.ts                                              /session (list all)
  1. SessionSelector 下拉(全 OpenCode sessions)                      
  2. Provider + Model 选择(本地存储)                                  
  3. History 区域(流式 markdown 渲染)                               
  4. "📋 插入单元格内容" 按钮(显式 opt-in)                         
  5. 输入框 + send/cancel                                            
                                                                     
  SSE 订阅 GET /events?session=      GlobalEventHandler ──proxy──>  /global/event
  → 5 类事件路由:text/reasoning/tool/permission.asked/question.asked + session.idle

关键代码位置

文件 作用
src/components/opencode_cell_actions.ts cell 工具栏按钮 + 流编排(open/edit/SSE/permission/question/session 管理)
src/components/opencode_inline_prompt.ts 内嵌 prompt 面板(最复杂,~1300 行)
src/api/opencode_client.ts 8 个 session 管理 API + 4 个原有 API(edit/messages/permissions/questions/events)
src/components/model_selection.ts (provider, model) localStorage 持久化
opencode_bridge/opencode_client.py HTTP 客户端,带 send_message_async + stream_global_events(SSE) + reply_permission/question + list_all_sessions
opencode_bridge/routes.py 11 个路由
opencode_bridge/session_manager.py 1 notebook N sessions 模型(_active + _bound)
opencode_bridge/config.py OpencodeConfig Configurable(env var → OpencodeConfig Traitlets)

主要路由

端点 方法 用途
/opencode-bridge/edit POST 异步发 prompt_async(只发 prompt + context.notebookPath)
/opencode-bridge/events?session=X GET SSE 代理 OpenCode /global/event
/opencode-bridge/session-messages?notebook=X GET 当前 session 的消息历史
/opencode-bridge/sessions/all GET OpenCode 上所有 session(全局)
/opencode-bridge/sessions/notebook?notebook=X GET/POST/PUT/DELETE 绑定 + 列出 + 创建 + 删除
/opencode-bridge/sessions/active?notebook=X GET/PUT/DELETE active session 操作
/opencode-bridge/permissions/:permId?session=X POST 权限响应(once/always/reject)
/opencode-bridge/questions/:qId/reply?session=X POST 提问响应

关键设计决策(避免来回改)

1. SSE 事件路由(applyEvent)

  • 客户端对内容事件(text/reasoning/tool/idle)做 sessionID 过滤 — 服务端 ?session= 参数是唯一权威
  • 只对 permission.asked / question.asked 严格过滤,防止用户误答别 session 的提示
  • 这条规则是 bug fix 310d507 的核心,改之前会"切 session 后发消息无回复"

2. Cell 上下文不再自动注入

  • OpenCodeRequest.context 收缩为 {notebookPath} — 只告诉服务端哪个 notebook
  • _build_request_body 只发 parts=[{text: prompt}]
  • 用户通过"📋 插入单元格内容"按钮显式 opt-in 把 cell 源码作为 markdown code block 插入
  • 理由:LLM context 严格匹配用户意图,避免被前一个 cell 的 traceback 污染
  • 这是 310d507 的第二部分

3. 1 notebook N sessions

  • SessionManager._active (dict[notebook] → active sessionId) + _bound (dict[notebook] → [sessionId, ...])
  • 新方法:create_new / bind_existing / set_active / unbind / unbind_active / delete_session / list_sessions_for_notebook / get_active
  • release(notebook) 删所有 bound sessions(不只是 active)
  • delete_session(notebook, sid):先调 OpenCode delete,成功后再 unbind(失败时 retry 仍可能)

4. 流式 markdown 渲染(避免 partial-render flicker)

  • 流式期间只 textContent += delta,marked.parse(否则 fence 没闭合时的部分渲染会闪 toolbar)
  • session.idle_resetStreamPointers 触发一次完整 marked.parse 渲染
  • 终态与 setMessages 路径(setMessages 重新打开时拉的)完全一致

5. 交互 UI 权限

  • permission.asked / question.asked 事件渲染真实交互组件(按钮 / 输入框)
  • 点击通过后端路由代理回 OpenCode Serve
  • 后端路由:PUT /session/:sid/permissions/:permId + PUT /session/:sid/question/:qId/reply

测试

  • 后端:73 pytest(主要看 test_routes.py + test_session_manager.py)
  • 前端:72 jest(opencode_cell_actions.spec.ts 是核心,900+ 行覆盖了 SessionSelector / 流式渲染 / permission/question 交互)
  • 命令: source .venv/bin/activate && jlpm test && python -m pytest opencode_bridge/tests/

已知小问题 / 待优化

  1. 多 session 切换时,前端用 PUT /sessions/notebook 一个调用搞定(bind + set active 都做),不再用 /sessions/active — 后者保留供"已 bound sessions 之间切换"用
  2. cell_context.ts 已删,如果新增"只想要 cellId"的功能,需要重建
  3. package.json version 是 0.1.1,跟远端 tag 一致(之前是用户手工 bump 上去的)
  4. UNIFIED_SYSTEM_PROMPT 还在后端 routes.py 里,描述 LLM 的角色("你是一个代码助手...")。如果改 prompt,会同时影响所有 prompt

修改后的交互模式

  1. 用户在 cell 工具栏点 🪄 按钮
  2. 内嵌 prompt 出现:SessionSelector(列出 OpenCode 所有 session) + Provider/Model + history + "📋 插入单元格内容" + 输入框 + send/cancel
  3. 用户输入 prompt,可选按"📋 插入单元格内容"把 cell 源码作为 code block 追加
  4. send → 后端 prompt_async 立即返回 {ok, sessionId} + 订阅 SSE
  5. 流式事件实时渲染,session.idle 触发 markdown 渲染(终态正确)
  6. permission/question 事件渲染交互 UI,点击代理回 OpenCode
  7. 切换 session → 关闭旧 SSE + bind 新 session(后端 PUT /sessions/notebook)+ reload history

还没做过的事(如果新会话要继续)

  • UI tests (Playwright) — 写了 spec 但没真跑
  • 持久化 session 选择(目前只持久化 provider/model)
  • cell 类型推断优化(目前用 model.type,真实 cell 应该有 metadata.language)
  • 后端 session TTL / cleanup(目前不删,除非用户显式 release)
  • 真实 OpenCode Serve 接入测试(目前用 mock)