deepseek-harness/examples/echo-agent/README.md

35 lines
2.3 KiB
Markdown
Raw Normal View History

Document the codebase thoroughly and tighten type safety Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2026-06-11 12:39:27 +08:00
# echo-agent
refactor(examples): extract the app spine into dsh-agent-core + app packages Implements docs/rfc/.../2026-06-20-extract-example-app-packages.md. Each example was thick — a hand-rolled start.ts, an infra preamble, nested base.yml/base-core.yml/acp-tail.yml includes, and a coupled front-door cluster enforced only by prose. This moves the composition into packages so each example is a thin leaf cordis.yml: pick the swappable backends, load one app package. New packages: - @deepseek-ai/dsh-agent-core (packages/core/agent-core): one bundle plugin that loads the providerless/executor-less/UI-less spine (timer + llm + sessions + system-prompt + tools + agents + invariants + tool-bash + agent-loop) via ctx.plugin(...) inside apply(), and forwards agent-loop's `agents` list as its own Config (export const Config = AgentLoop.Config, default []). - @deepseek-ai/dsh-stdio-agent (packages/ui/stdio-agent): terminal chat APP — agent-core + console logger + readline UI + a pre-created `main` agent, with a bin. The demo:echo/coding front door. - @deepseek-ai/dsh-acp-agent (packages/ui/acp-agent): ACP server APP — agent-core + JSONL persistence + the acp bridge, NO stdout logger, with a bin. The stdout-purity footgun is structurally unreachable from the leaf. Amendment to the RFC: hmr stays a LEAF cordis.yml entry, not baked into dsh-stdio-agent. hmr is a Loader-only dev plugin (throws without --expose-internals; the in-process test tier can't even import its decorator form), so a package statically importing it could never carry the per-file coverage gate. Unlike the console logger, a stray hmr is not a stdout-purity footgun, so leaving it at the leaf costs no safety. With hmr out, all three new packages carry in-process unit specs at 100%. Boot glue (Loader tail, .env load, snapshot-mode selection, stdin-dispose lifecycle) moves into each app's bin; start.ts and base.yml/base-core.yml/ acp-tail.yml are deleted. Each app package gets a keyless real-load-path test that boots through its bin + the cordis Loader (guarding the unwrapExports export-shape bug class, postmortem 0001). ACP snapshot replay stays green against the existing committed goldens (pure boot restructuring). RFC moved proposed->implemented with the amendment recorded; package/example/architecture docs and the module graph updated.
2026-06-21 12:03:44 +08:00
Runnable demo: stdin chat with a scripted mock model and an echo tool. The all-mock skeleton — "swap the backend, keep the app".
Document the codebase thoroughly and tighten type safety Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2026-06-11 12:39:27 +08:00
## What it shows
refactor(examples): extract the app spine into dsh-agent-core + app packages Implements docs/rfc/.../2026-06-20-extract-example-app-packages.md. Each example was thick — a hand-rolled start.ts, an infra preamble, nested base.yml/base-core.yml/acp-tail.yml includes, and a coupled front-door cluster enforced only by prose. This moves the composition into packages so each example is a thin leaf cordis.yml: pick the swappable backends, load one app package. New packages: - @deepseek-ai/dsh-agent-core (packages/core/agent-core): one bundle plugin that loads the providerless/executor-less/UI-less spine (timer + llm + sessions + system-prompt + tools + agents + invariants + tool-bash + agent-loop) via ctx.plugin(...) inside apply(), and forwards agent-loop's `agents` list as its own Config (export const Config = AgentLoop.Config, default []). - @deepseek-ai/dsh-stdio-agent (packages/ui/stdio-agent): terminal chat APP — agent-core + console logger + readline UI + a pre-created `main` agent, with a bin. The demo:echo/coding front door. - @deepseek-ai/dsh-acp-agent (packages/ui/acp-agent): ACP server APP — agent-core + JSONL persistence + the acp bridge, NO stdout logger, with a bin. The stdout-purity footgun is structurally unreachable from the leaf. Amendment to the RFC: hmr stays a LEAF cordis.yml entry, not baked into dsh-stdio-agent. hmr is a Loader-only dev plugin (throws without --expose-internals; the in-process test tier can't even import its decorator form), so a package statically importing it could never carry the per-file coverage gate. Unlike the console logger, a stray hmr is not a stdout-purity footgun, so leaving it at the leaf costs no safety. With hmr out, all three new packages carry in-process unit specs at 100%. Boot glue (Loader tail, .env load, snapshot-mode selection, stdin-dispose lifecycle) moves into each app's bin; start.ts and base.yml/base-core.yml/ acp-tail.yml are deleted. Each app package gets a keyless real-load-path test that boots through its bin + the cordis Loader (guarding the unwrapExports export-shape bug class, postmortem 0001). ACP snapshot replay stays green against the existing committed goldens (pure boot restructuring). RFC moved proposed->implemented with the amendment recorded; package/example/architecture docs and the module graph updated.
2026-06-21 12:03:44 +08:00
This example is just a leaf `cordis.yml`: it loads the [`@deepseek-ai/dsh-stdio-agent`](../../packages/ui/stdio-agent) app (which bundles the whole [`@deepseek-ai/dsh-agent-core`](../../packages/core/agent-core) spine, the console logger, JSONL persistence, the readline UI, and a pre-created `main` agent), and swaps in two example-local backends plus `hmr`:
- `mock-llm.ts` — a mock `LlmAdapter` that streams scripted responses and calls the `echo` tool when the user types "echo <something>". Registered with `ctx.llm.registerAdapter(['mock-echo'], …)`.
- `echo-tool.ts` — a tool registered via `ctx.tools.register(defineTool(…))` with typed `execute` args; echoes text back uppercased.
Swapping `mock-llm` for the real `llm-deepseek` adapter is all that separates this from `coding-agent` — the same app, a different backend.
Document the codebase thoroughly and tighten type safety Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2026-06-11 12:39:27 +08:00
## Plugin files
| File | Role | Key patterns demonstrated |
|---|---|---|
refactor(examples): extract the app spine into dsh-agent-core + app packages Implements docs/rfc/.../2026-06-20-extract-example-app-packages.md. Each example was thick — a hand-rolled start.ts, an infra preamble, nested base.yml/base-core.yml/acp-tail.yml includes, and a coupled front-door cluster enforced only by prose. This moves the composition into packages so each example is a thin leaf cordis.yml: pick the swappable backends, load one app package. New packages: - @deepseek-ai/dsh-agent-core (packages/core/agent-core): one bundle plugin that loads the providerless/executor-less/UI-less spine (timer + llm + sessions + system-prompt + tools + agents + invariants + tool-bash + agent-loop) via ctx.plugin(...) inside apply(), and forwards agent-loop's `agents` list as its own Config (export const Config = AgentLoop.Config, default []). - @deepseek-ai/dsh-stdio-agent (packages/ui/stdio-agent): terminal chat APP — agent-core + console logger + readline UI + a pre-created `main` agent, with a bin. The demo:echo/coding front door. - @deepseek-ai/dsh-acp-agent (packages/ui/acp-agent): ACP server APP — agent-core + JSONL persistence + the acp bridge, NO stdout logger, with a bin. The stdout-purity footgun is structurally unreachable from the leaf. Amendment to the RFC: hmr stays a LEAF cordis.yml entry, not baked into dsh-stdio-agent. hmr is a Loader-only dev plugin (throws without --expose-internals; the in-process test tier can't even import its decorator form), so a package statically importing it could never carry the per-file coverage gate. Unlike the console logger, a stray hmr is not a stdout-purity footgun, so leaving it at the leaf costs no safety. With hmr out, all three new packages carry in-process unit specs at 100%. Boot glue (Loader tail, .env load, snapshot-mode selection, stdin-dispose lifecycle) moves into each app's bin; start.ts and base.yml/base-core.yml/ acp-tail.yml are deleted. Each app package gets a keyless real-load-path test that boots through its bin + the cordis Loader (guarding the unwrapExports export-shape bug class, postmortem 0001). ACP snapshot replay stays green against the existing committed goldens (pure boot restructuring). RFC moved proposed->implemented with the amendment recorded; package/example/architecture docs and the module graph updated.
2026-06-21 12:03:44 +08:00
| `src/mock-llm.ts` | `LlmAdapter` registration | `ctx.llm.registerAdapter(['mock-echo'], …)`, streaming chunks with the proper `block-start`/`block-end` protocol |
| `src/echo-tool.ts` | Tool registration | `ctx.tools.register(defineTool(…))` with typed `execute` args, returning `ContentBlock[]` |
| `cordis.yml` | Leaf wiring | the two backends + `hmr` + one `@deepseek-ai/dsh-stdio-agent` entry carrying the app config |
Document the codebase thoroughly and tighten type safety Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2026-06-11 12:39:27 +08:00
refactor(examples): extract the app spine into dsh-agent-core + app packages Implements docs/rfc/.../2026-06-20-extract-example-app-packages.md. Each example was thick — a hand-rolled start.ts, an infra preamble, nested base.yml/base-core.yml/acp-tail.yml includes, and a coupled front-door cluster enforced only by prose. This moves the composition into packages so each example is a thin leaf cordis.yml: pick the swappable backends, load one app package. New packages: - @deepseek-ai/dsh-agent-core (packages/core/agent-core): one bundle plugin that loads the providerless/executor-less/UI-less spine (timer + llm + sessions + system-prompt + tools + agents + invariants + tool-bash + agent-loop) via ctx.plugin(...) inside apply(), and forwards agent-loop's `agents` list as its own Config (export const Config = AgentLoop.Config, default []). - @deepseek-ai/dsh-stdio-agent (packages/ui/stdio-agent): terminal chat APP — agent-core + console logger + readline UI + a pre-created `main` agent, with a bin. The demo:echo/coding front door. - @deepseek-ai/dsh-acp-agent (packages/ui/acp-agent): ACP server APP — agent-core + JSONL persistence + the acp bridge, NO stdout logger, with a bin. The stdout-purity footgun is structurally unreachable from the leaf. Amendment to the RFC: hmr stays a LEAF cordis.yml entry, not baked into dsh-stdio-agent. hmr is a Loader-only dev plugin (throws without --expose-internals; the in-process test tier can't even import its decorator form), so a package statically importing it could never carry the per-file coverage gate. Unlike the console logger, a stray hmr is not a stdout-purity footgun, so leaving it at the leaf costs no safety. With hmr out, all three new packages carry in-process unit specs at 100%. Boot glue (Loader tail, .env load, snapshot-mode selection, stdin-dispose lifecycle) moves into each app's bin; start.ts and base.yml/base-core.yml/ acp-tail.yml are deleted. Each app package gets a keyless real-load-path test that boots through its bin + the cordis Loader (guarding the unwrapExports export-shape bug class, postmortem 0001). ACP snapshot replay stays green against the existing committed goldens (pure boot restructuring). RFC moved proposed->implemented with the amendment recorded; package/example/architecture docs and the module graph updated.
2026-06-21 12:03:44 +08:00
The spine, UI, persistence, and boot glue all live in `@deepseek-ai/dsh-stdio-agent` and the bundle it loads — this folder holds only the demo-specific mocks and the leaf wiring.
Document the codebase thoroughly and tighten type safety Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2026-06-11 12:39:27 +08:00
## Run
```sh
2026-06-16 14:55:37 +08:00
pnpm run demo:echo
Document the codebase thoroughly and tighten type safety Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2026-06-11 12:39:27 +08:00
# or:
refactor(examples): extract the app spine into dsh-agent-core + app packages Implements docs/rfc/.../2026-06-20-extract-example-app-packages.md. Each example was thick — a hand-rolled start.ts, an infra preamble, nested base.yml/base-core.yml/acp-tail.yml includes, and a coupled front-door cluster enforced only by prose. This moves the composition into packages so each example is a thin leaf cordis.yml: pick the swappable backends, load one app package. New packages: - @deepseek-ai/dsh-agent-core (packages/core/agent-core): one bundle plugin that loads the providerless/executor-less/UI-less spine (timer + llm + sessions + system-prompt + tools + agents + invariants + tool-bash + agent-loop) via ctx.plugin(...) inside apply(), and forwards agent-loop's `agents` list as its own Config (export const Config = AgentLoop.Config, default []). - @deepseek-ai/dsh-stdio-agent (packages/ui/stdio-agent): terminal chat APP — agent-core + console logger + readline UI + a pre-created `main` agent, with a bin. The demo:echo/coding front door. - @deepseek-ai/dsh-acp-agent (packages/ui/acp-agent): ACP server APP — agent-core + JSONL persistence + the acp bridge, NO stdout logger, with a bin. The stdout-purity footgun is structurally unreachable from the leaf. Amendment to the RFC: hmr stays a LEAF cordis.yml entry, not baked into dsh-stdio-agent. hmr is a Loader-only dev plugin (throws without --expose-internals; the in-process test tier can't even import its decorator form), so a package statically importing it could never carry the per-file coverage gate. Unlike the console logger, a stray hmr is not a stdout-purity footgun, so leaving it at the leaf costs no safety. With hmr out, all three new packages carry in-process unit specs at 100%. Boot glue (Loader tail, .env load, snapshot-mode selection, stdin-dispose lifecycle) moves into each app's bin; start.ts and base.yml/base-core.yml/ acp-tail.yml are deleted. Each app package gets a keyless real-load-path test that boots through its bin + the cordis Loader (guarding the unwrapExports export-shape bug class, postmortem 0001). ACP snapshot replay stays green against the existing committed goldens (pure boot restructuring). RFC moved proposed->implemented with the amendment recorded; package/example/architecture docs and the module graph updated.
2026-06-21 12:03:44 +08:00
node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/echo-agent/cordis.yml
Document the codebase thoroughly and tighten type safety Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2026-06-11 12:39:27 +08:00
```
Type a message and press Enter. "echo <text>" triggers a tool call round-trip (the mock model requests the `echo` tool, which echoes the text uppercased, and the next model step acknowledges it).
Document the codebase thoroughly and tighten type safety Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2026-06-11 12:39:27 +08:00
The session is persisted under `.sessions/` relative to the directory you launch the demo from. `pnpm run demo:echo` runs from the repo root, so the logs land in `<repo-root>/.sessions/` (a session with no cwd goes in the `_no-cwd/` bucket, one `.jsonl` log per session). Clean up with: `rm -rf .sessions`