diff --git a/docs/config-catalog.md b/docs/config-catalog.md index bcee8614ed..2a076a4f91 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -941,6 +941,36 @@ export interface StagehandModelConfig { ``` + + + +## `@deepseek-ai/dsh-experimental-claude-code-mods` + +- `source`: [`packages/experimental/claude-code-mods/src/index.ts:58`](../packages/experimental/claude-code-mods/src/index.ts) + +```ts config-catalog +/** Plugin config: which mod directories to load and the limits their hooks run under. */ +export interface Config { + /** + * Plugin directories to load, each holding `.claude-plugin/plugin.json` and + * `hooks/hooks.json`, as `claude --plugin-dir` takes them. A relative path + * resolves against the process launch cwd. Load order is chain order. + */ + pluginDirs: string[] + /** `register` option values by plugin name, overlaid on the manifest's `userConfig` defaults. */ + options?: Record> + /** A hook's own running-time limit in milliseconds (Claude Code: 10 seconds). */ + hookTimeoutMs?: number + /** A `.catch` handler's running-time limit in milliseconds (Claude Code: 1 second). */ + catchTimeoutMs?: number + /** Default `$.process.run` and `$.http.fetch` timeout in milliseconds (Claude Code: 30 seconds). */ + processTimeoutMs?: number + /** Claude Code tool name → harness tool name entries added to the built-in alias table. */ + toolAliases?: Record +} +``` + + diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index 1f734eac99..161354250c 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -943,6 +943,36 @@ export interface StagehandModelConfig { ``` + + + +## `@deepseek-ai/dsh-experimental-claude-code-mods` + +- `source`: [`packages/experimental/claude-code-mods/src/index.ts:58`](../packages/experimental/claude-code-mods/src/index.ts) + +```ts config-catalog +/** Plugin config: which mod directories to load and the limits their hooks run under. */ +export interface Config { + /** + * Plugin directories to load, each holding `.claude-plugin/plugin.json` and + * `hooks/hooks.json`, as `claude --plugin-dir` takes them. A relative path + * resolves against the process launch cwd. Load order is chain order. + */ + pluginDirs: string[] + /** `register` option values by plugin name, overlaid on the manifest's `userConfig` defaults. */ + options?: Record> + /** A hook's own running-time limit in milliseconds (Claude Code: 10 seconds). */ + hookTimeoutMs?: number + /** A `.catch` handler's running-time limit in milliseconds (Claude Code: 1 second). */ + catchTimeoutMs?: number + /** Default `$.process.run` and `$.http.fetch` timeout in milliseconds (Claude Code: 30 seconds). */ + processTimeoutMs?: number + /** Claude Code tool name → harness tool name entries added to the built-in alias table. */ + toolAliases?: Record +} +``` + + diff --git a/docs/event-producer-consumer.md b/docs/event-producer-consumer.md index 3ed51c618a..b2b9c6f0ea 100644 --- a/docs/event-producer-consumer.md +++ b/docs/event-producer-consumer.md @@ -11,13 +11,13 @@ This matrix shows which packages dispatch each harness-owned event and which pac | `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:238`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - | | `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:69`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | `remotes` | | `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:363`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` | -| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:261`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | -| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:270`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | +| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:261`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | +| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:270`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | | `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:393`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`session-telemetry`](../packages/session/session-telemetry) | | `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) | | `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:307`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) | | `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:288`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver) | -| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:320`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) | +| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:320`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) | | `agent/request` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:337`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`webhook`](../packages/webhook/webhook) | | `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:353`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) | | `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:280`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` | @@ -63,7 +63,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac | `session-telemetry/record` | `waterfall` | [`packages/session/session-telemetry/src/index.ts:44`](../packages/session/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/session/session-telemetry) (`waterfall`) | - | | `session/created` | `emit` | [`packages/core/session/src/index.ts:54`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`permission-presets`](../packages/interaction/permission-presets), [`schedule`](../packages/schedule/schedule), `server`, `session-controller`, [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry) | | `session/disposed` | `emit` | [`packages/core/session/src/index.ts:63`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `agent-team`, `file-upload`, `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-title`](../packages/session/session-title), [`workspace-changes`](../packages/deliverables/workspace-changes) | -| `session/event` | `emit` | [`packages/core/session/src/index.ts:74`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/acp/acp), [`agent-instructions`](../packages/context/agent-instructions), [`agent-loop`](../packages/core/agent-loop), [`agent-preset-registry`](../packages/preset/agent-preset-registry), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`file-reference-local`](../packages/context/file-reference-local), `file-upload`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), [`loader-smoke`](../packages/test-support/loader-smoke), `product-analytics`, `server`, `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-telemetry-otel`](../packages/session/session-telemetry-otel), [`session-title`](../packages/session/session-title), [`token-meter`](../packages/llm/token-meter), [`user-questions`](../packages/interaction/user-questions), [`workspace-changes`](../packages/deliverables/workspace-changes) | +| `session/event` | `emit` | [`packages/core/session/src/index.ts:74`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/acp/acp), [`agent-instructions`](../packages/context/agent-instructions), [`agent-loop`](../packages/core/agent-loop), [`agent-preset-registry`](../packages/preset/agent-preset-registry), `agent-team`, `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`file-reference-local`](../packages/context/file-reference-local), `file-upload`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), [`loader-smoke`](../packages/test-support/loader-smoke), `product-analytics`, `server`, `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-telemetry-otel`](../packages/session/session-telemetry-otel), [`session-title`](../packages/session/session-title), [`token-meter`](../packages/llm/token-meter), [`user-questions`](../packages/interaction/user-questions), [`workspace-changes`](../packages/deliverables/workspace-changes) | | `session/flush` | `parallel` | [`packages/core/session/src/index.ts:82`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-telemetry`](../packages/session/session-telemetry) | | `settings/document-updated` | `emit` | [`packages/settings/settings/src/types.ts:75`](../packages/settings/settings/src/types.ts) | [`settings`](../packages/settings/settings) (`emit`) | `remotes` | | `skills/change` | `emit` | [`packages/skill/skill/src/index.ts:296`](../packages/skill/skill/src/index.ts) | [`skill`](../packages/skill/skill) (`events.dispatch`) | - | @@ -74,8 +74,8 @@ This matrix shows which packages dispatch each harness-owned event and which pac | `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:31`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | [`agent`](../packages/core/agent), `browser-use-runtime`, [`session-reference`](../packages/context/session-reference) | | `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:37`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - | | `tools/change` | `emit` | [`packages/core/tools/src/index.ts:208`](../packages/core/tools/src/index.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`), [`tools`](../packages/core/tools) (`emit`) | `browser-use-runtime`, [`tool-subagent`](../packages/subagent/tool-subagent) | -| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:164`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `browser-use-runtime`, `browser-use-stagehand-native`, `computer-use-cua-driver-native`, [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `timeout-policy` | -| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:176`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) | +| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:164`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `browser-use-runtime`, `browser-use-stagehand-native`, `claude-code-mods`, `computer-use-cua-driver-native`, [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `timeout-policy` | +| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:176`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `claude-code-mods`, [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) | | `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:153`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `auto-review`, [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`tool-jobs`](../packages/jobs/tool-jobs), [`workspace-changes`](../packages/deliverables/workspace-changes) | | `tools/ptc-dispatch-log` | `waterfall` | [`packages/core/tools/src/index.ts:190`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`spill-policy`](../packages/spill/spill-policy) | | `tools/result` | `emit` | [`packages/core/tools/src/index.ts:198`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`agent-instructions`](../packages/context/agent-instructions), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`tool-present`](../packages/deliverables/tool-present) | diff --git a/docs/event-producer-consumer.zh.md b/docs/event-producer-consumer.zh.md index 911434f569..4726a04cec 100644 --- a/docs/event-producer-consumer.zh.md +++ b/docs/event-producer-consumer.zh.md @@ -13,13 +13,13 @@ | `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:238`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - | | `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:69`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | `remotes` | | `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:363`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` | -| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:261`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | -| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:270`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | +| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:261`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | +| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:270`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) | | `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:393`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`session-telemetry`](../packages/session/session-telemetry) | | `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) | | `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:307`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) | | `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:288`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver) | -| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:320`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) | +| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:320`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) | | `agent/request` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:337`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`webhook`](../packages/webhook/webhook) | | `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:353`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) | | `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:280`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` | @@ -65,7 +65,7 @@ | `session-telemetry/record` | `waterfall` | [`packages/session/session-telemetry/src/index.ts:44`](../packages/session/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/session/session-telemetry) (`waterfall`) | - | | `session/created` | `emit` | [`packages/core/session/src/index.ts:54`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`permission-presets`](../packages/interaction/permission-presets), [`schedule`](../packages/schedule/schedule), `server`, `session-controller`, [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry) | | `session/disposed` | `emit` | [`packages/core/session/src/index.ts:63`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `agent-team`, `file-upload`, `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-title`](../packages/session/session-title), [`workspace-changes`](../packages/deliverables/workspace-changes) | -| `session/event` | `emit` | [`packages/core/session/src/index.ts:74`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/acp/acp), [`agent-instructions`](../packages/context/agent-instructions), [`agent-loop`](../packages/core/agent-loop), [`agent-preset-registry`](../packages/preset/agent-preset-registry), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`file-reference-local`](../packages/context/file-reference-local), `file-upload`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), [`loader-smoke`](../packages/test-support/loader-smoke), `product-analytics`, `server`, `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-telemetry-otel`](../packages/session/session-telemetry-otel), [`session-title`](../packages/session/session-title), [`token-meter`](../packages/llm/token-meter), [`user-questions`](../packages/interaction/user-questions), [`workspace-changes`](../packages/deliverables/workspace-changes) | +| `session/event` | `emit` | [`packages/core/session/src/index.ts:74`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/acp/acp), [`agent-instructions`](../packages/context/agent-instructions), [`agent-loop`](../packages/core/agent-loop), [`agent-preset-registry`](../packages/preset/agent-preset-registry), `agent-team`, `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`file-reference-local`](../packages/context/file-reference-local), `file-upload`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), [`loader-smoke`](../packages/test-support/loader-smoke), `product-analytics`, `server`, `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-telemetry-otel`](../packages/session/session-telemetry-otel), [`session-title`](../packages/session/session-title), [`token-meter`](../packages/llm/token-meter), [`user-questions`](../packages/interaction/user-questions), [`workspace-changes`](../packages/deliverables/workspace-changes) | | `session/flush` | `parallel` | [`packages/core/session/src/index.ts:82`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-telemetry`](../packages/session/session-telemetry) | | `settings/document-updated` | `emit` | [`packages/settings/settings/src/types.ts:75`](../packages/settings/settings/src/types.ts) | [`settings`](../packages/settings/settings) (`emit`) | `remotes` | | `skills/change` | `emit` | [`packages/skill/skill/src/index.ts:296`](../packages/skill/skill/src/index.ts) | [`skill`](../packages/skill/skill) (`events.dispatch`) | - | @@ -76,8 +76,8 @@ | `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:31`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | [`agent`](../packages/core/agent), `browser-use-runtime`, [`session-reference`](../packages/context/session-reference) | | `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:37`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - | | `tools/change` | `emit` | [`packages/core/tools/src/index.ts:208`](../packages/core/tools/src/index.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`), [`tools`](../packages/core/tools) (`emit`) | `browser-use-runtime`, [`tool-subagent`](../packages/subagent/tool-subagent) | -| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:164`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `browser-use-runtime`, `browser-use-stagehand-native`, `computer-use-cua-driver-native`, [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `timeout-policy` | -| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:176`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) | +| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:164`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `browser-use-runtime`, `browser-use-stagehand-native`, `claude-code-mods`, `computer-use-cua-driver-native`, [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `timeout-policy` | +| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:176`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `claude-code-mods`, [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) | | `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:153`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `auto-review`, [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`tool-jobs`](../packages/jobs/tool-jobs), [`workspace-changes`](../packages/deliverables/workspace-changes) | | `tools/ptc-dispatch-log` | `waterfall` | [`packages/core/tools/src/index.ts:190`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`spill-policy`](../packages/spill/spill-policy) | | `tools/result` | `emit` | [`packages/core/tools/src/index.ts:198`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`agent-instructions`](../packages/context/agent-instructions), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`tool-present`](../packages/deliverables/tool-present) | diff --git a/docs/module-graph.md b/docs/module-graph.md index 7aa818fae4..9e80a63045 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -254,6 +254,7 @@ flowchart TD pkg_experimental_browser_use_playwright_mcp["experimental-browser-use-playwright-mcp"] pkg_experimental_browser_use_runtime["experimental-browser-use-runtime"] pkg_experimental_browser_use_stagehand_native["experimental-browser-use-stagehand-native"] + pkg_experimental_claude_code_mods["experimental-claude-code-mods"] pkg_experimental_client_ui_agent_team["experimental-client-ui-agent-team"] pkg_experimental_client_ui_voice_input["experimental-client-ui-voice-input"] pkg_experimental_computer_use_cua_driver_mcp["experimental-computer-use-cua-driver-mcp"] @@ -1137,6 +1138,18 @@ flowchart TD pkg_experimental_browser_use_runtime --> pkg_scope pkg_experimental_browser_use_runtime --> pkg_system_prompt pkg_experimental_browser_use_runtime --> pkg_tools + pkg_experimental_claude_code_mods --> pkg_agent + pkg_experimental_claude_code_mods --> pkg_commands + pkg_experimental_claude_code_mods --> pkg_fs + pkg_experimental_claude_code_mods --> pkg_llm + pkg_experimental_claude_code_mods --> pkg_scope + pkg_experimental_claude_code_mods --> pkg_session + pkg_experimental_claude_code_mods --> pkg_session_projection + pkg_experimental_claude_code_mods --> pkg_storage_domain + pkg_experimental_claude_code_mods --> pkg_subprocess + pkg_experimental_claude_code_mods --> pkg_token_meter + pkg_experimental_claude_code_mods --> pkg_tools + pkg_experimental_claude_code_mods --> pkg_user_questions pkg_webhook_github --> pkg_credentials pkg_webhook_github --> pkg_host_webserver pkg_webhook_github --> pkg_session @@ -1640,6 +1653,7 @@ flowchart TD | [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query) | | [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) | | [`experimental-browser-use-runtime`](../packages/experimental/browser-use-runtime) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`mcp-client`](../packages/mcp/mcp-client), [`scope`](../packages/core/scope), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | +| [`experimental-claude-code-mods`](../packages/experimental/claude-code-mods) | `experimental` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`storage-domain`](../packages/storage/storage-domain), [`subprocess`](../packages/subprocess/subprocess), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) | | [`webhook-github`](../packages/webhook/webhook-github) | `webhook` | [`credentials`](../packages/credentials/credentials), [`host-webserver`](../packages/host/webserver), [`session`](../packages/core/session), [`webhook`](../packages/webhook/webhook) | | [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) | | [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | `subagent` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) | diff --git a/docs/module-graph.zh.md b/docs/module-graph.zh.md index 48da82511d..bd496be3e8 100644 --- a/docs/module-graph.zh.md +++ b/docs/module-graph.zh.md @@ -256,6 +256,7 @@ flowchart TD pkg_experimental_browser_use_playwright_mcp["experimental-browser-use-playwright-mcp"] pkg_experimental_browser_use_runtime["experimental-browser-use-runtime"] pkg_experimental_browser_use_stagehand_native["experimental-browser-use-stagehand-native"] + pkg_experimental_claude_code_mods["experimental-claude-code-mods"] pkg_experimental_client_ui_agent_team["experimental-client-ui-agent-team"] pkg_experimental_client_ui_voice_input["experimental-client-ui-voice-input"] pkg_experimental_computer_use_cua_driver_mcp["experimental-computer-use-cua-driver-mcp"] @@ -1139,6 +1140,18 @@ flowchart TD pkg_experimental_browser_use_runtime --> pkg_scope pkg_experimental_browser_use_runtime --> pkg_system_prompt pkg_experimental_browser_use_runtime --> pkg_tools + pkg_experimental_claude_code_mods --> pkg_agent + pkg_experimental_claude_code_mods --> pkg_commands + pkg_experimental_claude_code_mods --> pkg_fs + pkg_experimental_claude_code_mods --> pkg_llm + pkg_experimental_claude_code_mods --> pkg_scope + pkg_experimental_claude_code_mods --> pkg_session + pkg_experimental_claude_code_mods --> pkg_session_projection + pkg_experimental_claude_code_mods --> pkg_storage_domain + pkg_experimental_claude_code_mods --> pkg_subprocess + pkg_experimental_claude_code_mods --> pkg_token_meter + pkg_experimental_claude_code_mods --> pkg_tools + pkg_experimental_claude_code_mods --> pkg_user_questions pkg_webhook_github --> pkg_credentials pkg_webhook_github --> pkg_host_webserver pkg_webhook_github --> pkg_session @@ -1642,6 +1655,7 @@ flowchart TD | [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query) | | [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) | | [`experimental-browser-use-runtime`](../packages/experimental/browser-use-runtime) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`mcp-client`](../packages/mcp/mcp-client), [`scope`](../packages/core/scope), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | +| [`experimental-claude-code-mods`](../packages/experimental/claude-code-mods) | `experimental` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`storage-domain`](../packages/storage/storage-domain), [`subprocess`](../packages/subprocess/subprocess), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) | | [`webhook-github`](../packages/webhook/webhook-github) | `webhook` | [`credentials`](../packages/credentials/credentials), [`host-webserver`](../packages/host/webserver), [`session`](../packages/core/session), [`webhook`](../packages/webhook/webhook) | | [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) | | [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | `subagent` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) | diff --git a/docs/persistence-catalog.i18n.yaml b/docs/persistence-catalog.i18n.yaml index 0797c29333..59a86a91fa 100644 --- a/docs/persistence-catalog.i18n.yaml +++ b/docs/persistence-catalog.i18n.yaml @@ -9,8 +9,8 @@ en: e7c80d1de4d328b4 zh: d9f368c27fd9f998 /session-persistence-event-catalog/persistence-type-fingerprints: - en: 33dd28b1c768cd8b - zh: 7256442baf2faed1 + en: 05f46180f4a99e08 + zh: 6116771ce8dc8f9b /session-persistence-event-catalog/event-envelope: en: 7ebb1a2e0dd1f916 zh: 8946f415dda5c1b8 @@ -378,8 +378,11 @@ en: 491d3885b56f0ecd zh: 491d3885b56f0ecd /session-persistence-event-catalog/resolved-persistence-types/claude-code: - en: 6c96d71d5948c1fb - zh: 6c96d71d5948c1fb + en: 50bec9f3f584a2c6 + zh: 50bec9f3f584a2c6 +/session-persistence-event-catalog/resolved-persistence-types/claude-code-mods: + en: bf8d8a5d4dbf5ac4 + zh: bf8d8a5d4dbf5ac4 /session-persistence-event-catalog/resolved-persistence-types/clear: en: ae48fcd465c67547 zh: ae48fcd465c67547 @@ -957,11 +960,11 @@ en: 586743dfeab78c55 zh: 26ccef4a02dd7a58 /session-persistence-event-catalog/resolved-persistence-types/deepseeksearchllmrequest: - en: ea25cd944b4b4020 - zh: 87f8944f766d3a85 + en: e6155a9d4d17e1c5 + zh: 45a94a45151d3718 /session-persistence-event-catalog/resolved-persistence-types/developermessage: - en: af7ce240db9e3463 - zh: cd15cd0cd369c2c0 + en: b3fa7a23895ce5f0 + zh: efeb9ef4554704a7 /session-persistence-event-catalog/resolved-persistence-types/epochheader: en: d31020fa1fda6fa4 zh: 77984c4bf09443fb @@ -1059,11 +1062,11 @@ en: 6dbe099c62f7c226 zh: 6fd788f63c038c91 /session-persistence-event-catalog/resolved-persistence-types/llmretrystartedeventdata: - en: f4202ef53de39006 - zh: f8f1d0bfc0451c31 + en: f1d973b803c8846c + zh: a2f22a87efae66c4 /session-persistence-event-catalog/resolved-persistence-types/message: - en: a3d2a5be822c5a86 - zh: 871cc3cafabe99be + en: eff4b522b67f0c9c + zh: fbafb568e2b78e6f /session-persistence-event-catalog/resolved-persistence-types/messagefeedbackdelete: en: c582953270d4ca44 zh: 966ccfe2e7660a2a @@ -1074,14 +1077,14 @@ en: f58575a1fc96df42 zh: 9f0c6ed9033e5c9d /session-persistence-event-catalog/resolved-persistence-types/messagefeedbackrating: - en: ef7b940d5654ce5a - zh: 72d4c5b3f6e2ff17 + en: c9877febe941ec00 + zh: 4cdfcd185af442ef /session-persistence-event-catalog/resolved-persistence-types/messagesource: - en: f1789e91fd4338e3 - zh: 8250eb33cda749b5 + en: 2a212cbeab12439b + zh: eba0aaf887d862c4 /session-persistence-event-catalog/resolved-persistence-types/message~2: - en: 412cc1296786d489 - zh: f457e8629515a526 + en: 4262a27e79e8fe58 + zh: 0af2986991ba0bd6 /session-persistence-event-catalog/resolved-persistence-types/modelmessagesource: en: 3b677f021d7dd4d3 zh: 2a875742b22879a2 @@ -1140,11 +1143,11 @@ en: 9a54c4478ad3b620 zh: 02c4c1fd7e252770 /session-persistence-event-catalog/resolved-persistence-types/sessiontitleeventdata: - en: 2c684a3db9219c45 - zh: 202000f582ca1ec8 + en: d2b8d98e5a61080a + zh: 9f3906d07f01cb11 /session-persistence-event-catalog/resolved-persistence-types/sessiontitlellmrequesteventdata: - en: 7b180deead48738e - zh: 35d43b7ef489f97b + en: 190cb14ae9e2643e + zh: 4def666bc8227eae /session-persistence-event-catalog/resolved-persistence-types/sessiontitlemodelidentity: en: 9531468264499498 zh: 9da23c0ad2776563 @@ -1251,14 +1254,14 @@ en: 544292eddeff5faa zh: 4e36e575a5030fb2 /session-persistence-event-catalog/resolved-persistence-types/turnendreason: - en: 7fd4741c641f9252 - zh: be115df1a8ee59ce + en: 09d904c208591ecf + zh: 4691378e6edf4a10 /session-persistence-event-catalog/resolved-persistence-types/usermessage: - en: c9090b73ac4ce05b - zh: beb1f58404b0a2b8 + en: 5bf558e2dad6cd71 + zh: 6f4a7faa001a401f /session-persistence-event-catalog/resolved-persistence-types/usermessage~2: - en: a3974d05a5d357d3 - zh: 99e73822f538c99d + en: 13e25e9c0987cdb3 + zh: f22a445f674c117e /session-persistence-event-catalog/resolved-persistence-types/workflowagentoutcome: en: d0a3f9aab18c3de5 zh: 76ac69a835ca4e29 @@ -1428,11 +1431,11 @@ en: 51af619ed8532a43 zh: a5ad878c63d0e046 /session-persistence-event-catalog/resolved-persistence-types/header-reason-startsseries: - en: 2b549aa36fe3ab7b - zh: 069ef4498112c4e7 + en: 8a701a96d7ae3a3a + zh: 2a040ba2b0ab1d7a /session-persistence-event-catalog/resolved-persistence-types/headerseq-message-step-turn: - en: 97dc4446fd0b02ac - zh: 57ec4b27d0221957 + en: 12c5aacc0757cb46 + zh: 0a3bce7a4b6af6b6 /session-persistence-event-catalog/resolved-persistence-types/height-width: en: a5df50710083ac25 zh: aad3ccf6f6de70e4 @@ -1449,11 +1452,11 @@ en: 41f73c58a77dfa28 zh: af77f4aee3cb4a40 /session-persistence-event-catalog/resolved-persistence-types/inherited: - en: 7dcf0c1ade316952 - zh: 3c9cd7564659ec3d + en: 10f315a16414761a + zh: 1ec6e038b61cc73c /session-persistence-event-catalog/resolved-persistence-types/inserted-outcome-removedcount-start: - en: f2167c4cf3aa9b4d - zh: b4ed04a9a526f49e + en: 889df881faa1b240 + zh: a9e3d8ea7b02d735 /session-persistence-event-catalog/resolved-persistence-types/interrupted-message-step-stream: en: f460572284c92755 zh: bae7561ce887b040 @@ -1461,8 +1464,29 @@ en: c7755cbd343d67ee zh: e00a7477cd664cb5 /session-persistence-event-catalog/resolved-persistence-types/kind-blocked: - en: 46465e6fa6292ec3 - zh: b8ed143600bf51d2 + en: de399acba4f72941 + zh: 8b9a7f17605fe70a +/session-persistence-event-catalog/resolved-persistence-types/kind-claude-code-mods: + en: 511a9f101c0a3ca8 + zh: 08ef0e60864b1f34 +/session-persistence-event-catalog/resolved-persistence-types/kind-claude-code-mods-form-catalog: + en: c5ea3ce6b1b86451 + zh: e713c1b413035904 +/session-persistence-event-catalog/resolved-persistence-types/kind-claude-code-mods-form-instructions: + en: 2484cd3e89d9b43d + zh: 0d895de7e75cea20 +/session-persistence-event-catalog/resolved-persistence-types/kind-claude-code-mods-form-notice: + en: 9d4632d9debb94c5 + zh: 60260b29486d8f6b +/session-persistence-event-catalog/resolved-persistence-types/kind-claude-code-mods-form-recall: + en: 2dd438951de72b81 + zh: ed07c0a857342138 +/session-persistence-event-catalog/resolved-persistence-types/kind-claude-code-mods-form-relay: + en: 1c4b32d0d8555ebd + zh: 864eb390d0c508b7 +/session-persistence-event-catalog/resolved-persistence-types/kind-claude-code-mods-form-snapshot: + en: 3c4a923cd1f3294b + zh: 68271c0032914bdd /session-persistence-event-catalog/resolved-persistence-types/kind-completed: en: 2f3ffb879c568b64 zh: ffc7d0f2b64494fc @@ -1845,11 +1869,11 @@ en: a79b2ab16b9ed215 zh: 764cd1400ee8db63 /session-persistence-event-catalog/resolved-persistence-types/type-agent-preset-selected: - en: c8b6e46c8e161688 - zh: 3f45f3e70ce9f600 + en: 26c01bf2e45c51bf + zh: 93cec8b8de4cce42 /session-persistence-event-catalog/resolved-persistence-types/type-agent-inbox-spliced: - en: d73cb120a4aa138e - zh: 1f5ce9f1bb430aec + en: 8ff344aec4a5f540 + zh: 5d6425608a8f9bc8 /session-persistence-event-catalog/resolved-persistence-types/type-approval-asked: en: 9c77f18b0dad22f0 zh: 8894cf2878fe90db @@ -1893,11 +1917,11 @@ en: ba73fb10b7e2ffc9 zh: 8d5c97e63e5f16da /session-persistence-event-catalog/resolved-persistence-types/type-deliverables-presented: - en: ec7e9f13864db847 - zh: 694e948cd52387ea + en: 6912bd6d98b2a5a2 + zh: cd7e29afe0bf3b5c /session-persistence-event-catalog/resolved-persistence-types/type-developer-message: - en: 73514ab7122858e8 - zh: 335ebf8ba5454d7e + en: e148a0cbc33ef953 + zh: 1e27596468a7be7d /session-persistence-event-catalog/resolved-persistence-types/type-feedback-message-delete: en: 204561e2311db97e zh: 77264396bd5e25c4 @@ -1962,11 +1986,11 @@ en: acf7e21e4d21ebff zh: fb83a853634d204a /session-persistence-event-catalog/resolved-persistence-types/type-session-title: - en: 8dd08982ac65646b - zh: 3f22850e3160ed36 + en: 16a98c9a668d6d6d + zh: ccde0b3c75cd23eb /session-persistence-event-catalog/resolved-persistence-types/type-session-title-llm-request: - en: 48c9d78076942c91 - zh: 6cafb7732d3a1258 + en: 30496330748c4e76 + zh: 9315f63b0374792b /session-persistence-event-catalog/resolved-persistence-types/type-step-end: en: 7fc233c4a569dae2 zh: bc2b3fed4a397c00 @@ -2043,11 +2067,11 @@ en: 1b045de96172be4b zh: 8a15c6f35b576b37 /session-persistence-event-catalog/resolved-persistence-types/type-usage: - en: 5e70d314feb1b86b - zh: b98f69d4d8442c14 + en: 6a0d59f657768102 + zh: 8c6fd33a9158c931 /session-persistence-event-catalog/resolved-persistence-types/type-user-message: - en: 4f793420e83a748e - zh: 70a4cb82e27965dd + en: 7d9b6e71730faa5b + zh: c1f1a417d47c0f4e /session-persistence-event-catalog/resolved-persistence-types/type-web-deepseek-search-llm-request: en: f109997e295af37d zh: b2dcfd599f73b61c diff --git a/docs/persistence-catalog.md b/docs/persistence-catalog.md index 989987d5df..fb793c2f3e 100644 --- a/docs/persistence-catalog.md +++ b/docs/persistence-catalog.md @@ -21,7 +21,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover | `JsonlHeaderLine` | header | `18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f` | [`HeaderLine`](#persistence-type-sha256-18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f) | | `SessionEventEnvelope` | envelope | `5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc` | [`union (3 variants)`](#persistence-type-sha256-5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc) | | `event:agent-preset/selected` | event | `a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b` | [`{ type: "agent-preset/selected" }`](#persistence-type-sha256-a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b) | -| `event:agent/inbox/spliced` | event | `6178f1edcb23f361f2a6cb2c859b1c2187220d5695ece4a3400e0d92845a7178` | [`{ type: "agent/inbox/spliced" }`](#persistence-type-sha256-6178f1edcb23f361f2a6cb2c859b1c2187220d5695ece4a3400e0d92845a7178) | +| `event:agent/inbox/spliced` | event | `f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c` | [`{ type: "agent/inbox/spliced" }`](#persistence-type-sha256-f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c) | | `event:approval/asked` | event | `3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1` | [`{ type: "approval/asked" }`](#persistence-type-sha256-3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1) | | `event:approval/decided` | event | `bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3` | [`{ type: "approval/decided" }`](#persistence-type-sha256-bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3) | | `event:approval/policy` | event | `26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78` | [`{ type: "approval/policy" }`](#persistence-type-sha256-26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78) | @@ -34,7 +34,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover | `event:compaction/start` | event | `db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965` | [`{ type: "compaction/start" }`](#persistence-type-sha256-db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965) | | `event:compaction/summary` | event | `e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602` | [`{ type: "compaction/summary" }`](#persistence-type-sha256-e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602) | | `event:deliverables/presented` | event | `13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176` | [`{ type: "deliverables/presented" }`](#persistence-type-sha256-13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176) | -| `event:developer/message` | event | `186159f5f6f67a0b8cd095b8fe55bef42d4f25ca1a1c248f859867af2ece0467` | [`{ type: "developer/message" }`](#persistence-type-sha256-186159f5f6f67a0b8cd095b8fe55bef42d4f25ca1a1c248f859867af2ece0467) | +| `event:developer/message` | event | `e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321` | [`{ type: "developer/message" }`](#persistence-type-sha256-e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321) | | `event:feedback/message-delete` | event | `3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796` | [`{ type: "feedback/message-delete" }`](#persistence-type-sha256-3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796) | | `event:feedback/message-put` | event | `b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf` | [`{ type: "feedback/message-put" }`](#persistence-type-sha256-b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf) | | `event:feedback/record` | event | `b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8` | [`{ type: "feedback/record" }`](#persistence-type-sha256-b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8) | @@ -54,7 +54,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover | `event:session-log-deepseek/delivery-accepted` | event | `d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9` | [`{ type: "session-log-deepseek/delivery-accepted" }`](#persistence-type-sha256-d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9) | | `event:session/end-seed` | event | `5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e` | [`{ type: "session/end-seed" }`](#persistence-type-sha256-5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e) | | `event:session/title` | event | `1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5` | [`{ type: "session/title" }`](#persistence-type-sha256-1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5) | -| `event:session/title-llm-request` | event | `ae84c5e493f94acc63cfb70389073ba616ed7ae7aa4fadde048bdcc64d49bb46` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-ae84c5e493f94acc63cfb70389073ba616ed7ae7aa4fadde048bdcc64d49bb46) | +| `event:session/title-llm-request` | event | `5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510) | | `event:step/end` | event | `e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05` | [`{ type: "step/end" }`](#persistence-type-sha256-e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05) | | `event:step/start` | event | `4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986` | [`{ type: "step/start" }`](#persistence-type-sha256-4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986) | | `event:subagent/catalog` | event | `3abae7324356f155cb42450c00b806d134ec93bd6439d2063b8d724162d58604` | [`{ type: "subagent/catalog" }`](#persistence-type-sha256-3abae7324356f155cb42450c00b806d134ec93bd6439d2063b8d724162d58604) | @@ -76,7 +76,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover | `event:tool/result` | event | `7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397` | [`{ type: "tool/result" }`](#persistence-type-sha256-7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397) | | `event:turn/end` | event | `0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce` | [`{ type: "turn/end" }`](#persistence-type-sha256-0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce) | | `event:turn/start` | event | `aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8` | [`{ type: "turn/start" }`](#persistence-type-sha256-aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8) | -| `event:user/message` | event | `b83ed1b1cfffbd7bd5cca06ea72e44be57beb68b39e5a96660ce42a9e21aa411` | [`{ type: "user/message" }`](#persistence-type-sha256-b83ed1b1cfffbd7bd5cca06ea72e44be57beb68b39e5a96660ce42a9e21aa411) | +| `event:user/message` | event | `83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628` | [`{ type: "user/message" }`](#persistence-type-sha256-83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628) | | `event:web/deepseek-search-llm-request` | event | `cf6e3aaf1e2de6480aa0157730a41b9a492108a55304100b0f7e112711dd4331` | [`{ type: "web/deepseek-search-llm-request" }`](#persistence-type-sha256-cf6e3aaf1e2de6480aa0157730a41b9a492108a55304100b0f7e112711dd4331) | | `event:workspace/changes` | event | `e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72` | [`{ type: "workspace/changes" }`](#persistence-type-sha256-e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72) | @@ -1517,6 +1517,14 @@ SHA-256: `aff60ef947c18b012644852bdd0d5bfe72065e061dd563787c76c646b4a10f6b` `"claude-code"` + + +### `"claude-code-mods"` + +SHA-256: `edcdb9e0a1abce6f50580bbc9d8ac8b25082f3e6312281ec125d81e6b8e8dd40` + +`"claude-code-mods"` + ### `"clear"` @@ -3223,7 +3231,7 @@ Sources: [`packages/web/web-search-deepseek/src/provider.ts:55`](../packages/web | `body` | required | [`{ max_tokens, messages, model, tools }`](#persistence-type-sha256-930a6567a10bb62ddd157bd8abdf4b182810c8c3b49d91309b3b009a5fed9731) | | `endpoint` | required | `string` | - + @@ -3231,7 +3239,7 @@ Sources: [`packages/web/web-search-deepseek/src/provider.ts:55`](../packages/web ### `DeveloperMessage` -SHA-256: `1020bbc3c744c060cd31c0891ae79b9e4c3e0adfa3f0be49869309b34e9ef200` +SHA-256: `6bad5e2ed590f9440817fa6f5b5e3b94acd677fc5169a931238b056e5b3057f9` Sources: [`packages/llm/llm/src/message.ts:157`](../packages/llm/llm/src/message.ts) @@ -3240,11 +3248,11 @@ Sources: [`packages/llm/llm/src/message.ts:157`](../packages/llm/llm/src/message | `content` | required | [`ContentBlock[]`](#persistence-type-sha256-b52f215a6cd691058a3413c12f161fa3b563a2f4895adb3df6f744ec4b0a6ef1) | | `id` | required | `string` | | `role` | required | `"developer"` | -| `source` | required | [`MessageSource`](#persistence-type-sha256-26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116) | +| `source` | required | [`MessageSource`](#persistence-type-sha256-fb0eadf82093a66bbbd71c8df0d0fd27f251249947693b57fb930d97022b2477) | Source compatibility: `source` — `session-source-attribution` v1; `session.developer-message.source`; `kind`; `preserve`. -Attribution-only additions: `tmux-context`, `user-question-reply`. +Attribution-only additions: `claude-code-mods`, `tmux-context`, `user-question-reply`. @@ -3857,7 +3865,7 @@ Sources: [`packages/llm/llm-retry/src/types.ts:43`](../packages/llm/llm-retry/sr | `step` | required | `number` | | `turn` | required | `number` | - + @@ -3865,17 +3873,17 @@ Sources: [`packages/llm/llm-retry/src/types.ts:43`](../packages/llm/llm-retry/sr ### `Message` -SHA-256: `cb7b863ddb7b3eab5f18f216602feba25f47b456e73e4e72b6bcc259875c6dab` +SHA-256: `fbf5aca9de0e38e9426bea40bd94711e0e115fcdf282b8f8bbe4d3dcde26bfed` Sources: [`packages/llm/llm/src/message.ts:196`](../packages/llm/llm/src/message.ts) One of: - [`ToolResultMessage`](#persistence-type-sha256-4b259f7f8f0a44ad5e90b70a73553ced1df27012d126921d397ff403b6c86731) -- [`DeveloperMessage`](#persistence-type-sha256-1020bbc3c744c060cd31c0891ae79b9e4c3e0adfa3f0be49869309b34e9ef200) -- [`UserMessage`](#persistence-type-sha256-cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd) -- [`SystemMessage`](#persistence-type-sha256-09f3f8dc417f278257202714132f98ba7b52957d7b558c4a04456603890eb363) +- [`DeveloperMessage`](#persistence-type-sha256-6bad5e2ed590f9440817fa6f5b5e3b94acd677fc5169a931238b056e5b3057f9) +- [`UserMessage`](#persistence-type-sha256-a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47) - [`AssistantMessage`](#persistence-type-sha256-fa87537f4a9c1e25602990a8bf686b383e9e00352ec8946e16ae24ba7c1625be) +- [`SystemMessage`](#persistence-type-sha256-09f3f8dc417f278257202714132f98ba7b52957d7b558c4a04456603890eb363) @@ -3950,7 +3958,7 @@ One of: - `"negative"` - `"positive"` - + @@ -3958,7 +3966,7 @@ One of: ### `MessageSource` -SHA-256: `26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116` +SHA-256: `fb0eadf82093a66bbbd71c8df0d0fd27f251249947693b57fb930d97022b2477` Sources: [`packages/llm/llm/src/message.ts:136`](../packages/llm/llm/src/message.ts) @@ -3966,6 +3974,13 @@ Sources: [`packages/llm/llm/src/message.ts:136`](../packages/llm/llm/src/message |---|---|---|---| | `agent-instructions` | `"instructions"` | `changes`: [`AgentInstructionChange[]`](#persistence-type-sha256-2ab12a0e345658f9342816d773fe610f049982bcad82631f43b9205abe43e5ee) | [`AgentInstructionSource`](#persistence-type-sha256-6f22a9d02b9a1f1157f3b0e7bdccd7e7b12b085c4efdb709dfc74a465cd4e590) | | `agent-message` | `"relay"` | `senderSessionId`: `string` | [`AgentMessageSource`](#persistence-type-sha256-4a9bef9f27a942520cc2279ca7dd3082556e4bff543f66b5c0333e1bbed1cd86) | +| `claude-code-mods` | `"catalog"` | none | [`{ kind: "claude-code-mods", form: "catalog" }`](#persistence-type-sha256-5e78e8bebe50889028294fa6918b9bd340a5f1a26f12418d47a2b1cfa637b749) | +| `claude-code-mods` | `"instructions"` | none | [`{ kind: "claude-code-mods", form: "instructions" }`](#persistence-type-sha256-a66a864d5c1ed32387c7b45603e567d9ed2fc5559a96926fdebfff7c753f7c0f) | +| `claude-code-mods` | `"notice"` | `summary`: `string` | [`{ kind: "claude-code-mods", form: "notice" }`](#persistence-type-sha256-2e285da62c0ed94e6e4bfbb9161e4090502ff95e379e753ebabe745e60eb7d5c) | +| `claude-code-mods` | `"recall"` | none | [`{ kind: "claude-code-mods", form: "recall" }`](#persistence-type-sha256-ccea69c3f118930ca9679e93a4347978995180ec87b997e2cb075ad5caac4a38) | +| `claude-code-mods` | `"relay"` | none | [`{ kind: "claude-code-mods", form: "relay" }`](#persistence-type-sha256-93845ea41cc823c2ae619309ab2b4465363431b15a04d7d7877b65845ae44c3a) | +| `claude-code-mods` | `"snapshot"` | `sections`: [`ContextSnapshotSection[]`](#persistence-type-sha256-c748fb42c80a120038a8c587af80ec39924803c401364db11c8974d5bf6d0262) | [`{ kind: "claude-code-mods", form: "snapshot" }`](#persistence-type-sha256-d17f8336e133bafdcbf95836c93e65aa3930558be3d676ae91aaa6b1e399c9fa) | +| `claude-code-mods` | not declared | none | [`{ kind: "claude-code-mods" }`](#persistence-type-sha256-c52b232ec5d7f7dec08d3f6059318e3e67e753e9028cdca607ddc53b90f44a08) | | `compact-checkpoint` | not declared | `compactionId`: `string` | [`CompactionCheckpointSource`](#persistence-type-sha256-31907de9563751822af6d9be02bafaf26faa098bbd1cb412524134d577cc1648) | | `cordis-host-runner` | not declared | none | [`{ kind: "cordis-host-runner" }`](#persistence-type-sha256-02b2c23f2d94b028c9588a8faace3fce1217405db346da3389368b495457bf79) | | `dsh-session-title-llm` | `"catalog"` | none | [`{ kind: "dsh-session-title-llm", form: "catalog" }`](#persistence-type-sha256-80677c8c8b804189a0fde8aa70dd4ad7af8bb12dfd03027e8e023c8592c838bc) | @@ -4075,13 +4090,13 @@ Sources: [`packages/llm/llm/src/message.ts:136`](../packages/llm/llm/src/message | `user-question-reply` | not declared | `callId`: `string`; `outcome`: `"answered"` | [`{ kind: "user-question-reply" }`](#persistence-type-sha256-c55827fba7018198772c50e37cb7d3f60a3d8bb6e94b07de72dee0d9f8968005) | | `webhook` | `"notice"` | `deliveryId`: `string`; `provider`: `string`; `ruleId`: `string`; `source`: `string`; `summary`: `string` | [`{ kind: "webhook", form: "notice" }`](#persistence-type-sha256-9edc162949abf93bd5ff049191b345454ba104f583937ce5580fb50f067d2b94) | - + ### `Message[]` -SHA-256: `3f42f45cbb8a06586d247e5f3763dc02c8d31a145a9a3b82fabcd101e7e224cc` +SHA-256: `ce7cb492827bba51c9c8cda4cb55f67fe9348944cabdced798c3b7baea6090d6` -Array of [`Message`](#persistence-type-sha256-cb7b863ddb7b3eab5f18f216602feba25f47b456e73e4e72b6bcc259875c6dab). +Array of [`Message`](#persistence-type-sha256-fbf5aca9de0e38e9426bea40bd94711e0e115fcdf282b8f8bbe4d3dcde26bfed). @@ -4448,7 +4463,7 @@ Sources: [`packages/session/session-title/src/types.ts:41`](../packages/session/ | `source` | required | [`SessionTitleSource`](#persistence-type-sha256-30d5a537b296685770b1241c4b4bbde19d5a31ff81aa23df73fab5e90b8fc65f) | | `title` | required | `string` | - + @@ -4456,7 +4471,7 @@ Sources: [`packages/session/session-title/src/types.ts:41`](../packages/session/ ### `SessionTitleLlmRequestEventData` -SHA-256: `fe3cdc6f9050584cea4a6d6998c31334bff7899f5ff1095082a44fdf3b47e996` +SHA-256: `9d38c01a12d75b16898d0ac01f3bd47e8d4bb1e1a074f22926aaf30d457165c3` Sources: [`packages/session/session-title-llm/src/index.ts:34`](../packages/session/session-title-llm/src/index.ts) @@ -4464,7 +4479,7 @@ Sources: [`packages/session/session-title-llm/src/index.ts:34`](../packages/sess |---|---|---| | `maxTokens` | required | `number` | | `messageSeqs` | required | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) | -| `messages` | required | [`Message[]`](#persistence-type-sha256-3f42f45cbb8a06586d247e5f3763dc02c8d31a145a9a3b82fabcd101e7e224cc) | +| `messages` | required | [`Message[]`](#persistence-type-sha256-ce7cb492827bba51c9c8cda4cb55f67fe9348944cabdced798c3b7baea6090d6) | | `route` | required | [`SessionTitleModelIdentity`](#persistence-type-sha256-07e1a58c58b593f507b8e7f25723bb0aa42baae6e00f5ecfddf28cb1e64d3235) | | `system` | required | `string` | | `titleProvider` | required | `string` | @@ -5118,7 +5133,7 @@ Sources: [`packages/core/session/src/types.ts:232`](../packages/core/session/src | `interrupted` | not declared | none | [`{ kind: "interrupted" }`](#persistence-type-sha256-56b7c047694875a843ba020dfea3c8380d54ae4ff88ab349e72004dc21e07c23) | | `max-tokens` | not declared | none | [`{ kind: "max-tokens" }`](#persistence-type-sha256-ab7ec5f9eb2c6dfacca5da4c37cf7ba85d04172053ee6b1ebf1709ffcd99c1a7) | - + @@ -5126,7 +5141,7 @@ Sources: [`packages/core/session/src/types.ts:232`](../packages/core/session/src ### `UserMessage` -SHA-256: `cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd` +SHA-256: `a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47` Sources: [`packages/llm/llm/src/message.ts:162`](../packages/llm/llm/src/message.ts) @@ -5135,19 +5150,19 @@ Sources: [`packages/llm/llm/src/message.ts:162`](../packages/llm/llm/src/message | `content` | required | [`ContentBlock[]`](#persistence-type-sha256-b52f215a6cd691058a3413c12f161fa3b563a2f4895adb3df6f744ec4b0a6ef1) | | `id` | required | `string` | | `role` | required | `"user"` | -| `source` | required | [`MessageSource`](#persistence-type-sha256-26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116) | +| `source` | required | [`MessageSource`](#persistence-type-sha256-fb0eadf82093a66bbbd71c8df0d0fd27f251249947693b57fb930d97022b2477) | Source compatibility: `source` — `session-source-attribution` v1; `session.user-message.source`; `kind`; `preserve`. -Attribution-only additions: `tmux-context`, `user-question-reply`. +Attribution-only additions: `claude-code-mods`, `tmux-context`, `user-question-reply`. - + ### `UserMessage[]` -SHA-256: `5a92f12db2059c282cdea45b6845c65e6f4a95dd3d649aeddbde6f9ae03c561f` +SHA-256: `35bc598be70e0b9646ccdf5629b55bb80ba5a9cf49723ecf49957abe0a12cf99` -Array of [`UserMessage`](#persistence-type-sha256-cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd). +Array of [`UserMessage`](#persistence-type-sha256-a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47). @@ -5990,18 +6005,18 @@ Sources: [`packages/core/session/src/types.ts:390`](../packages/core/session/src | `reason` | required | [`RequestHeaderReason`](#persistence-type-sha256-530da6c0267813bb4511af72883cc518834e624e3b3220a7062955fd57a788a6) | | `startsSeries` | optional | `true` | - + ### `{ headerSeq?, message, step, turn }` -SHA-256: `a5d80b5a2f3a6bbaf8f0334126632b78f94d8346122c68e8122ea89906d63d26` +SHA-256: `f2039f3c7aff513cfe2c30ec0e8a2677796d60b280a0b627c36226788a013132` Sources: [`packages/core/session/src/types.ts:311`](../packages/core/session/src/types.ts) | Property | Presence | Type | |---|---|---| | `headerSeq` | optional | `number` | -| `message` | required | [`DeveloperMessage`](#persistence-type-sha256-1020bbc3c744c060cd31c0891ae79b9e4c3e0adfa3f0be49869309b34e9ef200) | +| `message` | required | [`DeveloperMessage`](#persistence-type-sha256-6bad5e2ed590f9440817fa6f5b5e3b94acd677fc5169a931238b056e5b3057f9) | | `step` | required | `number` | | `turn` | required | `number` | @@ -6085,17 +6100,17 @@ Sources: [`packages/core/session/src/types.ts:427`](../packages/core/session/src |---|---|---| | `inherited` | optional | `true` | - + ### `{ inserted, outcome?, removedCount?, start, … }` -SHA-256: `149c5a77529cb0dc06f5ceb6273069b7606174d1e7893b5f9b1071c15306b846` +SHA-256: `40056a392eceb4445d649205e99900fe770ce3d3beb4b10301c1e50d2095e9ef` Sources: [`packages/core/agent/src/types.ts:96`](../packages/core/agent/src/types.ts) | Property | Presence | Type | |---|---|---| -| `inserted` | required | [`UserMessage[]`](#persistence-type-sha256-5a92f12db2059c282cdea45b6845c65e6f4a95dd3d649aeddbde6f9ae03c561f) | +| `inserted` | required | [`UserMessage[]`](#persistence-type-sha256-35bc598be70e0b9646ccdf5629b55bb80ba5a9cf49723ecf49957abe0a12cf99) | | `outcome` | optional | `"canceled"` | | `removedCount` | optional | `number` | | `start` | required | `number` | @@ -6143,6 +6158,84 @@ Sources: [`packages/core/session/src/types.ts:206`](../packages/core/session/src |---|---|---| | `kind` | required | `"blocked"` | + + +### `{ kind: "claude-code-mods" }` + +SHA-256: `c52b232ec5d7f7dec08d3f6059318e3e67e753e9028cdca607ddc53b90f44a08` + +| Property | Presence | Type | +|---|---|---| +| `kind` | required | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "catalog" }` + +SHA-256: `5e78e8bebe50889028294fa6918b9bd340a5f1a26f12418d47a2b1cfa637b749` + +| Property | Presence | Type | +|---|---|---| +| `form` | required | `"catalog"` | +| `kind` | required | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "instructions" }` + +SHA-256: `a66a864d5c1ed32387c7b45603e567d9ed2fc5559a96926fdebfff7c753f7c0f` + +| Property | Presence | Type | +|---|---|---| +| `form` | required | `"instructions"` | +| `kind` | required | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "notice" }` + +SHA-256: `2e285da62c0ed94e6e4bfbb9161e4090502ff95e379e753ebabe745e60eb7d5c` + +| Property | Presence | Type | +|---|---|---| +| `form` | required | `"notice"` | +| `kind` | required | `"claude-code-mods"` | +| `summary` | required | `string` | + + + +### `{ kind: "claude-code-mods", form: "recall" }` + +SHA-256: `ccea69c3f118930ca9679e93a4347978995180ec87b997e2cb075ad5caac4a38` + +| Property | Presence | Type | +|---|---|---| +| `form` | required | `"recall"` | +| `kind` | required | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "relay" }` + +SHA-256: `93845ea41cc823c2ae619309ab2b4465363431b15a04d7d7877b65845ae44c3a` + +| Property | Presence | Type | +|---|---|---| +| `form` | required | `"relay"` | +| `kind` | required | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "snapshot" }` + +SHA-256: `d17f8336e133bafdcbf95836c93e65aa3930558be3d676ae91aaa6b1e399c9fa` + +| Property | Presence | Type | +|---|---|---| +| `form` | required | `"snapshot"` | +| `kind` | required | `"claude-code-mods"` | +| `sections` | required | [`ContextSnapshotSection[]`](#persistence-type-sha256-c748fb42c80a120038a8c587af80ec39924803c401364db11c8974d5bf6d0262) | + ### `{ kind: "completed" }` @@ -7643,17 +7736,17 @@ SHA-256: `a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b` | `time` | required | `number` | | `type` | required | `"agent-preset/selected"` | - + ### `{ type: "agent/inbox/spliced" }` -SHA-256: `6178f1edcb23f361f2a6cb2c859b1c2187220d5695ece4a3400e0d92845a7178` +SHA-256: `f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c` | Property | Presence | Type | |---|---|---| -| `data` | required | [`{ inserted, outcome?, removedCount?, start, … }`](#persistence-type-sha256-149c5a77529cb0dc06f5ceb6273069b7606174d1e7893b5f9b1071c15306b846) | +| `data` | required | [`{ inserted, outcome?, removedCount?, start, … }`](#persistence-type-sha256-40056a392eceb4445d649205e99900fe770ce3d3beb4b10301c1e50d2095e9ef) | | `ignorable` | optional | `true` | | `seq` | required | `number` | | `time` | required | `number` | @@ -7894,17 +7987,17 @@ SHA-256: `13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176` | `time` | required | `number` | | `type` | required | `"deliverables/presented"` | - + ### `{ type: "developer/message" }` -SHA-256: `186159f5f6f67a0b8cd095b8fe55bef42d4f25ca1a1c248f859867af2ece0467` +SHA-256: `e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321` | Property | Presence | Type | |---|---|---| -| `data` | required | [`{ headerSeq?, message, step, turn }`](#persistence-type-sha256-a5d80b5a2f3a6bbaf8f0334126632b78f94d8346122c68e8122ea89906d63d26) | +| `data` | required | [`{ headerSeq?, message, step, turn }`](#persistence-type-sha256-f2039f3c7aff513cfe2c30ec0e8a2677796d60b280a0b627c36226788a013132) | | `ignorable` | optional | `true` | | `seq` | required | `number` | | `sourceEventSeqs` | optional | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) | @@ -8260,17 +8353,17 @@ SHA-256: `1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5` | `time` | required | `number` | | `type` | required | `"session/title"` | - + ### `{ type: "session/title-llm-request" }` -SHA-256: `ae84c5e493f94acc63cfb70389073ba616ed7ae7aa4fadde048bdcc64d49bb46` +SHA-256: `5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510` | Property | Presence | Type | |---|---|---| -| `data` | required | [`SessionTitleLlmRequestEventData`](#persistence-type-sha256-fe3cdc6f9050584cea4a6d6998c31334bff7899f5ff1095082a44fdf3b47e996) | +| `data` | required | [`SessionTitleLlmRequestEventData`](#persistence-type-sha256-9d38c01a12d75b16898d0ac01f3bd47e8d4bb1e1a074f22926aaf30d457165c3) | | `ignorable` | optional | `true` | | `seq` | required | `number` | | `time` | required | `number` | @@ -8693,17 +8786,17 @@ Sources: [`packages/llm/llm/src/types.ts:458`](../packages/llm/llm/src/types.ts) | `type` | required | `"usage"` | | `usage` | required | [`TokenUsage`](#persistence-type-sha256-fec5442b9ee4122e10514d81d5a43648003fe8bfb7ee2ba50c0cdbdd383ed394) | - + ### `{ type: "user/message" }` -SHA-256: `b83ed1b1cfffbd7bd5cca06ea72e44be57beb68b39e5a96660ce42a9e21aa411` +SHA-256: `83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628` | Property | Presence | Type | |---|---|---| -| `data` | required | [`UserMessage`](#persistence-type-sha256-cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd) | +| `data` | required | [`UserMessage`](#persistence-type-sha256-a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47) | | `ignorable` | optional | `true` | | `seq` | required | `number` | | `sourceEventSeqs` | optional | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) | diff --git a/docs/persistence-catalog.zh.md b/docs/persistence-catalog.zh.md index 295160c5d0..9c12710f5e 100644 --- a/docs/persistence-catalog.zh.md +++ b/docs/persistence-catalog.zh.md @@ -23,7 +23,7 @@ | `JsonlHeaderLine` | header | `18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f` | [`HeaderLine`](#persistence-type-sha256-18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f) | | `SessionEventEnvelope` | envelope | `5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc` | [`union (3 variants)`](#persistence-type-sha256-5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc) | | `event:agent-preset/selected` | event | `a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b` | [`{ type: "agent-preset/selected" }`](#persistence-type-sha256-a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b) | -| `event:agent/inbox/spliced` | event | `6178f1edcb23f361f2a6cb2c859b1c2187220d5695ece4a3400e0d92845a7178` | [`{ type: "agent/inbox/spliced" }`](#persistence-type-sha256-6178f1edcb23f361f2a6cb2c859b1c2187220d5695ece4a3400e0d92845a7178) | +| `event:agent/inbox/spliced` | event | `f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c` | [`{ type: "agent/inbox/spliced" }`](#persistence-type-sha256-f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c) | | `event:approval/asked` | event | `3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1` | [`{ type: "approval/asked" }`](#persistence-type-sha256-3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1) | | `event:approval/decided` | event | `bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3` | [`{ type: "approval/decided" }`](#persistence-type-sha256-bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3) | | `event:approval/policy` | event | `26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78` | [`{ type: "approval/policy" }`](#persistence-type-sha256-26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78) | @@ -36,7 +36,7 @@ | `event:compaction/start` | event | `db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965` | [`{ type: "compaction/start" }`](#persistence-type-sha256-db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965) | | `event:compaction/summary` | event | `e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602` | [`{ type: "compaction/summary" }`](#persistence-type-sha256-e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602) | | `event:deliverables/presented` | event | `13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176` | [`{ type: "deliverables/presented" }`](#persistence-type-sha256-13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176) | -| `event:developer/message` | event | `186159f5f6f67a0b8cd095b8fe55bef42d4f25ca1a1c248f859867af2ece0467` | [`{ type: "developer/message" }`](#persistence-type-sha256-186159f5f6f67a0b8cd095b8fe55bef42d4f25ca1a1c248f859867af2ece0467) | +| `event:developer/message` | event | `e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321` | [`{ type: "developer/message" }`](#persistence-type-sha256-e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321) | | `event:feedback/message-delete` | event | `3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796` | [`{ type: "feedback/message-delete" }`](#persistence-type-sha256-3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796) | | `event:feedback/message-put` | event | `b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf` | [`{ type: "feedback/message-put" }`](#persistence-type-sha256-b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf) | | `event:feedback/record` | event | `b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8` | [`{ type: "feedback/record" }`](#persistence-type-sha256-b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8) | @@ -56,7 +56,7 @@ | `event:session-log-deepseek/delivery-accepted` | event | `d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9` | [`{ type: "session-log-deepseek/delivery-accepted" }`](#persistence-type-sha256-d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9) | | `event:session/end-seed` | event | `5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e` | [`{ type: "session/end-seed" }`](#persistence-type-sha256-5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e) | | `event:session/title` | event | `1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5` | [`{ type: "session/title" }`](#persistence-type-sha256-1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5) | -| `event:session/title-llm-request` | event | `ae84c5e493f94acc63cfb70389073ba616ed7ae7aa4fadde048bdcc64d49bb46` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-ae84c5e493f94acc63cfb70389073ba616ed7ae7aa4fadde048bdcc64d49bb46) | +| `event:session/title-llm-request` | event | `5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510) | | `event:step/end` | event | `e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05` | [`{ type: "step/end" }`](#persistence-type-sha256-e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05) | | `event:step/start` | event | `4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986` | [`{ type: "step/start" }`](#persistence-type-sha256-4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986) | | `event:subagent/catalog` | event | `3abae7324356f155cb42450c00b806d134ec93bd6439d2063b8d724162d58604` | [`{ type: "subagent/catalog" }`](#persistence-type-sha256-3abae7324356f155cb42450c00b806d134ec93bd6439d2063b8d724162d58604) | @@ -78,7 +78,7 @@ | `event:tool/result` | event | `7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397` | [`{ type: "tool/result" }`](#persistence-type-sha256-7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397) | | `event:turn/end` | event | `0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce` | [`{ type: "turn/end" }`](#persistence-type-sha256-0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce) | | `event:turn/start` | event | `aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8` | [`{ type: "turn/start" }`](#persistence-type-sha256-aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8) | -| `event:user/message` | event | `b83ed1b1cfffbd7bd5cca06ea72e44be57beb68b39e5a96660ce42a9e21aa411` | [`{ type: "user/message" }`](#persistence-type-sha256-b83ed1b1cfffbd7bd5cca06ea72e44be57beb68b39e5a96660ce42a9e21aa411) | +| `event:user/message` | event | `83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628` | [`{ type: "user/message" }`](#persistence-type-sha256-83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628) | | `event:web/deepseek-search-llm-request` | event | `cf6e3aaf1e2de6480aa0157730a41b9a492108a55304100b0f7e112711dd4331` | [`{ type: "web/deepseek-search-llm-request" }`](#persistence-type-sha256-cf6e3aaf1e2de6480aa0157730a41b9a492108a55304100b0f7e112711dd4331) | | `event:workspace/changes` | event | `e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72` | [`{ type: "workspace/changes" }`](#persistence-type-sha256-e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72) | @@ -1519,6 +1519,14 @@ SHA-256: `aff60ef947c18b012644852bdd0d5bfe72065e061dd563787c76c646b4a10f6b` `"claude-code"` + + +### `"claude-code-mods"` + +SHA-256: `edcdb9e0a1abce6f50580bbc9d8ac8b25082f3e6312281ec125d81e6b8e8dd40` + +`"claude-code-mods"` + ### `"clear"` @@ -3225,7 +3233,7 @@ SHA-256: `2517ba143a271508d3ca35126d5aca8f7f1facf5aaddce4adf011832042fa2b9` | `body` | 必需 | [`{ max_tokens, messages, model, tools }`](#persistence-type-sha256-930a6567a10bb62ddd157bd8abdf4b182810c8c3b49d91309b3b009a5fed9731) | | `endpoint` | 必需 | `string` | - + @@ -3233,7 +3241,7 @@ SHA-256: `2517ba143a271508d3ca35126d5aca8f7f1facf5aaddce4adf011832042fa2b9` ### `DeveloperMessage` -SHA-256: `1020bbc3c744c060cd31c0891ae79b9e4c3e0adfa3f0be49869309b34e9ef200` +SHA-256: `6bad5e2ed590f9440817fa6f5b5e3b94acd677fc5169a931238b056e5b3057f9` 来源:[`packages/llm/llm/src/message.ts:157`](../packages/llm/llm/src/message.ts) @@ -3242,11 +3250,11 @@ SHA-256: `1020bbc3c744c060cd31c0891ae79b9e4c3e0adfa3f0be49869309b34e9ef200` | `content` | 必需 | [`ContentBlock[]`](#persistence-type-sha256-b52f215a6cd691058a3413c12f161fa3b563a2f4895adb3df6f744ec4b0a6ef1) | | `id` | 必需 | `string` | | `role` | 必需 | `"developer"` | -| `source` | 必需 | [`MessageSource`](#persistence-type-sha256-26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116) | +| `source` | 必需 | [`MessageSource`](#persistence-type-sha256-fb0eadf82093a66bbbd71c8df0d0fd27f251249947693b57fb930d97022b2477) | 消息源兼容策略:`source` — `session-source-attribution` v1; `session.developer-message.source`; `kind`; `preserve`. -仅表示归属的新增 kind:`tmux-context`, `user-question-reply`. +仅表示归属的新增 kind:`claude-code-mods`, `tmux-context`, `user-question-reply`. @@ -3859,7 +3867,7 @@ SHA-256: `6f72ece8f317cd080c4f20a93f1bf709965f5b81ac4669528f5a023191cfcf20` | `step` | 必需 | `number` | | `turn` | 必需 | `number` | - + @@ -3867,17 +3875,17 @@ SHA-256: `6f72ece8f317cd080c4f20a93f1bf709965f5b81ac4669528f5a023191cfcf20` ### `Message` -SHA-256: `cb7b863ddb7b3eab5f18f216602feba25f47b456e73e4e72b6bcc259875c6dab` +SHA-256: `fbf5aca9de0e38e9426bea40bd94711e0e115fcdf282b8f8bbe4d3dcde26bfed` 来源:[`packages/llm/llm/src/message.ts:196`](../packages/llm/llm/src/message.ts) 以下类型之一: - [`ToolResultMessage`](#persistence-type-sha256-4b259f7f8f0a44ad5e90b70a73553ced1df27012d126921d397ff403b6c86731) -- [`DeveloperMessage`](#persistence-type-sha256-1020bbc3c744c060cd31c0891ae79b9e4c3e0adfa3f0be49869309b34e9ef200) -- [`UserMessage`](#persistence-type-sha256-cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd) -- [`SystemMessage`](#persistence-type-sha256-09f3f8dc417f278257202714132f98ba7b52957d7b558c4a04456603890eb363) +- [`DeveloperMessage`](#persistence-type-sha256-6bad5e2ed590f9440817fa6f5b5e3b94acd677fc5169a931238b056e5b3057f9) +- [`UserMessage`](#persistence-type-sha256-a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47) - [`AssistantMessage`](#persistence-type-sha256-fa87537f4a9c1e25602990a8bf686b383e9e00352ec8946e16ae24ba7c1625be) +- [`SystemMessage`](#persistence-type-sha256-09f3f8dc417f278257202714132f98ba7b52957d7b558c4a04456603890eb363) @@ -3952,7 +3960,7 @@ SHA-256: `390eed1028aba02774f14408647acec10ec5516e97d4f15ecde2b9989d5a69f2` - `"negative"` - `"positive"` - + @@ -3960,7 +3968,7 @@ SHA-256: `390eed1028aba02774f14408647acec10ec5516e97d4f15ecde2b9989d5a69f2` ### `MessageSource` -SHA-256: `26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116` +SHA-256: `fb0eadf82093a66bbbd71c8df0d0fd27f251249947693b57fb930d97022b2477` 来源:[`packages/llm/llm/src/message.ts:136`](../packages/llm/llm/src/message.ts) @@ -3968,6 +3976,13 @@ SHA-256: `26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116` |---|---|---|---| | `agent-instructions` | `"instructions"` | `changes`: [`AgentInstructionChange[]`](#persistence-type-sha256-2ab12a0e345658f9342816d773fe610f049982bcad82631f43b9205abe43e5ee) | [`AgentInstructionSource`](#persistence-type-sha256-6f22a9d02b9a1f1157f3b0e7bdccd7e7b12b085c4efdb709dfc74a465cd4e590) | | `agent-message` | `"relay"` | `senderSessionId`: `string` | [`AgentMessageSource`](#persistence-type-sha256-4a9bef9f27a942520cc2279ca7dd3082556e4bff543f66b5c0333e1bbed1cd86) | +| `claude-code-mods` | `"catalog"` | 无 | [`{ kind: "claude-code-mods", form: "catalog" }`](#persistence-type-sha256-5e78e8bebe50889028294fa6918b9bd340a5f1a26f12418d47a2b1cfa637b749) | +| `claude-code-mods` | `"instructions"` | 无 | [`{ kind: "claude-code-mods", form: "instructions" }`](#persistence-type-sha256-a66a864d5c1ed32387c7b45603e567d9ed2fc5559a96926fdebfff7c753f7c0f) | +| `claude-code-mods` | `"notice"` | `summary`: `string` | [`{ kind: "claude-code-mods", form: "notice" }`](#persistence-type-sha256-2e285da62c0ed94e6e4bfbb9161e4090502ff95e379e753ebabe745e60eb7d5c) | +| `claude-code-mods` | `"recall"` | 无 | [`{ kind: "claude-code-mods", form: "recall" }`](#persistence-type-sha256-ccea69c3f118930ca9679e93a4347978995180ec87b997e2cb075ad5caac4a38) | +| `claude-code-mods` | `"relay"` | 无 | [`{ kind: "claude-code-mods", form: "relay" }`](#persistence-type-sha256-93845ea41cc823c2ae619309ab2b4465363431b15a04d7d7877b65845ae44c3a) | +| `claude-code-mods` | `"snapshot"` | `sections`: [`ContextSnapshotSection[]`](#persistence-type-sha256-c748fb42c80a120038a8c587af80ec39924803c401364db11c8974d5bf6d0262) | [`{ kind: "claude-code-mods", form: "snapshot" }`](#persistence-type-sha256-d17f8336e133bafdcbf95836c93e65aa3930558be3d676ae91aaa6b1e399c9fa) | +| `claude-code-mods` | 未声明 | 无 | [`{ kind: "claude-code-mods" }`](#persistence-type-sha256-c52b232ec5d7f7dec08d3f6059318e3e67e753e9028cdca607ddc53b90f44a08) | | `compact-checkpoint` | 未声明 | `compactionId`: `string` | [`CompactionCheckpointSource`](#persistence-type-sha256-31907de9563751822af6d9be02bafaf26faa098bbd1cb412524134d577cc1648) | | `cordis-host-runner` | 未声明 | 无 | [`{ kind: "cordis-host-runner" }`](#persistence-type-sha256-02b2c23f2d94b028c9588a8faace3fce1217405db346da3389368b495457bf79) | | `dsh-session-title-llm` | `"catalog"` | 无 | [`{ kind: "dsh-session-title-llm", form: "catalog" }`](#persistence-type-sha256-80677c8c8b804189a0fde8aa70dd4ad7af8bb12dfd03027e8e023c8592c838bc) | @@ -4077,13 +4092,13 @@ SHA-256: `26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116` | `user-question-reply` | 未声明 | `callId`: `string`; `outcome`: `"answered"` | [`{ kind: "user-question-reply" }`](#persistence-type-sha256-c55827fba7018198772c50e37cb7d3f60a3d8bb6e94b07de72dee0d9f8968005) | | `webhook` | `"notice"` | `deliveryId`: `string`; `provider`: `string`; `ruleId`: `string`; `source`: `string`; `summary`: `string` | [`{ kind: "webhook", form: "notice" }`](#persistence-type-sha256-9edc162949abf93bd5ff049191b345454ba104f583937ce5580fb50f067d2b94) | - + ### `Message[]` -SHA-256: `3f42f45cbb8a06586d247e5f3763dc02c8d31a145a9a3b82fabcd101e7e224cc` +SHA-256: `ce7cb492827bba51c9c8cda4cb55f67fe9348944cabdced798c3b7baea6090d6` -[`Message`](#persistence-type-sha256-cb7b863ddb7b3eab5f18f216602feba25f47b456e73e4e72b6bcc259875c6dab) 的数组。 +[`Message`](#persistence-type-sha256-fbf5aca9de0e38e9426bea40bd94711e0e115fcdf282b8f8bbe4d3dcde26bfed) 的数组。 @@ -4450,7 +4465,7 @@ SHA-256: `b56f6a885da3dc394c69a3e3cc5c0cfc4da15601b63fda4989eee8f40efc5be4` | `source` | 必需 | [`SessionTitleSource`](#persistence-type-sha256-30d5a537b296685770b1241c4b4bbde19d5a31ff81aa23df73fab5e90b8fc65f) | | `title` | 必需 | `string` | - + @@ -4458,7 +4473,7 @@ SHA-256: `b56f6a885da3dc394c69a3e3cc5c0cfc4da15601b63fda4989eee8f40efc5be4` ### `SessionTitleLlmRequestEventData` -SHA-256: `fe3cdc6f9050584cea4a6d6998c31334bff7899f5ff1095082a44fdf3b47e996` +SHA-256: `9d38c01a12d75b16898d0ac01f3bd47e8d4bb1e1a074f22926aaf30d457165c3` 来源:[`packages/session/session-title-llm/src/index.ts:34`](../packages/session/session-title-llm/src/index.ts) @@ -4466,7 +4481,7 @@ SHA-256: `fe3cdc6f9050584cea4a6d6998c31334bff7899f5ff1095082a44fdf3b47e996` |---|---|---| | `maxTokens` | 必需 | `number` | | `messageSeqs` | 必需 | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) | -| `messages` | 必需 | [`Message[]`](#persistence-type-sha256-3f42f45cbb8a06586d247e5f3763dc02c8d31a145a9a3b82fabcd101e7e224cc) | +| `messages` | 必需 | [`Message[]`](#persistence-type-sha256-ce7cb492827bba51c9c8cda4cb55f67fe9348944cabdced798c3b7baea6090d6) | | `route` | 必需 | [`SessionTitleModelIdentity`](#persistence-type-sha256-07e1a58c58b593f507b8e7f25723bb0aa42baae6e00f5ecfddf28cb1e64d3235) | | `system` | 必需 | `string` | | `titleProvider` | 必需 | `string` | @@ -5120,7 +5135,7 @@ SHA-256: `f1687f982fa3d41ef29e159feed154ecd13af4dd209acaee16038f223f2e9402` | `interrupted` | 未声明 | 无 | [`{ kind: "interrupted" }`](#persistence-type-sha256-56b7c047694875a843ba020dfea3c8380d54ae4ff88ab349e72004dc21e07c23) | | `max-tokens` | 未声明 | 无 | [`{ kind: "max-tokens" }`](#persistence-type-sha256-ab7ec5f9eb2c6dfacca5da4c37cf7ba85d04172053ee6b1ebf1709ffcd99c1a7) | - + @@ -5128,7 +5143,7 @@ SHA-256: `f1687f982fa3d41ef29e159feed154ecd13af4dd209acaee16038f223f2e9402` ### `UserMessage` -SHA-256: `cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd` +SHA-256: `a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47` 来源:[`packages/llm/llm/src/message.ts:162`](../packages/llm/llm/src/message.ts) @@ -5137,19 +5152,19 @@ SHA-256: `cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd` | `content` | 必需 | [`ContentBlock[]`](#persistence-type-sha256-b52f215a6cd691058a3413c12f161fa3b563a2f4895adb3df6f744ec4b0a6ef1) | | `id` | 必需 | `string` | | `role` | 必需 | `"user"` | -| `source` | 必需 | [`MessageSource`](#persistence-type-sha256-26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116) | +| `source` | 必需 | [`MessageSource`](#persistence-type-sha256-fb0eadf82093a66bbbd71c8df0d0fd27f251249947693b57fb930d97022b2477) | 消息源兼容策略:`source` — `session-source-attribution` v1; `session.user-message.source`; `kind`; `preserve`. -仅表示归属的新增 kind:`tmux-context`, `user-question-reply`. +仅表示归属的新增 kind:`claude-code-mods`, `tmux-context`, `user-question-reply`. - + ### `UserMessage[]` -SHA-256: `5a92f12db2059c282cdea45b6845c65e6f4a95dd3d649aeddbde6f9ae03c561f` +SHA-256: `35bc598be70e0b9646ccdf5629b55bb80ba5a9cf49723ecf49957abe0a12cf99` -[`UserMessage`](#persistence-type-sha256-cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd) 的数组。 +[`UserMessage`](#persistence-type-sha256-a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47) 的数组。 @@ -5992,18 +6007,18 @@ SHA-256: `ad0c38a0adb82a94c925456df41586d9b5f9202fca18593eae2213d4445b4585` | `reason` | 必需 | [`RequestHeaderReason`](#persistence-type-sha256-530da6c0267813bb4511af72883cc518834e624e3b3220a7062955fd57a788a6) | | `startsSeries` | 可选 | `true` | - + ### `{ headerSeq?, message, step, turn }` -SHA-256: `a5d80b5a2f3a6bbaf8f0334126632b78f94d8346122c68e8122ea89906d63d26` +SHA-256: `f2039f3c7aff513cfe2c30ec0e8a2677796d60b280a0b627c36226788a013132` 来源:[`packages/core/session/src/types.ts:311`](../packages/core/session/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| | `headerSeq` | 可选 | `number` | -| `message` | 必需 | [`DeveloperMessage`](#persistence-type-sha256-1020bbc3c744c060cd31c0891ae79b9e4c3e0adfa3f0be49869309b34e9ef200) | +| `message` | 必需 | [`DeveloperMessage`](#persistence-type-sha256-6bad5e2ed590f9440817fa6f5b5e3b94acd677fc5169a931238b056e5b3057f9) | | `step` | 必需 | `number` | | `turn` | 必需 | `number` | @@ -6087,17 +6102,17 @@ SHA-256: `17d1afb770d9941936130996da00dc86782cfef731d8d6526162c301256a4ac3` |---|---|---| | `inherited` | 可选 | `true` | - + ### `{ inserted, outcome?, removedCount?, start, … }` -SHA-256: `149c5a77529cb0dc06f5ceb6273069b7606174d1e7893b5f9b1071c15306b846` +SHA-256: `40056a392eceb4445d649205e99900fe770ce3d3beb4b10301c1e50d2095e9ef` 来源:[`packages/core/agent/src/types.ts:96`](../packages/core/agent/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| -| `inserted` | 必需 | [`UserMessage[]`](#persistence-type-sha256-5a92f12db2059c282cdea45b6845c65e6f4a95dd3d649aeddbde6f9ae03c561f) | +| `inserted` | 必需 | [`UserMessage[]`](#persistence-type-sha256-35bc598be70e0b9646ccdf5629b55bb80ba5a9cf49723ecf49957abe0a12cf99) | | `outcome` | 可选 | `"canceled"` | | `removedCount` | 可选 | `number` | | `start` | 必需 | `number` | @@ -6145,6 +6160,84 @@ SHA-256: `023a355b2be0d688fb6e4df8388dadb942761d46cfe96d0f499aae9631a159d4` |---|---|---| | `kind` | 必需 | `"blocked"` | + + +### `{ kind: "claude-code-mods" }` + +SHA-256: `c52b232ec5d7f7dec08d3f6059318e3e67e753e9028cdca607ddc53b90f44a08` + +| 属性 | 存在性 | 类型 | +|---|---|---| +| `kind` | 必需 | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "catalog" }` + +SHA-256: `5e78e8bebe50889028294fa6918b9bd340a5f1a26f12418d47a2b1cfa637b749` + +| 属性 | 存在性 | 类型 | +|---|---|---| +| `form` | 必需 | `"catalog"` | +| `kind` | 必需 | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "instructions" }` + +SHA-256: `a66a864d5c1ed32387c7b45603e567d9ed2fc5559a96926fdebfff7c753f7c0f` + +| 属性 | 存在性 | 类型 | +|---|---|---| +| `form` | 必需 | `"instructions"` | +| `kind` | 必需 | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "notice" }` + +SHA-256: `2e285da62c0ed94e6e4bfbb9161e4090502ff95e379e753ebabe745e60eb7d5c` + +| 属性 | 存在性 | 类型 | +|---|---|---| +| `form` | 必需 | `"notice"` | +| `kind` | 必需 | `"claude-code-mods"` | +| `summary` | 必需 | `string` | + + + +### `{ kind: "claude-code-mods", form: "recall" }` + +SHA-256: `ccea69c3f118930ca9679e93a4347978995180ec87b997e2cb075ad5caac4a38` + +| 属性 | 存在性 | 类型 | +|---|---|---| +| `form` | 必需 | `"recall"` | +| `kind` | 必需 | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "relay" }` + +SHA-256: `93845ea41cc823c2ae619309ab2b4465363431b15a04d7d7877b65845ae44c3a` + +| 属性 | 存在性 | 类型 | +|---|---|---| +| `form` | 必需 | `"relay"` | +| `kind` | 必需 | `"claude-code-mods"` | + + + +### `{ kind: "claude-code-mods", form: "snapshot" }` + +SHA-256: `d17f8336e133bafdcbf95836c93e65aa3930558be3d676ae91aaa6b1e399c9fa` + +| 属性 | 存在性 | 类型 | +|---|---|---| +| `form` | 必需 | `"snapshot"` | +| `kind` | 必需 | `"claude-code-mods"` | +| `sections` | 必需 | [`ContextSnapshotSection[]`](#persistence-type-sha256-c748fb42c80a120038a8c587af80ec39924803c401364db11c8974d5bf6d0262) | + ### `{ kind: "completed" }` @@ -7645,17 +7738,17 @@ SHA-256: `a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b` | `time` | 必需 | `number` | | `type` | 必需 | `"agent-preset/selected"` | - + ### `{ type: "agent/inbox/spliced" }` -SHA-256: `6178f1edcb23f361f2a6cb2c859b1c2187220d5695ece4a3400e0d92845a7178` +SHA-256: `f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c` | 属性 | 存在性 | 类型 | |---|---|---| -| `data` | 必需 | [`{ inserted, outcome?, removedCount?, start, … }`](#persistence-type-sha256-149c5a77529cb0dc06f5ceb6273069b7606174d1e7893b5f9b1071c15306b846) | +| `data` | 必需 | [`{ inserted, outcome?, removedCount?, start, … }`](#persistence-type-sha256-40056a392eceb4445d649205e99900fe770ce3d3beb4b10301c1e50d2095e9ef) | | `ignorable` | 可选 | `true` | | `seq` | 必需 | `number` | | `time` | 必需 | `number` | @@ -7896,17 +7989,17 @@ SHA-256: `13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176` | `time` | 必需 | `number` | | `type` | 必需 | `"deliverables/presented"` | - + ### `{ type: "developer/message" }` -SHA-256: `186159f5f6f67a0b8cd095b8fe55bef42d4f25ca1a1c248f859867af2ece0467` +SHA-256: `e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321` | 属性 | 存在性 | 类型 | |---|---|---| -| `data` | 必需 | [`{ headerSeq?, message, step, turn }`](#persistence-type-sha256-a5d80b5a2f3a6bbaf8f0334126632b78f94d8346122c68e8122ea89906d63d26) | +| `data` | 必需 | [`{ headerSeq?, message, step, turn }`](#persistence-type-sha256-f2039f3c7aff513cfe2c30ec0e8a2677796d60b280a0b627c36226788a013132) | | `ignorable` | 可选 | `true` | | `seq` | 必需 | `number` | | `sourceEventSeqs` | 可选 | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) | @@ -8262,17 +8355,17 @@ SHA-256: `1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5` | `time` | 必需 | `number` | | `type` | 必需 | `"session/title"` | - + ### `{ type: "session/title-llm-request" }` -SHA-256: `ae84c5e493f94acc63cfb70389073ba616ed7ae7aa4fadde048bdcc64d49bb46` +SHA-256: `5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510` | 属性 | 存在性 | 类型 | |---|---|---| -| `data` | 必需 | [`SessionTitleLlmRequestEventData`](#persistence-type-sha256-fe3cdc6f9050584cea4a6d6998c31334bff7899f5ff1095082a44fdf3b47e996) | +| `data` | 必需 | [`SessionTitleLlmRequestEventData`](#persistence-type-sha256-9d38c01a12d75b16898d0ac01f3bd47e8d4bb1e1a074f22926aaf30d457165c3) | | `ignorable` | 可选 | `true` | | `seq` | 必需 | `number` | | `time` | 必需 | `number` | @@ -8695,17 +8788,17 @@ SHA-256: `f1a96afe7503ac1817de769cd0ba13d9b80111c7034c50106b3344ecc14cc515` | `type` | 必需 | `"usage"` | | `usage` | 必需 | [`TokenUsage`](#persistence-type-sha256-fec5442b9ee4122e10514d81d5a43648003fe8bfb7ee2ba50c0cdbdd383ed394) | - + ### `{ type: "user/message" }` -SHA-256: `b83ed1b1cfffbd7bd5cca06ea72e44be57beb68b39e5a96660ce42a9e21aa411` +SHA-256: `83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628` | 属性 | 存在性 | 类型 | |---|---|---| -| `data` | 必需 | [`UserMessage`](#persistence-type-sha256-cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd) | +| `data` | 必需 | [`UserMessage`](#persistence-type-sha256-a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47) | | `ignorable` | 可选 | `true` | | `seq` | 必需 | `number` | | `sourceEventSeqs` | 可选 | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) | diff --git a/docs/persistence-changes/2026-10-02-claude-code-mods-source.i18n.yaml b/docs/persistence-changes/2026-10-02-claude-code-mods-source.i18n.yaml new file mode 100644 index 0000000000..f7328c0ec3 --- /dev/null +++ b/docs/persistence-changes/2026-10-02-claude-code-mods-source.i18n.yaml @@ -0,0 +1,28 @@ +# Bilingual-pair consistency record for 2026-10-02-claude-code-mods-source.md (docs/i18n/README.md): per heading +# section, a hash of its English and Chinese blocks outside code blocks and generated regions. +# After editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write docs/persistence-changes/2026-10-02-claude-code-mods-source.md +/: + en: 3d5a424ae7d7e46b + zh: c22a8e4a62d8cc1a +/2026-10-02-claude-code-mods-source: + en: 1062a72062fee52c + zh: 6fe56722e99f86ec +/2026-10-02-claude-code-mods-source/summary: + en: 14942da2b2ec97d2 + zh: 99323b1d64755295 +/2026-10-02-claude-code-mods-source/table-of-contents: + en: 6cf314a069ceb451 + zh: 9adff678e9ecab3b +/2026-10-02-claude-code-mods-source/declaration: + en: 6907136f28e48c90 + zh: 02f8298485f3d8e7 +/2026-10-02-claude-code-mods-source/compatibility: + en: b893a114c45d7294 + zh: 27c18012cb5a0ce1 +/2026-10-02-claude-code-mods-source/verification: + en: bcda00d5afe5d054 + zh: d197fff6a80650e9 +/2026-10-02-claude-code-mods-source/dev-note: + en: b75c8458928b72b9 + zh: 003ba4e17b4b5269 diff --git a/docs/persistence-changes/2026-10-02-claude-code-mods-source.md b/docs/persistence-changes/2026-10-02-claude-code-mods-source.md new file mode 100644 index 0000000000..82e531ee29 --- /dev/null +++ b/docs/persistence-changes/2026-10-02-claude-code-mods-source.md @@ -0,0 +1,60 @@ +--- +description: "Records a persistence type transition and its compatibility acknowledgement." +kind: persistence-change +--- + +# 2026-10-02-claude-code-mods-source + +English | [中文](2026-10-02-claude-code-mods-source.zh.md) + +## Summary + +Adds the attribution-only `claude-code-mods` user-message source kind for context a Claude Code mod adds through `prompt.submit` and prompts a mod submits through `$.prompt.submit`. + +## Table of Contents + +- [Declaration](#declaration) +- [Compatibility](#compatibility) +- [Verification](#verification) +- [Dev Note](#dev-note) + + +## Declaration + +```yaml persistence-change +schemaVersion: 1 +id: 2026-10-02-claude-code-mods-source +baseline: false +changes: + - root: "event:agent/inbox/spliced" + previous: "2026-09-21-user-question-reply" + after: "f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c" + decision: same-version + - root: "event:developer/message" + previous: "2026-09-21-user-question-reply" + after: "e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321" + decision: same-version + - root: "event:session/title-llm-request" + previous: "2026-09-21-user-question-reply" + after: "5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510" + decision: same-version + - root: "event:user/message" + previous: "2026-09-21-user-question-reply" + after: "83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628" + decision: same-version +``` + + +## Compatibility + +Existing records remain valid. Readers preserve a `claude-code-mods` sourced message without the producer; no reader validates, replays, or authorizes on the kind, and the message text itself names the mod. + + +## Verification + +pnpm exec vitest run packages/experimental/claude-code-mods/tests: 88 tests passed, including the prompt-context and submitted-prompt source assertions in bridge.spec.ts. + + +## Dev Note + +None. diff --git a/docs/persistence-changes/2026-10-02-claude-code-mods-source.schema.json b/docs/persistence-changes/2026-10-02-claude-code-mods-source.schema.json new file mode 100644 index 0000000000..4f83cd148e --- /dev/null +++ b/docs/persistence-changes/2026-10-02-claude-code-mods-source.schema.json @@ -0,0 +1,11922 @@ +{ + "formatVersion": 2, + "roots": [ + { + "key": "event:agent/inbox/spliced", + "kind": "event", + "event": "agent/inbox/spliced", + "surface": false, + "digest": "f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c", + "schema": { + "root": 0, + "nodes": [ + { + "kind": "object", + "properties": [ + { + "name": "data", + "type": 1, + "optional": false + }, + { + "name": "ignorable", + "type": 27, + "optional": true + }, + { + "name": "seq", + "type": 20, + "optional": false + }, + { + "name": "time", + "type": 20, + "optional": false + }, + { + "name": "type", + "type": 210, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "inserted", + "type": 2, + "optional": false + }, + { + "name": "outcome", + "type": 206, + "optional": true + }, + { + "name": "removedCount", + "type": 20, + "optional": true + }, + { + "name": "start", + "type": 20, + "optional": false + }, + { + "name": "target", + "type": 207, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 3 + }, + { + "kind": "object", + "properties": [ + { + "name": "content", + "type": 4, + "optional": false + }, + { + "name": "id", + "type": 7, + "optional": false + }, + { + "name": "role", + "type": 32, + "optional": false + }, + { + "name": "source", + "type": 33, + "optional": false, + "compatibility": { + "version": 1, + "policy": "session-source-attribution", + "binding": "session.user-message.source", + "discriminator": "kind", + "unknownKinds": "preserve", + "attributionKinds": [ + "claude-code-mods", + "tmux-context", + "user-question-reply" + ] + } + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 5 + }, + { + "kind": "union", + "types": [ + 6, + 9, + 11, + 14, + 16, + 18, + 29 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "text", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 8, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "string" + }, + { + "kind": "literal", + "value": "reasoning" + }, + { + "kind": "object", + "properties": [ + { + "name": "text", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "text" + }, + { + "kind": "object", + "properties": [ + { + "name": "tool", + "type": 12, + "optional": true + }, + { + "name": "toolName", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 13, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "never" + }, + { + "kind": "literal", + "value": "tool-addition" + }, + { + "kind": "object", + "properties": [ + { + "name": "toolName", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 15, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-removal" + }, + { + "kind": "object", + "properties": [ + { + "name": "arguments", + "type": 7, + "optional": false + }, + { + "name": "id", + "type": 7, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 17, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-call" + }, + { + "kind": "object", + "properties": [ + { + "name": "attachment", + "type": 19, + "optional": false + }, + { + "name": "offloaded", + "type": 27, + "optional": true + }, + { + "name": "type", + "type": 28, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "attachmentId", + "type": 7, + "optional": false + }, + { + "name": "bytes", + "type": 20, + "optional": false + }, + { + "name": "height", + "type": 20, + "optional": false + }, + { + "name": "mediaType", + "type": 21, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": true + }, + { + "name": "originalDimensions", + "type": 26, + "optional": true + }, + { + "name": "width", + "type": 20, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "number" + }, + { + "kind": "union", + "types": [ + 22, + 23, + 24, + 25 + ] + }, + { + "kind": "literal", + "value": "image/gif" + }, + { + "kind": "literal", + "value": "image/jpeg" + }, + { + "kind": "literal", + "value": "image/png" + }, + { + "kind": "literal", + "value": "image/webp" + }, + { + "kind": "object", + "properties": [ + { + "name": "height", + "type": 20, + "optional": false + }, + { + "name": "width", + "type": 20, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": true + }, + { + "kind": "literal", + "value": "image" + }, + { + "kind": "object", + "properties": [ + { + "name": "attachment", + "type": 30, + "optional": false + }, + { + "name": "type", + "type": 31, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "attachmentId", + "type": 7, + "optional": false + }, + { + "name": "bytes", + "type": 20, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "file" + }, + { + "kind": "literal", + "value": "user" + }, + { + "kind": "union", + "types": [ + 34, + 39, + 41, + 44, + 47, + 49, + 51, + 53, + 55, + 57, + 59, + 61, + 63, + 65, + 67, + 69, + 71, + 72, + 73, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 134, + 136, + 139, + 140, + 141, + 142, + 143, + 144, + 146, + 147, + 148, + 149, + 151, + 152, + 153, + 154, + 155, + 157, + 158, + 159, + 160, + 162, + 170, + 173, + 175, + 176, + 178, + 180, + 184, + 186, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "snapshot" + }, + { + "kind": "literal", + "value": "user-approval" + }, + { + "kind": "array", + "element": 38 + }, + { + "kind": "object", + "properties": [ + { + "name": "name", + "type": 7, + "optional": false + }, + { + "name": "text", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "claude-code-mods" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 43, + "optional": false + }, + { + "name": "senderSessionId", + "type": 7, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "notice" + }, + { + "kind": "literal", + "value": "subagent-settled" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + }, + { + "name": "senderSessionId", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "relay" + }, + { + "kind": "literal", + "value": "agent-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "dsh-session-title-llm" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-claude-code" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-codex" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "model-selection" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "plan-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "repeat-tool-reminder" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "runtime-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "schedule" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "time-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tmux-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-jobs" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "instructions" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "recall" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "catalog" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "goalId", + "type": 7, + "optional": false + }, + { + "name": "kind", + "type": 133, + "optional": false + }, + { + "name": "revision", + "type": 20, + "optional": false + }, + { + "name": "round", + "type": 20, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 135, + "optional": false + }, + { + "name": "messageId", + "type": 7, + "optional": false + }, + { + "name": "senderId", + "type": 7, + "optional": false + }, + { + "name": "senderName", + "type": 7, + "optional": false + }, + { + "name": "teamId", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "team-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 137, + "optional": false + }, + { + "name": "model", + "type": 7, + "optional": false + }, + { + "name": "provider", + "type": 7, + "optional": false + }, + { + "name": "replayState", + "type": 138, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "model" + }, + { + "kind": "opaque", + "reason": "unknown" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 145, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "ptc-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 150, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system-prompt" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 156, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-registry" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 32, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 161, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "cordis-host-runner" + }, + { + "kind": "object", + "properties": [ + { + "name": "baseline", + "type": 27, + "optional": true + }, + { + "name": "baselineIdentity", + "type": 7, + "optional": true + }, + { + "name": "changes", + "type": 163, + "optional": false + }, + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 169, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 164 + }, + { + "kind": "object", + "properties": [ + { + "name": "action", + "type": 165, + "optional": false + }, + { + "name": "digest", + "type": 7, + "optional": true + }, + { + "name": "path", + "type": 7, + "optional": false + }, + { + "name": "scope", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 166, + 167, + 168 + ] + }, + { + "kind": "literal", + "value": "remove" + }, + { + "kind": "literal", + "value": "replace" + }, + { + "kind": "literal", + "value": "set" + }, + { + "kind": "literal", + "value": "agent-instructions" + }, + { + "kind": "object", + "properties": [ + { + "name": "callId", + "type": 7, + "optional": false + }, + { + "name": "kind", + "type": 171, + "optional": false + }, + { + "name": "outcome", + "type": 172, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user-question-reply" + }, + { + "kind": "literal", + "value": "answered" + }, + { + "kind": "object", + "properties": [ + { + "name": "callId", + "type": 7, + "optional": false + }, + { + "name": "kind", + "type": 174, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool" + }, + { + "kind": "object", + "properties": [ + { + "name": "clientTimeZone", + "type": 7, + "optional": true + }, + { + "name": "kind", + "type": 32, + "optional": false + }, + { + "name": "rpcId", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "compactionId", + "type": 7, + "optional": false + }, + { + "name": "kind", + "type": 177, + "optional": false + }, + { + "name": "sourceCommandId", + "type": 7, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "compact-checkpoint" + }, + { + "kind": "object", + "properties": [ + { + "name": "deliveryId", + "type": 7, + "optional": false + }, + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 179, + "optional": false + }, + { + "name": "provider", + "type": 7, + "optional": false + }, + { + "name": "ruleId", + "type": 7, + "optional": false + }, + { + "name": "source", + "type": 7, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "webhook" + }, + { + "kind": "object", + "properties": [ + { + "name": "entries", + "type": 181, + "optional": false + }, + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 183, + "optional": false + }, + { + "name": "update", + "type": 27, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 182 + }, + { + "kind": "object", + "properties": [ + { + "name": "description", + "type": 7, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "skill-catalog" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 185, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "skill-invocation" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 187, + "optional": false + }, + { + "name": "references", + "type": 188, + "optional": false + }, + { + "name": "version", + "type": 193, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "session-reference" + }, + { + "kind": "array", + "element": 189 + }, + { + "kind": "object", + "properties": [ + { + "name": "capturedFormatVersion", + "type": 20, + "optional": true + }, + { + "name": "capturedThroughSeq", + "type": 190, + "optional": false + }, + { + "name": "compacted", + "type": 192, + "optional": false + }, + { + "name": "inputIndex", + "type": 20, + "optional": false + }, + { + "name": "label", + "type": 7, + "optional": false + }, + { + "name": "omittedBytes", + "type": 20, + "optional": false + }, + { + "name": "omittedMessages", + "type": 20, + "optional": false + }, + { + "name": "originalMessages", + "type": 20, + "optional": false + }, + { + "name": "retainedMessages", + "type": 20, + "optional": false + }, + { + "name": "sessionId", + "type": 7, + "optional": false + }, + { + "name": "truncated", + "type": 192, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 20, + 191 + ] + }, + { + "kind": "primitive", + "type": "null" + }, + { + "kind": "primitive", + "type": "boolean" + }, + { + "kind": "literal", + "value": 1 + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "canceled" + }, + { + "kind": "union", + "types": [ + 208, + 209 + ] + }, + { + "kind": "literal", + "value": "next-step" + }, + { + "kind": "literal", + "value": "next-turn" + }, + { + "kind": "literal", + "value": "agent/inbox/spliced" + } + ] + } + }, + { + "key": "event:developer/message", + "kind": "event", + "event": "developer/message", + "surface": true, + "digest": "e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321", + "schema": { + "root": 0, + "nodes": [ + { + "kind": "object", + "properties": [ + { + "name": "data", + "type": 1, + "optional": false + }, + { + "name": "ignorable", + "type": 26, + "optional": true + }, + { + "name": "seq", + "type": 2, + "optional": false + }, + { + "name": "sourceEventSeqs", + "type": 206, + "optional": true + }, + { + "name": "surfaceOp", + "type": 207, + "optional": false + }, + { + "name": "time", + "type": 2, + "optional": false + }, + { + "name": "type", + "type": 210, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "headerSeq", + "type": 2, + "optional": true + }, + { + "name": "message", + "type": 3, + "optional": false + }, + { + "name": "step", + "type": 2, + "optional": false + }, + { + "name": "turn", + "type": 2, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "number" + }, + { + "kind": "object", + "properties": [ + { + "name": "content", + "type": 4, + "optional": false + }, + { + "name": "id", + "type": 7, + "optional": false + }, + { + "name": "role", + "type": 31, + "optional": false + }, + { + "name": "source", + "type": 32, + "optional": false, + "compatibility": { + "version": 1, + "policy": "session-source-attribution", + "binding": "session.developer-message.source", + "discriminator": "kind", + "unknownKinds": "preserve", + "attributionKinds": [ + "claude-code-mods", + "tmux-context", + "user-question-reply" + ] + } + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 5 + }, + { + "kind": "union", + "types": [ + 6, + 9, + 11, + 14, + 16, + 18, + 28 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "text", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 8, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "string" + }, + { + "kind": "literal", + "value": "reasoning" + }, + { + "kind": "object", + "properties": [ + { + "name": "text", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "text" + }, + { + "kind": "object", + "properties": [ + { + "name": "tool", + "type": 12, + "optional": true + }, + { + "name": "toolName", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 13, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "never" + }, + { + "kind": "literal", + "value": "tool-addition" + }, + { + "kind": "object", + "properties": [ + { + "name": "toolName", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 15, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-removal" + }, + { + "kind": "object", + "properties": [ + { + "name": "arguments", + "type": 7, + "optional": false + }, + { + "name": "id", + "type": 7, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": false + }, + { + "name": "type", + "type": 17, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-call" + }, + { + "kind": "object", + "properties": [ + { + "name": "attachment", + "type": 19, + "optional": false + }, + { + "name": "offloaded", + "type": 26, + "optional": true + }, + { + "name": "type", + "type": 27, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "attachmentId", + "type": 7, + "optional": false + }, + { + "name": "bytes", + "type": 2, + "optional": false + }, + { + "name": "height", + "type": 2, + "optional": false + }, + { + "name": "mediaType", + "type": 20, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": true + }, + { + "name": "originalDimensions", + "type": 25, + "optional": true + }, + { + "name": "width", + "type": 2, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 21, + 22, + 23, + 24 + ] + }, + { + "kind": "literal", + "value": "image/gif" + }, + { + "kind": "literal", + "value": "image/jpeg" + }, + { + "kind": "literal", + "value": "image/png" + }, + { + "kind": "literal", + "value": "image/webp" + }, + { + "kind": "object", + "properties": [ + { + "name": "height", + "type": 2, + "optional": false + }, + { + "name": "width", + "type": 2, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": true + }, + { + "kind": "literal", + "value": "image" + }, + { + "kind": "object", + "properties": [ + { + "name": "attachment", + "type": 29, + "optional": false + }, + { + "name": "type", + "type": 30, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "attachmentId", + "type": 7, + "optional": false + }, + { + "name": "bytes", + "type": 2, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "file" + }, + { + "kind": "literal", + "value": "developer" + }, + { + "kind": "union", + "types": [ + 33, + 38, + 40, + 43, + 46, + 48, + 50, + 52, + 54, + 56, + 58, + 60, + 62, + 64, + 66, + 68, + 70, + 71, + 72, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 133, + 135, + 138, + 139, + 140, + 141, + 142, + 143, + 145, + 146, + 147, + 148, + 150, + 151, + 152, + 153, + 154, + 156, + 158, + 159, + 160, + 162, + 170, + 173, + 175, + 176, + 178, + 180, + 184, + 186, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "snapshot" + }, + { + "kind": "literal", + "value": "user-approval" + }, + { + "kind": "array", + "element": 37 + }, + { + "kind": "object", + "properties": [ + { + "name": "name", + "type": 7, + "optional": false + }, + { + "name": "text", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "claude-code-mods" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + }, + { + "name": "senderSessionId", + "type": 7, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "notice" + }, + { + "kind": "literal", + "value": "subagent-settled" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 45, + "optional": false + }, + { + "name": "senderSessionId", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "relay" + }, + { + "kind": "literal", + "value": "agent-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 47, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "dsh-session-title-llm" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 49, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-claude-code" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 51, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-codex" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 53, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "model-selection" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 55, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "plan-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "repeat-tool-reminder" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "runtime-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "schedule" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "time-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tmux-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-jobs" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 47, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "instructions" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 49, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 51, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 53, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 47, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "recall" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 49, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 51, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 53, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 47, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 49, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 51, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 53, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 47, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "catalog" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 49, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 51, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 53, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "goalId", + "type": 7, + "optional": false + }, + { + "name": "kind", + "type": 132, + "optional": false + }, + { + "name": "revision", + "type": 2, + "optional": false + }, + { + "name": "round", + "type": 2, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 134, + "optional": false + }, + { + "name": "messageId", + "type": 7, + "optional": false + }, + { + "name": "senderId", + "type": 7, + "optional": false + }, + { + "name": "senderName", + "type": 7, + "optional": false + }, + { + "name": "teamId", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "team-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 136, + "optional": false + }, + { + "name": "model", + "type": 7, + "optional": false + }, + { + "name": "provider", + "type": 7, + "optional": false + }, + { + "name": "replayState", + "type": 137, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "model" + }, + { + "kind": "opaque", + "reason": "unknown" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 47, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 49, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 51, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 53, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 144, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "ptc-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 149, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system-prompt" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 155, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-registry" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 157, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 39, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 161, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "cordis-host-runner" + }, + { + "kind": "object", + "properties": [ + { + "name": "baseline", + "type": 26, + "optional": true + }, + { + "name": "baselineIdentity", + "type": 7, + "optional": true + }, + { + "name": "changes", + "type": 163, + "optional": false + }, + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 169, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 164 + }, + { + "kind": "object", + "properties": [ + { + "name": "action", + "type": 165, + "optional": false + }, + { + "name": "digest", + "type": 7, + "optional": true + }, + { + "name": "path", + "type": 7, + "optional": false + }, + { + "name": "scope", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 166, + 167, + 168 + ] + }, + { + "kind": "literal", + "value": "remove" + }, + { + "kind": "literal", + "value": "replace" + }, + { + "kind": "literal", + "value": "set" + }, + { + "kind": "literal", + "value": "agent-instructions" + }, + { + "kind": "object", + "properties": [ + { + "name": "callId", + "type": 7, + "optional": false + }, + { + "name": "kind", + "type": 171, + "optional": false + }, + { + "name": "outcome", + "type": 172, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user-question-reply" + }, + { + "kind": "literal", + "value": "answered" + }, + { + "kind": "object", + "properties": [ + { + "name": "callId", + "type": 7, + "optional": false + }, + { + "name": "kind", + "type": 174, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool" + }, + { + "kind": "object", + "properties": [ + { + "name": "clientTimeZone", + "type": 7, + "optional": true + }, + { + "name": "kind", + "type": 157, + "optional": false + }, + { + "name": "rpcId", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "compactionId", + "type": 7, + "optional": false + }, + { + "name": "kind", + "type": 177, + "optional": false + }, + { + "name": "sourceCommandId", + "type": 7, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "compact-checkpoint" + }, + { + "kind": "object", + "properties": [ + { + "name": "deliveryId", + "type": 7, + "optional": false + }, + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 179, + "optional": false + }, + { + "name": "provider", + "type": 7, + "optional": false + }, + { + "name": "ruleId", + "type": 7, + "optional": false + }, + { + "name": "source", + "type": 7, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "webhook" + }, + { + "kind": "object", + "properties": [ + { + "name": "entries", + "type": 181, + "optional": false + }, + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 183, + "optional": false + }, + { + "name": "update", + "type": 26, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 182 + }, + { + "kind": "object", + "properties": [ + { + "name": "description", + "type": 7, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "skill-catalog" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 185, + "optional": false + }, + { + "name": "name", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "skill-invocation" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 187, + "optional": false + }, + { + "name": "references", + "type": 188, + "optional": false + }, + { + "name": "version", + "type": 193, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "session-reference" + }, + { + "kind": "array", + "element": 189 + }, + { + "kind": "object", + "properties": [ + { + "name": "capturedFormatVersion", + "type": 2, + "optional": true + }, + { + "name": "capturedThroughSeq", + "type": 190, + "optional": false + }, + { + "name": "compacted", + "type": 192, + "optional": false + }, + { + "name": "inputIndex", + "type": 2, + "optional": false + }, + { + "name": "label", + "type": 7, + "optional": false + }, + { + "name": "omittedBytes", + "type": 2, + "optional": false + }, + { + "name": "omittedMessages", + "type": 2, + "optional": false + }, + { + "name": "originalMessages", + "type": 2, + "optional": false + }, + { + "name": "retainedMessages", + "type": 2, + "optional": false + }, + { + "name": "sessionId", + "type": 7, + "optional": false + }, + { + "name": "truncated", + "type": 192, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 2, + 191 + ] + }, + { + "kind": "primitive", + "type": "null" + }, + { + "kind": "primitive", + "type": "boolean" + }, + { + "kind": "literal", + "value": 1 + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 47, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 49, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 51, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 53, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 55, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 2 + }, + { + "kind": "union", + "types": [ + 208, + 209 + ] + }, + { + "kind": "literal", + "value": "append" + }, + { + "kind": "object", + "properties": [ + { + "name": "endSeq", + "type": 2, + "optional": false + }, + { + "name": "op", + "type": 167, + "optional": false + }, + { + "name": "startSeq", + "type": 2, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "developer/message" + } + ] + } + }, + { + "key": "event:session/title-llm-request", + "kind": "event", + "event": "session/title-llm-request", + "surface": false, + "digest": "5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510", + "schema": { + "root": 0, + "nodes": [ + { + "kind": "object", + "properties": [ + { + "name": "data", + "type": 1, + "optional": false + }, + { + "name": "ignorable", + "type": 29, + "optional": true + }, + { + "name": "seq", + "type": 2, + "optional": false + }, + { + "name": "time", + "type": 2, + "optional": false + }, + { + "name": "type", + "type": 216, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "maxTokens", + "type": 2, + "optional": false + }, + { + "name": "messageSeqs", + "type": 3, + "optional": false + }, + { + "name": "messages", + "type": 4, + "optional": false + }, + { + "name": "route", + "type": 215, + "optional": false + }, + { + "name": "system", + "type": 10, + "optional": false + }, + { + "name": "titleProvider", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "number" + }, + { + "kind": "array", + "element": 2 + }, + { + "kind": "array", + "element": 5 + }, + { + "kind": "union", + "types": [ + 6, + 37, + 210, + 211, + 213 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "content", + "type": 7, + "optional": false + }, + { + "name": "id", + "type": 10, + "optional": false + }, + { + "name": "isError", + "type": 34, + "optional": true + }, + { + "name": "role", + "type": 35, + "optional": false + }, + { + "name": "source", + "type": 36, + "optional": false + }, + { + "name": "toolCallId", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 8 + }, + { + "kind": "union", + "types": [ + 9, + 12, + 14, + 17, + 19, + 21, + 31 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "text", + "type": 10, + "optional": false + }, + { + "name": "type", + "type": 11, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "string" + }, + { + "kind": "literal", + "value": "reasoning" + }, + { + "kind": "object", + "properties": [ + { + "name": "text", + "type": 10, + "optional": false + }, + { + "name": "type", + "type": 13, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "text" + }, + { + "kind": "object", + "properties": [ + { + "name": "tool", + "type": 15, + "optional": true + }, + { + "name": "toolName", + "type": 10, + "optional": false + }, + { + "name": "type", + "type": 16, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "never" + }, + { + "kind": "literal", + "value": "tool-addition" + }, + { + "kind": "object", + "properties": [ + { + "name": "toolName", + "type": 10, + "optional": false + }, + { + "name": "type", + "type": 18, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-removal" + }, + { + "kind": "object", + "properties": [ + { + "name": "arguments", + "type": 10, + "optional": false + }, + { + "name": "id", + "type": 10, + "optional": false + }, + { + "name": "name", + "type": 10, + "optional": false + }, + { + "name": "type", + "type": 20, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-call" + }, + { + "kind": "object", + "properties": [ + { + "name": "attachment", + "type": 22, + "optional": false + }, + { + "name": "offloaded", + "type": 29, + "optional": true + }, + { + "name": "type", + "type": 30, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "attachmentId", + "type": 10, + "optional": false + }, + { + "name": "bytes", + "type": 2, + "optional": false + }, + { + "name": "height", + "type": 2, + "optional": false + }, + { + "name": "mediaType", + "type": 23, + "optional": false + }, + { + "name": "name", + "type": 10, + "optional": true + }, + { + "name": "originalDimensions", + "type": 28, + "optional": true + }, + { + "name": "width", + "type": 2, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 24, + 25, + 26, + 27 + ] + }, + { + "kind": "literal", + "value": "image/gif" + }, + { + "kind": "literal", + "value": "image/jpeg" + }, + { + "kind": "literal", + "value": "image/png" + }, + { + "kind": "literal", + "value": "image/webp" + }, + { + "kind": "object", + "properties": [ + { + "name": "height", + "type": 2, + "optional": false + }, + { + "name": "width", + "type": 2, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": true + }, + { + "kind": "literal", + "value": "image" + }, + { + "kind": "object", + "properties": [ + { + "name": "attachment", + "type": 32, + "optional": false + }, + { + "name": "type", + "type": 33, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "attachmentId", + "type": 10, + "optional": false + }, + { + "name": "bytes", + "type": 2, + "optional": false + }, + { + "name": "name", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "file" + }, + { + "kind": "primitive", + "type": "boolean" + }, + { + "kind": "literal", + "value": "tool" + }, + { + "kind": "object", + "properties": [ + { + "name": "callId", + "type": 10, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "content", + "type": 7, + "optional": false + }, + { + "name": "id", + "type": 10, + "optional": false + }, + { + "name": "role", + "type": 38, + "optional": false + }, + { + "name": "source", + "type": 39, + "optional": false, + "compatibility": { + "version": 1, + "policy": "session-source-attribution", + "binding": "session.developer-message.source", + "discriminator": "kind", + "unknownKinds": "preserve", + "attributionKinds": [ + "claude-code-mods", + "tmux-context", + "user-question-reply" + ] + } + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "developer" + }, + { + "kind": "union", + "types": [ + 36, + 40, + 45, + 47, + 49, + 51, + 53, + 55, + 57, + 59, + 61, + 63, + 65, + 67, + 69, + 70, + 71, + 73, + 75, + 77, + 79, + 80, + 82, + 84, + 85, + 86, + 89, + 92, + 93, + 94, + 103, + 106, + 107, + 108, + 110, + 112, + 113, + 118, + 120, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 205, + 207 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "snapshot" + }, + { + "kind": "literal", + "value": "tmux-context" + }, + { + "kind": "array", + "element": 44 + }, + { + "kind": "object", + "properties": [ + { + "name": "name", + "type": 10, + "optional": false + }, + { + "name": "text", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "dsh-session-title-llm" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-claude-code" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-codex" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "model-selection" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "plan-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "ptc-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "repeat-tool-reminder" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "runtime-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "schedule" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system-prompt" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "time-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-jobs" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 74, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-registry" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 76, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 78, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user-approval" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 81, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "claude-code-mods" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 83, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "cordis-host-runner" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 78, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 81, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 88, + "optional": false + }, + { + "name": "senderSessionId", + "type": 10, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "notice" + }, + { + "kind": "literal", + "value": "subagent-settled" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 91, + "optional": false + }, + { + "name": "senderSessionId", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "relay" + }, + { + "kind": "literal", + "value": "agent-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "baseline", + "type": 29, + "optional": true + }, + { + "name": "baselineIdentity", + "type": 10, + "optional": true + }, + { + "name": "changes", + "type": 95, + "optional": false + }, + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 102, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 96 + }, + { + "kind": "object", + "properties": [ + { + "name": "action", + "type": 97, + "optional": false + }, + { + "name": "digest", + "type": 10, + "optional": true + }, + { + "name": "path", + "type": 10, + "optional": false + }, + { + "name": "scope", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 98, + 99, + 100 + ] + }, + { + "kind": "literal", + "value": "remove" + }, + { + "kind": "literal", + "value": "replace" + }, + { + "kind": "literal", + "value": "set" + }, + { + "kind": "literal", + "value": "instructions" + }, + { + "kind": "literal", + "value": "agent-instructions" + }, + { + "kind": "object", + "properties": [ + { + "name": "callId", + "type": 10, + "optional": false + }, + { + "name": "kind", + "type": 104, + "optional": false + }, + { + "name": "outcome", + "type": 105, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user-question-reply" + }, + { + "kind": "literal", + "value": "answered" + }, + { + "kind": "object", + "properties": [ + { + "name": "clientTimeZone", + "type": 10, + "optional": true + }, + { + "name": "kind", + "type": 76, + "optional": false + }, + { + "name": "rpcId", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "compactionId", + "type": 10, + "optional": false + }, + { + "name": "kind", + "type": 109, + "optional": false + }, + { + "name": "sourceCommandId", + "type": 10, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "compact-checkpoint" + }, + { + "kind": "object", + "properties": [ + { + "name": "deliveryId", + "type": 10, + "optional": false + }, + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 111, + "optional": false + }, + { + "name": "provider", + "type": 10, + "optional": false + }, + { + "name": "ruleId", + "type": 10, + "optional": false + }, + { + "name": "source", + "type": 10, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "webhook" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "entries", + "type": 114, + "optional": false + }, + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 117, + "optional": false + }, + { + "name": "update", + "type": 29, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 115 + }, + { + "kind": "object", + "properties": [ + { + "name": "description", + "type": 10, + "optional": false + }, + { + "name": "name", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "catalog" + }, + { + "kind": "literal", + "value": "skill-catalog" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 119, + "optional": false + }, + { + "name": "name", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "skill-invocation" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 122, + "optional": false + }, + { + "name": "references", + "type": 123, + "optional": false + }, + { + "name": "version", + "type": 127, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "recall" + }, + { + "kind": "literal", + "value": "session-reference" + }, + { + "kind": "array", + "element": 124 + }, + { + "kind": "object", + "properties": [ + { + "name": "capturedFormatVersion", + "type": 2, + "optional": true + }, + { + "name": "capturedThroughSeq", + "type": 125, + "optional": false + }, + { + "name": "compacted", + "type": 34, + "optional": false + }, + { + "name": "inputIndex", + "type": 2, + "optional": false + }, + { + "name": "label", + "type": 10, + "optional": false + }, + { + "name": "omittedBytes", + "type": 2, + "optional": false + }, + { + "name": "omittedMessages", + "type": 2, + "optional": false + }, + { + "name": "originalMessages", + "type": 2, + "optional": false + }, + { + "name": "retainedMessages", + "type": 2, + "optional": false + }, + { + "name": "sessionId", + "type": 10, + "optional": false + }, + { + "name": "truncated", + "type": 34, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 2, + 126 + ] + }, + { + "kind": "primitive", + "type": "null" + }, + { + "kind": "literal", + "value": 1 + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 78, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 81, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 78, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 81, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 78, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 81, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 78, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 81, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 78, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 81, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "goalId", + "type": 10, + "optional": false + }, + { + "name": "kind", + "type": 204, + "optional": false + }, + { + "name": "revision", + "type": 2, + "optional": false + }, + { + "name": "round", + "type": 2, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 206, + "optional": false + }, + { + "name": "messageId", + "type": 10, + "optional": false + }, + { + "name": "senderId", + "type": 10, + "optional": false + }, + { + "name": "senderName", + "type": 10, + "optional": false + }, + { + "name": "teamId", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "team-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 208, + "optional": false + }, + { + "name": "model", + "type": 10, + "optional": false + }, + { + "name": "provider", + "type": 10, + "optional": false + }, + { + "name": "replayState", + "type": 209, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "model" + }, + { + "kind": "opaque", + "reason": "unknown" + }, + { + "kind": "object", + "properties": [ + { + "name": "content", + "type": 7, + "optional": false + }, + { + "name": "id", + "type": 10, + "optional": false + }, + { + "name": "role", + "type": 76, + "optional": false + }, + { + "name": "source", + "type": 39, + "optional": false, + "compatibility": { + "version": 1, + "policy": "session-source-attribution", + "binding": "session.user-message.source", + "discriminator": "kind", + "unknownKinds": "preserve", + "attributionKinds": [ + "claude-code-mods", + "tmux-context", + "user-question-reply" + ] + } + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "content", + "type": 7, + "optional": false + }, + { + "name": "id", + "type": 10, + "optional": false + }, + { + "name": "role", + "type": 212, + "optional": false + }, + { + "name": "source", + "type": 207, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "assistant" + }, + { + "kind": "object", + "properties": [ + { + "name": "content", + "type": 7, + "optional": false + }, + { + "name": "id", + "type": 10, + "optional": false + }, + { + "name": "role", + "type": 214, + "optional": false + }, + { + "name": "source", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system" + }, + { + "kind": "object", + "properties": [ + { + "name": "model", + "type": 10, + "optional": false + }, + { + "name": "provider", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "session/title-llm-request" + } + ] + } + }, + { + "key": "event:user/message", + "kind": "event", + "event": "user/message", + "surface": true, + "digest": "83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628", + "schema": { + "root": 0, + "nodes": [ + { + "kind": "object", + "properties": [ + { + "name": "data", + "type": 1, + "optional": false + }, + { + "name": "ignorable", + "type": 25, + "optional": true + }, + { + "name": "seq", + "type": 18, + "optional": false + }, + { + "name": "sourceEventSeqs", + "type": 204, + "optional": true + }, + { + "name": "surfaceOp", + "type": 205, + "optional": false + }, + { + "name": "time", + "type": 18, + "optional": false + }, + { + "name": "type", + "type": 208, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "content", + "type": 2, + "optional": false + }, + { + "name": "id", + "type": 5, + "optional": false + }, + { + "name": "role", + "type": 30, + "optional": false + }, + { + "name": "source", + "type": 31, + "optional": false, + "compatibility": { + "version": 1, + "policy": "session-source-attribution", + "binding": "session.user-message.source", + "discriminator": "kind", + "unknownKinds": "preserve", + "attributionKinds": [ + "claude-code-mods", + "tmux-context", + "user-question-reply" + ] + } + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 3 + }, + { + "kind": "union", + "types": [ + 4, + 7, + 9, + 12, + 14, + 16, + 27 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "text", + "type": 5, + "optional": false + }, + { + "name": "type", + "type": 6, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "string" + }, + { + "kind": "literal", + "value": "reasoning" + }, + { + "kind": "object", + "properties": [ + { + "name": "text", + "type": 5, + "optional": false + }, + { + "name": "type", + "type": 8, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "text" + }, + { + "kind": "object", + "properties": [ + { + "name": "tool", + "type": 10, + "optional": true + }, + { + "name": "toolName", + "type": 5, + "optional": false + }, + { + "name": "type", + "type": 11, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "never" + }, + { + "kind": "literal", + "value": "tool-addition" + }, + { + "kind": "object", + "properties": [ + { + "name": "toolName", + "type": 5, + "optional": false + }, + { + "name": "type", + "type": 13, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-removal" + }, + { + "kind": "object", + "properties": [ + { + "name": "arguments", + "type": 5, + "optional": false + }, + { + "name": "id", + "type": 5, + "optional": false + }, + { + "name": "name", + "type": 5, + "optional": false + }, + { + "name": "type", + "type": 15, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-call" + }, + { + "kind": "object", + "properties": [ + { + "name": "attachment", + "type": 17, + "optional": false + }, + { + "name": "offloaded", + "type": 25, + "optional": true + }, + { + "name": "type", + "type": 26, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "attachmentId", + "type": 5, + "optional": false + }, + { + "name": "bytes", + "type": 18, + "optional": false + }, + { + "name": "height", + "type": 18, + "optional": false + }, + { + "name": "mediaType", + "type": 19, + "optional": false + }, + { + "name": "name", + "type": 5, + "optional": true + }, + { + "name": "originalDimensions", + "type": 24, + "optional": true + }, + { + "name": "width", + "type": 18, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "primitive", + "type": "number" + }, + { + "kind": "union", + "types": [ + 20, + 21, + 22, + 23 + ] + }, + { + "kind": "literal", + "value": "image/gif" + }, + { + "kind": "literal", + "value": "image/jpeg" + }, + { + "kind": "literal", + "value": "image/png" + }, + { + "kind": "literal", + "value": "image/webp" + }, + { + "kind": "object", + "properties": [ + { + "name": "height", + "type": 18, + "optional": false + }, + { + "name": "width", + "type": 18, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": true + }, + { + "kind": "literal", + "value": "image" + }, + { + "kind": "object", + "properties": [ + { + "name": "attachment", + "type": 28, + "optional": false + }, + { + "name": "type", + "type": 29, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "attachmentId", + "type": 5, + "optional": false + }, + { + "name": "bytes", + "type": 18, + "optional": false + }, + { + "name": "name", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "file" + }, + { + "kind": "literal", + "value": "user" + }, + { + "kind": "union", + "types": [ + 32, + 35, + 37, + 39, + 41, + 43, + 45, + 47, + 49, + 51, + 53, + 55, + 57, + 60, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 121, + 123, + 126, + 127, + 128, + 129, + 130, + 132, + 133, + 134, + 135, + 137, + 138, + 139, + 140, + 141, + 143, + 144, + 145, + 146, + 148, + 149, + 157, + 160, + 162, + 163, + 165, + 167, + 171, + 173, + 181, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 200, + 202, + 203 + ] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 34, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "notice" + }, + { + "kind": "literal", + "value": "model-selection" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "plan-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 38, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "repeat-tool-reminder" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "runtime-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "schedule" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 44, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "time-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tmux-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-jobs" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user-approval" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "claude-code-mods" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "dsh-session-title-llm" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "instructions" + }, + { + "kind": "literal", + "value": "hooks-claude-code" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-codex" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 34, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 38, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 44, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "recall" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 34, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 38, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 44, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "relay" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 34, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 38, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 44, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "catalog" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 34, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 38, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 44, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "goalId", + "type": 5, + "optional": false + }, + { + "name": "kind", + "type": 120, + "optional": false + }, + { + "name": "revision", + "type": 18, + "optional": false + }, + { + "name": "round", + "type": 18, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 122, + "optional": false + }, + { + "name": "messageId", + "type": 5, + "optional": false + }, + { + "name": "senderId", + "type": 5, + "optional": false + }, + { + "name": "senderName", + "type": 5, + "optional": false + }, + { + "name": "teamId", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "team-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 124, + "optional": false + }, + { + "name": "model", + "type": 5, + "optional": false + }, + { + "name": "provider", + "type": 5, + "optional": false + }, + { + "name": "replayState", + "type": 125, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "model" + }, + { + "kind": "opaque", + "reason": "unknown" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 34, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 131, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "ptc-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 38, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 136, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system-prompt" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 44, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 142, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-registry" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 30, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 147, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "cordis-host-runner" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "baseline", + "type": 25, + "optional": true + }, + { + "name": "baselineIdentity", + "type": 5, + "optional": true + }, + { + "name": "changes", + "type": 150, + "optional": false + }, + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 156, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 151 + }, + { + "kind": "object", + "properties": [ + { + "name": "action", + "type": 152, + "optional": false + }, + { + "name": "digest", + "type": 5, + "optional": true + }, + { + "name": "path", + "type": 5, + "optional": false + }, + { + "name": "scope", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 153, + 154, + 155 + ] + }, + { + "kind": "literal", + "value": "remove" + }, + { + "kind": "literal", + "value": "replace" + }, + { + "kind": "literal", + "value": "set" + }, + { + "kind": "literal", + "value": "agent-instructions" + }, + { + "kind": "object", + "properties": [ + { + "name": "callId", + "type": 5, + "optional": false + }, + { + "name": "kind", + "type": 158, + "optional": false + }, + { + "name": "outcome", + "type": 159, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user-question-reply" + }, + { + "kind": "literal", + "value": "answered" + }, + { + "kind": "object", + "properties": [ + { + "name": "callId", + "type": 5, + "optional": false + }, + { + "name": "kind", + "type": 161, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool" + }, + { + "kind": "object", + "properties": [ + { + "name": "clientTimeZone", + "type": 5, + "optional": true + }, + { + "name": "kind", + "type": 30, + "optional": false + }, + { + "name": "rpcId", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "compactionId", + "type": 5, + "optional": false + }, + { + "name": "kind", + "type": 164, + "optional": false + }, + { + "name": "sourceCommandId", + "type": 5, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "compact-checkpoint" + }, + { + "kind": "object", + "properties": [ + { + "name": "deliveryId", + "type": 5, + "optional": false + }, + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 166, + "optional": false + }, + { + "name": "provider", + "type": 5, + "optional": false + }, + { + "name": "ruleId", + "type": 5, + "optional": false + }, + { + "name": "source", + "type": 5, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "webhook" + }, + { + "kind": "object", + "properties": [ + { + "name": "entries", + "type": 168, + "optional": false + }, + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 170, + "optional": false + }, + { + "name": "update", + "type": 25, + "optional": true + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 169 + }, + { + "kind": "object", + "properties": [ + { + "name": "description", + "type": 5, + "optional": false + }, + { + "name": "name", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "skill-catalog" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 172, + "optional": false + }, + { + "name": "name", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "skill-invocation" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 174, + "optional": false + }, + { + "name": "references", + "type": 175, + "optional": false + }, + { + "name": "version", + "type": 180, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "session-reference" + }, + { + "kind": "array", + "element": 176 + }, + { + "kind": "object", + "properties": [ + { + "name": "capturedFormatVersion", + "type": 18, + "optional": true + }, + { + "name": "capturedThroughSeq", + "type": 177, + "optional": false + }, + { + "name": "compacted", + "type": 179, + "optional": false + }, + { + "name": "inputIndex", + "type": 18, + "optional": false + }, + { + "name": "label", + "type": 5, + "optional": false + }, + { + "name": "omittedBytes", + "type": 18, + "optional": false + }, + { + "name": "omittedMessages", + "type": 18, + "optional": false + }, + { + "name": "originalMessages", + "type": 18, + "optional": false + }, + { + "name": "retainedMessages", + "type": 18, + "optional": false + }, + { + "name": "sessionId", + "type": 5, + "optional": false + }, + { + "name": "truncated", + "type": 179, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "union", + "types": [ + 18, + 178 + ] + }, + { + "kind": "primitive", + "type": "null" + }, + { + "kind": "primitive", + "type": "boolean" + }, + { + "kind": "literal", + "value": 1 + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "snapshot" + }, + { + "kind": "array", + "element": 184 + }, + { + "kind": "object", + "properties": [ + { + "name": "name", + "type": 5, + "optional": false + }, + { + "name": "text", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 34, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 38, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 44, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 50, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 199, + "optional": false + }, + { + "name": "senderSessionId", + "type": 5, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "subagent-settled" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 201, + "optional": false + }, + { + "name": "senderSessionId", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "agent-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "array", + "element": 18 + }, + { + "kind": "union", + "types": [ + 206, + 207 + ] + }, + { + "kind": "literal", + "value": "append" + }, + { + "kind": "object", + "properties": [ + { + "name": "endSeq", + "type": 18, + "optional": false + }, + { + "name": "op", + "type": 154, + "optional": false + }, + { + "name": "startSeq", + "type": 18, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user/message" + } + ] + } + } + ], + "types": [] +} diff --git a/docs/persistence-changes/2026-10-02-claude-code-mods-source.zh.md b/docs/persistence-changes/2026-10-02-claude-code-mods-source.zh.md new file mode 100644 index 0000000000..9371bbbdfb --- /dev/null +++ b/docs/persistence-changes/2026-10-02-claude-code-mods-source.zh.md @@ -0,0 +1,60 @@ +--- +description: "记录持久化类型更改及其兼容性确认。" +kind: persistence-change +--- + +# 2026-10-02-claude-code-mods-source + +[English](2026-10-02-claude-code-mods-source.md) | 中文 + +## 概述 + +新增仅用于归因的 `claude-code-mods` user 消息来源种类,用于 Claude Code 模组通过 `prompt.submit` 添加的上下文以及通过 `$.prompt.submit` 提交的提示词。 + +## 目录 + +- [声明](#declaration) +- [兼容性](#compatibility) +- [验证](#verification) +- [开发备注](#dev-note) + + +## 声明 + +```yaml persistence-change +schemaVersion: 1 +id: 2026-10-02-claude-code-mods-source +baseline: false +changes: + - root: "event:agent/inbox/spliced" + previous: "2026-09-21-user-question-reply" + after: "f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c" + decision: same-version + - root: "event:developer/message" + previous: "2026-09-21-user-question-reply" + after: "e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321" + decision: same-version + - root: "event:session/title-llm-request" + previous: "2026-09-21-user-question-reply" + after: "5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510" + decision: same-version + - root: "event:user/message" + previous: "2026-09-21-user-question-reply" + after: "83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628" + decision: same-version +``` + + +## 兼容性 + +已有记录仍然有效。读取方在没有该生产者时也会保留 `claude-code-mods` 来源的消息;没有读取方依据该种类做校验、回放或授权,消息文本本身已指明模组。 + + +## 验证 + +pnpm exec vitest run packages/experimental/claude-code-mods/tests:88 个测试通过,包括 bridge.spec.ts 中对提示词上下文与提交提示词来源的断言。 + + +## 开发备注 + +无。 diff --git a/docs/persistence-changes/historical-formats/README.i18n.yaml b/docs/persistence-changes/historical-formats/README.i18n.yaml index ab050bed1e..f332e00020 100644 --- a/docs/persistence-changes/historical-formats/README.i18n.yaml +++ b/docs/persistence-changes/historical-formats/README.i18n.yaml @@ -15,8 +15,8 @@ en: 14c57f30e676f6e3 zh: b1a0ccf26fab3727 /session-persistence-formats/format-references: - en: b19e6cfc84bfc46d - zh: 5b1d86bcdab672ca + en: fb839d4db1eac313 + zh: 233bfb5d2c79fd63 /session-persistence-formats/scope-and-evidence: en: b220062139ddac1e zh: 7c4c0efe05dafa9c diff --git a/docs/persistence-changes/historical-formats/README.md b/docs/persistence-changes/historical-formats/README.md index c2da060729..d347cf6e4d 100644 --- a/docs/persistence-changes/historical-formats/README.md +++ b/docs/persistence-changes/historical-formats/README.md @@ -32,7 +32,7 @@ The index is generated from validated snapshots and the current writer constant. | 1 | PR #3349 | [V1](v1.md) | [JSON](v1.schema.json) | 54 / 415 | | 2 | `dsh-v0.1.3-alpha.2` | [V2](v2.md) | [JSON](v2.schema.json) | 56 / 435 | | 3 | PR #4320 | [V3](v3.md) | [JSON](v3.schema.json) | 60 / 467 | -| 4 | Current checkout | [Current catalog](../../persistence-catalog.md) | [JSON](../../persistence-schema.json) | 62 / 590 | +| 4 | Current checkout | [Current catalog](../../persistence-catalog.md) | [JSON](../../persistence-schema.json) | 62 / 598 | diff --git a/docs/persistence-changes/historical-formats/README.zh.md b/docs/persistence-changes/historical-formats/README.zh.md index 253c0943ba..e2858a4432 100644 --- a/docs/persistence-changes/historical-formats/README.zh.md +++ b/docs/persistence-changes/historical-formats/README.zh.md @@ -32,7 +32,7 @@ description: "查找从 V0 到工作区写入器版本的每个 Session 格式 | 1 | PR #3349 | [V1](v1.zh.md) | [JSON](v1.schema.json) | 54 / 415 | | 2 | `dsh-v0.1.3-alpha.2` | [V2](v2.zh.md) | [JSON](v2.schema.json) | 56 / 435 | | 3 | PR #4320 | [V3](v3.zh.md) | [JSON](v3.schema.json) | 60 / 467 | -| 4 | 当前工作树 | [当前目录](../../persistence-catalog.zh.md) | [JSON](../../persistence-schema.json) | 62 / 590 | +| 4 | 当前工作树 | [当前目录](../../persistence-catalog.zh.md) | [JSON](../../persistence-schema.json) | 62 / 598 | diff --git a/docs/persistence-schema.json b/docs/persistence-schema.json index f29dde8245..c28f2b9338 100644 --- a/docs/persistence-schema.json +++ b/docs/persistence-schema.json @@ -406,7 +406,7 @@ "kind": "event", "event": "agent/inbox/spliced", "surface": false, - "digest": "6178f1edcb23f361f2a6cb2c859b1c2187220d5695ece4a3400e0d92845a7178", + "digest": "f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c", "schema": { "root": 0, "nodes": [ @@ -435,7 +435,7 @@ }, { "name": "type", - "type": 202, + "type": 210, "optional": false } ], @@ -451,7 +451,7 @@ }, { "name": "outcome", - "type": 198, + "type": 206, "optional": true }, { @@ -466,7 +466,7 @@ }, { "name": "target", - "type": 199, + "type": 207, "optional": false } ], @@ -505,6 +505,7 @@ "discriminator": "kind", "unknownKinds": "preserve", "attributionKinds": [ + "claude-code-mods", "tmux-context", "user-question-reply" ] @@ -816,30 +817,24 @@ "kind": "union", "types": [ 34, - 37, 39, 41, - 43, - 45, + 44, 47, 49, 51, 53, 55, - 58, - 60, - 62, + 57, + 59, + 61, 63, - 64, 65, - 66, 67, - 68, 69, - 70, 71, 72, - 74, + 73, 75, 76, 77, @@ -852,8 +847,8 @@ 84, 85, 86, + 87, 88, - 89, 90, 91, 92, @@ -865,6 +860,7 @@ 98, 99, 100, + 101, 102, 103, 104, @@ -878,53 +874,65 @@ 112, 113, 114, + 115, 116, - 118, + 117, + 119, + 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, + 131, 132, - 133, 134, - 135, 136, - 138, 139, 140, + 141, 142, 143, + 144, + 146, + 147, + 148, + 149, 151, + 152, + 153, 154, - 156, + 155, 157, + 158, 159, - 161, - 165, - 167, + 160, + 162, + 170, + 173, 175, - 179, + 176, + 178, 180, - 181, - 182, - 183, 184, - 185, 186, - 187, - 188, - 189, - 190, - 191, - 193, + 194, 195, 196, - 197 + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205 ] }, { @@ -941,8 +949,8 @@ "optional": false }, { - "name": "summary", - "type": 7, + "name": "sections", + "type": 37, "optional": false } ], @@ -950,37 +958,32 @@ }, { "kind": "literal", - "value": "notice" + "value": "snapshot" }, { "kind": "literal", - "value": "plan-mode" + "value": "user-approval" + }, + { + "kind": "array", + "element": 38 }, { "kind": "object", "properties": [ { - "name": "form", - "type": 35, + "name": "name", + "type": 7, "optional": false }, { - "name": "kind", - "type": 38, - "optional": false - }, - { - "name": "summary", + "name": "text", "type": 7, "optional": false } ], "indices": [] }, - { - "kind": "literal", - "value": "repeat-tool-reminder" - }, { "kind": "object", "properties": [ @@ -995,8 +998,8 @@ "optional": false }, { - "name": "summary", - "type": 7, + "name": "sections", + "type": 37, "optional": false } ], @@ -1004,44 +1007,24 @@ }, { "kind": "literal", - "value": "runtime-context" + "value": "claude-code-mods" }, { "kind": "object", "properties": [ { "name": "form", - "type": 35, - "optional": false - }, - { - "name": "kind", "type": 42, "optional": false }, - { - "name": "summary", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "schedule" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 35, - "optional": false - }, { "name": "kind", - "type": 44, + "type": 43, + "optional": false + }, + { + "name": "senderSessionId", + "type": 7, "optional": false }, { @@ -1054,14 +1037,18 @@ }, { "kind": "literal", - "value": "time-context" + "value": "notice" + }, + { + "kind": "literal", + "value": "subagent-settled" }, { "kind": "object", "properties": [ { "name": "form", - "type": 35, + "type": 45, "optional": false }, { @@ -1070,7 +1057,7 @@ "optional": false }, { - "name": "summary", + "name": "senderSessionId", "type": 7, "optional": false } @@ -1079,14 +1066,18 @@ }, { "kind": "literal", - "value": "tmux-context" + "value": "relay" + }, + { + "kind": "literal", + "value": "agent-message" }, { "kind": "object", "properties": [ { "name": "form", - "type": 35, + "type": 42, "optional": false }, { @@ -1102,81 +1093,6 @@ ], "indices": [] }, - { - "kind": "literal", - "value": "tool-goal" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 35, - "optional": false - }, - { - "name": "kind", - "type": 50, - "optional": false - }, - { - "name": "summary", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "tool-jobs" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 35, - "optional": false - }, - { - "name": "kind", - "type": 52, - "optional": false - }, - { - "name": "summary", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "user-approval" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 35, - "optional": false - }, - { - "name": "kind", - "type": 54, - "optional": false - }, - { - "name": "summary", - "type": 7, - "optional": false - } - ], - "indices": [] - }, { "kind": "literal", "value": "dsh-session-title-llm" @@ -1186,21 +1102,22 @@ "properties": [ { "name": "form", - "type": 56, + "type": 42, "optional": false }, { "name": "kind", - "type": 57, + "type": 50, + "optional": false + }, + { + "name": "summary", + "type": 7, "optional": false } ], "indices": [] }, - { - "kind": "literal", - "value": "instructions" - }, { "kind": "literal", "value": "hooks-claude-code" @@ -1210,12 +1127,17 @@ "properties": [ { "name": "form", - "type": 56, + "type": 42, "optional": false }, { "name": "kind", - "type": 59, + "type": 52, + "optional": false + }, + { + "name": "summary", + "type": 7, "optional": false } ], @@ -1230,12 +1152,17 @@ "properties": [ { "name": "form", - "type": 56, + "type": 42, "optional": false }, { "name": "kind", - "type": 61, + "type": 54, + "optional": false + }, + { + "name": "summary", + "type": 7, "optional": false } ], @@ -1250,28 +1177,217 @@ "properties": [ { "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", "type": 56, "optional": false }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "plan-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "repeat-tool-reminder" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "runtime-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "schedule" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "time-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tmux-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-jobs" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 42, + "optional": false + }, { "name": "kind", "type": 36, "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 56, - "optional": false }, { - "name": "kind", - "type": 38, + "name": "summary", + "type": 7, "optional": false } ], @@ -1282,28 +1398,17 @@ "properties": [ { "name": "form", - "type": 56, + "type": 42, "optional": false }, { "name": "kind", "type": 40, "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 56, - "optional": false }, { - "name": "kind", - "type": 42, + "name": "summary", + "type": 7, "optional": false } ], @@ -1314,39 +1419,7 @@ "properties": [ { "name": "form", - "type": 56, - "optional": false - }, - { - "name": "kind", - "type": 44, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 56, - "optional": false - }, - { - "name": "kind", - "type": 46, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 56, + "type": 74, "optional": false }, { @@ -1357,12 +1430,16 @@ ], "indices": [] }, + { + "kind": "literal", + "value": "instructions" + }, { "kind": "object", "properties": [ { "name": "form", - "type": 56, + "type": 74, "optional": false }, { @@ -1378,7 +1455,7 @@ "properties": [ { "name": "form", - "type": 56, + "type": 74, "optional": false }, { @@ -1394,7 +1471,7 @@ "properties": [ { "name": "form", - "type": 56, + "type": 74, "optional": false }, { @@ -1410,12 +1487,172 @@ "properties": [ { "name": "form", - "type": 73, + "type": 74, "optional": false }, { "name": "kind", - "type": 57, + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 74, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 48, "optional": false } ], @@ -1430,151 +1667,7 @@ "properties": [ { "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 59, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 61, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 36, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 38, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 40, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 42, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 44, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 46, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, - "optional": false - }, - { - "name": "kind", - "type": 48, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 73, + "type": 89, "optional": false }, { @@ -1590,7 +1683,7 @@ "properties": [ { "name": "form", - "type": 73, + "type": 89, "optional": false }, { @@ -1606,7 +1699,7 @@ "properties": [ { "name": "form", - "type": 73, + "type": 89, "optional": false }, { @@ -1622,32 +1715,12 @@ "properties": [ { "name": "form", - "type": 87, + "type": 89, "optional": false }, { "name": "kind", - "type": 57, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "relay" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 87, - "optional": false - }, - { - "name": "kind", - "type": 59, + "type": 56, "optional": false } ], @@ -1658,12 +1731,12 @@ "properties": [ { "name": "form", - "type": 87, + "type": 89, "optional": false }, { "name": "kind", - "type": 61, + "type": 58, "optional": false } ], @@ -1674,7 +1747,103 @@ "properties": [ { "name": "form", - "type": 87, + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 89, "optional": false }, { @@ -1690,23 +1859,7 @@ "properties": [ { "name": "form", - "type": 87, - "optional": false - }, - { - "name": "kind", - "type": 38, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 87, + "type": 89, "optional": false }, { @@ -1722,55 +1875,7 @@ "properties": [ { "name": "form", - "type": 87, - "optional": false - }, - { - "name": "kind", - "type": 42, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 87, - "optional": false - }, - { - "name": "kind", - "type": 44, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 87, - "optional": false - }, - { - "name": "kind", - "type": 46, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 87, + "type": 45, "optional": false }, { @@ -1786,7 +1891,7 @@ "properties": [ { "name": "form", - "type": 87, + "type": 45, "optional": false }, { @@ -1802,7 +1907,7 @@ "properties": [ { "name": "form", - "type": 87, + "type": 45, "optional": false }, { @@ -1818,7 +1923,7 @@ "properties": [ { "name": "form", - "type": 87, + "type": 45, "optional": false }, { @@ -1834,12 +1939,172 @@ "properties": [ { "name": "form", - "type": 101, + "type": 45, "optional": false }, { "name": "kind", - "type": 57, + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 45, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 48, "optional": false } ], @@ -1854,151 +2119,7 @@ "properties": [ { "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 59, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 61, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 36, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 38, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 40, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 42, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 44, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 46, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, - "optional": false - }, - { - "name": "kind", - "type": 48, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 101, + "type": 118, "optional": false }, { @@ -2014,7 +2135,7 @@ "properties": [ { "name": "form", - "type": 101, + "type": 118, "optional": false }, { @@ -2030,7 +2151,7 @@ "properties": [ { "name": "form", - "type": 101, + "type": 118, "optional": false }, { @@ -2041,6 +2162,166 @@ ], "indices": [] }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 118, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + } + ], + "indices": [] + }, { "kind": "object", "properties": [ @@ -2051,7 +2332,7 @@ }, { "name": "kind", - "type": 115, + "type": 133, "optional": false }, { @@ -2076,7 +2357,7 @@ "properties": [ { "name": "kind", - "type": 117, + "type": 135, "optional": false }, { @@ -2111,7 +2392,7 @@ "properties": [ { "name": "kind", - "type": 119, + "type": 137, "optional": false }, { @@ -2126,7 +2407,7 @@ }, { "name": "replayState", - "type": 120, + "type": 138, "optional": true } ], @@ -2140,135 +2421,6 @@ "kind": "opaque", "reason": "unknown" }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 57, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 59, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 61, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 36, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 126, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "ptc-mode" - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 38, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 40, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 42, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 131, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "system-prompt" - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 44, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 46, - "optional": false - } - ], - "indices": [] - }, { "kind": "object", "properties": [ @@ -2296,7 +2448,147 @@ "properties": [ { "name": "kind", - "type": 137, + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 145, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "ptc-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 150, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system-prompt" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 70, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 156, "optional": false } ], @@ -2322,7 +2614,7 @@ "properties": [ { "name": "kind", - "type": 52, + "type": 36, "optional": false } ], @@ -2333,7 +2625,18 @@ "properties": [ { "name": "kind", - "type": 141, + "type": 40, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 161, "optional": false } ], @@ -2343,17 +2646,6 @@ "kind": "literal", "value": "cordis-host-runner" }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 54, - "optional": false - } - ], - "indices": [] - }, { "kind": "object", "properties": [ @@ -2369,17 +2661,17 @@ }, { "name": "changes", - "type": 144, + "type": 163, "optional": false }, { "name": "form", - "type": 56, + "type": 74, "optional": false }, { "name": "kind", - "type": 150, + "type": 169, "optional": false } ], @@ -2387,14 +2679,14 @@ }, { "kind": "array", - "element": 145 + "element": 164 }, { "kind": "object", "properties": [ { "name": "action", - "type": 146, + "type": 165, "optional": false }, { @@ -2418,9 +2710,9 @@ { "kind": "union", "types": [ - 147, - 148, - 149 + 166, + 167, + 168 ] }, { @@ -2449,12 +2741,12 @@ }, { "name": "kind", - "type": 152, + "type": 171, "optional": false }, { "name": "outcome", - "type": 153, + "type": 172, "optional": false } ], @@ -2478,7 +2770,7 @@ }, { "name": "kind", - "type": 155, + "type": 174, "optional": false } ], @@ -2519,7 +2811,7 @@ }, { "name": "kind", - "type": 158, + "type": 177, "optional": false }, { @@ -2544,12 +2836,12 @@ }, { "name": "form", - "type": 35, + "type": 42, "optional": false }, { "name": "kind", - "type": 160, + "type": 179, "optional": false }, { @@ -2584,17 +2876,17 @@ "properties": [ { "name": "entries", - "type": 162, + "type": 181, "optional": false }, { "name": "form", - "type": 101, + "type": 118, "optional": false }, { "name": "kind", - "type": 164, + "type": 183, "optional": false }, { @@ -2607,7 +2899,7 @@ }, { "kind": "array", - "element": 163 + "element": 182 }, { "kind": "object", @@ -2634,12 +2926,12 @@ "properties": [ { "name": "form", - "type": 56, + "type": 74, "optional": false }, { "name": "kind", - "type": 166, + "type": 185, "optional": false }, { @@ -2659,22 +2951,22 @@ "properties": [ { "name": "form", - "type": 73, + "type": 89, "optional": false }, { "name": "kind", - "type": 168, + "type": 187, "optional": false }, { "name": "references", - "type": 169, + "type": 188, "optional": false }, { "name": "version", - "type": 174, + "type": 193, "optional": false } ], @@ -2686,7 +2978,7 @@ }, { "kind": "array", - "element": 170 + "element": 189 }, { "kind": "object", @@ -2698,12 +2990,12 @@ }, { "name": "capturedThroughSeq", - "type": 171, + "type": 190, "optional": false }, { "name": "compacted", - "type": 173, + "type": 192, "optional": false }, { @@ -2743,7 +3035,7 @@ }, { "name": "truncated", - "type": 173, + "type": 192, "optional": false } ], @@ -2753,7 +3045,7 @@ "kind": "union", "types": [ 20, - 172 + 191 ] }, { @@ -2773,220 +3065,7 @@ "properties": [ { "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 57, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "snapshot" - }, - { - "kind": "array", - "element": 178 - }, - { - "kind": "object", - "properties": [ - { - "name": "name", - "type": 7, - "optional": false - }, - { - "name": "text", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 59, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 61, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 36, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 38, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 40, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 42, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 44, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 46, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, + "type": 35, "optional": false }, { @@ -2996,7 +3075,7 @@ }, { "name": "sections", - "type": 177, + "type": 37, "optional": false } ], @@ -3007,7 +3086,7 @@ "properties": [ { "name": "form", - "type": 176, + "type": 35, "optional": false }, { @@ -3017,7 +3096,7 @@ }, { "name": "sections", - "type": 177, + "type": 37, "optional": false } ], @@ -3028,7 +3107,7 @@ "properties": [ { "name": "form", - "type": 176, + "type": 35, "optional": false }, { @@ -3038,7 +3117,7 @@ }, { "name": "sections", - "type": 177, + "type": 37, "optional": false } ], @@ -3049,7 +3128,7 @@ "properties": [ { "name": "form", - "type": 176, + "type": 35, "optional": false }, { @@ -3059,7 +3138,7 @@ }, { "name": "sections", - "type": 177, + "type": 37, "optional": false } ], @@ -3075,67 +3154,12 @@ }, { "name": "kind", - "type": 192, + "type": 56, "optional": false }, { - "name": "senderSessionId", - "type": 7, - "optional": false - }, - { - "name": "summary", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "subagent-settled" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 87, - "optional": false - }, - { - "name": "kind", - "type": 194, - "optional": false - }, - { - "name": "senderSessionId", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "agent-message" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 35, - "optional": false - }, - { - "name": "kind", - "type": 57, - "optional": false - }, - { - "name": "summary", - "type": 7, + "name": "sections", + "type": 37, "optional": false } ], @@ -3151,12 +3175,12 @@ }, { "name": "kind", - "type": 59, + "type": 58, "optional": false }, { - "name": "summary", - "type": 7, + "name": "sections", + "type": 37, "optional": false } ], @@ -3172,12 +3196,117 @@ }, { "name": "kind", - "type": 61, + "type": 60, "optional": false }, { - "name": "summary", - "type": 7, + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 66, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + }, + { + "name": "sections", + "type": 37, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 35, + "optional": false + }, + { + "name": "kind", + "type": 70, + "optional": false + }, + { + "name": "sections", + "type": 37, "optional": false } ], @@ -3190,8 +3319,8 @@ { "kind": "union", "types": [ - 200, - 201 + 208, + 209 ] }, { @@ -6554,7 +6683,7 @@ "kind": "event", "event": "developer/message", "surface": true, - "digest": "186159f5f6f67a0b8cd095b8fe55bef42d4f25ca1a1c248f859867af2ece0467", + "digest": "e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321", "schema": { "root": 0, "nodes": [ @@ -6578,12 +6707,12 @@ }, { "name": "sourceEventSeqs", - "type": 198, + "type": 206, "optional": true }, { "name": "surfaceOp", - "type": 199, + "type": 207, "optional": false }, { @@ -6593,7 +6722,7 @@ }, { "name": "type", - "type": 202, + "type": 210, "optional": false } ], @@ -6658,6 +6787,7 @@ "discriminator": "kind", "unknownKinds": "preserve", "attributionKinds": [ + "claude-code-mods", "tmux-context", "user-question-reply" ] @@ -6965,30 +7095,24 @@ "kind": "union", "types": [ 33, - 36, 38, 40, - 42, - 44, + 43, 46, 48, 50, 52, 54, - 57, - 59, - 61, + 56, + 58, + 60, 62, - 63, 64, - 65, 66, - 67, 68, - 69, 70, 71, - 73, + 72, 74, 75, 76, @@ -7001,8 +7125,8 @@ 83, 84, 85, + 86, 87, - 88, 89, 90, 91, @@ -7014,6 +7138,7 @@ 97, 98, 99, + 100, 101, 102, 103, @@ -7027,53 +7152,65 @@ 111, 112, 113, + 114, 115, - 117, + 116, + 118, + 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, - 132, + 131, 133, - 134, 135, - 136, 138, + 139, 140, 141, + 142, 143, + 145, + 146, + 147, + 148, + 150, 151, + 152, + 153, 154, 156, - 157, + 158, 159, - 161, - 165, - 167, + 160, + 162, + 170, + 173, 175, - 179, + 176, + 178, 180, - 181, - 182, - 183, 184, - 185, 186, - 187, - 188, - 189, - 190, - 191, - 193, + 194, 195, 196, - 197 + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205 ] }, { @@ -7089,6 +7226,85 @@ "type": 35, "optional": false }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "snapshot" + }, + { + "kind": "literal", + "value": "user-approval" + }, + { + "kind": "array", + "element": 37 + }, + { + "kind": "object", + "properties": [ + { + "name": "name", + "type": 7, + "optional": false + }, + { + "name": "text", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "claude-code-mods" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + }, + { + "name": "senderSessionId", + "type": 7, + "optional": false + }, { "name": "summary", "type": 7, @@ -7101,6 +7317,135 @@ "kind": "literal", "value": "notice" }, + { + "kind": "literal", + "value": "subagent-settled" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 45, + "optional": false + }, + { + "name": "senderSessionId", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "relay" + }, + { + "kind": "literal", + "value": "agent-message" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 47, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "dsh-session-title-llm" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 49, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-claude-code" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 51, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "hooks-codex" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 53, + "optional": false + }, + { + "name": "summary", + "type": 7, + "optional": false + } + ], + "indices": [] + }, { "kind": "literal", "value": "model-selection" @@ -7110,12 +7455,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 37, + "type": 55, "optional": false }, { @@ -7135,12 +7480,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 39, + "type": 57, "optional": false }, { @@ -7160,12 +7505,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 41, + "type": 59, "optional": false }, { @@ -7185,12 +7530,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 43, + "type": 61, "optional": false }, { @@ -7210,12 +7555,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 45, + "type": 63, "optional": false }, { @@ -7235,12 +7580,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 47, + "type": 65, "optional": false }, { @@ -7260,12 +7605,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 49, + "type": 67, "optional": false }, { @@ -7285,12 +7630,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 51, + "type": 69, "optional": false }, { @@ -7310,12 +7655,12 @@ "properties": [ { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 53, + "type": 35, "optional": false }, { @@ -7326,133 +7671,22 @@ ], "indices": [] }, - { - "kind": "literal", - "value": "user-approval" - }, { "kind": "object", "properties": [ { "name": "form", - "type": 55, - "optional": false - }, - { - "name": "kind", - "type": 56, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "instructions" - }, - { - "kind": "literal", - "value": "dsh-session-title-llm" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 55, - "optional": false - }, - { - "name": "kind", - "type": 58, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "hooks-claude-code" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 55, - "optional": false - }, - { - "name": "kind", - "type": 60, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "hooks-codex" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 55, - "optional": false - }, - { - "name": "kind", - "type": 35, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 55, - "optional": false - }, - { - "name": "kind", - "type": 37, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 55, + "type": 41, "optional": false }, { "name": "kind", "type": 39, "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 55, - "optional": false }, { - "name": "kind", - "type": 41, + "name": "summary", + "type": 7, "optional": false } ], @@ -7463,39 +7697,7 @@ "properties": [ { "name": "form", - "type": 55, - "optional": false - }, - { - "name": "kind", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 55, - "optional": false - }, - { - "name": "kind", - "type": 45, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 55, + "type": 73, "optional": false }, { @@ -7506,12 +7708,16 @@ ], "indices": [] }, + { + "kind": "literal", + "value": "instructions" + }, { "kind": "object", "properties": [ { "name": "form", - "type": 55, + "type": 73, "optional": false }, { @@ -7527,7 +7733,7 @@ "properties": [ { "name": "form", - "type": 55, + "type": 73, "optional": false }, { @@ -7543,7 +7749,7 @@ "properties": [ { "name": "form", - "type": 55, + "type": 73, "optional": false }, { @@ -7559,12 +7765,172 @@ "properties": [ { "name": "form", - "type": 72, + "type": 73, "optional": false }, { "name": "kind", - "type": 56, + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 73, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 47, "optional": false } ], @@ -7579,151 +7945,7 @@ "properties": [ { "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 58, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 60, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 35, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 37, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 39, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 41, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 45, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, - "optional": false - }, - { - "name": "kind", - "type": 47, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 72, + "type": 88, "optional": false }, { @@ -7739,7 +7961,7 @@ "properties": [ { "name": "form", - "type": 72, + "type": 88, "optional": false }, { @@ -7755,7 +7977,7 @@ "properties": [ { "name": "form", - "type": 72, + "type": 88, "optional": false }, { @@ -7771,32 +7993,12 @@ "properties": [ { "name": "form", - "type": 86, + "type": 88, "optional": false }, { "name": "kind", - "type": 56, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "relay" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 58, + "type": 55, "optional": false } ], @@ -7807,12 +8009,12 @@ "properties": [ { "name": "form", - "type": 86, + "type": 88, "optional": false }, { "name": "kind", - "type": 60, + "type": 57, "optional": false } ], @@ -7823,7 +8025,103 @@ "properties": [ { "name": "form", - "type": 86, + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 88, "optional": false }, { @@ -7839,23 +8137,7 @@ "properties": [ { "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 37, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, + "type": 88, "optional": false }, { @@ -7871,55 +8153,7 @@ "properties": [ { "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 41, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 45, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, + "type": 44, "optional": false }, { @@ -7935,7 +8169,7 @@ "properties": [ { "name": "form", - "type": 86, + "type": 44, "optional": false }, { @@ -7951,7 +8185,7 @@ "properties": [ { "name": "form", - "type": 86, + "type": 44, "optional": false }, { @@ -7967,7 +8201,7 @@ "properties": [ { "name": "form", - "type": 86, + "type": 44, "optional": false }, { @@ -7983,12 +8217,172 @@ "properties": [ { "name": "form", - "type": 100, + "type": 44, "optional": false }, { "name": "kind", - "type": 56, + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 44, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 47, "optional": false } ], @@ -8003,151 +8397,7 @@ "properties": [ { "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 58, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 60, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 35, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 37, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 39, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 41, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 45, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, - "optional": false - }, - { - "name": "kind", - "type": 47, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 100, + "type": 117, "optional": false }, { @@ -8163,7 +8413,7 @@ "properties": [ { "name": "form", - "type": 100, + "type": 117, "optional": false }, { @@ -8179,7 +8429,7 @@ "properties": [ { "name": "form", - "type": 100, + "type": 117, "optional": false }, { @@ -8190,6 +8440,166 @@ ], "indices": [] }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 35, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 117, + "optional": false + }, + { + "name": "kind", + "type": 39, + "optional": false + } + ], + "indices": [] + }, { "kind": "object", "properties": [ @@ -8200,7 +8610,7 @@ }, { "name": "kind", - "type": 114, + "type": 132, "optional": false }, { @@ -8225,7 +8635,7 @@ "properties": [ { "name": "kind", - "type": 116, + "type": 134, "optional": false }, { @@ -8260,7 +8670,7 @@ "properties": [ { "name": "kind", - "type": 118, + "type": 136, "optional": false }, { @@ -8275,7 +8685,7 @@ }, { "name": "replayState", - "type": 119, + "type": 137, "optional": true } ], @@ -8289,135 +8699,6 @@ "kind": "opaque", "reason": "unknown" }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 56, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 58, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 60, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 35, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 37, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 126, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "ptc-mode" - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 39, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 41, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 131, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "system-prompt" - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 45, - "optional": false - } - ], - "indices": [] - }, { "kind": "object", "properties": [ @@ -8456,7 +8737,136 @@ "properties": [ { "name": "kind", - "type": 137, + "type": 53, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 55, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 144, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "ptc-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 57, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 59, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 61, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 149, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system-prompt" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 63, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 65, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 67, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 69, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 155, "optional": false } ], @@ -8471,7 +8881,7 @@ "properties": [ { "name": "kind", - "type": 139, + "type": 157, "optional": false } ], @@ -8486,7 +8896,7 @@ "properties": [ { "name": "kind", - "type": 53, + "type": 35, "optional": false } ], @@ -8497,7 +8907,18 @@ "properties": [ { "name": "kind", - "type": 142, + "type": 39, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 161, "optional": false } ], @@ -8522,17 +8943,17 @@ }, { "name": "changes", - "type": 144, + "type": 163, "optional": false }, { "name": "form", - "type": 55, + "type": 73, "optional": false }, { "name": "kind", - "type": 150, + "type": 169, "optional": false } ], @@ -8540,14 +8961,14 @@ }, { "kind": "array", - "element": 145 + "element": 164 }, { "kind": "object", "properties": [ { "name": "action", - "type": 146, + "type": 165, "optional": false }, { @@ -8571,9 +8992,9 @@ { "kind": "union", "types": [ - 147, - 148, - 149 + 166, + 167, + 168 ] }, { @@ -8602,12 +9023,12 @@ }, { "name": "kind", - "type": 152, + "type": 171, "optional": false }, { "name": "outcome", - "type": 153, + "type": 172, "optional": false } ], @@ -8631,7 +9052,7 @@ }, { "name": "kind", - "type": 155, + "type": 174, "optional": false } ], @@ -8651,7 +9072,7 @@ }, { "name": "kind", - "type": 139, + "type": 157, "optional": false }, { @@ -8672,7 +9093,7 @@ }, { "name": "kind", - "type": 158, + "type": 177, "optional": false }, { @@ -8697,12 +9118,12 @@ }, { "name": "form", - "type": 34, + "type": 41, "optional": false }, { "name": "kind", - "type": 160, + "type": 179, "optional": false }, { @@ -8737,17 +9158,17 @@ "properties": [ { "name": "entries", - "type": 162, + "type": 181, "optional": false }, { "name": "form", - "type": 100, + "type": 117, "optional": false }, { "name": "kind", - "type": 164, + "type": 183, "optional": false }, { @@ -8760,7 +9181,7 @@ }, { "kind": "array", - "element": 163 + "element": 182 }, { "kind": "object", @@ -8787,12 +9208,12 @@ "properties": [ { "name": "form", - "type": 55, + "type": 73, "optional": false }, { "name": "kind", - "type": 166, + "type": 185, "optional": false }, { @@ -8812,22 +9233,22 @@ "properties": [ { "name": "form", - "type": 72, + "type": 88, "optional": false }, { "name": "kind", - "type": 168, + "type": 187, "optional": false }, { "name": "references", - "type": 169, + "type": 188, "optional": false }, { "name": "version", - "type": 174, + "type": 193, "optional": false } ], @@ -8839,7 +9260,7 @@ }, { "kind": "array", - "element": 170 + "element": 189 }, { "kind": "object", @@ -8851,12 +9272,12 @@ }, { "name": "capturedThroughSeq", - "type": 171, + "type": 190, "optional": false }, { "name": "compacted", - "type": 173, + "type": 192, "optional": false }, { @@ -8896,7 +9317,7 @@ }, { "name": "truncated", - "type": 173, + "type": 192, "optional": false } ], @@ -8906,7 +9327,7 @@ "kind": "union", "types": [ 2, - 172 + 191 ] }, { @@ -8926,220 +9347,7 @@ "properties": [ { "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 56, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "snapshot" - }, - { - "kind": "array", - "element": 178 - }, - { - "kind": "object", - "properties": [ - { - "name": "name", - "type": 7, - "optional": false - }, - { - "name": "text", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 58, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 60, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 35, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 37, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 39, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 41, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 43, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, - "optional": false - }, - { - "name": "kind", - "type": 45, - "optional": false - }, - { - "name": "sections", - "type": 177, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 176, + "type": 34, "optional": false }, { @@ -9149,7 +9357,7 @@ }, { "name": "sections", - "type": 177, + "type": 36, "optional": false } ], @@ -9160,7 +9368,7 @@ "properties": [ { "name": "form", - "type": 176, + "type": 34, "optional": false }, { @@ -9170,7 +9378,7 @@ }, { "name": "sections", - "type": 177, + "type": 36, "optional": false } ], @@ -9181,7 +9389,7 @@ "properties": [ { "name": "form", - "type": 176, + "type": 34, "optional": false }, { @@ -9191,7 +9399,7 @@ }, { "name": "sections", - "type": 177, + "type": 36, "optional": false } ], @@ -9202,7 +9410,7 @@ "properties": [ { "name": "form", - "type": 176, + "type": 34, "optional": false }, { @@ -9212,7 +9420,7 @@ }, { "name": "sections", - "type": 177, + "type": 36, "optional": false } ], @@ -9228,67 +9436,12 @@ }, { "name": "kind", - "type": 192, + "type": 55, "optional": false }, { - "name": "senderSessionId", - "type": 7, - "optional": false - }, - { - "name": "summary", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "subagent-settled" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 194, - "optional": false - }, - { - "name": "senderSessionId", - "type": 7, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "agent-message" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 34, - "optional": false - }, - { - "name": "kind", - "type": 56, - "optional": false - }, - { - "name": "summary", - "type": 7, + "name": "sections", + "type": 36, "optional": false } ], @@ -9304,12 +9457,12 @@ }, { "name": "kind", - "type": 58, + "type": 57, "optional": false }, { - "name": "summary", - "type": 7, + "name": "sections", + "type": 36, "optional": false } ], @@ -9325,12 +9478,117 @@ }, { "name": "kind", - "type": 60, + "type": 59, "optional": false }, { - "name": "summary", - "type": 7, + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 63, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 65, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 67, + "optional": false + }, + { + "name": "sections", + "type": 36, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 34, + "optional": false + }, + { + "name": "kind", + "type": 69, + "optional": false + }, + { + "name": "sections", + "type": 36, "optional": false } ], @@ -9343,8 +9601,8 @@ { "kind": "union", "types": [ - 200, - 201 + 208, + 209 ] }, { @@ -9361,7 +9619,7 @@ }, { "name": "op", - "type": 148, + "type": 167, "optional": false }, { @@ -11856,7 +12114,7 @@ "kind": "event", "event": "session/title-llm-request", "surface": false, - "digest": "ae84c5e493f94acc63cfb70389073ba616ed7ae7aa4fadde048bdcc64d49bb46", + "digest": "5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510", "schema": { "root": 0, "nodes": [ @@ -11885,7 +12143,7 @@ }, { "name": "type", - "type": 208, + "type": 216, "optional": false } ], @@ -11911,7 +12169,7 @@ }, { "name": "route", - "type": 207, + "type": 215, "optional": false }, { @@ -11944,9 +12202,9 @@ "types": [ 6, 37, - 202, - 203, - 205 + 210, + 211, + 213 ] }, { @@ -12329,6 +12587,7 @@ "discriminator": "kind", "unknownKinds": "preserve", "attributionKinds": [ + "claude-code-mods", "tmux-context", "user-question-reply" ] @@ -12354,37 +12613,35 @@ 55, 57, 59, - 60, - 62, - 64, + 61, + 63, 65, - 68, + 67, + 69, + 70, 71, 73, 75, 77, 79, - 88, - 91, + 80, + 82, + 84, + 85, + 86, + 89, 92, + 93, 94, - 96, - 98, - 100, - 105, + 103, + 106, 107, - 115, - 116, - 117, + 108, + 110, + 112, + 113, 118, - 119, 120, - 122, - 123, - 124, - 125, - 126, - 127, 128, 129, 130, @@ -12443,17 +12700,26 @@ 183, 184, 185, + 186, 187, + 188, 189, + 190, + 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, - 201 + 201, + 202, + 203, + 205, + 207 ] }, { @@ -12483,7 +12749,7 @@ }, { "kind": "literal", - "value": "user-approval" + "value": "tmux-context" }, { "kind": "array", @@ -12518,7 +12784,7 @@ }, { "kind": "literal", - "value": "system-prompt" + "value": "dsh-session-title-llm" }, { "kind": "object", @@ -12533,7 +12799,7 @@ }, { "kind": "literal", - "value": "time-context" + "value": "hooks-claude-code" }, { "kind": "object", @@ -12548,7 +12814,7 @@ }, { "kind": "literal", - "value": "tmux-context" + "value": "hooks-codex" }, { "kind": "object", @@ -12563,7 +12829,7 @@ }, { "kind": "literal", - "value": "tool-goal" + "value": "model-selection" }, { "kind": "object", @@ -12578,7 +12844,7 @@ }, { "kind": "literal", - "value": "tool-jobs" + "value": "plan-mode" }, { "kind": "object", @@ -12593,7 +12859,7 @@ }, { "kind": "literal", - "value": "tool-registry" + "value": "ptc-mode" }, { "kind": "object", @@ -12608,7 +12874,92 @@ }, { "kind": "literal", - "value": "user" + "value": "repeat-tool-reminder" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "runtime-context" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "schedule" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-goal" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 66, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system-prompt" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "time-context" }, { "kind": "object", @@ -12626,7 +12977,114 @@ "properties": [ { "name": "kind", - "type": 61, + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-jobs" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 74, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "tool-registry" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 76, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 78, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "user-approval" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 81, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "claude-code-mods" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 83, "optional": false } ], @@ -12646,7 +13104,7 @@ }, { "name": "kind", - "type": 63, + "type": 78, "optional": false }, { @@ -12657,16 +13115,22 @@ ], "indices": [] }, - { - "kind": "literal", - "value": "dsh-session-title-llm" - }, { "kind": "object", "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, { "name": "kind", - "type": 63, + "type": 81, + "optional": false + }, + { + "name": "sections", + "type": 43, "optional": false } ], @@ -12677,12 +13141,12 @@ "properties": [ { "name": "form", - "type": 66, + "type": 87, "optional": false }, { "name": "kind", - "type": 67, + "type": 88, "optional": false }, { @@ -12711,12 +13175,12 @@ "properties": [ { "name": "form", - "type": 69, + "type": 90, "optional": false }, { "name": "kind", - "type": 70, + "type": 91, "optional": false }, { @@ -12740,12 +13204,12 @@ "properties": [ { "name": "form", - "type": 66, + "type": 87, "optional": false }, { "name": "kind", - "type": 72, + "type": 46, "optional": false }, { @@ -12756,21 +13220,17 @@ ], "indices": [] }, - { - "kind": "literal", - "value": "hooks-claude-code" - }, { "kind": "object", "properties": [ { "name": "form", - "type": 66, + "type": 87, "optional": false }, { "name": "kind", - "type": 74, + "type": 48, "optional": false }, { @@ -12781,60 +13241,6 @@ ], "indices": [] }, - { - "kind": "literal", - "value": "hooks-codex" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 66, - "optional": false - }, - { - "name": "kind", - "type": 76, - "optional": false - }, - { - "name": "summary", - "type": 10, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "model-selection" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 66, - "optional": false - }, - { - "name": "kind", - "type": 78, - "optional": false - }, - { - "name": "summary", - "type": 10, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "plan-mode" - }, { "kind": "object", "properties": [ @@ -12850,17 +13256,17 @@ }, { "name": "changes", - "type": 80, + "type": 95, "optional": false }, { "name": "form", - "type": 86, + "type": 101, "optional": false }, { "name": "kind", - "type": 87, + "type": 102, "optional": false } ], @@ -12868,14 +13274,14 @@ }, { "kind": "array", - "element": 81 + "element": 96 }, { "kind": "object", "properties": [ { "name": "action", - "type": 82, + "type": 97, "optional": false }, { @@ -12899,9 +13305,9 @@ { "kind": "union", "types": [ - 83, - 84, - 85 + 98, + 99, + 100 ] }, { @@ -12934,12 +13340,12 @@ }, { "name": "kind", - "type": 89, + "type": 104, "optional": false }, { "name": "outcome", - "type": 90, + "type": 105, "optional": false } ], @@ -12963,7 +13369,7 @@ }, { "name": "kind", - "type": 58, + "type": 76, "optional": false }, { @@ -12979,12 +13385,12 @@ "properties": [ { "name": "form", - "type": 66, + "type": 87, "optional": false }, { "name": "kind", - "type": 93, + "type": 50, "optional": false }, { @@ -12995,10 +13401,6 @@ ], "indices": [] }, - { - "kind": "literal", - "value": "repeat-tool-reminder" - }, { "kind": "object", "properties": [ @@ -13009,7 +13411,7 @@ }, { "name": "kind", - "type": 95, + "type": 109, "optional": false }, { @@ -13034,12 +13436,12 @@ }, { "name": "form", - "type": 66, + "type": 87, "optional": false }, { "name": "kind", - "type": 97, + "type": 111, "optional": false }, { @@ -13074,12 +13476,12 @@ "properties": [ { "name": "form", - "type": 66, + "type": 87, "optional": false }, { "name": "kind", - "type": 99, + "type": 52, "optional": false }, { @@ -13090,26 +13492,22 @@ ], "indices": [] }, - { - "kind": "literal", - "value": "runtime-context" - }, { "kind": "object", "properties": [ { "name": "entries", - "type": 101, + "type": 114, "optional": false }, { "name": "form", - "type": 103, + "type": 116, "optional": false }, { "name": "kind", - "type": 104, + "type": 117, "optional": false }, { @@ -13122,7 +13520,7 @@ }, { "kind": "array", - "element": 102 + "element": 115 }, { "kind": "object", @@ -13153,12 +13551,12 @@ "properties": [ { "name": "form", - "type": 86, + "type": 101, "optional": false }, { "name": "kind", - "type": 106, + "type": 119, "optional": false }, { @@ -13178,22 +13576,22 @@ "properties": [ { "name": "form", - "type": 108, + "type": 121, "optional": false }, { "name": "kind", - "type": 109, + "type": 122, "optional": false }, { "name": "references", - "type": 110, + "type": 123, "optional": false }, { "name": "version", - "type": 114, + "type": 127, "optional": false } ], @@ -13209,7 +13607,7 @@ }, { "kind": "array", - "element": 111 + "element": 124 }, { "kind": "object", @@ -13221,7 +13619,7 @@ }, { "name": "capturedThroughSeq", - "type": 112, + "type": 125, "optional": false }, { @@ -13276,7 +13674,7 @@ "kind": "union", "types": [ 2, - 113 + 126 ] }, { @@ -13297,7 +13695,7 @@ }, { "name": "kind", - "type": 72, + "type": 46, "optional": false }, { @@ -13316,123 +13714,14 @@ "type": 41, "optional": false }, - { - "name": "kind", - "type": 74, - "optional": false - }, - { - "name": "sections", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 41, - "optional": false - }, - { - "name": "kind", - "type": 76, - "optional": false - }, - { - "name": "sections", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 41, - "optional": false - }, - { - "name": "kind", - "type": 78, - "optional": false - }, - { - "name": "sections", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 41, - "optional": false - }, - { - "name": "kind", - "type": 93, - "optional": false - }, - { - "name": "sections", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 66, - "optional": false - }, - { - "name": "kind", - "type": 121, - "optional": false - }, - { - "name": "summary", - "type": 10, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "schedule" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 66, - "optional": false - }, { "name": "kind", "type": 48, "optional": false }, { - "name": "summary", - "type": 10, + "name": "sections", + "type": 43, "optional": false } ], @@ -13443,7 +13732,7 @@ "properties": [ { "name": "form", - "type": 66, + "type": 41, "optional": false }, { @@ -13452,8 +13741,8 @@ "optional": false }, { - "name": "summary", - "type": 10, + "name": "sections", + "type": 43, "optional": false } ], @@ -13464,28 +13753,7 @@ "properties": [ { "name": "form", - "type": 66, - "optional": false - }, - { - "name": "kind", - "type": 52, - "optional": false - }, - { - "name": "summary", - "type": 10, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 66, + "type": 87, "optional": false }, { @@ -13506,7 +13774,91 @@ "properties": [ { "name": "form", - "type": 66, + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 87, "optional": false }, { @@ -13527,12 +13879,12 @@ "properties": [ { "name": "form", - "type": 66, + "type": 87, "optional": false }, { "name": "kind", - "type": 63, + "type": 64, "optional": false }, { @@ -13548,28 +13900,17 @@ "properties": [ { "name": "form", - "type": 86, + "type": 87, "optional": false }, { "name": "kind", "type": 72, "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, - "optional": false }, { - "name": "kind", - "type": 74, + "name": "summary", + "type": 10, "optional": false } ], @@ -13580,29 +13921,18 @@ "properties": [ { "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 76, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, + "type": 87, "optional": false }, { "name": "kind", "type": 78, "optional": false + }, + { + "name": "summary", + "type": 10, + "optional": false } ], "indices": [] @@ -13612,12 +13942,17 @@ "properties": [ { "name": "form", - "type": 86, + "type": 87, "optional": false }, { "name": "kind", - "type": 93, + "type": 81, + "optional": false + }, + { + "name": "summary", + "type": 10, "optional": false } ], @@ -13628,12 +13963,12 @@ "properties": [ { "name": "form", - "type": 86, + "type": 101, "optional": false }, { "name": "kind", - "type": 99, + "type": 46, "optional": false } ], @@ -13644,23 +13979,7 @@ "properties": [ { "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 121, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, + "type": 101, "optional": false }, { @@ -13676,7 +13995,7 @@ "properties": [ { "name": "form", - "type": 86, + "type": 101, "optional": false }, { @@ -13692,7 +14011,7 @@ "properties": [ { "name": "form", - "type": 86, + "type": 101, "optional": false }, { @@ -13708,28 +14027,7 @@ "properties": [ { "name": "form", - "type": 41, - "optional": false - }, - { - "name": "kind", - "type": 99, - "optional": false - }, - { - "name": "sections", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, + "type": 101, "optional": false }, { @@ -13745,124 +14043,12 @@ "properties": [ { "name": "form", - "type": 86, + "type": 101, "optional": false }, { "name": "kind", - "type": 42, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 86, - "optional": false - }, - { - "name": "kind", - "type": 63, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 108, - "optional": false - }, - { - "name": "kind", - "type": 72, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 108, - "optional": false - }, - { - "name": "kind", - "type": 74, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 108, - "optional": false - }, - { - "name": "kind", - "type": 76, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 108, - "optional": false - }, - { - "name": "kind", - "type": 78, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 108, - "optional": false - }, - { - "name": "kind", - "type": 93, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 108, - "optional": false - }, - { - "name": "kind", - "type": 99, + "type": 58, "optional": false } ], @@ -13878,7 +14064,7 @@ }, { "name": "kind", - "type": 121, + "type": 52, "optional": false }, { @@ -13894,12 +14080,12 @@ "properties": [ { "name": "form", - "type": 108, + "type": 101, "optional": false }, { "name": "kind", - "type": 121, + "type": 60, "optional": false } ], @@ -13910,7 +14096,156 @@ "properties": [ { "name": "form", - "type": 108, + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 42, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 78, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 101, + "optional": false + }, + { + "name": "kind", + "type": 81, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 41, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, "optional": false }, { @@ -13926,7 +14261,7 @@ "properties": [ { "name": "form", - "type": 108, + "type": 121, "optional": false }, { @@ -13942,7 +14277,7 @@ "properties": [ { "name": "form", - "type": 108, + "type": 121, "optional": false }, { @@ -13958,7 +14293,7 @@ "properties": [ { "name": "form", - "type": 108, + "type": 121, "optional": false }, { @@ -13974,7 +14309,71 @@ "properties": [ { "name": "form", - "type": 108, + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, "optional": false }, { @@ -13985,54 +14384,6 @@ ], "indices": [] }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 108, - "optional": false - }, - { - "name": "kind", - "type": 63, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 69, - "optional": false - }, - { - "name": "kind", - "type": 72, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 69, - "optional": false - }, - { - "name": "kind", - "type": 74, - "optional": false - } - ], - "indices": [] - }, { "kind": "object", "properties": [ @@ -14043,7 +14394,7 @@ }, { "name": "kind", - "type": 48, + "type": 58, "optional": false }, { @@ -14059,12 +14410,12 @@ "properties": [ { "name": "form", - "type": 69, + "type": 121, "optional": false }, { "name": "kind", - "type": 76, + "type": 64, "optional": false } ], @@ -14075,7 +14426,23 @@ "properties": [ { "name": "form", - "type": 69, + "type": 121, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 121, "optional": false }, { @@ -14091,45 +14458,13 @@ "properties": [ { "name": "form", - "type": 69, - "optional": false - }, - { - "name": "kind", - "type": 93, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 69, - "optional": false - }, - { - "name": "kind", - "type": 99, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 69, - "optional": false - }, - { - "name": "kind", "type": 121, "optional": false + }, + { + "name": "kind", + "type": 81, + "optional": false } ], "indices": [] @@ -14139,7 +14474,23 @@ "properties": [ { "name": "form", - "type": 69, + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 46, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, "optional": false }, { @@ -14155,7 +14506,7 @@ "properties": [ { "name": "form", - "type": 69, + "type": 90, "optional": false }, { @@ -14171,7 +14522,7 @@ "properties": [ { "name": "form", - "type": 69, + "type": 90, "optional": false }, { @@ -14187,7 +14538,7 @@ "properties": [ { "name": "form", - "type": 69, + "type": 90, "optional": false }, { @@ -14208,7 +14559,7 @@ }, { "name": "kind", - "type": 50, + "type": 60, "optional": false }, { @@ -14224,7 +14575,71 @@ "properties": [ { "name": "form", - "type": 69, + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, "optional": false }, { @@ -14240,12 +14655,12 @@ "properties": [ { "name": "form", - "type": 69, + "type": 90, "optional": false }, { "name": "kind", - "type": 63, + "type": 64, "optional": false } ], @@ -14256,7 +14671,7 @@ "properties": [ { "name": "form", - "type": 103, + "type": 90, "optional": false }, { @@ -14272,39 +14687,7 @@ "properties": [ { "name": "form", - "type": 103, - "optional": false - }, - { - "name": "kind", - "type": 74, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 103, - "optional": false - }, - { - "name": "kind", - "type": 76, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 103, + "type": 90, "optional": false }, { @@ -14320,12 +14703,12 @@ "properties": [ { "name": "form", - "type": 103, + "type": 90, "optional": false }, { "name": "kind", - "type": 93, + "type": 81, "optional": false } ], @@ -14336,44 +14719,12 @@ "properties": [ { "name": "form", - "type": 103, + "type": 116, "optional": false }, { "name": "kind", - "type": 99, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 103, - "optional": false - }, - { - "name": "kind", - "type": 121, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 103, - "optional": false - }, - { - "name": "kind", - "type": 48, + "type": 46, "optional": false } ], @@ -14389,7 +14740,7 @@ }, { "name": "kind", - "type": 52, + "type": 62, "optional": false }, { @@ -14405,7 +14756,23 @@ "properties": [ { "name": "form", - "type": 103, + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, "optional": false }, { @@ -14421,7 +14788,7 @@ "properties": [ { "name": "form", - "type": 103, + "type": 116, "optional": false }, { @@ -14437,7 +14804,7 @@ "properties": [ { "name": "form", - "type": 103, + "type": 116, "optional": false }, { @@ -14453,7 +14820,71 @@ "properties": [ { "name": "form", - "type": 103, + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 58, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 60, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 62, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 68, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, "optional": false }, { @@ -14469,12 +14900,81 @@ "properties": [ { "name": "form", - "type": 103, + "type": 41, "optional": false }, { "name": "kind", - "type": 63, + "type": 68, + "optional": false + }, + { + "name": "sections", + "type": 43, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 64, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 72, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 78, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 116, + "optional": false + }, + { + "name": "kind", + "type": 81, "optional": false } ], @@ -14490,7 +14990,7 @@ }, { "name": "kind", - "type": 186, + "type": 204, "optional": false }, { @@ -14515,7 +15015,7 @@ "properties": [ { "name": "kind", - "type": 188, + "type": 206, "optional": false }, { @@ -14550,7 +15050,7 @@ "properties": [ { "name": "kind", - "type": 190, + "type": 208, "optional": false }, { @@ -14565,7 +15065,7 @@ }, { "name": "replayState", - "type": 191, + "type": 209, "optional": true } ], @@ -14579,119 +15079,6 @@ "kind": "opaque", "reason": "unknown" }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 41, - "optional": false - }, - { - "name": "kind", - "type": 54, - "optional": false - }, - { - "name": "sections", - "type": 43, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 72, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 74, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 76, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 78, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 198, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "ptc-mode" - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 93, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 99, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 121, - "optional": false - } - ], - "indices": [] - }, { "kind": "object", "properties": [ @@ -14707,7 +15094,7 @@ }, { "name": "role", - "type": 58, + "type": 76, "optional": false }, { @@ -14721,6 +15108,7 @@ "discriminator": "kind", "unknownKinds": "preserve", "attributionKinds": [ + "claude-code-mods", "tmux-context", "user-question-reply" ] @@ -14744,12 +15132,12 @@ }, { "name": "role", - "type": 204, + "type": 212, "optional": false }, { "name": "source", - "type": 189, + "type": 207, "optional": false } ], @@ -14774,12 +15162,12 @@ }, { "name": "role", - "type": 206, + "type": 214, "optional": false }, { "name": "source", - "type": 45, + "type": 65, "optional": false } ], @@ -18726,7 +19114,7 @@ "kind": "event", "event": "user/message", "surface": true, - "digest": "b83ed1b1cfffbd7bd5cca06ea72e44be57beb68b39e5a96660ce42a9e21aa411", + "digest": "83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628", "schema": { "root": 0, "nodes": [ @@ -18750,12 +19138,12 @@ }, { "name": "sourceEventSeqs", - "type": 196, + "type": 204, "optional": true }, { "name": "surfaceOp", - "type": 197, + "type": 205, "optional": false }, { @@ -18765,7 +19153,7 @@ }, { "name": "type", - "type": 200, + "type": 208, "optional": false } ], @@ -18800,6 +19188,7 @@ "discriminator": "kind", "unknownKinds": "preserve", "attributionKinds": [ + "claude-code-mods", "tmux-context", "user-question-reply" ] @@ -19120,11 +19509,10 @@ 47, 49, 51, - 54, - 56, - 58, + 53, + 55, + 57, 60, - 61, 62, 63, 64, @@ -19133,11 +19521,11 @@ 67, 68, 69, + 70, 71, 72, 73, 74, - 75, 76, 77, 78, @@ -19146,12 +19534,12 @@ 81, 82, 83, + 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, @@ -19159,67 +19547,75 @@ 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, + 112, 113, + 114, 115, + 116, + 117, 118, 119, - 120, 121, - 122, - 124, - 125, + 123, 126, 127, + 128, 129, 130, - 131, 132, 133, + 134, 135, - 136, 137, + 138, 139, 140, + 141, + 143, + 144, + 145, + 146, 148, - 151, - 153, - 154, - 156, - 158, + 149, + 157, + 160, 162, - 164, - 172, - 176, - 177, - 178, - 179, - 180, + 163, + 165, + 167, + 171, + 173, 181, - 182, - 183, - 184, 185, 186, 187, 188, + 189, 190, + 191, 192, 193, 194, - 195 + 195, + 196, + 197, + 198, + 200, + 202, + 203 ] }, { @@ -19249,7 +19645,7 @@ }, { "kind": "literal", - "value": "repeat-tool-reminder" + "value": "model-selection" }, { "kind": "object", @@ -19272,6 +19668,56 @@ ], "indices": [] }, + { + "kind": "literal", + "value": "plan-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 38, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "repeat-tool-reminder" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 40, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, { "kind": "literal", "value": "runtime-context" @@ -19286,7 +19732,7 @@ }, { "name": "kind", - "type": 38, + "type": 42, "optional": false }, { @@ -19311,7 +19757,7 @@ }, { "name": "kind", - "type": 40, + "type": 44, "optional": false }, { @@ -19336,7 +19782,7 @@ }, { "name": "kind", - "type": 42, + "type": 46, "optional": false }, { @@ -19361,7 +19807,7 @@ }, { "name": "kind", - "type": 44, + "type": 48, "optional": false }, { @@ -19386,7 +19832,7 @@ }, { "name": "kind", - "type": 46, + "type": 50, "optional": false }, { @@ -19411,7 +19857,7 @@ }, { "name": "kind", - "type": 48, + "type": 52, "optional": false }, { @@ -19436,7 +19882,32 @@ }, { "name": "kind", - "type": 50, + "type": 54, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "claude-code-mods" + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 56, "optional": false }, { @@ -19456,12 +19927,12 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { "name": "kind", - "type": 53, + "type": 59, "optional": false } ], @@ -19480,12 +19951,12 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { "name": "kind", - "type": 55, + "type": 61, "optional": false } ], @@ -19500,47 +19971,7 @@ "properties": [ { "name": "form", - "type": 52, - "optional": false - }, - { - "name": "kind", - "type": 57, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "model-selection" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 52, - "optional": false - }, - { - "name": "kind", - "type": 59, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "literal", - "value": "plan-mode" - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19556,7 +19987,7 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19572,7 +20003,7 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19588,7 +20019,7 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19604,7 +20035,7 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19620,7 +20051,7 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19636,7 +20067,7 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19652,7 +20083,7 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19668,7 +20099,7 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { @@ -19684,12 +20115,60 @@ "properties": [ { "name": "form", - "type": 70, + "type": 58, "optional": false }, { "name": "kind", - "type": 53, + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 58, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 59, "optional": false } ], @@ -19704,12 +20183,12 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { "name": "kind", - "type": 55, + "type": 61, "optional": false } ], @@ -19720,39 +20199,7 @@ "properties": [ { "name": "form", - "type": 70, - "optional": false - }, - { - "name": "kind", - "type": 57, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 70, - "optional": false - }, - { - "name": "kind", - "type": 59, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19768,7 +20215,7 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19784,7 +20231,7 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19800,7 +20247,7 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19816,7 +20263,7 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19832,7 +20279,7 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19848,7 +20295,7 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19864,7 +20311,7 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19880,7 +20327,7 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { @@ -19896,12 +20343,60 @@ "properties": [ { "name": "form", - "type": 84, + "type": 75, "optional": false }, { "name": "kind", - "type": 53, + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 75, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 59, "optional": false } ], @@ -19916,12 +20411,12 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { "name": "kind", - "type": 55, + "type": 61, "optional": false } ], @@ -19932,39 +20427,7 @@ "properties": [ { "name": "form", - "type": 84, - "optional": false - }, - { - "name": "kind", - "type": 57, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 84, - "optional": false - }, - { - "name": "kind", - "type": 59, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -19980,7 +20443,7 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -19996,7 +20459,7 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -20012,7 +20475,7 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -20028,7 +20491,7 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -20044,7 +20507,7 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -20060,7 +20523,7 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -20076,7 +20539,7 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -20092,7 +20555,7 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { @@ -20108,12 +20571,60 @@ "properties": [ { "name": "form", - "type": 98, + "type": 90, "optional": false }, { "name": "kind", - "type": 53, + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 90, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 59, "optional": false } ], @@ -20128,12 +20639,12 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { "name": "kind", - "type": 55, + "type": 61, "optional": false } ], @@ -20144,39 +20655,7 @@ "properties": [ { "name": "form", - "type": 98, - "optional": false - }, - { - "name": "kind", - "type": 57, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 98, - "optional": false - }, - { - "name": "kind", - "type": 59, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20192,7 +20671,7 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20208,7 +20687,7 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20224,7 +20703,7 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20240,7 +20719,7 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20256,7 +20735,7 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20272,7 +20751,7 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20288,7 +20767,7 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20304,7 +20783,7 @@ "properties": [ { "name": "form", - "type": 98, + "type": 105, "optional": false }, { @@ -20315,6 +20794,54 @@ ], "indices": [] }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 105, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + } + ], + "indices": [] + }, { "kind": "object", "properties": [ @@ -20325,7 +20852,7 @@ }, { "name": "kind", - "type": 112, + "type": 120, "optional": false }, { @@ -20350,7 +20877,7 @@ "properties": [ { "name": "kind", - "type": 114, + "type": 122, "optional": false }, { @@ -20385,7 +20912,7 @@ "properties": [ { "name": "kind", - "type": 116, + "type": 124, "optional": false }, { @@ -20400,7 +20927,7 @@ }, { "name": "replayState", - "type": 117, + "type": 125, "optional": true } ], @@ -20414,39 +20941,6 @@ "kind": "opaque", "reason": "unknown" }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 53, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 55, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 57, - "optional": false - } - ], - "indices": [] - }, { "kind": "object", "properties": [ @@ -20463,16 +20957,12 @@ "properties": [ { "name": "kind", - "type": 123, + "type": 61, "optional": false } ], "indices": [] }, - { - "kind": "literal", - "value": "ptc-mode" - }, { "kind": "object", "properties": [ @@ -20500,18 +20990,7 @@ "properties": [ { "name": "kind", - "type": 38, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "kind", - "type": 128, + "type": 131, "optional": false } ], @@ -20519,7 +20998,18 @@ }, { "kind": "literal", - "value": "system-prompt" + "value": "ptc-mode" + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 38, + "optional": false + } + ], + "indices": [] }, { "kind": "object", @@ -20543,6 +21033,21 @@ ], "indices": [] }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 136, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "literal", + "value": "system-prompt" + }, { "kind": "object", "properties": [ @@ -20570,7 +21075,29 @@ "properties": [ { "name": "kind", - "type": 134, + "type": 48, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 50, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 142, "optional": false } ], @@ -20596,7 +21123,7 @@ "properties": [ { "name": "kind", - "type": 48, + "type": 52, "optional": false } ], @@ -20607,7 +21134,18 @@ "properties": [ { "name": "kind", - "type": 138, + "type": 54, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "kind", + "type": 147, "optional": false } ], @@ -20622,7 +21160,7 @@ "properties": [ { "name": "kind", - "type": 50, + "type": 56, "optional": false } ], @@ -20643,17 +21181,17 @@ }, { "name": "changes", - "type": 141, + "type": 150, "optional": false }, { "name": "form", - "type": 52, + "type": 58, "optional": false }, { "name": "kind", - "type": 147, + "type": 156, "optional": false } ], @@ -20661,14 +21199,14 @@ }, { "kind": "array", - "element": 142 + "element": 151 }, { "kind": "object", "properties": [ { "name": "action", - "type": 143, + "type": 152, "optional": false }, { @@ -20692,9 +21230,9 @@ { "kind": "union", "types": [ - 144, - 145, - 146 + 153, + 154, + 155 ] }, { @@ -20723,12 +21261,12 @@ }, { "name": "kind", - "type": 149, + "type": 158, "optional": false }, { "name": "outcome", - "type": 150, + "type": 159, "optional": false } ], @@ -20752,7 +21290,7 @@ }, { "name": "kind", - "type": 152, + "type": 161, "optional": false } ], @@ -20793,7 +21331,7 @@ }, { "name": "kind", - "type": 155, + "type": 164, "optional": false }, { @@ -20823,7 +21361,7 @@ }, { "name": "kind", - "type": 157, + "type": 166, "optional": false }, { @@ -20858,17 +21396,17 @@ "properties": [ { "name": "entries", - "type": 159, + "type": 168, "optional": false }, { "name": "form", - "type": 98, + "type": 105, "optional": false }, { "name": "kind", - "type": 161, + "type": 170, "optional": false }, { @@ -20881,7 +21419,7 @@ }, { "kind": "array", - "element": 160 + "element": 169 }, { "kind": "object", @@ -20908,12 +21446,12 @@ "properties": [ { "name": "form", - "type": 52, + "type": 58, "optional": false }, { "name": "kind", - "type": 163, + "type": 172, "optional": false }, { @@ -20933,22 +21471,22 @@ "properties": [ { "name": "form", - "type": 70, + "type": 75, "optional": false }, { "name": "kind", - "type": 165, + "type": 174, "optional": false }, { "name": "references", - "type": 166, + "type": 175, "optional": false }, { "name": "version", - "type": 171, + "type": 180, "optional": false } ], @@ -20960,7 +21498,7 @@ }, { "kind": "array", - "element": 167 + "element": 176 }, { "kind": "object", @@ -20972,12 +21510,12 @@ }, { "name": "capturedThroughSeq", - "type": 168, + "type": 177, "optional": false }, { "name": "compacted", - "type": 170, + "type": 179, "optional": false }, { @@ -21017,7 +21555,7 @@ }, { "name": "truncated", - "type": 170, + "type": 179, "optional": false } ], @@ -21027,7 +21565,7 @@ "kind": "union", "types": [ 18, - 169 + 178 ] }, { @@ -21047,17 +21585,17 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { "name": "kind", - "type": 53, + "type": 59, "optional": false }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21069,7 +21607,7 @@ }, { "kind": "array", - "element": 175 + "element": 184 }, { "kind": "object", @@ -21092,17 +21630,17 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { "name": "kind", - "type": 55, + "type": 61, "optional": false }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21113,49 +21651,7 @@ "properties": [ { "name": "form", - "type": 173, - "optional": false - }, - { - "name": "kind", - "type": 57, - "optional": false - }, - { - "name": "sections", - "type": 174, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 173, - "optional": false - }, - { - "name": "kind", - "type": 59, - "optional": false - }, - { - "name": "sections", - "type": 174, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21165,7 +21661,7 @@ }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21176,7 +21672,7 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21186,7 +21682,7 @@ }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21197,7 +21693,7 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21207,7 +21703,7 @@ }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21218,7 +21714,7 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21228,7 +21724,7 @@ }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21239,7 +21735,7 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21249,7 +21745,7 @@ }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21260,7 +21756,7 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21270,7 +21766,7 @@ }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21281,7 +21777,7 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21291,7 +21787,7 @@ }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21302,7 +21798,7 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21312,7 +21808,7 @@ }, { "name": "sections", - "type": 174, + "type": 183, "optional": false } ], @@ -21323,7 +21819,7 @@ "properties": [ { "name": "form", - "type": 173, + "type": 182, "optional": false }, { @@ -21333,7 +21829,70 @@ }, { "name": "sections", - "type": 174, + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 52, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 54, + "optional": false + }, + { + "name": "sections", + "type": 183, + "optional": false + } + ], + "indices": [] + }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 182, + "optional": false + }, + { + "name": "kind", + "type": 56, + "optional": false + }, + { + "name": "sections", + "type": 183, "optional": false } ], @@ -21349,7 +21908,7 @@ }, { "name": "kind", - "type": 189, + "type": 199, "optional": false }, { @@ -21374,12 +21933,12 @@ "properties": [ { "name": "form", - "type": 84, + "type": 90, "optional": false }, { "name": "kind", - "type": 191, + "type": 201, "optional": false }, { @@ -21394,69 +21953,6 @@ "kind": "literal", "value": "agent-message" }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 33, - "optional": false - }, - { - "name": "kind", - "type": 53, - "optional": false - }, - { - "name": "summary", - "type": 5, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 33, - "optional": false - }, - { - "name": "kind", - "type": 55, - "optional": false - }, - { - "name": "summary", - "type": 5, - "optional": false - } - ], - "indices": [] - }, - { - "kind": "object", - "properties": [ - { - "name": "form", - "type": 33, - "optional": false - }, - { - "name": "kind", - "type": 57, - "optional": false - }, - { - "name": "summary", - "type": 5, - "optional": false - } - ], - "indices": [] - }, { "kind": "object", "properties": [ @@ -21478,6 +21974,27 @@ ], "indices": [] }, + { + "kind": "object", + "properties": [ + { + "name": "form", + "type": 33, + "optional": false + }, + { + "name": "kind", + "type": 61, + "optional": false + }, + { + "name": "summary", + "type": 5, + "optional": false + } + ], + "indices": [] + }, { "kind": "array", "element": 18 @@ -21485,8 +22002,8 @@ { "kind": "union", "types": [ - 198, - 199 + 206, + 207 ] }, { @@ -21503,7 +22020,7 @@ }, { "name": "op", - "type": 145, + "type": 154, "optional": false }, { @@ -22005,15 +22522,6 @@ "names": [], "sources": [] }, - { - "digest": "1020bbc3c744c060cd31c0891ae79b9e4c3e0adfa3f0be49869309b34e9ef200", - "names": [ - "packages/llm/llm/src/message.ts#DeveloperMessage" - ], - "sources": [ - "packages/llm/llm/src/message.ts:157" - ] - }, { "digest": "1033093edd0db80ff410e00830b523405e00bb0c7684948e531ff65095799625", "names": [], @@ -22077,13 +22585,6 @@ "names": [], "sources": [] }, - { - "digest": "149c5a77529cb0dc06f5ceb6273069b7606174d1e7893b5f9b1071c15306b846", - "names": [], - "sources": [ - "packages/core/agent/src/types.ts:96" - ] - }, { "digest": "15196447222782e773eb943c92b18316ce96b9af0f0cfddb6e57ba8274ecc5ff", "names": [], @@ -22128,11 +22629,6 @@ "packages/core/session/src/types.ts:427" ] }, - { - "digest": "186159f5f6f67a0b8cd095b8fe55bef42d4f25ca1a1c248f859867af2ece0467", - "names": [], - "sources": [] - }, { "digest": "18dc21f12bbac8ade8377e6fbb54179e20c3d955d73fba3244c57fb6bf001df0", "names": [ @@ -22402,15 +22898,6 @@ "names": [], "sources": [] }, - { - "digest": "26df091b55c8a9b58004931ce5425b441201032cfab0b75e2ba03f428ecbd116", - "names": [ - "packages/llm/llm/src/message.ts#MessageSource" - ], - "sources": [ - "packages/llm/llm/src/message.ts:136" - ] - }, { "digest": "2765e9119edf5816ac1ab4c95c5ee32c90403f3742962eff11544f7752d59d2e", "names": [], @@ -22487,6 +22974,11 @@ "names": [], "sources": [] }, + { + "digest": "2e285da62c0ed94e6e4bfbb9161e4090502ff95e379e753ebabe745e60eb7d5c", + "names": [], + "sources": [] + }, { "digest": "2e3523541e942ef6cc9fbc5b91f242e0ca4c6ada479b79b96025b797f6e41f3f", "names": [ @@ -22598,6 +23090,11 @@ "names": [], "sources": [] }, + { + "digest": "35bc598be70e0b9646ccdf5629b55bb80ba5a9cf49723ecf49957abe0a12cf99", + "names": [], + "sources": [] + }, { "digest": "36d6a9a871d949ea4f3edd919d81d66aa69218bebef38899d2a1282c39420a1f", "names": [], @@ -22742,11 +23239,6 @@ "names": [], "sources": [] }, - { - "digest": "3f42f45cbb8a06586d247e5f3763dc02c8d31a145a9a3b82fabcd101e7e224cc", - "names": [], - "sources": [] - }, { "digest": "3f74ba74acd10644c1ed6947a411d8088bd2cfac74f61bb8540f96d68e31f004", "names": [], @@ -22761,6 +23253,13 @@ "packages/schedule/schedule/src/types.ts:161" ] }, + { + "digest": "40056a392eceb4445d649205e99900fe770ce3d3beb4b10301c1e50d2095e9ef", + "names": [], + "sources": [ + "packages/core/agent/src/types.ts:96" + ] + }, { "digest": "408927267422a7b2beb6df354ff7c1406d929a5646554f48adf85eb8372f811c", "names": [], @@ -23023,6 +23522,11 @@ "names": [], "sources": [] }, + { + "digest": "5163154425bd5e0d6b2617b7d3d9816ee04bd237a030decbbececac66a36c510", + "names": [], + "sources": [] + }, { "digest": "516da4cdd6d2f1e5ce488e648578ca51f40e458f707b803e5b24de870e799415", "names": [], @@ -23148,11 +23652,6 @@ "names": [], "sources": [] }, - { - "digest": "5a92f12db2059c282cdea45b6845c65e6f4a95dd3d649aeddbde6f9ae03c561f", - "names": [], - "sources": [] - }, { "digest": "5abab3a27fd2736660ecddd94b28d6bbf114986dd894880153bd1be3ccacb563", "names": [], @@ -23227,6 +23726,11 @@ "names": [], "sources": [] }, + { + "digest": "5e78e8bebe50889028294fa6918b9bd340a5f1a26f12418d47a2b1cfa637b749", + "names": [], + "sources": [] + }, { "digest": "5ebce1317eacd2c148456627a09668e927ee15c204f0643f5ea76b46b54a2647", "names": [], @@ -23270,11 +23774,6 @@ "names": [], "sources": [] }, - { - "digest": "6178f1edcb23f361f2a6cb2c859b1c2187220d5695ece4a3400e0d92845a7178", - "names": [], - "sources": [] - }, { "digest": "6292bcbedaa97feaec5b48a03bb8144b581b93309d9d6c905f26da7a6a7a0a4f", "names": [], @@ -23393,6 +23892,15 @@ "names": [], "sources": [] }, + { + "digest": "6bad5e2ed590f9440817fa6f5b5e3b94acd677fc5169a931238b056e5b3057f9", + "names": [ + "packages/llm/llm/src/message.ts#DeveloperMessage" + ], + "sources": [ + "packages/llm/llm/src/message.ts:157" + ] + }, { "digest": "6bbe793f6a93dc8d50a4d4d3fbb6f8ed15650bcb7e151d28669628f7b7c3fc25", "names": [ @@ -23736,6 +24244,11 @@ "packages/context/agent-instructions/src/render.ts:47" ] }, + { + "digest": "83e827b9ea6b4d7309bcf7f1f97040f4750d4ddaf5f19bf18e4cde7605c7e628", + "names": [], + "sources": [] + }, { "digest": "8465ece5e9d9007c038b357015d849c15262cb0bc25abc59e47692d87ee01a15", "names": [ @@ -23948,6 +24461,11 @@ "names": [], "sources": [] }, + { + "digest": "93845ea41cc823c2ae619309ab2b4465363431b15a04d7d7877b65845ae44c3a", + "names": [], + "sources": [] + }, { "digest": "938f7c7357bc403ea3b281a9dd016939df7a0880fc8db12612585be574d0d615", "names": [], @@ -24078,6 +24596,15 @@ "names": [], "sources": [] }, + { + "digest": "9d38c01a12d75b16898d0ac01f3bd47e8d4bb1e1a074f22926aaf30d457165c3", + "names": [ + "packages/session/session-title-llm/src/index.ts#SessionTitleLlmRequestEventData" + ], + "sources": [ + "packages/session/session-title-llm/src/index.ts:34" + ] + }, { "digest": "9d80129e13edfb6feb03ed327e6085c6ef60b97ad074244b2f5fd4667424a6a4", "names": [], @@ -24158,6 +24685,15 @@ "names": [], "sources": [] }, + { + "digest": "a27c8b2ff9bf8cbf2fbea1e025535ac6692c49e1d2c00e907e63626b6438fb47", + "names": [ + "packages/llm/llm/src/message.ts#UserMessage" + ], + "sources": [ + "packages/llm/llm/src/message.ts:162" + ] + }, { "digest": "a2b49331716ba0a7078b026d145306a70eae64b7313c3e6204bd931e75ec3bb1", "names": [], @@ -24193,14 +24729,12 @@ ] }, { - "digest": "a5d80b5a2f3a6bbaf8f0334126632b78f94d8346122c68e8122ea89906d63d26", + "digest": "a6567ccb2e530606b775371eb4fa31468d72084339968e8b0440a516a23b39dc", "names": [], - "sources": [ - "packages/core/session/src/types.ts:311" - ] + "sources": [] }, { - "digest": "a6567ccb2e530606b775371eb4fa31468d72084339968e8b0440a516a23b39dc", + "digest": "a66a864d5c1ed32387c7b45603e567d9ed2fc5559a96926fdebfff7c753f7c0f", "names": [], "sources": [] }, @@ -24372,11 +24906,6 @@ "names": [], "sources": [] }, - { - "digest": "ae84c5e493f94acc63cfb70389073ba616ed7ae7aa4fadde048bdcc64d49bb46", - "names": [], - "sources": [] - }, { "digest": "aec150797b655c5f15ebb7a501454b0c208fbaccec9ace49d7de2171fc02f47d", "names": [], @@ -24582,11 +25111,6 @@ "packages/goal/goal/src/types.ts:45" ] }, - { - "digest": "b83ed1b1cfffbd7bd5cca06ea72e44be57beb68b39e5a96660ce42a9e21aa411", - "names": [], - "sources": [] - }, { "digest": "b8fc636a2121df9d8423c242e03c9d23b7ab7c6fdce00e45746932c60b730f97", "names": [ @@ -24719,6 +25243,11 @@ "names": [], "sources": [] }, + { + "digest": "c52b232ec5d7f7dec08d3f6059318e3e67e753e9028cdca607ddc53b90f44a08", + "names": [], + "sources": [] + }, { "digest": "c55827fba7018198772c50e37cb7d3f60a3d8bb6e94b07de72dee0d9f8968005", "names": [], @@ -24778,16 +25307,12 @@ "sources": [] }, { - "digest": "cb7b863ddb7b3eab5f18f216602feba25f47b456e73e4e72b6bcc259875c6dab", - "names": [ - "packages/llm/llm/src/message.ts#Message" - ], - "sources": [ - "packages/llm/llm/src/message.ts:196" - ] + "digest": "cbca49bde16b656d5b8fbe4a8a507769f7b594c6287782b809c03814941bc5e3", + "names": [], + "sources": [] }, { - "digest": "cbca49bde16b656d5b8fbe4a8a507769f7b594c6287782b809c03814941bc5e3", + "digest": "ccea69c3f118930ca9679e93a4347978995180ec87b997e2cb075ad5caac4a38", "names": [], "sources": [] }, @@ -24814,18 +25339,14 @@ ] }, { - "digest": "cf6e3aaf1e2de6480aa0157730a41b9a492108a55304100b0f7e112711dd4331", + "digest": "ce7cb492827bba51c9c8cda4cb55f67fe9348944cabdced798c3b7baea6090d6", "names": [], "sources": [] }, { - "digest": "cfa4409ab0ef198a50a1121ad5013a740760444adc225b9ea816b4667c0158fd", - "names": [ - "packages/llm/llm/src/message.ts#UserMessage" - ], - "sources": [ - "packages/llm/llm/src/message.ts:162" - ] + "digest": "cf6e3aaf1e2de6480aa0157730a41b9a492108a55304100b0f7e112711dd4331", + "names": [], + "sources": [] }, { "digest": "cffcdc19881ab61ec0a9ce18743996f7ac44ac8889f34dfac36ae745574aa22f", @@ -24841,6 +25362,11 @@ "names": [], "sources": [] }, + { + "digest": "d17f8336e133bafdcbf95836c93e65aa3930558be3d676ae91aaa6b1e399c9fa", + "names": [], + "sources": [] + }, { "digest": "d28765f87d6b6ab408e0b2f3e7e297791b004b806d8e302c49903ebae815feb2", "names": [], @@ -25035,6 +25561,11 @@ "packages/core/session/src/types.ts:297" ] }, + { + "digest": "e4b8ca6005c0b20db2b7b9db5f1a46b2d5fb9ff1f6bad61c241512519bce8321", + "names": [], + "sources": [] + }, { "digest": "e4c18e294232c3ba6c9f1999f168263cc55956147cfe4121720899be8e52edd1", "names": [], @@ -25139,6 +25670,11 @@ "names": [], "sources": [] }, + { + "digest": "edcdb9e0a1abce6f50580bbc9d8ac8b25082f3e6312281ec125d81e6b8e8dd40", + "names": [], + "sources": [] + }, { "digest": "ee577a0cc18664c9daee50e9455153321155604f8f1817362f8f4a4b6fb9e1ad", "names": [], @@ -25215,6 +25751,13 @@ "packages/llm/llm/src/types.ts:458" ] }, + { + "digest": "f2039f3c7aff513cfe2c30ec0e8a2677796d60b280a0b627c36226788a013132", + "names": [], + "sources": [ + "packages/core/session/src/types.ts:311" + ] + }, { "digest": "f230c0b583e23b25ee0a61dbe4c05e95c1e2ae44b27939501eb9e03696f0c278", "names": [], @@ -25243,6 +25786,11 @@ "packages/llm/llm/src/types.ts:396" ] }, + { + "digest": "f4654f14522a5b84ae84e2d80959ea596c0684d04ca315bde3705261065a929c", + "names": [], + "sources": [] + }, { "digest": "f4a66f654996c093aae700ea4c434ff2ee3569fea9c0e5813ccf6afdccabd163", "names": [], @@ -25343,6 +25891,15 @@ "names": [], "sources": [] }, + { + "digest": "fb0eadf82093a66bbbd71c8df0d0fd27f251249947693b57fb930d97022b2477", + "names": [ + "packages/llm/llm/src/message.ts#MessageSource" + ], + "sources": [ + "packages/llm/llm/src/message.ts:136" + ] + }, { "digest": "fb5e0517050d2c6d40e5b8145db1f7c5ca7044d5d50fada6ff80e0b22c6ca973", "names": [], @@ -25358,6 +25915,15 @@ "names": [], "sources": [] }, + { + "digest": "fbf5aca9de0e38e9426bea40bd94711e0e115fcdf282b8f8bbe4d3dcde26bfed", + "names": [ + "packages/llm/llm/src/message.ts#Message" + ], + "sources": [ + "packages/llm/llm/src/message.ts:196" + ] + }, { "digest": "fc2ae6962f9f8801c2b1e414668e1e17cbb7544c4f39381198977a38bd10c093", "names": [ @@ -25387,15 +25953,6 @@ "names": [], "sources": [] }, - { - "digest": "fe3cdc6f9050584cea4a6d6998c31334bff7899f5ff1095082a44fdf3b47e996", - "names": [ - "packages/session/session-title-llm/src/index.ts#SessionTitleLlmRequestEventData" - ], - "sources": [ - "packages/session/session-title-llm/src/index.ts:34" - ] - }, { "digest": "fe792a6cd430494503837e21e79f91677704ccdd9e870c679aabd7d43ed47ddd", "names": [], diff --git a/packages/experimental/README.i18n.yaml b/packages/experimental/README.i18n.yaml index a1caa86aac..3fda367693 100644 --- a/packages/experimental/README.i18n.yaml +++ b/packages/experimental/README.i18n.yaml @@ -15,8 +15,8 @@ en: 1eb35b015a6c423f zh: a8a3dceee2c9e943 /packages-experimental/packages: - en: e6df3f1cb1dd4730 - zh: 11e45781cee8c731 + en: a727510157a5f1ca + zh: e07dbd518147312e /packages-experimental/related-documentation: en: f8ab36525b5a89ec zh: 1e828d1993e102f8 diff --git a/packages/experimental/README.md b/packages/experimental/README.md index f03216a9f0..6b17adf017 100644 --- a/packages/experimental/README.md +++ b/packages/experimental/README.md @@ -33,6 +33,7 @@ Experimental prototypes may change their contracts and carry no support promise. | [`agent-team`](agent-team/README.md) | Named teammates with durable messages and a shared task board | `ctx.agentTeams` | | [`client-ui-agent-team`](client-ui-agent-team/README.md) | Team roster, task board, and teammate navigation for Web | — | | [`auto-review`](auto-review/README.md) | Explicit Web layer for same-model review before each native or PTC inner tool call | — | +| [`claude-code-mods`](claude-code-mods/README.md) | Load Claude Code mods and run their hook chains on harness extension points | — | | [`ptc-runtime-python`](ptc-runtime-python/README.md) | CPython subprocess backend for the PTC execution seam | `ctx.ptcRuntime` | | [`computer-use-cua-driver-mcp`](computer-use-cua-driver-mcp/README.md) | Use an installed Cua Driver through MCP | `ctx.computerUse` | | [`computer-use-cua-driver-native`](computer-use-cua-driver-native/README.md) | Embed the Cua Driver native npm runtime | `ctx.computerUse` | diff --git a/packages/experimental/README.zh.md b/packages/experimental/README.zh.md index 32ab3f20a1..3061f4bc28 100644 --- a/packages/experimental/README.zh.md +++ b/packages/experimental/README.zh.md @@ -33,6 +33,7 @@ kind: "package-group" | [`agent-team`](agent-team/README.zh.md) | 具名 teammate,成员之间持久消息与共享任务板 | `ctx.agentTeams` | | [`client-ui-agent-team`](client-ui-agent-team/README.zh.md) | Web Team roster、任务板与 teammate 导航 | — | | [`auto-review`](auto-review/README.zh.md) | 显式 Web 层,在每个原生或 PTC inner 工具调用前使用同一模型审查 | — | +| [`claude-code-mods`](claude-code-mods/README.zh.md) | 加载 Claude Code 模组并把它们的钩子链运行在 harness 扩展点上 | — | | [`ptc-runtime-python`](ptc-runtime-python/README.zh.md) | PTC 执行 seam 的 CPython 子进程后端 | `ctx.ptcRuntime` | | [`computer-use-cua-driver-mcp`](computer-use-cua-driver-mcp/README.zh.md) | 通过 MCP 使用已安装的 Cua Driver | `ctx.computerUse` | | [`computer-use-cua-driver-native`](computer-use-cua-driver-native/README.zh.md) | 嵌入 Cua Driver 原生 npm 运行时 | `ctx.computerUse` | diff --git a/packages/experimental/claude-code-mods/README.i18n.yaml b/packages/experimental/claude-code-mods/README.i18n.yaml new file mode 100644 index 0000000000..32b698b917 --- /dev/null +++ b/packages/experimental/claude-code-mods/README.i18n.yaml @@ -0,0 +1,82 @@ +# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading +# section, a hash of its English and Chinese blocks outside code blocks and generated regions. +# After editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write packages/experimental/claude-code-mods/README.md +/: + en: de39279865f4476c + zh: 97dddf7eb08be7a4 +/deepseek-ai-dsh-experimental-claude-code-mods: + en: bf54b493565f1624 + zh: 6f28a518f7b0dea8 +/deepseek-ai-dsh-experimental-claude-code-mods/summary: + en: 247a19d5149d3ac7 + zh: 4ef3ba1e6c11fc4f +/deepseek-ai-dsh-experimental-claude-code-mods/table-of-contents: + en: d152484eb41ac6b4 + zh: 09388d293f9be9cb +/deepseek-ai-dsh-experimental-claude-code-mods/use-this-package: + en: fe639cd32d5a93ef + zh: e18b600db39dc0b4 +/deepseek-ai-dsh-experimental-claude-code-mods/use-this-package/which-events-your-mod-receives: + en: e723c069aa63b3e7 + zh: 160a8f3bf318f98c +/deepseek-ai-dsh-experimental-claude-code-mods/use-this-package/which-members-your-mod-can-call: + en: 25b88ca8118074a6 + zh: f2c790873969e295 +/deepseek-ai-dsh-experimental-claude-code-mods/use-this-package/test-a-mod: + en: 3cae088ff37d850e + zh: 2e14ac722e557f74 +/deepseek-ai-dsh-experimental-claude-code-mods/understand-the-implementation: + en: 42b15ac6321706aa + zh: 7ae097f3abcc5d9c +/deepseek-ai-dsh-experimental-claude-code-mods/understand-the-implementation/loading: + en: 14435e4e423c5402 + zh: b9262f9880e5c578 +/deepseek-ai-dsh-experimental-claude-code-mods/understand-the-implementation/the-chain: + en: 422817ec3ace5b55 + zh: 304275a34a6ef299 +/deepseek-ai-dsh-experimental-claude-code-mods/understand-the-implementation/mapping-onto-the-harness: + en: 1dd1b98f31754ed1 + zh: 2823ce34cd9b3057 +/deepseek-ai-dsh-experimental-claude-code-mods/understand-the-implementation/source-map: + en: a84fdd519f0d4509 + zh: 86e683294462189f +/deepseek-ai-dsh-experimental-claude-code-mods/further-exploration: + en: 5e11d6480eca0081 + zh: c6f477b988608bd7 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience: + en: 215e7ba838619b7b + zh: a311da3843709f90 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/prompt-context-and-submitted-prompts: + en: 2e9d6b50a26a5e10 + zh: d884e62e639cad69 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/prompt-context-and-submitted-prompts/what-the-model-sees: + en: f4aa4d78a2b205c0 + zh: e15dc8d8c0597758 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/prompt-context-and-submitted-prompts/what-the-model-sees/framing-of-a-prompt-a-mod-submits: + en: f521d5aba5ab7027 + zh: d217f859b32b6af4 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/prompt-context-and-submitted-prompts/token-effect: + en: 04a7869d92d45eb8 + zh: d689ba72f67fd5a3 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/prompt-context-and-submitted-prompts/kv-cache-effect: + en: 75b19101cbd036ff + zh: 39bdcbeb9e80ed2e +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/tool-outcomes-a-mod-decides: + en: fe8166c6d3eb962a + zh: 5f8fe74778a78166 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/tool-outcomes-a-mod-decides/what-the-model-sees: + en: dde54e809fe02aa3 + zh: ab22c28f81129669 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/tool-outcomes-a-mod-decides/token-effect: + en: cbf27f737c6254e6 + zh: 4a515035ab6240b4 +/deepseek-ai-dsh-experimental-claude-code-mods/model-experience/tool-outcomes-a-mod-decides/kv-cache-effect: + en: ca31263f47f654d9 + zh: 7967970be4122a87 +/deepseek-ai-dsh-experimental-claude-code-mods/known-limitations-and-deferred-work: + en: 4b55d7f407bedd5a + zh: 435bc79895c6a3c5 +/deepseek-ai-dsh-experimental-claude-code-mods/known-limitations-and-deferred-work/dev-note: + en: 7076945241405766 + zh: 6747c882ba35eb21 diff --git a/packages/experimental/claude-code-mods/README.md b/packages/experimental/claude-code-mods/README.md new file mode 100644 index 0000000000..31ea3ad2c0 --- /dev/null +++ b/packages/experimental/claude-code-mods/README.md @@ -0,0 +1,202 @@ +--- +description: "Experimental bridge that loads Claude Code mods (hooks modules) and runs their hook chains on harness extension points, for users mounting a mod and maintainers extending the mapping." +kind: "package-reference" +--- + +# @deepseek-ai/dsh-experimental-claude-code-mods + +English | [中文](README.zh.md) + +## Summary + +Run [Claude Code mods](https://code.claude.com/docs/en/plugins/mods/overview) inside agent runs: point `pluginDirs` at mod directories and their `register(on, options)` hooks guard tool calls, rewrite prompts, add commands and tools, and read session facts through the same `$`, `e`, `next` chain. Mounting costs nothing until a mod acts; each `$` call rides a composed harness service. Choose it to try an existing mod unchanged; it is an alpha interface-compatibility demonstration, so unserved events and `$` members fail with a message naming the gap, and nothing draws in the GUI. + +## Table of Contents + +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + +Mount the package with the plugin directories to load, as `claude --plugin-dir` takes them. Each directory holds `.claude-plugin/plugin.json` and `hooks/hooks.json` with exactly one `modules` entry. + +```yaml +- name: '@deepseek-ai/dsh-experimental-claude-code-mods' + config: + pluginDirs: + - ./mods/token-weather + - ./mods/blast-radius + options: + token-weather: + history: 12 +``` + +| Field | Default | Meaning | +|---|---|---| +| `pluginDirs` | required | Plugin directories in chain order; a relative path resolves against the process launch cwd | +| `options` | — | `register` option values by plugin name, overlaid on the manifest's `userConfig` defaults | +| `hookTimeoutMs` | `10000` | A hook's own running time per event (Claude Code's limit); time inside `next` or a `$` call does not count | +| `catchTimeoutMs` | `1000` | A `.catch` handler's running time | +| `processTimeoutMs` | `30000` | Default `$.process.run` and `$.http.fetch` timeout | +| `toolAliases` | — | Claude Code tool name → harness tool name entries added to the built-in table | + +The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-claude-code-mods) is the exhaustive source for every accepted field. + +### Which events your mod receives + +| Event | Raised from | A hook can | +|---|---|---| +| `session.start` | `agent/created` of a root agent, awaited before its first turn | observe; register commands and tools | +| `prompt.submit` | `agent/pre-step` with claimed messages | rewrite `text`, add `context` the model reads after the prompt, or `{ drop }` the prompt | +| `turn.start` | the first `agent/pre-step` of a turn | observe | +| `tool.call` | the `tools/execute` waterfall, after the harness permission decision | observe before and after, `{ deny }`, answer with `{ result }`, or rewrite the result after `next` | +| `turn.complete` | the `turn/end` session event | observe; return `{ text }` for a line in the host log | +| `command.run` | a command the mod registered with `$.command.register` is typed | answer with `{ text }` or `{}` | +| `session.end` | `agent/disposed` of a root agent | observe | +| `.` | another mod's `$` call | observe, rewrite, or `{ deny }` it | + +`e.tool` and `tool` matchers use Claude Code's names where a harness tool has one (`Bash` ↔ `bash`, `Read` ↔ `read`, `Edit` ↔ `edit`, `Write` ↔ `write`, `Glob` ↔ `glob`, `Grep` ↔ `grep`, `WebFetch` ↔ `web_fetch`, `WebSearch` ↔ `web_search`, `Task` ↔ `subagent`, `TodoWrite` ↔ `todo_write`, `AskUserQuestion` ↔ `ask_user_question`, `ExitPlanMode` ↔ `exit_plan_mode`, `Skill` ↔ `skill`); every other tool keeps its harness name. Subagent events carry `e.agentId`. Every other Claude Code event name registers without error and never fires. + +### Which `$` members your mod can call + +| Namespace | Served | Over | +|---|---|---| +| `$.plugin` | `name`, `root` | the manifest | +| `$.ui` | `log`, `toast`, `status`, `invalidate`, `open`, `close`, `panes`, `ask` | `ask` on `ctx.userQuestions`; the others reach the host log, and `open` answers `{ isPlaced: false }` | +| `$.command` | `register`, `run`, `list` | `ctx.commands`, scoped to the agent whose event is running | +| `$.tool` | `register`, `call`, `list` | `ctx.tools`; a registered tool is named `mcp____` | +| `$.prompt` | `submit` | `agent.followup()`, framed as a message from the mod unless `asUser` | +| `$.session` | `id`, `cwd`, `root`, `model`, `turns`, `messages`, `usage`, `version` | the agent's Session and the `turnBoundary` and `contextPressure` projections | +| `$.state` | `get`, `set` | memory held for the session | +| `$.store` | `get`, `set`, `delete`, `keys` | the `claude_code_mods` storage domain, one JSON object per plugin, 4 MiB | +| `$.clock` | `now`, `sleep`, `after`, `every` | timers the bridge cancels on unload | +| `$.fs` | `read`, `write`, `list`, `exists`, `stat` | `ctx.fs`, relative to the session workspace, 4 MiB per file | +| `$.process` | `run` | `ctx.subprocess`, argv without a shell | +| `$.http` | `fetch` | the process's `fetch` | +| `$.env` | `get`, `set` | this process's environment | + +A call whose service is not composed rejects with the missing service's package name; a namespace or method outside this table rejects with `no implementation for .`. + +### Test a mod + +`createModTestKit` loads mods from directories or inline modules and raises events through them with stubs beneath, in the shape of `claude-code/testing`: `kit.on('tool.call', () => ({ result: 'ok' }))` answers in the engine's place, `kit.$.tool.call({ tool: 'Bash', command: 'ls' })` raises the event, and `mock.store(kit.on)` answers `$.store` from memory. The [test-kit specs](tests/testing.spec.ts) run Claude Code's own `first-mod` tutorial test against the fixture mod unchanged. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +### Loading + +[`manifest.ts`](src/manifest.ts) reads and validates `plugin.json` and `hooks.json`; a directory that does not read as a mod fails the plugin load, because that is a deployment error. [`module.ts`](src/module.ts) imports the hooks module through Node with a per-load query so a reload evaluates it afresh, runs `register`, and collects each `on(...)` into a `HookRegistry`; `on` refuses an unknown event name and a second matcher-less registration of one event with Claude Code's wording. A module that fails to import or whose `register` throws is skipped with a warning and the session continues, as Claude Code does. + +### The chain + +[`chain.ts`](src/chain.ts) runs one event through the selected hooks, outermost first, with the engine behavior at the bottom. Each hook's `next` delegates beneath; a hook that throws, times out, or settles without a result object is skipped and reported once per failure kind, the result from beneath stands when it had already called `next`, and a `.catch` handler may answer in its place. The budget clock counts only the hook's own running time: it pauses inside `next` and inside every `$` call except `$.clock.sleep`. [`engine.ts`](src/engine.ts) owns the registry, per-session `$.state`, mod timers, and the two dispatch directions: an engine event reaches every selected hook, while a `$` call raised by one mod reaches only the mods loaded before it. + +### Mapping onto the harness + +[`index.ts`](src/index.ts) registers the listeners. `tool.call` runs around `tools/execute`, so the harness permission decision precedes the chain; a `{ deny }` becomes an error result with the reason, a `{ result }` for a mod-registered tool becomes a successful content result, and a `{ result }` for a built-in tool becomes an error-shaped result because a built-in tool's success value must satisfy its own output schema. A result a hook rewrote after `next` is installed as replacement content through `tools/post-execute`. [`host-ops.ts`](src/host-ops.ts) holds the engine behavior for each `$` call over `ctx.get(...)` services, so a deployment composes only what its mods use. + +### Source map + +| File | Role | +|---|---| +| [`src/index.ts`](src/index.ts) | Plugin entry: config, mod loading, extension-point listeners | +| [`src/host-ops.ts`](src/host-ops.ts) | `$` behavior over harness services | +| [`src/engine.ts`](src/engine.ts) | Registry, `$.state`, timers, dispatch directions | +| [`src/chain.ts`](src/chain.ts) | Middleware chain, budget clock, failure rules | +| [`src/api.ts`](src/api.ts) | The `$` object a hook receives | +| [`src/module.ts`](src/module.ts), [`src/manifest.ts`](src/manifest.ts) | Hooks-module import and `on`; plugin-directory validation | +| [`src/matcher.ts`](src/matcher.ts), [`src/tool-names.ts`](src/tool-names.ts) | Event names, matchers, tool-name aliases | +| [`src/testing.ts`](src/testing.ts) | The test kit | + +
+ +----- + + +## Further Exploration + +- [Claude Code mods reference](https://code.claude.com/docs/en/plugins/mods/reference) — the events, methods, and limits this bridge mirrors. +- [Experimental packages](../README.md) — publication policy and dependency isolation. +- [Hooks group](../../hooks/README.md) — the settings-hook bridges; a `hooks.json` that also holds settings hooks needs `dsh-hooks-claude-code` for them. +- [Tool execution pipeline](../../../docs/tool-execution-pipeline.md) — the waterfalls `tool.call` runs around. +- [Human commands](../../interaction/commands/README.md) — the registry `$.command.register` lands on. + +----- + + +## Model Experience + +### Prompt context and submitted prompts + +#### What the model sees + +Strings a `prompt.submit` hook adds to `e.context` arrive as one user message with source `{ kind: 'claude-code-mods' }` after the prompt; a rewritten `text` replaces the prompt's text blocks. `$.prompt.submit({ text })` queues a user message with the same source: the text alone with `asUser: true`, otherwise framed as below with the plugin name and the text filled in. + +##### Framing of a prompt a mod submits + +```markdown +Message from the "" mod: + +``` + +#### Token effect + +No cost until a mod adds context or submits a prompt; that text is data-dependent, logged, and resent in later requests until compaction. + +#### KV Cache effect + +Append-only: added context and submitted prompts follow the reusable request prefix and do not invalidate existing entries. + +### Tool outcomes a mod decides + +#### What the model sees + +A `{ deny: reason }` answer renders `Error: ` as the tool result. A `{ result }` answer for a mod-registered tool renders the text as a successful result; for a built-in tool it renders the text as an error-shaped result. A result rewritten after `next` replaces the tool's content text. A dropped prompt ends the turn as `blocked` with no model-visible message. + +#### Token effect + +Denial and answered calls replace the tool's own output with the mod's text; a dropped prompt sends no request. + +#### KV Cache effect + +Tool results append after the reusable prefix; a dropped prompt invalidates nothing. + +## Known Limitations and Deferred Work + + + +These limits describe where a Claude Code mod behaves differently through this bridge. They are current package constraints, not a task backlog. + +- **No drawing surface** — `ui.render`, `ui.press`, `ui.input`, `ui.select`, `ui.resolve`, panes, the band above the prompt, and element trees are not served: `ui.render` hooks never run, `$.ui.open` answers `{ isPlaced: false }`, and `$.ui.log`, `$.ui.toast`, and `$.ui.status` reach the host log, not the transcript. The Web GUI slots that could carry them (`conversation.input.dock` for the band, `shell.overlay` for toasts, `conversation.composer.dock` for status, a log-only session event with a Chat node for log lines) exist but no client plugin renders mod trees yet. +- **Unserved events** — `tool.check`, `tool.describe`, `turn.step`, the other `prompt.*` events, `command.describe`, `config.*`, `session.compact`, `session.receive`, `session.send`, `session.append`, `session.attach`, `session.detach`, `session.measure`, `agent.*`, `plugin.register`, `engine.create`, `telemetry.*`, and `classic.*` register and never fire. +- **Unserved `$` namespaces** — `$.model`, `$.agent`, `$.config`, `$.settings`, `$.mcp`, `$.audio`, `$.telemetry`, `$.turn`, `$.ui.notice`, `$.ui.blit`, `$.ui.copy`, `$.fs.ancestors`, `$.process.spawn`, and `$.session.repo`, `send`, `append`, `authorize`, `compact`, `surfaces` reject with `no implementation`. `$.model.complete` waits on a logged side-request event so a mod's model call stays reconstructable from the Session log. +- **`tool.call` runs after the permission decision** — Claude Code runs mod `tool.call` hooks before its permission check; here the harness `tools/pre-execute` waterfall, including approval, settles first. Argument rewrites passed to `next` are not honored because the call's arguments are already logged; the bridge warns once per tool. +- **Answering a built-in tool** — a `{ result }` in place of a built-in tool's run reaches the model as an error-shaped result carrying the text, because a successful value must satisfy that tool's output schema. +- **One process, many sessions** — a mod's module-level variables are shared by every session in the process, where Claude Code runs one session per process; keep per-session values in `$.state`. `session.start` and `session.end` fire for root agents only. +- **No sandbox, no static analysis, no hot reload** — the hooks module runs in-process with Node's globals; the `$`-only access rule, `claude plugin validate`, type generation, `--plugin-dir` watching, and the in-session mod authoring flow are not implemented. A mod's `.ts` module loads only where Node strips types or the launcher transpiles. +- **`turn.complete` text** — the `{ text }` a hook returns reaches the host log, not a line under the answer; `durationMs` counts from the turn's `turn/start`. +- **`$.session.usage`** — `window` is `0` and `percent` absent until the route's context window and a provider usage report are known through the token meter; `rateLimits` is always empty. `$.fs.stat` reports `mtimeMs: 0`. +- **Settings hooks in `hooks.json`** — ignored with a warning; mount `@deepseek-ai/dsh-hooks-claude-code` for them. + + +### Dev Note + +
+Working context for maintainers — click to expand + +The next steps, in order of mod-author value: a `claude-code-mods/ui` session event and Client Chat node for `$.ui.log` lines, an `AbovePrompt` renderer over `conversation.input.dock` for `Box`/`Text`/`Button` trees, and `$.model.complete` over `ctx.llm` with a logged request event. + +
diff --git a/packages/experimental/claude-code-mods/README.zh.md b/packages/experimental/claude-code-mods/README.zh.md new file mode 100644 index 0000000000..44fd35ae5d --- /dev/null +++ b/packages/experimental/claude-code-mods/README.zh.md @@ -0,0 +1,202 @@ +--- +description: "实验性桥接:加载 Claude Code 模组(hooks module),把它们的钩子链运行在 harness 扩展点上,供挂载模组的用户与扩展映射的维护者阅读。" +kind: "package-reference" +--- + +# @deepseek-ai/dsh-experimental-claude-code-mods + +[English](README.md) | 中文 + +## 概述 + +在 agent(智能体)运行中运行 [Claude Code 模组(mod)](https://code.claude.com/docs/en/plugins/mods/overview):把 `pluginDirs` 指向模组目录,它们的 `register(on, options)` 钩子就能通过同一条 `$`、`e`、`next` 链守护工具调用、改写提示词、添加命令与工具并读取会话事实。在模组行动之前挂载没有成本;每个 `$` 调用都基于一个已组合的 harness 服务。需要原样试用现有模组时选择本包;它是 alpha 质量的接口兼容性演示,未提供的事件与 `$` 成员会以指明缺口的消息失败,也不会在 GUI 中绘制任何内容。 + +## 目录 + +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 + +挂载本包并给出要加载的插件目录,用法与 `claude --plugin-dir` 相同。每个目录包含 `.claude-plugin/plugin.json` 以及恰好有一个 `modules` 条目的 `hooks/hooks.json`。 + +```yaml +- name: '@deepseek-ai/dsh-experimental-claude-code-mods' + config: + pluginDirs: + - ./mods/token-weather + - ./mods/blast-radius + options: + token-weather: + history: 12 +``` + +| 字段 | 默认值 | 含义 | +|---|---|---| +| `pluginDirs` | 必填 | 按链顺序排列的插件目录;相对路径相对于进程启动 cwd 解析 | +| `options` | — | 按插件名给出的 `register` 选项值,叠加在 manifest(元数据清单)的 `userConfig` 默认值之上 | +| `hookTimeoutMs` | `10000` | 一个钩子每个事件的自身运行时间(Claude Code 的限制);在 `next` 或 `$` 调用内的时间不计 | +| `catchTimeoutMs` | `1000` | `.catch` 处理器的运行时间 | +| `processTimeoutMs` | `30000` | `$.process.run` 与 `$.http.fetch` 的默认超时 | +| `toolAliases` | — | 追加到内置表的「Claude Code 工具名 → harness 工具名」条目 | + +生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-claude-code-mods)是每个可接受字段的权威来源。 + +### 你的模组会收到哪些事件 + +| 事件 | 触发自 | 钩子可以 | +|---|---|---| +| `session.start` | 根 agent 的 `agent/created`,在其第一个轮次前等待完成 | 观察;注册命令与工具 | +| `prompt.submit` | 带有已认领消息的 `agent/pre-step` | 改写 `text`、添加模型在提示词之后读取的 `context`,或用 `{ drop }` 丢弃提示词 | +| `turn.start` | 一个轮次的第一个 `agent/pre-step` | 观察 | +| `tool.call` | `tools/execute` waterfall(瀑布式事件),在 harness 权限决策之后 | 在前后观察、`{ deny }`、用 `{ result }` 应答,或在 `next` 之后改写结果 | +| `turn.complete` | `turn/end` 会话事件 | 观察;返回 `{ text }` 在 host 日志中写一行 | +| `command.run` | 用户输入了模组通过 `$.command.register` 注册的命令 | 用 `{ text }` 或 `{}` 应答 | +| `session.end` | 根 agent 的 `agent/disposed` | 观察 | +| `.` | 另一个模组的 `$` 调用 | 观察、改写或 `{ deny }` 该调用 | + +harness 工具若有 Claude Code 对应名称,`e.tool` 与 `tool` matcher 使用 Claude Code 的名称(`Bash` ↔ `bash`、`Read` ↔ `read`、`Edit` ↔ `edit`、`Write` ↔ `write`、`Glob` ↔ `glob`、`Grep` ↔ `grep`、`WebFetch` ↔ `web_fetch`、`WebSearch` ↔ `web_search`、`Task` ↔ `subagent`、`TodoWrite` ↔ `todo_write`、`AskUserQuestion` ↔ `ask_user_question`、`ExitPlanMode` ↔ `exit_plan_mode`、`Skill` ↔ `skill`);其余工具保持 harness 名称。subagent 事件携带 `e.agentId`。其他每个 Claude Code 事件名都能无错注册,但永远不会触发。 + +### 你的模组能调用哪些 `$` 成员 + +| 命名空间 | 已提供 | 基于 | +|---|---|---| +| `$.plugin` | `name`、`root` | manifest | +| `$.ui` | `log`、`toast`、`status`、`invalidate`、`open`、`close`、`panes`、`ask` | `ask` 基于 `ctx.userQuestions`;其余写入 host 日志,`open` 应答 `{ isPlaced: false }` | +| `$.command` | `register`、`run`、`list` | `ctx.commands`,作用域为事件所属的 agent | +| `$.tool` | `register`、`call`、`list` | `ctx.tools`;注册的工具命名为 `mcp____` | +| `$.prompt` | `submit` | `agent.followup()`,除非 `asUser`,否则框定为来自模组的消息 | +| `$.session` | `id`、`cwd`、`root`、`model`、`turns`、`messages`、`usage`、`version` | agent 的 Session 以及 `turnBoundary` 与 `contextPressure` 投影 | +| `$.state` | `get`、`set` | 为该会话保留的内存 | +| `$.store` | `get`、`set`、`delete`、`keys` | `claude_code_mods` storage domain,每个插件一个 JSON 对象,上限 4 MiB | +| `$.clock` | `now`、`sleep`、`after`、`every` | 桥接在卸载时取消的定时器 | +| `$.fs` | `read`、`write`、`list`、`exists`、`stat` | `ctx.fs`,相对会话工作区解析,单文件上限 4 MiB | +| `$.process` | `run` | `ctx.subprocess`,argv 不经 shell | +| `$.http` | `fetch` | 本进程的 `fetch` | +| `$.env` | `get`、`set` | 本进程的环境变量 | + +所需服务未组合时,调用会以缺失服务的包名拒绝;表外的命名空间或方法以 `no implementation for .` 拒绝。 + +### 测试模组 + +`createModTestKit` 从目录或内联模块加载模组,并以 `claude-code/testing` 的形态在它们之下放置 stub 后触发事件:`kit.on('tool.call', () => ({ result: 'ok' }))` 代替引擎应答,`kit.$.tool.call({ tool: 'Bash', command: 'ls' })` 触发事件,`mock.store(kit.on)` 用内存应答 `$.store`。[测试工具包 spec](tests/testing.spec.ts) 对 fixture(测试前置数据)模组原样运行 Claude Code 自己的 `first-mod` 教程测试。 + +----- + + +## 理解实现 + +
+实现内部细节——点击展开 + +### 加载 + +[`manifest.ts`](src/manifest.ts) 读取并校验 `plugin.json` 与 `hooks.json`;无法按模组解读的目录会使插件加载失败,因为那是部署错误。[`module.ts`](src/module.ts) 通过 Node 导入 hooks module,并附带每次加载唯一的 query,使重新加载重新求值模块;随后运行 `register`,把每个 `on(...)` 收集进 `HookRegistry`;`on` 以 Claude Code 的措辞拒绝未知事件名以及同一事件第二次无 matcher 的注册。导入失败或 `register` 抛错的模块会被跳过并给出警告,会话照常继续,与 Claude Code 一致。 + +### 链 + +[`chain.ts`](src/chain.ts) 让一个事件依次经过选中的钩子(最外层在前),底部是引擎行为。每个钩子的 `next` 委托给下层;抛错、超时或未以结果对象结束的钩子会被跳过,并按失败种类各报告一次;若它已经调用过 `next`,下层的结果保持有效;`.catch` 处理器可以代替它应答。预算时钟只计钩子自身的运行时间:在 `next` 内以及除 `$.clock.sleep` 外的每个 `$` 调用内暂停。[`engine.ts`](src/engine.ts) 拥有注册表、按会话的 `$.state`、模组定时器以及两个派发方向:引擎事件到达每个选中的钩子,而某个模组发起的 `$` 调用只到达在它之前加载的模组。 + +### 映射到 harness + +[`index.ts`](src/index.ts) 注册监听器。`tool.call` 围绕 `tools/execute` 运行,因此 harness 权限决策先于这条链;`{ deny }` 变成带原因的错误结果,模组注册工具的 `{ result }` 变成成功的内容结果,而内置工具的 `{ result }` 变成错误形态的结果,因为内置工具的成功值必须满足它自己的输出 schema。钩子在 `next` 之后改写的结果通过 `tools/post-execute` 作为替换内容安装。[`host-ops.ts`](src/host-ops.ts) 持有每个 `$` 调用基于 `ctx.get(...)` 服务的引擎行为,因此部署只需组合其模组实际使用的服务。 + +### 源码地图 + +| 文件 | 角色 | +|---|---| +| [`src/index.ts`](src/index.ts) | 插件入口:配置、模组加载、扩展点监听器 | +| [`src/host-ops.ts`](src/host-ops.ts) | 基于 harness 服务的 `$` 行为 | +| [`src/engine.ts`](src/engine.ts) | 注册表、`$.state`、定时器、派发方向 | +| [`src/chain.ts`](src/chain.ts) | middleware 链、预算时钟、失败规则 | +| [`src/api.ts`](src/api.ts) | 钩子收到的 `$` 对象 | +| [`src/module.ts`](src/module.ts)、[`src/manifest.ts`](src/manifest.ts) | hooks module 导入与 `on`;插件目录校验 | +| [`src/matcher.ts`](src/matcher.ts)、[`src/tool-names.ts`](src/tool-names.ts) | 事件名、matcher、工具名别名 | +| [`src/testing.ts`](src/testing.ts) | 测试工具包 | + +
+ +----- + + +## 进一步探索 + +- [Claude Code 模组参考](https://code.claude.com/docs/en/plugins/mods/reference)——本桥接所镜像的事件、方法与限制。 +- [实验性包](../README.zh.md)——发布策略与依赖隔离。 +- [hooks 组](../../hooks/README.zh.md)——settings 钩子桥接;同时包含 settings 钩子的 `hooks.json` 需要 `dsh-hooks-claude-code` 来运行它们。 +- [工具执行流水线](../../../docs/tool-execution-pipeline.zh.md)——`tool.call` 所围绕的 waterfall。 +- [人类命令](../../interaction/commands/README.zh.md)——`$.command.register` 落在的注册表。 + +----- + + +## 模型体验 + +### 提示词上下文与提交的提示词 + +#### 模型看到什么 + +`prompt.submit` 钩子加入 `e.context` 的字符串会作为一条来源为 `{ kind: 'claude-code-mods' }` 的 user 消息跟在提示词之后到达;改写的 `text` 会替换提示词的文本块。`$.prompt.submit({ text })` 入队一条同样来源的 user 消息:带 `asUser: true` 时只有该文本,否则按下文框定,填入插件名与该文本。 + +##### 模组提交的提示词的框定 + +```markdown +Message from the "" mod: + +``` + +#### Token 影响 + +在模组添加上下文或提交提示词之前无成本;这些文本依赖数据、会被记录,并在后续请求中重复发送直至上下文压缩。 + +#### KV Cache 影响 + +仅追加:添加的上下文与提交的提示词跟在可复用请求前缀之后,不会使已有条目失效。 + +### 模组决定的工具结果 + +#### 模型看到什么 + +`{ deny: reason }` 应答把 `Error: ` 渲染为工具结果。对模组注册的工具,`{ result }` 应答把文本渲染为成功结果;对内置工具则渲染为错误形态的结果。在 `next` 之后改写的结果替换工具的内容文本。被丢弃的提示词以 `blocked` 结束轮次,没有模型可见的消息。 + +#### Token 影响 + +拒绝与应答的调用用模组的文本替换工具自身的输出;被丢弃的提示词不发送请求。 + +#### KV Cache 影响 + +工具结果追加在可复用前缀之后;被丢弃的提示词不会使任何内容失效。 + +## 已知限制与延期工作 + + + +这些限制描述 Claude Code 模组经由本桥接时行为不同之处。它们是当前包约束,而非任务积压。 + +- **没有绘制表面**——`ui.render`、`ui.press`、`ui.input`、`ui.select`、`ui.resolve`、面板、提示框上方的横带以及元素树均未提供:`ui.render` 钩子永不运行,`$.ui.open` 应答 `{ isPlaced: false }`,`$.ui.log`、`$.ui.toast` 与 `$.ui.status` 写入 host 日志而非 transcript(文本记录)。可以承载它们的 Web GUI slot(横带对应 `conversation.input.dock`,toast 对应 `shell.overlay`,状态对应 `conversation.composer.dock`,日志行对应一个仅记录的会话事件加 Chat 节点)已经存在,但尚无客户端插件渲染模组树。 +- **未提供的事件**——`tool.check`、`tool.describe`、`turn.step`、其余 `prompt.*` 事件、`command.describe`、`config.*`、`session.compact`、`session.receive`、`session.send`、`session.append`、`session.attach`、`session.detach`、`session.measure`、`agent.*`、`plugin.register`、`engine.create`、`telemetry.*` 与 `classic.*` 可以注册但永不触发。 +- **未提供的 `$` 命名空间**——`$.model`、`$.agent`、`$.config`、`$.settings`、`$.mcp`、`$.audio`、`$.telemetry`、`$.turn`、`$.ui.notice`、`$.ui.blit`、`$.ui.copy`、`$.fs.ancestors`、`$.process.spawn`,以及 `$.session.repo`、`send`、`append`、`authorize`、`compact`、`surfaces` 以 `no implementation` 拒绝。`$.model.complete` 等待一个记录副请求的会话事件,使模组的模型调用仍可从会话日志重建。 +- **`tool.call` 在权限决策之后运行**——Claude Code 在其权限检查之前运行模组的 `tool.call` 钩子;这里 harness 的 `tools/pre-execute` waterfall(包括审批)先行落定。传给 `next` 的参数改写不被采纳,因为调用参数已被记录;桥接按工具各警告一次。 +- **代替内置工具应答**——代替内置工具运行的 `{ result }` 以携带该文本的错误形态结果到达模型,因为成功值必须满足该工具的输出 schema。 +- **一个进程,多个会话**——模组的模块级变量由进程内的所有会话共享,而 Claude Code 每个进程只运行一个会话;按会话的值请放在 `$.state`。`session.start` 与 `session.end` 只为根 agent 触发。 +- **没有沙箱、静态分析与热重载**——hooks module 在进程内以 Node 全局对象运行;仅经 `$` 访问的规则、`claude plugin validate`、类型生成、`--plugin-dir` 监视以及会话内模组编写流程均未实现。模组的 `.ts` 模块只在 Node 剥离类型或启动器转译时才能加载。 +- **`turn.complete` 文本**——钩子返回的 `{ text }` 写入 host 日志,而非答案下方的一行;`durationMs` 从该轮次的 `turn/start` 起计。 +- **`$.session.usage`**——在路由的上下文窗口与 provider 用量报告经 token meter 可知之前,`window` 为 `0` 且没有 `percent`;`rateLimits` 始终为空。`$.fs.stat` 报告 `mtimeMs: 0`。 +- **`hooks.json` 中的 settings 钩子**——忽略并给出警告;请为它们挂载 `@deepseek-ai/dsh-hooks-claude-code`。 + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +按对模组作者的价值排序,下一步是:为 `$.ui.log` 行提供 `claude-code-mods/ui` 会话事件与 Client Chat 节点;在 `conversation.input.dock` 上为 `Box`/`Text`/`Button` 树提供 `AbovePrompt` 渲染器;基于 `ctx.llm` 并带记录请求事件地提供 `$.model.complete`。 + +
diff --git a/packages/experimental/claude-code-mods/package.json b/packages/experimental/claude-code-mods/package.json new file mode 100644 index 0000000000..1289a7950a --- /dev/null +++ b/packages/experimental/claude-code-mods/package.json @@ -0,0 +1,75 @@ +{ + "name": "@deepseek-ai/dsh-experimental-claude-code-mods", + "description": "Experimental bridge: load Claude Code mods (hooks modules) and run their hook chains on DeepSeek Harness extension points", + "version": "0.2.0-rc.2", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/deepseek-ai/deepseek-harness.git", + "directory": "packages/experimental/claude-code-mods" + }, + "type": "module", + "main": "lib/index.js", + "types": "lib/types/index.d.ts", + "exports": { + ".": { + "types": "./lib/types/index.d.ts", + "default": "./lib/index.js" + }, + "./src/*": "./src/*", + "./package.json": "./package.json" + }, + "files": [ + "lib/index.js", + "lib/types/**/*.d.ts" + ], + "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-util-crypto": "workspace:*", + "@deepseek-ai/dsh-util-values": "workspace:*", + "@deepseek-ai/schemastery": "workspace:~", + "zod": "^4.4.3" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "workspace:~", + "@deepseek-ai/dsh-agent": "workspace:*", + "@deepseek-ai/dsh-commands": "workspace:*", + "@deepseek-ai/dsh-fs": "workspace:*", + "@deepseek-ai/dsh-llm": "workspace:*", + "@deepseek-ai/dsh-scope": "workspace:*", + "@deepseek-ai/dsh-session": "workspace:*", + "@deepseek-ai/dsh-session-projection": "workspace:*", + "@deepseek-ai/dsh-storage-domain": "workspace:*", + "@deepseek-ai/dsh-subprocess": "workspace:*", + "@deepseek-ai/dsh-token-meter": "workspace:*", + "@deepseek-ai/dsh-tools": "workspace:*", + "@deepseek-ai/dsh-user-questions": "workspace:*" + }, + "devDependencies": { + "@deepseek-ai/cordis": "workspace:~", + "@deepseek-ai/cordis-plugin-include": "workspace:~", + "@deepseek-ai/cordis-plugin-loader": "workspace:~", + "@deepseek-ai/dsh-agent": "workspace:*", + "@deepseek-ai/dsh-agent-loop": "workspace:*", + "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", + "@deepseek-ai/dsh-attachment": "workspace:*", + "@deepseek-ai/dsh-commands": "workspace:*", + "@deepseek-ai/dsh-fs": "workspace:*", + "@deepseek-ai/dsh-fs-local": "workspace:*", + "@deepseek-ai/dsh-llm": "workspace:*", + "@deepseek-ai/dsh-scope": "workspace:*", + "@deepseek-ai/dsh-session": "workspace:*", + "@deepseek-ai/dsh-session-projection": "workspace:*", + "@deepseek-ai/dsh-storage": "workspace:*", + "@deepseek-ai/dsh-storage-domain": "workspace:*", + "@deepseek-ai/dsh-storage-json": "workspace:*", + "@deepseek-ai/dsh-subprocess": "workspace:*", + "@deepseek-ai/dsh-subprocess-local": "workspace:*", + "@deepseek-ai/dsh-system-prompt": "workspace:*", + "@deepseek-ai/dsh-token-meter": "workspace:*", + "@deepseek-ai/dsh-tools": "workspace:*", + "@deepseek-ai/dsh-user-questions": "workspace:*" + } +} diff --git a/packages/experimental/claude-code-mods/src/api.ts b/packages/experimental/claude-code-mods/src/api.ts new file mode 100644 index 0000000000..1b26d9ad9a --- /dev/null +++ b/packages/experimental/claude-code-mods/src/api.ts @@ -0,0 +1,174 @@ +/** + * The `$` a hook receives. Each method shapes its arguments into the mods API + * event of the same name and hands it to the binding's `invoke`, which runs + * the mods loaded before this one and then the engine's implementation. The + * hook's running-time clock pauses while a call is awaited, except for + * `$.clock.sleep`, which Claude Code counts as the hook's own time. + * @module + */ + +import type { BudgetClock, LoadedMod } from './chain.ts' +import { messageOf } from './values.ts' +import type { + AskOptions, CommandInfo, CommandRunResult, CommandSpec, FsEntry, FsStat, HttpInit, HttpResponse, ModsApi, PaneOpenArgs, + PaneOpenResult, ProcessRunInit, ProcessRunResult, PromptSubmitArgs, SessionMessage, SessionUsage, SessionVersion, + StateRef, ModTimer, ToastOptions, ToolCallResult, ToolInfo, ToolSpec, UiLogOptions, +} from './types.ts' + +/** Timers the engine owns on a mod's behalf, so disposal and reload can cancel them. */ +export interface TimerHost { + after(ms: number, fn: () => unknown): ModTimer + every(ms: number, fn: () => unknown): ModTimer +} + +/** What one `$` instance is bound to. */ +export interface ApiBinding { + readonly mod: LoadedMod + /** The running hook's clock, paused while mods API calls are awaited; absent outside a hook. */ + readonly clock: BudgetClock | undefined + /** + * Raise one mods API call as its event and resolve to the value the chain + * answered with; a `{ deny }` answer rejects with the reason. + */ + readonly invoke: (op: string, input: unknown) => Promise + readonly timers: TimerHost + /** Receives a rejected fire-and-forget call's reason. */ + readonly report: (line: string) => void +} + +/** + * Build the `$` for one hook invocation. + * @param binding - the mod, clock, and engine entry points this instance uses. + * @returns the mods API. + */ +export function createModsApi(binding: ApiBinding): ModsApi { + const { mod, clock, timers } = binding + + async function call(op: string, input: unknown): Promise { + clock?.pause() + try { + return await binding.invoke(op, input) as T + } finally { + clock?.resume() + } + } + + function fire(op: string, input: unknown): void { + call(op, input).catch((error: unknown) => { + binding.report(`${mod.name}: $.${op} failed: ${messageOf(error)}`) + }) + } + + return Object.freeze({ + plugin: Object.freeze({ name: mod.name, root: mod.root }), + ui: Object.freeze({ + log(text: string, options?: UiLogOptions): void { + fire('ui.log', { text, to: options?.to ?? 'transcript' }) + }, + toast(text: string, options?: ToastOptions): void { + fire('ui.toast', { text, ...options?.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs } }) + }, + status(text: string | undefined): void { + fire('ui.status', { text }) + }, + invalidate(event: string): void { + fire('ui.invalidate', { event }) + }, + open(pane: PaneOpenArgs): Promise { + return call('ui.open', pane) + }, + close(pane: { id: string }): Promise { + return call('ui.close', { id: pane.id, origin: 'plugin' }) + }, + panes(): Promise { + return call('ui.panes', {}) + }, + ask(question: string, options?: readonly string[] | AskOptions): Promise { + const normalized: AskOptions = Array.isArray(options) ? { options } : options as AskOptions | undefined ?? {} + return call('ui.ask', { question, ...normalized }) + }, + resolve(): never { + throw new Error(`${mod.name}: $.ui.resolve has no element table: this bridge raises no ui.render events`) + }, + }), + command: Object.freeze({ + register(command: CommandSpec): Promise { + return call('command.register', command) + }, + run(input: { command: string; args?: string }): Promise { + return call('command.run', { command: input.command, args: input.args ?? '' }) + }, + list(): Promise { + return call('command.list', {}) + }, + }), + tool: Object.freeze({ + register(tool: ToolSpec): Promise { + return call('tool.register', tool) + }, + call(input: { tool: string; [argument: string]: unknown }): Promise { + return call('tool.call', input) + }, + list(): Promise { + return call('tool.list', {}) + }, + }), + prompt: Object.freeze({ + submit(input: PromptSubmitArgs): Promise<{ text: string }> { + return call('prompt.submit', { text: input.text, asUser: input.asUser === true }) + }, + }), + session: Object.freeze({ + id: (): Promise => call('session.id', {}), + cwd: (): Promise => call('session.cwd', {}), + root: (): Promise => call('session.root', {}), + model: (): Promise => call('session.model', {}), + turns: (): Promise => call('session.turns', {}), + messages: (): Promise => call('session.messages', {}), + usage: (): Promise => call('session.usage', {}), + version: (): Promise => call('session.version', {}), + }), + state: Object.freeze({ + get(ref: StateRef): Promise<{ value: unknown }> { + return call('state.get', { plugin: ref.plugin, key: ref.key }) + }, + set(ref: StateRef, value: unknown): Promise { + return call('state.set', { plugin: ref.plugin, key: ref.key, value }) + }, + }), + store: Object.freeze({ + get: (key: string): Promise => call('store.get', { key }), + set: (key: string, value: unknown): Promise => call('store.set', { key, value }), + delete: (key: string): Promise => call('store.delete', { key }), + keys: (): Promise => call('store.keys', {}), + }), + clock: Object.freeze({ + now: (): Promise => call('clock.now', {}), + // Counted as the hook's own time, so the clock is not paused. + sleep: (ms: number): Promise => binding.invoke('clock.sleep', { ms }) as Promise, + after: (ms: number, fn: () => unknown): ModTimer => timers.after(ms, fn), + every: (ms: number, fn: () => unknown): ModTimer => timers.every(ms, fn), + }), + fs: Object.freeze({ + read: (path: string): Promise => call('fs.read', { path, as: 'text' }), + write: (path: string, text: string): Promise => call('fs.write', { path, text }), + list: (path?: string): Promise => call('fs.list', { path: path ?? '.' }), + exists: (path: string): Promise => call('fs.exists', { path }), + stat: (path: string): Promise => call('fs.stat', { path, resolve: true }), + }), + process: Object.freeze({ + run(argv: readonly string[], init?: ProcessRunInit): Promise { + return call('process.run', { argv, ...init === undefined ? {} : { init } }) + }, + }), + http: Object.freeze({ + fetch(url: string, init?: HttpInit): Promise { + return call('http.fetch', { url, ...init === undefined ? {} : { init } }) + }, + }), + env: Object.freeze({ + get: (name: string): Promise => call('env.get', { name }), + set: (name: string, value: string | undefined): Promise => call('env.set', { name, ...value === undefined ? {} : { value } }), + }), + }) +} diff --git a/packages/experimental/claude-code-mods/src/chain.ts b/packages/experimental/claude-code-mods/src/chain.ts new file mode 100644 index 0000000000..e871df94b4 --- /dev/null +++ b/packages/experimental/claude-code-mods/src/chain.ts @@ -0,0 +1,295 @@ +/** + * The middleware chain: runs the hooks selected for one event in order, each + * hook's `next` reaching the hooks beneath and then the engine behavior, under + * Claude Code's failure rules (skip a hook that throws, times out, or returns + * no result; keep the result from beneath when it had already called `next`; + * give a `.catch` handler the chance to answer in its place). + * @module + */ + +import { deepFreeze } from '@deepseek-ai/dsh-util-values' +import { messageOf } from './values.ts' +import type { AnyHook, HookFailure, HookOrigin, HookMatcher, ModsApi, HookNext, PluginOptions } from './types.ts' +import { matcherMatches } from './matcher.ts' + +/** One loaded mod: its manifest identity, module path, and resolved `register` options. */ +export interface LoadedMod { + readonly name: string + readonly version: string | undefined + /** Absolute plugin directory, the one holding `.claude-plugin/plugin.json`. */ + readonly root: string + /** Absolute path of the hooks module. */ + readonly modulePath: string + readonly options: PluginOptions + /** Load order; hooks of an earlier mod run outside those of a later one. */ + readonly order: number +} + +/** One `on(...)` registration. */ +export interface RegisteredHook { + readonly mod: LoadedMod + readonly event: string + readonly matcher: HookMatcher | undefined + readonly hook: AnyHook + catchHandler: AnyHook | undefined + /** Failure kinds already reported for this hook; one line per kind until the mod reloads. */ + readonly reported: Set +} + +/** The `next.error.kind === 'timeout'` failure. */ +export class HookTimeoutError extends Error { + constructor(budgetMs: number) { + super(`ran past its ${budgetMs} ms limit`) + this.name = 'HookTimeoutError' + } +} + +/** + * Accounts a hook's own running time: the clock runs while the hook is busy + * and pauses while it awaits `next` or a mods API call. The deadline promise + * rejects once the busy time reaches the limit. + */ +export class BudgetClock { + private spent = 0 + private busySince: number | undefined + private pauses = 0 + private stopped = false + private timer: ReturnType | undefined + private readonly deadline = Promise.withResolvers() + + /** + * @param ms - the running-time limit in milliseconds. + * @param now - monotonic clock in milliseconds. + */ + constructor(readonly ms: number, private readonly now: () => number = () => performance.now()) { + // The deadline only matters while a race awaits it; an unobserved + // rejection after the hook settled must not surface. + this.deadline.promise.catch(() => {}) + } + + /** Rejects with {@link HookTimeoutError} when the busy time reaches the limit. */ + get expired(): Promise { + return this.deadline.promise + } + + /** Milliseconds left before the deadline fires. */ + get remainingMs(): number { + const busy = this.busySince === undefined ? 0 : this.now() - this.busySince + return Math.max(0, this.ms - this.spent - busy) + } + + /** Start counting; the hook is busy from now on. */ + start(): void { + if (this.stopped) return + this.busySince = this.now() + this.arm() + } + + /** Stop counting while the hook awaits `next` or a mods API call. Nested pauses are counted. */ + pause(): void { + if (this.stopped) return + this.pauses += 1 + if (this.busySince !== undefined) { + this.spent += this.now() - this.busySince + this.busySince = undefined + } + this.disarm() + } + + /** Resume counting after the awaited call settled. */ + resume(): void { + if (this.stopped || this.pauses === 0) return + this.pauses -= 1 + if (this.pauses === 0) { + this.busySince = this.now() + this.arm() + } + } + + /** The hook settled: no deadline can fire from now on. */ + stop(): void { + if (this.busySince !== undefined) this.spent += this.now() - this.busySince + this.stopped = true + this.busySince = undefined + this.disarm() + } + + private arm(): void { + this.disarm() + const remaining = this.remainingMs + this.timer = setTimeout(() => { + this.stopped = true + this.busySince = undefined + this.spent = this.ms + this.deadline.reject(new HookTimeoutError(this.ms)) + }, remaining) + this.timer.unref() + } + + private disarm(): void { + if (this.timer !== undefined) clearTimeout(this.timer) + this.timer = undefined + } +} + +/** Everything one dispatch needs besides the hooks themselves. */ +export interface DispatchRequest { + readonly event: string + readonly input: E + /** The engine behavior at the bottom of the chain; receives the input as the chain left it. */ + readonly core: (e: E) => Promise | R + readonly origin: HookOrigin + /** Hooks whose event pattern selects this event, outermost first; matchers are evaluated per hook. */ + readonly hooks: readonly RegisteredHook[] + /** Builds the `$` a hook receives; the clock pauses while its calls are awaited. */ + readonly api: (hook: RegisteredHook, clock: BudgetClock) => ModsApi + /** A hook's own running-time limit. */ + readonly budgetMs: number + /** A `.catch` handler's running-time limit. */ + readonly catchBudgetMs: number + readonly signal: AbortSignal + /** Receives one diagnostic line per skipped hook and failure kind. */ + readonly report: (line: string) => void +} + +/** The engine's own origin. */ +export const ENGINE_ORIGIN: HookOrigin = Object.freeze({ plugin: 'engine', tier: 'core' }) + +/** A hook settled with something other than a result object. */ +class NoResultError extends Error { + constructor() { + super('returned no result') + this.name = 'NoResultError' + } +} + +/** Classify a failed hook and word the diagnostic line's tail. */ +function failureOf(error: unknown): HookFailure & { readonly line: string } { + if (error instanceof HookTimeoutError) return { kind: 'timeout', message: error.message, line: `timeout, ${error.message}` } + if (error instanceof NoResultError) return { kind: 'throw', message: error.message, line: error.message } + const message = error instanceof Error ? `${error.name}: ${error.message}` : messageOf(error) + return { kind: 'throw', message, line: `threw ${message}` } +} + +/** Freeze the event input in place at every depth, as the mods API promises. */ +function freezeInput(input: E): E { + return typeof input === 'object' && input !== null ? deepFreeze(input) : input +} + +/** + * Run one event through its hooks and the engine behavior. + * @param request - the event, its hooks, and the engine behavior beneath them. + * @returns the event's result as the outermost hook returned it. + */ +export async function dispatch(request: DispatchRequest): Promise { + const { hooks } = request + + async function runFrom(index: number, input: E): Promise { + for (let i = index; i < hooks.length; i += 1) { + const hook = hooks[i] as RegisteredHook + if (hook.matcher !== undefined && !matcherMatches(hook.matcher, input)) continue + return runHook(hook, i, input) + } + return request.core(input) + } + + async function runHook(hook: RegisteredHook, index: number, input: E): Promise { + const frozen = freezeInput(input) + const clock = new BudgetClock(request.budgetMs) + // Closure-written flags live on one object so the catch path reads their current values. + const state: { + abandoned: boolean + called: boolean + beneath: Promise | undefined + beneathError: { readonly error: unknown } | undefined + } = { abandoned: false, called: false, beneath: undefined, beneathError: undefined } + const next = Object.assign( + (e: E): Promise => { + state.called = true + if (state.abandoned) return Promise.resolve(undefined as R) + clock.pause() + state.beneath = runFrom(index + 1, e) + .catch((error: unknown) => { + state.beneathError = { error } + throw error + }) + .finally(() => { clock.resume() }) + return state.beneath + }, + { + signal: request.signal, + origin: request.origin, + budget: { ms: request.budgetMs, get remainingMs(): number { return clock.remainingMs } }, + to(): Promise { + return Promise.reject(new Error('next.to is only available to mods in managed prependPlugins or appendPlugins')) + }, + }, + ) as HookNext + const api = request.api(hook, clock) + clock.start() + try { + const running = Promise.resolve().then(() => hook.hook(api, frozen, next as HookNext) as R | Promise) + // An abandoned hook's later rejection is not an unhandled rejection. + running.catch(() => {}) + const result = await Promise.race([running, clock.expired]) + if (typeof result !== 'object' || result === null) throw new NoResultError() + return result + } catch (error: unknown) { + state.abandoned = true + // The engine beneath failed, not the hook: the failure is the event's own. + if (state.beneathError !== undefined && error === state.beneathError.error) throw error + const failure = failureOf(error) + if (!hook.reported.has(failure.kind)) { + hook.reported.add(failure.kind) + request.report(`${hook.mod.name}: ${request.event} hook skipped: ${failure.line}`) + } + if (hook.catchHandler !== undefined) { + const answered = await runCatch(hook, index, frozen, { kind: failure.kind, message: failure.message }, state.called, state.beneath) + if (answered !== undefined) return answered + } + if (state.called && state.beneath !== undefined) return await state.beneath + return await runFrom(index + 1, input) + } finally { + clock.stop() + } + } + + async function runCatch( + hook: RegisteredHook, index: number, input: E, failure: HookFailure, called: boolean, beneath: Promise | undefined, + ): Promise { + const clock = new BudgetClock(request.catchBudgetMs) + const next = Object.assign( + (e: E): Promise => { + if (beneath !== undefined) return beneath + clock.pause() + beneath = runFrom(index + 1, e).finally(() => { clock.resume() }) + return beneath + }, + { + signal: request.signal, + origin: request.origin, + budget: { ms: request.catchBudgetMs, get remainingMs(): number { return clock.remainingMs } }, + to(): Promise { return Promise.reject(new Error('next.to is not available in a .catch handler')) }, + error: failure, + called, + }, + ) as HookNext + const api = request.api(hook, clock) + clock.start() + try { + const handler = hook.catchHandler as AnyHook + const running = Promise.resolve() + .then(() => handler(api, input, next as HookNext) as R | undefined | Promise) + running.catch(() => {}) + const result = await Promise.race([running, clock.expired]) + return typeof result === 'object' && result !== null ? result : undefined + } catch (error: unknown) { + request.report(`${hook.mod.name}: ${request.event} .catch handler skipped: ${failureOf(error).line}`) + return undefined + } finally { + clock.stop() + } + } + + return runFrom(0, request.input) +} diff --git a/packages/experimental/claude-code-mods/src/engine.ts b/packages/experimental/claude-code-mods/src/engine.ts new file mode 100644 index 0000000000..7dd6aaf027 --- /dev/null +++ b/packages/experimental/claude-code-mods/src/engine.ts @@ -0,0 +1,281 @@ +/** + * The mods engine: loaded mods and their hooks, engine-raised events, mods + * API calls raised as events, per-session `$.state`, and the timers mods + * start. It knows nothing of Cordis; the plugin supplies the engine behavior + * for each mods API call and the test kit supplies stubs. + * @module + */ + +import type { BudgetClock, LoadedMod, RegisteredHook } from './chain.ts' +import { dispatch, ENGINE_ORIGIN } from './chain.ts' +import { createModsApi } from './api.ts' +import type { TimerHost } from './api.ts' +import { messageOf, record } from './values.ts' +import { HookRegistry } from './module.ts' +import type { ModsApi, OpResult, ModTimer } from './types.ts' + +/** The engine behavior for one mods API call. */ +export type OpCore = (input: unknown, context: OpContext) => unknown + +/** Engine behaviors by event name, `.`. */ +export type OpTable = Readonly>> + +/** Looks up the engine behavior for one mods API call; `undefined` means none is served. */ +export type OpResolver = (op: string) => OpCore | undefined + +/** + * Thrown by an engine behavior to answer a mods API call with `{ deny }`, so + * the calling mod's `$` call rejects with the reason. + */ +export class OpDenied extends Error { + constructor(readonly reason: string) { + super(reason) + this.name = 'OpDenied' + } +} + +/** What an op core and a `$` instance know about the raising hook's surroundings. */ +export interface OpContext { + readonly mod: LoadedMod + /** The engine-specific binding of the event that is running: the plugin's agent, or the test kit's fixture. */ + readonly binding: B + readonly signal: AbortSignal + readonly engine: ModsEngine +} + +/** Engine construction options. */ +export interface ModsEngineOptions { + /** + * Engine behaviors for mods API calls. The engine answers `state.*`, + * `clock.now`, and `clock.sleep` itself when the resolver serves none. + */ + readonly ops: OpResolver + /** The key `$.state` values live under: Claude Code's "for the whole session". */ + readonly stateKey: (binding: B) => string + /** A hook's own running-time limit in milliseconds. */ + readonly budgetMs: number + /** A `.catch` handler's running-time limit in milliseconds. */ + readonly catchBudgetMs: number + /** Receives diagnostics: skipped hooks, failed fire-and-forget calls, timer callback errors. */ + readonly report: (line: string) => void +} + +/** Options for raising one engine event. */ +export interface RaiseOptions { + readonly binding: B + readonly signal?: AbortSignal +} + +const NEVER_ABORTS = new AbortController().signal + +/** One named `$.state` slot: `\u0000`. */ +function stateSlot(plugin: unknown, key: unknown): string { + if (typeof plugin !== 'string' || typeof key !== 'string') throw new TypeError('$.state needs { plugin, key } strings') + return `${plugin}\u0000${key}` +} + +/** Timers owned by the engine on mods' behalf; disposal cancels them all. */ +class TimerSet implements TimerHost { + private readonly active = new Set>() + + constructor(private readonly report: (line: string) => void, private readonly owner: string) {} + + after(ms: number, fn: () => unknown): ModTimer { + const handle = setTimeout(() => { + this.active.delete(handle) + this.run(fn) + }, ms) + handle.unref() + this.active.add(handle) + return { cancel: () => { clearTimeout(handle); this.active.delete(handle) } } + } + + every(ms: number, fn: () => unknown): ModTimer { + const handle = setInterval(() => { this.run(fn) }, ms) + handle.unref() + this.active.add(handle) + return { cancel: () => { clearInterval(handle); this.active.delete(handle) } } + } + + /** Cancel every timer still scheduled. */ + clear(): void { + for (const handle of this.active) clearTimeout(handle) + this.active.clear() + } + + private run(fn: () => unknown): void { + Promise.resolve().then(fn).catch((error: unknown) => { + this.report(`${this.owner}: timer callback failed: ${messageOf(error)}`) + }) + } +} + +/** + * Loaded mods, their hooks, and the two ways events reach them: the engine + * raises one through every selected hook, and a mod's `$` call raises one + * through the mods loaded before it. + */ +export class ModsEngine { + /** The loaded mods and their registrations. */ + readonly registry = new HookRegistry() + private readonly state = new Map>() + private readonly timers = new Map() + + constructor(private readonly options: ModsEngineOptions) {} + + /** + * Raise one engine event through its hooks, outermost first. + * @param event - the event name. + * @param input - the event input; frozen before a hook sees it. + * @param core - the engine behavior beneath every hook. + * @param options - the binding events of this agent share, and its cancellation. + * @returns the result as the outermost hook returned it. + */ + raise(event: string, input: E, core: (e: E) => Promise | R, options: RaiseOptions): Promise { + const signal = options.signal ?? NEVER_ABORTS + return dispatch({ + event, + input, + core, + origin: ENGINE_ORIGIN, + hooks: this.registry.select(event), + api: (hook, clock) => this.api(hook.mod, clock, options.binding, signal), + budgetMs: this.options.budgetMs, + catchBudgetMs: this.options.catchBudgetMs, + signal, + report: this.options.report, + }) + } + + /** + * Raise one mods API call as its event through the mods loaded before the + * caller, then answer it with the engine behavior. + * @param mod - the calling mod. + * @param op - the event name, `.`. + * @param input - the call's input. + * @param binding - the binding of the event the caller is handling. + * @param signal - cancellation of that event. + * @returns the value the chain answered with. + * @throws Error with the reason when a hook or the engine answered `{ deny }`. + */ + async invoke(mod: LoadedMod, op: string, input: unknown, binding: B, signal: AbortSignal): Promise { + const result = await dispatch({ + event: op, + input, + core: async (e): Promise => { + try { + return { value: await this.opCore(op, e, { mod, binding, signal, engine: this }) } + } catch (error: unknown) { + if (error instanceof OpDenied) return { deny: error.reason } + throw error + } + }, + origin: { plugin: mod.name, tier: 'user' }, + hooks: this.registry.select(op, mod), + api: (hook, clock) => this.api(hook.mod, clock, binding, signal), + budgetMs: this.options.budgetMs, + catchBudgetMs: this.options.catchBudgetMs, + signal, + report: this.options.report, + }) + // Hooks that settle with anything but an object are skipped, so the result is the core's or a hook's object. + const answer = result as { value?: unknown; deny?: unknown } + if (!('value' in answer) && !('deny' in answer)) throw new Error(`${op}: a hook returned neither { value } nor { deny }`) + if (typeof answer.deny === 'string') throw new Error(`${op} refused: ${answer.deny}`) + return answer.value + } + + /** + * Build the `$` for one mod inside one event. + * @param mod - the mod the instance belongs to. + * @param clock - the running hook's clock, or undefined outside a hook. + * @param binding - the event's binding. + * @param signal - the event's cancellation. + * @returns the mods API. + */ + api(mod: LoadedMod, clock: BudgetClock | undefined, binding: B, signal: AbortSignal): ModsApi { + return createModsApi({ + mod, + clock, + invoke: (op, input) => this.invoke(mod, op, input, binding, signal), + timers: this.timersOf(mod), + report: this.options.report, + }) + } + + /** + * Forget one session's `$.state` values. + * @param key - the session key the values were kept under. + */ + forgetState(key: string): void { + this.state.delete(key) + } + + /** + * The `claude plugin validate` style `hooks:` line for one mod. + * @param mod - the loaded mod. + * @returns its events with matchers, comma-separated. + */ + describe(mod: LoadedMod): string { + return this.registry.describe(mod) + } + + /** + * Drop one mod: its hooks stop receiving events and its timers are cancelled. + * @param name - the plugin name. + */ + unload(name: string): void { + this.registry.remove(name) + this.timers.get(name)?.clear() + this.timers.delete(name) + } + + /** Cancel every mod's timers and drop every registration. */ + dispose(): void { + for (const mod of [...this.registry.list()]) this.unload(mod.name) + this.state.clear() + } + + private timersOf(mod: LoadedMod): TimerSet { + let timers = this.timers.get(mod.name) + if (timers === undefined) { + timers = new TimerSet(this.options.report, mod.name) + this.timers.set(mod.name, timers) + } + return timers + } + + private opCore(op: string, input: unknown, context: OpContext): unknown { + const served = this.options.ops(op) + if (served !== undefined) return served(input, context) + const fields = record(input) + switch (op) { + case 'state.get': { + const slot = stateSlot(fields.plugin, fields.key) + return { value: this.state.get(this.options.stateKey(context.binding))?.get(slot) } + } + case 'state.set': { + const slot = stateSlot(fields.plugin, fields.key) + const key = this.options.stateKey(context.binding) + let values = this.state.get(key) + if (values === undefined) { + values = new Map() + this.state.set(key, values) + } + values.set(slot, fields.value) + return undefined + } + case 'clock.now': + return Date.now() + case 'clock.sleep': { + const ms = fields.ms + if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) throw new TypeError('$.clock.sleep needs a non-negative number of milliseconds') + return new Promise((resolve) => { setTimeout(resolve, ms) }) + } + default: + throw new Error(`no implementation for ${op}`) + } + } +} + +export type { RegisteredHook } diff --git a/packages/experimental/claude-code-mods/src/host-ops.ts b/packages/experimental/claude-code-mods/src/host-ops.ts new file mode 100644 index 0000000000..894f9a9312 --- /dev/null +++ b/packages/experimental/claude-code-mods/src/host-ops.ts @@ -0,0 +1,490 @@ +/** + * The engine behavior for each mods API call this bridge serves, mapped onto + * harness services: commands and tools on the agent's scoped registries, + * `ui.ask` on the user-questions answerer, files on `ctx.fs`, processes on + * `ctx.subprocess`, the store on a storage domain, session facts on the + * agent's Session and projections. A service a deployment did not compose + * fails the call with the service name. + * @module + */ + +import type { Context } from '@deepseek-ai/cordis' +import type { Agent } from '@deepseek-ai/dsh-agent' +// Type-only: activate the `ctx.get('subprocess')` and `ctx.get('sessionProjections')` Context declarations. +import type {} from '@deepseek-ai/dsh-subprocess' +import type {} from '@deepseek-ai/dsh-session-projection' +import type { SessionProjectionStateMap } from '@deepseek-ai/dsh-session-projection/types' +import type { CommandDefinition } from '@deepseek-ai/dsh-commands' +import type { FileSystem, FsTarget } from '@deepseek-ai/dsh-fs' +import { createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm' +import type { ContentBlock, Message } from '@deepseek-ai/dsh-llm' +import { scopeOf } from '@deepseek-ai/dsh-scope' +import type { Session } from '@deepseek-ai/dsh-session' +// Type-only: merges the token-meter projection states into SessionProjectionStateMap. +import type {} from '@deepseek-ai/dsh-token-meter' +import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain' +import type { Domain } from '@deepseek-ai/dsh-storage-domain' +import { assertObjectJsonSchema } from '@deepseek-ai/dsh-tools' +import type { ToolDefinition, ToolExecutionResult } from '@deepseek-ai/dsh-tools' +import type { AskUserQuestionOption } from '@deepseek-ai/dsh-user-questions' +import { randomUUID } from '@deepseek-ai/dsh-util-crypto' +import type { JsonValue } from '@deepseek-ai/dsh-util-values' +import { record, requireString, stringify } from './values.ts' +import { z as zod } from 'zod' +import type { ModsEngine, OpContext, OpTable } from './engine.ts' +import type { ToolNameAliases } from './tool-names.ts' +import type { + CommandInfo, CommandRunInput, CommandRunResult, FsEntry, FsStat, HttpResponse, ProcessRunResult, SessionMessage, + SessionUsage, SessionVersion, ToolCallResult, ToolInfo, +} from './types.ts' + +/** The binding every event of this bridge carries: the agent it belongs to, when one does. */ +export interface AgentBinding { + readonly agent: Agent | undefined +} + +/** The mods API version string `$.session.version()` reports. */ +export const MODS_API_VERSION: SessionVersion = Object.freeze({ version: 'claude-code-mods/0.1', engine: 'deepseek-harness' }) + +/** Largest file `$.fs.read` returns and `$.fs.write` accepts, Claude Code's own limit. */ +export const FS_MAX_BYTES = 4 * 1024 * 1024 + +/** Largest JSON a plugin's `$.store` holds in total, Claude Code's own limit. */ +export const STORE_MAX_BYTES = 4 * 1024 * 1024 + +/** Largest stdout or stderr `$.process.run` keeps. */ +const PROCESS_OUTPUT_MAX_BYTES = 1024 * 1024 + +/** + * Full name the model sees for a mod-registered tool: Claude Code's MCP-style spelling. + * @param plugin - the registering plugin's name. + * @param tool - the name the mod registered. + * @returns `mcp____`. + */ +export function modToolName(plugin: string, tool: string): string { + return `mcp__${plugin}__${tool}` +} + +/** Tool-local names stay short and id-safe, Claude Code's own rule. */ +const REGISTERED_NAME = /^[A-Za-z0-9_-]{1,64}$/u + +/** Per-plugin `$.store` records: one JSON object of keys per plugin. */ +const storeDomainSpec = defineDomain({ + name: 'claude_code_mods', + version: 1, + tables: { store: domainTable>(zod.record(zod.string(), zod.json())) }, +}) + +/** Options the host op table is built from. */ +export interface HostOpsOptions { + readonly ctx: Context + readonly aliases: ToolNameAliases + /** Default `$.process.run` timeout in milliseconds. */ + readonly processTimeoutMs: number + /** Collects disposers of registrations made on agents' scoped registries, so bridge disposal can undo them. */ + readonly registrations: Set<() => void> + /** Names of commands mods registered, for `$.command.list` sources. */ + readonly modCommands: Set + /** Full names of tools mods registered, whose calls a `tool.call` hook may answer with a successful result. */ + readonly modTools: Set +} + +function requireAgent(context: OpContext, op: string): Agent { + const agent = context.binding.agent + if (agent === undefined) throw new Error(`$.${op} needs a session, and this event has none`) + return agent +} + +function textOf(content: readonly ContentBlock[]): string { + return content.filter((block): block is Extract => block.type === 'text').map(block => block.text).join('') +} + +/** + * Project one tool outcome onto Claude Code's `tool.call` result. + * @param result - the harness tool outcome. + * @returns its text as `result`, with `isError` on a failure. + */ +export function toolCallResultOf(result: ToolExecutionResult): ToolCallResult { + return result.isError ? { result: textOf(result.content), isError: true } : { result: textOf(result.content) } +} + +/** The model's raw JSON arguments as an object; an unparsable string is an empty input. */ +function argumentsOf(raw: string): Record { + try { + return record(JSON.parse(raw)) + } catch { + // A truncated or malformed tool call keeps its id and name; its input is unknown. + return {} + } +} + +function sessionMessages(messages: readonly Message[], aliases: ToolNameAliases): SessionMessage[] { + const out: SessionMessage[] = [] + for (const message of messages) { + if (message.role !== 'user' && message.role !== 'assistant') continue + const toolUses = message.content + .filter((block): block is Extract => block.type === 'tool-call') + .map(block => ({ tool_use_id: block.id, tool: aliases.toMod(block.name), input: argumentsOf(block.arguments) })) + out.push({ role: message.role, text: textOf(message.content), toolUses }) + } + return out +} + +/** Entry kind in Claude Code's vocabulary. */ +function kindOf(type: 'file' | 'directory' | 'symlink' | 'other'): FsEntry['kind'] { + return type === 'file' ? 'file' : type === 'directory' ? 'dir' : 'other' +} + +function readOutput(reader: { readFrom(fromByte: number): { text: string } } | undefined): string { + /* v8 ignore next -- both streams are spawned collected, so their readers exist */ + return reader === undefined ? '' : reader.readFrom(0).text +} + +/** An entry's size, or 0 where the provider reports none. */ +function sizeOf(info: { size?: number } | undefined): number { + return info?.size ?? 0 +} + +/** + * Build the op table over the composed harness services. + * @param options - the context, aliases, limits, and registration tracking. + * @returns engine behaviors by event name. + */ +export function createHostOps(options: HostOpsOptions): OpTable { + const { ctx, aliases } = options + let storeDomain: Promise> | undefined + + function store(): Promise> { + if (storeDomain !== undefined) return storeDomain + const domains = ctx.get('storageDomain') + if (domains === undefined) throw new Error('$.store needs a storage domain service (dsh-storage-domain), which this deployment did not compose') + const opened = domains.open(storeDomainSpec) + storeDomain = opened + ctx.effect(() => () => opened.then(domain => domain.close()), 'claude-code-mods: close store domain') + return opened + } + + async function target(path: string, agent: Agent | undefined, signal: AbortSignal): Promise { + const cwd = agent?.session.header.cwd + return fs().resolve(path, { ...cwd === undefined ? {} : { cwd }, signal }) + } + + function fs(): FileSystem { + const service = ctx.get('fs') + if (service === undefined) throw new Error('$.fs needs a filesystem service (dsh-fs), which this deployment did not compose') + return service + } + + function track(dispose: () => void): void { + options.registrations.add(dispose) + } + + /** One projection's state, or undefined when the registry or the projection's owning plugin is not composed. */ + function projection(session: Session, key: K): SessionProjectionStateMap[K] | undefined { + return ctx.get('sessionProjections')?.stateOf(session, key) + } + + return { + // ---- ui: no mod drawing surface exists; log-style calls reach the host log ---- + 'ui.log': (input, { mod }) => { + const { text, to } = record(input) + const line = `${mod.name}: ${requireString(text, '$.ui.log text')}` + if (to === 'debug') ctx.logger.debug(line) + else ctx.logger.info(line) + }, + 'ui.toast': (input, { mod }) => { ctx.logger.info(`${mod.name} (toast): ${requireString(record(input).text, '$.ui.toast text')}`) }, + 'ui.status': (input, { mod }) => { + const { text } = record(input) + ctx.logger.info(`${mod.name} (status): ${text === undefined ? '(cleared)' : requireString(text, '$.ui.status text')}`) + }, + 'ui.invalidate': () => undefined, + 'ui.open': () => ({ isPlaced: false, reason: 'this bridge places no panes: the harness Web GUI has no mod pane' }), + 'ui.close': () => undefined, + 'ui.panes': () => [], + 'ui.ask': async (input, context) => { + const agent = requireAgent(context, 'ui.ask') + const questions = ctx.get('userQuestions') + if (questions === undefined) throw new Error('$.ui.ask needs a user-questions service (dsh-user-questions), which this deployment did not compose') + const { question, options: labels, header, multiSelect } = record(input) + const choices: AskUserQuestionOption[] | undefined = Array.isArray(labels) + ? labels.map(label => ({ label: requireString(label, '$.ui.ask option') })) + : undefined + const answer = await questions.ask({ + questions: [{ + id: 'answer', + question: requireString(question, '$.ui.ask question'), + ...typeof header === 'string' ? { header } : {}, + ...choices === undefined ? {} : { options: choices }, + ...multiSelect === true ? { multiSelect: true } : {}, + }], + agent, + signal: context.signal, + }) + const item = answer.answers[0] + if (item === undefined) throw new Error('$.ui.ask: the user dismissed the question') + return item.custom ?? item.selected.join(', ') + }, + + // ---- commands ---- + 'command.register': (input, { mod, binding, engine }) => { + const spec = record(input) + const name = requireString(spec.name, '$.command.register name') + if (!REGISTERED_NAME.test(name)) throw new Error(`"/${name}" refused: command names are letters, digits, "_" and "-", up to 64 characters`) + const description = requireString(spec.description, '$.command.register description') + const registry = (binding.agent?.ctx ?? ctx).get('commands') + if (registry === undefined) throw new Error('$.command.register needs a command registry (dsh-commands), which this deployment did not compose') + const lowered = name.toLowerCase() + const definition: CommandDefinition = { + name: lowered, + description, + ...typeof spec.argumentHint === 'string' ? { input: { hint: spec.argumentHint } } : {}, + handler: async (invocation) => { + const run = await engine.raise( + 'command.run', + { command: lowered, args: invocation.rawInput.trim(), origin: { kind: 'composer' } }, + () => ({ text: `${mod.name} registered /${lowered} but no command.run hook answered it; add on('command.run', { command: '${lowered}' }, hook)` }), + { binding: { agent: invocation.agent }, signal: invocation.signal }, + ) + return { kind: 'success', ...run.text === undefined ? {} : { text: `${mod.name}: ${run.text}` } } + }, + } + const dispose = registry.register(definition) + options.modCommands.add(lowered) + track(dispose) + }, + 'command.run': async (input, context) => { + const agent = requireAgent(context, 'command.run') + const registry = ctx.get('commands') + if (registry === undefined) throw new Error('$.command.run needs a command registry (dsh-commands), which this deployment did not compose') + const { command, args } = record(input) + const name = requireString(command, '$.command.run command') + const line = typeof args === 'string' && args.length > 0 ? `/${name} ${args}` : `/${name}` + const execution = await registry.execute(agent, line, [], context.signal) + if (execution === undefined) throw new Error(`/${name} is not a command`) + const { result } = execution + if (result.kind === 'error') throw new Error(result.text) + return { ...result.text === undefined ? {} : { text: result.text } } satisfies CommandRunResult + }, + 'command.list': (_input, context) => { + const agent = requireAgent(context, 'command.list') + const registry = ctx.get('commands') + if (registry === undefined) return [] + return registry.list(agent).map((descriptor): CommandInfo => ({ + name: descriptor.name, + description: descriptor.description, + source: options.modCommands.has(descriptor.name) ? 'plugin' : 'builtin', + })) + }, + + // ---- tools ---- + 'tool.register': (input, { mod, binding }) => { + const spec = record(input) + const name = requireString(spec.name, '$.tool.register name') + if (!REGISTERED_NAME.test(name)) throw new Error(`tool "${name}" refused: tool names are letters, digits, "_" and "-", up to 64 characters`) + const description = requireString(spec.description, '$.tool.register description') + const inputSchema = spec.inputSchema === undefined ? { type: 'object', properties: {} } : record(spec.inputSchema) + // The schema reaches the model, so an unsupported keyword fails this call, not a later request. + assertObjectJsonSchema(inputSchema) + const registry = (binding.agent?.ctx ?? ctx).get('tools') + if (registry === undefined) throw new Error('$.tool.register needs a tool registry (dsh-tools), which this deployment did not compose') + const fullName = modToolName(mod.name, name) + const definition: ToolDefinition = { + name: fullName, + description, + parameters: inputSchema, + output: { + // The answering hook's text, already rendered as content blocks. + schema: { type: 'array' }, + render: (_args, value) => value as unknown as ContentBlock[], + }, + execute() { + return Promise.reject(new Error(`${mod.name} registered ${fullName} but no tool.call hook answered it; add on('tool.call', { tool: '${fullName}' }, hook)`)) + }, + } + track(registry.register(definition)) + options.modTools.add(fullName) + }, + 'tool.call': async (input, context) => { + const agent = context.binding.agent + const registry = ctx.get('tools') + if (registry === undefined) throw new Error('$.tool.call needs a tool registry (dsh-tools), which this deployment did not compose') + const { tool, tool_use_id: _ignored, agentId: _agent, ...args } = record(input) + const result = await registry.execute({ + callId: ToolCallId(`mod-${randomUUID()}`), + name: aliases.toHarness(requireString(tool, '$.tool.call tool')), + arguments: args, + ...agent === undefined ? {} : { agent }, + signal: context.signal, + }) + return toolCallResultOf(result) + }, + 'tool.list': (_input, context) => { + const registry = ctx.get('tools') + if (registry === undefined) return [] + const agent = context.binding.agent + return registry.schemas(agent === undefined ? undefined : scopeOf(agent.ctx)) + .map((schema): ToolInfo => ({ name: aliases.toMod(schema.name), description: schema.description })) + }, + + // ---- prompt ---- + 'prompt.submit': (input, context) => { + const agent = requireAgent(context, 'prompt.submit') + const { text, asUser } = record(input) + const body = requireString(text, '$.prompt.submit text') + const framed = asUser === true ? body : `Message from the "${context.mod.name}" mod:\n${body}` + agent.followup(createUserMessage({ + content: [{ type: 'text', text: framed }], + source: { kind: 'claude-code-mods' }, + })) + return { text: framed } + }, + + // ---- session facts ---- + 'session.id': (_input, context) => requireAgent(context, 'session.id').session.id, + 'session.cwd': (_input, context) => requireAgent(context, 'session.cwd').session.header.cwd ?? process.cwd(), + 'session.root': (_input, context) => requireAgent(context, 'session.root').session.header.cwd ?? process.cwd(), + 'session.model': (_input, context) => { + const agent = requireAgent(context, 'session.model') + return agent.session.requestHeader()?.config.model ?? agent.options.model ?? '' + }, + 'session.turns': (_input, context) => { + const agent = requireAgent(context, 'session.turns') + const boundary = projection(agent.session, 'turnBoundary') + if (boundary === undefined) throw new Error('$.session.turns needs the turnBoundary projection, which this deployment did not compose') + return boundary.lastTurn + }, + 'session.messages': (_input, context) => sessionMessages(requireAgent(context, 'session.messages').session.deriveMessages(), aliases), + 'session.usage': (_input, context): SessionUsage => { + const agent = requireAgent(context, 'session.usage') + const pressure = projection(agent.session, 'contextPressure') + const tokens = pressure?.pressureTokens + const window = pressure?.contextWindow ?? 0 + return { + startedAt: agent.session.header.createdAt, + context: { + ...tokens === undefined ? {} : { tokens }, + window, + ...tokens === undefined || window === 0 ? {} : { percent: Math.round((tokens / window) * 100) }, + }, + rateLimits: [], + } + }, + 'session.version': () => MODS_API_VERSION, + + // ---- store: one durable JSON object per plugin ---- + 'store.get': async (input, { mod }) => (await store()).table('store').get(mod.name)?.[requireString(record(input).key, '$.store.get key')], + 'store.set': async (input, { mod }) => { + const { key, value } = record(input) + const name = requireString(key, '$.store.set key') + const table = (await store()).table('store') + const next = { ...table.get(mod.name), [name]: value as JsonValue } + const encoded = stringify(next) + if (encoded === undefined || encoded.length > STORE_MAX_BYTES) throw new Error(`$.store.set: ${mod.name}'s store would exceed ${STORE_MAX_BYTES} bytes of JSON`) + await table.put(mod.name, JSON.parse(encoded) as Record) + }, + 'store.delete': async (input, { mod }) => { + const name = requireString(record(input).key, '$.store.delete key') + const table = (await store()).table('store') + const current = table.get(mod.name) + if (current === undefined || !(name in current)) return undefined + const { [name]: _removed, ...rest } = current + await table.put(mod.name, rest) + }, + 'store.keys': async (_input, { mod }) => Object.keys((await store()).table('store').get(mod.name) ?? {}), + + // ---- files: through the filesystem seam, relative to the session workspace ---- + 'fs.read': async (input, context) => { + const path = requireString(record(input).path, '$.fs.read path') + const resolved = await target(path, context.binding.agent, context.signal) + const info = await fs().stat(resolved, context.signal) + if (info?.size !== undefined && info.size > FS_MAX_BYTES) throw new Error(`$.fs.read: ${path} is larger than ${FS_MAX_BYTES} bytes`) + return fs().readText(resolved, context.signal) + }, + 'fs.write': async (input, context) => { + const { path, text } = record(input) + const body = requireString(text, '$.fs.write text') + if (Buffer.byteLength(body, 'utf8') > FS_MAX_BYTES) throw new Error(`$.fs.write: content is larger than ${FS_MAX_BYTES} bytes`) + const resolved = await target(requireString(path, '$.fs.write path'), context.binding.agent, context.signal) + await fs().writeText(resolved, body, undefined, context.signal) + }, + 'fs.list': async (input, context) => { + const resolved = await target(requireString(record(input).path, '$.fs.list path'), context.binding.agent, context.signal) + const entries = await fs().listDir(resolved, context.signal) + return entries.map((entry): FsEntry => ({ name: entry.name, kind: kindOf(entry.type), size: sizeOf(entry), isLink: false })) + }, + 'fs.exists': async (input, context) => { + const resolved = await target(requireString(record(input).path, '$.fs.exists path'), context.binding.agent, context.signal) + return await fs().stat(resolved, context.signal) !== undefined + }, + 'fs.stat': async (input, context) => { + const path = requireString(record(input).path, '$.fs.stat path') + const cwd = context.binding.agent?.session.header.cwd + const info = await fs().lstat(path, cwd === undefined ? {} : { cwd }, context.signal) + if (info === undefined) throw new Error(`$.fs.stat: ${path} does not exist`) + if (info.type !== 'symlink') return { kind: kindOf(info.type), size: sizeOf(info), mtimeMs: 0, isLink: false } satisfies FsStat + // A link reports what it points at, as Claude Code's `resolve: true` does; a dangling one is `other`. + const followed = await fs().stat(await target(path, context.binding.agent, context.signal), context.signal) + return { kind: kindOf(followed?.type ?? 'other'), size: sizeOf(followed), mtimeMs: 0, isLink: true } satisfies FsStat + }, + + // ---- processes: argv, no shell, collected output ---- + 'process.run': async (input, context) => { + const subprocess = ctx.get('subprocess') + if (subprocess === undefined) throw new Error('$.process.run needs a subprocess service (dsh-subprocess), which this deployment did not compose') + const { argv, init } = record(input) + if (!Array.isArray(argv) || argv.length === 0 || !argv.every(item => typeof item === 'string')) { + throw new TypeError('$.process.run needs a non-empty argv list of strings') + } + const settings = record(init) + const timeoutMs = typeof settings.timeoutMs === 'number' ? settings.timeoutMs : options.processTimeoutMs + const cwd = typeof settings.cwd === 'string' ? settings.cwd : context.binding.agent?.session.header.cwd ?? process.cwd() + const env = typeof settings.env === 'object' && settings.env !== null ? settings.env as Record : undefined + const deadline = AbortSignal.any([context.signal, AbortSignal.timeout(timeoutMs)]) + const handle = subprocess.spawn({ + argv, + cwd, + stdio: { stdin: 'ignore', stdout: { maxBytes: PROCESS_OUTPUT_MAX_BYTES }, stderr: { maxBytes: PROCESS_OUTPUT_MAX_BYTES } }, + graceMs: 1000, + signal: deadline, + ...env === undefined ? {} : { env }, + }) + const outcome = await handle.done + if (deadline.aborted) throw new Error(`$.process.run: ${argv[0]} did not exit within ${timeoutMs} ms`) + if (outcome.exitCode === null) throw new Error(`$.process.run: ${argv[0]} was terminated by signal ${String(outcome.signal)}`) + return { + exitCode: outcome.exitCode, + stdout: readOutput(handle.collected.stdout), + stderr: readOutput(handle.collected.stderr), + } satisfies ProcessRunResult + }, + + // ---- network ---- + 'http.fetch': async (input, context) => { + const { url, init } = record(input) + const settings = record(init) + const timeoutMs = typeof settings.timeoutMs === 'number' ? settings.timeoutMs : options.processTimeoutMs + const response = await fetch(requireString(url, '$.http.fetch url'), { + ...typeof settings.method === 'string' ? { method: settings.method } : {}, + ...typeof settings.headers === 'object' && settings.headers !== null ? { headers: settings.headers as Record } : {}, + ...typeof settings.body === 'string' ? { body: settings.body } : {}, + signal: AbortSignal.any([context.signal, AbortSignal.timeout(timeoutMs)]), + }) + const headers: Record = {} + response.headers.forEach((value, name) => { + headers[name] = value + }) + return { status: response.status, ok: response.ok, headers, text: await response.text() } satisfies HttpResponse + }, + + // ---- environment of this process ---- + 'env.get': input => process.env[requireString(record(input).name, '$.env.get name')], + 'env.set': (input) => { + const { name, value } = record(input) + const key = requireString(name, '$.env.set name') + if (value === undefined) Reflect.deleteProperty(process.env, key) + else process.env[key] = requireString(value, '$.env.set value') + }, + } +} + +export type { ModsEngine } diff --git a/packages/experimental/claude-code-mods/src/index.ts b/packages/experimental/claude-code-mods/src/index.ts new file mode 100644 index 0000000000..abbf5d8931 --- /dev/null +++ b/packages/experimental/claude-code-mods/src/index.ts @@ -0,0 +1,363 @@ +/** + * Experimental bridge for Claude Code mods: loads each configured plugin + * directory's hooks module, runs `register(on, options)`, and raises the mod + * events from harness extension points — `session.start` on `agent/created`, + * `prompt.submit` and `turn.start` on `agent/pre-step`, `tool.call` around + * `tools/execute`, `turn.complete` on `turn/end`, `session.end` on + * `agent/disposed`, and `command.run` from the commands a mod registers. The + * `$` a hook receives is served by {@link createHostOps} over the composed + * harness services. + * @module @deepseek-ai/dsh-experimental-claude-code-mods + */ + +import type { Context } from '@deepseek-ai/cordis' +import z from '@deepseek-ai/schemastery' +import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent' +import { createUserMessage } from '@deepseek-ai/dsh-llm' +import type { AssistantMessage, ContentBlock, ContextFormed, TokenUsage } from '@deepseek-ai/dsh-llm' +import type { UserMessage } from '@deepseek-ai/dsh-session' +import type { PostToolDecision, ToolExecutionResult, ToolExecutionToken } from '@deepseek-ai/dsh-tools' +import type { JsonValue } from '@deepseek-ai/dsh-util-values' +import { ModsEngine } from './engine.ts' +import { createHostOps, toolCallResultOf } from './host-ops.ts' +import type { AgentBinding } from './host-ops.ts' +import { readModManifest, resolvePluginOptions } from './manifest.ts' +import { loadHooksModule } from './module.ts' +import { createToolNameAliases } from './tool-names.ts' +import { messageOf, record, stringify } from './values.ts' +import type { + PromptSubmitInput, PromptSubmitResult, SessionEndInput, SessionEndResult, SessionStartInput, + SessionStartResult, ToolCallInput, ToolCallResult, TurnCompleteInput, TurnCompleteResult, TurnStartInput, + TurnStartResult, TurnUsage, +} from './types.ts' + +declare module '@deepseek-ai/dsh-llm' { + interface MessageSourceMap { + /** + * Context a mod added through `prompt.submit`, or a prompt a mod submitted + * through `$.prompt.submit`; the text names the mod. Readers preserve the + * message without this producer; nothing reads the kind back. + * @persistenceAttribution + */ + 'claude-code-mods': { kind: 'claude-code-mods' } & ContextFormed + } +} + +export type * from './types.ts' +export { createModTestKit, mock, type ModTestKit, type ModTestKitOptions, type StubHook, type TestKitRaisers, type TestModule } from './testing.ts' +export { DEFAULT_TOOL_ALIASES } from './tool-names.ts' +export { KNOWN_EVENTS } from './matcher.ts' +export { MODS_API_VERSION } from './host-ops.ts' + +export const name = 'claude-code-mods' +// Every harness service is read through `ctx.get` when a mod's call needs it, +// so a deployment composes only what its mods use. +export const inject: string[] = [] + +/** Plugin config: which mod directories to load and the limits their hooks run under. */ +export interface Config { + /** + * Plugin directories to load, each holding `.claude-plugin/plugin.json` and + * `hooks/hooks.json`, as `claude --plugin-dir` takes them. A relative path + * resolves against the process launch cwd. Load order is chain order. + */ + pluginDirs: string[] + /** `register` option values by plugin name, overlaid on the manifest's `userConfig` defaults. */ + options?: Record> + /** A hook's own running-time limit in milliseconds (Claude Code: 10 seconds). */ + hookTimeoutMs?: number + /** A `.catch` handler's running-time limit in milliseconds (Claude Code: 1 second). */ + catchTimeoutMs?: number + /** Default `$.process.run` and `$.http.fetch` timeout in milliseconds (Claude Code: 30 seconds). */ + processTimeoutMs?: number + /** Claude Code tool name → harness tool name entries added to the built-in alias table. */ + toolAliases?: Record +} + +const optionValue = z.union([z.string(), z.number(), z.boolean(), z.array(z.string())]) + +export const Config: z = z.object({ + pluginDirs: z.array(z.string()).required(), + options: z.dict(z.dict(optionValue)), + hookTimeoutMs: z.number().default(10_000), + catchTimeoutMs: z.number().default(1_000), + processTimeoutMs: z.number().default(30_000), + toolAliases: z.dict(z.string()), +}) + +function assertPositive(field: string, value: number): void { + if (!Number.isFinite(value) || value <= 0) throw new Error(`claude-code-mods: ${field} must be a positive number of milliseconds`) +} + +function textOf(blocks: readonly ContentBlock[]): string { + return blocks.filter((block): block is Extract => block.type === 'text').map(block => block.text).join('') +} + +/** + * Replace the prompt text across the claimed messages with one rewritten + * text: the first text block carries it, other text blocks go, every + * non-text block stays in place. + */ +function rewritePromptText(claimed: readonly UserMessage[], text: string): UserMessage[] { + let placed = false + return claimed.map((message) => { + if (!message.content.some(block => block.type === 'text')) return message + const content: ContentBlock[] = [] + for (const block of message.content) { + if (block.type !== 'text') { + content.push(block) + } else if (!placed) { + placed = true + content.push({ type: 'text', text }) + } + } + return { ...message, content } + }) +} + +/** What the bridge folds from one session's event stream about its open turn. */ +interface TurnRecord { + turn: number + startedAt: number + /** The last committed assistant text of the turn. */ + answer: string + usage: TurnUsage | undefined +} + +/** Fold one committed assistant message into the turn's answer and usage. */ +function foldAssistantMessage(turnRecord: TurnRecord, data: { message: AssistantMessage; usage?: TokenUsage }): void { + turnRecord.answer = textOf(data.message.content) + if (data.usage === undefined) return + const model = data.message.source.model + const usage = turnRecord.usage ?? { input_tokens: 0, output_tokens: 0, cache_read_input_tokens: 0, cache_creation_input_tokens: 0, model } + usage.input_tokens += data.usage.inputTokens + usage.output_tokens += data.usage.outputTokens + usage.cache_read_input_tokens += data.usage.cacheReadTokens ?? 0 + usage.cache_creation_input_tokens += data.usage.cacheWriteTokens ?? 0 + usage.model = model + turnRecord.usage = usage +} + +/** Detached mod runs the bridge tracks so disposal can await them. */ +class DetachedRuns { + private readonly pending = new Set>() + readonly controller = new AbortController() + + track(run: Promise): void { + this.pending.add(run) + void run.finally(() => { this.pending.delete(run) }) + } + + async drain(): Promise { + this.controller.abort(new Error('claude-code-mods disposed')) + await Promise.allSettled([...this.pending]) + } +} + +export async function apply(ctx: Context, config: Config): Promise { + const hookTimeoutMs = config.hookTimeoutMs ?? 10_000 + const catchTimeoutMs = config.catchTimeoutMs ?? 1_000 + const processTimeoutMs = config.processTimeoutMs ?? 30_000 + assertPositive('hookTimeoutMs', hookTimeoutMs) + assertPositive('catchTimeoutMs', catchTimeoutMs) + assertPositive('processTimeoutMs', processTimeoutMs) + const aliases = createToolNameAliases(config.toolAliases) + const registrations = new Set<() => void>() + const modCommands = new Set() + const modTools = new Set() + const report = (line: string): void => { ctx.logger.warn(`claude-code-mods: ${line}`) } + const ops = createHostOps({ ctx, aliases, processTimeoutMs, registrations, modCommands, modTools }) + const engine = new ModsEngine({ + ops: op => ops[op], + stateKey: binding => binding.agent?.session.id ?? '', + budgetMs: hookTimeoutMs, + catchBudgetMs: catchTimeoutMs, + report, + }) + const detached = new DetachedRuns() + ctx.effect(() => async () => { + for (const dispose of registrations) dispose() + registrations.clear() + engine.dispose() + await detached.drain() + }, 'claude-code-mods: unload mods') + + // A plugin directory that does not read as a mod is a deployment error and + // fails the load; a module that does not import or whose `register` throws is + // the mod author's, so the session continues without that mod. + const directories = config.pluginDirs.map(dir => readModManifest(dir, process.cwd())) + for (const [order, directory] of directories.entries()) { + if (directory.hasSettingsHooks) { + ctx.logger.warn(`claude-code-mods: ${directory.manifest.name}: settings hooks in hooks.json are not run by this bridge; mount @deepseek-ai/dsh-hooks-claude-code for them`) + } + const options = resolvePluginOptions(directory.manifest, config.options?.[directory.manifest.name]) + try { + const { mod, hooks } = await loadHooksModule(directory, options, order) + engine.registry.add(mod, hooks) + ctx.logger.info(`claude-code-mods: hooks module ${mod.name}@inline loaded (tier user); events: ${engine.describe(mod) || '(none)'}`) + } catch (error: unknown) { + ctx.logger.warn(`claude-code-mods: ${messageOf(error)}`) + } + } + + const agents = ctx.get('agents') + const isRoot = (agent: Agent): boolean => agents === undefined || agents.roots().includes(agent) + /** Root agents that received `session.start`; the registry no longer lists an agent once it is disposed. */ + const startedRoots = new Set() + const agentIdOf = (agent: Agent | undefined): { agentId: string } | Record => + agent !== undefined && !isRoot(agent) ? { agentId: agent.id } : {} + + /** Per-session fold of the open turn, keyed by session id. */ + const turns = new Map() + /** `:` keys whose first pre-step already raised `turn.start`. */ + const startedTurns = new Set() + /** Rewritten tool results the post-execute listener installs as content. */ + const replacements = new Map() + /** Tools whose argument rewrite was already reported. */ + const rewritesReported = new Set() + + ctx.on('agent/created', async ({ agent }) => { + if (!isRoot(agent)) return + startedRoots.add(agent.session.id) + const input: SessionStartInput = { + cwd: agent.session.header.cwd ?? process.cwd(), + surface: null, + isInteractive: ctx.get('userQuestions') !== undefined, + } + await engine.raise( + 'session.start', input, e => ({ cwd: e.cwd }), { binding: { agent }, signal: detached.controller.signal }, + ) + }) + + ctx.on('agent/disposed', ({ agent }) => { + turns.delete(agent.session.id) + engine.forgetState(agent.session.id) + if (!startedRoots.delete(agent.session.id)) return + const input: SessionEndInput = { reason: 'other', sessionId: agent.session.id } + detached.track(engine.raise( + 'session.end', input, e => ({ sessionId: e.sessionId }), { binding: { agent }, signal: detached.controller.signal }, + )) + }) + + ctx.on('agent/pre-step', async ({ agent, messages, turn, signal }, next): Promise => { + const binding: AgentBinding = { agent } + const turnKey = `${agent.session.id}:${turn}` + const first = !startedTurns.has(turnKey) + startedTurns.add(turnKey) + const text = textOf(messages.flatMap(message => message.content)) + let submitted: PromptSubmitResult = { text } + if (messages.length > 0) { + const input: PromptSubmitInput = { text, wait: false, origin: { kind: 'composer' } } + submitted = await engine.raise( + 'prompt.submit', input, e => ({ text: e.text, ...e.context === undefined ? {} : { context: e.context } }), { binding, signal }, + ) + if (submitted.drop !== undefined) { + ctx.logger.info(`claude-code-mods: prompt dropped: ${submitted.drop}`) + return { kind: 'reject' } + } + } + if (first) { + const input: TurnStartInput = { text: submitted.text, turnId: String(turn), ...agentIdOf(agent) } + await engine.raise('turn.start', input, e => ({ turnId: e.turnId }), { binding, signal }) + } + const downstream = await next() + if (downstream.kind !== 'enter' || messages.length === 0) return downstream + let entered = downstream.messages + if (submitted.text !== text) { + const claimed = new Set(messages) + entered = [...rewritePromptText(messages, submitted.text), ...downstream.messages.filter(message => !claimed.has(message))] + } + const context = submitted.context ?? [] + if (context.length > 0) { + entered = [...entered, createUserMessage({ + content: context.map(line => ({ type: 'text', text: line })), + source: { kind: 'claude-code-mods' }, + })] + } + return { ...downstream, messages: entered } + }) + + ctx.on('tools/execute', async (exec, next): Promise => { + if (engine.registry.select('tool.call').length === 0) return next() + const agent = exec.agent + const callArguments = record(exec.arguments) + const input: ToolCallInput = { ...callArguments, tool: aliases.toMod(exec.name), tool_use_id: exec.callId, ...agentIdOf(agent) } + let beneath: ToolExecutionResult | undefined + const answer = await engine.raise('tool.call', input, async (e) => { + const { tool: _tool, tool_use_id: _id, agentId: _agentId, ...rewritten } = e + if (JSON.stringify(rewritten) !== JSON.stringify(callArguments) && !rewritesReported.has(exec.name)) { + rewritesReported.add(exec.name) + ctx.logger.warn(`claude-code-mods: a tool.call hook rewrote the arguments of ${exec.name}; this bridge runs the call with its logged arguments`) + } + beneath = await next() + return toolCallResultOf(beneath) + }, { binding: { agent }, signal: exec.signal }) + if (answer.deny !== undefined) { + return { + isError: true, + error: { message: answer.deny, info: { name: 'ModDenied', code: 'MOD_DENIED' } }, + content: [{ type: 'text', text: `Error: ${answer.deny}` }], + } + } + const text = typeof answer.result === 'string' ? answer.result : stringify(answer.result) ?? '' + if (beneath !== undefined) { + const mapped = toolCallResultOf(beneath) + if (mapped.result === answer.result && mapped.isError === answer.isError) return beneath + replacements.set(exec.token, text) + return beneath + } + if (modTools.has(exec.name) && answer.isError !== true) { + const content: ContentBlock[] = [{ type: 'text', text }] + return { isError: false, value: content as unknown as JsonValue, content } + } + // A built-in tool's success value must satisfy its own output schema, so a + // mod's answer in its place is reported as an error-shaped result. + return { isError: true, error: { message: text, info: { name: 'ModAnswered', code: 'MOD_ANSWERED' } }, content: [{ type: 'text', text }] } + }) + + ctx.on('tools/post-execute', async (exec, _result, next): Promise => { + const replacement = replacements.get(exec.token) + if (replacement === undefined) return next() + replacements.delete(exec.token) + const downstream = await next() + if (downstream.kind === 'block') return downstream + return { + kind: 'accept', + content: [{ type: 'text', text: replacement }], + ...downstream.additionalContexts === undefined ? {} : { additionalContexts: downstream.additionalContexts }, + } + }) + + ctx.on('session/event', (session, event) => { + if (event.type === 'turn/start') { + turns.set(session.id, { turn: event.data.turn, startedAt: Date.now(), answer: '', usage: undefined }) + return + } + if (event.type === 'assistant/message') { + const turnRecord = turns.get(session.id) + if (turnRecord !== undefined) foldAssistantMessage(turnRecord, event.data) + return + } + if (event.type !== 'turn/end') return + const { turn, reason } = event.data + startedTurns.delete(`${session.id}:${turn}`) + const agent = agents?.get(session.id) + if (agent === undefined) return + const turnRecord = turns.get(session.id) + const folded = turnRecord?.turn === turn ? turnRecord : undefined + const input: TurnCompleteInput = { + turnId: String(turn), + answer: folded?.answer ?? '', + durationMs: folded === undefined ? 0 : Date.now() - folded.startedAt, + isAborted: reason.kind === 'aborted', + reason: reason.kind === 'aborted' ? 'aborted' : reason.kind === 'error' ? 'error' : 'answer', + ...agentIdOf(agent), + ...folded?.usage === undefined ? {} : { usage: folded.usage }, + } + detached.track(engine.raise( + 'turn.complete', input, () => ({ text: '' }), { binding: { agent }, signal: detached.controller.signal }, + ).then((result) => { + if (result.text.length > 0) ctx.logger.info(`claude-code-mods: ${result.text}`) + })) + }) +} diff --git a/packages/experimental/claude-code-mods/src/manifest.ts b/packages/experimental/claude-code-mods/src/manifest.ts new file mode 100644 index 0000000000..46e5a4a2be --- /dev/null +++ b/packages/experimental/claude-code-mods/src/manifest.ts @@ -0,0 +1,146 @@ +/** + * The plugin directory a mod ships as: `.claude-plugin/plugin.json` names it, + * `hooks/hooks.json` points `modules` at the hooks module. Parsing and + * validation are pure; the directory is read by {@link readModManifest}. + * @module + */ + +import { readFileSync } from 'node:fs' +import { dirname, isAbsolute, resolve, sep } from 'node:path' +import { messageOf } from './values.ts' +import type { PluginOptions, PluginOptionValue } from './types.ts' + +/** The extensions Claude Code loads a hooks module from; this bridge imports them through Node. */ +export const HOOKS_MODULE_EXTENSIONS: readonly string[] = ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.mts', '.cts', '.tsx'] + +/** Plugin names must stay path- and id-safe; Claude Code's own rule. */ +const PLUGIN_NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/u + +/** One `userConfig` field of the manifest: its default supplies `options` when the deployment sets none. */ +interface UserConfigField { + readonly default?: PluginOptionValue +} + +/** The parsed manifest fields this bridge reads. */ +export interface ModManifest { + readonly name: string + readonly version: string | undefined + readonly description: string | undefined + /** Field defaults declared under `userConfig`, by field name. */ + readonly userConfigDefaults: Readonly> +} + +/** A mod directory read and validated, before its module is imported. */ +export interface ModDirectory { + readonly manifest: ModManifest + /** Absolute plugin directory. */ + readonly root: string + /** Absolute path of the one hooks module `hooks.json` names. */ + readonly modulePath: string + /** `hooks.json` also holds settings hooks under `hooks`, which this bridge does not run. */ + readonly hasSettingsHooks: boolean +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value) +} + +function isOptionValue(value: unknown): value is PluginOptionValue { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return true + return Array.isArray(value) && value.every(item => typeof item === 'string') +} + +/** + * Validate the parsed `plugin.json` value. + * @param raw - the parsed JSON. + * @param source - path used in error messages. + * @returns the fields this bridge reads. + */ +export function parseModManifest(raw: unknown, source: string): ModManifest { + if (!isRecord(raw)) throw new Error(`${source}: plugin.json must be a JSON object`) + const { name, version, description, userConfig } = raw + if (typeof name !== 'string' || !PLUGIN_NAME.test(name)) { + throw new Error(`${source}: plugin.json "name" must be lowercase letters, digits, "_" or "-", up to 64 characters`) + } + if (version !== undefined && typeof version !== 'string') throw new Error(`${source}: plugin.json "version" must be a string`) + if (description !== undefined && typeof description !== 'string') { + throw new Error(`${source}: plugin.json "description" must be a string`) + } + const userConfigDefaults: Record = {} + if (userConfig !== undefined) { + if (!isRecord(userConfig)) throw new Error(`${source}: plugin.json "userConfig" must be an object of fields`) + for (const [field, spec] of Object.entries(userConfig)) { + if (!isRecord(spec)) throw new Error(`${source}: plugin.json userConfig.${field} must be an object`) + const { default: fallback } = spec as UserConfigField + if (fallback === undefined) continue + if (!isOptionValue(fallback)) { + throw new Error(`${source}: plugin.json userConfig.${field}.default must be a string, number, boolean, or string list`) + } + userConfigDefaults[field] = fallback + } + } + return { name, version, description, userConfigDefaults } +} + +/** + * Validate the parsed `hooks.json` value and resolve its one module path. + * @param raw - the parsed JSON. + * @param hooksJsonPath - absolute path of the file, which relative module paths resolve against. + * @returns the absolute module path and whether settings hooks are also declared. + */ +export function parseHooksJson(raw: unknown, hooksJsonPath: string): { modulePath: string; hasSettingsHooks: boolean } { + if (!isRecord(raw)) throw new Error(`${hooksJsonPath}: hooks.json must be a JSON object`) + const { modules, hooks } = raw + if (!Array.isArray(modules) || modules.length !== 1 || typeof modules[0] !== 'string') { + throw new Error(`${hooksJsonPath}: hooks.json "modules" must list exactly one module path`) + } + const relative = modules[0] + if (isAbsolute(relative)) throw new Error(`${hooksJsonPath}: hooks.json module path must be relative to hooks.json`) + const modulePath = resolve(dirname(hooksJsonPath), relative) + if (!modulePath.startsWith(dirname(dirname(hooksJsonPath)) + sep)) { + throw new Error(`${hooksJsonPath}: hooks.json module path must stay inside the plugin directory`) + } + if (!HOOKS_MODULE_EXTENSIONS.some(extension => modulePath.endsWith(extension))) { + throw new Error(`${hooksJsonPath}: hooks module "${relative}" must end in one of ${HOOKS_MODULE_EXTENSIONS.join(', ')}`) + } + return { modulePath, hasSettingsHooks: hooks !== undefined } +} + +/** + * Overlay deployment option values on the manifest's `userConfig` defaults. + * @param manifest - the parsed manifest. + * @param configured - values the deployment sets for this plugin. + * @returns the frozen `options` argument of `register`. + */ +export function resolvePluginOptions(manifest: ModManifest, configured: PluginOptions | undefined): PluginOptions { + return Object.freeze({ ...manifest.userConfigDefaults, ...configured }) +} + +function readJson(path: string): unknown { + let text: string + try { + text = readFileSync(path, 'utf8') + } catch (error: unknown) { + throw new Error(`${path}: cannot read: ${messageOf(error)}`) + } + try { + return JSON.parse(text) as unknown + } catch (error: unknown) { + throw new Error(`${path}: invalid JSON: ${messageOf(error)}`) + } +} + +/** + * Read and validate one mod directory. + * @param dir - the plugin directory; a relative path resolves against `cwd`. + * @param cwd - base for a relative `dir`. + * @returns the validated directory, ready for its module to be imported. + */ +export function readModManifest(dir: string, cwd: string): ModDirectory { + const root = resolve(cwd, dir) + const manifestPath = resolve(root, '.claude-plugin', 'plugin.json') + const hooksJsonPath = resolve(root, 'hooks', 'hooks.json') + const manifest = parseModManifest(readJson(manifestPath), manifestPath) + const { modulePath, hasSettingsHooks } = parseHooksJson(readJson(hooksJsonPath), hooksJsonPath) + return { manifest, root, modulePath, hasSettingsHooks } +} diff --git a/packages/experimental/claude-code-mods/src/matcher.ts b/packages/experimental/claude-code-mods/src/matcher.ts new file mode 100644 index 0000000000..cfd90a25d8 --- /dev/null +++ b/packages/experimental/claude-code-mods/src/matcher.ts @@ -0,0 +1,121 @@ +/** + * Event-name patterns and field matchers: which registered hooks an event + * selects. Event names are Claude Code's; a hook on a name this bridge never + * raises registers fine and never runs. + * @module + */ + +import type { HookMatcher, MatcherValue } from './types.ts' + +/** + * Every event name a mod may hook, in Claude Code's reference order: engine + * events, then every mods API method as `.`. `on` refuses a + * name outside this list with Claude Code's own wording, so a misspelling + * fails at load rather than registering a hook that never runs. + */ +export const KNOWN_EVENTS: ReadonlySet = new Set([ + // tools + 'tool.call', 'tool.check', 'tool.describe', + // prompts and what the model reads + 'prompt.submit', 'prompt.fill', 'prompt.suggest', 'prompt.edit', 'prompt.compose', 'prompt.section', + 'prompt.context', 'prompt.attachment', 'skill.prompt', 'attribution.text', + // commands and configuration + 'command.run', 'command.describe', 'config.set', 'config.describe', + // turns + 'turn.start', 'turn.step', 'turn.complete', + // session + 'session.start', 'session.end', 'session.compact', 'session.receive', 'session.send', 'session.append', + 'session.attach', 'session.detach', 'session.measure', + // subagents + 'agent.offer', 'agent.spawn', + // interface + 'ui.render', 'ui.resolve', 'ui.press', 'ui.input', 'ui.select', 'ui.focus', 'ui.scroll', 'ui.close', 'ui.message', + // other mods + 'plugin.register', 'engine.create', + // telemetry + 'telemetry.log', 'telemetry.mark', + // mods API calls + 'ui.log', 'ui.toast', 'ui.status', 'ui.notice', 'ui.invalidate', 'ui.open', 'ui.panes', 'ui.blit', 'ui.ask', 'ui.copy', + 'command.register', 'command.list', + 'tool.register', 'tool.list', + 'agent.register', 'agent.list', + 'model.complete', 'model.fork', 'model.classify', + 'prompt.read', + 'turn.abort', + 'session.messages', 'session.cwd', 'session.root', 'session.model', 'session.turns', 'session.id', 'session.repo', + 'session.surface', 'session.surfaces', 'session.usage', 'session.version', 'session.authorize', + 'config.list', + 'settings.read', + 'env.get', 'env.set', + 'fs.read', 'fs.write', 'fs.list', 'fs.exists', 'fs.stat', 'fs.ancestors', + 'store.get', 'store.set', 'store.delete', 'store.keys', + 'state.get', 'state.set', + 'clock.now', 'clock.sleep', 'clock.after', 'clock.every', + 'http.fetch', + 'process.run', 'process.spawn', + 'mcp.call', 'mcp.connect', + 'audio.play', 'audio.speak', + 'telemetry.log', 'telemetry.mark', +]) + +/** + * Whether a name passed to `on` is an exact event name, `*`, or a `.*` glob. + * @param pattern - the name or glob. + * @returns true when `on` accepts it. + */ +export function isEventPattern(pattern: string): boolean { + if (pattern === '*') return true + if (pattern.endsWith('.*')) { + const namespace = pattern.slice(0, -2) + return namespace.length > 0 && [...KNOWN_EVENTS].some(event => event.startsWith(`${namespace}.`)) + } + return KNOWN_EVENTS.has(pattern) +} + +/** + * Whether a registered pattern selects an event. `*` selects every event + * except the telemetry ones, which a mod hooks by name or as `telemetry.*`. + * @param pattern - the name or glob passed to `on`. + * @param event - the event being raised. + * @returns true when the pattern selects the event. + */ +export function eventMatches(pattern: string, event: string): boolean { + if (pattern === event) return true + if (pattern === '*') return !event.startsWith('telemetry.') + return pattern.endsWith('.*') && event.startsWith(pattern.slice(0, -1)) +} + +function valueMatches(expected: MatcherValue, actual: unknown): boolean { + if (expected instanceof RegExp) { + return (typeof actual === 'string' || typeof actual === 'number') && expected.test(String(actual)) + } + if (Array.isArray(expected)) return expected.some(candidate => candidate === actual) + return expected === actual +} + +/** + * Whether every matcher field accepts the event's top-level field of the same + * name: a scalar by equality, an array by membership, a RegExp by test. + * @param matcher - the matcher passed to `on`. + * @param input - the event input as this hook would receive it. + * @returns true when the hook should run. + */ +export function matcherMatches(matcher: HookMatcher, input: unknown): boolean { + if (typeof input !== 'object' || input === null) return false + const record = input as Record + return Object.entries(matcher).every(([field, expected]) => valueMatches(expected, record[field])) +} + +/** + * Render a matcher the way `claude plugin validate` prints it. + * @param matcher - the matcher, or undefined for a hook without one. + * @returns `{field=value,...}`, or an empty string. + */ +export function describeMatcher(matcher: HookMatcher | undefined): string { + if (matcher === undefined) return '' + const fields = Object.entries(matcher).map(([field, value]) => { + const rendered = value instanceof RegExp ? String(value) : Array.isArray(value) ? value.join('|') : String(value) + return `${field}=${rendered}` + }) + return `{${fields.join(',')}}` +} diff --git a/packages/experimental/claude-code-mods/src/module.ts b/packages/experimental/claude-code-mods/src/module.ts new file mode 100644 index 0000000000..911af244ea --- /dev/null +++ b/packages/experimental/claude-code-mods/src/module.ts @@ -0,0 +1,164 @@ +/** + * Hooks-module loading: import the module Node-side, run its `register(on, + * options)`, and keep every `on(...)` registration in one ordered registry + * that dispatch selects from. + * @module + */ + +import { pathToFileURL } from 'node:url' +import type { LoadedMod, RegisteredHook } from './chain.ts' +import { messageOf } from './values.ts' +import { describeMatcher, eventMatches, isEventPattern } from './matcher.ts' +import type { ModDirectory } from './manifest.ts' +import type { AnyHook, HooksModule, HookMatcher, ModOn, PluginOptions, HookRegistration } from './types.ts' + +/** Mods ordered by load; hooks ordered by mod, then by registration. */ +export class HookRegistry { + private readonly hooks: RegisteredHook[] = [] + private readonly mods: LoadedMod[] = [] + + /** + * The loaded mods. + * @returns the mods in load order. + */ + list(): readonly LoadedMod[] { + return this.mods + } + + /** + * Add one mod and its registrations; a mod of the same name must be removed first. + * @param mod - the mod the hooks belong to. + * @param hooks - its registrations in `on` order. + */ + add(mod: LoadedMod, hooks: readonly RegisteredHook[]): void { + if (this.mods.some(loaded => loaded.name === mod.name)) { + throw new Error(`hooks module ${mod.name} not loaded: another plugin of that name loads first`) + } + this.mods.push(mod) + this.hooks.push(...hooks) + } + + /** + * Drop one mod and its hooks. + * @param name - the plugin name. + */ + remove(name: string): void { + const index = this.mods.findIndex(mod => mod.name === name) + if (index === -1) return + const [mod] = this.mods.splice(index, 1) + for (let i = this.hooks.length - 1; i >= 0; i -= 1) { + if (this.hooks[i]?.mod === mod) this.hooks.splice(i, 1) + } + } + + /** + * Hooks whose pattern selects `event`, outermost first. A mods API call a mod + * raised is seen only by the mods loaded before it. + * @param event - the event name. + * @param raisedBy - the mod whose `$` call became the event, or undefined for the engine. + * @returns the ordered hooks; matchers are evaluated at run time. + */ + select(event: string, raisedBy?: LoadedMod): RegisteredHook[] { + return this.hooks + .filter(hook => (raisedBy === undefined || hook.mod.order < raisedBy.order) && eventMatches(hook.event, event)) + .sort((left, right) => left.mod.order - right.mod.order) + } + + /** + * Event names with matchers, as `claude plugin validate` prints the `hooks:` line. + * @param mod - the loaded mod. + * @returns its events with matchers, comma-separated. + */ + describe(mod: LoadedMod): string { + return this.hooks + .filter(hook => hook.mod === mod) + .map(hook => `${hook.event}${describeMatcher(hook.matcher)}`) + .join(', ') + } +} + +function isMatcher(value: unknown): value is HookMatcher { + return typeof value === 'object' && value !== null && !Array.isArray(value) +} + +/** + * Build the `on` function one `register` call receives and collect its + * registrations. Validation follows Claude Code: the event name must be a + * known name or glob, and one event may be registered without a matcher only + * once. + * @param mod - the mod registering. + * @returns `on` and the list it appends to. + */ +export function createOn(mod: LoadedMod): { on: ModOn; hooks: RegisteredHook[] } { + const hooks: RegisteredHook[] = [] + const unmatched = new Set() + const on = ((event: unknown, matcherOrHook: unknown, maybeHook?: unknown): HookRegistration => { + if (typeof event !== 'string') throw new TypeError(`${mod.name}: the event name passed to on() is not a string literal`) + if (!isEventPattern(event)) throw new Error(`${mod.name}: "${event}" is not an event`) + const hook = maybeHook ?? matcherOrHook + const matcher = maybeHook === undefined ? undefined : matcherOrHook + if (typeof hook !== 'function') throw new TypeError(`${mod.name}: on("${event}") needs a hook function`) + if (matcher !== undefined && !isMatcher(matcher)) throw new TypeError(`${mod.name}: on("${event}") matcher must be an object`) + if (matcher === undefined) { + if (unmatched.has(event)) throw new Error(`${mod.name}: on("${event}") is registered twice without a matcher`) + unmatched.add(event) + } + const registered: RegisteredHook = { + mod, event, matcher, hook: hook as AnyHook, catchHandler: undefined, reported: new Set(), + } + hooks.push(registered) + return { + catch(handler: AnyHook): void { + if (typeof handler !== 'function') throw new TypeError(`${mod.name}: on("${event}").catch needs a handler function`) + registered.catchHandler = handler + }, + } + }) as ModOn + return { on, hooks } +} + +/** Distinguishes one import of a module path from the next, so every load evaluates the module afresh. */ +let loadCounter = 0 + +/** + * Import one validated mod directory's hooks module and run `register`. Every + * load evaluates the module anew, as a Claude Code reload does, so module-level + * variables start over. + * @param directory - the validated plugin directory. + * @param options - the resolved `options` argument of `register`. + * @param order - the mod's load order. + * @returns the loaded mod and its registrations. + */ +export async function loadHooksModule( + directory: ModDirectory, + options: PluginOptions, + order: number, +): Promise<{ mod: LoadedMod; hooks: RegisteredHook[] }> { + const { manifest } = directory + const mod: LoadedMod = Object.freeze({ + name: manifest.name, + version: manifest.version, + root: directory.root, + modulePath: directory.modulePath, + options, + order, + }) + let namespace: unknown + try { + loadCounter += 1 + namespace = await import(`${pathToFileURL(directory.modulePath).href}?load=${loadCounter}`) + } catch (error: unknown) { + throw new Error(`${manifest.name}: hooks module did not load: ${messageOf(error)}`, { cause: error }) + } + const register = (namespace as Partial | null)?.register + if (typeof register !== 'function') { + throw new Error(`${manifest.name}: hooks module did not load: ${directory.modulePath} does not export a register function`) + } + const { on, hooks } = createOn(mod) + try { + await register(on, options) + } catch (error: unknown) { + throw new Error(`${manifest.name}: hooks module did not load: register threw ${messageOf(error)}`, { cause: error }) + } + return { mod, hooks } +} diff --git a/packages/experimental/claude-code-mods/src/testing.ts b/packages/experimental/claude-code-mods/src/testing.ts new file mode 100644 index 0000000000..73063af1eb --- /dev/null +++ b/packages/experimental/claude-code-mods/src/testing.ts @@ -0,0 +1,236 @@ +/** + * A test kit in the spirit of `claude-code/testing`: load mods from + * directories or inline modules, register stubs that answer in the engine's + * place beneath every mod, and raise events through the mods' hooks — no + * session, agent, or harness service involved. + * @module + */ + +import { resolve } from 'node:path' +import type { LoadedMod } from './chain.ts' +import { ModsEngine, OpDenied } from './engine.ts' +import type { OpCore } from './engine.ts' +import { readModManifest, resolvePluginOptions } from './manifest.ts' +import { createOn, loadHooksModule } from './module.ts' +import type { + CommandRunInput, CommandRunResult, ModsApi, PluginOptions, PromptSubmitInput, PromptSubmitResult, ModRegister, + SessionEndInput, SessionEndResult, SessionStartInput, SessionStartResult, ToolCallInput, ToolCallResult, + TurnCompleteInput, TurnCompleteResult, TurnStartInput, TurnStartResult, +} from './types.ts' + +/** A mod written inline for a test instead of read from a directory. */ +export interface TestModule { + readonly name: string + readonly register: ModRegister + readonly options?: PluginOptions +} + +/** + * A stub answering one event in the engine's place. For an engine event it + * returns that event's result; for a mods API call it returns `{ value }` or + * `{ deny }`, and `undefined` from either reads as "not answered". + */ +export type StubHook = ($: ModsApi, e: never) => unknown + +/** Test kit construction options. */ +export interface ModTestKitOptions { + /** Plugin directories to load, in chain order. A relative path resolves against `cwd`. */ + readonly dirs?: readonly string[] + /** Inline mods, loaded after `dirs` in the given order. */ + readonly modules?: readonly TestModule[] + /** Base for relative `dirs`; defaults to the process cwd. */ + readonly cwd?: string + /** `register` option values by plugin name, overlaid on manifest `userConfig` defaults. */ + readonly options?: Readonly> + /** A hook's own running-time limit in milliseconds; defaults to 5 seconds, Claude Code's test limit. */ + readonly budgetMs?: number +} + +/** The engine's own `$`-shaped raisers: each sends its event through the mods and resolves to the result. */ +export interface TestKitRaisers { + readonly tool: { call(input: Omit & { tool_use_id?: string }): Promise } + readonly command: { run(input: Pick): Promise } + readonly prompt: { submit(input: Pick & Partial): Promise } + readonly session: { + start(input: SessionStartInput): Promise + end(input: SessionEndInput): Promise + } + readonly turn: { + start(input: TurnStartInput): Promise + complete(input: TurnCompleteInput): Promise + } +} + +/** A loaded test kit. */ +export interface ModTestKit { + /** Register a stub that answers `event` beneath every mod; name a mods API call without the `$.`. */ + on(event: string, stub: StubHook): void + /** Raise any event through the mods; a stub, a built-in default, or `no implementation for ` answers at the bottom. */ + raise(event: string, input: unknown): Promise + /** Typed raisers for the events this bridge sends from the harness. */ + readonly $: TestKitRaisers + /** Diagnostic lines the engine reported: skipped hooks, failed fire-and-forget calls, timer errors. */ + readonly reports: string[] + /** The loaded mods in chain order. */ + readonly mods: readonly LoadedMod[] + /** Cancel every mod timer and drop every registration. */ + dispose(): void +} + +/** Stubs that answer a whole namespace from memory. */ +export const mock = Object.freeze({ + /** + * Answer `$.store` from an in-memory map. + * @param on - the kit's `on`. + * @param initial - entries the store starts with. + * @returns the live map, to read what the mod saved. + */ + store(on: ModTestKit['on'], initial: Readonly> = {}): Map { + const saved = new Map(Object.entries(initial)) + on('store.get', (_$, e: { key: string }) => ({ value: saved.get(e.key) })) + on('store.set', (_$, e: { key: string; value: unknown }) => { + saved.set(e.key, e.value) + return { value: undefined } + }) + on('store.delete', (_$, e: { key: string }) => { + saved.delete(e.key) + return { value: undefined } + }) + on('store.keys', () => ({ value: [...saved.keys()] })) + return saved + }, + /** + * Answer `$.env.get` from fixed variables and record `$.env.set` writes. + * @param on - the kit's `on`. + * @param variables - the environment the mod sees. + * @returns the live record, to read what the mod set. + */ + env(on: ModTestKit['on'], variables: Readonly> = {}): Record { + const env: Record = { ...variables } + on('env.get', (_$, e: { name: string }) => ({ value: env[e.name] })) + on('env.set', (_$, e: { name: string; value?: string }) => { + env[e.name] = e.value + return { value: undefined } + }) + return env + }, +}) + +/** Engine events whose default answer mirrors Claude Code's test-kit table when no stub is registered. */ +const DEFAULT_ENGINE_ANSWERS: Readonly unknown>> = { + 'session.start': (e: SessionStartInput) => ({ cwd: e.cwd }), + 'session.end': (e: SessionEndInput) => ({ sessionId: e.sessionId }), + 'turn.start': (e: TurnStartInput) => ({ turnId: e.turnId }), + 'turn.complete': () => ({ text: '' }), + 'prompt.submit': (e: PromptSubmitInput) => ({ text: e.text, ...e.context === undefined ? {} : { context: e.context } }), + 'command.run': () => ({}), +} + +/** Mods API calls the kit answers itself, as Claude Code's kit does. */ +const DEFAULT_OP_ANSWERS: Readonly> = { + 'ui.invalidate': () => ({ value: undefined }), +} + +/** + * Load mods for a test and return the kit. + * @param options - the mods to load and the hook time limit. + * @returns the kit, with every mod's `register` already run. + */ +export async function createModTestKit(options: ModTestKitOptions = {}): Promise { + const reports: string[] = [] + const stubs = new Map() + const cwd = options.cwd ?? process.cwd() + const engine = new ModsEngine>({ + ops: (op) => { + const stub = stubs.get(op) ?? DEFAULT_OP_ANSWERS[op] + return stub === undefined ? undefined : opStub(op, stub) + }, + stateKey: () => 'test', + budgetMs: options.budgetMs ?? 5_000, + catchBudgetMs: 1_000, + report: (line) => { reports.push(line) }, + }) + const kitMod: LoadedMod = Object.freeze({ + name: 'claude-code-testing', version: undefined, root: cwd, modulePath: '', options: Object.freeze({}), order: Number.MAX_SAFE_INTEGER, + }) + const binding = {} + const signal = new AbortController().signal + + function opStub(op: string, stub: StubHook): OpCore> { + return async (input) => { + const answer = await stub(engine.api(kitMod, undefined, binding, signal), input as never) + if (typeof answer !== 'object' || answer === null) throw new Error(`${op}: a stub returned neither { value } nor { deny }`) + const result = answer as { value?: unknown; deny?: unknown } + if (typeof result.deny === 'string') throw new OpDenied(result.deny) + if (!('value' in result)) throw new Error(`${op}: a stub returned neither { value } nor { deny }`) + return result.value + } + } + + let order = 0 + for (const dir of options.dirs ?? []) { + const directory = readModManifest(resolve(cwd, dir), cwd) + const resolved = resolvePluginOptions(directory.manifest, options.options?.[directory.manifest.name]) + const { mod, hooks } = await loadHooksModule(directory, resolved, order) + engine.registry.add(mod, hooks) + order += 1 + } + for (const module of options.modules ?? []) { + const mod: LoadedMod = Object.freeze({ + name: module.name, version: undefined, root: cwd, modulePath: ``, + options: Object.freeze({ ...module.options, ...options.options?.[module.name] }), order, + }) + const { on, hooks } = createOn(mod) + await module.register(on, mod.options) + engine.registry.add(mod, hooks) + order += 1 + } + + function raise(event: string, input: unknown): Promise { + return engine.raise(event, input, async (e) => { + const stub = stubs.get(event) + if (stub !== undefined) { + const answer = await stub(engine.api(kitMod, undefined, binding, signal), e as never) + if (typeof answer !== 'object' || answer === null) throw new Error(`${event}: the stub returned no result`) + return answer as R + } + const fallback = DEFAULT_ENGINE_ANSWERS[event] + if (fallback === undefined) throw new Error(`no implementation for ${event}`) + return fallback(e as never) as R + }, { binding, signal }) + } + + let calls = 0 + return { + on(event, stub) { + stubs.set(event, stub) + }, + raise, + $: { + tool: { + call: input => raise('tool.call', { tool_use_id: `test-${++calls}`, ...input }), + }, + command: { + run: input => raise('command.run', { ...input, origin: { kind: 'composer' } }), + }, + prompt: { + submit: input => raise('prompt.submit', { wait: false, origin: { kind: 'composer' }, ...input }), + }, + session: { + start: input => raise('session.start', input), + end: input => raise('session.end', input), + }, + turn: { + start: input => raise('turn.start', input), + complete: input => raise('turn.complete', input), + }, + }, + reports, + get mods() { + return engine.registry.list() + }, + dispose() { + engine.dispose() + }, + } +} diff --git a/packages/experimental/claude-code-mods/src/tool-names.ts b/packages/experimental/claude-code-mods/src/tool-names.ts new file mode 100644 index 0000000000..d274c9e8de --- /dev/null +++ b/packages/experimental/claude-code-mods/src/tool-names.ts @@ -0,0 +1,45 @@ +/** + * Tool-name aliases between Claude Code's built-in tool names, which a mod's + * matchers and `e.tool` use, and the harness tool names the registry knows. + * @module + */ + +/** Claude Code tool name → harness tool name, for the built-in tools both products ship. */ +export const DEFAULT_TOOL_ALIASES: Readonly> = Object.freeze({ + Bash: 'bash', + Read: 'read', + Edit: 'edit', + Write: 'write', + Glob: 'glob', + Grep: 'grep', + WebFetch: 'web_fetch', + WebSearch: 'web_search', + Task: 'subagent', + TodoWrite: 'todo_write', + AskUserQuestion: 'ask_user_question', + ExitPlanMode: 'exit_plan_mode', + Skill: 'skill', +}) + +/** Two-way tool-name translation. */ +export interface ToolNameAliases { + /** The name a mod sees for a harness tool: its Claude Code alias, or the harness name itself. */ + toMod(harnessName: string): string + /** The harness tool a mod named: the alias target, or the name itself. */ + toHarness(modName: string): string +} + +/** + * Build the translation from the built-in table plus deployment overrides. + * @param overrides - Claude Code name → harness name entries that extend or replace the defaults. + * @returns the two-way translation. + */ +export function createToolNameAliases(overrides: Readonly> = {}): ToolNameAliases { + const forward = new Map(Object.entries({ ...DEFAULT_TOOL_ALIASES, ...overrides })) + const reverse = new Map() + for (const [modName, harnessName] of forward) reverse.set(harnessName, modName) + return { + toMod: harnessName => reverse.get(harnessName) ?? harnessName, + toHarness: modName => forward.get(modName) ?? modName, + } +} diff --git a/packages/experimental/claude-code-mods/src/types.ts b/packages/experimental/claude-code-mods/src/types.ts new file mode 100644 index 0000000000..48127c8c2a --- /dev/null +++ b/packages/experimental/claude-code-mods/src/types.ts @@ -0,0 +1,432 @@ +/** + * Mods-facing types: the hooks-module contract (`register`, `on`, hooks, + * `next`), the event inputs and results this bridge raises, and the `$` mods + * API subset it serves. Field names follow Claude Code's declarations so an + * unmodified mod type-checks against the same names; only the members this + * bridge implements are declared. + * @module + */ + +/** Values a plugin manifest's `userConfig` fields and `register(on, options)` carry. */ +export type PluginOptionValue = string | number | boolean | readonly string[] + +/** The `options` argument of `register`: manifest `userConfig` defaults overlaid by deployment values. */ +export type PluginOptions = Readonly> + +/** Tier a mod runs in; this bridge loads every mod as `user`, and its own engine behavior is `core`. */ +export type ModTier = 'prepend' | 'user' | 'append' | 'builtin' | 'core' + +/** Who raised an event: the engine itself, or the mod whose `$` call became the event. */ +export interface HookOrigin { + readonly plugin: string + readonly tier: ModTier +} + +/** The failing hook's error as a `.catch` handler sees it on `next.error`. */ +export interface HookFailure { + readonly kind: 'throw' | 'timeout' + readonly message: string +} + +/** The hook's own running-time limit: the whole limit and what is left now. */ +export interface HookBudget { + readonly ms: number + readonly remainingMs: number +} + +/** + * The `next` handler every hook receives: calling it runs the hooks beneath, + * then the engine behavior, and resolves to the event's result. Time spent + * awaiting it does not count against the hook's budget. + */ +export interface HookNext { + (e: E): Promise + /** Fires when the event is abandoned (the owning agent cancelled). */ + readonly signal: AbortSignal + /** Who raised the event. */ + readonly origin: HookOrigin + /** The hook's running-time limit. */ + readonly budget: HookBudget + /** Skipping tiers is a managed-settings feature this bridge does not implement; always throws. */ + to(e: E, tier: ModTier): Promise + /** In a `.catch` handler: how the hook failed. */ + readonly error?: HookFailure + /** In a `.catch` handler: whether the failed hook had called `next`. */ + readonly called?: boolean +} + +/** A hook on one event: the mods API, the frozen event input, and the next handler. */ +export type ModHook = ($: ModsApi, e: E, next: HookNext) => R | Promise + +/** A hook whose event and result the registry does not narrow. */ +export type AnyHook = ModHook + +/** What `on` returns: `.catch` attaches the hook's error handler. */ +export interface HookRegistration { + catch(handler: AnyHook): void +} + +/** A matcher field: one value, a list of allowed values, or a regular expression. */ +export type MatcherValue = string | number | boolean | null | RegExp | readonly (string | number | boolean | null)[] + +/** The optional second argument of `on`: top-level event fields the hook requires. */ +export type HookMatcher = Readonly> + +/** The `on` function `register` receives. */ +export interface ModOn { + (event: string, hook: AnyHook): HookRegistration + (event: string, matcher: HookMatcher, hook: AnyHook): HookRegistration +} + +/** The function a hooks module exports. */ +export type ModRegister = (on: ModOn, options: PluginOptions) => unknown + +/** The module namespace a hooks module evaluates to. */ +export interface HooksModule { + register: ModRegister +} + +// ---- Event inputs and results ---- + +/** Where a prompt or command came from. */ +export type PromptOrigin = + | { kind: 'composer' } + | { kind: 'plugin'; name: string } + +/** `session.start`: before the first prompt of a root agent, and again for every loaded mod. */ +export interface SessionStartInput { + cwd: string + /** The drawing surface; this bridge draws nothing, so always `null`. */ + surface: null + /** Whether a human answerer is composed, so `$.ui.ask` can resolve. */ + isInteractive: boolean +} + +/** Result of `session.start`. */ +export interface SessionStartResult { + cwd: string +} + +/** `session.end`: the agent was disposed. */ +export interface SessionEndInput { + reason: 'clear' | 'logout' | 'prompt_input_exit' | 'resume' | 'other' + sessionId: string +} + +/** Result of `session.end`. */ +export interface SessionEndResult { + sessionId: string +} + +/** `prompt.submit`: a prompt is about to enter a turn. */ +export interface PromptSubmitInput { + text: string + /** Extra text only the model reads, appended after the prompt. */ + context?: readonly string[] + turnId?: string + wait: boolean + origin: PromptOrigin +} + +/** Result of `prompt.submit`: the text that entered, or a drop with its reason. */ +export type PromptSubmitResult = + | { text: string; context?: readonly string[]; drop?: undefined } + | { drop: string; text?: undefined; context?: undefined } + +/** `turn.start`: a turn begins with this prompt text. */ +export interface TurnStartInput { + text: string + turnId: string + agentId?: string +} + +/** Result of `turn.start`. */ +export interface TurnStartResult { + turnId: string +} + +/** Token totals of one turn, in Claude Code's API vocabulary. */ +export interface TurnUsage { + input_tokens: number + output_tokens: number + cache_read_input_tokens: number + cache_creation_input_tokens: number + model: string +} + +/** `turn.complete`: a turn ended. */ +export interface TurnCompleteInput { + turnId: string + answer: string + durationMs: number + isAborted: boolean + reason: 'answer' | 'aborted' | 'error' + agentId?: string + usage?: TurnUsage +} + +/** Result of `turn.complete`: `text` is shown as a line under the answer. */ +export interface TurnCompleteResult { + text: string +} + +/** `tool.call`: the tool's name and arguments as top-level fields. */ +export interface ToolCallInput { + tool: string + tool_use_id: string + agentId?: string + [argument: string]: unknown +} + +/** Result of `tool.call`: a refusal, or the tool's result. */ +export type ToolCallResult = + | { deny: string; result?: undefined; isError?: undefined } + | { result: unknown; isError?: true; deny?: undefined } + +/** `command.run`: a registered command was typed. */ +export interface CommandRunInput { + command: string + /** Text typed after the command name, or an empty string. */ + args: string + origin: PromptOrigin +} + +/** Result of `command.run`: `text` prints in the transcript; `{}` prints nothing. */ +export interface CommandRunResult { + text?: string +} + +/** Result of a mods API call as an event: the value, or a refusal. */ +export type OpResult = { value: unknown; deny?: undefined } | { deny: string; value?: undefined } + +// ---- The mods API (`$`) ---- + +/** A command a mod registers for the user. */ +export interface CommandSpec { + name: string + description: string + argumentHint?: string + immediate?: true +} + +/** A command in the effective list. */ +export interface CommandInfo { + name: string + description: string + source: 'builtin' | 'plugin' | 'user' | 'mcp' + plugin?: string +} + +/** A tool a mod registers for the model. */ +export interface ToolSpec { + name: string + description: string + inputSchema?: Record +} + +/** A tool in the effective list. */ +export interface ToolInfo { + name: string + description: string +} + +/** One message of the transcript as `$.session.messages()` returns it. */ +export interface SessionMessage { + role: 'user' | 'assistant' + text: string + toolUses: ToolUseSummary[] +} + +/** One tool call inside an assistant message. */ +export interface ToolUseSummary { + tool_use_id: string + tool: string + input: Record +} + +/** Context-window occupancy and plan limits. */ +export interface SessionUsage { + startedAt: number + context: { tokens?: number; window: number; percent?: number } + rateLimits: never[] +} + +/** The mods API version the bridge reports. */ +export interface SessionVersion { + version: string + engine: 'deepseek-harness' +} + +/** `$.ui.ask` options. */ +export interface AskOptions { + options?: readonly string[] + header?: string + multiSelect?: true +} + +/** Where `$.ui.log` writes. */ +export interface UiLogOptions { + to?: 'transcript' | 'debug' +} + +/** `$.ui.toast` options. */ +export interface ToastOptions { + timeoutMs?: number +} + +/** `$.ui.open` arguments. */ +export interface PaneOpenArgs { + id: string + title?: string + focus?: true + closeOnEscape?: true + holdToasts?: true + rows?: number + columns?: number +} + +/** `$.ui.open` result: this bridge places no panes. */ +export interface PaneOpenResult { + isPlaced: boolean + reason?: string +} + +/** A `$.state` value reference. */ +export interface StateRef { + plugin: string + key: string +} + +/** A timer returned by `$.clock.after` and `$.clock.every`. */ +export interface ModTimer { + cancel(): void +} + +/** A file entry from `$.fs.list`. */ +export interface FsEntry { + name: string + kind: 'file' | 'dir' | 'other' + size: number + isLink: boolean +} + +/** `$.fs.stat` result. */ +export interface FsStat { + kind: 'file' | 'dir' | 'other' + size: number + mtimeMs: number + isLink: boolean +} + +/** `$.process.run` options. */ +export interface ProcessRunInit { + cwd?: string + env?: Record + timeoutMs?: number +} + +/** `$.process.run` result, whatever the exit code. */ +export interface ProcessRunResult { + exitCode: number + stdout: string + stderr: string +} + +/** `$.http.fetch` options. */ +export interface HttpInit { + method?: string + headers?: Record + body?: string + timeoutMs?: number +} + +/** `$.http.fetch` result once the body is read. */ +export interface HttpResponse { + status: number + ok: boolean + headers: Record + text: string +} + +/** `$.prompt.submit` arguments. */ +export interface PromptSubmitArgs { + text: string + /** Send the text as the user's own words, without the sentence naming the mod. */ + asUser?: boolean +} + +/** + * The mods API a hook receives as `$`. Every method except `$.plugin` and + * `$.ui.resolve` is also an event named `.` that mods + * earlier in the chain may observe, rewrite, or refuse. + */ +export interface ModsApi { + readonly plugin: { readonly name: string; readonly root: string } + readonly ui: { + log(text: string, options?: UiLogOptions): void + toast(text: string, options?: ToastOptions): void + status(text: string | undefined): void + invalidate(event: string): void + open(pane: PaneOpenArgs): Promise + close(pane: { id: string }): Promise + panes(): Promise + ask(question: string, options?: readonly string[] | AskOptions): Promise + resolve(e: unknown): never + } + readonly command: { + register(command: CommandSpec): Promise + run(input: { command: string; args?: string }): Promise + list(): Promise + } + readonly tool: { + register(tool: ToolSpec): Promise + call(input: { tool: string; [argument: string]: unknown }): Promise + list(): Promise + } + readonly prompt: { + submit(input: PromptSubmitArgs): Promise<{ text: string }> + } + readonly session: { + id(): Promise + cwd(): Promise + root(): Promise + model(): Promise + turns(): Promise + messages(): Promise + usage(): Promise + version(): Promise + } + readonly state: { + get(ref: StateRef): Promise<{ value: unknown }> + set(ref: StateRef, value: unknown): Promise + } + readonly store: { + get(key: string): Promise + set(key: string, value: unknown): Promise + delete(key: string): Promise + keys(): Promise + } + readonly clock: { + now(): Promise + sleep(ms: number): Promise + after(ms: number, fn: () => unknown): ModTimer + every(ms: number, fn: () => unknown): ModTimer + } + readonly fs: { + read(path: string): Promise + write(path: string, text: string): Promise + list(path?: string): Promise + exists(path: string): Promise + stat(path: string): Promise + } + readonly process: { + run(argv: readonly string[], init?: ProcessRunInit): Promise + } + readonly http: { + fetch(url: string, init?: HttpInit): Promise + } + readonly env: { + get(name: string): Promise + set(name: string, value: string | undefined): Promise + } +} diff --git a/packages/experimental/claude-code-mods/src/values.ts b/packages/experimental/claude-code-mods/src/values.ts new file mode 100644 index 0000000000..efbb502516 --- /dev/null +++ b/packages/experimental/claude-code-mods/src/values.ts @@ -0,0 +1,46 @@ +/** + * Small value helpers shared by the engine and the host: reading a mod's + * untyped call input, wording a thrown value, and JSON encoding as typed by + * what it does. + * @module + */ + +/** + * The message of a thrown value: an Error's `message`, anything else as text. + * @param error - the thrown value. + * @returns the message text. + */ +export function messageOf(error: unknown): string { + return error instanceof Error ? error.message : String(error) +} + +/** + * A mod's call input as a field record; anything that is not an object reads as empty. + * @param input - the value a mod passed. + * @returns the fields, or an empty record. + */ +export function record(input: unknown): Record { + return typeof input === 'object' && input !== null ? input as Record : {} +} + +/** + * Require one string field of a mod's call input. + * @param value - the field value. + * @param what - the field, named in the error. + * @returns the string. + * @throws TypeError when the value is not a string. + */ +export function requireString(value: unknown, what: string): string { + if (typeof value !== 'string') throw new TypeError(`${what} must be a string`) + return value +} + +/** + * `JSON.stringify` as typed by what it does: undefined for a value JSON cannot carry. + * @param value - the value to encode. + * @returns the JSON text, or undefined. + */ +export function stringify(value: unknown): string | undefined { + const encoded: string | undefined = JSON.stringify(value) + return encoded +} diff --git a/packages/experimental/claude-code-mods/tests/bridge.spec.ts b/packages/experimental/claude-code-mods/tests/bridge.spec.ts new file mode 100644 index 0000000000..0ff9e1a08e --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/bridge.spec.ts @@ -0,0 +1,782 @@ +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { createServer } from 'node:http' +import type { AddressInfo } from 'node:net' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { Context, type Fiber } from '@deepseek-ai/cordis' +import Loader from '@deepseek-ai/cordis-plugin-loader' +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 CommandRuntime from '@deepseek-ai/dsh-commands' +import LocalFileSystem from '@deepseek-ai/dsh-fs-local' +import { createAssistantMessage, createUserMessage, ToolCallId as ToolCallIdOf } from '@deepseek-ai/dsh-llm' +import type { LlmResolvedModelInfo } from '@deepseek-ai/dsh-llm' +import TokenMeter from '@deepseek-ai/dsh-token-meter' +import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment/types' +import { scopeOf } from '@deepseek-ai/dsh-scope' +import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session' +import Storage from '@deepseek-ai/dsh-storage' +import * as StorageDomain from '@deepseek-ai/dsh-storage-domain' +import * as StorageJson from '@deepseek-ai/dsh-storage-json' +import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local' +import { defineContentToolFixture } from '@deepseek-ai/dsh-tools' +import UserQuestionService from '@deepseek-ai/dsh-user-questions' +import * as ClaudeCodeMods from '../src/index.ts' +import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts' + +/** + * Full-loop bridge tests: a scripted mock MODEL drives the REAL agent loop, + * tool registry, command registry, filesystem, subprocess, user-questions and + * storage services, and the REAL bridge loads REAL mod directories — only the + * model is mocked. Each test asserts the mod's effect on the loop or the + * services, not on the bridge's internals. + */ + +const FIXTURES = resolve(import.meta.dirname, 'fixtures') +const dirs: string[] = [] +const fibers: Fiber[] = [] +afterEach(async () => { + for (const fiber of fibers.splice(0)) await fiber.dispose() + for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }) +}) + +function scratch(prefix = 'dsh-cc-mods-'): string { + const dir = mkdtempSync(join(tmpdir(), prefix)) + dirs.push(dir) + return dir +} + +/** Write an inline mod directory: a manifest, hooks.json, and the given register.js body. */ +function writeMod(name: string, body: string): string { + const root = join(scratch(), name) + mkdirSync(join(root, '.claude-plugin'), { recursive: true }) + mkdirSync(join(root, 'hooks')) + writeFileSync(join(root, '.claude-plugin', 'plugin.json'), JSON.stringify({ name, version: '0.0.1' })) + writeFileSync(join(root, 'hooks', 'hooks.json'), JSON.stringify({ modules: ['./register.js'] })) + writeFileSync(join(root, 'hooks', 'register.js'), body) + return root +} + +interface HarnessOptions { + readonly config?: Partial + readonly services?: (ctx: Context, workspace: string) => Promise + readonly tools?: { mode?: 'native' | 'ptc' | 'both' } +} + +interface Harness { + readonly ctx: Context + readonly adapter: MockAdapter + readonly workspace: string + readonly mods: Fiber + readonly info: ReturnType + readonly warn: ReturnType + agent(id?: string): Promise + turn(agent: Agent, text: string): Promise +} + +async function harness(pluginDirs: string[], adapter: MockAdapter, options: HarnessOptions = {}): Promise { + const ctx = new Context() + fibers.push(ctx.fiber) + const info = vi.fn() + const warn = vi.fn() + ctx.logger.info = info as never + ctx.logger.warn = warn as never + await mountAgentLoopTestDependencies(ctx, { tools: options.tools ?? {} }) + await ctx.plugin(AgentLoop, { agents: [] }) + await ctx.plugin(CommandRuntime) + const workspace = scratch('dsh-cc-mods-ws-') + await options.services?.(ctx, workspace) + const mods = await ctx.plugin(ClaudeCodeMods, { pluginDirs, ...options.config }) + await mods.await() + ctx.llm.registerAdapter(['mock'], adapter) + return { + ctx, adapter, workspace, mods, info, warn, + agent: id => ctx.agentLoop.create(SessionId(id ?? 'a1'), { provider: 'mock', model: 'mock' }, { cwd: workspace }), + async turn(agent, text) { + agent.followup(createUserMessage({ content: [{ type: 'text', text }], source: { kind: 'user' } })) + await agent.whenIdle() + }, + } +} + +function events(agent: Agent): readonly SessionEvent[] { + return agent.session.snapshotEvents() +} + +function toolResult(agent: Agent): { isError: boolean; text: string } | undefined { + const event = events(agent).find(e => e.type === 'tool/result') + if (event?.type !== 'tool/result') return undefined + const { message } = event.data + return { isError: message.isError === true, text: message.content.map(block => block.type === 'text' ? block.text : '').join('') } +} + +function echoTool(name: string, ran: string[] = []) { + return defineContentToolFixture({ + name, description: 'echoes its command', parameters: { command: { type: 'string' } }, + async execute(args) { + ran.push(String(args.command)) + return [{ type: 'text', text: `ran ${String(args.command)}` }] + }, + }) +} + +async function waitFor(predicate: () => boolean, timeout = 5000): Promise { + const deadline = Date.now() + timeout + while (!predicate()) { + if (Date.now() > deadline) throw new Error('waitFor: condition not met before deadline') + await new Promise(resolve => setTimeout(resolve, 10)) + } +} + +describe('first-mod: the tutorial mod on the real loop', () => { + it('registers /tally at session start, counts the turn\'s tool calls, prints the count without a model turn, and unregisters on dispose', async () => { + const adapter = new MockAdapter([toolCallResponse('c1', 'echo', { command: 'ls' }), textResponse('done')]) + const h = await harness([join(FIXTURES, 'first-mod')], adapter) + h.ctx.tools.register(echoTool('echo')) + const agent = await h.agent() + expect(h.ctx.commands.list(agent).map(command => command.name)).toEqual(['tally']) + expect(h.info).toHaveBeenCalledWith( + 'claude-code-mods: hooks module first-mod@inline loaded (tier user); ' + + 'events: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}', + ) + + await h.turn(agent, 'list the files here') + expect(adapter.requests).toHaveLength(2) + const run = await h.ctx.commands.execute(agent, '/tally', [], new AbortController().signal) + expect(run?.result).toEqual({ kind: 'success', text: 'first-mod: Claude has made 1 tool calls since this mod loaded' }) + expect(events(agent).filter(e => e.type === 'command/run' || e.type === 'command/done').map(e => e.type)).toEqual(['command/run', 'command/done']) + + // The command is scoped to the agent that started the session, not global. + const other = await h.agent('a2') + expect(h.ctx.commands.list(other).map(command => command.name)).toEqual(['tally']) + + await h.mods.dispose() + expect(h.ctx.commands.list(agent)).toEqual([]) + expect(h.ctx.commands.list(other)).toEqual([]) + }) +}) + +describe('guard-mod: tool.call deny, observe-after, and fail-closed .catch', () => { + it('refuses a risky Bash command before the tool body runs, with the reason as the model-visible error', async () => { + const ran: string[] = [] + const adapter = new MockAdapter([toolCallResponse('c1', 'bash', { command: 'git push --force' }), textResponse('ok')]) + const h = await harness([join(FIXTURES, 'guard-mod')], adapter) + h.ctx.tools.register(echoTool('bash', ran)) + expect(h.warn).toHaveBeenCalledWith(expect.stringMatching(/settings hooks in hooks.json are not run by this bridge/)) + const agent = await h.agent() + await h.turn(agent, 'force push') + expect(ran).toEqual([]) + expect(toolResult(agent)).toEqual({ isError: true, text: 'Error: guard-mod refused this command: git push --force' }) + }) + + it('lets a safe command through, observes the result, and reaches the host log through $.ui.log', async () => { + const ran: string[] = [] + const adapter = new MockAdapter([toolCallResponse('c1', 'bash', { command: 'ls' }), textResponse('ok')]) + const h = await harness([join(FIXTURES, 'guard-mod')], adapter) + h.ctx.tools.register(echoTool('bash', ran)) + const agent = await h.agent() + await h.turn(agent, 'list') + expect(ran).toEqual(['ls']) + expect(toolResult(agent)).toEqual({ isError: false, text: 'ran ls' }) + await waitFor(() => h.info.mock.calls.some(call => call[0] === 'guard-mod: ran Bash: ls')) + }) + + it('fails closed through .catch when the guard throws', async () => { + const ran: string[] = [] + const adapter = new MockAdapter([toolCallResponse('c1', 'bash', { command: 'explode' }), textResponse('ok')]) + const h = await harness([join(FIXTURES, 'guard-mod')], adapter) + h.ctx.tools.register(echoTool('bash', ran)) + const agent = await h.agent() + await h.turn(agent, 'boom') + expect(ran).toEqual([]) + expect(toolResult(agent)).toEqual({ isError: true, text: 'Error: The command guard failed, so this command was not run: throw' }) + expect(h.warn).toHaveBeenCalledWith('claude-code-mods: guard-mod: tool.call hook skipped: threw Error: guard exploded') + }) +}) + +describe('ticket-mod: a mod-registered tool, prompt context, and a turn.complete line', () => { + it('offers mcp__ticket-mod__ticket to the model and answers its calls with a successful result', async () => { + const adapter = new MockAdapter([toolCallResponse('c1', 'mcp__ticket-mod__ticket', { id: 'T-1' }), textResponse('ok')]) + const h = await harness([join(FIXTURES, 'ticket-mod')], adapter) + const agent = await h.agent() + const schema = h.ctx.tools.schemas(scopeOf(agent.ctx)).find(tool => tool.name === 'mcp__ticket-mod__ticket') + expect(schema).toMatchObject({ description: 'Look up a ticket by its id and return its title and status', parameters: { type: 'object', required: ['id'] } }) + await h.turn(agent, 'what is T-1 about?') + expect(toolResult(agent)).toEqual({ isError: false, text: 'Login button does nothing (open)' }) + expect(adapter.requests[0]?.tools?.map(tool => tool.name)).toContain('mcp__ticket-mod__ticket') + await waitFor(() => h.info.mock.calls.some(call => call[0] === 'claude-code-mods: Done in some ms')) + }) + + it('appends prompt.submit context as a mod-sourced message the model reads after the prompt', async () => { + const adapter = new MockAdapter([textResponse('ok')]) + const h = await harness([join(FIXTURES, 'ticket-mod')], adapter) + const agent = await h.agent() + await h.turn(agent, 'open a PR for this change') + expect(JSON.stringify(adapter.requests[0]?.messages)).toContain('Current branch: feature/mods') + const context = events(agent).find(e => e.type === 'user/message' && e.data.source.kind !== 'user') + expect(context?.type === 'user/message' && context.data.source).toEqual({ kind: 'claude-code-mods' }) + expect(toolResult(agent)).toBeUndefined() + }) +}) + +describe('prompt.submit: rewrite and drop', () => { + it('rewrites the prompt text the model sees and drops a prompt with a reason', async () => { + const mod = writeMod('prompt-mod', ` + export function register(on) { + on('prompt.submit', async ($, e, next) => { + if (e.text.includes('secret')) return { drop: 'secrets stay out' } + return next({ ...e, text: e.text.trim().toUpperCase() }) + }) + } + `) + const adapter = new MockAdapter([textResponse('ok')]) + const h = await harness([mod], adapter) + const agent = await h.agent() + await h.turn(agent, ' hello there ') + const entered = events(agent).find(e => e.type === 'user/message') + expect(entered?.type === 'user/message' && entered.data.content).toEqual([{ type: 'text', text: 'HELLO THERE' }]) + expect(JSON.stringify(adapter.requests[0]?.messages)).toContain('HELLO THERE') + + await h.turn(agent, 'tell me the secret') + expect(adapter.requests).toHaveLength(1) + const turns = events(agent).filter(e => e.type === 'turn/end').map(e => e.type === 'turn/end' && e.data.reason.kind) + expect(turns).toEqual(['completed', 'blocked']) + expect(h.info).toHaveBeenCalledWith('claude-code-mods: prompt dropped: secrets stay out') + }) +}) + +describe('tool.call: answers and rewrites', () => { + it('answers a built-in tool in its place as an error-shaped result, keeps a rewritten result through post-execute, and warns once about rewritten arguments', async () => { + const mod = writeMod('answer-mod', ` + export function register(on) { + on('tool.call', { tool: 'echo' }, async ($, e, next) => { + if (e.command === 'skip') return { result: 'Skipped by answer-mod' } + if (e.command === 'redact') { + const r = await next({ ...e, command: 'rewritten' }) + return { ...r, result: String(r.result).replace('ran', 'RAN') } + } + return next(e) + }) + } + `) + const ran: string[] = [] + const adapter = new MockAdapter([ + toolCallResponse('c1', 'echo', { command: 'skip' }), textResponse('one'), + toolCallResponse('c2', 'echo', { command: 'redact' }), textResponse('two'), + toolCallResponse('c3', 'echo', { command: 'redact' }), textResponse('three'), + ]) + const h = await harness([mod], adapter) + h.ctx.tools.register(echoTool('echo', ran)) + const agent = await h.agent() + await h.turn(agent, 'skip it') + expect(ran).toEqual([]) + expect(toolResult(agent)).toEqual({ isError: true, text: 'Skipped by answer-mod' }) + const first = events(agent).find(e => e.type === 'tool/result') + expect(first?.type === 'tool/result' && JSON.stringify(first.data)).toContain('MOD_ANSWERED') + + await h.turn(agent, 'redact it') + await h.turn(agent, 'redact again') + expect(ran).toEqual(['redact', 'redact']) + const results = events(agent).filter(e => e.type === 'tool/result').map(e => e.type === 'tool/result' && e.data.message.content[0]) + expect(results.slice(1)).toEqual([{ type: 'text', text: 'RAN redact' }, { type: 'text', text: 'RAN redact' }]) + expect(h.warn.mock.calls.filter(call => String(call[0]).includes('rewrote the arguments of echo'))).toHaveLength(1) + }) + + it('leaves tools alone when no mod hooks tool.call', async () => { + const mod = writeMod('quiet-mod', 'export function register(on) { on("turn.start", ($, e, next) => next(e)) }') + const ran: string[] = [] + const adapter = new MockAdapter([toolCallResponse('c1', 'echo', { command: 'ls' }), textResponse('ok')]) + const h = await harness([mod], adapter) + h.ctx.tools.register(echoTool('echo', ran)) + const agent = await h.agent() + await h.turn(agent, 'go') + expect(ran).toEqual(['ls']) + expect(toolResult(agent)).toEqual({ isError: false, text: 'ran ls' }) + }) +}) + +describe('the mods API over harness services', () => { + it('serves files, processes, session facts, the store, env, http, commands, and tools', async () => { + const mod = writeMod('api-mod', ` + export function register(on) { + on('command.run', { command: 'probe' }, async ($, e) => { + const out = {} + await $.fs.write('notes.md', '# Notes') + out.read = await $.fs.read('notes.md') + out.exists = [await $.fs.exists('notes.md'), await $.fs.exists('missing.md')] + out.list = (await $.fs.list()).map(entry => entry.name + ':' + entry.kind) + out.stat = (await $.fs.stat('notes.md')).kind + await $.fs.stat('missing.md').catch(error => { out.statError = error.message }) + out.run = await $.process.run(['node', '-e', 'process.stdout.write("out"); process.stderr.write("err"); process.exit(3)']) + await $.process.run(['node', '-e', 'setTimeout(() => {}, 5000)'], { timeoutMs: 200 }).catch(error => { out.timeout = error.message }) + out.session = { + id: await $.session.id(), cwd: await $.session.cwd(), root: await $.session.root(), model: await $.session.model(), + turns: await $.session.turns(), version: await $.session.version(), usage: await $.session.usage(), + messages: await $.session.messages(), + } + await $.store.set('count', 7) + await $.store.set('name', 'x') + await $.store.delete('name') + await $.store.delete('never') + out.store = { count: await $.store.get('count'), keys: await $.store.keys() } + await $.env.set('CC_MODS_PROBE', 'yes') + out.env = await $.env.get('CC_MODS_PROBE') + await $.env.set('CC_MODS_PROBE', undefined) + out.envGone = await $.env.get('CC_MODS_PROBE') + const response = await $.http.fetch(e.args, { method: 'POST', headers: { 'x-mod': 'api-mod' }, body: 'ping', timeoutMs: 2000 }) + out.http = { status: response.status, ok: response.ok, text: response.text, echoed: response.headers['x-echo'] } + out.get = (await $.http.fetch(e.args)).headers['x-echo'] + out.commands = (await $.command.list()).map(command => command.name + '/' + command.source) + out.tools = (await $.tool.list()).map(tool => tool.name) + out.call = await $.tool.call({ tool: 'echo', command: 'from mod' }) + out.toasts = [$.ui.toast('hi'), $.ui.status('busy'), $.ui.status(undefined), $.ui.log('debug line', { to: 'debug' })] + out.open = await $.ui.open({ id: 'pane' }) + out.close = await $.ui.close({ id: 'pane' }) + out.panes = await $.ui.panes() + out.state = await $.state.get({ plugin: 'api-mod', key: 'missing' }) + return { text: JSON.stringify(out) } + }) + on('session.start', async ($, e, next) => { + await $.command.register({ name: 'probe', description: 'Probe the mods API', argumentHint: '' }) + await $.command.register({ name: 'Bad Name!', description: 'x' }).catch(() => {}) + return next(e) + }) + } + `) + const server = createServer((request, response) => { + let body = '' + request.on('data', (chunk: Buffer) => { body += chunk.toString() }) + request.on('end', () => { + response.setHeader('x-echo', `${request.method} ${request.headers['x-mod']} ${body}`) + response.end('pong') + }) + }) + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) + try { + const adapter = new MockAdapter([textResponse('ok')]) + const h = await harness([mod], adapter, { + services: async (ctx, workspace) => { + await ctx.plugin(LocalFileSystem, { cwd: workspace }) + await ctx.plugin(LocalSubprocessRuntime) + await ctx.plugin(Storage) + await ctx.plugin(StorageJson, { root: join(workspace, '.storage') }) + await ctx.plugin(StorageDomain, { backend: 'json' }) + }, + }) + h.ctx.tools.register(echoTool('echo')) + const agent = await h.agent() + await h.turn(agent, 'hello') + const url = `http://127.0.0.1:${(server.address() as AddressInfo).port}/` + const run = await h.ctx.commands.execute(agent, `/probe ${url}`, [], new AbortController().signal) + expect(run?.result.kind).toBe('success') + const out = JSON.parse(String(run?.result.text).replace(/^api-mod: /, '')) as Record + expect(out.read).toBe('# Notes') + expect(readFileSync(join(h.workspace, 'notes.md'), 'utf8')).toBe('# Notes') + expect(out.exists).toEqual([true, false]) + expect(out.list).toContain('notes.md:file') + expect(out.stat).toBe('file') + expect(out.statError).toMatch(/missing\.md does not exist/) + expect(out.run).toEqual({ exitCode: 3, stdout: 'out', stderr: 'err' }) + expect(out.timeout).toMatch(/did not exit within 200 ms/) + expect(out.session).toEqual({ + id: 'a1', cwd: h.workspace, root: h.workspace, model: 'mock', turns: 1, + version: ClaudeCodeMods.MODS_API_VERSION, + usage: { startedAt: agent.session.header.createdAt, context: { window: 0 }, rateLimits: [] }, + messages: [{ role: 'user', text: 'hello', toolUses: [] }, { role: 'assistant', text: 'ok', toolUses: [] }], + }) + expect(out.store).toEqual({ count: 7, keys: ['count'] }) + expect(out.env).toBe('yes') + expect(out.envGone).toBeUndefined() + expect(out.http).toEqual({ status: 200, ok: true, text: 'pong', echoed: 'POST api-mod ping' }) + expect(out.get).toBe('GET undefined ') + expect(out.commands).toEqual(['probe/plugin']) + expect(out.tools).toContain('echo') + expect(out.call).toEqual({ result: 'ran from mod' }) + expect(out.toasts).toEqual([null, null, null, null]) + expect(out.open).toMatchObject({ isPlaced: false }) + expect((out.open as { reason: string }).reason).toMatch(/places no panes/) + expect(out.close).toBeUndefined() + expect(out.panes).toEqual([]) + expect(out.state).toEqual({}) + expect(h.info).toHaveBeenCalledWith('api-mod (toast): hi') + expect(h.info).toHaveBeenCalledWith('api-mod (status): busy') + expect(h.info).toHaveBeenCalledWith('api-mod (status): (cleared)') + } finally { + await new Promise((resolve) => { server.close(() => { resolve() }) }) + } + }) +}) + +describe('$.ui.ask through the user-questions answerer', () => { + function askMod(): string { + return writeMod('ask-mod', ` + export function register(on) { + on('tool.call', { tool: 'echo' }, async ($, e, next) => { + let answer + if (e.command === 'plain') answer = await $.ui.ask('Run it?') + else if (e.command === 'multi') answer = await $.ui.ask('Pick', { options: ['a', 'b', 'c'], header: 'Choices', multiSelect: true }) + else answer = await $.ui.ask('Run ' + e.command + '?', ['Run it', 'Refuse']) + if (answer === 'Run it' || answer === 'typed yes' || answer === 'a, c') return next(e) + return { deny: 'The user declined: ' + answer } + }) + } + `) + } + + it('holds the call until the answerer settles and maps selections, typed answers, and dismissals', async () => { + const ran: string[] = [] + const answers: string[][] = [] + const adapter = new MockAdapter([ + toolCallResponse('c1', 'echo', { command: 'first' }), textResponse('one'), + toolCallResponse('c2', 'echo', { command: 'second' }), textResponse('two'), + toolCallResponse('c3', 'echo', { command: 'plain' }), textResponse('three'), + toolCallResponse('c4', 'echo', { command: 'multi' }), textResponse('four'), + toolCallResponse('c5', 'echo', { command: 'dismissed' }), textResponse('five'), + ]) + const seen: unknown[] = [] + const h = await harness([askMod()], adapter, { + services: async (ctx) => { + await ctx.plugin(UserQuestionService) + ctx.on('user-questions/request', (request) => { + seen.push(request.questions[0]) + const next = answers.shift() ?? [] + return Promise.resolve({ answers: next.length === 0 && request.questions[0]?.question === 'Run dismissed?' ? [] : [{ id: 'answer', selected: next, ...next.length === 0 ? { custom: 'typed yes' } : {} }] }) + }) + }, + }) + h.ctx.tools.register(echoTool('echo', ran)) + const agent = await h.agent() + answers.push(['Refuse'], ['Run it'], [], ['a', 'c'], []) + await h.turn(agent, 'first') + await h.turn(agent, 'second') + await h.turn(agent, 'plain') + await h.turn(agent, 'multi') + await h.turn(agent, 'dismissed') + expect(ran).toEqual(['second', 'plain', 'multi', 'dismissed']) + const results = events(agent).filter(e => e.type === 'tool/result').map(e => e.type === 'tool/result' && e.data.message.content[0]) + expect(results[0]).toEqual({ type: 'text', text: 'Error: The user declined: Refuse' }) + expect(seen[0]).toEqual({ id: 'answer', question: 'Run first?', options: [{ label: 'Run it' }, { label: 'Refuse' }] }) + expect(seen[2]).toEqual({ id: 'answer', question: 'Run it?' }) + expect(seen[3]).toEqual({ id: 'answer', question: 'Pick', header: 'Choices', options: [{ label: 'a' }, { label: 'b' }, { label: 'c' }], multiSelect: true }) + expect(h.warn).toHaveBeenCalledWith('claude-code-mods: ask-mod: tool.call hook skipped: threw Error: $.ui.ask: the user dismissed the question') + }) +}) + +describe('commands and prompts raised by a mod', () => { + it('runs commands, submits prompts framed or as the user, and reports a command the mod forgot to answer', async () => { + const mod = writeMod('driver-mod', ` + export function register(on) { + on('session.start', async ($, e, next) => { + await $.command.register({ name: 'nudge', description: 'Submit a prompt from the mod' }) + await $.command.register({ name: 'silent', description: 'Registered without a command.run hook' }) + await $.command.register({ name: 'relay', description: 'Run another command' }) + await $.command.register({ name: 'quiet', description: 'Prints nothing' }) + return next(e) + }) + on('command.run', { command: 'quiet' }, async ($) => { + $.ui.log((await $.command.list()).map(c => c.name + '/' + c.source).join(',')) + return {} + }) + on('command.run', { command: 'nudge' }, async ($, e) => { + const sent = await $.prompt.submit(e.args === 'user' ? { text: 'as the user', asUser: true } : { text: 'from the mod' }) + return { text: 'sent ' + sent.text.length } + }) + on('command.run', { command: 'relay' }, async ($, e) => { + if (e.args === 'missing') return { text: await $.command.run({ command: 'nope' }).catch(error => error.message) } + if (e.args === 'silent') return { text: await $.command.run({ command: 'silent' }).then(r => r.text) } + if (e.args === 'fail') return { text: await $.command.run({ command: 'fail' }).catch(error => 'failed: ' + error.message) } + if (e.args === 'mute') return { text: JSON.stringify(await $.command.run({ command: 'mute' })) } + return { text: JSON.stringify(await $.command.run({ command: 'nudge', args: 'user' })) } + }) + } + `) + const adapter = new MockAdapter([textResponse('one'), textResponse('two'), textResponse('three')]) + const h = await harness([mod], adapter) + h.ctx.commands.register({ name: 'fail', description: 'always fails', handler: () => ({ kind: 'error', text: 'nope' }) }) + h.ctx.commands.register({ name: 'mute', description: 'succeeds silently', handler: () => ({ kind: 'success' }) }) + const agent = await h.agent() + const signal = new AbortController().signal + const nudged = await h.ctx.commands.execute(agent, '/nudge', [], signal) + expect(nudged?.result).toEqual({ kind: 'success', text: 'driver-mod: sent 47' }) + await agent.whenIdle() + expect(JSON.stringify(adapter.requests[0]?.messages)).toContain('Message from the \\"driver-mod\\" mod:\\nfrom the mod') + const submitted = events(agent).find(e => e.type === 'user/message') + expect(submitted?.type === 'user/message' && submitted.data.source).toEqual({ kind: 'claude-code-mods' }) + + const relayed = await h.ctx.commands.execute(agent, '/relay', [], signal) + expect(relayed?.result).toEqual({ kind: 'success', text: 'driver-mod: {"text":"driver-mod: sent 11"}' }) + await agent.whenIdle() + const last = adapter.requests[1]?.messages.at(-1) + expect(last?.role === 'user' && last.content).toEqual([{ type: 'text', text: 'as the user' }]) + + const silent = await h.ctx.commands.execute(agent, '/relay silent', [], signal) + expect(silent?.result.text).toBe("driver-mod: driver-mod: driver-mod registered /silent but no command.run hook answered it; add on('command.run', { command: 'silent' }, hook)") + const missing = await h.ctx.commands.execute(agent, '/relay missing', [], signal) + expect(missing?.result.text).toBe('driver-mod: /nope is not a command') + expect((await h.ctx.commands.execute(agent, '/relay fail', [], signal))?.result.text).toBe('driver-mod: failed: nope') + expect((await h.ctx.commands.execute(agent, '/relay mute', [], signal))?.result.text).toBe('driver-mod: {}') + expect((await h.ctx.commands.execute(agent, '/quiet', [], signal))?.result).toEqual({ kind: 'success' }) + expect(h.info).toHaveBeenCalledWith('driver-mod: fail/builtin,mute/builtin,nudge/plugin,quiet/plugin,relay/plugin,silent/plugin') + }) +}) + +describe('mod tools: registration errors and unanswered calls', () => { + it('fails an invalid registration inside $.tool.register and reports a call no hook answered as a tool error', async () => { + const mod = writeMod('tools-mod', ` + export function register(on) { + on('session.start', async ($, e, next) => { + await $.tool.register({ name: 'orphan', description: 'No hook answers this tool' }) + const errors = [] + await $.tool.register({ name: 'bad name', description: 'x' }).catch(error => errors.push(error.message)) + await $.tool.register({ name: 'badschema', description: 'x', inputSchema: { type: 'string' } }).catch(error => errors.push(error.message)) + $.ui.log(errors.join(' | ')) + return next(e) + }) + on('tool.call', { tool: 'mcp__tools-mod__orphan' }, async ($, e, next) => { + if (e.mode === 'object') return { result: { structured: true } } + if (e.mode === 'empty') return { result: undefined } + return next(e) + }) + } + `) + const adapter = new MockAdapter([ + toolCallResponse('c1', 'mcp__tools-mod__orphan', { mode: 'pass' }), textResponse('one'), + toolCallResponse('c2', 'mcp__tools-mod__orphan', { mode: 'object' }), textResponse('two'), + toolCallResponse('c3', 'mcp__tools-mod__orphan', { mode: 'empty' }), textResponse('three'), + ]) + const h = await harness([mod], adapter) + const agent = await h.agent() + await waitFor(() => h.info.mock.calls.some(call => String(call[0]).startsWith('tools-mod: tool "bad name" refused'))) + expect(h.info).toHaveBeenCalledWith(expect.stringMatching(/tool "bad name" refused.*\| .*object/)) + await h.turn(agent, 'one') + await h.turn(agent, 'two') + await h.turn(agent, 'three') + const results = events(agent).filter(e => e.type === 'tool/result').map(e => e.type === 'tool/result' && [e.data.message.isError, e.data.message.content[0]]) + expect(results[0]).toEqual([true, { type: 'text', text: "Error: tools-mod registered mcp__tools-mod__orphan but no tool.call hook answered it; add on('tool.call', { tool: 'mcp__tools-mod__orphan' }, hook)" }]) + expect(results[1]).toEqual([false, { type: 'text', text: '{"structured":true}' }]) + expect(results[2]).toEqual([false, { type: 'text', text: '' }]) + }) +}) + +describe('subagents, programmatic calls, and malformed tool arguments', () => { + it('marks a child agent\'s events with agentId, skips session.start for it, and reads an unparsable tool input as empty', async () => { + const mod = writeMod('watch-mod', ` + export function register(on) { + on('session.start', async ($, e, next) => { $.ui.log('session.start ' + await $.session.id()); return next(e) }) + on('session.end', async ($, e, next) => { $.ui.log('session.end ' + e.sessionId); return next(e) }) + on('tool.call', async ($, e, next) => { + const { value = 0 } = await $.state.get({ plugin: 'watch-mod', key: 'calls' }) + await $.state.set({ plugin: 'watch-mod', key: 'calls' }, value + 1) + $.ui.log('tool.call ' + e.tool + ' agent=' + (e.agentId ?? 'root') + ' n=' + (value + 1)) + if (e.command === 'direct') { + const names = (await $.tool.list()).map(tool => tool.name) + const nested = await $.tool.call({ tool: 'echo', command: 'nested' }) + $.ui.log('direct saw ' + names.join(',') + ' and ' + JSON.stringify(nested)) + } + return next(e) + }) + on('turn.start', async ($, e, next) => { $.ui.log('turn.start agent=' + (e.agentId ?? 'root')); return next(e) }) + on('turn.complete', async ($, e, next) => { + const messages = await $.session.messages() + $.ui.log('turn.complete agent=' + (e.agentId ?? 'root') + ' inputs=' + JSON.stringify(messages.flatMap(m => m.toolUses.map(t => t.input)))) + return next(e) + }) + } + `) + const malformedCall = [ + { type: 'block-start' as const, index: 0, blockType: 'tool-call' as const }, + { type: 'tool-call-delta' as const, index: 0, id: ToolCallIdOf('m1'), name: 'echo', argumentsDelta: '{bad json' }, + { type: 'block-end' as const, index: 0, block: { type: 'tool-call' as const, id: ToolCallIdOf('m1'), name: 'echo', arguments: '{bad json' } }, + { type: 'finish' as const, reason: { kind: 'tool-calls' as const } }, + ] + const adapter = new MockAdapter([ + toolCallResponse('c1', 'echo', { command: 'child' }), textResponse('child done'), + malformedCall, textResponse('root done'), + ]) + const h = await harness([mod], adapter) + h.ctx.tools.register(echoTool('echo')) + const root = await h.agent() + const child = await h.ctx.agentLoop.createAgent(h.ctx, { + sessionId: SessionId('child-1'), parentAgent: root, agentOptions: { provider: 'mock', model: 'mock' }, meta: { cwd: h.workspace, origin: 'subagent' }, + }) + await h.turn(child.agent, 'child task') + await child.dispose() + await h.turn(root, 'root task') + const lines = () => h.info.mock.calls.map(call => String(call[0])).filter(line => line.startsWith('watch-mod: ')) + await waitFor(() => lines().length === 7) + expect(lines()).toEqual([ + 'watch-mod: session.start a1', + 'watch-mod: turn.start agent=child-1', + 'watch-mod: tool.call echo agent=child-1 n=1', + 'watch-mod: turn.complete agent=child-1 inputs=[{"command":"child"}]', + 'watch-mod: turn.start agent=root', + 'watch-mod: tool.call echo agent=root n=1', + 'watch-mod: turn.complete agent=root inputs=[{}]', + ]) + const direct = await h.ctx.tools.execute({ callId: ToolCallIdOf('direct'), name: 'echo', arguments: { command: 'direct' }, signal: new AbortController().signal }) + expect(direct.isError).toBe(false) + expect(h.info).toHaveBeenCalledWith('watch-mod: direct saw echo and {"result":"ran nested"}') + expect(h.info.mock.calls.filter(call => String(call[0]).includes('agent=root n='))).toHaveLength(3) + }) +}) + +describe('turn.complete reasons and sessions the bridge did not follow from the start', () => { + it('reports aborted and error turns, folds only the open turn, and ignores a session without a live agent', async () => { + const mod = writeMod('ends-mod', ` + export function register(on) { + on('turn.complete', async ($, e, next) => { + $.ui.log('turn.complete ' + e.turnId + ' ' + e.reason + ' aborted=' + e.isAborted + ' answer=' + JSON.stringify(e.answer) + ' usage=' + (e.usage ? e.usage.model : 'none')) + return next(e) + }) + } + `) + const adapter = new MockAdapter([textResponse('fine'), 'hang']) + const h = await harness([mod], adapter) + const agent = await h.agent() + await h.turn(agent, 'one') + agent.followup(createUserMessage({ content: [{ type: 'text', text: 'two' }], source: { kind: 'user' } })) + await waitFor(() => adapter.requests.length === 2) + agent.cancel({ kind: 'user' }) + await agent.whenIdle() + await h.turn(agent, 'three') + const lines = () => h.info.mock.calls.map(call => String(call[0])).filter(line => line.startsWith('ends-mod: ')) + await waitFor(() => lines().length === 3) + expect(lines()[0]).toBe('ends-mod: turn.complete 1 answer aborted=false answer="fine" usage=mock') + expect(lines()[1]).toMatch(/^ends-mod: turn.complete 2 aborted aborted=true answer="(partial)?" usage=/) + expect(lines()[2]).toMatch(/^ends-mod: turn.complete 3 error aborted=false/) + + // A fold for a turn the bridge never saw open, and an event for a session no agent owns. + agent.session.append('turn/end', { turn: 9, reason: { kind: 'completed' } }) + await waitFor(() => lines().length === 4) + expect(lines()[3]).toBe('ends-mod: turn.complete 9 answer aborted=false answer="" usage=none') + const orphan = h.ctx.sessions.create(SessionId('orphan')) + orphan.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + await new Promise(resolve => setTimeout(resolve, 20)) + expect(lines()).toHaveLength(4) + }) + + it('forgets a session\'s state and raises session.end when its agent is disposed', async () => { + const mod = writeMod('end-mod', ` + export function register(on) { + on('session.end', async ($, e, next) => { $.ui.log('ended ' + e.sessionId + ' ' + e.reason); return next(e) }) + } + `) + const h = await harness([mod], new MockAdapter([])) + const handle = await h.ctx.agentLoop.createAgent(h.ctx, { sessionId: SessionId('ends'), agentOptions: { provider: 'mock', model: 'mock' } }) + // An assistant message outside any turn the bridge follows is left alone. + handle.agent.session.append('assistant/message', { + turn: 0, step: 0, stream: [], + message: createAssistantMessage({ content: [{ type: 'text', text: 'stray' }], source: { provider: 'mock', model: 'mock' } }), + }, { surfaceOp: 'append' }) + await handle.dispose() + await waitFor(() => h.info.mock.calls.some(call => call[0] === 'end-mod: ended ends other')) + }) +}) + +describe('$.session.usage with the token meter', () => { + it('reports the provider-measured prompt size against the route\'s context window', async () => { + class WindowedAdapter extends MockAdapter { + override async resolveModel(provider: string, model: string): Promise { + return { ...await super.resolveModel(provider, model), context: { contextWindow: 1000 } } + } + } + const mod = writeMod('usage-mod', ` + export function register(on) { + on('turn.complete', async ($, e, next) => { + const usage = await $.session.usage() + $.ui.log('usage ' + JSON.stringify(usage.context)) + return next(e) + }) + } + `) + const adapter = new WindowedAdapter([textResponse('ok')]) + const h = await harness([mod], adapter, { services: async (ctx) => { await ctx.plugin(TokenMeter) } }) + const agent = await h.agent() + await h.turn(agent, 'measure me') + await waitFor(() => h.info.mock.calls.some(call => String(call[0]).startsWith('usage-mod: usage '))) + const line = h.info.mock.calls.map(call => String(call[0])).find(text => text.startsWith('usage-mod: usage ')) ?? '' + const context = JSON.parse(line.slice('usage-mod: usage '.length)) as { tokens: number; window: number; percent: number } + expect(context.window).toBe(1000) + expect(context.tokens).toBeGreaterThan(0) + expect(context.percent).toBe(Math.round((context.tokens / 1000) * 100)) + }) +}) + +describe('loading diagnostics and configuration', () => { + it('skips a mod whose module fails and one that exports no register, loads the rest, and fails loud on a missing directory', async () => { + const quiet = writeMod('quiet-mod', 'export function register() {}') + const adapter = new MockAdapter([]) + const h = await harness([join(FIXTURES, 'broken-mod'), join(FIXTURES, 'no-register'), quiet], adapter, { + config: { hookTimeoutMs: 50, catchTimeoutMs: 20, processTimeoutMs: 100 }, + }) + const warnings = h.warn.mock.calls.map(call => String(call[0])) + expect(warnings).toContainEqual( + expect.stringMatching(/broken-mod: hooks module did not load: register threw .*"tool.calls" is not an event/), + ) + expect(warnings).toContainEqual(expect.stringMatching(/no-register: hooks module did not load: .*does not export a register function/)) + expect(h.info).toHaveBeenCalledWith('claude-code-mods: hooks module quiet-mod@inline loaded (tier user); events: (none)') + + const ctx = new Context() + fibers.push(ctx.fiber) + await expect(ClaudeCodeMods.apply(ctx, { pluginDirs: [join(FIXTURES, 'missing')] })).rejects.toThrow(/plugin\.json: cannot read/) + await expect(ClaudeCodeMods.apply(ctx, { pluginDirs: [], hookTimeoutMs: 0 })).rejects.toThrow(/hookTimeoutMs must be a positive number/) + await expect(ClaudeCodeMods.apply(ctx, { pluginDirs: [], catchTimeoutMs: -1 })) + .rejects.toThrow(/catchTimeoutMs must be a positive number/) + await expect(ClaudeCodeMods.apply(ctx, { pluginDirs: [], processTimeoutMs: Number.NaN })) + .rejects.toThrow(/processTimeoutMs must be a positive number/) + await expect(ClaudeCodeMods.apply(ctx, { pluginDirs: [] })).resolves.toBeUndefined() + }) + + it('has the namespace-plugin export shape (no stray default) so the Loader keeps name/inject/apply', () => { + expect('default' in ClaudeCodeMods).toBe(false) + expect(ClaudeCodeMods.name).toBe('claude-code-mods') + expect(ClaudeCodeMods.inject).toEqual([]) + const loader = Object.create(Loader.prototype) as Loader + const unwrapped: unknown = loader.unwrapExports(ClaudeCodeMods) + expect(unwrapped).toBe(ClaudeCodeMods) + }) +}) + +describe('post-execute interplay and prompt rewrites with images', () => { + it('keeps a downstream block, carries downstream contexts onto a replaced result, and rewrites only text blocks', async () => { + const mod = writeMod('rewrite-mod', ` + export function register(on) { + on('tool.call', { tool: 'echo' }, async ($, e, next) => { + const r = await next(e) + return { ...r, result: 'REPLACED' } + }) + on('prompt.submit', ($, e, next) => next({ ...e, text: 'rewritten: ' + e.text })) + } + `) + const adapter = new MockAdapter([ + toolCallResponse('c1', 'echo', { command: 'a' }), textResponse('one'), + toolCallResponse('c2', 'echo', { command: 'b' }), textResponse('two'), + ]) + const h = await harness([mod], adapter) + h.ctx.tools.register(echoTool('echo')) + let block = true + h.ctx.on('tools/post-execute', async (_exec, _result, next) => { + if (block) return { kind: 'block', feedback: [{ type: 'text', text: 'blocked downstream' }] } + const downstream = await next() + return { ...downstream, additionalContexts: [createUserMessage({ content: [{ type: 'text', text: 'downstream context' }], source: { kind: 'user' } })] } + }) + const agent = await h.agent() + const image = { type: 'image' as const, attachment: { attachmentId: 'att-1', mediaType: 'image/png', bytes: 1, width: 1, height: 1 } as ImageAttachmentRef } + // Injected context is claimed with the waking prompt, so one pre-step sees both messages. + agent.inject(createUserMessage({ content: [image], source: { kind: 'user' } })) + agent.followup(createUserMessage({ content: [{ type: 'text', text: 'first' }, image, { type: 'text', text: 'second' }], source: { kind: 'user' } })) + await agent.whenIdle() + const entered = events(agent).filter(e => e.type === 'user/message').map(e => e.type === 'user/message' && e.data.content) + expect(entered).toContainEqual([image]) + expect(entered).toContainEqual([{ type: 'text', text: 'rewritten: firstsecond' }, image]) + expect(toolResult(agent)).toEqual({ isError: true, text: 'blocked downstream' }) + + block = false + await h.turn(agent, 'again') + const results = events(agent).filter(e => e.type === 'tool/result').map(e => e.type === 'tool/result' && e.data.message.content[0]) + expect(results[1]).toEqual({ type: 'text', text: 'REPLACED' }) + expect(JSON.stringify(adapter.requests[3]?.messages)).toContain('downstream context') + }) +}) diff --git a/packages/experimental/claude-code-mods/tests/chain.spec.ts b/packages/experimental/claude-code-mods/tests/chain.spec.ts new file mode 100644 index 0000000000..8b37603988 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/chain.spec.ts @@ -0,0 +1,339 @@ +import { describe, expect, it, vi } from 'vitest' +import { BudgetClock, dispatch, ENGINE_ORIGIN, HookTimeoutError } from '../src/chain.ts' +import type { DispatchRequest, LoadedMod, RegisteredHook } from '../src/chain.ts' +import type { AnyHook, HookMatcher, ModsApi } from '../src/types.ts' + +const mod = (name: string, order = 0): LoadedMod => ({ name, version: undefined, root: '/mods/' + name, modulePath: '', options: {}, order }) + +function hook(owner: LoadedMod, event: string, fn: AnyHook, matcher?: HookMatcher): RegisteredHook { + return { mod: owner, event, matcher, hook: fn, catchHandler: undefined, reported: new Set() } +} + +const fakeApi = {} as ModsApi + +function request(overrides: Partial> & Pick, 'input' | 'core' | 'hooks'>): DispatchRequest { + return { + event: 'tool.call', + origin: ENGINE_ORIGIN, + api: () => fakeApi, + budgetMs: 200, + catchBudgetMs: 100, + signal: new AbortController().signal, + report: () => {}, + ...overrides, + } +} + +describe('dispatch: the three moves', () => { + it('observes: a hook that returns next(e) sees the result from beneath and the core sees the original input', async () => { + const seen: string[] = [] + const a = mod('a') + const hooks = [ + hook(a, 'tool.call', async (_$, e, next) => { + seen.push('before') + const result = await next(e) + seen.push('after') + return result + }), + ] + const result = await dispatch(request({ input: { tool: 'Bash' }, core: e => ({ result: 'ran ' + e.tool }), hooks })) + expect(result).toEqual({ result: 'ran Bash' }) + expect(seen).toEqual(['before', 'after']) + }) + + it('rewrites: next receives the copy and later hooks and the core see it; the hook input is frozen', async () => { + const a = mod('a') + const b = mod('b', 1) + let frozen = false + const hooks = [ + hook(a, 'prompt.submit', (_$, e, next) => { + frozen = Object.isFrozen(e) && Object.isFrozen((e as { nested: object }).nested) + return next({ ...(e as object), text: 'trimmed' }) + }), + hook(b, 'prompt.submit', (_$, e, next) => next({ ...(e as object), seenBy: 'b' })), + ] + const result = await dispatch(request({ + event: 'prompt.submit', + input: { text: ' raw ', nested: { deep: true } }, + core: e => e, + hooks, + })) + expect(result).toMatchObject({ text: 'trimmed', seenBy: 'b' }) + expect(frozen).toBe(true) + }) + + it('answers: a hook that returns without calling next short-circuits later hooks and the core', async () => { + const a = mod('a') + const b = mod('b', 1) + const later = vi.fn(async (_$: ModsApi, e: unknown, next: (e: unknown) => Promise) => next(e)) + const core = vi.fn(() => ({ result: 'ran' })) + const result = await dispatch(request({ + input: { tool: 'Bash' }, + core, + hooks: [hook(a, 'tool.call', () => ({ deny: 'no' })), hook(b, 'tool.call', later)], + })) + expect(result).toEqual({ deny: 'no' }) + expect(later).not.toHaveBeenCalled() + expect(core).not.toHaveBeenCalled() + }) + + it('runs hooks outermost first and the core last, with matchers evaluated against the input each hook receives', async () => { + const order: string[] = [] + const a = mod('a') + const b = mod('b', 1) + const c = mod('c', 2) + const hooks = [ + hook(a, 'tool.call', async (_$, e, next) => { + order.push('a') + return next({ ...(e as object), tool: 'Edit' }) + }), + hook(b, 'tool.call', async (_$, e, next) => { + order.push('b') + return next(e) + }, { tool: 'Bash' }), + hook(c, 'tool.call', async (_$, e, next) => { + order.push('c') + return next(e) + }, { tool: ['Edit', 'Write'] }), + ] + await dispatch(request({ input: { tool: 'Bash' }, core: () => { order.push('core'); return {} }, hooks })) + expect(order).toEqual(['a', 'c', 'core']) + }) + + it('reaches the core directly when no hook is registered, and passes a primitive input through unfrozen', async () => { + await expect(dispatch(request({ input: 1, core: e => e + 1, hooks: [] }))).resolves.toBe(2) + const a = mod('a') + await expect(dispatch(request({ input: 'text', core: e => ({ got: e }), hooks: [hook(a, 'tool.call', (_$, e, next) => next(e))] }))) + .resolves.toEqual({ got: 'text' }) + }) + + it('exposes origin, budget, and signal on next; next.to rejects', async () => { + const a = mod('a') + const controller = new AbortController() + let observed: Record = {} + await dispatch(request({ + input: {}, + core: () => ({}), + signal: controller.signal, + hooks: [hook(a, 'tool.call', async (_$, e, next) => { + observed = { origin: next.origin, ms: next.budget.ms, remaining: next.budget.remainingMs, aborted: next.signal.aborted } + await expect(next.to(e, 'append')).rejects.toThrow(/next\.to is only available/) + return next(e) + })], + })) + expect(observed.origin).toEqual(ENGINE_ORIGIN) + expect(observed.ms).toBe(200) + expect(observed.remaining).toBeLessThanOrEqual(200) + expect(observed.aborted).toBe(false) + }) +}) + +describe('dispatch: failures', () => { + it('skips a hook that throws before next and reports it once per kind; the next hook runs in its place', async () => { + const a = mod('a') + const b = mod('b', 1) + const report = vi.fn() + const failing = hook(a, 'tool.call', () => { throw new Error('boom') }) + const hooks = [failing, hook(b, 'tool.call', async (_$, e, next) => ({ ...(await next(e)) as object, b: true }))] + const first = await dispatch(request({ input: {}, core: () => ({ result: 'ran' }), hooks, report })) + const second = await dispatch(request({ input: {}, core: () => ({ result: 'ran' }), hooks, report })) + expect(first).toEqual({ result: 'ran', b: true }) + expect(second).toEqual({ result: 'ran', b: true }) + expect(report).toHaveBeenCalledTimes(1) + expect(report).toHaveBeenCalledWith('a: tool.call hook skipped: threw Error: boom') + }) + + it('keeps the result from beneath when the hook fails after next resolved, and runs nothing a second time', async () => { + const a = mod('a') + const core = vi.fn(() => ({ result: 'ran' })) + const report = vi.fn() + const result = await dispatch(request({ + input: {}, + core, + report, + hooks: [hook(a, 'tool.call', async (_$, e, next) => { + await next(e) + throw 'late' + })], + })) + expect(result).toEqual({ result: 'ran' }) + expect(core).toHaveBeenCalledTimes(1) + expect(report).toHaveBeenCalledWith('a: tool.call hook skipped: threw late') + }) + + it('treats a hook that resolves to no result as skipped', async () => { + const a = mod('a') + const report = vi.fn() + const result = await dispatch(request({ input: {}, core: () => ({ ok: true }), report, hooks: [hook(a, 'tool.call', () => undefined)] })) + expect(result).toEqual({ ok: true }) + expect(report).toHaveBeenCalledWith('a: tool.call hook skipped: returned no result') + }) + + it('propagates an error thrown by the core without blaming the hook that awaited it', async () => { + const a = mod('a') + const report = vi.fn() + await expect(dispatch(request({ + input: {}, + core: () => { throw new Error('engine down') }, + report, + hooks: [hook(a, 'tool.call', (_$, e, next) => next(e))], + }))).rejects.toThrow('engine down') + expect(report).not.toHaveBeenCalled() + }) + + it('skips a hook whose own time runs past the budget; a late next from it is ignored', async () => { + const a = mod('a') + const report = vi.fn() + const core = vi.fn(() => ({ result: 'ran' })) + let lateNext: Promise | undefined + const result = await dispatch(request({ + input: {}, + core, + report, + budgetMs: 20, + hooks: [hook(a, 'tool.call', async (_$, e, next) => { + await new Promise(resolve => setTimeout(resolve, 60)) + lateNext = next(e) + return { deny: 'too late' } + })], + })) + expect(result).toEqual({ result: 'ran' }) + expect(report).toHaveBeenCalledWith('a: tool.call hook skipped: timeout, ran past its 20 ms limit') + await new Promise(resolve => setTimeout(resolve, 60)) + await expect(lateNext).resolves.toBeUndefined() + expect(core).toHaveBeenCalledTimes(1) + }) + + it('does not count time spent inside next or a mods API call against the budget', async () => { + const a = mod('a') + const report = vi.fn() + const result = await dispatch(request({ + input: {}, + core: () => new Promise((resolve) => { setTimeout(() => { resolve({ result: 'slow core' }) }, 60) }), + report, + budgetMs: 25, + api: (_hook, clock) => ({ + clock: { + async sleepOutside(ms: number) { + clock.pause() + await new Promise(resolve => setTimeout(resolve, ms)) + clock.resume() + }, + }, + }) as unknown as ModsApi, + hooks: [hook(a, 'tool.call', async ($, e, next) => { + await ($ as unknown as { clock: { sleepOutside(ms: number): Promise } }).clock.sleepOutside(60) + return next(e) + })], + })) + expect(result).toEqual({ result: 'slow core' }) + expect(report).not.toHaveBeenCalled() + }) +}) + +describe('dispatch: .catch handlers', () => { + it('lets the handler answer in a failed hook\'s place with next.error and next.called set', async () => { + const a = mod('a') + const failing = hook(a, 'tool.call', () => { throw new Error('boom') }) + let caught: Record = {} + failing.catchHandler = (_$, _e, next) => { + caught = { kind: next.error?.kind, message: next.error?.message, called: next.called } + return { deny: 'failed closed' } + } + const result = await dispatch(request({ input: {}, core: () => ({ result: 'ran' }), hooks: [failing] })) + expect(result).toEqual({ deny: 'failed closed' }) + expect(caught).toEqual({ kind: 'throw', message: 'Error: boom', called: false }) + }) + + it('gives the handler the already-resolved result from beneath when the hook had called next', async () => { + const a = mod('a') + const core = vi.fn(() => ({ result: 'ran' })) + const failing = hook(a, 'tool.call', async (_$, e, next) => { + await next(e) + throw new Error('after') + }) + failing.catchHandler = async (_$, e, next) => { + expect(next.called).toBe(true) + return { ...(await next(e)) as object, patched: true } + } + const result = await dispatch(request({ input: {}, core, hooks: [failing] })) + expect(result).toEqual({ result: 'ran', patched: true }) + expect(core).toHaveBeenCalledTimes(1) + }) + + it('lets the handler call next itself when the hook never did, and falls back to skipping when it returns nothing or fails', async () => { + const a = mod('a') + const report = vi.fn() + const viaNext = hook(a, 'tool.call', () => { throw new Error('boom') }) + viaNext.catchHandler = (_$, e, next) => next(e) + expect(await dispatch(request({ input: {}, core: () => ({ ok: 1 }), hooks: [viaNext], report }))).toEqual({ ok: 1 }) + + const silent = hook(a, 'tool.call', () => { throw new Error('boom') }) + silent.catchHandler = () => undefined + expect(await dispatch(request({ input: {}, core: () => ({ ok: 2 }), hooks: [silent], report }))).toEqual({ ok: 2 }) + + const throwing = hook(a, 'tool.call', () => { throw new Error('boom') }) + throwing.catchHandler = () => { throw new Error('handler boom') } + expect(await dispatch(request({ input: {}, core: () => ({ ok: 3 }), hooks: [throwing], report }))).toEqual({ ok: 3 }) + expect(report).toHaveBeenCalledWith('a: tool.call .catch handler skipped: threw Error: handler boom') + + const inspecting = hook(a, 'tool.call', () => { throw new Error('boom') }) + inspecting.catchHandler = async (_$, e, next) => { + expect(next.budget.ms).toBe(100) + expect(next.budget.remainingMs).toBeLessThanOrEqual(100) + await expect(next.to(e, 'core')).rejects.toThrow(/not available in a \.catch handler/) + return { deny: 'inspected' } + } + expect(await dispatch(request({ input: {}, core: () => ({ ok: 5 }), hooks: [inspecting], report }))).toEqual({ deny: 'inspected' }) + + const slow = hook(a, 'tool.call', () => { throw new Error('boom') }) + slow.catchHandler = async (_$, e) => { + await new Promise(resolve => setTimeout(resolve, 60)) + return { deny: 'late ' + String(e) } + } + expect(await dispatch(request({ input: {}, core: () => ({ ok: 4 }), hooks: [slow], report, catchBudgetMs: 15 }))).toEqual({ ok: 4 }) + expect(report).toHaveBeenCalledWith('a: tool.call .catch handler skipped: timeout, ran past its 15 ms limit') + }) +}) + +describe('BudgetClock', () => { + it('counts only busy time, pauses while nested, and stops without firing once settled', async () => { + let now = 0 + const clock = new BudgetClock(100, () => now) + expect(clock.remainingMs).toBe(100) + clock.start() + now = 30 + expect(clock.remainingMs).toBe(70) + clock.pause() + clock.pause() + now = 500 + expect(clock.remainingMs).toBe(70) + clock.resume() + expect(clock.remainingMs).toBe(70) + clock.resume() + now = 520 + expect(clock.remainingMs).toBe(50) + clock.stop() + clock.start() + clock.pause() + clock.resume() + expect(clock.remainingMs).toBe(50) + const pending = new Promise((resolve) => { setTimeout(() => { resolve('pending') }, 20) }) + const raced = await Promise.race([clock.expired.catch((error: unknown) => error), pending]) + expect(raced).toBe('pending') + }) + + it('rejects the deadline with HookTimeoutError once busy time reaches the limit', async () => { + const clock = new BudgetClock(10) + clock.start() + await expect(clock.expired).rejects.toBeInstanceOf(HookTimeoutError) + expect(clock.remainingMs).toBe(0) + }) + + it('resume without a matching pause is a no-op', () => { + const clock = new BudgetClock(10) + clock.resume() + expect(clock.remainingMs).toBe(10) + clock.stop() + }) +}) diff --git a/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/.claude-plugin/plugin.json b/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/.claude-plugin/plugin.json new file mode 100644 index 0000000000..71546798ba --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/.claude-plugin/plugin.json @@ -0,0 +1 @@ +{ "name": "broken-mod", "version": "0.0.1" } diff --git a/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/hooks/hooks.json b/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/hooks/hooks.json new file mode 100644 index 0000000000..687749f783 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/hooks/hooks.json @@ -0,0 +1 @@ +{ "modules": ["./register.js"] } diff --git a/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/hooks/register.js b/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/hooks/register.js new file mode 100644 index 0000000000..4499f30ff9 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/broken-mod/hooks/register.js @@ -0,0 +1,3 @@ +export function register(on) { + on('tool.calls', async ($, e, next) => next(e)) +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/first-mod/.claude-plugin/plugin.json b/packages/experimental/claude-code-mods/tests/fixtures/first-mod/.claude-plugin/plugin.json new file mode 100644 index 0000000000..f6688245af --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/first-mod/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "first-mod", + "version": "0.1.0", + "description": "Counts the model's tool calls, shows the count beside the spinner, and adds a /tally command", + "author": { "name": "Claude Code docs" }, + "userConfig": { + "greeting": { "type": "string", "default": "Claude has made" } + } +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/first-mod/hooks/hooks.json b/packages/experimental/claude-code-mods/tests/fixtures/first-mod/hooks/hooks.json new file mode 100644 index 0000000000..869c9ef60d --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/first-mod/hooks/hooks.json @@ -0,0 +1,4 @@ +{ + "description": "The first-mod hooks module", + "modules": ["./register.js"] +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/first-mod/hooks/register.js b/packages/experimental/claude-code-mods/tests/fixtures/first-mod/hooks/register.js new file mode 100644 index 0000000000..8a6479443c --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/first-mod/hooks/register.js @@ -0,0 +1,41 @@ +// The tutorial mod from Claude Code's "Create a mod" page, unchanged apart +// from the configurable greeting: it counts tool calls, adds /tally, and +// restyles the spinner (a render site this bridge never raises). + +// The count, shared by the hooks below +let calls = 0 + +// Claude Code calls this once when the mod loads +export function register(on, options) { + // Runs when the session starts, before your first prompt + on('session.start', async ($, e, next) => { + // Add the /tally command + await $.command.register({ + name: 'tally', + description: 'Show how many tool calls Claude has made', + }) + // Let the session start as usual + return next(e) + }) + + // Runs each time Claude is about to use a tool + on('tool.call', async ($, e, next) => { + calls += 1 + // Ask Claude Code to draw the interface again, so the new count shows + $.ui.invalidate('ui.render') + // Let the tool run as usual + return next(e) + }) + + // Runs when you type /tally, and only then, because of the matcher + on('command.run', { command: 'tally' }, async () => { + // The text to print in the transcript + return { text: options.greeting + ' ' + calls + ' tool calls since this mod loaded' } + }) + + // Runs each time Claude Code draws the spinner + on('ui.render', { component: 'Spinner' }, async ($, e, next) => { + // Keep Claude Code's spinner, with the count added after its word + return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } }) + }) +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/.claude-plugin/plugin.json b/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/.claude-plugin/plugin.json new file mode 100644 index 0000000000..58bd47e0cd --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "guard-mod", + "version": "0.1.0", + "description": "Refuses risky Bash commands and fails closed when the guard itself breaks" +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/hooks/hooks.json b/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/hooks/hooks.json new file mode 100644 index 0000000000..d18b72a632 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/hooks/hooks.json @@ -0,0 +1,6 @@ +{ + "modules": ["./register.js"], + "hooks": { + "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "echo settings hooks are not this bridge's job" }] }] + } +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/hooks/register.js b/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/hooks/register.js new file mode 100644 index 0000000000..e703df43ec --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/guard-mod/hooks/register.js @@ -0,0 +1,20 @@ +// A tool.call guard in the style of Claude Code's events page: refuse a +// force push, log what ran, and fail closed through `.catch`. + +const RISKY = /\bgit\s+push\b.*--force|\brm\s+-rf?\b/ + +async function guard($, e, next) { + if (e.command === 'explode') throw new Error('guard exploded') + if (RISKY.test(String(e.command ?? ''))) { + return { deny: 'guard-mod refused this command: ' + e.command } + } + const result = await next(e) + $.ui.log('ran ' + e.tool + ': ' + String(e.command ?? '')) + return result +} + +export function register(on) { + on('tool.call', { tool: 'Bash' }, guard).catch(async ($, e, next) => { + return { deny: 'The command guard failed, so this command was not run: ' + next.error.kind } + }) +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/no-register/.claude-plugin/plugin.json b/packages/experimental/claude-code-mods/tests/fixtures/no-register/.claude-plugin/plugin.json new file mode 100644 index 0000000000..6f2e9ce1cc --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/no-register/.claude-plugin/plugin.json @@ -0,0 +1 @@ +{ "name": "no-register" } diff --git a/packages/experimental/claude-code-mods/tests/fixtures/no-register/hooks/hooks.json b/packages/experimental/claude-code-mods/tests/fixtures/no-register/hooks/hooks.json new file mode 100644 index 0000000000..687749f783 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/no-register/hooks/hooks.json @@ -0,0 +1 @@ +{ "modules": ["./register.js"] } diff --git a/packages/experimental/claude-code-mods/tests/fixtures/no-register/hooks/register.js b/packages/experimental/claude-code-mods/tests/fixtures/no-register/hooks/register.js new file mode 100644 index 0000000000..a287c20c28 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/no-register/hooks/register.js @@ -0,0 +1 @@ +export const notRegister = true diff --git a/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/.claude-plugin/plugin.json b/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/.claude-plugin/plugin.json new file mode 100644 index 0000000000..514956351e --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "ticket-mod", + "version": "0.1.0", + "description": "Registers a ticket lookup tool, adds branch context to PR prompts, and times turns" +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/hooks/hooks.json b/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/hooks/hooks.json new file mode 100644 index 0000000000..0d5260d176 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/hooks/hooks.json @@ -0,0 +1,3 @@ +{ + "modules": ["./register.js"] +} diff --git a/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/hooks/register.js b/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/hooks/register.js new file mode 100644 index 0000000000..97ec70d6a5 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/fixtures/ticket-mod/hooks/register.js @@ -0,0 +1,30 @@ +// A mod that adds a tool for the model, context for the model, and a line +// under each answer, from the examples on Claude Code's mods API page. + +const TICKETS = { 'T-1': 'Login button does nothing (open)' } + +export function register(on) { + on('session.start', async ($, e, next) => { + await $.tool.register({ + name: 'ticket', + description: 'Look up a ticket by its id and return its title and status', + inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] }, + }) + return next(e) + }) + + // The full tool name is mcp__, the plugin's name, and the registered name + on('tool.call', { tool: 'mcp__ticket-mod__ticket' }, async ($, e) => { + return { result: TICKETS[e.id] ?? 'Lookup failed: no ticket ' + e.id } + }) + + on('prompt.submit', async ($, e, next) => { + if (!/\bPR\b|pull request/i.test(e.text)) return next(e) + return next({ ...e, context: [...(e.context ?? []), 'Current branch: feature/mods'] }) + }) + + on('turn.complete', async ($, e, next) => { + const result = await next(e) + return { ...result, text: 'Done in ' + (e.durationMs >= 0 ? 'some' : 'negative') + ' ms' } + }) +} diff --git a/packages/experimental/claude-code-mods/tests/host-ops.spec.ts b/packages/experimental/claude-code-mods/tests/host-ops.spec.ts new file mode 100644 index 0000000000..ec7d4a13e1 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/host-ops.spec.ts @@ -0,0 +1,230 @@ +import { mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs' +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 type { Agent } from '@deepseek-ai/dsh-agent' +import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' +import CommandRuntime from '@deepseek-ai/dsh-commands' +import LocalFileSystem from '@deepseek-ai/dsh-fs-local' +import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local' +import { ModsEngine } from '../src/engine.ts' +import type { OpContext } from '../src/engine.ts' +import { createHostOps, FS_MAX_BYTES, STORE_MAX_BYTES, toolCallResultOf } from '../src/host-ops.ts' +import type { AgentBinding } from '../src/host-ops.ts' +import type { LoadedMod } from '../src/chain.ts' +import { createToolNameAliases } from '../src/tool-names.ts' +import { messageOf, record, requireString, stringify } from '../src/values.ts' + +/** + * The host op table against bare and partially composed contexts: the error a + * mod's call gets when a service is not composed, the input checks, and the + * option branches the full-loop bridge tests do not reach. + */ + +const dirs: string[] = [] +const contexts: Context[] = [] +afterEach(async () => { + for (const ctx of contexts.splice(0)) await ctx.fiber.dispose() + for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }) +}) + +const mod: LoadedMod = { name: 'unit-mod', version: undefined, root: '/mods/unit-mod', modulePath: '', options: {}, order: 0 } + +function setup(ctx = new Context()) { + contexts.push(ctx) + const registrations = new Set<() => void>() + const ops = createHostOps({ + ctx, aliases: createToolNameAliases(), processTimeoutMs: 5_000, registrations, modCommands: new Set(), modTools: new Set(), + }) + const engine = new ModsEngine({ ops: op => ops[op], stateKey: () => 's', budgetMs: 1000, catchBudgetMs: 100, report: () => {} }) + const context = (agent?: Agent): OpContext => ({ mod, binding: { agent }, signal: new AbortController().signal, engine }) + const call = (op: string, input: unknown, agent?: Agent): Promise => + Promise.resolve().then(() => ops[op]?.(input, context(agent))) + return { ctx, ops, engine, context, call, registrations } +} + +/** A fake agent with the session facts the ops read; its scoped ctx is the root ctx. */ +function fakeAgent(ctx: Context, overrides: { cwd?: string; model?: string; requestModel?: string } = {}): Agent { + return { + id: 'fake', + ctx, + options: overrides.model === undefined ? {} : { model: overrides.model }, + session: { + id: 'fake', + header: { id: 'fake', createdAt: 1, ...overrides.cwd === undefined ? {} : { cwd: overrides.cwd } }, + requestHeader: () => overrides.requestModel === undefined ? undefined : { config: { provider: 'p', model: overrides.requestModel } }, + deriveMessages: () => [], + }, + } as unknown as Agent +} + +describe('values', () => { + it('reads records, requires strings, words errors, and encodes JSON', () => { + expect(record({ a: 1 })).toEqual({ a: 1 }) + expect(record('x')).toEqual({}) + expect(record(null)).toEqual({}) + expect(requireString('ok', 'f')).toBe('ok') + expect(() => requireString(1, 'the field')).toThrow('the field must be a string') + expect(messageOf(new Error('e'))).toBe('e') + expect(messageOf('plain')).toBe('plain') + expect(stringify({ a: 1 })).toBe('{"a":1}') + expect(stringify(undefined)).toBeUndefined() + expect(toolCallResultOf({ isError: false, value: null, content: [{ type: 'text', text: 'a' }, { type: 'text', text: 'b' }] })).toEqual({ result: 'ab' }) + expect(toolCallResultOf({ isError: true, error: { message: 'x' }, content: [{ type: 'text', text: 'Error: x' }] })).toEqual({ result: 'Error: x', isError: true }) + }) +}) + +describe('services a deployment did not compose', () => { + it('names the missing service in each call', async () => { + const { call, ctx } = setup() + const agent = fakeAgent(ctx) + await expect(call('ui.ask', { question: 'q' }, agent)).rejects.toThrow(/dsh-user-questions/) + await expect(call('command.register', { name: 'x', description: 'd' }, agent)).rejects.toThrow(/dsh-commands/) + await expect(call('command.run', { command: 'x' }, agent)).rejects.toThrow(/dsh-commands/) + await expect(call('command.list', {}, agent)).resolves.toEqual([]) + await expect(call('tool.register', { name: 'x', description: 'd' }, agent)).rejects.toThrow(/dsh-tools/) + await expect(call('tool.call', { tool: 'x' }, agent)).rejects.toThrow(/dsh-tools/) + await expect(call('tool.list', {}, agent)).resolves.toEqual([]) + await expect(call('tool.list', {})).resolves.toEqual([]) + await expect(call('store.get', { key: 'k' })).rejects.toThrow(/dsh-storage-domain/) + await expect(call('fs.read', { path: 'x' })).rejects.toThrow(/dsh-fs/) + await expect(call('process.run', { argv: ['ls'] })).rejects.toThrow(/dsh-subprocess/) + await expect(call('session.turns', {}, agent)).rejects.toThrow(/turnBoundary projection/) + expect(await call('session.usage', {}, agent)).toEqual({ startedAt: 1, context: { window: 0 }, rateLimits: [] }) + }) + + it('requires a session for session-bound calls and validates mod input', async () => { + const { call } = setup() + for (const op of ['ui.ask', 'command.run', 'command.list', 'prompt.submit', 'session.id', 'session.cwd', 'session.root', 'session.model', 'session.turns', 'session.messages', 'session.usage']) { + await expect(call(op, {})).rejects.toThrow(`$.${op} needs a session, and this event has none`) + } + await expect(call('ui.log', { text: 1, to: 'transcript' })).rejects.toThrow('$.ui.log text must be a string') + await expect(call('ui.toast', {})).rejects.toThrow('$.ui.toast text must be a string') + await expect(call('ui.status', { text: 2 })).rejects.toThrow('$.ui.status text must be a string') + await expect(call('env.set', { name: 'X', value: 1 })).rejects.toThrow('$.env.set value must be a string') + await expect(call('http.fetch', {})).rejects.toThrow('$.http.fetch url must be a string') + }) +}) + +describe('session facts from the agent', () => { + it('falls back from the logged route to the agent option to an empty model, and from the session cwd to the process cwd', async () => { + const { call, ctx } = setup() + expect(await call('session.model', {}, fakeAgent(ctx, { requestModel: 'logged', model: 'opt' }))).toBe('logged') + expect(await call('session.model', {}, fakeAgent(ctx, { model: 'opt' }))).toBe('opt') + expect(await call('session.model', {}, fakeAgent(ctx))).toBe('') + expect(await call('session.cwd', {}, fakeAgent(ctx))).toBe(process.cwd()) + expect(await call('session.cwd', {}, fakeAgent(ctx, { cwd: '/work' }))).toBe('/work') + expect(await call('session.root', {}, fakeAgent(ctx))).toBe(process.cwd()) + expect(await call('session.root', {}, fakeAgent(ctx, { cwd: '/work' }))).toBe('/work') + expect(await call('session.id', {}, fakeAgent(ctx))).toBe('fake') + expect(await call('session.messages', {}, fakeAgent(ctx))).toEqual([]) + }) +}) + +describe('files through the filesystem seam', () => { + it('resolves against the session cwd or the filesystem cwd, enforces the size limits, and reports links', async () => { + const root = mkdtempSync(join(tmpdir(), 'dsh-cc-mods-fs-')) + dirs.push(root) + const ctx = new Context() + await ctx.plugin(LocalFileSystem, { cwd: root }) + const { call } = setup(ctx) + writeFileSync(join(root, 'small.txt'), 'small') + writeFileSync(join(root, 'big.txt'), Buffer.alloc(FS_MAX_BYTES + 1, 97)) + symlinkSync(join(root, 'small.txt'), join(root, 'link.txt')) + symlinkSync(join(root, 'gone.txt'), join(root, 'dangling.txt')) + expect(await call('fs.read', { path: 'small.txt' })).toBe('small') + expect(await call('fs.read', { path: 'small.txt' }, fakeAgent(ctx, { cwd: root }))).toBe('small') + await expect(call('fs.read', { path: 'big.txt' })).rejects.toThrow(/larger than/) + await expect(call('fs.write', { path: 'out.txt', text: 'x'.repeat(FS_MAX_BYTES + 1) })).rejects.toThrow(/larger than/) + await expect(call('fs.write', { path: 1, text: 'x' })).rejects.toThrow('$.fs.write path must be a string') + expect(await call('fs.stat', { path: 'link.txt' })).toEqual({ kind: 'file', size: 5, mtimeMs: 0, isLink: true }) + expect(await call('fs.stat', { path: 'dangling.txt' })).toEqual({ kind: 'other', size: 0, mtimeMs: 0, isLink: true }) + expect(await call('fs.stat', { path: 'small.txt' }, fakeAgent(ctx, { cwd: root }))).toMatchObject({ kind: 'file', isLink: false }) + expect(await call('fs.stat', { path: '.' })).toMatchObject({ kind: 'dir', isLink: false }) + const listed = await call('fs.list', { path: '.' }) as { name: string; kind: string }[] + expect(listed.map(entry => entry.kind)).toContain('file') + expect(await call('fs.exists', { path: 'nope' })).toBe(false) + }) +}) + +describe('processes through the subprocess seam', () => { + it('runs argv with an explicit cwd and env, and reports a signal-terminated child', async () => { + const ctx = new Context() + await ctx.plugin(LocalSubprocessRuntime) + const { call } = setup(ctx) + const cwd = mkdtempSync(join(tmpdir(), 'dsh-cc-mods-proc-')) + dirs.push(cwd) + const run = await call('process.run', { + argv: ['node', '-e', 'process.stdout.write(process.cwd() + "|" + process.env.CC_MODS_ENV)'], + init: { cwd, env: { CC_MODS_ENV: 'set' } }, + }) as { exitCode: number; stdout: string } + expect(run.exitCode).toBe(0) + expect(run.stdout.endsWith('|set')).toBe(true) + const viaAgent = await call('process.run', { argv: ['node', '-e', 'process.stdout.write(process.cwd())'] }, fakeAgent(ctx, { cwd })) as { stdout: string } + expect(viaAgent.stdout.length).toBeGreaterThan(0) + await expect(call('process.run', { argv: ['node', '-e', 'process.kill(process.pid, "SIGKILL")'] })).rejects.toThrow(/was terminated by signal SIGKILL/) + await expect(call('process.run', { argv: [] })).rejects.toThrow(/non-empty argv/) + await expect(call('process.run', { argv: ['node', 1] })).rejects.toThrow(/non-empty argv/) + }) +}) + +describe('the store limit', () => { + it('refuses a value that would push the plugin past its JSON budget', async () => { + const ctx = new Context() + const table = new Map>() + const domain = { + table: () => ({ + get: (key: string) => table.get(key), + put: (key: string, value: Record) => { + table.set(key, value) + return Promise.resolve() + }, + }), + close: () => Promise.resolve(), + } + ctx.provide('storageDomain', { open: () => Promise.resolve(domain) }) + const { call } = setup(ctx) + expect(await call('store.keys', {})).toEqual([]) + await call('store.set', { key: 'ok', value: 1 }) + expect(await call('store.keys', {})).toEqual(['ok']) + await expect(call('store.set', { key: 'big', value: 'x'.repeat(STORE_MAX_BYTES) })).rejects.toThrow(/would exceed/) + expect(await call('store.get', { key: 'ok' })).toBe(1) + expect(await call('store.get', { key: 'missing' })).toBeUndefined() + await call('store.delete', { key: 'ok' }) + expect(await call('store.keys', {})).toEqual([]) + }) +}) + +describe('registrations outside a session', () => { + it('registers commands and tools globally when the raising event has no agent', async () => { + const ctx = new Context() + await mountAgentLoopTestDependencies(ctx) + await ctx.plugin(CommandRuntime) + const { call, registrations, engine } = setup(ctx) + await call('command.register', { name: 'Global', description: 'global command' }) + await call('tool.register', { name: 'global_tool', description: 'global tool' }) + expect(ctx.tools.schemas().map(tool => tool.name)).toContain('mcp__unit-mod__global_tool') + expect((await call('tool.list', {}) as { name: string }[]).map(tool => tool.name)).toContain('mcp__unit-mod__global_tool') + const agent = fakeAgent(ctx) + expect((await call('command.list', {}, agent) as { name: string; source: string }[])).toEqual([{ name: 'global', description: 'global command', source: 'plugin' }]) + expect(registrations.size).toBe(2) + void engine + for (const dispose of registrations) dispose() + expect(ctx.tools.schemas().map(tool => tool.name)).not.toContain('mcp__unit-mod__global_tool') + }) +}) + +describe('ui.log sinks', () => { + it('writes transcript lines at info and debug lines at debug', async () => { + const { call, ctx } = setup() + const info = vi.fn() + const debug = vi.fn() + ctx.logger.info = info as never + ctx.logger.debug = debug as never + await call('ui.log', { text: 'seen', to: 'transcript' }) + await call('ui.log', { text: 'hidden', to: 'debug' }) + expect(info).toHaveBeenCalledWith('unit-mod: seen') + expect(debug).toHaveBeenCalledWith('unit-mod: hidden') + }) +}) diff --git a/packages/experimental/claude-code-mods/tests/loader-composition.spec.ts b/packages/experimental/claude-code-mods/tests/loader-composition.spec.ts new file mode 100644 index 0000000000..b7ec708d00 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/loader-composition.spec.ts @@ -0,0 +1,83 @@ +/** Real Loader composition: the bridge mounted from a cordis.yml row beside the shipped core plugins. */ + +import { mkdtemp, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { pathToFileURL } from 'node:url' +import { afterEach, expect, it } from 'vitest' +import { Context } from '@deepseek-ai/cordis' +import Loader from '@deepseek-ai/cordis-plugin-loader' +import Include from '@deepseek-ai/cordis-plugin-include' +import AgentRegistry from '@deepseek-ai/dsh-agent' +import AgentLoop from '@deepseek-ai/dsh-agent-loop' +import CommandRuntime from '@deepseek-ai/dsh-commands' +import LlmRuntime, { createUserMessage } from '@deepseek-ai/dsh-llm' +import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' +import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' +import SystemPrompt from '@deepseek-ai/dsh-system-prompt' +import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools' +import * as ClaudeCodeMods from '../src/index.ts' +import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts' + +const FIXTURES = resolve(import.meta.dirname, 'fixtures') + +let ctx: Context | undefined +let root: string | undefined + +afterEach(async () => { + await ctx?.fiber.dispose() + if (root !== undefined) await rm(root, { recursive: true, force: true }) + ctx = undefined + root = undefined +}) + +it('loads from cordis.yml, counts the model\'s tool calls, and answers /tally through the composed command registry', async () => { + root = await mkdtemp(join(tmpdir(), 'dsh-cc-mods-composition-')) + const configPath = join(root, 'cordis.yml') + const modules = new Map([ + ['@deepseek-ai/dsh-llm', LlmRuntime], + ['@deepseek-ai/dsh-session', SessionStore], + ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], + ['@deepseek-ai/dsh-system-prompt', SystemPrompt], + ['@deepseek-ai/dsh-tools', ToolRuntime], + ['@deepseek-ai/dsh-agent', AgentRegistry], + ['@deepseek-ai/dsh-agent-loop', AgentLoop], + ['@deepseek-ai/dsh-commands', CommandRuntime], + ['@deepseek-ai/dsh-experimental-claude-code-mods', ClaudeCodeMods], + ]) + await writeFile(configPath, [...modules.keys()].flatMap(name => [ + `- name: '${name}'`, + ...name === '@deepseek-ai/dsh-experimental-claude-code-mods' + ? [' config:', ' pluginDirs:', ` - ${JSON.stringify(join(FIXTURES, 'first-mod'))}`, ' options:', ' first-mod:', ' greeting: The model made'] + : [], + ]).join('\n') + '\n') + + const context = ctx = new Context() + context.baseUrl = pathToFileURL(root).href + '/' + await context.plugin(Loader) + context.loader.builtins.include = Include + context.loader.internal = { + version: 'v2', + async import(specifier: string) { + if (!modules.has(specifier)) throw new Error(`Unexpected fixture module: ${specifier}`) + return modules.get(specifier) + }, + } as unknown as NonNullable + await context.loader.create({ name: 'cordis:include', config: { path: pathToFileURL(configPath).href } }) + await context.loader.await() + for (const entry of context.loader.entries()) await entry.fiber?.await() + + const adapter = new MockAdapter([toolCallResponse('c1', 'echo', { command: 'ls' }), textResponse('listed')]) + context.llm.registerAdapter(['mock'], adapter) + context.tools.register(defineContentToolFixture({ + name: 'echo', description: 'echo', parameters: { command: { type: 'string' } }, + async execute(args) { return [{ type: 'text', text: `ran ${args.command}` }] }, + })) + const agent = await context.agentLoop.create(SessionId('composed'), { provider: 'mock', model: 'mock' }) + expect(context.commands.list(agent).map(command => command.name)).toEqual(['tally']) + agent.followup(createUserMessage({ content: [{ type: 'text', text: 'list the files here' }], source: { kind: 'user' } })) + await agent.whenIdle() + expect(adapter.requests).toHaveLength(2) + const run = await context.commands.execute(agent, '/tally', [], new AbortController().signal) + expect(run?.result).toEqual({ kind: 'success', text: 'first-mod: The model made 1 tool calls since this mod loaded' }) +}) diff --git a/packages/experimental/claude-code-mods/tests/manifest.spec.ts b/packages/experimental/claude-code-mods/tests/manifest.spec.ts new file mode 100644 index 0000000000..88b9383b47 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/manifest.spec.ts @@ -0,0 +1,140 @@ +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { afterEach, describe, expect, it } from 'vitest' +import { HOOKS_MODULE_EXTENSIONS, parseHooksJson, parseModManifest, readModManifest, resolvePluginOptions } from '../src/manifest.ts' +import { describeMatcher, eventMatches, isEventPattern, KNOWN_EVENTS, matcherMatches } from '../src/matcher.ts' +import { createToolNameAliases, DEFAULT_TOOL_ALIASES } from '../src/tool-names.ts' + +const FIXTURES = resolve(import.meta.dirname, 'fixtures') +const dirs: string[] = [] +afterEach(() => { for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }) }) + +function scratch(): string { + const dir = mkdtempSync(join(tmpdir(), 'dsh-cc-mods-')) + dirs.push(dir) + return dir +} + +describe('parseModManifest', () => { + it('reads the name, version, description, and userConfig defaults', () => { + const manifest = parseModManifest({ + name: 'first-mod', version: '0.1.0', description: 'd', + userConfig: { greeting: { type: 'string', default: 'hi' }, count: { type: 'number' }, list: { default: ['a'] } }, + }, 'plugin.json') + expect(manifest).toEqual({ name: 'first-mod', version: '0.1.0', description: 'd', userConfigDefaults: { greeting: 'hi', list: ['a'] } }) + }) + + it.each([ + [null, /must be a JSON object/], + [{ name: 'Bad Name' }, /"name" must be lowercase/], + [{ name: 'claude-x' }, undefined], + [{ name: 'ok', version: 1 }, /"version" must be a string/], + [{ name: 'ok', description: 1 }, /"description" must be a string/], + [{ name: 'ok', userConfig: [] }, /"userConfig" must be an object/], + [{ name: 'ok', userConfig: { f: 1 } }, /userConfig\.f must be an object/], + [{ name: 'ok', userConfig: { f: { default: { nested: true } } } }, /userConfig\.f\.default must be/], + ])('validates %j', (raw, error) => { + if (error === undefined) expect(() => parseModManifest(raw, 'p')).not.toThrow() + else expect(() => parseModManifest(raw, 'p')).toThrow(error) + }) +}) + +describe('parseHooksJson', () => { + const hooksJson = '/plugins/demo/hooks/hooks.json' + + it('resolves the one module path against hooks.json and notes settings hooks', () => { + expect(parseHooksJson({ modules: ['./register.js'] }, hooksJson)).toEqual({ modulePath: '/plugins/demo/hooks/register.js', hasSettingsHooks: false }) + expect(parseHooksJson({ modules: ['../lib/mod.mts'], hooks: {} }, hooksJson)).toEqual({ modulePath: '/plugins/demo/lib/mod.mts', hasSettingsHooks: true }) + expect(HOOKS_MODULE_EXTENSIONS).toContain('.tsx') + }) + + it.each([ + [null, /must be a JSON object/], + [{}, /exactly one module path/], + [{ modules: ['./a.js', './b.js'] }, /exactly one module path/], + [{ modules: [1] }, /exactly one module path/], + [{ modules: ['/abs/register.js'] }, /must be relative/], + [{ modules: ['../../outside.js'] }, /stay inside the plugin directory/], + [{ modules: ['./register.json'] }, /must end in one of/], + ])('rejects %j', (raw, error) => { + expect(() => parseHooksJson(raw, hooksJson)).toThrow(error) + }) +}) + +describe('readModManifest and resolvePluginOptions', () => { + it('reads a fixture directory and overlays configured options on manifest defaults', () => { + const directory = readModManifest('first-mod', FIXTURES) + expect(directory.root).toBe(join(FIXTURES, 'first-mod')) + expect(directory.modulePath).toBe(join(FIXTURES, 'first-mod', 'hooks', 'register.js')) + expect(directory.hasSettingsHooks).toBe(false) + expect(resolvePluginOptions(directory.manifest, undefined)).toEqual({ greeting: 'Claude has made' }) + expect(resolvePluginOptions(directory.manifest, { greeting: 'The model made', extra: 1 })).toEqual({ greeting: 'The model made', extra: 1 }) + expect(readModManifest(join(FIXTURES, 'guard-mod'), '/elsewhere').hasSettingsHooks).toBe(true) + }) + + it('fails loud on a missing directory, unreadable file, or invalid JSON', () => { + expect(() => readModManifest(join(FIXTURES, 'missing'), FIXTURES)).toThrow(/plugin\.json: cannot read/) + const dir = scratch() + mkdirSync(join(dir, '.claude-plugin')) + writeFileSync(join(dir, '.claude-plugin', 'plugin.json'), '{ not json') + expect(() => readModManifest(dir, FIXTURES)).toThrow(/invalid JSON/) + writeFileSync(join(dir, '.claude-plugin', 'plugin.json'), '{ "name": "x" }') + expect(() => readModManifest(dir, FIXTURES)).toThrow(/hooks\.json: cannot read/) + }) +}) + +describe('event patterns and matchers', () => { + it('knows Claude Code\'s event names and globs', () => { + expect(KNOWN_EVENTS.has('tool.call')).toBe(true) + expect(isEventPattern('tool.call')).toBe(true) + expect(isEventPattern('tool.calls')).toBe(false) + expect(isEventPattern('*')).toBe(true) + expect(isEventPattern('classic.*')).toBe(false) + expect(isEventPattern('telemetry.*')).toBe(true) + expect(isEventPattern('.*')).toBe(false) + }) + + it('matches exact names, namespace globs, and the star without telemetry', () => { + expect(eventMatches('tool.call', 'tool.call')).toBe(true) + expect(eventMatches('tool.call', 'tool.check')).toBe(false) + expect(eventMatches('tool.*', 'tool.check')).toBe(true) + expect(eventMatches('tool.*', 'turn.start')).toBe(false) + expect(eventMatches('*', 'turn.start')).toBe(true) + expect(eventMatches('*', 'telemetry.log')).toBe(false) + expect(eventMatches('telemetry.*', 'telemetry.log')).toBe(true) + }) + + it('compares scalars by equality, arrays by membership, and regular expressions by test', () => { + const input = { tool: 'mcp__github__issues', count: 3, flag: true } + expect(matcherMatches({ tool: 'mcp__github__issues' }, input)).toBe(true) + expect(matcherMatches({ tool: 'Bash' }, input)).toBe(false) + expect(matcherMatches({ tool: ['Edit', 'mcp__github__issues'] }, input)).toBe(true) + expect(matcherMatches({ tool: /^mcp__github__/ }, input)).toBe(true) + expect(matcherMatches({ count: /^3$/ }, input)).toBe(true) + expect(matcherMatches({ flag: /true/ }, input)).toBe(false) + expect(matcherMatches({ tool: 'x', count: 3 }, input)).toBe(false) + expect(matcherMatches({ tool: 'x' }, 'not an object')).toBe(false) + expect(matcherMatches({}, input)).toBe(true) + }) + + it('describes matchers the way claude plugin validate prints them', () => { + expect(describeMatcher(undefined)).toBe('') + expect(describeMatcher({ component: 'Spinner' })).toBe('{component=Spinner}') + expect(describeMatcher({ tool: ['Edit', 'Write'], id: /^T-/ })).toBe('{tool=Edit|Write,id=/^T-/}') + }) +}) + +describe('tool-name aliases', () => { + it('translates both ways, falling back to the name itself, and takes overrides', () => { + const aliases = createToolNameAliases({ Bash: 'pwsh', Custom: 'my_tool' }) + expect(DEFAULT_TOOL_ALIASES.Bash).toBe('bash') + expect(aliases.toHarness('Bash')).toBe('pwsh') + expect(aliases.toMod('pwsh')).toBe('Bash') + expect(aliases.toMod('bash')).toBe('bash') + expect(aliases.toHarness('Custom')).toBe('my_tool') + expect(aliases.toHarness('Read')).toBe('read') + expect(aliases.toMod('read')).toBe('Read') + expect(aliases.toMod('unknown_tool')).toBe('unknown_tool') + }) +}) diff --git a/packages/experimental/claude-code-mods/tests/module.spec.ts b/packages/experimental/claude-code-mods/tests/module.spec.ts new file mode 100644 index 0000000000..5f2ed0d257 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/module.spec.ts @@ -0,0 +1,195 @@ +import { resolve } from 'node:path' +import { describe, expect, it, vi } from 'vitest' +import type { LoadedMod } from '../src/chain.ts' +import { ModsEngine, OpDenied } from '../src/engine.ts' +import { readModManifest } from '../src/manifest.ts' +import { createOn, HookRegistry, loadHooksModule } from '../src/module.ts' +import type { AnyHook, ModsApi } from '../src/types.ts' + +const FIXTURES = resolve(import.meta.dirname, 'fixtures') +const mod = (name: string, order = 0): LoadedMod => ({ name, version: undefined, root: '/mods/' + name, modulePath: '', options: {}, order }) +const noop: AnyHook = (_$, e, next) => next(e) + +describe('createOn', () => { + it('collects registrations in order, with matchers and .catch handlers', () => { + const owner = mod('a') + const { on, hooks } = createOn(owner) + const handler: AnyHook = () => ({ deny: 'closed' }) + on('tool.call', noop).catch(handler) + on('tool.call', { tool: 'Bash' }, noop) + on('ui.render', { component: 'Spinner' }, noop) + expect(hooks.map(hook => [hook.event, hook.matcher])).toEqual([['tool.call', undefined], ['tool.call', { tool: 'Bash' }], ['ui.render', { component: 'Spinner' }]]) + expect(hooks[0]?.catchHandler).toBe(handler) + expect(hooks.every(hook => hook.mod === owner)).toBe(true) + }) + + it('refuses what claude plugin validate refuses', () => { + const { on } = createOn(mod('a')) + expect(() => on('tool.call', noop)).not.toThrow() + expect(() => on(42 as unknown as string, noop)).toThrow(/not a string literal/) + expect(() => on('tool.calls', noop)).toThrow(/"tool.calls" is not an event/) + expect(() => on('session.start', noop)).not.toThrow() + expect(() => on('session.start', noop)).toThrow(/on\("session.start"\) is registered twice without a matcher/) + expect(() => on('session.start', { cwd: '/x' }, noop)).not.toThrow() + expect(() => on('turn.start', 'nope' as unknown as AnyHook)).toThrow(/needs a hook function/) + expect(() => on('turn.start', 'nope' as unknown as Record, noop)).toThrow(/matcher must be an object/) + expect(() => { on('turn.complete', noop).catch('nope' as unknown as AnyHook) }).toThrow(/needs a handler function/) + }) +}) + +describe('HookRegistry', () => { + it('orders hooks by mod load then registration, scopes op events to earlier mods, and describes a mod', () => { + const registry = new HookRegistry() + const a = mod('a', 0) + const b = mod('b', 1) + const onA = createOn(a) + onA.on('tool.call', noop) + onA.on('fs.read', noop) + const onB = createOn(b) + onB.on('tool.call', { tool: 'Bash' }, noop) + onB.on('fs.read', noop) + onB.on('*', noop) + registry.add(b, onB.hooks) + registry.add(a, onA.hooks) + expect(registry.list().map(m => m.name)).toEqual(['b', 'a']) + expect(registry.select('tool.call').map(hook => hook.mod.name)).toEqual(['a', 'b', 'b']) + expect(registry.select('fs.read', b).map(hook => hook.mod.name)).toEqual(['a']) + expect(registry.select('fs.read', a).map(hook => hook.mod.name)).toEqual([]) + const c = mod('c', 2) + expect(registry.select('fs.read', c).map(hook => hook.mod.name)).toEqual(['a', 'b', 'b']) + expect(registry.describe(b)).toBe('tool.call{tool=Bash}, fs.read, *') + expect(() => { registry.add(mod('a', 5), []) }).toThrow(/another plugin of that name loads first/) + registry.remove('b') + registry.remove('nobody') + expect(registry.select('tool.call').map(hook => hook.mod.name)).toEqual(['a']) + expect(registry.list().map(m => m.name)).toEqual(['a']) + }) +}) + +describe('loadHooksModule', () => { + it('imports the fixture module and runs register with the resolved options', async () => { + const directory = readModManifest('first-mod', FIXTURES) + const { mod: loaded, hooks } = await loadHooksModule(directory, { greeting: 'hi' }, 3) + expect(loaded).toMatchObject({ name: 'first-mod', version: '0.1.0', root: directory.root, modulePath: directory.modulePath, order: 3, options: { greeting: 'hi' } }) + expect(hooks.map(hook => hook.event)).toEqual(['session.start', 'tool.call', 'command.run', 'ui.render']) + }) + + it('reports a module without register, a register that throws, and an import that fails', async () => { + await expect(loadHooksModule(readModManifest('no-register', FIXTURES), {}, 0)).rejects.toThrow(/does not export a register function/) + await expect(loadHooksModule(readModManifest('broken-mod', FIXTURES), {}, 0)).rejects.toThrow(/register threw .*"tool.calls" is not an event/) + const missing = { ...readModManifest('first-mod', FIXTURES), modulePath: resolve(FIXTURES, 'first-mod/hooks/absent.js') } + await expect(loadHooksModule(missing, {}, 0)).rejects.toThrow(/hooks module did not load/) + }) +}) + +describe('ModsEngine', () => { + function engine(ops: Record unknown> = {}, report = vi.fn()) { + return { + engine: new ModsEngine<{ key: string }>({ ops: op => ops[op], stateKey: b => b.key, budgetMs: 200, catchBudgetMs: 100, report }), + report, + } + } + + it('keeps $.state per session key and answers clock.now and clock.sleep itself', async () => { + const { engine: e } = engine() + const owner = mod('a') + const first = e.api(owner, undefined, { key: 's1' }, new AbortController().signal) + const second = e.api(owner, undefined, { key: 's2' }, new AbortController().signal) + const ref = { plugin: 'a', key: 'readings' } + expect(await first.state.get(ref)).toEqual({ value: undefined }) + await first.state.set(ref, [1, 2]) + await first.state.set({ plugin: 'a', key: 'other' }, 'o') + expect(await first.state.get(ref)).toEqual({ value: [1, 2] }) + expect(await second.state.get(ref)).toEqual({ value: undefined }) + e.forgetState('s1') + expect(await first.state.get(ref)).toEqual({ value: undefined }) + await expect(first.state.get({ plugin: 1 as unknown as string, key: 'x' })).rejects.toThrow(/needs \{ plugin, key \} strings/) + const before = Date.now() + expect(await first.clock.now()).toBeGreaterThanOrEqual(before) + await first.clock.sleep(1) + await expect(first.clock.sleep(-1)).rejects.toThrow(/non-negative/) + await expect(first.fs.read('x')).rejects.toThrow('no implementation for fs.read') + }) + + it('serves ops from the resolver, lets earlier mods intercept, and maps deny answers to rejections', async () => { + const calls: string[] = [] + const { engine: e } = engine({ + 'fs.read': (input) => { calls.push(JSON.stringify(input)); return 'contents' }, + 'fs.write': () => { throw new OpDenied('read-only deployment') }, + }) + const policy = mod('policy', 0) + const user = mod('user', 1) + const onPolicy = createOn(policy) + onPolicy.on('fs.read', (_$, event, next) => { + const input = event as { path: string } + if (input.path === '/secret') return { deny: 'policy: no secrets' } + if (input.path === '/junk') return { nonsense: true } + return next({ ...input, path: input.path + '.redirected' }) + }) + e.registry.add(policy, onPolicy.hooks) + e.registry.add(user, []) + const signal = new AbortController().signal + const $ = e.api(user, undefined, { key: 's' }, signal) + expect(await $.fs.read('/notes')).toBe('contents') + expect(calls).toEqual(['{"path":"/notes.redirected","as":"text"}']) + await expect($.fs.read('/secret')).rejects.toThrow('fs.read refused: policy: no secrets') + await expect($.fs.read('/junk')).rejects.toThrow(/returned neither \{ value \} nor \{ deny \}/) + await expect($.fs.write('/x', 'y')).rejects.toThrow('fs.write refused: read-only deployment') + const $policy = e.api(policy, undefined, { key: 's' }, signal) + expect(await $policy.fs.read('/secret')).toBe('contents') + await expect(e.invoke(user, 'fs.read', { path: '/p' }, { key: 's' }, signal)).resolves.toBe('contents') + }) + + it('owns timers per mod: callbacks run and report failures, cancel stops them, unload and dispose clear them', async () => { + vi.useFakeTimers() + try { + const { engine: e, report } = engine() + const owner = mod('a') + e.registry.add(owner, []) + const $ = e.api(owner, undefined, { key: 's' }, new AbortController().signal) + const ticks: string[] = [] + const once = $.clock.after(10, () => { ticks.push('after') }) + const every = $.clock.every(10, () => { ticks.push('every') }) + $.clock.after(10, () => { throw new Error('tick failed') }) + const cancelled = $.clock.after(5, () => { ticks.push('cancelled') }) + cancelled.cancel() + await vi.advanceTimersByTimeAsync(25) + expect(ticks).toEqual(['after', 'every', 'every']) + expect(report).toHaveBeenCalledWith('a: timer callback failed: tick failed') + once.cancel() + every.cancel() + await vi.advanceTimersByTimeAsync(20) + expect(ticks).toEqual(['after', 'every', 'every']) + $.clock.every(10, () => { ticks.push('late') }) + e.unload('a') + await vi.advanceTimersByTimeAsync(30) + expect(ticks).toEqual(['after', 'every', 'every']) + const again = mod('b') + e.registry.add(again, []) + e.api(again, undefined, { key: 's' }, new AbortController().signal).clock.every(10, () => { ticks.push('b') }) + e.dispose() + await vi.advanceTimersByTimeAsync(30) + expect(ticks).toEqual(['after', 'every', 'every']) + expect(e.registry.list()).toEqual([]) + } finally { + vi.useRealTimers() + } + }) + + it('raises engine events through selected hooks with the engine origin and describes mods', async () => { + const { engine: e } = engine() + const owner = mod('a') + const { on, hooks } = createOn(owner) + let origin: unknown + on('turn.start', ($: ModsApi, event, next) => { + origin = next.origin + expect($.plugin).toEqual({ name: 'a', root: '/mods/a' }) + return next(event) + }) + e.registry.add(owner, hooks) + const result = await e.raise('turn.start', { turnId: '1', text: 'hi' }, event => ({ turnId: (event as { turnId: string }).turnId }), { binding: { key: 's' } }) + expect(result).toEqual({ turnId: '1' }) + expect(origin).toEqual({ plugin: 'engine', tier: 'core' }) + expect(e.describe(owner)).toBe('turn.start') + }) +}) diff --git a/packages/experimental/claude-code-mods/tests/testing.spec.ts b/packages/experimental/claude-code-mods/tests/testing.spec.ts new file mode 100644 index 0000000000..8ccc2946c2 --- /dev/null +++ b/packages/experimental/claude-code-mods/tests/testing.spec.ts @@ -0,0 +1,178 @@ +import { resolve } from 'node:path' +import { describe, expect, it } from 'vitest' +import { createModTestKit, mock } from '../src/testing.ts' +import type { ModsApi } from '../src/types.ts' + +const FIXTURES = resolve(import.meta.dirname, 'fixtures') + +describe('createModTestKit: the tutorial mod', () => { + it('/tally reports the tool calls the mod has seen (the test from Claude Code\'s "Test a mod" page)', async () => { + const kit = await createModTestKit({ dirs: ['first-mod'], cwd: FIXTURES }) + try { + // Answer each tool call in Claude Code's place, so no tool runs + kit.on('tool.call', () => ({ result: 'ok' })) + + // Raise two tool calls, which the mod's tool.call hook counts + await kit.$.tool.call({ tool: 'Bash', command: 'ls' }) + await kit.$.tool.call({ tool: 'Read', file_path: 'README.md' }) + + // Run /tally and check the text its hook returns + const answer = await kit.$.command.run({ command: 'tally', args: '' }) + expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded') + expect(kit.mods.map(mod => mod.name)).toEqual(['first-mod']) + expect(kit.reports).toEqual([]) + } finally { + kit.dispose() + } + }) + + it('passes configured options to register and answers session.start by default', async () => { + const kit = await createModTestKit({ dirs: [resolve(FIXTURES, 'first-mod')], options: { 'first-mod': { greeting: 'The model made' } } }) + try { + const registered: unknown[] = [] + kit.on('command.register', (_$, e) => { + registered.push(e) + return { value: undefined } + }) + expect(await kit.$.session.start({ cwd: '/work', surface: null, isInteractive: true })).toEqual({ cwd: '/work' }) + expect(registered).toEqual([{ name: 'tally', description: 'Show how many tool calls Claude has made' }]) + expect((await kit.$.command.run({ command: 'tally', args: '' })).text).toBe('The model made 0 tool calls since this mod loaded') + expect(await kit.$.command.run({ command: 'other', args: '' })).toEqual({}) + } finally { + kit.dispose() + } + }) +}) + +describe('createModTestKit: stubs, defaults, and inline mods', () => { + it('keeps $.state for the test, reports a fire-and-forget call nobody answers, and refuses $.ui.resolve', async () => { + const kit = await createModTestKit({ modules: [{ + name: 'stateful', + register(on) { + on('turn.start', async ($, e, next) => { + const { value = 0 } = await $.state.get({ plugin: 'stateful', key: 'turns' }) as { value?: number } + await $.state.set({ plugin: 'stateful', key: 'turns' }, value + 1) + $.ui.toast(`turn ${value + 1}`, { timeoutMs: 10 }) + let resolved = 'resolved' + try { + $.ui.resolve(e) + } catch (error) { + resolved = (error as Error).message + } + return { ...(await next(e) as object), turns: value + 1, resolved } + }) + }, + }] }) + try { + expect(await kit.$.turn.start({ turnId: '1', text: 'a' })).toEqual({ turnId: '1', turns: 1, resolved: 'stateful: $.ui.resolve has no element table: this bridge raises no ui.render events' }) + expect(await kit.$.turn.start({ turnId: '2', text: 'b' })).toMatchObject({ turns: 2 }) + await new Promise(resolve => setTimeout(resolve, 0)) + expect(kit.reports).toEqual(['stateful: $.ui.toast failed: no implementation for ui.toast', 'stateful: $.ui.toast failed: no implementation for ui.toast']) + } finally { + kit.dispose() + } + }) + + it('requires a stub for tool.call and reports a mods API call nobody answers', async () => { + const kit = await createModTestKit({ modules: [{ + name: 'reader', + register(on) { + on('tool.call', async ($, e, next) => { + await $.store.set('last', (e as { tool: string }).tool) + return next(e) + }) + }, + }] }) + try { + await expect(kit.$.tool.call({ tool: 'Bash', command: 'ls' })).rejects.toThrow('no implementation for tool.call') + expect(kit.reports).toEqual(['reader: tool.call hook skipped: threw Error: no implementation for store.set']) + } finally { + kit.dispose() + } + }) + + it('answers mods API calls from stubs returning { value } or { deny }, and the kit answers ui.invalidate itself', async () => { + const kit = await createModTestKit({ modules: [{ + name: 'grader', + register(on) { + on('command.run', { command: 'grade' }, async ($, e) => { + $.ui.invalidate('ui.render') + const reply = await $.fs.read((e as { args: string }).args) + .then(text => ({ ok: true, text }), (error: unknown) => ({ ok: false, text: (error as Error).message })) + return { text: reply.ok ? 'Passed: ' + reply.text : 'Try again: ' + reply.text } + }) + }, + }] }) + try { + kit.on('fs.read', (_$, e: { path: string }) => ({ value: e.path.endsWith('grade.txt') ? 'PASS' : '' })) + expect((await kit.$.command.run({ command: 'grade', args: 'grade.txt' })).text).toBe('Passed: PASS') + kit.on('fs.read', () => ({ deny: 'no files in tests' })) + expect((await kit.$.command.run({ command: 'grade', args: 'x' })).text).toBe('Try again: fs.read refused: no files in tests') + kit.on('fs.read', () => 'bare value') + expect((await kit.$.command.run({ command: 'grade', args: 'x' })).text).toMatch(/returned neither \{ value \} nor \{ deny \}/) + kit.on('fs.read', () => ({ nothing: true })) + expect((await kit.$.command.run({ command: 'grade', args: 'x' })).text).toMatch(/returned neither \{ value \} nor \{ deny \}/) + } finally { + kit.dispose() + } + }) + + it('mock.store and mock.env answer whole namespaces from memory', async () => { + const kit = await createModTestKit({ modules: [{ + name: 'notes', + options: { prefix: 'note:' }, + async register(on, options) { + on('session.start', async ($, e, next) => { + const saved = await $.store.get('count') + await $.store.set('count', (typeof saved === 'number' ? saved : 0) + 1) + await $.store.set('prefix', options.prefix) + await $.store.delete('stale') + await $.env.set('NOTES_HOME', await $.env.get('HOME_DIR') ?? 'unset') + await $.env.set('GONE', undefined) + return { ...(await next(e) as object), keys: await $.store.keys() } + }) + }, + }] }) + try { + const on = kit.on.bind(kit) + const saved = mock.store(on, { count: 7, stale: true }) + const env = mock.env(on, { HOME_DIR: '/home/me', GONE: 'yes' }) + const result = await kit.$.session.start({ cwd: '/work', surface: null, isInteractive: false }) + expect(result).toEqual({ cwd: '/work', keys: ['count', 'prefix'] }) + expect(saved.get('count')).toBe(8) + expect(saved.get('prefix')).toBe('note:') + expect(env).toEqual({ HOME_DIR: '/home/me', NOTES_HOME: '/home/me', GONE: undefined }) + } finally { + kit.dispose() + } + }) + + it('raises the other bridged events with their default answers and lets a stub replace them', async () => { + const seen: string[] = [] + const kit = await createModTestKit({ modules: [{ + name: 'observer', + register(on) { + on('*', (_$: ModsApi, e, next) => { + const fields = e as { turnId?: string; text?: string; sessionId?: string } + seen.push(String(fields.turnId ?? fields.text ?? fields.sessionId)) + return next(e) + }) + }, + }] }) + try { + expect(await kit.$.turn.start({ turnId: '1', text: 'go' })).toEqual({ turnId: '1' }) + expect(await kit.$.turn.complete({ turnId: '1', answer: 'done', durationMs: 5, isAborted: false, reason: 'answer' })).toEqual({ text: '' }) + expect(await kit.$.prompt.submit({ text: 'hello', context: ['extra'] })).toEqual({ text: 'hello', context: ['extra'] }) + expect(await kit.$.prompt.submit({ text: 'plain' })).toEqual({ text: 'plain' }) + expect(await kit.$.session.end({ reason: 'other', sessionId: 's1' })).toEqual({ sessionId: 's1' }) + await expect(kit.raise('agent.spawn', { prompt: 'x' })).rejects.toThrow('no implementation for agent.spawn') + kit.on('turn.complete', () => ({ text: 'from stub' })) + expect(await kit.$.turn.complete({ turnId: '2', answer: '', durationMs: 0, isAborted: true, reason: 'aborted' })).toEqual({ text: 'from stub' }) + kit.on('turn.complete', () => undefined) + await expect(kit.$.turn.complete({ turnId: '3', answer: '', durationMs: 0, isAborted: false, reason: 'answer' })).rejects.toThrow('the stub returned no result') + expect(seen).toEqual(['1', '1', 'hello', 'plain', 's1', 'undefined', '2', '3']) + } finally { + kit.dispose() + } + }) +}) diff --git a/packages/experimental/claude-code-mods/tsconfig.json b/packages/experimental/claude-code-mods/tsconfig.json new file mode 100644 index 0000000000..48701706d2 --- /dev/null +++ b/packages/experimental/claude-code-mods/tsconfig.json @@ -0,0 +1,63 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib/types" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../../../vendor/cosmokit" + }, + { + "path": "../../../vendor/cordis" + }, + { + "path": "../../../vendor/schemastery" + }, + { + "path": "../../core/agent" + }, + { + "path": "../../core/scope" + }, + { + "path": "../../core/session" + }, + { + "path": "../../core/tools" + }, + { + "path": "../../fs/fs" + }, + { + "path": "../../interaction/commands" + }, + { + "path": "../../interaction/user-questions" + }, + { + "path": "../../llm/llm" + }, + { + "path": "../../llm/token-meter" + }, + { + "path": "../../session/session-projection" + }, + { + "path": "../../storage/storage-domain" + }, + { + "path": "../../subprocess/subprocess" + }, + { + "path": "../../util/crypto" + }, + { + "path": "../../util/values" + } + ] +} diff --git a/packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md b/packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md index 512b7b6f4b..56cedc78ad 100644 --- a/packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md +++ b/packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md @@ -184,6 +184,7 @@ Every package below exports a Cordis plugin that a bundle patch can name in a Lo | `@deepseek-ai/dsh-experimental-browser-use-chrome-devtools-mcp` | yes | Experimental per-Session Chromium browser tools through chrome-devtools-mcp | | `@deepseek-ai/dsh-experimental-browser-use-playwright-mcp` | yes | Experimental per-Session Chromium browser tools through @playwright/mcp | | `@deepseek-ai/dsh-experimental-browser-use-stagehand-native` | yes | Experimental Stagehand browser tools with separately configured native models | +| `@deepseek-ai/dsh-experimental-claude-code-mods` | yes | Experimental bridge: load Claude Code mods (hooks modules) and run their hook chains on DeepSeek Harness extension points | | `@deepseek-ai/dsh-experimental-client-ui-agent-team` | no | Web Agent Teams roster, task board, and teammate navigation | | `@deepseek-ai/dsh-experimental-client-ui-voice-input` | no | Record speech and insert editable text into the conversation draft | | `@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp` | yes | Experimental computer use through an installed Cua Driver MCP executable | diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93011c30f4..20d8cd990d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7650,6 +7650,91 @@ importers: specifier: ^4.19.2 version: 4.22.4 + packages/experimental/claude-code-mods: + dependencies: + '@deepseek-ai/dsh-util-crypto': + specifier: workspace:* + version: link:../../util/crypto + '@deepseek-ai/dsh-util-values': + specifier: workspace:* + version: link:../../util/values + '@deepseek-ai/schemastery': + specifier: link:../../../vendor/schemastery + version: link:../../../vendor/schemastery + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@deepseek-ai/cordis': + specifier: workspace:~ + version: link:../../../vendor/cordis + '@deepseek-ai/cordis-plugin-include': + specifier: workspace:~ + version: link:../../../vendor/include + '@deepseek-ai/cordis-plugin-loader': + specifier: workspace:~ + version: link:../../../vendor/loader + '@deepseek-ai/dsh-agent': + specifier: workspace:* + version: link:../../core/agent + '@deepseek-ai/dsh-agent-loop': + specifier: workspace:* + version: link:../../core/agent-loop + '@deepseek-ai/dsh-agent-loop-testkit': + specifier: workspace:* + version: link:../../test-support/agent-loop-testkit + '@deepseek-ai/dsh-attachment': + specifier: workspace:* + version: link:../../attachment/attachment + '@deepseek-ai/dsh-commands': + specifier: workspace:* + version: link:../../interaction/commands + '@deepseek-ai/dsh-fs': + specifier: workspace:* + version: link:../../fs/fs + '@deepseek-ai/dsh-fs-local': + specifier: workspace:* + version: link:../../fs/fs-local + '@deepseek-ai/dsh-llm': + specifier: workspace:* + version: link:../../llm/llm + '@deepseek-ai/dsh-scope': + specifier: workspace:* + version: link:../../core/scope + '@deepseek-ai/dsh-session': + specifier: workspace:* + version: link:../../core/session + '@deepseek-ai/dsh-session-projection': + specifier: workspace:* + version: link:../../session/session-projection + '@deepseek-ai/dsh-storage': + specifier: workspace:* + version: link:../../storage/storage + '@deepseek-ai/dsh-storage-domain': + specifier: workspace:* + version: link:../../storage/storage-domain + '@deepseek-ai/dsh-storage-json': + specifier: workspace:* + version: link:../../storage/storage-json + '@deepseek-ai/dsh-subprocess': + specifier: workspace:* + version: link:../../subprocess/subprocess + '@deepseek-ai/dsh-subprocess-local': + specifier: workspace:* + version: link:../../subprocess/subprocess-local + '@deepseek-ai/dsh-system-prompt': + specifier: workspace:* + version: link:../../core/system-prompt + '@deepseek-ai/dsh-token-meter': + specifier: workspace:* + version: link:../../llm/token-meter + '@deepseek-ai/dsh-tools': + specifier: workspace:* + version: link:../../core/tools + '@deepseek-ai/dsh-user-questions': + specifier: workspace:* + version: link:../../interaction/user-questions + packages/experimental/client-ui-agent-team: devDependencies: '@deepseek-ai/cordis': diff --git a/tsconfig.base.json b/tsconfig.base.json index 4c90a7db9e..e88f44a248 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -304,6 +304,7 @@ "@deepseek-ai/dsh-sdk-jsonrpc-server": ["./packages/sdk/server/src"], "@deepseek-ai/dsh-experimental-agent-team": ["./packages/experimental/agent-team/src"], "@deepseek-ai/dsh-experimental-auto-review": ["./packages/experimental/auto-review/src"], + "@deepseek-ai/dsh-experimental-claude-code-mods": ["./packages/experimental/claude-code-mods/src"], "@deepseek-ai/dsh-experimental-agent-team-profile": ["./packages/experimental/agent-team-profile/src"], "@deepseek-ai/dsh-experimental-tool-agent-team": ["./packages/experimental/tool-agent-team/src"], "@deepseek-ai/dsh-experimental-webworker-runtime": ["./packages/experimental/webworker-runtime/src"], diff --git a/tsconfig.host.json b/tsconfig.host.json index fa944f9653..e1e3739d04 100644 --- a/tsconfig.host.json +++ b/tsconfig.host.json @@ -347,6 +347,7 @@ { "path": "./packages/interaction/user-approval" }, { "path": "./packages/interaction/permission-presets" }, { "path": "./packages/experimental/auto-review" }, + { "path": "./packages/experimental/claude-code-mods" }, { "path": "./packages/core/tools" }, { "path": "./packages/core/agent-tool-presentation" }, { "path": "./packages/skill/skill" },