Files
deepseek-harness/missions/tasks/20260719-1902-apiproxy-api-design/core-coverage.md
imccyu 0681ac47de chore(gui): mission work logs
chore(gui): mission work logs — cordis design finalization, tool-card wire archive, incident records

chore: missions

chore: missions

chore(gui): mission ledger — batch-2 answers, parallel dispatch state, jsdom coverage re-scope

chore(gui): ledger — night-mode standing orders (self-commit small, no push, 5-min refresh)

chore(gui): ledger — jsdom batches 2-4 landed (233 green), coverage probe next

chore(gui): ledger — web-ui coverage probe 65%, four-tier fill plan approved

chore(gui): ledger — cordis-impl B1 state after third API drop, decisions on file

chore(gui): ledger — 01:32 patrol snapshot (jsdom tier-1 landed, coverage-fixer probed)

chore(gui): ledger — 01:37 patrol (peer src trio landed, coverage-fixer still silent)

chore(gui): ledger — 01:42 patrol (jsdom tier-2 landed, peer committed x2, coverage-fixer 2nd probe)

chore(gui): ledger — coverage diagnosis complete (6-file gap list), web-ui at 91.4%

chore(gui): ledger — 01:46 patrol (B1 done, jsdom tier-3 landed, coverage fix batch running)

chore(gui): ledger — 01:51 patrol (jsdom tails x2 landed, B2 underway)

chore(gui): ledger — 01:56 patrol (gateway.ts 337 lines, checkpoint T-7min)

chore(gui): ledger — hold/pending split ruling, coverage-fixer externalize-or-restart ultimatum

chore(gui): ledger — 02:01 patrol (B2 done, jsdom final arms, coverage ultimatum pending)

chore(gui): ledger — 02:03 checkpoint executed (fixer2 respawn, four lanes released, three owners cold-started)

chore(gui): ledger — all six lanes acked, type isolation first live proof (client closure clean)

chore(gui): ledger — 02:08 patrol (all seven lanes active, wire carrier assembled)

docs(gui): respond-design task checkpoint — apiproxy wire-layer recon done

chore(gui): ledger — P0-2 contributor AGENTS.md landed (dd28a5019)

docs(gui): respond-design checkpoint 2 — host-side recon (stub respond, frame types, approval seam, ACP answerer precedent)

docs(gui): OOP debt inventory — seven territories, 2 real debts (createApiProxy, createFixtureApi), rest ruled keep-as-is

docs(gui): disambiguation note on the archived i18n design task

chore(gui): ledger — 02:12 patrol (exclude removed, mixed-knife incident under reconciliation)

chore(gui): ledger — 02:15 wave (jsdom mission closed, OOP audit done, B3 isolation proof, mixed-knife resolved)

chore(gui): ledger — 02:17 patrol (attribution reversal filed, arch-session probed)

chore(gui): ledger — 02:22 patrol (B4 done, B5+B6 merged batch, arch-session deadline set)

docs(gui): respond-design checkpoint 3 — client-side recon (pending map, PendingCard onRespond stub, AbstractApiClient.respond ready, bootHost missing approval mounts)

docs(gui): peer carrier territory review — 2 fixes (SSE cancel leak, route-reservation guard), 1 ruling ask (RPC-log visibility), compliance ledger

chore(gui): ledger — 02:27 (arch-shell respawn, territory review verdicts routed, ask-deny finding flagged)

docs(gui): P1-5 respond design page complete — pending registry, wire answerer, client state machine, first-wins arbitration

chore(gui): ledger — 02:31 patrol (respond design complete, B5 wire smoke green, shell knife 1 underway)

docs(gui): respond design — add §0 status warning (web host ask defaults to deny), mount-behavior delta, no-timeout ruling with Config discipline

chore(gui): ledger — 02:42 patrol (respond line closed pending review, B7 last piece underway)

docs(gui): respond design contract review — direction pass, 2 doc fixes (settle-order contradiction, answering-state race), A/B/C compliance ledger

docs(gui): respond design — contract review fixes (R1 verify-before-delete arbitration, R2 answering+resolved-frame transition, ask dual-source wording, rejected-is-ok-value note)

chore(gui): ledger — 02:46 patrol (respond line final, two user decisions distilled, arch-shell deadline)

docs(gui): respond review addendum — contract-gap ruling: approve plan A (ApprovalRequest.id), wire unchanged, drop plan B backscan

chore(gui): ledger — cordis B7 summit: real-browser 10/10 green, user acceptance criterion proven

docs(gui): respond design — contract gap #4 approved as plan A (ApprovalRequest.id), backscan fallback retired, blade 0 prepended

docs(gui): respond design — final polish (owner-approval vs user-go-ahead wording, implementation handoff notes)

chore(gui): ledger — 02:51 (shell-exec third respawn with operational script, respond line 4-knife final)

chore(gui): ledger — 02:53 wave (respond five-knife true final, B7 closed 12/12, client.ts green)

chore(gui): ledger — 02:56 patrol (cordis closeout bounced pending R1/R2/N1, shell-exec first sign of life)

chore(gui): ledger — 03:01 patrol (R1/R2/N1 remediation in flight across four files)

chore(gui): ledger — cordis line officially closed and archived, verified on disk (24 knives, 12/12, reviews closed)

chore(gui): ledger — 03:06 patrol (shell-exec final window, lowered first-knife bar)

chore(gui): ledger — 03:11 patrol (shell line iced-broken: three registries on disk)

chore(gui): ledger — 03:15 patrol (quiet window, both active lanes within threshold)

chore(gui): ledger — 03:20 patrol (shell five files up, api-proxy plan reported)

chore(gui): ledger — 03:25 patrol (shell migration in flight with history-preserving moves, webserver green)

chore(gui): ledger — 03:30 patrol (shell knife-1 in verification, api-proxy patching)

chore(gui): ledger — shell knife 1 accepted (694cecc53), knife 2 released

chore(gui): ledger — 03:40 patrol (knife 2 pre-move stage, cold-list spec appears)

chore(gui): ledger — 03:45 patrol (rpclog moves staged, api-proxy two specs in flight)

chore(gui): ledger — 03:54 patrol (knife-2 code done, coverage full-run final check)

docs(gui): coverage-fixer task ledger — fixer2 takeover, per-file fix log, isolated reportsDirectory pitfall

chore(gui): ledger — coverage lane closed and accepted (a19f069a5), the PR #443 CI fix knife

chore(gui): ledger — 04:04 patrol (knife-2 calibration, sole active lane)

chore(gui): ledger — shell knife 2 accepted (f8fb77b95), knife 3 released as final night task

chore(gui): ledger — 04:19 patrol (knife-3 past half: callback chain through, ToolCallDetail up)

chore(gui): ledger — night closeout summary: seven lanes closed, wake-up decision sheet

chore: missions

chore: missions

chore: missions

chore(gui): mission-local browser/probe verify scripts under missions/scripts/

The six acceptance/probe scripts move here as mission-side working
material (headers and relative imports adjusted for the new location):
carrier-errors, rpclog-panel, session, session-real,
webserver-backpressure, webserver-hardening.

chore(gui): verify-relocate mission log

chore(gui): verify-relocate mission log — R1 guard addendum

chore(gui): gates-continue mission log — CI-equivalent sequence all green

chore(gui): VS Code 扩展体系双边设计调研报告

chore(gui): 调研追加 4.5 节——git 扩展数据面与 scope 绑定

docs(gui): web plugin system RFC — walkthrough + design notes

docs(gui): RFC — restore existing SSE/POST as the v1 transport; envelope rides on it (D16)

docs(gui): RFC — envelope demoted to chan-dispatch, scope out of envelope, rpc-log cut, peer deferred, scope tree is native cordis (D17-D21)

docs(gui): RFC — hooks re-derived from component needs: useWatch/useAction only, useService removed; sessionHub cut, projections user-space, router rename, loader-only root (D22-D25)

docs(gui): RFC — drop stale fork vocabulary (vendored cordis has Fiber only; scope = mintScope pattern), hook idempotence contract (D26-D27)

docs(gui): RFC — session precision seam: plugins read scope key (host paradigm), React gets it from tree position via SlotOutlet (D28)

docs(gui): RFC — domain hooks owned by plugins over framework primitives; useConversation paradigm carried over (D29)

docs(gui): RFC — ctx services are the inter-plugin API (cordis proper); declarations are wire-only; get(id) returns scoped ctx (D30)

docs(gui): RFC — full ctx.conversation walkthrough: root-singleton scope-sensitive service, caller-ctx scope key, get(key) as scoped ctx (D31)

docs(gui): RFC — no client-side agents collection: session state machine already expresses the duality; agent resolution stays host authority (D32)

docs(gui): RFC — v1 stays session-precision, no agent-level isolation; incarnation/agent-axis designs archived in ledger (D33)

docs(gui): RFC walkthrough — full rewrite to final state (D16-D33 consolidated), end-to-end chain restored

docs(gui): RFC — apiproxy demoted to generic channel routing; domain RPCs dissolve into owner plugins (D34)

docs(gui): RFC — TS-interface-first wire contract (zod internal), conversation owns the dialogue frame with pluggable views (D35)

docs(gui): RFC — page skeleton (sidebar+conversation), projects as plugin not service, nested slots via owner registries (D36)

docs(gui): RFC — SlotMap declaration-merging slot model: single register API, inject-as-ownership, FC-typed registration, typed outlets (D37)

docs(gui): RFC — slot props whitelist: identity, display params, materialized snapshot slices, stable UI callbacks (D38)

docs(gui): RFC — end-to-end data flow: three transforms, equality protocol table, immer placement; i18n/theme kept standard (D39-D40)

docs(gui): RFC — full external-injection model: props carry values + stable injected hooks; shared/client/react example rewritten (D41-D43)

docs(gui): RFC final trio — modules.md (agent implementation spec), architecture.md (human walkthrough), plugins.md (business plugin inventory)

docs(gui): RFC — props three-source merge (scope-standard useSession auto-injected); keyed key vs list id disambiguated (D44)

docs(gui): RFC — inject comment says what it is (the React-facing props bundle); SessionHandle rename; snapshot-production story unified on buildSnapshot

docs(gui): RFC architecture — full React component tree walkthrough: props three sources, slot vs plain children, hook taxonomy per node

docs(gui): RFC — module map finalized (ui-slots/web-react/connection/runtime/ui-*/web); slots onChange replaced by cordis events; toolcall dimension; detail sidebar default-collapsed with toolName-keyed routing

docs(gui): RFC plugins — openDetail relay chain: toolcard calls chat-view injected action, chat-view relays to conversation sidebar

chore(gui): progress ledger — full archive rewrite: RFC outcome digest, open gaps, dispatch plan, cold-start entry

docs(gui): RFC grill pass 1 — SlotScope axis (root/session) on declares, Gate dependency inversion, inject handle by scope, W5 acceptance list, gantt relay chain fixed

docs(gui): figma analysis — sidebar/projects/sessions 区域交互视觉理解报告

docs(gui): figma 解析报告 — details 面板/多视图 tabs/未来功能区盘点 + slot 需求清单

docs(gui): figma 对话主区解析报告 — 消息流/tool calls 变体/审批接管输入框/Header tabs/视觉 token

docs(gui): plugins.md rewritten from figma analysis — three-column layout, full slot reservation table, selection channel, composer-takeover approvals, phased scope

docs(gui): layout dynamics ruled (drag+collapse both rails, details yields first, composer swap-panel, same-component transition); toolviews promoted to named scope-aware registry

docs(gui): P-I scope locked (details minimal, dual theme, chat-view, custom toolview sample); teammate dispatch plan — 6 owners by package, dependency-driven waves, contract arbitration

docs(gui): P-I api-contracts (full inter-package API spec) + dispatch plan (T0 skeleton knife, 7-dev roster, task briefs, milestones)

docs(gui): api-contracts v2 — scope tree in P-I, bundle loader + per-plugin CSS isolation in P-I, agent-scoped toolviews live, zustand engine, renames (SessionProvider/ObservableSnapshot/SessionBinding), router owns all shell view-state

docs(gui): services roster + progressive loading (no blocking loadAll), SlotsService as real cordis Service, renderSlot/renderSuspenseSlot duo, ui-traj teammate

docs(gui): loading-chain gaps ruled — dev=rebundle no HMR, ui-primitives package, externals on globals (no import map), host injects __DSH_BOOT__ into HTML (zero round-trip)

docs(gui): api-contracts v3 + dispatch v2 final — 12 packages, services merged in, progressive loader, global externals, __DSH_BOOT__ injection, 8-dev roster with convo split and ui-traj

docs(gui): v3 amendments — router renamed ctx.layout, ui-trajectory has no service (pure consumer sample), wait-for-settled loading (no Suspense in P-I, ledger 6b)

chore(gui): progress — pre-compact final state: v3 revision chain, 8-dev roster, T0 procedure, doc authority order

docs(gui): authority banners — modules/architecture get v3 term-mapping headers, walkthrough marked as archived process doc

docs(gui): cssdesign token set is THE theme source (--dsw-* variables, data-ds-dark-theme switch); recorded in contracts + progress

docs(gui): architecture.md full v3 rewrite — loading chain, 12-package map, service roster, slot/inject/toolviews, data flow, component tree, perf model, all current

docs(gui): contracts — UI plugins are dual-entry host plugins (node half serves client asset via ctx.webPlugins; __DSH_BOOT__ derives from it; client-closure gate back in scope)

docs(gui): contracts — closure-factory bundles with DI require (no globals), package.json dshWeb declarative discovery (no serve ritual), create-then-send empty state with project picker, ancestry() for breadcrumb, unload stubbed until HMR, props.renderSlot confirmed

docs(gui): dshClient declaration (inject/platform/immediately, exports./client), closure-DI require loading — synced across contracts/dispatch/modules/architecture/walkthrough

chore(gui): progress — record final loading-chain rulings (dshClient declaration, closure-DI require, startSession) before compact

docs(gui): architecture.md — developer-facing whole-web architecture on master baseline 6b16a67cb: what exists, what is new, no process narrative

docs(gui): architecture.md — self-contained whole-web architecture: absorbs still-valid substance from the branch RFCs (host layering, four-quadrant RPC, object layer, testing tiers) under the new plugin system as the override

chore(gui): progress — final pre-compact snapshot: contracts digest, apiproxy purity ruling, T0 procedure with first-action list

docs(gui): api-contracts v3 §3.1 — apiproxy purity principle with three-way existing-code verdicts

docs(gui): api-contracts v3 — immediately reinterpreted as static-infra group (8-package dshClient scope, boot manifest reconciliation)

docs(gui): api-contracts v3 — immediately corrected to early-load dynamic group (prod shell must not rebundle); loader shell-held; bundles register their export surface into module table

docs(gui): architecture — align with immediately=early-load dynamic group ruling; loader shell-held; module-table registration of loaded bundles

docs(gui): T0 checklist — 12-package skeleton table, 4-cut sequence, mv/attic/rewire rules (pre-drafted, awaiting go)

docs(gui): t0-checklist — pin figma-flows findings (missing font-family base vars, three alias vars behind upstream)

docs(gui): dispatch v2.1 — drop cordis-web salvage wording, two-wave staffing, loader/immediately boundary updates

docs(gui): progress + t0-checklist ledger — T0 landed, staffing status, execution accounting

docs(gui): api-contracts v3 — arbitration round 1: renderBody deps, RootBindingProvider, flush default sync, prune current, loader subpath, config-source P-I bar

docs(gui): v3 §3.2 connection 导出清单附录(rt-core 对账)+ rt-core 实现计划档案

docs(gui): progress — T1 milestone, arbitration round 1 ledger, fw-react timeout escalation

docs(gui): progress rolling update — per-line battlefield state at 00:2x, mailbox-vs-contract lesson, small-batch discipline reinforced

docs(gui): fw-react notes — v3 §2 complete, seven knives, T1/T2 follow-ups

docs(fw-slots): archive — four packages landed, open tails logged

docs(gui): progress — framework layer complete (web-react five, fw-slots four packages), T2 gated on rt-core runtime knife only

docs: api-contracts

docs: style-spec

docs

docs(gui): tsconfig convergence ruling — no host.json, root resumes host-aggregate duty, typecheck = root + client aggregates

docs(gui): missions 根三份 07-18 世代档案加「已被取代」头注——指向 web-plugin-rfc 现行权威并注明新旧对应

missions

docs(gui): progress rewritten for post-closeout state — wave ledger, architecture finale, teammate roster with handover notes, pending-user-command queue
2026-07-22 21:30:30 +08:00

23 KiB
Raw Blame History

core 能力面 × 契约 v1.3 覆盖度盘点

2026-07-19 起盘(分批落盘中)。方法:逐包读 core 源码 types/service 面(file:line 为证),对照 design.md v1.3 标注四态:已覆盖 / 有意不做(引拍板)/ 接缝已留(说在哪)/ 漏判(需新裁决,汇总见文末清单)。 背景:rpc-compare 发现契约漏 subagent 谱系,根因是当初只按 UI 需求反查 core、未做系统盘点;本文件补这道工序。

1. session 域(packages/core/session)

1.1 SessionEventMap 全类型表(types.ts:180-252)

事件 core 事实 契约状态
turn/start / turn/end types.ts:187/193,turn 边界 + TurnTrigger/TurnEndReason(merge-extensible,types.ts:79-124) 已覆盖——mux session/event + history 纯透传(design §3.3 透传纪律)
step/start / step/end types.ts:195/197 已覆盖(同上透传)
user/message types.ts:199 已覆盖(透传;client fold 消费)
prompt/blocked types.ts:204,veto 的 durable 记录 已覆盖(透传)。UI 是否渲染是 client fold 决策,不是契约缺口
context/message types.ts:212-217,含 meta(模型不可见 durable JSON) 已覆盖(透传)
assistant/chunk types.ts:219,token 级 已覆盖——「token 流即事件流」(design §3.3)
assistant/message types.ts:226,含 usage?: TokenUsage 已覆盖(透传)。usage/token 统计随之免费到达 client,无需独立统计接口(§7 LLM 层回引此行)
tool/call / tool/result types.ts:232/242;result 带 meta?: unknown(tool 私有 presentation 载荷) 已覆盖(透传);render intent(presentCall/presentResult)有意不做——design §3.3 tool presentation 拍板「先透传,additive 附件帧留座」
steering/message types.ts:244 已覆盖(透传)
todo/write types.ts:246,全量快照、last-write-wins、log-only 已覆盖(透传);client fold 照 last-write-wins 折即可,无需独立 todo 接口(§6 回引此行)
request/header types.ts:251,EpochHeader 快照(config/system/tools/messagePrefix) 已覆盖(透传)。UI 可从中读 provider/model 现值变化
merge-extensible 扩展键 types.ts:180 map 声明 已覆盖——design §3.3:client fold 对未知 type documented-default,schema 留「合法信封+未知类型」分支

1.2 Session/SessionStore 服务面(index.ts)

能力 core 事实 契约状态
session/created / session/disposed 事件 index.ts:47/57 已覆盖——HostFrame host/session-added/removed(design §3.3)
session/event 追加流 index.ts:69 已覆盖——mux 的源
session/flush 检查点 index.ts:79 有意不做(不对客暴露)——durability 是 host 内部事务,client 只见已落地事件
store.get/list index.ts:818/826(live only) 已覆盖——sessions.list(live+冷合并的持久化清单,v1 mtime 三字段拍板)
SessionHeader.parentSession + seedLength types.ts:47/52;fork 时写入 index.ts:854-855 漏判 →【L1】——谱系在 core 持久化面存在,契约 SessionSummary/HostFrame 均未携带
SessionHeader.cwd / createdAt types.ts:43/45 漏判 →【L2】——create 收 cwd 入参但 list/describe 均不回吐;createdAt 被 v1「mtime 即 updatedAt」拍板部分覆盖但非同一语义
SessionStore.fork() index.ts:843-857;SessionForkSource index.ts:546;错误码 SessionForkErrorCode index.ts:556-562(turn/end 边界约束 :890-895) 漏判 →【L3】——core 有完整 fork 原语(opencode 也有 POST /session/:id/fork 对照),契约无 session.fork 方法
repair(interruptedTurnClosers) index.ts:25 导出;repair.ts;persistence 加载时闭合 crash 孤儿 turn(TurnEndReason interrupted,types.ts:120) 已覆盖(间接)——修复产物就是 turn/end interrupted 事件,随 history 透传到达;修复动作本身是 host 内部行为,无需接口
surface(foldSurface/SurfaceOp/replace) index.ts:26-27 导出;types.ts:262-309(SurfaceOp append/replace,compaction 用) 已覆盖——design §5「优先复用 core foldSurface」;replace 语义随事件透传,client fold 天然处理 compaction
deriveMessages/requestHeader 折叠 index.ts:469/432 有意不做(server 不代折)——「历史=事件重放,client 单一 fold」拍板(README 拍板表)

2. agent 域(packages/core/agent)

能力 core 事实 契约状态
agent/created / agent/disposed types.ts:147/156(注册/注销时 emit) 已覆盖——HostFrame added/removed 的 agent 侧对应(HostFrame 语义=「session 出现/消失」,v1 agent 与 session 同生命周期)
agent/status(idle⇄running→disposed) types.ts:165;AgentStatus types.ts:47 已覆盖——HostFrame host/session-status running 布尔。三态压两态是拍板(冷 session 拍板:attach 不暴露,disposed 即 removed)
agent/queued(入箱通知) types.ts:175 有意不做——prompt() 返回 accepted 即达意;入箱细节属 host 内部(CQRS:渲染靠 session 事件)
send/steer/cancel 原语 types.ts:103/110/127 已覆盖——prompt(mode: queue/steer)、cancel 1:1 映射(design §3.1)
inject(注入上下文不跑模型) types.ts:119 漏判 →【L4】——core 第三条输入原语,契约只映射了 send/steer;UI 场景(如「贴文件给 agent 但不触发回复」)v1 是否需要待裁决
whenIdle() types.ts:130 有意不做——client 由 host/session-status 事件驱动,不需要 promise 面
AgentRegistry.create/resume(CreateAgentOptions:sessionId/meta{cwd,parentSession,seedLength}/seed/agentOptions/setup) index.ts:44-90、:352/:371 已覆盖(部分)——sessions.create 走此路(cwd 已在契约入参);meta.parentSession/seed 是 fork/spawn 用的,与【L3】同源,client 侧 v1 不透出
AgentRegistry.get/list/roots/isOwnedBy index.ts:530/550/560+/543 list/get 已覆盖(sessions.list + running);roots()/isOwnedBy(运行时归属树)漏判 →【L1】共同体——UI 要画 subagent 树需要谱系,见 L1 处置提案
initiator scope(withInitiator/initiator()) index.ts:288/:258;RFC 2026-07-15-agent-initiator-scope 有意不做——进程内 AsyncLocalStorage 机制,本质不可序列化,不属 wire 契约;UI 需要的「谁创建了谁」由持久谱系(L1)承担
扩展 seam 事件(pre-step/prompt-submit/request/session-prefix/step-result/post-step/request-error/turn-continuation/turn-stop、agent/error) types.ts:204-311 有意不做(对客)——插件扩展 seam,是 host 进程内 waterfall/serial 钩子;durable 后果已进 session log 透传(如 prompt/blocked、turn/end error)。agent/error 的无 turn 位置失败 →【L5】边缘:live-only 诊断无 session 事件时 client 不可见,待裁决是否要 stream/error 级 host 通知

3. subagent 包组(packages/subagent/*:spawn/fork/inprocess/subprocess/acp + tool-subagent)

能力 core 事实 契约状态
子 agent 创建(spawn=白纸 / fork=继承 turn 前缀) subagent/src/types.ts:52-101(StartRequest:parent 必填、读 parent.session.header 拿 cwd+stamp parentSession,:56-61);SubagentRun.id=子 session id、parentSession 记录 parent(:148-154) 对 UI 的可观测面 = 普通 session:子 agent 就是 registry 里一个 live agent + store 里一个 session,mux/hostEvents 天然看得见。缺的只是谱系标注 →【L1】(host/session-added 无 parentSessionId,UI 无法区分「用户开的」和「agent spawn 的」)
运行时 run 面(result promise/dispose/sendMessage?/resume?) types.ts:148-185 有意不做——run 生命周期属 parent agent 的工具调用(tool/call task → tool/result),已随事件透传;client 不直接操纵子 run
stopReason / structured output types.ts:109-141 已覆盖(间接)——结果进 parent 的 tool/result 透传
ACP/subprocess 远程子 agent subagent-acp、subagent-subprocess 同上——远程 run 无本地 session;parent 侧 tool 事件已覆盖其可观测面;v1 不做远程子会话浏览(不做清单精神,未明文 → 盘点顺手补进 §6 不做清单措辞即可,不算漏判)

4. ctx.tasks 后台任务(packages/tasks/tasks)

能力 core 事实 契约状态
tasks.list/get/wait(TaskSnapshot:kind/label/status/detail/output/startedAt/finishedAt) index.ts:153/167/226/326 漏判 →【L6】——运行时全局后台任务注册表(bash 后台、subagent run 等挂在这),UI「后台任务列表」是常见诉求;但 v1 UI 范围未含此面板,处置建议偏「留接缝」
onTaskDone 完成通知 index.ts:283 同【L6】——若做任务面板需 HostFrame 或独立流;不做面板则无需
任务归属(owner: Agent、session-scoped 授权) index.ts:44-48(TrackedTask.owner)、list(caller) 过滤 同【L6】附注:core 已有按 agent 过滤语义,接口若做可直接映射

5. 审批与问答(packages/ui/user-approval、user-interaction)

能力 core 事实 契约状态
approval/request waterfall(待决问题推给 answerer 链) user-approval/src/index.ts:23-32;ApprovalOutcome :91(allowed-once/rejected/cancelled/unavailable,fail-closed) 有意不做(v1)——design §6 不做清单明文「审批/问答域」。结构性事实需记录:这是 client→server 反向要答案的面,纯 mux 单向流装不下,将来要么复用 unary(poll/answer 方法对)要么加双向帧——接缝形态建议在拍板时一并定
approval/asked / approval/decided session 审计事件 index.ts:35-60(log-only,merge into SessionEventMap) 已覆盖——merge-extensible 事件随 mux 透传(design §3.3 未知类型分支),UI 已可"看到"审批发生过;缺的只是"参与决定"(上行)
approval/policy(ask/never,session 内覆写) index.ts:108 + SessionEventMap merge 已覆盖(事件透传);改 policy 的命令面归审批域一并 v2
ctx.userInteraction.ask(AskUserQuestionRequest/Answer,单 provider 注册制) user-interaction/src/index.ts:43-71(registerProvider 单占 :96-107) 有意不做(v1)——同上不做清单。附注:单 provider 语义 ⇒ Web client 接管问答时要经 host 侧代理 provider 中转(provider 在 host 进程注册、答案从 wire 上取),这决定将来接缝在 impl 不在契约新增语义

6. workflow / todo / skill / compact 可观测面速查

包 core 事实 契约状态
todo(packages/todo) 唯一持久面 = todo/write session 事件(types.ts:246) 已覆盖——透传 + client fold last-write-wins(§1.1 已列)
compact(packages/compact) 产物 = surface replace 事件 + context/message(SurfaceOp types.ts:292-294) 已覆盖——透传;client fold 处理 replace 即正确渲染压缩后视图(design §5 foldSurface 复用)
skill(packages/skill) 装载产物 = context/message(skill 内容注入)+ tool/call 事件 已覆盖(透传);skill 目录浏览/管理面 v1 无 UI 诉求,有意不做(catalog 工具是模型面不是 client 面)
workflow(packages/workflow) worker-thread 引擎;对 session 的可观测面 = 其 tool/call、tool/result + 子 agent session(同 §3) 已覆盖(间接);workflow 进度独立流 v1 不做,与 L6 任务面板同性质
guard(packages/guard) loop-hygiene 插件,干预结果落 session 事件(steering/turn-stop) 已覆盖(透传,无独立面)

7. LLM 层(packages/llm)

能力 core 事实 契约状态
usage/token 统计 assistant/message.usage?: TokenUsage(session types.ts:226,与消息同travel);request/header 里 config 已覆盖——透传即达(§1.1 已列);聚合统计(session 累计 token)是 client fold 的算术,不需要 server 接口
adapter 注册面(provider 现值) LlmService 注册表;AgentOptions.provider/model(agent types.ts:21-26) 已覆盖——host.describe 的 provider/model 现值(20:02 拍板);adapter 列表枚举(UI 下拉「可用 provider 有哪些」)漏判 →【L7】:describe 只给现值不给候选集,「模型切换」在不做清单但「枚举可选项」是它的读前提,处置建议留接缝
模型切换(运行中改 provider/model) agent/request waterfall 可换 config 有意不做——design §6 不做清单明文

漏判清单(已裁决,2026-07-19 21:13:L1/L2/L5 合入 design.md v1.5;L3/L4/L6/L7 类型预留 design §8;审批/问答提案整体采纳并入 §3.4)

# 缺口 core 事实(file:line) 建议处置 一句话理由
L1 subagent/fork 谱系不可见(已知条目):host/session-added 与 SessionSummary 均无 parent 信息,UI 无法画子 agent 树、无法区分用户开的还是 agent spawn 的 SessionHeader.parentSession/seedLength(session types.ts:47/52);fork 时写入(session index.ts:854);spawn 时 stamp(subagent types.ts:56-61 REQUIRED parent) 进 v1 契约。补法:① host/session-added 帧加可选 parentSessionId?: SessionId(从 session.header.parentSession 读,无谱系时缺省);② SessionSummary 同补 parentSessionId?(冷 session 列表也要能画树;jsonl 后端从持久化 header 读)。运行时归属(registry owner/roots,agent index.ts:543/560)不透出——durable 谱系已够 UI 用,运行时树是进程内概念 字段 core 已持久化、读取零成本;缺它 UI 树状视图无法做,且 additive 可选字段不破坏现契约
L2 session 元数据有入无出:create 收 cwd 但 list/history 均不回吐;createdAt 同 SessionHeader.cwd/createdAt(session types.ts:43/45) 进 v1 契约(顺手):SessionSummary 加 cwd?: string。createdAt 不加——v1「mtime=updatedAt」拍板已覆盖排序诉求,再加是第二时间语义 多 session 不同 cwd 时列表页无法标注工作目录;一字段事,与 L1 同一次 SessionSummary 改动
L3 fork 无契约方法:core 有完整原语+类型化错误码,opencode 有同款端点 SessionStore.fork(session index.ts:843-857);SessionForkErrorCode :556-562;turn/end 边界 :890-895 留接缝:v1 不加 session.fork(UI 无 fork 按钮诉求);接缝=将来 RpcMethodMap 加 'session.fork' 一行 + SessionForkErrorCode 并入 RpcErrorCode 按域扩展,零结构变化 形态 B 下加方法是纯 additive;现在加则要陪审 UI 交互(fork 点选择、边界约束提示)不值 v1
L4 agent.inject 第三输入原语无映射:prompt 只有 queue/steer Agent.inject(agent types.ts:119);idle 时一次性 turn 语义 types.ts:84-89 留接缝:prompt 的 mode union 将来加 'inject' 即可(merge 进闭合 union + impl 分发)。v1 Web UI 无「注入不触发回复」交互 三原语中 inject 是插件/自动化面(文件变更通知等 host 内部已在用);人机 UI 场景未出现,union 扩展零迁移
L5 无 turn 位置的 live 失败 client 不可见:agent/error 在 session log 无对应事件时(如 flush 失败、驱动崩溃)UI 只能看到 session 卡死 agent/error emit(agent types.ts:311,"even when the error has no in-turn position") 进 v1 契约(轻量):HostFrame 加 { type: 'host/agent-error'; sessionId; message: string }——只做诊断展示不做恢复语义 不加则「agent 停了但 UI 永远转圈」无解释渠道;一帧类型,Frame union additive
L6 后台任务注册表无接口:bash 后台/长任务在 ctx.tasks,UI 任务面板无数据源 tasks.list/get/wait/onTaskDone(tasks index.ts:153/167/226/283);TaskSnapshot :326 留接缝:任务面板不在 v1 UI 范围;接缝=将来新 tasks 域(一域一文件 + RpcMethodMap 数行 + 完成通知并入 hostEvents 或独立流)。设计已天然支持新域(design §1「新域=新文件对+根接口一字段」) v1 UI 无此面板;域级 additive 是本契约的标准扩展路径,无需预留字段
L7 provider 候选集不可枚举:describe 给现值,UI「切换模型」下拉无数据源 LlmService adapter 注册表;AgentOptions.provider/model(agent types.ts:21-26) 留接缝:模型切换整域在不做清单,枚举是其读前提,一起进将来的 provider 域;不单独提前 只读枚举脱离切换动作无用户价值;避免半个域
— 审批/问答:形态已拍板(2026-07-19 21:0x,mux 下行帧 + HTTP unary 上行),协议提案见下节,随本清单一并裁决 见下节逐条 file:line 本轮定契约形状,实现可排后 —

审批/问答域协议提案(已采纳并入 design.md §3.4;21:19 修订:QuestionAskId 取消、问题标识复用 RpcId——下文为原提案存档,以 design.md 为准)

已定:请求下行 = mux 控制帧(稳定 id + session 锚点);回答上行 = 普通 unary(respond 带 id 回传)。不做双向帧/双工通道——SSE 本就是 server→client,回送有 HTTP。

A. 方法与帧

// RpcMethodMap 增两行(key 按域单数 convention)
'approval.respond': ApprovalApi['respond']
'question.respond': QuestionApi['respond']

export interface ApprovalApi {
  /** 回答一个待决审批。outcome 只收 client 可给的子集(cancelled/unavailable 是 host 侧结局)。 */
  respond(input: { sessionId: SessionId; id: ApprovalRequestId; outcome: 'allowed-once' | 'rejected' }):
    Promise<RpcResponse<{ accepted: true }>>
}
export interface QuestionApi {
  /** 整批回答一次 ask(core 事实:一次 ask 多题一个 answer,user-interaction index.ts:63-67)。 */
  respond(input: { sessionId: SessionId; id: QuestionAskId; answer: AskUserQuestionAnswer }):
    Promise<RpcResponse<{ accepted: true }>>
}

// MuxFrame 增四帧(Frame 族,session 锚点)
| { type: 'approval/requested'; sessionId: SessionId; id: ApprovalRequestId; toolName: string; callId?: CallId; reason?: string }
| { type: 'approval/resolved';  sessionId: SessionId; id: ApprovalRequestId; outcome: ApprovalOutcome }
| { type: 'question/requested'; sessionId: SessionId; id: QuestionAskId; questions: AskUserQuestionItem[] }
| { type: 'question/resolved';  sessionId: SessionId; id: QuestionAskId; outcome: 'answered' | 'cancelled' }
  • requested 载荷 = core 类型透传:审批帧字段即 ApprovalRequest 去 agent(换 sessionId 锚)去 signal(user-approval index.ts:190-212);问题帧直接透传 AskUserQuestionItem[](user-interaction index.ts:29-41,model 自带题内 id)。
  • resolved 帧是收敛面:多 client 同看一 session 时,别人答掉/超时取消/policy 决掉,观察方靠 resolved 撤卡片;自己答成功也等 resolved 帧统一收敛(respond 的 accepted 只表示受理)。

B. id 纪律(按现行纪律推导)

  • 审批:复用 core ApprovalRequestId(已 brand,user-approval index.ts:76)——SessionId 同款先例:type-only import、id 全部源自 server(requested 帧),client 只回传。
  • 问答:host 造 QuestionAskId(api 层新 brand:Branded<'question-ask-id'>)——core 事实:user-interaction 无 request 级 id(AskUserQuestionRequest 只有题内 model 自给的 string id,index.ts:29-33),host 代理 provider 受理 ask() 时 mint UUID。与 cursor 占位不同(那是未实现故不 brand),此 id 实装即进签名,按「opaque 跨界 id 必 brand」仓规上 brand。

C. 竞争语义

  • 先到先赢,host 内存 pending 表是唯一裁判:一个 id 只被 settle 一次。竞争方:client respond vs signal abort(tool 取消/step 中止 → cancelled,user-approval index.ts:206-211)vs 另一 client respond。policy 'never' 在 answerer 链之前解决(index.ts:100-108),requested 帧根本不发——天然对齐。core 无审批超时(signal 是唯一撤回通道),不发明。
  • 迟到/重复回答:RpcErrorDetailsMap 增两码——'approval-not-pending': { id: ApprovalRequestId }、'question-not-pending': { id: QuestionAskId }(分域两码不合一:details 类型不同,且按域扩展是既定纪律)。

D. 刷新恢复(推荐:subscribed 基线重放)

推荐:client 重开 mux 后,host 在每个 session 的 session/subscribed 帧之后立即重放该 session 仍 pending 的 */requested 帧(来源=host 内存 pending 表)。理由:单一事实源(全走 mux),与 lastSeq 补缝流程同构,client 无需第二条 bootstrap 路径做 join。不推荐 host.describe 带 pending 列表:跨 session 聚合 + 与流竞态,两处真相。 不能从 history 推 pending(审批虽有 approval/asked/decided 审计事件,但 crash 后 asked-without-decided 是永久悬案——pending 真相只在 host 内存,重放帧无此歧义)。

E. core 事实对齐(盘点批3 + 本批补核)

core 事实 file:line 提案对齐
approval 走 policy → answerer waterfall 链,fail-closed unavailable user-approval index.ts:23-32、:100-108 host 侧注册一个「wire answerer」进链:收 approval/request → 发 requested 帧 → 等 respond/abort → 返回 outcome。链上仍可有其他 answerer(组合语义不变)
approval/asked/decided 已是 session 审计事件(log-only) index.ts:35-60 保持透传不动;帧是 live 控制面、事件是 durable 审计,职责分离不算重复造 DTO
ApprovalOutcome 四值闭合 union index.ts:91 resolved 帧透传全集;respond 入参窄化为 client 可给二值
userInteraction 单 provider 注册制 user-interaction index.ts:96-107 host 代理 provider 是唯一注册者(盘点 §5 已注),多 web client 竞争在 wire 层由 pending 表裁决,不违单 provider
ask() 不落 session 日志 user-interaction 全文无 SessionEventMap merge(本批 grep 核实) 问答无审计事件可依赖 → requested/resolved 帧是问答唯一可观测面,D 的内存重放是唯一恢复路径(自洽)
一次 ask 多题、整批回答 index.ts:43-67 respond 收整个 AskUserQuestionAnswer,不拆单题方法

汇总(裁决后):L1/L2/L5 已合入 v1.5(SessionSummary.parentSessionId?/cwd?、session-added 帧 parentSessionId?、host/agent-error 帧);L3/L4/L6/L7 类型已预留(design §8 完整签名,不进 map——fail loud 优于 not-implemented 兜底);审批/问答提案(下节)整体采纳并入 design §3.4。本文件转为盘点存档,后续契约变更以 design.md 为准。