You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds Codex-style per-session memory controls on top of the existing project memory feature: a session can now opt out of USING saved memories (injection and recall) and/or of CONTRIBUTING to future memory generation. Both default to on whenever memory is enabled.
The controls live in the session's metadata record (the same per-session flag store the /sandbox toggle uses) under memoryUse / memoryContribute, with helpers in the new @opencode-ai/memory/controls module. Only an explicit false opts a session out, so existing sessions keep full memory behavior:
/** True when the session may use saved memories: index injection and targeted recall. */exportfunctionuse(metadata: Record<string,unknown>|null|undefined){returnmetadata?.[USE]!==false}/** True when the session may contribute to future memory generation: turn-close capture and saves. */exportfunctioncontribute(metadata: Record<string,unknown>|null|undefined){returnmetadata?.[CONTRIBUTE]!==false}
Enforcement follows the existing pipeline shape without restructuring it:
Generation: MemoryHost.close (the turn-close capture hook in packages/opencode/src/memory/host.ts) returns before MemoryTurn.close when the session opted out of contribution, and the memory_save tool reports the session as off instead of writing.
Injection: the V2 runner (packages/core/src/session/runner/llm.ts) drops the core/memory system-context source (new SystemContext.omit) before the context epoch snapshots the baseline, and the memory_recall tool reports the session as off instead of recalling. The tools read SessionTable.metadata the same way the bash tool reads the sandbox flag.
TUI surface: the /memory dialog gains two "Session" toggle rows with checkbox-style state that flip in place (persisted via session.update metadata, mirroring the sandbox toggle), and /memory use on|off / /memory contribute on|off typed commands are added to the shared command catalog and parser.
How did you verify your code works?
bun typecheck in packages/memory, packages/core, packages/opencode, and packages/tui (all clean).
bun test in packages/memory (168 pass, including new parser fixtures for use/contribute and a new controls.test.ts).
bun test test/system-context in packages/core (29 pass) and bun test test/memory in packages/opencode (4 pass).
Screenshots / recordings
TUI dialog change: two toggle rows ("Use saved memories in this session" / "Save new memories from this session") appear at the top of the /memory dialog when a session is active; no recording available from this environment.
Checklist
I have tested my changes locally
I have not included unrelated changes in this PR
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.
Summary by CodeRabbit
New Features
Added session-level controls for enabling or disabling memory use and memory contributions.
Added /memory use on|off and /memory contribute on|off commands.
Memory settings can be changed from the memory help dialog, with status feedback and error notifications.
Bug Fixes
Disabled memory settings now prevent recalling, saving, or generating memory for the selected session.
Added validation and usage guidance for incomplete or invalid memory commands.
@DevFlex-AI, you've reached your PR review limit, so we couldn't start this review.
Next review available in:47 minutes
Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).
How can I continue?
After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.
To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.
How do review limits work?
CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.
For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.
Reviewing files that changed from the base of the PR and between 3d464f0 and 3185d46.
📒 Files selected for processing (1)
packages/tui/src/component/dialog-memory.tsx
📝 Walkthrough
Walkthrough
Per-session memory controls now support use and contribute toggles. Metadata controls memory context loading, recall, saving, and turn-close processing. The TUI exposes session-scoped toggles and commands.
Changes
Per-session memory controls
Layer / File(s)
Summary
Control and command contracts packages/memory/src/controls.ts, packages/memory/src/commands.ts, packages/memory/src/index.ts, packages/memory/package.json, packages/memory/test/*
Memory use and contribution default to enabled. The command parser accepts use on|off and contribute on|off, with validation and test coverage.
Consider centralizing the metadata-merge logic for memory controls.
The { ...meta, [key]: value } merge pattern for MemoryControls.USE/CONTRIBUTE is duplicated here and in packages/tui/src/util/memory-command.ts (Lines 84-99). Move this into a shared helper in packages/memory/src/controls.ts (for example MemoryControls.merge(metadata, key, value)), so both call sites share one implementation and any future fix, such as the refetch-before-write change above, applies in one place.
As per coding guidelines, "Keep code in one function unless it is composable or reusable; do not extract single-use helpers unless they hide a genuinely complex boundary or have a clear independent concept" — this logic is used in two places, so extraction is warranted.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/tui/src/component/dialog-memory.tsx` around lines 120 - 127,
Centralize the memory metadata merge used by toggle and command flows by adding
a reusable helper under MemoryControls in controls.ts, such as merge(metadata,
key, value), that preserves existing metadata while updating the selected
control. Replace the inline merge logic in toggle and the corresponding logic in
memory-command.ts with this shared helper so future write-related fixes apply
consistently.
Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/session/runner/llm.ts`:
- Around line 172-194: Update the session metadata update flow to reset the
context epoch whenever memory-control metadata changes, so subsequent
preparation rebuilds the baseline with the current setting. Locate the metadata
update handler used by initialize and ensure it invalidates the existing epoch
before prepare reuses its stored baseline; preserve existing behavior for
unrelated metadata updates.
In `@packages/tui/src/component/dialog-memory.tsx`:
- Around line 120-127: Update toggle in dialog-memory.tsx to fetch the current
session with sdk.client.session.get immediately before
sdk.client.session.update, and build the metadata payload from that fresh
session metadata instead of the cached metadata() snapshot. Preserve the
existing key flip and error-toast behavior while narrowing the staleness window.
---
Nitpick comments:
In `@packages/tui/src/component/dialog-memory.tsx`:
- Around line 120-127: Centralize the memory metadata merge used by toggle and
command flows by adding a reusable helper under MemoryControls in controls.ts,
such as merge(metadata, key, value), that preserves existing metadata while
updating the selected control. Replace the inline merge logic in toggle and the
corresponding logic in memory-command.ts with this shared helper so future
write-related fixes apply consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
Push a commit to this branch (recommended)
Create a new PR with the fixes
ℹ️ Review info⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b57b317b-5686-42a1-b989-920d6945823e
📥 Commits
Reviewing files that changed from the base of the PR and between a06cf86 and 3d464f0.
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bashset -euo pipefail
# Locate the epoch implementation and inspect its initialization and persistence behavior.
fd -t f -e ts -e tsx . packages/core/src/session |while IFS= read -r file;doif rg -q '\bSessionContextEpoch\b'"$file";then
ast-grep outline "$file" --items all
rg -n -C 8 '\b(SessionContextEpoch|initialize|prepare|baseline|metadata)\b'"$file"fidone# Inspect all core call sites that can refresh, replace, or reuse an epoch.
rg -n -C 6 '\bSessionContextEpoch\.(initialize|prepare)\b' packages/core/src --glob '*.ts'
Repository: bolt-builder/bolt-cli
Length of output: 24913
Reset the context epoch when session metadata changes.
When initialize is called on a session with an existing epoch, it returns undefined. The code then falls back to prepare, which reuses the stored baseline (line 64 in context-epoch.ts). The memory control is applied only when the context is first loaded. If a user changes the memory control via /memory, the epoch baseline does not update; the old baseline persists until the session is renamed or reverted. When memory is enabled after being disabled, memory is not injected. When memory is disabled after being enabled, memory remains.
Reset the epoch in the session metadata update handler (or add a check in prepare to re-evaluate the memory control against the stored baseline before reuse).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/core/src/session/runner/llm.ts` around lines 172 - 194, Update the
session metadata update flow to reset the context epoch whenever memory-control
metadata changes, so subsequent preparation rebuilds the baseline with the
current setting. Locate the metadata update handler used by initialize and
ensure it invalidates the existing epoch before prepare reuses its stored
baseline; preserve existing behavior for unrelated metadata updates.
The reason will be displayed to describe this comment to others. Learn more.
False positive: prepare() runs every turn with a freshly loaded context (loadSystemContext reads current session metadata from the DB), and reconcile handles both directions of the toggle. Since the memory source defines no removed renderer, omitting it triggers a full replacement generation without memory, and re-adding it emits its baseline as a context update, so no epoch reset on metadata change is needed.
We reviewed changes in a06cf86...3185d46 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.
Some issues found as part of this review are outside of the diff in this pull request and aren't shown in the inline review comments due to GitHub's API limitations. You can see those issues on the DeepSource dashboard.
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
The reason will be displayed to describe this comment to others. Learn more.
Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable
It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.
The reason will be displayed to describe this comment to others. Learn more.
Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable
It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.
The reason will be displayed to describe this comment to others. Learn more.
False positive: this is a module-level exported function in an ES module, not a browser script polluting the global scope. Exported function declarations are the convention throughout this codebase.
The reason will be displayed to describe this comment to others. Learn more.
Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable
It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.
The reason will be displayed to describe this comment to others. Learn more.
Module imports itself
A module should never import itself. This usually happens as a mistake or typo and occurs mostly during refactoring. Self importing might result in unexpected results like wrong functions/variables being used.
The reason will be displayed to describe this comment to others. Learn more.
Remove redundant `undefined` from function call
When an argument is omitted from a function call, it will default to undefined. It is therefore redundant to explicitly pass an undefined literal as the last argument.
The reason will be displayed to describe this comment to others. Learn more.
Remove redundant `undefined` from function call
When an argument is omitted from a function call, it will default to undefined. It is therefore redundant to explicitly pass an undefined literal as the last argument.
The reason will be displayed to describe this comment to others. Learn more.
React Hook "MemoryControls.use" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function
Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.
The reason will be displayed to describe this comment to others. Learn more.
Expected 'undefined' and instead saw 'void'
The void operator takes an operand and returns undefined. It can be used to ignore the value produced by an expression. However, this can lead to code that is difficult to understand and maintain. Historically, the void operator was used to get a "pure" undefined value, as the undefined variable was mutable prior to ES5.
The reason will be displayed to describe this comment to others. Learn more.
Expected 'undefined' and instead saw 'void'
The void operator takes an operand and returns undefined. It can be used to ignore the value produced by an expression. However, this can lead to code that is difficult to understand and maintain. Historically, the void operator was used to get a "pure" undefined value, as the undefined variable was mutable prior to ES5.
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
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.
Issue for this PR
Closes #100
Type of change
What does this PR do?
Adds Codex-style per-session memory controls on top of the existing project memory feature: a session can now opt out of USING saved memories (injection and recall) and/or of CONTRIBUTING to future memory generation. Both default to on whenever memory is enabled.
The controls live in the session's
metadatarecord (the same per-session flag store the/sandboxtoggle uses) undermemoryUse/memoryContribute, with helpers in the new@opencode-ai/memory/controlsmodule. Only an explicitfalseopts a session out, so existing sessions keep full memory behavior:Enforcement follows the existing pipeline shape without restructuring it:
MemoryHost.close(the turn-close capture hook inpackages/opencode/src/memory/host.ts) returns beforeMemoryTurn.closewhen the session opted out of contribution, and thememory_savetool reports the session as off instead of writing.packages/core/src/session/runner/llm.ts) drops thecore/memorysystem-context source (newSystemContext.omit) before the context epoch snapshots the baseline, and thememory_recalltool reports the session as off instead of recalling. The tools readSessionTable.metadatathe same way the bash tool reads the sandbox flag.TUI surface: the
/memorydialog gains two "Session" toggle rows with checkbox-style state that flip in place (persisted viasession.updatemetadata, mirroring the sandbox toggle), and/memory use on|off//memory contribute on|offtyped commands are added to the shared command catalog and parser.How did you verify your code works?
bun typecheckinpackages/memory,packages/core,packages/opencode, andpackages/tui(all clean).bun testinpackages/memory(168 pass, including new parser fixtures foruse/contributeand a newcontrols.test.ts).bun test test/system-contextinpackages/core(29 pass) andbun test test/memoryinpackages/opencode(4 pass).Screenshots / recordings
TUI dialog change: two toggle rows ("Use saved memories in this session" / "Save new memories from this session") appear at the top of the
/memorydialog when a session is active; no recording available from this environment.Checklist
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is enabled.Summary by CodeRabbit
/memory use on|offand/memory contribute on|offcommands.