Skip to content

feat(core): let the agent consult its own past sessions - #272

Open
oratis wants to merge 1 commit into
mainfrom
feat/session-search
Open

feat(core): let the agent consult its own past sessions#272
oratis wants to merge 1 commit into
mainfrom
feat/session-search

Conversation

@oratis

@oratis oratis commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Fifth implementation PR from docs/DSH_ADOPTION_PLAN.md §1.5.

The gap

Every session is already written to ~/.deepcode/sessions as JSONL. Nothing could read it back. "How did we fix this CI failure last month" was unanswerable, while the answer sat in a file the agent itself wrote.

Having the whole history on the machine is the one structural advantage a local agent has over a hosted one, and it was going unused.

SessionSearch finds text across past sessions, newest first, returning excerpts tagged with a session id and message offset. SessionRead follows a hit into the surrounding conversation. Together they are Grep and Read, for history.

Scope is the decision here, not search

This is the part worth reviewing. Searching every session on the machine would let a session opened in one project pull another project's code, client names, or credential fragments into the current context. That is a privacy surface, not a convenience knob.

So the default — and, unless the user changes a setting, the only behaviour — is the current workspace, matched on a resolved path boundary so /a/project cannot capture /a/project-two.

Neither tool takes a scope argument, and there is a test asserting the schema has no third property. A parameter would let the model consent to reading another project's history on the user's behalf, which is not the model's consent to give. SessionRead enforces the same rule independently, so knowing an id is not authorisation — otherwise scoping would be a suggestion rather than a rule.

No index

dsh backs its equivalent with SQLite FTS. Skipped: a few thousand JSONL files scan in tens of milliseconds, and an index is a second copy of the truth that can disagree with it — plus a schema version to migrate and a native dependency to ship. If volume ever outgrows a scan, an index goes behind this same interface with no caller change.

A corrupt or half-written session is skipped rather than failing the search: it is one of many, and the rest are still worth returning.

Verification

pnpm typecheck, lint, format:check clean. Full suite green — core 1062 passed / 28 skipped, cli 242, desktop 104, server 49, protocol 34, lsp 13, vscode 12, scripts 42.

28 new tests. The ones carrying weight are the negative ones: another workspace stays invisible by default, SessionRead refuses a known id from another workspace and does not leak its content in the error, /a/project-two is not inside /a/project, and the running session is excluded from its own results.

Related

While writing these tests I found new SessionManager(sessionsRoot) silently ignores a positional argument (the constructor wants { root }) and falls back to the real ~/.deepcode/sessions. A test I added in #268 was doing exactly that and writing into the user's home directory on every run — fixed on that branch, and this PR's tests use the correct form throughout.

Every session is already on disk as JSONL. Nothing could read it back, so "how
did we fix this CI failure last month" was unanswerable — while the answer sat
in a file the agent itself wrote. Having the history locally is the one thing a
local agent has over a hosted one, and it was going unused.

SessionSearch finds text across past sessions, newest first, and returns
excerpts tagged with a session id and message offset. SessionRead follows a hit
into the surrounding conversation. Together they are Grep and Read for history.

Scope is the load-bearing decision, not the search. Searching every session on
the machine would let a session opened in one project pull another project's
code, client names, or credential fragments into this context. So the default —
and, unless the user changes a setting, the only behavior — is the current
workspace, matched on a resolved path boundary so /a/project does not capture
/a/project-two.

Neither tool takes a scope argument. That is deliberate: a parameter would let
the model consent to reading another project's history on the user's behalf.
SessionRead enforces the same rule, so knowing an id is not authorization.

No index. A few thousand JSONL files scan in tens of milliseconds, and an index
is a second copy of the truth that can disagree with it. If the volume ever
outgrows a scan, an index goes behind this same interface.

A corrupt or half-written session is skipped rather than failing the search: it
is one of many, and the rest are still worth returning.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant