Skip to content

fix(plugin): confine automatic recall to the calling session by default - #2562

Open
saya-ch wants to merge 1 commit into
MemTensor:mainfrom
saya-ch:fix/recall-scope-session
Open

saya-ch wants to merge 1 commit into
MemTensor:mainfrom
saya-ch:fix/recall-scope-session

Conversation

@saya-ch

@saya-ch saya-ch commented Nov 5, 2026 •

Copy link
Copy Markdown

Description

Automatic turn-start recall selected every trace visible to the profile. A trace stores its transcript verbatim ([user] … / [assistant] …), so with two Sessions open under one profileId, one Session's assistant text reached another Session's prompt with nothing distinguishing it from that Session's own history. Observed on DSH 0.2.1-rc.5: Session 08a39873 recalled a trace owned by Session f03c9595, whose assistant field opened with a first-person self-description.

Root cause: core/runtime/namespace.ts::visibilityWhere() filters on owner_agent_kind and owner_profile_id only; it has no session dimension. The storage layer already accepts filter.sessionId — retrieval never passed it down.

What this changes

algorithm.retrieval.recallScope (default session) is applied only to the turn-start automatic injection path.

path recall scope
onTurnStart → recallTurn → retrieveTurnStart (automatic prompt injection) confined to the calling Session
explicit memos_search profile-wide
decision_repair profile-wide
skill_invoke, sub_agent profile-wide
L2 policies, L3 world_model, skills profile-wide

Pinning the explicit paths would be a functional regression, not a fix: decision_repair exists to find historical anti-patterns across Sessions, and memos_search is a deliberate user request. This PR therefore narrows the scope to the injection path, which is the one that corrupts agent identity.

Mechanics:

  • The Session key comes from the existing RuntimeNamespace.sessionKey (adapters/deepseek-harness/namespace.ts), which both adapters already populate. No new config channel, no new runtime field, no synthetic-id heuristics.
  • The pin is pushed into the SQL WHERE (session_id = @vis_recall_session_key) rather than applied after Top-K, so foreign rows cannot consume the LIMIT budget.
  • When pinned, tier-2 drops the legacy same-session session_id != X predicate; otherwise the two AND into an always-empty result (session_id = X AND session_id != X). The ts / user_text halves of those predicates are preserved.
  • A missing or non-string sessionKey fails open to profile-wide recall — a Session id that does not exist must never confine recall to zero rows.
  • getManyByIds keeps its isVisibleTo namespace guard and is not session-filtered: L2/L3 artifacts cite trace ids as evidence, and dropping a foreign row there would inject an unsupported citation.

A Viewer toggle exposes the same key under Settings → General.

Scope and limitations

  • Breaking default. Existing installs have no recallScope on disk, so they inherit session and lose cross-session transcript recall from automatic injection. Set algorithm.retrieval.recallScope: profile to opt back in. Documented in docs/CONFIG-ADVANCED.md.
  • Not an isolation guarantee. timeline, memos_get, listTraces and the Team Hub path do not go through the retrieval wrapper and are unaffected by this switch. They require an explicit id and do not inject into the prompt automatically. This PR fixes the injection path only; it does not claim to make stored memory unreadable across Sessions.
  • A new Session starts with no recall until it has written its first trace. That is the intended trade-off.
  • All changes are under apps/memos-local-plugin/; production dependencies are unchanged.

Related Issue: #1532 (same failure mode; #1869 addressed it for the OpenClaw adapter, whose change set touches only apps/memos-local-openclaw/), #2520 (DSH 0.2.x support; this does not change peer ranges or the Cloud plugin).

Type of change

  • Bug fix — cross-session transcript injection into the prompt
  • Breaking change — the default recallScope changes from profile-wide to session-scoped for turn-start recall

How Has This Been Tested?

Environment: Windows 11 x64, Node 24.13.1, pnpm 10.10.0, DSH 0.2.1-rc.5, dependencies installed with pnpm install --ignore-scripts.

The regression suite is tests/unit/pipeline/recall-scope.test.ts (9 cases). It runs against a real temporary SQLite database through the real wrapRetrievalRepos and, for two cases, through createPipeline + createMemoryCore. It supersedes three earlier test files that asserted on mocked arguments and an empty database — those were tautological and are not part of this revision.

Coverage:

  • every trace search channel (vector / FTS / pattern / error-signature) is confined;
  • k=1 returns the pinned Session's row, proving the pin is in SQL and not a post-filter;
  • scope=profile and a missing sessionKey both restore profile-wide recall;
  • getManyByIds still rejects a row from another profile;
  • L2 policies / L3 world models remain visible;
  • the real onTurnStart injection contains the calling Session and not the other;
  • explicit searchMemory still returns both Sessions — the functional-regression guard;
  • with lightweightMemory: false (which supplies excludeSessionId), the pin is not cancelled by the legacy exclusion.

Verification actually run:

$ pnpm lint                      # tsc -p tsconfig.json --noEmit
EXIT_CODE=0

$ npx tsc -p tsconfig.viewer.json --noEmit
EXIT_CODE=0

$ npx vitest run tests/unit/retrieval tests/unit/pipeline
 Test Files  25 passed (25)
      Tests  233 passed (233)
EXIT_CODE=0

$ pnpm build:viewer
✓ built

Two independent regression probes confirm the tests bind to the implementation rather than to themselves:

  • disabling the SQL pin → 3 failures, exactly the confinement cases;
  • disabling the tier-2 cooperation → 1 failure, the excludeSessionId case (context comes back empty).

Note: pnpm lint's tsconfig.json does not include tests/** and excludes viewer, so it does not cover the two areas this PR touches most. That is why the viewer typecheck is run separately, and why the new test file was type-checked on its own (tsconfig including only tests/unit/pipeline/recall-scope.test.ts): 0 errors. The repository's other test files do report pre-existing type errors under that config; they are untouched by this PR.

Full suite on this machine is not clean for environment reasons I did not triage exhaustively (tests/unit/config/paths.test.ts asserts POSIX separators, tests/unit/embedding/* needs network). A Linux full-suite run is still required before merge.

No live profile, credentials, or customer sessions were used.

Checklist

  • I have performed a self-review of my own code
  • I have commented the session-scoping logic where it is not self-evident
  • I have added tests demonstrating the fix
  • Documentation update in MemOS-Docs — not applicable; the key is documented in the config schema comment, docs/CONFIG-ADVANCED.md and the Viewer toggle
  • Related issues are linked above
  • Reviewers: @hijzy and @whipser030

Reviewer Checklist

  • Scope reviewed: changes confined to apps/memos-local-plugin/; the default-value change and the non-guarantee are both called out
  • Local checks passed on Windows; Linux full-suite run still needed
  • Tests and real output provided, including the two probe results

@Memtensor-AI Memtensor-AI added area:plugin OpenClaw & Hermes status:in-progress Someone or AI is working on it | 人工或 AI 正在处理 labels Nov 5, 2026
@saya-ch
saya-ch force-pushed the fix/recall-scope-session branch from 40baa3b to a347647 Compare November 5, 2026 15:34
@Memtensor-AI

Memtensor-AI commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

🤖 Open Code Review

Target: PR #2562
Task: fc77d82c215e2605
Base: main
Head: fix/recall-scope-session
Head SHA: db1194019d700d790e110ed9fff070470b9f5ffc

🔍 OpenCodeReview found 6 issue(s) in this PR.

⚠️ Review warnings (5)

  • bounded analysis finalized with explicit task_done after reviewing all group files
  • bounded analysis finalized with explicit task_done after reviewing all group files
  • bounded analysis finalized with explicit task_done after reviewing all group files
  • bounded analysis finalized with explicit task_done after reviewing all group files
  • Filtered 1 low-confidence OCR finding(s) before posting/fix-loop.

1. apps/memos-local-plugin/viewer/src/views/SettingsView.tsx (L60)

AlgorithmBlock.retrieval models only recallScope, but get("algorithm") (line ~186) shallow-merges the saved block with dirty, and patch likewise only replaces retrieval in the dirty slice. As soon as this toggle is flipped, algorithm.retrieval becomes { recallScope } and every other retrieval field from the saved config disappears from the object handed to GeneralTab. Harmless today because only recallScope is read here, but any future read of e.g. algorithm.retrieval.tier1TopK would silently be undefined. Either model the full retrieval block in AlgorithmBlock or deep-merge algorithm in get/patch.


2. apps/memos-local-plugin/core/config/defaults.ts (L288)

Defaulting to session means every consumer of the base retrieval deps now observes config.recallScope === "session" — including handle.retrievalDeps(), which memory-core.searchMemory re-wraps via wrapRetrievalRepos(handle.repos, ns) without the session pin. tier2-trace.buildTraceSearchFilters uses that config flag as proof the SQL is already session_id = <pin> and therefore drops the session_id != … / excludeSessionId predicates, even though the SQL is actually unpinned. Today nothing breaks (the tool-driven path never supplies visibleContext/excludeSessionId), but the two settings are now coupled only by convention. Consider deriving the "scoped" decision from an explicit pin marker on the wrapped repos (or always setting config.recallScope together with the pin) so a future caller of retrievalDeps() cannot silently lose the same-session exclusion.


3. apps/memos-local-plugin/core/pipeline/deps.ts (L144)

This seeds the runtime marker from the on-disk preference (default "session"), but buildRetrievalDeps still wraps repos as wrapRetrievalRepos(deps.repos, deps.namespace) — without the recallScope option, i.e. without the SQL pin. The two switches are therefore out of lockstep for every consumer of the base deps: handle.retrievalDeps() is used by memory-core.searchMemory, which rebuilds deps as config: applyTopKOverride(baseDeps.config, …) + repos: wrapRetrievalRepos(handle.repos, ns) (unpinned). Any such caller that supplies visibleContext/excludeSessionId will have tier-2 drop the same-session exclusions while no pin exists, silently re-injecting already-visible traces. Either leave the marker unset here (let retrievalDepsFor be the single owner) or pass { recallScope: alg.retrieval.recallScope } to the wrapper in the same construction.


4. apps/memos-local-plugin/core/pipeline/retrieval-repos.ts (L37-L42)

The fail-open path leaves the marker and the SQL pin out of sync: on a turn-start call deps.config.recallScope is still "session", so buildTraceSearchFilters takes the scoped branch and drops both (session_id != @visible_context_session_id OR …) and the input.excludeSessionId fallback, even though no pin was emitted here. The result is profile-wide recall without the visible-context dedupe — i.e. already-visible same-session traces can be re-injected, and the #1532 fix is silently a no-op whenever the host does not supply sessionKey. Suggested: derive the pin from a guaranteed source (the turn-start ctx sessionId, which resolveVisibleContext already receives) instead of the optional namespace.sessionKey hint, and/or expose whether the pin applied so the tier-2 scoped flag cannot diverge from it.


5. apps/memos-local-plugin/core/pipeline/retrieval-repos.ts (L237)

Please confirm namespace.sessionKey and traces.session_id live in the same value domain. sessionKey is an optional host hint (RuntimeNamespace.sessionKey?: string), whereas traces.session_id is the id minted by the session manager / bridge. If they can differ (e.g. OpenClaw sessionKey vs. bridgeSessionId), this exact-match pin matches zero rows and turn-start recall silently returns nothing once recallScope: "session" is the default.


6. apps/memos-local-plugin/core/retrieval/tier2-trace.ts (L355)

The scoped branch silently removes the last remaining caller-side exclusion: resolveVisibleContext returns { excludeSessionId: sessionId } when the adapter does not set visibleContextKnown (retrieve.ts:755-760), and with scoped === true the new !scoped && input.excludeSessionId guard drops that predicate entirely. Combined with the pin, the current Session's already model-visible transcript is then the only candidate pool, so it gets re-injected as snippets (the exact no-repeat behaviour this filter existed for). Since recallScope defaults to "session" on disk, this hits default installs whose adapters only send sessionId. Either keep a ts/user_text-based exclusion for this fallback, or have the upgrade path of those adapters populate visibleContextKnown before flipping the default.


🧹 Filtered 1 low-confidence OCR finding(s) before posting/fix-loop (duplicate: 1).

Generated by cloud-assistant via Open Code Review.

@Memtensor-AI

Copy link
Copy Markdown
Collaborator

⚠️ Automated Test Results: ENV ISSUE

The test environment encountered an issue that requires manual attention.

Details: Executor error: Node dependency preparation failed for memos_local_plugin: Command failed with exit code 1: npm ci --include=dev --no-audit --no-fund
stdout:
stderr: npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
npm error code 1
npm error path /data/test-workspaces/61f30985f5d371e4/repo/apps/memos-local-plugin/node_modules/onnxruntime-node
npm error command failed
npm error command sh -c node ./script/install
npm error /data/test-workspaces/61f30985f5d371e4/repo/apps/memos-local-plugin/node_modules/onnxruntime-node/script/install-utils.js:57
npm error reject(new Error(Failed to download build list. HTTP status code = ${statusCode}));
npm error ^
npm error
npm error Error: Failed to download build list. HTTP status code = 302
npm error at ClientRequest. (/data/test-workspaces/61f30985f5d371e4/repo/apps/memos-local-plugin/node_modules/onnxruntime-node/script/install-utils.js:57:18)
npm error at Object.onceWrapper (node:events:634:26)
npm error at ClientRequest.emit (node:events:519:28)
npm error at ClientRequest.emit (node:domain:489:12)
npm error at HTTPParser.parserOnIncomingClient (node:_http_client:780:27)
npm error at HTTPParser.parserOnHeadersComplete (node:_http_common:125:17)
npm error at TLSSocket.socketOnData (node:_http_client:615:22)
npm error at TLSSocket.emit (node:events:519:28)
npm error at TLSSocket.emit (node:domain:489:12)
npm error at addChunk (node:internal/streams/readable:561:12)
npm error
npm error Node.js v22.22.2
npm error A complete log of this run can be found in: /data/test-workspaces/_shared/npm-downloads/_logs/2026-11-05T15_58_25_293Z-debug-0.log
Branch: fix/recall-scope-session

Tested source: a34764762dc219078bc9a531e60f73af028fad3b
Tested base: d326a6419467c2205041b58533b0ebfdf4f38173
Tested commit: a34764762dc219078bc9a531e60f73af028fad3b
These results apply only to this immutable source/base snapshot.

@saya-ch
saya-ch force-pushed the fix/recall-scope-session branch from a347647 to 53764d2 Compare November 5, 2026 16:18
@saya-ch

saya-ch commented Nov 5, 2026

Copy link
Copy Markdown
Author

Thanks for the review — findings #1 and #7 were correct and critical, and they exposed a gap my own verification had missed.

The first revision scoped only memory-core.searchMemory (the tool-driven memos_search path). The primary turn_start chain goes onTurnStart → recallTurn → retrieveTurnStart → orchestrator.retrievalDepsFor(), which rebuilt its repos with wrapRetrievalRepos(deps.repos, namespace) and never received a sessionKey. So the default claimed isolation while automatic recall stayed profile-wide. I had treated an A/B smoke as proof of the fix, but that run merely happened to surface a same-session trace first; it was correlation, not causation.

Changes in 53764d2:

The reason my five original tests did not catch this is that they exercised wrapRetrievalRepos directly. I added recall-scope-turnstart.test.ts, which spies on the wrapper and asserts the turn-start path itself threads sessionKey. Verified that it fails when scopedSessionKey is temporarily disabled, and passes after the fix. Also added coverage for profile mode leaving recall unscoped.

Current verification: pnpm lint passes; recall-scope.test.ts (5) + recall-scope-turnstart.test.ts (2) + retrieval-repos.test.ts (1) + orchestrator.test.ts (15) = 23 passed. Full suite on this Windows sandbox still has environment-only failures (config/paths.test.ts POSIX separators, embedding/* requiring Hugging Face); a Linux full-suite run is still needed.

Regarding the automated test result: the ENV ISSUE is onnxruntime-node failing to download its build list (HTTP 302) during npm ci — it is a dependency-preparation failure, not a test failure on this branch.

Thanks again for the thorough review; #1/#7 materially improved the fix.

@Memtensor-AI

Copy link
Copy Markdown
Collaborator

⚠️ Automated Test Results: ENV ISSUE

The test environment encountered an issue that requires manual attention.

Details: Executor error: Node dependency preparation failed for memos_local_plugin: Command failed with exit code 1: npm ci --include=dev --no-audit --no-fund
stdout:
stderr: npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
npm error code 1
npm error path /data/test-workspaces/9e60c8bf57ce1aa0/repo/apps/memos-local-plugin/node_modules/onnxruntime-node
npm error command failed
npm error command sh -c node ./script/install
npm error /data/test-workspaces/9e60c8bf57ce1aa0/repo/apps/memos-local-plugin/node_modules/onnxruntime-node/script/install-utils.js:57
npm error reject(new Error(Failed to download build list. HTTP status code = ${statusCode}));
npm error ^
npm error
npm error Error: Failed to download build list. HTTP status code = 302
npm error at ClientRequest. (/data/test-workspaces/9e60c8bf57ce1aa0/repo/apps/memos-local-plugin/node_modules/onnxruntime-node/script/install-utils.js:57:18)
npm error at Object.onceWrapper (node:events:634:26)
npm error at ClientRequest.emit (node:events:519:28)
npm error at ClientRequest.emit (node:domain:489:12)
npm error at HTTPParser.parserOnIncomingClient (node:_http_client:780:27)
npm error at HTTPParser.parserOnHeadersComplete (node:_http_common:125:17)
npm error at TLSSocket.socketOnData (node:_http_client:615:22)
npm error at TLSSocket.emit (node:events:519:28)
npm error at TLSSocket.emit (node:domain:489:12)
npm error at addChunk (node:internal/streams/readable:561:12)
npm error
npm error Node.js v22.22.2
npm error A complete log of this run can be found in: /data/test-workspaces/_shared/npm-downloads/_logs/2026-11-05T16_46_15_421Z-debug-0.log
Branch: fix/recall-scope-session

Tested source: 53764d201046071040be485a57e6e2c858d5ac3d
Tested base: d326a6419467c2205041b58533b0ebfdf4f38173
Tested commit: 53764d201046071040be485a57e6e2c858d5ac3d
These results apply only to this immutable source/base snapshot.

@saya-ch
saya-ch force-pushed the fix/recall-scope-session branch from 53764d2 to e334e0c Compare November 5, 2026 18:11
@saya-ch

saya-ch commented Nov 5, 2026

Copy link
Copy Markdown
Author

Thanks for the second pass — finding #2 was the important one, and it identified a defect my first fix introduced rather than one it missed.

retrieve.ts supplies excludeSessionId = <calling session> for turn_start recall whenever lightweightMemory is off, and buildTraceSearchFilters() turns that into session_id != @exclude_session_id. AND-ing it with the new SQL pin (session_id = @vis_session_key) yields an always-empty predicate. So on those hosts the default would have disabled trace recall entirely. I confirmed this in the source before changing anything.

Changes in e334e0c:

Current verification: pnpm lint passes with no output; retrieval + pipeline suites are 234/234. Four independent regression probes (disable sessionKey, remove only the SQL predicate, disable turn-start chaining, disable tier-2 cooperation) each produce failures in the corresponding test, so every layer is covered by an assertion that actually binds to it.

The repeated ENV ISSUE is still onnxruntime-node failing to fetch its build list (HTTP 302) during npm ci — a dependency-preparation failure in the executor, identical in both runs and unrelated to this branch. A Linux full-suite run remains needed before merge.

Thanks again; the second round caught a real regression in my own fix.

@Memtensor-AI

Copy link
Copy Markdown
Collaborator

⚠️ Automated Test Results: ENV ISSUE

The test environment encountered an issue that requires manual attention.

Details: Executor error: Node dependency preparation failed for memos_local_plugin: Command failed with exit code 1: npm ci --include=dev --no-audit --no-fund
stdout:
stderr: npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
npm error code 1
npm error path /data/test-workspaces/8571292b4098200d/repo/apps/memos-local-plugin/node_modules/onnxruntime-node
npm error command failed
npm error command sh -c node ./script/install
npm error /data/test-workspaces/8571292b4098200d/repo/apps/memos-local-plugin/node_modules/onnxruntime-node/script/install-utils.js:57
npm error reject(new Error(Failed to download build list. HTTP status code = ${statusCode}));
npm error ^
npm error
npm error Error: Failed to download build list. HTTP status code = 302
npm error at ClientRequest. (/data/test-workspaces/8571292b4098200d/repo/apps/memos-local-plugin/node_modules/onnxruntime-node/script/install-utils.js:57:18)
npm error at Object.onceWrapper (node:events:634:26)
npm error at ClientRequest.emit (node:events:519:28)
npm error at ClientRequest.emit (node:domain:489:12)
npm error at HTTPParser.parserOnIncomingClient (node:_http_client:780:27)
npm error at HTTPParser.parserOnHeadersComplete (node:_http_common:125:17)
npm error at TLSSocket.socketOnData (node:_http_client:615:22)
npm error at TLSSocket.emit (node:events:519:28)
npm error at TLSSocket.emit (node:domain:489:12)
npm error at addChunk (node:internal/streams/readable:561:12)
npm error
npm error Node.js v22.22.2
npm error A complete log of this run can be found in: /data/test-workspaces/_shared/npm-downloads/_logs/2026-11-05T18_19_06_379Z-debug-0.log
Branch: fix/recall-scope-session

Tested source: e334e0cdd6693550c78d3beb605b20eaed921f9d
Tested base: d326a6419467c2205041b58533b0ebfdf4f38173
Tested commit: e334e0cdd6693550c78d3beb605b20eaed921f9d
These results apply only to this immutable source/base snapshot.

Automatic turn-start recall selected every trace visible to the profile. A trace stores its transcript verbatim, so a second Session's assistant text reached the first Session's prompt with nothing marking it as foreign (MemOS#1532).

Add algorithm.retrieval.recallScope (default session). Only the turn-start injection path is pinned: explicit searches, decision repair and sub-agent retrieval keep profile-wide recall, because they deliberately look across history. The pin is pushed into the SQL WHERE so foreign rows cannot consume the LIMIT budget, and tier-2 drops the legacy same-session exclusion when pinned so the two predicates cannot AND into an always-empty result. L2/L3 stay unconfined.

Session key comes from the existing RuntimeNamespace.sessionKey; no new config or runtime channel is introduced. A missing key fails open to profile-wide recall.
@saya-ch
saya-ch force-pushed the fix/recall-scope-session branch from e334e0c to db11940 Compare November 6, 2026 06:40
@saya-ch

saya-ch commented Nov 6, 2026

Copy link
Copy Markdown
Author

Thanks — finding #1 was correct and it invalidated the approach rather than a parameter.

I verified it against the source: orchestrator applied the pin to tool_driven, skill_invoke, sub_agent and decision_repair through the same helper, and RepairCtx.sessionId / ToolDrivenCtx.sessionId are required fields, so the "missing sessionId falls back to profile-wide" escape never applied to them. decision_repair exists precisely to find historical anti-patterns across Sessions, so pinning it returns nothing. Your end-to-end reproduction (profile-wide 1 row / owner session 1 row / fresh session 0 rows) matches what I now see.

I did not patch that call site. I restructured the change in db11940:

Scope narrowed to the turn-start injection path. retrievalDepsFor() now takes an optional recallScope that only retrieveTurnStart passes; every other entry point gets "profile". The explicit paths are profile-wide by design, and the PR description states the table explicitly instead of claiming "both entry points are scoped".

The Session key no longer travels through a new channel. It is read from the existing RuntimeNamespace.sessionKey, which both adapters already populate. That removed config.sessionKey, the resolveRecallSessionKey helper, the adhoc-session- prefix heuristic (#2) and the misleading path: "turn_start" log. A missing key fails open with no heuristic at all.

#3 — getManyByIds keeps isVisibleTo(r, namespace). Removing it was my error in the previous revision; you are right that the wrapper is a public export and that only the session restriction may be skipped.

#4 — visibleContext is no longer discarded. When pinned, only the session_id != X half of each OR is dropped; ts < @visible_context_start_ts and user_text NOT IN (…) are preserved. The earlier comment claimed this while the code did the opposite.

#5 — parameter renamed to @vis_recall_session_key, merged after opts.params.

#7 — the synthetic-id hack is gone, so the two paths cannot disagree; there is only one path that pins now.

On patch payloads (#2 in round three): I checked core/config/writer.ts::applyPatch — it recurses into plain objects, so sending only { retrieval: { recallScope } } is correct and does not clobber siblings. Round two's advice to spread the whole resolved block was wrong on that point; I followed the code, not the earlier recommendation.

On tests: the previous suites were tautological, as you said — where.includes("session_id =") && where.includes("session_id !=") was false-by-construction, and the turn-start spy ran against an empty database. I deleted all three files. The replacement is one suite of 9 cases against a real SQLite database, including a functional-regression guard (explicit searchMemory must still return both Sessions) and a case where lightweightMemory: false supplies excludeSessionId and would zero the result if tier-2 cooperation were missing.

Verification, actually run:

pnpm lint                                            EXIT 0
npx tsc -p tsconfig.viewer.json --noEmit             EXIT 0
npx vitest run tests/unit/retrieval tests/unit/pipeline
  Test Files  25 passed (25)   Tests  233 passed (233)
pnpm build:viewer                                    ✓ built

Two probes: disabling the SQL pin → 3 failures (the confinement cases); disabling tier-2 cooperation → 1 failure (the excludeSessionId case). Both reverted.

I also agree on the pnpm lint coverage gap: tsconfig.json excludes tests/** and viewer, so it never checked either. The viewer now gets its own tsc run and the new test file was type-checked separately (0 errors); the repository's pre-existing test type errors are untouched by this PR.

Given three rounds of automated findings, I would value a human review pass before merge — particularly on whether narrowing to the turn-start path is the right product decision.

@Memtensor-AI

Copy link
Copy Markdown
Collaborator

⚠️ Automated Test Results: ENV ISSUE

The test environment encountered an issue that requires manual attention.

Details: Executor error: Node dependency preparation failed for memos_local_plugin: Command failed with exit code 1: npm ci --include=dev --no-audit --no-fund
stdout:
stderr: npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
npm error code 1
npm error path /data/test-workspaces/fc77d82c215e2605/repo/apps/memos-local-plugin/node_modules/onnxruntime-node
npm error command failed
npm error command sh -c node ./script/install
npm error /data/test-workspaces/fc77d82c215e2605/repo/apps/memos-local-plugin/node_modules/onnxruntime-node/script/install-utils.js:57
npm error reject(new Error(Failed to download build list. HTTP status code = ${statusCode}));
npm error ^
npm error
npm error Error: Failed to download build list. HTTP status code = 302
npm error at ClientRequest. (/data/test-workspaces/fc77d82c215e2605/repo/apps/memos-local-plugin/node_modules/onnxruntime-node/script/install-utils.js:57:18)
npm error at Object.onceWrapper (node:events:634:26)
npm error at ClientRequest.emit (node:events:519:28)
npm error at ClientRequest.emit (node:domain:489:12)
npm error at HTTPParser.parserOnIncomingClient (node:_http_client:780:27)
npm error at HTTPParser.parserOnHeadersComplete (node:_http_common:125:17)
npm error at TLSSocket.socketOnData (node:_http_client:615:22)
npm error at TLSSocket.emit (node:events:519:28)
npm error at TLSSocket.emit (node:domain:489:12)
npm error at addChunk (node:internal/streams/readable:561:12)
npm error
npm error Node.js v22.22.2
npm error A complete log of this run can be found in: /data/test-workspaces/_shared/npm-downloads/_logs/2026-11-06T10_12_49_813Z-debug-0.log
Branch: fix/recall-scope-session

Tested source: db1194019d700d790e110ed9fff070470b9f5ffc
Tested base: d326a6419467c2205041b58533b0ebfdf4f38173
Tested commit: db1194019d700d790e110ed9fff070470b9f5ffc
These results apply only to this immutable source/base snapshot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:plugin OpenClaw & Hermes status:in-progress Someone or AI is working on it | 人工或 AI 正在处理

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants