Repository navigation
feat: add per-folder notebook memory with mid-task note attach and interactive review - #43058
SaeedAqamiri wants to merge 11 commits into
Conversation
Add a first-class notebook memory to core in packages/core/src/notebook: a per-folder .note.yaml store (pure model + FSUtil-backed file access), a core/notebook SystemContext source that folds a conservative memory digest into the system baseline, and built-in notes_get/notes_commit tools. Wires the memory source into location-services and the tools into BuiltInTools. Shared pure notebook logic underpins both this native (V2) implementation and the external V1 notes-plugin.
Give the native notebook memory the same save-reminder the V1 plugin has. A per-session evidence tracker records exploration (read/grep/glob or tools with a filePath/path arg) in the runner and silences on notes_commit; the save nudge is injected alongside the system baseline on subsequent turns, honoring a cooldown. Adds focuses evidence tests.
- Add notes_get and notes_commit tools for per-session knowledge persistence - Add notebook system context source with freshness badges - Add evidence tracking to prevent empty or repetitive commits - Add Save Learnings button to message UI (TUI and web) - Propagate onSaveLearnings through component chain - Add write-notes permission guard (never auto-approved) - Add bookmark icon to v1 and v2 icon sets - Add i18n keys for saveLearnings command in all locales - Add permissions diff display in session permission dock - Exclude write-notes from auto-respond in permission context - Add /record-notes (TUI) and /save (web) slash commands - Add deep quality guard: reject empty, non-English, or short commits
…otes to tool output - Add hasConcreteAnchor to the notes_commit quality gate so entry/relation summaries that name no real symbols are rejected as vague instead of saved. - Add NotebookAttach service (attach.ts): noteFor() surfaces a per-file notebook note once per session on first read/edit/write/apply_patch; prependNote() merges it into a single-text ToolOutput preserving the text result type. - SessionRunner prepends the note to the published toolResult so leaf memory reaches the model mid-task (registry stays free of FSUtil/Location deps). - Fix notes_get naked-directory path mode: descend into the target as a leaf and stop skipping relations-only notebooks.
… questions The injected notebook digest only samples up to 2 entries per folder and never instructed the model to pull the full per-file memory, so overview questions (what a project is, how to run it, its progress) were answered by re-exploring code instead of consulting notes_get. Make the digest say it is only a sample and direct the model to call notes_get (task or path) first — cheap and local — at task start and before any general project question.
79a8124 to
38693d5
Compare
- SaveLearningsButton: label is a plain string, not JSX; drop String() on aria-label. - session-permission-dock: remove a redundant type assertion on metadata.diff. - notebook tools: drop an unnecessary template expression in save labels. - notebook test: use a template literal instead of concat.
Locale translations move to a separate PR (anomalyco#43066) to shrink the memory PR diff; English keys remain because the app/ui i18n types derive from en and the runtime merges { ...en, ...locale } with English as the fallback.
…ok.attach The mid-task note prepending is the most opinionated part of the feature, so give it an explicit knob: experimental.notebook.attach = false disables only the auto-attach; notes_get/notes_commit and the memory digest are unaffected. Default stays enabled.
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
|
|
… save prompt - parseNotebook now loads .note.yaml with CORE_SCHEMA so hostile JS tags (!!js/function etc.) cannot be instantiated; add a regression test. - Extract the shared RECORD_NOTES_PROMPT used by the /save command and the save-learnings button so a rename/gating of notes_commit cannot silently degrade one path while the other keeps a stale tool name.
2caf8bc to
96a31a4
Compare
|
Automated PR Cleanup Thank you for contributing to opencode. Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions. This PR was closed because it matched the following cleanup criteria:
PRs created within the last month are not affected by this cleanup. If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate. Thanks again for taking the time to contribute. |
|
Still actively maintained, and the automated review feedback has been addressed since this was opened:
|
|
Reopen request: this PR was auto-closed by the stale-PR cleanup bot (age + reactions), not by a maintainer. The implementation is still current: safe YAML parsing (CORE_SCHEMA) with a regression test, a centralized save-learnings prompt, and mid-task attach opt-out via Could a maintainer reopen it for human review? Happy to split it into smaller PRs if that is easier to land. |
Issue for this PR
Closes #43068 (per-folder notebook memory); also related to #16077 (persistent session memory). Implements per-project memory as
.note.yamlfiles withnotes_get/notes_commit, mid-task leaf-note attach, and a save-learnings nudge.Type of change
What does this PR do?
Adds per-folder notebook memory for the agent. Each folder can carry a
.note.yamlwith a folder summary, per-file entries, and cross-file relations, grounded inbased_onsource fingerprints with freshness badges (✓/⚠/✗).notes_getreads the ancestor-chain memory for a path, or keyword-searches all notebooks; bare-directory targets are handled as leaves (fixes the old "No memory on this path yet" for directories) and relations-only notebooks are no longer skipped.notes_commitwrites summaries/entries/relations withPermissionV2(write-notes) approval and a real diff, never auto-approved even in--auto.NotebookAttachprepends the relevant per-file note toread/edit/write/apply_patchoutput on first touch in a session, so leaf memory reaches the model mid-task. This is the opinionated part, so it is opt-out viaexperimental.notebook.attach = false.notes_getbefore answering general project questions.Why attach in the SessionRunner instead of the registry:
ToolRegistrydeliberately has no FSUtil/Location deps and is a standalone unit-test boundary; the Location-scoped runner already owns the step lifecycle and notebook evidence, so prepending a bounded text note to the published tool result fits there with no new cross-cutting seams.How did you verify your code works?
bun typecheckgreen in core, app, session-ui, tui.prependNote, directory-targetnotes_get, and notebook tools/memory/evidence all pass (61 tests), plus the portable V1 plugin suite.Screenshots / recordings
N/A here — UI adds a save-learnings button and a permission diff view; can attach once the CI build is visible.
Checklist
Splits: interactive review helper in #43059, locale keys in #43066.