feat: session persistence + streaming during --deep (v0.9.0)#26
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two features that compose well: every run is now saved to disk, and
--deepmode finally streams.1. Session persistence
Every successful agent run is saved to
~/.deepdive/sessions/<id>.jsonwith 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:
resumeis 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.resolveSessionIdaccepts unique prefixes —deepdive resume 2026-05-07_134509works when only one session matches. After every run a one-line stderr hint prints the id + the resume command.Atomic
.tmp+renamewrites mirror the cache module. Persistence failures are non-fatal warnings.2. Streaming during
--deepPreviously auto-disabled because intermediate rounds would print multiple full drafts back-to-back with no separation. v0.9 enables it with a clear UX:
\n\n---\n\n## Round N (deep)\n\nseparator before tokens start.--out=file.mdstill gets only the final answer; intermediate drafts are visible in the terminal but not persisted there.--no-streamand--jsoncontinue to suppress streaming.What's added
src/sessions.ts(~190 lines, no new runtime deps):generateSessionId,saveSession,loadSession,listSessions,resolveSessionId,renderSessionsList,defaultSessionsDir,humanDuration+SessionRecord/SessionMetatypessessions ls,show <id>,resume <id> [<q>](parseArgs gains a smallextras: string[]array, used only when the verb is doctor/sessions/show/resume)--no-sessions; env varsDEEPDIVE_NO_SESSIONS,DEEPDIVE_SESSIONS_DIRAgentResult.sourcestype tightened fromSource[]toSourceWithContent[]— runtime value was alwaysSourceWithContent, so this is type-only and non-breakingRuntimeConfig.sessions: { enabled, dir }--deepsection explaining the new round-header streaming UXpackage.jsonbumpedWhat'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=<id>flag that resumes inside the main run path (subsumed by the dedicatedresumeverb for now).Test plan
test/sessions.test.mjscovering: ID format, ID uniqueness across same-second calls, save/load round trip, atomic write (no.tmpleft over), schema rejection, listSessions sorting + bad-file surfacing + missing-dir tolerance, prefix resolution including ambiguity error and no-match error,renderSessionsListempty + non-empty casesextras,doctorstill works,--no-sessions, sanity check that bare unquoted multi-word questions still throwDEEPDIVE_SESSIONS_DIRoverride--deepno longer disables streamingnpm run typecheckcleannpm run buildcleannpm test— 378/378 passing (up from 355)--helpshows new verbs + flag;sessions lsagainst an empty dir prints the bootstrap hint