- agent/README: the inject() line said "without triggering a turn",
regressing the #32 turn-enclosure model. Restored the running-vs-idle
wording (idle inject wraps a one-shot injection turn; ADR 0017) to match
the interface JSDoc.
- agent-loop resume() JSDoc said "throws a typed error" but the code
throws a plain Error (consistent with the sibling assertAgentIdFree
throw). Softened to "rejects with a clear error" — no behavior change;
plain Error is intentional (no consumer needs a structured code here).
Add the agent-creation factory seam on ctx.agents (AgentRegistry):
setFactory/create/resume plus the AgentFactory interface and
CreateAgentOptions/ResumeAgentOptions. AgentLoop implements AgentFactory
and registers itself via ctx.agents.setFactory(this), so plugins
create/resume agents through the interface without depending on the
concrete loop package.
- create({ agentId, sessionId, meta?, agentOptions? }) — programmatic
create on a caller-supplied session id (e.g. an ACP-generated id).
- resume({ agentId, resumeSessionId, agentOptions? }) — load a persisted
session via ctx.sessionPersistence (RFC 009) and resume an agent on it;
the live session id is the resumed id, turn numbering and derived
history continue from the loaded log. sessionPersistence is NOT
hard-injected (non-persistent demos still work); resume rejects with a
typed error when it is absent. assertAgentIdFree runs before any
session is created (and again after the load await) so a duplicate id
never leaves an orphaned live session.
Adds the runtime dsh-session-persistence dependency to agent-loop.
Every session event now lives inside a turn (between turn/start and its
turn/end). The loop records queued user/message events AFTER turn/start;
an idle agent.inject() wraps its context/message in a one-shot injection
turn. This makes the turn the single durability/replay boundary so a
persistence backend can treat anything after the last turn/end as a
crash tail without dropping legitimate between-turn context.
A failure once the turn is already closed (rejecting session/flush, a
throwing agent/turn-end listener) has no in-turn position for a session
error event, so it is reported via agent/error + logger only; the turn
stays balanced. failTurn appends an error event only while the turn is
open.
The dsh-invariants plugin enforces turn-enclosure via a default case:
every non-boundary event type — including plugin-added merge-extensible
keys — must sit inside an open turn or it throws.
Documented in ADR 0017 + architecture.md.
Hard line breaks mid-paragraph make docs harder to edit and diff — a
one-word change reflows and re-diffs the whole paragraph. Reflow all
tracked non-vendor Markdown (plus vendor/AGENTS.md) so each prose
paragraph is a single line; soft-wrapping is the editor's job. Fenced
code, tables, and list structure are preserved (wrapped list items fold
to one line per bullet). Documents the convention in AGENTS.md.
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.