Skip to content

feat(opencode): enhance recall/retain config and memory instructions#2340

Open
sdrobov wants to merge 3 commits into
vectorize-io:mainfrom
sdrobov:fix/opencode-lazy-memory-usage
Open

feat(opencode): enhance recall/retain config and memory instructions#2340
sdrobov wants to merge 3 commits into
vectorize-io:mainfrom
sdrobov:fix/opencode-lazy-memory-usage

Conversation

@sdrobov

@sdrobov sdrobov commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Update default configuration values for the OpenCode integration to improve memory management. Increase recallMaxTokens from 1024 to 4096 and recallContextTurns from 1 to 3 to allow for richer context retrieval. Add "observation" to recall types and retain every turn instead of every 3 to capture more granular interaction data.

Refactor content preparation by removing the messageCount return value from prepareRetentionTranscript, simplifying the interface. Update the retention context description to be more explicit about the conversation participants.

Introduce buildMemoryInstructions to generate comprehensive system prompts that guide the LLM on when and how to use memory tools proactively, improving long-term context awareness and user preference retention.

@sdrobov

sdrobov commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

This PR improves memory utilization in OpenCode by making memory retrieval and retention happen more proactively.

I validated the changes through practical usage and observed that OpenCode uses memory significantly more often and in more relevant situations. The agent is noticeably better at recalling previously learned information and applying it during subsequent interactions.

One of the main goals was to improve memory effectiveness without increasing context overhead. In my testing, token consumption remained nearly unchanged while memory usage became substantially more productive.

The implementation is heavily inspired by the memory system used in Hermes Agent. In particular, the overall retrieval/retention strategy and the motivational tool instructions were adapted from mechanisms that have already proven effective in practice. The idea is to encourage the model to treat memory as an active problem-solving resource rather than a passive storage system.

Given Hindsight's focus on helping agents learn over time rather than simply storing information, I believe these changes align well with the project's goals and improve the real-world usefulness of memory-enabled agents.

@koriyoshi2041

Copy link
Copy Markdown
Contributor

I reproduced the failing verify-generated-files part locally. This looks like a formatting-only issue from the integration Prettier step, not a failing OpenCode behavior test.

Running Prettier on the integration rewrites only:

  • hindsight-integrations/opencode/src/hooks.ts
  • hindsight-integrations/opencode/src/hooks.test.ts

The diff is long-line wrapping, indentation in string concatenation, and adding the final newline. After applying that formatter output, these passed for me:

npx --yes prettier --check --config "$PWD/.prettierrc.json" --ignore-path "$PWD/.gitignore" hindsight-integrations/opencode
cd hindsight-integrations/opencode
npm test -- --run src/hooks.test.ts src/config.test.ts src/content.test.ts src/plugin.test.ts

Result: 4 files / 65 tests passed. Committing the Prettier output should clear this CI failure.

@sdrobov

sdrobov commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

@koriyoshi2041 thank you so much for the explanation. I guess it was a mistake to create this PR at night, because my mind wasn't working as well and I couldn't figure out why the tests were failing. I fixed the formatting, and now everything is fine.

@nicoloboschi nicoloboschi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the full diff, the source on the branch, and ran the suite. Direction is reasonable and the changed-surface tests pass, but there are three correctness bugs in the new every-turn retain path plus a design issue with the prefetch that I'd resolve before merge.

Must fix

1. Off-by-one turn pairing in retainTurn (correctness)hooks.ts:526-535
experimental.chat.system.transform fires before the assistant generates its reply to the current user message. At that point messages = […, asst(N-1), userN]. The code pairs lastUserMsg (= userN, current/unanswered) with lastAssistantMsg (= asst(N-1), the previous answer), so every retained "turn" mismatches the current question with the previous answer. This corrupts the conversational structure fact extraction relies on. Retaining from system.transform is structurally wrong — the assistant response doesn't exist yet. This belongs on a post-response event (e.g. session.idle / message-completed), not the system-prompt transform.

2. sessionTurns buffer is never cleared after a retainhooks.ts:275-306
retainTurn pushes to the buffer and then sends the entire accumulated buffer every time (content = "[" + turns.join(",") + "]") into a new documentId (${sessionId}-turn-${turnNum}). With the new default retainEveryNTurns: 1, turn 2 sends [t2], turn 3 [t2,t3], turn 4 [t2,t3,t4]… so the same early turns get re-extracted into every subsequent document — duplicate facts and redundant async LLM extraction that grows O(n²) over a session. Reset the buffer after a successful flush so each retain sends only the turns since the last one.

3. Three overlapping retain paths run simultaneously
retainTurn (every turn) + handleSessionIdleretainSession (full window on idle) + pre-compaction retain all persist the same conversation. Combined with #2 this is a lot of duplicate ingestion. Was every-turn retain meant to replace the idle retain? Please make the intended relationship explicit.

Should fix

4. Prefetch serves stale recall and undermines contextual recallhooks.ts:323-367, 488-521
queueBackgroundPrefetch is called with the current turn's user message but its result is consumed on the next turn (a different message). When the cache hits, the live recall is skipped (if (!cachedContext)), so the injected memories are relevant to the previous question — exactly the staleness contextual recall was meant to fix. After turn 1 nearly every turn takes the stale cached path. Reconsider whether the prefetch earns its complexity, given recall is already awaited inline.

5. Default-value blast radiusconfig.ts
Recall goes once/session → every turn; recallMaxTokens 1024→4096 (4×), recallContextTurns 1→3, retainEveryNTurns 3→1. Large jump in per-turn injected tokens + recall load + retain volume for everyone on upgrade. Worth a deliberate call on each default and a changelog note.

6. Docs not updatedhindsight-docs/docs-integrations/opencode.md still says retain happens "when the session goes idle" and shows retainEveryNTurns: 3. Behavior and defaults changed; docs should follow.

Nits

  • Misleading comment hooks.ts:492-494 ("Use only if this entry was created after…") — the code unconditionally deletes and uses the cache; the described guard doesn't exist.
  • The <hindsight_memories>… block is now duplicated in recallForContext and queueBackgroundPrefetch — extract a helper.
  • Removed recallPromptPreamble is fine for OpenCode (not env-backed/not documented there), but the new preamble is a hardcoded English string and ignores any language config.
  • Tests rely on setTimeout(100) to await background work — mild CI flakiness risk.

I'd ask for #1#3 before merge — as written, the every-turn retain path produces mispaired, heavily-duplicated memories.

@sdrobov sdrobov force-pushed the fix/opencode-lazy-memory-usage branch from 85e2d54 to 43db6ea Compare June 25, 2026 13:54
@sdrobov

sdrobov commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

@nicoloboschi Thank you for your feedback. I've simplified the code and fixed the errors and issues.

I couldn't figure out why build-docs was failing. I looked at how the script hindsight-docs/scripts/check-integrations.mjs works, checked the tags, and didn't find a single one that matched 'eve':

❯ git tag -l 'integrations/*' | cat | grep eve || echo 'not found'
not found

@koriyoshi2041

Copy link
Copy Markdown
Contributor

I checked the build-docs failure. The failing guard is the reverse integration check, not the OpenCode docs change itself:

  • CI fetches tags and sees refs/tags/integrations/eve/v0.1.0
  • this PR head (43db6ead) does not have hindsight-docs/docs-integrations/eve.md or the eve entry in hindsight-docs/src/data/integrations.json
  • current main already has both the Eve doc page and the integrations.json entry

So this should clear by updating the branch from current main rather than adding a new Eve entry from scratch. Your local git tag -l ... | grep eve probably missed it because the local checkout did not have the integration tags fetched; CI uses the full tag set for this guard.

sdrobov added 3 commits June 25, 2026 18:23
Update default configuration values for the OpenCode integration to improve memory
management. Increase `recallMaxTokens` from 1024 to 4096 and `recallContextTurns`
from 1 to 3 to allow for richer context retrieval. Add "observation" to recall types
and retain every turn instead of every 3 to capture more granular interaction data.

Refactor content preparation by removing the `messageCount` return value from
`prepareRetentionTranscript`, simplifying the interface. Update the retention context
description to be more explicit about the conversation participants.

Introduce `buildMemoryInstructions` to generate comprehensive system prompts that
guide the LLM on when and how to use memory tools proactively, improving long-term
context awareness and user preference retention.
The OpenCode integration documentation has been updated to reflect a shift from session-based memory operations to per-turn operations.

- Changed auto-recall to trigger on every turn based on the latest user message, ensuring contextually relevant memories.
- Changed auto-retain to trigger after each agent response, capturing even one-shot prompts.
- Added new configuration options: `recallMaxTokens`, `recallTypes`, `recallContextTurns`, `retainContext`, and `recallMaxQueryChars`.
- Updated environment variable documentation to match new config options.
- Added notes regarding API load implications of per-turn operations.
@sdrobov sdrobov force-pushed the fix/opencode-lazy-memory-usage branch from 43db6ea to f653186 Compare June 25, 2026 15:23
@sdrobov sdrobov requested a review from nicoloboschi June 25, 2026 15:45
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.

3 participants