Summary
LifeOS ships a ranked, task-scoped memory retriever — getRelevantContext (the "F6" function in LIFEOS/TOOLS/MemoryRetriever.ts): a BM25 search over MEMORY/KNOWLEDGE/* plus the two hot-layer files, documented in-code as "a markdown block ready for injection into buildLifeosContextBlock."
That injection happens on the Telegram path (buildLifeosContextBlock → getRelevantContext). It does not happen on the primary CLI turn path. On the CLI, hooks/MemoryTurnStart.hook.ts composes LoadMemory.run() (the static hot-layer <pai-memory> digest) and MemoryDeltaSurface.run() (freshness telemetry) — but never calls getRelevantContext. Confirmed on current main: hooks/LoadMemory.hook.ts references getRelevantContext zero times.
So a terminal session sees only the fixed ~48-entry hot-layer digest, never a retrieval scoped to what was actually asked. Prior work that is present in the corpus — and top-ranked for the query — is never surfaced, because the retriever is never invoked. The digest is capped and generic by design, so it can't carry a task-specific fact.
Environment
Current main, CLI (terminal) session. Files: hooks/MemoryTurnStart.hook.ts, hooks/LoadMemory.hook.ts, LIFEOS/TOOLS/MemoryRetriever.ts.
Steps to reproduce
- Add a note under
MEMORY/KNOWLEDGE/ on some topic.
- In a CLI session, submit a prompt about that topic.
- Inspect the injected context:
<pai-memory> (static digest) is present; no task-scoped retrieval block appears. Then run bun LIFEOS/TOOLS/MemoryRetriever.ts "<the prompt>" directly and see the note ranked at the top — proof the retriever would have surfaced it had it been called.
Observed / impact
A small label-based check over the KNOWLEDGE corpus: for a feasibility-style prompt whose answer already existed as a note, getRelevantContext ranks the correct note #1 (recall@5 = 100%) on the query. The retrieval works; it is simply never called on the CLI path, so the ranked hit reaches the model on Telegram but not in a terminal session. This is a wiring gap, not a retrieval-quality problem — and it surfaces as "the assistant fanned out to research something its own corpus already answered."
Fix suggestion
Option A (preferred — completes the existing orchestration). In MemoryTurnStart.hook.ts, after LoadMemory.run(), call getRelevantContext(prompt, { topK: 5, threshold: 0.20 }) with the submitted prompt (already on the hook's stdin JSON as prompt) and append its markdownBlock as a <pai-ground> section of the same additionalContext. ~10 lines, stays synchronous (the hook is already async: false), no new registration, and reaches CLI/Telegram parity in the one hook that already owns CLI memory injection.
Option B (standalone hook). A small GroundFirst.hook.ts on UserPromptSubmit doing the same, independently toggleable, with an idempotency guard so it composes safely if Option A also lands.
Both are cheap (getRelevantContext is synchronous BM25 over ~50 notes, 60s-cached) and fail-open. Read-side only; same corpus getRelevantContext already reads; no new dependencies. Happy to send a diff for Option A if that shape is preferred.
Thanks as always for the project and the pace it moves at.
Summary
LifeOS ships a ranked, task-scoped memory retriever —
getRelevantContext(the "F6" function inLIFEOS/TOOLS/MemoryRetriever.ts): a BM25 search overMEMORY/KNOWLEDGE/*plus the two hot-layer files, documented in-code as "a markdown block ready for injection intobuildLifeosContextBlock."That injection happens on the Telegram path (
buildLifeosContextBlock→getRelevantContext). It does not happen on the primary CLI turn path. On the CLI,hooks/MemoryTurnStart.hook.tscomposesLoadMemory.run()(the static hot-layer<pai-memory>digest) andMemoryDeltaSurface.run()(freshness telemetry) — but never callsgetRelevantContext. Confirmed on currentmain:hooks/LoadMemory.hook.tsreferencesgetRelevantContextzero times.So a terminal session sees only the fixed ~48-entry hot-layer digest, never a retrieval scoped to what was actually asked. Prior work that is present in the corpus — and top-ranked for the query — is never surfaced, because the retriever is never invoked. The digest is capped and generic by design, so it can't carry a task-specific fact.
Environment
Current
main, CLI (terminal) session. Files:hooks/MemoryTurnStart.hook.ts,hooks/LoadMemory.hook.ts,LIFEOS/TOOLS/MemoryRetriever.ts.Steps to reproduce
MEMORY/KNOWLEDGE/on some topic.<pai-memory>(static digest) is present; no task-scoped retrieval block appears. Then runbun LIFEOS/TOOLS/MemoryRetriever.ts "<the prompt>"directly and see the note ranked at the top — proof the retriever would have surfaced it had it been called.Observed / impact
A small label-based check over the KNOWLEDGE corpus: for a feasibility-style prompt whose answer already existed as a note,
getRelevantContextranks the correct note #1 (recall@5 = 100%) on the query. The retrieval works; it is simply never called on the CLI path, so the ranked hit reaches the model on Telegram but not in a terminal session. This is a wiring gap, not a retrieval-quality problem — and it surfaces as "the assistant fanned out to research something its own corpus already answered."Fix suggestion
Option A (preferred — completes the existing orchestration). In
MemoryTurnStart.hook.ts, afterLoadMemory.run(), callgetRelevantContext(prompt, { topK: 5, threshold: 0.20 })with the submitted prompt (already on the hook's stdin JSON asprompt) and append itsmarkdownBlockas a<pai-ground>section of the sameadditionalContext. ~10 lines, stays synchronous (the hook is alreadyasync: false), no new registration, and reaches CLI/Telegram parity in the one hook that already owns CLI memory injection.Option B (standalone hook). A small
GroundFirst.hook.tsonUserPromptSubmitdoing the same, independently toggleable, with an idempotency guard so it composes safely if Option A also lands.Both are cheap (
getRelevantContextis synchronous BM25 over ~50 notes, 60s-cached) and fail-open. Read-side only; same corpusgetRelevantContextalready reads; no new dependencies. Happy to send a diff for Option A if that shape is preferred.Thanks as always for the project and the pace it moves at.