fix(memory-core): allow bounded dreaming session cleanup#70464
fix(memory-core): allow bounded dreaming session cleanup#70464chiyouYCH wants to merge 2 commits into
Conversation
Greptile SummaryThis PR fixes dreaming narrative session leaks by making Confidence Score: 5/5Safe to merge; remaining findings are P2 style improvements that do not block correctness. The logic for stable session keys, idempotency-key divergence, pre-cleanup, and scoped admin elevation is sound. The new tests correctly validate all three authorization branches. The two open comments are P2: one suggests adding a warn-level log to the silent pre-cleanup catch, the other flags dead-code in the colon-stripping helper that is harmless but could confuse future maintainers. Neither affects runtime correctness. No files require special attention. Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/memory-core/src/dreaming-narrative.ts
Line: 858-863
Comment:
**Silent pre-cleanup swallows non-404 errors**
The empty `catch` block here discards any error — including auth failures (e.g. if the plugin scope isn't set correctly) or transient network errors — with no log output. A real failure would silently leave stale context in place and start the run in a dirty state, making it very hard to diagnose. Consider logging at `warn` level for unexpected errors while still ignoring expected "not found" cases.
```suggestion
// Clear stale context from a previous failed cleanup before reusing the stable session key.
try {
await params.subagent.deleteSession({ sessionKey });
} catch (preCleanupErr) {
// The session may not exist yet; that is expected. Log unexpected errors so
// they don't vanish silently.
params.logger.warn(
`memory-core: pre-cleanup delete failed for ${params.data.phase} phase: ${formatErrorMessage(preCleanupErr)}`,
);
}
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/gateway/server-plugins.ts
Line: 254-264
Comment:
**Colon-stripping logic doesn't match current key format**
`buildNarrativeSessionKey` now produces keys like `dreaming-narrative-light-<hash>` — no `:` separators — so `getCanonicalSessionKeySegment` always falls through to `return sessionKey` for these keys. The two-colon namespace-stripping path is dead code for the current key format. If namespaced keys are a future concern, a comment explaining the expected format (`ns1:ns2:dreaming-narrative-...`) would make the intent clearer and prevent future maintainers from removing what looks like unreachable logic.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(memory-core): allow bounded dreaming..." | Re-trigger Greptile Re-review progress:
|
| // Clear stale context from a previous failed cleanup before reusing the stable session key. | ||
| try { | ||
| await params.subagent.deleteSession({ sessionKey }); | ||
| } catch { | ||
| // The session may not exist yet, and narrative generation is best-effort. | ||
| } |
There was a problem hiding this comment.
Silent pre-cleanup swallows non-404 errors
The empty catch block here discards any error — including auth failures (e.g. if the plugin scope isn't set correctly) or transient network errors — with no log output. A real failure would silently leave stale context in place and start the run in a dirty state, making it very hard to diagnose. Consider logging at warn level for unexpected errors while still ignoring expected "not found" cases.
| // Clear stale context from a previous failed cleanup before reusing the stable session key. | |
| try { | |
| await params.subagent.deleteSession({ sessionKey }); | |
| } catch { | |
| // The session may not exist yet, and narrative generation is best-effort. | |
| } | |
| // Clear stale context from a previous failed cleanup before reusing the stable session key. | |
| try { | |
| await params.subagent.deleteSession({ sessionKey }); | |
| } catch (preCleanupErr) { | |
| // The session may not exist yet; that is expected. Log unexpected errors so | |
| // they don't vanish silently. | |
| params.logger.warn( | |
| `memory-core: pre-cleanup delete failed for ${params.data.phase} phase: ${formatErrorMessage(preCleanupErr)}`, | |
| ); | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/memory-core/src/dreaming-narrative.ts
Line: 858-863
Comment:
**Silent pre-cleanup swallows non-404 errors**
The empty `catch` block here discards any error — including auth failures (e.g. if the plugin scope isn't set correctly) or transient network errors — with no log output. A real failure would silently leave stale context in place and start the run in a dirty state, making it very hard to diagnose. Consider logging at `warn` level for unexpected errors while still ignoring expected "not found" cases.
```suggestion
// Clear stale context from a previous failed cleanup before reusing the stable session key.
try {
await params.subagent.deleteSession({ sessionKey });
} catch (preCleanupErr) {
// The session may not exist yet; that is expected. Log unexpected errors so
// they don't vanish silently.
params.logger.warn(
`memory-core: pre-cleanup delete failed for ${params.data.phase} phase: ${formatErrorMessage(preCleanupErr)}`,
);
}
```
How can I resolve this? If you propose a fix, please make it concise.| function getCanonicalSessionKeySegment(sessionKey: string): string { | ||
| const firstSeparator = sessionKey.indexOf(":"); | ||
| if (firstSeparator < 0) { | ||
| return sessionKey; | ||
| } | ||
| const secondSeparator = sessionKey.indexOf(":", firstSeparator + 1); | ||
| if (secondSeparator < 0) { | ||
| return sessionKey; | ||
| } | ||
| return sessionKey.slice(secondSeparator + 1); | ||
| } |
There was a problem hiding this comment.
Colon-stripping logic doesn't match current key format
buildNarrativeSessionKey now produces keys like dreaming-narrative-light-<hash> — no : separators — so getCanonicalSessionKeySegment always falls through to return sessionKey for these keys. The two-colon namespace-stripping path is dead code for the current key format. If namespaced keys are a future concern, a comment explaining the expected format (ns1:ns2:dreaming-narrative-...) would make the intent clearer and prevent future maintainers from removing what looks like unreachable logic.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-plugins.ts
Line: 254-264
Comment:
**Colon-stripping logic doesn't match current key format**
`buildNarrativeSessionKey` now produces keys like `dreaming-narrative-light-<hash>` — no `:` separators — so `getCanonicalSessionKeySegment` always falls through to `return sessionKey` for these keys. The two-colon namespace-stripping path is dead code for the current key format. If namespaced keys are a future concern, a comment explaining the expected format (`ns1:ns2:dreaming-narrative-...`) would make the intent clearer and prevent future maintainers from removing what looks like unreachable logic.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb05821040
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| deleteTranscript, | ||
| }, | ||
| { | ||
| syntheticScopes, |
There was a problem hiding this comment.
Apply synthetic delete scope with request-scoped clients
This syntheticScopes override does not take effect for the common plugin-hook path where a request scope already has client set, because dispatchGatewayMethod prefers scope.client over synthetic client construction. In those runs (all gateway handlers are wrapped with request scope in src/gateway/server-methods.ts), memory-core still hits missing scope: operator.admin on sessions.delete, so the new stable dreaming-narrative-* key can keep reusing uncleared session state and reintroduce narrative/session pollution.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f228ca913d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| deleteTranscript, | ||
| }, | ||
| { | ||
| syntheticScopes, |
There was a problem hiding this comment.
Apply cleanup admin scope to request-scoped clients
deleteSession now computes syntheticScopes, but this option is ineffective whenever a request scope already has client because dispatchGatewayMethod prefers scope.client over synthetic client creation. Fresh evidence: gateway handlers run inside withPluginRuntimeGatewayRequestScope({ context, client, ... }) in src/gateway/server-methods.ts:174, so plugin calls typically carry a non-admin scope.client; in that path, memory-core dreaming cleanup still invokes sessions.delete without operator.admin and fails with missing scope: operator.admin, leaving the stable dreaming-narrative-* session uncleared.
Useful? React with 👍 / 👎.
f228ca9 to
c4f77d9
Compare
|
Related work from PRtags group Title: Open PR candidate: memory-core dreaming-narrative session leak cleanup
|
c4f77d9 to
7a9e42c
Compare
|
Codex review: needs maintainer review before merge. Workflow note: Future ClawSweeper reviews update this same comment in place. How this review workflow works
Summary Reproducibility: yes. at source level. Current main still embeds PR rating Rank-up moves:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. Real behavior proof Risk before merge
Maintainer options:
Next step before merge Security Review detailsBest possible solution: Land the focused memory-core stable-key and pre-cleanup fix after resolving current-main conflicts and rerunning validation, while leaving trajectory-sidecar and restart-residue cleanup to #77723 and #78458. Do we have a high-confidence way to reproduce the issue? Yes, at source level. Current main still embeds Is this the best way to solve the issue? Yes. Stable workspace/phase session keys plus timestamped idempotency keys and plugin-local pre-cleanup are the narrow maintainable fix, and the latest branch preserves the existing gateway cleanup contract. Label justifications:
What I checked:
Likely related people:
Codex review notes: model gpt-5.5, reasoning high; reviewed against a13468320c63. |
7a9e42c to
095cf08
Compare
095cf08 to
db2852f
Compare
|
Updated this branch to match the narrower repair requested by review. Current state:
CI note: the remaining failing checks I can inspect are inherited from current main around |
d322ebb to
8b169f8
Compare
|
Updated the branch to address the latest ClawSweeper What changed:
Local validation:
Note: |
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Neon Clawlet Hatch commandComment Hatchability rules:
Rarity: 🥚 common. What is this egg doing here?
|
|
@clawsweeper automerge |
|
ClawSweeper 🐠 reef update Thanks for the work on this. ClawSweeper could not push to this branch with the permissions available, so it opened a narrow replacement PR to keep the fix swimming forward without losing the contributor trail. not your fault, just GitHub branch-permission tides. Why replacement: ClawSweeper could not update the source PR branch directly; GitHub did not grant sufficient push rights to the bot for that branch.
fish notes: model gpt-5.5, reasoning high; reviewed against c752d0f. |
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Add `plugins.entries.memory-core.config.dreaming.excludeAgents` so specific agent ids can be kept out of scheduled dreaming workspace fan-out while their normal memory and transcripts remain intact. Motivation: email/messaging agents that handle every inbound message (e.g. an email triage agent on a Gmail hook session like `agent:email:hook:gmail:triage`) feed reactive content into dreaming that is too noisy and frequently mis-classified — a marketing invitation read as a personal interest, an unsolicited promo absorbed as the user's preference. v2026.5.20's built-in cron-session filter (`cronRunTranscriptPaths`, scoped to session keys matching `cron:<jobId>:run:<runId>`) does not cover these channel-inbound sessions, so an agent-id-level opt-out is needed in addition. - src/memory-host-sdk/dreaming.ts: add `excludeAgents: string[]` to MemoryDreamingConfig, a `normalizeAgentIdArray` helper, and a resolveMemoryDreamingWorkspaces filter that drops excluded agent workspaces before fan-out. - extensions/memory-core/openclaw.plugin.json: configSchema + uiHints entry for `dreaming.excludeAgents`. - src/memory-host-sdk/dreaming.test.ts: covers the workspace filter. This is the agent-exclusion half of the original Chris patch (May 5 v2026.5.5-era); the cron-session-exclusion half was upstreamed in v2026.5.20 via openclaw#70464 and is intentionally not duplicated here. Upstream PR candidate.
Summary
Fixes the remaining dreaming narrative session pollution path without changing the Gateway plugin-owned cleanup contract:
dreaming-narrative-*session keys stable per workspace + phase, instead of embeddingnowMsin the session keynowMsinto the subagentidempotencyKeysessions.deletebehavior; this revision intentionally does not touchsrc/gateway/*This is intended to address the cleanup failure and UI/session-store leak reported in #68252, #69187, and #70402 while keeping the fix scoped to bundled
memory-corebehavior.Tests
mainverified the final PR diff is limited toCHANGELOG.mdandextensions/memory-core/src/*.main.check-prod-types/check-test-typesfailures are inherited from main commit2e78fc57in unrelatedextensions/codexandextensions/microsoft-foundryfiles, matching the main-branch CI note ond4e04f33.Real behavior proof
Behavior or issue addressed: Memory-core dreaming narrative sessions should be bounded by workspace and phase. Repeated dreaming sweeps for the same workspace/phase should reuse the same
dreaming-narrative-*session key while still using timestamp-specific idempotency keys so separate sweeps do not dedupe incorrectly. Stale prior narrative context should be removed before the next run.Real environment tested: Isolated local OpenClaw workspace and session store used while preparing this repair, separate from my normal OpenClaw service. The local smoke used the installed package hotfix path equivalent to this PR's memory-core session-key/idempotency/pre-cleanup behavior.
Exact steps or command run after this patch:
dreaming-narrative-*sessions through the plugin subagentsessions.deletepath and recounted the session store.Evidence after fix:
Observed result after fix: The narrative session key stayed stable for the same workspace and phase across timestamps, remained different across workspaces, and the isolated session store reached
dreaming_session_count=0after deleting staledreaming-narrative-*sessions. This confirms the repair bounds session accumulation without removing per-run idempotency.What was not tested: I did not run a full production dreaming cron sweep against a real user workspace in this PR because that would mutate private memory/dream diary state. The official GitHub Actions matrix covers type, lint, boundary, and unit-test validation for the submitted branch.