Skip to content

feat: inject core + relevant memories into long-horizon agent wake context - #2266

Open
lsm wants to merge 2 commits into
devfrom
space/inject-core-relevant-memories-into-long-horizon-agent-wake
Open

feat: inject core + relevant memories into long-horizon agent wake context#2266
lsm wants to merge 2 commits into
devfrom
space/inject-core-relevant-memories-into-long-horizon-agent-wake

Conversation

@lsm

@lsm lsm commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Long-horizon agents started cold on every wake (external event, reminder, or peer message) unless they manually called memory_search, breaking continuity across their lifetime. This mirrors the worker kickoff injection: every wake message now gets a ## Core Memories (top-10) + ## Relevant Memories (top-5) block prepended, using the wake message text as the search query so relevant—not just core—memories surface.

Implementation is centralized in injectLongTermAgentMessage so all three wake paths (external events, deliverToLongTermAgent, inbox flush) carry memory context, and reuses a newly-factored buildMemorySectionLines helper so formatting and char budgets (core ~2000 chars, each relevant ~500) stay identical to worker kickoff. When the space has no memories, the wake message is injected unchanged.

Closes the worker/LH asymmetry: workers get memories at kickoff; LH agents now get them at every wake.

lsm added 2 commits July 27, 2026 06:22
Factor the `## Core Memories` + `## Relevant Memories` rendering out of
`buildCustomAgentTaskMessage` into an exported `buildMemorySectionLines`
helper so worker kickoffs and (upcoming) long-horizon wakes reuse the
same formatting and char budgets (core ~2000 chars, each relevant ~500).

No behavior change for worker kickoff; existing memory tests stay green.
Long-horizon agents started cold on every wake unless they manually called
memory_search, breaking continuity across their lifetime. Mirror the worker
kickoff injection: prepend a `## Core Memories` (listCoreMemories, top-10) +
`## Relevant Memories` (search by wake message text, top-5) block to every
injected wake message via the shared `buildMemorySectionLines` helper.

Centralized in `injectLongTermAgentMessage` so all wake paths (external
events, peer messages, inbox flush) carry memory context. The wake message
text is the search query, so relevant — not just core — memories surface.
When no memories are stored the wake message is injected unchanged.

Char budgets stay consistent with workers via the shared helper.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@lsm lsm left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Review by glm-5.1[1m] (NeoKai)

Model: glm-5.1[1m] | Client: NeoKai | Provider: Zhipu GLM

Recommendation: APPROVE — zero P0–P3 blocking findings. The implementation is correct, complete, well-tested, and faithfully mirrors the worker kickoff; all failure modes are mitigated. Approving as the PR author (so this posts as a COMMENT, not an APPROVE event).

What I reviewed

  • Both commits (313 additions, 4 files): refactor extracts buildMemorySectionLines; feat centralizes the prepend in injectLongTermAgentMessage.
  • Full changed files + worker-kickoff parity (task-agent-manager.ts:821-850) + AgentMemoryRepository signatures + embedder wiring + all 3 callers of injectLongTermAgentMessage (external event, peer message, inbox flush) and their upstream catch boundaries.
  • Ran the two affected test files: 72 pass, 0 fail.

Correctness ✓

  • buildMemorySectionLines extraction is behavior-preserving — worker output is byte-identical (it now does sections.push(...buildMemorySectionLines(...))).
  • LH wake prepends ## Core Memories (top-10) + ## Relevant Memories (top-5) with the wake message text as the search query (space-runtime-service.ts:531-532) — matches the spec's "relevant, not just core" intent.
  • All three wake paths funnel through injectLongTermAgentMessage, so memory context is consistent across external events, peer messages, and inbox flushes.
  • Empty-memory case handled cleanly: lines.length === 0'' → message injected unchanged (verified by test).
  • Leading blank-line separator is correctly trimmed via .join('\n').trim() since the block is prepended, not appended to a sections[] array.

Parity & budgets ✓

  • Identical to worker kickoff: listCoreMemories(spaceId, 10) + search(spaceId, query, 5); char budgets (core ~2000, each relevant ~500) shared via the same helper. No regression to worker injection.

Error handling ✓

  • AgentMemoryRepository.search degrades to [] via fallbackToNull (embedder failure → FTS-only → []); listCoreMemories only throws on SQLite hard errors. Traced all three callers: external events retry with bounded attempts (space-runtime.ts), peer messages become a structured failed delivery record (messaging-adapter.ts), inbox flush has a per-message try/catch. The worker kickoff path is actually less defensive than this. No new risk.

Tests ✓

  • Helper: empty input, core-before-relevant ordering, core-block char budget, per-relevant truncation.
  • Runtime: full prepended block, section ordering, wake message trails the block, exact listCoreMemories(id, 10) / search(id, 'event payload', 5) args, and the no-memories unchanged path.

Non-blocking design notes (P2 suggestions — not defects, consequences of the explicit spec)

  1. Core-memory re-injection accumulates in context. Core memories are stable and space-scoped, but are now prepended + persisted on every wake via saveUserMessage. SDK auto-compaction (query-options-builder.ts) eventually reclaims them, so it's not a hard ceiling, but on a 200k-window model ~100 wakes produce ~150k tokens of duplicated memory text, accelerating compaction cycles. The natural split would be core → system prompt append (once per session), relevant → per-wake user message. This is exactly what the spec asked for ("mirror worker kickoff at every wake"), so approving as-is — flagging as a worthwhile follow-up.

  2. Latency is acceptable. The per-wake search uses the local in-process ONNX embedder (TransformersAgentMemoryEmbedder, granite-embedding-small q4, 384-dim) — a local CPU inference + SQLite FTS, not a network round-trip. Modest, best-effort, degrades gracefully, and is parity with worker kickoff.

Nice clean two-commit split (refactor → feat) and good test coverage. Ship it.

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