首页
/ gstack × Conductor 侧边栏集成设计:让 Chrome 侧边栏成为 Agent 会话的实时视窗

gstack × Conductor 侧边栏集成设计:让 Chrome 侧边栏成为 Agent 会话的实时视窗

2026-09-06 12:51:38作者:廉彬冶Miranda

本文基于 gstack 仓库中的设计文档 CONDUCTOR_CHROME_SIDEBAR_INTEGRATION.md,完整解析 gstack 的 Chrome 侧边栏如何从"独立运行的第二个 Claude 实例"演进为 Conductor 主会话的实时视窗:读完后你将理解三项核心 API 需求(会话事件订阅、消息注入、工作区注册)的设计动机与契约细节,以及 gstack 侧已完成的扩展架构(SSE 事件渲染、双令牌鉴权、PTY 终端)如何与之对接。

1. 问题背景:双窗口盲点与"两个互不说话的 Agent"

在 gstack 工作流中,Claude 常在 Conductor 工作区内并行干活——编辑文件、跑测试、浏览你的应用。当前 $B connect 启动的 Chrome 侧边栏只能让你看到 browse 命令流,但当 Claude 在 Conductor 工作区里做 QA、看你的网站时,你只能盯着 Conductor 的聊天窗口看工具调用一条条滚过,却看不到浏览器里实际发生了什么

原始设计文档指出,这里存在一个结构性问题:

侧边栏目前运行的是它自己独立的 Claude 实例。它看不到主 Conductor 会话在做什么,主会话也看不到侧边栏在做什么——这是两个互不通信的独立 Agent。

这个"双实例"架构在仓库早期设计中有迹可循。GSTACK_BROWSER_V0.md 的架构图显示,侧边栏 Agent 是一个 claude -p 子进程包装器("Future: BoomLooper" 即预留的替换点),而 SIDEBAR_MESSAGE_FLOW.md 记录了现行实现:侧边栏的主界面是一个交互式 claude PTY(xterm.js + 独立的 terminal-agent.ts 子进程持有 claude 子进程)。这些实现都证明了"侧边栏自有 Agent"的路线是可行的,但正是它造成了上下文割裂。

修复方向在设计文档中一句话概括:让侧边栏成为 Conductor 会话的"视窗"(window into the session),而不是另一个独立的东西。

2. 核心设计:向 Conductor 提出的三项 API 需求

设计文档把需求收敛为三个接口能力,全部围绕"一个 Agent、两个视图"的目标。

2.1 需求一:让我们看到 Agent 正在做什么(事件订阅)

侧边栏需要一条从 Conductor 会话到扩展的事件管道,文档给出的形态是 SSE 流或 WebSocket,事件随会话发生实时推送,典型事件包括:

  • "Claude 正在编辑 src/App.tsx"
  • "Claude 正在运行 npm test"
  • "Claude 说:我会修复这个 CSS 问题……"

关键前提是:侧边栏已经具备渲染这些事件的能力——工具调用渲染为紧凑徽章(badge),文本渲染为聊天气泡。缺的只是那根"管子"。

配套的 CONDUCTOR_SESSION_API.md 把这一需求落地成了具体的 API 契约:一个 SSE 端点 GET http://127.0.0.1:{PORT}/workspace/{ID}/session/stream,以 NDJSON 事件重放 Claude Code 的会话流。事件类型直接复用 Claude Code 的 --output-format stream-json 格式,无需发明新 schema:

event: assistant
data: {"type":"assistant","content":"Let me check that page...","truncated":true}

event: tool_use
data: {"type":"tool_use","name":"Bash","input":"$B snapshot","truncated_input":true}

event: tool_result
data: {"type":"tool_result","name":"Bash","output":"[snapshot output...]","truncated_output":true}

event: turn_complete
data: {"type":"turn_complete","input_tokens":1234,"output_tokens":567,"cost_usd":0.02}

这条契约在 gstack 侧有现成的对接面:extension/sidepanel.js 已经用 EventSource 消费 browse 服务器的 /activity/stream SSE 流,并带 after= 游标参数实现断点续传(withCredentials: true 携带一次性 view-only cookie 鉴权)。换成 Conductor 的会话流只是换一个 URL——渲染层逻辑不变。

2.2 需求二:让我们向会话中发送消息(消息注入)

当用户在 Chrome 侧边栏输入"点击另一个按钮"时,这条消息应当以用户在 workspace 聊天框中亲自输入的身份出现在 Conductor 会话里,Agent 在下一轮自行拾取并执行。

设计文档称之为"魔法时刻"(the magic moment):用户正盯着 Chrome 看,发现了不对的地方,直接在侧边栏输入纠正指令,Claude 立刻响应——全程无需切换窗口。这条需求是"一个 Agent、两个视图"体验闭环的另一半:需求一只管看,需求二才让用户能改。

2.3 需求三:从目录创建 Conductor 工作区

$B connect 启动时已会为文件隔离创建一个 git worktree。需求三是把这个 worktree 注册为 Conductor 工作区,让用户能在 Conductor 的文件树中看到侧边栏 Agent 的文件改动。文档同时点明其战略意义:这为多浏览器会话(multiple browser sessions)打好地基——每个浏览器会话拥有各自独立的工作区。

3. 为什么这件事重要:把黑盒变成可视过程

设计文档用三个要点说明收益,核心是消除 /qa/design-review 这类技能的"黑盒感"(Claude 说"我发现了 3 个问题",但你不知道它在看什么):

  • 实时观看 Claude 测试你的应用——每一次点击、每一次导航、每一张截图都同步呈现在你正看着的 Chrome 里;
  • 可以随时打断——"不,测一下移动端视图""跳过那个页面",无需切换窗口;
  • 一个 Agent,两个视图——正在改你代码的那个 Claude,就是正在控制浏览器的那个 Claude。没有上下文复制,没有状态陈旧(stale state)。

4. gstack 侧的现状:几乎零改动即可对接

设计文档明确列出了 gstack 侧已完成并随版本发布的组件清单,这也是"把侧边栏变成会话视窗"成本如此之低的根本原因:

已建成的 gstack 侧组件 说明
Chrome 扩展自动加载 $B connect 运行时自动装载(manifest 中 key 字段固定扩展 ID,服务端仅向固定 Origin 发放令牌)
侧边栏自动打开 用户零配置
流式事件渲染器 工具调用、文本、结果的 SSE 驱动渲染
聊天输入 + 消息队列 输入缓冲与排队
重连逻辑 + 状态横幅 断线自动恢复
会话管理 带持久化聊天历史
Agent 生命周期 spawn / stop / kill / 超时检测

仓库中这些组件都有对应实体可查证:

  • extension/manifest.json:Manifest V3,sidePanel 权限 + 固定 key(对应 POST /extension-token 的 pinned-origin 发令牌机制),host_permissions 仅放行 http://127.0.0.1:*/ws://127.0.0.1:*/,与 browse 服务器的本地信任模型一致;
  • extension/sidepanel.jsEventSource 消费 /activity/stream,先取 view-only cookie 再开流,after= 参数续传;
  • extension/sidepanel-terminal.jsextension/background.js:PTY 终端与服务引导(/health 探活 → /extension-token 换令牌 → POST /pty-session → WebSocket 握手);
  • SIDEBAR_MESSAGE_FLOW.md:完整的启动时序、双令牌模型与威胁模型文档。

文档结论是:gstack 侧唯一要做的改动,是把数据源从"本地 claude -p 子进程"换成"Conductor 会话流"。扩展代码保持不变。

值得注意的安全设计同样来自现有实现:SIDEBAR_MESSAGE_FLOW.md 中的双令牌模型(AUTH_TOKEN 用于 /pty-session,短生命周期 gstack-pty.<token>Sec-WebSocket-Protocol 传递用于 /ws 升级鉴权,二者严格不互通)正是为"令牌泄漏不能升级为 shell 访问"而设计的分层隔离——Conductor 侧新增 SSE 端点时沿用同样的本地信任模型即可。

5. Conductor 侧的 API 契约与关键设计决策

CONDUCTOR_SESSION_API.md 给出了 Conductor 需要交付的完整服务端契约,这里完整保留其核心内容。

5.1 会话流端点与截断策略

GET http://127.0.0.1:{PORT}/workspace/{ID}/session/stream(SSE,NDJSON 事件,如 2.1 节所示)。内容截断规则:工具输入/输出在流中上限 500 字符,完整数据保留在 Conductor 的 UI 中。文档把截断明确定义为隐私特性(privacy feature)——长代码输出、文件内容、敏感工具结果永远不离开 Conductor 的完整 UI;侧边栏是"摘要视图,不是替代品"(300px 宽的面板里长内容没有意义)。

5.2 工作区发现端点

GET http://127.0.0.1:{PORT}/api/workspaces 列出活跃工作区:

{
  "workspaces": [
    {
      "id": "abc123",
      "name": "gstack",
      "branch": "garrytan/chrome-extension-ctrl",
      "directory": "/Users/garry/gstack",
      "pid": 12345,
      "active": true
    }
  ]
}

扩展通过匹配 browse 服务器 git 仓库(来自 /health 响应)与工作区的 directory 或 name 来自动选中工作区。

5.3 安全模型

  • 仅本地回环(Localhost-only):与 Claude Code 自身 debug 输出同一信任模型;
  • 默认无鉴权:若 Conductor 希望加鉴权,可在 workspace 列表里附带 Bearer token,扩展在 SSE 请求中携带;
  • 内容截断即隐私控制:长内容不出 Conductor 完整 UI。

5.4 设计决策表

决策项 选择 理由
传输层 SSE(而非 WebSocket) 单向、自动重连、更简单
格式 Claude 的 stream-json Conductor 内部本就在解析它;无新 schema
发现机制 HTTP 端点(而非文件) Chrome 扩展无法读文件系统
鉴权 无(localhost) 与 browse 服务器、CDP 端口、Claude Code 一致
截断 500 字符 侧边栏约 300px 宽,长内容无用

6. gstack 中已存在的 Conductor 集成点

有意思的是,gstack 并非第一次与 Conductor 打交道——仓库中已有若干为 Conductor 环境专门编写的适配代码,可以印证"Conductor 会话"在 gstack 心智模型中的位置:

  • lib/is-conductor.ts:Conductor 宿主检测的单一事实来源。Conductor(一个并行运行多个 coding agent 的 Mac 应用)会在会话环境中设置 CONDUCTOR_WORKSPACE_PATH / CONDUCTOR_PORT;该辅助函数在调用时读取传入 env(而非模块加载时快照),原因注释写得很清楚——ESM 会把静态 import 提升,加载期读取无法被测试用 process.env.X = ... 固定;
  • lib/conductor-env-shim.ts:Conductor 工作区不继承用户交互式 shell 环境,ANTHROPIC_API_KEY / OPENAI_API_KEY 可能缺失而 GSTACK_ 前缀形式存在。该 shim 在标准名空缺时把 GSTACK_ANTHROPIC_API_KEY 等提升为标准名,供子进程(gbrain embed、@anthropic-ai/claude-agent-sdk 等)拾取;
  • conductor.json:为 Conductor 宿主声明的脚本钩子(setup / archive 映射到 bin/dev-setupbin/dev-teardown);
  • test/is-conductor.test.tstest/conductor-env-shim.test.ts:对上述两个适配点的单测覆盖。

从源码结构看,这些适配层说明 gstack 已把 Conductor 视为一等宿主(first-class host);本文所述的侧边栏集成,是把这种关系从"环境检测"深化到"会话级实时桥接"。

7. 实施路径与工作量估算

综合 CONDUCTOR_SESSION_API.md,两侧分工与步骤如下。

扩展侧(gstack,5 步)——当 Conductor API 就绪后:

  1. 侧边栏通过端口探测或手动输入发现 Conductor;
  2. 拉取 /api/workspaces,与 browse 服务器的仓库匹配;
  3. /workspace/{id}/session/stream 打开 EventSource
  4. 渲染:assistant 消息、工具名 + 图标、轮次边界、成本;
  5. 优雅降级:Conductor 不可达时显示 "Connect Conductor for full session view"。

服务端(Conductor):1) 按工作区重放 Claude Code stream-json 的 SSE 端点;2) /api/workspaces 发现端点;3) 500 字符截断逻辑。

工作量估算(原文档结论):Conductor 工程 2–3 天,gstack 集成 1 天;扩展侧约 200 行改动(集中在 sidepanel.js),服务端约 100–200 行——前提是 Conductor 内部已捕获 Claude Code 的 stream-json(它为自己的 UI 渲染本来就在捕获)。

8. 小结

这份设计文档的价值在于把"看 Agent 干活"从产品愿景压缩成了三项可验收的接口需求:事件订阅(看)、消息注入(说)、工作区注册(文件树可见)。gstack 侧的扩展、SSE 渲染器、双令牌鉴权、PTY 生命周期均已就位并被 SIDEBAR_MESSAGE_FLOW.md 完整文档化,因此整个集成的剩余工作量几乎全部落在 Conductor 的会话流导出端点上。实现完成后,"一个 Agent、两个视图"——在 Conductor 里写代码的 Claude 与在 Chrome 里被观察的 Claude——将是同一个 Agent,/qa/design-review 的每一次点击和截图都将实时可见、随时可打断。

登录后查看全文
热门项目推荐
相关项目推荐