






















@@ -125,6 +125,20 @@ describe("active-memory plugin", () => {
125125"utf8",
126126);
127127};
128+const waitForAbort = async (abortSignal?: AbortSignal): Promise<never> => {
129+if (abortSignal?.aborted) {
130+throw (abortSignal.reason as unknown) ?? new Error("Operation aborted");
131+}
132+return await new Promise<never>((_resolve, reject) => {
133+abortSignal?.addEventListener(
134+"abort",
135+() => {
136+reject((abortSignal.reason as unknown) ?? new Error("Operation aborted"));
137+},
138+{ once: true },
139+);
140+});
141+};
128142const makeMemoryToolAllowlistError = (
129143reason: string,
130144sources = "runtime toolsAllow: memory_recall, memory_search, memory_get",
@@ -145,6 +159,7 @@ describe("active-memory plugin", () => {
145159146160beforeEach(async () => {
147161vi.clearAllMocks();
162+runEmbeddedPiAgent.mockReset();
148163stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-active-memory-test-"));
149164configFile = {
150165plugins: {
@@ -171,6 +186,9 @@ describe("active-memory plugin", () => {
171186},
172187},
173188};
189+for (const key of Object.keys(hoisted.sessionStore)) {
190+delete hoisted.sessionStore[key];
191+}
174192hoisted.sessionStore["agent:main:main"] = {
175193sessionId: "s-main",
176194updatedAt: 0,
@@ -1547,26 +1565,28 @@ describe("active-memory plugin", () => {
15471565const sessionKey = "agent:main:transcript-debug";
15481566hoisted.sessionStore[sessionKey] = { sessionId: "s-main", updatedAt: 0 };
154915671550-runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
1551-const lines = [
1552-JSON.stringify({
1553-message: {
1554-role: "toolResult",
1555-toolName: "memory_search",
1556-details: { debug: { backend: "qmd", hits: 3 } },
1557-},
1558-}),
1559-JSON.stringify({
1560-message: {
1561-role: "toolResult",
1562-toolName: "memory_search",
1563-details: {},
1564-},
1565-}),
1566-];
1567-await fs.writeFile(params.sessionFile, `${lines.join("\n")}\n`, "utf8");
1568-return { payloads: [{ text: "wings are fine." }] };
1569-});
1568+runEmbeddedPiAgent.mockImplementationOnce(
1569+async (params: { sessionFile: string; abortSignal?: AbortSignal }) => {
1570+const lines = [
1571+JSON.stringify({
1572+message: {
1573+role: "toolResult",
1574+toolName: "memory_search",
1575+details: { debug: { backend: "qmd", hits: 3 } },
1576+},
1577+}),
1578+JSON.stringify({
1579+message: {
1580+role: "toolResult",
1581+toolName: "memory_search",
1582+details: {},
1583+},
1584+}),
1585+];
1586+await fs.writeFile(params.sessionFile, `${lines.join("\n")}\n`, "utf8");
1587+return { payloads: [{ text: "wings are fine." }] };
1588+},
1589+);
1570159015711591await hooks.before_prompt_build(
15721592{ prompt: "debug transcript bug", messages: [] },
@@ -1807,27 +1827,29 @@ describe("active-memory plugin", () => {
18071827sessionId: "s-timeout-partial",
18081828updatedAt: 0,
18091829};
1810-runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
1811-await writeTranscriptJsonl(
1812-params.sessionFile,
1813-[
1814-{ type: "message", message: { role: "user", content: "ignore this user text" } },
1815-{
1816-type: "message",
1817-message: { role: "assistant", content: "alpha beta gamma delta" },
1818-},
1819-{
1820-type: "message",
1821-message: {
1822-role: "assistant",
1823-content: [{ type: "text", text: "epsilon zeta eta theta" }],
1830+runEmbeddedPiAgent.mockImplementationOnce(
1831+async (params: { sessionFile: string; abortSignal?: AbortSignal }) => {
1832+await writeTranscriptJsonl(
1833+params.sessionFile,
1834+[
1835+{ type: "message", message: { role: "user", content: "ignore this user text" } },
1836+{
1837+type: "message",
1838+message: { role: "assistant", content: "alpha beta gamma delta" },
18241839},
1825-},
1826-],
1827-"\n{",
1828-);
1829-return await new Promise<never>(() => {});
1830-});
1840+{
1841+type: "message",
1842+message: {
1843+role: "assistant",
1844+content: [{ type: "text", text: "epsilon zeta eta theta" }],
1845+},
1846+},
1847+],
1848+"\n{",
1849+);
1850+return await waitForAbort(params.abortSignal);
1851+},
1852+);
1831185318321854const result = await hooks.before_prompt_build(
18331855{ prompt: "what wings should i order? timeout partial", messages: [] },
@@ -1879,15 +1901,7 @@ describe("active-memory plugin", () => {
18791901message: { role: "assistant", content: "temporary partial recall summary" },
18801902},
18811903]);
1882-await new Promise<never>((_resolve, reject) => {
1883-params.abortSignal?.addEventListener(
1884-"abort",
1885-() => {
1886-reject(params.abortSignal?.reason ?? new Error("Operation aborted"));
1887-},
1888-{ once: true },
1889-);
1890-});
1904+await waitForAbort(params.abortSignal);
18911905},
18921906);
18931907@@ -1899,7 +1913,9 @@ describe("active-memory plugin", () => {
18991913expect(result).toEqual({
19001914prependContext: expect.stringContaining("temporary partial recall summary"),
19011915});
1902-await expect(fs.access(tempSessionFile)).rejects.toThrow();
1916+await vi.waitFor(async () => {
1917+await expect(fs.access(tempSessionFile)).rejects.toThrow();
1918+});
19031919expect(getActiveMemoryLines(sessionKey)).toEqual(
19041920expect.arrayContaining([
19051921expect.stringContaining("🧩 Active Memory: status=timeout_partial"),
@@ -1925,10 +1941,12 @@ describe("active-memory plugin", () => {
19251941sessionId: "s-timeout-empty-transcript",
19261942updatedAt: 0,
19271943};
1928-runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
1929-await fs.writeFile(params.sessionFile, "", "utf8");
1930-return await new Promise<never>(() => {});
1931-});
1944+runEmbeddedPiAgent.mockImplementationOnce(
1945+async (params: { sessionFile: string; abortSignal?: AbortSignal }) => {
1946+await fs.writeFile(params.sessionFile, "", "utf8");
1947+return await waitForAbort(params.abortSignal);
1948+},
1949+);
1932195019331951const result = await hooks.before_prompt_build(
19341952{ prompt: "what wings should i order? empty timeout transcript", messages: [] },
@@ -1956,7 +1974,9 @@ describe("active-memory plugin", () => {
19561974sessionId: "s-timeout-missing-transcript",
19571975updatedAt: 0,
19581976};
1959-runEmbeddedPiAgent.mockImplementationOnce(async () => await new Promise<never>(() => {}));
1977+runEmbeddedPiAgent.mockImplementationOnce(
1978+async (params: { abortSignal?: AbortSignal }) => await waitForAbort(params.abortSignal),
1979+);
1960198019611981const result = await hooks.before_prompt_build(
19621982{ prompt: "what wings should i order? missing timeout transcript", messages: [] },
@@ -1983,18 +2003,20 @@ describe("active-memory plugin", () => {
19832003sessionId: "s-timeout-boilerplate-transcript",
19842004updatedAt: 0,
19852005};
1986-runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
1987-await writeTranscriptJsonl(params.sessionFile, [
1988-{
1989-type: "message",
1990-message: {
1991-role: "assistant",
1992-content: "LLM request timed out after 15000 ms.",
2006+runEmbeddedPiAgent.mockImplementationOnce(
2007+async (params: { sessionFile: string; abortSignal?: AbortSignal }) => {
2008+await writeTranscriptJsonl(params.sessionFile, [
2009+{
2010+type: "message",
2011+message: {
2012+role: "assistant",
2013+content: "LLM request timed out after 15000 ms.",
2014+},
19932015},
1994-},
1995-]);
1996-await new Promise<never>(() => {});
1997-});
2016+]);
2017+ await waitForAbort(params.abortSignal);
2018+},
2019+);
1998202019992021const result = await hooks.before_prompt_build(
20002022{ prompt: "what wings should i order? timeout boilerplate", messages: [] },
@@ -2494,18 +2516,20 @@ describe("active-memory plugin", () => {
24942516plugin.register(api as unknown as OpenClawPluginApi);
24952517const sessionKey = "agent:main:terminal-zero-hit";
24962518hoisted.sessionStore[sessionKey] = { sessionId: "s-terminal-zero-hit", updatedAt: 0 };
2497-runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
2498-await writeTranscriptJsonl(params.sessionFile, [
2499-{
2500-message: {
2501-role: "toolResult",
2502-toolName: "memory_search",
2503-details: { results: [], debug: { backend: "qmd", hits: 0, searchMs: 8 } },
2519+runEmbeddedPiAgent.mockImplementationOnce(
2520+async (params: { sessionFile: string; abortSignal?: AbortSignal }) => {
2521+await writeTranscriptJsonl(params.sessionFile, [
2522+{
2523+message: {
2524+role: "toolResult",
2525+toolName: "memory_search",
2526+details: { results: [], debug: { backend: "qmd", hits: 0, searchMs: 8 } },
2527+},
25042528},
2505-},
2506-]);
2507-await new Promise<never>(() => {});
2508-});
2529+]);
2530+ await waitForAbort(params.abortSignal);
2531+},
2532+);
2509253325102534const result = await hooks.before_prompt_build(
25112535{ prompt: "what food do i usually order? zero hit", messages: [] },
@@ -2579,23 +2603,25 @@ describe("active-memory plugin", () => {
25792603plugin.register(api as unknown as OpenClawPluginApi);
25802604const sessionKey = "agent:main:terminal-unavailable";
25812605hoisted.sessionStore[sessionKey] = { sessionId: "s-terminal-unavailable", updatedAt: 0 };
2582-runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
2583-await writeTranscriptJsonl(params.sessionFile, [
2584-{
2585-message: {
2586-role: "toolResult",
2587-toolName: "memory_search",
2588-details: {
2589-disabled: true,
2590-warning: "Memory search is unavailable due to an embedding/provider error.",
2591-action: "Check the embedding provider configuration, then retry memory_search.",
2592-error: "embedding request failed",
2606+runEmbeddedPiAgent.mockImplementationOnce(
2607+async (params: { sessionFile: string; abortSignal?: AbortSignal }) => {
2608+await writeTranscriptJsonl(params.sessionFile, [
2609+{
2610+message: {
2611+role: "toolResult",
2612+toolName: "memory_search",
2613+details: {
2614+disabled: true,
2615+warning: "Memory search is unavailable due to an embedding/provider error.",
2616+action: "Check the embedding provider configuration, then retry memory_search.",
2617+error: "embedding request failed",
2618+},
25932619},
25942620},
2595-},
2596-]);
2597-await new Promise<never>(() => {});
2598-});
2621+]);
2622+ await waitForAbort(params.abortSignal);
2623+},
2624+);
2599262526002626const result = await hooks.before_prompt_build(
26012627{ prompt: "what food do i usually order? unavailable", messages: [] },
@@ -3591,7 +3617,9 @@ describe("active-memory plugin", () => {
35913617circuitBreakerCooldownMs: 60_000,
35923618};
35933619plugin.register(api as unknown as OpenClawPluginApi);
3594-runEmbeddedPiAgent.mockImplementation(async () => await new Promise<never>(() => {}));
3620+runEmbeddedPiAgent.mockImplementation(
3621+async (params: { abortSignal?: AbortSignal }) => await waitForAbort(params.abortSignal),
3622+);
3595362335963624// First two calls should actually attempt the subagent (and timeout).
35973625await hooks.before_prompt_build(
@@ -3646,7 +3674,9 @@ describe("active-memory plugin", () => {
36463674plugin.register(api as unknown as OpenClawPluginApi);
3647367536483676// First call: timeout (trips the breaker with max=1).
3649-runEmbeddedPiAgent.mockImplementationOnce(async () => await new Promise<never>(() => {}));
3677+runEmbeddedPiAgent.mockImplementationOnce(
3678+async (params: { abortSignal?: AbortSignal }) => await waitForAbort(params.abortSignal),
3679+);
36503680await hooks.before_prompt_build(
36513681{ prompt: "cb reset test timeout", messages: [] },
36523682{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。