Skip to content

Commit eabab1f

Browse files
fix(active-memory): expose memory tools to recall runs (#74592)
Fix Active Memory recall runs so plugin tool allowlists from composed Memory Core agents flow into embedded tool execution, restoring callable memory plugin tools during recall.\n\nCo-authored-by: vyctorbrzezowski <vyctorbrzezowski@users.noreply.github.com>
1 parent 54f44ec commit eabab1f

8 files changed

Lines changed: 49 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ Docs: https://docs.openclaw.ai
170170
- Providers/DeepSeek: expose native DeepSeek V4 `xhigh` and `max` thinking levels through the provider `resolveThinkingProfile` hook so `/think xhigh|max` applies the intended effort instead of falling back to base levels. (#73008) Thanks @ai-hpc.
171171
- Agents/Codex: bound embedded-run cleanup, trajectory flushing, and command-lane task timeouts after runtime failures, so Discord and other chat sessions return to idle instead of staying stuck in processing. Thanks @vincentkoc.
172172
- Heartbeat/exec: consume successful metadata-only async exec completions silently so Telegram and other chat surfaces no longer ask users for missing command logs after `No session found`. Fixes #74595. Thanks @gkoch02.
173+
- Active Memory/Memory: materialize allowlisted memory plugin tools for lightweight embedded recall runs so Memory Core tools do not collapse to an empty runtime allowlist. Fixes #74572. (#74592) Thanks @LaFleurAdvertising and @vyctorbrzezowski.
173174
- Web fetch: add a documented `tools.web.fetch.ssrfPolicy.allowIpv6UniqueLocalRange` opt-in and thread it through cache keys and DNS/IP checks so trusted fake-IP proxy stacks using `fc00::/7` can work without broad private-network access. Fixes #74351. Thanks @jeffrey701.
174175
- OpenAI Codex: restore `/verbose full` persistence and app-server tool-output forwarding, and retry Gateway E2E temp-home cleanup so debug runs do not regress on stale validation or cleanup flakes. Thanks @vincentkoc.
175176
- Anthropic/Meridian: preserve text and thinking content seeded on `content_block_start` in anthropic-messages streams, so `[thinking, text]` replies no longer persist as empty turns or trigger empty-response fallbacks. Fixes #74410. Thanks @vyctorbrzezowski.

extensions/active-memory/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,7 @@ describe("active-memory plugin", () => {
10391039
"If memory_recall is unavailable, use memory_search and memory_get.",
10401040
);
10411041
expect(runParams?.toolsAllow).toEqual(["memory_recall", "memory_search", "memory_get"]);
1042+
expect(runParams?.allowGatewaySubagentBinding).toBe(true);
10421043
expect(runParams?.prompt).toContain(
10431044
"When searching for preference or habit recall, use a permissive recall limit or memory_search threshold before deciding that no useful memory exists.",
10441045
);

extensions/active-memory/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,6 +2138,7 @@ async function runRecallSubagent(params: {
21382138
trigger: "manual",
21392139
toolsAllow: ["memory_recall", "memory_search", "memory_get"],
21402140
disableMessageTool: true,
2141+
allowGatewaySubagentBinding: true,
21412142
bootstrapContextMode: "lightweight",
21422143
verboseLevel: "off",
21432144
thinkLevel: params.config.thinking,

src/agents/pi-embedded-runner/run/attempt.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
wrapStreamFnSanitizeMalformedToolCalls,
3131
wrapStreamFnTrimToolCallNames,
3232
} from "./attempt.js";
33+
import { buildEmbeddedAttemptToolRunContext } from "./attempt.tool-run-context.js";
3334

3435
type FakeWrappedStream = {
3536
result: () => Promise<unknown>;
@@ -82,6 +83,24 @@ describe("applyEmbeddedAttemptToolsAllow", () => {
8283
});
8384
});
8485

86+
describe("buildEmbeddedAttemptToolRunContext", () => {
87+
it("carries runtime toolsAllow into coding tool construction", () => {
88+
expect(
89+
buildEmbeddedAttemptToolRunContext({
90+
trigger: "manual",
91+
jobId: "job-1",
92+
memoryFlushWritePath: "memory/log.md",
93+
toolsAllow: ["memory_search", "memory_get"],
94+
}),
95+
).toMatchObject({
96+
trigger: "manual",
97+
jobId: "job-1",
98+
memoryFlushWritePath: "memory/log.md",
99+
runtimeToolAllowlist: ["memory_search", "memory_get"],
100+
});
101+
});
102+
});
103+
85104
describe("normalizeMessagesForLlmBoundary", () => {
86105
it("strips tool result details before provider conversion", () => {
87106
const input = [

src/agents/pi-embedded-runner/run/attempt.tool-run-context.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@ export function buildEmbeddedAttemptToolRunContext(params: {
88
trigger?: EmbeddedRunTrigger;
99
jobId?: string;
1010
memoryFlushWritePath?: string;
11+
toolsAllow?: string[];
1112
trace?: DiagnosticTraceContext;
1213
}): {
1314
trigger?: EmbeddedRunTrigger;
1415
jobId?: string;
1516
memoryFlushWritePath?: string;
17+
runtimeToolAllowlist?: string[];
1618
trace?: DiagnosticTraceContext;
1719
} {
1820
return {
1921
trigger: params.trigger,
2022
jobId: params.jobId,
2123
memoryFlushWritePath: params.memoryFlushWritePath,
24+
...(params.toolsAllow ? { runtimeToolAllowlist: params.toolsAllow } : {}),
2225
...(params.trace ? { trace: freezeDiagnosticTraceContext(params.trace) } : {}),
2326
};
2427
}

src/agents/pi-tools.create-openclaw-coding-tools.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from "node:fs/promises";
22
import os from "node:os";
33
import path from "node:path";
4-
import { describe, expect, it } from "vitest";
4+
import { describe, expect, it, vi } from "vitest";
55
import type { OpenClawConfig } from "../config/types.openclaw.js";
66
import {
77
applyXaiModelCompat,
@@ -12,6 +12,7 @@ import {
1212
import "./test-helpers/fast-bash-tools.js";
1313
import "./test-helpers/fast-coding-tools.js";
1414
import "./test-helpers/fast-openclaw-tools.js";
15+
import { createOpenClawTools } from "./openclaw-tools.js";
1516
import { createOpenClawCodingTools } from "./pi-tools.js";
1617
import { createHostSandboxFsBridge } from "./test-helpers/host-sandbox-fs-bridge.js";
1718
import { expectReadWriteEditTools } from "./test-helpers/pi-tools-fs-helpers.js";
@@ -163,6 +164,22 @@ describe("createOpenClawCodingTools", () => {
163164
).toBeNull();
164165
});
165166

167+
it("uses runtime toolsAllow when materializing plugin tools", () => {
168+
const createOpenClawToolsMock = vi.mocked(createOpenClawTools);
169+
createOpenClawToolsMock.mockClear();
170+
171+
createOpenClawCodingTools({
172+
config: testConfig,
173+
runtimeToolAllowlist: ["memory_search", "memory_get"],
174+
});
175+
176+
expect(createOpenClawToolsMock).toHaveBeenCalledWith(
177+
expect.objectContaining({
178+
pluginToolAllowlist: expect.arrayContaining(["memory_search", "memory_get"]),
179+
}),
180+
);
181+
});
182+
166183
it("preserves action enums in normalized schemas", () => {
167184
const defaultTools = createOpenClawCodingTools({ config: testConfig, senderIsOwner: true });
168185
const toolNames = ["canvas", "nodes", "cron", "gateway", "message"];

src/agents/pi-tools.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ export function createOpenClawCodingTools(options?: {
333333
hasRepliedRef?: { value: boolean };
334334
/** Allow plugin tools for this run to late-bind the gateway subagent. */
335335
allowGatewaySubagentBinding?: boolean;
336+
/** Runtime-scoped explicit allowlist used to materialize matching plugin tools. */
337+
runtimeToolAllowlist?: string[];
336338
/** If true, the model has native vision capability */
337339
modelHasVision?: boolean;
338340
/** Require explicit message targets (no implicit last-route sends). */
@@ -629,6 +631,7 @@ export function createOpenClawCodingTools(options?: {
629631
groupPolicy,
630632
sandboxToolPolicy,
631633
subagentPolicy,
634+
options?.runtimeToolAllowlist ? { allow: options.runtimeToolAllowlist } : undefined,
632635
]),
633636
currentChannelId: options?.currentChannelId,
634637
currentThreadTs: options?.currentThreadTs,

src/agents/test-helpers/fast-openclaw-tools.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ const coreTools = [
4646
stubTool("pdf"),
4747
];
4848

49+
const createOpenClawToolsMock = vi.fn(() => coreTools.map((tool) => Object.assign({}, tool)));
50+
4951
vi.mock("../openclaw-tools.js", () => ({
50-
createOpenClawTools: () => coreTools.map((tool) => Object.assign({}, tool)),
52+
createOpenClawTools: createOpenClawToolsMock,
5153
__testing: {
5254
setDepsForTest: () => {},
5355
},

0 commit comments

Comments
 (0)