diff --git a/AGENTS.md b/AGENTS.md index 1ef6af2ba0..c272ad5dda 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,6 +20,7 @@ packages/ Harness packages at packages///, all named @deepseek-ai subagent/ subagent seam + spawn/fork/ACP backends + delegation tool todo/ the todo_write tool guard/ loop-hygiene plugins + cordis/ self-referential toolset: the agent inspects/mounts plugins in its own runtime hooks/ Claude Code / Codex hook bridges + shared wire-protocol library session-persistence/ persistence seam + JSONL/SQLite backends ui/ ACP bridge + app-boot glue + the stdio/ACP app bins @@ -48,6 +49,7 @@ pnpm run hygiene # knip + publint + workspace constraints + NodeNext cons pnpm run doc-sync # all documentation gates; see the doc-sync script in package.json pnpm run demo:echo # mock-model REPL, no key needed pnpm run demo:repl # real REPL coding agent (needs DEEPSEEK_API_KEY) +pnpm run demo:cordis # self-referential demo: the agent modifies its own runtime (needs key) pnpm run demo:acp # ACP server agent (needs DEEPSEEK_API_KEY) ``` diff --git a/docs/capability-seams.md b/docs/capability-seams.md index 12ce8033b9..b85fa40305 100644 --- a/docs/capability-seams.md +++ b/docs/capability-seams.md @@ -31,6 +31,7 @@ flowchart LR pkg_tool_web["tool-web"] svc_tools["ctx.tools
Tool registry and execution waterfall"] pkg_tool_bash["tool-bash"] + pkg_tool_cordis["tool-cordis"] pkg_tool_subagent["tool-subagent"] pkg_tool_todo["tool-todo"] svc_agents["ctx.agents
Agent registry"] @@ -121,6 +122,7 @@ flowchart LR svc_tools --> pkg_acp svc_tools --> pkg_agent_loop svc_tools --> pkg_tool_bash + svc_tools --> pkg_tool_cordis svc_tools --> pkg_tool_fs svc_tools --> pkg_tool_subagent svc_tools --> pkg_tool_todo @@ -135,7 +137,7 @@ flowchart LR | `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session-persistence/session-persistence), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`invariants`](../packages/support/invariants) | - | Owns append-only Session instances and emits the durable session event feed. | | `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session-persistence/session-persistence) | [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/ui/acp) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. | | `ctx.systemPrompt` | `core` | [`system-prompt`](../packages/core/system-prompt) | - | [`agent-loop`](../packages/core/agent-loop), [`tools`](../packages/core/tools), [`tool-fs`](../packages/fs/tool-fs), [`tool-web`](../packages/web/tool-web) | - | Collects prompt sections and model-facing tool schemas for each step. | -| `ctx.tools` | `core` | [`tools`](../packages/core/tools) | - | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/bash/tool-bash), [`tool-fs`](../packages/fs/tool-fs), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-todo`](../packages/todo/tool-todo), [`tool-web`](../packages/web/tool-web), [`acp`](../packages/ui/acp) | - | Registers tool definitions, exposes schemas to the prompt, and routes calls through tools/pre-execute and tools/post-execute. | +| `ctx.tools` | `core` | [`tools`](../packages/core/tools) | - | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/bash/tool-bash), [`tool-cordis`](../packages/cordis/tool-cordis), [`tool-fs`](../packages/fs/tool-fs), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-todo`](../packages/todo/tool-todo), [`tool-web`](../packages/web/tool-web), [`acp`](../packages/ui/acp) | - | Registers tool definitions, exposes schemas to the prompt, and routes calls through tools/pre-execute and tools/post-execute. | | `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/ui/acp), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`stdio-agent`](../packages/ui/stdio-agent), [`invariants`](../packages/support/invariants) | - | Owns live Agent handles and the create/resume factory seam. | | `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-core`](../packages/core/agent-core) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. | | `ctx.bash` | `seam` | [`bash`](../packages/bash/bash) | [`bash-local`](../packages/bash/bash-local) | [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) | - | The model-facing bash tools and hook bridges consume this seam; sandboxed or remote executors can replace bash-local. | diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 2429fe3163..d466bff4c0 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -670,6 +670,24 @@ export interface Config { Source: [`packages/core/system-prompt/src/index.ts:179`](../packages/core/system-prompt/src/index.ts) +## `@deepseek-ai/dsh-tool-cordis` + +Requires: `tools` + +```ts config-catalog +/** Config for the tool-cordis plugin: the sandbox evaluation bound. */ +export interface Config { + /** + * Milliseconds the SYNCHRONOUS portion of mount code may run in the vm + * before evaluation is aborted (default 5000). An async body escapes this + * bound — see docs/rfc/implemented/feature/2026-07-08-self-referential-cordis-toolset.md for the trust stance. + */ + vmTimeoutMs?: number +} +``` + +Source: [`packages/cordis/tool-cordis/src/index.ts:49`](../packages/cordis/tool-cordis/src/index.ts) + ## `@deepseek-ai/dsh-tool-fs` Requires: `tools` · `fs` · `systemPrompt` diff --git a/docs/graph-atlas.md b/docs/graph-atlas.md index 2a3d674bec..60de01ef81 100644 --- a/docs/graph-atlas.md +++ b/docs/graph-atlas.md @@ -14,6 +14,7 @@ The process decision behind this index is recorded in [the documentation graph R | [capability seams and core services](capability-seams.md) | `hybrid generated` | | [echo-agent app composition](../examples/echo-agent/composition.md) | `hybrid generated` | | [coding-agent app composition](../examples/coding-agent/composition.md) | `hybrid generated` | +| [cordis-agent app composition](../examples/cordis-agent/composition.md) | `hybrid generated` | | [acp-agent app composition](../examples/acp-agent/composition.md) | `hybrid generated` | | [event producer/consumer matrix](event-producer-consumer.md) | `hybrid generated` | | [agent turn and step lifecycle](agent-lifecycle.md) | `curated` | diff --git a/docs/module-graph.md b/docs/module-graph.md index 1e4e70151f..beabf14c69 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -61,6 +61,9 @@ flowchart TD subgraph group_todo["packages/todo"] pkg_tool_todo["tool-todo"] end + subgraph group_cordis["packages/cordis"] + pkg_tool_cordis["tool-cordis"] + end subgraph group_hooks["packages/hooks"] pkg_hook_protocol["hook-protocol"] pkg_hooks_claude["hooks-claude"] @@ -164,6 +167,7 @@ flowchart TD pkg_tool_todo --> pkg_agent pkg_tool_todo --> pkg_session pkg_tool_todo --> pkg_tools + pkg_tool_cordis --> pkg_tools pkg_hooks_codex --> pkg_agent pkg_hooks_codex --> pkg_hook_protocol pkg_hooks_codex --> pkg_llm @@ -264,6 +268,7 @@ flowchart TD | [`tool-web`](../packages/web/tool-web) | `web` | [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`web`](../packages/web/web) | | [`timeout-policy`](../packages/timeout/timeout-policy) | `timeout` | [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) | | [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`tools`](../packages/core/tools) | +| [`tool-cordis`](../packages/cordis/tool-cordis) | `cordis` | [`tools`](../packages/core/tools) | | [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools) | | [`acp`](../packages/ui/acp) | `ui` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`tools`](../packages/core/tools) | | [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) | `guard` | [`agent`](../packages/core/agent), [`tools`](../packages/core/tools) | diff --git a/examples/AGENTS.md b/examples/AGENTS.md index 6c1cc717df..94597371cf 100644 --- a/examples/AGENTS.md +++ b/examples/AGENTS.md @@ -21,6 +21,7 @@ A keyless smoke that spawns the example from a temp cwd must set `TSX_TSCONFIG_P |---|---|---| | `echo-agent` | `tests/echo.e2e.ts` — boots the real `cordis.yml`, drives the echo tool round-trip and the direct canned reply | **N/A — keyless by nature** (the `mock-echo` model has no real provider) | | `coding-agent` | `tests/keyless-smoke.e2e.ts` — boots the full real tree (dummy key, no prompt → no model call), asserts banner + clean exit | `tests/{full-loop,coding-task,resume,compaction,todo-write}.e2e.ts` — real model + real bash + real todo_write, world-verified | +| `cordis-agent` | `tests/keyless-smoke.e2e.ts` — boots the real tree incl. `@deepseek-ai/dsh-tool-cordis` by package name; the tool logic is unit-tested in `packages/cordis/tool-cordis` | `tests/cordis-tools.e2e.ts` — real model mounts a listener (tagged line fires), builds+calls its own tool, composes two mounts via provide/inject | | `acp-agent` | `pnpm run test:snapshot` — boots the real ACP subprocess and replays a recorded session keyless (incl. the hook matrix: a scenario per hook point × outcome for BOTH the Claude and Codex bridges — block, deny, ask, context-fold, force-continue); `tests/acp.e2e.ts` also asserts stdout purity without a key | `tests/acp.e2e.ts` — real ACP prompt, verifies a file the agent wrote; `tests/hooks.e2e.ts` — a real `PreToolUse` hook blocks bash, verifies the file is NOT written | See [the root AGENTS.md](../AGENTS.md) for repo-wide conventions and [docs/architecture.md](../docs/architecture.md) for the design. diff --git a/examples/README.md b/examples/README.md index 1e3134ba2d..c308df73e1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -19,6 +19,12 @@ A REPL agent demo: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + th Run with: `pnpm run demo:repl` (needs `DEEPSEEK_API_KEY` in the environment or a gitignored repo-root `.env`). See [coding-agent/README.md](coding-agent/README.md) for details. +## cordis-agent + +The **self-referential** demo: the coding spine plus [`@deepseek-ai/dsh-tool-cordis`](../packages/cordis/tool-cordis), whose three tools (`cordis_inspect` / `cordis_mount` / `cordis_unmount`) let the agent inspect the live cordis runtime it runs inside, mount model-written plugins into it (an event listener, a brand-new tool for itself, or a service another mount injects), and dispose them again — all dynamic mounts grouped under one `cordis-dynamic` fiber subtree. + +Run with: `pnpm run demo:cordis` (needs `DEEPSEEK_API_KEY`). See [cordis-agent/README.md](cordis-agent/README.md) for the staged demo script and [the toolset RFC](../docs/rfc/implemented/feature/2026-07-08-self-referential-cordis-toolset.md) for the design and sandbox caveats. + ## acp-agent An agent demo exposed as an **Agent Client Protocol (ACP)** server over JSON-RPC stdio, via the [`@deepseek-ai/dsh-acp-agent`](../packages/ui/acp-agent) app — drive it from Zed or any other ACP client. Also the home of the keyless snapshot tests. diff --git a/examples/cordis-agent/composition.md b/examples/cordis-agent/composition.md new file mode 100644 index 0000000000..6822129c26 --- /dev/null +++ b/examples/cordis-agent/composition.md @@ -0,0 +1,40 @@ + + +# Cordis Agent App Composition + +The self-referential demo puts @deepseek-ai/dsh-tool-cordis on the coding spine, letting the agent inspect its own runtime and mount/unmount plugins into it. + +```mermaid +flowchart LR + cfg["examples/cordis-agent
cordis.yml"] + plugin_cordis_hmr["hmr
@cordisjs/plugin-hmr"] + cfg --> plugin_cordis_hmr + plugin_cordis_llm_deepseek["llm-deepseek
@deepseek-ai/dsh-llm-deepseek"] + cfg --> plugin_cordis_llm_deepseek + plugin_cordis_bash["bash
@deepseek-ai/dsh-bash-local"] + cfg --> plugin_cordis_bash + plugin_cordis_stdio_agent["stdio-agent
@deepseek-ai/dsh-stdio-agent"] + cfg --> plugin_cordis_stdio_agent + plugin_cordis_stdio_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-core"] + plugin_cordis_stdio_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"] + plugin_cordis_stdio_agent --> frontdoor_stdio["readline UI
console logger
pre-created main agent"] + bundle_agent_core --> spine_llm["ctx.llm"] + bundle_agent_core --> spine_sessions["ctx.sessions"] + bundle_agent_core --> spine_tools["ctx.tools + tool-bash"] + bundle_agent_core --> spine_loop["ctx.agents + ctx.agentLoop"] + plugin_cordis_tool_cordis["tool-cordis
@deepseek-ai/dsh-tool-cordis"] + cfg --> plugin_cordis_tool_cordis +``` + +| Plugin id | Package / module | +| --- | --- | +| `hmr` | `@cordisjs/plugin-hmr` | +| `llm-deepseek` | `@deepseek-ai/dsh-llm-deepseek` | +| `bash` | `@deepseek-ai/dsh-bash-local` | +| `stdio-agent` | `@deepseek-ai/dsh-stdio-agent` | +| `tool-cordis` | `@deepseek-ai/dsh-tool-cordis` | + +Source config: [`examples/cordis-agent/cordis.yml`](cordis.yml). + +Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source. diff --git a/knip.json b/knip.json index 407f892775..198cad3cce 100644 --- a/knip.json +++ b/knip.json @@ -8,6 +8,7 @@ "examples/echo-agent/src/*.ts", "examples/echo-agent/tests/**/*.e2e.ts", "examples/coding-agent/tests/**/*.e2e.ts", + "examples/cordis-agent/tests/**/*.e2e.ts", "examples/acp-agent/tests/**/*.e2e.ts", "examples/*/tests/**/*.snapshot.ts" ], diff --git a/packages/README.md b/packages/README.md index bc09826777..d7c3c4caf1 100644 --- a/packages/README.md +++ b/packages/README.md @@ -19,6 +19,7 @@ Packages are grouped by modular role at `packages///`. The group dir | [`timeout/`](timeout/README.md) | Tool-call timeout policy: the `tools/execute` deadline enforcer | Product — stable surface | | [`todo/`](todo/README.md) | Todo/planning family: the model-facing `todo_write` tool | Product — stable surface | | [`guard/`](guard/README.md) | Loop-hygiene guards: advisory repeat-call reminders | Product — stable surface | +| [`cordis/`](cordis/README.md) | Self-referential runtime toolset: inspect the live runtime's plugins and services, mount/unmount model-written plugins ([design](../docs/rfc/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)) | Product — stable surface | | [`hooks/`](hooks/README.md) | Hook bridges + the shared Claude Code / Codex wire-protocol library | Product — stable surface | | [`session-persistence/`](session-persistence/README.md) | Persistence capability family: the seam + JSONL/SQLite backends | Product — stable surface | | [`ui/`](ui/README.md) | Editor/client integration surfaces (the ACP bridge) + the app packages | Product — stable surface | diff --git a/scripts/doc-budgets.manifest.json b/scripts/doc-budgets.manifest.json index 337fc57763..8cadde12e9 100644 --- a/scripts/doc-budgets.manifest.json +++ b/scripts/doc-budgets.manifest.json @@ -1,11 +1,11 @@ { - "AGENTS.md": 1691, + "AGENTS.md": 1802, "docs/AGENTS.md": 1315, "docs/architecture.md": 1640, "docs/cordis-primer.md": 550, "docs/defensive-patterns.md": 550, "docs/testing.md": 800, - "examples/AGENTS.md": 610, + "examples/AGENTS.md": 653, "packages/AGENTS.md": 450, "packages/README.md": 610 } diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts index 14396a6645..d8d320df33 100644 --- a/scripts/gen-doc-graphs.ts +++ b/scripts/gen-doc-graphs.ts @@ -75,6 +75,7 @@ const GROUP_ORDER = [ 'subagent', 'web', 'todo', + 'cordis', 'hooks', 'session-persistence', 'support', @@ -121,7 +122,7 @@ const SERVICE_ROLES: ServiceRole[] = [ pkg: 'tools', title: 'Tool registry and execution waterfall', mode: 'core', - consumers: ['agent-loop', 'tool-bash', 'tool-fs', 'tool-subagent', 'tool-todo', 'tool-web', 'acp'], + consumers: ['agent-loop', 'tool-bash', 'tool-cordis', 'tool-fs', 'tool-subagent', 'tool-todo', 'tool-web', 'acp'], note: 'Registers tool definitions, exposes schemas to the prompt, and routes calls through tools/pre-execute and tools/post-execute.', }, { @@ -409,6 +410,14 @@ const APP_EXAMPLES = [ config: 'examples/coding-agent/cordis.yml', summary: 'The coding REPL demo adds the real DeepSeek adapter, filesystem tools, todo_write, compaction, and both subagent transports on top of the stdio app package.', }, + { + id: 'cordis', + rel: 'examples/cordis-agent/composition.md', + title: 'Cordis Agent App Composition', + label: 'examples/cordis-agent', + config: 'examples/cordis-agent/cordis.yml', + summary: 'The self-referential demo puts @deepseek-ai/dsh-tool-cordis on the coding spine, letting the agent inspect its own runtime and mount/unmount plugins into it.', + }, { id: 'acp', rel: 'examples/acp-agent/composition.md', @@ -718,6 +727,7 @@ function renderIndex(docs: GraphDoc[]): string { 'docs/capability-seams.md': 'capability seams and core services', 'examples/echo-agent/composition.md': 'echo-agent app composition', 'examples/coding-agent/composition.md': 'coding-agent app composition', + 'examples/cordis-agent/composition.md': 'cordis-agent app composition', 'examples/acp-agent/composition.md': 'acp-agent app composition', 'docs/event-producer-consumer.md': 'event producer/consumer matrix', 'docs/agent-lifecycle.md': 'agent turn and step lifecycle', @@ -728,6 +738,7 @@ function renderIndex(docs: GraphDoc[]): string { 'docs/capability-seams.md': 'hybrid generated', 'examples/echo-agent/composition.md': 'hybrid generated', 'examples/coding-agent/composition.md': 'hybrid generated', + 'examples/cordis-agent/composition.md': 'hybrid generated', 'examples/acp-agent/composition.md': 'hybrid generated', 'docs/event-producer-consumer.md': 'hybrid generated', 'docs/agent-lifecycle.md': 'curated', diff --git a/scripts/gen-module-graph.ts b/scripts/gen-module-graph.ts index fe39d325f8..dc66dc843e 100644 --- a/scripts/gen-module-graph.ts +++ b/scripts/gen-module-graph.ts @@ -47,6 +47,7 @@ const GROUP_ORDER = [ 'web', 'timeout', 'todo', + 'cordis', 'hooks', 'session-persistence', 'support',