Skip to content

feat: advertise search_memory when a project has no memories yet; bump to 0.1.7 - #125

Merged
Dhravya merged 1 commit into
mainfrom
feat/recall-discovery-on-empty
Sep 17, 2026
Merged

Dhravya merged 1 commit into
mainfrom
feat/recall-discovery-on-empty

Conversation

@Dhravya

@Dhravya Dhravya commented Sep 12, 2026

Copy link
Copy Markdown
Member

The problem

The recall hook can only inject what it finds. When a project has no stored memories, main() returns silently:

if (fresh.length === 0) {
  writeOutput({ continue: true, suppressOutput: true });
  return;
}

Silence reads to the model as "there is nothing to search". So it never reaches for search_memory, and any history living in another container stays invisible for the whole session.

The guidance that would have fixed this already exists — formatRecall() ends with:

For deeper history, call the supermemory search_memory tool (containerTag: )

But formatRecall() only runs when recall already found something. The routing instruction was delivered in the case where it was least needed, and withheld in the case where it mattered most.

Observed

A session in a repo whose container had 0 memories. Session start correctly reported the container was empty. The model then went looking on its own, got no guidance, followed the MCP tool description ("omit this field so the server uses the active space"), and landed in the account's shared space without realizing it — while the answer it wanted sat in a container it was never told to ask for.

The change

Emit a discovery block on the first genuinely-empty recall of a session:

<supermemory-recall>
No stored memories matched this prompt for this project.

Deeper history may still exist. Search it with the supermemory search_memory
tool — exposed as mcp__…supermemory__search_memory; if its schema is not
loaded, resolve the exact name with ToolSearch("+supermemory search_memory").

- Scope to this project with containerTag: "repo_example__05efa6d3b677caff"
- Omit containerTag to search the account's active/shared space instead.
- Read-only supermemory calls are auto-approved; they never prompt the user.

Worth a call when the user refers to past decisions, earlier sessions, or says
"remember" / "we decided" / "last time". Skip it for self-contained tasks.
</supermemory-recall>

Roughly 110 tokens. Design notes:

  • Points at ToolSearch, not a hardcoded name. The tool ships under three names depending on install shape — see TOOL_NAME_RE in recall-approve.js — and under deferred tool loading the model holds a bare name with no schema, so a direct call fails with InputValidationError. A lookup is correct for every install; one hardcoded string is wrong for two of three.
  • Says the call is free. recall-approve.js already auto-approves read-only supermemory tools. That was built but never surfaced to the model, and unknown permission cost is a reason not to bother.
  • Includes negative guidance, so awareness doesn't turn into a search on every turn.

Gating

Fires only when all four hold:

  1. fresh.length === 0 — recall found nothing new
  2. results.length === 0 — container is genuinely empty, not all-repeats (if hits came back, formatRecall already delivered the same guidance)
  3. sessionDir exists
  4. not already sent this session

Misses are the common case in an empty project, so an ungated block would tax every prompt. The marker is a file in the session dir next to recalled.json; writeState would have meant widening the EVENT_NAMES allowlist and touching the statusline renderer.

Every earlier return is untouched — shouldSkip, missing API key, and the recallDirective override all still bail before this point.

Testing

No automated coverage added; verified manually against the live API rather than stubs. From a repo with 0 memories the block renders with the real container tag interpolated; a second prompt in the same session is silent; a fresh session shows it again; a repo with memories takes the normal recall path unchanged. Existing suite unaffected at 24/24.

Release

Bumps 0.1.6 -> 0.1.7 across latest.json, package.json, and plugin/.claude-plugin/plugin.json, matching the convention in fde77a7 and c8c3cd2.

Not included

  • mcp-proxy.js arg injection. The proxy is currently a pure pass-through that only adds Authorization, but it already resolves cwd and getContainerTag sits in the same lib/. Defaulting containerTag on tools/call would make repo scoping true by construction instead of dependent on the model cooperating. Higher-value follow-up than this PR.
  • biome.json lints nothing. Its includes is ["src/**", "scripts/**"], neither of which exists in this layout — leftover from the 0.0.9 structure. npm run lint silently checks 0 files.

🤖 Generated with Claude Code

https://claude.ai/code/session_011qEtmi9chBJMgirVCTpSLu

@Dhravya
Dhravya force-pushed the feat/recall-discovery-on-empty branch from 9a91b91 to ed39afa Compare September 12, 2026 22:53
@capy-ai

capy-ai Bot commented Sep 12, 2026

Copy link
Copy Markdown

If claude-supermemory#124 lands, this discovery block's omit line inverts.

mcp-proxy.js will inject the repo container when containerTag is missing, so:

- Omit containerTag to search the account's active/shared space instead.

becomes wrong. After injection, omit = this project. Suggested replacement for the bullets:

- search_memory defaults to this project's container: "${containerTag}" (omit containerTag)
- Pass containerTag only to search a different space; resolve names with listSpaces
- Read-only supermemory calls are auto-approved; they never prompt the user.

The ToolSearch / mcp__…supermemory__search_memory wording is correct for Claude Code (three namespaces + deferred schemas). Don't copy that prompt to the other plugins — Codex is mcp__supermemory__search_memory, Cursor is supermemory_search, OpenCode is the supermemory tool with mode: "search".

…p to 0.1.7

The recall hook can only inject what it finds. On a miss it returned
silently, and silence reads to the model as "there is nothing to search"
— so it never reached for the tool, and history living in another
container stayed invisible for the whole session.

The guidance that would have fixed this already existed, but only inside
formatRecall(), which runs when recall already found something. It was
delivered in the case where it was least needed and withheld in the case
where it mattered most.

Emit a discovery block on the first genuinely-empty recall of a session:
name the tool, point at ToolSearch to resolve it (the tool ships under
three names depending on install shape, and deferred loading means the
model holds a bare name with no schema), interpolate the repo
containerTag, and note that read-only calls are auto-approved.

Gated to fire once per session, and only when the container is truly
empty rather than all-repeats — misses are the common case in an empty
project, so repeating it would tax every prompt and invite searches on
turns where memory is irrelevant. The marker is a file in the session
dir next to recalled.json; writeState would have meant widening the
EVENT_NAMES allowlist and touching the statusline renderer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qEtmi9chBJMgirVCTpSLu
@Dhravya Dhravya changed the title feat: advertise search_memory when a project has no memories yet feat: advertise search_memory when a project has no memories yet; bump to 0.1.7 Sep 17, 2026
@Dhravya
Dhravya force-pushed the feat/recall-discovery-on-empty branch from ed39afa to 54d6ca2 Compare September 17, 2026 01:05
@Dhravya
Dhravya merged commit a1d2bb9 into main Sep 17, 2026
4 checks passed
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