Skip to content

feat: session persistence + streaming during --deep (v0.9.0)#26

Merged
askalf merged 1 commit into
masterfrom
claude/plan-deep-dive-next-7b8Lx
May 7, 2026
Merged

feat: session persistence + streaming during --deep (v0.9.0)#26
askalf merged 1 commit into
masterfrom
claude/plan-deep-dive-next-7b8Lx

Conversation

@askalf
Copy link
Copy Markdown
Owner

@askalf askalf commented May 7, 2026

Summary

Two features that compose well: every run is now saved to disk, and --deep mode finally streams.

1. Session persistence

Every successful agent run is saved to ~/.deepdive/sessions/<id>.json with the full plan, round trace, kept sources with extracted content, answer, verification report, and cost — enough to re-synthesize without touching the network.

Three new subcommands:

deepdive sessions ls                         # newest first; id, age, source/round counts, question
deepdive show <id>                           # re-print the original markdown answer
deepdive resume <id> [<new-question>]        # re-synthesize against the saved sources

resume is the headline. One LLM call, against the saved source corpus, with the original or a refined question. No re-search, no re-fetch, no critic loop. The cache solved "don't re-fetch URLs"; sessions solve "don't re-run the entire pipeline." Refining a question into a follow-up costs one synthesis call instead of plan + N×search + N×fetch + N×synth + N×critique.

IDs are timestamp-prefixed (YYYY-MM-DD_HHMMSS_<8-hex>), sortable chronologically. resolveSessionId accepts unique prefixes — deepdive resume 2026-05-07_134509 works when only one session matches. After every run a one-line stderr hint prints the id + the resume command.

Atomic .tmp + rename writes mirror the cache module. Persistence failures are non-fatal warnings.

2. Streaming during --deep

Previously auto-disabled because intermediate rounds would print multiple full drafts back-to-back with no separation. v0.9 enables it with a clear UX:

  • Round 0 streams under the question's H1 (current single-pass behavior, unchanged).
  • Round 1+ each get a \n\n---\n\n## Round N (deep)\n\n separator before tokens start.
  • The terminal scrolls naturally as each round's draft writes itself.
  • --out=file.md still gets only the final answer; intermediate drafts are visible in the terminal but not persisted there.
  • --no-stream and --json continue to suppress streaming.

What's added

  • src/sessions.ts (~190 lines, no new runtime deps): generateSessionId, saveSession, loadSession, listSessions, resolveSessionId, renderSessionsList, defaultSessionsDir, humanDuration + SessionRecord / SessionMeta types
  • New CLI subcommands: sessions ls, show <id>, resume <id> [<q>] (parseArgs gains a small extras: string[] array, used only when the verb is doctor/sessions/show/resume)
  • New CLI flag --no-sessions; env vars DEEPDIVE_NO_SESSIONS, DEEPDIVE_SESSIONS_DIR
  • AgentResult.sources type tightened from Source[] to SourceWithContent[] — runtime value was always SourceWithContent, so this is type-only and non-breaking
  • RuntimeConfig.sessions: { enabled, dir }
  • README "Sessions" section + flag-table row + a paragraph in the --deep section explaining the new round-header streaming UX
  • CHANGELOG entry under v0.9.0; package.json bumped

What's explicitly out of scope (v1)

  • deepdive resume <id> --continue — running another round of search/critique against an existing session. Defer; the v1 surface is "re-synthesize cheap." Continuation is a coherent v2 feature with its own design conversation.
  • Session export/import (compressed bundle for sharing / archival).
  • Auto-pruning of old sessions / size cap.
  • A --session=<id> flag that resumes inside the main run path (subsumed by the dedicated resume verb for now).

Test plan

  • 15 unit tests in test/sessions.test.mjs covering: ID format, ID uniqueness across same-second calls, save/load round trip, atomic write (no .tmp left over), schema rejection, listSessions sorting + bad-file surfacing + missing-dir tolerance, prefix resolution including ambiguity error and no-match error, renderSessionsList empty + non-empty cases
  • 6 parseArgs tests: sessions/show/resume verbs land in extras, doctor still works, --no-sessions, sanity check that bare unquoted multi-word questions still throw
  • 2 config tests: sessions enabled/disabled, DEEPDIVE_SESSIONS_DIR override
  • Existing streaming-config test updated to reflect that --deep no longer disables streaming
  • npm run typecheck clean
  • npm run build clean
  • npm test — 378/378 passing (up from 355)
  • CLI smoke test: --help shows new verbs + flag; sessions ls against an empty dir prints the bootstrap hint

Two features:

1. Sessions (src/sessions.ts, ~190 lines, no new runtime deps)
   Every successful run is saved to ~/.deepdive/sessions/<id>.json with
   the full plan, round trace, kept sources (with content), answer,
   verification report, and cost. Three new subcommands:
   - `deepdive sessions ls` — newest first, columns: id age src/round
     model truncated-question
   - `deepdive show <id>` — re-render the original markdown answer
   - `deepdive resume <id> [<new-question>]` — re-synthesize against
     the saved sources (one LLM call), optionally with a refined
     question. No re-search, no re-fetch, no critic loop. The cache
     stops re-fetching pages; resume stops re-running the pipeline.

   IDs are timestamp-prefixed (YYYY-MM-DD_HHMMSS_<8-hex>) so they sort
   chronologically. resolveSessionId accepts unique prefixes. Atomic
   .tmp + rename writes mirror the cache module. Per-run stderr hint
   shows the id + the resume command. Persistence failures are
   non-fatal warnings.

   New CLI flag --no-sessions; env vars DEEPDIVE_NO_SESSIONS and
   DEEPDIVE_SESSIONS_DIR.

   AgentResult.sources type tightened from Source[] to
   SourceWithContent[] — the runtime value was already
   SourceWithContent, so this is type-only and non-breaking
   (SourceWithContent extends Source).

2. Streaming during --deep mode
   Previously streaming was auto-disabled in deep mode because
   intermediate rounds would print multiple full drafts back-to-back
   with no separation. Now each round-after-the-first streams under a
   `\n\n---\n\n## Round N (deep)\n\n` separator. The terminal scrolls
   as each round writes itself; --out=file.md still gets only the
   final answer. --no-stream and --json continue to suppress streaming.

23 new tests (15 sessions: id format, round-trip, atomic write, schema
rejection, list with bad files, prefix resolution, render; 6 parseArgs:
sessions/show/resume verbs in extras, --no-sessions, sanity check that
bare unquoted multi-word questions still throw; 2 config: sessions
enabled/disabled and DEEPDIVE_SESSIONS_DIR override). Suite goes from
355 → 378. Existing streaming-config test updated to reflect v0.9 behavior.

v0.9.0.
@askalf askalf merged commit b1b2857 into master May 7, 2026
5 checks passed
@askalf askalf deleted the claude/plan-deep-dive-next-7b8Lx branch May 7, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants