Commit Graph

2 Commits

Author SHA1 Message Date
Tianyi Cui
9a4006cb2b feat(agent): create/resume factory seam
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.
2026-06-15 21:12:14 +08:00
Tianyi Cui
df4b7d3d9a feat(session-persistence): abstract seam + JSONL backend + wiring
Add the durable session-persistence capability seam (ADR 0016): an
abstract SessionPersistence service (dsh-session-persistence,
ctx.sessionPersistence) defining create/append/load/list/has/delete/
update over the existing SessionEvent — no parallel persisted type — and
a first implementation (dsh-session-persistence-jsonl): an append-only
JSONL log per session with crash-safe atomic writes, truncation-repair
of a never-committed crash tail, and a read/replay path. SessionMeta
(format version, cwd, lineage) travels out-of-log via session.header.

A shared runPersistenceContract suite holds every backend to the same
append-only / contiguous-seq / lazy-materialization / serializability
semantics.

Config-driven create() now uses a per-run ${id}-session-<uuid> session
id so a fixed name no longer collides with an on-disk log once a durable
backend is loaded; each run is a new session (a demo simplification). The
examples drop their hand-rolled session-jsonl.ts and load the JSONL
backend via cordis.yml; CI smoke-loads it too.

The agent-facing create/resume factory that consumes load() is a
separate seam, deferred to a follow-up; this change stops at the load
primitive and does not reach into the loop.
2026-06-15 21:05:46 +08:00