feat(coding-agent): wire the filesystem tools into the demo
Load dsh-fs-local + dsh-fs-policy + dsh-tool-fs after tool-todo (mirroring the acp-agent wiring), and steer the system prompt to prefer read/write/edit for file ops with bash for shell/tests/search. Update the welcome line and the FIXME(config-comments) bash note. Doc sweep now that both demos ship the fs tools and the seam resolves per-session cwd: architecture.md and the event-gate RFC no longer say the demos do file ops through bash / that no config wires the tools; the coding-agent + examples READMEs and the AGENTS.md layout blurb list the fs tools; the acp-agent README drops the launch-dir caveat (per-session cwd now works, so the server can launch anywhere). (stdio-agent is single-session, so fs-local's cwd = process.cwd() is the workspace. Keyless boot smoke is blocked locally by an unrelated inotify watcher-limit ENOSPC that also hits demo:echo; the config parses and the same fs stack boots green in the acp-agent snapshot tier.)
This commit is contained in:
7 files changed
+35
-18
No files matched your search
@@ -115,10 +115,11 @@ examples/ Runnable demos (not workspaces; see examples/AGENTS.md). Each is a
|
||||
teaching plugins. The app package bundles the agent-core spine +
|
||||
front-door cluster + boot glue (a bin). No start.ts. echo-agent =
|
||||
mock model + echo tool on dsh-stdio-agent (pnpm run demo:echo, no
|
||||
key). coding-agent = the real thing: DeepSeek V4 + bash tools +
|
||||
subagent + todo_write on the same app (pnpm run demo:coding, needs
|
||||
DEEPSEEK_API_KEY). acp-agent = the coding agent as an ACP server on
|
||||
dsh-acp-agent (pnpm run demo:acp, needs DEEPSEEK_API_KEY).
|
||||
key). coding-agent = the real thing: DeepSeek V4 + fs tools
|
||||
(read/write/edit) + bash tools + subagent + todo_write on the same
|
||||
app (pnpm run demo:coding, needs DEEPSEEK_API_KEY). acp-agent = the
|
||||
coding agent as an ACP server on dsh-acp-agent (pnpm run demo:acp,
|
||||
needs DEEPSEEK_API_KEY).
|
||||
cordis.snapshot.yml = the acp leaf with llm-replay for keyless
|
||||
snapshot replay.
|
||||
docs/ architecture.md — the design doc. module-graph.md — generated
|
||||
|
||||
@@ -78,7 +78,7 @@ Swappable capabilities are split into **three packages** so each part evolves in
|
||||
|
||||
The LLM seam has the same topology folded differently: `dsh-llm` carries the interface (`LlmAdapter`) AND the consumer surface (`ctx.llm.stream()`), with adapters as implementation packages — there the consumer is the loop itself, not a swappable schema surface. Use the full three-package split when the consumer is independently replaceable; keep interface + consumer together when they are one concern. Don't split preemptively: a capability with one conceivable implementation and one consumer stays one package until proven otherwise.
|
||||
|
||||
The filesystem capability follows the bash topology with a fourth layer, but the policy is contributed through an **event gate**, not a method service: `dsh-fs` owns the abstract `ctx.fs` provider seam (text IO + atomic mutation primitives whose version guard is optional) and the `fs/*` policy event vocabulary, `dsh-fs-local` provides the local backend, `dsh-tool-fs` is the model-facing `read`/`write`/`edit` tools AND the executor (it reads/writes/edits through `ctx.fs` directly, owns read windowing, dispatches the `fs/*` events), and `dsh-fs-policy` is a policy PLUGIN (no service) that decides the `fs/write-intent`/`fs/edit-intent` waterfalls and records on `fs/observed` to add observed-state + read-before-edit + version-guarded write/edit. Because the tool is not method-coupled to the policy, dropping `dsh-fs-policy` gracefully loses the policy and leaves the unconstrained bare provider rather than breaking the tool at a service-injection boundary. The fs tools are not wired into any default/example config yet (the demo agents do file ops through bash); a deployment that loads `dsh-tool-fs` is expected to also load `dsh-fs-policy` so the default behavior is read-before-write/edit. See [the fs-policy event-gate RFC](rfc/implemented/architecture/2026-06-26-file-context-as-event-gate.md).
|
||||
The filesystem capability follows the bash topology with a fourth layer, but the policy is contributed through an **event gate**, not a method service: `dsh-fs` owns the abstract `ctx.fs` provider seam (text IO + atomic mutation primitives whose version guard is optional) and the `fs/*` policy event vocabulary, `dsh-fs-local` provides the local backend, `dsh-tool-fs` is the model-facing `read`/`write`/`edit` tools AND the executor (it reads/writes/edits through `ctx.fs` directly, owns read windowing, dispatches the `fs/*` events), and `dsh-fs-policy` is a policy PLUGIN (no service) that decides the `fs/write-intent`/`fs/edit-intent` waterfalls and records on `fs/observed` to add observed-state + read-before-edit + version-guarded write/edit. Because the tool is not method-coupled to the policy, dropping `dsh-fs-policy` gracefully loses the policy and leaves the unconstrained bare provider rather than breaking the tool at a service-injection boundary. The demo agents (`coding-agent`, `acp-agent`) wire the full stack — `dsh-fs-local` + `dsh-fs-policy` + `dsh-tool-fs` — so `read`/`write`/`edit` are the default file surface (bash stays for shell/tests/search); the tools resolve a relative path against the caller's session cwd, matching bash ([the per-session cwd RFC](rfc/implemented/architecture/2026-07-02-fs-per-session-cwd.md)). See [the fs-policy event-gate RFC](rfc/implemented/architecture/2026-06-26-file-context-as-event-gate.md).
|
||||
|
||||
> **"Capability" — two unrelated meanings.** (1) The *seam pattern* above ("one plugin provides a capability, another needs it") is realized by plain Cordis **services + `inject`**: a provider registers a service (`ctx.bash`, declared in `interface Context`); a consumer declares `inject: ['bash']` and its fiber stays pending until the service exists, tearing down via HMR if it later vanishes. No extra library is needed. (2) `@cordisjs/plugin-capability` is a different axis entirely — a **permission/capability-security** service (named permissions with inheritance/dependency, tested against a session via `ctx.capability.test`). It is a candidate for the deferred permissions/sandbox work (the `tools/execute` veto seam), NOT a mechanism for swapping implementations.
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ provider seam dsh-fs ctx.fs: text IO + ATOMIC mutation primitives who
|
||||
provider dsh-fs-local local implementation of ctx.fs
|
||||
```
|
||||
|
||||
The model is **additive, not subtractive**: `ctx.fs` on its own is a complete, unconstrained text-storage seam — `read` reads, `write` unconditionally creates-or-overwrites, `edit` unconditionally replaces literal text in the current content. There is no "先读后写", no version check, nothing to remove; the bare provider just does the I/O atomically. `dsh-fs-policy` is a plugin that *adds* constraints on top: observed-state, read-before-edit, and "write/edit must be based on the version you read". So removing `dsh-fs-policy` does not break `dsh-tool-fs` at the service-injection boundary; it removes the policy gate and leaves the bare provider behavior. The intended deployment stance is that a config loading the fs tools also loads `dsh-fs-policy`, so the user-facing behavior and prompt discipline are read-before-write/edit (no default/example config wires the fs tools yet — the demo agents do file ops through bash). The bare-provider mode exists because the tool should not be method-coupled to the policy plugin, not because an unconstrained filesystem is the normal product stance.
|
||||
The model is **additive, not subtractive**: `ctx.fs` on its own is a complete, unconstrained text-storage seam — `read` reads, `write` unconditionally creates-or-overwrites, `edit` unconditionally replaces literal text in the current content. There is no "先读后写", no version check, nothing to remove; the bare provider just does the I/O atomically. `dsh-fs-policy` is a plugin that *adds* constraints on top: observed-state, read-before-edit, and "write/edit must be based on the version you read". So removing `dsh-fs-policy` does not break `dsh-tool-fs` at the service-injection boundary; it removes the policy gate and leaves the bare provider behavior. The intended deployment stance is that a config loading the fs tools also loads `dsh-fs-policy`, so the user-facing behavior and prompt discipline are read-before-write/edit (the `coding-agent` and `acp-agent` demos wire the full stack). The bare-provider mode exists because the tool should not be method-coupled to the policy plugin, not because an unconstrained filesystem is the normal product stance.
|
||||
|
||||
`dsh-tool-fs` no longer injects `fileContext`. It injects `fs` and `tools`/`systemPrompt`.
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ Run with: `pnpm run demo:echo`. When prompted, type "echo <something>" to trigge
|
||||
|
||||
## coding-agent
|
||||
|
||||
The real thing: DeepSeek V4 + the bash tool suite, `subagent` delegation, and the `todo_write` task tracker on the same `@deepseek-ai/dsh-stdio-agent` app. Where echo-agent proves the skeleton with mocks, this is a usable coding assistant.
|
||||
The real thing: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite, `subagent` delegation, and the `todo_write` task tracker on the same `@deepseek-ai/dsh-stdio-agent` app. Where echo-agent proves the skeleton with mocks, this is a usable coding assistant.
|
||||
|
||||
Run with: `pnpm run demo:coding` (needs `DEEPSEEK_API_KEY` in the environment or a gitignored repo-root `.env`). See [coding-agent/README.md](coding-agent/README.md) for details.
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Add to your Zed `settings.json` under `agent_servers`:
|
||||
}
|
||||
```
|
||||
|
||||
The editor sets each session's `cwd` to the project it opens; the agent's bash tools run there (see the per-session `cwd` note in `packages/ui/acp`). The filesystem tools in this demo use the local filesystem backend and resolve relative paths from the server launch directory, so launch the server from the harness repo with `pnpm --dir …` when using `read`/`write`/`edit` against this checkout.
|
||||
The editor sets each session's `cwd` to the project it opens; both the agent's bash tools and the `read`/`write`/`edit` filesystem tools resolve relative paths against that per-session workspace (see the per-session `cwd` note in `packages/ui/acp` and [the per-session cwd RFC](../../docs/rfc/implemented/architecture/2026-07-02-fs-per-session-cwd.md)), so the server can be launched anywhere and each session still acts on its own project directory.
|
||||
|
||||
## Snapshot tests (record-once / replay-deterministic)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# coding-agent
|
||||
|
||||
The real stdio coding-agent wiring: DeepSeek V4 + the bash tool suite + subagent delegation + `todo_write` + stdio chat + JSONL persistence, loaded from `cordis.yml`. Where echo-agent proves the skeleton with mocks, this example is a usable coding assistant.
|
||||
The real stdio coding-agent wiring: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite + subagent delegation + `todo_write` + stdio chat + JSONL persistence, loaded from `cordis.yml`. Where echo-agent proves the skeleton with mocks, this example is a usable coding assistant.
|
||||
|
||||
## Run it
|
||||
|
||||
@@ -11,7 +11,7 @@ The real stdio coding-agent wiring: DeepSeek V4 + the bash tool suite + subagent
|
||||
pnpm run demo:coding
|
||||
```
|
||||
|
||||
Type a coding task. The agent works through `bash` (+ `bash_output` / `bash_kill` for background tasks): file reads, writes, searches, and test runs all happen through shell commands, each in a fresh `bash -c` (the system prompt tells the model to pass `workdir` instead of `cd`). It can also delegate with `subagent`/`subagent_fork` and track multi-step work with `todo_write` (a whole-list task tracker rendered as a checklist). Reasoning streams dimmed; tool calls/results render inline.
|
||||
Type a coding task. The agent works through the `read`/`write`/`edit` filesystem tools for ordinary file operations and `bash` (+ `bash_output` / `bash_kill` for background tasks) for shell commands, searches, and test runs, each in a fresh `bash -c` (the system prompt tells the model to pass `workdir` instead of `cd`). Both the fs tools and bash resolve relative paths against the session workspace. It can also delegate with `subagent`/`subagent_fork` and track multi-step work with `todo_write` (a whole-list task tracker rendered as a checklist). Reasoning streams dimmed; tool calls/results render inline.
|
||||
|
||||
```
|
||||
> fix the failing test in /path/to/project
|
||||
@@ -44,6 +44,7 @@ This example is a thin leaf `cordis.yml`: it picks the swappable backends, loads
|
||||
| `subagent`, `subagent-spawn`, `subagent-fork` | the subagent provider registry plus the two in-process backends: a fresh child and a child seeded with the parent's completed-turn prefix |
|
||||
| `tool-subagent`, `tool-subagent-fork` | two model-facing `dsh-tool-subagent` loads, each bound to a different provider and exposed under a distinct tool name (`subagent`, `subagent_fork`) |
|
||||
| `tool-todo` | the model-facing `todo_write` tool; writes the whole task list to the session log and renders as a checklist in stdio |
|
||||
| `fs-local`, `fs-policy`, `tool-fs` | the filesystem stack: the local `ctx.fs` provider, the read-before-write/edit policy gate (on the `fs/*` event gate), and the model-facing `read`/`write`/`edit` tools. Relative paths resolve against the session workspace |
|
||||
|
||||
## End-to-end tests (`pnpm run test:e2e`, key-gated)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
# Local bash executor for agent-core's tool-bash schema.
|
||||
# FIXME(config-comments): keep this executor note from implying bash is the
|
||||
# whole tool set; subagent and todo_write are loaded below.
|
||||
# whole tool set; filesystem, subagent, and todo_write are loaded below.
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
@@ -46,16 +46,17 @@
|
||||
# under ./.sessions); unset starts a fresh session each run.
|
||||
resumeSessionId: !!js process.env.RESUME_SESSION_ID
|
||||
persistenceRoot: './.sessions'
|
||||
welcome: 'coding-agent ready. Give it a coding task (its tools are bash, subagent, and todo_write).'
|
||||
welcome: 'coding-agent ready. Give it a coding task (its tools are read, write, edit, bash, subagent, and todo_write).'
|
||||
systemPrompt: |
|
||||
You are coding-agent, a CLI coding assistant.
|
||||
|
||||
Your tools are bash (plus bash_output/bash_kill for background
|
||||
tasks) and subagent. Do ALL file operations through bash: read with
|
||||
cat/sed/head, search with grep, write with heredocs (cat <<'EOF' >
|
||||
file), edit with sed or a rewrite. Each bash call runs in a fresh
|
||||
shell — pass workdir instead of cd, and never rely on shell state
|
||||
between calls.
|
||||
Your tools are read/write/edit for file operations, bash (plus
|
||||
bash_output/bash_kill for background tasks), and subagent. Use read to
|
||||
inspect UTF-8 text files, write to create or replace files, and edit for
|
||||
targeted literal replacements. Use bash for shell commands, tests,
|
||||
searches, and operations that are not ordinary file reads or edits. Each
|
||||
bash call runs in a fresh shell — pass workdir instead of cd, and never
|
||||
rely on shell state between calls.
|
||||
|
||||
Use the subagent tool to delegate a focused, self-contained subtask
|
||||
to a fresh child agent (it works in its own context and returns only
|
||||
@@ -123,3 +124,17 @@
|
||||
# session log (todo/write), rendered as a stdio checklist / ACP plan.
|
||||
- id: tool-todo
|
||||
name: '@deepseek-ai/dsh-tool-todo'
|
||||
|
||||
# Filesystem capability stack: local provider, read-before-write/edit policy
|
||||
# gate, then the model-facing read/write/edit tools. stdio-agent is a single
|
||||
# session, so relative paths resolve from the process cwd (the workspace).
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
|
||||
- id: tool-fs
|
||||
name: '@deepseek-ai/dsh-tool-fs'
|
||||
Reference in New Issue
Block a user