Repository navigation
Room archive and search: a local memory of a room's whole history - #137
Merged
Merged
Conversation
petrus, 1 Oct 2026: "can you index the room so you have a memory here? add it to IAK". - src/room-archive.mjs: one append-only JSONL per room (~/.ide-agent-kit/room-archive, or room_archive.dir), deduped by id on load, tolerant of a torn last line. Sync pages backwards with an URL-encoded before= (newest-first pages of 100), stops on overlap with the archive, and refuses to loop when the server ignores before=. Search: all words or a regex, from / since / until filters, newest first, and a scope report (0 hits = not in the archive). - MCP tools room_search and room_archive_sync; CLI `rooms search` and `rooms archive`. - Safety (codexmb review notes): only rooms in this config are archived, never DMs; output is labelled untrusted evidence; credential-looking values are redacted in tool output and the kinds are reported, while the archive itself stays verbatim; nothing lands in the repo. Measured: 40,000 messages of thinkoff-development archived back to 2026-02-27 in 590 s; an incremental sync takes one page. Tests: 9 new, 823/823 total pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
To use Codex here, create an environment for this repo. |
…all fields, owner-only files, fail closed on refused access All from @codexmb's review of #137 at f318eb0; each has a test that fails on that head: - A sync whose page budget ran out before reaching the archive left m100..m199 missing forever. It now records where it stopped (<room>.state.json) and the next sync walks on until it meets the older block; gapPending reports an open hole. - A crash fragment without a newline swallowed the next appended record; a newline now closes the fragment before appending. - room_search redacted only the body; every string field of a hit is redacted now. - New files were 0644 under the default umask; directory 0700 and files 0600, and existing archives are tightened on load. - A sync that the server refused (401/403/404) used to fall back to cached hits; it now returns no archived text. sync:false still checks access with a one-message request. A network failure still returns the archive, labelled stale. - README and the tool note now say the archive is a snapshot (later edits and deletions are not reconciled). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… end From @codexmb's re-review of d5a9acf: - A second page-budget exhaustion before an older gap healed overwrote the single gapCursor, so the older hole (m100..m199 in his reproduction) never filled. Gaps are now a list in the state file (the old single-cursor format is migrated); each heals on its own. The reproduction is a test and fails on d5a9acf. - The refused-access path was only inspected, not exercised. The room_search body moved into an exported roomSearchTool(), and tests drive it with a fake server: 401/403/404 with sync on and off return an error and no archived text; a network failure returns the archive labelled stale; an unconfigured room is refused before any request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From @codexmb's third review (c4f2669): the resume state was saved only when a sync finished, so a network error after page 1 left that page archived above an unrecorded hole; the next sync overlapped it and stopped, and m100..m599 never came back. Gap state is now written per page, in the order that survives a crash at any point: - opening a gap records it BEFORE the page is appended (the hole lies below that page); - healing appends the page BEFORE the cursor moves (a crash re-fetches the page, appends are idempotent by id; moving first would skip the page for good). Tests: his failure-after-page-1 reproduction (fails on c4f2669), and a crash mid-heal, which fails on the cursor-first ordering. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd state save From @codexmb's fourth review (4554d22): stage 1 advanced an existing gap cursor BEFORE the page was appended, so a crash in between skipped that page (m500..m599 in his reproduction). Reworking the order exposed a deeper flaw: healing stopped when a page held an id already in the archive. A crash after a page was saved but before its cursor moved made the next run re-fetch that page, see known ids, and close the gap with the hole below still open. - A gap is now { before, until }: `until` is the newest message of the archived block below the hole, fixed when the gap opens, and healing stops by time, so re-fetching a saved page can never close a gap early. Bare-cursor state files from earlier commits are migrated. - A gap is opened before its first page is saved and advanced only after each further page is on disk; healing saves the page before moving the cursor. - New exhaustive test: a crash injected at every add() and every saveState() call of a gap-producing workload (28 crash points), restart, and require all 900 messages. It fails on 4554d22 at add #2; the two targeted reproductions are kept too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h-safety scope precisely From @codexmb's fifth review (fa4dfa4): with maxPages 1 the only page always went to the newest-end overlap, so a pending gap never healed. With gaps pending, one page per call is now kept for healing (budget >= 2), or newest and healing calls alternate (budget 1). Test: reload, then maxPages 1 calls heal m100..m199 and still pick up a new message at the newest end; it fails on fa4dfa4. The module header now says exactly what the crash tests prove: an exception at every page save and state save recovers fully; that is not a power-loss/fsync guarantee (writes are not fsynced). It also restates the snapshot, offline-stale and refusal policies and that gaps from older state files keep the id-based stop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
petrus asked on 1 Oct 2026: "can you index the room so you have a memory here? add it to IAK".
What it adds
src/room-archive.mjs: a durable local archive of a configured GroupMind room.~/.ide-agent-kit/room-archive/<room>.jsonl(orroom_archive.dir). It is deduped by id on load and tolerates a torn last line.before=. Pages are newest-first, 100 each, and an unencoded+00:00makes the server answer 500.before=, the sync stops instead of looping.from/since/untilfilters, newest first. It reports its scope, so "0 hits" reads "not in the archive", never "never said".room_searchandroom_archive_sync.ide-agent-kit rooms search "words"androoms archive [--backfill].Safety (from @codexmb's review notes)
poller.rooms,mcp.confirmations.room). A slug the config doesn't know is refused, and DMs are never archived.SECRET_PATTERNS) are redacted in tool output, and the kinds are listed, so redaction is never silent. The archive file itself stays verbatim as evidence, and it lives outside the repo.Measured
rooms search "5 inch display" --from petrusfound 6 matches across 40,004 messages, with ids and timestamps.Tests
test/room-archive.test.mjs:before=;before=URL encoding.npm test: 823/823 pass.🤖 Generated with Claude Code