docs: address review sync gaps
This commit is contained in:
36
AGENTS.md
36
AGENTS.md
@@ -110,15 +110,17 @@ packages/ Harness packages, grouped by role at packages/<group>/<pkg>/.
|
|||||||
code, no harness deps; owns the brand for cross-boundary ids)
|
code, no harness deps; owns the brand for cross-boundary ids)
|
||||||
examples/ Runnable demos (not workspaces; see examples/AGENTS.md). Each is a
|
examples/ Runnable demos (not workspaces; see examples/AGENTS.md). Each is a
|
||||||
THIN leaf cordis.yml: it picks the swappable backends (an LLM adapter,
|
THIN leaf cordis.yml: it picks the swappable backends (an LLM adapter,
|
||||||
a bash executor) and loads ONE app package (dsh-stdio-agent or
|
a bash executor), loads ONE app package (dsh-stdio-agent or
|
||||||
dsh-acp-agent), which bundles the agent-core spine + front-door
|
dsh-acp-agent), and may add optional product tools or demo-local
|
||||||
cluster + boot glue (a bin). No start.ts. echo-agent = mock model +
|
teaching plugins. The app package bundles the agent-core spine +
|
||||||
echo tool on dsh-stdio-agent (pnpm run demo:echo, no key).
|
front-door cluster + boot glue (a bin). No start.ts. echo-agent =
|
||||||
coding-agent = the real thing: DeepSeek V4 + bash tools on the same
|
mock model + echo tool on dsh-stdio-agent (pnpm run demo:echo, no
|
||||||
app (pnpm run demo:coding, needs DEEPSEEK_API_KEY). acp-agent = the
|
key). coding-agent = the real thing: DeepSeek V4 + bash tools +
|
||||||
coding agent as an ACP server on dsh-acp-agent (pnpm run demo:acp,
|
subagent + todo_write on the same app (pnpm run demo:coding, needs
|
||||||
needs DEEPSEEK_API_KEY). cordis.snapshot.yml = the acp leaf with
|
DEEPSEEK_API_KEY). acp-agent = the coding agent as an ACP server on
|
||||||
llm-replay for keyless snapshot replay.
|
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
|
docs/ architecture.md — the design doc. module-graph.md — generated
|
||||||
inter-package dependency graph (Mermaid; `pnpm run gen-module-graph`).
|
inter-package dependency graph (Mermaid; `pnpm run gen-module-graph`).
|
||||||
rfc/ — design decisions and proposals, one kind of doc grouped by
|
rfc/ — design decisions and proposals, one kind of doc grouped by
|
||||||
@@ -191,7 +193,21 @@ pnpm run demo:acp # run examples/acp-agent — the coding agent as an ACP
|
|||||||
CI is the backstop, not the first place a gate runs. Before you open a non-draft PR or move one from draft to ready, run the same gates CI runs, on your own tree, and confirm they pass — do not lean on CI (or a Codex pass) to discover a red gate you could have caught locally. The CI-equivalent local run is:
|
CI is the backstop, not the first place a gate runs. Before you open a non-draft PR or move one from draft to ready, run the same gates CI runs, on your own tree, and confirm they pass — do not lean on CI (or a Codex pass) to discover a red gate you could have caught locally. The CI-equivalent local run is:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pnpm run typecheck && pnpm run lint && pnpm run test:coverage && pnpm run test:snapshot && pnpm run doc-sync && pnpm run verify-module-graph && pnpm run build && pnpm run hygiene
|
set -euo pipefail
|
||||||
|
pnpm run typecheck
|
||||||
|
pnpm run lint
|
||||||
|
pnpm run test:coverage
|
||||||
|
pnpm run test:snapshot
|
||||||
|
pnpm run doc-sync
|
||||||
|
pnpm run verify-module-graph
|
||||||
|
pnpm run build
|
||||||
|
pnpm run hygiene
|
||||||
|
out=$(printf 'echo ci smoke\n' | pnpm run demo:echo 2>&1)
|
||||||
|
printf '%s\n' "$out" | grep -q '\[tool call\] echo({"text":"ci smoke"})'
|
||||||
|
printf '%s\n' "$out" | grep -q '\[tool result\] ECHO: CI SMOKE'
|
||||||
|
ls .sessions/_no-cwd/main-session-*.jsonl >/dev/null
|
||||||
|
rm -rf .sessions
|
||||||
|
pnpm exec vitest run --config vitest.e2e.config.ts packages/ui/stdio-agent/tests/built-bin.e2e.ts packages/ui/acp-agent/tests/built-bin.e2e.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
**`pnpm run test:coverage`, NOT `pnpm run test`, is the gating test command.** `pnpm run test` runs `vitest run` with no coverage; CI's node job runs `test:coverage`, which enforces a **per-file 100%** threshold on `packages/*/*/src`. A suite that is green under `test` can still fail CI on an uncovered line — and that uncovered line is often *dead code* the 100% gate is correctly flagging for deletion (see [§ Defensive patterns](#defensive-patterns-hard-won) "Line coverage is not behavior coverage"), not a missing test to bolt on. `hygiene` (knip + publint + workspace constraints + NodeNext types) and `test:snapshot` (keyless ACP replay) are likewise CI gates that `test` alone does not cover. When you rely on a Codex convergence pass for sign-off, check WHICH commands it ran: a pass that ran `test` but not `test:coverage`/`hygiene`/`doc-sync` has not exercised those gates.
|
**`pnpm run test:coverage`, NOT `pnpm run test`, is the gating test command.** `pnpm run test` runs `vitest run` with no coverage; CI's node job runs `test:coverage`, which enforces a **per-file 100%** threshold on `packages/*/*/src`. A suite that is green under `test` can still fail CI on an uncovered line — and that uncovered line is often *dead code* the 100% gate is correctly flagging for deletion (see [§ Defensive patterns](#defensive-patterns-hard-won) "Line coverage is not behavior coverage"), not a missing test to bolt on. `hygiene` (knip + publint + workspace constraints + NodeNext types) and `test:snapshot` (keyless ACP replay) are likewise CI gates that `test` alone does not cover. When you rely on a Codex convergence pass for sign-off, check WHICH commands it ran: a pass that ran `test` but not `test:coverage`/`hygiene`/`doc-sync` has not exercised those gates.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ A backend that reloads a log crashed mid-turn finds an open `turn/start` with no
|
|||||||
|
|
||||||
## `SessionHeader` — metadata beside the log
|
## `SessionHeader` — metadata beside the log
|
||||||
|
|
||||||
Per-session metadata travels **separately** from the event log: format version, cwd, and lineage are storage concerns, not conversation events, so they stay out of `SessionEventMap` and never reach `deriveMessages()`. The header is attached to a `Session` via `session.header`.
|
Per-session metadata travels **separately** from the event log: format version, cwd, lineage, and the seed boundary are storage concerns, not conversation events, so they stay out of `SessionEventMap` and never reach `deriveMessages()`. The header is attached to a `Session` via `session.header`.
|
||||||
|
|
||||||
Source: [`packages/core/session/src/types.ts`](../../packages/core/session/src/types.ts)
|
Source: [`packages/core/session/src/types.ts`](../../packages/core/session/src/types.ts)
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ lefthook is configured in `lefthook.yml` as an early local checkpoint before rev
|
|||||||
|
|
||||||
The vendor manifest guard checks that changes under `vendor/*/src` are staged with the matching `vendor/README.md` manifest update. See `vendor/README.md` before editing vendored code.
|
The vendor manifest guard checks that changes under `vendor/*/src` are staged with the matching `vendor/README.md` manifest update. See `vendor/README.md` before editing vendored code.
|
||||||
|
|
||||||
These hooks do not exactly mirror CI. Notably, `pre-push` runs unit tests without coverage, while CI runs `pnpm run test:coverage`; CI also runs an echo-agent smoke test and exercises the matrix on Node 24 and 26.
|
These hooks do not exactly mirror CI. Notably, `pre-push` runs unit tests without coverage, while CI runs `pnpm run test:coverage`; CI also runs echo-agent and built-bin smoke tests and exercises the matrix on Node 24 and 26.
|
||||||
|
|
||||||
## CI gates
|
## CI gates
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
Runnable demos (not workspaces) that showcase how the harness is wired. Each example is now a **thin leaf**: a `cordis.yml` that picks the swappable backends (an LLM adapter, a bash executor) and loads ONE app package, plus any demo-only mocks. The composition — the spine, the front-door cluster, and the boot glue — lives in the app packages ([`@deepseek-ai/dsh-stdio-agent`](../packages/ui/stdio-agent), [`@deepseek-ai/dsh-acp-agent`](../packages/ui/acp-agent)) and the [`@deepseek-ai/dsh-agent-core`](../packages/core/agent-core) bundle they share. There is no `start.ts`; the `demo:*` scripts invoke each app package's `bin`.
|
Runnable demos (not workspaces) that showcase how the harness is wired. Each example is now a **thin leaf**: a `cordis.yml` that picks the swappable backends (an LLM adapter, a bash executor), loads ONE app package, and may add optional product tools or demo-only mocks. The composition — the spine, the front-door cluster, and the boot glue — lives in the app packages ([`@deepseek-ai/dsh-stdio-agent`](../packages/ui/stdio-agent), [`@deepseek-ai/dsh-acp-agent`](../packages/ui/acp-agent)) and the [`@deepseek-ai/dsh-agent-core`](../packages/core/agent-core) bundle they share. There is no `start.ts`; the `demo:*` scripts invoke each app package's `bin`.
|
||||||
|
|
||||||
## echo-agent
|
## echo-agent
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,9 @@
|
|||||||
|
|
||||||
Use the subagent tool to delegate a focused, self-contained subtask to
|
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 its
|
a fresh child agent (it works in its own context and returns only its
|
||||||
final result) — give it a complete, standalone instruction.
|
final result) — give it a complete, standalone instruction. Use
|
||||||
|
subagent_fork instead when the subtask needs THIS conversation's
|
||||||
|
context: the child inherits the log so far.
|
||||||
|
|
||||||
For multi-step work, use the todo_write tool to track a task list:
|
For multi-step work, use the todo_write tool to track a task list:
|
||||||
send the WHOLE list each call (it replaces the previous one), keep at
|
send the WHOLE list each call (it replaces the previous one), keep at
|
||||||
|
|||||||
@@ -50,7 +50,9 @@
|
|||||||
|
|
||||||
Use the subagent tool to delegate a focused, self-contained subtask to
|
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 its
|
a fresh child agent (it works in its own context and returns only its
|
||||||
final result) — give it a complete, standalone instruction.
|
final result) — give it a complete, standalone instruction. Use
|
||||||
|
subagent_fork instead when the subtask needs THIS conversation's
|
||||||
|
context: the child inherits the log so far.
|
||||||
|
|
||||||
For multi-step work, use the todo_write tool to track a task list:
|
For multi-step work, use the todo_write tool to track a task list:
|
||||||
send the WHOLE list each call (it replaces the previous one), keep at
|
send the WHOLE list each call (it replaces the previous one), keep at
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ The packages every harness build is assembled from: the session log, the system-
|
|||||||
|
|
||||||
`agent-loop` is the one concrete implementation of the `agent` seam and lives here because it is the harness's default product loop; everything else in `core/` is interface/vocabulary. Plugins depend on the `agent` vocabulary, never on `agent-loop` directly, so the loop stays swappable.
|
`agent-loop` is the one concrete implementation of the `agent` seam and lives here because it is the harness's default product loop; everything else in `core/` is interface/vocabulary. Plugins depend on the `agent` vocabulary, never on `agent-loop` directly, so the loop stays swappable.
|
||||||
|
|
||||||
`agent-core` is the composition counterpart: one bundle plugin that loads the whole providerless spine (`timer` + `llm` + sessions + system-prompt + tools + agents + invariants + `tool-bash` + `agent-loop`) and forwards `agent-loop`'s `agents` list as its own config. App packages (`ui/stdio-agent`, `ui/acp-agent`) consume it and add only a front door; a leaf adds only the swappable backends. It lives in `core/` because it composes exclusively `core/` + interface packages and ships no provider, executor, or UI of its own.
|
`agent-core` is the composition counterpart: one bundle plugin that loads the whole providerless spine (`timer` + `llm` + sessions + system-prompt + tools + agents + invariants + `tool-bash` + `agent-loop`) and forwards `agent-loop`'s `agents` list as its own config. App packages (`ui/stdio-agent`, `ui/acp-agent`) consume it and add only a front door; a leaf adds the swappable backends plus any optional product tools it wants to expose. It lives in `core/` because it composes exclusively `core/` + interface packages and ships no provider, executor, or UI of its own.
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ This is the only package in the harness that contains concrete loop logic. Every
|
|||||||
|
|
||||||
`AgentLoop` also implements the `AgentFactory` seam and registers itself via `ctx.agents.setFactory(this)`, so plugins create/resume agents through `ctx.agents` (the interface):
|
`AgentLoop` also implements the `AgentFactory` seam and registers itself via `ctx.agents.setFactory(this)`, so plugins create/resume agents through `ctx.agents` (the interface):
|
||||||
|
|
||||||
- `ctx.agents.create({ agentId, sessionId, meta?, agentOptions? }): AgentHandle` — programmatic create on a caller-supplied `sessionId` (e.g. an ACP-generated id), NOT `${id}-session`. Returns an [`AgentHandle`](../agent/README.md) — the owner disposes it to tear down exactly this agent (stop loop + await quiescence + unregister + remove session).
|
- `ctx.agents.create({ agentId, sessionId, meta?, seed?, agentOptions? }): AgentHandle` — programmatic create on a caller-supplied `sessionId` (e.g. an ACP-generated id), NOT `${id}-session`; `meta` carries cwd/lineage/seed-boundary metadata and `seed` reconstructs a forked child prefix. Returns an [`AgentHandle`](../agent/README.md) — the owner disposes it to tear down exactly this agent (stop loop + await quiescence + unregister + remove session).
|
||||||
- `ctx.agents.resume({ agentId, resumeSessionId, agentOptions? }): Promise<AgentHandle>` — load a persisted session via `ctx.sessionPersistence` ([session persistence](../../../docs/rfc/implemented/architecture/2026-06-14-session-persistence.md)) and resume an agent on it. The live session id is the resumed id; turn numbering and derived history continue from the loaded log. Requires a session-persistence backend (NOT hard-injected — non-persistent demos still work; `resume` rejects with a clear error when persistence is absent). Returns an `AgentHandle`.
|
- `ctx.agents.resume({ agentId, resumeSessionId, agentOptions? }): Promise<AgentHandle>` — load a persisted session via `ctx.sessionPersistence` ([session persistence](../../../docs/rfc/implemented/architecture/2026-06-14-session-persistence.md)) and resume an agent on it. The live session id is the resumed id; turn numbering and derived history continue from the loaded log. Requires a session-persistence backend (NOT hard-injected — non-persistent demos still work; `resume` rejects with a clear error when persistence is absent). Returns an `AgentHandle`.
|
||||||
|
|
||||||
The config-driven `ctx.agentLoop.create()` path keeps its agent owned by the loop fiber (it discards the handle) — only the programmatic factory callers (the ACP bridge) hold a handle and own per-agent teardown.
|
The config-driven `ctx.agentLoop.create()` path keeps its agent owned by the loop fiber (it discards the handle) — only the programmatic factory callers (the ACP bridge and in-process subagent backends) hold a handle and own per-agent teardown.
|
||||||
|
|
||||||
### Injected services
|
### Injected services
|
||||||
|
|
||||||
@@ -76,6 +76,6 @@ Everything that goes beyond "call the model, run the tools, repeat" belongs to p
|
|||||||
- Hooks: `agent/request`, `agent/step-result`, `tools/execute`, `agent/turn-continuation`
|
- Hooks: `agent/request`, `agent/step-result`, `tools/execute`, `agent/turn-continuation`
|
||||||
- Compaction: `agent/request`
|
- Compaction: `agent/request`
|
||||||
- Sandbox, permission, plan mode: `tools/execute`
|
- Sandbox, permission, plan mode: `tools/execute`
|
||||||
- Sub-agents: TODO seam on `AgentLoop.create()`
|
- Sub-agents: implemented outside the loop as `ctx.subagents` providers; in-process providers use `ctx.agents.create()` and owned `AgentHandle` teardown, while child streaming/progress and background/poll collection remain deferred.
|
||||||
- Persistence: `session/event` + `session/flush`
|
- Persistence: `session/event` + `session/flush`
|
||||||
- UI: `agent/stream-chunk` + `agent/*` events
|
- UI: `agent/stream-chunk` + `agent/*` events
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ Tracks live agents so UI, hook, and orchestrator plugins can find them without i
|
|||||||
Agent *creation* is provided by whichever plugin implements `AgentFactory` (phase 1: `dsh-agent-loop`), registered via `setFactory`. This keeps creation on the `dsh-agent` interface so consumers (UI, the ACP bridge) program against `ctx.agents` without depending on the concrete loop package.
|
Agent *creation* is provided by whichever plugin implements `AgentFactory` (phase 1: `dsh-agent-loop`), registered via `setFactory`. This keeps creation on the `dsh-agent` interface so consumers (UI, the ACP bridge) program against `ctx.agents` without depending on the concrete loop package.
|
||||||
|
|
||||||
- `ctx.agents.setFactory(factory: AgentFactory): () => void` — register the creation factory (the loop calls this on construction). Throws on a second factory; the slot clears on dispose.
|
- `ctx.agents.setFactory(factory: AgentFactory): () => void` — register the creation factory (the loop calls this on construction). Throws on a second factory; the slot clears on dispose.
|
||||||
- `ctx.agents.create(options: CreateAgentOptions): AgentHandle` — construct, start, AND register a new agent on a caller-supplied `sessionId` (with optional `meta.cwd`). Distinct from `register` (which only records). Throws if no factory is registered.
|
- `ctx.agents.create(options: CreateAgentOptions): AgentHandle` — construct, start, AND register a new agent on a caller-supplied `sessionId` (with optional `meta.cwd`/`meta.parentSession`/`meta.seedLength` and optional `seed` events for forked children). Distinct from `register` (which only records). Throws if no factory is registered.
|
||||||
- `ctx.agents.resume(options: ResumeAgentOptions): Promise<AgentHandle>` — load a persisted session ([session persistence](../../../docs/rfc/implemented/architecture/2026-06-14-session-persistence.md)) and resume an agent on it. Async; rejects if no factory is registered, or if the factory finds session persistence unconfigured.
|
- `ctx.agents.resume(options: ResumeAgentOptions): Promise<AgentHandle>` — load a persisted session ([session persistence](../../../docs/rfc/implemented/architecture/2026-06-14-session-persistence.md)) and resume an agent on it. Async; rejects if no factory is registered, or if the factory finds session persistence unconfigured.
|
||||||
|
|
||||||
`AgentHandle = { agent: Agent; dispose(): Promise<void> }`. The disposer is a **capability** — only the holder can tear this agent down. `dispose()` stops the loop, `await`s its exit (quiescence — NOT just the `disposed` status flip), unregisters the agent, and removes its session from the store, in an order that captures the loop's final `session/flush` before the session is detached. `ctx.agents.get(id)` still returns a bare `Agent` — the handle is only for the OWNER that created it. The ACP bridge is the production consumer (one handle per session, disposed on disconnect/teardown); config-created agents are owned by the loop fiber and never need a handle.
|
`AgentHandle = { agent: Agent; dispose(): Promise<void> }`. The disposer is a **capability** — only the holder can tear this agent down. `dispose()` stops the loop, `await`s its exit (quiescence — NOT just the `disposed` status flip), unregisters the agent, and removes its session from the store, in an order that captures the loop's final `session/flush` before the session is detached. `ctx.agents.get(id)` still returns a bare `Agent` — the handle is only for the OWNER that created it. The ACP bridge and in-process subagent backends are production consumers; config-created agents are owned by the loop fiber and never need a handle.
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
@@ -62,9 +62,10 @@ The handle every plugin programs against:
|
|||||||
|
|
||||||
### Extension points
|
### Extension points
|
||||||
|
|
||||||
- Agent creation: `AgentLoop.create()` is the concrete implementation (in `dsh-agent-loop`). Replace the loop by implementing `Agent` and registering via `ctx.agents.register()`.
|
- Agent creation: `AgentLoop.create()` is the concrete config-path implementation (in `dsh-agent-loop`), while programmatic consumers create/resume owned agents through `ctx.agents.create()` / `ctx.agents.resume()`. Replace the loop by implementing `Agent` and registering via `ctx.agents.register()`.
|
||||||
- Event listeners: all `agent/*` events are declared here — no dependency on the loop package needed.
|
- Event listeners: all `agent/*` events are declared here — no dependency on the loop package needed.
|
||||||
|
- Subagent delegation: implemented by `@deepseek-ai/dsh-subagent`, not by a method on `Agent`; providers create or drive ordinary `Agent` handles through the factory seam, so spawn/fork/ACP transports stay outside the core agent interface.
|
||||||
|
|
||||||
### What is NOT here (TODO)
|
### What is NOT here (TODO)
|
||||||
|
|
||||||
- **Sub-agent spawn/fork** — seam on `AgentLoop.create()`, semantics deferred.
|
- **Inter-agent channels beyond delegation** — shared state, streaming child output, and background/poll semantics remain outside the current synchronous `ctx.subagents` seam.
|
||||||
|
|||||||
@@ -95,12 +95,12 @@ export class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable creation metadata (format version, cwd, lineage). Supplied by
|
* Immutable creation metadata (format version, cwd, lineage, seed boundary).
|
||||||
* the store via `ctx.sessions.create()`. When a `Session` is constructed
|
* Supplied by the store via `ctx.sessions.create()`. When a `Session` is
|
||||||
* bare (tests, ad-hoc replay), a minimal header is synthesized (stamped with
|
* constructed bare (tests, ad-hoc replay), a minimal header is synthesized
|
||||||
* the current {@link SESSION_FORMAT_VERSION}) so `session.header` is always
|
* (stamped with the current {@link SESSION_FORMAT_VERSION}) so
|
||||||
* present. Kept out of the event log — it is a storage concern, not
|
* `session.header` is always present. Kept out of the event log — it is a
|
||||||
* replayable conversation state.
|
* storage concern, not replayable conversation state.
|
||||||
*/
|
*/
|
||||||
readonly header: SessionHeader
|
readonly header: SessionHeader
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
* parallel "persisted message" type the log must be converted to and from
|
* parallel "persisted message" type the log must be converted to and from
|
||||||
* (faithful to the event-sourced model: the log is the single source of
|
* (faithful to the event-sourced model: the log is the single source of
|
||||||
* truth). Metadata that is NOT replayable conversation state (format version,
|
* truth). Metadata that is NOT replayable conversation state (format version,
|
||||||
* cwd, lineage) travels separately as {@link SessionHeader}, which is owned by
|
* cwd, lineage, seed boundary) travels separately as {@link SessionHeader},
|
||||||
* `dsh-session` and re-exported here.
|
* which is owned by `dsh-session` and re-exported here.
|
||||||
*
|
*
|
||||||
* @module @deepseek-ai/dsh-session-persistence
|
* @module @deepseek-ai/dsh-session-persistence
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ Integrations that expose the agent to an external editor or client. These are **
|
|||||||
|
|
||||||
A UI integration is a client-driver plugin, not a loop change and not a capability seam: it consumes the existing `agent/*` event taxonomy and the `dsh-agent` factory. The readline `ui-stdio` plugin is the unstructured analogue but lives in `support/` because it exists chiefly for the examples and the coverage gate — `ui/` is reserved for surfaces shipped as product.
|
A UI integration is a client-driver plugin, not a loop change and not a capability seam: it consumes the existing `agent/*` event taxonomy and the `dsh-agent` factory. The readline `ui-stdio` plugin is the unstructured analogue but lives in `support/` because it exists chiefly for the examples and the coverage gate — `ui/` is reserved for surfaces shipped as product.
|
||||||
|
|
||||||
`stdio-agent` and `acp-agent` are the two **app packages**: each composes the [`core/agent-core`](../core/agent-core/README.md) spine with its coupled front-door cluster (and owns the boot `bin`), so a leaf `cordis.yml` is just the swappable backends plus one app entry. They live in `ui/` because each IS a user-facing front door; the stdout-purity coupling (logger vs. no logger) becomes a property of the artifact rather than a leaf convention.
|
`stdio-agent` and `acp-agent` are the two **app packages**: each composes the [`core/agent-core`](../core/agent-core/README.md) spine with its coupled front-door cluster (and owns the boot `bin`), so a leaf `cordis.yml` is the swappable backends plus one app entry plus any optional product tools. They live in `ui/` because each IS a user-facing front door; the stdout-purity coupling (logger vs. no logger) becomes a property of the artifact rather than a leaf convention.
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
* which this app does not prevent — so the rule "never add a stdout logger to an
|
* which this app does not prevent — so the rule "never add a stdout logger to an
|
||||||
* ACP leaf" still stands; the app just gives the leaf nothing to misconfigure.)
|
* ACP leaf" still stands; the app just gives the leaf nothing to misconfigure.)
|
||||||
*
|
*
|
||||||
* The leaf supplies only the swappable backends: the LLM adapter (`llm-deepseek`
|
* The leaf supplies the swappable backends: the LLM adapter (`llm-deepseek` for
|
||||||
* for the real model, `llm-replay` for keyless snapshot replay) and the bash
|
* the real model, `llm-replay` for keyless snapshot replay), the bash executor
|
||||||
* executor (`bash-local`). This app's {@link Config} (model, system prompt,
|
* (`bash-local`), and any optional product tools it wants to expose. This app's
|
||||||
* persistence root) routes each value to where it is wired — model/prompt onto
|
* {@link Config} (model, system prompt, persistence root) routes each value to
|
||||||
* the bridge's per-session agent template, the root onto the JSONL backend.
|
* where it is wired — model/prompt onto the bridge's per-session agent
|
||||||
|
* template, the root onto the JSONL backend.
|
||||||
*
|
*
|
||||||
* Plugin export shape: named `name`/`Config`/`apply`, NO default export — the
|
* Plugin export shape: named `name`/`Config`/`apply`, NO default export — the
|
||||||
* cordis Loader's `unwrapExports` does `exports.default ?? exports`, so a stray
|
* cordis Loader's `unwrapExports` does `exports.default ?? exports`, so a stray
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
*
|
*
|
||||||
* The cluster is BAKED IN, not left to the leaf: a stdio app always logs to the
|
* The cluster is BAKED IN, not left to the leaf: a stdio app always logs to the
|
||||||
* console (stdout is just the terminal) and always pre-creates the `main` agent
|
* console (stdout is just the terminal) and always pre-creates the `main` agent
|
||||||
* `ui-stdio` sends to. The leaf supplies only the swappable backends (the LLM
|
* `ui-stdio` sends to. The leaf supplies the swappable backends (the LLM
|
||||||
* adapter, the bash executor), the optional `hmr` dev-reload plugin, and this
|
* adapter, the bash executor), optional product tools, the optional `hmr`
|
||||||
* app's {@link Config} (model, prompt, persistence root, welcome banner).
|
* dev-reload plugin, and this app's {@link Config} (model, prompt, persistence
|
||||||
|
* root, welcome banner).
|
||||||
*
|
*
|
||||||
* `hmr` is deliberately a LEAF entry, not baked in here: it is a Loader-only,
|
* `hmr` is deliberately a LEAF entry, not baked in here: it is a Loader-only,
|
||||||
* subprocess-only dev plugin (its constructor throws without `--expose-internals`
|
* subprocess-only dev plugin (its constructor throws without `--expose-internals`
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
* resolved against the linking file's directory, and the result must exist on
|
* resolved against the linking file's directory, and the result must exist on
|
||||||
* disk. This is checker, not fixer: it reports and never rewrites.
|
* disk. This is checker, not fixer: it reports and never rewrites.
|
||||||
*
|
*
|
||||||
* Scope is the other doc-sync gates' set plus example Markdown, the two
|
* Scope is the other doc-sync gates' set plus example Markdown, AGENTS.md
|
||||||
* AGENTS.md files AND the repo-authored agent-skill Markdown under
|
* files in those checked trees, AND the repo-authored agent-skill Markdown under
|
||||||
* `.agents/skills/` — those skill files cross-link into the docs tree (e.g. the
|
* `.agents/skills/` — those skill files cross-link into the docs tree (e.g. the
|
||||||
* dsh-code-review skill cites the RFC index), so a rename must not silently
|
* dsh-code-review skill cites the RFC index), so a rename must not silently
|
||||||
* break them either: README.md, docs/** /*.md, packages/* /README.md,
|
* break them either: README.md, docs/** /*.md, packages/* /README.md,
|
||||||
|
|||||||
Reference in New Issue
Block a user