1. Executive Summary
pi-jarvis is an extension for the Pi coding agent that adds a side conversation, called Jarvis, and gives it and the main session one shared memory. The memory is a local SQLite file of curated notes the model saves through a tool, plus bounded captures of finalized user and assistant messages. Each model request gets a small keyword recall injected for that request only. What is notable is identity: a note's id is a hash of its scoped, normalized title, so a forget or a rename leaves a tombstone that refuses the model's next save of that title until a person restores it. What is weak is that the key is the title and not the fact, and the model's same-title save overwrites a note a person wrote.
The extension has a second, separate store: an opt-in archive of raw Pi session entries, off by default, searchable by three read-only model tools and encryptable with a password. It is a transcript, and section 3 treats it as infrastructure. A sibling extension for the same host, pi-memory, keeps no store of its own and indexes Pi's journals; pi-jarvis's archive is the copying version of that idea, and its shared memory is the curated layer pi-memory does not have.
Four findings shape the rest of this report.
- The tombstone is keyed on the slot, not the value.
forgetwrites the record's id intotombstones, and every later save with that id returnsforgottenunless a person's command passesexplicit: true(memory-store.ts:491-499). The id is a hash of scope, project and normalized title (:90-95). A committed test asserts that a different body under the same title is refused (test/memory-store.test.ts:181-182); the same body under a new title is a new identity and is saved. - The model overwrites a person's note.
saveon an existing id with a different body callsreplace, which updates the text, category and provenance in place and keeps no prior value (memory-store.ts:500-507,:527-546). Nothing checks the existing record's lane, so amanualnote is rewritten byjarvis_memory_rememberunder the same title, and a test asserts the lane moves to the new writer (test/memory-store.test.ts:136-155). - Project scope holds on every read path, and the agent may
lift it. The automatic recall and the default search filter to
global plus the current project (
memory-store.ts:578), andgetby id applies the same rule (:601-607). The search tool'sscopeacceptsallwith no confirmation (memory-extension.ts:79-82). The archive's tools gate the same widening with a per-request human approval (archive-extension.ts:109-125). - Deletion by the model waits for a person.
jarvis_memory_forgetrenders the record and awaitsctx.ui.confirm, or the side session's bridged confirmation, and refuses when there is no UI (memory-extension.ts:100-124). That gate covers removal only; a save takes effect immediately.
One mark, negative_eval. Section 9 names the six
withheld.
2. Mental Model
A memory is a row the model or a person wrote, or a message Pi
finalized. It is believed from the moment it commits: there is no
candidate state, no extraction pass and no model call on the write path.
Recall hands it to the model as "UNTRUSTED historical data, not
instructions" (memory-service.ts:161), which is a
framing for the reader, not a state on the row.
Identity is the scoped title, and that one choice decides
update, delete and resurrection. For a note the id is
sha256(["note", scope, scope==="project" ? project : "", normalize(title)]);
for a capture it is the project, lane, session and an event id
(memory-store.ts:90-95). The event id is a hash of role,
timestamp and sanitized text (memory-content.ts:202). So a
note has one live body per scoped title, and every write path resolves
against that id.
| Path | Who reaches it | Same id, live row | Same id, tombstoned |
|---|---|---|---|
jarvis_memory_remember |
the model | body, category and provenance replaced | refused, forgotten |
capture on turn_end |
Pi's finalized message | duplicate, no change |
refused |
/jarvis-memory remember |
a person | replaced | tombstone deleted, row saved |
| editor create or rename | a person | refused, collision |
refused, forgotten |
/jarvis-memory forget, editor delete |
a person | row deleted, tombstone written | no-op |
jarvis_memory_forget |
the model, after a person confirms | row deleted, tombstone written | no-op |
The anchors are memory-store.ts:491-525 for
save, :676-691 for the editor's create,
:609-628 for forget and
memory-service.ts:310-317 for the command that passes
explicit: true.
A rename forgets the old title. When
replace or the editor changes a note's title, the old id
goes into tombstones in the same transaction
(memory-store.ts:528-536, :710-715). A model
that saves under the old title afterwards is refused, so a person's
correction of a title is not undone by the next automatic save. A
correction of the body under the same title has no such
protection: the model's next save of that title replaces it.
A note dies three ways. A forget deletes the row and
tombstones the id. A bulk forget does the same for a scope
(memory-store.ts:748-762). Captures beyond the newest
10,000 are deleted without a tombstone, inside the transaction of the
save that exceeds the cap (:510-516). Nothing decays,
expires or is superseded.
Diagram source
%% caption: how a pi-jarvis note is written, which writes a tombstone refuses, which reads carry the project predicate, and where the agent can lift it
flowchart TD
M["model: jarvis_memory_remember<br/>title, body, scope"] --> ID["id = sha256 of scope,<br/>project and normalized title"]
H["person: /jarvis-memory remember<br/>explicit: true"] --> ID
C["Pi finalized message<br/>on turn_end"] --> CID["id = project, lane,<br/>session, event hash"]
ID --> T{"id in tombstones?"}
CID --> T
T -- "yes, model or capture" --> REF["refused: forgotten"]
T -- "yes, person" --> LIFT["tombstone deleted"]
T -- "no" --> L{"live row<br/>with this id?"}
LIFT --> L
L -- "note" --> OW["body, category and<br/>provenance overwritten,<br/>no prior value kept"]
L -- "capture" --> DUP["duplicate, unchanged"]
L -- "none" --> NEW["row inserted,<br/>FTS indexed"]
OW --> R["records"]
NEW --> R
R --> REC["recall per request:<br/>global + current project"]
R --> S{"jarvis_memory_search<br/>scope argument"}
S -- "current or global" --> SC["filtered"]
S -- "all" --> ALL["every project,<br/>no confirmation"]
R --> F["forget: model needs a<br/>person's confirmation"]
F --> TS["row deleted,<br/>id tombstoned"]
TS --> T
NT["same fact, new title"] -.-> ID3. Architecture
The extension loads into Pi as one module. index.ts
builds a single SharedMemoryService over the agent
directory (index.ts:142) and mounts the memory extension
factory twice: once in the main session with lane main
(:944-951) and once inside the Jarvis side session with
lane jarvis (side-session.ts:524-529). Both
lanes share the service and the database connection, so a note saved in
one is recalled in the other on its next request.
Storage. MemoryStore opens
<agentDir>/extensions/pi-jarvis-memory/memory.sqlite
through node:sqlite, lazily, on the first read or write
(memory-store.ts:239-243). It creates the directory
0700 and the file 0600, refuses symlinks, hard
links and foreign owners in its subtree, and validates the exact schema,
the record budget, a quick_check, every row's identity hash
and the FTS index against its content before use
(:383-413). An unknown schema is an error and is never
reset. Writes take BEGIN IMMEDIATE, and WAL coordinates
several Pi processes on one store.
The archive is a second store with its own controls.
ArchiveStore keeps raw Pi session entries — every entry
type and branch, unredacted — in archive.sqlite, with an
FTS5 index, a project and session key, and its own tombstones keyed on
project, session and entry id (archive-store.ts:22-48,
:610-632). It is off by default and records only when
enabled, from the session journal on turn and session boundaries
(archive-extension.ts:235-257). A person can import old Pi
session files after a preview and a sensitive-data confirmation.
Optional encryption moves the database to
better-sqlite3-multiple-ciphers with a random data key
wrapped by scrypt and AES-256-GCM (archive-crypto.ts:9-13).
The archive is never injected into context; it is a transcript, and
nothing in it can be corrected.
Controls. Memory policy is three booleans,
enabled, capture and recall,
resolved project over global over default, with global
enabled: false a master switch and an untrusted project or
unreadable settings forcing all three off
(memory-config.ts:25-47). The archive has four, adding
modelAccess and modelWideSearch, and defaults
enabled to false
(archive-config.ts:45-49).
Deployment and ergonomics
Nothing runs beside Pi. Install the npm package into Pi, reload, and
memory is on in trusted projects. It needs Node 22.19 or later for
node:sqlite, no API key and no network. The store is a
SQLite file a person can open and read, and the README gives the reset:
stop every Pi process and delete the directory, which removes the
tombstones too. The archive's encryption needs an optional native
binding, validated on Linux only according to
docs/archive-encryption-design.md. The extension's UI
requires Pi's TUI mode; memory itself also works headless and discloses
itself on stderr.
4. Essential Implementation Paths
Model write. jarvis_memory_remember
(memory-extension.ts:85-99) checks the live policy, trust,
session generation and service epoch, then calls
SharedMemoryService.remember
(memory-service.ts:175-186). That rejects text the
sanitizer redacted or omitted, defaults scope to project
and category to project, and calls
MemoryStore.save without explicit
(memory-store.ts:491-525). The tool is annotated
destructiveHint: true because the upsert can replace a fact
(memory-extension.ts:89-91).
Capture. message_end records only an
anchor — the session leaf id, role, session and epoch — for a user or
assistant message (memory-extension.ts:240-250). On
turn_end or agent_settled,
flushCaptures walks the newly persisted suffix of the
session tree back to that anchor, at most 1,024 entries, and captures
the first entry of the right role after each anchor
(:139-175). extractMemoryText keeps only text
parts, drops failed or aborted assistant output, and hashes the event id
(memory-content.ts:168-207); capture saves it
as a conversation record
(memory-service.ts:129-146). A pending anchor is discarded
on any policy, trust or session change.
Recall and injection. On Pi's context
event, recall turns the last prompt into at most 12 literal
terms after removing stop words (memory-service.ts:19-27),
then makes three store reads: the 3 newest global notes, 5 notes in the
current scope by keyword, and 4 captures by keyword with the current
session's removed and 2 kept (:155-158). It dedupes, keeps
7 and renders JSON capped at 6,000 bytes (:159-161). The
hook prepends the result as a hidden custom message and removes any
earlier copy, so nothing is persisted
(memory-extension.ts:209-215). A second hook injects the
memory policy text next to the system message
(:216-239).
Search. jarvis_memory_search calls
service.search with the tool's scope, default
current (memory-extension.ts:74-84,
memory-service.ts:168-173), which reaches
MemoryStore.list
(memory-store.ts:561-599).
Forget. The model's
jarvis_memory_forget reads the record in the current scope,
renders it, awaits a person's confirmation and then calls
forget with the reviewed snapshot, which is compared to the
row inside the delete's transaction
(memory-extension.ts:100-124,
memory-store.ts:609-628). The main lane's confirmation
refuses while the overlay, the editor or a picker is open
(index.ts:944-951); the side lane bridges to the overlay
(side-session.ts:528). Human commands reach
forget and forgetAll directly
(memory-service.ts:325-336).
Human editor. /jarvis-memory editor
opens a TUI over notes only (index.ts:1574-1582). Its
create, update and delete run through editorCreate,
editorUpdate and editorForget, each in a
transaction that compares the complete reviewed version before writing
(memory-store.ts:676-745).
5. Memory Data Model
The memory schema is two tables and an index
(memory-store.ts:25-50). records is
STRICT with CHECK constraints on every column.
It holds a 64-hex id, kind note or
conversation, category user, feedback, project or
reference, scope global or project, and an absolute
project path. Beside them sit lane main,
jarvis or manual, session_id, event_id, an
optional role, created_at and
updated_at in milliseconds, a 160-character
title, a body of at most 16 KiB, and normalized copies of
both for search. tombstones holds an id and
deleted_at, nothing else. records_fts is an
external-content FTS5 table over the two normalized columns with
unicode61 remove_diacritics 2.
Scope is a column and a key component. A project
note's id includes the project path; a global note's does not, so a
global note saved from a second project updates the same row and
rewrites its project column to the newer writer
(test/memory-store.test.ts:149-153). The
project field on a global note is therefore the last
writer's project, not a boundary.
Provenance is one tuple and is overwritten. Lane,
session, event and role describe the most recent write, and
replace sets them from the new input
(memory-store.ts:539-543). There is no history table; the
earlier body and its source are gone after an upsert.
Time is the write clock. created_at is
set on insert and updated_at on each change, kept monotonic
by Math.max(Date.now(), existing.updatedAt + 1)
(memory-store.ts:543). No field records when a fact was
true.
Budgets are hard. 1,000 notes, 10,000 captures,
100,000 tombstones, and 100,000 identities across live rows and
tombstones (memory-store.ts:17). A new identity fails at
capacity while forgetting stays available, because each live row
reserves its future tombstone (:477-483). Notes are never
evicted; a save past 1,000 fails.
6. Retrieval Mechanics
All retrieval is FTS5 over normalized text. list quotes
each term, joins them with OR, ranks by
bm25(records_fts, 8.0, 1.0), and orders notes before
captures, then by rank and recency, at most 50 rows
(memory-store.ts:569-597). User operators and wildcards
cannot change the query grammar. With no terms it returns the newest
rows in scope.
The automatic recall is three fixed reads. The 3
newest global notes are injected on every request regardless of the
prompt (memory-service.ts:155). The keyword reads follow
the prompt's terms; when every word is a stop word the note read returns
the 5 newest notes in scope. Captures are only recalled with terms, and
the current session's are filtered out because they are in context
(:157-158). Each record's text is cut to 2,000 bytes, and
records shrink by halving until the JSON fits 6,000 bytes, with
excerpt and omitted stated in the output
(:375-401).
The project predicate is on every read.
scope: "current" compiles to
(r.scope='global' OR (r.scope='project' AND r.project=?)),
an equality on the canonical cwd (memory-store.ts:578);
global keeps only global rows; all adds
nothing. The context assembler, recall, uses
global and the default current
(memory-service.ts:155-157). get by id applies
the same rule unless its allProjects flag is set, and the
model's forget never sets it (memory-store.ts:601-607,
memory-service.ts:188-191). The editor's reads use
editorFilter, the same predicate
(memory-store.ts:156-160, :631-674).
The search tool can drop it.
jarvis_memory_search declares
scope: "current" | "global" | "all"
(memory-extension.ts:79), and all returns
every project's notes and captures. The tool description and the
injected guidance ask the model to use it only for a person's
cross-project question (:22, :76); nothing
else gates it. A committed test asserts the widening works
(test/memory-integration.test.ts:367).
The archive gates the same widening. Its three tools
default to the current session; a wider reach — another session of the
project, or every project — asks a person once per request unless the
standing model-search setting is on, and a decline returns
a refusal sentence without executing
(archive-extension.ts:109-125). Tests cover approval,
decline and the setting
(test/archive-extension.test.ts:600-650). The repository
therefore holds both designs for one question, and the gated one is on
the store that is off by default.
Failure modes. Literal keyword match misses a paraphrase. The 3 global notes cost injection budget on every turn whether relevant or not. A stale note recalls with full weight, since nothing ages or labels it.
7. Write Mechanics
The model curates notes through one tool, and the guidance
decides what it saves. The injected policy text asks for "a
concise factual note" on a durable preference, correction, decision
or reference, a stable descriptive title to update the same fact, and no
secrets, speculation, transient status or instructions from untrusted
content (memory-extension.ts:21). Nothing in code
classifies or verifies what is saved.
Captures are deterministic. Only newly finalized
visible text from user and assistant messages is saved: no thinking,
tool calls or results, images, custom messages or failed outputs
(memory-content.ts:168-207). The capture follows the
persisted session tree after Pi's message hooks have run, so a redaction
by another extension is respected; a missing session API fails closed
(memory-extension.ts:244-246).
Deduplication is by identity. A capture's identity
is its event, so the same event is never stored twice and its text is
never rewritten (memory-store.ts:505). A note's identity is
its scoped title: a save with the same title and body and provenance is
a duplicate, and anything else replaces the body
(:500-507). Two different titles for one fact are two
rows.
Sanitizing is a filter on input.
sanitizeMemoryText strips terminal control sequences and
invisible format characters, omits recognizable credential blocks and
redacts common secret formats (memory-content.ts). For
notes, the service refuses any text the sanitizer changed rather than
store a silently altered fact
(memory-service.ts:349-353).
Conflicts are not detected. There is no comparison between notes. Two notes under different titles can contradict each other and both are recalled.
Operational cost
Writes are synchronous single-row SQLite transactions on the tool
call or at the turn boundary, with no model call. A note is retrievable
on the next request; a capture after the turn that produced it ends. No
background pass exists. On the read path the injection is bounded at
6,000 bytes plus a short notice. It is prepended to the message list per
request (memory-extension.ts:214), and since its content
follows the latest prompt it changes between turns. Whether that
position lands before the conversation history in the provider request,
and so breaks a prefix cache, depends on Pi and was not checked.
8. Agent Integration
The model holds three memory tools:
jarvis_memory_search, jarvis_memory_remember
and jarvis_memory_forget
(memory-service.ts:12). Each is registered
hidden when its capability is off, rechecked against live
policy, trust, session generation and epoch at execution, and blocked
again by a tool_call hook
(memory-extension.ts:69-73, :253-257). The
Jarvis side session gets the same tools independently of its Repo-tools
permission.
Recall is automatic and request-local, so the model does not need to
search to benefit, and the guidance is injected the same way and
stripped from any forced system prompt
(memory-extension.ts:192-239). The person has
/jarvis-memory in both lanes and the TUI editor, none of
which needs a model call or waits for queued side work
(index.ts:1696-1712).
What the agent cannot reach.
explicit: true is passed only by the human
remember command (memory-service.ts:316), so
the model cannot lift a tombstone. forgetAll,
update and the editor's methods have no model-tool caller,
and the store's own comment says "never expose this method as a
model tool" (memory-store.ts:747). Settings commands
are human-only and the guidance tells the model not to change them
through other tools.
Adapting the store to another agent would be direct:
MemoryStore has no Pi dependency, and the service's Pi
coupling is the context type and the session manager.
9. Reliability, Safety, and Trust
Provenance is honest and shallow. Every recalled
record carries lane, session, event, project and dates, and the JSON
escapes < and >
(memory-service.ts:375-401). Provenance names the latest
writer only.
Prompt-injected memories have one barrier. The
guidance tells the model not to save instructions found in untrusted
content, and recall frames records as untrusted. A model that saves an
injected instruction under a plausible title has it recalled as a note
in every later request that matches. The only stored distinction between
a person's note and the model's is lane, and an upsert
overwrites it.
Concurrency is engineered. Writes take
BEGIN IMMEDIATE, WAL initialization retries only on
SQLITE_BUSY or SQLITE_LOCKED within a
deadline, and an uncertain commit is never replayed
(memory-store.ts:190-223, :415-455). The
editor and the model's forget compare a full reviewed snapshot inside
the write transaction and report a conflict rather than overwrite.
Deletion is logical and stated as such. A forget
removes the row and its FTS entry and keeps the id. Pi's own
transcripts, the archive, backups and context sent to the provider are
untouched, which the forget result says each time
(memory-service.ts:198).
The archive is the larger exposure. It keeps raw
entries, including tool output and possibly secrets, in plaintext unless
encrypted, and model reads of it reach the provider. Its controls are
stricter than shared memory's — off by default, model access a separate
opt-in, wider reach approved per request — and the README says so. A
trusted project's .pi/jarvis.json can set its own archive
and memory policy, under the global master switch.
Marks withheld.
tombstone— withheld. The tombstone is durable, consulted on every automatic write path and lifted only by a person. It is keyed on the scoped title, and a test asserts that a changed body under that title is refused (test/memory-store.test.ts:181-182). The same fact under any other title is a new identity, so the record refuses a slot rather than a value.scope_enforced— withheld. The predicate is on every read path, including the context assembler and get-by-id. The agent's own search tool takesall, which removes it with no gate. This matches the reading of pi-memory, whoseglobalargument is the same shape.human_review— withheld. A model write takes effect at commit; the editor is a person editing after the fact; the forget confirmation gates removal, not admission.trust_state— withheld.categoryis a write-time genre and no read filters on a status.bitemporal— withheld. Both timestamps are the write clock.audit_log— withheld. No table records a mutation, and an upsert discards the prior body. The archive, when on, records the session entries that carry memory tool calls; it is a transcript, off by default, and not the store's own log.
10. Tests, Evals, and Benchmarks
The suite is node:test under tsx, listed
file by file in the test script and run by CI on Node
22.19.0 and 24 (package.json,
.github/workflows/ci.yml). I did not run it.
Shared memory has 229 cases in eight files. The
store tests cover scope isolation, title deduplication, capture
identity, tombstones, rename, literal keyword recall, retention, the
identity budget, symlink and hard-link refusal, schema and integrity
refusal, WAL retry without replay, two concurrent writer processes and a
10,000-capture bound (test/memory-store.test.ts). The
integration tests drive the extension through mocked and real Pi SDK
runtimes: request-local injection, revocation on policy and trust
change, capture of only final text, and the forget confirmation's
refusal paths (test/memory-integration.test.ts).
The negative cases have positive controls. The scope
test saves rows in two projects and a global row, and asserts the
default list returns exactly the current project's and the global one
(test/memory-store.test.ts:112-134). The cross-project
recall assertion uses assert.doesNotMatch on the recall
string, which throws on undefined, so a filter returning
nothing fails it (test/memory-integration.test.ts:369). The
echo test asserts a prior session's capture is in the injected string
and the current session's is not, in one string (:876-880).
That is negative_eval.
Gaps. No case asserts that a forgotten note is
absent from a recall beside a live note; the forget tests read the row
back by id or assert an empty list
(test/memory-store.test.ts:173-191,
test/memory-integration.test.ts:496-504). No case covers
two titles for one fact. The model overwriting a person's note is
asserted as intended behaviour. There is no retrieval-quality evaluation
and no benchmark. No paper is cited in the tree.
11. For Your Own Build
Steal
- Make the id a hash of the natural key and tombstone the id. One table of ids makes "forget" survive every automatic writer, at a cost of 64 bytes per forgotten slot. Reserve a tombstone slot for each live row so forgetting never fails at capacity.
- Tombstone the old key on a rename. A correction to a title is then not undone by the next automatic save of the old one.
- Lift a tombstone only through a path the model cannot call. Pass the flag from the human command handler, not from an argument on the tool.
- Inject recall per request and never persist it. The transcript stays clean, a disable takes effect on the next request, and nothing recalled becomes evidence for the next recall.
- Refuse rather than silently alter. A note the sanitizer would change is rejected with a reason, so a stored fact is the text the writer sent.
Avoid
- A key that is not the fact. A title-keyed tombstone refuses the next body under that title and admits the same body under a new one; key on the normalized value if the goal is that a forgotten fact stays forgotten.
- An upsert that ignores who wrote the row. If a person's note can be replaced by the model's same-title save, keep the prior version or refuse the write when the lanes differ.
- Two scope policies in one product. If cross-scope reach needs a person on one store, it needs one on the other.
Fit
This suits one developer on one machine who wants Pi and a side session to share preferences and decisions without running anything, and who will curate by hand in the editor. The cost is low and the engineering around the SQLite file is careful. It does not suit a shared or regulated setting: projects are a relevance default the agent can widen, corrections leave no record, and the model's saves land without review. Teams that need a forgotten fact to stay forgotten under any wording, or a record of what was believed when, should not adopt it.
12. Open Questions
- Where does Pi place a custom message prepended by a
contexthook in the provider request, and does the per-turn recall invalidate a prefix cache? - Does the model choose stable titles in practice, so that the upsert and the tombstone act on the fact a person meant, or does the same fact accumulate under several titles?
- How often does the model pass
scope: "all", given that only prose restrains it? - What does the project's own use of the archive show about how often curated notes miss what the transcript holds?
Appendix: File Index
- Storage and schema:
memory-store.ts,memory-types.ts,memory-editor-types.ts. - Write path:
memory-service.ts(remember,capture,command),memory-extension.ts(flushCaptures,jarvis_memory_remember),memory-content.ts. - Retrieval and injection:
memory-service.ts(recall,search,renderMemoryRecords,memoryRecallQuery),memory-extension.ts(contextandcontext_with_systemhooks). - Policy:
memory-config.ts,archive-config.ts. - Human surfaces:
index.ts(dispatchMemoryCommand,confirmForget),memory-editor.ts,memory-editor-controller.ts,side-session.ts. - Archive:
archive-store.ts,archive-service.ts,archive-extension.ts,archive-import.ts,archive-crypto.ts,archive-sqlite.ts,docs/archive-encryption-design.md. - Tests:
test/memory-store.test.ts,test/memory-integration.test.ts,test/memory-editor-store.test.ts,test/archive-extension.test.ts,.github/workflows/ci.yml.
Recorded searches
Run with git grep in the checkout at the pinned
revision, from the tree root.
git grep -n -E 'CREATE TABLE|CREATE VIRTUAL TABLE' -- memory-store.ts archive-store.ts— each store hasrecords,tombstonesand an FTS5 table; no history or audit table.git grep -n -i -E 'valid_from|valid_to|valid_until|as_of|valid_at|effective_' -- '*.ts' ':!test'— no match.git grep -n -i -E '\b(status|verified|candidate|pending|approved|rejected)\b' -- memory-store.ts memory-types.ts— no match.git grep -n -i -E 'audit|mutation|history' -- 'memory-*.ts'— comments and UI strings only; no mutation record.git grep -n -E 'remember\(|explicit' -- memory-service.ts memory-extension.ts—explicitistrueonly atmemory-service.ts:316, the human command; the tool atmemory-extension.ts:97passes none.git grep -n "FROM tombstones WHERE id" -- memory-store.ts— checked insave,replace,editorCreateandeditorUpdate; deleted only insaveunderexplicit.git grep -n -E '"all"' -- memory-extension.ts memory-service.ts— the search tool'sscopeunion atmemory-extension.ts:79; the rest are human command flags.git grep -n -E 'ui\.confirm\(' -- '*.ts' ':!test'— the archive reach gate, the main lane's forget confirmation and the forget tool's fallback; no confirmation on the memory search tool.git grep -n -i -E 'embed|vector|cosine' -- 'memory-*.ts' 'archive-*.ts'— no match.git grep -l -i -E 'arxiv|bibtex|@article|@misc|doi\.org|CITATION'— no match, and noCITATION.cff.
History
2026-10-15 — 1dab7a41…
— first reading, at the head of main, the v1.14.0 release
commit of 11 October 2026. One mark, negative_eval.
Screened before reading from a full clone: no auto-run surface and no
build-time execution point; package.json and
package-lock.json inside the seven-day cooldown, changed
three days before the pin; three floating dev-dependency ranges against
a present lockfile; an AGENTS.md addressed to a reading
agent, read as data. MIT, no rider. Read with git grep and
sed; nothing was installed, built or run.