Skip to content

fix(chat): recover interrupted Agent conversation reads - #6592

Merged
huangruiteng merged 7 commits into
mainfrom
codex/attached-session-live-acceptance-20261016
Oct 16, 2026
Merged

huangruiteng merged 7 commits into
mainfrom
codex/attached-session-live-acceptance-20261016

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

A temporary failure during the first Agent conversation lookup leaves the work page stuck even after the API recovers. The user has to reconnect manually. Active history reads also expose a technical fetch error without explaining whether records survive.

Retry initial discovery after network interruptions, timeouts or API server errors. Keep the selected original Agent and recover without another user action. Explain that displayed records are retained and updates resume automatically. Permission and other client errors stay visible without automatic retries.

  • Basis: reproduced initial-discovery failure and active-history outage; base: main.
  • Ordinary journey: one owner entry → original conversation, including automatic recovery from a temporary discovery failure.

Author Declaration

  • Written by: model_agent — OpenAI Codex, GPT-6 family.
  • Specification: docs/integrations/session-runtime-control-plane-adapter.md, “Experimental owner-local Codex App conversations”; roadmap App-first conversation delivery / Desktop Slice B.
Criterion Disposition Evidence
Temporary initial failure recovers without a reconnect click implemented Compiled UI with production read API: network, timeout and HTTP503 recovery
Permission denial stays stopped; leaving stops discovery retries implemented HTTP403, explicit reconnect after recovery, and unmount observation
Existing history, original binding and reply guards remain intact implemented Shared local error copy; no changes to provider or input-receipt owners; original busy sends disabled
Complete native input, token and approval parity out_of_scope Existing working-session acceptance remains open

Scope And Continuation

The existing discovery effect owns one bounded request at a time and the one-second retry timer. Cleanup cancels its timer and ignores late results. HTTP status remains private request-error metadata. The bounded companion refactor shares interruption classification and localized feedback with the history reader, while a locale ref avoids restarting history. No new settings, provider, shared protocol or executor.

This changes the default only for failed initial discovery: transient read failures now recover automatically. It never retries native input or turns a permission rejection into authorization. The packaged patch is proposed; it has not been installed in the main App or merged.

Validation

  • Exact author head: b9f17a739138747f74e90f71d20e7e844f7b7225; base: 96945d3c964c670a237203c30bb9c5bc168f7da6.
  • Input classes: public_fixture, synthetic, authorized_private_read_only.
  • Local targeted validation and the packaged attached-conversation CI scenario passed. Remaining repository workflows and independent exact-head review are separate checks.
Check kind Result Evidence / limitation
static passed Dashboard TypeScript check; bundle build and source verification at the exact head; JavaScript syntax, diff, DCO and public-boundary checks
real_entrypoint / real_backend passed Installed LoopX 1.4.2 read API plus compiled candidate: original selected Agent, automatic network/timeout/503 recovery, HTTP403 stop and explicit recovery, leaving cancels discovery observation; all product writes blocked by validation proxy
characterization passed Baseline 1.4.2 remains empty with a fetch error after its discovery API is healthy; candidate recovers automatically
durable regression passed in Linux CI Dashboard browser shard 3: the production packaged attached-host-follow-up scenario invokes confirmed and uncertain codex-attached.mjs cases, including network/503 discovery recovery, permission stop, original binding and zero native inputs during read recovery; isolated Desktop protocol peer
prior unchanged-provider evidence retained 25 provider/HTTP tests passed before this continuation; provider source is unchanged. Earlier installed history/refresh/isolation and compiled Chinese/English/390px cases remain distinct evidence
visual passed Public synthetic images below have unchanged blobs; localized active-history feedback is unchanged by this continuation
integration partial Packaged attached-conversation CI passed against the exact author head merged with the stated base. macOS/Windows artifact jobs and Windows PowerShell CI passed; these do not qualify installed native sending, token streaming, approval parity or native WebView shortcuts. Remaining workflows and independent review retain their own gates

The production-API validation proxy forwarded no product writes; unrelated overview operations were outside this validation. No live messages or model calls were made. Live/private records, screenshots, local paths and raw logs are excluded.

Frontend / Visual Evidence

Initial entry, navigation, layout and primary actions are unchanged. Existing recovery feedback now explains preservation and automatic recovery. These active-history examples use public synthetic records with an isolated Desktop protocol peer; initial-discovery retry is qualified separately above.

Before After
Technical read error Retained records and automatic recovery guidance

Attention review: no added cards, controls, motion or color. The normal viewport and workspace composition are unchanged; no new showcase composition is proposed.

Type of Change

  • Bug fix
  • Documentation update

LoopX Area

  • Public docs or presentation surface

Technical Direction

Existing native Chat / Codex attached read surface. No authority-store or TypeScript migration claim.

Shared-authority RFC fixture impact

N/A: no store, routing, provider policy or shared-state contract change.

Boundary Checklist

  • Diff, body and images exclude private state, raw records, credentials, internal links and local paths.
  • Product UI, owning docs and focused existing browser regressions form one recovery change.
  • Visual evidence uses public synthetic data; no live/private screenshots.
  • Every author commit includes DCO. CI, independent exact-head review, merge authorization and installed adoption remain separate.

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…n-live-acceptance-20261016

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
@loopx-agent loopx-agent changed the title fix(chat): explain interrupted Agent history reads fix(chat): recover interrupted Agent conversation reads Oct 16, 2026

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max

Request changes conclusion (author-owned PR; GitHub blocks formal self-review)

评审对应 6592@b9f17a739138747f74e90f71d20e7e844f7b7225。发现一项阻塞性 P2:首次发现收到非 JSON 的 HTTP 503 后不会自动恢复。下面覆盖本次全部五个文件,保留已通过的恢复路径和独立的合并限制。

动机

在 Goal 中查看指定 Agent 原会话、等待工作结果的本机所有者。

原先首次发现失败后,即使接口恢复,页面仍停住;本头能自动恢复网络、超时和 JSON 503,但 HTML 503 后仍须手动重连。

已验证同一 worker 的原记录保留、自动读回和新结果继续出现;非 JSON 服务端错误的自动恢复仍有明确缺口。

不建立第二个执行器,不自动发送回复,不改变原 App 权限;完整原生输入、token streaming、审批和已安装采用不在这个切片内。

具体场景是:所有者从 Goal 的负责人入口进入 worker 工作记录,首次 GET 碰到服务短暂中断。原行为会卡在错误页;即使同一接口已经能读取原记录,用户仍要再点重连。这增加了一次无必要的恢复操作,也打断等待后续工作结果。此次改动确实修复了若干普通失败形式,但不能把 JSON 503 的成功推广到所有服务端失败。

改动思路

发现 effect 继续拥有读重试和清理,局部 request 保留传输事实,既有 provider 独立授权;这比另造重连服务或复制原生会话更小,也保留无自动输入这一不变量。

当前 PR 应完整交付原会话读取中断的恢复、错误反馈及既有浏览器回归;仅需补齐非 JSON 服务端错误分类,不扩展 provider、设置或共享状态。

不改动会保留已复现的卡住行为;只把 TypeError 换成文案也不能修复首次发现。当前设计保持每次请求六秒上限、失败后一个一秒定时器,离开时取消,不引入状态库、连接服务或额外执行器。历史观察沿用原有 cursor/generation 边界;locale ref 供错误回调读取当前语言,避免把语言本身变成重启读取的依赖。原生 provider 仍独立决定绑定和回复权限,前端错误恢复没有获得发送或恢复执行权。

路径 当前观察
初次网络错误、超时、JSON 503 自动重新读取同一 worker,无重连点击。
初次 HTTP 403 停止自动重试;明确重连后重新授权读取。
初次 HTML 503 JSON 解析先失败,状态码未进入重试判断;接口恢复后仍停住。
已有历史期间网络中断 保留原记录和草稿、禁止发送;新读回后恢复显示后续原结果。

此表是本头的实际路径对比;它不描述原生 token streaming 或模型行为。

具体改动

完整差异为 5 文件、+84/-8。生产 TSX 为 +34/-7,既有浏览器场景 +32/-1,集成文档新增18行,两张 PNG 是公开合成记录的前后示意。图片已逐张检查,文案与原生/安装资格的区别写在文档里;没有生成日志、私有会话、额外 provider 或设置变更。

关键代码讲解

  • request 在所有相关请求共用的错误类增加 httpStatus。问题是它在第16行先 await response.json():503 的 HTML 或空体会先抛 SyntaxError,因此新增状态字段根本没有机会记录。
  • interruptedRead / readFailureMessage 用具体错误类型区分网络/超时,给出记录保留和自动恢复说明。它不按报错文本做字符串分类,也不改变发送回执。这个共享小边界合理,但不能凭异常类型恢复丢失的 HTTP 状态。
  • discover 把初次读取放入原 effect 的串行重试,成功后保留指定 targetAgentId。第111行只对传输异常或带状态的服务端错误安排定时器,正是非 JSON 503 漏出的消费点;cleanup 的 active 标志和 clearTimeout 保持离开后停止。
  • history 观察 effect 在失败时保留 messages/draft,清除 snapshot 使回复失效,然后沿既有观察继续读取。真实回读已经验证新追加结果恢复;没有从旧历史推断空闲或自动重发原生输入。

独立规范依据为改动前的 docs/integrations/session-runtime-control-plane-adapter.md,固定 revision 96945d3,章节 “Experimental owner-local Codex App conversations”。该节没有正式验收编号,下列是段落定位标签:original-conversation 已实现原负责人及无绑定不替换;idle-only-reply 已验证中断读期间禁止发送与新鲜读后恢复;leaving-stops-observation 已验证离开后无后续发现;native-parity-boundary 保留原实验阶段,完整原生输入/stream/审批仍由既有 Desktop Slice B 验收。新增文档不是用来给自身实现作独立证明;初次服务失败恢复还须满足这次明确的缺陷修复结果。

对主干的风险

[P2] 保留错误响应状态后再决定是否重试。 我在源码构建的打包页面,让首次发现的真实 Fetch 收到 503 text/html,随后恢复到实际生产读取 API/provider。页面显示 SyntaxError。恢复后观察2200毫秒,计数仍为 discovery=1、history=0,没有表单或原结果;明确点重连后才变成2/1并读取同一 worker。新浏览器用例的503和403都固定返回JSON,因而即使它们全部成功,也捕捉不到这条路径。这是原缺陷仍未覆盖的响应形式,不是声称相较主干新增的数据损坏。

在现有 request 边界保留非 JSON 错误响应的 HTTP 状态,再由现有发现 effect 决定是否重试;只对可恢复服务端失败重试,继续停止 4xx,并补 HTML/空体 503 与非 JSON 403 的浏览器负例。

修复后在既有 codex-attached.mjs 的首次发现循环补这些响应形态,保持同一目标、零原生输入和离开清理断言;健康200的坏JSON不应被无差别当作可恢复错误循环重试。最小修复属于原 request/发现 effect,而非另建重连机制。

本轮验证:两侧源码构建、TypeScript和打包Chat源码校验通过;生产HTTP/provider测试 25 passed。独立打包交互覆盖网络、六秒超时、JSON503、403停止/显式恢复、离开取消、无绑定不替换、历史/草稿保留及实际后续追加,原生输入计数始终0。正常base/head历史文本逐字一致;基线JSON503停住与本头自动恢复形成对照。中文1200×878和英文冷加载390×844的全视口已检查,窄屏无横向溢出;实时切换语言后的滚动位置没有独立端到端验收。

边界保持清楚:页面是当前源码打包产物,会话GET走真实HTTP/provider与隔离SQLite/JSONL、Unix Desktop协议peer;仅错误响应和无关工作区摘要由合成fixture控制。没有使用活跃Goal或原生App会话,没有发送真实消息、调用付费模型或宣称已安装采用。全量既有浏览器场景仅做语法检查,没有冒称已经执行。

语义与 CI 对齐

这是既有UI局部传输分类,未创建共享枚举、协议或第二权威源。开发期语义探针已在全树检查前运行;其不支持的TSX/动态情况仍需源码判断,空结果不是语义证明。默认首次失败由手动重连变为自动重读,PR正文、集成文档及既有场景明确披露;发送仍须显式操作。预合并 3项直接检查和17项执行检查通过,另有本地旧scope的 quality_stale_receipt hold,不能称所有合并门通过,也不能把它当作本次恢复代码的运行时回归。本轮未获取、轮询或等待远端CI。

我的整体评价

结论为 REQUEST_CHANGES,交付判断 not_yet_proven。long_horizon=not_yet_proven:网络/JSON错误之后的继续读取有效,但HTML503仍让后续结果停在初次发现之前。user_experience=not_yet_proven:多数临时失败已免去额外操作,服务错误的另一种合法响应却仍要求手动重连。

整个局部改动的边界和体量合理,共享分类与locale引用是相关的有界整理;没有理由引入更多状态或框架。当前只需在现有请求owner修正状态保存并补响应形态反例。权限拒绝、原会话绑定和原生输入限制必须继续保留。修复后重新构建并重跑上述恢复/停止对照,再按新的精确head评审;质量收据、合并授权、已安装采用与完整原生会话能力继续独立判断。

English verdict: REQUEST_CHANGES - b9f17a7; non-JSON HTTP 503 is parsed before its status is captured, so initial discovery never retries after service recovery. JSON503/network/timeout recovery, 403/unmount isolation, packaged base/head reads and 25 production HTTP tests pass; preserve status in the existing request boundary and add the missing browser case. No merge or live-App adoption is implied.

} catch (failure) {
if (active) {
setError(readFailureMessage(failure, readLocale.current));
if (interruptedRead(failure) || (failure instanceof ConversationRequestError && (failure.httpStatus ?? 0) >= 500)) timer = setTimeout(discover, 1000);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve the server-error status before decoding its body

For an initial discovery response such as HTTP 503 with an HTML or empty error body, request() throws SyntaxError at response.json() before creating ConversationRequestError. Neither predicate here then schedules a retry. In the source-built page I restored the real read API and still observed only one discovery/no history after 2200ms; explicit reconnect immediately loaded the same worker. The new scenario always supplies JSON for 503, so it misses this form of the original stall. Preserve HTTP status on non-JSON error responses in the existing request boundary, then test HTML/empty 503 recovery and a non-JSON 403 stop control; do not retry every malformed successful response.

…n-live-acceptance-20261016

No conflicts: main's independent changes to the dashboard and control plane do not overlap this branch's attached-conversation read recovery.

Signed-off-by: huangruiteng <huangrt01@163.com>
request awaited response.json() before reading the status, so a service
failure with an HTML or empty body surfaced as a SyntaxError and the discovery
effect never scheduled its retry: the work page stayed stuck after the API
recovered. Read the body as text, parse it defensively and carry the status, so
the existing effect retries 5xx, still stops 4xx, and does not spin on an
unreadable success response. Extend the attached browser case with HTML/empty
503, non-JSON 403 and unreadable 200 forms.

Signed-off-by: huangruiteng <huangrt01@163.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Reviewer: model_agent; model=DeepSeek V4 Flash; provider=DeepSeek; self_reported; reasoning_effort=high — the host could not publicly identify the execution provider, so this line is self-reported provenance, not a credential and not a grant.

Reviewed exact head: 1cc3304 (PR #6592, base main).

动机

某个 Agent 的原会话读取在本地 Codex App 短暂不可用时,页面以前只会给出一个技术错误并停在那里;这次修复要把它变成一个能自愈的状态。

  • 受影响的人是在本地 Codex App 读取服务短暂中断时打开某个 Agent 工作会话的 LoopX 操作员。
  • 改动前,发现接口返回非 JSON 的 503 时页面抛出原始 SyntaxError 并一直卡在错误状态,只能由操作员手动重连;改动后,页面保留已读记录、说明连接恢复后会自动更新,并自动重试被中断的读取。
  • 操作员会看到原会话自行恢复,阅读位置和所选 Agent 保持不动,之后仍能像以前一样回复。
  • 本次改动不新增任何权限,不代表操作员提交或重放输入,也不改变宿主的审批、执行器或模型设置。

这不是一次普通的文案调整:在真正恢复之前,页面既没有记录也没有下一步动作,操作员只能自己发现"服务已经好了"并手动重连。

改动思路

读取失败有两条来源:网络中断或 6 秒超时会让 fetch 直接拒绝;服务器也可能用非 JSON 的响应体回答错误码,例如 HTML 或空体的 503。改动前 request() 先 await response.json(),非 JSON 体在解析阶段就抛 SyntaxError,HTTP 状态随之丢失,调用方于是无法判断这次失败是否值得重试。

读取边界是唯一仍然持有 HTTP 状态的地方,因此在这一层保留状态,可以把"是否可重试"的判断集中在一个带类型的点上,而不是从解析后的响应体里猜测。

触发 head 行为 是否自动重试
网络中断 / 6 秒超时 保留已读记录,说明会自动恢复 是
JSON 503 显示服务端错误,保留已读记录 是
HTML 或空体 503 显示不可用提示,保留已读记录 是
403 或其它 4xx 显示服务端错误 否
200 但响应体不可解析 显示 Conversation unavailable 否

关键判断是"该不该自愈"而不是"错误长什么样":只有网络、超时和 5xx 会进入重试;权限类错误和"成功但读不懂"的响应必须留在页面上,不能变成静默循环。页面离开时 clearTimeout,重试随页面生命周期结束。

具体改动

改动 5 个文件,共 +109/-12,另加两张 before/after 截图;审查的固定 head 是 1cc3304。

  • apps/presentation/dashboard/src/features/personal-workspace/attached-agent-conversations.tsx(+43/-11):request() 改为先 await response.text(),再防御式 JSON.parse;解析失败把 value 记为 null,消息回退为 Conversation unavailable。ConversationRequestError 增加 httpStatus;发现流程按 interruptedRead(failure) 或 httpStatus >= 500 决定是否 setTimeout(discover, 1000),清理时 clearTimeout。
  • examples/personal-workspace-browser/codex-attached.mjs(+46/-1):把发现故障扩成 7 个用例(network、json-503、html-503、empty-503、json-403、text-403、html-200)。可重试的三种必须自行恢复;retryable:false 的三种先断言 1300ms 内读取次数不增长,再通过"重新连接"按钮恢复。每个用例都断言 workAgent=worker 且没有原生输入框。
  • docs/integrations/session-runtime-control-plane-adapter.md(+20):把读恢复语义写进公开契约,位置见 docs/integrations/session-runtime-control-plane-adapter.md。
  • 两张截图:examples/personal-workspace-browser/assets/attached-history-outage-before.png 与 attached-history-outage-after.png,作为公开合成 fixture 的 before/after 证据。

对应的验收点来自既有公开契约 docs/integrations/session-runtime-control-plane-adapter.md(审查所依据的不可变版本 96945d3,即本次文档改动之前的文本):

  • SR-1 中断或超时的历史读取保留已显示的记录,并说明连接恢复后会自动更新。
  • SR-2 恢复过程只读:不提交输入、不重新绑定或选择 Agent、不改变宿主设置。
  • SR-3 只对可恢复失败自动重试;权限类错误与不可解析的成功响应保持可见且不自动重试。
  • SR-4 读取/重试范围有界,离开 Agent 页面即取消,不会无界扫描整个 rollout。

实机验收:在 head 上重建 chat bundle 后,attached-host-follow-up 场景(含上面 7 个发现故障)通过,耗时约 38 秒;单独回退 .tsx 到修复前版本并重建后,同一条用例在 html-503 处失败(SyntaxError: Unexpected token '<' ... is not valid JSON,且此后不再恢复),说明这些负例确实能咬住本次缺陷。

对主干的风险

  • 恢复路径是只读的:它不提交输入、不重放 operation、不重新绑定或选择另一个 Agent,也不改变宿主的审批、执行器与模型设置。回复仍然只在观察到空闲且绑定唯一时可用。
  • 自动重试是 1 秒一次,寿命由页面在场决定(清理函数 clearTimeout)。它与既有读取轮询同频,不引入新的调度、额度或持久化副作用。
  • 旧 head b9f17a7 的 CI 红灯(test-shard 3/4/5)与本 PR 无关:本 PR 相对 origin/main 只改了 5 个非 Python 文件(git diff --stat origin/main HEAD)。其中 benchmark/tests/test_checkpoint_archive_transport.py::test_failed_archive_always_closes_daemon_reader 的失败签名是 ModuleNotFoundError: No module named 'docker',属于运行环境缺少 docker extra,同一命令在 origin/main 与 head 上都是这个签名。tests/test_lark_private_conversations.py::test_recovery_waits_for_inflight_capture_without_readmitting 是既有竞态:同一断言在 origin/main 与 head 上都会间歇失败(本地 8 次运行中分别 3 次与 5 次出现失败),失败参数在两次之间还会互换。两者都不在本 PR 的因果路径上,属于另行处置的红灯,不是本次评审的放行条件。
  • 遗留风险:4xx 的可见文案直接使用服务端 error 字段,语义仍由服务端决定;对"成功但不可解析"的响应只给出固定提示,不区分具体原因。这两点不影响本次修复的结论,可以在后续按需细化。
  • 可读性风险:修复把"读文本再解析"写在 request() 里,配合 httpStatus 字段才成立;如果以后有人把这段退回 response.json(),故障矩阵会立刻变红,这正是用例要守住的不变量。

我的整体评价

修复方向正确且克制。本 PR 把重试判断留在现有 dashboard 功能边界内,刻意不引入共享重试框架,也不新增服务端契约。

它同时满足了三件事:把非 JSON 503 这类失败还原为可判断的状态;把"可重试/不可重试"的边界固定在网络、超时与 5xx;并用可执行的负例把这条边界钉住。上面四个验收点 SR-1 到 SR-4 都在 head 上得到验证:presentation 单测 25 passed(整个 tests/presentation 82 passed / 7 skipped)、tsc 干净、chat bundle 构建通过、attached-host-follow-up 场景通过、loopx canary premerge 17 项选中 0 失败、公开边界扫描通过。

没有发现阻塞性问题;剩下的只是一条与本次改动无关的既有红灯,以及上面列出的文案与可读性遗留项。

English verdict: APPROVE - at 1cc3304 the interrupted-read fix is verified by a real regression-sensitivity check, and the unrelated red Python tests are separately attributed rather than treated as this PR's defect.

@huangruiteng
huangruiteng merged commit f6cf958 into main Oct 16, 2026
27 of 40 checks passed
@huangruiteng
huangruiteng deleted the codex/attached-session-live-acceptance-20261016 branch October 16, 2026 07:32
@loopx-agent

Copy link
Copy Markdown
Collaborator Author

Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max

Post-merge audit — new P2 read-recovery finding

完整审计 6592@1cc3304b80db553fd72d4e87c21ddacaec0f74ca。评审开始时 PR 尚开放,期间由维护者账号合并为 f6cf9583794eb8c4cca41fa832a1c2f150059583;本轮读回的主干请求文件与本头逐字相同。已有同头批准和上轮发现均保留。本评论补充响应体读取中途失败这一实际新反例。

动机

在 Goal 中打开指定 Agent 原工作记录、等待后续结果的本机所有者。

普通 HTML/空体 503 现在能自行恢复;但 503 响应头已到、正文读取超时后仍停在 AbortError,接口恢复也必须手动重连;403 正文断流又会错误地自动重试。

同一 worker 的健康记录、完整错误体恢复、草稿保留和停止观察均已验证;响应体读取失败时的正确恢复/停止边界仍未交付。

不新增执行器,不自动提交或重放输入,不更改原 App 权限;完整原生输入、token/审批及已安装采用仍属原工作会话验收。

当前有用结果是打开既定负责人后,原记录及后续工作能继续读到。临时服务恢复不需要新增目标、身份或授权意图;503 正文超时仍要求额外重连,因而留下与原缺陷同类的中断。权限拒绝则应保留明确的停止边界,不能因为错误体断流而改变。

改动思路

既有发现 effect 拥有串行读取和定时器,provider 独立授权;共享局部错误分类合理,修复应继续留在保有 Response 状态的同一 request 边界。

当前 PR 应完整交付原会话读取中断的恢复、反馈及既有浏览器回归;尚缺正文读取异常的状态保存,无需新连接管理器或共享协议。

现有发现 effect 用 active 标志和单个定时器控制读取,历史观察保留原 cursor/generation。httpStatus 来自同一次 Response,本应是一个派生诊断事实,不是新的权限或会话状态。当前实现把 JSON.parse 移入保护分支,确实修复完整 HTML/空体;但仍先 await text() 才创建带状态的错误。正文读取本身拒绝时,事实还未传到分类器,两个相反方向都会出错。

具体改动

完整 5 文件、+109/-12:TSX 为 +43/-11,既有浏览器场景 +46/-1,集成文档新增20行,两张 PNG 是公开合成示例。两张图片已检查且与前轮实审 blob 相同;当前1200宽故障页及390宽实际工作页也已检查,窄屏 scrollWidth=390。没有新 provider、设置、持久化状态或执行器。

关键代码讲解

  • request 先 Fetch,再于第19行 await response.text()。只有成功读完后,才在22—25行构建带 httpStatus 的 ConversationRequestError;因此“能解析非 JSON”并不保证“读取正文失败时仍保留状态”。409 的原未提交 operation 判断保持在原请求 owner,恢复读取不获得 native input 权限。现有 data/chat.ts requestJson 已捕获正文读取异常并保存 response.status,可复用这个小模式,保留此处自己的409/回执含义。
  • interruptedRead / readFailureMessage 将 TypeError 或 DOMException TimeoutError 视为暂时中断。真实浏览器中,正文超时给出 AbortError,尽管 signal.reason 是 TimeoutError;已知403的正文断流却给出 TypeError。这是具体分类误差,不是建议按报错文案做字符串判断。
  • discover 在116行消费上述类型或 httpStatus≥500 决定一秒后重试。由于 text() 的异常绕过状态封装,这里对503不重试、对403反而重试。cleanup 的 active/clearTimeout 仍有效;已有历史的失败路径保留 messages/draft 并清 snapshot,实际回读后再恢复发送资格。

规范依据为改动前的 docs/integrations/session-runtime-control-plane-adapter.md,固定 revision bf4a124,章节 “Experimental owner-local Codex App conversations”。该节无正式验收编号,下列为段落定位标签:original-conversation 已验证同一 worker 与无绑定不替换;idle-only-reply 已验证读中断保留记录/草稿且发送停用、鲜读后恢复;leaving-stops-observation 已验证离开后 discovery1 不再增长;native-parity-boundary 保留既有实验阶段及原生输入/stream/审批限制。新恢复文案是本 PR 的交付声明,未当作改动前的独立规范给自身背书。

对主干的风险

[P2] 在现有 request 边界让 HTTP 状态贯穿正文读取失败,再由现有 discover 判断重试;保留已知 4xx 的停止边界,并补响应头之后的正文超时/断流反例。 可复用 chat.ts requestJson 已有捕获正文读取异常并保留 response.status 的模式。

实际打包页面通过真实 Fetch 接收503响应头,错误体先到部分内容,然后超过已有6000ms请求期限。页面显示 AbortError: The user aborted a request.。接口恢复并观察2800ms后,仍为 discovery=1/history=0;明确点击重连才变为2/1并显示同一 worker 的 Original result。独立浏览器读取也确认:已取得 status=503,text() 抛 AbortError,而 signal.reason.name=TimeoutError。

反向控制是在403响应头和部分正文之后断开连接。完整403原本保持1次读取并等待手动操作;断流版本却显示“暂时无法读取…自动更新”,1450ms内 discovery 从1变2,恢复接口后无需点击便到3/1。这没有绕过 native provider 的逐次授权,但违反已知权限拒绝应停住的读取策略。

当前七个场景通过 route.fulfill 一次性提供完整错误体,或在响应头之前模拟网络失败;它们可全部通过而漏过正文读取拒绝。最小回归应在既有 HTTP fixture 增加“先发头、再让正文超时/断流”,保留完整403、坏200、同一目标、离开清理和零原生输入的断言。不要用无差别重试所有 AbortError 来替代已知 HTTP 状态。

独立通过项也明确保留:固定 base/head 健康记录文本逐字一致;基线完整 HTML503 在接口健康2200ms后仍停1/0,新头 HTML/空体503可自动恢复。完整403和坏200均停住,显式重连有效;历史网络中断保留原记录与草稿、禁用发送,鲜读后恢复;离开 cancels discovery,other-worker 无绑定时不替换成 worker。两个实际 fixture 的 native input 冷读计数均为0。

当前生产 HTTP/provider 25 passed;两侧 TypeScript/打包构建和源码校验、JS语法、advisory→全树 semantic 均通过。预合并 3项直接与17项可执行选择检查通过,另保留 DSH maintainability advisory 和本地 quality_receipt_missing hold。没有冒领其它评审者的全量浏览器、CI 或82/7结果,也未查询、轮询或等待远端 CI。页面使用当前源码打包产物、真实只读 HTTP/provider、隔离 SQLite/JSONL 与 Unix Desktop peer;故障响应及工作区摘要由公开合成 fixture 控制,没有活动 Goal、真实会话、模型调用或已安装 App 验收。

我的整体评价

合并后审计结论 REQUEST_CHANGES,交付判断 not_yet_proven。long_horizon=not_yet_proven:普通503后可继续读取,但正文超时仍让后续有用结果停在初次发现之前。user_experience=not_yet_proven:在目标与读取权限已知的临时失败中,仍多一次无依据的手动恢复;已知403的断流也被错误地当成暂时中断。

已有共享分类、locale ref 和局部边界的有界整理合理,体量并非问题。补齐同一 request 的状态保存及对应反例即可继续复评;没有必要增加状态框架、迁移、权限或其它宿主门。此处是新实证的修复需求,保留已有批准、已通过路径、实际合并事实及独立安装/平台限制。

English verdict: REQUEST_CHANGES - post-merge audit of 1cc3304. Response.text rejection still discards known HTTP status: a503 body timeout stalls until manual reconnect, while a truncated403 body retries. Complete-body recovery/stop controls, actual baseline/head reads, history/scope safety,25 HTTP tests and3/17 executable canary checks pass; preserve status through body failures and add streamed-body regressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants