From dc92793f10abe154615ae146f996de6fb0db1081 Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:15:20 +0800 Subject: [PATCH] fix(client): align domain split with repository gates --- ...7-19-gui-web-client-architecture.i18n.yaml | 4 +- .../2026-07-19-gui-web-client-architecture.md | 2 +- ...26-07-19-gui-web-client-architecture.zh.md | 2 +- ...ient-tool-presentation-ownership.i18n.yaml | 4 +- ...8-08-client-tool-presentation-ownership.md | 2 +- ...8-client-tool-presentation-ownership.zh.md | 2 +- ...lient-conversation-node-assembly.i18n.yaml | 4 +- ...08-09-client-conversation-node-assembly.md | 12 +- ...09-client-conversation-node-assembly.zh.md | 12 +- ...cancelled-stream-prefix-finalize.i18n.yaml | 4 +- ...-08-10-cancelled-stream-prefix-finalize.md | 2 +- ...-10-cancelled-stream-prefix-finalize.zh.md | 2 +- ...t-session-conversation-ownership.i18n.yaml | 6 + ...0-client-session-conversation-ownership.md | 461 ++++++++++ ...ranscript-log-ordered-projection.i18n.yaml | 4 +- ...0-web-transcript-log-ordered-projection.md | 4 +- ...eb-transcript-log-ordered-projection.zh.md | 4 +- ...e-blank-session-reuse-membership.i18n.yaml | 4 +- ...orkspace-blank-session-reuse-membership.md | 2 +- ...space-blank-session-reuse-membership.zh.md | 2 +- ...-attribution-observed-top-ledger.i18n.yaml | 4 +- ...-scroll-attribution-observed-top-ledger.md | 2 +- ...roll-attribution-observed-top-ledger.zh.md | 2 +- ...6-08-02-web-thinking-tail-scroll.i18n.yaml | 4 +- .../2026-08-02-web-thinking-tail-scroll.md | 2 +- .../2026-08-02-web-thinking-tail-scroll.zh.md | 2 +- ...08-08-web-background-job-display.i18n.yaml | 4 +- .../2026-08-08-web-background-job-display.md | 2 +- ...026-08-08-web-background-job-display.zh.md | 2 +- apps/web/tests/README.i18n.yaml | 4 +- apps/web/tests/README.md | 11 +- apps/web/tests/README.zh.md | 8 +- docs/api-gateway.i18n.yaml | 4 +- docs/api-gateway.md | 2 +- docs/api-gateway.zh.md | 2 +- docs/config-catalog.i18n.yaml | 4 +- docs/config-catalog.md | 5 +- docs/config-catalog.zh.md | 5 +- .../adding-a-conversation-node.i18n.yaml | 4 +- docs/cookbook/adding-a-conversation-node.md | 15 +- .../cookbook/adding-a-conversation-node.zh.md | 15 +- .../cookbook/adding-a-settings-card.i18n.yaml | 4 +- docs/cookbook/adding-a-settings-card.md | 2 +- docs/cookbook/adding-a-settings-card.zh.md | 2 +- docs/module-graph.i18n.yaml | 4 +- docs/module-graph.md | 602 +++++++------ docs/module-graph.zh.md | 602 +++++++------ docs/subsystems/user-questions.i18n.yaml | 4 +- docs/subsystems/user-questions.md | 34 +- docs/subsystems/user-questions.zh.md | 34 +- packages/api/gateway/README.i18n.yaml | 4 +- packages/api/remotes/README.i18n.yaml | 2 +- packages/client/AGENTS.md | 2 +- packages/client/README.i18n.yaml | 4 +- packages/client/README.md | 5 +- packages/client/README.zh.md | 5 +- packages/client/store/README.i18n.yaml | 6 + packages/client/store/README.md | 17 + packages/client/store/README.zh.md | 17 + packages/client/ui-approval/README.i18n.yaml | 6 + packages/client/ui-approval/README.md | 17 + packages/client/ui-approval/README.zh.md | 17 + .../tests/ui-approval.client.spec.tsx | 17 + packages/client/ui-chat/README.i18n.yaml | 6 + packages/client/ui-chat/README.md | 17 + packages/client/ui-chat/README.zh.md | 17 + .../client/ui-conversation/README.i18n.yaml | 4 +- packages/client/ui-conversation/README.md | 21 +- packages/client/ui-conversation/README.zh.md | 19 +- .../ui-conversation/src/client/apply.ts | 1 + packages/client/ui-jobs/README.i18n.yaml | 4 +- packages/client/ui-jobs/README.md | 2 +- packages/client/ui-jobs/README.zh.md | 2 +- packages/client/ui-session/README.i18n.yaml | 6 + packages/client/ui-session/README.md | 17 + packages/client/ui-session/README.zh.md | 17 + .../client/ui-session/src/client/index.ts | 18 +- .../tests/ui-session.client.spec.ts | 44 +- .../client/ui-sidebar/src/client/index.ts | 2 + packages/client/ui-slots/README.i18n.yaml | 4 +- packages/client/ui-slots/README.md | 2 +- packages/client/ui-slots/README.zh.md | 2 +- .../tests/browser-plugin.client.spec.ts | 1 + .../tests/workflow-run.client.spec.tsx | 2 +- .../ui-workspace/tests/tree.client.spec.ts | 13 + .../src/client/slot-catalog.ts | 835 +++++++++++------- .../extensions/tool-cordis/src/api-catalog.ts | 36 +- .../interaction/user-questions/src/index.ts | 1 + .../tests/runtime.client.spec.tsx | 2 + packages/typert/protocol/README.i18n.yaml | 4 +- packages/typert/registry/README.i18n.yaml | 4 +- scripts/gen-cordis-catalog.ts | 2 + scripts/package-graph.spec.ts | 51 ++ scripts/package-graph.ts | 85 +- scripts/run-oxlint.spec.ts | 2 +- scripts/run-oxlint.ts | 2 +- scripts/type-equiv.manifest.json | 10 +- 97 files changed, 2256 insertions(+), 1048 deletions(-) create mode 100644 .agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.i18n.yaml create mode 100644 .agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.md create mode 100644 packages/client/store/README.i18n.yaml create mode 100644 packages/client/store/README.md create mode 100644 packages/client/store/README.zh.md create mode 100644 packages/client/ui-approval/README.i18n.yaml create mode 100644 packages/client/ui-approval/README.md create mode 100644 packages/client/ui-approval/README.zh.md create mode 100644 packages/client/ui-chat/README.i18n.yaml create mode 100644 packages/client/ui-chat/README.md create mode 100644 packages/client/ui-chat/README.zh.md create mode 100644 packages/client/ui-session/README.i18n.yaml create mode 100644 packages/client/ui-session/README.md create mode 100644 packages/client/ui-session/README.zh.md create mode 100644 scripts/package-graph.spec.ts diff --git a/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.i18n.yaml b/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.i18n.yaml index 9ee365f446..b80381f031 100644 --- a/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md -2026-07-19-gui-web-client-architecture.md: 8b4f940299cbba78d403c34b1e5fc9740e44f2c2 -2026-07-19-gui-web-client-architecture.zh.md: 705b1337dd97ac37bd01bdcc5aa22484b7971908 +2026-07-19-gui-web-client-architecture.md: 4448fd5c6871d67b30b71cfe4377682639704235 +2026-07-19-gui-web-client-architecture.zh.md: e8a8121a1a495db7f5392e288f3bcada92c70495 diff --git a/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md b/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md index 8b4f940299..4448fd5c68 100644 --- a/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md +++ b/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md @@ -48,7 +48,7 @@ There is no component registration model besides slots — the former view and t **Scope addressing** mirrors the host's agent-scope idiom: services are root singletons whose methods take no sessionId — they read the caller's scope mark (`scopeOf(ctx)`). Inside a session scope, `ctx.conversation.send('hi', 'queue')` targets that session; cross-session calls re-target by switching ctx (`ctx.sessions.scope(id)!.conversation.send(...)`); calling a scoped method from root ctx throws. Client session scopes are minted like host agent scopes (a no-op plugin fiber + a scope-key extend), built lazily on first viewing and torn down only when the session is removed and unwatched — host-session death alone does not tear a scope (it freezes into a read-only viewport). -## The data object layer (`packages/client/runtime/src/client/sessions/`) +## The data object layer (`packages/api/session-controller/src/client/`) Frames enter, snapshots exit, the Conversation assembler sits between — React-free (zero React imports, grep-assertable): diff --git a/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md b/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md index 705b1337dd..e8a8121a1a 100644 --- a/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md +++ b/.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md @@ -48,7 +48,7 @@ slot 之外不存在第二种组件注册模型——原视图环与工具环都 **scope 寻址**与 host 侧 agent(智能体)scope 惯例同构:服务是 root 单例,方法不收 sessionId——它们读调用方 ctx 上的 scope 标(`scopeOf(ctx)`)。在会话 scope 内,`ctx.conversation.send('hi', 'queue')` 自动打到该会话;跨会话调用换 ctx 定向(`ctx.sessions.scope(id)!.conversation.send(...)`);从 root ctx 直接调 scoped 方法即 throw。client 会话 scope 的铸造方式与 host agent scope 相同(no-op 插件 fiber + scope 键 extend),首次观看时惰性建,只有会话被移除且无人观看才拆——仅 host 会话死亡不拆 scope(冻结为只读视窗)。 -## 数据对象层(`packages/client/runtime/src/client/sessions/`) +## 数据对象层(`packages/api/session-controller/src/client/`) 帧从这里进、快照从这里出、Conversation assembler 坐在中间——React-free(零 React import,grep 可断言): diff --git a/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.i18n.yaml index 7a63fc4c41..c32026ebc7 100644 --- a/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.md -2026-08-08-client-tool-presentation-ownership.md: 3feefc3cfbe538024b8610394b9f170c423556e8 -2026-08-08-client-tool-presentation-ownership.zh.md: 181c57a0da61795292d70b3d37ebd1485832795b +2026-08-08-client-tool-presentation-ownership.md: 1daad1559a6c8ef15fadb8e7c8dfeb2874ae3f9a +2026-08-08-client-tool-presentation-ownership.zh.md: f980db28e1174aa95b29defb8b0a36fc0ba4cf2e diff --git a/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.md b/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.md index 3feefc3cfb..1daad1559a 100644 --- a/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.md +++ b/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.md @@ -16,7 +16,7 @@ Tool is a first-class Client UI presentation concept. `@deepseek-ai/dsh-client-u Conversation data assembly follows the later [Conversation business-node decision](2026-08-09-client-conversation-node-assembly.md). The `ui-conversation` Tool Definition pairs root call/result Session Events, folds Code Dispatch edges into recursive `ToolCallBlock.subCalls`, and emits one stable `tool-call` Chat Node. This data responsibility handles only official Tool identity and topology; it does not interpret presentation for concrete Tool names. -[`ChatView`](../../../../packages/client/ui-conversation/src/client/chat/ChatView.tsx) only places generic [`ChatNodeSeat`](../../../../packages/client/ui-conversation/src/client/chat/ChatNodeSeat.tsx) entries in Chat snapshot `order`. A Seat dispatches `'conversation.chat.node'` by `node.kind`; [`ui-tool`](../../../../packages/client/ui-tool/src/client/apply.ts) registers the `tool-call` entry, and [`ToolCallTree`](../../../../packages/client/ui-tool/src/client/tool/ToolCallTree.tsx) recursively traverses the root block. Every root or child level dispatches through the same keyed/session `'tool.call.toolview'` child slot with `entryKey: toolName`, falling back to `GenericToolCard` when no registration exists. +[`ChatView`](../../../../packages/client/ui-chat/src/client/chat/ChatView.tsx) only places generic [`ChatNodeSeat`](../../../../packages/client/ui-chat/src/client/chat/ChatNodeSeat.tsx) entries in Chat snapshot `order`. A Seat dispatches `'conversation.chat.node'` by `node.kind`; [`ui-tool`](../../../../packages/client/ui-tool/src/client/apply.ts) registers the `tool-call` entry, and [`ToolCallTree`](../../../../packages/client/ui-tool/src/client/tool/ToolCallTree.tsx) recursively traverses the root block. Every root or child level dispatches through the same keyed/session `'tool.call.toolview'` child slot with `entryKey: toolName`, falling back to `GenericToolCard` when no registration exists. A business Tool plugin receives one standard `ToolCallBlock`, identity, workspace cwd, and host actions; it does not read Session, Context, or the Conversation assembler. Skill remains an ordinary Tool and uses the same keyed-slot registration path as other business Tools. diff --git a/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.zh.md b/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.zh.md index 181c57a0da..f980db28e1 100644 --- a/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-08-client-tool-presentation-ownership.zh.md @@ -16,7 +16,7 @@ Client 运行时已经按 `callId` 配对工具调用/结果事件,并能从 C Conversation 数据组装遵循后续的 [Conversation 业务节点决策](2026-08-09-client-conversation-node-assembly.zh.md)。`ui-conversation` 的工具 Definition 从会话事件配对 root call/result,把 Code Dispatch edge fold 成递归 `ToolCallBlock.subCalls`,并生成一个稳定的 `tool-call` Chat Node;这里的数据职责只处理官方工具 identity 和拓扑,不解释具体工具名称的展示。 -[`ChatView`](../../../../packages/client/ui-conversation/src/client/chat/ChatView.tsx) 只按 Chat 快照的 `order` 放置通用 [`ChatNodeSeat`](../../../../packages/client/ui-conversation/src/client/chat/ChatNodeSeat.tsx)。Seat 以 `node.kind` 分发 `'conversation.chat.node'`;[`ui-tool`](../../../../packages/client/ui-tool/src/client/apply.ts) 注册 `tool-call` entry,并由 [`ToolCallTree`](../../../../packages/client/ui-tool/src/client/tool/ToolCallTree.tsx) 递归遍历 root block。每一层 root 或 child 都通过同一个 keyed/session `'tool.call.toolview'` 子 slot 以 `entryKey: toolName` 分发,缺少注册时渲染 `GenericToolCard`。 +[`ChatView`](../../../../packages/client/ui-chat/src/client/chat/ChatView.tsx) 只按 Chat 快照的 `order` 放置通用 [`ChatNodeSeat`](../../../../packages/client/ui-chat/src/client/chat/ChatNodeSeat.tsx)。Seat 以 `node.kind` 分发 `'conversation.chat.node'`;[`ui-tool`](../../../../packages/client/ui-tool/src/client/apply.ts) 注册 `tool-call` entry,并由 [`ToolCallTree`](../../../../packages/client/ui-tool/src/client/tool/ToolCallTree.tsx) 递归遍历 root block。每一层 root 或 child 都通过同一个 keyed/session `'tool.call.toolview'` 子 slot 以 `entryKey: toolName` 分发,缺少注册时渲染 `GenericToolCard`。 业务工具插件接收一个标准 `ToolCallBlock`、identity、workspace cwd 和宿主动作,不读取会话、上下文或 Conversation assembler。skill(技能)仍是普通工具;它和其他业务工具使用同一 keyed slot 注册路径。 diff --git a/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.i18n.yaml index 90160da7db..e7aa813ad5 100644 --- a/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md -2026-08-09-client-conversation-node-assembly.md: 69f92b906e46ae881b7aa6b5e46e998047fca8c2 -2026-08-09-client-conversation-node-assembly.zh.md: d075e009d9a04f20dbc8dda518e90b368b54286f +2026-08-09-client-conversation-node-assembly.md: e6c0e790a361265870a04ee63301b9f11940c648 +2026-08-09-client-conversation-node-assembly.zh.md: 702ddba0019e125d3976727f841db775276b3b77 diff --git a/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md b/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md index 69f92b906e..e6c0e790a3 100644 --- a/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md +++ b/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md @@ -33,7 +33,7 @@ Registry contributions are Cordis effects. Removing a Definition causes a low-fr ### Overall `ConversationNodeDefinition` contract -Each [`ConversationNodeDefinition`](../../../../packages/client/runtime/src/client/contract/conversation.ts) independently owns one business object's conversion from Events to State and final view Nodes. A Definition's `kind` is its unique Registry name and the namespace for its business IDs. +Each [`ConversationNodeDefinition`](../../../../packages/client/ui-conversation/src/client/contract/conversation.ts) independently owns one business object's conversion from Events to State and final view Nodes. A Definition's `kind` is its unique Registry name and the namespace for its business IDs. One Event may be claimed by several ordinary Definitions. For example, an Assistant Event updates both the Assistant Node and Turn Tail, while a Retry Event updates Retry, Assistant, and Turn Tail. The Assembler asks the fallback only when every ordinary Definition returns `null`. @@ -160,7 +160,7 @@ IDs are never reused. Completed Contexts remain in the current window, providing ### Location is a first-class engine fact -[`ConversationLocationIndex`](../../../../packages/client/runtime/src/client/sessions/conversation-location-index.ts) maps Events to Locations from `turn/start`, `step/start`, explicit turn and step payloads, `step/end`, and `turn/end`. +[`ConversationLocationIndex`](../../../../packages/client/ui-conversation/src/client/conversation/location-index.ts) maps Events to Locations from `turn/start`, `step/start`, explicit turn and step payloads, `step/end`, and `turn/end`. Location has four shapes: `session`, `turn`, `step`, and `unresolved`. Turns and Steps each carry `open`, `closed`, or `unknown` status plus any loaded start and end Events. @@ -302,19 +302,19 @@ Unknown fallback demonstrates Registry ownership: it handles only append-surface ## View Builder and React identity -[`ConversationViewRegistry`](../../../../packages/client/runtime/src/client/conversation/view-registry.ts) creates an independent per-Session builder for each target. The Registry stores factories and shares no Session's ordering or caches. +[`ConversationViewRegistry`](../../../../packages/client/ui-conversation/src/client/conversation/view-registry.ts) creates an independent per-Session builder for each target. The Registry stores factories and shares no Session's ordering or caches. The Assembler calls `replace({ nodes, timeline })` on low-frequency complete replacements and `apply({ upserts, timeline })` for ordinary prepend/append flushes. Builders receive only final target Nodes already constructed by Definitions. -[`ChatSnapshotBuilder`](../../../../packages/client/ui-conversation/src/client/conversation-nodes/chat-snapshot-builder.ts) maintains `order`, a keyed `nodes` store, the turn/step `locations` index, `timeline`, and the `legacy` slice used by StatsLine and mirrored into top-level public compatibility fields. +[`ChatSnapshotBuilder`](../../../../packages/client/ui-chat/src/client/conversation-nodes/chat-snapshot-builder.ts) maintains `order`, a keyed `nodes` store, the turn/step `locations` index, `timeline`, and the `legacy` slice used by StatsLine and mirrored into top-level public compatibility fields. Only a new key or a change to `anchorSeq`, visibility, or Location identity makes a Chat update structural. An ordinary content change does not rebuild `order`; the keyed Node store replaces only that key's value. For a structural change, the Builder computes visible order from current store values and reuses unchanged index arrays by reference. Prepend may add earlier history keys, append may add a key at the tail or its business anchor, and ordering never renames existing keys. -[`ChatView`](../../../../packages/client/ui-conversation/src/client/chat/ChatView.tsx) only traverses `order`. Each [`ChatNodeSeat`](../../../../packages/client/ui-conversation/src/client/chat/ChatNodeSeat.tsx) remains in the same parent list under its Context key and dispatches the `'conversation.chat.node'` keyed slot by `node.kind`. +[`ChatView`](../../../../packages/client/ui-chat/src/client/chat/ChatView.tsx) only traverses `order`. Each [`ChatNodeSeat`](../../../../packages/client/ui-chat/src/client/chat/ChatNodeSeat.tsx) remains in the same parent list under its Context key and dispatches the `'conversation.chat.node'` keyed slot by `node.kind`. -[`ChatNodeDataMap`](../../../../packages/client/ui-conversation/src/client/contract/chat-nodes.ts) is a declaration-merged renderer payload registry. Each business module registers its own Definition and keyed renderer; `registerConversationNodes()` and `registerChatNodeRenderers()` only assemble those independent contributions and do not interpret business through a closed union or central switch. Built-ins still live in `ui-conversation`, but this type and registration boundary allows a business to move into an independent package without changing the Chat dispatcher. +[`ChatNodeDataMap`](../../../../packages/client/ui-chat/src/client/contract/chat-nodes.ts) is a declaration-merged renderer payload registry. Each business module registers its own Definition and keyed renderer; `registerConversationNodes()` and `registerChatNodeRenderers()` only assemble those independent contributions and do not interpret business through a closed union or central switch. Built-ins live in `ui-chat`, and this type and registration boundary allows a business to move into an independent package without changing the Chat dispatcher. The Chat entry in `conversation.view` registers `ChatNodeTurnDataInjected` once when it declares the `conversation.chat.node` child slot. `ChatNodeSeat` passes only the stable Node key as `hookContext`; the Slot renderer combines that key with `useSession` from the official standard props to construct `useTurnData(businessKey)`. Every keyed Chat renderer therefore reads strongly typed, read-only data from its own Node's Turn, and the Assistant renderer has no special injection authority. diff --git a/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.zh.md b/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.zh.md index d075e009d9..702ddba001 100644 --- a/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.zh.md @@ -33,7 +33,7 @@ Registry 注册是 Cordis effect,Definition 卸载会触发现有 Session 的 ### `ConversationNodeDefinition` 总体契约 -每个 [`ConversationNodeDefinition`](../../../../packages/client/runtime/src/client/contract/conversation.ts) 独立拥有一种业务对象从 Event 到 State 和最终 view Node 的转换。Definition 的 `kind` 是 Registry 内唯一名称,也是业务 ID 的命名空间。 +每个 [`ConversationNodeDefinition`](../../../../packages/client/ui-conversation/src/client/contract/conversation.ts) 独立拥有一种业务对象从 Event 到 State 和最终 view Node 的转换。Definition 的 `kind` 是 Registry 内唯一名称,也是业务 ID 的命名空间。 同一个 Event 可以被多个普通 Definition 认领。例如一条 Assistant Event 同时更新 Assistant Node 和 Turn Tail;一条 Retry Event 同时更新 Retry、Assistant 和 Turn Tail。Assembler 只有在全部普通 Definition 都返回 `null` 时才询问 fallback。 @@ -160,7 +160,7 @@ ID 不复用,完成的 Context 继续存在于当前窗口,既提供稳定 ### Location 是一级引擎事实 -[`ConversationLocationIndex`](../../../../packages/client/runtime/src/client/sessions/conversation-location-index.ts) 根据 `turn/start`、`step/start`、显式 turn/step payload、`step/end` 和 `turn/end` 建立 Event 到 Location 的映射。 +[`ConversationLocationIndex`](../../../../packages/client/ui-conversation/src/client/conversation/location-index.ts) 根据 `turn/start`、`step/start`、显式 turn/step payload、`step/end` 和 `turn/end` 建立 Event 到 Location 的映射。 Location 有 `session`、`turn`、`step` 和 `unresolved` 四种形状。Turn/Step 各自带 `open`、`closed` 或 `unknown` 状态,以及已加载的 start/end Event。 @@ -302,19 +302,19 @@ Unknown fallback 展示了 Registry ownership:fallback 只处理没有任何 ## View Builder 与 React identity -[`ConversationViewRegistry`](../../../../packages/client/runtime/src/client/conversation/view-registry.ts) 为每个 target 创建独立的 per-Session builder。Registry 保存 factory,不共享某个 Session 的排序或缓存。 +[`ConversationViewRegistry`](../../../../packages/client/ui-conversation/src/client/conversation/view-registry.ts) 为每个 target 创建独立的 per-Session builder。Registry 保存 factory,不共享某个 Session 的排序或缓存。 Assembler 低频完整替换时调用 `replace({ nodes, timeline })`;普通 prepend/append flush 调用 `apply({ upserts, timeline })`。Builder 只接收 Definition 已构造完成的 target Nodes。 -[`ChatSnapshotBuilder`](../../../../packages/client/ui-conversation/src/client/conversation-nodes/chat-snapshot-builder.ts) 维护 `order`、keyed `nodes` store、turn/step `locations` index、`timeline`,以及由 StatsLine 使用并镜像到顶层公共兼容字段的 `legacy` slice。 +[`ChatSnapshotBuilder`](../../../../packages/client/ui-chat/src/client/conversation-nodes/chat-snapshot-builder.ts) 维护 `order`、keyed `nodes` store、turn/step `locations` index、`timeline`,以及由 StatsLine 使用并镜像到顶层公共兼容字段的 `legacy` slice。 Chat 结构变化只由新 key、`anchorSeq`、visibility 或 Location identity 变化触发。普通内容变化不重建 `order`;keyed Node store 只替换该 key 的 value。 Builder 遇到结构变化时从 store 的当前 values 计算 visible order,并按未变化引用复用索引数组。Prepend 可以增加前部历史 key,append 可以增加尾部或按业务 anchor 落位,既有 key 不因排序变化而重命名。 -[`ChatView`](../../../../packages/client/ui-conversation/src/client/chat/ChatView.tsx) 只遍历 `order`。每个 [`ChatNodeSeat`](../../../../packages/client/ui-conversation/src/client/chat/ChatNodeSeat.tsx) 以 Context key 固定在同一个父列表中,并按 `node.kind` 分发 `'conversation.chat.node'` keyed slot。 +[`ChatView`](../../../../packages/client/ui-chat/src/client/chat/ChatView.tsx) 只遍历 `order`。每个 [`ChatNodeSeat`](../../../../packages/client/ui-chat/src/client/chat/ChatNodeSeat.tsx) 以 Context key 固定在同一个父列表中,并按 `node.kind` 分发 `'conversation.chat.node'` keyed slot。 -[`ChatNodeDataMap`](../../../../packages/client/ui-conversation/src/client/contract/chat-nodes.ts) 是 declaration-merged 的 renderer payload registry。每个业务模块分别注册自己的 Definition 和 keyed renderer;`registerConversationNodes()` 与 `registerChatNodeRenderers()` 只负责装配这些独立贡献,不通过 closed union 或中心 switch 解释业务。内建实现仍位于 `ui-conversation`,但该类型和注册边界允许业务迁入独立 package 而不修改 Chat dispatcher。 +[`ChatNodeDataMap`](../../../../packages/client/ui-chat/src/client/contract/chat-nodes.ts) 是 declaration-merged 的 renderer payload registry。每个业务模块分别注册自己的 Definition 和 keyed renderer;`registerConversationNodes()` 与 `registerChatNodeRenderers()` 只负责装配这些独立贡献,不通过 closed union 或中心 switch 解释业务。内建实现位于 `ui-chat`,且该类型和注册边界允许业务迁入独立 package 而不修改 Chat dispatcher。 `conversation.view` 的 Chat entry 在声明 `conversation.chat.node` child slot 时统一注册 `ChatNodeTurnDataInjected`。`ChatNodeSeat` 只把稳定 Node key 作为 `hookContext` 传给 slot;Slot renderer 用官方 standard props 中的 `useSession` 和该 key 构造 `useTurnData(businessKey)`,因此每个 keyed Chat renderer 都能读取自己 Node 所属 Turn 的强类型只读 data,Assistant renderer 不拥有特殊注入权限。 diff --git a/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.i18n.yaml index 50cdedbe5b..d9823a5e88 100644 --- a/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.md -2026-08-10-cancelled-stream-prefix-finalize.md: 0cae25b786922fba8204d68ca9c0a669e43d76a0 -2026-08-10-cancelled-stream-prefix-finalize.zh.md: e961ea6a51f74dcc244e4ad8970eae4cbe4c9a6c +2026-08-10-cancelled-stream-prefix-finalize.md: fd397a02663908f5984b4e1798d1b1759b140c79 +2026-08-10-cancelled-stream-prefix-finalize.zh.md: 44adb2ff4163cd1904a9a93895c99519bae2f234 diff --git a/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.md b/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.md index 0cae25b786..fd397a0266 100644 --- a/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.md +++ b/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.md @@ -36,4 +36,4 @@ Terminal provider errors still discard their streamed prefix. That asymmetry rem ## Testing -`packages/core/agent-loop/tests/cancel.spec.ts` covers content, cited seqs, event order, next-request parity, reasoning-only output, tool-call omission, recovery cancellation, and the empty-prefix case. `packages/llm/llm/tests/assembler.spec.ts` covers `interruptedBlocks()`. `packages/client/ui-conversation/tests/conversation-node-definitions.client.spec.ts` and `packages/client/ui-trajectory/tests/conversation-definitions.client.spec.ts` cover both client projections. The keyless `cancel` ACP snapshot and `goal-round-driver` goal snapshot cover assembled applications. +`packages/core/agent-loop/tests/cancel.spec.ts` covers content, cited seqs, event order, next-request parity, reasoning-only output, tool-call omission, recovery cancellation, and the empty-prefix case. `packages/llm/llm/tests/assembler.spec.ts` covers `interruptedBlocks()`. `packages/client/ui-chat/tests/conversation-node-definitions.client.spec.ts` and `packages/client/ui-trajectory/tests/conversation-definitions.client.spec.ts` cover both client projections. The keyless `cancel` ACP snapshot and `goal-round-driver` goal snapshot cover assembled applications. diff --git a/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.zh.md b/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.zh.md index e961ea6a51..44adb2ff41 100644 --- a/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-10-cancelled-stream-prefix-finalize.zh.md @@ -36,4 +36,4 @@ Chat 和 Trajectory Conversation Definition 从持久消息读取 `interrupted` ## Testing -`packages/core/agent-loop/tests/cancel.spec.ts` 覆盖内容、引用的 seq、事件顺序、下一请求的一致性、仅 reasoning 的输出、工具调用省略、恢复期间的取消和空前缀情形。`packages/llm/llm/tests/assembler.spec.ts` 覆盖 `interruptedBlocks()`。`packages/client/ui-conversation/tests/conversation-node-definitions.client.spec.ts` 和 `packages/client/ui-trajectory/tests/conversation-definitions.client.spec.ts` 覆盖两种客户端投影。keyless 的 `cancel` ACP 快照和 `goal-round-driver` goal 快照覆盖完整应用。 +`packages/core/agent-loop/tests/cancel.spec.ts` 覆盖内容、引用的 seq、事件顺序、下一请求的一致性、仅 reasoning 的输出、工具调用省略、恢复期间的取消和空前缀情形。`packages/llm/llm/tests/assembler.spec.ts` 覆盖 `interruptedBlocks()`。`packages/client/ui-chat/tests/conversation-node-definitions.client.spec.ts` 和 `packages/client/ui-trajectory/tests/conversation-definitions.client.spec.ts` 覆盖两种客户端投影。keyless 的 `cancel` ACP 快照和 `goal-round-driver` goal 快照覆盖完整应用。 diff --git a/.agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.i18n.yaml new file mode 100644 index 0000000000..43a002ec74 --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.md +2026-08-20-client-session-conversation-ownership.md: e7ab737c13721c41d9244c7830c74cf56ea33f54 +2026-08-20-client-session-conversation-ownership.zh.md: deeca51740cf07e15192744c027e7bc09369bdfa diff --git a/.agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.md b/.agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.md new file mode 100644 index 0000000000..e7ab737c13 --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-08-20-client-session-conversation-ownership.md @@ -0,0 +1,461 @@ +# Agent Note: Client Session, Conversation, and UI ownership layers + +Status: implemented + +English | [中文](2026-08-20-client-session-conversation-ownership.zh.md) + +## Problem + +The Web Client once placed Session and Workspace objects, event windows, Conversation assembly, React hooks, the Slot registry, and the Store engine in one general Runtime. Protocol state, business projections, React bindings, and page presentation shared one dependency hub, so a change in any layer could spread across the entire frontend. + +Session snapshots could also accumulate data they did not own, including event arrays, Conversation Views, Chat Nodes, and pending interactions. Ordinary consumers then had to understand event replay and concrete views, while adding a Conversation target could require changes to Session, Runtime, and the renderer. + +Without an explicit interface between React and Session lifetimes, binding release, Hook source replacement, and Slot store cleanup became dedicated callback protocols. Approval and Question both affect sidebar state and composer takeover; independently maintained state could make those surfaces select different pending requests. + +The Client needs one-way dependencies between data owners, React adapters, generic rendering machinery, and concrete views while preserving application behavior. + +## Decision + +The Client uses the layering “Controller and domain object → UI adapter → renderer → Slot component.” Controllers and domain objects publish React-free observable sources; their `ui-*` packages declare standard props and register sources; `ui-renderer` creates selector hooks at Slot binding points; components read data and actions only from Slot props. + +```text +[Remote / Controller / domain object] + | + | bare observable source + v + [ui-* adapter] + | + | standard source registration + v + [ui-renderer] + | + | selector hook binding + v + [Slot component] +``` + +Client Session and Workspace objects belong to `api/session-controller/client` and `api/workspace-controller/client`, respectively. Target-neutral Conversation data structures and assembly belong to `client/ui-conversation`; Chat and Trajectory belong to `client/ui-chat` and `client/ui-trajectory`, respectively. + +The React adapters for Session and Workspace belong to `client/ui-session` and `client/ui-workspace`. The Store engine belongs to `client/store`; the Slot registry, scope materialization, and observable-to-hook binding belong to `client/ui-renderer`. + +The system has no aggregate `client/runtime` package and no replacement central facade. [Session history and event transport](2026-08-18-session-history-and-event-transport.md) defines Session history, Remote streams, pagination cursors, and reconnect continuity; this note starts from the Client objects and sources published by Controllers. + +## Layering principles + +### Controllers are React-free logic owners + +A Controller may be installed as a Cordis service, but it does not own React Contexts, React hooks, Slot props, or components. A Controller snapshot contains only facts that it owns, and its commands change only Host or domain-object state. + +The UI layer may read multiple Controllers for one navigation decision, but it does not write the combined result back into any Controller snapshot. A UI adapter does not duplicate a Controller command's business implementation. + +### UI adapters own React integration + +Each standard hook belongs to the `ui-*` package closest to its data semantics. + +| Hook | Owner | Source | +| --- | --- | --- | +| `useSessions` | `client/ui-session` | Session Controller global list | +| `useSession` | `client/ui-session` | Current Session snapshot | +| `useProjection` | `client/ui-session` | Current Session keyed projection | +| `useSessionPendingInteraction` | `client/ui-session` | Aggregated pending domains | +| `useWorkspaces` | `client/ui-workspace` | Workspace Controller list | +| `useConversation` | `client/ui-conversation` | Conversation binding snapshot | +| `useChat` | `client/ui-chat` | `chat` target source | +| `useTrajectory` | `client/ui-trajectory` | `trajectory` target source | + +`ui-renderer` implements only generic binding. It does not import Session, Workspace, Conversation, Chat, or Trajectory business types or values. + +### Slot scopes and standard props are separate + +`ui-slots` declares root, session, and session-maybe scopes plus declaration-merge-extensible standard prop types. It does not decide which hooks each scope installs. + +`ui-renderer` implements generic scope adapters and source materialization. `ui-session` installs the Session scope and supplies its built-in sources; other domain packages register only their own sources and the Slot entries that consume them. + +Adding a target does not add a branch to the renderer or Session Controller. The data owner handles state identity, updates, errors, and release; the UI adapter owns the hook; the presentation owner owns target-specific projections and interaction state. + +## Package ownership + +| Package | Owns | Explicitly does not own | +| --- | --- | --- | +| `api/session-controller/client` | Session objects, list, selection, commands, projections, queue, event windows, and Agent Contexts | Conversation targets, React, Slots, Workspace | +| `api/workspace-controller/client` | Workspace objects, ordering, archive state, commands, and snapshots | React, Session navigation policy, directory UI | +| `client/ui-session` | Session scope, standard sources, `SessionProvider`, and pending-interaction aggregation | Session transport, Conversation assembly, Approval/Question results | +| `client/ui-workspace` | Workspace hook, browser UI, and cross-Controller navigation policy | Workspace transport, copies of Session data | +| `client/ui-conversation` | Conversation core, registries, bindings, shell, input, composer, queue, and View navigation | Session transport, Chat/Trajectory snapshots | +| `client/ui-chat` | Chat target, Node definitions, renderers, selection, details, locale, and historical images | Session lifecycle, generic View navigation, Trajectory | +| `client/ui-trajectory` | Trajectory target, event-record projection, and inspection view | Session snapshots, Chat snapshots | +| `client/ui-approval` | Pending Approval, Remote listener, composer, and approval UI | Session control, generic composer election | +| `client/ui-user-questions` | Pending Question, Remote listener, composer, and question UI | Session control, generic composer election | +| `client/store` | React-free Store contract and implementation | Domain objects, React hooks, Slot lifetimes | +| `client/ui-renderer` | SlotRegistry, scope binding, selector hooks, outlets, and React root | Session, Workspace, and Conversation business logic | + +## Overall data flow + +Session data reaches the UI through this path: + +```text +[ctx.remote.session] + | + v +[api/session-controller/client] + |-- SessionListState --------------------------> [ui-session] -> useSessions + |-- SessionSnapshot ----------------------------> [ui-session] -> useSession + |-- ProjectionValueSource ----------------------> [ui-session] -> useProjection + `-- per-Session SessionEventSource + | + v + [client/ui-conversation] + | + | assemble + v + ConversationSnapshot ----------------> useConversation + | + |---------+----------| + v v + [ui-chat] [ui-trajectory] + | | + useChat useTrajectory +``` + +Workspace data enters the Workspace Controller from `ctx.remote.workspace`, then `ui-workspace` exposes it as `useWorkspaces`. For cross-domain navigation, `ui-workspace` temporarily reads the Session Controller and issues a selection or command. + +Approval and Question arrive from the Host waterfall through `ctx.remote.$on` at their respective UI owners. Each owner publishes a Pending object; `ui-session.pendingInteractions` then supplies that same object to Session navigation state and Conversation composer selection. + +## Session Controller Client + +### Scope of SessionSnapshot + +`SessionSnapshot` represents control and lifecycle facts belonging to a Session. It may contain identity, running, removed, blank, subagent address, open phase, history phase, prompt error, agent error, and queue state. + +It does not contain: + +- a raw event array; +- Conversation Views; +- Chat Nodes; +- Trajectory rows; +- pending Approval or Question objects; +- presentation state that requires callers to traverse events. + +Whether a field derives from an event, control frame, or local command does not automatically determine its owner; consumption semantics determine ownership. `composerPhase` depends on both Session lifecycle and Conversation target activity, so `ui-conversation` composes it instead of placing it in `SessionSnapshot`. + +### Three read faces + +The Session Controller exposes three distinct read faces: + +1. The global Session list and current-selection source, used by navigation and `useSessions`. +2. A logical binding for each Session containing `sessionId`, a `SessionSnapshot` source, commands, and projection sources. +3. A Conversation-facing `SessionEventSource` used only by the Conversation assembly core. + +Ordinary UI components do not read `SessionEventSource` directly. `ui-session` does not read private event windows, and the `ui-conversation` core receives neither React bindings nor Slot APIs. + +### SessionEventSource + +`SessionEventSource` exposes a materialized event window, not a transport. + +The window carries ordered `entries`, `hasMore`, a monotonic `revision`, and a `replace | prepend | append` change description. + +Initial open, reconnect, gap repair, and updates whose continuity cannot be proven publish `replace`; history pagination publishes `prepend`; a continuous live event publishes `append`. The Conversation core selects incremental update or complete rebuild from the revision and change. + +`MutableSessionEventSource` is the Session Controller's internal write face. Consumers depend only on the read-only `SessionEventSource`. + +### Session binding lifecycle + +Each Session binding owns a Cordis Context and Fiber. The Session Controller creates and releases the binding. + +Objects that depend on a Session register cleanup through `binding.ctx.effect()`. Releasing a binding cleans up Conversation bindings, UI materializations, and scoped Slot stores without a dedicated `onBindingRelease` or `onRelease` callback protocol. + +This cleanup does not require the Session Controller to know the roster of upper-layer consumers. + +## UI Session + +### Service responsibilities + +`client/ui-session` is the sole Session adapter between the Session Controller and the React/Slot system. It provides `ctx.uiSession` and: + +- observes the Session list, current selection, and per-Session bindings; +- installs the session and session-maybe scope adapters; +- supplies `SessionProvider` rendering semantics; +- supplies built-in Session snapshot, projection, and sessionId sources; +- accepts Session-scoped source contributions from other domain packages; +- aggregates pending interactions registered by business packages. + +It does not own Session transport, event folding, Conversation targets, or concrete business results. + +### Standard source registration + +A domain package calls `ctx.uiSession.provide()` to register a bare source. The descriptor statically declares its hook, keyed-hook, and prop rosters; `resolve(binding)` returns exactly those values for one Session binding. For example, `ui-conversation` registers each binding's snapshot as the `conversation` hook source. + +The renderer converts an ordinary source into `use`. Open key spaces such as projections use a keyed-hook resolver, while stable values use props. + +The runtime rejects undeclared, missing, or duplicate standard props. `ui-session` materializes its own built-ins through the same mechanism, so the renderer has no Session-specific name branches. + +### Scope binding + +session and session-maybe use the same adapter with different binding semantics: + +- a strict session scope refuses to render without a current binding; +- session-maybe uses a stable absent binding to preserve hook call order; +- changing the current Session rebuilds the strict Session subtree under the `sessionId` key; +- root and session-maybe entries may remain mounted across Session changes. + +Each real materialized binding retains the Controller binding's Context. `ui-session` removes the cache entry and withdraws the current binding through `binding.ctx.effect()`. + +Changing the contribution roster rematerializes existing bindings and publishes a new source set. Source identity remains stable within one binding lifetime, as required by `useSyncExternalStore` caching. + +### SessionProvider + +`SessionProvider` is a standard seat derived by `PropsRenderSlots` from a session-scoped child declaration, not a React Context imported directly by business components. + +It accepts ordinary `ReactNode` children rather than a `(sessionId) => ReactNode` render function; callers wrap `renderSlot('details', {})` directly. + +Session identity comes from the scope binding and standard `sessionId` prop. The Provider handles only the absent branch and subtree isolation by Session identity; components do not obtain Session data through a Provider callback. + +### Pending interactions + +Business packages extend `SessionPendingInteractionMap` through declaration merging. Every pending object carries at least a stable `key`, domain `kind`, and `sessionId`; `ui-session` does not import concrete Approval or Question types. + +A business plugin calls `registerPendingInteraction(precedence)` in `apply()` to create a stable registration for its pending domain. The returned per-request publication function publishes one exact object and returns an idempotent disposer for that object. + +Concurrent objects with the same key are rejected; replacement requests use a new key. One Session may hold multiple domains or requests at once. + +`ui-session` selects each Session's effective object using domain precedence. Higher precedence wins; at equal precedence, the later valid object in traversal order wins. + +The aggregate is published as `pendingInteractions: ObservableSnapshot>`; `useSessionPendingInteraction` is its React read face. + +Session navigation state and composer takeover read the same effective object. They do not maintain separate status maps or takeover rosters. + +## Workspace Controller and UI Workspace + +### Scope of WorkspaceSnapshot + +`WorkspaceSnapshot` contains only Host-authoritative data owned by the Workspace Controller, including Workspace rows, order, archive set, follow phase, and errors. A Workspace row's `sessionIds` is an association field, not a copy of Session objects in the Workspace snapshot. + +These combined facts do not enter `WorkspaceSnapshot`: + +- whether the Workspace and Session baselines are both ready; +- the most recent Workspace derived from Session update times; +- whether the current Session is cleared because it was archived; +- which blank Session New Session should reuse; +- which Session initial startup should select. + +### UI Workspace composition responsibilities + +`client/ui-workspace` registers the Workspace list source as the root standard source `workspaces`, from which the renderer provides `useWorkspaces`. + +Initial selection, blank-Session reuse, new-session navigation, concurrent-create coalescing, and navigation after archival are UI navigation policy. That policy may read both `ctx.workspaces` and `ctx.sessions` at decision time, but it issues only Controller commands and selection actions and does not publish a combined snapshot. + +Directory pickers, directory browsing, and `openPath` are separate directory capabilities and do not enter the Workspace Controller. + +## UI Conversation + +### Assembly core + +`client/ui-conversation` contains both the React-free Conversation assembly core and the React adapter for the same domain. + +The core owns `ConversationSnapshot`, the Definition registry, the View registry, the event assembler, the location index, per-Session bindings, target sources, and target activity. + +The core obtains `SessionEventSource` from a Session binding. Append and prepend changes with continuous revisions use incremental assembly; replace changes or revision gaps rebuild from the complete window. + +Definition or View roster changes rebuild only the Conversation binding; they do not rebuild a Session or reopen a Remote stream. The core does not import React and can test event folding, incremental updates, and registry lifetimes independently. + +`ConversationSnapshot` does not copy `SessionSnapshot` or expose raw events. It publishes only the target-neutral View roster, target activity, and target-source lookup. + +`useSession` and `useConversation` come from separate sources and are not guaranteed to publish atomically in one React commit. Components that read both compute purely from their current snapshots and do not treat notification order as business causality. + +### Definition and View registries + +`UiConversation.events` is the sole registry for event Definitions, and `UiConversation.views` is the sole registry for target snapshot builders. + +The registries reject duplicate keys, preserve registration order, and return idempotent disposers. Existing Conversation bindings rebuild from their current event windows when a roster changes. + +A target package extends snapshot and location-data maps through declaration merging, then registers its Definitions, builder, and View. Registrations follow Cordis effect disposal. + +`ui-conversation` does not import concrete target packages. + +### Conversation React adapter + +The React adapter registers each Conversation binding snapshot as the Session standard source `conversation`, from which the renderer provides `useConversation`. + +The package also owns the shell, input, composer chain, queue UI, drafts, View navigation, and phase composition. The core reads no React Context, Slot props, or component state. + +View selection order is a valid persisted selection, registered `chat`, then no View. An invalid selection does not overwrite the persisted value, and the system does not fall back to the first registered View. + +Without `ui-chat`, the shell can still activate and mount but does not implicitly select Trajectory or another target. + +The shell phase is a pure composition of Session lifecycle and Conversation target activity. An active Session or any target reporting visible content produces active; a failed first prompt remains engaging. + +### Input and composer + +The composer chain belongs to `ui-conversation`; a concrete takeover belongs to its business package. `ConversationRoot` reads the current Session's effective object through `useSessionPendingInteraction` and supplies it to chain selectors as `ComposerChainProps.pendingInteraction`. + +A selector is a pure function of owner currency. Its non-null result reaches the selected component as `matched`. A stable composer entry and the default composer remain mounted together, while the chain selects one effective presentation. + +Draft and input state belong to Conversation UI and do not enter the Session snapshot. Queue commands use a Session-scoped service for addressing and do not write queue UI into the Conversation core. + +## Chat and Trajectory targets + +### Chat owner + +`client/ui-chat` registers target id `chat` and owns the Chat snapshot builder, Conversation Node definitions, keyed node renderers, selection, details, statistics, locale, Tool-inspection collaboration, and historical-image cache. + +It registers the `chat` target source through `ctx.uiSession.provide()`. `ChatNodeSeat` and internal Chat consumers use `useChat` instead of passing `useConversation(snapshot => snapshot.views.get('chat'))`. + +Only visible non-command Chat Nodes activate Chat. Ordinary command-only history keeps the Hero visible; the `/goal` `command-input` Node activates a fresh Conversation. + +The historical-image cache's Session key, pending promise, generation guard, blob URL, and disposer all belong to `ui-chat`; draft images remain part of Conversation input. + +### Trajectory owner + +`client/ui-trajectory` registers `trajectory` through the same target protocol. It owns event-record projection, timelines, virtual rows, selection, and the inspection view, and exposes `useTrajectory` through a standard source. + +Session lifecycle reads `useSession`, while Trajectory data reads `useTrajectory`. Trajectory does not obtain its own data through a Session or Chat snapshot. + +Other targets use the same registration flow without modifying the renderer, Session Controller, or ui-session. + +## Approval and Question + +### Stable registration + +Approval and Question plugin installation separates stable registrations from per-request handling. `apply()` registers locale data, calls `registerPendingInteraction()` once for its pending domain, and registers one stable entry in `conversation.composer`. + +The stable Approval entry also declares its detail child Slot. Concurrent requests and Session count do not add composer entries or redeclare Slots, and every registration follows plugin-fiber disposal. + +### One waterfall request + +A Remote Event listener resolves the Session from its own Agent Context. Without a Session scope it calls `next()` to continue the waterfall; with a Session scope it creates a `PendingApproval` or `PendingQuestion`. + +The listener publishes the object through the registered domain publication function, waits for user completion, cancellation, or request-signal abortion, and removes the exact object in `finally`. + +One request does not register a Slot, create another lifecycle effect, or mutate the Session snapshot. + +Approval exposes allow and reject; Question exposes answer and cancel. User cancellation of a Question returns `ASK_CANCELLED`; interruption of a pending request by `AbortSignal` returns `UserQuestionError(ASK_ABORTED)` rather than leaking the carrier's `AbortError` or an ordinary `Error`. + +The Gateway requires only that Remote Event arguments and results are valid JSON transport values. It does not duplicate domain validation of Question options. + +### One pending projection + +The Sidebar and composer consume the same `pendingInteractions` snapshot. Navigation displays approval, plan-review, or question state from the effective object's `kind`; each composer entry selects its own panel by object identity. + +The same request identity drives both UI surfaces. A request that replaces another request of the same type uses a new key, so selectors and subscribers observe the identity change. + +`ui-session` implements only cross-domain precedence and does not interpret Approval or Question fields. + +## UI Renderer and Store + +### UI Renderer + +`client/ui-renderer` owns the `SlotRegistry` service and React renderer. It is responsible for: + +- `ctx.slots.register()`, `inject()`, `renderSlot()`, and declaration lifetimes; +- root, session, and session-maybe scope adapters; +- binding standard observable sources to selector hooks; +- Slot outlets, error isolation, root mount, and hydration; +- managing Slot store instance lifetimes by scope key. + +The renderer may know generic scope names and binding protocols but does not read domain services. Rendering Session scope without an installed adapter is an assembly error that fails immediately. + +### Store + +`client/store` is a plain React-free library owning `ObservableSnapshot`, `SnapshotStore`, `defineStore`, `createSnapshotStore`, and `shallowEqual`. + +`ui-slots` references the Store contract; `ui-renderer` manages Store instances and supplies `useStore`. + +Stores hold viewing and interaction state such as drafts, View selection, Chat selection, inspection requests, and panel size. Session, Workspace, Conversation, Remote streams, and connection generations do not enter Stores. + +### Registration and release order + +When one plugin provides both a source and a Slot entry, it registers the source first and the entry second. Reverse Cordis disposal then removes the entry before the source, so a mounted entry never briefly loses a required hook. + +Releasing a Session binding cleans up UI materialization and scoped Stores through `binding.ctx.effect()`. Releasing a plugin fiber cleans up sources, listeners, and Slot entries through registration disposers. + +Every disposer is idempotent and depends on no implicit callback outside the Cordis lifecycle. + +## Composition and dependency direction + +The application bundle explicitly installs the required Controller, adapter, target, and renderer plugins. Each owner's `apply()` installs only its own service, listener, and contributions. + +Runtime consumption flows as `session-controller → ui-session → ui-conversation → target UI`, `workspace-controller → ui-workspace`, and `store → ui-slots → ui-renderer`; Approval and Question depend only on the pending-registration point exposed by `ui-session`. + +Arrows in this description represent runtime consumption and do not include type-only declaration-merge edges. Controllers do not depend back on UI adapters, the renderer does not depend back on domain packages, and the Conversation core does not depend on a concrete target. + +UI components do not receive `ctx`. Cross-package collaboration uses Cordis services, standard sources, or Slot registrations without introducing an aggregate facade. + +## Developer guidance + +### Choose the data owner first + +Before adding state, choose its sole owner from its consumption semantics: Host communication, commands, and entity lifecycle belong to an API Controller; data assembled from Session events but independent of a target belongs to the Conversation core; projections serving only one View belong to that target package; drafts, selections, and panel state belong to the UI package that owns the interaction. + +The same fact must not be retained simultaneously in a Controller snapshot, Conversation snapshot, and Store. A cross-domain decision reads multiple sources and immediately issues a command; it does not create a joined snapshot or cache another domain's object. + +These are signs of incorrect ownership: a Controller imports React; the renderer branches on business types; a component traverses Session events; a Store holds Session or Workspace entities; changing one target requires changing the Session Controller. + +### Add Session-scoped data + +1. Provide a React-free observable source in the domain owner. +2. Declaration-merge the standard prop type in the owning UI adapter. +3. Declare a fixed roster through `ctx.uiSession.provide()` and resolve its source from a Session binding. +4. Let the Slot component receive the generated hook through `PropsRuntime`; do not pass `ctx` to a component. +5. Attach each binding resource's cleanup to `binding.ctx.effect()` and leave registration cleanup to the plugin fiber. +6. Test missing values, duplicate names, roster replacement, Session changes, and binding disposal. + +Only open key spaces use keyed hooks. Finite stable sources use ordinary hooks, and immutable identifiers use props. Do not hard-code business names in the renderer to save one registration. + +### Add a Conversation target + +1. Extend the Conversation snapshot or location-data map in the target package. +2. Register the required event Definitions with `UiConversation.events`. +3. Register the snapshot builder, target id, View, and activity rule with `UiConversation.views`. +4. Expose the target's standard selector hook through `ctx.uiSession.provide()`. +5. Register the renderer, locale, and target-specific Slot entries in the same package. +6. Verify that unloading the target rebuilds only the Conversation binding without changing the Session, other targets, or Remote stream. + +A target must not use another target's snapshot as its data source. Optional collaboration uses a narrow port or Slot; when a target is absent, the shell remains bootable and does not guess a fallback. + +### Add a pending-interaction domain + +1. Define the Pending object and its completion, cancellation, and interruption semantics in the business package. +2. Add the object to `SessionPendingInteractionMap` through declaration merging. +3. Call `registerPendingInteraction()` once in `apply()` and register one stable composer entry. +4. Resolve the Session from the Agent Context in the Remote waterfall listener; call `next()` when the listener cannot handle the request. +5. When it can handle the request, create the Pending object, publish it through the publication function, await its result, and remove it in `finally`. +6. Test concurrent keys, precedence, user cancellation, transport abort, plugin disposal, and delegation without a Session. + +A request does not register Slots, declare child Slots, mutate the Session snapshot, or create a separate state index. Sidebar and composer both read one effective object from `useSessionPendingInteraction`. + +### Review checks + +- Every new source, registry contribution, listener, and cache has an explicit Cordis-fiber or Session-binding owner. +- Every public hook traces to one React-free source; no selector is forwarded through layers only to pass arguments. +- Every component obtains data and actions from standard props or the owning Slot's inject face. +- Every target has defined behavior when absent, dynamically registered, and unloaded. +- Every cross-layer import advances in the one-way Controller, adapter, renderer, component direction. +- Each error is classified by the earliest owner that can explain its semantics; carrier errors do not leak directly as business errors. + +## Verification + +Tests owned by each layer pin Controller bindings and event sources, UI scopes and pending precedence, incremental Conversation assembly and View fallback, target projections, waterfall results, and renderer scope/Store lifetimes. Application-composition tests cover both the complete roster and startup without a concrete target; component tests do not replace object-layer, replay, and lifecycle tests. + +## Alternatives considered + +- **Keep a Runtime facade.** One entry point would retain the dependency hub and let new code bypass domain owners, so the system provides neither the facade nor a compatibility export. +- **Put all Client state in API Controllers.** Protocol objects would then own React, Views, and presentation policy, so Controllers retain only React-free domain state. +- **Let Controllers provide React hooks directly.** Non-React consumers could not reuse the same objects, and transport and renderer lifetimes would become interdependent. +- **Put Conversation in SessionSnapshot.** This would expand the Session API and force ordinary Session consumers to understand event folding and target rosters. +- **Let Chat and Trajectory replay Session events independently.** Ordering, locations, and registry rebuild would be duplicated, so the shared assembly core stays in `ui-conversation`. +- **Extract the Conversation core into another non-UI package.** The core and adapter currently evolve together and have no other non-UI package consumer; directory separation within one package keeps the core React-free. +- **Combine Workspace and Session into one snapshot.** This would create another cross-domain owner, so cross-domain logic remains an immediate decision in `ui-workspace`. +- **Build every standard hook into the renderer.** Generic infrastructure would need to know every domain, so standard-source registration keeps the renderer independent from business types. +- **Dynamically register a composer entry for every pending request.** This would redeclare child Slots and make concurrent requests compete through registration order, so stable entries are separate from request publication. +- **Write pending interactions into a Session projection.** An unanswered waterfall is not a committed durable Session fact; Remote Event replay restores it after refresh, so it remains in a business UI source. +- **Add a dedicated release callback to bindings.** This would duplicate the Cordis lifecycle; `binding.ctx.effect()` already attaches consumer cleanup to the same owner. +- **Pass the Session id from SessionProvider through a render function.** This would create another data-injection path; ordinary children and the standard `sessionId` prop retain one entry for scoped data. +- **Keep Store in the renderer.** The Store contract does not depend on React and is reused by objects and test infrastructure, so `client/store` keeps the engine separate from rendering lifetimes. + +## Consequences + +Session, Workspace, Conversation, and each concrete target own one authoritative state. Non-React consumers can reuse Controllers and the assembly core directly. A new Conversation target registers its Definition, builder, View, standard source, and Slot entries; a new pending-interaction domain declares its type, registers its domain, and provides one stable composer entry. + +The renderer and Session Controller gain no branch for a new business domain, while Session bindings and plugin fibers provide two explicit, composable release paths. The UI can observe independent Session and Conversation source publications, and consumers cannot depend on their notification order. + +Composition packages must explicitly load the required adapter and target plugins. The shell remains operational without a concrete target but neither creates nor guesses that target's View. More packages and explicit registrations add assembly work, while dependency direction, test scope, and failure ownership become locally identifiable. diff --git a/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.i18n.yaml b/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.i18n.yaml index 6db4668e86..deb8efc160 100644 --- a/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.i18n.yaml +++ b/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.md -2026-07-30-web-transcript-log-ordered-projection.md: e4c9fdd6fcdec0b14ed2724dd58365e1d98af8b4 -2026-07-30-web-transcript-log-ordered-projection.zh.md: 625bd993a0b20bbbed318edf71d79866fa80bff0 +2026-07-30-web-transcript-log-ordered-projection.md: ed6fa4df3ac25bf6fe2947e6fb7bb7cbde6ac00a +2026-07-30-web-transcript-log-ordered-projection.zh.md: cf8dc35099f416356310081f758d0b89fe8282b9 diff --git a/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.md b/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.md index e4c9fdd6fc..ed6fa4df3a 100644 --- a/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.md +++ b/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.md @@ -37,9 +37,9 @@ import type { CompactionCheckpointSource } from '@deepseek-ai/dsh-compaction/che const COMPACT_PLUGIN: CompactionCheckpointSource['plugin'] = 'compact' ``` -Renaming the Service Definition's plugin id is now a compile error in the client: `TS2322: Type '"compact"' is not assignable to type '"compaction"'`. The import must stay **type-only** — a value import of any `@deepseek-ai` package that is neither a platform module nor an inline-safe wire layer is rejected by the client purity gate (`packages/client/tsdown.client.ts`), whose own message records that type-only imports are erased and never reach it. A type-only leaf import needs both a `tsconfig.base.json` `paths` entry and `{"path": "../../compaction/compaction"}` in `packages/client/runtime/tsconfig.json` `references`: composite `rootDir` rules apply to erased imports as well, and without the reference the diagnostic is `TS6059`/`TS6307`. +Renaming the Service Definition's plugin id is now a compile error in the client: `TS2322: Type '"compact"' is not assignable to type '"compaction"'`. The import must stay **type-only** — a value import of any `@deepseek-ai` package that is neither a platform module nor an inline-safe wire layer is rejected by the client purity gate (`packages/client/tsdown.client.ts`), whose own message records that type-only imports are erased and never reach it. A type-only leaf import needs both a `tsconfig.base.json` `paths` entry and `{"path": "../../compaction/compaction"}` in `packages/client/ui-chat/tsconfig.json` `references`: composite `rootDir` rules apply to erased imports as well, and without the reference the diagnostic is `TS6059`/`TS6307`. -`packages/client/ui-conversation/tests/conversation-node-definitions.client.spec.ts` is the behavioral half, driving the compaction Definition with checkpoint and provenance records and proving that an older page can fill missing summary data. The Definition's type-only leaf import keeps the client isolated from the compact package root and the host-side `Context` merges reachable through it. +`packages/client/ui-chat/tests/conversation-node-definitions.client.spec.ts` is the behavioral half, driving the compaction Definition with checkpoint and provenance records and proving that an older page can fill missing summary data. The Definition's type-only leaf import keeps the client isolated from the compact package root and the host-side `Context` merges reachable through it. The divergence from the terminal is therefore narrow: both frontends recognize a checkpoint from the same declaration — the terminal value-imports `isCompactCheckpointSource` host-side, where no gate applies, and the client pins the type. diff --git a/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.zh.md b/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.zh.md index 625bd993a0..cf8dc35099 100644 --- a/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.zh.md +++ b/.agents/notes/implemented/bug-fix/2026-07-30-web-transcript-log-ordered-projection.zh.md @@ -37,9 +37,9 @@ import type { CompactionCheckpointSource } from '@deepseek-ai/dsh-compaction/che const COMPACT_PLUGIN: CompactionCheckpointSource['plugin'] = 'compact' ``` -重命名 Service Definition 的插件 id 现在会在客户端产生编译错误:`TS2322: Type '"compact"' is not assignable to type '"compaction"'`。该导入必须保持**仅类型**——任何既非平台模块又非 inline-safe wire 层的 `@deepseek-ai` 包值导入都会被客户端纯度门禁(`packages/client/tsdown.client.ts`)拒绝,而它自己的报错信息就记录着仅类型导入会被擦除、永不抵达该门禁。仅类型的叶子导入同时需要 `tsconfig.base.json` 的一条 `paths` 条目和 `packages/client/runtime/tsconfig.json` `references` 中的 `{"path": "../../compaction/compaction"}`:composite 的 `rootDir` 规则同样适用于被擦除的导入,缺少该引用时的诊断是 `TS6059`/`TS6307`。 +重命名 Service Definition 的插件 id 现在会在客户端产生编译错误:`TS2322: Type '"compact"' is not assignable to type '"compaction"'`。该导入必须保持**仅类型**——任何既非平台模块又非 inline-safe wire 层的 `@deepseek-ai` 包值导入都会被客户端纯度门禁(`packages/client/tsdown.client.ts`)拒绝,而它自己的报错信息就记录着仅类型导入会被擦除、永不抵达该门禁。仅类型的叶子导入同时需要 `tsconfig.base.json` 的一条 `paths` 条目和 `packages/client/ui-chat/tsconfig.json` `references` 中的 `{"path": "../../compaction/compaction"}`:composite 的 `rootDir` 规则同样适用于被擦除的导入,缺少该引用时的诊断是 `TS6059`/`TS6307`。 -`packages/client/ui-conversation/tests/conversation-node-definitions.client.spec.ts` 是行为侧的另一半,用检查点与溯源记录驱动压缩 Definition,并证明后续加载的旧分页可以补齐缺失的摘要数据。Definition 仅类型导入该叶子路径,使客户端继续与 compact 包根及经由它可达的宿主侧 `Context` 合并隔离。 +`packages/client/ui-chat/tests/conversation-node-definitions.client.spec.ts` 是行为侧的另一半,用检查点与溯源记录驱动压缩 Definition,并证明后续加载的旧分页可以补齐缺失的摘要数据。Definition 仅类型导入该叶子路径,使客户端继续与 compact 包根及经由它可达的宿主侧 `Context` 合并隔离。 因此与终端的分歧很窄:两个前端都从同一份声明识别检查点——终端在宿主侧值导入 `isCompactCheckpointSource`(那里不适用任何门禁),客户端钉住类型。 diff --git a/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.i18n.yaml b/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.i18n.yaml index 9f352def8f..a148348ba4 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.i18n.yaml +++ b/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.md -2026-08-05-workspace-blank-session-reuse-membership.md: 0d46a0ccf6924c508db2e6c0f3591468e2956722 -2026-08-05-workspace-blank-session-reuse-membership.zh.md: 6350c6773265edebb381d5ca10fd5126a5722a5a +2026-08-05-workspace-blank-session-reuse-membership.md: df8cc898f5a80937b8aac69ebd51a6a93882971b +2026-08-05-workspace-blank-session-reuse-membership.zh.md: 73ccdbbb002920eea8f7b01bc0fa8a18859bd3ee diff --git a/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.md b/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.md index 0d46a0ccf6..df8cc898f5 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.md +++ b/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.md @@ -26,4 +26,4 @@ Stray blank sessions remain visible in Ungrouped (the user can still open them) ## Testing -`packages/client/runtime/tests/workspaces-service.client.spec.ts` covers the four outcomes: a member blank session is reused (no create RPC); a stray blank with matching cwd is **not** reused and a fresh accounted session is created (regression case); an archived blank is not reused; a rejected first prompt keeps a member blank eligible. The full client suite (`pnpm run test:gui`) stays green. +`packages/client/ui-workspace/tests/workspaces-service.client.spec.ts` covers the four outcomes: a member blank session is reused (no create RPC); a stray blank with matching cwd is **not** reused and a fresh accounted session is created (regression case); an archived blank is not reused; a rejected first prompt keeps a member blank eligible. The full client suite (`pnpm run test:gui`) stays green. diff --git a/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.zh.md b/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.zh.md index 6350c67732..73ccdbbb00 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.zh.md +++ b/.agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.zh.md @@ -26,4 +26,4 @@ Status: implemented ## 测试 -`packages/client/runtime/tests/workspaces-service.client.spec.ts` 覆盖四种结果:成员空白会话被复用(无 create RPC);cwd 匹配但非成员的游离空白会话**不被**复用、改为创建全新入账会话(回归用例);已归档空白会话不被复用;首次提示词被拒后成员空白会话仍可复用。完整客户端套件(`pnpm run test:gui`)保持绿色。 +`packages/client/ui-workspace/tests/workspaces-service.client.spec.ts` 覆盖四种结果:成员空白会话被复用(无 create RPC);cwd 匹配但非成员的游离空白会话**不被**复用、改为创建全新入账会话(回归用例);已归档空白会话不被复用;首次提示词被拒后成员空白会话仍可复用。完整客户端套件(`pnpm run test:gui`)保持绿色。 diff --git a/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.i18n.yaml b/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.i18n.yaml index a28a57cfd5..841f1c8442 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.i18n.yaml +++ b/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.md -2026-08-06-reader-scroll-attribution-observed-top-ledger.md: 66a1ca361cf28bf0beab95fa81da9cac3527474c -2026-08-06-reader-scroll-attribution-observed-top-ledger.zh.md: 6aa1866e802b00d0a3431dfd5bef9efd57121294 +2026-08-06-reader-scroll-attribution-observed-top-ledger.md: b55bbc39f6e1f24bb7751b7743da23736abbd06b +2026-08-06-reader-scroll-attribution-observed-top-ledger.zh.md: e38ead6c9b80e41b37556172bd6c1f411858b6ed diff --git a/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.md b/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.md index 66a1ca361c..b55bbc39f6 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.md +++ b/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.md @@ -18,7 +18,7 @@ A shrink clamp whose layout regrows within the same rendering update before the ## Testing -Unit specs in `packages/client/ui-conversation/tests/chat-view.client.spec.tsx` pin the ledger contract directly: a `readerScroll` helper delivers a position the component never wrote, programmatic deliveries land on the ledger, and the stream-finalization shrink clamp keeps following. Two scenarios in `apps/web/tests/chat-scroll-contract.e2e.ts` extend the [browser e2e lane](../testing/2026-07-24-web-gui-browser-e2e-lane.md): keyboard paging over a settled transcript and a touch-style momentum fling against paced streaming, both red under the wheel-only implementation and green under the ledger. +Unit specs in `packages/client/ui-chat/tests/chat-view.client.spec.tsx` pin the ledger contract directly: a `readerScroll` helper delivers a position the component never wrote, programmatic deliveries land on the ledger, and the stream-finalization shrink clamp keeps following. Two scenarios in `apps/web/tests/chat-scroll-contract.e2e.ts` extend the [browser e2e lane](../testing/2026-07-24-web-gui-browser-e2e-lane.md): keyboard paging over a settled transcript and a touch-style momentum fling against paced streaming, both red under the wheel-only implementation and green under the ledger. The lane's Chromium cannot synthesize any non-wheel device scrolling, which bounds what the e2e can drive for real: `Input.synthesizeScrollGesture` with a touch source and hand-rolled `Input.dispatchTouchEvent` sequences deliver DOM events but never move a scroller (headless and headed-under-Xvfb alike); the `default` gesture source synthesizes wheel events; and compositor scrollbars ignore synthetic mouse input entirely, with a gutter visible only when `--hide-scrollbars` is removed. Keyboard is the one working non-wheel primitive, so it carries the real-input-pipeline proof, and the fling scenario replays touch's signature — per-frame decaying displacements the component never authored — through the scrollport directly. diff --git a/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.zh.md b/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.zh.md index 6aa1866e80..e38ead6c9b 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.zh.md +++ b/.agents/notes/implemented/bug-fix/2026-08-06-reader-scroll-attribution-observed-top-ledger.zh.md @@ -18,7 +18,7 @@ ChatView 的贴底跟随此前只把滚轮/触控板手势识别为读者输 ## 测试 -`packages/client/ui-conversation/tests/chat-view.client.spec.tsx` 中的单元测试直接钉住 ledger 约定:`readerScroll` 辅助函数交付一个组件从未写入过的位置,程序化交付落在 ledger 上,流收尾阶段的收缩钳制保持跟随。`apps/web/tests/chat-scroll-contract.e2e.ts` 中的两个场景扩展了[浏览器 e2e 车道](../testing/2026-07-24-web-gui-browser-e2e-lane.zh.md):在已停稳的 transcript 上做键盘翻页,以及对着按节奏推进的流式输出做一次触控式惯性快滑(momentum fling);两者在仅认滚轮的实现下均为红、在 ledger 下均为绿。 +`packages/client/ui-chat/tests/chat-view.client.spec.tsx` 中的单元测试直接钉住 ledger 约定:`readerScroll` 辅助函数交付一个组件从未写入过的位置,程序化交付落在 ledger 上,流收尾阶段的收缩钳制保持跟随。`apps/web/tests/chat-scroll-contract.e2e.ts` 中的两个场景扩展了[浏览器 e2e 车道](../testing/2026-07-24-web-gui-browser-e2e-lane.zh.md):在已停稳的 transcript 上做键盘翻页,以及对着按节奏推进的流式输出做一次触控式惯性快滑(momentum fling);两者在仅认滚轮的实现下均为红、在 ledger 下均为绿。 该车道的 Chromium 无法合成任何非滚轮的设备滚动,这限定了 e2e 能真实驱动的范围:触控来源的 `Input.synthesizeScrollGesture` 与手工构造的 `Input.dispatchTouchEvent` 序列都能交付 DOM 事件,却从不移动滚动容器(无头模式与 Xvfb 下的有头模式皆然);`default` 手势来源合成的是滚轮事件;合成器滚动条则完全无视合成的鼠标输入,且只有移除 `--hide-scrollbars` 后才能看到滚动条槽。键盘是唯一可用的非滚轮原语,因此由它承担真实输入流水线的证明;快滑场景则把触控的特征(组件从未写入过的逐帧衰减位移)直接回放进滚动容器。 diff --git a/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.i18n.yaml b/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.i18n.yaml index a20606b5ed..6cc0c25b8e 100644 --- a/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.md -2026-08-02-web-thinking-tail-scroll.md: b9aa47a01b4d8e22baddac1b03f52b3524250941 -2026-08-02-web-thinking-tail-scroll.zh.md: 41fe29f06202aef3307d756201eb4745fecca537 +2026-08-02-web-thinking-tail-scroll.md: 38c27274b2c85974044c2bb467c1519e19bfd148 +2026-08-02-web-thinking-tail-scroll.zh.md: bf637228b3a793f318fa5a5d7b0968ecd8e081d5 diff --git a/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.md b/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.md index b9aa47a01b..38c27274b2 100644 --- a/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.md +++ b/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.md @@ -28,4 +28,4 @@ The collapsed row now communicates provider cadence through content motion as we ## Testing -`packages/client/ui-conversation/tests/reasoning-row.client.spec.tsx` pins the latest-line selection, the calculated right-edge scroll position, and the settlement reset to the first line and `scrollLeft = 0`. The keyless assembled Chromium scenario in `apps/web/tests/lifecycle-chrome.e2e.ts` replays real recorded reasoning chunks at observable pacing, narrows the viewport until the summary overflows, and asserts that the live collapsed Think row reaches its actual browser scroll extent. Its settled replay golden remains unchanged, proving the historical summary contract stays stable. +`packages/client/ui-chat/tests/reasoning-row.client.spec.tsx` pins the latest-line selection, the calculated right-edge scroll position, and the settlement reset to the first line and `scrollLeft = 0`. The keyless assembled Chromium scenario in `apps/web/tests/lifecycle-chrome.e2e.ts` replays real recorded reasoning chunks at observable pacing, narrows the viewport until the summary overflows, and asserts that the live collapsed Think row reaches its actual browser scroll extent. Its settled replay golden remains unchanged, proving the historical summary contract stays stable. diff --git a/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.zh.md b/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.zh.md index 41fe29f062..bf637228b3 100644 --- a/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.zh.md +++ b/.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.zh.md @@ -28,4 +28,4 @@ Web Think 行在结算与流式 block 中都把 reasoning 首行渲染成折叠 ## 测试 -`packages/client/ui-conversation/tests/reasoning-row.client.spec.tsx` 固定最新行选择、算出的右端滚动位置,以及结算后恢复首行和 `scrollLeft = 0`。`apps/web/tests/lifecycle-chrome.e2e.ts` 中的无密钥组装态 Chromium 场景以可观察节奏回放真实录制的 reasoning chunks,把视口收窄到摘要溢出,并断言实时折叠 Think 行到达真实浏览器的滚动边界。其结算态 replay golden 保持不变,证明历史摘要约定仍然稳定。 +`packages/client/ui-chat/tests/reasoning-row.client.spec.tsx` 固定最新行选择、算出的右端滚动位置,以及结算后恢复首行和 `scrollLeft = 0`。`apps/web/tests/lifecycle-chrome.e2e.ts` 中的无密钥组装态 Chromium 场景以可观察节奏回放真实录制的 reasoning chunks,把视口收窄到摘要溢出,并断言实时折叠 Think 行到达真实浏览器的滚动边界。其结算态 replay golden 保持不变,证明历史摘要约定仍然稳定。 diff --git a/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.i18n.yaml b/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.i18n.yaml index 2ae59b9806..1ee7b9dfc3 100644 --- a/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-08-web-background-job-display.md -2026-08-08-web-background-job-display.md: 962d29e35ffe436c5ab91307d0cdfa557bbd539f -2026-08-08-web-background-job-display.zh.md: 8b391c272b8ff38028cc6819f2fbec49505d85aa +2026-08-08-web-background-job-display.md: 8da6c2fd914bf07cfa7d3545cff1e42552c69d27 +2026-08-08-web-background-job-display.zh.md: 0e05ef9d2fcd8193c661f471b5f7b9a84891f98a diff --git a/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.md b/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.md index 962d29e35f..8da6c2fd91 100644 --- a/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.md +++ b/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.md @@ -101,7 +101,7 @@ A running one-shot background subagent therefore appears both there and in the s ## Alternatives considered -**Signal frame plus RPC pull, the subagent-catalog shape.** Push a payload-free `jobs-changed` signal, debounce, then re-read authoritative state over a unary RPC. This is what the subagent catalog does, and the cost is visible in [`SessionManager`](../../../../packages/client/runtime/src/client/sessions/manager.ts): `catalogInflight` for single-flight, `catalogStale` for a trailing re-pull when a membership frame lands mid-request, `updateCatalogActivity` patching loaded rows in place *and* writing into the in-flight request so a response older than the frame gets overwritten, `parentAvailableOverride` replaying a stale `false`, and a reconnect path re-pulling every open catalog. That apparatus exists because the catalog's authority is split — durable lineage from a projection, liveness sampled at response time — and tasks have no durable half to justify inheriting it. It also fails specifically at the moment the output phase cares about: a task settles, its output stream closes immediately, but status only arrives after debounce plus round-trip, so the UI shows a running task with a dead stream for that window. +**Signal frame plus RPC pull, the subagent-catalog shape.** Push a payload-free `jobs-changed` signal, debounce, then re-read authoritative state over a unary RPC. This is what the subagent catalog does, and the cost is visible in [`SessionManager`](../../../../packages/api/session-controller/src/client/sessions/manager.ts): `catalogInflight` for single-flight, `catalogStale` for a trailing re-pull when a membership frame lands mid-request, `updateCatalogActivity` patching loaded rows in place *and* writing into the in-flight request so a response older than the frame gets overwritten, `parentAvailableOverride` replaying a stale `false`, and a reconnect path re-pulling every open catalog. That apparatus exists because the catalog's authority is split — durable lineage from a projection, liveness sampled at response time — and tasks have no durable half to justify inheriting it. It also fails specifically at the moment the output phase cares about: a task settles, its output stream closes immediately, but status only arrives after debounce plus round-trip, so the UI shows a running task with a dead stream for that window. **Popover-scoped polling with no seam change.** Cheapest to build and the only option that avoids touching `JobRegistry`. It cannot support a resident count on the trigger without a resident poll, and both later phases need a real change feed anyway, so it buys a week and spends it back. diff --git a/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.zh.md b/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.zh.md index 8b391c272b..0e05ef9d2f 100644 --- a/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.zh.md +++ b/.agents/notes/implemented/feature/2026-08-08-web-background-job-display.zh.md @@ -101,7 +101,7 @@ abstract onJobsChanged(listener: JobsChangedListener): () => void ## 备选方案 -**信号帧加 RPC 拉取,即 subagent 目录的形状。** 推一个无 payload 的 `jobs-changed` 信号,防抖后用一元 RPC 重读权威状态。subagent 目录就是这么做的,代价在 [`SessionManager`](../../../../packages/client/runtime/src/client/sessions/manager.ts) 里一览无余:`catalogInflight` 做单飞行、`catalogStale` 在成员帧落于请求中途时补一次尾拉、`updateCatalogActivity` 既就地打补丁又往在途请求里写一份好让比帧更旧的响应被覆盖、`parentAvailableOverride` 重放一个过期的 `false`,还有重连时逐一重拉每个打开的目录。这套装置之所以存在,是因为目录的权威被劈成两半——持久血缘来自投影,活跃度是响应时刻的采样——而任务没有持久的那一半,不该继承这份复杂度。它还恰好在输出那一期最在意的时刻失效:任务结算,输出流立即关闭,状态却要等防抖加一次往返才到,那段窗口里 UI 显示一个流已死的运行中任务。 +**信号帧加 RPC 拉取,即 subagent 目录的形状。** 推一个无 payload 的 `jobs-changed` 信号,防抖后用一元 RPC 重读权威状态。subagent 目录就是这么做的,代价在 [`SessionManager`](../../../../packages/api/session-controller/src/client/sessions/manager.ts) 里一览无余:`catalogInflight` 做单飞行、`catalogStale` 在成员帧落于请求中途时补一次尾拉、`updateCatalogActivity` 既就地打补丁又往在途请求里写一份好让比帧更旧的响应被覆盖、`parentAvailableOverride` 重放一个过期的 `false`,还有重连时逐一重拉每个打开的目录。这套装置之所以存在,是因为目录的权威被劈成两半——持久血缘来自投影,活跃度是响应时刻的采样——而任务没有持久的那一半,不该继承这份复杂度。它还恰好在输出那一期最在意的时刻失效:任务结算,输出流立即关闭,状态却要等防抖加一次往返才到,那段窗口里 UI 显示一个流已死的运行中任务。 **只在弹层打开时轮询,不改 seam。** 最省事,也是唯一不碰 `JobRegistry` 的选项。它无法在不常驻轮询的前提下支持触发器上的常驻计数,而后面两期反正都需要一条真正的变更订阅,所以它省下一周又还回去。 diff --git a/apps/web/tests/README.i18n.yaml b/apps/web/tests/README.i18n.yaml index 5daf55b021..3c260eab76 100644 --- a/apps/web/tests/README.i18n.yaml +++ b/apps/web/tests/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write apps/web/tests/README.md -README.md: acb0c300bafe221f6a92f0168908bebf965377b9 -README.zh.md: 029f3190bb89bede5506d3d58f5e6df229218493 +README.md: 2104d9422cfbbcbc7ffc4b12e491c0a62daa3b9d +README.zh.md: 4dfa5b2f61c757e481d9b8e012b37a5e71d75093 diff --git a/apps/web/tests/README.md b/apps/web/tests/README.md index acb0c300ba..2104d9422c 100644 --- a/apps/web/tests/README.md +++ b/apps/web/tests/README.md @@ -33,14 +33,11 @@ then surfaces as a missed selector or a stale mirrored value — a loud failure, never a silent pass. `scaffold.ts` follows this rule for the welcome-notice namespace, acknowledgement field, version, and asserted Chinese copy. -Two kinds of Client import stand. `assembled-boot.ts` drives the shell itself, so +One kind of Client import stands. `assembled-boot.ts` drives the shell itself, so it imports `AppWebEntry` from `@deepseek-ai/dsh-client-web` and the boot-manifest type from `@deepseek-ai/dsh-client-modules/client`: booting the real shell is what -that harness is for, and both packages are already in the Host graph. Separately, -the chat scenarios import `conversationContextKey` from -`@deepseek-ai/dsh-client-runtime/client` because `client/runtime` is reachable -through the unsplit `directory-picker` packages and pulls nothing further in. -That reachability is incidental, not a guarantee — if it ever leaves the graph, -mirror the helper like the rest. +that harness is for, and both packages are already in the Host graph. The chat +scenarios mirror `conversationContextKey` in `support.ts` instead of importing +its Client owner. Nothing mechanically enforces this rule; keep it in review. diff --git a/apps/web/tests/README.zh.md b/apps/web/tests/README.zh.md index 029f3190bb..4dfa5b2f61 100644 --- a/apps/web/tests/README.zh.md +++ b/apps/web/tests/README.zh.md @@ -26,12 +26,10 @@ Client face,而该 face 必须等 Host tsdown 生成 `@deepseek-ai/dsh-goal/re import 点明源模块。这样漂移会表现为选择器未命中或镜像值过期——是响亮的失败,绝不会是静默 通过。`scaffold.ts` 按此规则镜像欢迎声明的 namespace、确认字段、版本和被断言的中文文案。 -有两类 Client import 是长期成立的。`assembled-boot.ts` 驱动 shell 本身,因此它从 +有一类 Client import 是长期成立的。`assembled-boot.ts` 驱动 shell 本身,因此它从 `@deepseek-ai/dsh-client-web` import `AppWebEntry`、从 `@deepseek-ai/dsh-client-modules/client` import boot manifest 类型:启动真实 shell 正是该 -harness 的用途,且这两个包本来就在 Host 图中。另外,chat 场景从 -`@deepseek-ai/dsh-client-runtime/client` import `conversationContextKey`,因为 -`client/runtime` 经未拆分的 `directory-picker` 包可达,且不会再牵入别的东西。这种可达性是 -偶然而非保证——一旦它离开该图,就像其余情形那样镜像该 helper。 +harness 的用途,且这两个包本来就在 Host 图中。chat 场景则在 `support.ts` 中镜像 +`conversationContextKey`,而不 import 其 Client owner。 没有任何机制强制这条规则;靠 review 守住它。 diff --git a/docs/api-gateway.i18n.yaml b/docs/api-gateway.i18n.yaml index 5c6e297fc2..bdf326f8db 100644 --- a/docs/api-gateway.i18n.yaml +++ b/docs/api-gateway.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/api-gateway.md -api-gateway.md: cd3103a172d75a4ab325a2368e37af354f09051b -api-gateway.zh.md: fd7494917f209af4a16f88b87afbc47d75c6afd3 +api-gateway.md: 60b9893675ad965c3f88677eac32352acfffeb31 +api-gateway.zh.md: fc217ce3a976fd8cf045848aa331c2115c3a4d65 diff --git a/docs/api-gateway.md b/docs/api-gateway.md index cd3103a172..60b9893675 100644 --- a/docs/api-gateway.md +++ b/docs/api-gateway.md @@ -59,7 +59,7 @@ The Client uses concrete functions on ordinary objects, not a JavaScript Proxy. ```ts ignore-check import type { SessionId } from '@deepseek-ai/dsh-session/types' -import type { AgentContext } from '@deepseek-ai/dsh-client-runtime/client' +import type { AgentContext } from '@deepseek-ai/dsh-api-session-controller/client' import type { Context } from '@deepseek-ai/cordis' import type {} from '@deepseek-ai/dsh-api-remotes/client' diff --git a/docs/api-gateway.zh.md b/docs/api-gateway.zh.md index fd7494917f..fc217ce3a9 100644 --- a/docs/api-gateway.zh.md +++ b/docs/api-gateway.zh.md @@ -59,7 +59,7 @@ Client 使用普通对象上的具体函数,不使用 JavaScript Proxy。直 ```ts ignore-check import type { SessionId } from '@deepseek-ai/dsh-session/types' -import type { AgentContext } from '@deepseek-ai/dsh-client-runtime/client' +import type { AgentContext } from '@deepseek-ai/dsh-api-session-controller/client' import type { Context } from '@deepseek-ai/cordis' import type {} from '@deepseek-ai/dsh-api-remotes/client' diff --git a/docs/config-catalog.i18n.yaml b/docs/config-catalog.i18n.yaml index 6c0a1a7e11..c9e8aa0367 100644 --- a/docs/config-catalog.i18n.yaml +++ b/docs/config-catalog.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/config-catalog.md -config-catalog.md: e83d794302e7fbcf84cb5c1672821e59842e2b28 -config-catalog.zh.md: 406dbb3c77527317332b48cf513909c56a9b8a3b +config-catalog.md: 7636b1e3c7933f6d70a8e40d761b3617e746c13d +config-catalog.zh.md: 4567f09059a05d3d87f336caa55fa7db831145af diff --git a/docs/config-catalog.md b/docs/config-catalog.md index e83d794302..7636b1e3c7 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -3244,10 +3244,11 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co - `@deepseek-ai/dsh-authorization` — requires `credentials` ([`packages/credentials/authorization/src/index.ts`](../packages/credentials/authorization/src/index.ts)) - `@deepseek-ai/dsh-client-locale` ([`packages/client/locale/src/index.ts`](../packages/client/locale/src/index.ts)) - `@deepseek-ai/dsh-client-modules` — requires `webServer` · `loader` ([`packages/client/modules/src/index.ts`](../packages/client/modules/src/index.ts)) -- `@deepseek-ai/dsh-client-runtime` ([`packages/client/runtime/src/index.ts`](../packages/client/runtime/src/index.ts)) - `@deepseek-ai/dsh-client-ui-agent-preset` ([`packages/client/ui-agent-preset/src/index.ts`](../packages/client/ui-agent-preset/src/index.ts)) +- `@deepseek-ai/dsh-client-ui-approval` ([`packages/client/ui-approval/src/index.ts`](../packages/client/ui-approval/src/index.ts)) - `@deepseek-ai/dsh-client-ui-attachment` ([`packages/client/ui-attachment/src/index.ts`](../packages/client/ui-attachment/src/index.ts)) - `@deepseek-ai/dsh-client-ui-brand-official` ([`packages/client/ui-brand-official/src/index.ts`](../packages/client/ui-brand-official/src/index.ts)) +- `@deepseek-ai/dsh-client-ui-chat` ([`packages/client/ui-chat/src/index.ts`](../packages/client/ui-chat/src/index.ts)) - `@deepseek-ai/dsh-client-ui-commands` ([`packages/client/ui-commands/src/index.ts`](../packages/client/ui-commands/src/index.ts)) - `@deepseek-ai/dsh-client-ui-conversation` ([`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts)) - `@deepseek-ai/dsh-client-ui-cordis` ([`packages/extensions/ui-cordis/src/index.ts`](../packages/extensions/ui-cordis/src/index.ts)) @@ -3264,6 +3265,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co - `@deepseek-ai/dsh-client-ui-plan` ([`packages/client/ui-plan/src/index.ts`](../packages/client/ui-plan/src/index.ts)) - `@deepseek-ai/dsh-client-ui-reference` ([`packages/client/ui-reference/src/index.ts`](../packages/client/ui-reference/src/index.ts)) - `@deepseek-ai/dsh-client-ui-renderer` ([`packages/client/ui-renderer/src/index.ts`](../packages/client/ui-renderer/src/index.ts)) +- `@deepseek-ai/dsh-client-ui-session` ([`packages/client/ui-session/src/index.ts`](../packages/client/ui-session/src/index.ts)) - `@deepseek-ai/dsh-client-ui-settings` ([`packages/client/ui-settings/src/index.ts`](../packages/client/ui-settings/src/index.ts)) - `@deepseek-ai/dsh-client-ui-settings-general` ([`packages/client/ui-settings-general/src/index.ts`](../packages/client/ui-settings-general/src/index.ts)) - `@deepseek-ai/dsh-client-ui-settings-models` ([`packages/client/ui-settings-models/src/index.ts`](../packages/client/ui-settings-models/src/index.ts)) @@ -3344,6 +3346,7 @@ Imported as libraries by other packages; a `cordis.yml` cannot load them. - `@deepseek-ai/dsh-atomic-write` ([`packages/util/atomic-write/src/index.ts`](../packages/util/atomic-write/src/index.ts)) - `@deepseek-ai/dsh-base` ([`packages/bundle/base/src/index.ts`](../packages/bundle/base/src/index.ts)) - `@deepseek-ai/dsh-brand` ([`packages/util/brand/src/index.ts`](../packages/util/brand/src/index.ts)) +- `@deepseek-ai/dsh-client-store` ([`packages/client/store/src/index.ts`](../packages/client/store/src/index.ts)) - `@deepseek-ai/dsh-client-test-runtime` ([`packages/test-support/client-runtime/src/index.ts`](../packages/test-support/client-runtime/src/index.ts)) - `@deepseek-ai/dsh-client-ui-primitives` ([`packages/client/ui-primitives/src/index.ts`](../packages/client/ui-primitives/src/index.ts)) - `@deepseek-ai/dsh-client-ui-slots` ([`packages/client/ui-slots/src/index.ts`](../packages/client/ui-slots/src/index.ts)) diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index 406dbb3c77..4567f09059 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -3246,10 +3246,11 @@ export interface Config { - `@deepseek-ai/dsh-authorization` — 需要 `credentials`([`packages/credentials/authorization/src/index.ts`](../packages/credentials/authorization/src/index.ts)) - `@deepseek-ai/dsh-client-locale`([`packages/client/locale/src/index.ts`](../packages/client/locale/src/index.ts)) - `@deepseek-ai/dsh-client-modules` — 需要 `webServer` · `loader`([`packages/client/modules/src/index.ts`](../packages/client/modules/src/index.ts)) -- `@deepseek-ai/dsh-client-runtime`([`packages/client/runtime/src/index.ts`](../packages/client/runtime/src/index.ts)) - `@deepseek-ai/dsh-client-ui-agent-preset`([`packages/client/ui-agent-preset/src/index.ts`](../packages/client/ui-agent-preset/src/index.ts)) +- `@deepseek-ai/dsh-client-ui-approval`([`packages/client/ui-approval/src/index.ts`](../packages/client/ui-approval/src/index.ts)) - `@deepseek-ai/dsh-client-ui-attachment`([`packages/client/ui-attachment/src/index.ts`](../packages/client/ui-attachment/src/index.ts)) - `@deepseek-ai/dsh-client-ui-brand-official`([`packages/client/ui-brand-official/src/index.ts`](../packages/client/ui-brand-official/src/index.ts)) +- `@deepseek-ai/dsh-client-ui-chat`([`packages/client/ui-chat/src/index.ts`](../packages/client/ui-chat/src/index.ts)) - `@deepseek-ai/dsh-client-ui-commands`([`packages/client/ui-commands/src/index.ts`](../packages/client/ui-commands/src/index.ts)) - `@deepseek-ai/dsh-client-ui-conversation`([`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts)) - `@deepseek-ai/dsh-client-ui-cordis`([`packages/extensions/ui-cordis/src/index.ts`](../packages/extensions/ui-cordis/src/index.ts)) @@ -3266,6 +3267,7 @@ export interface Config { - `@deepseek-ai/dsh-client-ui-plan`([`packages/client/ui-plan/src/index.ts`](../packages/client/ui-plan/src/index.ts)) - `@deepseek-ai/dsh-client-ui-reference`([`packages/client/ui-reference/src/index.ts`](../packages/client/ui-reference/src/index.ts)) - `@deepseek-ai/dsh-client-ui-renderer`([`packages/client/ui-renderer/src/index.ts`](../packages/client/ui-renderer/src/index.ts)) +- `@deepseek-ai/dsh-client-ui-session`([`packages/client/ui-session/src/index.ts`](../packages/client/ui-session/src/index.ts)) - `@deepseek-ai/dsh-client-ui-settings`([`packages/client/ui-settings/src/index.ts`](../packages/client/ui-settings/src/index.ts)) - `@deepseek-ai/dsh-client-ui-settings-general`([`packages/client/ui-settings-general/src/index.ts`](../packages/client/ui-settings-general/src/index.ts)) - `@deepseek-ai/dsh-client-ui-settings-models`([`packages/client/ui-settings-models/src/index.ts`](../packages/client/ui-settings-models/src/index.ts)) @@ -3345,6 +3347,7 @@ export interface Config { - `@deepseek-ai/dsh-atomic-write`([`packages/util/atomic-write/src/index.ts`](../packages/util/atomic-write/src/index.ts)) - `@deepseek-ai/dsh-base`([`packages/bundle/base/src/index.ts`](../packages/bundle/base/src/index.ts)) - `@deepseek-ai/dsh-brand`([`packages/util/brand/src/index.ts`](../packages/util/brand/src/index.ts)) +- `@deepseek-ai/dsh-client-store`([`packages/client/store/src/index.ts`](../packages/client/store/src/index.ts)) - `@deepseek-ai/dsh-client-test-runtime`([`packages/test-support/client-runtime/src/index.ts`](../packages/test-support/client-runtime/src/index.ts)) - `@deepseek-ai/dsh-client-ui-primitives`([`packages/client/ui-primitives/src/index.ts`](../packages/client/ui-primitives/src/index.ts)) - `@deepseek-ai/dsh-client-ui-slots`([`packages/client/ui-slots/src/index.ts`](../packages/client/ui-slots/src/index.ts)) diff --git a/docs/cookbook/adding-a-conversation-node.i18n.yaml b/docs/cookbook/adding-a-conversation-node.i18n.yaml index e06b41a788..e51fcf65c5 100644 --- a/docs/cookbook/adding-a-conversation-node.i18n.yaml +++ b/docs/cookbook/adding-a-conversation-node.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/adding-a-conversation-node.md -adding-a-conversation-node.md: c1965dc8a3081eebb8c1026ac53d2f7b8964edb7 -adding-a-conversation-node.zh.md: 2986f695b351cd17637d7ff99112c38042950692 +adding-a-conversation-node.md: daa86f90473cb7023a21e1cfa25339fcd79aa558 +adding-a-conversation-node.zh.md: 8c6360a99acb49aebadf9a28f440853b18514b9a diff --git a/docs/cookbook/adding-a-conversation-node.md b/docs/cookbook/adding-a-conversation-node.md index c1965dc8a3..daa86f9047 100644 --- a/docs/cookbook/adding-a-conversation-node.md +++ b/docs/cookbook/adding-a-conversation-node.md @@ -28,12 +28,13 @@ The example keeps the producer declarations and client contribution in one block ```ts ignore-check import { createElement } from 'react' +import type { Context as ClientContext } from '@deepseek-ai/cordis' import type { Branded } from '@deepseek-ai/dsh-brand' import type { - ClientContext, ConversationLocation, ConversationNodeContext, + ConversationLocation, ConversationNodeContext, ConversationNodeDefinition, -} from '@deepseek-ai/dsh-client-runtime/client' -import type { ChatNodeViewProps } from '@deepseek-ai/dsh-client-ui-conversation/client' +} from '@deepseek-ai/dsh-client-ui-conversation/client' +import type { ChatNodeViewProps } from '@deepseek-ai/dsh-client-ui-chat/client' type ReviewId = Branded<'ReviewId'> @@ -94,7 +95,7 @@ declare module '@deepseek-ai/dsh-client-ui-conversation/client' { } } -declare module '@deepseek-ai/dsh-client-runtime/client' { +declare module '@deepseek-ai/dsh-client-ui-conversation/client' { interface ConversationStepDataMap { 'review-job': ReviewChatData } @@ -182,10 +183,10 @@ function ReviewNodeView({ node }: ChatNodeViewProps<'review-job'>) { return createElement('p', null, text) } -export const inject = ['conversationEvents', 'slots'] +export const inject = ['uiConversation', 'slots'] export function apply(ctx: ClientContext): void { - ctx.conversationEvents.register(reviewDefinition) + ctx.uiConversation.events.register(reviewDefinition) ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({ name: 'conversation.chat.node', key: 'review-job', @@ -230,4 +231,4 @@ Add focused tests that establish these outcomes: 5. Repeated visible deltas preserve `context.key` and publish at most once per animation frame when requested. 6. The keyed renderer consumes `node.data` and constrained Location hooks only; it does not scan the Session event window, Contexts, or Chat Nodes. -Use [`packages/client/ui-conversation/src/client/conversation-nodes/assistant.ts`](../../packages/client/ui-conversation/src/client/conversation-nodes/assistant.ts) for streaming and interruption, [`inbox.ts`](../../packages/client/ui-conversation/src/client/conversation-nodes/inbox.ts) plus [`message.ts`](../../packages/client/ui-conversation/src/client/conversation-nodes/message.ts) for predecessor queries, and [`packages/client/ui-deliverables`](../../packages/client/ui-deliverables) for a Definition that publishes Turn data without creating its own Node. +Use [`packages/client/ui-chat/src/client/conversation-nodes/assistant.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/assistant.ts) for streaming and interruption, [`inbox.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/inbox.ts) plus [`message.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/message.ts) for predecessor queries, and [`packages/client/ui-deliverables`](../../packages/client/ui-deliverables) for a Definition that publishes Turn data without creating its own Node. diff --git a/docs/cookbook/adding-a-conversation-node.zh.md b/docs/cookbook/adding-a-conversation-node.zh.md index 2986f695b3..8c6360a99a 100644 --- a/docs/cookbook/adding-a-conversation-node.zh.md +++ b/docs/cookbook/adding-a-conversation-node.zh.md @@ -28,12 +28,13 @@ ```ts ignore-check import { createElement } from 'react' +import type { Context as ClientContext } from '@deepseek-ai/cordis' import type { Branded } from '@deepseek-ai/dsh-brand' import type { - ClientContext, ConversationLocation, ConversationNodeContext, + ConversationLocation, ConversationNodeContext, ConversationNodeDefinition, -} from '@deepseek-ai/dsh-client-runtime/client' -import type { ChatNodeViewProps } from '@deepseek-ai/dsh-client-ui-conversation/client' +} from '@deepseek-ai/dsh-client-ui-conversation/client' +import type { ChatNodeViewProps } from '@deepseek-ai/dsh-client-ui-chat/client' type ReviewId = Branded<'ReviewId'> @@ -94,7 +95,7 @@ declare module '@deepseek-ai/dsh-client-ui-conversation/client' { } } -declare module '@deepseek-ai/dsh-client-runtime/client' { +declare module '@deepseek-ai/dsh-client-ui-conversation/client' { interface ConversationStepDataMap { 'review-job': ReviewChatData } @@ -182,10 +183,10 @@ function ReviewNodeView({ node }: ChatNodeViewProps<'review-job'>) { return createElement('p', null, text) } -export const inject = ['conversationEvents', 'slots'] +export const inject = ['uiConversation', 'slots'] export function apply(ctx: ClientContext): void { - ctx.conversationEvents.register(reviewDefinition) + ctx.uiConversation.events.register(reviewDefinition) ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({ name: 'conversation.chat.node', key: 'review-job', @@ -230,4 +231,4 @@ Assembler 会记录这项依赖。如果后续 older prepend 带来了更近的 5. 重复的可见 delta 保持 `context.key`,并在请求 `animation-frame` 时每帧最多发布一次。 6. keyed renderer 只消费 `node.data` 与受限 Location hook,不扫描 Session 事件窗口、Context 或 Chat Node。 -流式与中断处理可参考 [`packages/client/ui-conversation/src/client/conversation-nodes/assistant.ts`](../../packages/client/ui-conversation/src/client/conversation-nodes/assistant.ts),前序查询可参考 [`inbox.ts`](../../packages/client/ui-conversation/src/client/conversation-nodes/inbox.ts) 与 [`message.ts`](../../packages/client/ui-conversation/src/client/conversation-nodes/message.ts),只发布 Turn data 而不创建自有 Node 的例子见 [`packages/client/ui-deliverables`](../../packages/client/ui-deliverables)。 +流式与中断处理可参考 [`packages/client/ui-chat/src/client/conversation-nodes/assistant.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/assistant.ts),前序查询可参考 [`inbox.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/inbox.ts) 与 [`message.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/message.ts),只发布 Turn data 而不创建自有 Node 的例子见 [`packages/client/ui-deliverables`](../../packages/client/ui-deliverables)。 diff --git a/docs/cookbook/adding-a-settings-card.i18n.yaml b/docs/cookbook/adding-a-settings-card.i18n.yaml index 1ebdaf5789..d4411c819b 100644 --- a/docs/cookbook/adding-a-settings-card.i18n.yaml +++ b/docs/cookbook/adding-a-settings-card.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/adding-a-settings-card.md -adding-a-settings-card.md: 8fc0f63dfaa932adba25caf1504cfd8cf28c7460 -adding-a-settings-card.zh.md: 956af879b1c1fcbb018b9420f31b2ae11244b415 +adding-a-settings-card.md: 6035cc3c586cd319c89fad95c12610d35742708c +adding-a-settings-card.zh.md: 79a4372c24f53e3d31b165d9300af591987da1f6 diff --git a/docs/cookbook/adding-a-settings-card.md b/docs/cookbook/adding-a-settings-card.md index 8fc0f63dfa..6035cc3c58 100644 --- a/docs/cookbook/adding-a-settings-card.md +++ b/docs/cookbook/adding-a-settings-card.md @@ -48,7 +48,7 @@ export function apply(ctx: Context, config: Config) { The card registers into `settings.plugin.item` under its namespace and owns everything inside it — chrome, controls, and copy. It reads and writes through `ctx.settingsScope`, which fences each write with the revision it read: ```ts ignore-check -import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client' +import type { Context as ClientContext } from '@deepseek-ai/cordis' // Type-only: the keyed slot's declaration. Cross-plugin collaboration goes // through cordis services; a value import fails the client bundle-purity gate. import type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client' diff --git a/docs/cookbook/adding-a-settings-card.zh.md b/docs/cookbook/adding-a-settings-card.zh.md index 956af879b1..79a4372c24 100644 --- a/docs/cookbook/adding-a-settings-card.zh.md +++ b/docs/cookbook/adding-a-settings-card.zh.md @@ -48,7 +48,7 @@ export function apply(ctx: Context, config: Config) { 卡片以自己的命名空间为键注册进 `settings.plugin.item`,并拥有其中的一切——外观、控件与文案。它通过 `ctx.settingsScope` 读写,后者用读取时的 revision 为每次写入设栅: ```ts ignore-check -import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client' +import type { Context as ClientContext } from '@deepseek-ai/cordis' // Type-only: the keyed slot's declaration. Cross-plugin collaboration goes // through cordis services; a value import fails the client bundle-purity gate. import type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client' diff --git a/docs/module-graph.i18n.yaml b/docs/module-graph.i18n.yaml index 0dfc790546..6280fbe004 100644 --- a/docs/module-graph.i18n.yaml +++ b/docs/module-graph.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/module-graph.md -module-graph.md: ab49462eac55dec981c4a5951a7eb0bfb28b08f0 -module-graph.zh.md: cf0f12f71fd59370059c5a875dfcaa6c7ae4b4ba +module-graph.md: 9bbbb81dc1e5b4aa65619702d380eeb126cfdea3 +module-graph.zh.md: 86298ce1ed68aa550f5b0b489ee6dccd49771638 diff --git a/docs/module-graph.md b/docs/module-graph.md index ab49462eac..9bbbb81dc1 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -129,10 +129,12 @@ flowchart TD pkg_client_hmr["client-hmr"] pkg_client_locale["client-locale"] pkg_client_modules["client-modules"] - pkg_client_runtime["client-runtime"] + pkg_client_store["client-store"] pkg_client_ui_agent_preset["client-ui-agent-preset"] + pkg_client_ui_approval["client-ui-approval"] pkg_client_ui_attachment["client-ui-attachment"] pkg_client_ui_brand_official["client-ui-brand-official"] + pkg_client_ui_chat["client-ui-chat"] pkg_client_ui_commands["client-ui-commands"] pkg_client_ui_conversation["client-ui-conversation"] pkg_client_ui_deliverables["client-ui-deliverables"] @@ -149,6 +151,7 @@ flowchart TD pkg_client_ui_primitives["client-ui-primitives"] pkg_client_ui_reference["client-ui-reference"] pkg_client_ui_renderer["client-ui-renderer"] + pkg_client_ui_session["client-ui-session"] pkg_client_ui_settings["client-ui-settings"] pkg_client_ui_settings_general["client-ui-settings-general"] pkg_client_ui_settings_models["client-ui-settings-models"] @@ -362,7 +365,9 @@ flowchart TD pkg_acp_app --> pkg_invariants pkg_base --> pkg_invariants pkg_sdk_app --> pkg_invariants + pkg_client_store --> pkg_invariants pkg_client_ui_primitives --> pkg_invariants + pkg_client_ui_renderer --> pkg_invariants pkg_client_ui_slots --> pkg_invariants pkg_client_web --> pkg_invariants pkg_code_runtime --> pkg_invariants @@ -1236,6 +1241,7 @@ flowchart TD pkg_api_session_controller --> pkg_api_gateway pkg_api_session_controller --> pkg_attachment pkg_api_session_controller --> pkg_brand + pkg_api_session_controller --> pkg_client_connection pkg_api_session_controller --> pkg_invariants pkg_api_session_controller --> pkg_jobs pkg_api_session_controller --> pkg_llm @@ -1252,6 +1258,7 @@ flowchart TD pkg_api_session_controller --> pkg_typert_registry pkg_api_session_controller --> pkg_workspace pkg_api_workspace_controller --> pkg_api_gateway + pkg_api_workspace_controller --> pkg_client_connection pkg_api_workspace_controller --> pkg_invariants pkg_api_workspace_controller --> pkg_session pkg_api_workspace_controller --> pkg_storage_domain @@ -1275,272 +1282,352 @@ flowchart TD pkg_api_remotes --> pkg_settings pkg_api_remotes --> pkg_user_approval pkg_api_remotes --> pkg_user_questions - pkg_client_runtime --> pkg_agent - pkg_client_runtime --> pkg_api_gateway - pkg_client_runtime --> pkg_api_remotes - pkg_client_runtime --> pkg_api_session_controller - pkg_client_runtime --> pkg_api_workspace_controller - pkg_client_runtime --> pkg_attachment - pkg_client_runtime --> pkg_client_connection - pkg_client_runtime --> pkg_commands - pkg_client_runtime --> pkg_host_apiproxy - pkg_client_runtime --> pkg_invariants - pkg_client_runtime --> pkg_llm - pkg_client_runtime --> pkg_llm_retry - pkg_client_runtime --> pkg_session - pkg_client_runtime --> pkg_session_projection - pkg_client_runtime --> pkg_session_title - pkg_client_runtime --> pkg_tool_todo - pkg_client_runtime --> pkg_tools - pkg_client_runtime --> pkg_typert_protocol - pkg_client_runtime --> pkg_typert_registry - pkg_client_runtime --> pkg_util_crypto - pkg_client_ui_renderer --> pkg_client_runtime - pkg_client_ui_renderer --> pkg_invariants + pkg_client_ui_session --> pkg_api_session_controller + pkg_client_ui_session --> pkg_client_ui_renderer + pkg_client_ui_session --> pkg_invariants + pkg_client_ui_session --> pkg_session pkg_client_ui_settings --> pkg_api_remotes pkg_client_ui_settings --> pkg_client_connection - pkg_client_ui_settings --> pkg_client_runtime pkg_client_ui_settings --> pkg_invariants pkg_client_ui_settings --> pkg_settings pkg_client_locale --> pkg_api_remotes pkg_client_locale --> pkg_client_connection - pkg_client_locale --> pkg_client_runtime + pkg_client_locale --> pkg_client_ui_renderer pkg_client_locale --> pkg_client_ui_settings pkg_client_locale --> pkg_invariants pkg_client_locale --> pkg_settings - pkg_client_test_runtime --> pkg_client_runtime - pkg_client_test_runtime --> pkg_client_ui_renderer - pkg_client_test_runtime --> pkg_client_ui_slots - pkg_client_test_runtime --> pkg_host_apiproxy - pkg_client_test_runtime --> pkg_invariants - pkg_client_ui_input_trigger --> pkg_client_locale - pkg_client_ui_input_trigger --> pkg_client_runtime - pkg_client_ui_input_trigger --> pkg_file_reference - pkg_client_ui_input_trigger --> pkg_invariants pkg_client_ui_settings_models --> pkg_api_remotes pkg_client_ui_settings_models --> pkg_client_connection pkg_client_ui_settings_models --> pkg_client_locale - pkg_client_ui_settings_models --> pkg_client_runtime + pkg_client_ui_settings_models --> pkg_client_ui_renderer pkg_client_ui_settings_models --> pkg_client_ui_settings pkg_client_ui_settings_models --> pkg_invariants pkg_client_ui_settings_plugin_inventory --> pkg_api_remotes pkg_client_ui_settings_plugin_inventory --> pkg_client_locale - pkg_client_ui_settings_plugin_inventory --> pkg_client_runtime + pkg_client_ui_settings_plugin_inventory --> pkg_client_ui_renderer pkg_client_ui_settings_plugin_inventory --> pkg_client_ui_settings pkg_client_ui_settings_plugin_inventory --> pkg_invariants pkg_client_ui_settings_plugins --> pkg_api_remotes pkg_client_ui_settings_plugins --> pkg_client_connection pkg_client_ui_settings_plugins --> pkg_client_locale - pkg_client_ui_settings_plugins --> pkg_client_runtime + pkg_client_ui_settings_plugins --> pkg_client_ui_renderer pkg_client_ui_settings_plugins --> pkg_client_ui_settings pkg_client_ui_settings_plugins --> pkg_invariants pkg_client_ui_theme --> pkg_api_remotes pkg_client_ui_theme --> pkg_client_connection pkg_client_ui_theme --> pkg_client_locale - pkg_client_ui_theme --> pkg_client_runtime + pkg_client_ui_theme --> pkg_client_ui_renderer pkg_client_ui_theme --> pkg_client_ui_settings pkg_client_ui_theme --> pkg_host_webserver pkg_client_ui_theme --> pkg_invariants pkg_client_ui_theme --> pkg_settings - pkg_client_ui_layout --> pkg_client_runtime + pkg_client_ui_layout --> pkg_client_ui_renderer + pkg_client_ui_layout --> pkg_client_ui_session pkg_client_ui_layout --> pkg_client_ui_theme pkg_client_ui_layout --> pkg_invariants + pkg_cordis_client_runner --> pkg_api_remotes + pkg_cordis_client_runner --> pkg_client_connection + pkg_cordis_client_runner --> pkg_client_modules + pkg_cordis_client_runner --> pkg_client_ui_renderer + pkg_cordis_client_runner --> pkg_client_ui_theme + pkg_cordis_client_runner --> pkg_invariants + pkg_client_ui_conversation --> pkg_api_remotes + pkg_client_ui_conversation --> pkg_api_session_controller + pkg_client_ui_conversation --> pkg_api_workspace_controller + pkg_client_ui_conversation --> pkg_attachment + pkg_client_ui_conversation --> pkg_brand + pkg_client_ui_conversation --> pkg_client_locale + pkg_client_ui_conversation --> pkg_client_ui_layout + pkg_client_ui_conversation --> pkg_client_ui_renderer + pkg_client_ui_conversation --> pkg_client_ui_session + pkg_client_ui_conversation --> pkg_client_ui_settings + pkg_client_ui_conversation --> pkg_client_ui_workspace + pkg_client_ui_conversation --> pkg_commands + pkg_client_ui_conversation --> pkg_goal + pkg_client_ui_conversation --> pkg_invariants + pkg_client_ui_conversation --> pkg_llm + pkg_client_ui_conversation --> pkg_llm_retry + pkg_client_ui_conversation --> pkg_permission_presets + pkg_client_ui_conversation --> pkg_plan_mode + pkg_client_ui_conversation --> pkg_session + pkg_client_ui_conversation --> pkg_settings + pkg_client_ui_conversation --> pkg_token_meter + pkg_client_ui_conversation --> pkg_tool_todo + pkg_client_ui_conversation --> pkg_util_crypto + pkg_client_ui_conversation --> pkg_workspace + pkg_client_ui_sidebar --> pkg_api_workspace_controller + pkg_client_ui_sidebar --> pkg_client_locale + pkg_client_ui_sidebar --> pkg_client_ui_layout + pkg_client_ui_sidebar --> pkg_client_ui_renderer + pkg_client_ui_sidebar --> pkg_client_ui_session + pkg_client_ui_sidebar --> pkg_client_ui_workspace + pkg_client_ui_sidebar --> pkg_invariants + pkg_client_ui_workspace --> pkg_api_session_controller + pkg_client_ui_workspace --> pkg_api_workspace_controller + pkg_client_ui_workspace --> pkg_client_connection + pkg_client_ui_workspace --> pkg_client_locale + pkg_client_ui_workspace --> pkg_client_ui_conversation + pkg_client_ui_workspace --> pkg_client_ui_renderer + pkg_client_ui_workspace --> pkg_client_ui_session + pkg_client_ui_workspace --> pkg_client_ui_sidebar + pkg_client_ui_workspace --> pkg_invariants + pkg_client_ui_workspace --> pkg_session + pkg_client_ui_agent_preset --> pkg_api_remotes + pkg_client_ui_agent_preset --> pkg_api_session_controller + pkg_client_ui_agent_preset --> pkg_client_connection + pkg_client_ui_agent_preset --> pkg_client_locale + pkg_client_ui_agent_preset --> pkg_client_ui_conversation + pkg_client_ui_agent_preset --> pkg_client_ui_renderer + pkg_client_ui_agent_preset --> pkg_client_ui_session + pkg_client_ui_agent_preset --> pkg_client_ui_settings + pkg_client_ui_agent_preset --> pkg_client_ui_workspace + pkg_client_ui_agent_preset --> pkg_invariants + pkg_client_ui_agent_preset --> pkg_session + pkg_client_ui_approval --> pkg_api_remotes + pkg_client_ui_approval --> pkg_api_session_controller + pkg_client_ui_approval --> pkg_client_locale + pkg_client_ui_approval --> pkg_client_ui_conversation + pkg_client_ui_approval --> pkg_client_ui_renderer + pkg_client_ui_approval --> pkg_client_ui_session + pkg_client_ui_approval --> pkg_invariants + pkg_client_ui_approval --> pkg_llm + pkg_client_ui_approval --> pkg_session + pkg_client_ui_approval --> pkg_typert_protocol + pkg_client_ui_brand_official --> pkg_client_ui_conversation + pkg_client_ui_brand_official --> pkg_client_ui_renderer + pkg_client_ui_brand_official --> pkg_client_ui_sidebar + pkg_client_ui_brand_official --> pkg_invariants + pkg_client_ui_directory_picker_browse --> pkg_client_connection + pkg_client_ui_directory_picker_browse --> pkg_client_locale + pkg_client_ui_directory_picker_browse --> pkg_client_ui_renderer + pkg_client_ui_directory_picker_browse --> pkg_client_ui_workspace + pkg_client_ui_directory_picker_browse --> pkg_invariants + pkg_client_ui_directory_picker_native --> pkg_client_ui_renderer + pkg_client_ui_directory_picker_native --> pkg_client_ui_workspace + pkg_client_ui_directory_picker_native --> pkg_invariants + pkg_client_ui_input_trigger --> pkg_api_session_controller + pkg_client_ui_input_trigger --> pkg_client_locale + pkg_client_ui_input_trigger --> pkg_client_ui_conversation + pkg_client_ui_input_trigger --> pkg_client_ui_renderer + pkg_client_ui_input_trigger --> pkg_client_ui_session + pkg_client_ui_input_trigger --> pkg_file_reference + pkg_client_ui_input_trigger --> pkg_invariants + pkg_client_ui_input_trigger --> pkg_session + pkg_client_ui_jobs --> pkg_api_session_controller + pkg_client_ui_jobs --> pkg_client_locale + pkg_client_ui_jobs --> pkg_client_ui_conversation + pkg_client_ui_jobs --> pkg_client_ui_renderer + pkg_client_ui_jobs --> pkg_client_ui_session + pkg_client_ui_jobs --> pkg_invariants + pkg_client_ui_plan --> pkg_api_remotes + pkg_client_ui_plan --> pkg_client_locale + pkg_client_ui_plan --> pkg_client_ui_conversation + pkg_client_ui_plan --> pkg_client_ui_renderer + pkg_client_ui_plan --> pkg_client_ui_session + pkg_client_ui_plan --> pkg_invariants + pkg_client_ui_plan --> pkg_plan_mode + pkg_client_ui_plan --> pkg_session + pkg_client_ui_settings_general --> pkg_api_remotes + pkg_client_ui_settings_general --> pkg_client_connection + pkg_client_ui_settings_general --> pkg_client_locale + pkg_client_ui_settings_general --> pkg_client_ui_renderer + pkg_client_ui_settings_general --> pkg_client_ui_session + pkg_client_ui_settings_general --> pkg_client_ui_settings + pkg_client_ui_settings_general --> pkg_client_ui_sidebar + pkg_client_ui_settings_general --> pkg_invariants + pkg_client_ui_settings_general --> pkg_settings + pkg_client_ui_trajectory --> pkg_agent + pkg_client_ui_trajectory --> pkg_api_session_controller + pkg_client_ui_trajectory --> pkg_client_locale + pkg_client_ui_trajectory --> pkg_client_ui_conversation + pkg_client_ui_trajectory --> pkg_client_ui_renderer + pkg_client_ui_trajectory --> pkg_client_ui_session + pkg_client_ui_trajectory --> pkg_compaction + pkg_client_ui_trajectory --> pkg_invariants + pkg_client_ui_trajectory --> pkg_session + pkg_client_ui_trajectory --> pkg_tools + pkg_client_ui_user_questions --> pkg_api_remotes + pkg_client_ui_user_questions --> pkg_api_session_controller + pkg_client_ui_user_questions --> pkg_client_locale + pkg_client_ui_user_questions --> pkg_client_ui_conversation + pkg_client_ui_user_questions --> pkg_client_ui_renderer + pkg_client_ui_user_questions --> pkg_client_ui_session + pkg_client_ui_user_questions --> pkg_invariants + pkg_client_ui_user_questions --> pkg_session + pkg_client_ui_user_questions --> pkg_typert_protocol + pkg_client_ui_user_questions --> pkg_user_questions + pkg_client_ui_chat --> pkg_agent + pkg_client_ui_chat --> pkg_api_remotes + pkg_client_ui_chat --> pkg_api_session_controller + pkg_client_ui_chat --> pkg_api_workspace_controller + pkg_client_ui_chat --> pkg_attachment + pkg_client_ui_chat --> pkg_client_locale + pkg_client_ui_chat --> pkg_client_ui_approval + pkg_client_ui_chat --> pkg_client_ui_conversation + pkg_client_ui_chat --> pkg_client_ui_layout + pkg_client_ui_chat --> pkg_client_ui_renderer + pkg_client_ui_chat --> pkg_client_ui_session + pkg_client_ui_chat --> pkg_client_ui_workspace + pkg_client_ui_chat --> pkg_commands + pkg_client_ui_chat --> pkg_compaction + pkg_client_ui_chat --> pkg_invariants + pkg_client_ui_chat --> pkg_llm + pkg_client_ui_chat --> pkg_llm_retry + pkg_client_ui_chat --> pkg_session + pkg_client_ui_chat --> pkg_session_stats + pkg_client_ui_chat --> pkg_token_meter + pkg_client_ui_chat --> pkg_tools + pkg_client_ui_commands --> pkg_api_remotes + pkg_client_ui_commands --> pkg_api_session_controller + pkg_client_ui_commands --> pkg_client_locale + pkg_client_ui_commands --> pkg_client_ui_conversation + pkg_client_ui_commands --> pkg_client_ui_input_trigger + pkg_client_ui_commands --> pkg_client_ui_renderer + pkg_client_ui_commands --> pkg_client_ui_session + pkg_client_ui_commands --> pkg_commands + pkg_client_ui_commands --> pkg_invariants + pkg_client_ui_commands --> pkg_session pkg_client_ui_reference --> pkg_api_remotes pkg_client_ui_reference --> pkg_client_locale - pkg_client_ui_reference --> pkg_client_runtime pkg_client_ui_reference --> pkg_client_ui_input_trigger pkg_client_ui_reference --> pkg_file_reference pkg_client_ui_reference --> pkg_invariants pkg_client_ui_reference --> pkg_session_reference pkg_client_ui_reference --> pkg_typert_protocol - pkg_cordis_client_runner --> pkg_api_remotes - pkg_cordis_client_runner --> pkg_client_connection - pkg_cordis_client_runner --> pkg_client_modules - pkg_cordis_client_runner --> pkg_client_runtime - pkg_cordis_client_runner --> pkg_client_ui_theme - pkg_cordis_client_runner --> pkg_invariants - pkg_client_ui_conversation --> pkg_agent - pkg_client_ui_conversation --> pkg_api_remotes - pkg_client_ui_conversation --> pkg_attachment - pkg_client_ui_conversation --> pkg_brand - pkg_client_ui_conversation --> pkg_client_connection - pkg_client_ui_conversation --> pkg_client_locale - pkg_client_ui_conversation --> pkg_client_runtime - pkg_client_ui_conversation --> pkg_client_ui_input_trigger - pkg_client_ui_conversation --> pkg_client_ui_layout - pkg_client_ui_conversation --> pkg_client_ui_settings - pkg_client_ui_conversation --> pkg_commands - pkg_client_ui_conversation --> pkg_compaction - pkg_client_ui_conversation --> pkg_goal - pkg_client_ui_conversation --> pkg_invariants - pkg_client_ui_conversation --> pkg_llm_retry - pkg_client_ui_conversation --> pkg_permission_presets - pkg_client_ui_conversation --> pkg_plan_mode - pkg_client_ui_conversation --> pkg_session_stats - pkg_client_ui_conversation --> pkg_settings - pkg_client_ui_conversation --> pkg_token_meter - pkg_client_ui_conversation --> pkg_tool_todo - pkg_client_ui_conversation --> pkg_tools - pkg_client_ui_conversation --> pkg_util_crypto - pkg_client_ui_sidebar --> pkg_client_locale - pkg_client_ui_sidebar --> pkg_client_runtime - pkg_client_ui_sidebar --> pkg_client_ui_layout - pkg_client_ui_sidebar --> pkg_invariants - pkg_client_ui_agent_preset --> pkg_api_remotes - pkg_client_ui_agent_preset --> pkg_client_connection - pkg_client_ui_agent_preset --> pkg_client_locale - pkg_client_ui_agent_preset --> pkg_client_runtime - pkg_client_ui_agent_preset --> pkg_client_ui_conversation - pkg_client_ui_agent_preset --> pkg_client_ui_settings - pkg_client_ui_agent_preset --> pkg_invariants - pkg_client_ui_attachment --> pkg_attachment - pkg_client_ui_attachment --> pkg_client_runtime - pkg_client_ui_attachment --> pkg_client_ui_conversation - pkg_client_ui_attachment --> pkg_invariants - pkg_client_ui_brand_official --> pkg_client_runtime - pkg_client_ui_brand_official --> pkg_client_ui_conversation - pkg_client_ui_brand_official --> pkg_client_ui_sidebar - pkg_client_ui_brand_official --> pkg_invariants - pkg_client_ui_commands --> pkg_api_remotes - pkg_client_ui_commands --> pkg_client_locale - pkg_client_ui_commands --> pkg_client_runtime - pkg_client_ui_commands --> pkg_client_ui_conversation - pkg_client_ui_commands --> pkg_client_ui_input_trigger - pkg_client_ui_commands --> pkg_commands - pkg_client_ui_commands --> pkg_invariants - pkg_client_ui_deliverables --> pkg_client_connection - pkg_client_ui_deliverables --> pkg_client_locale - pkg_client_ui_deliverables --> pkg_client_runtime - pkg_client_ui_deliverables --> pkg_client_ui_conversation - pkg_client_ui_deliverables --> pkg_invariants - pkg_client_ui_deliverables --> pkg_system_prompt - pkg_client_ui_goal --> pkg_api_remotes - pkg_client_ui_goal --> pkg_client_locale - pkg_client_ui_goal --> pkg_client_runtime - pkg_client_ui_goal --> pkg_client_ui_conversation - pkg_client_ui_goal --> pkg_commands - pkg_client_ui_goal --> pkg_goal - pkg_client_ui_goal --> pkg_invariants - pkg_client_ui_goal --> pkg_session - pkg_client_ui_goal --> pkg_typert_protocol - pkg_client_ui_jobs --> pkg_client_locale - pkg_client_ui_jobs --> pkg_client_runtime - pkg_client_ui_jobs --> pkg_client_ui_conversation - pkg_client_ui_jobs --> pkg_invariants - pkg_client_ui_message_feedback --> pkg_api_remotes - pkg_client_ui_message_feedback --> pkg_client_connection - pkg_client_ui_message_feedback --> pkg_client_locale - pkg_client_ui_message_feedback --> pkg_client_runtime - pkg_client_ui_message_feedback --> pkg_client_ui_conversation - pkg_client_ui_message_feedback --> pkg_invariants - pkg_client_ui_message_feedback --> pkg_message_feedback - pkg_client_ui_message_feedback --> pkg_typert_protocol - pkg_client_ui_plan --> pkg_api_remotes - pkg_client_ui_plan --> pkg_client_locale - pkg_client_ui_plan --> pkg_client_runtime - pkg_client_ui_plan --> pkg_client_ui_conversation - pkg_client_ui_plan --> pkg_invariants - pkg_client_ui_plan --> pkg_plan_mode - pkg_client_ui_settings_general --> pkg_api_remotes - pkg_client_ui_settings_general --> pkg_client_connection - pkg_client_ui_settings_general --> pkg_client_locale - pkg_client_ui_settings_general --> pkg_client_runtime - pkg_client_ui_settings_general --> pkg_client_ui_settings - pkg_client_ui_settings_general --> pkg_client_ui_sidebar - pkg_client_ui_settings_general --> pkg_invariants - pkg_client_ui_settings_general --> pkg_settings + pkg_client_ui_subagent --> pkg_api_session_controller + pkg_client_ui_subagent --> pkg_client_connection pkg_client_ui_subagent --> pkg_client_locale - pkg_client_ui_subagent --> pkg_client_runtime pkg_client_ui_subagent --> pkg_client_ui_conversation pkg_client_ui_subagent --> pkg_client_ui_input_trigger + pkg_client_ui_subagent --> pkg_client_ui_renderer + pkg_client_ui_subagent --> pkg_client_ui_session pkg_client_ui_subagent --> pkg_invariants + pkg_client_ui_subagent --> pkg_session pkg_client_ui_subagent --> pkg_subagent pkg_client_ui_subagent --> pkg_token_meter - pkg_client_ui_tool --> pkg_api_remotes - pkg_client_ui_tool --> pkg_client_connection - pkg_client_ui_tool --> pkg_client_locale - pkg_client_ui_tool --> pkg_client_runtime - pkg_client_ui_tool --> pkg_client_ui_conversation - pkg_client_ui_tool --> pkg_invariants - pkg_client_ui_trajectory --> pkg_agent - pkg_client_ui_trajectory --> pkg_client_locale - pkg_client_ui_trajectory --> pkg_client_runtime - pkg_client_ui_trajectory --> pkg_client_ui_conversation - pkg_client_ui_trajectory --> pkg_compaction - pkg_client_ui_trajectory --> pkg_invariants - pkg_client_ui_trajectory --> pkg_tools - pkg_client_ui_user_questions --> pkg_api_remotes - pkg_client_ui_user_questions --> pkg_client_locale - pkg_client_ui_user_questions --> pkg_client_runtime - pkg_client_ui_user_questions --> pkg_client_ui_conversation - pkg_client_ui_user_questions --> pkg_invariants - pkg_client_ui_workflow_run --> pkg_client_locale - pkg_client_ui_workflow_run --> pkg_client_runtime - pkg_client_ui_workflow_run --> pkg_client_ui_conversation - pkg_client_ui_workflow_run --> pkg_invariants - pkg_client_ui_workflow_run --> pkg_session - pkg_client_ui_workflow_run --> pkg_tool_workflow - pkg_client_ui_workflow_run --> pkg_workflow - pkg_client_ui_workspace --> pkg_client_connection - pkg_client_ui_workspace --> pkg_client_locale - pkg_client_ui_workspace --> pkg_client_runtime - pkg_client_ui_workspace --> pkg_client_ui_conversation - pkg_client_ui_workspace --> pkg_client_ui_sidebar - pkg_client_ui_workspace --> pkg_invariants - pkg_session_log_export --> pkg_client_locale - pkg_session_log_export --> pkg_client_runtime - pkg_session_log_export --> pkg_client_ui_commands - pkg_session_log_export --> pkg_client_ui_conversation - pkg_session_log_export --> pkg_commands - pkg_session_log_export --> pkg_invariants - pkg_client_ui_directory_picker_browse --> pkg_client_locale - pkg_client_ui_directory_picker_browse --> pkg_client_runtime - pkg_client_ui_directory_picker_browse --> pkg_client_ui_workspace - pkg_client_ui_directory_picker_browse --> pkg_invariants - pkg_client_ui_directory_picker_native --> pkg_client_runtime - pkg_client_ui_directory_picker_native --> pkg_client_ui_workspace - pkg_client_ui_directory_picker_native --> pkg_invariants - pkg_client_ui_model_selection --> pkg_api_remotes - pkg_client_ui_model_selection --> pkg_api_session_controller - pkg_client_ui_model_selection --> pkg_client_connection - pkg_client_ui_model_selection --> pkg_client_locale - pkg_client_ui_model_selection --> pkg_client_runtime - pkg_client_ui_model_selection --> pkg_client_ui_commands - pkg_client_ui_model_selection --> pkg_client_ui_conversation - pkg_client_ui_model_selection --> pkg_client_ui_input_trigger - pkg_client_ui_model_selection --> pkg_invariants - pkg_client_ui_model_selection --> pkg_typert_protocol - pkg_client_ui_permission_presets --> pkg_api_remotes - pkg_client_ui_permission_presets --> pkg_client_connection - pkg_client_ui_permission_presets --> pkg_client_locale - pkg_client_ui_permission_presets --> pkg_client_runtime - pkg_client_ui_permission_presets --> pkg_client_ui_commands - pkg_client_ui_permission_presets --> pkg_client_ui_input_trigger - pkg_client_ui_permission_presets --> pkg_client_ui_settings - pkg_client_ui_permission_presets --> pkg_invariants - pkg_client_ui_permission_presets --> pkg_permission_presets - pkg_client_ui_skill --> pkg_api_remotes - pkg_client_ui_skill --> pkg_client_connection - pkg_client_ui_skill --> pkg_client_locale - pkg_client_ui_skill --> pkg_client_runtime - pkg_client_ui_skill --> pkg_client_ui_input_trigger - pkg_client_ui_skill --> pkg_client_ui_tool - pkg_client_ui_skill --> pkg_invariants - pkg_client_ui_cordis --> pkg_api_remotes - pkg_client_ui_cordis --> pkg_client_connection - pkg_client_ui_cordis --> pkg_client_locale - pkg_client_ui_cordis --> pkg_client_runtime - pkg_client_ui_cordis --> pkg_client_ui_input_trigger - pkg_client_ui_cordis --> pkg_client_ui_sidebar - pkg_client_ui_cordis --> pkg_client_ui_tool - pkg_client_ui_cordis --> pkg_cordis_client_runner - pkg_client_ui_cordis --> pkg_invariants pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_browse pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_native pkg_host_directory_picker_auto --> pkg_host_directory_picker_browse pkg_host_directory_picker_auto --> pkg_host_directory_picker_native pkg_host_directory_picker_auto --> pkg_host_webserver pkg_host_directory_picker_auto --> pkg_invariants + pkg_session_log_export --> pkg_client_locale + pkg_session_log_export --> pkg_client_ui_commands + pkg_session_log_export --> pkg_client_ui_conversation + pkg_session_log_export --> pkg_client_ui_renderer + pkg_session_log_export --> pkg_client_ui_session + pkg_session_log_export --> pkg_commands + pkg_session_log_export --> pkg_invariants + pkg_client_ui_attachment --> pkg_attachment + pkg_client_ui_attachment --> pkg_client_ui_chat + pkg_client_ui_attachment --> pkg_client_ui_conversation + pkg_client_ui_attachment --> pkg_client_ui_renderer + pkg_client_ui_attachment --> pkg_invariants + pkg_client_ui_deliverables --> pkg_client_connection + pkg_client_ui_deliverables --> pkg_client_locale + pkg_client_ui_deliverables --> pkg_client_ui_chat + pkg_client_ui_deliverables --> pkg_client_ui_conversation + pkg_client_ui_deliverables --> pkg_client_ui_renderer + pkg_client_ui_deliverables --> pkg_invariants + pkg_client_ui_deliverables --> pkg_session + pkg_client_ui_deliverables --> pkg_system_prompt + pkg_client_ui_goal --> pkg_api_remotes + pkg_client_ui_goal --> pkg_api_session_controller + pkg_client_ui_goal --> pkg_client_locale + pkg_client_ui_goal --> pkg_client_ui_chat + pkg_client_ui_goal --> pkg_client_ui_conversation + pkg_client_ui_goal --> pkg_client_ui_renderer + pkg_client_ui_goal --> pkg_client_ui_session + pkg_client_ui_goal --> pkg_commands + pkg_client_ui_goal --> pkg_goal + pkg_client_ui_goal --> pkg_invariants + pkg_client_ui_goal --> pkg_session + pkg_client_ui_goal --> pkg_typert_protocol + pkg_client_ui_message_feedback --> pkg_api_remotes + pkg_client_ui_message_feedback --> pkg_client_connection + pkg_client_ui_message_feedback --> pkg_client_locale + pkg_client_ui_message_feedback --> pkg_client_ui_chat + pkg_client_ui_message_feedback --> pkg_client_ui_conversation + pkg_client_ui_message_feedback --> pkg_client_ui_renderer + pkg_client_ui_message_feedback --> pkg_client_ui_session + pkg_client_ui_message_feedback --> pkg_invariants + pkg_client_ui_message_feedback --> pkg_message_feedback + pkg_client_ui_message_feedback --> pkg_session + pkg_client_ui_message_feedback --> pkg_typert_protocol + pkg_client_ui_model_selection --> pkg_api_remotes + pkg_client_ui_model_selection --> pkg_api_session_controller + pkg_client_ui_model_selection --> pkg_client_connection + pkg_client_ui_model_selection --> pkg_client_locale + pkg_client_ui_model_selection --> pkg_client_ui_commands + pkg_client_ui_model_selection --> pkg_client_ui_conversation + pkg_client_ui_model_selection --> pkg_client_ui_input_trigger + pkg_client_ui_model_selection --> pkg_client_ui_renderer + pkg_client_ui_model_selection --> pkg_client_ui_session + pkg_client_ui_model_selection --> pkg_invariants + pkg_client_ui_model_selection --> pkg_session + pkg_client_ui_model_selection --> pkg_typert_protocol + pkg_client_ui_permission_presets --> pkg_api_remotes + pkg_client_ui_permission_presets --> pkg_api_session_controller + pkg_client_ui_permission_presets --> pkg_client_connection + pkg_client_ui_permission_presets --> pkg_client_locale + pkg_client_ui_permission_presets --> pkg_client_ui_commands + pkg_client_ui_permission_presets --> pkg_client_ui_input_trigger + pkg_client_ui_permission_presets --> pkg_client_ui_renderer + pkg_client_ui_permission_presets --> pkg_client_ui_session + pkg_client_ui_permission_presets --> pkg_client_ui_settings + pkg_client_ui_permission_presets --> pkg_invariants + pkg_client_ui_permission_presets --> pkg_permission_presets + pkg_client_ui_tool --> pkg_api_remotes + pkg_client_ui_tool --> pkg_api_workspace_controller + pkg_client_ui_tool --> pkg_client_connection + pkg_client_ui_tool --> pkg_client_locale + pkg_client_ui_tool --> pkg_client_ui_chat + pkg_client_ui_tool --> pkg_client_ui_conversation + pkg_client_ui_tool --> pkg_client_ui_renderer + pkg_client_ui_tool --> pkg_client_ui_session + pkg_client_ui_tool --> pkg_invariants + pkg_client_ui_workflow_run --> pkg_api_session_controller + pkg_client_ui_workflow_run --> pkg_client_locale + pkg_client_ui_workflow_run --> pkg_client_ui_chat + pkg_client_ui_workflow_run --> pkg_client_ui_conversation + pkg_client_ui_workflow_run --> pkg_client_ui_renderer + pkg_client_ui_workflow_run --> pkg_client_ui_session + pkg_client_ui_workflow_run --> pkg_invariants + pkg_client_ui_workflow_run --> pkg_session + pkg_client_ui_workflow_run --> pkg_tool_workflow + pkg_client_ui_workflow_run --> pkg_workflow + pkg_client_test_runtime --> pkg_api_session_controller + pkg_client_test_runtime --> pkg_api_workspace_controller + pkg_client_test_runtime --> pkg_attachment + pkg_client_test_runtime --> pkg_client_connection + pkg_client_test_runtime --> pkg_client_store + pkg_client_test_runtime --> pkg_client_ui_chat + pkg_client_test_runtime --> pkg_client_ui_conversation + pkg_client_test_runtime --> pkg_client_ui_renderer + pkg_client_test_runtime --> pkg_client_ui_session + pkg_client_test_runtime --> pkg_client_ui_settings + pkg_client_test_runtime --> pkg_client_ui_slots + pkg_client_test_runtime --> pkg_invariants + pkg_client_test_runtime --> pkg_session + pkg_client_ui_skill --> pkg_api_remotes + pkg_client_ui_skill --> pkg_api_session_controller + pkg_client_ui_skill --> pkg_client_connection + pkg_client_ui_skill --> pkg_client_locale + pkg_client_ui_skill --> pkg_client_ui_input_trigger + pkg_client_ui_skill --> pkg_client_ui_renderer + pkg_client_ui_skill --> pkg_client_ui_tool + pkg_client_ui_skill --> pkg_invariants + pkg_client_ui_skill --> pkg_session + pkg_client_ui_cordis --> pkg_api_remotes + pkg_client_ui_cordis --> pkg_client_connection + pkg_client_ui_cordis --> pkg_client_locale + pkg_client_ui_cordis --> pkg_client_ui_input_trigger + pkg_client_ui_cordis --> pkg_client_ui_renderer + pkg_client_ui_cordis --> pkg_client_ui_session + pkg_client_ui_cordis --> pkg_client_ui_sidebar + pkg_client_ui_cordis --> pkg_client_ui_tool + pkg_client_ui_cordis --> pkg_cordis_client_runner + pkg_client_ui_cordis --> pkg_invariants ``` | Package | Group | Depends on | @@ -1560,7 +1647,9 @@ flowchart TD | [`acp-app`](../packages/bundle/acp-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`base`](../packages/bundle/base) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`sdk-app`](../packages/bundle/sdk-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-store`](../packages/client/store) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-renderer`](../packages/client/ui-renderer) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-slots`](../packages/client/ui-slots) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-web`](../packages/client/web) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | [`invariants`](../packages/runtime-diagnostics/invariants) | @@ -1742,45 +1831,46 @@ flowchart TD | [`sdk-client`](../packages/sdk/client) | `sdk` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session) | | [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) | | [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) | -| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`workspace`](../packages/workspace/workspace) | -| [`api-workspace-controller`](../packages/api/workspace-controller) | `api` | [`api-gateway`](../packages/api/gateway), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol), [`workspace`](../packages/workspace/workspace) | +| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`workspace`](../packages/workspace/workspace) | +| [`api-workspace-controller`](../packages/api/workspace-controller) | `api` | [`api-gateway`](../packages/api/gateway), [`client-connection`](../packages/client/connection), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol), [`workspace`](../packages/workspace/workspace) | | [`api-remotes`](../packages/api/remotes) | `api` | [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`commands`](../packages/interaction/commands), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`credentials`](../packages/credentials/credentials), [`file-reference`](../packages/context/file-reference), [`goal`](../packages/goal/goal), [`host-plugin-inventory`](../packages/host/plugin-inventory), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`session-reference`](../packages/context/session-reference), [`settings`](../packages/settings/settings), [`user-approval`](../packages/interaction/user-approval), [`user-questions`](../packages/interaction/user-questions) | -| [`client-runtime`](../packages/client/runtime) | `client` | [`agent`](../packages/core/agent), [`api-gateway`](../packages/api/gateway), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`commands`](../packages/interaction/commands), [`host-apiproxy`](../packages/host/apiproxy), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-title`](../packages/session/session-title), [`tool-todo`](../packages/todo/tool-todo), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`util-crypto`](../packages/util/crypto) | -| [`client-ui-renderer`](../packages/client/ui-renderer) | `client` | [`client-runtime`](../packages/client/runtime), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | -| [`client-locale`](../packages/client/locale) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | -| [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`client-runtime`](../packages/client/runtime), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-slots`](../packages/client/ui-slots), [`host-apiproxy`](../packages/host/apiproxy), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | -| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-reference`](../packages/client/ui-reference) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session-reference`](../packages/context/session-reference), [`typert-protocol`](../packages/typert/protocol) | -| [`cordis-client-runner`](../packages/extensions/cordis-client-runner) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`client-runtime`](../packages/client/runtime), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`agent`](../packages/core/agent), [`api-remotes`](../packages/api/remotes), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-settings`](../packages/client/ui-settings), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`session-stats`](../packages/session/session-stats), [`settings`](../packages/settings/settings), [`token-meter`](../packages/llm/token-meter), [`tool-todo`](../packages/todo/tool-todo), [`tools`](../packages/core/tools), [`util-crypto`](../packages/util/crypto) | -| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-layout`](../packages/client/ui-layout), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | [`attachment`](../packages/attachment/attachment), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-commands`](../packages/client/ui-commands) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants), [`system-prompt`](../packages/core/system-prompt) | -| [`client-ui-goal`](../packages/client/ui-goal) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | -| [`client-ui-jobs`](../packages/client/ui-jobs) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-message-feedback`](../packages/client/ui-message-feedback) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback), [`typert-protocol`](../packages/typert/protocol) | -| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants), [`plan-mode`](../packages/plan/plan-mode) | -| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | -| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`invariants`](../packages/runtime-diagnostics/invariants), [`subagent`](../packages/subagent/subagent), [`token-meter`](../packages/llm/token-meter) | -| [`client-ui-tool`](../packages/client/ui-tool) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`agent`](../packages/core/agent), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`tools`](../packages/core/tools) | -| [`client-ui-user-questions`](../packages/client/ui-user-questions) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) | -| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-model-selection`](../packages/client/ui-model-selection) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-protocol`](../packages/typert/protocol) | -| [`client-ui-permission-presets`](../packages/client/ui-permission-presets) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`permission-presets`](../packages/interaction/permission-presets) | -| [`client-ui-skill`](../packages/client/ui-skill) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-tool`](../packages/client/ui-tool), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`client-ui-tool`](../packages/client/ui-tool), [`cordis-client-runner`](../packages/extensions/cordis-client-runner), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-session`](../packages/client/ui-session) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | +| [`client-locale`](../packages/client/locale) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | +| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | +| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`cordis-client-runner`](../packages/extensions/cordis-client-runner) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-locale`](../packages/client/locale), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`token-meter`](../packages/llm/token-meter), [`tool-todo`](../packages/todo/tool-todo), [`util-crypto`](../packages/util/crypto), [`workspace`](../packages/workspace/workspace) | +| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`api-workspace-controller`](../packages/api/workspace-controller), [`client-locale`](../packages/client/locale), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-approval`](../packages/client/ui-approval) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-jobs`](../packages/client/ui-jobs) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session) | +| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | +| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`agent`](../packages/core/agent), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`tools`](../packages/core/tools) | +| [`client-ui-user-questions`](../packages/client/ui-user-questions) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol), [`user-questions`](../packages/interaction/user-questions) | +| [`client-ui-chat`](../packages/client/ui-chat) | `client` | [`agent`](../packages/core/agent), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-locale`](../packages/client/locale), [`client-ui-approval`](../packages/client/ui-approval), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-workspace`](../packages/client/ui-workspace), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-stats`](../packages/session/session-stats), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools) | +| [`client-ui-commands`](../packages/client/ui-commands) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-reference`](../packages/client/ui-reference) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session-reference`](../packages/context/session-reference), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`token-meter`](../packages/llm/token-meter) | | [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse), [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | [`attachment`](../packages/attachment/attachment), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) | +| [`client-ui-goal`](../packages/client/ui-goal) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-message-feedback`](../packages/client/ui-message-feedback) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-model-selection`](../packages/client/ui-model-selection) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-permission-presets`](../packages/client/ui-permission-presets) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`permission-presets`](../packages/interaction/permission-presets) | +| [`client-ui-tool`](../packages/client/ui-tool) | `client` | [`api-remotes`](../packages/api/remotes), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) | +| [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-store`](../packages/client/store), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-skill`](../packages/client/ui-skill) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-tool`](../packages/client/ui-tool), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`client-ui-tool`](../packages/client/ui-tool), [`cordis-client-runner`](../packages/extensions/cordis-client-runner), [`invariants`](../packages/runtime-diagnostics/invariants) | diff --git a/docs/module-graph.zh.md b/docs/module-graph.zh.md index cf0f12f71f..86298ce1ed 100644 --- a/docs/module-graph.zh.md +++ b/docs/module-graph.zh.md @@ -131,10 +131,12 @@ flowchart TD pkg_client_hmr["client-hmr"] pkg_client_locale["client-locale"] pkg_client_modules["client-modules"] - pkg_client_runtime["client-runtime"] + pkg_client_store["client-store"] pkg_client_ui_agent_preset["client-ui-agent-preset"] + pkg_client_ui_approval["client-ui-approval"] pkg_client_ui_attachment["client-ui-attachment"] pkg_client_ui_brand_official["client-ui-brand-official"] + pkg_client_ui_chat["client-ui-chat"] pkg_client_ui_commands["client-ui-commands"] pkg_client_ui_conversation["client-ui-conversation"] pkg_client_ui_deliverables["client-ui-deliverables"] @@ -151,6 +153,7 @@ flowchart TD pkg_client_ui_primitives["client-ui-primitives"] pkg_client_ui_reference["client-ui-reference"] pkg_client_ui_renderer["client-ui-renderer"] + pkg_client_ui_session["client-ui-session"] pkg_client_ui_settings["client-ui-settings"] pkg_client_ui_settings_general["client-ui-settings-general"] pkg_client_ui_settings_models["client-ui-settings-models"] @@ -364,7 +367,9 @@ flowchart TD pkg_acp_app --> pkg_invariants pkg_base --> pkg_invariants pkg_sdk_app --> pkg_invariants + pkg_client_store --> pkg_invariants pkg_client_ui_primitives --> pkg_invariants + pkg_client_ui_renderer --> pkg_invariants pkg_client_ui_slots --> pkg_invariants pkg_client_web --> pkg_invariants pkg_code_runtime --> pkg_invariants @@ -1238,6 +1243,7 @@ flowchart TD pkg_api_session_controller --> pkg_api_gateway pkg_api_session_controller --> pkg_attachment pkg_api_session_controller --> pkg_brand + pkg_api_session_controller --> pkg_client_connection pkg_api_session_controller --> pkg_invariants pkg_api_session_controller --> pkg_jobs pkg_api_session_controller --> pkg_llm @@ -1254,6 +1260,7 @@ flowchart TD pkg_api_session_controller --> pkg_typert_registry pkg_api_session_controller --> pkg_workspace pkg_api_workspace_controller --> pkg_api_gateway + pkg_api_workspace_controller --> pkg_client_connection pkg_api_workspace_controller --> pkg_invariants pkg_api_workspace_controller --> pkg_session pkg_api_workspace_controller --> pkg_storage_domain @@ -1277,272 +1284,352 @@ flowchart TD pkg_api_remotes --> pkg_settings pkg_api_remotes --> pkg_user_approval pkg_api_remotes --> pkg_user_questions - pkg_client_runtime --> pkg_agent - pkg_client_runtime --> pkg_api_gateway - pkg_client_runtime --> pkg_api_remotes - pkg_client_runtime --> pkg_api_session_controller - pkg_client_runtime --> pkg_api_workspace_controller - pkg_client_runtime --> pkg_attachment - pkg_client_runtime --> pkg_client_connection - pkg_client_runtime --> pkg_commands - pkg_client_runtime --> pkg_host_apiproxy - pkg_client_runtime --> pkg_invariants - pkg_client_runtime --> pkg_llm - pkg_client_runtime --> pkg_llm_retry - pkg_client_runtime --> pkg_session - pkg_client_runtime --> pkg_session_projection - pkg_client_runtime --> pkg_session_title - pkg_client_runtime --> pkg_tool_todo - pkg_client_runtime --> pkg_tools - pkg_client_runtime --> pkg_typert_protocol - pkg_client_runtime --> pkg_typert_registry - pkg_client_runtime --> pkg_util_crypto - pkg_client_ui_renderer --> pkg_client_runtime - pkg_client_ui_renderer --> pkg_invariants + pkg_client_ui_session --> pkg_api_session_controller + pkg_client_ui_session --> pkg_client_ui_renderer + pkg_client_ui_session --> pkg_invariants + pkg_client_ui_session --> pkg_session pkg_client_ui_settings --> pkg_api_remotes pkg_client_ui_settings --> pkg_client_connection - pkg_client_ui_settings --> pkg_client_runtime pkg_client_ui_settings --> pkg_invariants pkg_client_ui_settings --> pkg_settings pkg_client_locale --> pkg_api_remotes pkg_client_locale --> pkg_client_connection - pkg_client_locale --> pkg_client_runtime + pkg_client_locale --> pkg_client_ui_renderer pkg_client_locale --> pkg_client_ui_settings pkg_client_locale --> pkg_invariants pkg_client_locale --> pkg_settings - pkg_client_test_runtime --> pkg_client_runtime - pkg_client_test_runtime --> pkg_client_ui_renderer - pkg_client_test_runtime --> pkg_client_ui_slots - pkg_client_test_runtime --> pkg_host_apiproxy - pkg_client_test_runtime --> pkg_invariants - pkg_client_ui_input_trigger --> pkg_client_locale - pkg_client_ui_input_trigger --> pkg_client_runtime - pkg_client_ui_input_trigger --> pkg_file_reference - pkg_client_ui_input_trigger --> pkg_invariants pkg_client_ui_settings_models --> pkg_api_remotes pkg_client_ui_settings_models --> pkg_client_connection pkg_client_ui_settings_models --> pkg_client_locale - pkg_client_ui_settings_models --> pkg_client_runtime + pkg_client_ui_settings_models --> pkg_client_ui_renderer pkg_client_ui_settings_models --> pkg_client_ui_settings pkg_client_ui_settings_models --> pkg_invariants pkg_client_ui_settings_plugin_inventory --> pkg_api_remotes pkg_client_ui_settings_plugin_inventory --> pkg_client_locale - pkg_client_ui_settings_plugin_inventory --> pkg_client_runtime + pkg_client_ui_settings_plugin_inventory --> pkg_client_ui_renderer pkg_client_ui_settings_plugin_inventory --> pkg_client_ui_settings pkg_client_ui_settings_plugin_inventory --> pkg_invariants pkg_client_ui_settings_plugins --> pkg_api_remotes pkg_client_ui_settings_plugins --> pkg_client_connection pkg_client_ui_settings_plugins --> pkg_client_locale - pkg_client_ui_settings_plugins --> pkg_client_runtime + pkg_client_ui_settings_plugins --> pkg_client_ui_renderer pkg_client_ui_settings_plugins --> pkg_client_ui_settings pkg_client_ui_settings_plugins --> pkg_invariants pkg_client_ui_theme --> pkg_api_remotes pkg_client_ui_theme --> pkg_client_connection pkg_client_ui_theme --> pkg_client_locale - pkg_client_ui_theme --> pkg_client_runtime + pkg_client_ui_theme --> pkg_client_ui_renderer pkg_client_ui_theme --> pkg_client_ui_settings pkg_client_ui_theme --> pkg_host_webserver pkg_client_ui_theme --> pkg_invariants pkg_client_ui_theme --> pkg_settings - pkg_client_ui_layout --> pkg_client_runtime + pkg_client_ui_layout --> pkg_client_ui_renderer + pkg_client_ui_layout --> pkg_client_ui_session pkg_client_ui_layout --> pkg_client_ui_theme pkg_client_ui_layout --> pkg_invariants + pkg_cordis_client_runner --> pkg_api_remotes + pkg_cordis_client_runner --> pkg_client_connection + pkg_cordis_client_runner --> pkg_client_modules + pkg_cordis_client_runner --> pkg_client_ui_renderer + pkg_cordis_client_runner --> pkg_client_ui_theme + pkg_cordis_client_runner --> pkg_invariants + pkg_client_ui_conversation --> pkg_api_remotes + pkg_client_ui_conversation --> pkg_api_session_controller + pkg_client_ui_conversation --> pkg_api_workspace_controller + pkg_client_ui_conversation --> pkg_attachment + pkg_client_ui_conversation --> pkg_brand + pkg_client_ui_conversation --> pkg_client_locale + pkg_client_ui_conversation --> pkg_client_ui_layout + pkg_client_ui_conversation --> pkg_client_ui_renderer + pkg_client_ui_conversation --> pkg_client_ui_session + pkg_client_ui_conversation --> pkg_client_ui_settings + pkg_client_ui_conversation --> pkg_client_ui_workspace + pkg_client_ui_conversation --> pkg_commands + pkg_client_ui_conversation --> pkg_goal + pkg_client_ui_conversation --> pkg_invariants + pkg_client_ui_conversation --> pkg_llm + pkg_client_ui_conversation --> pkg_llm_retry + pkg_client_ui_conversation --> pkg_permission_presets + pkg_client_ui_conversation --> pkg_plan_mode + pkg_client_ui_conversation --> pkg_session + pkg_client_ui_conversation --> pkg_settings + pkg_client_ui_conversation --> pkg_token_meter + pkg_client_ui_conversation --> pkg_tool_todo + pkg_client_ui_conversation --> pkg_util_crypto + pkg_client_ui_conversation --> pkg_workspace + pkg_client_ui_sidebar --> pkg_api_workspace_controller + pkg_client_ui_sidebar --> pkg_client_locale + pkg_client_ui_sidebar --> pkg_client_ui_layout + pkg_client_ui_sidebar --> pkg_client_ui_renderer + pkg_client_ui_sidebar --> pkg_client_ui_session + pkg_client_ui_sidebar --> pkg_client_ui_workspace + pkg_client_ui_sidebar --> pkg_invariants + pkg_client_ui_workspace --> pkg_api_session_controller + pkg_client_ui_workspace --> pkg_api_workspace_controller + pkg_client_ui_workspace --> pkg_client_connection + pkg_client_ui_workspace --> pkg_client_locale + pkg_client_ui_workspace --> pkg_client_ui_conversation + pkg_client_ui_workspace --> pkg_client_ui_renderer + pkg_client_ui_workspace --> pkg_client_ui_session + pkg_client_ui_workspace --> pkg_client_ui_sidebar + pkg_client_ui_workspace --> pkg_invariants + pkg_client_ui_workspace --> pkg_session + pkg_client_ui_agent_preset --> pkg_api_remotes + pkg_client_ui_agent_preset --> pkg_api_session_controller + pkg_client_ui_agent_preset --> pkg_client_connection + pkg_client_ui_agent_preset --> pkg_client_locale + pkg_client_ui_agent_preset --> pkg_client_ui_conversation + pkg_client_ui_agent_preset --> pkg_client_ui_renderer + pkg_client_ui_agent_preset --> pkg_client_ui_session + pkg_client_ui_agent_preset --> pkg_client_ui_settings + pkg_client_ui_agent_preset --> pkg_client_ui_workspace + pkg_client_ui_agent_preset --> pkg_invariants + pkg_client_ui_agent_preset --> pkg_session + pkg_client_ui_approval --> pkg_api_remotes + pkg_client_ui_approval --> pkg_api_session_controller + pkg_client_ui_approval --> pkg_client_locale + pkg_client_ui_approval --> pkg_client_ui_conversation + pkg_client_ui_approval --> pkg_client_ui_renderer + pkg_client_ui_approval --> pkg_client_ui_session + pkg_client_ui_approval --> pkg_invariants + pkg_client_ui_approval --> pkg_llm + pkg_client_ui_approval --> pkg_session + pkg_client_ui_approval --> pkg_typert_protocol + pkg_client_ui_brand_official --> pkg_client_ui_conversation + pkg_client_ui_brand_official --> pkg_client_ui_renderer + pkg_client_ui_brand_official --> pkg_client_ui_sidebar + pkg_client_ui_brand_official --> pkg_invariants + pkg_client_ui_directory_picker_browse --> pkg_client_connection + pkg_client_ui_directory_picker_browse --> pkg_client_locale + pkg_client_ui_directory_picker_browse --> pkg_client_ui_renderer + pkg_client_ui_directory_picker_browse --> pkg_client_ui_workspace + pkg_client_ui_directory_picker_browse --> pkg_invariants + pkg_client_ui_directory_picker_native --> pkg_client_ui_renderer + pkg_client_ui_directory_picker_native --> pkg_client_ui_workspace + pkg_client_ui_directory_picker_native --> pkg_invariants + pkg_client_ui_input_trigger --> pkg_api_session_controller + pkg_client_ui_input_trigger --> pkg_client_locale + pkg_client_ui_input_trigger --> pkg_client_ui_conversation + pkg_client_ui_input_trigger --> pkg_client_ui_renderer + pkg_client_ui_input_trigger --> pkg_client_ui_session + pkg_client_ui_input_trigger --> pkg_file_reference + pkg_client_ui_input_trigger --> pkg_invariants + pkg_client_ui_input_trigger --> pkg_session + pkg_client_ui_jobs --> pkg_api_session_controller + pkg_client_ui_jobs --> pkg_client_locale + pkg_client_ui_jobs --> pkg_client_ui_conversation + pkg_client_ui_jobs --> pkg_client_ui_renderer + pkg_client_ui_jobs --> pkg_client_ui_session + pkg_client_ui_jobs --> pkg_invariants + pkg_client_ui_plan --> pkg_api_remotes + pkg_client_ui_plan --> pkg_client_locale + pkg_client_ui_plan --> pkg_client_ui_conversation + pkg_client_ui_plan --> pkg_client_ui_renderer + pkg_client_ui_plan --> pkg_client_ui_session + pkg_client_ui_plan --> pkg_invariants + pkg_client_ui_plan --> pkg_plan_mode + pkg_client_ui_plan --> pkg_session + pkg_client_ui_settings_general --> pkg_api_remotes + pkg_client_ui_settings_general --> pkg_client_connection + pkg_client_ui_settings_general --> pkg_client_locale + pkg_client_ui_settings_general --> pkg_client_ui_renderer + pkg_client_ui_settings_general --> pkg_client_ui_session + pkg_client_ui_settings_general --> pkg_client_ui_settings + pkg_client_ui_settings_general --> pkg_client_ui_sidebar + pkg_client_ui_settings_general --> pkg_invariants + pkg_client_ui_settings_general --> pkg_settings + pkg_client_ui_trajectory --> pkg_agent + pkg_client_ui_trajectory --> pkg_api_session_controller + pkg_client_ui_trajectory --> pkg_client_locale + pkg_client_ui_trajectory --> pkg_client_ui_conversation + pkg_client_ui_trajectory --> pkg_client_ui_renderer + pkg_client_ui_trajectory --> pkg_client_ui_session + pkg_client_ui_trajectory --> pkg_compaction + pkg_client_ui_trajectory --> pkg_invariants + pkg_client_ui_trajectory --> pkg_session + pkg_client_ui_trajectory --> pkg_tools + pkg_client_ui_user_questions --> pkg_api_remotes + pkg_client_ui_user_questions --> pkg_api_session_controller + pkg_client_ui_user_questions --> pkg_client_locale + pkg_client_ui_user_questions --> pkg_client_ui_conversation + pkg_client_ui_user_questions --> pkg_client_ui_renderer + pkg_client_ui_user_questions --> pkg_client_ui_session + pkg_client_ui_user_questions --> pkg_invariants + pkg_client_ui_user_questions --> pkg_session + pkg_client_ui_user_questions --> pkg_typert_protocol + pkg_client_ui_user_questions --> pkg_user_questions + pkg_client_ui_chat --> pkg_agent + pkg_client_ui_chat --> pkg_api_remotes + pkg_client_ui_chat --> pkg_api_session_controller + pkg_client_ui_chat --> pkg_api_workspace_controller + pkg_client_ui_chat --> pkg_attachment + pkg_client_ui_chat --> pkg_client_locale + pkg_client_ui_chat --> pkg_client_ui_approval + pkg_client_ui_chat --> pkg_client_ui_conversation + pkg_client_ui_chat --> pkg_client_ui_layout + pkg_client_ui_chat --> pkg_client_ui_renderer + pkg_client_ui_chat --> pkg_client_ui_session + pkg_client_ui_chat --> pkg_client_ui_workspace + pkg_client_ui_chat --> pkg_commands + pkg_client_ui_chat --> pkg_compaction + pkg_client_ui_chat --> pkg_invariants + pkg_client_ui_chat --> pkg_llm + pkg_client_ui_chat --> pkg_llm_retry + pkg_client_ui_chat --> pkg_session + pkg_client_ui_chat --> pkg_session_stats + pkg_client_ui_chat --> pkg_token_meter + pkg_client_ui_chat --> pkg_tools + pkg_client_ui_commands --> pkg_api_remotes + pkg_client_ui_commands --> pkg_api_session_controller + pkg_client_ui_commands --> pkg_client_locale + pkg_client_ui_commands --> pkg_client_ui_conversation + pkg_client_ui_commands --> pkg_client_ui_input_trigger + pkg_client_ui_commands --> pkg_client_ui_renderer + pkg_client_ui_commands --> pkg_client_ui_session + pkg_client_ui_commands --> pkg_commands + pkg_client_ui_commands --> pkg_invariants + pkg_client_ui_commands --> pkg_session pkg_client_ui_reference --> pkg_api_remotes pkg_client_ui_reference --> pkg_client_locale - pkg_client_ui_reference --> pkg_client_runtime pkg_client_ui_reference --> pkg_client_ui_input_trigger pkg_client_ui_reference --> pkg_file_reference pkg_client_ui_reference --> pkg_invariants pkg_client_ui_reference --> pkg_session_reference pkg_client_ui_reference --> pkg_typert_protocol - pkg_cordis_client_runner --> pkg_api_remotes - pkg_cordis_client_runner --> pkg_client_connection - pkg_cordis_client_runner --> pkg_client_modules - pkg_cordis_client_runner --> pkg_client_runtime - pkg_cordis_client_runner --> pkg_client_ui_theme - pkg_cordis_client_runner --> pkg_invariants - pkg_client_ui_conversation --> pkg_agent - pkg_client_ui_conversation --> pkg_api_remotes - pkg_client_ui_conversation --> pkg_attachment - pkg_client_ui_conversation --> pkg_brand - pkg_client_ui_conversation --> pkg_client_connection - pkg_client_ui_conversation --> pkg_client_locale - pkg_client_ui_conversation --> pkg_client_runtime - pkg_client_ui_conversation --> pkg_client_ui_input_trigger - pkg_client_ui_conversation --> pkg_client_ui_layout - pkg_client_ui_conversation --> pkg_client_ui_settings - pkg_client_ui_conversation --> pkg_commands - pkg_client_ui_conversation --> pkg_compaction - pkg_client_ui_conversation --> pkg_goal - pkg_client_ui_conversation --> pkg_invariants - pkg_client_ui_conversation --> pkg_llm_retry - pkg_client_ui_conversation --> pkg_permission_presets - pkg_client_ui_conversation --> pkg_plan_mode - pkg_client_ui_conversation --> pkg_session_stats - pkg_client_ui_conversation --> pkg_settings - pkg_client_ui_conversation --> pkg_token_meter - pkg_client_ui_conversation --> pkg_tool_todo - pkg_client_ui_conversation --> pkg_tools - pkg_client_ui_conversation --> pkg_util_crypto - pkg_client_ui_sidebar --> pkg_client_locale - pkg_client_ui_sidebar --> pkg_client_runtime - pkg_client_ui_sidebar --> pkg_client_ui_layout - pkg_client_ui_sidebar --> pkg_invariants - pkg_client_ui_agent_preset --> pkg_api_remotes - pkg_client_ui_agent_preset --> pkg_client_connection - pkg_client_ui_agent_preset --> pkg_client_locale - pkg_client_ui_agent_preset --> pkg_client_runtime - pkg_client_ui_agent_preset --> pkg_client_ui_conversation - pkg_client_ui_agent_preset --> pkg_client_ui_settings - pkg_client_ui_agent_preset --> pkg_invariants - pkg_client_ui_attachment --> pkg_attachment - pkg_client_ui_attachment --> pkg_client_runtime - pkg_client_ui_attachment --> pkg_client_ui_conversation - pkg_client_ui_attachment --> pkg_invariants - pkg_client_ui_brand_official --> pkg_client_runtime - pkg_client_ui_brand_official --> pkg_client_ui_conversation - pkg_client_ui_brand_official --> pkg_client_ui_sidebar - pkg_client_ui_brand_official --> pkg_invariants - pkg_client_ui_commands --> pkg_api_remotes - pkg_client_ui_commands --> pkg_client_locale - pkg_client_ui_commands --> pkg_client_runtime - pkg_client_ui_commands --> pkg_client_ui_conversation - pkg_client_ui_commands --> pkg_client_ui_input_trigger - pkg_client_ui_commands --> pkg_commands - pkg_client_ui_commands --> pkg_invariants - pkg_client_ui_deliverables --> pkg_client_connection - pkg_client_ui_deliverables --> pkg_client_locale - pkg_client_ui_deliverables --> pkg_client_runtime - pkg_client_ui_deliverables --> pkg_client_ui_conversation - pkg_client_ui_deliverables --> pkg_invariants - pkg_client_ui_deliverables --> pkg_system_prompt - pkg_client_ui_goal --> pkg_api_remotes - pkg_client_ui_goal --> pkg_client_locale - pkg_client_ui_goal --> pkg_client_runtime - pkg_client_ui_goal --> pkg_client_ui_conversation - pkg_client_ui_goal --> pkg_commands - pkg_client_ui_goal --> pkg_goal - pkg_client_ui_goal --> pkg_invariants - pkg_client_ui_goal --> pkg_session - pkg_client_ui_goal --> pkg_typert_protocol - pkg_client_ui_jobs --> pkg_client_locale - pkg_client_ui_jobs --> pkg_client_runtime - pkg_client_ui_jobs --> pkg_client_ui_conversation - pkg_client_ui_jobs --> pkg_invariants - pkg_client_ui_message_feedback --> pkg_api_remotes - pkg_client_ui_message_feedback --> pkg_client_connection - pkg_client_ui_message_feedback --> pkg_client_locale - pkg_client_ui_message_feedback --> pkg_client_runtime - pkg_client_ui_message_feedback --> pkg_client_ui_conversation - pkg_client_ui_message_feedback --> pkg_invariants - pkg_client_ui_message_feedback --> pkg_message_feedback - pkg_client_ui_message_feedback --> pkg_typert_protocol - pkg_client_ui_plan --> pkg_api_remotes - pkg_client_ui_plan --> pkg_client_locale - pkg_client_ui_plan --> pkg_client_runtime - pkg_client_ui_plan --> pkg_client_ui_conversation - pkg_client_ui_plan --> pkg_invariants - pkg_client_ui_plan --> pkg_plan_mode - pkg_client_ui_settings_general --> pkg_api_remotes - pkg_client_ui_settings_general --> pkg_client_connection - pkg_client_ui_settings_general --> pkg_client_locale - pkg_client_ui_settings_general --> pkg_client_runtime - pkg_client_ui_settings_general --> pkg_client_ui_settings - pkg_client_ui_settings_general --> pkg_client_ui_sidebar - pkg_client_ui_settings_general --> pkg_invariants - pkg_client_ui_settings_general --> pkg_settings + pkg_client_ui_subagent --> pkg_api_session_controller + pkg_client_ui_subagent --> pkg_client_connection pkg_client_ui_subagent --> pkg_client_locale - pkg_client_ui_subagent --> pkg_client_runtime pkg_client_ui_subagent --> pkg_client_ui_conversation pkg_client_ui_subagent --> pkg_client_ui_input_trigger + pkg_client_ui_subagent --> pkg_client_ui_renderer + pkg_client_ui_subagent --> pkg_client_ui_session pkg_client_ui_subagent --> pkg_invariants + pkg_client_ui_subagent --> pkg_session pkg_client_ui_subagent --> pkg_subagent pkg_client_ui_subagent --> pkg_token_meter - pkg_client_ui_tool --> pkg_api_remotes - pkg_client_ui_tool --> pkg_client_connection - pkg_client_ui_tool --> pkg_client_locale - pkg_client_ui_tool --> pkg_client_runtime - pkg_client_ui_tool --> pkg_client_ui_conversation - pkg_client_ui_tool --> pkg_invariants - pkg_client_ui_trajectory --> pkg_agent - pkg_client_ui_trajectory --> pkg_client_locale - pkg_client_ui_trajectory --> pkg_client_runtime - pkg_client_ui_trajectory --> pkg_client_ui_conversation - pkg_client_ui_trajectory --> pkg_compaction - pkg_client_ui_trajectory --> pkg_invariants - pkg_client_ui_trajectory --> pkg_tools - pkg_client_ui_user_questions --> pkg_api_remotes - pkg_client_ui_user_questions --> pkg_client_locale - pkg_client_ui_user_questions --> pkg_client_runtime - pkg_client_ui_user_questions --> pkg_client_ui_conversation - pkg_client_ui_user_questions --> pkg_invariants - pkg_client_ui_workflow_run --> pkg_client_locale - pkg_client_ui_workflow_run --> pkg_client_runtime - pkg_client_ui_workflow_run --> pkg_client_ui_conversation - pkg_client_ui_workflow_run --> pkg_invariants - pkg_client_ui_workflow_run --> pkg_session - pkg_client_ui_workflow_run --> pkg_tool_workflow - pkg_client_ui_workflow_run --> pkg_workflow - pkg_client_ui_workspace --> pkg_client_connection - pkg_client_ui_workspace --> pkg_client_locale - pkg_client_ui_workspace --> pkg_client_runtime - pkg_client_ui_workspace --> pkg_client_ui_conversation - pkg_client_ui_workspace --> pkg_client_ui_sidebar - pkg_client_ui_workspace --> pkg_invariants - pkg_session_log_export --> pkg_client_locale - pkg_session_log_export --> pkg_client_runtime - pkg_session_log_export --> pkg_client_ui_commands - pkg_session_log_export --> pkg_client_ui_conversation - pkg_session_log_export --> pkg_commands - pkg_session_log_export --> pkg_invariants - pkg_client_ui_directory_picker_browse --> pkg_client_locale - pkg_client_ui_directory_picker_browse --> pkg_client_runtime - pkg_client_ui_directory_picker_browse --> pkg_client_ui_workspace - pkg_client_ui_directory_picker_browse --> pkg_invariants - pkg_client_ui_directory_picker_native --> pkg_client_runtime - pkg_client_ui_directory_picker_native --> pkg_client_ui_workspace - pkg_client_ui_directory_picker_native --> pkg_invariants - pkg_client_ui_model_selection --> pkg_api_remotes - pkg_client_ui_model_selection --> pkg_api_session_controller - pkg_client_ui_model_selection --> pkg_client_connection - pkg_client_ui_model_selection --> pkg_client_locale - pkg_client_ui_model_selection --> pkg_client_runtime - pkg_client_ui_model_selection --> pkg_client_ui_commands - pkg_client_ui_model_selection --> pkg_client_ui_conversation - pkg_client_ui_model_selection --> pkg_client_ui_input_trigger - pkg_client_ui_model_selection --> pkg_invariants - pkg_client_ui_model_selection --> pkg_typert_protocol - pkg_client_ui_permission_presets --> pkg_api_remotes - pkg_client_ui_permission_presets --> pkg_client_connection - pkg_client_ui_permission_presets --> pkg_client_locale - pkg_client_ui_permission_presets --> pkg_client_runtime - pkg_client_ui_permission_presets --> pkg_client_ui_commands - pkg_client_ui_permission_presets --> pkg_client_ui_input_trigger - pkg_client_ui_permission_presets --> pkg_client_ui_settings - pkg_client_ui_permission_presets --> pkg_invariants - pkg_client_ui_permission_presets --> pkg_permission_presets - pkg_client_ui_skill --> pkg_api_remotes - pkg_client_ui_skill --> pkg_client_connection - pkg_client_ui_skill --> pkg_client_locale - pkg_client_ui_skill --> pkg_client_runtime - pkg_client_ui_skill --> pkg_client_ui_input_trigger - pkg_client_ui_skill --> pkg_client_ui_tool - pkg_client_ui_skill --> pkg_invariants - pkg_client_ui_cordis --> pkg_api_remotes - pkg_client_ui_cordis --> pkg_client_connection - pkg_client_ui_cordis --> pkg_client_locale - pkg_client_ui_cordis --> pkg_client_runtime - pkg_client_ui_cordis --> pkg_client_ui_input_trigger - pkg_client_ui_cordis --> pkg_client_ui_sidebar - pkg_client_ui_cordis --> pkg_client_ui_tool - pkg_client_ui_cordis --> pkg_cordis_client_runner - pkg_client_ui_cordis --> pkg_invariants pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_browse pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_native pkg_host_directory_picker_auto --> pkg_host_directory_picker_browse pkg_host_directory_picker_auto --> pkg_host_directory_picker_native pkg_host_directory_picker_auto --> pkg_host_webserver pkg_host_directory_picker_auto --> pkg_invariants + pkg_session_log_export --> pkg_client_locale + pkg_session_log_export --> pkg_client_ui_commands + pkg_session_log_export --> pkg_client_ui_conversation + pkg_session_log_export --> pkg_client_ui_renderer + pkg_session_log_export --> pkg_client_ui_session + pkg_session_log_export --> pkg_commands + pkg_session_log_export --> pkg_invariants + pkg_client_ui_attachment --> pkg_attachment + pkg_client_ui_attachment --> pkg_client_ui_chat + pkg_client_ui_attachment --> pkg_client_ui_conversation + pkg_client_ui_attachment --> pkg_client_ui_renderer + pkg_client_ui_attachment --> pkg_invariants + pkg_client_ui_deliverables --> pkg_client_connection + pkg_client_ui_deliverables --> pkg_client_locale + pkg_client_ui_deliverables --> pkg_client_ui_chat + pkg_client_ui_deliverables --> pkg_client_ui_conversation + pkg_client_ui_deliverables --> pkg_client_ui_renderer + pkg_client_ui_deliverables --> pkg_invariants + pkg_client_ui_deliverables --> pkg_session + pkg_client_ui_deliverables --> pkg_system_prompt + pkg_client_ui_goal --> pkg_api_remotes + pkg_client_ui_goal --> pkg_api_session_controller + pkg_client_ui_goal --> pkg_client_locale + pkg_client_ui_goal --> pkg_client_ui_chat + pkg_client_ui_goal --> pkg_client_ui_conversation + pkg_client_ui_goal --> pkg_client_ui_renderer + pkg_client_ui_goal --> pkg_client_ui_session + pkg_client_ui_goal --> pkg_commands + pkg_client_ui_goal --> pkg_goal + pkg_client_ui_goal --> pkg_invariants + pkg_client_ui_goal --> pkg_session + pkg_client_ui_goal --> pkg_typert_protocol + pkg_client_ui_message_feedback --> pkg_api_remotes + pkg_client_ui_message_feedback --> pkg_client_connection + pkg_client_ui_message_feedback --> pkg_client_locale + pkg_client_ui_message_feedback --> pkg_client_ui_chat + pkg_client_ui_message_feedback --> pkg_client_ui_conversation + pkg_client_ui_message_feedback --> pkg_client_ui_renderer + pkg_client_ui_message_feedback --> pkg_client_ui_session + pkg_client_ui_message_feedback --> pkg_invariants + pkg_client_ui_message_feedback --> pkg_message_feedback + pkg_client_ui_message_feedback --> pkg_session + pkg_client_ui_message_feedback --> pkg_typert_protocol + pkg_client_ui_model_selection --> pkg_api_remotes + pkg_client_ui_model_selection --> pkg_api_session_controller + pkg_client_ui_model_selection --> pkg_client_connection + pkg_client_ui_model_selection --> pkg_client_locale + pkg_client_ui_model_selection --> pkg_client_ui_commands + pkg_client_ui_model_selection --> pkg_client_ui_conversation + pkg_client_ui_model_selection --> pkg_client_ui_input_trigger + pkg_client_ui_model_selection --> pkg_client_ui_renderer + pkg_client_ui_model_selection --> pkg_client_ui_session + pkg_client_ui_model_selection --> pkg_invariants + pkg_client_ui_model_selection --> pkg_session + pkg_client_ui_model_selection --> pkg_typert_protocol + pkg_client_ui_permission_presets --> pkg_api_remotes + pkg_client_ui_permission_presets --> pkg_api_session_controller + pkg_client_ui_permission_presets --> pkg_client_connection + pkg_client_ui_permission_presets --> pkg_client_locale + pkg_client_ui_permission_presets --> pkg_client_ui_commands + pkg_client_ui_permission_presets --> pkg_client_ui_input_trigger + pkg_client_ui_permission_presets --> pkg_client_ui_renderer + pkg_client_ui_permission_presets --> pkg_client_ui_session + pkg_client_ui_permission_presets --> pkg_client_ui_settings + pkg_client_ui_permission_presets --> pkg_invariants + pkg_client_ui_permission_presets --> pkg_permission_presets + pkg_client_ui_tool --> pkg_api_remotes + pkg_client_ui_tool --> pkg_api_workspace_controller + pkg_client_ui_tool --> pkg_client_connection + pkg_client_ui_tool --> pkg_client_locale + pkg_client_ui_tool --> pkg_client_ui_chat + pkg_client_ui_tool --> pkg_client_ui_conversation + pkg_client_ui_tool --> pkg_client_ui_renderer + pkg_client_ui_tool --> pkg_client_ui_session + pkg_client_ui_tool --> pkg_invariants + pkg_client_ui_workflow_run --> pkg_api_session_controller + pkg_client_ui_workflow_run --> pkg_client_locale + pkg_client_ui_workflow_run --> pkg_client_ui_chat + pkg_client_ui_workflow_run --> pkg_client_ui_conversation + pkg_client_ui_workflow_run --> pkg_client_ui_renderer + pkg_client_ui_workflow_run --> pkg_client_ui_session + pkg_client_ui_workflow_run --> pkg_invariants + pkg_client_ui_workflow_run --> pkg_session + pkg_client_ui_workflow_run --> pkg_tool_workflow + pkg_client_ui_workflow_run --> pkg_workflow + pkg_client_test_runtime --> pkg_api_session_controller + pkg_client_test_runtime --> pkg_api_workspace_controller + pkg_client_test_runtime --> pkg_attachment + pkg_client_test_runtime --> pkg_client_connection + pkg_client_test_runtime --> pkg_client_store + pkg_client_test_runtime --> pkg_client_ui_chat + pkg_client_test_runtime --> pkg_client_ui_conversation + pkg_client_test_runtime --> pkg_client_ui_renderer + pkg_client_test_runtime --> pkg_client_ui_session + pkg_client_test_runtime --> pkg_client_ui_settings + pkg_client_test_runtime --> pkg_client_ui_slots + pkg_client_test_runtime --> pkg_invariants + pkg_client_test_runtime --> pkg_session + pkg_client_ui_skill --> pkg_api_remotes + pkg_client_ui_skill --> pkg_api_session_controller + pkg_client_ui_skill --> pkg_client_connection + pkg_client_ui_skill --> pkg_client_locale + pkg_client_ui_skill --> pkg_client_ui_input_trigger + pkg_client_ui_skill --> pkg_client_ui_renderer + pkg_client_ui_skill --> pkg_client_ui_tool + pkg_client_ui_skill --> pkg_invariants + pkg_client_ui_skill --> pkg_session + pkg_client_ui_cordis --> pkg_api_remotes + pkg_client_ui_cordis --> pkg_client_connection + pkg_client_ui_cordis --> pkg_client_locale + pkg_client_ui_cordis --> pkg_client_ui_input_trigger + pkg_client_ui_cordis --> pkg_client_ui_renderer + pkg_client_ui_cordis --> pkg_client_ui_session + pkg_client_ui_cordis --> pkg_client_ui_sidebar + pkg_client_ui_cordis --> pkg_client_ui_tool + pkg_client_ui_cordis --> pkg_cordis_client_runner + pkg_client_ui_cordis --> pkg_invariants ``` | Package | Group | Depends on | @@ -1562,7 +1649,9 @@ flowchart TD | [`acp-app`](../packages/bundle/acp-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`base`](../packages/bundle/base) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`sdk-app`](../packages/bundle/sdk-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-store`](../packages/client/store) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-renderer`](../packages/client/ui-renderer) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-ui-slots`](../packages/client/ui-slots) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`client-web`](../packages/client/web) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) | | [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | [`invariants`](../packages/runtime-diagnostics/invariants) | @@ -1744,45 +1833,46 @@ flowchart TD | [`sdk-client`](../packages/sdk/client) | `sdk` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session) | | [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) | | [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) | -| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`workspace`](../packages/workspace/workspace) | -| [`api-workspace-controller`](../packages/api/workspace-controller) | `api` | [`api-gateway`](../packages/api/gateway), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol), [`workspace`](../packages/workspace/workspace) | +| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`workspace`](../packages/workspace/workspace) | +| [`api-workspace-controller`](../packages/api/workspace-controller) | `api` | [`api-gateway`](../packages/api/gateway), [`client-connection`](../packages/client/connection), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol), [`workspace`](../packages/workspace/workspace) | | [`api-remotes`](../packages/api/remotes) | `api` | [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`commands`](../packages/interaction/commands), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`credentials`](../packages/credentials/credentials), [`file-reference`](../packages/context/file-reference), [`goal`](../packages/goal/goal), [`host-plugin-inventory`](../packages/host/plugin-inventory), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`session-reference`](../packages/context/session-reference), [`settings`](../packages/settings/settings), [`user-approval`](../packages/interaction/user-approval), [`user-questions`](../packages/interaction/user-questions) | -| [`client-runtime`](../packages/client/runtime) | `client` | [`agent`](../packages/core/agent), [`api-gateway`](../packages/api/gateway), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`commands`](../packages/interaction/commands), [`host-apiproxy`](../packages/host/apiproxy), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-title`](../packages/session/session-title), [`tool-todo`](../packages/todo/tool-todo), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`util-crypto`](../packages/util/crypto) | -| [`client-ui-renderer`](../packages/client/ui-renderer) | `client` | [`client-runtime`](../packages/client/runtime), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | -| [`client-locale`](../packages/client/locale) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | -| [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`client-runtime`](../packages/client/runtime), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-slots`](../packages/client/ui-slots), [`host-apiproxy`](../packages/host/apiproxy), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | -| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-reference`](../packages/client/ui-reference) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session-reference`](../packages/context/session-reference), [`typert-protocol`](../packages/typert/protocol) | -| [`cordis-client-runner`](../packages/extensions/cordis-client-runner) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`client-runtime`](../packages/client/runtime), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`agent`](../packages/core/agent), [`api-remotes`](../packages/api/remotes), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-settings`](../packages/client/ui-settings), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`session-stats`](../packages/session/session-stats), [`settings`](../packages/settings/settings), [`token-meter`](../packages/llm/token-meter), [`tool-todo`](../packages/todo/tool-todo), [`tools`](../packages/core/tools), [`util-crypto`](../packages/util/crypto) | -| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-layout`](../packages/client/ui-layout), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | [`attachment`](../packages/attachment/attachment), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-commands`](../packages/client/ui-commands) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants), [`system-prompt`](../packages/core/system-prompt) | -| [`client-ui-goal`](../packages/client/ui-goal) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | -| [`client-ui-jobs`](../packages/client/ui-jobs) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-message-feedback`](../packages/client/ui-message-feedback) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback), [`typert-protocol`](../packages/typert/protocol) | -| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants), [`plan-mode`](../packages/plan/plan-mode) | -| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | -| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`invariants`](../packages/runtime-diagnostics/invariants), [`subagent`](../packages/subagent/subagent), [`token-meter`](../packages/llm/token-meter) | -| [`client-ui-tool`](../packages/client/ui-tool) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`agent`](../packages/core/agent), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`tools`](../packages/core/tools) | -| [`client-ui-user-questions`](../packages/client/ui-user-questions) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) | -| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-model-selection`](../packages/client/ui-model-selection) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-protocol`](../packages/typert/protocol) | -| [`client-ui-permission-presets`](../packages/client/ui-permission-presets) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`permission-presets`](../packages/interaction/permission-presets) | -| [`client-ui-skill`](../packages/client/ui-skill) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-tool`](../packages/client/ui-tool), [`invariants`](../packages/runtime-diagnostics/invariants) | -| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`client-ui-tool`](../packages/client/ui-tool), [`cordis-client-runner`](../packages/extensions/cordis-client-runner), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-session`](../packages/client/ui-session) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | +| [`client-locale`](../packages/client/locale) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | +| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | +| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`cordis-client-runner`](../packages/extensions/cordis-client-runner) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-locale`](../packages/client/locale), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`token-meter`](../packages/llm/token-meter), [`tool-todo`](../packages/todo/tool-todo), [`util-crypto`](../packages/util/crypto), [`workspace`](../packages/workspace/workspace) | +| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`api-workspace-controller`](../packages/api/workspace-controller), [`client-locale`](../packages/client/locale), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-approval`](../packages/client/ui-approval) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-jobs`](../packages/client/ui-jobs) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session) | +| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) | +| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`agent`](../packages/core/agent), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`tools`](../packages/core/tools) | +| [`client-ui-user-questions`](../packages/client/ui-user-questions) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol), [`user-questions`](../packages/interaction/user-questions) | +| [`client-ui-chat`](../packages/client/ui-chat) | `client` | [`agent`](../packages/core/agent), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-locale`](../packages/client/locale), [`client-ui-approval`](../packages/client/ui-approval), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-workspace`](../packages/client/ui-workspace), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-stats`](../packages/session/session-stats), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools) | +| [`client-ui-commands`](../packages/client/ui-commands) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-reference`](../packages/client/ui-reference) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session-reference`](../packages/context/session-reference), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`token-meter`](../packages/llm/token-meter) | | [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse), [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | [`attachment`](../packages/attachment/attachment), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) | +| [`client-ui-goal`](../packages/client/ui-goal) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-message-feedback`](../packages/client/ui-message-feedback) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-model-selection`](../packages/client/ui-model-selection) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) | +| [`client-ui-permission-presets`](../packages/client/ui-permission-presets) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`permission-presets`](../packages/interaction/permission-presets) | +| [`client-ui-tool`](../packages/client/ui-tool) | `client` | [`api-remotes`](../packages/api/remotes), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants) | +| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) | +| [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-store`](../packages/client/store), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-skill`](../packages/client/ui-skill) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-tool`](../packages/client/ui-tool), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) | +| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`client-ui-tool`](../packages/client/ui-tool), [`cordis-client-runner`](../packages/extensions/cordis-client-runner), [`invariants`](../packages/runtime-diagnostics/invariants) | diff --git a/docs/subsystems/user-questions.i18n.yaml b/docs/subsystems/user-questions.i18n.yaml index b9ee6605e6..1138e03de1 100644 --- a/docs/subsystems/user-questions.i18n.yaml +++ b/docs/subsystems/user-questions.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/user-questions.md -user-questions.md: 93b1bc52580da16fe8ad0de4bc16ec309aaacc1f -user-questions.zh.md: 50098f6e0f6b1b38a1035566b04e24e0b7a80ff7 +user-questions.md: fbbfb1435586c7191e47a6eaa5b1783c9f172d48 +user-questions.zh.md: 054ca06cd3c8a85cb35d99658c325848ea780cb5 diff --git a/docs/subsystems/user-questions.md b/docs/subsystems/user-questions.md index 93b1bc5258..fbbfb14355 100644 --- a/docs/subsystems/user-questions.md +++ b/docs/subsystems/user-questions.md @@ -2,7 +2,7 @@ English | [中文](user-questions.zh.md) -The user-questions seam of [dsh-user-questions](../../packages/interaction/user-questions). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. UI surfaces provide the active `UserQuestionProvider`; the host runtime relays requests to its connected client. +The user-questions seam of [dsh-user-questions](../../packages/interaction/user-questions). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. Agent-scoped waterfall listeners compose the available UI surfaces, including listeners relayed to a connected client. Source: [`packages/interaction/user-questions/src/index.ts`](../../packages/interaction/user-questions/src/index.ts) @@ -74,14 +74,7 @@ interface AskUserQuestionItem { ```ts type-equiv /** Request for a human answer. */ -interface AskUserQuestionRequest { - /** Questions to display. */ - questions: AskUserQuestionItem[] - /** Exact live calling agent, when the request came from an agent tool call. */ - agent?: Agent - /** Abort signal for the owning tool/step. */ - signal?: AbortSignal -} +interface AskUserQuestionRequest extends AskUserQuestionRequestEvent {} ``` ## Answer @@ -108,17 +101,6 @@ interface AskUserQuestionAnswer { } ``` -## Provider - -Only one provider may be active in a context. Provider registration is effect-bound so HMR/disposal removes the active UI. - -```ts type-equiv -/** UI-side provider for user questions. */ -interface UserQuestionProvider { - ask(request: AskUserQuestionRequest): Promise -} -``` - ## Errors `UserQuestionError` extends `HarnessError`, so `ctx.tools.execute()` preserves `{ name, code }` for model-facing tool failures such as `EMPTY_QUESTIONS`, `NO_PROVIDER`, `ASK_ABORTED`, or UI-side cancellation. @@ -145,19 +127,11 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp ### `ctx.userQuestions` — `UserQuestionService` -`ctx.userQuestions`: one active UI provider plus an `ask()` API. +`ctx.userQuestions`: validation plus the scoped answerer waterfall. ```ts cordis-catalog /** - * Register the UI provider. Only one provider may be active in a context. - * - * @param provider UI-side implementation that collects answers. - * @returns Disposer that unregisters this provider. - */ -registerProvider(provider: UserQuestionProvider): () => void - -/** - * Ask the active UI provider and wait for the user's answer. + * Ask the scoped answerer waterfall and wait for the user's answer. * * When a caller supplies an agent, human interaction is valid only for the * exact live runtime root. Runtime ownership, not durable session lineage, diff --git a/docs/subsystems/user-questions.zh.md b/docs/subsystems/user-questions.zh.md index 50098f6e0f..054ca06cd3 100644 --- a/docs/subsystems/user-questions.zh.md +++ b/docs/subsystems/user-questions.zh.md @@ -2,7 +2,7 @@ [English](user-questions.md) | 中文 -[dsh-user-questions](../../packages/interaction/user-questions) 的用户交互 seam。它是工具或权限插件需要人类回答后 agent(智能体)才能继续时所使用的、提供方无关的词汇。UI 界面提供活跃的 `UserQuestionProvider`;host 运行时把请求转发给其连接的客户端。 +[dsh-user-questions](../../packages/interaction/user-questions) 的用户交互 seam。它是工具或权限插件需要人类回答后 agent(智能体)才能继续时所使用的、提供方无关的词汇。Agent-scoped waterfall listener 组合可用的 UI 界面,其中包括转发到已连接 client 的 listener。 源码:[`packages/interaction/user-questions/src/index.ts`](../../packages/interaction/user-questions/src/index.ts) @@ -74,14 +74,7 @@ interface AskUserQuestionItem { ```ts type-equiv /** Request for a human answer. */ -interface AskUserQuestionRequest { - /** Questions to display. */ - questions: AskUserQuestionItem[] - /** Exact live calling agent, when the request came from an agent tool call. */ - agent?: Agent - /** Abort signal for the owning tool/step. */ - signal?: AbortSignal -} +interface AskUserQuestionRequest extends AskUserQuestionRequestEvent {} ``` ## 回答 @@ -108,17 +101,6 @@ interface AskUserQuestionAnswer { } ``` -## 提供方 - -同一上下文中只能有一个活跃的提供方。提供方注册绑定到 effect,因此 HMR(热模块替换)或 dispose(资源释放)会移除当前活跃的 UI。 - -```ts type-equiv -/** UI-side provider for user questions. */ -interface UserQuestionProvider { - ask(request: AskUserQuestionRequest): Promise -} -``` - ## 错误 `UserQuestionError` 继承 `HarnessError`,因此 `ctx.tools.execute()` 会保留 `{ name, code }`,用于面向模型的工具失败,如 `EMPTY_QUESTIONS`、`NO_PROVIDER`、`ASK_ABORTED` 或 UI 侧取消。 @@ -145,19 +127,11 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp ### `ctx.userQuestions` — `UserQuestionService` -`ctx.userQuestions`: one active UI provider plus an `ask()` API. +`ctx.userQuestions`: validation plus the scoped answerer waterfall. ```ts cordis-catalog /** - * Register the UI provider. Only one provider may be active in a context. - * - * @param provider UI-side implementation that collects answers. - * @returns Disposer that unregisters this provider. - */ -registerProvider(provider: UserQuestionProvider): () => void - -/** - * Ask the active UI provider and wait for the user's answer. + * Ask the scoped answerer waterfall and wait for the user's answer. * * When a caller supplies an agent, human interaction is valid only for the * exact live runtime root. Runtime ownership, not durable session lineage, diff --git a/packages/api/gateway/README.i18n.yaml b/packages/api/gateway/README.i18n.yaml index e0cc0dd70d..cfeead8e18 100644 --- a/packages/api/gateway/README.i18n.yaml +++ b/packages/api/gateway/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/api/gateway/README.md -README.md: fe4834b4ca36590bc289be9b6863ffb082e8ee71 -README.zh.md: dff5a58599f0d3bf97996ceb36b134bf62da8ca0 +README.md: 2546b0c4e54ea106c9203ce419027fe8253c7ca5 +README.zh.md: 9281519cda422137c6fd08ff6680ba0d57902913 diff --git a/packages/api/remotes/README.i18n.yaml b/packages/api/remotes/README.i18n.yaml index 50a7c83058..fd100820c1 100644 --- a/packages/api/remotes/README.i18n.yaml +++ b/packages/api/remotes/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/api/remotes/README.md README.md: 0c4f0fcab4a741f457f1ffbdd9a4ba7688b9d32c -README.zh.md: b381751cec5285c34bb310b330fc55d34033cbf2 +README.zh.md: f83e31e63b57f09b16403df911154c9d93b07958 diff --git a/packages/client/AGENTS.md b/packages/client/AGENTS.md index 9baa35ebd0..93e3cba07b 100644 --- a/packages/client/AGENTS.md +++ b/packages/client/AGENTS.md @@ -51,7 +51,7 @@ Non-negotiables across the layers: - **Business data lives in the object layer, never a store.** Entry-declared stores carry shared viewing/interaction state (selection, drafts, panel widths); sessions, frames, and connections stay in the object layer. - **rpcId is strictly bidirectional**: the initiator mints, the responder echoes; business signatures see only `RpcRequest

`, minting stays in the carrier layer ([layering and RPC protocol note](../../.agents/notes/implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md)). -- **Notifier publication discipline**: `notifyNow` is only the direct echo of a user gesture; structural updates use microtask-batched `markDirty`, while visible streaming chunks use cumulative `markFrameDirty`. See `runtime/src/client/sessions/notifier.ts`. +- **Notifier publication discipline**: `notifyNow` is only the direct echo of a user gesture; structural updates use microtask-batched `markDirty`, while visible streaming chunks use cumulative `markFrameDirty`. See `../api/session-controller/src/client/sessions/notifier.ts`. - **The web layer is pure presentation.** Nothing that is "how to draw" (tool-card views, queue states) enters the session log; the host computes such data per frame or pushes it live, and replay recomputes it — falling back to the generic form when it can't. A new *model-visible* input still requires a session event (repo-wide rule). ## Dependency declaration diff --git a/packages/client/README.i18n.yaml b/packages/client/README.i18n.yaml index d80ae58f90..db771381dd 100644 --- a/packages/client/README.i18n.yaml +++ b/packages/client/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/README.md -README.md: b18aad486cd7d3fafe8261fee61fa9e26a7feaa6 -README.zh.md: 58c9aeb5c91f13d9306ba8de16de5af760fd43a2 +README.md: eaf01b282ead3c4638435e3d02d6a803ad1faa15 +README.zh.md: 2ff4b1529f07e0f31b3d06a9577d3c480dd34916 diff --git a/packages/client/README.md b/packages/client/README.md index b18aad486c..eaf01b282e 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -10,11 +10,12 @@ The browser side of the dsh web GUI: shell boot, browser-host communication, sha | [`ui-renderer/`](ui-renderer/README.md) | Binds slot data to React and mounts the assembled application after client boot settles. | | [`modules/`](modules/README.md) | Loads browser-side client modules. | | [`connection/`](connection/README.md) | Maintains browser-host RPC communication and event delivery. | -| [`runtime/`](runtime/README.md) | Provides shared client services for sessions, workspaces, and UI composition. | | [`hmr/`](hmr/README.md) | Refreshes client plugins during development. | | [`locale/`](locale/README.md) | Provides localization preferences and message dictionaries. | +| [`store/`](store/README.md) | Provides React-free observable and snapshot-store primitives. | | [`test-runtime/`](../test-support/client-runtime/README.md) | Provides shared repository test support for client feature packages. | | [`ui-slots/`](ui-slots/README.md) | Defines how UI features register and compose extension slots. | +| [`ui-session/`](ui-session/README.md) | Adapts Session Controller state into standard Slot sources and hooks. | | [`ui-theme/`](ui-theme/README.md) | Applies the selected color theme. | | [`ui-primitives/`](ui-primitives/README.md) | Provides shared React controls, icons, and content renderers. | | [`ui-attachment/`](ui-attachment/README.md) | Registers composer and message-image attachment presentation. | @@ -23,6 +24,8 @@ The browser side of the dsh web GUI: shell boot, browser-host communication, sha | [`ui-brand-official/`](ui-brand-official/README.md) | Fills the generic browser-brand slots with the official name and marks. | | [`ui-workspace/`](ui-workspace/README.md) | Provides workspace selection and creation surfaces. | | [`ui-conversation/`](ui-conversation/README.md) | Presents the active conversation and its input surface. | +| [`ui-chat/`](ui-chat/README.md) | Projects and renders the Chat conversation target. | +| [`ui-approval/`](ui-approval/README.md) | Presents approval requests and returns user decisions. | | [`ui-tool/`](ui-tool/README.md) | Composes Tool call trees and keyed per-Tool views. | | [`ui-workflow-run/`](ui-workflow-run/README.md) | Replays durable workflow runs as nested Chat disclosures with live-only child navigation. | | [`ui-goal/`](ui-goal/README.md) | Presents and manages the current goal. | diff --git a/packages/client/README.zh.md b/packages/client/README.zh.md index 58c9aeb5c9..2ff4b1529f 100644 --- a/packages/client/README.zh.md +++ b/packages/client/README.zh.md @@ -10,11 +10,12 @@ dsh web GUI 的浏览器侧:shell 启动、浏览器与宿主通信、共享 U | [`ui-renderer/`](ui-renderer/README.zh.md) | 将 slot 数据绑定到 React,并在客户端启动稳定后挂载组装完成的应用。 | | [`modules/`](modules/README.zh.md) | 加载浏览器侧客户端模块。 | | [`connection/`](connection/README.zh.md) | 维护浏览器与宿主之间的 RPC 通信和事件传递。 | -| [`runtime/`](runtime/README.zh.md) | 为会话、工作区和 UI 组合提供共享客户端服务。 | | [`hmr/`](hmr/README.zh.md) | 在开发期间刷新客户端插件。 | | [`locale/`](locale/README.zh.md) | 提供本地化偏好与消息词典。 | +| [`store/`](store/README.zh.md) | 提供不依赖 React 的 observable 与 snapshot-store 基础设施。 | | [`test-runtime/`](../test-support/client-runtime/README.zh.md) | 为客户端功能包提供共享的仓库测试支持。 | | [`ui-slots/`](ui-slots/README.zh.md) | 定义 UI 功能注册和组合扩展 slot 的方式。 | +| [`ui-session/`](ui-session/README.zh.md) | 把 Session Controller 状态适配为标准 Slot source 与 hook。 | | [`ui-theme/`](ui-theme/README.zh.md) | 应用所选颜色主题。 | | [`ui-primitives/`](ui-primitives/README.zh.md) | 提供共享 React 控件、图标和内容渲染器。 | | [`ui-attachment/`](ui-attachment/README.zh.md) | 注册输入框与消息图片的附件呈现。 | @@ -23,6 +24,8 @@ dsh web GUI 的浏览器侧:shell 启动、浏览器与宿主通信、共享 U | [`ui-brand-official/`](ui-brand-official/README.zh.md) | 使用官方名称和标记填充通用浏览器品牌 slot。 | | [`ui-workspace/`](ui-workspace/README.zh.md) | 提供工作区选择与创建界面。 | | [`ui-conversation/`](ui-conversation/README.zh.md) | 展示当前对话及其输入界面。 | +| [`ui-chat/`](ui-chat/README.zh.md) | 投影并渲染 Chat conversation target。 | +| [`ui-approval/`](ui-approval/README.zh.md) | 展示审批请求并返回用户决定。 | | [`ui-tool/`](ui-tool/README.zh.md) | 编排工具调用树和按工具键控的视图。 | | [`ui-workflow-run/`](ui-workflow-run/README.zh.md) | 把持久工作流运行回放为 Chat 嵌套折叠项,并只为实时子 Session 提供导航。 | | [`ui-goal/`](ui-goal/README.zh.md) | 展示和管理当前目标。 | diff --git a/packages/client/store/README.i18n.yaml b/packages/client/store/README.i18n.yaml new file mode 100644 index 0000000000..a67f7a84ec --- /dev/null +++ b/packages/client/store/README.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write packages/client/store/README.md +README.md: 51299c9730157eaf1cd7226d64a54e08b497b76e +README.zh.md: 1f2883db9a0b591a12438eba96f2fb36d96fbda0 diff --git a/packages/client/store/README.md b/packages/client/store/README.md new file mode 100644 index 0000000000..51299c9730 --- /dev/null +++ b/packages/client/store/README.md @@ -0,0 +1,17 @@ +# @deepseek-ai/dsh-client-store + +English | [中文](README.zh.md) + +React-free observable and snapshot-store primitives shared by Client controllers and renderer adapters. The package owns synchronous and animation-frame publication, Immer-backed updates, shallow equality, and optional browser persistence; React hook construction remains in `@deepseek-ai/dsh-client-ui-renderer`. + +## Model Experience + +None, as this package provides browser-side state primitives and registers nothing model-facing. + +#### KV Cache effect + +None; the stores neither assemble nor send model requests. + +## Known Limitations and Deferred Work + +- **Persistence is browser-local** — persisted stores use JSON in `localStorage`; non-browser runtimes disable persistence, and the package provides no cross-device synchronization. diff --git a/packages/client/store/README.zh.md b/packages/client/store/README.zh.md new file mode 100644 index 0000000000..1f2883db9a --- /dev/null +++ b/packages/client/store/README.zh.md @@ -0,0 +1,17 @@ +# @deepseek-ai/dsh-client-store + +[English](README.md) | 中文 + +供 Client controller 与 renderer adapter 共用的不依赖 React 的 observable 和 snapshot-store 基础设施。本包负责同步与 animation-frame 发布、基于 Immer 的更新、浅比较和可选的浏览器持久化;React hook 的构造仍属于 `@deepseek-ai/dsh-client-ui-renderer`。 + +## 模型体验 + +无,因为本包提供浏览器侧状态基础设施,不注册任何面向模型的内容。 + +#### KV Cache 影响 + +无;这些 store 既不组装也不发送模型请求。 + +## 已知限制与暂缓事项 + +- **持久化仅限浏览器本地**——持久化 store 使用 `localStorage` 中的 JSON;非浏览器运行时会禁用持久化,本包也不提供跨设备同步。 diff --git a/packages/client/ui-approval/README.i18n.yaml b/packages/client/ui-approval/README.i18n.yaml new file mode 100644 index 0000000000..8158657bd3 --- /dev/null +++ b/packages/client/ui-approval/README.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write packages/client/ui-approval/README.md +README.md: efc3a81ad88d94b2835b32a7663f72c2e95c1614 +README.zh.md: 8eaa561b9cbdffd0998b568109497c0e9587f043 diff --git a/packages/client/ui-approval/README.md b/packages/client/ui-approval/README.md new file mode 100644 index 0000000000..efc3a81ad8 --- /dev/null +++ b/packages/client/ui-approval/README.md @@ -0,0 +1,17 @@ +# @deepseek-ai/dsh-client-ui-approval + +English | [中文](README.zh.md) + +Browser approval presentation over the Agent-scoped Remote Event waterfall. The plugin publishes each pending request through `ctx.uiSession`, takes over the Conversation composer, optionally renders correlated Tool detail, and returns the user's decision to the waiting Host request. + +## Model Experience + +None, as this package presents approval requests in the browser and registers nothing model-facing. + +#### KV Cache effect + +None; approval request and response rendering does not alter a model request. + +## Known Limitations and Deferred Work + +- **The panel exposes transient decisions only** — it supports allow-once and reject; persistent permission policy remains owned by Host-side approval packages. diff --git a/packages/client/ui-approval/README.zh.md b/packages/client/ui-approval/README.zh.md new file mode 100644 index 0000000000..8eaa561b9c --- /dev/null +++ b/packages/client/ui-approval/README.zh.md @@ -0,0 +1,17 @@ +# @deepseek-ai/dsh-client-ui-approval + +[English](README.md) | 中文 + +基于 Agent-scoped Remote Event waterfall 的浏览器审批界面。插件通过 `ctx.uiSession` 发布每个待处理请求、接管 Conversation composer、按需渲染关联的 Tool 详情,并将用户决定返回给等待中的 Host 请求。 + +## 模型体验 + +无,因为本包只在浏览器中呈现审批请求,不注册任何面向模型的内容。 + +#### KV Cache 影响 + +无;审批请求和响应的呈现不会改变模型请求。 + +## 已知限制与暂缓事项 + +- **面板只提供临时决定**——它支持仅本次允许和拒绝;持久权限策略仍由 Host 侧审批 package 拥有。 diff --git a/packages/client/ui-approval/tests/ui-approval.client.spec.tsx b/packages/client/ui-approval/tests/ui-approval.client.spec.tsx index 204af86f03..9e326f18f0 100644 --- a/packages/client/ui-approval/tests/ui-approval.client.spec.tsx +++ b/packages/client/ui-approval/tests/ui-approval.client.spec.tsx @@ -56,6 +56,7 @@ function setupPlugin(): PluginBench { const registerPendingInteraction = vi.fn((_precedence: (value: PendingApproval) => number) => ( value: PendingApproval, ) => { + _precedence(value) pending = [...pending, value] return () => { pending = pending.filter(candidate => candidate !== value) } }) @@ -257,6 +258,22 @@ describe('approval Remote Event consumer', () => { await scope.fiber.dispose() }) + it('publishes a scoped request without optional request metadata', async () => { + const bench = setupPlugin() + const scope = createScope(bench.ctx, id('s1')) + await scope.fiber.await() + const result = bench.listener.call(scope.ctx, { toolName: 'read' }, () => Promise.resolve('unavailable')) + const pending = bench.pending.getSnapshot()[0]! + + await pending.answer('rejected') + + await expect(result).resolves.toBe('rejected') + expect(pending).toMatchObject({ toolName: 'read' }) + expect(pending.callId).toBeUndefined() + expect(pending.reason).toBeUndefined() + await scope.fiber.dispose() + }) + it('removes stable registrations with the plugin lifetime', async () => { const bench = setupPlugin() await bench.ctx.fiber.dispose() diff --git a/packages/client/ui-chat/README.i18n.yaml b/packages/client/ui-chat/README.i18n.yaml new file mode 100644 index 0000000000..4abebdd3f3 --- /dev/null +++ b/packages/client/ui-chat/README.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write packages/client/ui-chat/README.md +README.md: 56bb20ab7b4d9b5c0c95142b311a07ad9b8a1fd3 +README.zh.md: 40ee1ee710e2f86e802ffaac4dc0bb10852f128f diff --git a/packages/client/ui-chat/README.md b/packages/client/ui-chat/README.md new file mode 100644 index 0000000000..56bb20ab7b --- /dev/null +++ b/packages/client/ui-chat/README.md @@ -0,0 +1,17 @@ +# @deepseek-ai/dsh-client-ui-chat + +English | [中文](README.zh.md) + +The browser Chat target for Conversation assembly. It registers Chat event definitions and snapshot construction, supplies `useChat`, renders transcript nodes and details, and owns Chat-specific stores, actions, localization, historical images, and scroll restoration. + +## Model Experience + +None, as this package renders logged conversation state in the browser and registers nothing model-facing. + +#### KV Cache effect + +None; Chat presentation does not assemble or mutate provider requests. + +## Known Limitations and Deferred Work + +- **The view reflects the loaded Session window** — older transcript nodes become available only after Session Controller loads the preceding event page. diff --git a/packages/client/ui-chat/README.zh.md b/packages/client/ui-chat/README.zh.md new file mode 100644 index 0000000000..40ee1ee710 --- /dev/null +++ b/packages/client/ui-chat/README.zh.md @@ -0,0 +1,17 @@ +# @deepseek-ai/dsh-client-ui-chat + +[English](README.md) | 中文 + +Conversation 组装的浏览器 Chat target。本包注册 Chat event definition 与 snapshot 构造、提供 `useChat`、渲染 transcript node 和详情,并拥有 Chat 专属 store、action、本地化、历史图片与滚动位置恢复。 + +## 模型体验 + +无,因为本包在浏览器中渲染已记录的对话状态,不注册任何面向模型的内容。 + +#### KV Cache 影响 + +无;Chat 呈现不会组装或修改提供方请求。 + +## 已知限制与暂缓事项 + +- **视图只反映已加载的 Session 窗口**——只有 Session Controller 加载前一页 event 后,更早的 transcript node 才会出现。 diff --git a/packages/client/ui-conversation/README.i18n.yaml b/packages/client/ui-conversation/README.i18n.yaml index 081d70e9cd..ffb66bc995 100644 --- a/packages/client/ui-conversation/README.i18n.yaml +++ b/packages/client/ui-conversation/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-conversation/README.md -README.md: b70bb96cc877279cc29662773eddf6fd7d6453ca -README.zh.md: 604beeb336054376971277446e1690137132c29e +README.md: 6f47cd87af46cc270d3160482ad047b249aa5053 +README.zh.md: 38e3c626073e9e90a16eebdb91af1e89d4da7c22 diff --git a/packages/client/ui-conversation/README.md b/packages/client/ui-conversation/README.md index b70bb96cc8..6f47cd87af 100644 --- a/packages/client/ui-conversation/README.md +++ b/packages/client/ui-conversation/README.md @@ -24,10 +24,15 @@ The resident composer survives no-Session and Session transitions. The no-Sessio `conversation.composer` is a generic chain. Its complete owner currency is: -```ts -export interface ComposerChainProps { +```ts type-equiv +/** Owner values used to elect a composer takeover. */ +interface ComposerChainProps { + /** Current Session identity used by temporary business-owned entries. */ sessionId: SessionId | undefined + /** Current Session lifecycle state, absent without a selected Session. */ session: SessionSnapshot | undefined + /** Effective business-owned interaction awaiting the user in this Session. */ + pendingInteraction: SessionPendingInteraction | undefined } ``` @@ -62,6 +67,14 @@ try { The selector must be a pure function of the owner currency. Its non-null return is delivered to the component as `matched`; `PropsRuntime<'conversation.composer'>` supplies the standard Session and global props. Chain order remains ascending `priority`, then registration order, and the first non-null selector wins. The shell keeps the default composer mounted beneath a takeover. Request state, listeners, response encoding, and any request-specific child slots belong to the business package; they are not carried by `SessionSnapshot` or declared by this core package. -## Model experience +## Model Experience -None. The package renders browser state and sends user-admitted inputs through Session Controller APIs; it does not construct model requests. +None, as this package renders browser state and sends user-admitted inputs through Session Controller APIs without constructing model requests. + +#### KV Cache effect + +None; Conversation assembly and browser input state do not alter provider-side prompt caching. + +## Known Limitations and Deferred Work + +- **Only registered targets can render** — the shell deliberately has no implicit fallback target beyond the registered `chat` preference. diff --git a/packages/client/ui-conversation/README.zh.md b/packages/client/ui-conversation/README.zh.md index 604beeb336..38e3c62607 100644 --- a/packages/client/ui-conversation/README.zh.md +++ b/packages/client/ui-conversation/README.zh.md @@ -24,10 +24,15 @@ View 选择规则固定:有效且已注册的持久化选择优先,其次是 `conversation.composer` 是通用 chain,其完整 owner currency 为: -```ts -export interface ComposerChainProps { +```ts type-equiv +/** Owner values used to elect a composer takeover. */ +interface ComposerChainProps { + /** Current Session identity used by temporary business-owned entries. */ sessionId: SessionId | undefined + /** Current Session lifecycle state, absent without a selected Session. */ session: SessionSnapshot | undefined + /** Effective business-owned interaction awaiting the user in this Session. */ + pendingInteraction: SessionPendingInteraction | undefined } ``` @@ -64,4 +69,12 @@ selector 必须是 owner currency 的纯函数。非 null 返回值作为 `match ## 模型体验 -无。本包渲染浏览器状态,并通过 Session Controller API 发送用户确认提交的输入;它不构造模型请求。 +无,因为本包渲染浏览器状态,并通过 Session Controller API 发送用户确认提交的输入,而不构造模型请求。 + +#### KV Cache 影响 + +无;Conversation 组装和浏览器输入状态不会改变提供方侧的 prompt cache。 + +## 已知限制与暂缓事项 + +- **只有已注册 target 可以渲染**——除已注册的 `chat` 偏好外,shell 刻意不提供隐式 fallback target。 diff --git a/packages/client/ui-conversation/src/client/apply.ts b/packages/client/ui-conversation/src/client/apply.ts index a570478a45..0edc19c42f 100644 --- a/packages/client/ui-conversation/src/client/apply.ts +++ b/packages/client/ui-conversation/src/client/apply.ts @@ -9,6 +9,7 @@ import type {} from '@deepseek-ai/dsh-client-locale/client' import type {} from '@deepseek-ai/dsh-client-ui-renderer/client' import type {} from '@deepseek-ai/dsh-client-ui-session/client' import type {} from '@deepseek-ai/dsh-client-ui-settings/client' +import type {} from '@deepseek-ai/dsh-client-ui-workspace/client' import { UiConversation } from './conversation/assembly.ts' import type { ViewTab } from './contract/views.ts' import type { diff --git a/packages/client/ui-jobs/README.i18n.yaml b/packages/client/ui-jobs/README.i18n.yaml index 2767bc42f9..23ebb49d68 100644 --- a/packages/client/ui-jobs/README.i18n.yaml +++ b/packages/client/ui-jobs/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-jobs/README.md -README.md: e8c1852aca0b7fb375b3c58748a9f37385e8ebee -README.zh.md: b9f980283a7cbddce3c8a4de07079e5fc3b61212 +README.md: 0523aa10ebfbaa6cde0bef391017bc0307d8bdd5 +README.zh.md: bb713f446855a9e8b23c5543827b9897f1177a48 diff --git a/packages/client/ui-jobs/README.md b/packages/client/ui-jobs/README.md index e8c1852aca..0523aa10eb 100644 --- a/packages/client/ui-jobs/README.md +++ b/packages/client/ui-jobs/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Web background-job feature owner: contributes one entry to `conversation.session.header.actions` listing the `ctx.jobs` records this session can see. The data arrives entirely through the `jobsBySession` list mirror that [`dsh-client-runtime`](../runtime/README.md) folds from `session/jobs` frames, so this package issues no RPC and holds no state beyond popover visibility. +Web background-job feature owner: contributes one entry to `conversation.session.header.actions` listing the `ctx.jobs` records this session can see. The data arrives entirely through the `jobsBySession` list mirror that the [Session Controller](../../api/session-controller/README.md) folds from `session/jobs` frames, so this package issues no RPC and holds no state beyond popover visibility. The trigger renders only when the session has at least one job, so an ordinary conversation never grows a control for a capability it is not using. Its badge counts `running` plus `stopping` and is omitted at zero, leaving a session that holds only finished jobs a quiet entry point into its history rather than one advertising a count of nothing. The popover is a flat list: live rows first by `startedAt` ascending, then settled rows by `finishedAt` descending, with a same-millisecond tie broken on start order so the host's map iteration never decides it. A row shows the producer kind, the label, a status marker, the producer's `detail` in place of the generic status word once it has one, and an elapsed duration. That duration advances once per second while the row is live and freezes at `finishedAt`; the clock runs only while an open list holds something that moves. A settled row missing `finishedAt` reads as zero rather than as a negative figure, and a duration past an hour stays in hours rather than growing a day vocabulary no producer currently reaches. diff --git a/packages/client/ui-jobs/README.zh.md b/packages/client/ui-jobs/README.zh.md index b9f980283a..bb713f4468 100644 --- a/packages/client/ui-jobs/README.zh.md +++ b/packages/client/ui-jobs/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -Web 后台任务特性的归属方:向 `conversation.session.header.actions` 贡献一个条目,列出当前会话可见的 `ctx.jobs` 记录。数据完全来自 [`dsh-client-runtime`](../runtime/README.zh.md) 从 `session/jobs` 帧折叠出的 `jobsBySession` 列表镜像,因此本包不发任何 RPC,除弹层开合外不持有任何状态。 +Web 后台任务特性的归属方:向 `conversation.session.header.actions` 贡献一个条目,列出当前会话可见的 `ctx.jobs` 记录。数据完全来自 [Session Controller](../../api/session-controller/README.zh.md) 从 `session/jobs` 帧折叠出的 `jobsBySession` 列表镜像,因此本包不发任何 RPC,除弹层开合外不持有任何状态。 只有当会话至少有一个任务时才渲染触发器,普通对话不会因为一项未被使用的能力而长出控件。角标计数为 `running` 加 `stopping`,为零时省略,这样只剩已完成任务的会话保留一个安静的历史入口,而不是宣告一个「零」。弹层是一个扁平列表:活跃行在前按 `startedAt` 升序,随后终态行按 `finishedAt` 降序;毫秒相同的并列按启动顺序打破,宿主的 map 迭代顺序永远不参与决定。一行显示生产者 kind、label、状态标记、生产者一旦给出 `detail` 就取代通用状态词的那段文字,以及已耗时。该耗时在活跃时每秒推进,并在 `finishedAt` 冻结;只有当打开的列表里确实有会动的东西时时钟才运行。缺少 `finishedAt` 的终态行读作零而不是负数,超过一小时的耗时停留在小时单位,不会长出任何生产者目前都到不了的「天」词汇。 diff --git a/packages/client/ui-session/README.i18n.yaml b/packages/client/ui-session/README.i18n.yaml new file mode 100644 index 0000000000..f5a325ce8c --- /dev/null +++ b/packages/client/ui-session/README.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write packages/client/ui-session/README.md +README.md: d18dec6232ff43112af761e27104cc50db84b169 +README.zh.md: 4c72c767a3a2cddef7dc9793b88425b3d20a4d90 diff --git a/packages/client/ui-session/README.md b/packages/client/ui-session/README.md new file mode 100644 index 0000000000..d18dec6232 --- /dev/null +++ b/packages/client/ui-session/README.md @@ -0,0 +1,17 @@ +# @deepseek-ai/dsh-client-ui-session + +English | [中文](README.zh.md) + +React and Slot adapter for Session Controller state. It contributes Session list and pending-interaction hooks at root scope, materializes per-Session hooks and props, and owns the standard `SessionProvider` rendering behavior without taking ownership of Session transport or lifecycle state. + +## Model Experience + +None, as this package adapts browser-side Session state and registers nothing model-facing. + +#### KV Cache effect + +None; Session selectors and Slot scopes do not assemble model requests. + +## Known Limitations and Deferred Work + +- **Pending interactions are process-local projections** — the owning Remote waterfall must replay an outstanding request after a browser reconnect. diff --git a/packages/client/ui-session/README.zh.md b/packages/client/ui-session/README.zh.md new file mode 100644 index 0000000000..4c72c767a3 --- /dev/null +++ b/packages/client/ui-session/README.zh.md @@ -0,0 +1,17 @@ +# @deepseek-ai/dsh-client-ui-session + +[English](README.md) | 中文 + +面向 Session Controller 状态的 React 与 Slot adapter。本包在 root scope 提供 Session list 和 pending-interaction hook,物化逐 Session hook 与 prop,并拥有标准 `SessionProvider` 渲染行为,但不接管 Session transport 或 lifecycle 状态。 + +## 模型体验 + +无,因为本包适配浏览器侧 Session 状态,不注册任何面向模型的内容。 + +#### KV Cache 影响 + +无;Session selector 与 Slot scope 不会组装模型请求。 + +## 已知限制与暂缓事项 + +- **Pending interaction 是进程本地投影**——浏览器重连后,所属 Remote waterfall 必须重放仍未完成的请求。 diff --git a/packages/client/ui-session/src/client/index.ts b/packages/client/ui-session/src/client/index.ts index 2af1804cd3..36676d1403 100644 --- a/packages/client/ui-session/src/client/index.ts +++ b/packages/client/ui-session/src/client/index.ts @@ -77,7 +77,6 @@ class PendingInteractionDomain { return () => { if (!active) return active = false - if (this.values.get(interaction.key) !== interaction) return this.values.delete(interaction.key) this.changed() } @@ -206,7 +205,7 @@ export class UiSession extends Service { private readonly pendingListeners = new Set<() => void>() /** Root source of pending UI interactions, independent from Controller snapshots. */ readonly pendingInteractions: HostObservable = { - getSnapshot: () => this.pendingSnapshot as SessionPendingInteractionSnapshot, + getSnapshot: () => this.pendingSnapshot, subscribe: (listener) => { this.pendingListeners.add(listener) return () => { this.pendingListeners.delete(listener) } @@ -294,7 +293,7 @@ export class UiSession extends Service { this.publishPendingInteractions() return () => { const index = this.pendingDomains.indexOf(runtimeDomain) - if (index !== -1) this.pendingDomains.splice(index, 1) + this.pendingDomains.splice(index, 1) this.publishPendingInteractions() } }, 'uiSession.registerPendingInteraction()') @@ -366,19 +365,18 @@ export class UiSession extends Service { private createMaterializedBinding(owner: SessionBinding): MaterializedBinding { const value = this.materialize(owner) - let releaseEffect: () => void | Promise = () => {} - const record: MaterializedBinding = { - owner, - value, - release: () => { void releaseEffect() }, - } - releaseEffect = owner.ctx.effect(() => () => { + const releaseEffect = owner.ctx.effect(() => () => { if (this.bindings.get(owner.sessionId) !== record) return this.bindings.delete(owner.sessionId) if (this.currentBinding !== value) return this.currentBinding = this.absent notifySubscribers(this.currentListeners, '[ui-session] current binding') }, `ui-session: binding ${owner.sessionId}`) + const record: MaterializedBinding = { + owner, + value, + release: () => { void releaseEffect() }, + } return record } diff --git a/packages/client/ui-session/tests/ui-session.client.spec.ts b/packages/client/ui-session/tests/ui-session.client.spec.ts index 63293251fd..4045e4abb9 100644 --- a/packages/client/ui-session/tests/ui-session.client.spec.ts +++ b/packages/client/ui-session/tests/ui-session.client.spec.ts @@ -260,6 +260,19 @@ describe('UiSession bindings', () => { ) }) + it('releases cached bindings when the owning Client context stops', async () => { + const ctx = new Context() + const bench = createSessionsBench(ctx) + const service = createUiSession(ctx, bench) + const id = sessionId('s1') + bench.binding(id) + bench.select(id) + service.adapter.current.getSnapshot() + + await expect(ctx.fiber.dispose()).resolves.toBeUndefined() + await bench.release(id) + }) + it('rebuilds live bindings and removes only the disposed source contribution', () => { const ctx = new Context() const bench = createSessionsBench(ctx) @@ -370,10 +383,30 @@ describe('UiSession bindings', () => { })).toThrow("uiSession.provide: duplicate prop 'useFeature' at prop 'useFeature'") expect(service.adapter.current.getSnapshot()).toBe(before) }) + + it('releases partially rebuilt bindings when a later Session contribution fails', () => { + const ctx = new Context() + const bench = createSessionsBench(ctx) + const service = createUiSession(ctx, bench) + service.adapter.resolve(bench.binding(sessionId('s1')).sessionId) + service.adapter.resolve(bench.binding(sessionId('s2')).sessionId) + let calls = 0 + + expect(() => service.provide({ + props: ['partial'], + resolve: () => { + calls += 1 + if (calls === 2) throw new Error('second binding failed') + return { props: { partial: true } } + }, + })).toThrow('second binding failed') + expect(calls).toBe(2) + expect(service.adapter.resolve(sessionId('s1'))?.props).not.toHaveProperty('partial') + }) }) describe('UiSession pending interactions', () => { - it('publishes the highest-precedence exact object and removes each source independently', () => { + it('publishes the highest-precedence exact object and removes each source independently', async () => { const ctx = new Context() const bench = createSessionsBench(ctx) const service = createUiSession(ctx, bench) @@ -386,12 +419,16 @@ describe('UiSession pending interactions', () => { const registerQuestion = service.registerPendingInteraction( interaction => interaction.kind === 'plan-review' ? 2 : 1, ) + const registerBackground = service.registerPendingInteraction( + () => -1, + ) listener.mockClear() const approval = { key: 'approval:1', kind: 'approval', sessionId: id } const duplicate = { key: 'approval:2', kind: 'approval', sessionId: id } const question = { key: 'question:1', kind: 'question', sessionId: id } const plan = { key: 'question:2', kind: 'plan-review', sessionId: id } + const background = { key: 'background:1', kind: 'background', sessionId: id } const removeApproval = registerApproval(approval) expect(service.pendingInteractions.getSnapshot().get(id)).toBe(approval) const removeDuplicate = registerApproval(duplicate) @@ -400,7 +437,10 @@ describe('UiSession pending interactions', () => { expect(service.pendingInteractions.getSnapshot().get(id)).toBe(question) const removePlan = registerQuestion(plan) expect(service.pendingInteractions.getSnapshot().get(id)).toBe(plan) + const removeBackground = registerBackground(background) + expect(service.pendingInteractions.getSnapshot().get(id)).toBe(plan) + removeBackground() removeQuestion() expect(service.pendingInteractions.getSnapshot().get(id)).toBe(plan) removePlan() @@ -408,8 +448,10 @@ describe('UiSession pending interactions', () => { removeDuplicate() expect(service.pendingInteractions.getSnapshot().get(id)).toBe(approval) removeApproval() + removeApproval() expect(service.pendingInteractions.getSnapshot().has(id)).toBe(false) off() + await ctx.fiber.dispose() }) it('rejects duplicate keys and contains a failing aggregate subscriber', () => { diff --git a/packages/client/ui-sidebar/src/client/index.ts b/packages/client/ui-sidebar/src/client/index.ts index 0960a67bb9..401274034d 100644 --- a/packages/client/ui-sidebar/src/client/index.ts +++ b/packages/client/ui-sidebar/src/client/index.ts @@ -6,6 +6,8 @@ import type {} from '@deepseek-ai/dsh-client-locale/client' import type {} from '@deepseek-ai/dsh-client-ui-renderer/client' // Type-only: pulls the Session root standard-props merge. import type {} from '@deepseek-ai/dsh-client-ui-session/client' +// Type-only: records the Workspace UI service dependency used below. +import type {} from '@deepseek-ai/dsh-client-ui-workspace/client' import type { SidebarRootInjected } from './contract/slots.ts' import { SidebarRoot } from './SidebarRoot.tsx' import { en, zh, type SidebarKey } from './locales.ts' diff --git a/packages/client/ui-slots/README.i18n.yaml b/packages/client/ui-slots/README.i18n.yaml index 2f9729cfbd..01538011c2 100644 --- a/packages/client/ui-slots/README.i18n.yaml +++ b/packages/client/ui-slots/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-slots/README.md -README.md: 332bd092e8a4dec5a424acccce57c3dd0d8c27d8 -README.zh.md: 369776ea72c6f21184c0accd183aa9b409ee867a +README.md: 7d5bffae32f4f1056f54d02f4a949bc7ed34058d +README.zh.md: 8c242b94c88aa7414a6e32641269fb1e4ce4308c diff --git a/packages/client/ui-slots/README.md b/packages/client/ui-slots/README.md index 332bd092e8..7d5bffae32 100644 --- a/packages/client/ui-slots/README.md +++ b/packages/client/ui-slots/README.md @@ -19,7 +19,7 @@ The standard-kit interfaces (`SessionStandardProps`, `GlobalStandardProps`) are The store family (`defineStore` spec in / `StoreHandle` out) types the store seat: `init` infers the state schema, `actions` is the complete draft-transform write set, `BakedActions` strips the draft parameter into the callbacks components and inject factories receive. The `defineStore` value implementation lives in the runtime package (the engine's home) and satisfies the `DefineStore` contract exported here. Engine products and the renderer host contract carry bare snapshot sources (`getSnapshot`/`subscribe`), never React hooks — hook binding belongs to the render machinery; only the props-contract hook type (`SnapshotSelectorHook`) lives here. -`SlotCore` seeds the a-priori `'root'` slot at construction and enforces load-time validation (undeclared-slot registration, duplicate child declaration, one shared handle under two scopes, a chain registration without `select` — all throw at register). An entry's disposer collapses its declared child slots recursively: ledger rows, contributions, and store mounts die on one lifecycle axis. Each key also carries a declaration epoch that advances only on declaration and collapse; the runtime uses it for [`ctx.slots.inject`](../runtime/README.md#slot-declaration-injection), independently from ordinary entry versions. `renderer.ts` carries the installation contract (`SlotRenderer`, `SlotRendererHost`) plus `StaleAuthorizationError`/`SlotOwnershipError`; ui-renderer owns both the implementation and its plugin-lifecycle installation. +`SlotCore` seeds the a-priori `'root'` slot at construction and enforces load-time validation (undeclared-slot registration, duplicate child declaration, one shared handle under two scopes, a chain registration without `select` — all throw at register). An entry's disposer collapses its declared child slots recursively: ledger rows, contributions, and store mounts die on one lifecycle axis. Each key also carries a declaration epoch that advances only on declaration and collapse; `ui-renderer` uses it for [`ctx.slots.inject`](../../../.agents/notes/implemented/architecture/2026-08-05-slot-declaration-injection.md), independently from ordinary entry versions. `renderer.ts` carries the installation contract (`SlotRenderer`, `SlotRendererHost`) plus `StaleAuthorizationError`/`SlotOwnershipError`; ui-renderer owns both the implementation and its plugin-lifecycle installation. ## Model Experience diff --git a/packages/client/ui-slots/README.zh.md b/packages/client/ui-slots/README.zh.md index 369776ea72..8c242b94c8 100644 --- a/packages/client/ui-slots/README.zh.md +++ b/packages/client/ui-slots/README.zh.md @@ -19,7 +19,7 @@ chain-kind slot 会反转键控路由:条目自行提名,而不是由分发 store 家族(输入 `defineStore` 规范/输出 `StoreHandle`)为 store seat 建模:`init` 推断状态 schema;`actions` 是完整的 draft-transform 写入集合;`BakedActions` 移除 draft 参数,成为组件和 inject factory 收到的回调。`defineStore` 值实现位于运行时包(引擎所属位置),并满足这里导出的 `DefineStore` 约定。引擎产物与 renderer host 约定携带裸快照 source(`getSnapshot`/`subscribe`),绝不携带 React 钩子;钩子绑定属于渲染机制,只有 props 约定钩子类型(`SnapshotSelectorHook`)位于这里。 -`SlotCore` 在构造时预置 `'root'` slot,并强制执行加载时验证(注册未声明 slot、重复声明子项、在两个 scope 下使用同一个共享 handle、chain 注册缺少 `select`,这些情况都在 register 时抛出)。条目的 disposer 会递归移除其声明的子 slot:账本行、贡献和 store 挂载都会随同一生命周期结束而移除。每个 key 还携带一个 declaration epoch(声明代次),它只在声明与移除时递增;运行时将其用于 [`ctx.slots.inject`](../runtime/README.zh.md#slot-declaration-injection),且与普通条目版本相互独立。`renderer.ts` 携带安装约定(`SlotRenderer`、`SlotRendererHost`)以及 `StaleAuthorizationError`/`SlotOwnershipError`;ui-renderer 同时持有实现及其插件生命周期安装。 +`SlotCore` 在构造时预置 `'root'` slot,并强制执行加载时验证(注册未声明 slot、重复声明子项、在两个 scope 下使用同一个共享 handle、chain 注册缺少 `select`,这些情况都在 register 时抛出)。条目的 disposer 会递归移除其声明的子 slot:账本行、贡献和 store 挂载都会随同一生命周期结束而移除。每个 key 还携带一个 declaration epoch(声明代次),它只在声明与移除时递增;`ui-renderer` 将其用于 [`ctx.slots.inject`](../../../.agents/notes/implemented/architecture/2026-08-05-slot-declaration-injection.zh.md),且与普通条目版本相互独立。`renderer.ts` 携带安装约定(`SlotRenderer`、`SlotRendererHost`)以及 `StaleAuthorizationError`/`SlotOwnershipError`;ui-renderer 同时持有实现及其插件生命周期安装。 ## 模型体验 diff --git a/packages/client/ui-user-questions/tests/browser-plugin.client.spec.ts b/packages/client/ui-user-questions/tests/browser-plugin.client.spec.ts index 64baeaf3ee..866ac883e0 100644 --- a/packages/client/ui-user-questions/tests/browser-plugin.client.spec.ts +++ b/packages/client/ui-user-questions/tests/browser-plugin.client.spec.ts @@ -53,6 +53,7 @@ async function bench(declare = true) { const registerPendingInteraction = vi.fn((_precedence: (value: PendingQuestion) => number) => ( value: PendingQuestion, ) => { + _precedence(value) pending = [...pending, value] return () => { pending = pending.filter(candidate => candidate !== value) } }) diff --git a/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx b/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx index afa4be8aeb..9016b974d9 100644 --- a/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx +++ b/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx @@ -876,7 +876,7 @@ describe('plugin lifecycle', () => { ctx.provide('remote', { $on: () => () => {} } as never) ctx.provide('settingsScope', { bind: () => stubSettingsScope().scope } as never) await ctx.plugin(TestSessions).await() - const conversationEvents = new UiConversation(ctx, ctx.sessions as never).events + const conversationEvents = new UiConversation(ctx, ctx.sessions).events ctx.slots.register({ name: 'root', children: { 'conversation.chat.node': { kind: 'keyed', scope: 'session' } }, diff --git a/packages/client/ui-workspace/tests/tree.client.spec.ts b/packages/client/ui-workspace/tests/tree.client.spec.ts index c32359ce9e..767109dd98 100644 --- a/packages/client/ui-workspace/tests/tree.client.spec.ts +++ b/packages/client/ui-workspace/tests/tree.client.spec.ts @@ -57,6 +57,19 @@ describe('deriveGroups', () => { .toMatchObject({ pendingInteraction: 'plan-review', running: true }) }) + it.each(['approval', 'question'] as const)( + 'projects the %s pending-interaction kind', + (kind) => { + const awaiting = summary(kind, 10) + const attention: ReadonlyMap = new Map([[ + awaiting.id, + { key: `${kind}:1`, kind, sessionId: awaiting.id }, + ]]) + + expect(deriveFlat(list(awaiting), noArchive, attention)[0]?.pendingInteraction).toBe(kind) + }, + ) + it('puts only real unaccounted Sessions in the trailing Ungrouped group', () => { const sessions = list(summary('owned', 1, '/projects/first'), summary('loose', 9, '/other')) const groups = deriveGroups( diff --git a/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts b/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts index f3377f10e6..3419d73e89 100644 --- a/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts +++ b/packages/extensions/cordis-client-runner/src/client/slot-catalog.ts @@ -91,13 +91,16 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: MaybeSnapshotSelectorHook', - 'sessionId: SessionId | undefined', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useConversation: MaybeSnapshotSelectorHook', 'useInput: MaybeSnapshotSelectorHook', 'inputActions: InputActions | undefined', + 'useSession: MaybeSnapshotSelectorHook', + 'sessionId: SessionId | undefined', + 'useProjection: UseProjection', ], keyDomain: '', hookContext: '', @@ -108,14 +111,50 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation\', () => ctx.slots.register(\n { name: \'conversation\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-layout/src/client/index.ts:62', + source: 'packages/client/ui-layout/src/client/index.ts:64', + }, + { + key: 'conversation.approval.detail', + kind: 'single', + scope: 'session', + summary: 'Optional detail for the Tool call correlated with an approval request.', + doc: 'Optional detail for the Tool call correlated with an approval request.', + registerOptions: [], + ownerProps: [ + '/** Stable identity handed to an optional approval-detail renderer. */\nexport interface ApprovalDetailOwnerProps {\n /** Tool call correlated with the request. */\n callId: CallId\n}', + ], + ownerPropsReferences: [], + standardProps: [ + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', + 'useInput: SnapshotSelectorHook', + 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', + ], + keyDomain: '', + hookContext: '', + slotInject: '', + declaredBy: 'an entry in \'conversation.composer\' (client-ui-approval), so it exists while that entry is mounted', + occupants: [ + 'client-ui-chat ApprovalCommand', + ], + replaceRisk: 'shadows-shipped-ui', + example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.approval.detail\', () => ctx.slots.register(\n { name: \'conversation.approval.detail\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', + source: 'packages/client/ui-approval/src/client/contract/slots.ts:25', }, { key: 'conversation.chat.assistant-actions', kind: 'list', scope: 'session', - summary: 'Action strip attached to one finalized assistant message, rendered inside that message\'s IconActions row.', - doc: 'Action strip attached to one finalized assistant message, rendered\ninside that message\'s IconActions row. The chat entry owns the render\nsite and passes the addressed message identity; contributors add\nper-message actions without importing the conversation implementation.\nEntries render by ascending `order`.', + summary: 'Ordered actions for one finalized assistant message.', + doc: 'Ordered actions for one finalized assistant message. Each entry receives\nthe durable message id; a fresh `id` adds an action and reusing one replaces\nthat entry. With no entries, the standard action row remains unchanged.', registerOptions: [ { name: 'id', @@ -137,37 +176,42 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * Owner currency of the assistant-message action strip: the durable identity\n * of the one finalized message the contributed actions address. Only finalized\n * messages reach this slot, so the id is always present.\n */\nexport interface AssistantActionOwnerProps {\n /** Stable identity carried from the `assistant/message` event. */\n messageId: MessageId\n}', + '/** Owner currency of finalized-assistant actions. */\nexport interface AssistantActionOwnerProps {\n messageId: MessageId\n}', ], ownerPropsReferences: [ 'MessageId', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', slotInject: '', - declaredBy: 'an entry in \'conversation.chat.node\' (client-ui-conversation), so it exists while that entry is mounted', + declaredBy: 'an entry in \'conversation.chat.node\' (client-ui-chat), so it exists while that entry is mounted', occupants: [ 'client-ui-message-feedback MessageFeedbackActions id \'feedback\'', ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.assistant-actions\', () => ctx.slots.register(\n { name: \'conversation.chat.assistant-actions\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:148', + source: 'packages/client/ui-chat/src/client/contract/slots.ts:197', }, { key: 'conversation.chat.commandview', kind: 'keyed', scope: 'session', - summary: 'The chat view\'s per-command row hole: keyed dispatch on the command name (`command/run.name`; a run-less cross-window node has none and always lands on the fallback).', - doc: 'The chat view\'s per-command row hole: keyed dispatch on the command\nname (`command/run.name`; a run-less cross-window node has none and\nalways lands on the fallback). Declared by the chat view entry; the\nrender site dispatches via `entryKey: name` with GenericCommandCard as\nthe `fallback` — a slash command renders durably with zero\nregistration, and a domain upgrades by registering one row component.', + summary: 'Command row keyed by the command name.', + doc: 'Command row keyed by the command name. The component receives the folded\ncommand lifecycle and linked compaction when present. Reusing a key\nreplaces that command renderer; an unoccupied key uses the generic card.', registerOptions: [ { name: 'key', @@ -177,36 +221,42 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * Owner share of the per-command row slot: the frozen {@link CommandNode}\n * slice off the snapshot (cache-stable reference — memo premise). The node\n * carries the whole lifecycle (structured name/args, pairing id, and\n * outcome-or-executing). A successful domain command may also carry the\n * explicitly linked projection node needed to fold two log records into one\n * presentation row.\n */\nexport interface CommandRowOwnerProps {\n /** Folded command lifecycle node (run + optional done). */\n node: CommandNode\n /** Explicitly linked compaction checkpoint for the settled `/compact` presentation. */\n compaction?: CompactionSummaryNode\n}', + '/** Command-row owner share. */\nexport interface CommandRowOwnerProps {\n node: CommandNode\n compaction?: CompactionSummaryNode\n}', ], ownerPropsReferences: [ + 'Command', 'CommandNode', 'CompactionSummaryNode', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: 'open: any string the owner dispatches (no compile-time key set), none are taken yet', hookContext: '', slotInject: '', - declaredBy: 'an entry in \'conversation.chat.node\' (client-ui-conversation), so it exists while that entry is mounted', + declaredBy: 'an entry in \'conversation.chat.node\' (client-ui-chat), so it exists while that entry is mounted', occupants: [], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.commandview\', () => ctx.slots.register(\n { name: \'conversation.chat.commandview\', key: \'\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:133', + source: 'packages/client/ui-chat/src/client/contract/slots.ts:185', }, { key: 'conversation.chat.node', kind: 'keyed', scope: 'session', - summary: 'Final business node renderer, dispatched by `ChatConversationViewNode.kind`.', - doc: 'Final business node renderer, dispatched by `ChatConversationViewNode.kind`.', + summary: 'Final Chat node renderer, keyed by `ChatNodeKind`.', + doc: 'Final Chat node renderer, keyed by `ChatNodeKind`. The component receives\nthe typed node, shared Chat actions, and Turn-data hook. Reusing a key\nreplaces that node renderer; a kind with no occupant renders no row.', registerOptions: [ { name: 'key', @@ -216,7 +266,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/** Stable owner currency delivered to one keyed Chat business renderer. */\nexport interface ChatNodeOwnerProps {\n /** Selected Tool call, when the shared details store names one. */\n selectedCallId?: CallId | undefined\n /** Session workspace root; Tool summaries display paths relative to it. */\n cwd?: string | undefined\n openFile: (path: string) => void\n inspectCall: (callId: CallId) => void\n forkAt: (seq: number) => void\n /** Render a historical image group through the attachment slot. */\n renderMessageImages: RenderMessageImages\n fileMentions: (owner: TurnTailOwnerProps) => MarkdownFileMentions | undefined\n}', + '/** Stable owner currency delivered to a keyed Chat renderer. */\nexport interface ChatNodeOwnerProps {\n selectedCallId?: CallId | undefined\n cwd?: string | undefined\n openFile: (path: string) => void\n inspectCall: (callId: CallId) => void\n forkAt: (seq: number) => void\n renderMessageImages: RenderMessageImages\n fileMentions: (owner: TurnTailOwnerProps) => MarkdownFileMentions | undefined\n}', ], ownerPropsReferences: [ 'MarkdownFileMentions', @@ -224,45 +274,50 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ 'TurnTailOwnerProps', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: 'fixed by the owner\'s key table { [Kind in ChatNodeKind]: { node: ChatNode } }, already taken: assistant-step, command, command-input, compaction, context, manual-compaction, model-retry, steering, tool-call, turn-error, turn-max-tokens, turn-tail, unknown, user, workflow-run', hookContext: 'string', slotInject: 'ChatNodeTurnDataInjected', - declaredBy: 'an entry in \'conversation.view\' (client-ui-conversation), so it exists while that entry is mounted', + declaredBy: 'an entry in \'conversation.view\' (client-ui-chat), so it exists while that entry is mounted', occupants: [ - 'client-ui-conversation UserMessageNodeView key \'user\'', - 'client-ui-conversation UserMessageNodeView key \'steering\'', - 'client-ui-conversation ContextMessageNodeView key \'context\'', - 'client-ui-conversation AssistantNodeView key \'assistant-step\'', - 'client-ui-conversation CommandNodeView key \'command\'', - 'client-ui-conversation ManualCompactionNodeView key \'manual-compaction\'', - 'client-ui-conversation CompactionNodeView key \'compaction\'', - 'client-ui-conversation RetryNodeView key \'model-retry\'', - 'client-ui-conversation TurnErrorNodeView key \'turn-error\'', - 'client-ui-conversation TurnMaxTokensNodeView key \'turn-max-tokens\'', - 'client-ui-conversation TurnTailNodeView key \'turn-tail\'', - 'client-ui-conversation UnknownNodeView key \'unknown\'', + 'client-ui-chat UserMessageNodeView key \'user\'', + 'client-ui-chat UserMessageNodeView key \'steering\'', + 'client-ui-chat ContextMessageNodeView key \'context\'', + 'client-ui-chat AssistantNodeView key \'assistant-step\'', + 'client-ui-chat CommandNodeView key \'command\'', + 'client-ui-chat ManualCompactionNodeView key \'manual-compaction\'', + 'client-ui-chat CompactionNodeView key \'compaction\'', + 'client-ui-chat RetryNodeView key \'model-retry\'', + 'client-ui-chat TurnErrorNodeView key \'turn-error\'', + 'client-ui-chat TurnMaxTokensNodeView key \'turn-max-tokens\'', + 'client-ui-chat TurnTailNodeView key \'turn-tail\'', + 'client-ui-chat UnknownNodeView key \'unknown\'', 'client-ui-goal GoalCommandInputView key \'command-input\'', 'client-ui-tool ToolCallTree key \'tool-call\'', 'client-ui-workflow-run WorkflowRunPanel key \'workflow-run\'', ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.node\', () => ctx.slots.register(\n { name: \'conversation.chat.node\', key: \'\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:115', + source: 'packages/client/ui-chat/src/client/contract/slots.ts:166', }, { key: 'conversation.chat.turnTail', kind: 'chain', scope: 'session', - summary: 'The completed Turn Node\'s extension chain, rendered before that Node\'s IconActions.', - doc: 'The completed Turn Node\'s extension chain, rendered before that Node\'s\nIconActions. Entries derive a match from the engine-owned Turn and\nclosing seq before mounting, so presentation components never mount\nonly to return null; an all-declined chain renders nothing.', + summary: 'Selector-routed extension before a completed Turn\'s action row.', + doc: 'Selector-routed extension before a completed Turn\'s action row. The\ncomponent receives the Turn, closing sequence, and file opener. The first\nselector that accepts the owner renders; an all-declined chain is empty.', registerOptions: [ { name: 'select', @@ -272,37 +327,42 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * Owner currency of the chat view\'s turn-tail hole: the engine-owned Turn and\n * the closing assistant\'s anchor. Registrants read their own typed Turn data\n * and open files through the same opener the tool rows use.\n */\nexport interface TurnTailOwnerProps {\n /** Engine-owned closing Turn boundary. */\n turn: TurnLocation\n /** The closing assistant\'s seq — the anchor the tail renders under. */\n seq: number\n /**\n * Open a filesystem path through the Host (tool-row semantics; the chat\n * view resolves relative paths against the session cwd).\n */\n openFile: (path: string) => void\n}', + '/** Owner currency of the completed-Turn extension chain. */\nexport interface TurnTailOwnerProps {\n turn: TurnLocation\n seq: number\n openFile: (path: string) => void\n}', ], ownerPropsReferences: [ 'TurnLocation', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', slotInject: '', - declaredBy: 'an entry in \'conversation.chat.node\' (client-ui-conversation), so it exists while that entry is mounted', + declaredBy: 'an entry in \'conversation.chat.node\' (client-ui-chat), so it exists while that entry is mounted', occupants: [ 'client-ui-deliverables ProducedFiles', ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.turnTail\', () => ctx.slots.register(\n { name: \'conversation.chat.turnTail\', select: owner => null },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:140', + source: 'packages/client/ui-chat/src/client/contract/slots.ts:191', }, { key: 'conversation.composer', kind: 'chain', scope: 'session', - summary: 'The composer takeover chain: entries are selector-routed replacements of the default InputBar.', - doc: 'The composer takeover chain: entries are selector-routed replacements\nof the default InputBar. Declared by this package\'s \'conversation\'\nentry; the owner dispatches the ComposerChainProps currency and\nrouting lives in entry selectors — new takeover kinds register with\nzero owner changes.', + summary: 'Selector-routed replacements for the current Session\'s resident composer.', + doc: 'Selector-routed replacements for the current Session\'s resident composer.', registerOptions: [ { name: 'select', @@ -312,55 +372,64 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * Composer chain currency: what ConversationRoot dispatches at its\n * renderSlotChain site. The owner declares the currency only — never a\n * per-entry contract; takeover packages narrow it in their own selectors\n * (`interactions.find(i => i.kind === ...)`), so new takeover kinds register\n * with zero owner changes.\n */\nexport interface ComposerChainProps {\n /** Effective domain-owned interaction selected for this Session. */\n pendingInteraction: PendingInteraction | undefined\n /** Current conversation facts for feature-owned takeover selectors. */\n session: ConversationSnapshot | undefined\n}', + '/** Owner values used to elect a composer takeover. */\nexport interface ComposerChainProps {\n /** Current Session identity used by temporary business-owned entries. */\n sessionId: SessionId | undefined\n /** Current Session lifecycle state, absent without a selected Session. */\n session: SessionSnapshot | undefined\n /** Effective business-owned interaction awaiting the user in this Session. */\n pendingInteraction: SessionPendingInteraction | undefined\n}', ], ownerPropsReferences: [ - 'ConversationSnapshot', - 'PendingInteraction', + 'SessionId', + 'SessionPendingInteraction', + 'SessionSnapshot', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', slotInject: '', declaredBy: 'an entry in \'conversation\' (client-ui-conversation), so it exists while that entry is mounted', occupants: [ - 'client-ui-conversation ApprovalPanel', + 'client-ui-approval ApprovalPanel', 'client-ui-subagent SubagentReadOnlyComposer', 'client-ui-user-questions QuestionComposer', ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.composer\', () => ctx.slots.register(\n { name: \'conversation.composer\', select: owner => null },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:171', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:78', }, { key: 'conversation.composer.bar', kind: 'single', scope: 'session-maybe', - summary: 'The default composer body: a single slot rendered as the composer chain\'s fallback (a real entry, not a chain rider, so a takeover election hides rather than unmounts it and the textarea DOM survives).', - doc: 'The default composer body: a single slot rendered as the composer\nchain\'s fallback (a real entry, not a chain rider, so a\ntakeover election hides rather than unmounts it and the textarea DOM\nsurvives). Session-maybe: the bar stays mounted across the\nno-session/session transition — the no-workspace hero renders the SAME\ntextarea DOM as a read-only Workspace-picker trigger instead of a\nparallel inert tree — with the machine hooks absent until a session is\ncurrent. InputBar registers\nhere from this package\'s apply; its machine state arrives through the\nstandard provide channel (useInput + inputActions), the keyboard\ncommand face through its own inject.', + summary: 'Resident composer body, including the no-Session inert state.', + doc: 'Resident composer body, including the no-Session inert state.', registerOptions: [], ownerProps: [ - '/**\n * Owner share of the composer-bar slot: ConversationRoot\'s layout-phase\n * inputs plus the input-region child-slot content it renders (the region\n * slots stay declared/rendered by the conversation entry; the bar hosts the\n * results as chrome).\n */\nexport interface ComposerBarOwnerProps {\n /** Hero = empty-state centered card; composer = resident bottom bar. */\n variant: \'hero\' | \'composer\'\n /**\n * A block another plugin raised for this session: the bar refuses input and\n * shows the blocker\'s reason as the placeholder, but — unlike `disabled` —\n * keeps the model seat live. Every block this contract has is one the user\n * clears by choosing a model, so locking that seat too would leave the\n * composer telling them to do the one thing it prevents.\n */\n blocked?: { readonly reason: string }\n /**\n * Inert no-workspace state: the bar locks message actions while preserving\n * its normal DOM so the Workspace pick transitions in place.\n */\n disabled?: boolean\n /** Whether the shared Workspace picker menu is expanded, regardless of which trigger opened it. */\n workspacePickerOpen?: boolean\n /** Open the existing Workspace picker from the inert textarea. */ /* …truncated — full shape in source */', + '/** Owner share of the resident composer bar. */\nexport interface ComposerBarOwnerProps {\n /** Hero uses centered placement; composer uses the active bottom placement. */\n variant: \'hero\' | \'composer\'\n /** A feature-owned reason that makes message input inert while leaving model selection live. */\n blocked?: { readonly reason: string }\n /** Lock all message actions while preserving the resident textarea. */\n disabled?: boolean\n /** Whether the shared Workspace picker is expanded. */\n workspacePickerOpen?: boolean\n /** Open the Workspace picker from the inert textarea. */\n onRequestWorkspace?: () => void\n placeholder?: string\n /** Optional content rendered above the textarea. */\n accessory?: ReactNode\n /** Floating overlay content rendered inside the composer card. */\n overlay?: ReactNode\n /** Left-side input controls. */\n leftItems?: ReactNode\n /** Right-side input controls. */\n rightItems?: ReactNode\n /** Ambient content below the card. */\n footer?: ReactNode\n}', ], ownerPropsReferences: [ 'Workspace', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: MaybeSnapshotSelectorHook', - 'sessionId: SessionId | undefined', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useConversation: MaybeSnapshotSelectorHook', 'useInput: MaybeSnapshotSelectorHook', 'inputActions: InputActions | undefined', + 'useSession: MaybeSnapshotSelectorHook', + 'sessionId: SessionId | undefined', + 'useProjection: UseProjection', ], keyDomain: '', hookContext: '', @@ -371,14 +440,14 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.composer.bar\', () => ctx.slots.register(\n { name: \'conversation.composer.bar\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:245', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:96', }, { key: 'conversation.composer.dock', kind: 'list', scope: 'session', - summary: 'The band under the composer card, inside the bar\'s width column — the seat for an ambient readout about the conversation (the shipped stats line lives here).', - doc: 'The band under the composer card, inside the bar\'s width column — the\nseat for an ambient readout about the conversation (the shipped stats\nline lives here). Same InputZone owner share as the other\nregions. Anything the user must click belongs in the tool row instead\n(`conversation.input.left` / `.right`); anything needing its own line\nabove the card belongs in `conversation.input.dock`.', + summary: 'Ambient entries below the composer card.', + doc: 'Ambient entries below the composer card.', registerOptions: [ { name: 'id', @@ -400,77 +469,89 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * The input-region slot currency: dock/left/right entries read\n * the conversation snapshot and the live input state as owner props (both\n * are point-in-time snapshots — the dispatching skeleton re-renders on\n * either store\'s change, so entries stay current without subscribing).\n */\nexport interface InputZone {\n readonly session: ConversationSnapshot\n readonly input: InputState\n}', + '/** Point-in-time owner values for composer extension entries. */\nexport interface InputZone {\n readonly session: SessionSnapshot\n readonly input: InputState\n}', ], ownerPropsReferences: [ - 'ConversationSnapshot', 'InputState', + 'SessionSnapshot', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', slotInject: '', declaredBy: 'an entry in \'conversation\' (client-ui-conversation), so it exists while that entry is mounted', occupants: [ - 'client-ui-conversation StatsLine id \'stats\'', + 'client-ui-chat StatsLine id \'stats\'', ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.composer.dock\', () => ctx.slots.register(\n { name: \'conversation.composer.dock\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:214', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:90', }, { key: 'conversation.details.tool', kind: 'single', scope: 'session', - summary: 'The body of the details panel for the tool call the user selected — one occupant, so taking it means rendering every tool\'s output, not just the ones you know.', - doc: 'The body of the details panel for the tool call the user selected —\none occupant, so taking it means rendering every tool\'s output, not just\nthe ones you know. The owner passes a frozen `block` whose two lifecycle\nforms must both be handled: branch on `\'kind\' in block` (a settled\n`ToolResultNode` has it, a still-running call does not), and treat\n`cwd` as display-only, for shortening workspace-rooted paths.\nA per-tool renderer belongs in the keyed `tool.call.toolview` seat\ninstead; this one is the whole panel.', + summary: 'Whole details-panel body for the selected Tool call.', + doc: 'Whole details-panel body for the selected Tool call. The component receives\nthe running or settled block and optional workspace root. A registration\nreplaces the shipped Tool details renderer; absence uses the raw fallback.', registerOptions: [], ownerProps: [ - '/** Owner currency of the details panel\'s Tool output renderer. */\nexport interface DetailsToolOwnerProps {\n /** Frozen selected call slice. */\n block: ToolCallBlock\n /** Session workspace root for card cwd and relative-path display. */\n cwd?: string | undefined\n}', + '/** Tool block rendered in the details panel. */\nexport interface DetailsToolOwnerProps {\n block: ToolCallBlock\n cwd?: string | undefined\n}', ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', slotInject: '', - declaredBy: 'an entry in \'details\' (client-ui-conversation), so it exists while that entry is mounted', + declaredBy: 'an entry in \'details\' (client-ui-chat), so it exists while that entry is mounted', occupants: [ 'client-ui-tool ToolDetails', ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.details.tool\', () => ctx.slots.register(\n { name: \'conversation.details.tool\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:163', + source: 'packages/client/ui-chat/src/client/contract/slots.ts:203', }, { key: 'conversation.hero.agentPreset', kind: 'single', scope: 'root', - summary: 'The agent-preset chip beside the workspace picker on the new-session screen.', - doc: 'The agent-preset chip beside the workspace picker on the new-session\nscreen. Root scope: no session exists yet, so the choice is staged for\nthe next one rather than applied to a current one.', + summary: 'Agent-preset control staged for a New Session.', + doc: 'Agent-preset control staged for a New Session.', registerOptions: [], ownerProps: [ - '/** Owner share of the hero agent-preset chip: the shell supplies nothing. */\nexport interface HeroAgentPresetOwnerProps {\n /** Marker field: the chip owns its own roster, staging, and menu state. */\n children?: never\n}', + '/** Owner share of the Hero agent-preset control. */\nexport interface HeroAgentPresetOwnerProps {\n /** Marker field: the occupant owns its roster and staged selection. */\n children?: never\n}', ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -481,22 +562,24 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.hero.agentPreset\', () => ctx.slots.register(\n { name: \'conversation.hero.agentPreset\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:189', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:84', }, { key: 'conversation.hero.brand.mark', kind: 'single', scope: 'root', - summary: 'Brand mark leading the blank-session headline.', - doc: 'Brand mark leading the blank-session headline. Declared by this\npackage\'s `conversation` entry; the shell supplies a fish fallback.', + summary: 'Brand mark shown before the blank-session headline.', + doc: 'Brand mark shown before the blank-session headline.', registerOptions: [], ownerProps: [ - '/** Presentation props supplied to the blank-session brand-mark occupant. */\nexport interface HeroBrandMarkOwnerProps {\n /** Requested square edge in pixels. */\n size: number\n /** Host CSS class for preserving the default hero mark color and hover motion. */\n className?: string | undefined\n}', + '/** Presentation props supplied to the blank-session brand mark. */\nexport interface HeroBrandMarkOwnerProps {\n /** Requested square edge in pixels. */\n size: number\n /** Host class preserving the surrounding mark geometry. */\n className?: string | undefined\n}', ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -507,24 +590,26 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.hero.brand.mark\', () => ctx.slots.register(\n { name: \'conversation.hero.brand.mark\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:183', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:82', }, { key: 'conversation.hero.workspace', kind: 'single', scope: 'root', - summary: 'The hero-phase Workspace picker hole: rendered by ConversationRoot while the session is blank (picking another workspace switches to that workspace\'s blank session, draft carried).', - doc: 'The hero-phase Workspace picker hole: rendered by ConversationRoot\nwhile the session is blank (picking another workspace switches to that\nworkspace\'s blank session, draft carried). Root scope: the picker\nreads the global workspace list.', + summary: 'Workspace picker shown by the blank-session Hero.', + doc: 'Workspace picker shown by the blank-session Hero.', registerOptions: [], ownerProps: [ - '/** Owner share common to the hero / New-Session Workspace pickers. */\nexport interface EmptyWorkspaceOwnerProps {\n open: boolean\n anchorRef?: RefObject\n /** Currently active workspace (renders a trailing check in the picker list). */\n selectedId?: WorkspaceId | undefined\n onPick: (workspaceId: WorkspaceId) => void\n onClose: () => void\n}', + '/** Owner share common to blank-session Workspace pickers. */\nexport interface EmptyWorkspaceOwnerProps {\n open: boolean\n anchorRef?: RefObject\n /** Currently selected Workspace, when available. */\n selectedId?: WorkspaceId | undefined\n onPick: (workspaceId: WorkspaceId) => void\n onClose: () => void\n}', ], ownerPropsReferences: [ 'Workspace', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -535,7 +620,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.hero.workspace\', () => ctx.slots.register(\n { name: \'conversation.hero.workspace\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:178', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:80', }, { key: 'conversation.hero.workspace.directoryFlow', @@ -549,8 +634,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -568,24 +655,27 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ key: 'conversation.input.attachments', kind: 'single', scope: 'session-maybe', - summary: 'Optional draft-image rail, drop target, and preview surface inside the composer.', - doc: 'Optional draft-image rail, drop target, and preview surface inside the composer.', + summary: 'Optional draft-image rail and drop target.', + doc: 'Optional draft-image rail and drop target.', registerOptions: [], ownerProps: [ - '/** Input state handed to the optional attachment presentation plugin. */\nexport interface ComposerAttachmentsOwnerProps {\n /** Browser-owned draft images in input order. */\n attachments: readonly ComposerAttachment[]\n /** Whether a document-level file drop may add images now. */\n canAcceptDrop: boolean\n /** Add one dropped batch through the composer\'s validation path. */\n onAddImages: (files: readonly File[]) => void\n /** Remove one draft image through the conversation service. */\n onRemoveImage: (id: DraftAttachmentId) => void\n /** Display-ready limits for the drop invitation. */\n dropLimits?: { readonly count: number; readonly size: string } | undefined\n}', + '/** Input state handed to the optional attachment presentation plugin. */\nexport interface ComposerAttachmentsOwnerProps {\n /** Browser-owned draft images in input order. */\n attachments: readonly ComposerAttachment[]\n /** Whether a document-level file drop may add images now. */\n canAcceptDrop: boolean\n /** Add one dropped batch through the composer\'s validation path. */\n onAddImages: (files: readonly File[]) => void\n /** Remove one draft image through the Conversation service. */\n onRemoveImage: (id: DraftAttachmentId) => void\n /** Display-ready limits for the drop invitation. */\n dropLimits?: { readonly count: number; readonly size: string } | undefined\n}', ], ownerPropsReferences: [ 'ComposerAttachment', 'DraftAttachmentId', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: MaybeSnapshotSelectorHook', - 'sessionId: SessionId | undefined', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useConversation: MaybeSnapshotSelectorHook', 'useInput: MaybeSnapshotSelectorHook', 'inputActions: InputActions | undefined', + 'useSession: MaybeSnapshotSelectorHook', + 'sessionId: SessionId | undefined', + 'useProjection: UseProjection', ], keyDomain: '', hookContext: '', @@ -596,14 +686,14 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.attachments\', () => ctx.slots.register(\n { name: \'conversation.input.attachments\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:247', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:98', }, { key: 'conversation.input.dock', kind: 'list', scope: 'session', - summary: 'A full-width row of its own, stacked above the composer card — the seat for anything that needs a line to itself (queue rows, a todo strip, a goal bar).', - doc: 'A full-width row of its own, stacked above the composer card — the seat\nfor anything that needs a line to itself (queue rows, a todo strip, a\ngoal bar). Pick this over the three seats below when your content wraps\nor carries prose; pick `conversation.composer.dock` for an ambient\nreadout under the card, and `conversation.input.left` /\n`.right` for a small control INSIDE the card\'s tool row.\nRead only `session`/`input` off the owner share (InputZone) —\nboth are point-in-time snapshots re-rendered for you, never subscribe.', + summary: 'Full-width entries above the composer card.', + doc: 'Full-width entries above the composer card.', registerOptions: [ { name: 'id', @@ -625,20 +715,25 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * The input-region slot currency: dock/left/right entries read\n * the conversation snapshot and the live input state as owner props (both\n * are point-in-time snapshots — the dispatching skeleton re-renders on\n * either store\'s change, so entries stay current without subscribing).\n */\nexport interface InputZone {\n readonly session: ConversationSnapshot\n readonly input: InputState\n}', + '/** Point-in-time owner values for composer extension entries. */\nexport interface InputZone {\n readonly session: SessionSnapshot\n readonly input: InputState\n}', ], ownerPropsReferences: [ - 'ConversationSnapshot', 'InputState', + 'SessionSnapshot', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -651,14 +746,14 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.dock\', () => ctx.slots.register(\n { name: \'conversation.input.dock\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:205', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:86', }, { key: 'conversation.input.left', kind: 'list', scope: 'session', - summary: 'The left end of the tool row INSIDE the composer card, after the resident chrome (access mode, plan, attach) — the seat for a small always-visible control.', - doc: 'The left end of the tool row INSIDE the composer card, after the\nresident chrome (access mode, plan, attach) — the seat for a small\nalways-visible control. Entries sit beside that chrome, never replace\nit. Same InputZone owner share; use `.right` for a control that\nbelongs next to the send button, and the docks for anything taller than\none row.', + summary: 'Compact controls at the left of the composer tool row.', + doc: 'Compact controls at the left of the composer tool row.', registerOptions: [ { name: 'id', @@ -680,20 +775,25 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * The input-region slot currency: dock/left/right entries read\n * the conversation snapshot and the live input state as owner props (both\n * are point-in-time snapshots — the dispatching skeleton re-renders on\n * either store\'s change, so entries stay current without subscribing).\n */\nexport interface InputZone {\n readonly session: ConversationSnapshot\n readonly input: InputState\n}', + '/** Point-in-time owner values for composer extension entries. */\nexport interface InputZone {\n readonly session: SessionSnapshot\n readonly input: InputState\n}', ], ownerPropsReferences: [ - 'ConversationSnapshot', 'InputState', + 'SessionSnapshot', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -702,27 +802,32 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ occupants: [], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.left\', () => ctx.slots.register(\n { name: \'conversation.input.left\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:223', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:92', }, { key: 'conversation.input.model', kind: 'single', scope: 'session', - summary: 'The named model-select seat at the right end of the composer tool row, left of the send button — one occupant, so taking it means rendering the whole model affordance yourself.', - doc: 'The named model-select seat at the right end of the composer tool row,\nleft of the send button — one occupant, so taking it means rendering the\nwhole model affordance yourself. Same `locked`-only owner share and same\nrenders-nothing-while-empty contract as the plan seat. Note the composer\ndeliberately keeps this seat LIVE while it refuses text for a\nmodel-related block: every such block is one the user clears by picking\na model here.', + summary: 'Model selector inside the composer tool row.', + doc: 'Model selector inside the composer tool row.', registerOptions: [], ownerProps: [ - '/**\n * Owner share of the two named composer control seats (plan / model): the\n * bar passes its disable state; the filling entry owns everything else.\n */\nexport interface InputControlOwnerProps {\n /** Session-removed lock (the bar\'s chrome disable state). */\n locked: boolean\n}', + '/** Owner share of the named plan and model controls. */\nexport interface InputControlOwnerProps {\n /** Whether the composer currently refuses interaction. */\n locked: boolean\n}', ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -733,14 +838,14 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.model\', () => ctx.slots.register(\n { name: \'conversation.input.model\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:271', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:106', }, { key: 'conversation.input.overlay', kind: 'list', scope: 'session', - summary: 'The InputBar floating overlay anchor: MenuView (this package) and the popupSelect shell (ui-commands) contribute list entries; each reads its own store and renders null while closed.', - doc: 'The InputBar floating overlay anchor: MenuView (this package) and the\npopupSelect shell (ui-commands) contribute list entries; each reads its\nown store and renders null while closed. Declared (children table) by\nui-conversation\'s composer entry; the anchor hides with the input\nunder a takeover.', + summary: 'Floating entries rendered inside the resident composer card.', + doc: 'Floating entries rendered inside the resident composer card.', registerOptions: [ { name: 'id', @@ -764,13 +869,18 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ownerProps: [], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -782,27 +892,32 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.overlay\', () => ctx.slots.register(\n { name: \'conversation.input.overlay\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-input-trigger/src/client/slots.ts:24', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:88', }, { key: 'conversation.input.plan', kind: 'single', scope: 'session', - summary: 'The named plan-status seat in the composer tool row, immediately right of the access-mode control — one occupant, so taking it means rendering the plan affordance yourself.', - doc: 'The named plan-status seat in the composer tool row, immediately right\nof the access-mode control — one occupant, so taking it means rendering\nthe plan affordance yourself. The owner passes only `locked` (see\nInputControlOwnerProps): honour it by refusing interaction, and\ntake everything else from the framework session kit or your own inject.\nUnoccupied, the seat renders nothing at all — the bar paints no\nplaceholder, so an absent plan plugin costs no layout.', + summary: 'Plan control inside the composer tool row.', + doc: 'Plan control inside the composer tool row.', registerOptions: [], ownerProps: [ - '/**\n * Owner share of the two named composer control seats (plan / model): the\n * bar passes its disable state; the filling entry owns everything else.\n */\nexport interface InputControlOwnerProps {\n /** Session-removed lock (the bar\'s chrome disable state). */\n locked: boolean\n}', + '/** Owner share of the named plan and model controls. */\nexport interface InputControlOwnerProps {\n /** Whether the composer currently refuses interaction. */\n locked: boolean\n}', ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -813,14 +928,14 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.plan\', () => ctx.slots.register(\n { name: \'conversation.input.plan\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:261', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:104', }, { key: 'conversation.input.right', kind: 'list', scope: 'session', - summary: 'The right end of the same tool row, before the primary send button — the seat for a control the user reaches on the way to sending (the model select sits in its own named seat just left of here).', - doc: 'The right end of the same tool row, before the primary send button —\nthe seat for a control the user reaches on the way to sending (the\nmodel select sits in its own named seat just left of here). Same\nInputZone owner share and the same one-row height budget as\n`conversation.input.left`.', + summary: 'Compact controls before the composer submit action.', + doc: 'Compact controls before the composer submit action.', registerOptions: [ { name: 'id', @@ -842,20 +957,25 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * The input-region slot currency: dock/left/right entries read\n * the conversation snapshot and the live input state as owner props (both\n * are point-in-time snapshots — the dispatching skeleton re-renders on\n * either store\'s change, so entries stay current without subscribing).\n */\nexport interface InputZone {\n readonly session: ConversationSnapshot\n readonly input: InputState\n}', + '/** Point-in-time owner values for composer extension entries. */\nexport interface InputZone {\n readonly session: SessionSnapshot\n readonly input: InputState\n}', ], ownerPropsReferences: [ - 'ConversationSnapshot', 'InputState', + 'SessionSnapshot', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -864,59 +984,68 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ occupants: [], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.right\', () => ctx.slots.register(\n { name: \'conversation.input.right\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:231', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:94', }, { key: 'conversation.message.images', kind: 'single', scope: 'session', - summary: 'Optional renderer for one consecutive group of durable message images.', - doc: 'Optional renderer for one consecutive group of durable message images.', + summary: 'Renderer for one consecutive group of durable message images.', + doc: 'Renderer for one consecutive group of durable message images. The owner\nsupplies image references, an authorized loader, and alignment. A\nregistration replaces the shipped gallery; without one, images are omitted.', registerOptions: [], ownerProps: [ - '/** Historical image group handed to the optional attachment presentation plugin. */\nexport interface MessageImagesOwnerProps {\n /** Consecutive image blocks rendered as one gallery. */\n images: readonly { readonly attachment: ImageAttachmentRef }[]\n /** Session-authorized durable image loader. */\n loadImage: (attachment: ImageAttachmentRef) => Promise\n /** Message-side alignment. */\n align: \'start\' | \'end\'\n}', + '/** Historical image group handed to the optional attachment presentation plugin. */\nexport interface MessageImagesOwnerProps {\n images: readonly { readonly attachment: ImageAttachmentRef }[]\n loadImage: (attachment: ImageAttachmentRef) => Promise\n align: \'start\' | \'end\'\n}', ], ownerPropsReferences: [ 'ImageAttachmentRef', - 'Message', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', slotInject: '', - declaredBy: 'an entry in \'conversation.view\' (client-ui-conversation), so it exists while that entry is mounted', + declaredBy: 'an entry in \'conversation.view\' (client-ui-chat), so it exists while that entry is mounted', occupants: [ 'client-ui-attachment MessageImages', ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.message.images\', () => ctx.slots.register(\n { name: \'conversation.message.images\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:124', + source: 'packages/client/ui-chat/src/client/contract/slots.ts:179', }, { key: 'conversation.session', kind: 'single', scope: 'session', - summary: 'The entire body of one session: taking this seat means rendering that session\'s conversation yourself.', - doc: 'The entire body of one session: taking this seat means rendering that\nsession\'s conversation yourself. The occupant also owns the per-session\ndraft mirror and the active view ring, so a replacement inherits both\nduties and an empty one leaves a blank session pane — nothing here\ndegrades gracefully. To ADD rather than replace, take a seat inside the\nflow instead: `conversation.view` for a whole tab, the input regions for\ncomposer chrome.', + summary: 'Strict per-Session Conversation body.', + doc: 'Strict per-Session Conversation body.', registerOptions: [], ownerProps: [], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -927,25 +1056,30 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session\', () => ctx.slots.register(\n { name: \'conversation.session\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:71', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:54', }, { key: 'conversation.session.header', kind: 'single', scope: 'session', - summary: 'The strip above the session\'s scrollport: title, view tabs, and the action row.', - doc: 'The strip above the session\'s scrollport: title, view tabs, and the\naction row. Taking this seat means rendering all three yourself, and it\nalso collapses `conversation.session.header.actions` — that additive\nseat is declared by whoever occupies this one, so replacing the header\ntakes every action entry down with it.', + summary: 'Strict per-Session title, actions, and View navigation.', + doc: 'Strict per-Session title, actions, and View navigation.', registerOptions: [], ownerProps: [], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -956,14 +1090,14 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session.header\', () => ctx.slots.register(\n { name: \'conversation.session.header\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:79', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:56', }, { key: 'conversation.session.header.actions', kind: 'list', scope: 'session', - summary: 'One button in the session header\'s action row — the additive way to put a per-session control beside the title without replacing the header.', - doc: 'One button in the session header\'s action row — the additive way to put\na per-session control beside the title without replacing the header.\nEntries render by ascending `order`; negative values are reserved for\nstatic session context that precedes interactive actions. The owner\npasses nothing: everything a control needs comes from the framework\nsession kit (`sessionId`, `useSession`, `useInput`, `inputActions`) and\nfrom the registrant\'s own inject face, so an empty owner share means\nself-sufficient, not starved.', + summary: 'Title-adjacent Session actions in ascending order.', + doc: 'Title-adjacent Session actions in ascending order.', registerOptions: [ { name: 'id', @@ -985,17 +1119,22 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/** Header actions derive their state from the standard session/global kit. */\nexport interface ConversationHeaderActionOwnerProps {}', + '/** Header actions derive their state from standard Session props. */\nexport interface ConversationHeaderActionOwnerProps {\n /** Marker field: entries receive no owner-specific values. */\n children?: never\n}', ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -1007,29 +1146,34 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session.header.actions\', () => ctx.slots.register(\n { name: \'conversation.session.header.actions\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:100', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:64', }, { key: 'conversation.session.header.lineage', kind: 'single', scope: 'session', - summary: 'One breadcrumb title and its lineage controls.', - doc: 'One breadcrumb title and its lineage controls. The render site keeps\nthe ordinary title as fallback; an occupant receives plain title data\nand may replace a subagent title with one combined navigation control.', + summary: 'Optional replacement for one Session breadcrumb title.', + doc: 'Optional replacement for one Session breadcrumb title.', registerOptions: [], ownerProps: [ - '/** Plain breadcrumb data handed to the optional lineage renderer. */\nexport interface ConversationHeaderLineageOwnerProps {\n /** Session represented by this breadcrumb title. */\n lineageSessionId: SessionId\n /** Display title available to a renderer that combines the title with a control. */\n displayTitle: string\n /** Navigate to an ancestor title when its combined control is clicked. */\n openTitle?: () => void\n}', + '/** Plain breadcrumb data handed to the optional lineage renderer. */\nexport interface ConversationHeaderLineageOwnerProps {\n /** Session represented by this breadcrumb title. */\n lineageSessionId: SessionId\n /** Display title available to a combined title/control renderer. */\n displayTitle: string\n /** Navigate to an ancestor title when present. */\n openTitle?: () => void\n}', ], ownerPropsReferences: [ 'SessionId', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -1040,14 +1184,14 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session.header.lineage\', () => ctx.slots.register(\n { name: \'conversation.session.header.lineage\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:85', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:58', }, { key: 'conversation.session.header.utilities', kind: 'list', scope: 'session', - summary: 'Right-aligned Session utilities kept outside the title-adjacent action group, so an optional utility cannot reorder session context or lineage.', - doc: 'Right-aligned Session utilities kept outside the title-adjacent action\ngroup, so an optional utility cannot reorder session context or lineage.', + summary: 'Right-aligned Session utilities in ascending order.', + doc: 'Right-aligned Session utilities in ascending order.', registerOptions: [ { name: 'id', @@ -1069,17 +1213,22 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/** Header actions derive their state from the standard session/global kit. */\nexport interface ConversationHeaderActionOwnerProps {}', + '/** Header actions derive their state from standard Session props. */\nexport interface ConversationHeaderActionOwnerProps {\n /** Marker field: entries receive no owner-specific values. */\n children?: never\n}', ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', @@ -1090,14 +1239,14 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session.header.utilities\', () => ctx.slots.register(\n { name: \'conversation.session.header.utilities\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:105', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:70', }, { key: 'conversation.view', kind: 'list', scope: 'session', - summary: 'The conversation view ring: one list entry per view tab (chat here; trajectory/waterfall from ui-trajectory), rendered one-at-a-time by the session body via `only: `.', - doc: 'The conversation view ring: one list entry per view tab (chat here;\ntrajectory/waterfall from ui-trajectory), rendered one-at-a-time by\nthe session body via `only: `. Declared by this package\'s\nbody entry (declaring is claiming). Session scope: views read the\nconversation snapshot through the standard kit.', + summary: 'Registered Conversation target Views, rendered one at a time.', + doc: 'Registered Conversation target Views, rendered one at a time.', registerOptions: [ { name: 'id', @@ -1119,29 +1268,36 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ }, ], ownerProps: [ - '/**\n * View-slot owner share: the cross-view inspect handoff (otherwise views need\n * nothing from the render site — sessionId and the snapshot hook arrive as\n * framework-standard props; tool rows go through each view\'s own declared\n * toolview hole).\n */\nexport interface ConvViewOwnerProps {\n /** One-shot inspect request from another view (chat\'s Inspect button); null when idle. */\n inspect?: { callId: CallId } | null\n /** Acknowledge the inspect request once applied (clears the store field). */\n onInspectDone?: () => void\n}', + '/** Conversation View entries obtain their data from registered standard hooks. */\nexport interface ConvViewOwnerProps {\n /** Focus request addressed to the selected View. */\n viewRequest: import(\'./views.ts\').ConversationViewRequest | null\n /** Select a View and address one opaque focus identity to it. */\n openView: (view: string, focus: string) => void\n /** Acknowledge the current one-shot focus request. */\n completeViewRequest: () => void\n}', + ], + ownerPropsReferences: [ + 'ConversationViewRequest', ], - ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', slotInject: '', declaredBy: 'an entry in \'conversation.session\' (client-ui-conversation), so it exists while that entry is mounted', occupants: [ - 'client-ui-conversation ChatView id \'chat\'', + 'client-ui-chat ChatView id \'chat\'', 'client-ui-trajectory TrajectoryView id \'trajectory\'', ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.view\', () => ctx.slots.register(\n { name: \'conversation.view\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-conversation/src/client/contract/slots.ts:113', + source: 'packages/client/ui-conversation/src/client/contract/slots.ts:76', }, { key: 'details', @@ -1155,24 +1311,29 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: '', hookContext: '', slotInject: '', declaredBy: 'an entry in \'root\' (client-ui-layout), so it exists while that entry is mounted', occupants: [ - 'client-ui-conversation DetailsPanel', + 'client-ui-chat DetailsPanel', ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'details\', () => ctx.slots.register(\n { name: \'details\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-layout/src/client/index.ts:72', + source: 'packages/client/ui-layout/src/client/index.ts:74', }, { key: 'root', @@ -1186,8 +1347,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1198,7 +1361,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'root\', () => ctx.slots.register(\n { name: \'root\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/runtime/src/client/slots.ts:41', + source: 'packages/client/ui-renderer/src/client/registry.ts:43', }, { key: 'settings.action', @@ -1231,8 +1394,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1243,7 +1408,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.action\', () => ctx.slots.register(\n { name: \'settings.action\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-settings/src/client/contract/slots.ts:35', + source: 'packages/client/ui-settings/src/client/contract/slots.ts:36', }, { key: 'settings.close', @@ -1257,8 +1422,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1269,7 +1436,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.close\', () => ctx.slots.register(\n { name: \'settings.close\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-settings/src/client/contract/slots.ts:41', + source: 'packages/client/ui-settings/src/client/contract/slots.ts:42', }, { key: 'settings.general.item', @@ -1302,8 +1469,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1318,7 +1487,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.general.item\', () => ctx.slots.register(\n { name: \'settings.general.item\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-settings/src/client/contract/slots.ts:88', + source: 'packages/client/ui-settings/src/client/contract/slots.ts:89', }, { key: 'settings.header', @@ -1332,8 +1501,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1344,7 +1515,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.header\', () => ctx.slots.register(\n { name: \'settings.header\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-settings/src/client/contract/slots.ts:29', + source: 'packages/client/ui-settings/src/client/contract/slots.ts:30', }, { key: 'settings.onboarding', @@ -1377,8 +1548,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1390,7 +1563,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.onboarding\', () => ctx.slots.register(\n { name: \'settings.onboarding\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-settings/src/client/contract/slots.ts:73', + source: 'packages/client/ui-settings/src/client/contract/slots.ts:74', }, { key: 'settings.plugin.item', @@ -1411,8 +1584,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: 'open: any string the owner dispatches (no compile-time key set), none are taken yet', hookContext: '', @@ -1458,8 +1633,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1471,7 +1648,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.plugins.tab\', () => ctx.slots.register(\n { name: \'settings.plugins.tab\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-settings/src/client/contract/slots.ts:62', + source: 'packages/client/ui-settings/src/client/contract/slots.ts:63', }, { key: 'settings.section', @@ -1504,8 +1681,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1519,7 +1698,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.section\', () => ctx.slots.register(\n { name: \'settings.section\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-settings/src/client/contract/slots.ts:53', + source: 'packages/client/ui-settings/src/client/contract/slots.ts:54', }, { key: 'settings.trigger', @@ -1533,8 +1712,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1545,7 +1726,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.trigger\', () => ctx.slots.register(\n { name: \'settings.trigger\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-settings/src/client/contract/slots.ts:23', + source: 'packages/client/ui-settings/src/client/contract/slots.ts:24', }, { key: 'shell.overlay', @@ -1576,8 +1757,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ownerProps: [], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1586,7 +1769,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ occupants: [], replaceRisk: 'none', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'shell.overlay\', () => ctx.slots.register(\n { name: \'shell.overlay\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-layout/src/client/index.ts:83', + source: 'packages/client/ui-layout/src/client/index.ts:85', }, { key: 'sidebar', @@ -1600,8 +1783,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1612,7 +1797,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], replaceRisk: 'shadows-shipped-ui', example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'sidebar\', () => ctx.slots.register(\n { name: \'sidebar\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}', - source: 'packages/client/ui-layout/src/client/index.ts:49', + source: 'packages/client/ui-layout/src/client/index.ts:51', }, { key: 'sidebar.brand.mark', @@ -1626,8 +1811,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1652,8 +1839,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1697,8 +1886,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1723,8 +1914,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1749,8 +1942,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1775,8 +1970,10 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ ], ownerPropsReferences: [], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', ], keyDomain: '', hookContext: '', @@ -1811,13 +2008,18 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ 'Wire', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: 'open: any string the owner dispatches (no compile-time key set), already taken: ask_user_question, bash, cordis_define, cordis_run, cordis_stop, cordis_undefine, edit, glob, grep, read, skill, todo_write, web_fetch, web_search, write', hookContext: '', @@ -1867,13 +2069,18 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [ 'CordisDynamicPluginRunId', ], standardProps: [ - 'useSessions: SnapshotSelectorHook', - 'useWorkspaces: SnapshotSelectorHook', - 'useSession: SnapshotSelectorHook', - 'sessionId: SessionId', - 'useProjection: UseProjection', + 'useWorkspaces: SnapshotSelectorHook', + 'useSessions: UseSessions', + 'useSessionPendingInteraction: UseSessionPendingInteraction', + 'useWorkspaces: SnapshotSelectorHook', + 'useChat: UseChat', + 'useConversation: UseConversation', 'useInput: SnapshotSelectorHook', 'inputActions: InputActions', + 'useSession: SessionSnapshotSelector', + 'sessionId: SessionId', + 'useProjection: UseProjection', + 'useTrajectory: UseTrajectory', ], keyDomain: 'open: any string the owner dispatches (no compile-time key set), none are taken yet', hookContext: '', diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index e450f379cd..bb3e9918ca 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -2339,18 +2339,12 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ }, { key: 'userQuestions', - summary: '`ctx.userQuestions`: one active UI provider plus an `ask()` API.', - description: '`ctx.userQuestions`: one active UI provider plus an `ask()` API.', + summary: '`ctx.userQuestions`: validation plus the scoped answerer waterfall.', + description: '`ctx.userQuestions`: validation plus the scoped answerer waterfall.', methods: [ - { - signature: 'registerProvider(provider: UserQuestionProvider): () => void', - description: 'Register the UI provider. Only one provider may be active in a context.', - parameters: [{ name: 'provider', description: 'UI-side implementation that collects answers.' }], - returns: 'Disposer that unregisters this provider.', - }, { signature: 'async ask(request: AskUserQuestionRequest): Promise', - description: 'Ask the active UI provider and wait for the user\'s answer.\n\nWhen a caller supplies an agent, human interaction is valid only for the exact live runtime root. Runtime ownership, not durable session lineage, decides this boundary: an owned child has no human answerer and would block forever, while a lineage-bearing session resumed as a new runtime root may ask normally.', + description: 'Ask the scoped answerer waterfall and wait for the user\'s answer.\n\nWhen a caller supplies an agent, human interaction is valid only for the exact live runtime root. Runtime ownership, not durable session lineage, decides this boundary: an owned child has no human answerer and would block forever, while a lineage-bearing session resumed as a new runtime root may ask normally.', parameters: [{ name: 'request', description: 'Questions, owner agent, and abort signal.' }], returns: 'The answer chosen or typed by the human.', throws: ['{UserQuestionError} code `ASK_ABORTED` when the supplied signal is already or becomes aborted, `CALLER_NOT_LIVE` when a supplied agent is not the registry\'s exact live instance, or `DELEGATED_CALLER` when that live agent is owned by another agent.'], @@ -3185,11 +3179,11 @@ export const TYPE_API: readonly TypeApiEntry[] = [ }, { name: 'AskUserQuestionRequest', - declaration: 'export interface AskUserQuestionRequest {\n questions: AskUserQuestionItem[];\n agent?: Agent;\n signal?: AbortSignal;\n}', + declaration: 'export interface AskUserQuestionRequest extends AskUserQuestionRequestEvent {\n}', }, { name: 'AskUserQuestionRequestEvent', - declaration: 'export interface AskUserQuestionRequestEvent {\n questions: AskUserQuestionItem[];\n agent: Agent;\n signal?: AbortSignal;\n}', + declaration: 'export interface AskUserQuestionRequestEvent {\n questions: AskUserQuestionItem[];\n agent?: Agent;\n signal?: AbortSignal;\n}', }, { name: 'AssembleContext', @@ -4363,6 +4357,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'ServerResponse', declaration: 'export interface ServerResponse {\n type: \'server-response\';\n rpcId: RpcId;\n result: RpcResult;\n}', }, + { + name: 'Session', + declaration: 'export class Session {\n get surface(): SessionSurface;\n readonly header: SessionHeader;\n get id(): SessionId;\n readonly firstLiveSeq: number;\n static create(id: SessionId, seed?: readonly SessionEvent[], header?: SessionHeader): Session;\n static fromRestore(id: SessionId, seed: readonly SessionEvent[], header: SessionHeader): Session;\n get events(): readonly SessionEvent[];\n get seq(): number;\n append(type: T, data: SessionEventMap[T], ...opts: T extends SurfaceEventType ? [\n opts: SurfaceIntent\n ] : [\n ]): SessionEvent;\n requestHeader(): EpochHeader | undefined;\n requestContext(): RequestContext | undefined;\n deriveMessages(): Message[];\n deriveEventMessage(event: SessionEvent): Message | null;\n}', + }, { name: 'SessionAddress', declaration: 'export type SessionAddress = {\n readonly kind: \'session\';\n readonly sessionId: SessionId;\n} | {\n readonly kind: \'subagent\';\n readonly parentSessionId: SessionId;\n readonly childSessionId: SessionId;\n readonly mode: \'one-shot\' | \'continuable\';\n};', @@ -4683,6 +4681,14 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'SessionStartSource', declaration: 'export type SessionStartSource = \'startup\' | \'resume\' | \'clear\' | \'compact\';', }, + { + name: 'SessionSummary', + declaration: 'export interface SessionSummary {\n readonly sessionId: SessionId;\n readonly updatedAt: number;\n readonly running: boolean;\n readonly blank: boolean;\n readonly parentSessionId?: SessionId;\n readonly origin?: \'subagent\';\n readonly cwd?: string;\n readonly agentPreset?: string;\n readonly projections?: SessionProjectionsBlock;\n}', + }, + { + name: 'SessionSurface', + declaration: 'export interface SessionSurface {\n readonly nodes: readonly number[];\n readonly replaceGeneration: number;\n}', + }, { name: 'SessionSurfaceSnapshot', declaration: 'export interface SessionSurfaceSnapshot {\n session: SessionHeader;\n capturedThroughSeq: number | null;\n events: SurfaceEvent[];\n}', @@ -5047,6 +5053,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'SurfaceEventType', declaration: 'export type SurfaceEventType = \'user/message\' | \'assistant/message\' | \'tool/result\';', }, + { + name: 'SurfaceIntent', + declaration: 'export interface SurfaceIntent {\n surfaceOp: SurfaceOp;\n sourceEventSeqs?: number[];\n}', + }, { name: 'SurfaceOp', declaration: 'export type SurfaceOp = \'append\' | {\n op: \'replace\';\n start: number;\n end: number;\n};', @@ -5411,10 +5421,6 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'UserMessage', declaration: 'export interface UserMessage extends Message {\n readonly role: \'user\';\n}', }, - { - name: 'UserQuestionProvider', - declaration: 'export interface UserQuestionProvider {\n ask(request: AskUserQuestionRequest): Promise;\n}', - }, { name: 'VerifiedWebhookDelivery', declaration: 'export interface VerifiedWebhookDelivery {\n readonly kind: K;\n readonly source: WebhookSourceId;\n readonly deliveryId: WebhookDeliveryId;\n readonly event: WebhookEventOf;\n readonly receivedAt: number;\n}', diff --git a/packages/interaction/user-questions/src/index.ts b/packages/interaction/user-questions/src/index.ts index b51722d089..1d5d136426 100644 --- a/packages/interaction/user-questions/src/index.ts +++ b/packages/interaction/user-questions/src/index.ts @@ -8,6 +8,7 @@ */ import { Context, Service } from '@deepseek-ai/cordis' +import type {} from '@deepseek-ai/dsh-agent' import { HarnessError } from '@deepseek-ai/dsh-llm' import { scopeTarget } from '@deepseek-ai/dsh-scope' diff --git a/packages/test-support/client-runtime/tests/runtime.client.spec.tsx b/packages/test-support/client-runtime/tests/runtime.client.spec.tsx index a888168994..f98b93bb0c 100644 --- a/packages/test-support/client-runtime/tests/runtime.client.spec.tsx +++ b/packages/test-support/client-runtime/tests/runtime.client.spec.tsx @@ -256,9 +256,11 @@ describe('stores', () => { it('storeOf guards: before renderRoot, and for storeless entries', async () => { const runtime = await runtimeWithFrame() runtime.slots.register({ name: 'trt.panel' }, () => null) + runtime.slots.register({ name: 'trt.chat', store: createSuiteStore() }, () => null) expect(() => runtime.storeOf('trt.panel')).toThrow(/before renderRoot/) runtime.renderRoot() expect(() => runtime.storeOf('trt.panel')).toThrow(/declares no store/) + expect(() => runtime.storeOf('trt.chat', 'missing')).toThrow(/no live Session binding/) await runtime.dispose() }) diff --git a/packages/typert/protocol/README.i18n.yaml b/packages/typert/protocol/README.i18n.yaml index af8408917c..90f8a626e2 100644 --- a/packages/typert/protocol/README.i18n.yaml +++ b/packages/typert/protocol/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/typert/protocol/README.md -README.md: 84f9f1b31cca35cffce5dc5fc84db1017adaad08 -README.zh.md: 97e4734a7292f0a305ff8ed00ff1d72d1a53b5e9 +README.md: 3d8df3808a378cae115edb938160f73766f68747 +README.zh.md: 15247f37b0c08c1e0e9de38d159e5e7aea945109 diff --git a/packages/typert/registry/README.i18n.yaml b/packages/typert/registry/README.i18n.yaml index b3c081b128..fe01da8e7c 100644 --- a/packages/typert/registry/README.i18n.yaml +++ b/packages/typert/registry/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/typert/registry/README.md -README.md: fa227b1c8faf1abd5a6492d4b8fe7d0c51ceeef1 -README.zh.md: 62c5a2141de9ccdd564654658a464d721b77f97d +README.md: 2f3056735f5352c06b1e115a18c7ad1968ff90ae +README.zh.md: 770d3111fc65b08ee5d30ead6c3e6899630dd3e0 diff --git a/scripts/gen-cordis-catalog.ts b/scripts/gen-cordis-catalog.ts index 299d0eec76..d8bed47236 100644 --- a/scripts/gen-cordis-catalog.ts +++ b/scripts/gen-cordis-catalog.ts @@ -148,7 +148,9 @@ export const SERVICE_WALK_EXEMPTIONS: Record = { launchEnvironment: 'not a service: launcher-provided root accessor value (LaunchEnvironmentSnapshot | undefined) — packages/util/launch-environment/README.md owns this launcher contract', connection: 'interface-typed (HostConnectionHandle); implementing class HostConnectionService is declared in rpc-host.ts — packages/client/connection/README.md owns the API', uiRenderer: 'client-side interface-typed browser service — packages/client/ui-renderer/README.md owns the API', + uiSession: 'client-side Session source adapter — packages/client/ui-session/README.md owns the API', uiConversation: 'client-side Conversation registries and assembler — packages/client/ui-conversation/README.md owns the API', + uiWorkspace: 'client-side Workspace navigation adapter — packages/client/ui-workspace/README.md owns the API', settingsSchema: 'client-side schema introspection service — packages/client/ui-settings/README.md owns the API', settingsScope: 'client-side settings-namespace transport service — packages/client/ui-settings/README.md owns the API', chatFileMentions: 'client-side slot-contract accessor (ChatFileMentions) — packages/client/ui-chat/README.md owns the API', diff --git a/scripts/package-graph.spec.ts b/scripts/package-graph.spec.ts new file mode 100644 index 0000000000..fecbccedfe --- /dev/null +++ b/scripts/package-graph.spec.ts @@ -0,0 +1,51 @@ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it } from 'vitest' +import { collectPackageGraph } from './package-graph.ts' + +const roots: string[] = [] + +afterEach(() => { + for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }) +}) + +function fixture(packages: Readonly>): string { + const root = mkdtempSync(join(tmpdir(), 'dsh-package-graph-')) + roots.push(root) + for (const [name, dependencies] of Object.entries(packages)) { + const directory = join(root, 'packages', 'client', name) + mkdirSync(directory, { recursive: true }) + writeFileSync(join(directory, 'package.json'), `${JSON.stringify({ + name: `@deepseek-ai/dsh-${name}`, + peerDependencies: Object.fromEntries(dependencies.map(dependency => [ + `@deepseek-ai/dsh-${dependency}`, + 'workspace:^', + ])), + }, null, 2)}\n`) + } + return root +} + +describe('collectPackageGraph', () => { + it('orders packages after their dependencies', () => { + const root = fixture({ application: ['feature'], feature: ['foundation'], foundation: [] }) + + expect(collectPackageGraph(root, ['client'], 'fixture').map(pkg => pkg.short)) + .toEqual(['foundation', 'feature', 'application']) + }) + + it('keeps a dependency cycle together and before its consumers', () => { + const root = fixture({ consumer: ['left'], left: ['right'], right: ['left'], foundation: [] }) + + expect(collectPackageGraph(root, ['client'], 'fixture').map(pkg => pkg.short)) + .toEqual(['foundation', 'left', 'right', 'consumer']) + }) + + it('rejects a missing in-repo peer', () => { + const root = fixture({ consumer: ['missing'] }) + + expect(() => collectPackageGraph(root, ['client'], 'fixture')) + .toThrow('fixture: @deepseek-ai/dsh-consumer references missing in-repo peer @deepseek-ai/dsh-missing') + }) +}) diff --git a/scripts/package-graph.ts b/scripts/package-graph.ts index 0853b5c1ca..3d12a904b1 100644 --- a/scripts/package-graph.ts +++ b/scripts/package-graph.ts @@ -25,11 +25,12 @@ export interface PackageGraphNode { } /** - * Read every harness package manifest and return dependency-safe graph nodes. + * Read every harness package manifest and return dependency-first graph nodes. * @param root - absolute repository root. * @param groupOrder - caller-specific tiebreak order for packages in the same dependency layer. * @param gate - command name used in structural error messages. - * @returns package nodes ordered after all of their in-repo dependencies. + * @returns package nodes ordered after their in-repo dependencies, except for + * stable back edges inside a dependency cycle. */ export function collectPackageGraph(root: string, groupOrder: readonly string[], gate: string): PackageGraphNode[] { const packages: PackageGraphNode[] = [] @@ -57,14 +58,28 @@ export function collectPackageGraph(root: string, groupOrder: readonly string[], } function topoSort(packages: PackageGraphNode[], groupOrder: readonly string[], gate: string): PackageGraphNode[] { - const remaining = new Map(packages.map(pkg => [pkg.short, pkg])) + const byName = new Map(packages.map(pkg => [pkg.short, pkg])) + for (const pkg of packages) { + for (const dependency of pkg.deps) { + if (!byName.has(dependency)) { + throw new Error(`${gate}: ${pkg.name} references missing in-repo peer ${SCOPE}${dependency}`) + } + } + } + const remaining = new Map(byName) const placed = new Set() const out: PackageGraphNode[] = [] while (remaining.size > 0) { - const ready = [...remaining.values()] + let ready = [...remaining.values()] .filter(pkg => pkg.deps.every(dep => placed.has(dep))) .sort((a, b) => comparePackages(a, b, groupOrder)) - if (ready.length === 0) throw new Error(`${gate}: dependency cycle among ${[...remaining.keys()].join(', ')}`) + if (ready.length === 0) { + const cycle = sinkCycles(remaining) + .map(component => component.sort((a, b) => comparePackages(a, b, groupOrder))) + .sort((a, b) => comparePackages(a[0], b[0], groupOrder))[0] + if (cycle === undefined) throw new Error(`${gate}: could not order package dependency graph`) + ready = cycle + } for (const pkg of ready) { out.push(pkg) placed.add(pkg.short) @@ -74,6 +89,66 @@ function topoSort(packages: PackageGraphNode[], groupOrder: readonly string[], g return out } +type PackageGraphComponent = [PackageGraphNode, ...PackageGraphNode[]] + +function sinkCycles(remaining: ReadonlyMap): PackageGraphComponent[] { + let nextIndex = 0 + const indices = new Map() + const lowLinks = new Map() + const stack: PackageGraphNode[] = [] + const stacked = new Set() + const components: PackageGraphComponent[] = [] + + const visit = (pkg: PackageGraphNode): void => { + const index = nextIndex + nextIndex += 1 + indices.set(pkg.short, index) + lowLinks.set(pkg.short, index) + stack.push(pkg) + stacked.add(pkg.short) + for (const dependency of pkg.deps) { + const target = remaining.get(dependency) + if (target === undefined) continue + if (!indices.has(target.short)) { + visit(target) + lowLinks.set(pkg.short, Math.min(requiredValue(lowLinks, pkg.short), requiredValue(lowLinks, target.short))) + } else if (stacked.has(target.short)) { + lowLinks.set(pkg.short, Math.min(requiredValue(lowLinks, pkg.short), requiredValue(indices, target.short))) + } + } + if (lowLinks.get(pkg.short) !== indices.get(pkg.short)) return + const first = stack.pop() + if (first === undefined) throw new Error('package graph traversal lost its active component') + stacked.delete(first.short) + const component: PackageGraphComponent = [first] + let member = first + while (member !== pkg) { + const next = stack.pop() + if (next === undefined) throw new Error('package graph traversal lost its active component') + stacked.delete(next.short) + component.push(next) + member = next + } + components.push(component) + } + + for (const pkg of remaining.values()) { + if (!indices.has(pkg.short)) visit(pkg) + } + return components.filter((component) => { + const names = new Set(component.map(pkg => pkg.short)) + const first = component[0] + const cyclic = component.length > 1 || first.deps.includes(first.short) + return cyclic && component.every(pkg => pkg.deps.every(dep => !remaining.has(dep) || names.has(dep))) + }) +} + +function requiredValue(values: ReadonlyMap, key: K): V { + const value = values.get(key) + if (value === undefined) throw new Error('package graph traversal lost an indexed node') + return value +} + function comparePackages(a: PackageGraphNode, b: PackageGraphNode, groupOrder: readonly string[]): number { const groupA = groupOrder.indexOf(a.group) const groupB = groupOrder.indexOf(b.group) diff --git a/scripts/run-oxlint.spec.ts b/scripts/run-oxlint.spec.ts index 85628382e8..07ee0b3954 100644 --- a/scripts/run-oxlint.spec.ts +++ b/scripts/run-oxlint.spec.ts @@ -18,7 +18,7 @@ describe('Oxlint invocation', () => { it('uses location-preserving diagnostics in CI', () => { expect(resolveOxlintInvocation(['.'], { CI: 'true', DSH_OXLINT_THREADS: '4' })).toEqual({ - args: ['.', '--format=unix', '--threads=4'], + args: ['.', '--format=default', '--threads=4'], env: { CI: 'true', DSH_OXLINT_THREADS: '4', GOMAXPROCS: '4' }, }) }) diff --git a/scripts/run-oxlint.ts b/scripts/run-oxlint.ts index bcbddb5011..335bf213ed 100644 --- a/scripts/run-oxlint.ts +++ b/scripts/run-oxlint.ts @@ -32,7 +32,7 @@ export interface OxlintInvocation { */ export function resolveOxlintInvocation(args: readonly string[], env: NodeJS.ProcessEnv): OxlintInvocation { const resolvedArgs = [...args] - if (env.CI === 'true' && !hasOutputFormat(args)) resolvedArgs.push('--format=unix') + if (env.CI === 'true' && !hasOutputFormat(args)) resolvedArgs.push('--format=default') const raw = env.DSH_OXLINT_THREADS if (raw === undefined || raw === '') return { args: resolvedArgs, env: { ...env } } const parsed = Number.parseInt(raw, 10) diff --git a/scripts/type-equiv.manifest.json b/scripts/type-equiv.manifest.json index 05ff95154e..06cce11143 100644 --- a/scripts/type-equiv.manifest.json +++ b/scripts/type-equiv.manifest.json @@ -876,11 +876,6 @@ "symbol": "AskUserQuestionAnswer", "source": "packages/interaction/user-questions/src/types.ts" }, - { - "doc": "docs/subsystems/user-questions.md", - "symbol": "UserQuestionProvider", - "source": "packages/interaction/user-questions/src/index.ts" - }, { "doc": "docs/subsystems/user-questions.md", "symbol": "UserQuestionError", @@ -1980,6 +1975,11 @@ "doc": "docs/subsystems/feedback.md", "symbol": "MessageFeedbackDeleteResult", "source": "packages/feedback/message-feedback/src/types.ts" + }, + { + "doc": "packages/client/ui-conversation/README.md", + "symbol": "ComposerChainProps", + "source": "packages/client/ui-conversation/src/client/contract/slots.ts" } ] }