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.
Architecture Decision Records
Short, immutable records of the why behind decisions that shape this codebase. Code and docs say what the system does; ADRs say why it does it that way and what we gave up.
Format: one file per decision, numbered, with Status / Context / Decision / Consequences. An ADR is never edited into a different decision — supersede it with a new one and cross-link.
When to write an ADR
Write one when a decision is all three of: durable (it shapes the codebase beyond a single function or package), contested (there was a real alternative you rejected, and a reasonable engineer might have chosen it), and surprising (a future reader would otherwise ask "why on earth is it done this way?"). The ADR captures the why and what we gave up — the parts code and docs can't.
Do NOT write an ADR for: a mechanical or local choice (a variable name, a one-file refactor); anything already enforced and explained by a gate or a convention in AGENTS.md; or a still-provisional decision tagged TODO(...) in the code — record those as TODOs and promote to an ADR only once they settle. When in doubt, the test is the "why on earth" question: if the code alone would mislead a careful reader about intent, write the ADR.
| # | Title | Status |
|---|---|---|
| 0001 | Vendor Cordis as source, not npm dependencies | accepted |
| 0002 | Microkernel: extension via Cordis event taxonomy, one concrete loop | accepted |
| 0003 | Event-sourced sessions with derived message history | accepted |
| 0004 | Provider-neutral content-block vocabulary owned by dsh-llm | accepted |
| 0005 | Custom typed tool-schema DSL instead of schemastery | accepted |
| 0006 | Tool schemas are part of the system-prompt assembly | accepted |
| 0007 | Mechanical quality gates over prose guidelines | accepted |
| 0008 | tsdown for JS bundling instead of dumble | accepted |
| 0009 | Capability seams — interface / implementation / consumer split | accepted |
| 0010 | Two LLM adapters as a design-verification twin | accepted |
| 0011 | Runtime arg validation at the model boundary | accepted |
| 0012 | Dev-mode invariants over compile-time deep-readonly | accepted |
| 0013 | Property-based testing for protocol-shaped code | accepted |
| 0014 | Doc-sync enforcement (doc code blocks + event taxonomy) | accepted |
| 0015 | Structured error taxonomy (HarnessError base) | accepted |
| 0016 | Session persistence as an abstract service over the existing SessionEvent |
accepted |
| 0017 | Every session event is enclosed in a turn | accepted |