Procházet zdrojové kódy

refactor(llm): rename CallId to ToolCallId

_Kerman před 3 týdny
rodič
revize
a789637db6
100 změnil soubory, kde provedl 593 přidání a 593 odebrání
  1. 2 2
      apps/cli/tests/fixtures/dsh-badge/snapshot.ts
  2. 2 2
      apps/cli/tests/web-agent-presets.e2e.ts
  3. 2 2
      apps/web/tests/background-job-list.e2e.ts
  4. 3 3
      apps/web/tests/chat-continuous-conversation.e2e.ts
  5. 2 2
      apps/web/tests/chat-scroll-contract.e2e.ts
  6. 2 2
      apps/web/tests/chat-scroll-fixture.ts
  7. 3 3
      apps/web/tests/complex-history.perf.ts
  8. 4 4
      apps/web/tests/minimal-preset.snapshot.ts
  9. 2 2
      apps/web/tests/produced-file-mentions.e2e.ts
  10. 2 2
      apps/web/tests/produced-files.e2e.ts
  11. 2 2
      apps/web/tests/replay-round-trip.e2e.ts
  12. 4 4
      apps/web/tests/schedule-after.e2e.ts
  13. 4 4
      apps/web/tests/shipped-composition.e2e.ts
  14. 2 2
      docs/cordis-tutorial/07-into-the-harness.i18n.yaml
  15. 3 3
      docs/cordis-tutorial/07-into-the-harness.md
  16. 3 3
      docs/cordis-tutorial/07-into-the-harness.zh.md
  17. 2 2
      docs/persistence-catalog.i18n.yaml
  18. 4 4
      docs/persistence-catalog.md
  19. 4 4
      docs/persistence-catalog.zh.md
  20. 2 2
      docs/subsystems/approval.i18n.yaml
  21. 1 1
      docs/subsystems/approval.md
  22. 1 1
      docs/subsystems/approval.zh.md
  23. 2 2
      docs/subsystems/compaction.i18n.yaml
  24. 1 1
      docs/subsystems/compaction.md
  25. 1 1
      docs/subsystems/compaction.zh.md
  26. 2 2
      docs/subsystems/core.i18n.yaml
  27. 2 2
      docs/subsystems/core.md
  28. 2 2
      docs/subsystems/core.zh.md
  29. 2 2
      docs/subsystems/llm-streaming.i18n.yaml
  30. 2 2
      docs/subsystems/llm-streaming.md
  31. 2 2
      docs/subsystems/llm-streaming.zh.md
  32. 2 2
      docs/subsystems/session.i18n.yaml
  33. 1 1
      docs/subsystems/session.md
  34. 1 1
      docs/subsystems/session.zh.md
  35. 2 2
      docs/subsystems/spill.i18n.yaml
  36. 1 1
      docs/subsystems/spill.md
  37. 1 1
      docs/subsystems/spill.zh.md
  38. 2 2
      docs/subsystems/tools.i18n.yaml
  39. 4 4
      docs/subsystems/tools.md
  40. 4 4
      docs/subsystems/tools.zh.md
  41. 2 2
      docs/user/develop/practice/llm-adapter.i18n.yaml
  42. 3 3
      docs/user/develop/practice/llm-adapter.md
  43. 3 3
      docs/user/develop/practice/llm-adapter.zh.md
  44. 4 4
      examples/acp-agent/tests/fixtures/shell/tool-pwsh/driver.ts
  45. 3 3
      examples/acp-agent/tests/fixtures/subagent/subagent-acp/mock-delegating-llm.ts
  46. 2 2
      examples/headless-agent/tests/code-mode.e2e.ts
  47. 3 3
      examples/headless-agent/tests/fixtures/cli-mock-llm.ts
  48. 2 2
      examples/headless-agent/tests/fixtures/team-llm.mjs
  49. 3 3
      examples/headless-agent/tests/semantic-checkpoint.snapshot.ts
  50. 3 3
      examples/jsonrpc-agent/tests/fixtures/subagent/subagent-dsh-sdk/mock-delegating-llm.ts
  51. 3 3
      packages/acp/acp/tests/approval.spec.ts
  52. 3 3
      packages/acp/acp/tests/edges.spec.ts
  53. 2 2
      packages/client/connection/src/client/fixture.ts
  54. 2 2
      packages/client/runtime/tests/event-script.client.ts
  55. 5 5
      packages/client/ui-conversation/src/client/contract/slots.ts
  56. 3 3
      packages/client/ui-conversation/src/client/contract/views.ts
  57. 1 1
      packages/client/ui-conversation/src/client/index.ts
  58. 3 3
      packages/client/ui-conversation/src/client/stores.ts
  59. 8 8
      packages/compaction/compaction-basic/tests/compaction-basic.spec.ts
  60. 2 2
      packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts
  61. 2 2
      packages/compaction/compaction-tool-result-pruner/src/types.ts
  62. 6 6
      packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts
  63. 16 16
      packages/compaction/compaction/tests/tool-pairing.spec.ts
  64. 92 92
      packages/context/agent-instructions/tests/agent-instructions.spec.ts
  65. 2 2
      packages/context/session-reference/tests/session-reference.spec.ts
  66. 2 2
      packages/context/time-context/tests/time-context.spec.ts
  67. 2 2
      packages/core/agent-loop/tests/agent-initiator.spec.ts
  68. 10 10
      packages/core/agent-loop/tests/contract-regressions.spec.ts
  69. 3 3
      packages/core/agent-loop/tests/coverage-edges.spec.ts
  70. 3 3
      packages/core/agent-loop/tests/interception.spec.ts
  71. 4 4
      packages/core/agent-loop/tests/loop.spec.ts
  72. 2 2
      packages/core/agent-loop/tests/mock-adapter.ts
  73. 27 27
      packages/core/agent-loop/tests/tool-calls.spec.ts
  74. 3 3
      packages/core/session/src/chunk-rows.ts
  75. 3 3
      packages/core/session/src/invariant.ts
  76. 2 2
      packages/core/session/src/repair.ts
  77. 2 2
      packages/core/session/src/types.ts
  78. 6 6
      packages/core/session/tests/chunk-rows.spec.ts
  79. 2 2
      packages/core/session/tests/fork.spec.ts
  80. 14 14
      packages/core/session/tests/invariant.spec.ts
  81. 2 2
      packages/core/session/tests/properties.spec.ts
  82. 15 15
      packages/core/session/tests/repair.spec.ts
  83. 5 5
      packages/core/session/tests/session.spec.ts
  84. 5 5
      packages/core/session/tests/surface.spec.ts
  85. 2 2
      packages/core/tools/src/code-mode.ts
  86. 5 5
      packages/core/tools/src/index.ts
  87. 4 4
      packages/core/tools/src/types.ts
  88. 11 11
      packages/core/tools/tests/code-mode.spec.ts
  89. 2 2
      packages/core/tools/tests/execution-mode.spec.ts
  90. 2 2
      packages/core/tools/tests/execution-signal-types.spec.ts
  91. 41 41
      packages/core/tools/tests/invariant.spec.ts
  92. 14 14
      packages/core/tools/tests/scoped.spec.ts
  93. 94 94
      packages/core/tools/tests/tools.spec.ts
  94. 4 4
      packages/examples/agent-spine-demo/tests/agent-core.spec.ts
  95. 15 15
      packages/examples/agent-spine-demo/tests/multi-project-sandbox.e2e.ts
  96. 2 2
      packages/experimental/tool-agent-team/tests/tool-team.spec.ts
  97. 2 2
      packages/extensions/cordis-client-runner/src/client/slot-catalog.ts
  98. 2 2
      packages/extensions/cordis-host-runner/tests/helpers.ts
  99. 10 10
      packages/extensions/tool-cordis/src/api-catalog.ts
  100. 3 3
      packages/fs/tool-fs-search/tests/integration.spec.ts

+ 2 - 2
apps/cli/tests/fixtures/dsh-badge/snapshot.ts

@@ -1,7 +1,7 @@
 import { fileURLToPath } from 'node:url'
 import { Context } from '@deepseek-ai/cordis'
 import { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import { boot, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot'
 import { SessionId } from '@deepseek-ai/dsh-session'
 import type {} from '@deepseek-ai/dsh-skill'
@@ -45,7 +45,7 @@ try {
     : undefined
   const summary = (await ctx.skills.list()).find(skill => skill.name === 'dsh-badge')
   const result = await ctx.tools.execute({
-    callId: CallId('dsh-badge-snapshot'),
+    callId: ToolCallId('dsh-badge-snapshot'),
     name: 'skill',
     arguments: { name: 'dsh-badge' },
     signal: new AbortController().signal,

+ 2 - 2
apps/cli/tests/web-agent-presets.e2e.ts

@@ -13,7 +13,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
 import { settingsNamespace } from '@deepseek-ai/dsh-settings'
 import { resolveSessionPreset, SETTINGS_NAMESPACE } from '@deepseek-ai/dsh-agent-presets'
 import { applyChildComposition, childSessionMeta } from '@deepseek-ai/dsh-subagent'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import type {} from '@deepseek-ai/dsh-compaction-basic'
 import type {} from '@deepseek-ai/dsh-skill'
 import type {} from '@deepseek-ai/dsh-tools'
@@ -403,7 +403,7 @@ describe('the shipped Web composition', () => {
 
       // The preset's own loader tool resolves the global-layer skill.
       const loaded = await ctx.tools.execute({
-        callId: CallId('preset-skills-load'),
+        callId: ToolCallId('preset-skills-load'),
         name: 'skill',
         arguments: { name: 'dsh-badge' },
         signal: new AbortController().signal,

+ 2 - 2
apps/web/tests/background-job-list.e2e.ts

@@ -10,7 +10,7 @@ import type { Browser, Page } from 'playwright'
 import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
 import type { Agent } from '@deepseek-ai/dsh-agent'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import { SessionId } from '@deepseek-ai/dsh-session'
 import { JobId } from '@deepseek-ai/dsh-jobs'
 import {
@@ -90,7 +90,7 @@ describe.skipIf(MODE === 'record')('web e2e: background job list', () => {
 
     const started = await scaffold.ctx.tools.execute({
       signal: new AbortController().signal,
-      callId: CallId('background-job-list-e2e'),
+      callId: ToolCallId('background-job-list-e2e'),
       name: 'bash',
       arguments: { command: COMMAND, description: 'Hold a background slot open', run_in_background: true },
       agent,

+ 3 - 3
apps/web/tests/chat-continuous-conversation.e2e.ts

@@ -9,7 +9,7 @@ import { join } from 'node:path'
 import type { Browser, Page } from 'playwright'
 import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
-import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
 import type { ReplayEntry, ReplayOverrideDoc } from '@deepseek-ai/dsh-llm-replay'
 import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
 import {
@@ -32,7 +32,7 @@ interface TurnSpec {
   readonly firstMarker: string
   readonly doneMarker: string
   readonly deltas: readonly string[]
-  readonly callId?: ReturnType<typeof CallId>
+  readonly callId?: ReturnType<typeof ToolCallId>
   readonly toolResultMarker?: string
 }
 
@@ -81,7 +81,7 @@ function turnSpec(index: number): TurnSpec {
     firstMarker,
     doneMarker,
     deltas,
-    callId: CallId(`continuous-chat-tool-${id}`),
+    callId: ToolCallId(`continuous-chat-tool-${id}`),
     toolResultMarker: `CONTINUOUS_CHAT_TOOL_RESULT_${id}`,
   }
 }

+ 2 - 2
apps/web/tests/chat-scroll-contract.e2e.ts

@@ -9,7 +9,7 @@ import type { Browser, Page } from 'playwright'
 import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it } from 'vitest'
 import type { StreamChunk } from '@deepseek-ai/dsh-llm'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import type { ReplayEntry, ReplayOverrideDoc } from '@deepseek-ai/dsh-llm-replay'
 import type { SessionEvent } from '@deepseek-ai/dsh-session'
 import { createChatScrollFixture, type ChatScrollFixture } from './chat-scroll-fixture.ts'
@@ -34,7 +34,7 @@ const LIVE_TEXT_PROMPT = 'CHAT_SCROLL_LIVE_USER Continue this long conversation
 const LIVE_TEXT_FIRST = 'CHAT_SCROLL_LIVE_FIRST'
 const LIVE_TEXT_DONE = 'CHAT_SCROLL_LIVE_DONE'
 const LIVE_TOOL_PROMPT = 'CHAT_SCROLL_TOOL_USER Run the requested diagnostic and then summarize it.'
-const LIVE_TOOL_CALL_ID = CallId('chat-scroll-live-tool-call')
+const LIVE_TOOL_CALL_ID = ToolCallId('chat-scroll-live-tool-call')
 const LIVE_TOOL_RESULT = 'CHAT_SCROLL_LIVE_TOOL_RESULT'
 const LIVE_TOOL_FIRST = 'CHAT_SCROLL_TOOL_STREAM_FIRST'
 const LIVE_TOOL_DONE = 'CHAT_SCROLL_TOOL_STREAM_DONE'

+ 2 - 2
apps/web/tests/chat-scroll-fixture.ts

@@ -3,7 +3,7 @@
 // persisted conversations, while unique markers identify semantic rows
 // without depending on CSS-module names or virtualizer DOM positions.
 import {
-  CallId,
+  ToolCallId,
   createAssistantMessage,
   createToolResultMessage,
   createUserMessage,
@@ -105,7 +105,7 @@ function appendToolStep(
 ): void {
   const calls = [1, 2].map((index) => {
     const marker = markers.tool(turn, index)
-    const callId = CallId(`chat-scroll-${suffix(turn)}-${String(index)}`)
+    const callId = ToolCallId(`chat-scroll-${suffix(turn)}-${String(index)}`)
     const args = JSON.stringify({
       command: `printf '${marker}\\n'`,
       description: marker,

+ 3 - 3
apps/web/tests/complex-history.perf.ts

@@ -11,7 +11,7 @@ import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it } from 'vitest'
 import type { StreamChunk } from '@deepseek-ai/dsh-llm'
 import {
-  CallId,
+  ToolCallId,
   createAssistantMessage,
   createToolResultMessage,
   createUserMessage,
@@ -233,7 +233,7 @@ function appendToolStep(
   toolCount: number,
 ): void {
   const calls = Array.from({ length: toolCount }, (_, index) => {
-    const callId = CallId(`perf-call-${String(turn)}-${String(index)}`)
+    const callId = ToolCallId(`perf-call-${String(turn)}-${String(index)}`)
     const args = JSON.stringify({
       turn,
       index,
@@ -467,7 +467,7 @@ function soakTurn(index: number): ConversationTurnSpec {
 }
 
 function toolStream(index: number, marker: string): StreamChunk[] {
-  const callId = CallId(`performance-tool-${marker.toLowerCase()}-${String(index)}`)
+  const callId = ToolCallId(`performance-tool-${marker.toLowerCase()}-${String(index)}`)
   const args = JSON.stringify({
     command: `printf '${marker}\\n'`,
     description: `Emit performance marker ${String(index)}`,

+ 4 - 4
apps/web/tests/minimal-preset.snapshot.ts

@@ -3,7 +3,7 @@ import { join } from 'node:path'
 import { fileURLToPath } from 'node:url'
 import { afterAll, beforeAll, describe, expect, it } from 'vitest'
 import type { AgentHandle } from '@deepseek-ai/dsh-agent'
-import { CallId, createUserMessage } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, createUserMessage } from '@deepseek-ai/dsh-llm'
 import { SessionId } from '@deepseek-ai/dsh-session'
 import type {} from '@deepseek-ai/dsh-agent-presets'
 import type {} from '@deepseek-ai/dsh-system-prompt'
@@ -68,14 +68,14 @@ describe('minimal agent preset', () => {
     const signal = new AbortController().signal
     await scaffold.ctx.tools.execute({
       signal,
-      callId: CallId('minimal-bash-state-setup'),
+      callId: ToolCallId('minimal-bash-state-setup'),
       name: 'bash',
       arguments: { command: `cd ${JSON.stringify(stateDir)} && export DSH_MINIMAL_STATE=PERSISTED` },
       agent: agentHandle.agent,
     })
     const bash = await scaffold.ctx.tools.execute({
       signal,
-      callId: CallId('minimal-bash-state-read'),
+      callId: ToolCallId('minimal-bash-state-read'),
       name: 'bash',
       arguments: { command: 'printf \'%s:%s\n\' "$DSH_MINIMAL_STATE" "$PWD"' },
       agent: agentHandle.agent,
@@ -84,7 +84,7 @@ describe('minimal agent preset', () => {
     await writeFile(seedPath, 'MINIMAL_EDITOR_OK\n')
     const editor = await scaffold.ctx.tools.execute({
       signal,
-      callId: CallId('minimal-editor-smoke'),
+      callId: ToolCallId('minimal-editor-smoke'),
       name: 'str_replace_editor',
       arguments: { command: 'view', path: seedPath },
       agent: agentHandle.agent,

+ 2 - 2
apps/web/tests/produced-file-mentions.e2e.ts

@@ -9,7 +9,7 @@
 import type { Browser, Page } from 'playwright'
 import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
-import { CallId, createAssistantMessage, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, createAssistantMessage, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
 import { SESSION_FORMAT_VERSION, Session, SessionId } from '@deepseek-ai/dsh-session'
 import type {} from '@deepseek-ai/dsh-session-title'
 import {
@@ -46,7 +46,7 @@ function mentionFixture(): string {
   session.append('step/start', { turn: 1, step: 1 })
   const calls = WRITES.map((path, index) => ({
     path,
-    callId: CallId(`file-mention-${String(index)}`),
+    callId: ToolCallId(`file-mention-${String(index)}`),
     args: JSON.stringify({ file_path: path, content: `content of ${path}\n` }),
   }))
   session.append('assistant/message', {

+ 2 - 2
apps/web/tests/produced-files.e2e.ts

@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url'
 import type { Browser, Page } from 'playwright'
 import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it, onTestFailed, vi } from 'vitest'
-import { CallId, createAssistantMessage, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, createAssistantMessage, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
 import { SESSION_FORMAT_VERSION, Session, SessionId } from '@deepseek-ai/dsh-session'
 import type {} from '@deepseek-ai/dsh-session-title'
 import {
@@ -49,7 +49,7 @@ function producedFixture(): string {
   session.append('step/start', { turn: 1, step: 1 })
   const calls = PRODUCED.map((path, index) => ({
     path,
-    callId: CallId(`produced-files-${String(index)}`),
+    callId: ToolCallId(`produced-files-${String(index)}`),
     args: JSON.stringify({ file_path: path, content: `content of ${path}\n` }),
   }))
   session.append('assistant/message', {

+ 2 - 2
apps/web/tests/replay-round-trip.e2e.ts

@@ -14,7 +14,7 @@ import { fileURLToPath } from 'node:url'
 import type { Browser, Page } from 'playwright'
 import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
 import {
   assertFixtureInventory, captureStableAria, compareOrRefreshGolden, fixtureUserPrompts,
@@ -98,7 +98,7 @@ describe('web e2e: fresh round trip through the real assembly', () => {
     if (agent === undefined) throw new Error(`the settled Web agent ${settledSessionId} is no longer live`)
     const result = await scaffold.ctx.tools.execute({
       signal: AbortSignal.timeout(5_000),
-      callId: CallId('web-url-probe'),
+      callId: ToolCallId('web-url-probe'),
       name: 'bash',
       arguments: {
         command: 'printf \'%s\\n\' "$DSH_WEB_URL"',

+ 4 - 4
apps/web/tests/schedule-after.e2e.ts

@@ -6,7 +6,7 @@ import type { Browser, Page } from 'playwright'
 import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
 import type { AgentHandle } from '@deepseek-ai/dsh-agent'
-import { CallId, createUserMessage, LlmAdapter } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, createUserMessage, LlmAdapter } from '@deepseek-ai/dsh-llm'
 import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
 import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
 import {
@@ -121,7 +121,7 @@ class BrowserZoneAtAdapter extends LlmAdapter {
       this.selectedAt = localAt(target, AT_BROWSER_ZONE)
       this.scheduledAt = new Date(target).toISOString()
       const argumentsJson = JSON.stringify({ prompt: AT_PROMPT, at: this.selectedAt })
-      const callId = CallId('schedule-at-browser-zone')
+      const callId = ToolCallId('schedule-at-browser-zone')
       yield { type: 'block-start', index: 0, blockType: 'tool-call' }
       yield {
         type: 'tool-call-delta',
@@ -258,7 +258,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
     await workspace.attachSession(afterHandle.agent.id)
     const afterCreated = await scaffold.ctx.tools.execute({
       signal: AbortSignal.timeout(10_000),
-      callId: CallId('schedule-after-create'),
+      callId: ToolCallId('schedule-after-create'),
       name: 'schedule_create',
       arguments: { prompt: AFTER_PROMPT, after_seconds: 1 },
       agent: afterHandle.agent,
@@ -314,7 +314,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
     await workspace.attachSession(everyHandle.agent.id)
     const everyListed = await scaffold.ctx.tools.execute({
       signal: AbortSignal.timeout(10_000),
-      callId: CallId('schedule-every-list'),
+      callId: ToolCallId('schedule-every-list'),
       name: 'schedule_list',
       arguments: {},
       agent: everyHandle.agent,

+ 4 - 4
apps/web/tests/shipped-composition.e2e.ts

@@ -7,7 +7,7 @@ import { readFileSync } from 'node:fs'
 import { tmpdir } from 'node:os'
 import { fileURLToPath } from 'node:url'
 import { afterEach, expect, it } from 'vitest'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import { canonicalPath, writableRoots } from '@deepseek-ai/dsh-sandbox'
 import { SessionId } from '@deepseek-ai/dsh-session'
 import { settingsNamespace } from '@deepseek-ai/dsh-settings'
@@ -199,7 +199,7 @@ it('lets a preset producer reach the background-job registry', async () => {
     // fails here — with every task control still listed in the catalog above.
     const started = await ctx.tools.execute({
       signal,
-      callId: CallId('shipped-bash-background'),
+      callId: ToolCallId('shipped-bash-background'),
       name: 'bash',
       arguments: {
         command: 'printf SHIPPED_BACKGROUND_OK',
@@ -217,7 +217,7 @@ it('lets a preset producer reach the background-job registry', async () => {
     // owner. A per-preset registry would list nothing here even on success.
     const listed = await ctx.tools.execute({
       signal,
-      callId: CallId('shipped-task-list'),
+      callId: ToolCallId('shipped-task-list'),
       name: 'job_list',
       arguments: {},
       agent: handle.agent,
@@ -231,7 +231,7 @@ it('lets a preset producer reach the background-job registry', async () => {
     // through a preset-plane control, which is the linkage the realm severed.
     const collected = await ctx.tools.execute({
       signal,
-      callId: CallId('shipped-task-output'),
+      callId: ToolCallId('shipped-task-output'),
       name: 'job_output',
       arguments: { job_id: 'bash-1', wait: true },
       agent: handle.agent,

+ 2 - 2
docs/cordis-tutorial/07-into-the-harness.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/cordis-tutorial/07-into-the-harness.md
-07-into-the-harness.md: 2d3c23f9f7f7fc6bd6cabd4e7e68ebfc46e20665
-07-into-the-harness.zh.md: 0e0fe031be92c8284d2ff6331d753b9b25994ee9
+07-into-the-harness.md: 1bfe05fb511b54067b913a1a2d0903a0772a22d7
+07-into-the-harness.zh.md: 5ed4ff9d95d6e8b14402e525e1e9bb5328bfb678

+ 3 - 3
docs/cordis-tutorial/07-into-the-harness.md

@@ -11,7 +11,7 @@ Create `greet-tool.ts` in `tmp/cordis-tutorial`:
 ```ts
 import type { Context } from '@deepseek-ai/cordis'
 import { defineTool } from '@deepseek-ai/dsh-tools'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 
 export const name = 'greet-tool'
 export const inject = ['tools']
@@ -33,10 +33,10 @@ export function apply(ctx: Context) {
   }))
 
   // Drive one call through the real execution pipeline, standing in for
-  // the model. CallId brands the correlation id a provider would issue.
+  // the model. ToolCallId brands the correlation id a provider would issue.
   void (async () => {
     const result = await ctx.tools.execute({
-      callId: CallId('demo-1'),
+      callId: ToolCallId('demo-1'),
       name: 'greet',
       arguments: { name: 'Cordis' },
       signal: new AbortController().signal,

+ 3 - 3
docs/cordis-tutorial/07-into-the-harness.zh.md

@@ -11,7 +11,7 @@
 ```ts
 import type { Context } from '@deepseek-ai/cordis'
 import { defineTool } from '@deepseek-ai/dsh-tools'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 
 export const name = 'greet-tool'
 export const inject = ['tools']
@@ -33,10 +33,10 @@ export function apply(ctx: Context) {
   }))
 
   // Drive one call through the real execution pipeline, standing in for
-  // the model. CallId brands the correlation id a provider would issue.
+  // the model. ToolCallId brands the correlation id a provider would issue.
   void (async () => {
     const result = await ctx.tools.execute({
-      callId: CallId('demo-1'),
+      callId: ToolCallId('demo-1'),
       name: 'greet',
       arguments: { name: 'Cordis' },
       signal: new AbortController().signal,

+ 2 - 2
docs/persistence-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/persistence-catalog.md
-persistence-catalog.md: f8c10821e4b6daa8f10b0cff63f838ecb381125c
-persistence-catalog.zh.md: 4cefec2eba0a3a3fa7f4c587236fa78e476ad9d7
+persistence-catalog.md: 44a9f61b7b53670186c565efc0b4c587e216d6b8
+persistence-catalog.zh.md: 2ea0e7c236301b786a3f5aab66ad58e802f017c7

+ 4 - 4
docs/persistence-catalog.md

@@ -153,12 +153,12 @@ Source: [`packages/preset/agent-presets/src/session.ts:26`](../packages/preset/a
 'approval/asked': {
   id: ApprovalRequestId
   toolName: string
-  callId?: CallId
+  callId?: ToolCallId
   reason?: string
 }
 ```
 
-Types: [CallId](subsystems/core.md)
+Types: [ToolCallId](subsystems/core.md)
 
 Source: [`packages/interaction/user-approval/src/index.ts:44`](../packages/interaction/user-approval/src/index.ts)
 
@@ -799,10 +799,10 @@ Source: [`packages/core/session/src/types.ts:303`](../packages/core/session/src/
  * JSON string exactly as the model produced it (unparsed). `callId` pairs the
  * call with its `tool/result`.
  */
-'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
+'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
 ```
 
-Types: [CallId](subsystems/core.md)
+Types: [ToolCallId](subsystems/core.md)
 
 Source: [`packages/core/session/src/types.ts:283`](../packages/core/session/src/types.ts)
 

+ 4 - 4
docs/persistence-catalog.zh.md

@@ -155,12 +155,12 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
 'approval/asked': {
   id: ApprovalRequestId
   toolName: string
-  callId?: CallId
+  callId?: ToolCallId
   reason?: string
 }
 ```
 
-类型:[CallId](subsystems/core.zh.md)
+类型:[ToolCallId](subsystems/core.zh.md)
 
 来源:[`packages/interaction/user-approval/src/index.ts:44`](../packages/interaction/user-approval/src/index.ts)
 
@@ -801,10 +801,10 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
  * JSON string exactly as the model produced it (unparsed). `callId` pairs the
  * call with its `tool/result`.
  */
-'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
+'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
 ```
 
-类型:[CallId](subsystems/core.zh.md)
+类型:[ToolCallId](subsystems/core.zh.md)
 
 来源:[`packages/core/session/src/types.ts:283`](../packages/core/session/src/types.ts)
 

+ 2 - 2
docs/subsystems/approval.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/approval.md
-approval.md: 7d3d09314f8fbb151cc8a00dbbee5e2cc14e2c9a
-approval.zh.md: 22d0b0ec4242fcb2ad6fb82c29893a5914369238
+approval.md: 276cac2c00e5b781e9cdbb5da461d035e61de719
+approval.zh.md: 5fe63b5d1aa39ddced51af2c0eaf860e4df10e86

+ 1 - 1
docs/subsystems/approval.md

@@ -70,7 +70,7 @@ interface ApprovalRequest {
    * The exact tool call being decided, when the asker has one — lets a UI
    * attach the prompt to the tool call it already streamed.
    */
-  readonly callId?: CallId
+  readonly callId?: ToolCallId
   /** The asker's human-readable explanation of WHY it is asking. */
   readonly reason?: string
   /**

+ 1 - 1
docs/subsystems/approval.zh.md

@@ -70,7 +70,7 @@ interface ApprovalRequest {
    * The exact tool call being decided, when the asker has one — lets a UI
    * attach the prompt to the tool call it already streamed.
    */
-  readonly callId?: CallId
+  readonly callId?: ToolCallId
   /** The asker's human-readable explanation of WHY it is asking. */
   readonly reason?: string
   /**

+ 2 - 2
docs/subsystems/compaction.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/compaction.md
-compaction.md: 4b6e9dee81cb30d42bb6194776457e354102b529
-compaction.zh.md: a9c57b64798be1d0361125eac68d589b3e0f1c66
+compaction.md: ebd8e6e9d7530ff5b0bb9faf4a4d5d24b00e464d
+compaction.zh.md: e51e8f7d627a339e3e5a9c079d5b5a57f384772d

+ 1 - 1
docs/subsystems/compaction.md

@@ -99,7 +99,7 @@ interface PrunedEntry {
   /** Newly appended pruned tool-result event. */
   readonly replacementSeq: number
   /** Tool call shared by the original and replacement. */
-  readonly callId: CallId
+  readonly callId: ToolCallId
   /** Original text size in Unicode code points. */
   readonly charsBefore: number
   /** Replacement text size in Unicode code points. */

+ 1 - 1
docs/subsystems/compaction.zh.md

@@ -99,7 +99,7 @@ interface PrunedEntry {
   /** Newly appended pruned tool-result event. */
   readonly replacementSeq: number
   /** Tool call shared by the original and replacement. */
-  readonly callId: CallId
+  readonly callId: ToolCallId
   /** Original text size in Unicode code points. */
   readonly charsBefore: number
   /** Replacement text size in Unicode code points. */

+ 2 - 2
docs/subsystems/core.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/core.md
-core.md: 18057c9a9a308e439bb6a08f598f51f158243496
-core.zh.md: dfb1d8ad482b2cf9c6de94bde04c4dbff83a0082
+core.md: 37c185ff8c90f88aecb0b2a7e426bc8f22162578
+core.zh.md: 6559e25a5cdb534d98c3fd28cd68c506bdbaf1f4

+ 2 - 2
docs/subsystems/core.md

@@ -295,7 +295,7 @@ Two large discriminated unions are the ones consumers `switch` over most: **`Str
 
 ### Branded IDs
 
-IDs passed between packages are **branded** — structurally strings, but non-interchangeable at the type level (a `SessionId` cannot be passed where a `CallId` is expected). Construction goes through a per-type factory; comparison, logging, and JSON behave as ordinary strings.
+IDs passed between packages are **branded** — structurally strings, but non-interchangeable at the type level (a `SessionId` cannot be passed where a `ToolCallId` is expected). Construction goes through a per-type factory; comparison, logging, and JSON behave as ordinary strings.
 
 The `Branded<B>` primitive lives in its own type-only package, [dsh-brand](../../packages/util/brand) (no runtime code, no harness-package dependency), so any package can brand the ids it owns without depending on an unrelated capability package.
 
@@ -306,7 +306,7 @@ Source: [`packages/util/brand/src/index.ts`](../../packages/util/brand/src/index
 type Branded<B extends string> = string & { readonly [BRAND]: B }
 ```
 
-The two core IDs are `CallId` (correlates a tool call with its result; dsh-llm) and `SessionId` (the shared live agent and durable session identity; dsh-session). Capability packages brand their own ids too, such as `JobId` in [jobs.md](jobs.md).
+The two core IDs are `ToolCallId` (correlates a tool call with its result; dsh-llm) and `SessionId` (the shared live agent and durable session identity; dsh-session). Capability packages brand their own ids too, such as `JobId` in [jobs.md](jobs.md).
 
 <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
 

+ 2 - 2
docs/subsystems/core.zh.md

@@ -305,7 +305,7 @@ declare module '@deepseek-ai/dsh-llm' {
 
 ### 品牌化 ID
 
-在包之间传递的 ID 都经过**品牌化**——结构上是字符串,但在类型层面不可互换(不能把 `SessionId` 传给需要 `CallId` 的位置)。每种类型通过各自的工厂构造;比较、日志记录和 JSON 行为与普通字符串相同。
+在包之间传递的 ID 都经过**品牌化**——结构上是字符串,但在类型层面不可互换(不能把 `SessionId` 传给需要 `ToolCallId` 的位置)。每种类型通过各自的工厂构造;比较、日志记录和 JSON 行为与普通字符串相同。
 
 `Branded<B>` 原语位于独立的纯类型包 [dsh-brand](../../packages/util/brand) 中(没有运行时代码,也不依赖 harness 包),因此任何包都能品牌化其拥有的 id,而无需依赖无关的能力包。
 
@@ -316,7 +316,7 @@ declare module '@deepseek-ai/dsh-llm' {
 type Branded<B extends string> = string & { readonly [BRAND]: B }
 ```
 
-两个核心 ID 是 `CallId`(关联工具调用及其结果;dsh-llm)和 `SessionId`(活跃 agent 与持久会话共享的标识;dsh-session)。能力包也会品牌化各自的 id,例如 [jobs.md](jobs.zh.md) 中的 `JobId`。
+两个核心 ID 是 `ToolCallId`(关联工具调用及其结果;dsh-llm)和 `SessionId`(活跃 agent 与持久会话共享的标识;dsh-session)。能力包也会品牌化各自的 id,例如 [jobs.md](jobs.zh.md) 中的 `JobId`。
 
 <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
 

+ 2 - 2
docs/subsystems/llm-streaming.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/llm-streaming.md
-llm-streaming.md: 4f322ca1024b9d74a4906e34f93fc6f8e4082cbf
-llm-streaming.zh.md: c74cabe27c1f5fdd44711ac0aae7cd6b0a7ba7dd
+llm-streaming.md: adb8fdde50c898b42f74a1562f95ab267afa6db6
+llm-streaming.zh.md: 197ae86a2182b93c3fbf4005b3fc51df96dc49b3

+ 2 - 2
docs/subsystems/llm-streaming.md

@@ -28,7 +28,7 @@ interface ContentBlockMap {
 }
 ```
 
-The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](attachment.md)), `ToolCallBlock` (`id: CallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
+The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](attachment.md)), `ToolCallBlock` (`id: ToolCallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
 
 Source: [`packages/llm/llm/src/message.ts`](../../packages/llm/llm/src/message.ts)
 
@@ -193,7 +193,7 @@ type StreamChunk =
   | { type: 'block-start'; index: number; blockType: ContentBlockType }
   | { type: 'text-delta'; index: number; text: string }
   | { type: 'reasoning-delta'; index: number; text: string }
-  | { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string }
+  | { type: 'tool-call-delta'; index: number; id: ToolCallId; name?: string; argumentsDelta: string }
   | { type: 'block-end'; index: number; block: ContentBlock }
   | { type: 'usage'; usage: TokenUsage }
   | {

+ 2 - 2
docs/subsystems/llm-streaming.zh.md

@@ -28,7 +28,7 @@ interface ContentBlockMap {
 }
 ```
 
-各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ImageBlock`(一个持久的[图片附件](attachment.zh.md))、`ToolCallBlock`(`id: CallId`、`name`、原始 JSON `arguments`),以及 `ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。仅当适配器、UI、压缩(compaction)和持久回放路径均支持某种新模态时,才将其纳入可合并扩展的 map。
+各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ImageBlock`(一个持久的[图片附件](attachment.zh.md))、`ToolCallBlock`(`id: ToolCallId`、`name`、原始 JSON `arguments`),以及 `ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。仅当适配器、UI、压缩(compaction)和持久回放路径均支持某种新模态时,才将其纳入可合并扩展的 map。
 
 源码:[`packages/llm/llm/src/message.ts`](../../packages/llm/llm/src/message.ts)
 
@@ -193,7 +193,7 @@ type StreamChunk =
   | { type: 'block-start'; index: number; blockType: ContentBlockType }
   | { type: 'text-delta'; index: number; text: string }
   | { type: 'reasoning-delta'; index: number; text: string }
-  | { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string }
+  | { type: 'tool-call-delta'; index: number; id: ToolCallId; name?: string; argumentsDelta: string }
   | { type: 'block-end'; index: number; block: ContentBlock }
   | { type: 'usage'; usage: TokenUsage }
   | {

+ 2 - 2
docs/subsystems/session.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/session.md
-session.md: 96520c72b83bf713b7fe09563f7d6e44c0386bcf
-session.zh.md: 28a259226b8149dbe26c7650bc574de4aa43a740
+session.md: a23ed4c43befc1c39b110560d933ff2bb1bce941
+session.zh.md: a564ddd34705d530267f3f5bba5afe08b7dcc0ae

+ 1 - 1
docs/subsystems/session.md

@@ -71,7 +71,7 @@ interface SessionEventMap {
    * JSON string exactly as the model produced it (unparsed). `callId` pairs the
    * call with its `tool/result`.
    */
-  'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
+  'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
   /**
    * A completed tool call's model-facing result, optional internal failure
    * identity, and optional tool-private `meta` presentation payload. `meta` is

+ 1 - 1
docs/subsystems/session.zh.md

@@ -71,7 +71,7 @@ interface SessionEventMap {
    * JSON string exactly as the model produced it (unparsed). `callId` pairs the
    * call with its `tool/result`.
    */
-  'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
+  'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
   /**
    * A completed tool call's model-facing result, optional internal failure
    * identity, and optional tool-private `meta` presentation payload. `meta` is

+ 2 - 2
docs/subsystems/spill.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/spill.md
-spill.md: 8356fc45f8581c6776238f212592c4ea2841a96a
-spill.zh.md: 353c5fea63ff6284c922a9bbf7d6d07f124fb9bd
+spill.md: 2a3201cf2fa0d7b034a01f64d0448536bfe8d861
+spill.zh.md: d4c5b3853be46780cd93e2f826bf399bff42c405

+ 1 - 1
docs/subsystems/spill.md

@@ -50,7 +50,7 @@ interface SpillSource {
   /** The tool whose result was spilled (e.g. `web_fetch`). */
   toolName: string
   /** The model-issued call id the result belongs to. */
-  callId: CallId
+  callId: ToolCallId
   /** A short human label for the artifact (e.g. `result`). */
   label: string
 }

+ 1 - 1
docs/subsystems/spill.zh.md

@@ -50,7 +50,7 @@ interface SpillSource {
   /** The tool whose result was spilled (e.g. `web_fetch`). */
   toolName: string
   /** The model-issued call id the result belongs to. */
-  callId: CallId
+  callId: ToolCallId
   /** A short human label for the artifact (e.g. `result`). */
   label: string
 }

+ 2 - 2
docs/subsystems/tools.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/tools.md
-tools.md: dd89d40c1401732dd5e6d8dfb5ccb08a89c58405
-tools.zh.md: 0d3e59bc222527e66dd5bd626203f4e1bb6aad9a
+tools.md: 5ac2401d662bca52cac529c0620a39930565a7c7
+tools.zh.md: a081c58a2ba4df50b035a4dae8fa7f9ab7d9de1a

+ 4 - 4
docs/subsystems/tools.md

@@ -183,12 +183,12 @@ type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
  * callers do not choose that token.
  */
 interface ToolExecutionInput {
-  readonly callId: CallId
+  readonly callId: ToolCallId
   /**
    * Root model-requested call owning this execution tree. Callers omit it for
    * a root execution; nested dispatchers propagate the enclosing value.
    */
-  readonly rootCallId?: CallId
+  readonly rootCallId?: ToolCallId
   readonly name: string
   /** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
   readonly arguments: unknown
@@ -270,7 +270,7 @@ interface CodeDispatchLog {
   /** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
   readonly agent?: Agent
   /** Deterministic sub-call id (`<parent>:code:<n>`). */
-  readonly subCallId: CallId
+  readonly subCallId: ToolCallId
   /** The dispatched sub-tool name. */
   readonly name: string
   /** Whether the sub-call settled as an error. */
@@ -290,7 +290,7 @@ interface CodeDispatchLog {
  */
 interface ToolExecution extends ToolExecutionInput {
   /** Root model-requested call, resolved for every root and nested execution. */
-  readonly rootCallId: CallId
+  readonly rootCallId: ToolCallId
   /** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
   readonly token: ToolExecutionToken
 }

+ 4 - 4
docs/subsystems/tools.zh.md

@@ -183,12 +183,12 @@ type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
  * callers do not choose that token.
  */
 interface ToolExecutionInput {
-  readonly callId: CallId
+  readonly callId: ToolCallId
   /**
    * Root model-requested call owning this execution tree. Callers omit it for
    * a root execution; nested dispatchers propagate the enclosing value.
    */
-  readonly rootCallId?: CallId
+  readonly rootCallId?: ToolCallId
   readonly name: string
   /** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
   readonly arguments: unknown
@@ -270,7 +270,7 @@ interface CodeDispatchLog {
   /** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
   readonly agent?: Agent
   /** Deterministic sub-call id (`<parent>:code:<n>`). */
-  readonly subCallId: CallId
+  readonly subCallId: ToolCallId
   /** The dispatched sub-tool name. */
   readonly name: string
   /** Whether the sub-call settled as an error. */
@@ -290,7 +290,7 @@ interface CodeDispatchLog {
  */
 interface ToolExecution extends ToolExecutionInput {
   /** Root model-requested call, resolved for every root and nested execution. */
-  readonly rootCallId: CallId
+  readonly rootCallId: ToolCallId
   /** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
   readonly token: ToolExecutionToken
 }

+ 2 - 2
docs/user/develop/practice/llm-adapter.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/user/develop/practice/llm-adapter.md
-llm-adapter.md: 882e82d880ac622cf886c6c24e75a1987e1c6702
-llm-adapter.zh.md: 27c480af2a19a68ac35900a2ce1b5e9dbdc85bf8
+llm-adapter.md: ff5fc5657e2be0a5fe0d4e10f148865e62f92b18
+llm-adapter.zh.md: b05bfad6cbc4038f54f816b212068d85e54c2100

+ 3 - 3
docs/user/develop/practice/llm-adapter.md

@@ -54,7 +54,7 @@ export function apply(ctx: Context, config: Config) {
 `stream()` yields chunks using this protocol:
 
 ```ts
-import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
 
 async function* exampleChunks(): AsyncIterable<StreamChunk> {
   // 1. Start each content block with block-start.
@@ -76,7 +76,7 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
   yield {
     type: 'tool-call-delta',
     index: 1,
-    id: CallId('call-123'),
+    id: ToolCallId('call-123'),
     name: 'bash',
     argumentsDelta: '{"command":"ls"}',
   }
@@ -85,7 +85,7 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
     index: 1,
     block: {
       type: 'tool-call',
-      id: CallId('call-123'),
+      id: ToolCallId('call-123'),
       name: 'bash',
       arguments: '{"command":"ls"}',
     },

+ 3 - 3
docs/user/develop/practice/llm-adapter.zh.md

@@ -54,7 +54,7 @@ export function apply(ctx: Context, config: Config) {
 `stream()` 必须按以下协议生成分片:
 
 ```ts
-import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
 
 async function* exampleChunks(): AsyncIterable<StreamChunk> {
   // 1. Start each content block with block-start.
@@ -76,7 +76,7 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
   yield {
     type: 'tool-call-delta',
     index: 1,
-    id: CallId('call-123'),
+    id: ToolCallId('call-123'),
     name: 'bash',
     argumentsDelta: '{"command":"ls"}',
   }
@@ -85,7 +85,7 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
     index: 1,
     block: {
       type: 'tool-call',
-      id: CallId('call-123'),
+      id: ToolCallId('call-123'),
       name: 'bash',
       arguments: '{"command":"ls"}',
     },

+ 4 - 4
examples/acp-agent/tests/fixtures/shell/tool-pwsh/driver.ts

@@ -8,7 +8,7 @@
 
 import { writeFile } from 'node:fs/promises'
 import { boot, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 
 const configPath = process.argv[2]
 if (configPath === undefined) throw new Error('tool-pwsh driver requires a config path')
@@ -21,7 +21,7 @@ try {
 
   const foreground = await ctx.tools.execute({
     signal: new AbortController().signal,
-    callId: CallId('loader-fg'),
+    callId: ToolCallId('loader-fg'),
     name: 'pwsh',
     arguments: { command: 'Write-Output loader-ok', description: 'loader foreground' },
   })
@@ -29,7 +29,7 @@ try {
 
   const background = await ctx.tools.execute({
     signal: new AbortController().signal,
-    callId: CallId('loader-bg'),
+    callId: ToolCallId('loader-bg'),
     name: 'pwsh',
     arguments: {
       command: 'Start-Sleep -Milliseconds 200; Write-Output loader-bg-ok',
@@ -46,7 +46,7 @@ try {
   while (Date.now() < deadline) {
     const read = await ctx.tools.execute({
       signal: new AbortController().signal,
-      callId: CallId('loader-bg-read'),
+      callId: ToolCallId('loader-bg-read'),
       name: 'job_output',
       arguments: { job_id: jobId },
     })

+ 3 - 3
examples/acp-agent/tests/fixtures/subagent/subagent-acp/mock-delegating-llm.ts

@@ -1,6 +1,6 @@
 import type { Context } from '@deepseek-ai/cordis'
 import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
-import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
 
 /**
  * Test adapter for the `mock-delegate` model: the first request calls the
@@ -20,8 +20,8 @@ class MockDelegatingAdapter extends LlmAdapter {
     if (toolResultText.length === 0) {
       const args = JSON.stringify({ description: 'cwd probe', prompt: 'report your workspace' })
       yield { type: 'block-start', index: 0, blockType: 'tool-call' }
-      yield { type: 'tool-call-delta', index: 0, id: CallId('call-delegate'), name: 'subagent', argumentsDelta: args }
-      yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('call-delegate'), name: 'subagent', arguments: args } }
+      yield { type: 'tool-call-delta', index: 0, id: ToolCallId('call-delegate'), name: 'subagent', argumentsDelta: args }
+      yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('call-delegate'), name: 'subagent', arguments: args } }
       yield { type: 'usage', usage: { inputTokens: 10, outputTokens: 5 } }
       yield { type: 'finish', reason: { kind: 'tool-calls' } }
       return

+ 2 - 2
examples/headless-agent/tests/code-mode.e2e.ts

@@ -3,7 +3,7 @@ import { tmpdir } from 'node:os'
 import { join } from 'node:path'
 import { afterEach, describe, expect, it, vi } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import LlmRuntime, { createUserMessage, CallId, HarnessError  } from '@deepseek-ai/dsh-llm'
+import LlmRuntime, { createUserMessage, ToolCallId, HarnessError  } from '@deepseek-ai/dsh-llm'
 import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
 import type { SessionEvent } from '@deepseek-ai/dsh-session'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
@@ -93,7 +93,7 @@ function runCode(
   agent?: Agent,
 ): Promise<ToolExecutionResult> {
   return harness.tools.execute({
-    callId: CallId(`keyless-code-${++keylessCall}`),
+    callId: ToolCallId(`keyless-code-${++keylessCall}`),
     name: RUN_CODE_NAME,
     arguments: { code, description: 'Run the e2e program' },
     signal,

+ 3 - 3
examples/headless-agent/tests/fixtures/cli-mock-llm.ts

@@ -1,6 +1,6 @@
 import type { Context } from '@deepseek-ai/cordis'
 import {
-  CallId,
+  ToolCallId,
   LlmAdapter,
   ReasoningEffortId,
   type GenerateOptions,
@@ -37,8 +37,8 @@ class CliMockAdapter extends LlmAdapter {
     if (toolResult === undefined) {
       const args = JSON.stringify({ command: 'printf CLI_TOOL_ROUND_TRIP', description: 'Prove the CLI tool round trip.' })
       yield { type: 'block-start', index: 0, blockType: 'tool-call' }
-      yield { type: 'tool-call-delta', index: 0, id: CallId('cli-smoke-call'), name: 'bash', argumentsDelta: args }
-      yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('cli-smoke-call'), name: 'bash', arguments: args } }
+      yield { type: 'tool-call-delta', index: 0, id: ToolCallId('cli-smoke-call'), name: 'bash', argumentsDelta: args }
+      yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('cli-smoke-call'), name: 'bash', arguments: args } }
       yield { type: 'usage', usage: { inputTokens: 11, outputTokens: 3, cacheReadTokens: 2 } }
       yield { type: 'finish', reason: { kind: 'tool-calls' } }
       return

+ 2 - 2
examples/headless-agent/tests/fixtures/team-llm.mjs

@@ -1,6 +1,6 @@
 /** Deterministic keyless Agent Teams adapter for the real headless Loader snapshot. */
 
-import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
 
 let nextCall = 0
 
@@ -38,7 +38,7 @@ function latestToolText(messages) {
 function toolChunks(specs) {
   const chunks = []
   for (const [index, spec] of specs.entries()) {
-    const id = CallId(`team-fixture-${++nextCall}`)
+    const id = ToolCallId(`team-fixture-${++nextCall}`)
     const args = JSON.stringify(spec.args)
     chunks.push(
       { type: 'block-start', index, blockType: 'tool-call' },

+ 3 - 3
examples/headless-agent/tests/semantic-checkpoint.snapshot.ts

@@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'
 import { Context } from '@deepseek-ai/cordis'
 import { normalizeSessionSnapshot, type NormalizeContext } from '@deepseek-ai/dsh-acp-snapshot'
 import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
-import { createUserMessage, CallId , createMessage } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId , createMessage } from '@deepseek-ai/dsh-llm'
 import SessionStore, { SESSION_FORMAT_VERSION, SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'
 import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
 import { describe, expect, it } from 'vitest'
@@ -46,7 +46,7 @@ async function seedInterruptedSession(root: string, cwd: string): Promise<string
         step: 1,
         message: createMessage({
           role: 'assistant',
-          content: [{ type: 'tool-call', id: CallId('unknown-outcome-call'), name: 'write_remote', arguments: '{"value":1}' }],
+          content: [{ type: 'tool-call', id: ToolCallId('unknown-outcome-call'), name: 'write_remote', arguments: '{"value":1}' }],
           source: {
             kind: 'model',
             ...{ provider: 'deepseek-official', model: 'deepseek-v4-flash' },
@@ -62,7 +62,7 @@ async function seedInterruptedSession(root: string, cwd: string): Promise<string
       data: {
         turn: 1,
         step: 1,
-        callId: CallId('unknown-outcome-call'),
+        callId: ToolCallId('unknown-outcome-call'),
         name: 'write_remote',
         arguments: '{"value":1}',
       },

+ 3 - 3
examples/jsonrpc-agent/tests/fixtures/subagent/subagent-dsh-sdk/mock-delegating-llm.ts

@@ -1,6 +1,6 @@
 import type { Context } from '@deepseek-ai/cordis'
 import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
-import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
 
 /**
  * Test adapter for the `mock-delegate` model: the first request calls the
@@ -20,8 +20,8 @@ class MockDelegatingAdapter extends LlmAdapter {
     if (toolResultText.length === 0) {
       const args = JSON.stringify({ description: 'cwd probe', prompt: 'report your workspace' })
       yield { type: 'block-start', index: 0, blockType: 'tool-call' }
-      yield { type: 'tool-call-delta', index: 0, id: CallId('call-delegate'), name: 'subagent', argumentsDelta: args }
-      yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('call-delegate'), name: 'subagent', arguments: args } }
+      yield { type: 'tool-call-delta', index: 0, id: ToolCallId('call-delegate'), name: 'subagent', argumentsDelta: args }
+      yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('call-delegate'), name: 'subagent', arguments: args } }
       yield { type: 'usage', usage: { inputTokens: 10, outputTokens: 5 } }
       yield { type: 'finish', reason: { kind: 'tool-calls' } }
       return

+ 3 - 3
packages/acp/acp/tests/approval.spec.ts

@@ -1,6 +1,6 @@
 import { afterEach, describe, expect, it } from 'vitest'
 import { PROTOCOL_VERSION } from '@agentclientprotocol/sdk'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import type { Agent } from '@deepseek-ai/dsh-agent'
 import { SessionId } from '@deepseek-ai/dsh-session'
 import ApprovalService, { type ApprovalRequest } from '@deepseek-ai/dsh-user-approval'
@@ -21,7 +21,7 @@ describe('ACP machine permission policy', () => {
     const { sessionId } = await harness.client.newSession({ cwd: process.cwd(), mcpServers: [] })
     const agent = harness.ctx.agents.get(SessionId(sessionId))!
     agent.session.append('turn/start', { turn: 1 })
-    return { agent, toolName: 'bash', callId: CallId('call-9'), ...overrides }
+    return { agent, toolName: 'bash', callId: ToolCallId('call-9'), ...overrides }
   }
 
   it('maps the two advertised one-shot choices', async () => {
@@ -63,7 +63,7 @@ describe('ACP machine permission policy', () => {
     const foreign = {
       session: { id: request.agent.session.id, events: [{ type: 'turn/start' }], append: () => ({}) },
     } as unknown as Agent
-    await expect(harness.ctx.approval.request({ agent: foreign, toolName: 'bash', callId: CallId('call') }))
+    await expect(harness.ctx.approval.request({ agent: foreign, toolName: 'bash', callId: ToolCallId('call') }))
       .resolves.toBe('unavailable')
     expect(harness.permissionRequests).toHaveLength(0)
   })

+ 3 - 3
packages/acp/acp/tests/edges.spec.ts

@@ -1,6 +1,6 @@
 import { afterEach, describe, expect, it, vi } from 'vitest'
 import { PROTOCOL_VERSION } from '@agentclientprotocol/sdk'
-import { createUserMessage, CallId, type StreamChunk  } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId, type StreamChunk  } from '@deepseek-ai/dsh-llm'
 import { SessionId } from '@deepseek-ai/dsh-session'
 import { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
 import { makeBridgeHarness, textResponse, type BridgeHarness } from './harness.ts'
@@ -8,8 +8,8 @@ import { makeBridgeHarness, textResponse, type BridgeHarness } from './harness.t
 function toolCallResponse(): StreamChunk[] {
   return [
     { type: 'block-start', index: 0, blockType: 'tool-call' },
-    { type: 'tool-call-delta', index: 0, id: CallId('call-1'), name: 'echo', argumentsDelta: '{}' },
-    { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('call-1'), name: 'echo', arguments: '{}' } },
+    { type: 'tool-call-delta', index: 0, id: ToolCallId('call-1'), name: 'echo', argumentsDelta: '{}' },
+    { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('call-1'), name: 'echo', arguments: '{}' } },
     { type: 'finish', reason: { kind: 'tool-calls' } },
   ]
 }

+ 2 - 2
packages/client/connection/src/client/fixture.ts

@@ -11,7 +11,7 @@ import {
   createUserMessage,
   isTokenDelta,
 } from '@deepseek-ai/dsh-llm/message'
-import { CallId } from '@deepseek-ai/dsh-llm/brand'
+import { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
 import type {
   AssistantMessage,
   ContentBlock,
@@ -62,7 +62,7 @@ function assistantMessage(content: ContentBlock[], model = 'fx-1'): AssistantMes
 }
 
 function toolResultMessage(callId: string, content: ContentBlock[], isError: boolean): ToolResultMessage {
-  return createToolResultMessage({ callId: CallId(callId), content, isError })
+  return createToolResultMessage({ callId: ToolCallId(callId), content, isError })
 }
 
 const MARKDOWN_FIXTURE = [

+ 2 - 2
packages/client/runtime/tests/event-script.client.ts

@@ -1,4 +1,4 @@
-import { createUserMessage, createMessage, createToolResultMessage, CallId } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, createMessage, createToolResultMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
 // Minimal SessionEvent builders for orchestration tests (shape mirrors what the
 // host emits; only the fields the object layer reads).
 import type { ContentBlock } from '@deepseek-ai/dsh-llm/types'
@@ -45,7 +45,7 @@ export const ev = {
         turn,
         step,
         message: createToolResultMessage({
-          callId: CallId(callId),
+          callId: ToolCallId(callId),
           content: text(body),
           isError: false,
         }),

+ 5 - 5
packages/client/ui-conversation/src/client/contract/slots.ts

@@ -20,7 +20,7 @@ import type {
 import type { createChatStore } from '../stores.ts'
 import type { ComposerSubmitGesture, InputSubmitMode } from './composer-submission.ts'
 import type { ChatNode, ChatNodeKind } from './chat-nodes.ts'
-import type { CallId, SelectionTarget, ViewTab } from './views.ts'
+import type { ToolCallId, SelectionTarget, ViewTab } from './views.ts'
 
 /** Browser-owned image that has not crossed the durable host boundary. */
 export interface ComposerAttachment {
@@ -344,7 +344,7 @@ export interface InputZone {
  */
 export interface ConvViewOwnerProps {
   /** One-shot inspect request from another view (chat's Inspect button); null when idle. */
-  inspect?: { callId: CallId } | null
+  inspect?: { callId: ToolCallId } | null
   /** Acknowledge the inspect request once applied (clears the store field). */
   onInspectDone?: () => void
 }
@@ -415,11 +415,11 @@ export interface ChatNodeTurnDataInjected {
 /** Stable owner currency delivered to one keyed Chat business renderer. */
 export interface ChatNodeOwnerProps {
   /** Selected Tool call, when the shared details store names one. */
-  selectedCallId?: CallId | undefined
+  selectedCallId?: ToolCallId | undefined
   /** Session workspace root; Tool summaries display paths relative to it. */
   cwd?: string | undefined
   openFile: (path: string) => void
-  inspectCall: (callId: CallId) => void
+  inspectCall: (callId: ToolCallId) => void
   forkAt: (seq: number) => void
   /** Render a historical image group through the attachment slot. */
   renderMessageImages: RenderMessageImages
@@ -761,7 +761,7 @@ export interface ChatViewInjected {
   /** Resolve a session-authorized historical image for inline display. */
   loadImage: (attachment: ImageAttachmentRef) => Promise<string>
   /** Hand a call off to the trajectory view: write the one-shot inspect target and switch tabs. */
-  inspectCall: (callId: CallId) => void
+  inspectCall: (callId: ToolCallId) => void
   /**
    * Per-session scroll memory surviving view switches (in-memory, never
    * persisted): the view saves on every scroll and restores on remount; a

+ 3 - 3
packages/client/ui-conversation/src/client/contract/views.ts

@@ -1,10 +1,10 @@
 /** Shared conversation view, selection, and store-state contracts. */
 
 /** Tool call identity as carried on the wire (branded upstream in connection). */
-export type CallId = string
+export type ToolCallId = string
 
 /** Selection target for the details linkage channel (toolcall is the step special case). */
-export interface SelectionTarget { turnSeq: number; stepSeq?: number; callId?: CallId; toolName?: string }
+export interface SelectionTarget { turnSeq: number; stepSeq?: number; callId?: ToolCallId; toolName?: string }
 
 /**
  * One conversation view tab, projected from a 'conversation.view' slot
@@ -28,5 +28,5 @@ export interface ChatStoreState {
    * view consumes it and acknowledges by clearing. Read with `?? null` —
    * persisted snapshots from before this field rehydrate without it.
    */
-  inspect: { callId: CallId } | null
+  inspect: { callId: ToolCallId } | null
 }

+ 1 - 1
packages/client/ui-conversation/src/client/index.ts

@@ -20,7 +20,7 @@ export type { IConversation } from './service.ts'
 export type { DraftAttachmentId } from './input/contract.ts'
 
 export type {
-  CallId, ChatStoreState, SelectionTarget, ViewTab,
+  ToolCallId, ChatStoreState, SelectionTarget, ViewTab,
 } from './contract/views.ts'
 export type { ConversationKey } from './locales.ts'
 export type {

+ 3 - 3
packages/client/ui-conversation/src/client/stores.ts

@@ -3,14 +3,14 @@
  * The plugin creates its handle at apply time so identity follows the fiber.
  */
 import { defineStore, type EngineStoreHandle } from '@deepseek-ai/dsh-client-runtime/client'
-import type { CallId, ChatStoreState, SelectionTarget } from './contract/views.ts'
+import type { ToolCallId, ChatStoreState, SelectionTarget } from './contract/views.ts'
 
 /** Declared action shape used to give the exported factory a stable return type. */
 type ChatActions = {
   select: (draft: ChatStoreState, target: SelectionTarget | null) => void
   setDraft: (draft: ChatStoreState, text: string) => void
   setView: (draft: ChatStoreState, view: string) => void
-  setInspect: (draft: ChatStoreState, target: { callId: CallId } | null) => void
+  setInspect: (draft: ChatStoreState, target: { callId: ToolCallId } | null) => void
 }
 
 /**
@@ -28,7 +28,7 @@ export function createChatStore(): EngineStoreHandle<ChatStoreState, ChatActions
       select: (d, target: SelectionTarget | null) => { d.selection = target },
       setDraft: (d, text: string) => { d.draft = text },
       setView: (d, view: string) => { d.view = view },
-      setInspect: (d, target: { callId: CallId } | null) => { d.inspect = target },
+      setInspect: (d, target: { callId: ToolCallId } | null) => { d.inspect = target },
     },
   })
 }

+ 8 - 8
packages/compaction/compaction-basic/tests/compaction-basic.spec.ts

@@ -12,7 +12,7 @@ import {
   resolveTargetPolicy,
 } from '@deepseek-ai/dsh-compaction-basic/src/config.ts'
 import type { CompactionResult } from '@deepseek-ai/dsh-compaction'
-import LlmRuntime, { createUserMessage, CallId, CONTEXT_WINDOW_EXCEEDED_CODE, createToolResultMessage, LlmAdapter , createMessage } from '@deepseek-ai/dsh-llm'
+import LlmRuntime, { createUserMessage, ToolCallId, CONTEXT_WINDOW_EXCEEDED_CODE, createToolResultMessage, LlmAdapter , createMessage } from '@deepseek-ai/dsh-llm'
 import type {
   ContentBlock,
   GenerateOptions,
@@ -142,7 +142,7 @@ function conversation(turns = 4, text = 'fixture '.repeat(40).trim()): Session {
 function toolConversation(): Session {
   const session = Session.create(SessionId('tools'))
   for (let turn = 1; turn <= 3; turn += 1) {
-    const callId = CallId(`call-${turn}`)
+    const callId = ToolCallId(`call-${turn}`)
     session.append('turn/start', { turn })
     session.append('user/message', createUserMessage({
       content: [{ type: 'text', text: `request ${turn} `.repeat(300) }],
@@ -190,7 +190,7 @@ function toolConversation(): Session {
 /** One closed routed tool step followed by an open turn for rewrite events. */
 function oversizedToolResult(chars = 3_000, withCompactablePrompt = false): Session {
   const session = Session.create(SessionId(`oversized-tool-${chars}`))
-  const callId = CallId('oversized')
+  const callId = ToolCallId('oversized')
   session.append('turn/start', { turn: 1 })
   if (withCompactablePrompt) {
     session.append('user/message', createUserMessage({
@@ -568,7 +568,7 @@ describe('pressure measurement and retention', () => {
   it('declines forced overflow when the whole surface is one indivisible tool pair', async () => {
     const compact = service(compactConfig)
     const session = Session.create(SessionId('single-tool-pair'))
-    const callId = CallId('single-call')
+    const callId = ToolCallId('single-call')
     session.append('turn/start', { turn: 1 })
     session.append('step/start', { turn: 1, step: 1 })
     session.append('request/header', {
@@ -734,7 +734,7 @@ describe('pressure measurement and retention', () => {
   it('declines when rounding a cut would consume the only tool pair', () => {
     const ctx = createContext()
     const session = Session.create(SessionId('one-tool-pair'))
-    const callId = CallId('only')
+    const callId = ToolCallId('only')
     session.append('turn/start', { turn: 1 })
     session.append('step/start', { turn: 1, step: 1 })
     session.append('assistant/message', {
@@ -1183,7 +1183,7 @@ describe('default one-shot summarizer', () => {
     const { adapter, compact } = await summarizerHarness([
       { type: 'reasoning', text: 'private' },
       { type: 'text', text: 'public summary' },
-      { type: 'tool-call', id: CallId('unexpected'), name: 'x', arguments: '{}' },
+      { type: 'tool-call', id: ToolCallId('unexpected'), name: 'x', arguments: '{}' },
     ], undefined, MODEL, {
       auto: false,
       summarizationProvider: MODEL,
@@ -1199,7 +1199,7 @@ describe('default one-shot summarizer', () => {
       rawOutput: [
         { type: 'reasoning', text: 'private' },
         { type: 'text', text: 'public summary' },
-        { type: 'tool-call', id: CallId('unexpected'), name: 'x', arguments: '{}' },
+        { type: 'tool-call', id: ToolCallId('unexpected'), name: 'x', arguments: '{}' },
       ],
       llmStreamCall: true,
       provider: MODEL,
@@ -1417,7 +1417,7 @@ describe('default one-shot summarizer', () => {
   it('rejects image summary output nested in a tool result', async () => {
     const { compact } = await summarizerHarness([{
       type: 'tool-result',
-      toolCallId: CallId('summary-tool'),
+      toolCallId: ToolCallId('summary-tool'),
       content: [{
         type: 'image',
         attachment: {

+ 2 - 2
packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts

@@ -3,7 +3,7 @@ import { Context } from '@deepseek-ai/cordis'
 import { toolPairingBalancedAfter, toolPairingBalancedBefore } from '@deepseek-ai/dsh-compaction'
 import { createUserMessage, CONTEXT_WINDOW_EXCEEDED_CODE, LlmError, resolveRetryPolicy , createMessage } from '@deepseek-ai/dsh-llm'
 import type { ContentBlock, GenerateOptions, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk } from '@deepseek-ai/dsh-llm'
-import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
 import { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
 import type { Agent } from '@deepseek-ai/dsh-agent'
 import AgentLoop from '@deepseek-ai/dsh-agent-loop'
@@ -54,7 +54,7 @@ class StepwiseToolAdapter extends LlmAdapter {
     const n = this.calls
     this.calls += 1
     if (n < this.toolSteps) {
-      const id = CallId(`c${n}`)
+      const id = ToolCallId(`c${n}`)
       const args = `{"i":${n}}`
       yield { type: 'block-start', index: 0, blockType: 'text' }
       yield { type: 'block-end', index: 0, block: { type: 'text', text: `step ${n}` } }

+ 2 - 2
packages/compaction/compaction-tool-result-pruner/src/types.ts

@@ -1,4 +1,4 @@
-import type { CallId } from '@deepseek-ai/dsh-llm'
+import type { ToolCallId } from '@deepseek-ai/dsh-llm'
 
 /** Character-budget policy for deterministic tool-result pruning. */
 export interface ToolResultPruneConfig {
@@ -24,7 +24,7 @@ export interface PrunedEntry {
   /** Newly appended pruned tool-result event. */
   readonly replacementSeq: number
   /** Tool call shared by the original and replacement. */
-  readonly callId: CallId
+  readonly callId: ToolCallId
   /** Original text size in Unicode code points. */
   readonly charsBefore: number
   /** Replacement text size in Unicode code points. */

+ 6 - 6
packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
+import { ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
 import type { ContentBlock } from '@deepseek-ai/dsh-llm'
 import SessionStore, {
   Session,
@@ -43,7 +43,7 @@ function appendToolStep(
   content: ContentBlock[],
   extra: Record<string, unknown> = {},
 ): number {
-  const callId = CallId(call)
+  const callId = ToolCallId(call)
   session.append('turn/start', {
     turn,
   })
@@ -126,7 +126,7 @@ describe('ToolResultPruner content transform', () => {
     const reasoning: ContentBlock = { type: 'reasoning', text: 'private-rich-block' }
     const call: ContentBlock = {
       type: 'tool-call',
-      id: CallId('nested'),
+      id: ToolCallId('nested'),
       name: 'nested',
       arguments: '{}',
     }
@@ -178,7 +178,7 @@ describe('ToolResultPruner session transaction', () => {
     expect(result.pruned).toHaveLength(1)
     expect(result.charsRemoved).toBeGreaterThan(0)
     const entry = result.pruned[0]!
-    expect(entry).toMatchObject({ originalSeq, callId: CallId('one'), charsBefore: 100 })
+    expect(entry).toMatchObject({ originalSeq, callId: ToolCallId('one'), charsBefore: 100 })
     expect(entry.charsAfter).toBeLessThanOrEqual(50)
 
     const original = session.events[originalSeq]!
@@ -201,7 +201,7 @@ describe('ToolResultPruner session transaction', () => {
         step: 1,
         isError: true,
         message: {
-          source: { kind: 'tool', callId: CallId('one') },
+          source: { kind: 'tool', callId: ToolCallId('one') },
         },
         error: { name: 'ExitError', code: 'EXIT_1' },
         meta: { diff: ['a', 'b'] },
@@ -236,7 +236,7 @@ describe('ToolResultPruner session transaction', () => {
     const prune = service()
     const first = prune.pruneSession(session)
     const second = prune.pruneSession(session)
-    expect(first.pruned.map(entry => entry.callId)).toEqual([CallId('a'), CallId('c')])
+    expect(first.pruned.map(entry => entry.callId)).toEqual([ToolCallId('a'), ToolCallId('c')])
     expect(first.charsRemoved).toBe(
       first.pruned.reduce((sum, entry) => sum + entry.charsBefore - entry.charsAfter, 0),
     )

+ 16 - 16
packages/compaction/compaction/tests/tool-pairing.spec.ts

@@ -1,5 +1,5 @@
 import { describe, expect, it } from 'vitest'
-import { createUserMessage, CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
 import { toolPairingBalancedAfter, toolPairingBalancedBefore } from '@deepseek-ai/dsh-compaction'
 import { Session, SessionId } from '@deepseek-ai/dsh-session'
 import type { SessionEvent } from '@deepseek-ai/dsh-session'
@@ -35,7 +35,7 @@ function closedToolStep(): Session {
     step: 1,
     message: createMessage({
       role: 'assistant',
-      content: [{ type: 'tool-call', id: CallId('c1'), name: 'bash', arguments: '{}' }],
+      content: [{ type: 'tool-call', id: ToolCallId('c1'), name: 'bash', arguments: '{}' }],
       source: {
         kind: 'model',
         ...{ provider: 'mock', model: 'mock' },
@@ -46,7 +46,7 @@ function closedToolStep(): Session {
     turn: 1,
     step: 1,
     message: createToolResultMessage({
-      callId: CallId('c1'),
+      callId: ToolCallId('c1'),
       content: [{ type: 'text', text: 'done' }],
       isError: false,
     }),
@@ -70,7 +70,7 @@ describe('tool-pairing boundaries', () => {
       step: 1,
       message: createMessage({
         role: 'assistant',
-        content: [{ type: 'tool-call', id: CallId('open'), name: 'bash', arguments: '{}' }],
+        content: [{ type: 'tool-call', id: ToolCallId('open'), name: 'bash', arguments: '{}' }],
         source: {
           kind: 'model',
           ...{ provider: 'mock', model: 'mock' },
@@ -88,8 +88,8 @@ describe('tool-pairing boundaries', () => {
       message: createMessage({
         role: 'assistant',
         content: [
-          { type: 'tool-call', id: CallId('c1'), name: 'one', arguments: '{}' },
-          { type: 'tool-call', id: CallId('c2'), name: 'two', arguments: '{}' },
+          { type: 'tool-call', id: ToolCallId('c1'), name: 'one', arguments: '{}' },
+          { type: 'tool-call', id: ToolCallId('c2'), name: 'two', arguments: '{}' },
         ],
         source: {
           kind: 'model',
@@ -100,7 +100,7 @@ describe('tool-pairing boundaries', () => {
     session.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('c1'),
+        callId: ToolCallId('c1'),
         content: [],
         isError: false,
       }),
@@ -108,7 +108,7 @@ describe('tool-pairing boundaries', () => {
     session.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('c2'),
+        callId: ToolCallId('c2'),
         content: [],
         isError: false,
       }),
@@ -125,7 +125,7 @@ describe('tool-pairing boundaries', () => {
       step: 1,
       message: createMessage({
         role: 'assistant',
-        content: [{ type: 'tool-call', id: CallId('c1'), name: 'bash', arguments: '{}' }],
+        content: [{ type: 'tool-call', id: ToolCallId('c1'), name: 'bash', arguments: '{}' }],
         source: {
           kind: 'model',
           ...{ provider: 'mock', model: 'mock' },
@@ -139,7 +139,7 @@ describe('tool-pairing boundaries', () => {
     midStep.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('c1'),
+        callId: ToolCallId('c1'),
         content: [],
         isError: false,
       }),
@@ -217,7 +217,7 @@ describe('tool-pairing cache refresh', () => {
           step: 1,
           message: createMessage({
             role: 'assistant',
-            content: [{ type: 'tool-call', id: CallId('c1'), name: 'one', arguments: '{}' }],
+            content: [{ type: 'tool-call', id: ToolCallId('c1'), name: 'one', arguments: '{}' }],
             source: {
               kind: 'model',
               ...{ provider: 'mock', model: 'mock' },
@@ -231,7 +231,7 @@ describe('tool-pairing cache refresh', () => {
         data: {
           turn: 1, step: 1,
           message: createToolResultMessage({
-            callId: CallId('c1'),
+            callId: ToolCallId('c1'),
             content: [],
             isError: false,
           }),
@@ -297,7 +297,7 @@ describe('tool-pairing cache refresh', () => {
           step: 1,
           message: createMessage({
             role: 'assistant',
-            content: [{ type: 'tool-call', id: CallId('c2'), name: 'two', arguments: '{}' }],
+            content: [{ type: 'tool-call', id: ToolCallId('c2'), name: 'two', arguments: '{}' }],
             source: {
               kind: 'model',
               ...{ provider: 'mock', model: 'mock' },
@@ -311,7 +311,7 @@ describe('tool-pairing cache refresh', () => {
         data: {
           turn: 2, step: 1,
           message: createToolResultMessage({
-            callId: CallId('c2'),
+            callId: ToolCallId('c2'),
             content: [],
             isError: false,
           }),
@@ -370,7 +370,7 @@ describe('tool-pairing corrupt surfaces', () => {
     session.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('orphan'),
+        callId: ToolCallId('orphan'),
         content: [],
         isError: false,
       }),
@@ -387,7 +387,7 @@ describe('tool-pairing corrupt surfaces', () => {
     session.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('orphan'),
+        callId: ToolCallId('orphan'),
         content: [],
         isError: false,
       }),

+ 92 - 92
packages/context/agent-instructions/tests/agent-instructions.spec.ts

@@ -5,7 +5,7 @@ import { describe, expect, it, vi } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
 import Loader from '@deepseek-ai/cordis-plugin-loader'
 import * as workspaceContext from '@deepseek-ai/dsh-agent-instructions'
-import LlmRuntime, { createUserMessage, CallId, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'
+import LlmRuntime, { createUserMessage, ToolCallId, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'
 import SessionStore, { Session, SessionId, SESSION_FORMAT_VERSION, type SessionEvent, type UserMessage } from '@deepseek-ai/dsh-session'
 import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
 import AgentLoop from '@deepseek-ai/dsh-agent-loop'
@@ -1657,7 +1657,7 @@ describe('workspace context request injection', () => {
       await write(join(root, 'AGENTS.md'), 'updated repo rule')
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-remount'),
+        callId: ToolCallId('read-after-remount'),
         name: 'read',
         arguments: { file_path: 'file.txt' },
         agent,
@@ -1854,7 +1854,7 @@ describe('workspace context request injection', () => {
       await write(join(root, 'AGENTS.md'), 'new root rule with more detail')
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-baseline-change'), name: 'read', arguments: { file_path: 'file.txt' }, agent,
+        callId: ToolCallId('read-after-baseline-change'), name: 'read', arguments: { file_path: 'file.txt' }, agent,
       })
 
       expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
@@ -1883,7 +1883,7 @@ describe('workspace context request injection', () => {
       await rm(join(root, 'AGENTS.md'))
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-baseline-remove'), name: 'read', arguments: { file_path: 'file.txt' }, agent,
+        callId: ToolCallId('read-after-baseline-remove'), name: 'read', arguments: { file_path: 'file.txt' }, agent,
       })
 
       expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
@@ -2499,9 +2499,9 @@ describe('dynamic nested workspace context injection', () => {
       const adapter = new MockAdapter([
         [
           { type: 'block-start', index: 0, blockType: 'tool-call' },
-          { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('read-before-abort'), name: 'read', arguments: '{"file_path":"pkg/deep/file.txt"}' } },
+          { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('read-before-abort'), name: 'read', arguments: '{"file_path":"pkg/deep/file.txt"}' } },
           { type: 'block-start', index: 1, blockType: 'tool-call' },
-          { type: 'block-end', index: 1, block: { type: 'tool-call', id: CallId('abort-after-read'), name: 'abort_step', arguments: '{}' } },
+          { type: 'block-end', index: 1, block: { type: 'tool-call', id: ToolCallId('abort-after-read'), name: 'abort_step', arguments: '{}' } },
           { type: 'finish', reason: { kind: 'tool-calls' } },
         ] satisfies StreamChunk[],
         toolCallResponse('read-after-abort', 'read', { file_path: join('pkg', 'deep', 'file.txt') }),
@@ -2595,7 +2595,7 @@ describe('dynamic nested workspace context injection', () => {
       const reason = new Error('cancel dynamic reconciliation')
       controller.abort(reason)
       const exec = stubToolExecution({
-        callId: CallId('cancelled-dynamic-read'),
+        callId: ToolCallId('cancelled-dynamic-read'),
         name: 'read',
         arguments: { file_path: join('pkg', 'file.txt') },
         agent: stubAgent(root),
@@ -2632,7 +2632,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const result = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-nested'),
+        callId: ToolCallId('read-nested'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -2678,7 +2678,7 @@ describe('dynamic nested workspace context injection', () => {
 
       ctx.emit('tools/result', stubToolExecution({
         signal: controller.signal,
-        callId: CallId('read-before-signal-end'),
+        callId: ToolCallId('read-before-signal-end'),
         name: 'read',
         arguments: { file_path: join('pkg', 'file.txt') },
         agent,
@@ -2711,7 +2711,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-configured-nested-candidate'),
+        callId: ToolCallId('read-configured-nested-candidate'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -2744,7 +2744,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-nested-overlay'),
+        callId: ToolCallId('read-nested-overlay'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -2787,7 +2787,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-nested-overlay-disabled'),
+        callId: ToolCallId('read-nested-overlay-disabled'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -2815,7 +2815,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const first = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-nested-1'),
+        callId: ToolCallId('read-nested-1'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -2823,7 +2823,7 @@ describe('dynamic nested workspace context injection', () => {
       await appendAdditionalContexts(ctx, agent)
       const second = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-nested-2'),
+        callId: ToolCallId('read-nested-2'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -2858,12 +2858,12 @@ describe('dynamic nested workspace context injection', () => {
 
       const first = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-version-fast-path'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-version-fast-path'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await appendAdditionalContexts(ctx, agent)
       const second = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-with-version-fast-path'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-with-version-fast-path'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       expect(first.additionalContexts).toBeUndefined()
@@ -2895,18 +2895,18 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-same-digest-version-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-same-digest-version-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await appendAdditionalContexts(ctx, agent)
       fs.entries.set(instructionPath, { type: 'file', content: 'same package rule', version: FsVersion('revision-2') })
       const afterVersionChange = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-same-digest-version-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-same-digest-version-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await syncWorkspaceContext(ctx, agent)
       const afterRefresh = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-version-cache-refresh'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-version-cache-refresh'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       expect(afterVersionChange.additionalContexts).toBeUndefined()
@@ -2941,11 +2941,11 @@ describe('dynamic nested workspace context injection', () => {
       const secondAgent = stubAgent(root)
       const first = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-from-first-session'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: firstAgent,
+        callId: ToolCallId('read-from-first-session'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: firstAgent,
       })
       const second = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-from-second-session'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: secondAgent,
+        callId: ToolCallId('read-from-second-session'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: secondAgent,
       })
 
       expect(first.additionalContexts).toBeUndefined()
@@ -2973,13 +2973,13 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await appendAdditionalContexts(ctx, agent)
       await write(join(root, 'pkg/AGENTS.md'), 'new package rule with more detail')
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
@@ -3016,7 +3016,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-both-siblings'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-both-siblings'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       const firstText = blocksText(((await syncedWorkspaceContext(ctx, agent))).content)
       expect(firstText).toContain('native package rule')
@@ -3025,7 +3025,7 @@ describe('dynamic nested workspace context injection', () => {
       await rm(join(root, 'pkg/AGENTS.md'))
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-one-sibling-removed'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-one-sibling-removed'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       // Removing one candidate only removes its own scope; the sibling scope is untouched.
@@ -3054,7 +3054,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-nested-dup-siblings'), name: 'read', arguments: { file_path: join('pkg', 'deep', 'file.txt') }, agent,
+        callId: ToolCallId('read-nested-dup-siblings'), name: 'read', arguments: { file_path: join('pkg', 'deep', 'file.txt') }, agent,
       })
 
       expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
@@ -3276,7 +3276,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-dup-convergence'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-dup-convergence'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       const firstText = blocksText(((await syncedWorkspaceContext(ctx, agent))).content)
       expect(firstText).toContain('canonical nested rule')
@@ -3285,7 +3285,7 @@ describe('dynamic nested workspace context injection', () => {
       await write(join(root, 'pkg/CLAUDE.md'), 'canonical nested rule')
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-dup-convergence'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-dup-convergence'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       const convergence = await syncedWorkspaceContext(ctx, agent)
@@ -3313,14 +3313,14 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-earlier-converges'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-earlier-converges'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await appendAdditionalContexts(ctx, agent)
       // Only the earlier candidate changes; the sibling stays byte-identical but now duplicates it.
       await write(join(root, 'pkg/AGENTS.md'), 'secondary nested rule')
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-earlier-converges'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-earlier-converges'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
@@ -3351,13 +3351,13 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-remove'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-remove'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await appendAdditionalContexts(ctx, agent)
       await rm(join(root, 'pkg/AGENTS.md'))
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-remove'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-remove'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
@@ -3391,7 +3391,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-symlink-dir'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-symlink-dir'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       const firstText = blocksText(((await syncedWorkspaceContext(ctx, agent))).content)
       await appendAdditionalContexts(ctx, agent)
@@ -3405,7 +3405,7 @@ describe('dynamic nested workspace context injection', () => {
       await symlink(join(root, 'pkg/elsewhere'), join(root, 'pkg/AGENTS.md'))
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-symlink-dir'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-symlink-dir'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
@@ -3431,20 +3431,20 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await appendAdditionalContexts(ctx, agent)
       await rm(join(root, 'pkg/AGENTS.md'))
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-to-create-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-to-create-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await appendAdditionalContexts(ctx, agent)
       await write(join(root, 'pkg/AGENTS.md'), 'restored package rule')
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-after-tombstone'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       expect(((await syncedWorkspaceContext(ctx, agent))).source).toMatchObject({
@@ -3476,13 +3476,13 @@ describe('dynamic nested workspace context injection', () => {
 
       const first = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-provider-failure'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-before-provider-failure'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await appendAdditionalContexts(ctx, agent)
       fs.throwOnStat.add(join(root, 'pkg/AGENTS.md'))
       const duringFailure = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-during-provider-failure'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-during-provider-failure'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
 
       expect(first.additionalContexts).toBeUndefined()
@@ -3506,7 +3506,7 @@ describe('dynamic nested workspace context injection', () => {
       const agent = stubAgent(root)
       const first = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-resume'),
+        callId: ToolCallId('read-before-resume'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -3516,7 +3516,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const afterResume = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-resume'),
+        callId: ToolCallId('read-after-resume'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent: resumed,
@@ -3542,7 +3542,7 @@ describe('dynamic nested workspace context injection', () => {
       const original = stubAgent(root)
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-offline-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: original,
+        callId: ToolCallId('read-before-offline-change'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent: original,
       })
       await appendAdditionalContexts(ctx, original)
       await write(join(root, 'pkg/AGENTS.md'), 'new nested rule after resume')
@@ -3573,7 +3573,7 @@ describe('dynamic nested workspace context injection', () => {
       const agent = stubAgent(root)
       const first = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-before-compact'),
+        callId: ToolCallId('read-before-compact'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -3581,7 +3581,7 @@ describe('dynamic nested workspace context injection', () => {
       const contextSeq = (await appendAdditionalContexts(ctx, agent))!
       const visibleBeforeCompact = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-while-visible'),
+        callId: ToolCallId('read-while-visible'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -3597,7 +3597,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const afterCompact = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-compact'),
+        callId: ToolCallId('read-after-compact'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -3629,7 +3629,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const whileVisible = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-visible-baseline'),
+        callId: ToolCallId('read-visible-baseline'),
         name: 'read',
         arguments: { file_path: 'file.txt' },
         agent,
@@ -3644,7 +3644,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-compacted-baseline'),
+        callId: ToolCallId('read-compacted-baseline'),
         name: 'read',
         arguments: { file_path: 'file.txt' },
         agent,
@@ -3653,7 +3653,7 @@ describe('dynamic nested workspace context injection', () => {
       await appendAdditionalContexts(ctx, agent)
       const afterRearm = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-rearmed-baseline'),
+        callId: ToolCallId('read-rearmed-baseline'),
         name: 'read',
         arguments: { file_path: 'file.txt' },
         agent,
@@ -3685,7 +3685,7 @@ describe('dynamic nested workspace context injection', () => {
       const agent = stubAgent(root)
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-package'),
+        callId: ToolCallId('read-package'),
         name: 'read',
         arguments: { file_path: join('pkg', 'file.txt') },
         agent,
@@ -3695,7 +3695,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-subtree'),
+        callId: ToolCallId('read-subtree'),
         name: 'read',
         arguments: { file_path: join('pkg', 'sub', 'file.txt') },
         agent,
@@ -3723,7 +3723,7 @@ describe('dynamic nested workspace context injection', () => {
       const agent = stubAgent(root)
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-subtree-omitting-parent'),
+        callId: ToolCallId('read-subtree-omitting-parent'),
         name: 'read',
         arguments: { file_path: join('pkg', 'sub', 'file.txt') },
         agent,
@@ -3733,7 +3733,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-parent-after-omit'),
+        callId: ToolCallId('read-parent-after-omit'),
         name: 'read',
         arguments: { file_path: join('pkg', 'other.txt') },
         agent,
@@ -3785,7 +3785,7 @@ describe('dynamic nested workspace context injection', () => {
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-spoofed-state'),
+        callId: ToolCallId('read-after-spoofed-state'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -3812,14 +3812,14 @@ describe('dynamic nested workspace context injection', () => {
 
       const rootResult = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-root-file'),
+        callId: ToolCallId('read-root-file'),
         name: 'read',
         arguments: { file_path: 'root.txt' },
         agent,
       })
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-absolute-nested-file'),
+        callId: ToolCallId('read-absolute-nested-file'),
         name: 'read',
         arguments: { file_path: join(root, 'pkg/deep/file.txt') },
         agent,
@@ -3855,7 +3855,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const result = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-with-unreadable-nested-instruction'),
+        callId: ToolCallId('read-with-unreadable-nested-instruction'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -3900,7 +3900,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const result = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-with-downstream'),
+        callId: ToolCallId('read-with-downstream'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -3953,7 +3953,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const result = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-blocked-downstream'),
+        callId: ToolCallId('read-blocked-downstream'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -3996,7 +3996,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const blocked = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('outer-block-first'),
+        callId: ToolCallId('outer-block-first'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -4007,7 +4007,7 @@ describe('dynamic nested workspace context injection', () => {
       shouldBlock = false
       const accepted = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('outer-block-retry'),
+        callId: ToolCallId('outer-block-retry'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -4043,7 +4043,7 @@ describe('dynamic nested workspace context injection', () => {
         async execute(_args, exec) {
           const nested = await ctx.tools.execute({
             signal: testToolSignal,
-            callId: CallId(`${exec.callId}:nested`),
+            callId: ToolCallId(`${exec.callId}:nested`),
             name: 'read',
             arguments: { file_path: join('pkg', 'deep', 'file.txt') },
             ...exec.agent === undefined ? {} : { agent: exec.agent },
@@ -4065,7 +4065,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const blocked = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('composite-first'), name: 'composite-read', arguments: {}, agent,
+        callId: ToolCallId('composite-first'), name: 'composite-read', arguments: {}, agent,
       })
 
       expect(blocked.isError).toBe(true)
@@ -4099,7 +4099,7 @@ describe('dynamic nested workspace context injection', () => {
         token: Symbol('nested-read') as ToolExecutionToken,
         parent: outerToken,
         signal: testToolSignal,
-        callId: CallId('nested-read'),
+        callId: ToolCallId('nested-read'),
         name: 'read',
         arguments: { file_path: join('pkg', 'file.txt') },
         agent,
@@ -4108,7 +4108,7 @@ describe('dynamic nested workspace context injection', () => {
         token: Symbol('nested-non-file') as ToolExecutionToken,
         parent: outerToken,
         signal: testToolSignal,
-        callId: CallId('nested-non-file'),
+        callId: ToolCallId('nested-non-file'),
         name: 'search',
         arguments: {},
         agent,
@@ -4117,7 +4117,7 @@ describe('dynamic nested workspace context injection', () => {
         token: Symbol('second-nested-read') as ToolExecutionToken,
         parent: outerToken,
         signal: testToolSignal,
-        callId: CallId('second-nested-read'),
+        callId: ToolCallId('second-nested-read'),
         name: 'read',
         arguments: { file_path: join('pkg', 'second.txt') },
         agent,
@@ -4125,7 +4125,7 @@ describe('dynamic nested workspace context injection', () => {
       ctx.emit('tools/result', stubToolExecution({
         token: outerToken,
         signal: testToolSignal,
-        callId: CallId('outer-code-run'),
+        callId: ToolCallId('outer-code-run'),
         name: 'run_code',
         arguments: {},
         agent,
@@ -4163,7 +4163,7 @@ describe('dynamic nested workspace context injection', () => {
 
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('read-after-closed-step'),
+        callId: ToolCallId('read-after-closed-step'),
         name: 'read',
         arguments: { file_path: join('pkg', 'file.txt') },
         agent,
@@ -4185,38 +4185,38 @@ describe('dynamic nested workspace context injection', () => {
       await ctx.plugin(workspaceContext, { maxBytes: 65536 })
       const fs = ctx.fs as RecordingFileSystem
       const agent = stubAgent('/')
-      const plainResult = { callId: CallId('plain'), content: [], isError: false as const, value: null }
+      const plainResult = { callId: ToolCallId('plain'), content: [], isError: false as const, value: null }
       const aborted = new AbortController()
       aborted.abort(new Error('cancelled'))
 
       ctx.emit('tools/result', stubToolExecution({
-        signal: testToolSignal, callId: CallId('agentless'), name: 'read', arguments: { file_path: 'file.txt' },
+        signal: testToolSignal, callId: ToolCallId('agentless'), name: 'read', arguments: { file_path: 'file.txt' },
       }), plainResult)
       ctx.emit('tools/result', stubToolExecution({
-        signal: testToolSignal, callId: CallId('failed'), name: 'read', arguments: { file_path: 'failed/file.txt' }, agent,
+        signal: testToolSignal, callId: ToolCallId('failed'), name: 'read', arguments: { file_path: 'failed/file.txt' }, agent,
       }), { content: [], isError: true, error: { message: 'failed' } })
       ctx.emit('tools/result', stubToolExecution({
-        signal: aborted.signal, callId: CallId('aborted'), name: 'read', arguments: { file_path: 'aborted/file.txt' }, agent,
+        signal: aborted.signal, callId: ToolCallId('aborted'), name: 'read', arguments: { file_path: 'aborted/file.txt' }, agent,
       }), plainResult)
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('null-arguments'), name: 'read', arguments: null, agent,
+        callId: ToolCallId('null-arguments'), name: 'read', arguments: null, agent,
       }), plainResult)
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('missing-path'), name: 'read', arguments: {}, agent,
+        callId: ToolCallId('missing-path'), name: 'read', arguments: {}, agent,
       }), plainResult)
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('non-string-path'), name: 'read', arguments: { file_path: 1 }, agent,
+        callId: ToolCallId('non-string-path'), name: 'read', arguments: { file_path: 1 }, agent,
       }), plainResult)
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('blank-path'), name: 'read', arguments: { file_path: ' ' }, agent,
+        callId: ToolCallId('blank-path'), name: 'read', arguments: { file_path: ' ' }, agent,
       }), plainResult)
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('non-fs'), name: 'composite', arguments: {}, agent,
+        callId: ToolCallId('non-fs'), name: 'composite', arguments: {}, agent,
       }), plainResult)
 
       await Promise.resolve()
@@ -4243,7 +4243,7 @@ describe('dynamic nested workspace context injection', () => {
 
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('projection-failure'),
+        callId: ToolCallId('projection-failure'),
         name: 'read',
         arguments: { file_path: 'file.txt' },
         agent,
@@ -4269,7 +4269,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const result = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-with-disabled-budget'),
+        callId: ToolCallId('read-with-disabled-budget'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent: stubAgent(root),
@@ -4302,12 +4302,12 @@ describe('dynamic nested workspace context injection', () => {
 
       const first = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-tiny-budget-1'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-tiny-budget-1'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await syncWorkspaceContext(ctx, agent)
       const second = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-tiny-budget-2'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('read-tiny-budget-2'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await syncWorkspaceContext(ctx, agent)
 
@@ -4335,7 +4335,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const result = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-missing'),
+        callId: ToolCallId('read-missing'),
         name: 'read',
         arguments: { file_path: join('pkg', 'missing.txt') },
         agent: stubAgent(root),
@@ -4363,7 +4363,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const result = await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('read-after-dispose'),
+        callId: ToolCallId('read-after-dispose'),
         name: 'read',
         arguments: { file_path: join('pkg', 'deep', 'file.txt') },
         agent,
@@ -4423,7 +4423,7 @@ describe('workspace context inbox synchronization', () => {
       const agent = stubAgent(root)
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('tiny-budget-touch'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('tiny-budget-touch'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       }), acceptedResult)
 
       await syncWorkspaceContext(ctx, agent)
@@ -4451,7 +4451,7 @@ describe('workspace context inbox synchronization', () => {
       const agent = stubAgent(root)
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('pending-v1'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('pending-v1'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await syncWorkspaceContext(ctx, agent)
       expect(blocksText(agent.inbox.nextStep[0]?.content)).toContain('pending version one')
@@ -4464,7 +4464,7 @@ describe('workspace context inbox synchronization', () => {
       await write(join(root, 'pkg/AGENTS.md'), 'pending version two with more detail')
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('pending-v2'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('pending-v2'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await syncWorkspaceContext(ctx, agent)
       expect(agent.inbox.nextStep).toHaveLength(1)
@@ -4475,7 +4475,7 @@ describe('workspace context inbox synchronization', () => {
       await rm(join(root, 'pkg/AGENTS.md'))
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('pending-delete'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
+        callId: ToolCallId('pending-delete'), name: 'read', arguments: { file_path: join('pkg', 'file.txt') }, agent,
       })
       await syncWorkspaceContext(ctx, agent)
       expect(agent.inbox.nextStep).toEqual([])
@@ -4499,7 +4499,7 @@ describe('workspace context inbox synchronization', () => {
       const agent = stubAgent(root)
       const first = stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('projected-before-abort'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent,
+        callId: ToolCallId('projected-before-abort'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent,
       })
       ctx.emit('tools/result', first, acceptedResult)
       const controller = new AbortController()
@@ -4512,7 +4512,7 @@ describe('workspace context inbox synchronization', () => {
 
       ctx.emit('tools/result', stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('projected-after-abort'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent,
+        callId: ToolCallId('projected-after-abort'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent,
       }), acceptedResult)
       await syncWorkspaceContext(ctx, agent)
       const text = blocksText(agent.inbox.nextStep[0]?.content)
@@ -4539,11 +4539,11 @@ describe('workspace context inbox synchronization', () => {
       const agent = stubAgent(root)
       const first = stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('concurrent-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent,
+        callId: ToolCallId('concurrent-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent,
       })
       const second = stubToolExecution({
         signal: testToolSignal,
-        callId: CallId('concurrent-b'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent,
+        callId: ToolCallId('concurrent-b'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent,
       })
 
       ctx.emit('tools/result', first, acceptedResult)
@@ -4577,14 +4577,14 @@ describe('workspace context inbox synchronization', () => {
       const original = stubAgent(root)
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('recover-pending-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent: original,
+        callId: ToolCallId('recover-pending-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent: original,
       })
       await syncWorkspaceContext(ctx, original)
       const resumed = stubAgent(root, [...original.session.events])
 
       await ctx.tools.execute({
         signal: testToolSignal,
-        callId: CallId('recover-pending-b'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent: resumed,
+        callId: ToolCallId('recover-pending-b'), name: 'read', arguments: { file_path: join('b', 'file.txt') }, agent: resumed,
       })
       await syncWorkspaceContext(ctx, resumed)
 

+ 2 - 2
packages/context/session-reference/tests/session-reference.spec.ts

@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
 import { agentEvents, type Agent } from '@deepseek-ai/dsh-agent'
 import { CompactionId, compactCheckpointSource } from '@deepseek-ai/dsh-compaction'
-import { createUserMessage, CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
 import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
 import SessionQueryEngine from '@deepseek-ai/dsh-session-query'
 import SessionReferenceResolver, {
@@ -122,7 +122,7 @@ function appendConversation(session: Session): void {
     {
       turn: 2, step: 1,
       message: createToolResultMessage({
-        callId: CallId('call'),
+        callId: ToolCallId('call'),
         content: [{ type: 'text', text: 'tool output' }],
         isError: false,
       }),

+ 2 - 2
packages/context/time-context/tests/time-context.spec.ts

@@ -1,7 +1,7 @@
 import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
 import Loader from '@deepseek-ai/cordis-plugin-loader'
-import { createUserMessage, CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
 import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
 import { Session, SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
 import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
@@ -113,7 +113,7 @@ function toolCallResponse(): StreamChunk[] {
     {
       type: 'block-end',
       index: 0,
-      block: { type: 'tool-call', id: CallId('tick-1'), name: 'tick', arguments: '{}' },
+      block: { type: 'tool-call', id: ToolCallId('tick-1'), name: 'tick', arguments: '{}' },
     },
     { type: 'finish', reason: { kind: 'tool-calls' } },
   ]

+ 2 - 2
packages/core/agent-loop/tests/agent-initiator.spec.ts

@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'
 import { Context, type Fiber } from '@deepseek-ai/cordis'
 import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
 import AgentLoop from '@deepseek-ai/dsh-agent-loop'
-import LlmRuntime, { createUserMessage, CallId, LlmAdapter  } from '@deepseek-ai/dsh-llm'
+import LlmRuntime, { createUserMessage, ToolCallId, LlmAdapter  } from '@deepseek-ai/dsh-llm'
 import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
 import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
@@ -310,7 +310,7 @@ describe('AgentLoop initiator scope', () => {
 
     const direct = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('direct'),
+      callId: ToolCallId('direct'),
       name: 'agentless-probe',
       arguments: {},
     })

+ 10 - 10
packages/core/agent-loop/tests/contract-regressions.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import LlmRuntime, { createUserMessage, CallId, LlmError, MessageSource, ProviderRequestId, StreamChunk  } from '@deepseek-ai/dsh-llm'
+import LlmRuntime, { createUserMessage, ToolCallId, LlmError, MessageSource, ProviderRequestId, StreamChunk  } from '@deepseek-ai/dsh-llm'
 import SessionStore, { Session, SessionEvent, SessionId, TurnEndReason, type UserMessage } from '@deepseek-ai/dsh-session'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
 import ToolRuntime, { defineContentToolFixture, type PostToolDecision } from '@deepseek-ai/dsh-tools'
@@ -135,9 +135,9 @@ describe('abort during tool execution ends the turn', () => {
   it('records post-tool context when a later call aborts the batch', async () => {
     const adapter = new MockAdapter([[
       { type: 'block-start', index: 0, blockType: 'tool-call' },
-      { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('c1'), name: 'first', arguments: '{}' } },
+      { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('c1'), name: 'first', arguments: '{}' } },
       { type: 'block-start', index: 1, blockType: 'tool-call' },
-      { type: 'block-end', index: 1, block: { type: 'tool-call', id: CallId('c2'), name: 'aborter', arguments: '{}' } },
+      { type: 'block-end', index: 1, block: { type: 'tool-call', id: ToolCallId('c2'), name: 'aborter', arguments: '{}' } },
       { type: 'finish', reason: { kind: 'tool-calls' } },
     ] satisfies StreamChunk[]])
     const ctx = await harness(adapter)
@@ -160,7 +160,7 @@ describe('abort during tool execution ends the turn', () => {
       },
     }))
     ctx.on('tools/post-execute', async (exec, _result, next): Promise<PostToolDecision> => {
-      if (exec.callId !== CallId('c1')) return next()
+      if (exec.callId !== ToolCallId('c1')) return next()
       return {
         kind: 'accept',
         additionalContexts: [createUserMessage({
@@ -259,9 +259,9 @@ describe('abort during tool execution ends the turn', () => {
     const adapter = new MockAdapter([
       [
         { type: 'block-start', index: 0, blockType: 'tool-call' },
-        { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('c1'), name: 'aborter', arguments: '{}' } },
+        { type: 'block-end', index: 0, block: { type: 'tool-call', id: ToolCallId('c1'), name: 'aborter', arguments: '{}' } },
         { type: 'block-start', index: 1, blockType: 'tool-call' },
-        { type: 'block-end', index: 1, block: { type: 'tool-call', id: CallId('c2'), name: 'second', arguments: '{}' } },
+        { type: 'block-end', index: 1, block: { type: 'tool-call', id: ToolCallId('c2'), name: 'second', arguments: '{}' } },
         { type: 'finish', reason: { kind: 'tool-calls' } },
       ] satisfies StreamChunk[],
       textResponse('later turn'),
@@ -553,7 +553,7 @@ describe('discriminated SessionEvent narrows without casts', () => {
   it('narrows event.data from event.type', () => {
     const session = Session.create(SessionId('s'))
     const appended: SessionEvent = session.append('tool/call', {
-      turn: 1, step: 1, callId: CallId('c1'), name: 'echo', arguments: '{}',
+      turn: 1, step: 1, callId: ToolCallId('c1'), name: 'echo', arguments: '{}',
     })
     // compile-time: this switch narrows; runtime: values flow through
     switch (appended.type) {
@@ -1069,7 +1069,7 @@ describe('tool result call identity', () => {
     // The loop must still record the tool/result under the model's authoritative
     // call.id, which is the immutable identity carried by the execution input.
     ctx.on('tools/post-execute', (exec, _result) => {
-      expect(exec.callId).toBe(CallId('c1')) // the loop passed the real id in
+      expect(exec.callId).toBe(ToolCallId('c1')) // the loop passed the real id in
       return Promise.resolve({ kind: 'accept', content: [{ type: 'text', text: 'ok' }] })
     }, { prepend: true })
 
@@ -1081,7 +1081,7 @@ describe('tool result call identity', () => {
     const resultEvent = [...agent.session.events].find(e => e.type === 'tool/result')
     expect(resultEvent?.type).toBe('tool/result')
     if (resultEvent?.type === 'tool/result') {
-      expect(resultEvent.data.message.source.callId).toBe(CallId('c1'))
+      expect(resultEvent.data.message.source.callId).toBe(ToolCallId('c1'))
     }
 
     // And deriveMessages pairs the tool-result with the assistant tool-call:
@@ -1092,7 +1092,7 @@ describe('tool result call identity', () => {
       .find(b => b.type === 'tool-result')
     expect(toolResultBlock?.type).toBe('tool-result')
     if (toolResultBlock?.type === 'tool-result') {
-      expect(toolResultBlock.toolCallId).toBe(CallId('c1'))
+      expect(toolResultBlock.toolCallId).toBe(ToolCallId('c1'))
     }
   })
 })

+ 3 - 3
packages/core/agent-loop/tests/coverage-edges.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import LlmRuntime, { createUserMessage, CallId, LlmError, StreamChunk, errorChain  } from '@deepseek-ai/dsh-llm'
+import LlmRuntime, { createUserMessage, ToolCallId, LlmError, StreamChunk, errorChain  } from '@deepseek-ai/dsh-llm'
 import SessionStore, { SessionId, TurnEndReason } from '@deepseek-ai/dsh-session'
 import type { SessionEvent } from '@deepseek-ai/dsh-session'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
@@ -47,7 +47,7 @@ describe('tool JSON parse', () => {
       // model emits tool-call with malformed arguments (not valid JSON)
       [
         { type: 'block-start' as const, index: 0, blockType: 'tool-call' as const },
-        { type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: CallId('c1'), name: 'echo', arguments: 'not json' } },
+        { type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: ToolCallId('c1'), name: 'echo', arguments: 'not json' } },
         { type: 'finish' as const, reason: { kind: 'tool-calls' as const } },
       ] satisfies StreamChunk[],
       textResponse('done'),
@@ -80,7 +80,7 @@ describe('tool JSON parse', () => {
     const adapter = new MockAdapter([
       [
         { type: 'block-start' as const, index: 0, blockType: 'tool-call' as const },
-        { type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: CallId('c1'), name: 'noarg', arguments: '' } },
+        { type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: ToolCallId('c1'), name: 'noarg', arguments: '' } },
         { type: 'finish' as const, reason: { kind: 'tool-calls' as const } },
       ] satisfies StreamChunk[],
       textResponse('done'),

+ 3 - 3
packages/core/agent-loop/tests/interception.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it, vi } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import LlmRuntime, { createUserMessage, CallId  } from '@deepseek-ai/dsh-llm'
+import LlmRuntime, { createUserMessage, ToolCallId  } from '@deepseek-ai/dsh-llm'
 import SessionStore, {
   SessionId,
   type SessionEvent,
@@ -613,9 +613,9 @@ describe('tool additionalContexts buffering across a step', () => {
     // One assistant step with TWO tool calls; the second model response stops.
     const twoCalls = [
       { type: 'block-start' as const, index: 0, blockType: 'tool-call' as const },
-      { type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: CallId('c1'), name: 'echo', arguments: '{"text":"a"}' } },
+      { type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: ToolCallId('c1'), name: 'echo', arguments: '{"text":"a"}' } },
       { type: 'block-start' as const, index: 1, blockType: 'tool-call' as const },
-      { type: 'block-end' as const, index: 1, block: { type: 'tool-call' as const, id: CallId('c2'), name: 'echo', arguments: '{"text":"b"}' } },
+      { type: 'block-end' as const, index: 1, block: { type: 'tool-call' as const, id: ToolCallId('c2'), name: 'echo', arguments: '{"text":"b"}' } },
       { type: 'usage' as const, usage: { inputTokens: 5, outputTokens: 5 } },
       { type: 'finish' as const, reason: { kind: 'tool-calls' as const } },
     ]

+ 4 - 4
packages/core/agent-loop/tests/loop.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import LlmRuntime, { createUserMessage, CallId, LlmError, StreamChunk  } from '@deepseek-ai/dsh-llm'
+import LlmRuntime, { createUserMessage, ToolCallId, LlmError, StreamChunk  } from '@deepseek-ai/dsh-llm'
 import SessionStore, { SessionId, TurnEndReason } from '@deepseek-ai/dsh-session'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
 import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
@@ -1052,7 +1052,7 @@ describe('agent loop', () => {
   })
 
   it('does not dispatch tool calls from a max-tokens-truncated step', async () => {
-    const callId = CallId('c1')
+    const callId = ToolCallId('c1')
     const adapter = new MockAdapter([[
       { type: 'block-start', index: 0, blockType: 'tool-call' },
       { type: 'tool-call-delta', index: 0, id: callId, name: 'echo', argumentsDelta: '{"text":"x"}' },
@@ -1107,7 +1107,7 @@ describe('agent loop', () => {
   it('appends an empty completion anchor for a max-tokens step with no usage', async () => {
     // The truncated tool call is dropped from durable content, while the
     // successful provider call still needs an exact replay anchor.
-    const callId = CallId('c1')
+    const callId = ToolCallId('c1')
     const adapter = new MockAdapter([[
       { type: 'block-start', index: 0, blockType: 'tool-call' },
       { type: 'tool-call-delta', index: 0, id: callId, name: 'echo', argumentsDelta: '{"text":"x"}' },
@@ -1185,7 +1185,7 @@ describe('agent loop', () => {
   })
 
   it('keeps safe max-tokens assistant content while dropping truncated tool calls', async () => {
-    const callId = CallId('c1')
+    const callId = ToolCallId('c1')
     const adapter = new MockAdapter([[
       { type: 'block-start', index: 0, blockType: 'text' },
       { type: 'text-delta', index: 0, text: 'partial text' },

+ 2 - 2
packages/core/agent-loop/tests/mock-adapter.ts

@@ -1,5 +1,5 @@
 import type { GenerateOptions, LlmModelReasoningInfo, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm'
-import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
 
 /** Helpers to write scripted responses tersely. */
 export function textResponse(text: string): StreamChunk[] {
@@ -28,7 +28,7 @@ export function maxTokensResponse(text: string): StreamChunk[] {
 }
 
 export function toolCallResponse(rawCallId: string, name: string, args: object, text?: string): StreamChunk[] {
-  const callId = CallId(rawCallId)
+  const callId = ToolCallId(rawCallId)
   const argumentsJson = JSON.stringify(args)
   const chunks: StreamChunk[] = []
   let index = 0

+ 27 - 27
packages/core/agent-loop/tests/tool-calls.spec.ts

@@ -5,7 +5,7 @@
 
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { createUserMessage, CallId, StreamChunk  } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId, StreamChunk  } from '@deepseek-ai/dsh-llm'
 import SessionStore, { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
 import LlmRuntime from '@deepseek-ai/dsh-llm'
@@ -49,7 +49,7 @@ function multiCall(calls: { id: string; name: string; args: object }[]): StreamC
   calls.forEach((call, index) => {
     chunks.push(
       { type: 'block-start', index, blockType: 'tool-call' },
-      { type: 'block-end', index, block: { type: 'tool-call', id: CallId(call.id), name: call.name, arguments: JSON.stringify(call.args) } },
+      { type: 'block-end', index, block: { type: 'tool-call', id: ToolCallId(call.id), name: call.name, arguments: JSON.stringify(call.args) } },
     )
   })
   chunks.push(
@@ -196,7 +196,7 @@ describe('tool-call scheduler: grouping and barriers', () => {
     const replacement = gatedExclusiveTool('x')
     const disposeInitial = ctx.tools.register(initial.tool)
     ctx.on('tools/result', (exec) => {
-      if (exec.callId !== CallId('c1')) return
+      if (exec.callId !== ToolCallId('c1')) return
       disposeInitial()
       ctx.tools.register(replacement.tool)
     })
@@ -206,7 +206,7 @@ describe('tool-call scheduler: grouping and barriers', () => {
     await until(() => initial.started.length === 2)
     initial.release('1')
     await until(() => events(agent).some(event =>
-      event.type === 'tool/result' && event.data.message.source.callId === CallId('c1')))
+      event.type === 'tool/result' && event.data.message.source.callId === ToolCallId('c1')))
     await new Promise(r => setTimeout(r, 5))
     expect(replacement.started).toEqual([])
     initial.release('2')
@@ -238,7 +238,7 @@ describe('tool-call scheduler: model-order results despite out-of-order settleme
     await waitForIdle(ctx, agent)
 
     const results = events(agent).filter(e => e.type === 'tool/result')
-    expect(results.map(e => e.data.message.source.callId)).toEqual([CallId('c1'), CallId('c2')])
+    expect(results.map(e => e.data.message.source.callId)).toEqual([ToolCallId('c1'), ToolCallId('c2')])
   })
 
   it('derived history pairs calls in model order regardless of tool/call log interleaving', async () => {
@@ -257,7 +257,7 @@ describe('tool-call scheduler: model-order results despite out-of-order settleme
 
     const messages = agent.session.deriveMessages()
     const toolResults = messages.flatMap(m => m.content.filter(b => b.type === 'tool-result'))
-    expect(toolResults.map(b => b.toolCallId)).toEqual([CallId('c1'), CallId('c2')])
+    expect(toolResults.map(b => b.toolCallId)).toEqual([ToolCallId('c1'), ToolCallId('c2')])
   })
 })
 
@@ -316,7 +316,7 @@ describe('tool-call scheduler: rolling pool honors maxParallelToolCalls', () =>
     gated.release('4')
     await waitForIdle(ctx, agent)
     expect(events(agent).filter(e => e.type === 'tool/result').map(e => e.data.message.source.callId))
-      .toEqual([CallId('c1'), CallId('c2'), CallId('c3'), CallId('c4')])
+      .toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3'), ToolCallId('c4')])
   })
 
   it('maxParallelToolCalls: 1 is fully serial (no second start before the first settles)', async () => {
@@ -386,8 +386,8 @@ describe('tool-call scheduler: ordered middleware and additional contexts', () =
     gated.release('3'); gated.release('2'); gated.release('1')
     await waitForIdle(ctx, agent)
 
-    expect(pre).toEqual([CallId('c1'), CallId('c2'), CallId('c3')].map(String))
-    expect(post).toEqual([CallId('c1'), CallId('c2'), CallId('c3')].map(String))
+    expect(pre).toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3')].map(String))
+    expect(post).toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3')].map(String))
   })
 
   it('injects additional contexts in model call order, not settlement order', async () => {
@@ -432,8 +432,8 @@ describe('tool-call scheduler: ordered middleware and additional contexts', () =
     ctx.tools.register(gated.tool)
     const post: string[] = []
     ctx.on('tools/pre-execute', async (exec, next): Promise<PreToolDecision> => {
-      if (exec.callId === CallId('c2')) return { kind: 'deny', reason: 'blocked by policy' }
-      if (exec.callId === CallId('c3')) throw new Error('pre exploded')
+      if (exec.callId === ToolCallId('c2')) return { kind: 'deny', reason: 'blocked by policy' }
+      if (exec.callId === ToolCallId('c3')) throw new Error('pre exploded')
       return next()
     })
     ctx.on('tools/post-execute', async (exec, _result, next): Promise<PostToolDecision> => {
@@ -450,7 +450,7 @@ describe('tool-call scheduler: ordered middleware and additional contexts', () =
     expect(gated.started).toEqual(['1'])
     expect(post).toEqual(['c1', 'c2'])
     const results = events(agent).filter(e => e.type === 'tool/result')
-    expect(results.map(e => e.data.message.source.callId)).toEqual([CallId('c1'), CallId('c2'), CallId('c3')])
+    expect(results.map(e => e.data.message.source.callId)).toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3')])
     expect((results[1]!.data.message.content[0].content[0] as { text: string }).text).toContain('blocked by policy')
     expect((results[2]!.data.message.content[0].content[0] as { text: string }).text).toContain('pre exploded')
   })
@@ -477,14 +477,14 @@ describe('tool-call scheduler: abort handling', () => {
 
     expect(gated.started).toEqual([])
     expect(events(agent).filter(e => e.type === 'tool/call').map(e => e.data.callId))
-      .toEqual([CallId('c1'), CallId('c2')])
+      .toEqual([ToolCallId('c1'), ToolCallId('c2')])
     expect(events(agent).filter(e => e.type === 'tool/result').map(e => ({
       callId: e.data.message.source.callId,
       isError: e.data.message.content[0].isError,
       error: e.data.error,
     }))).toEqual([
-      { callId: CallId('c1'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
-      { callId: CallId('c2'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
+      { callId: ToolCallId('c1'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
+      { callId: ToolCallId('c2'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
     ])
   })
 
@@ -498,7 +498,7 @@ describe('tool-call scheduler: abort handling', () => {
     ctx.tools.register(gated.tool)
     const agent = ctx.agentLoop.create(SessionId('a1'), { provider: 'mock', model: 'mock' })
     ctx.on('tools/pre-execute', async (exec, next): Promise<PreToolDecision> => {
-      if (exec.callId === CallId('c1')) {
+      if (exec.callId === ToolCallId('c1')) {
         agent.cancel({ kind: 'user' })
       }
       return next()
@@ -509,14 +509,14 @@ describe('tool-call scheduler: abort handling', () => {
 
     expect(gated.started).toEqual([])
     expect(events(agent).filter(e => e.type === 'tool/call').map(e => e.data.callId))
-      .toEqual([CallId('c1'), CallId('c2')])
+      .toEqual([ToolCallId('c1'), ToolCallId('c2')])
     expect(events(agent).filter(e => e.type === 'tool/result').map(e => ({
       callId: e.data.message.source.callId,
       isError: e.data.message.content[0].isError,
       error: e.data.error,
     }))).toEqual([
-      { callId: CallId('c1'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
-      { callId: CallId('c2'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
+      { callId: ToolCallId('c1'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
+      { callId: ToolCallId('c2'), isError: true, error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH } },
     ])
   })
 
@@ -545,9 +545,9 @@ describe('tool-call scheduler: abort handling', () => {
 
     expect(gated.started).toEqual(['1', '2'])
     expect(events(agent).filter(e => e.type === 'tool/call').map(e => e.data.callId))
-      .toEqual([CallId('c1'), CallId('c2'), CallId('c3'), CallId('c4')])
+      .toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3'), ToolCallId('c4')])
     expect(events(agent).filter(e => e.type === 'tool/result').map(e => e.data.message.source.callId))
-      .toEqual([CallId('c1'), CallId('c2'), CallId('c3'), CallId('c4')])
+      .toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3'), ToolCallId('c4')])
     expect(events(agent).filter(e => e.type === 'tool/result').slice(-2).map(e => ({
       callId: e.data.message.source.callId,
       isError: e.data.message.content[0].isError,
@@ -555,12 +555,12 @@ describe('tool-call scheduler: abort handling', () => {
     })))
       .toEqual([
         {
-          callId: CallId('c3'),
+          callId: ToolCallId('c3'),
           isError: true,
           error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH },
         },
         {
-          callId: CallId('c4'),
+          callId: ToolCallId('c4'),
           isError: true,
           error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH },
         },
@@ -618,11 +618,11 @@ describe('tool-call scheduler: abort handling', () => {
 
     expect(exclusive).toEqual([])
     expect(events(agent).filter(e => e.type === 'tool/call').map(e => e.data.callId))
-      .toEqual([CallId('c1'), CallId('c2'), CallId('c3')])
+      .toEqual([ToolCallId('c1'), ToolCallId('c2'), ToolCallId('c3')])
     expect(events(agent).filter(e => e.type === 'tool/result').at(-1)?.data)
       .toMatchObject({
         message: {
-          source: { kind: 'tool', callId: CallId('c3') },
+          source: { kind: 'tool', callId: ToolCallId('c3') },
           content: [{ isError: true }],
         },
         error: { name: 'AbortError', code: TOOL_ABORTED_BEFORE_DISPATCH },
@@ -651,7 +651,7 @@ describe('tool-call scheduler: failure quiescence', () => {
     let thirdPrepareEntered = false
     scheduler.prepare = async (exec) => {
       const prepared = await prepare(exec)
-      if (exec.callId === CallId('c3')) {
+      if (exec.callId === ToolCallId('c3')) {
         thirdPrepareEntered = true
         await prepareGate.promise
       }
@@ -660,7 +660,7 @@ describe('tool-call scheduler: failure quiescence', () => {
     const schedulerError = new Error('scheduler exploded')
     const drainedError = new Error('sibling failed while draining')
     let rejectFirst: ((error: Error) => void) | undefined
-    scheduler.dispatch = exec => exec.callId === CallId('c1')
+    scheduler.dispatch = exec => exec.callId === ToolCallId('c1')
       ? new Promise((_resolve, reject) => { rejectFirst = reject })
       : dispatch(exec).then(() => { throw drainedError })
     const agent = ctx.agentLoop.create(SessionId('scheduler-failure'), { provider: 'mock', model: 'mock' })

+ 3 - 3
packages/core/session/src/chunk-rows.ts

@@ -18,7 +18,7 @@
  * @module @deepseek-ai/dsh-session/chunk-rows
  */
 
-import { CallId, assertNever } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, assertNever } from '@deepseek-ai/dsh-llm'
 import type { StreamChunk } from '@deepseek-ai/dsh-llm'
 import type { SessionEvent } from './types.ts'
 
@@ -50,7 +50,7 @@ interface TextRunData extends RunDataBase {
 
 /** Payload of a `tool-call-chunks` row: the run-constant call identity plus each member's raw arguments fragment. */
 interface ToolCallRunData extends RunDataBase {
-  id: CallId
+  id: ToolCallId
   /** Present iff every member carried it, with one uniform value (a mixed run never packs). */
   name?: string
   args: string[]
@@ -167,7 +167,7 @@ function buildRow(kind: DeltaKind, run: readonly DeltaEvent[]): ChunkRow {
       ...envelope,
       data: {
         ...base,
-        id: CallId(call.id),
+        id: ToolCallId(call.id),
         ...Object.hasOwn(call, 'name') ? { name: call.name as string } : {},
         args: run.map(event => (event.data.chunk as { argumentsDelta: string }).argumentsDelta),
       },

+ 3 - 3
packages/core/session/src/invariant.ts

@@ -7,7 +7,7 @@
 
 import type { Context } from '@deepseek-ai/cordis'
 import { assertNever } from '@deepseek-ai/dsh-llm'
-import type { CallId } from '@deepseek-ai/dsh-llm'
+import type { ToolCallId } from '@deepseek-ai/dsh-llm'
 import type { InvariantFailure, InvariantInstaller } from '@deepseek-ai/dsh-invariants'
 import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
 import { TOOL_NOT_STARTED } from './repair.ts'
@@ -26,7 +26,7 @@ interface SessionTrace {
   openStep: number | null
   nextTurn: number
   nextStep: number
-  pendingCalls: Set<CallId>
+  pendingCalls: Set<ToolCallId>
 }
 
 /** One accepted event's deferred mutation of a committed session trace. */
@@ -34,7 +34,7 @@ interface SessionTraceTransition {
   scalars: Pick<SessionTrace, 'lastSeq' | 'openTurn' | 'openStep' | 'nextTurn' | 'nextStep'>
   pendingCalls:
     | { kind: 'none' }
-    | { kind: 'add' | 'delete'; callId: CallId }
+    | { kind: 'add' | 'delete'; callId: ToolCallId }
     | { kind: 'clear' }
 }
 

+ 2 - 2
packages/core/session/src/repair.ts

@@ -5,7 +5,7 @@
  * @module @deepseek-ai/dsh-session/repair
  */
 
-import { MessageId, freezeMessage, type CallId } from '@deepseek-ai/dsh-llm'
+import { MessageId, freezeMessage, type ToolCallId } from '@deepseek-ai/dsh-llm'
 import type { ToolResultMessage } from '@deepseek-ai/dsh-llm'
 import type { SessionEvent } from './types.ts'
 
@@ -29,7 +29,7 @@ export function interruptedTurnClosers(events: readonly SessionEvent[]): Session
   let openStep: number | null = null
   // Reset at each turn boundary so earlier calls cannot leak into tail repair.
   // Assistant blocks register calls; later `tool/call` events add their seqs to `sourceEventSeqs`.
-  const pendingCalls = new Map<CallId, { step: number; callSeq?: number }>()
+  const pendingCalls = new Map<ToolCallId, { step: number; callSeq?: number }>()
   for (const event of events) {
     switch (event.type) {
       case 'turn/start':

+ 2 - 2
packages/core/session/src/types.ts

@@ -1,7 +1,7 @@
 import type { Branded } from '@deepseek-ai/dsh-brand'
 import type {
   AssistantMessage,
-  CallId,
+  ToolCallId,
   LlmCallConfig,
   LlmCallConfigAdapterDefaults,
   LlmFailure,
@@ -280,7 +280,7 @@ export interface SessionEventMap {
    * JSON string exactly as the model produced it (unparsed). `callId` pairs the
    * call with its `tool/result`.
    */
-  'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
+  'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
   /**
    * A completed tool call's model-facing result, optional internal failure
    * identity, and optional tool-private `meta` presentation payload. `meta` is

+ 6 - 6
packages/core/session/tests/chunk-rows.spec.ts

@@ -6,7 +6,7 @@
 
 import { describe, expect, it } from 'vitest'
 import fc from 'fast-check'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import type { StreamChunk } from '@deepseek-ai/dsh-llm'
 import { decodeStorageRecord, packChunkRuns } from '@deepseek-ai/dsh-session'
 import type { ChunkRow, SessionEvent, StorageRecord } from '@deepseek-ai/dsh-session'
@@ -43,7 +43,7 @@ describe('packChunkRuns', () => {
   it('packs reasoning and tool-call runs under their own tags', () => {
     const reasoning = deltaRun('reasoning-delta', 3)
     const toolCall = [4, 5, 6].map(seq =>
-      chunkEvent(seq, 1000 + seq, { type: 'tool-call-delta', index: 1, id: CallId('c1'), name: 'write', argumentsDelta: `a${seq}` }))
+      chunkEvent(seq, 1000 + seq, { type: 'tool-call-delta', index: 1, id: ToolCallId('c1'), name: 'write', argumentsDelta: `a${seq}` }))
     const packed = packChunkRuns([...reasoning, ...toolCall])
     expect(packed.map(r => (r as ChunkRow).type)).toStrictEqual(['reasoning-chunks', 'tool-call-chunks'])
     const row = packed[1] as ChunkRow & { type: 'tool-call-chunks' }
@@ -53,7 +53,7 @@ describe('packChunkRuns', () => {
 
   it('packs a name-less tool-call run and round-trips field absence', () => {
     const events = [0, 1, 2].map(seq =>
-      chunkEvent(seq, 1000, { type: 'tool-call-delta', index: 0, id: CallId('c1'), argumentsDelta: `a${seq}` }))
+      chunkEvent(seq, 1000, { type: 'tool-call-delta', index: 0, id: ToolCallId('c1'), argumentsDelta: `a${seq}` }))
     const packed = packChunkRuns(events)
     expect(packed).toHaveLength(1)
     expect(Object.hasOwn((packed[0] as ChunkRow).data, 'name')).toBe(false)
@@ -91,7 +91,7 @@ describe('packChunkRuns', () => {
 
   it('breaks a tool-call run on call-id or name change', () => {
     const call = (seq: number, id: string, name?: string): SessionEvent =>
-      chunkEvent(seq, 1000, { type: 'tool-call-delta', index: 0, id: CallId(id), ...name !== undefined ? { name } : {}, argumentsDelta: 'a' })
+      chunkEvent(seq, 1000, { type: 'tool-call-delta', index: 0, id: ToolCallId(id), ...name !== undefined ? { name } : {}, argumentsDelta: 'a' })
     const idSwitch = [call(0, 'c1', 'w'), call(1, 'c1', 'w'), call(2, 'c2', 'w')]
     expect(packChunkRuns(idSwitch)).toStrictEqual(idSwitch)
     const namePresence = [call(0, 'c1', 'w'), call(1, 'c1', 'w'), call(2, 'c1')]
@@ -186,13 +186,13 @@ const deltaChunkArb: fc.Arbitrary<StreamChunk> = fc.oneof(
   fc.record({
     type: fc.constant<'tool-call-delta'>('tool-call-delta'),
     index: fc.nat(2),
-    id: fc.constantFrom(CallId('c1'), CallId('c2')),
+    id: fc.constantFrom(ToolCallId('c1'), ToolCallId('c2')),
     argumentsDelta: fc.string(),
   }),
   fc.record({
     type: fc.constant<'tool-call-delta'>('tool-call-delta'),
     index: fc.nat(2),
-    id: fc.constantFrom(CallId('c1'), CallId('c2')),
+    id: fc.constantFrom(ToolCallId('c1'), ToolCallId('c2')),
     name: fc.constantFrom('write', 'read'),
     argumentsDelta: fc.string(),
   }),

+ 2 - 2
packages/core/session/tests/fork.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { createUserMessage, CallId , createMessage } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId , createMessage } from '@deepseek-ai/dsh-llm'
 import SessionStore, { Session, SessionForkError, SessionId } from '@deepseek-ai/dsh-session'
 import type { SessionEvent, TurnEndReason } from '@deepseek-ai/dsh-session'
 
@@ -268,7 +268,7 @@ describe('SessionStore.fork', () => {
         return lastSeq(session)
       }],
       ['tool/call', (session) => {
-        const callId = CallId('call-open')
+        const callId = ToolCallId('call-open')
         session.append('turn/start', { turn: 1 })
         session.append('step/start', { turn: 1, step: 1 })
         session.append('assistant/message', {

+ 14 - 14
packages/core/session/tests/invariant.spec.ts

@@ -1,7 +1,7 @@
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
 import { createScope, scopeTarget } from '@deepseek-ai/dsh-scope'
-import { createUserMessage, CallId, createMessage, createToolResultMessage, freezeMessage } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId, createMessage, createToolResultMessage, freezeMessage } from '@deepseek-ai/dsh-llm'
 import SessionStore, { SessionId, TOOL_NOT_STARTED } from '@deepseek-ai/dsh-session'
 import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
 import InvariantRegistry, { InvariantError } from '@deepseek-ai/dsh-invariants'
@@ -46,18 +46,18 @@ describe('session-log invariants', () => {
         step: 1,
         message: createMessage({
           role: 'assistant',
-          content: [{ type: 'tool-call', id: CallId('c1'), name: 'echo', arguments: '{}' }],
+          content: [{ type: 'tool-call', id: ToolCallId('c1'), name: 'echo', arguments: '{}' }],
           source: {
             kind: 'model',
             ...{ provider: 'mock', model: 'mock' },
           },
         }),
       }, { surfaceOp: 'append' })
-      session.append('tool/call', { turn: 1, step: 1, callId: CallId('c1'), name: 'echo', arguments: '{}' })
+      session.append('tool/call', { turn: 1, step: 1, callId: ToolCallId('c1'), name: 'echo', arguments: '{}' })
       session.append('tool/result', {
         turn: 1, step: 1,
         message: createToolResultMessage({
-          callId: CallId('c1'),
+          callId: ToolCallId('c1'),
           content: [],
           isError: false,
         }),
@@ -223,7 +223,7 @@ describe('session-log invariants', () => {
       turn: 1,
       step: 1,
       message: createToolResultMessage({
-        callId: CallId('ghost'),
+        callId: ToolCallId('ghost'),
         content: [],
         isError: false,
       }),
@@ -238,7 +238,7 @@ describe('session-log invariants', () => {
       turn: 1,
       step: 1,
       message: createToolResultMessage({
-        callId: CallId('closed'),
+        callId: ToolCallId('closed'),
         content: [],
         isError: false,
       }),
@@ -253,7 +253,7 @@ describe('session-log invariants', () => {
     session.append('tool/call', {
       turn: 1,
       step: 1,
-      callId: CallId('rewrite'),
+      callId: ToolCallId('rewrite'),
       name: 'echo',
       arguments: '{}',
     })
@@ -261,7 +261,7 @@ describe('session-log invariants', () => {
       turn: 1,
       step: 1,
       message: createToolResultMessage({
-        callId: CallId('rewrite'),
+        callId: ToolCallId('rewrite'),
         content: [{ type: 'text', text: 'original' }],
         isError: false,
       }),
@@ -293,7 +293,7 @@ describe('session-log invariants', () => {
     session.append('tool/call', {
       turn: 1,
       step: 1,
-      callId: CallId('rewrite'),
+      callId: ToolCallId('rewrite'),
       name: 'echo',
       arguments: '{}',
     })
@@ -301,7 +301,7 @@ describe('session-log invariants', () => {
       turn: 1,
       step: 1,
       message: createToolResultMessage({
-        callId: CallId('rewrite'),
+        callId: ToolCallId('rewrite'),
         content: [{ type: 'text', text: 'original' }],
         isError: false,
       }),
@@ -333,7 +333,7 @@ describe('session-log invariants', () => {
         turn: 1,
         step: 1,
         message: createToolResultMessage({
-          callId: CallId('crashed'),
+          callId: ToolCallId('crashed'),
           content: [],
           isError: true,
         }),
@@ -347,7 +347,7 @@ describe('session-log invariants', () => {
     expect(() => {
       unresolved.append('turn/start', { turn: 1 })
       unresolved.append('step/start', { turn: 1, step: 1 })
-      unresolved.append('tool/call', { turn: 1, step: 1, callId: CallId('c1'), name: 'echo', arguments: '{}' })
+      unresolved.append('tool/call', { turn: 1, step: 1, callId: ToolCallId('c1'), name: 'echo', arguments: '{}' })
       unresolved.append('step/end', { turn: 1, step: 1 })
       unresolved.append('turn/end', { turn: 1, reason: { kind: 'error', error: { message: 'boom', code: 'UNKNOWN' } } })
     }).not.toThrow()
@@ -358,14 +358,14 @@ describe('session-log invariants', () => {
     const session = ctx.sessions.create()
     session.append('turn/start', { turn: 1 })
     session.append('step/start', { turn: 1, step: 1 })
-    session.append('tool/call', { turn: 1, step: 1, callId: CallId('c1'), name: 'echo', arguments: '{}' })
+    session.append('tool/call', { turn: 1, step: 1, callId: ToolCallId('c1'), name: 'echo', arguments: '{}' })
     session.append('step/end', { turn: 1, step: 1 })
     session.append('step/start', { turn: 1, step: 2 })
     expect(() => session.append('tool/result', {
       turn: 1,
       step: 2,
       message: createToolResultMessage({
-        callId: CallId('c1'),
+        callId: ToolCallId('c1'),
         content: [],
         isError: false,
       }),

+ 2 - 2
packages/core/session/tests/properties.spec.ts

@@ -9,7 +9,7 @@
 
 import { describe, expect, it } from 'vitest'
 import fc from 'fast-check'
-import { createUserMessage, CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
 import { Session, SessionId } from '@deepseek-ai/dsh-session'
 import type { SessionEventMap, SessionEventType, SurfaceIntent } from '@deepseek-ai/dsh-session'
 
@@ -61,7 +61,7 @@ const messageEventArb: fc.Arbitrary<Appendable> = fc.oneof(
     .map((r): Appendable => ({ type: 'tool/result', data: {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId(r.id),
+        callId: ToolCallId(r.id),
         content: r.content,
         isError: r.isError,
       }),

+ 15 - 15
packages/core/session/tests/repair.spec.ts

@@ -1,5 +1,5 @@
 import { describe, expect, it } from 'vitest'
-import { CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
+import { ToolCallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
 import { interruptedTurnClosers, TOOL_NOT_STARTED, TOOL_OUTCOME_UNKNOWN } from '../src/index.ts'
 import type { SessionEvent, SurfaceEvent } from '../src/index.ts'
 
@@ -57,7 +57,7 @@ describe('interruptedTurnClosers', () => {
           role: 'assistant',
           content: [
             { type: 'text', text: 'calling a tool' },
-            { type: 'tool-call', id: CallId('call-1'), name: 'bash', arguments: '{}' },
+            { type: 'tool-call', id: ToolCallId('call-1'), name: 'bash', arguments: '{}' },
           ],
           source: {
             kind: 'model',
@@ -75,7 +75,7 @@ describe('interruptedTurnClosers', () => {
       turn: 2,
       step: 1,
       message: {
-        source: { callId: CallId('call-1') },
+        source: { callId: ToolCallId('call-1') },
         content: [{ isError: true }],
       },
       error: { code: TOOL_NOT_STARTED },
@@ -94,7 +94,7 @@ describe('interruptedTurnClosers', () => {
         message: createMessage({
           role: 'assistant',
           content: [
-            { type: 'tool-call', id: CallId('call-1'), name: 'bash', arguments: '{}' },
+            { type: 'tool-call', id: ToolCallId('call-1'), name: 'bash', arguments: '{}' },
           ],
           source: {
             kind: 'model',
@@ -105,7 +105,7 @@ describe('interruptedTurnClosers', () => {
       { type: 'tool/result', seq: 3, time: 3, data: {
         turn: 2, step: 1,
         message: createToolResultMessage({
-          callId: CallId('call-1'),
+          callId: ToolCallId('call-1'),
           content: [{ type: 'text', text: 'ok' }],
           isError: false,
         }),
@@ -125,7 +125,7 @@ describe('interruptedTurnClosers', () => {
         message: createMessage({
           role: 'assistant',
           content: [
-            { type: 'tool-call', id: CallId('call-1'), name: 'bash', arguments: '{}' },
+            { type: 'tool-call', id: ToolCallId('call-1'), name: 'bash', arguments: '{}' },
           ],
           source: {
             kind: 'model',
@@ -152,7 +152,7 @@ describe('interruptedTurnClosers', () => {
         message: createMessage({
           role: 'assistant',
           content: [
-            { type: 'tool-call', id: CallId('old-call'), name: 'bash', arguments: '{}' },
+            { type: 'tool-call', id: ToolCallId('old-call'), name: 'bash', arguments: '{}' },
           ],
           source: {
             kind: 'model',
@@ -163,7 +163,7 @@ describe('interruptedTurnClosers', () => {
       { type: 'tool/result', seq: 3, time: 3, data: {
         turn: 1, step: 1,
         message: createToolResultMessage({
-          callId: CallId('old-call'),
+          callId: ToolCallId('old-call'),
           content: [],
           isError: false,
         }),
@@ -177,7 +177,7 @@ describe('interruptedTurnClosers', () => {
         message: createMessage({
           role: 'assistant',
           content: [
-            { type: 'tool-call', id: CallId('new-call'), name: 'bash', arguments: '{}' },
+            { type: 'tool-call', id: ToolCallId('new-call'), name: 'bash', arguments: '{}' },
           ],
           source: {
             kind: 'model',
@@ -201,8 +201,8 @@ describe('interruptedTurnClosers', () => {
         message: createMessage({
           role: 'assistant',
           content: [
-            { type: 'tool-call', id: CallId('call-a'), name: 'bash', arguments: '{}' },
-            { type: 'tool-call', id: CallId('call-b'), name: 'bash', arguments: '{}' },
+            { type: 'tool-call', id: ToolCallId('call-a'), name: 'bash', arguments: '{}' },
+            { type: 'tool-call', id: ToolCallId('call-b'), name: 'bash', arguments: '{}' },
           ],
           source: {
             kind: 'model',
@@ -214,7 +214,7 @@ describe('interruptedTurnClosers', () => {
       { type: 'tool/result', seq: 3, time: 3, data: {
         turn: 1, step: 1,
         message: createToolResultMessage({
-          callId: CallId('call-a'),
+          callId: ToolCallId('call-a'),
           content: [],
           isError: false,
         }),
@@ -235,7 +235,7 @@ describe('interruptedTurnClosers', () => {
         message: createMessage({
           role: 'assistant',
           content: [
-            { type: 'tool-call', id: CallId('call-1'), name: 'bash', arguments: '{}' },
+            { type: 'tool-call', id: ToolCallId('call-1'), name: 'bash', arguments: '{}' },
           ],
           source: {
             kind: 'model',
@@ -243,7 +243,7 @@ describe('interruptedTurnClosers', () => {
           },
         }),
       } },
-      { type: 'tool/call', seq: 3, time: 3, data: { turn: 1, step: 1, callId: CallId('call-1'), name: 'bash', arguments: '{}' } },
+      { type: 'tool/call', seq: 3, time: 3, data: { turn: 1, step: 1, callId: ToolCallId('call-1'), name: 'bash', arguments: '{}' } },
     ]
     const closers = interruptedTurnClosers(events)
     expect(closers.map(e => e.type)).toEqual(['tool/result', 'step/end', 'turn/end'])
@@ -266,7 +266,7 @@ describe('interruptedTurnClosers', () => {
     const events: SessionEvent[] = [
       userTurnStart(1, 0),
       { type: 'step/start', seq: 1, time: 1, data: { turn: 1, step: 1 } },
-      { type: 'tool/call', seq: 2, time: 2, data: { turn: 1, step: 1, callId: CallId('orphan'), name: 'bash', arguments: '{}' } },
+      { type: 'tool/call', seq: 2, time: 2, data: { turn: 1, step: 1, callId: ToolCallId('orphan'), name: 'bash', arguments: '{}' } },
     ]
     const closers = interruptedTurnClosers(events)
     // No pending calls → no synthetic tool/result, just step/end + turn/end.

+ 5 - 5
packages/core/session/tests/session.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, expectTypeOf, it, vi } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { createUserMessage, CallId, createMessage, createToolResultMessage, MessageId, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId, createMessage, createToolResultMessage, MessageId, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
 import SessionStore, {
   adoptSessionEvent,
   SESSION_FORMAT_VERSION,
@@ -33,7 +33,7 @@ describe('Session', () => {
         role: 'assistant',
         content: [
           { type: 'text', text: 'let me check' },
-          { type: 'tool-call', id: CallId('c1'), name: 'echo', arguments: '{}' },
+          { type: 'tool-call', id: ToolCallId('c1'), name: 'echo', arguments: '{}' },
         ],
         source: {
           kind: 'model',
@@ -44,7 +44,7 @@ describe('Session', () => {
     session.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('c1'),
+        callId: ToolCallId('c1'),
         content: [{ type: 'text', text: 'ok' }],
         isError: false,
       }),
@@ -55,7 +55,7 @@ describe('Session', () => {
     expect(messages.map(m => m.role)).toEqual(['user', 'assistant', 'user'])
     // raw chunks must NOT appear in derived history
     expect(messages[1]!.content).toHaveLength(2)
-    expect(messages[2]!.content[0]).toMatchObject({ type: 'tool-result', toolCallId: CallId('c1') })
+    expect(messages[2]!.content[0]).toMatchObject({ type: 'tool-result', toolCallId: ToolCallId('c1') })
   })
 
   it('accepts and round-trips a max-tokens turn/end reason', () => {
@@ -442,7 +442,7 @@ describe('Session', () => {
     session.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('c1'),
+        callId: ToolCallId('c1'),
         content: [{ type: 'text', text: 'tool out' }],
         isError: false,
       }),

+ 5 - 5
packages/core/session/tests/surface.spec.ts

@@ -15,7 +15,7 @@ import {
   createToolResultMessage,
   createUserMessage,
   freezeMessage,
-  CallId,
+  ToolCallId,
   MessageId,
 } from '@deepseek-ai/dsh-llm'
 
@@ -68,7 +68,7 @@ function toolResultEvent(
       turn: 1,
       step: 1,
       message: createToolResultMessage({
-        callId: CallId(callId),
+        callId: ToolCallId(callId),
         content: [{ type: 'text', text: `result ${seq}` }],
         isError: false,
       }),
@@ -175,7 +175,7 @@ describe('foldSurface tool-result rewrites', () => {
   })
 
   it.each([
-    ['toolCallId', { toolCallId: CallId('changed') }],
+    ['toolCallId', { toolCallId: ToolCallId('changed') }],
     ['isError', { isError: true }],
   ] as const)('rejects a replacement that changes the result block %s', (_field, patch) => {
     const original = toolResultEvent(0, 'original')
@@ -460,7 +460,7 @@ describe('SurfaceManager', () => {
     s.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('c1'),
+        callId: ToolCallId('c1'),
         content: [{ type: 'text', text: 'ok' }],
         isError: false,
       }),
@@ -474,7 +474,7 @@ describe('SurfaceManager', () => {
     original.append('tool/result', {
       turn: 1, step: 1,
       message: createToolResultMessage({
-        callId: CallId('c1'),
+        callId: ToolCallId('c1'),
         content: [{ type: 'text', text: 'ok' }],
         isError: false,
       }),

+ 2 - 2
packages/core/tools/src/code-mode.ts

@@ -6,7 +6,7 @@
  * @module @deepseek-ai/dsh-tools/src/code-mode
  */
 
-import { CallId, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm'
+import { ToolCallId, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm'
 import type { ContentBlock } from '@deepseek-ai/dsh-llm'
 import type { CodeBindingFunction, CodeRunResult, CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
 import { snapshotJsonValue } from '@deepseek-ai/dsh-session'
@@ -469,7 +469,7 @@ export function createRunCodeTool(registry: ToolRuntime, options: RunCodeBridgeO
         }
         const normalized = jsonNormalizeArgs(rawArgs)
         const n = ++dispatches
-        const subCallId = CallId(`${String(exec.callId)}:code:${n}`)
+        const subCallId = ToolCallId(`${String(exec.callId)}:code:${n}`)
         const input = {
           callId: subCallId,
           rootCallId: exec.rootCallId,

+ 5 - 5
packages/core/tools/src/index.ts

@@ -8,7 +8,7 @@ import { Context, Service } from '@deepseek-ai/cordis'
 import z from '@deepseek-ai/schemastery'
 import { AnonymousEntries, NamedEntries, ScopedLayers, scopeOf, scopeTarget } from '@deepseek-ai/dsh-scope'
 import type { ScopeKey, ScopeLayer, Scoped } from '@deepseek-ai/dsh-scope'
-import type { CallId, ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm'
+import type { ToolCallId, ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm'
 import { assertNever, deepFreeze, HarnessError } from '@deepseek-ai/dsh-llm'
 import type { Agent } from '@deepseek-ai/dsh-agent'
 import { snapshotJsonValue } from '@deepseek-ai/dsh-session'
@@ -312,12 +312,12 @@ export type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]:
  * callers do not choose that token.
  */
 export interface ToolExecutionInput {
-  readonly callId: CallId
+  readonly callId: ToolCallId
   /**
    * Root model-requested call owning this execution tree. Callers omit it for
    * a root execution; nested dispatchers propagate the enclosing value.
    */
-  readonly rootCallId?: CallId
+  readonly rootCallId?: ToolCallId
   readonly name: string
   /** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
   readonly arguments: unknown
@@ -360,7 +360,7 @@ export interface CodeDispatchLog {
   /** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
   readonly agent?: Agent
   /** Deterministic sub-call id (`<parent>:code:<n>`). */
-  readonly subCallId: CallId
+  readonly subCallId: ToolCallId
   /** The dispatched sub-tool name. */
   readonly name: string
   /** Whether the sub-call settled as an error. */
@@ -378,7 +378,7 @@ export interface CodeDispatchLog {
  */
 export interface ToolExecution extends ToolExecutionInput {
   /** Root model-requested call, resolved for every root and nested execution. */
-  readonly rootCallId: CallId
+  readonly rootCallId: ToolCallId
   /** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
   readonly token: ToolExecutionToken
 }

+ 4 - 4
packages/core/tools/src/types.ts

@@ -4,14 +4,14 @@
  * @module @deepseek-ai/dsh-tools/types
  */
 
-import type { CallId } from '@deepseek-ai/dsh-llm/brand'
+import type { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
 import type { ContentBlock } from '@deepseek-ai/dsh-llm/types'
 
 /** Payload recorded when one nested Code Mode Tool dispatch starts. */
 export interface CodeDispatchStartEventData {
-  rootCallId: CallId
-  parentCallId: CallId
-  subCallId: CallId
+  rootCallId: ToolCallId
+  parentCallId: ToolCallId
+  subCallId: ToolCallId
   name: string
   arguments: unknown
 }

+ 11 - 11
packages/core/tools/tests/code-mode.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { createUserMessage, CallId  } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId  } from '@deepseek-ai/dsh-llm'
 import { createScope } from '@deepseek-ai/dsh-scope'
 import type { Scope } from '@deepseek-ai/dsh-scope'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
@@ -107,7 +107,7 @@ async function runCode(
 ): Promise<ToolExecutionResult> {
   return ctx.tools.execute({
     signal: testToolSignal,
-    callId: CallId('call-1'),
+    callId: ToolCallId('call-1'),
     name: RUN_CODE_NAME,
     arguments: { code, description: extras.description ?? 'Run the test program' },
     ...extras.agent ? { agent: extras.agent } : {},
@@ -1624,9 +1624,9 @@ describe('the run_code dispatch bridge', () => {
       content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' },
     }), { surfaceOp: 'append' })
     session.append('tool/code-dispatch', {
-      rootCallId: CallId('p1'),
-      parentCallId: CallId('p1'),
-      subCallId: CallId('p1:code:1'),
+      rootCallId: ToolCallId('p1'),
+      parentCallId: ToolCallId('p1'),
+      subCallId: ToolCallId('p1:code:1'),
       name: 'echo',
       arguments: { value: 'x' },
       isError: false,
@@ -1666,7 +1666,7 @@ describe('the run_code dispatch bridge', () => {
     registerEcho(ctx, 'write')
     const result = await registry.execute({
       signal: testToolSignal,
-      callId: CallId('call-1'),
+      callId: ToolCallId('call-1'),
       name: 'write',
       arguments: { text: 'hello' },
     })
@@ -1688,7 +1688,7 @@ describe('the run_code dispatch bridge', () => {
     aborted.abort()
     const result = await registry.execute({
       signal: aborted.signal,
-      callId: CallId('call-1'),
+      callId: ToolCallId('call-1'),
       name: 'write',
       arguments: { text: 'hello' },
     })
@@ -1720,7 +1720,7 @@ describe('per-agent presentation', () => {
     // mode is its own rather than the deployment's.
     const denied = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('coded-direct'),
+      callId: ToolCallId('coded-direct'),
       name: 'echo',
       arguments: { value: 'coded' },
       agent,
@@ -1756,14 +1756,14 @@ describe('per-agent presentation', () => {
     // `dsh-agent-tool-presentation` produces.
     expect(ctx.tools.executionMode({
       signal: testToolSignal,
-      callId: CallId('preset-coded-schedule'),
+      callId: ToolCallId('preset-coded-schedule'),
       name: 'echo',
       arguments: { value: 'joined' },
       agent: joined.agent,
     })).toEqual({ kind: 'exclusive' })
     const denied = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('preset-coded-direct'),
+      callId: ToolCallId('preset-coded-direct'),
       name: 'echo',
       arguments: { value: 'joined' },
       agent: joined.agent,
@@ -1776,7 +1776,7 @@ describe('per-agent presentation', () => {
     expect(native.tools.map(tool => tool.name)).toEqual(['echo'])
     const allowed = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('native-sibling-direct'),
+      callId: ToolCallId('native-sibling-direct'),
       name: 'echo',
       arguments: { value: 'loner' },
       agent: loner.agent,

+ 2 - 2
packages/core/tools/tests/execution-mode.spec.ts

@@ -2,7 +2,7 @@
 
 import { describe, expect, expectTypeOf, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
 import ToolRuntime, {
   defineContentToolFixture,
@@ -21,7 +21,7 @@ async function setup() {
 }
 
 function exec(name: string, args: unknown): ToolExecutionInput {
-  return { signal: testToolSignal, callId: CallId('c1'), name, arguments: args }
+  return { signal: testToolSignal, callId: ToolCallId('c1'), name, arguments: args }
 }
 
 describe('ToolRuntime.executionMode', () => {

+ 2 - 2
packages/core/tools/tests/execution-signal-types.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expectTypeOf, it } from 'vitest'
 import type { Context } from '@deepseek-ai/cordis'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import { defineTool } from '@deepseek-ai/dsh-tools'
 import type {
   ToolDispatchExecution,
@@ -15,7 +15,7 @@ function inputAndExecutionContracts(
   run: ToolRunContext,
 ): void {
   // @ts-expect-error -- every typed invocation must supply a caller-owned signal.
-  const missingSignal: ToolExecutionInput = { callId: CallId('missing'), name: 'probe', arguments: {} }
+  const missingSignal: ToolExecutionInput = { callId: ToolCallId('missing'), name: 'probe', arguments: {} }
   void missingSignal
 
   // @ts-expect-error -- caller input is readonly after construction.

+ 41 - 41
packages/core/tools/tests/invariant.spec.ts

@@ -1,7 +1,7 @@
 import { describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
 import { scopeTarget } from '@deepseek-ai/dsh-scope'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
 import type { ToolExecution, ToolExecutionResult, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
 import * as ToolsInvariant from '@deepseek-ai/dsh-tools/invariant'
@@ -19,12 +19,12 @@ async function setup(): Promise<Context> {
 
 const execution = (overrides: Partial<ToolExecution> = {}): ToolExecution => ({
   token: Symbol('tool') as ToolExecutionToken,
-  callId: CallId('call-1'),
+  callId: ToolCallId('call-1'),
   name: 'echo',
   arguments: Object.freeze({ text: 'hi' }),
   ...overrides,
   signal: overrides.signal ?? testToolSignal,
-  rootCallId: overrides.rootCallId ?? overrides.callId ?? CallId('call-1'),
+  rootCallId: overrides.rootCallId ?? overrides.callId ?? ToolCallId('call-1'),
 })
 
 const outcome = (): ToolExecutionResult => Object.freeze({
@@ -55,7 +55,7 @@ describe('tool-pipeline invariants', () => {
     Object.freeze(dispatched)
     emitResult(ctx, dispatched, outcome())
 
-    const denied = execution({ callId: CallId('call-2') })
+    const denied = execution({ callId: ToolCallId('call-2') })
     await stage(ctx, 'tools/pre-execute', denied)
     await ctx.waterfall(ctx as never, 'tools/post-execute', denied, outcome(), () => Promise.resolve({ kind: 'accept' as const }))
     Object.freeze(denied)
@@ -69,7 +69,7 @@ describe('tool-pipeline invariants', () => {
     await stage(ctx, 'tools/pre-execute', exec)
     await expect(stage(ctx, 'tools/pre-execute', exec)).rejects.toThrow(/repeated/)
 
-    const noPre = execution({ callId: CallId('call-2') })
+    const noPre = execution({ callId: ToolCallId('call-2') })
     await expect(stage(ctx, 'tools/execute', noPre)).rejects.toThrow(/must follow tools\/pre-execute/)
     expect(() => ctx.waterfall(
       ctx as never, 'tools/post-execute', noPre, outcome(),
@@ -93,9 +93,9 @@ describe('tool-pipeline invariants', () => {
     const ctx = await setup()
     const session = ctx.sessions.create()
     const data = {
-      rootCallId: CallId('parent'),
-      parentCallId: CallId('parent'),
-      subCallId: CallId('child'),
+      rootCallId: ToolCallId('parent'),
+      parentCallId: ToolCallId('parent'),
+      subCallId: ToolCallId('child'),
       name: 'echo',
       arguments: {},
     }
@@ -109,18 +109,18 @@ describe('tool-pipeline invariants', () => {
     const ctx = await setup()
     const session = ctx.sessions.create()
     expect(() => session.append('tool/code-dispatch-start', {
-      rootCallId: CallId('rejected-root'),
-      parentCallId: CallId('rejected-root'),
-      subCallId: CallId('reused-child'),
+      rootCallId: ToolCallId('rejected-root'),
+      parentCallId: ToolCallId('rejected-root'),
+      subCallId: ToolCallId('reused-child'),
       name: 'echo',
       arguments: {},
     })).toThrow(/outside any open turn/)
 
     session.append('turn/start', { turn: 1 })
     expect(() => session.append('tool/code-dispatch-start', {
-      rootCallId: CallId('accepted-root'),
-      parentCallId: CallId('accepted-root'),
-      subCallId: CallId('reused-child'),
+      rootCallId: ToolCallId('accepted-root'),
+      parentCallId: ToolCallId('accepted-root'),
+      subCallId: ToolCallId('reused-child'),
       name: 'echo',
       arguments: {},
     })).not.toThrow()
@@ -131,24 +131,24 @@ describe('tool-pipeline invariants', () => {
     const session = ctx.sessions.create()
     session.append('turn/start', { turn: 1 })
     session.append('tool/code-dispatch-start', {
-      rootCallId: CallId('root'),
-      parentCallId: CallId('root'),
-      subCallId: CallId('child'),
+      rootCallId: ToolCallId('root'),
+      parentCallId: ToolCallId('root'),
+      subCallId: ToolCallId('child'),
       name: 'run_code',
       arguments: {},
     })
     session.append('tool/code-dispatch-start', {
-      rootCallId: CallId('root'),
-      parentCallId: CallId('child'),
-      subCallId: CallId('grandchild'),
+      rootCallId: ToolCallId('root'),
+      parentCallId: ToolCallId('child'),
+      subCallId: ToolCallId('grandchild'),
       name: 'echo',
       arguments: {},
     })
 
     expect(() => session.append('tool/code-dispatch-start', {
-      rootCallId: CallId('another-root'),
-      parentCallId: CallId('child'),
-      subCallId: CallId('invalid-grandchild'),
+      rootCallId: ToolCallId('another-root'),
+      parentCallId: ToolCallId('child'),
+      subCallId: ToolCallId('invalid-grandchild'),
       name: 'echo',
       arguments: {},
     })).toThrow(/parentCallId child does not belong to rootCallId another-root/)
@@ -161,24 +161,24 @@ describe('tool-pipeline invariants', () => {
     const session = ctx.sessions.create()
     session.append('turn/start', { turn: 1 })
     expect(() => session.append('tool/code-dispatch-start', {
-      rootCallId: CallId(''),
-      parentCallId: CallId('root'),
-      subCallId: CallId('child'),
+      rootCallId: ToolCallId(''),
+      parentCallId: ToolCallId('root'),
+      subCallId: ToolCallId('child'),
       name: 'echo',
       arguments: {},
     })).toThrow(/must carry non-empty rootCallId/)
 
     session.append('tool/code-dispatch-start', {
-      rootCallId: CallId('root'),
-      parentCallId: CallId('root'),
-      subCallId: CallId('child'),
+      rootCallId: ToolCallId('root'),
+      parentCallId: ToolCallId('root'),
+      subCallId: ToolCallId('child'),
       name: 'echo',
       arguments: {},
     })
     expect(() => session.append('tool/code-dispatch-start', {
-      rootCallId: CallId('other-root'),
-      parentCallId: CallId('other-root'),
-      subCallId: CallId('child'),
+      rootCallId: ToolCallId('other-root'),
+      parentCallId: ToolCallId('other-root'),
+      subCallId: ToolCallId('child'),
       name: 'echo',
       arguments: {},
     })).toThrow(/changed rootCallId for subCallId child/)
@@ -194,9 +194,9 @@ describe('tool-pipeline invariants', () => {
         seq: 1,
         time: 1,
         data: {
-          rootCallId: CallId('root'),
-          parentCallId: CallId('root'),
-          subCallId: CallId('child'),
+          rootCallId: ToolCallId('root'),
+          parentCallId: ToolCallId('root'),
+          subCallId: ToolCallId('child'),
           name: 'echo',
           arguments: {},
         },
@@ -210,9 +210,9 @@ describe('tool-pipeline invariants', () => {
     const session = ctx.sessions.create()
     session.append('turn/start', { turn: 1 })
     session.append('tool/code-dispatch', {
-      rootCallId: CallId('parent'),
-      parentCallId: CallId('parent'),
-      subCallId: CallId('child'),
+      rootCallId: ToolCallId('parent'),
+      parentCallId: ToolCallId('parent'),
+      subCallId: ToolCallId('child'),
       name: 'echo',
       arguments: {},
       isError: false,
@@ -227,9 +227,9 @@ describe('tool-pipeline invariants', () => {
     const ctx = new Context()
     await ctx.plugin(SessionStore)
     ctx.sessions.create().append('tool/code-dispatch-start', {
-      rootCallId: CallId('parent'),
-      parentCallId: CallId('parent'),
-      subCallId: CallId('child'),
+      rootCallId: ToolCallId('parent'),
+      parentCallId: ToolCallId('parent'),
+      subCallId: ToolCallId('child'),
       name: 'echo',
       arguments: {},
     })

+ 14 - 14
packages/core/tools/tests/scoped.spec.ts

@@ -8,7 +8,7 @@ import ToolRuntime from '@deepseek-ai/dsh-tools'
 import type { PreToolDecision, ToolDefinition, ToolExecution, ToolExecutionInput, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
 import type { Agent } from '@deepseek-ai/dsh-agent'
 
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import type { SessionId } from '@deepseek-ai/dsh-session'
 
 const testToolSignal = new AbortController().signal
@@ -49,7 +49,7 @@ function tool(name: string, reply = `ran:${name}`): ToolDefinition {
 async function run(ctx: Context, name: string, agent?: Agent): Promise<string> {
   const result = await ctx.tools.execute({
     signal: testToolSignal,
-    callId: CallId('c1'),
+    callId: ToolCallId('c1'),
     name,
     arguments: {},
     ...agent ? { agent } : {},
@@ -418,7 +418,7 @@ describe('scoped execution dispatch', () => {
     const callerArguments = { source: true }
     const safeResult = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('safe-call'),
+      callId: ToolCallId('safe-call'),
       name: 'safe',
       arguments: callerArguments,
       agent: key,
@@ -461,7 +461,7 @@ describe('scoped execution dispatch', () => {
       if (exec.name === 'parent') parent = exec.token
       return next()
     })
-    await ctx.tools.execute({ signal: testToolSignal, callId: CallId('parent'), name: 'parent', arguments: {} })
+    await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('parent'), name: 'parent', arguments: {} })
     stopCapture()
     policyCalls = 0
     const signal = new AbortController().signal
@@ -477,7 +477,7 @@ describe('scoped execution dispatch', () => {
     const callerArguments = { invalid: () => undefined }
 
     const scopedResult = await ctx.tools.execute({
-      callId: CallId('non-cloneable'),
+      callId: ToolCallId('non-cloneable'),
       name: 't',
       arguments: callerArguments,
       agent: key,
@@ -486,7 +486,7 @@ describe('scoped execution dispatch', () => {
     })
     const subjectlessResult = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('non-cloneable-subjectless'),
+      callId: ToolCallId('non-cloneable-subjectless'),
       name: 't',
       arguments: { invalid: () => undefined },
     })
@@ -525,7 +525,7 @@ describe('scoped execution dispatch', () => {
     const forged = { fake: true } as unknown as ToolExecutionToken
     let parentReads = 0
     const input = {
-      callId: CallId('stateful-parent'),
+      callId: ToolCallId('stateful-parent'),
       name: 't',
       arguments: {},
       signal: testToolSignal,
@@ -553,14 +553,14 @@ describe('scoped execution dispatch', () => {
       if (exec.name === 'parent') parent = exec.token
       return next()
     })
-    await ctx.tools.execute({ signal: testToolSignal, callId: CallId('parent'), name: 'parent', arguments: {} })
+    await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('parent'), name: 'parent', arguments: {} })
     stopCapture()
     const acceptedSignal = new AbortController().signal
     const driftSignal = new AbortController().signal
     const forged = { fake: true } as unknown as ToolExecutionToken
     const reads = { callId: 0, name: 0, arguments: 0, agent: 0, parent: 0, signal: 0 }
     const input = {
-      get callId() { reads.callId += 1; return CallId('unstable-error') },
+      get callId() { reads.callId += 1; return ToolCallId('unstable-error') },
       get name() { reads.name += 1; return 't' },
       get arguments(): unknown { reads.arguments += 1; return { invalid: () => undefined } },
       get agent() { reads.agent += 1; return reads.agent === 1 ? key : driftAgent },
@@ -578,7 +578,7 @@ describe('scoped execution dispatch', () => {
     expect(reads).toEqual({ callId: 1, name: 1, arguments: 1, agent: 1, parent: 1, signal: 1 })
     expect(scopedObserved).toBe(1)
     expect(observed).toMatchObject({
-      callId: CallId('unstable-error'),
+      callId: ToolCallId('unstable-error'),
       name: 't',
       agent: key,
       parent,
@@ -598,7 +598,7 @@ describe('scoped execution dispatch', () => {
       expect(result.isError).toBe(true)
     })
     const input = {
-      callId: CallId('throwing-arguments'),
+      callId: ToolCallId('throwing-arguments'),
       name: 't',
       signal: testToolSignal,
       get arguments(): unknown {
@@ -642,7 +642,7 @@ describe('scoped execution dispatch', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('bad-arguments'), name: 't', arguments: argumentsValue,
+      callId: ToolCallId('bad-arguments'), name: 't', arguments: argumentsValue,
     })
 
     expect(result.isError).toBe(true)
@@ -663,7 +663,7 @@ describe('scoped execution dispatch', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('unstable-arguments'), name: 't', arguments: argumentsValue,
+      callId: ToolCallId('unstable-arguments'), name: 't', arguments: argumentsValue,
     })
 
     expect(reads).toBe(1)
@@ -705,7 +705,7 @@ describe('scoped execution dispatch', () => {
     ctx.on('tools/result', () => Promise.reject(new Error('async observer failure')) as never)
     ctx.on('tools/result', (_exec, result) => { seen.push(result.isError) })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('final'), name: 't', arguments: {}, agent: key })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('final'), name: 't', arguments: {}, agent: key })
     await Promise.resolve()
     expect(result).toMatchObject({ isError: true, content: [{ type: 'text', text: 'outer failure' }] })
     expect(seen).toEqual([true, true])

+ 94 - 94
packages/core/tools/tests/tools.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, expectTypeOf, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { createUserMessage, CallId, HarnessError, type ContentBlock  } from '@deepseek-ai/dsh-llm'
+import { createUserMessage, ToolCallId, HarnessError, type ContentBlock  } from '@deepseek-ai/dsh-llm'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
 import type { Agent } from '@deepseek-ai/dsh-agent'
 import ApprovalService, { type ApprovalOutcome, type ApprovalRequest } from '@deepseek-ai/dsh-user-approval'
@@ -88,7 +88,7 @@ describe('ToolRuntime', () => {
     ctx.tools.register(echoTool)
     let observed: ToolExecutionResult | undefined
     ctx.on('tools/result', (_exec, result) => { observed = result })
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result).toEqual({ content: [{ type: 'text', text: 'hi' }], isError: false, value: 'hi' })
     expect(observed).toEqual(result)
   })
@@ -106,7 +106,7 @@ describe('ToolRuntime', () => {
         return 'ok'
       },
     })
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'meta-tool', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'meta-tool', arguments: {} })
     expect(result).toEqual({
       content: [{ type: 'text', text: 'ok' }],
       isError: false,
@@ -124,7 +124,7 @@ describe('ToolRuntime', () => {
         return 'ok'
       },
     })
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'no-meta-tool', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'no-meta-tool', arguments: {} })
     expect(result).toEqual({ content: [{ type: 'text', text: 'ok' }], isError: false, value: 'ok' })
     expect('meta' in result).toBe(false)
   })
@@ -147,7 +147,7 @@ describe('ToolRuntime', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('bad-meta'), name: 'bad-meta', arguments: {},
+      callId: ToolCallId('bad-meta'), name: 'bad-meta', arguments: {},
     })
     expect(result.isError).toBe(true)
     expect(result.content[0]?.type === 'text' && result.content[0].text).toContain('Error:')
@@ -185,7 +185,7 @@ describe('ToolRuntime', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('throwing-meta'), name: 'throwing-meta', arguments: {},
+      callId: ToolCallId('throwing-meta'), name: 'throwing-meta', arguments: {},
     })
 
     expect(result.isError).toBe(true)
@@ -210,7 +210,7 @@ describe('ToolRuntime', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('throwing-finalizer'), name: 'throwing-finalizer', arguments: {},
+      callId: ToolCallId('throwing-finalizer'), name: 'throwing-finalizer', arguments: {},
     })
 
     expect(result).toEqual({
@@ -251,8 +251,8 @@ describe('ToolRuntime', () => {
       execute: async () => 42 as unknown as string,
     }))
 
-    const lossy = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('lossy'), name: 'lossy-output', arguments: {} })
-    const mismatch = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('mismatch'), name: 'wrong-output', arguments: {} })
+    const lossy = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('lossy'), name: 'lossy-output', arguments: {} })
+    const mismatch = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('mismatch'), name: 'wrong-output', arguments: {} })
     expect(lossy.error).toMatchObject({ info: { name: 'ToolOutputError', code: 'INVALID_TOOL_OUTPUT' } })
     expect(lossy.content[0]?.type === 'text' ? lossy.content[0].text : '').toContain('not lossless JSON')
     expect(mismatch.error).toMatchObject({ info: { name: 'ToolOutputError', code: 'INVALID_TOOL_OUTPUT' } })
@@ -275,7 +275,7 @@ describe('ToolRuntime', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('hostile-body'), name: 'hostile-body', arguments: {},
+      callId: ToolCallId('hostile-body'), name: 'hostile-body', arguments: {},
     })
     expect(result.error?.message).toContain('value snapshot failed: body snapshot getter exploded')
     expect(result.error?.info).toEqual({ name: 'ToolOutputError', code: 'INVALID_TOOL_OUTPUT' })
@@ -301,7 +301,7 @@ describe('ToolRuntime', () => {
       execute: async () => 'ok',
     }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId(projector), name: `throwing-${projector}`, arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId(projector), name: `throwing-${projector}`, arguments: {} })
     expect(result.isError).toBe(true)
     expect(result.error?.message)
       .toContain(projector === 'render' ? 'renderer exploded' : 'metadata exploded')
@@ -333,7 +333,7 @@ describe('ToolRuntime', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId(`hostile-${projector}`), name: `hostile-${projector}`, arguments: {},
+      callId: ToolCallId(`hostile-${projector}`), name: `hostile-${projector}`, arguments: {},
     })
     expect(result.error?.message).toContain('snapshot getter exploded')
     expect(result.error?.info).toEqual({ name: 'ToolOutputError', code: 'INVALID_TOOL_OUTPUT' })
@@ -370,9 +370,9 @@ describe('ToolRuntime', () => {
       }
     })
 
-    const content = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('content'), name: 'projected', arguments: {} })
+    const content = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('content'), name: 'projected', arguments: {} })
     replacement = 'value'
-    const value = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('value'), name: 'projected', arguments: {} })
+    const value = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('value'), name: 'projected', arguments: {} })
 
     expect(content).toEqual({
       isError: false,
@@ -402,7 +402,7 @@ describe('ToolRuntime', () => {
       value: 'replacement',
       content: [{ type: 'text', text: 'also replacement' }],
     } as unknown as PostToolDecision))
-    const bothResult = await both.tools.execute({ signal: testToolSignal, callId: CallId('both'), name: 'echo', arguments: {} })
+    const bothResult = await both.tools.execute({ signal: testToolSignal, callId: ToolCallId('both'), name: 'echo', arguments: {} })
     expect(bothResult).toMatchObject({
       isError: true,
       error: { message: 'tools/post-execute accept decision cannot replace both value and content' },
@@ -411,7 +411,7 @@ describe('ToolRuntime', () => {
     const invalid = await setup()
     invalid.tools.register(echoTool)
     invalid.on('tools/post-execute', async () => ({ kind: 'accept', value: 1 }))
-    const invalidResult = await invalid.tools.execute({ signal: testToolSignal, callId: CallId('invalid'), name: 'echo', arguments: {} })
+    const invalidResult = await invalid.tools.execute({ signal: testToolSignal, callId: ToolCallId('invalid'), name: 'echo', arguments: {} })
     expect(invalidResult.error).toMatchObject({ info: { code: 'INVALID_TOOL_OUTPUT' } })
     expect('value' in invalidResult).toBe(false)
   })
@@ -424,7 +424,7 @@ describe('ToolRuntime', () => {
       feedback: [{ type: 'text', text: 'blocked by policy' }],
     }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('block'), name: 'echo', arguments: { text: 'secret' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('block'), name: 'echo', arguments: { text: 'secret' } })
     expect(result).toEqual({
       isError: true,
       error: { message: 'blocked by policy' },
@@ -438,7 +438,7 @@ describe('ToolRuntime', () => {
     ctx.tools.register(echoTool)
     ctx.on('tools/post-execute', async () => ({ kind: 'accept', value: 'replacement' }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('replace-value'), name: 'echo', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('replace-value'), name: 'echo', arguments: {} })
     expect(result).toEqual({
       isError: false,
       value: 'replacement',
@@ -454,7 +454,7 @@ describe('ToolRuntime', () => {
     ctx.tools.register(echoTool)
     ctx.on('tools/post-execute', async () => ({ kind: 'block', feedback: [...feedback] }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('block-message'), name: 'echo', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('block-message'), name: 'echo', arguments: {} })
     expect(result.error?.message).toBe(message)
   })
 
@@ -466,7 +466,7 @@ describe('ToolRuntime', () => {
       feedback: [{ type: 'text', text: 'blocked', invalid: () => undefined } as never],
     }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('invalid-block'), name: 'echo', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('invalid-block'), name: 'echo', arguments: {} })
     expect(result).toMatchObject({
       isError: true,
       error: { message: 'tool result must be losslessly JSON-serializable' },
@@ -484,7 +484,7 @@ describe('ToolRuntime', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('failed-replace'), name: 'throw-before-replace', arguments: {},
+      callId: ToolCallId('failed-replace'), name: 'throw-before-replace', arguments: {},
     })
     expect(result.error?.message).toBe('tools/post-execute cannot replace the value of a failed result')
   })
@@ -497,7 +497,7 @@ describe('ToolRuntime', () => {
       return { kind: 'accept', value: 'replacement' }
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('post-disposed'), name: 'echo', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('post-disposed'), name: 'echo', arguments: {} })
     expect(result.error).toEqual({
       message: 'unknown tool "echo"',
       info: { name: 'ToolNotFoundError', code: 'UNKNOWN_TOOL' },
@@ -517,7 +517,7 @@ describe('ToolRuntime', () => {
       })],
     }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('wrapper-failure'), name: 'echo', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('wrapper-failure'), name: 'echo', arguments: {} })
     expect(result).toEqual({
       isError: true,
       error: { message: 'wrapped failure' },
@@ -540,7 +540,7 @@ describe('ToolRuntime', () => {
       return { isError: false, value: 'replacement', content: [] }
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('wrapper-disposed'), name: 'echo', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('wrapper-disposed'), name: 'echo', arguments: {} })
     expect(result.error).toEqual({
       message: 'unknown tool "echo"',
       info: { name: 'ToolNotFoundError', code: 'UNKNOWN_TOOL' },
@@ -554,10 +554,10 @@ describe('ToolRuntime', () => {
       name: 'meta-suppression',
       output: { ...echoTool.output, presentationMeta: () => ({ card: true }) },
     })
-    const direct = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('direct'), name: 'meta-suppression', arguments: {} })
+    const direct = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('direct'), name: 'meta-suppression', arguments: {} })
     const nested = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('nested'),
+      callId: ToolCallId('nested'),
       name: 'meta-suppression',
       arguments: {},
       parent: Symbol('outer') as ToolExecutionToken,
@@ -588,7 +588,7 @@ describe('ToolRuntime', () => {
       async execute(_args, exec) {
         call += 1
         const nested = await ctx.tools.execute({
-          signal: exec.signal, callId: CallId(`nested-${call}`), name: 'terminal-nested', arguments: {}, parent: exec.token,
+          signal: exec.signal, callId: ToolCallId(`nested-${call}`), name: 'terminal-nested', arguments: {}, parent: exec.token,
         })
         if (nested.concludesTurn) exec.concludeTurn()
         return nested.isError ? 'nested failed, composite recovered' : 'nested succeeded'
@@ -602,7 +602,7 @@ describe('ToolRuntime', () => {
       return { kind: 'block', feedback: [{ type: 'text', text: 'nested success rejected' }] }
     })
     const recovered = await ctx.tools.execute({
-      signal: testToolSignal, callId: CallId('composite-vetoed'), name: 'composite', arguments: {},
+      signal: testToolSignal, callId: ToolCallId('composite-vetoed'), name: 'composite', arguments: {},
     })
     expect(recovered.isError).toBe(false)
     expect(recovered.concludesTurn).toBeUndefined()
@@ -611,7 +611,7 @@ describe('ToolRuntime', () => {
     // The same nested call succeeding carries the marker; the composite
     // forwards it onto its own successful result.
     const concluded = await ctx.tools.execute({
-      signal: testToolSignal, callId: CallId('composite-ok'), name: 'composite', arguments: {},
+      signal: testToolSignal, callId: ToolCallId('composite-ok'), name: 'composite', arguments: {},
     })
     expect(concluded.isError).toBe(false)
     expect(concluded.concludesTurn).toBe(true)
@@ -627,7 +627,7 @@ describe('ToolRuntime', () => {
       },
     })
 
-    const unknown = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'nope', arguments: {} })
+    const unknown = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'nope', arguments: {} })
     expect(unknown.isError).toBe(true)
     expect(unknown.content[0]).toMatchObject({ text: 'Error: unknown tool "nope"' })
     // An unknown tool is a routable failure class, same as a tool-thrown one.
@@ -636,7 +636,7 @@ describe('ToolRuntime', () => {
       info: { name: 'ToolNotFoundError', code: 'UNKNOWN_TOOL' },
     })
 
-    const thrown = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c2'), name: 'boom', arguments: {} })
+    const thrown = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c2'), name: 'boom', arguments: {} })
     expect(thrown.isError).toBe(true)
     expect(thrown.content[0]).toMatchObject({ text: 'Error: exploded' })
   })
@@ -657,7 +657,7 @@ describe('ToolRuntime', () => {
 
     await expect(ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('hostile'), name: 'hostile-throw', arguments: {},
+      callId: ToolCallId('hostile'), name: 'hostile-throw', arguments: {},
     })).resolves.toMatchObject({
       isError: true,
       content: [{ type: 'text', text: 'Error: <unprintable thrown value>' }],
@@ -688,7 +688,7 @@ describe('ToolRuntime', () => {
       return next()
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'Error: denied by policy' })
     expect(postSawFrozen).toBe(true)
@@ -701,7 +701,7 @@ describe('ToolRuntime', () => {
     ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> =>
       ({ kind: 'ask', reason: 'needs approval' }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'Error: needs approval' })
   })
@@ -712,7 +712,7 @@ describe('ToolRuntime', () => {
 
     ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'Error: tool "echo" requires approval (not yet supported)' })
   })
@@ -749,7 +749,7 @@ describe('ToolRuntime', () => {
         ({ kind: 'ask', reason: 'hook wants a human' }))
 
       const result = await ctx.tools.execute({
-        callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' }, agent, signal: controller.signal,
+        callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' }, agent, signal: controller.signal,
       })
 
       expect(result).toMatchObject({ isError: false, content: [{ type: 'text', text: 'hi' }] })
@@ -763,7 +763,7 @@ describe('ToolRuntime', () => {
       ctx.on('approval/request', () => Promise.resolve<ApprovalOutcome>('rejected'))
       ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
 
-      const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
+      const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
       expect(result.isError).toBe(true)
       expect(result.content[0]).toMatchObject({ text: 'Error: the user rejected tool "echo"' })
     })
@@ -773,7 +773,7 @@ describe('ToolRuntime', () => {
       ctx.on('approval/request', () => Promise.resolve<ApprovalOutcome>('cancelled'))
       ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
 
-      const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
+      const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
       expect(result.isError).toBe(true)
       expect(result.content[0]).toMatchObject({ text: 'Error: approval for tool "echo" was cancelled' })
     })
@@ -795,7 +795,7 @@ describe('ToolRuntime', () => {
       ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
       const controller = new AbortController()
       const pending = ctx.tools.execute({
-        callId: CallId('approval-cancelled'),
+        callId: ToolCallId('approval-cancelled'),
         name: 'approval-probe',
         arguments: {},
         agent: fakeAgent(),
@@ -817,7 +817,7 @@ describe('ToolRuntime', () => {
       const ctx = await approvalSetup()
       ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
 
-      const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
+      const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
       expect(result.isError).toBe(true)
       expect(result.content[0]).toMatchObject({ text: 'Error: tool "echo" requires approval, but no approval channel is available' })
     })
@@ -831,7 +831,7 @@ describe('ToolRuntime', () => {
       })
       ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
 
-      const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {} })
+      const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {} })
       expect(asked).toBe(false)
       expect(result.isError).toBe(true)
       expect(result.content[0]).toMatchObject({ text: 'Error: tool "echo" requires approval, but the call has no agent to route it through' })
@@ -846,7 +846,7 @@ describe('ToolRuntime', () => {
       ctx.provide('approval', { request: () => Promise.resolve('yolo') } as unknown as ApprovalService)
       ctx.on('tools/pre-execute', async (_exec, _next): Promise<PreToolDecision> => ({ kind: 'ask' }))
 
-      const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
+      const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: {}, agent: fakeAgent() })
       expect(result.isError).toBe(true)
       const text = result.content[0]?.type === 'text' ? result.content[0].text : ''
       expect(text).toContain('unreachable')
@@ -860,7 +860,7 @@ describe('ToolRuntime', () => {
     ctx.on('tools/post-execute', async (_exec, _result, _next): Promise<PostToolDecision> =>
       ({ kind: 'accept', content: [{ type: 'text', text: 'rewritten' }] }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result.isError).toBe(false)
     expect(result.content[0]).toMatchObject({ text: 'rewritten' })
   })
@@ -872,7 +872,7 @@ describe('ToolRuntime', () => {
     ctx.on('tools/post-execute', async (_exec, _result, _next): Promise<PostToolDecision> =>
       ({ kind: 'block', feedback: [{ type: 'text', text: 'output rejected: try again' }] }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'output rejected: try again' })
   })
@@ -895,7 +895,7 @@ describe('ToolRuntime', () => {
       throw new HarnessError('policy failed', 'POLICY_FAILED')
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('bounded'), name: 'bounded', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('bounded'), name: 'bounded', arguments: {} })
 
     expect(result).toEqual({
       content: [{ type: 'text', text: 'bounded failure' }],
@@ -919,7 +919,7 @@ describe('ToolRuntime', () => {
       },
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('identity-finalizer'), name: 'identity-finalizer', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('identity-finalizer'), name: 'identity-finalizer', arguments: {} })
 
     expect(result.isError).toBe(false)
     expect(result.content).toEqual([{ type: 'text', text: '' }])
@@ -939,7 +939,7 @@ describe('ToolRuntime', () => {
         })],
       }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'rejected' })
     expect(result.additionalContexts).toMatchObject([{ content: [{ text: 'why it was rejected' }], source: { kind: 'plugin', plugin: 'test' } }])
@@ -954,7 +954,7 @@ describe('ToolRuntime', () => {
         content: [{ type: 'text', text: 'fyi' }], source: { kind: 'plugin', plugin: 'test' },
       })] }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result.additionalContexts).toMatchObject([{ content: [{ text: 'fyi' }], source: { kind: 'plugin', plugin: 'test' } }])
   })
 
@@ -999,7 +999,7 @@ describe('ToolRuntime', () => {
       }
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('composite'), name: 'composite', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('composite'), name: 'composite', arguments: {} })
 
     expect(result.additionalContexts?.map(context => context.source)).toEqual([
       { kind: 'plugin', plugin: 'nested-1' },
@@ -1023,7 +1023,7 @@ describe('ToolRuntime', () => {
       },
     }))
 
-    const failed = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('failed'), name: 'failing-composite', arguments: {} })
+    const failed = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('failed'), name: 'failing-composite', arguments: {} })
     expect(failed.isError).toBe(true)
     expect(failed.additionalContexts?.map(context => context.source)).toEqual([{ kind: 'plugin', plugin: 'nested' }])
 
@@ -1034,7 +1034,7 @@ describe('ToolRuntime', () => {
         content: [{ type: 'text', text: 'block-only' }], source: { kind: 'plugin', plugin: 'blocker' },
       })],
     }))
-    const blocked = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('blocked'), name: 'failing-composite', arguments: {} })
+    const blocked = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('blocked'), name: 'failing-composite', arguments: {} })
     expect(blocked.isError).toBe(true)
     expect(blocked.additionalContexts?.map(context => context.source)).toEqual([{ kind: 'plugin', plugin: 'blocker' }])
   })
@@ -1057,7 +1057,7 @@ describe('ToolRuntime', () => {
       return decision
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'x' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'x' } })
     expect(result.isError).toBe(false)
     // pre runs fully (gate) before dispatch, then post runs over the result.
     expect(order).toEqual(['pre:before', 'pre:after', 'post:before', 'post:after'])
@@ -1085,7 +1085,7 @@ describe('ToolRuntime', () => {
     })
     ctx.on('tools/post-execute', async (_exec, _result, next) => { order.push('post'); return next() })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'traced', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'traced', arguments: { text: 'hi' } })
     expect(result).toEqual({ content: [{ type: 'text', text: 'hi' }], isError: false, value: [{ type: 'text', text: 'hi' }] })
     // The around-dispatch extension point wraps dispatch; pre gates before it, post runs over its result.
     expect(order).toEqual(['pre', 'execute:before', 'dispatch', 'execute:after', 'post'])
@@ -1109,7 +1109,7 @@ describe('ToolRuntime', () => {
 
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('cancelled-in-pre'), name: 'must-not-run', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('cancelled-in-pre'), name: 'must-not-run', arguments: {}, signal: controller.signal,
     })
     await entered.promise
     controller.abort('cancelled in policy')
@@ -1140,7 +1140,7 @@ describe('ToolRuntime', () => {
     })
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('denied-after-cancel'), name: 'denied-after-cancel', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('denied-after-cancel'), name: 'denied-after-cancel', arguments: {}, signal: controller.signal,
     })
 
     await entered.promise
@@ -1173,7 +1173,7 @@ describe('ToolRuntime', () => {
 
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('cancelled-pre-error'), name: 'must-not-run', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('cancelled-pre-error'), name: 'must-not-run', arguments: {}, signal: controller.signal,
     })
     await entered.promise
     controller.abort('cancelled in policy')
@@ -1212,7 +1212,7 @@ describe('ToolRuntime', () => {
 
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('cancelled-in-around'), name: 'must-not-run', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('cancelled-in-around'), name: 'must-not-run', arguments: {}, signal: controller.signal,
     })
     await entered.promise
     controller.abort('cancelled in wrapper')
@@ -1246,7 +1246,7 @@ describe('ToolRuntime', () => {
 
     const controller = new AbortController()
     const result = await ctx.tools.execute({
-      callId: CallId('cancelled-wrapper'), name: 'must-not-run', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('cancelled-wrapper'), name: 'must-not-run', arguments: {}, signal: controller.signal,
     })
 
     expect(result.error).toEqual({
@@ -1281,7 +1281,7 @@ describe('ToolRuntime', () => {
     })
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('cancelled-short-circuit'),
+      callId: ToolCallId('cancelled-short-circuit'),
       name: 'short-circuited',
       arguments: {},
       signal: controller.signal,
@@ -1323,7 +1323,7 @@ describe('ToolRuntime', () => {
     })
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('cancelled-after-body'), name: 'completed-before-wrapper', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('cancelled-after-body'), name: 'completed-before-wrapper', arguments: {}, signal: controller.signal,
     })
     await entered.promise
     controller.abort('cancelled while wrapper settled')
@@ -1366,7 +1366,7 @@ describe('ToolRuntime', () => {
     })
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('cancelled-in-post'), name: 'completed-before-post', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('cancelled-in-post'), name: 'completed-before-post', arguments: {}, signal: controller.signal,
     })
     await entered.promise
     controller.abort('cancelled while post policy waits')
@@ -1400,7 +1400,7 @@ describe('ToolRuntime', () => {
     })
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('wrapper-failure'), name: 'wrapper-failure', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('wrapper-failure'), name: 'wrapper-failure', arguments: {}, signal: controller.signal,
     })
 
     await entered.promise
@@ -1432,7 +1432,7 @@ describe('ToolRuntime', () => {
     })
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('tool-failure'), name: 'tool-failure', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('tool-failure'), name: 'tool-failure', arguments: {}, signal: controller.signal,
     })
 
     await entered.promise
@@ -1457,7 +1457,7 @@ describe('ToolRuntime', () => {
     })
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('post-failure'), name: 'echo', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('post-failure'), name: 'echo', arguments: {}, signal: controller.signal,
     })
 
     await entered.promise
@@ -1500,7 +1500,7 @@ describe('ToolRuntime', () => {
 
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('cancelled-body'), name: 'cooperative', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('cancelled-body'), name: 'cooperative', arguments: {}, signal: controller.signal,
     })
     await entered.promise
     expect(bodySignal).not.toBe(controller.signal)
@@ -1534,7 +1534,7 @@ describe('ToolRuntime', () => {
     const controller = new AbortController()
 
     await ctx.tools.execute({
-      callId: CallId('restored-signal'), name: 'echo', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('restored-signal'), name: 'echo', arguments: {}, signal: controller.signal,
     })
 
     expect(postSignal).toBe(controller.signal)
@@ -1558,7 +1558,7 @@ describe('ToolRuntime', () => {
     })
     const controller = new AbortController()
     const pending = ctx.tools.execute({
-      callId: CallId('drain-body'), name: 'uncooperative', arguments: {}, signal: controller.signal,
+      callId: ToolCallId('drain-body'), name: 'uncooperative', arguments: {}, signal: controller.signal,
     })
     await entered.promise
     controller.abort('must still drain')
@@ -1605,7 +1605,7 @@ describe('ToolRuntime', () => {
     })
 
     const result = await ctx.tools.execute({
-      callId: CallId('pre-aborted'),
+      callId: ToolCallId('pre-aborted'),
       name: 'domain-abort',
       get arguments() { argumentReads += 1; return callerArguments },
       signal: callerSignal,
@@ -1637,7 +1637,7 @@ describe('ToolRuntime', () => {
     ctx.on('tools/result', () => { observed += 1 })
 
     const result = await ctx.tools.execute({
-      callId: CallId('invalid-pre-aborted'),
+      callId: ToolCallId('invalid-pre-aborted'),
       name: 'missing',
       arguments: { invalid: () => undefined },
       signal: AbortSignal.abort('already cancelled'),
@@ -1662,7 +1662,7 @@ describe('ToolRuntime', () => {
       return next()
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'Error: nope' })
     expect(entered).toBe(false) // A denied call never enters the around-dispatch extension point.
@@ -1685,7 +1685,7 @@ describe('ToolRuntime', () => {
       return result
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'boom', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'boom', arguments: {} })
     expect(seen).toEqual({
       isError: true,
       error: { message: 'kaboom', info: { name: 'HarnessError', code: 'BOOM' } },
@@ -1710,7 +1710,7 @@ describe('ToolRuntime', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('frozen-canonical'), name: 'echo', arguments: { text: 'original' },
+      callId: ToolCallId('frozen-canonical'), name: 'echo', arguments: { text: 'original' },
     })
     expect(mutationAttempts).toEqual([false, false])
     expect(result.isError ? undefined : result.value).toBe('original')
@@ -1731,7 +1731,7 @@ describe('ToolRuntime', () => {
       return next()
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'boom', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'boom', arguments: {} })
     expect(postSaw).toBe(true) // the normalized isError still flows through post-execute
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'Error: exploded' })
@@ -1759,7 +1759,7 @@ describe('ToolRuntime', () => {
       return next()
     })
 
-    await ctx.tools.execute({ callId: CallId('c1'), name: 'signal-probe', arguments: {}, signal: upstream })
+    await ctx.tools.execute({ callId: ToolCallId('c1'), name: 'signal-probe', arguments: {}, signal: upstream })
     expect(seenSignal).toBeDefined()
     expect(seenSignal).not.toBe(upstream)
     expect(seenSignal).not.toBe(replacement)
@@ -1777,7 +1777,7 @@ describe('ToolRuntime', () => {
     ctx.on('tools/execute', async (_exec: ToolDispatchExecution, _next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult> =>
       ({ content: [{ type: 'text', text: 'ignored authored content' }], isError: false, value: 'short-circuited' }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'never-runs', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'never-runs', arguments: {} })
     expect(dispatched).toBe(false) // returning without next() skips core dispatch
     expect(result.content[0]).toMatchObject({ text: 'short-circuited' })
   })
@@ -1817,10 +1817,10 @@ describe('ToolRuntime', () => {
     })
 
     const first = await ctx.tools.execute({
-      signal: testToolSignal, callId: CallId('cached-first'), name: 'string-output', arguments: {},
+      signal: testToolSignal, callId: ToolCallId('cached-first'), name: 'string-output', arguments: {},
     })
     const second = await ctx.tools.execute({
-      signal: testToolSignal, callId: CallId('cached-second'), name: 'object-output', arguments: {},
+      signal: testToolSignal, callId: ToolCallId('cached-second'), name: 'object-output', arguments: {},
     })
 
     expect(first.isError ? undefined : first.value).toBe('cached')
@@ -1846,7 +1846,7 @@ describe('ToolRuntime', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('around-context'), name: 'echo', arguments: {},
+      callId: ToolCallId('around-context'), name: 'echo', arguments: {},
     })
     expect(result.additionalContexts).toEqual([{
       id: expect.any(String) as unknown,
@@ -1861,7 +1861,7 @@ describe('ToolRuntime', () => {
     ctx.tools.register(echoTool)
     ctx.on('tools/execute', async () => { throw new Error('wrapper broke') })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
     expect(result).toEqual({
       content: [{ type: 'text', text: 'Error: wrapper broke' }],
       error: { message: 'wrapper broke' },
@@ -1876,7 +1876,7 @@ describe('ToolRuntime', () => {
       throw new Error('permission hook broke')
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
 
     expect(result).toEqual({
       content: [{ type: 'text', text: 'Error: permission hook broke' }],
@@ -1892,7 +1892,7 @@ describe('ToolRuntime', () => {
       throw new Error('post hook broke')
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
 
     expect(result).toEqual({
       content: [{ type: 'text', text: 'Error: post hook broke' }],
@@ -1908,7 +1908,7 @@ describe('ToolRuntime', () => {
       throw new HarnessError('denied', 'DENIED')
     })
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'echo', arguments: { text: 'hi' } })
 
     expect(result).toMatchObject({
       isError: true,
@@ -2136,7 +2136,7 @@ describe('defineTool / schema DSL', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('c1'),
+      callId: ToolCallId('c1'),
       name: 'typed-echo',
       arguments: { text: 'hello', uppercase: true },
     })
@@ -2195,7 +2195,7 @@ describe('defineTool / schema DSL', () => {
     // Execution round-trip
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('c1'),
+      callId: ToolCallId('c1'),
       name: 'roundtrip',
       arguments: { req: 'hello' },
     })
@@ -2232,7 +2232,7 @@ describe('defineTool / schema DSL', () => {
 
     const result = await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('c1'),
+      callId: ToolCallId('c1'),
       name: 'raw-tool',
       arguments: { path: '/tmp' },
     })
@@ -2409,7 +2409,7 @@ describe('schema DSL optional and nested contracts', () => {
         throw { message: 'denied by object' }
       },
     })
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'object-thrower', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'object-thrower', arguments: {} })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'Error: denied by object' })
   })
@@ -2424,7 +2424,7 @@ describe('schema DSL optional and nested contracts', () => {
         throw 'kaboom'
       },
     })
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'string-thrower', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'string-thrower', arguments: {} })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({ text: 'Error: kaboom' })
   })
@@ -2439,7 +2439,7 @@ describe('schema DSL optional and nested contracts', () => {
         throw { code: 500 }
       },
     })
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'object-no-message', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'object-no-message', arguments: {} })
     expect(result.isError).toBe(true)
     const firstContent = result.content[0]!
     expect(firstContent.type).toBe('text')
@@ -2578,7 +2578,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
       },
     }))
 
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'reader', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'reader', arguments: {} })
     expect(result.isError).toBe(true)
     expect(result.content[0]).toMatchObject({
       text: 'Error: invalid arguments: missing required property "path"',
@@ -2595,7 +2595,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
         return [{ type: 'text', text: `read ${args.path}` }]
       },
     }))
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'reader', arguments: { path: '/x' } })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'reader', arguments: { path: '/x' } })
     expect(result).toEqual({
       content: [{ type: 'text', text: 'read /x' }],
       isError: false,
@@ -2622,7 +2622,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
         return [{ type: 'text', text: args.path }]
       },
     }))
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'reader', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'reader', arguments: {} })
     expect(result.isError).toBe(true)
     expect(result.error).toEqual({
       message: 'invalid arguments: missing required property "path"',
@@ -2640,7 +2640,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
         throw new HarnessError('disk full', 'ENOSPC')
       },
     })
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'coded', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'coded', arguments: {} })
     expect(result.isError).toBe(true)
     expect(result.error).toEqual({ message: 'disk full', info: { name: 'HarnessError', code: 'ENOSPC' } })
     expect(result.content[0]).toMatchObject({ text: 'Error: disk full' })
@@ -2655,7 +2655,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
         throw new Error('just a message')
       },
     })
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'plain', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'plain', arguments: {} })
     expect(result.isError).toBe(true)
     expect(result.error).toEqual({ message: 'just a message' })
     expect(result.content[0]).toMatchObject({ text: 'Error: just a message' })
@@ -2678,7 +2678,7 @@ describe('defineTool validation (the runtime-validation Agent Note, part 1)', ()
     })
     // Missing the "required" path — but raw tools validate their own input, so
     // this reaches execute rather than being rejected by the harness.
-    const result = await ctx.tools.execute({ signal: testToolSignal, callId: CallId('c1'), name: 'raw', arguments: {} })
+    const result = await ctx.tools.execute({ signal: testToolSignal, callId: ToolCallId('c1'), name: 'raw', arguments: {} })
     expect(result.isError).toBe(false)
   })
 

+ 4 - 4
packages/examples/agent-spine-demo/tests/agent-core.spec.ts

@@ -14,7 +14,7 @@ import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
 import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
 import {
   createUserMessage,
-  CallId,
+  ToolCallId,
   LlmAdapter,
   LlmError,
   resolveRetryPolicy,
@@ -605,8 +605,8 @@ describe('dsh-agent-spine-demo bundle', () => {
     const execution: ToolExecution = {
       signal: testToolSignal,
       token: Symbol('agent-core-dsh-home-test') as ToolExecution['token'],
-      callId: CallId('agent-core-dsh-home'),
-      rootCallId: CallId('agent-core-dsh-home'),
+      callId: ToolCallId('agent-core-dsh-home'),
+      rootCallId: ToolCallId('agent-core-dsh-home'),
       name: 'bash',
       arguments: { command: 'true' },
     }
@@ -685,7 +685,7 @@ describe('dsh-agent-spine-demo bundle', () => {
     const wait = vi.spyOn(ctx.jobs, 'wait')
     await ctx.tools.execute({
       signal: testToolSignal,
-      callId: CallId('task-config-forwarding'),
+      callId: ToolCallId('task-config-forwarding'),
       name: 'job_output',
       arguments: { job_id: id, wait: true },
     })

+ 15 - 15
packages/examples/agent-spine-demo/tests/multi-project-sandbox.e2e.ts

@@ -8,7 +8,7 @@ import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
 import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
 import * as FsPolicy from '@deepseek-ai/dsh-fs-observation-policy'
 import SandboxedFileSystem from '@deepseek-ai/dsh-fs-sandbox'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
 import { bwrapProfileArgs, seatbeltProfileArgs } from '@deepseek-ai/dsh-sandbox-local/src/profiles.ts'
 import SandboxPolicyService from '@deepseek-ai/dsh-sandbox-policy'
@@ -85,22 +85,22 @@ describe('one-context multi-project sandbox', () => {
     const { active, agentA, agentB } = await agents()
     const [aOwn, bOwn, aCross, bCross] = await Promise.all([
       active.tools.execute({
-        callId: CallId('bash-a-own'), name: 'bash', agent: agentA,
+        callId: ToolCallId('bash-a-own'), name: 'bash', agent: agentA,
         signal: new AbortController().signal,
         arguments: { command: 'printf a > a-owned.txt', description: 'Write project A marker' },
       }),
       active.tools.execute({
-        callId: CallId('bash-b-own'), name: 'bash', agent: agentB,
+        callId: ToolCallId('bash-b-own'), name: 'bash', agent: agentB,
         signal: new AbortController().signal,
         arguments: { command: 'printf b > b-owned.txt', description: 'Write project B marker' },
       }),
       active.tools.execute({
-        callId: CallId('bash-a-cross'), name: 'bash', agent: agentA,
+        callId: ToolCallId('bash-a-cross'), name: 'bash', agent: agentA,
         signal: new AbortController().signal,
         arguments: { command: `printf cross > ../${basename(projectB)}/from-a.txt`, description: 'Attempt project B write' },
       }),
       active.tools.execute({
-        callId: CallId('bash-b-cross'), name: 'bash', agent: agentB,
+        callId: ToolCallId('bash-b-cross'), name: 'bash', agent: agentB,
         signal: new AbortController().signal,
         arguments: { command: `printf cross > ../${basename(projectA)}/from-b.txt`, description: 'Attempt project A write' },
       }),
@@ -122,22 +122,22 @@ describe('one-context multi-project sandbox', () => {
     const { active, agentA, agentB } = await agents()
     const [aOwn, bOwn, aCross, bCross] = await Promise.all([
       active.tools.execute({
-        callId: CallId('fs-a-own'), name: 'write', agent: agentA,
+        callId: ToolCallId('fs-a-own'), name: 'write', agent: agentA,
         signal: new AbortController().signal,
         arguments: { file_path: 'a-owned.txt', content: 'a' },
       }),
       active.tools.execute({
-        callId: CallId('fs-b-own'), name: 'write', agent: agentB,
+        callId: ToolCallId('fs-b-own'), name: 'write', agent: agentB,
         signal: new AbortController().signal,
         arguments: { file_path: 'b-owned.txt', content: 'b' },
       }),
       active.tools.execute({
-        callId: CallId('fs-a-cross'), name: 'write', agent: agentA,
+        callId: ToolCallId('fs-a-cross'), name: 'write', agent: agentA,
         signal: new AbortController().signal,
         arguments: { file_path: join(projectB, 'from-a.txt'), content: 'cross' },
       }),
       active.tools.execute({
-        callId: CallId('fs-b-cross'), name: 'write', agent: agentB,
+        callId: ToolCallId('fs-b-cross'), name: 'write', agent: agentB,
         signal: new AbortController().signal,
         arguments: { file_path: join(projectA, 'from-b.txt'), content: 'cross' },
       }),
@@ -171,22 +171,22 @@ describe('one-context multi-project sandbox', () => {
 
     const [bashOwn, bashLexical, fsOwn, fsLexical] = await Promise.all([
       active.tools.execute({
-        callId: CallId('bash-symlink-own'), name: 'bash', agent: handle.agent,
+        callId: ToolCallId('bash-symlink-own'), name: 'bash', agent: handle.agent,
         signal: new AbortController().signal,
         arguments: { command: 'printf bash > bash-owned.txt', description: 'Write physical workspace marker' },
       }),
       active.tools.execute({
-        callId: CallId('bash-symlink-lexical'), name: 'bash', agent: handle.agent,
+        callId: ToolCallId('bash-symlink-lexical'), name: 'bash', agent: handle.agent,
         signal: new AbortController().signal,
         arguments: { command: `printf escaped > ${join(lexicalRoot, 'bash-escaped.txt')}`, description: 'Attempt lexical workspace write' },
       }),
       active.tools.execute({
-        callId: CallId('fs-symlink-own'), name: 'write', agent: handle.agent,
+        callId: ToolCallId('fs-symlink-own'), name: 'write', agent: handle.agent,
         signal: new AbortController().signal,
         arguments: { file_path: 'fs-owned.txt', content: 'fs' },
       }),
       active.tools.execute({
-        callId: CallId('fs-symlink-lexical'), name: 'write', agent: handle.agent,
+        callId: ToolCallId('fs-symlink-lexical'), name: 'write', agent: handle.agent,
         signal: new AbortController().signal,
         arguments: { file_path: join(lexicalRoot, 'fs-escaped.txt'), content: 'escaped' },
       }),
@@ -222,12 +222,12 @@ describe('one-context multi-project sandbox', () => {
 
     const [bashRead, fsRead] = await Promise.all([
       active.tools.execute({
-        callId: CallId('bash-symlink-parent-read'), name: 'bash', agent: handle.agent,
+        callId: ToolCallId('bash-symlink-parent-read'), name: 'bash', agent: handle.agent,
         signal: new AbortController().signal,
         arguments: { command: 'cat ../shared.txt', description: 'Read through the physical parent' },
       }),
       active.tools.execute({
-        callId: CallId('fs-symlink-parent-read'), name: 'read', agent: handle.agent,
+        callId: ToolCallId('fs-symlink-parent-read'), name: 'read', agent: handle.agent,
         signal: new AbortController().signal,
         arguments: { file_path: '../shared.txt' },
       }),

+ 2 - 2
packages/experimental/tool-agent-team/tests/tool-team.spec.ts

@@ -6,7 +6,7 @@ import { Context } from '@deepseek-ai/cordis'
 import type { Agent } from '@deepseek-ai/dsh-agent'
 import AgentLoop from '@deepseek-ai/dsh-agent-loop'
 import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import { scopeOf } from '@deepseek-ai/dsh-scope'
 import { SessionId } from '@deepseek-ai/dsh-session'
 import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
@@ -68,7 +68,7 @@ function execute(
   signal: AbortSignal = SIGNAL,
 ) {
   return ctx.tools.execute({
-    callId: CallId(`team-call-${++callNumber}`),
+    callId: ToolCallId(`team-call-${++callNumber}`),
     name,
     arguments: args,
     signal,

+ 2 - 2
packages/extensions/cordis-client-runner/src/client/slot-catalog.ts

@@ -216,7 +216,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 one keyed Chat business renderer. */\nexport interface ChatNodeOwnerProps {\n  /** Selected Tool call, when the shared details store names one. */\n  selectedCallId?: ToolCallId | undefined\n  /** Session workspace root; Tool summaries display paths relative to it. */\n  cwd?: string | undefined\n  openFile: (path: string) => void\n  inspectCall: (callId: ToolCallId) => 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}',
     ],
     ownerPropsReferences: [
       'MarkdownFileMentions',
@@ -1119,7 +1119,7 @@ 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}',
+      '/**\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: ToolCallId } | null\n  /** Acknowledge the inspect request once applied (clears the store field). */\n  onInspectDone?: () => void\n}',
     ],
     ownerPropsReferences: [],
     standardProps: [

+ 2 - 2
packages/extensions/cordis-host-runner/tests/helpers.ts

@@ -1,6 +1,6 @@
 import { Context } from '@deepseek-ai/cordis'
 import Timer from '@deepseek-ai/cordis-plugin-timer'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
 import ToolRegistry from '@deepseek-ai/dsh-tools'
 import type { ToolDefinition, ToolExecutionResult } from '@deepseek-ai/dsh-tools'
@@ -147,7 +147,7 @@ let callCounter = 0
 export function call(ctx: Context, name: string, args: unknown): Promise<ToolExecutionResult> {
   return ctx.tools.execute({
     signal: new AbortController().signal,
-    callId: CallId(`call-${++callCounter}`),
+    callId: ToolCallId(`call-${++callCounter}`),
     name,
     arguments: args,
   })

+ 10 - 10
packages/extensions/tool-cordis/src/api-catalog.ts

@@ -2894,7 +2894,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'ApprovalRequest',
-    declaration: 'export interface ApprovalRequest {\n    readonly agent: Agent;\n    readonly toolName: string;\n    readonly callId?: CallId;\n    readonly reason?: string;\n    readonly signal?: AbortSignal;\n}',
+    declaration: 'export interface ApprovalRequest {\n    readonly agent: Agent;\n    readonly toolName: string;\n    readonly callId?: ToolCallId;\n    readonly reason?: string;\n    readonly signal?: AbortSignal;\n}',
   },
   {
     name: 'ApprovalService',
@@ -3038,7 +3038,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'CodeDispatchLog',
-    declaration: 'export interface CodeDispatchLog {\n    readonly exec: ToolExecution;\n    readonly agent?: Agent;\n    readonly subCallId: CallId;\n    readonly name: string;\n    readonly isError: boolean;\n    readonly content: ContentBlock[];\n}',
+    declaration: 'export interface CodeDispatchLog {\n    readonly exec: ToolExecution;\n    readonly agent?: Agent;\n    readonly subCallId: ToolCallId;\n    readonly name: string;\n    readonly isError: boolean;\n    readonly content: ContentBlock[];\n}',
   },
   {
     name: 'CodeJsonValue',
@@ -3874,7 +3874,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'PrunedEntry',
-    declaration: 'export interface PrunedEntry {\n    readonly originalSeq: number;\n    readonly replacementSeq: number;\n    readonly callId: CallId;\n    readonly charsBefore: number;\n    readonly charsAfter: number;\n}',
+    declaration: 'export interface PrunedEntry {\n    readonly originalSeq: number;\n    readonly replacementSeq: number;\n    readonly callId: ToolCallId;\n    readonly charsBefore: number;\n    readonly charsAfter: number;\n}',
   },
   {
     name: 'PruneResult',
@@ -4066,7 +4066,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'SessionEventMap',
-    declaration: 'export interface SessionEventMap {\n    \'turn/start\': {\n        turn: number;\n    };\n    \'turn/end\': {\n        turn: number;\n        reason: TurnEndReason;\n    };\n    \'step/start\': {\n        turn: number;\n        step: number;\n    };\n    \'step/end\': {\n        turn: number;\n        step: number;\n    };\n    \'user/message\': UserMessage;\n    \'assistant/chunk\': {\n        turn: number;\n        step: number;\n        chunk: StreamChunk;\n    };\n    \'assistant/message\': {\n        turn: number;\n        step: number;\n        message: AssistantMessage;\n        usage?: TokenUsage;\n        interrupted?: true;\n    };\n    \'tool/call\': {\n        turn: number;\n        step: number;\n        callId: CallId;\n        name: string;\n        arguments: string;\n    };\n    \'tool/result\': {\n        turn: number;\n        step: number;\n        message: ToolResultMessage;\n        error?: {\n            name: string;\n            code: string;\n        };\n        meta?: JsonValue;\n    };\n    \'todo/write\': {\n        todos: TodoItem[];\n    };\n    \'request/header\': {\n        header: EpochHeader;\n        reason: RequestHeaderReason;\n    };\n    \'request/context\': RequestContext;\n    \'session/end-seed\': Record<string, never>;\n}',
+    declaration: 'export interface SessionEventMap {\n    \'turn/start\': {\n        turn: number;\n    };\n    \'turn/end\': {\n        turn: number;\n        reason: TurnEndReason;\n    };\n    \'step/start\': {\n        turn: number;\n        step: number;\n    };\n    \'step/end\': {\n        turn: number;\n        step: number;\n    };\n    \'user/message\': UserMessage;\n    \'assistant/chunk\': {\n        turn: number;\n        step: number;\n        chunk: StreamChunk;\n    };\n    \'assistant/message\': {\n        turn: number;\n        step: number;\n        message: AssistantMessage;\n        usage?: TokenUsage;\n        interrupted?: true;\n    };\n    \'tool/call\': {\n        turn: number;\n        step: number;\n        callId: ToolCallId;\n        name: string;\n        arguments: string;\n    };\n    \'tool/result\': {\n        turn: number;\n        step: number;\n        message: ToolResultMessage;\n        error?: {\n            name: string;\n            code: string;\n        };\n        meta?: JsonValue;\n    };\n    \'todo/write\': {\n        todos: TodoItem[];\n    };\n    \'request/header\': {\n        header: EpochHeader;\n        reason: RequestHeaderReason;\n    };\n    \'request/context\': RequestContext;\n    \'session/end-seed\': Record<string, never>;\n}',
   },
   {
     name: 'SessionEventMetadataFilter',
@@ -4426,7 +4426,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'SpillSource',
-    declaration: 'export interface SpillSource {\n    toolName: string;\n    callId: CallId;\n    label: string;\n}',
+    declaration: 'export interface SpillSource {\n    toolName: string;\n    callId: ToolCallId;\n    label: string;\n}',
   },
   {
     name: 'StorageBackend',
@@ -4442,7 +4442,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'StreamChunk',
-    declaration: 'export type StreamChunk = {\n    type: \'block-start\';\n    index: number;\n    blockType: ContentBlockType;\n} | {\n    type: \'text-delta\';\n    index: number;\n    text: string;\n} | {\n    type: \'reasoning-delta\';\n    index: number;\n    text: string;\n} | {\n    type: \'tool-call-delta\';\n    index: number;\n    id: CallId;\n    name?: string;\n    argumentsDelta: string;\n} | {\n    type: \'block-end\';\n    index: number;\n    block: ContentBlock;\n} | {\n    type: \'usage\';\n    usage: TokenUsage;\n} | {\n    type: \'finish\';\n    reason: FinishReason;\n    replayState?: ReplayEnvelope;\n};',
+    declaration: 'export type StreamChunk = {\n    type: \'block-start\';\n    index: number;\n    blockType: ContentBlockType;\n} | {\n    type: \'text-delta\';\n    index: number;\n    text: string;\n} | {\n    type: \'reasoning-delta\';\n    index: number;\n    text: string;\n} | {\n    type: \'tool-call-delta\';\n    index: number;\n    id: ToolCallId;\n    name?: string;\n    argumentsDelta: string;\n} | {\n    type: \'block-end\';\n    index: number;\n    block: ContentBlock;\n} | {\n    type: \'usage\';\n    usage: TokenUsage;\n} | {\n    type: \'finish\';\n    reason: FinishReason;\n    replayState?: ReplayEnvelope;\n};',
   },
   {
     name: 'SubagentCapabilities',
@@ -4750,7 +4750,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'ToolExecution',
-    declaration: 'export interface ToolExecution extends ToolExecutionInput {\n    readonly rootCallId: CallId;\n    readonly token: ToolExecutionToken;\n}',
+    declaration: 'export interface ToolExecution extends ToolExecutionInput {\n    readonly rootCallId: ToolCallId;\n    readonly token: ToolExecutionToken;\n}',
   },
   {
     name: 'ToolExecutionFailure',
@@ -4758,7 +4758,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'ToolExecutionInput',
-    declaration: 'export interface ToolExecutionInput {\n    readonly callId: CallId;\n    readonly rootCallId?: CallId;\n    readonly name: string;\n    readonly arguments: unknown;\n    readonly agent?: Agent;\n    readonly parent?: ToolExecutionToken;\n    readonly signal: AbortSignal;\n}',
+    declaration: 'export interface ToolExecutionInput {\n    readonly callId: ToolCallId;\n    readonly rootCallId?: ToolCallId;\n    readonly name: string;\n    readonly arguments: unknown;\n    readonly agent?: Agent;\n    readonly parent?: ToolExecutionToken;\n    readonly signal: AbortSignal;\n}',
   },
   {
     name: 'ToolExecutionMode',
@@ -4786,7 +4786,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'ToolMessageSource',
-    declaration: 'export interface ToolMessageSource {\n    kind: \'tool\';\n    callId: CallId;\n}',
+    declaration: 'export interface ToolMessageSource {\n    kind: \'tool\';\n    callId: ToolCallId;\n}',
   },
   {
     name: 'ToolOutputDefinition',
@@ -4810,7 +4810,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'ToolResultBlock',
-    declaration: 'export interface ToolResultBlock {\n    type: \'tool-result\';\n    toolCallId: CallId;\n    content: ContentBlock[];\n    isError?: boolean;\n}',
+    declaration: 'export interface ToolResultBlock {\n    type: \'tool-result\';\n    toolCallId: ToolCallId;\n    content: ContentBlock[];\n    isError?: boolean;\n}',
   },
   {
     name: 'ToolResultMessage',

+ 3 - 3
packages/fs/tool-fs-search/tests/integration.spec.ts

@@ -15,7 +15,7 @@ import { mkdir, mkdtemp, rm, utimes, writeFile } from 'node:fs/promises'
 import { tmpdir } from 'node:os'
 import { join } from 'node:path'
 import { Context } from '@deepseek-ai/cordis'
-import { CallId } from '@deepseek-ai/dsh-llm'
+import { ToolCallId } from '@deepseek-ai/dsh-llm'
 import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
 import ToolRuntime, { TOOL_ABORTED_BEFORE_DISPATCH } from '@deepseek-ai/dsh-tools'
 import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
@@ -30,7 +30,7 @@ let callCounter = 0
 function call(name: string, args: unknown, agentObj?: object) {
   return ctx.tools.execute({
     signal: testToolSignal,
-    callId: CallId(`it-${++callCounter}`),
+    callId: ToolCallId(`it-${++callCounter}`),
     name,
     arguments: args,
     ...agentObj ? { agent: agentObj as never } : {},
@@ -180,7 +180,7 @@ describe('search tools over the real subprocess service + the packaged rg', () =
       const controller = new AbortController()
       controller.abort()
       const result = await ctx.tools.execute({
-        callId: CallId(`it-${++callCounter}`),
+        callId: ToolCallId(`it-${++callCounter}`),
         name: 'grep',
         arguments: { pattern: 'x' },
         signal: controller.signal,

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů