Skip to content

feat(chat): share live activity, interruption and turn steering - #4999

Merged
huangruiteng merged 115 commits into
mainfrom
codex/reusable-conversation-controls-20260924
Sep 25, 2026
Merged

huangruiteng merged 115 commits into
mainfrom
codex/reusable-conversation-controls-20260924

Conversation

@huangruiteng

@huangruiteng huangruiteng commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Problem and result

A running steward or Goal conversation previously collapsed real executor activity into a generic waiting label. The shared timeline now shows the latest reported phase, an expandable six-entry history, and controls to interrupt or adjust the exact active Turn. Successful interruption preserves partial output and the conversation; a completion race preserves the completed answer.

Native Codex steering binds the expected Turn and durable ingress id. A matching executor receipt is required before the UI says the adjustment was received. A confirmed pre-delivery rejection now reports delivery_state: not_delivered, so the unchanged draft can safely use a new ingress id after recovery. An uncertain delivery retains its original id, preventing a blind duplicate. This resolves the prior exact-head review's “409 → 409 after recovery” blocker.

Scope and remaining acceptance

The existing Chat Session/Turn, ingress receipt and frontend timeline own the behavior; no second queue or event store was added. Steward, Goal and directly selected Agent conversations share the controls. The HTTP endpoint rejects stale Turn identities, unsupported adapters and closing Turns. The frontend keeps a rejected or uncertain draft visible and does not silently submit a new Turn.

The wider reusable conversation program remains open: adaptive reviewable answers, context-affine delegation and live Lark presentation are separate acceptance gaps. This PR does not claim a live-provider browser or Lark end-to-end pass. The earlier disposable real Codex gpt-6-sol steering/interruption check was on the original behavior head; the current head is qualified with the real Chat HTTP entrypoint and synthetic executor/browser fixtures.

Validation

Tested final head: 19180e1f433f98dc9cc7641fd02f944fbce5d0c2, integrated with main at 5a4617e6b96288eb2cbfbfe9ee97417c6c38f695. No live Goal was mutated.

Check Result Evidence and limit
Chat regressions Passed 67 focused Python tests, including refusal → recovery → unchanged-draft retry exactly once, duplicate ingress and closing-Turn cases.
Registry census Passed Generated 233-site manifest verified; nine architecture tests.
Chat entrypoints Passed Runtime and HTTP server smokes with a synthetic Codex adapter.
Dashboard Passed TypeScript, production dashboard and packaged Chat bundle build. Existing large-chunk advisory remains.
Browser Passed All 14 development scenarios on the integrated base; final-head conversation scenario in development and packaged browsers covers reported phases, steering receipt mismatch, uncertain replay, confirmed rejection/retry, interruption, partial output and completion race.
Python lint / public boundary Passed Ruff on changed Python files; boundary scan had zero errors. Two unrelated global-state warnings were reported.
Change quality / premerge Passed Exact-diff receipt cqr_54253738c33dc8f899f2; 16/16 selected premerge checks, zero failures or manual holds.
Remote CI Pending New head has been pushed for current CI and maintainer review.

Frontend evidence

Synthetic fixture screenshots: before · running desktop · mobile · interrupted · steering draft.

Attention review: one current phase and two scoped controls appear under the active reply. Activity history is collapsed by default, adjustment opens on demand, and failures stay beside the action. The opening viewport and primary navigation are unchanged.

Change type and boundaries

  • Bug fix
  • New feature
  • Documentation and focused regression coverage

Changed surfaces: Chat runtime/API, shared dashboard conversation UI, public guide and browser fixture. The result is a reviewable A22–A23 increment, not closure of the cross-channel conversation Todo. All commits have DCO sign-off; public/private boundary scan is clean. Future-facing pass: retained the existing ingress/Turn owner and added only the typed refusal distinction needed for safe retry. This runtime/product PR is left for maintainer review and merge.

Green-hats and others added 14 commits September 10, 2026 11:08
Signed-off-by: Green hats <183933879+Green-hats@users.noreply.github.com>
Signed-off-by: Green hats <183933879+Green-hats@users.noreply.github.com>
Signed-off-by: luw2007 <luw2007@gmail.com>
Signed-off-by: luw2007 <luw2007@gmail.com>
`LIVE_SURFACE_PREFIXES` guarded `.github/workflows/` alone, so the files GitHub
renders at a visitor or a reporter escaped the canonical-repository check
entirely: the private-vulnerability entry in `SECURITY.md`, the four contact
links in `ISSUE_TEMPLATE/config.yml` that GitHub prints on its own new-issue
page, `SUPPORT.md`'s routing table, the PR template's guidance pointer, and the
ruleset link `GOVERNANCE.md` itself calls live. Seventeen addresses named the
pre-transfer owner, and they kept resolving only because the transfer left a
redirect behind -- which is a courtesy, not an ownership fact.

Point each of those at `loopx-project/loopx`. Governance's record of the initial
public commit and of the issue that settled a roster entry keeps the address
those events happened under, reviewed per path and use like the existing pull
citations; the ruleset link does not, because the sentence around it calls it
live.

Only the widened prefix decides this, not a new rule: every classified use is an
offender until a path-and-use exception reviews it, so the two citations had to
be judged rather than inherited. The smoke now pins both directions -- the
new-issue contact file is a live surface, and a dated commit citation under it
stays tolerated.

`LIVE_ADDRESS_USES` is removed: nothing referenced it, while its comment
described a live-versus-citation split the implementation never applied, which
is the easiest thing in this file to "fix" by editing a constant that does
nothing.

Signed-off-by: Yue Dai <54579099+yuedai-pbc@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…t owns the Turn

A Turn whose receipt is bound to an autonomous replan obligation cannot hand its
settlement to another Todo. The refusal reported that case as a plain selection
conflict, calling the retained pending selection "the projection's current
selection", and published neither the retained selection nor the obligation that
owns the Turn, so a caller could not branch on either without parsing prose.

The conflict now states that the requested Todo cannot replace the retained
pending selection, names the autonomous replan obligation that owns the Turn,
and publishes `retained_selection`, `retained_selection_todo_id` and
`receipt_replan_obligation_id` in the failure payload. The recommended action
points at settling that obligation instead of rebinding the retained selection.
This is the last branch of the explicit-selection preflight that still carried
no typed retention facts; the eligibility predicates are unchanged.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
huangruiteng and others added 3 commits September 24, 2026 19:36
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…facts-0924

fix(quota): name the retained selection and the replan obligation that owns the Turn
@huangruiteng huangruiteng changed the title feat(chat): share live activity and turn interruption across conversations feat(chat): share live activity, interruption and turn steering Sep 24, 2026
huangruiteng and others added 11 commits September 24, 2026 19:57
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Four atomic writers opened parent directories after os.replace, so Windows reported failure after persisting the file. Guard directory fsync by os.name and add a cross-platform Windows emulation test to the native Windows job.

Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
…ce-20260924

feat(team): trace a correction through exact versioned work
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
The previous head still left active callers on a bare interpreter, so the
original failure mode stayed reachable even with a compatible worktree
environment present, and the static guard only recognized literal `python3`.

Route the remaining callers through resolveTestPython(): the repository
identity oracle in monitor_successor, the legacy fingerprint oracle in
quota_monitor_poll_commit, and the tab-upgrade chat server smoke that arrived
on main while this branch was open. Widen the guard to reject bare `python`
and `python3` launches, fallbacks and assigned defaults, and add negative
assertions so resolved paths such as `/usr/bin/python3` and validation argv
data stay allowed.

Validated with the full control-plane suite (3009 passed, 0 failed, 28
skipped), the guard and typecheck, and a restricted-PATH rerun where a bare
`python` mutation fails monitor_successor while the selector passes both
affected files.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: Jim-jimu <49069997+bmh201708@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…eter-discovery

test: discover compatible Python for Node-driven smokes
…reshold

Signed-off-by: Jim-jimu <49069997+bmh201708@users.noreply.github.com>
huangruiteng and others added 9 commits September 25, 2026 01:42
…-gate

fix(chat): restore workspace choices for gated goals
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
release: prepare LoopX v1.2.0 package version
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
chore: repair v1.2.0 release hygiene gates
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…tion-command

fix(turn): preserve complete signed host actions
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng 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.

Request changes conclusion (author-owned PR; GitHub blocks formal self-review). Reviewed exact head 47941d5b61588b0b31d85b7e1dba168811bfe945.

动机

共享管家/Goal 对话过去只显示笼统等待,用户看不到执行阶段,也无法在同一轮中断或追加指令。此 PR 让既有 Chat Session/Turn 的活动、控制动作和回执出现在同一时间线;对持续工作可见性有正向价值。但“临时失败后重试”目前会被持久失败回执卡住,因此可恢复的对话控制尚未交付完整。

改动思路

前端从 agent.phase 取最近活动,把中断/调整绑定到消息的 sourceTurnId;新的 /turns/{turn_id}/steer HTTP 路由复用现有 Chat runtime,以 client_ingress_id 为幂等键并校验预期轮次。正常路径中,匹配的执行器回执才显示“已接收”,原输出流继续,不创建第二轮。这避免另建队列。但失败路径必须区分“送达状态未知,重放同一编号”与“明确拒绝,允许安全地重新尝试”;当前前端把两者都保留成同一个编号。

具体改动

关键代码讲解

  • channel-timeline.tsx:31-43 的 MessageActivity.steer 以草稿文字生成编号;任何异常都保留 request.current,只有改文字或成功才换号。这里决定失败后的用户恢复能力。
  • chat_runtime.py:904-1019 的 steer_active_turn 持久化 ingress,核对会话、轮次与执行器;明确拒绝时将编号记为 failed,以后同号只能返回 live_steering_delivery_unresolved。
  • chat_server.py:818-853 暴露轮次级接口及身份回执;dashboard-page.tsx 把匹配回执后的调整投影回会话,并仅中断当前轮次。活动、移动端表单、文档、合成截图和浏览器 smoke 覆盖了主路径。

对主干的风险

[P1] 明确拒绝后,原文重试被永久卡住。 我用 PR 自带的合成 Codex/HTTP fixture 启动活动轮次,暂时移走 adapter 后提交同一编号得到 409 live_steering_session_not_attached;恢复 adapter 后按 UI 的相同草稿/编号重试仍为 409 live_steering_delivery_unresolved,只有换新编号才得到 200 delivered。前端失败后既不更新编号,也没有“重新发起”动作,用户只能无提示地改写文字或刷新并丢失草稿。请用类型化结果区分确定失败与未知送达:未知状态沿用原号,已持久确认拒绝后可显式换号重试;增加“拒绝→执行器恢复→原文重试成功且仅送达一次”的 HTTP+浏览器回归。

独立验证:tests/test_chat_turn_steering.py 10/10、Dashboard npm run build、npm run smoke:personal-workspace 的 12 个浏览器场景均通过;上述恢复反例仍失败。当前 head 与最新 main 的浏览器 smoke 和 registry I/O manifest 还存在合并冲突,修复后需在新 exact head 重跑。此轮未以远端 CI 状态代替本地证据。

我的整体评价

正常路径是有价值且边界基本正确的增量:复用现有会话与 ingress,不需要第二个 authority;长期持续工作可见性改善。但用户体验在可恢复失败上退化成无提示死路,当前 head 也不能干净集成主干,故请求修改。最小修复是类型化处理明确失败与未知送达、补上述反例、解决冲突,再复核完整 PR 的正反路径。相关状态可做有界收敛,不应为此增设新队列。

English verdict: REQUEST_CHANGES - At exact head 47941d5, a confirmed failed steering ingress cannot be retried with unchanged draft after adapter recovery (409 → 409; fresh id → 200). Ten Python tests, dashboard build and 12 browser scenarios passed; add typed retry recovery coverage and resolve current-main conflicts.

huangruiteng and others added 14 commits September 25, 2026 03:00
…rator-provider

fix(release): stabilize qualification on configured hosts
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…ard-memory-preview

fix(release): make v1.2.0 qualification host-independent
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…lay-proof

fix(quota): replay unleased monitor poll receipts
Keep the v1.2.0 default-model release gate evidence-linked: close onboarding command IDs, scope the required-vision wait, and reject missing durable vision receipts with correctable feedback.

Validated on the reviewed head: 65 focused tests, 18 premerge canaries, and 21 live scenarios plus 6 contrasts with no failures or skips.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Use the production-shaped compact quota packet for the release-only external-wait fallback decision probe. Keep the independent work, notification, and no-premature-close oracle, with focused negative tests and release documentation.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Repair installed-wheel deep doctor so it selects the invoked console script and identifies the wheel through an owned module in the distribution RECORD. Preserve source-checkout diagnostics and add a focused regression.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…diagnostics

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…eout-budget

fix(release): bound vision closeout and runtime responses under load
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng 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.

Approval conclusion (author-owned PR; GitHub blocks formal self-approval). Reviewed exact head 19180e1f433f98dc9cc7641fd02f944fbce5d0c2 against main at 5a4617e6b96288eb2cbfbfe9ee97417c6c38f695. The previous exact-head P1 retry blocker is resolved. This is a reviewable increment; the wider conversation program remains open.

动机

管家、Goal 和直接 Agent 对话原先把真实执行压成笼统等待,用户无法从回答处看到当前活动、只中断指定回合或在原回合补充指令。按 Presentation RFC §8.8 与 Manager RFC A22–A23,本 PR 交付共享会话控件及可信的送达反馈。用户可以看到最近执行阶段;中断成功后保留部分输出并在原会话续聊。该增量改善长程任务中的纠偏与恢复,但尚未完成 Lark 实时展示、自适应答案和上下文亲和路由。

改动思路

前端仍从现有 Chat Session/Turn 和 agent.phase 事件取状态,不引入第二条执行队列。ChannelTimeline 为运行中的回答呈现当前阶段、折叠的最近六条活动,以及绑定 sourceTurnId 的中断和调整;既有运行、等待、提案的呈现规则继续独立。调整走原生 Codex Turn 的 /steer 路由:服务端先持久化 ingress 身份与预期 Turn,核对绑定和能力,再把指令送给同一执行器。匹配的回执只表示执行器接收,不表示任务完成。明确在送达前拒绝与送达状态未知分开处理:前者恢复后用新编号重试原文,后者保留旧编号避免重复。旧 Lark/LoopX-mode 调用不提供预期 Turn,保留原有请求形态。

具体改动

关键代码讲解

  • loopx/chat_ingress.py::ChatIngressStore.create_ingress_receipt(约第 11 行)把 expected_turn_id 纳入新 HTTP ingress 的持久请求身份;同号换 Turn 或换正文不能伪装成重放,旧无该字段的调用保持兼容。
  • loopx/chat_runtime.py::ChatRuntimeController.steer_active_turn(约第 915 行)验证活动 Turn、attached 能力、适配器和上游 Turn;STEERING_NOT_DELIVERED_CODES(第 62 行)只覆盖调用 provider 之前的确定失败。已送达的同号重放返回原 Turn,未知送达继续拒绝盲目重投。
  • loopx/chat_server.py::ChatRequestHandler._steer_turn(约第 824 行)校验请求、绑定 URL 内的 Turn,并返回 not_delivered 或 unresolved 的类型化错误;成功回执带 Session、Turn 与 ingress 身份。
  • apps/presentation/dashboard/src/features/personal-workspace/channel-timeline.tsx::MessageActivity(约第 16 行)把运行阶段、调整草稿、回执与中断错误放在同一回答下;确认未送达时换新编号,未知状态沿用原编号。dashboard-page.tsx 把控件接到管家与 Goal 对话,完成竞态不覆盖最终答案,成功中断保留部分输出。

另外更新了共享模型、样式、API 客户端和双语使用指南;七张合成截图提供桌面、移动与失败状态的审阅证据。浏览器场景和 Python HTTP/运行时回归覆盖主要正反路径,生成的 registry I/O manifest 只同步新行号,没有改登记规则。变更始终由现有 Session/Turn/ingress 所有者负责,未复制状态机到前端或新增守护进程。

对主干的风险

上次评审的 P1 反例是:适配器临时不可用时同号得到 409,适配器恢复后同文同号仍得到 409,页面没有安全恢复路径。相同的 Chat HTTP/文件存储脚本在基线 5a4617e6b 上读到新路由 404,在上次评审的 47941d5b6 上读到 409→live_steering_delivery_unresolved→新编号 200,在当前 head 上读到类型化 409→同号稳定 409→新编号 200 且只留一条指令;浏览器回归证明草稿保留、未知失败同号重试、确定失败换号重试。关闭中的 Turn、错 Turn、重复编号换正文、回执身份不匹配及中断与完成竞态都有负例。合入主干后 14 个集成浏览器场景,以及最终 head 的 67 个聚焦 Python 测试和开发与打包 Chat 场景、Dashboard 构建、9 个登记架构测试、Ruff、change-quality 回执和 16/16 premerge 检查通过;为验证共享运行时未破坏旧调用,我还在相同 fixture 上对 main 与 head 分别运行 LoopX-mode/Lark 的 111 个测试,均为 111/111。

仍有明确边界:草稿和未确认 ingress 编号只在当前页面内保存,刷新前需要用户自行保留;本 head 未做真实 Lark 端到端验收,也未在最终 head 上重跑付费真实模型(原行为 head 的真实 Sol 运行已通过)。本次审查按 Goal 配置只要求本地证据,远端 CI 仍在运行,不能把 pending 说成通过。上述边界已在 PR 正文和指南披露,并留在现有产品 Todo 中,不扩大本 PR 的权限或合并范围。没有发现阻塞当前限定增量的回归。

我的整体评价

APPROVE(限本 PR 的共享对话控件增量)。长期续接方面,同一 Session/Turn 的中断、重放与明确拒绝后恢复比旧行为更可靠;用户体验方面,活动、草稿、错误和接收回执在同一回答处可读,失败不自动生成另一任务。代码量与现有 Chat/API/Timeline 所有权相称;预期 Turn 是请求者的真实意图,不是第二份进度权威。下一步仍要完成真实前端/Lark 读回、刷新后续接、自适应可审阅答案和上下文亲和返回,不能用此 PR 代替整条用户旅程的验收。运行时与产品表面按仓库规则交给维护者合并,不自行 bypass。

English verdict: APPROVE - Exact head 19180e1f433f98dc9cc7641fd02f944fbce5d0c2 resolves the confirmed-rejection retry blocker and integrates cleanly with current main. Final-head focused HTTP/browser/build/premerge checks pass; shared LoopX-mode/Lark fixtures pass 111/111 on both base and head. Live Lark adoption and final-head paid-provider exercise remain outside this bounded increment; maintainer merge and remote CI remain pending.

@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Merge-readiness follow-up for exact head 19180e1f433f98dc9cc7641fd02f944fbce5d0c2: the published COMMENTED review concludes APPROVE for this head, and there are no unresolved review threads. The current loopx pr-review --check-merge-readiness result is not ready solely because GitHub reports BEHIND (merge_state_requires_update). Per this Goal's local-evidence policy, CI was not consulted; the absence of a status-check rollup is not a pass.

I compared this head with current origin/main (1882feb0c1c0d13b0b3149b8d68404a7954f63d0): the shared base is 5a4617e6b96288eb2cbfbfe9ee97417c6c38f695; the 21 changed PR paths do not overlap the main-side quota/docs/test changes since that base, and git diff --check origin/main...HEAD passes. This reduces apparent conflict risk, but does not replace integration validation.

Next: update the PR branch onto current main, rerun the focused runtime/browser/build/premerge validation on the new head, publish a review conclusion for that exact head, and rerun merge-readiness immediately before merge. This PR changes loopx/** and apps/** behavior, so repository policy leaves the merge to a maintainer; no admin-bypass self-merge.

@huangruiteng
huangruiteng merged commit e5e1a6a into main Sep 25, 2026
30 of 31 checks passed
@huangruiteng
huangruiteng deleted the codex/reusable-conversation-controls-20260924 branch September 25, 2026 04:05
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.

6 participants