浏览代码

Merge branch 'code-mode-ui/web-ui-v1' into code-mode-ui/live-parallel

Conflict resolution: session.ts keeps this branch's paired-start callTime
(the start event exists here; a settle-only window stays null — the PR2
fix's semantics compose). The README's codeDispatches section is restored
to the running→settled lifecycle this branch implements, keeping PR2's
null-callTime nuance for starts outside the window.
Tianyi Cui 1 月之前
父节点
当前提交
7465dfe00f
共有 3 个文件被更改,包括 6 次插入1 次删除
  1. 2 0
      apps/cli/README.md
  2. 1 1
      packages/client/runtime/README.md
  3. 3 0
      packages/client/runtime/tests/session.spec.ts

+ 2 - 0
apps/cli/README.md

@@ -14,6 +14,8 @@ The TUI surface:
 
 The Web and headless surfaces boot one shared composition (`cordis.yml`): both treat the invoking directory as the default project and Workspace root, create named Workspaces beneath that root unless `--workspace-root <path>` overrides it, load applicable `AGENTS.md`/`CLAUDE.md` instructions into each agent-loop request prefix with a 65,536-byte render budget, and opt into first-message model titles. Headless differs only in listening on an OS-assigned port (parallel `dsh -p` runs never collide; the stderr-printed URL opens the live session in a browser). Both need the frontend dist and client bundles built (`pnpm run build && pnpm run build:web`).
 
+`DSH_TOOLS_MODE` selects the tool presentation mode for the whole Web/headless process: `native` (the schema default when unset), `code` (the `run_code`-only Code Mode wire), or `both`; any other value fails loud at boot through the `dsh-tools` config schema. It is a TEMPORARY seam — process-wide because Loader composition is static — and is removed once the web UI owns per-session tool-mode selection; the TUI surface ignores it (its config tree pins its own mode).
+
 ## Install (developer machine)
 
 Symlink the source-running launcher onto your PATH; it resolves the checkout through its own real path, so code changes apply on the next launch with no build step:

+ 1 - 1
packages/client/runtime/README.md

@@ -14,7 +14,7 @@ SlotsService gives the renderer separate bare observables for `useSessions` and
 
 ## Code Mode sub-dispatch index
 
-`ConversationSnapshot.codeDispatches` groups a `run_code` call's sub-dispatches under their parent callId, in start order, using the native call-block shapes: a started-but-unsettled sub-call is a `RunningToolCall` (rows derive the running ring from the shape) and its `tool/code-dispatch` settlement replaces it in place with the `ToolResultNode` form, `callTime` carrying the paired start's time. Live mux frames and history replay build the identical index; sub-calls never join the surface `nodes` flow; per-parent array and map references are memo-stable across unrelated snapshot swaps.
+`ConversationSnapshot.codeDispatches` groups a `run_code` call's sub-dispatches under their parent callId, in start order, using the native call-block shapes: a `tool/code-dispatch-start` event lands the `RunningToolCall` form (rows derive the running ring from the shape) and its `tool/code-dispatch` settlement replaces it in place with the `ToolResultNode` form, `callTime` carrying the paired start's time. A settle whose start fell outside the replay window appends directly with `callTime: null` (duration unknown — never a fabricated zero). Live mux frames and history replay build the identical index; sub-calls never join the surface `nodes` flow; per-parent array and map references are memo-stable across unrelated snapshot swaps.
 
 ## Session title projection
 

+ 3 - 0
packages/client/runtime/tests/session.spec.ts

@@ -685,6 +685,9 @@ describe('run_code sub-dispatch indexing', () => {
     expect(subs?.[0]).toMatchObject({
       kind: 'tool-result', callId: 'p1:code:1',
       call: { name: 'bash', argsRaw: '{"command":"ls","description":"列目录"}' },
+      // The settle event carries no start time: callTime stays null (never a
+      // fabricated zero-duration).
+      callTime: null,
       isError: false, content: [{ type: 'text', text: 'demo.txt' }],
     })
     expect(subs?.[1]).toMatchObject({ callId: 'p1:code:2', isError: true })