feat: session history searchable by agents — live+archived, retention (#491) - #495
Merged
Conversation
…n sweep (#491) Make session history cover live sessions and stop it growing unbounded: - `GET /api/history/search` gains `include_live` (default true): on top of the archived FTS hits, each running session's scrollback is scanned on-demand (last `history_live_scan_bytes`, ANSI-stripped, same AND-of-terms match) and appended with `live: true`. Zero DB writes; the combined list is held to `limit`. `SearchResult` gains a `live` flag (archived rows default false). - `GET /api/history/{id}/log` gains `strip_ansi` (default false) so an agent can read plain text; the byte counters still describe the raw tail window. - `history_retention_days` config (default 30, 0 = forever) + a daily retention sweeper mirroring the assistant-log sweeper, bounding both `history.db` and `session-logs/`. `history_live_scan_bytes` (default 256 KiB) tunes the live scan. Tests: pure unit tests for the live-scan helpers (query_tokens, live_match, truncate_chars) + integration tests for live-before-exit search and strip_ansi. Docs: ENGINE.md config table and history API section. Refs #491 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7eWQbHi8HyLyhaRA2piEn
…, list (#491) Agents could not reach any session history: the engine adapter is metadata-only and there were no history read ops. Add three, generated onto MCP/CLI/REST from the registry (scope=read, no reason): - session.search_output (mcp session_search_output, cli `session search`) — full-text over session output, live sessions included by default; each hit carries `live`. - session.log_tail (`session log`) — the tail of one session's output log, ANSI-stripped by default; works for live sessions too. - session.history_list (`session history`) — the archived-session listing. Static GET paths with the id as a query field (the house convention — the registry has no path params). Each result carries the FR-E9 `engine` field: no engine, or an unreachable one, sets it and returns an empty view rather than an error that reads as "no history". Engine client gains history_sessions / search_history / history_log plus their dataclasses; the "six things" narrative becomes nine. Tests: parity SCRIPT + stand-in engine history routes (CLI/REST/MCP agree); unit tests for mapping, query-param forwarding, missing-log, and the no-engine / dead-engine degrade paths. Docs: AGENT_GUIDE history-search section. Refs #491 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7eWQbHi8HyLyhaRA2piEn
… hits (#491) History search was archive-only, so a running session's output could not be found even though its rows already appear in the list (#477). Pass `include_live=true` to the search endpoint, add the `live` flag to `HistorySearchResult`, badge a live hit "Live" in the results, and retire the "not yet in the search index" caveat — it now says running sessions are searched too. Pin the result date hard-right so the badge groups with the name. Tests: a live hit badges and the search carries include_live=true; the caveat assertion updated. Refs #491 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7eWQbHi8HyLyhaRA2piEn
thedancingdeveloper
force-pushed
the
feat/491-history-search
branch
from
September 1, 2026 00:39
500884c to
7f8a1fe
Compare
8 tasks
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.
Closes #491.
Operators could search archived terminal output in the History GUI, but agents could not reach any of it, and "history" meant dead sessions only. This makes all session history — live and archived — searchable by agents (MCP), scripts (HTTP/REST), and operators (GUI), and stops the store growing unbounded.
Built in three layers, one commit each.
Engine (Rust)
GET /api/history/searchgainsinclude_live(default true): on top of the archived FTS hits, each running session's scrollback is scanned on-demand (lasthistory_live_scan_bytes, ANSI-stripped, same AND-of-terms match) and appended withlive: true. Zero DB writes — chosen over incremental FTS indexing, which is ~O(n²) write amplification for ~30 MB sessions (see design). Combined list held tolimit.GET /api/history/{id}/loggainsstrip_ansi(default false) so an agent reads plain text; byte counters still describe the raw tail window.history_retention_daysconfig (default 30,0= forever) + a daily retention sweeper mirroring the assistant-log sweeper, bounding bothhistory.dbandsession-logs/.history_live_scan_bytes(default 256 KiB) tunes the scan.vogt-core (Python)
Three registry read ops (scope=read, no
reason), generating MCP + CLI + REST at once:session.search_outputsession_search_outputsession searchGET /sessions/history/searchsession.log_tailsession_log_tailsession logGET /sessions/logsession.history_listsession_history_listsession historyGET /sessions/historyStatic GET paths with the id as a query field (the house convention — the registry has no path params). Each result carries the FR-E9
enginefield: no engine, or an unreachable one, sets it and returns an empty view rather than an error that reads as "no history". Tests: parity (CLI/REST/MCP agree) + unit tests for mapping, query-param forwarding, missing-log, and degrade paths.GUI (web)
The live-list/badge/live-replay already landed on dev (#477); the remaining gap was search. History search now passes
include_live=true, badges a live hit Live, and the "not yet in the search index" caveat is retired.Deliberately out of scope
The readable-transcript replay renderer (
historyReplay.ts) is PR #493 (#490), not duplicated here to avoid a merge collision; #491 uses the enginestrip_ansifor the agent path instead.Verification
cargo test --allgreen (234 lib + 87 integration + others), fmt + clippy clean.uv run pytest1313 passed / 91% coverage; mypy + ruff clean.pnpm typecheckclean; 849 tests;pnpm buildclean.Design doc (operator-local, git-ignored):
docs/local/SESSION_HISTORY_SEARCH_DESIGN.md. Docs updated:ENGINE.md(config table + history API),AGENT_GUIDE.md(history-search section).🤖 Generated with Claude Code