openclaw - 💡(How to fix) Fix v2026.4.24: Agent's `memory_search` tool never invokes `recordShortTermRecalls`; only the human CLI does, leaving `recallCount=0` for all agent-driven recalls [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#74333Fetched 2026-04-30 06:25:22
View on GitHub
Comments
2
Participants
2
Timeline
3
Reactions
2
Author
Timeline (top)
commented ×2closed ×1

recordShortTermRecalls is the function that increments per-entry recall counters in the short-term recall store (~/.openclaw/workspace-<agent>/memory/.dreams/short-term-recall.json). Those counters feed the dreaming-promotion ranker, which surfaces frequently-recalled snippets for write-up into the agent's MEMORY.md.

In v2026.4.24, recordShortTermRecalls is invoked from exactly two call sites in the entire /usr/lib/node_modules/openclaw/dist/ tree:

  • cli.runtime-CgCWDoKb.js — the human-driven openclaw memory search <query> CLI (passes signalType: "recall").
  • dreaming-DugQEpYx.js — three call sites for background scans (__dreaming_sessions__:, __dreaming_daily__:, __dreaming_grounded__:) using signalType: "daily" or "grounded".

The agent-side memory_search tool — the one Antonio / Sage / Dev call dozens of times per session via the gateway HTTP path (tools-invoke-http-DxEbZW3I.jsmanager.search(...)) — returns results to the agent without recording the recall. The store therefore captures recallCount > 0 only for the rare snippets that were searched by the operator on the CLI, not for any of the snippets the agents themselves searched.

This is an observability gap, not a hard breakage: the promotion ranker gates on signalCount = recallCount + dailyCount + groundedCount, and the daily background scan reliably populates dailyCount, so promotion still works. But the data doesn't reflect what the system claims to be measuring, and any future ranker that wants to distinguish agent-driven from scan-driven recalls (which would be a sensible thing to want) is stuck.

Root Cause

  1. On v2026.4.24, ensure memory-core is enabled and an agent has memory_search in its tool list (default for all five stock agents).
  2. Have the agent issue memory_search queries — the easiest path is to send the orchestrator a question that requires memory lookup ("what do you remember about <topic from your workspace>?"). The agent's trajectory will show one or more toolCall: memory_search entries with results returned.
  3. Inspect ~/.openclaw/workspace-<agent>/memory/.dreams/short-term-recall.json. None of the snippets returned in step 2 will have recallCount incremented. The most-recently-touched entries will show lastRecalledAt: <today> only because the daily background scan touched them; their recallCount remains 0 and queryHashes contains only the scan's synthetic hash.
  4. Run openclaw memory search "<same topic>" from the shell — same workspace. Observe that recallCount does increment for the snippets matched by the CLI invocation.

Fix Action

Workaround

None at the user level — the recall store cannot be populated for agent-driven recalls without modifying the binary. The promotion pipeline still works because it gates on signalCount, not recallCount. For low-volume workspaces where the default minSignalCount gate is too tight, override:

"plugins": {
  "entries": {
    "memory-core": {
      "config": {
        "dreaming": {
          "phases": {
            "deep": {
              "minRecallCount": 0,
              "minUniqueQueries": 0
            }
          }
        }
      }
    }
  }
}

(Note that the override path is plugins.entries.memory-core.config.dreaming.phases.deep, not plugins.memory-core.config.…resolveMemoryPluginConfig in cli.runtime-CgCWDoKb.js is the canonical reference for the schema.)

Code Example

$ grep -rln 'recordShortTermRecalls' /usr/lib/node_modules/openclaw/dist/
  /usr/lib/node_modules/openclaw/dist/short-term-promotion-CI1S22E7.js   # definition
  /usr/lib/node_modules/openclaw/dist/cli.runtime-CgCWDoKb.js            # CLI caller, signalType "recall"
  /usr/lib/node_modules/openclaw/dist/dreaming-DugQEpYx.js               # background scans, signalType "daily"/"grounded"

---

"plugins": {
  "entries": {
    "memory-core": {
      "config": {
        "dreaming": {
          "phases": {
            "deep": {
              "minRecallCount": 0,
              "minUniqueQueries": 0
            }
          }
        }
      }
    }
  }
}

---

recordShortTermRecalls({
  workspaceDir,
  query,
  results,
  timezone,
  signalType: "recall",
});
RAW_BUFFERClick to expand / collapse

Summary

recordShortTermRecalls is the function that increments per-entry recall counters in the short-term recall store (~/.openclaw/workspace-<agent>/memory/.dreams/short-term-recall.json). Those counters feed the dreaming-promotion ranker, which surfaces frequently-recalled snippets for write-up into the agent's MEMORY.md.

In v2026.4.24, recordShortTermRecalls is invoked from exactly two call sites in the entire /usr/lib/node_modules/openclaw/dist/ tree:

  • cli.runtime-CgCWDoKb.js — the human-driven openclaw memory search <query> CLI (passes signalType: "recall").
  • dreaming-DugQEpYx.js — three call sites for background scans (__dreaming_sessions__:, __dreaming_daily__:, __dreaming_grounded__:) using signalType: "daily" or "grounded".

The agent-side memory_search tool — the one Antonio / Sage / Dev call dozens of times per session via the gateway HTTP path (tools-invoke-http-DxEbZW3I.jsmanager.search(...)) — returns results to the agent without recording the recall. The store therefore captures recallCount > 0 only for the rare snippets that were searched by the operator on the CLI, not for any of the snippets the agents themselves searched.

This is an observability gap, not a hard breakage: the promotion ranker gates on signalCount = recallCount + dailyCount + groundedCount, and the daily background scan reliably populates dailyCount, so promotion still works. But the data doesn't reflect what the system claims to be measuring, and any future ranker that wants to distinguish agent-driven from scan-driven recalls (which would be a sensible thing to want) is stuck.

Environment

  • OpenClaw: 2026.4.24 (cbcfdf6).
  • Node: 22.22.2.
  • OS: Linux 6.8.0-106-generic.
  • Plugins: memory-core enabled; default dreaming config + this host's overrides for phases.deep (minRecallCount: 0, minUniqueQueries: 0).
  • Agents: orchestrator, reasoner, coder, fast, multimodal — all use memory_search from their tool surface, and all share the same workspace-scoped short-term-recall.json.

Steps to reproduce

  1. On v2026.4.24, ensure memory-core is enabled and an agent has memory_search in its tool list (default for all five stock agents).
  2. Have the agent issue memory_search queries — the easiest path is to send the orchestrator a question that requires memory lookup ("what do you remember about <topic from your workspace>?"). The agent's trajectory will show one or more toolCall: memory_search entries with results returned.
  3. Inspect ~/.openclaw/workspace-<agent>/memory/.dreams/short-term-recall.json. None of the snippets returned in step 2 will have recallCount incremented. The most-recently-touched entries will show lastRecalledAt: <today> only because the daily background scan touched them; their recallCount remains 0 and queryHashes contains only the scan's synthetic hash.
  4. Run openclaw memory search "<same topic>" from the shell — same workspace. Observe that recallCount does increment for the snippets matched by the CLI invocation.

Expected behavior

memory_search invoked through any path — CLI, agent tool, MCP bridge — should result in a recordShortTermRecalls call so the recall store reflects what was actually retrieved.

Actual behavior

Only the CLI path records. The HTTP tool path (tools-invoke-http-DxEbZW3I.js) calls manager.search(query, opts), returns the results to the agent runtime, and the request handler exits without ever calling recordShortTermRecalls. The store stays at recallCount: 0 for everything the agent has ever searched.

Diagnostic evidence

  • Call-site enumeration:
    $ grep -rln 'recordShortTermRecalls' /usr/lib/node_modules/openclaw/dist/
    /usr/lib/node_modules/openclaw/dist/short-term-promotion-CI1S22E7.js   # definition
    /usr/lib/node_modules/openclaw/dist/cli.runtime-CgCWDoKb.js            # CLI caller, signalType "recall"
    /usr/lib/node_modules/openclaw/dist/dreaming-DugQEpYx.js               # background scans, signalType "daily"/"grounded"
    No reference in tools-invoke-http-DxEbZW3I.js, tools-registry-*.js, or any of the plugin entrypoints.
  • This host's ~/.openclaw/workspace-orchestrator/memory/.dreams/short-term-recall.json:
    • Total entries: 948.
    • Entries with lastRecalledAt >= 2026-04-23: 265 — all of them have recallCount: 0 and queryHashes of length exactly 1 (the daily-scan's synthetic hash).
    • Entries with recallCount > 0: 2 — both pre-Apr-23, both from rare ad-hoc openclaw memory search invocations that I personally ran.
  • Promotion-ranker gate (rankShortTermPromotionCandidates in short-term-promotion-CI1S22E7.js): the gate is on signalCount = recallCount + dailyCount + groundedCount, not recallCount alone. That's why this hasn't visibly broken promotion — the daily scan's dailyCount: 1 per touched entry is sufficient to get past the default minSignalCount gate.

Why this looked like a regression at first glance

On 2026-04-22, the dreaming run produced applied=5. From 2026-04-23 through 2026-04-28 (six consecutive nightly runs), the run produced applied=0. That looked like a regression, and at first I assumed recordShortTermRecalls had stopped firing. Investigation showed the missing wire-up is not new — it appears to have always been this way — and the apparent six-day regression was actually caused by a separate snippet-normalization mismatch in the --apply rehydration path (filed separately, see v4.24-dreaming-rehydration-mismatch.md).

So this issue is not the cause of any observed breakage on this host. I'm filing it as a standalone observability bug because the fix is small and the missing instrumentation makes the rehydration bug harder to diagnose (you can't tell whether candidates have been touched by agents or only by the scan).

Workaround

None at the user level — the recall store cannot be populated for agent-driven recalls without modifying the binary. The promotion pipeline still works because it gates on signalCount, not recallCount. For low-volume workspaces where the default minSignalCount gate is too tight, override:

"plugins": {
  "entries": {
    "memory-core": {
      "config": {
        "dreaming": {
          "phases": {
            "deep": {
              "minRecallCount": 0,
              "minUniqueQueries": 0
            }
          }
        }
      }
    }
  }
}

(Note that the override path is plugins.entries.memory-core.config.dreaming.phases.deep, not plugins.memory-core.config.…resolveMemoryPluginConfig in cli.runtime-CgCWDoKb.js is the canonical reference for the schema.)

Suggested fix

In tools-invoke-http-DxEbZW3I.js, after a successful manager.search(query, opts) call inside the memory_search handler, invoke:

recordShortTermRecalls({
  workspaceDir,
  query,
  results,
  timezone,
  signalType: "recall",
});

The CLI caller in cli.runtime-CgCWDoKb.js is the canonical reference for the call shape. Existing imports already include manager and workspaceDir in scope; timezone is the agent-config timezone (resolvable from the same context the existing handler uses for delivery).

A defensive version: wrap the call in try/catch and emit a low-priority log on failure, so a recording bug in the new path never breaks the actual search response to the agent.

Severity

Low–medium. Observability gap rather than functional breakage — promotion still works via signalCount-based gating. The bug only matters if:

  • The project wants to distinguish agent-driven vs scan-driven recalls in the ranker (a future-feature concern).
  • An operator is debugging why a particular snippet didn't get promoted — the absent recallCount makes it harder to confirm whether an agent ever touched that snippet, which extends diagnosis time on adjacent issues like the rehydration-mismatch bug.

The fix is small, the call signature is already established by the CLI path, and shipping it would close a real evidence gap. Hence "low–medium" rather than "low".

extent analysis

TL;DR

The most likely fix is to invoke recordShortTermRecalls in tools-invoke-http-DxEbZW3I.js after a successful manager.search call.

Guidance

  • Identify the memory_search handler in tools-invoke-http-DxEbZW3I.js and add a call to recordShortTermRecalls with the required parameters.
  • Verify that the recordShortTermRecalls function is being called correctly by checking the short-term-recall.json file for updated recallCount values.
  • Consider adding error handling to the recordShortTermRecalls call to prevent search response failures.
  • Review the promotion ranker logic to ensure it can handle the updated recallCount values.

Example

// In tools-invoke-http-DxEbZW3I.js, after a successful manager.search call
recordShortTermRecalls({
  workspaceDir,
  query,
  results,
  timezone,
  signalType: "recall",
});

Notes

The fix is relatively small and only affects the observability of agent-driven recalls. The promotion pipeline still works due to the signalCount-based gating.

Recommendation

Apply the suggested fix to invoke recordShortTermRecalls in tools-invoke-http-DxEbZW3I.js, as it closes a real evidence gap and improves the accuracy of the promotion ranker.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

memory_search invoked through any path — CLI, agent tool, MCP bridge — should result in a recordShortTermRecalls call so the recall store reflects what was actually retrieved.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix v2026.4.24: Agent's `memory_search` tool never invokes `recordShortTermRecalls`; only the human CLI does, leaving `recallCount=0` for all agent-driven recalls [2 comments, 2 participants]