Commit Graph
153 Commits
Author SHA1 Message Date
Hypatia May 8fde0c4c25 Merge branch 'split/session-persistence-sqlite' of https://github.com/deepseek-ai/deepseek-harness into session-surface 2026-06-18 09:01:58 +08:00
Hypatia May c5a1c494e7 feature(session): session surface 2026-06-17 19:25:29 +08:00
Tianyi Cui d8e009d63e Merge remote-tracking branch 'origin/master' into split/session-persistence-sqlite 2026-06-17 15:08:54 +08:00
Tianyi Cui 1d1ce8143c Merge pull request #34 from deepseek-ai/split/agent-factory
feat(agent): create/resume factory seam (split 4/5)
2026-06-17 15:07:14 +08:00
Tianyi Cui c870c8fcb1 Merge remote-tracking branch 'origin/split/agent-factory' into split/session-persistence-sqlite 2026-06-17 15:05:30 +08:00
Tianyi Cui cae1517407 Merge remote-tracking branch 'origin/master' into split/agent-factory 2026-06-17 15:03:51 +08:00
Tianyi Cui edd6eb28dd Merge pull request #33 from deepseek-ai/split/session-persistence
feat(session-persistence): abstract seam + JSONL backend + wiring (split 3/5)
2026-06-17 15:01:52 +08:00
Tianyi Cui 36d3e1def4 Merge pull request #44 from deepseek-ai/worktree-md-wrap-guard
feat: merge-commit policy + markdown hard-wrap gate
2026-06-17 11:00:14 +08:00
Tianyi Cui ffc107aa57 docs: record verify-md-wrap in the doc-sync source-of-truth docs
Adding verify-md-wrap to the shared doc-sync gate left its defining docs
stale (Codex review):
- ADR 0014 described doc-sync as two gates; add a dated amendment for the
  third (verify-md-wrap) and drop the "two checkable classes" wording.
- CI step label/comment said "doc code blocks + event taxonomy"; include
  the markdown wrap check.
2026-06-17 10:50:58 +08:00
Tianyi Cui e31e19d99b docs: remove duplicate AGENTS.md convention bullets
The origin/master merge resolution accidentally duplicated the
"Symmetry" and "Tests" Conventions bullets. Collapse each back to a
single copy.
2026-06-17 10:05:07 +08:00
Tianyi Cui dbaef018cc Merge remote-tracking branch 'origin/split/agent-factory' into split/session-persistence-sqlite
# Conflicts:
#	docs/adr/0018-session-persistence.md
2026-06-16 23:53:54 +08:00
Tianyi Cui d3c9f6ca1e Merge remote-tracking branch 'origin/split/session-persistence' into split/agent-factory 2026-06-16 23:51:16 +08:00
Tianyi Cui 2bae18f811 fix(session): synthesize tool results for interrupted tool calls on crash recovery (review #33)
The loop logs the assistant/message (carrying tool-call blocks) BEFORE running
the tools, so a crash mid-tool leaves durable tool calls with no matching
tool/result. interruptedTurnClosers only added step/end + turn/end, so a resumed
session's deriveMessages() replayed a dangling assistant tool-call — which every
provider rejects as an invalid transcript on the next request.

interruptedTurnClosers now scans the interrupted turn for tool-call blocks
without a matching tool/result and synthesizes an error tool/result for each
(before the step/end), so the rehydrated history is a valid transcript. Adds a
dedicated repair.spec.ts and a shared-contract case proving both backends pair
every orphaned call with a result. Docs (ADR 0018, both persistence READMEs,
load() JSDoc) updated.

Also fixes the echo-agent README session-cleanup path: demo:echo runs from the
repo root, so sessions land in <repo-root>/.sessions/_no-cwd/, not
examples/echo-agent/.sessions/ (review #33).
2026-06-16 23:50:52 +08:00
Tianyi Cui 840dfbab84 Merge remote-tracking branch 'origin/master' into worktree-md-wrap-guard
# Conflicts:
#	AGENTS.md
2026-06-16 23:38:58 +08:00
Tianyi Cui 63425a2b87 refactor: detect md hard-wraps via mdast AST, not regex
Per review feedback (use a real markdown parser with an AST linked to
source positions), rewrite verify-md-wrap to parse each file with
mdast-util-from-markdown (the CommonMark parser behind remark) + the GFM
extension, then flag any `paragraph` node whose source span covers more
than one line.

Why a parser over the hand-rolled line scanner:
- It is a checker, not a formatter — it reports and never rewrites, so
  zero cosmetic churn (no emphasis-marker or table-delimiter
  normalization, which is why Prettier was rejected for this).
- The AST owns every structural exemption (fenced code of any fence
  length, tables, lists, blockquotes, HTML, headings, reference defs),
  fixing both bugs the regex version had: it now catches wrapped
  list-item / blockquote prose (a `paragraph` inside those nodes) and no
  longer false-positives on a longer ```` fence wrapping an inner ```.

Also unwrap two pre-existing hard-wrapped blockquotes (architecture.md,
adding-a-tool.md) that the stricter AST check correctly surfaced.
2026-06-16 23:35:33 +08:00
Tianyi Cui 973399e03c Merge pull request #45 from deepseek-ai/worktree-docs+todo-markers
docs(development): document FIXME/TODO/XXX markers
2026-06-16 23:21:37 +08:00
Tianyi Cui 9fb12740c7 Merge remote-tracking branch 'origin/split/agent-factory' into split/session-persistence-sqlite 2026-06-16 23:20:29 +08:00
Tianyi Cui 16e7bbc7c6 Merge remote-tracking branch 'origin/split/session-persistence' into split/agent-factory 2026-06-16 23:19:34 +08:00
Tianyi Cui 16e75c2631 Merge remote-tracking branch 'origin/master' into split/session-persistence 2026-06-16 23:18:06 +08:00
Tianyi Cui c2dc63f2ea Merge pull request #46 from deepseek-ai/split/turn-enclosure
feat(agent-loop): turn-enclosure invariant + post-turn error model (split 2/5, re-land)
2026-06-16 23:16:13 +08:00
Tianyi Cui 53b37b39c5 docs: point AGENTS.md to development.md for TODO marker semantics 2026-06-16 23:02:48 +08:00
Tianyi Cui ce4b32ace9 feat(session-persistence-sqlite): preserve interrupted turns on load, don't truncate
Mirror the JSONL backend's crash-recovery contract change: load() now PRESERVES
the real events of an interrupted final turn (a turn can be huge — truncating it
would destroy work) and durably CLOSES the orphaned turn with synthetic boundary
events (step/end if open, then turn/end {interrupted}) inside one transaction
that also deletes any torn tail row. load() is therefore mutating; the deferred
truncation-repair on the next append is gone. Replaces cutAtLastTurnEnd with
scanRows (longest preserved prefix + torn-tail offset). Updates the sqlite tests
and README to the preserve-and-close semantics; the shared runPersistenceContract
suite now holds both backends to identical interrupted-turn behavior.
2026-06-16 22:56:17 +08:00
Tianyi Cui 326b026ab3 docs(development): document FIXME/TODO/XXX markers
Define the three issue-urgency tags so contributors can flag a release
blocker, a soon-to-fix item, and a someday-maybe consistently.
2026-06-16 22:45:19 +08:00
Tianyi Cui f0d9383f49 Merge branch 'split/agent-factory' into split/session-persistence-sqlite
# Conflicts:
#	docs/adr/0018-session-persistence.md
#	docs/architecture.md
2026-06-16 22:32:49 +08:00
Tianyi Cui 0000cdb2c2 feat(agent-loop): config-driven session resume via RESUME_SESSION_ID
A config agent with `resumeSessionId` set continues a persisted session
instead of starting a fresh `${id}-session-<uuid>`. The id is sourced from
an env var in cordis.yml, so the coding-agent demo can resume a prior
conversation without code changes. The resume is deferred until the
`sessionPersistence` backend loads (via ctx.inject) and is contained: a
missing/unreadable id logs a warning and starts no agent. Adds a real-API
resume e2e proving cross-process continuity through the JSONL backend.
2026-06-16 22:28:01 +08:00
Tianyi Cui 8511b9639c Merge branch 'split/session-persistence' into split/agent-factory
# Conflicts:
#	docs/adr/0017-turn-enclosure-invariant.md
#	docs/architecture.md
2026-06-16 22:27:51 +08:00
Tianyi Cui 67447fcdc3 feat: enforce merge-commit policy and markdown wrap convention
- AGENTS.md: require merging PRs with a merge commit (gh pr merge
  --merge), never squash/rebase — the per-PR commit history (review-fix
  and regression-test commits) is intentional record.
- Add scripts/verify-md-wrap.ts: a doc-sync gate that fails on
  hard-wrapped prose paragraphs (one physical line per paragraph), with
  smart exemptions for fenced code, tables, lists, blockquotes, headings,
  HTML comments, hrs, and reference defs. Scope covers README.md,
  docs/**/*.md, packages/*/README.md, plus AGENTS.md / packages/AGENTS.md
  (the files doc-sync did not previously cover). Folded into doc-sync so
  it rides the existing pre-push and CI gates.
- Sync AGENTS.md and docs/development.md doc-sync descriptions and
  command lists to include verify-md-wrap.
2026-06-16 22:14:25 +08:00
Tianyi Cui 19518ebfd9 Merge pull request #43 from deepseek-ai/worktree-module-graph
feat: generated module dependency graph with freshness gate
2026-06-16 22:03:17 +08:00
Tianyi Cui efee449cfe feat(session-persistence): preserve interrupted turns on crash; don't truncate (review #33)
A crash can leave a durable log whose final turn never closed. The old
behavior truncated everything after the last turn/end as a "crash tail".
But a single turn can be HUGE in a long-horizon task (many steps, large
tool output), so truncating it silently destroys real, durably-written
work — truncating a turn is wrong.

New crash recovery (ADR 0018): load() PRESERVES the interrupted turn's
events and CLOSES the orphaned turn by durably appending synthetic
boundary events — a step/end if a step was open, then a turn/end carrying
the new merge-extensible TurnEndReason {kind:'interrupted'}. load()
returns the balanced log, so a resumed session is immediately usable. Only
a never-fully-written TORN tail fragment is discarded; corruption in the
committed region is still unloadable.

- dsh-session: TurnEndReason {kind:'interrupted'} + shared
  interruptedTurnClosers() repair helper.
- JSONL backend: scanLog preserves the longest contiguous prefix
  (including a partial final turn); loadCore truncates a torn fragment and
  durably writes the closers, returning the balanced log.
- runPersistenceContract gains a crash-recovery test (both backends + mock).
- Docs: ADR 0018/0017, architecture.md, package READMEs.

Also (review #33): RFC 013 records the "move event vocabulary to Zod"
question (merge-extensible maps → runtime schema registry) + blast radius;
deferred, not done here.
2026-06-16 21:27:50 +08:00
Tianyi Cui 8e6fd91e15 docs: document module-graph gate; unwrap generated prose
Codex review follow-up:
- Update docs/development.md: add verify-module-graph to the pre-push and
  CI gate lists and gen/verify-module-graph to the daily commands, so the
  contributor guide matches the new freshness gate.
- Emit the module-graph.md intro paragraph as a single line (repo Markdown
  convention: one line per paragraph), since regenerating would otherwise
  reintroduce hard wrapping.
2026-06-16 21:21:21 +08:00
Tianyi Cui b33668ef05 refactor: order module-graph table topologically (low→high level)
Replace the alphabetical package ordering with a Kahn-style topological
sort (alphabetical tiebreak within each layer to stay deterministic), so
the table and graph list leaf interfaces first and dependents last.
2026-06-16 21:11:09 +08:00
Tianyi Cui 4c8c1da8b3 feat: generate module dependency graph with freshness gate
Add scripts/gen-module-graph.ts, which derives the inter-package
dependency graph from each package's @deepseek-ai/dsh-* peerDependencies
and renders docs/module-graph.md (a GitHub-native Mermaid graph plus a
dependency table). Output is deterministic so a regenerate-and-diff
check is stable.

Wire a freshness gate the same way doc-sync is wired (ADR 0007: hooks
and CI run the same package.json scripts): verify-module-graph runs in
pre-push (lefthook) and as a CI step. It fails if the committed file
drifts from what the generator would produce.
2026-06-16 21:00:24 +08:00
Tianyi Cui 3bef6b38c7 refactor(session-persistence-sqlite): drop the materialized column; use row existence as the signal (review #35)
The `materialized` INTEGER column was redundant: create()/update() already
keep a lazy session in memory and write no row, so a `sessions` row is
written only by the first append. Its EXISTENCE is the materialization
signal — has()/list() now report exactly the sessions that have a row,
matching the JSONL backend's "file exists ⇔ materialized".

The column only existed to force has()/list() to FALSE for an all-tail
crash (a partial first turn, zero committed events). That actually
DIVERGED from the JSONL backend, whose file (and thus has()=true) survives
a first append that never reached turn/end. Removing the column drops that
special case: an all-tail session keeps its row and stays present, the
same as JSONL. The orphaned tail rows are still removed by the deferred
truncation-repair on the next append, and load() stays non-mutating.

Also: add a TODO to route through a cordis db service if one is adopted,
and correct the README's Node-version framing to the repo's engines (>=24).
2026-06-16 20:35:01 +08:00
Tianyi Cui f06259928f Merge pull request #31 from deepseek-ai/split/session-meta
feat(session): metadata seam + JSON-serializability invariant (split 1/5)
2026-06-16 20:11:34 +08:00
Tianyi Cui c27b1bba6e Merge branch 'split/agent-factory' into split/session-persistence-sqlite
# Conflicts:
#	yarn.lock
2026-06-16 17:09:39 +08:00
Tianyi Cui f40dcb5f7b Merge branch 'split/session-persistence' into split/agent-factory
# Conflicts:
#	docs/adr/README.md
#	packages/agent-loop/package.json
#	yarn.lock
2026-06-16 17:04:54 +08:00
Tianyi Cui 96331432b8 Merge branch 'split/turn-enclosure' into split/session-persistence
# Conflicts:
#	docs/adr/README.md
#	packages/agent-loop/package.json
#	yarn.lock
2026-06-16 17:01:36 +08:00
Tianyi Cui c4fd22f0fa Merge branch 'split/session-meta' into split/turn-enclosure
# Conflicts:
#	docs/adr/README.md
2026-06-16 16:53:37 +08:00
Tianyi Cui 3a6ebd1954 docs(session): renumber session-persistence ADR reference 0016→0018
master's pnpm migration claimed ADR 0016 (0016-pnpm-over-yarn), which
collides with this stack's session-persistence ADR. Renumbering the
session-persistence ADR to 0018 (turn-enclosure stays 0017); update the
json.ts module-doc reference accordingly.
2026-06-16 16:51:59 +08:00
Tianyi Cui a7fbd93f4f Merge remote-tracking branch 'origin/master' into split/session-meta 2026-06-16 16:45:32 +08:00
Tianyi Cui 5e36f990ab Merge pull request #39 from deepseek-ai/feat/pnpm
feat: migrate yarn to pnpm
2026-06-16 16:31:43 +08:00
Tianyi Cui 6c931b8869 docs: tighten allowBuilds comments 2026-06-16 16:25:24 +08:00
Tianyi Cui ce484c0e2d build: deny build scripts for unused pi-ai transitive deps
@google/genai and protobufjs are pulled in transitively by the
dsh-llm-pi-ai adapter and consumed prebuilt; their lifecycle scripts are
no-ops we don't need. Set them to false in allowBuilds (was true) so no
unnecessary install-time code runs, and document why each entry is the
way it is.
2026-06-16 16:17:03 +08:00
Tianyi Cui 49e74ed8d0 docs: add ADR 0016 for the pnpm migration
Records the why behind the Yarn 4 → pnpm move (PR #39): ecosystem
alignment, strict-linker phantom-dependency safety, build-script
allowlisting, and the package-manager-independent constraints script.
Captures migration-time install benchmarks and notes the gate suite
passes unchanged on pnpm.
2026-06-16 15:55:21 +08:00
07akioni dabc2ff411 feat: migrate to pnpm 2026-06-16 14:55:37 +08:00
Tianyi Cui 11e166db71 Merge branch 'split/agent-factory' into split/session-persistence-sqlite 2026-06-16 00:44:19 +08:00
Tianyi Cui b218e027d5 Merge branch 'split/session-persistence' into split/agent-factory 2026-06-16 00:44:10 +08:00
Tianyi Cui 1b1385e4f7 fix(session-persistence-jsonl): never wedge a published log on temp-cleanup failure (review #33)
materialize() removed the temp hard link in a finally that ran BEFORE
syncDir() and before state.materialized/cursor advanced. If link()
succeeded (log published) but the temp rm then threw, materialize()
rejected after publishing — leaving state.materialized false, so the
buffered events stayed unpersisted and every retry wedged on the
"already exists" exists() backstop.

Restructured to the robust shape: track link() success; on link failure
remove the temp (the only reference) before propagating; on success
fsync the directory, mark materialized, THEN best-effort remove the
now-redundant temp link (a leftover *.tmp is harmless and never read).
A temp-rm failure can no longer reject a session whose log published.
2026-06-16 00:43:40 +08:00
Tianyi Cui d76d02b666 Merge branch 'split/session-persistence' into split/agent-factory
# Conflicts:
#	docs/adr/0016-session-persistence.md
2026-06-16 00:40:10 +08:00
Tianyi Cui 9838fdb626 docs(adr-0016): soften 'typed error' to 'clear error' (review #34)
The resume seam intentionally throws a plain Error (the JSDoc and #34
were aligned to "clear error"). Match the ADR 0016 prose, which still
said "typed error". Docs-only; no behavior change. (Also carries the
#32 finalizer-containment fixes via the forward merge.)
2026-06-16 00:38:55 +08:00