chatgpt-web-bridge · diff
git:20260914.77a29e9 to git:20260920.3367b72
96 added, 144 removed. Audit B to A.
---
name: chatgpt-web-bridge
description: |
- 通过 chatgpt-web 桥(MCP / REST)把本地 agent 接入 ChatGPT 网页版:网页端当大脑,本地只做传输与执行。
- 触发:「推进/继续网页端会话」「让网页端设计、本地执行」「协作/对等/共同推进」「对等模式/让两边辩一下/协作评审」「共同协作/本地跑任务它维护模型」「用网页端的资料/只读拉取」「把网页版接进 harness」「conversation_id 续接」「网页版比较聪明所以用它」。
- 不适用:让网页端直接执行本地 shell/文件操作——桥只承载文本,网页端永远没有本地工具。
+ 通过 chatgpt-web 的 MCP 或 REST 桥,把本地 agent 接入已登录的 ChatGPT Web。
+ 适用于“推进/继续网页端会话”“让网页端设计、本地执行”“协作/对等推进”,以及只读拉取网页端会话/项目资料和 conversation_id 续接。
+ 桥只传递文本;网页端没有本地 shell、文件系统或其他本地工具。
---
# ChatGPT Web Bridge
- ## 快速使用(自然语言即模式)
-
- | 你说 | 模式 | 本地做什么 |
- |---|---|---|
- | 「推进网页端 XXX 项目/会话」 | A 推进 | 发「继续」式最小提示往前推;纠偏算在内(只给事实不给方案);判断何时真结束,没结束就不停 |
- | 「让网页端 GPT 指挥现在这个项目」 | B 指挥循环 | 它出指令 → 本地执行 → 回帖事实到同一 conv → 直到它说完成 |
- | 「协作/对等/共同推进 XX」 | C 协作 | **取长补短**:交锋(因模型不同/权限不同有信息差)+ 执行(永远本地,禁止交给网页端) |
- | 「用网页端 XXX 的资料/看它怎么说的」 | D 只读 | 只拉取不发送:会话记录、项目文件、记忆——零写操作 |
-
- **会话定位(项目制 + 申请制)**:**所有会话必须落在项目里**。首轮前先 `list_projects` 找最接近的项目 → **向用户确认该项目** → 首轮带 `project_id`(名字即可)——确认前禁止发送。无接近项目 → 报用户处理(桥不能建项目,由用户网页端建或显式批准例外);**独立会话只在用户显式批准时允许**。续接已有会话:指明 conversation_id/标题精确命中,并用 `list_conversations` 的 `gizmo_id` 核对该会话确属项目;目标会话是独立会话 → 报用户决定(迁项目或批例外)。**任何会产生新网页会话的动作(首轮、换 project_id、原会话损坏重建)都必须先向用户说明原因并获批准。**
-
- ## 核心原则:谁思考
+ 这个 skill 负责把网页端当作一个有自己上下文的协作者接入本地工作流。网页端负责基于会话、Project 指令、Project 文件和可用的网页能力进行判断;本地 agent 负责执行本地动作、保存原始证据、判断任务是否真的完成,并对隐私和权限边界负责。
- **网页端是大脑,本地是手和邮差。**
+ ## 先选模式
- - 网页端持有完整上下文(会话历史 + Project 指令 + Memory)——它的判断依赖这些,不需要在消息里复述给它。
- - 本地侧只做三件事:把文字送进正确的 conversation_id、把回文拿回来、(指挥模式下)忠实执行网页端给出的任务。
- - **不要替它写执行步骤。** 发「继续」式最小提示,让它自己决定下一步。消息写得越细,网页端思考得越少。
+ | 用户意图 | 模式 | 回合形状 |
+ | --- | --- | --- |
+ | “推进/继续这个网页会话” | A 推进 | 发最小推进消息,读回结果,按终止条件决定是否继续 |
+ | “让网页端指挥现在的项目” | B 指挥 | 网页端给指令,本地执行并回帖事实,直到指令循环结束 |
+ | “协作/对等/共同推进” | C 协作 | 双方先独立陈述,再交换证据、交锋,执行落在本地 |
+ | “用网页端项目/会话的资料” | D 只读 | 只读项目、会话或记忆;不发送消息、不创建会话 |
- ## 适用边界与安全
+ 不要把一次回帖本身当成任务完成。A、B、C 都需要检查当前模式的终止条件;D 在资料已经取回并交付后结束。
- **触发**:推进/继续已有网页端会话、网页端设计本地执行的回合制循环、「协作/对等/共同」类取长补短(C 协作模式:交锋+本地执行)、conversation_id 专线续接。
+ ## 项目和会话绑定
- **不适用**:
+ 先确定目标,再发送任何写操作。
- - 让网页端直接执行本地命令或读写文件——桥是文本桥,网页端永远没有本地工具
- - 高频自动轮询——这是低频人工节奏
- - Chrome 专用 profile 未登录 ChatGPT——先登录再调用,别让请求悬死
+ - 续接已有会话时,使用用户给出的 `conversation_id`;没有 ID 时用 `list_conversations` 查找,并用标题、更新时间和 Project 归属核对,不凭最近一条猜测。
+ - 创建新会话时,先用 `list_projects` 找候选 Project。Project 名称必须精确解析为唯一 ID;未知或歧义名称停止并报告。没有合适的 Project 时,请用户在网页端创建,或由用户明确批准独立会话例外。
+ - 首轮需要改变会话绑定(新会话、新 Project、换 `conversation_id`、损坏会话重建)时,向用户说明具体绑定和原因。一个已经明确批准的、相同的绑定不会因为 MCP 重连或进程重启而自动变成新的审批请求;只有绑定、权限范围或用户意图改变,或已有批准无法可靠关联时才重新确认。
+ - 记录并沿用返回的 `conversation_id`。不要把省略 ID 的自动续接当作跨进程的可靠专线。
- **工具边界(没有的恢复原语)**:桥**没有 resume/continue-generation 工具**——生成中断或死生成后不存在「续拉那条回复」的接口,唯一恢复路径是**在同一 conversation_id 里发短催促**(「继续」/「接着上一条写完」),网页端会基于已落盘上下文续写。不要在工具清单里找 resume,找到了也没有(2026-09-14 实证:危机中调 `mcp_list_tools` 找续拉工具是空转)。
+ Project 提供上下文范围、指令和文件入口,但不能把“Project”当成绝对隐私隔离证明。发送前仍要检查目标 Project 和会话;不要把本地私密材料放进网页端,除非用户明确要求且该传输在当前任务范围内。
- **固定成本与限制(结构性,不可优化——定价进决策)**:
+ ## 结果优先的调用纪律
- | # | 成本 | 实质 |
- |---|---|---|
- | C1 | 每轮数分钟生成 | 联网+长报告是 ChatGPT 思考本身,回合制本质,省不掉 |
- | C2 | 发送 ≥30s 节流、限流冷却 300s | 防账号级限流的必要成本,绕过会连累全部通道 |
- | C3 | 长文经 DOM composer 注入 | 不是干净 API——`\n` 截断已修但前端一改就出新坑;长稿拆多条进同一 conv |
- | C4 | 网页会话间无真隔离 | 新会话仍共享账号记忆+项目上下文——需盲/隔离的活只能本地子代理 |
- | C5 | 桥是 DOM 自动化 | 随 OpenAI 前端演进持续长新坑;事故只能降成本不能清零 |
+ 一次调用可能已经完成网页端动作,即使客户端随后超时、取消或丢失连接。每次 `chat_completion`、写操作或长读操作之后,先完整消费当前返回值,再决定是否需要下一次调用。
- **安全与验证**:所有写操作(新建会话、删除会话)都是真实网页操作;删除类工具需服务端显式 `W2A_ENABLE_DESTRUCTIVE=1`。本地侧只验证 conversation_id 续接正确、tab 复用正确,不替网页端校验其输出内容的正确性——那是它自己的责任。
+ 1. 检查结构化字段、内联文本、`conversation_id`、状态、delivery receipt,以及客户端暴露的 `overflow`、`out_file` 等结果指针。结果指出文件或溢出内容时,先读取该内容;不要为了“确认一下”再发相同消息或重复读会话。
+ 2. 如果结果带有可用的投递收据,收据就是本次投递的主要证据。`delivery_stage`/`reply_persisted=true` 说明已经提交或已有 assistant 节点,不自动等同于终态完成;仍要服从 tail 的 `in_progress`/`generating` 状态。保存会话 ID、消息/回合标识(如果有)、状态和时间;仅在收据明确为未知或不完整时做一次有目的的补读。
+ 3. 客户端超时、取消、断线或 transport error 只表示“本地没有拿到完整结果”。它不证明消息未发送,也不证明网页端没有继续生成。不要据此自动重发。
+ 4. 需要恢复时只使用同一 `conversation_id`。优先调用 `wait_reply` 或一次 `get_conversation(tail=N, fresh=true)`;`fresh` 绕过短缓存,同一时刻已有的 fresh 读取仍可能共享,但不会加入发送前启动的普通旧读取。长历史用 backend 分页或绝对路径 `out_file`。若接口返回 `partial=true`/`paging_supported=false`,它只是当前 tab 的局部观测,没有绝对 offset/total/has_more,不能继续递增 offset 把它当分页;等后端可读时再做一次 fresh/tail 读。
+ 5. tail 显示 `in_progress`、`generating` 或其他明确的进行中状态时,保持只读等待,绝不发送“继续”“接着写”或其他催促。读超时也不能改变这一点。
+ 6. tail 为 user、收据为 false/null、读结果为空或状态未知,都只是证据不足。只有桥明确给出终态失败、且确认没有仍在进行的生成时,才讨论是否在同一会话重试;没有这个证据就报告未知并等待用户/系统的下一步。
+ 7. 进度通知描述事实和阶段,例如 `phase=send_ack`、`phase=web_generation`、`phase=tail_read`、`elapsed_s`、`status`、`source`。不要用“没卡住”“只是正常慢”代替观测,也不要把某个固定分钟数当成系统契约。时间预算应来自当前任务、客户端和用户约束。
+ 8. 尊重工具返回的 `retry_after`、速率门和取消语义。不要写无界轮询或用重复读来绕过限流;如果客户端取消了等待,让调用结束,再依据现有收据或一次 tail 读恢复。
- ## 循环纪律:本地侧是唯一的发动机
+ 这条纪律适用于所有四种模式。它把“是否已经送达”“网页端是否仍在生成”“本地是否拿到完整回文”分成三个不同问题,避免一次失败被解释成三种失败。
- 网页端永远不会主动发起下一轮——**本地收到回帖不等于回合结束**。每轮拿到回文后,先对照本模式的终止条件判断;不满足就继续发下一轮,直到终止条件命中或用户叫停。「它回完了」不是停止理由,「终止条件命中」才是。
+ ## A. 推进模式
- **等回复纪律(2026-09-11 定型,网页端裁决后才收敛的坑)**:
+ 适合用户要把一个已有网页任务往前推进,但不要求本地替网页端规划步骤。
- - **长等待是常态**:联网搜索 + 长报告要数分钟到十几分钟。等待本身就是工作的一部分,不是回合结束信号。
- - **轮询到有结果为止**:发出消息后对方仍在生成 ≠ 本轮结束。在回合内持续拉取(REST 返回或 backend-api 拉尾部)直到拿到完整回文或明确失败;回合时长、上下文压缩预期都不构成停止理由。拿不准时就拉尾部确认——「还在写」和「写完了」是可观测的两种状态,不许靠猜。
- - **阻塞抛给协作者**:本地卡住的点(环境门禁、授权边界、协议歧义、缺工具)先发回同一 conversation_id 求方案,写清「阻塞事实 + 已试过的路径 + 待裁决项」,不要在本地只汇报给用户或写进任务控制就停。网页端看不见本机状态,它要的是可裁决的输入不是进度摘要。
- - **本地中断 ≠ 消息未送达**:chat_completion 在本地被 cancel/打断时,消息可能已落盘网页端——重发前先 `get_conversation` 核实尾部,否则产生重复发送(2026-09-12 实证:被打断的发送已完整落盘)。同理,`not_ready` / `Turn reconciliation failed` / `generation_stuck` 等报错也可能已送达(同日实证:两次 not_ready 报错均落盘,造成同一里程碑重复上报)——**任何报错后重发前必须先核实**
- - **失败发送会留草稿**:`Composer text verification failed` 时插入内容可能部分留在 composer 里成为该会话的草稿,后续发送连带校验失败——2026-09-15 起桥在发送窗口(type→click→ack)任何失败时自动清 composer 草稿;若仍见校验失败,草稿来自别的原因(人工输入/旧残留),用 `evaluate_script` 清或换新会话(2026-09-14 实证)
- - **`Composer text verification failed after retry`**:2026-09-14 前的主因是会话页 composer 遇 `\n` 只插入首段(ChatGPT 前端更新,`Input.insertText` 截断);vendored 源码已改走 `execCommand('insertText')`,若再见此错先怀疑残留草稿或前端又改
- - **`no driver slot available` / health `degraded`**:daemon 丢 CDP 连接不自愈。查 `http://127.0.0.1:8080/health` 的 `driver_connected`;为 false 则重跑 `start.ps1`(幂等,不动 Chrome/登录);若有多组同名 daemon 进程先清孤儿
- - **新会话 ≠ 干净隔离**:换 system_prompt 开新会话仍共享同账号、同 Memory、同项目上下文——A/B 生成、盲评这类需要真隔离的活只能用本地子代理,多开网页会话只是表面隔离(2026-09-14 实证:B 侧生成会话混进了盲评消息,评审看到了被评审稿的生成过程)
- - **死生成判定**:发完后拉尾部若最后一条仍是自己发的 user 消息 = 回复未持久化(生成中途死亡/被回收,`chat_completion` 可能只返回了残缺流)——**立即发短催促(「继续」)续接,不要轮询等一条永远不会来的回复**(2026-09-14 实证:轮询 ~7 分钟/8 次拉取后才催促);分页前先无参拉一次拿 `total` 再 `offset=total-2` 定位尾部,不许盲试 offset。机制侧:`chat_completion` 返回带 `reply_persisted`(true/false/null),false 即此情形;等回复用 `wait_reply` 工具而不是手写轮询——它在 tail 卡 user 超 `dead_after_seconds`(默认 120s)时返回 `status:"dead"` 提前退出,在长超时时也带进度通知防掐断
- - **桥侧超时 ≠ 网页端死**:`generation_stuck` / 读超时只是桥等不到了,网页端可能仍在正常生成——**尾部有 assistant 消息 ≠ 生成完**(引言节点会先落盘、status=in_progress 持续更新)。`wait_reply` 只在终态(非 in_progress)报 `replied`;若它 timeout 先看 `tail_status`——`in_progress` = 还在写,再 `wait_reply` 即可,**此时催促是错误动作**(2026-09-15 实证:误判死生成发「继续」,网页端实际仍在生成)
+ ```text
+ 目标:继续 conversation_id=C,直到网页端宣布阶段完成或提出需要用户裁决的问题。
+ 本地动作:chat_completion(message="继续", conversation_id=C)
+ 回合结束前:消费完整结果;若仍有下一步且没有进行中的生成,再发下一条最小消息。
+ ```
- ## 四种用法
+ 纠偏只发送事实,不替网页端写方案,例如:`事实更正:文件 X 的实际值是 Y。请据此继续。`
- ### A. 推进模式(默认,低频)
+ 终止条件是网页端明确宣布本阶段完成且没有下一步、它提出必须由用户/外部权限回答的问题,或用户叫停。写完一轮报告不是终止条件;仍在生成也不是发送催促的理由。
- 把已有网页任务往前推一格:
+ ## B. 指挥循环
- - 最小提示:`继续` / `按你的结论推进` / `下一步`
- - 纠偏只给判断不给方案:`这条线判断错了,事实是 X` —— 让它自己想修法
- - 每轮必须带 conversation_id;不知道就先 `list_conversations` 找目标
- - 网页端产出长报告时,本地只保存/转发原文,不做二次「翻译」
- - **终止条件**(满足其一才停):网页端书面宣布本阶段完成且没有它自己的下一步;它提出需要用户/外部世界才能回答的问题(权限、要买的数据、要你拍板的方向);用户叫停。**它写完一轮报告不是终止。**
+ 适合网页端维护判断、本地执行命令或改文件的回合制流程。
- ### B. 指挥循环(网页端指挥本地)
+ 首轮确定 Project 和会话绑定后,每轮只回帖事实:结果摘要、关键证据、阻塞项和可复核路径。网页端没有本地工具,所以任何 shell、文件写入、测试和真实外部动作都由本地完成。
- 网页端设计 → 本地执行 → 回帖 → 网页端继续设计:
+ ```text
+ 网页端:给出任务指令
+ 本地:执行并保存原始输出
+ 本地 → 同一 conversation_id:回帖“做了什么、观察到什么、哪里阻塞”
+ 网页端:给下一条指令或宣布结束
+ ```
- 1. 首轮前先按「会话定位」走完项目确认(`list_projects` → 用户确认 → `chat_completion` 带 `project_id`),存下返回的 `conversation_id` 作为专线
- 2. 每轮本地回帖只写事实:**结果摘要 + 关键证据 + 阻塞项**,不夹带自己的分析
- 3. 网页端输出的指令照做,做完把结果发回同一 conversation_id
- 4. 循环频率低是正常的——这是回合制协作,不是轮询系统
- 5. **继续义务**:回帖后必须读它的下一条指令并执行——一轮就停等于循环没建起来
- 6. **终止条件**(满足其一才停):网页端宣布任务完成/无下一轮指令;连续两轮它只复述已有结论不产生新指令;它的指令越权(要本地没有的能力)且澄清后仍无法执行;用户叫停
+ 如果回帖调用被取消或超时,先处理已有结果/收据,再恢复状态;不要把不确定的回帖重新发送两遍。终止条件是网页端宣布完成且没有新指令、连续两轮只复述而没有新指令、指令越过本地能力边界且澄清后仍无法执行,或用户叫停。
- ### C. 协作模式(取长补短:交锋 + 本地执行)
+ ## C. 协作模式
- 用户说「协作/对等/共同」即进入本模式。两个成分缺一不可:
+ 适合两侧确实拥有不同信息或权限,需要交换证据后共同推进的问题。它不是让网页端替本地执行,也不是把本地答案先发过去再请求同意。
- - **为什么交锋**:两个模型不一样(思考方式不同)、权限不一样(网页端看得见搜索/项目/Memory,本地看得见文件系统/执行/实时状态)——信息差决定了必须先把各自立场摊开再合并,任何一侧单方输出都会丢掉对方的盲区。
- - **为什么执行在本地**:网页端永远没有本地工具。需要执行(跑命令/改文件/取真实结果)的动作永远由本地完成,**禁止把执行交给网页端**。
+ 推荐信封:
- 适用前提:**token 不缺、本地智力被认可、两边互有知识盲区**。任何其他场景回到 A/B。
+ ```text
+ [PEER R{n}] {议题}
+ 我的立场:…
+ 依据:…
+ 对你上轮的最强复述及反驳:…
+ 我可能看不见的地方:…
+ 需要你侧提供的资料:…
+ 待裁决:…
+ ```
- **协议(防坍缩结构,顺序不可省)**:
+ 双方先独立作答,再交换;每个议题标记 `proposed`、`accepted`、`rejected`、`deferred` 或 `escalated`,并用新证据而不是语气翻案。执行、文件、测试和外部事实核验落在本地;网页端维护其上下文中的判断和裁决。
- 1. **R0 盲答**:同一问题两侧独立作答——网页端**不许看**本地已写好的答案,本地也别先发自己的立场去锚它。各自交卷后才交换。
- 2. **对齐盲区**:首轮双方各自声明「我看不见什么」。之后把问题路由给看得见的那侧:网页端要本地数据就问,本地要网页端搜/读项目就问。
- 3. **逐条交锋**:每个议题独立走状态机 `proposed → accepted / rejected / deferred / escalated`。反驳先 steelman(复述对方论点的最强版本),再给证据反驳;**只有新证据能翻案,自信语气不算证据**。
- 4. **执行落本地**:议题分出方案后,需要真实执行的步骤由本地完成;网页端只维护判断/模型/裁决。本地上报格式:`[OBS] 任务来源|事前预测(若有)|真实结果(保留原文摘录)|知情度标签`;需要模型修订时发 `[UPDATE-REQUEST]`。
- 5. **上报节奏**:默认每里程碑一次(一个里程碑 = 一条可判定的局部因果链);例外立即上报(预测失败/关键反证/State 明显错误/足以改变行动/不可逆操作前);无信息增量的中间步骤不上报。每步上报会把维护成本做实,只报失败会造成成功样本偏置——两个极端都不要。
- 6. **轮次封顶**:默认 5 轮或议题清零。到顶仍分歧 → 产出**分歧报告**(各自立场 + 关键证据 + 僵持点)交给用户裁决。禁止假装共识。
- 7. **谄媚自检**:连续两轮没有任何实质异议是危险信号不是成功信号——此时主动要求对方「指出我立场里最弱的一点」。
+ 需要盲评或独立比较时,不要假设“子代理”天然盲。使用新鲜上下文、明确的材料白名单、禁止答案钥匙/先验结论,并检查实际输入边界;网页端新会话也不自动提供账号、Project 或 Memory 隔离。达到预设轮数仍有分歧时交付分歧报告,不伪造共识。
- **信封格式**(每轮本地发给网页端的消息):
+ ## D. 只读模式
- ```text
- [PEER R{n}] {议题}
- 我的立场: …
- 依据: …
- 对你上轮的反驳: (先复述你的最强版本)…
- 我可能盲的地方: …
- 需要你侧的数据: …
- 待裁决: …(无则省)
- ```
+ 只读模式不发送消息、不创建会话、不写 Memory。常用工具是 `list_projects`、`list_conversations`、`get_conversation`、`get_project_files` 和 `get_memories`。
- **终止**:双方书面确认收敛,或分歧报告交付用户,或用户喊停。**只交换一轮不是对等协作——议题未达终态前本地侧必须继续发下一轮。**
+ - 先用列表结果确认 ID、Project 归属和可见性,再读取详情。
+ - `get_conversation` 返回的 `reason` 要区分 `ok`、`empty`、`not_found` 和 `fetch_failed`;空消息不等于会话不存在或正在生成。
+ - `backend_read_failed` 的 `kind`/`http_status` 描述网页后台请求失败,`cdp_timeout` 描述浏览器连接命令超时;两者都不能解释成空数据或未发送。权限拒绝立即停止,限流遵守 `retry_after`,不把重连或刷新当成所有读取错误的通用解法。
+ - 需要最新进度时用 tail 读;安全的局部 DOM 结果只能按工具返回的 `source`/`partial`/`total_kind`/状态解释。DOM 的 `total` 只是渲染下界,不能与 backend 总数比较,也不能自行把局部 DOM 拼成完整历史。
+ - 长回复优先 `out_file` 或分页,并消费返回的 `messages_written`、`total`、`has_more` 等字段。不要为了获取长文在工具结果被截断后反复请求同一页。
+ - 只读任务拿到资料即结束,不把网页端结论偷偷改写成本地事实。保留原文、来源和读取时间。
- ### D. 只读模式(用网页端的资料,不发送)
+ ## 通道和生命周期
- 用途:把网页端已积累的东西当本地输入——会话历史、项目文件、Memory、它的既有结论。零发送、零新会话、零 Memory 写入。
+ | 场景 | 推荐通道 | 生命周期处理 |
+ | --- | --- | --- |
+ | MCP 工具随当前 agent 会话使用 | stdio `chatgpt-web2api-mcp` | 由 MCP 客户端启动和回收;断线时重新建立该 stdio 连接,不启动共享 daemon |
+ | 多个客户端共享一个服务 | SSE daemon `:8090` | 用 `start.ps1`/`ensure` 管理共享服务;先看健康状态和运行时元数据,再决定是否重启 |
+ | 串行 REST 客户端 | REST `:8080` | 显式携带 `conversation_id`;遵守相同的结果、收据和取消纪律 |
- - 通道:`list_conversations` / `get_conversation` / `list_projects` / `get_project_files` / `get_memories`(MCP 走共享 utility 槽,不占会话 tab)
- - 单次请求即结束——只读没有循环,拿到资料就地交付
- - 拿到长资料本地只做筛选/引用,不改写它的结论
- - 触发词:「用网页端 XXX 的资料」「看看它/项目里怎么说的」「把 XXX 会话的结论拉下来」
- - **空结果先看 `reason`**:`get_conversation` 返回带 `reason` 字段消歧——`not_found`=后端 404(先核 conversation_id,以 `list_conversations` 的 id 逐字符为准或从 tab 的 `/c/{id}` URL 取)、`empty`=可达但暂无可视内容(常见于生成中)、`fetch_failed`=拉取本身报错、`ok`=正常。历史行为是把 404 `conversation_inaccessible` 静默映射成空——别再把空结果一律当「会话还在」
- - **长文回读走文件**:单页结果可能超 tool-result 预算被截断——`get_conversation` 传 `out_file`(绝对路径)把该页直接写盘再读文件,回文再长也不占上下文;不传 `out_file` 时记着小 `limit` 分页
- - **真实错误取证**:直连 `127.0.0.1:9222`(browser-level flatten attach,不占用桥的 tab),在 chatgpt.com 页内 fetch `/api/auth/session` 拿 token 后 fetch `/backend-api/conversation/{id}`,看真实 status——区分 404(ID 错) / 429(限流) / mapping 结构变化
+ stdio 与 daemon 是两种生命周期模型。当前构建提供只读 `runtime_info`;先调用它核对 `startup_source_fingerprint`、`disk_source_fingerprint`、`started_at`、`capabilities` 和 `restart_required`。如果 `list_tools` 中没有 `runtime_info`,说明连接到旧实例,不能据源码推断已加载修复。不要对 stdio 实例执行 daemon 重启排障;不要把一个 daemon 的旧进程状态当成当前 stdio 进程的证据;只有元数据或宿主明确要求时才重启对应实例。
- ## 硬规则
+ `Runtime.evaluate` 超时由桥根据投递阶段恢复:明确 `not_started` 时,同一请求最多自动重连原标签页一次(重连预算 15 秒,仍受请求总超时限制),然后重新检查发送前提。点击可能已发出时,先消费 `receipt_check` 与消息 ID;收据检查最多 8 秒。`unknown`、查不到节点或检查超时都不能作为未提交的证据,不重发。恢复失败应报告阶段、耗时和已用重连次数,不要求用户手动刷新网页作为常规流程。
- - **一对一专线(申请制)**:一个本地会话只绑定一个网页端 conversation_id,绑定后不得擅自更换。禁止为多路并行/A-B 盲测/分身评审开多个网页会话——需要隔离执行时用本地子代理(各自独立上下文,天然盲),不需要隔离的活本地自己做。**任何新开网页会话都必须先向用户说明原因并获批准**(含首轮——走「会话定位」的项目确认流程;含中途换 project_id、原会话损坏重建)。
- - **执行永远本地**:凡是要产出/执行/跑实验的活都在本地完成(含子代理);网页端只承担依赖它自身上下文与判断的角色(推理、评审、用它项目知识的分析),不是并行生成器。「我做不了干净的盲」不成立——本地起个子代理就是干净的盲。
- - **转述中立**:把用户任务发给网页端时逐字或中性改写,不自行加预设方向(如「哪里弱/哪里是自我安慰」这类措辞等于替它定了批判结论)。
+ `not_started` 只说明尚未进入提交阶段,不代表输入框没有变化;输入超时可能留下草稿。不要自行拼接重发、清空用户草稿或用低层脚本绕过桥的校验。只有桥在同一请求内依据其投递状态完成有界恢复,才能继续本次发送。
- 1. 专线靠 `conversation_id` 续接;首轮创建后可省略(同 session 自动续),跨 session 必须显式带
- 2. 永不发「记住/以后都/remember」措辞——会污染 ChatGPT Memory
- 3. 临时上下文塞在 message / system_prompt 里,不落 Memory
- 4. 同一会话永远落在同一个浏览器 tab(conv-affinity 内置);别手动挪桥用 tab
- 5. **会话必须落项目**:`list_projects` 取最接近项目 → 用户确认 → 首轮一次带对 `project_id`(gizmo id 或精确项目名,桥会校验,传错直接报错而不是落错项目);未确认不发送,无合适项目报用户,独立会话需用户显式批准
- 6. 发现发错会话/项目时**不要立刻重发**——先确认前一条是否已落地,否则产生重复会话;确需重发=新开会话,先向用户申请
- 7. 只读任务在消息里声明「只读测试」;删除类工具需服务端 `W2A_ENABLE_DESTRUCTIVE=1`
- 8. 生成很慢(联网搜索 + 长报告):客户端超时 ≥15 分钟,中途绝不重发
- 9. 桥内置账号级跨进程节流(send ≥30s、backend 读 ≥8s、命中限流全体冷却 300s,配置 `request_pace_*`/`W2A_PACE_*`)——别写循环猛发绕过它,限流会连累所有通道
+ 浏览器或宿主拒绝权限时立即停止对应操作,不改用其他浏览器、调试端口、直接请求或改权限来绕过。升级验收必须来自实际使用的 MCP 连接:本次恢复契约版本为 `2026-09-19.2`,检查 `restart_required=false` 且启动/磁盘指纹一致。另起进程的烟测不能证明旧连接已升级;无法调用该连接时明确报告尚未核实。
- ## 通道选择
+ 维护桥后,分别报告离线回归、真实浏览器不发送验收、真实发送/持久化验收、宿主版本核验的结果。未运行的层次写“未验证”,不能用前一层的通过替代。真实发送需要已存在的发送授权;纯维修请求不自动授权创建网页会话或发送测试消息。
- | 场景 | 通道 | 说明 |
- |---|---|---|
- | 串行推进会话 | REST `POST /v1/chat/completions` + `conversation_id` | daemon 复用会话 tab |
- | agent 工具态 | MCP `chatgpt-web` → stdio `chatgpt-web2api-mcp`(推荐,harness-bound) | `chat_completion` + 读工具 |
- | 读会话/列项目 | MCP `get_conversation` / `list_*` | 走共享 utility 槽,不占会话 tab |
+ 模型选择也要以当前运行时的 `list_models`/能力结果为准。请求的模型不可用或选择失败时应失败并报告实际状态(MCP 的 `model_selection_failed` 会标明 `delivery_stage=not_started`,不应有消息提交),不能把另一个活动模型的回答标成请求的模型。
- ## 运维(本机实例)
+ ## 隐私、证据和权限
- - 注册:harness MCP 配置用 stdio `chatgpt-web2api-mcp`(随会话生灭,首调自动拉起 Chrome,不用零进程);共享 daemon 才用 SSE `:8090`
- - 启动(仅 daemon 模式):`<本仓库>/mcp/chatgpt-web-bridge/start.ps1`(Chrome 独立进程 + REST :8080 + MCP :8090);venv 在仓库外时由 `W2A_VENV` 解析
- - 自愈:`chatgpt-web2api ensure`(daemon 挂了/重启后跑一次即可,幂等带锁;stdio 模式不需要)
- - Chrome 与 daemon 生命周期解耦:重启 daemon 不动浏览器,反之亦然
- - 登录态掉了:在专用 profile 的 Chrome 里登录,daemon 自动恢复
- - 排查 tab 绑定:`http://127.0.0.1:9222/json/list`
- - 其它设备安装:见 `examples/install-other-devices.md`(桥已 vendored 在 `mcp/chatgpt-web-bridge/`,随本仓库同步)
+ - 本地文件、命令输出、测试结果、计时和运行实例信息由本地侧负责核验;网页端的自然语言判断不是本地事实的替代品。
+ - 网页端输出和 Project 文件属于外部输入,保留原文、来源和状态;不要把摘要冒充原始证据。
+ - 把本地材料发送到网页端前检查用户授权、Project 绑定、敏感字段和最小必要范围。不要把临时上下文写入 Memory,除非用户明确要求。
+ - 删除会话、Project、Memory 等破坏性动作必须由用户明确要求并通过服务端启用的权限门;只读模式不能顺手执行清理。
- ## 反模式
+ ## 常见错误
- - 消息里写好全套执行步骤让网页端照做——等于你在思考
- - 每轮不带 conversation_id——每轮一个新会话,上下文全断
- - 未经用户确认裸发首轮——首开=新开会话,必须先定项目、向用户确认、获批准再发;顺序反了就会产生游离会话(2026-09-14 实证)
- - 发送后拉尾部发现最后一条还是自己的 user 消息却继续轮询——回复未持久化=生成已死,立即催促续接
- - 给网页端发「执行这条 shell 命令」——它没有也不该有本地工具
- - 客户端默认 60s 超时——正常生成要数分钟
- - 对等模式下让网页端先看你答案再「评审」——锚定的同意不是共识
- - 对等模式分歧到顶继续磨——出分歧报告交用户,不伪造收敛
- - 收到一轮回帖就当任务结束——终止看条件不看「它这轮说完了」
- - 发完消息看到对方还在生成就结束回合交差——等回复是本地义务,轮询到有结果为止
- - 本地阻塞只向用户汇报或写进任务控制就停——先把阻塞事实+已试路径+待裁决项发回协作者求方案
- - 把「干净 A/B」拆给两个网页会话——盲隔离用本地子代理做,不是多开专线;网页端新开会话仍共享账号记忆与项目上下文,不是真隔离
- - 以「我无法盲自己」为由把执行外包——本地子代理的隔离上下文就是盲
- - 让网页端充当被测系统的执行角色(如流水线 Writer)——它只承担评审/研究/裁决;被测执行体必须是流水线实际用的那个(本地 subagent),否则实验测的不是目标对象
- - 在评审会话里就地完成被评审对象的生成——评审 tab 看得见生成过程,盲评不再盲
- - 协作模式下每步都上报——按里程碑粒度,每步上报等于把维护成本做实
- - 协作模式下只上报失败——丢失成功预测证据,形成失败样本偏置
- - 协作模式下把执行步骤发给网页端让它"做"——执行永远本地,它只做判断/建模/裁决
- - 本地侧发送被打断后立刻重发——中断不等于未送达,先核实落盘
+ - 用省略 `conversation_id` 的调用跨进程推进,导致上下文落到错误会话。
+ - 把工具超时、取消或 `transport error` 当成未送达而立即重发。
+ - tail 已是 `in_progress` 仍发送“继续”,把一个正常生成变成重复回合。
+ - 工具或客户端暴露了 `out_file`/overflow/receipt 却不读取,随后重复调用同一读操作。
+ - 把 `empty`、`not_found`、`fetch_failed` 当成同一种空结果。
+ - 把网页端当成本地执行器,或把网页端新会话当成真实盲隔离。
+ - 用 stdio 的问题去重启 daemon,或用 daemon 的旧日志判断当前实例已经加载了新代码。