






















@@ -1789,6 +1789,50 @@ describe("active-memory plugin", () => {
17891789expect(lines.some((line) => line.includes("timeout_partial"))).toBe(false);
17901790});
179117911792+it("does not inject embedded timeout boilerplate from partial transcripts", async () => {
1793+__testing.setMinimumTimeoutMsForTests(1);
1794+__testing.setSetupGraceTimeoutMsForTests(0);
1795+api.pluginConfig = {
1796+agents: ["main"],
1797+timeoutMs: 1,
1798+logging: true,
1799+};
1800+plugin.register(api as unknown as OpenClawPluginApi);
1801+const sessionKey = "agent:main:timeout-boilerplate-transcript";
1802+hoisted.sessionStore[sessionKey] = {
1803+sessionId: "s-timeout-boilerplate-transcript",
1804+updatedAt: 0,
1805+};
1806+runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
1807+await writeTranscriptJsonl(params.sessionFile, [
1808+{
1809+type: "message",
1810+message: {
1811+role: "assistant",
1812+content: "LLM request timed out after 15000 ms.",
1813+},
1814+},
1815+]);
1816+await new Promise<never>(() => {});
1817+});
1818+1819+const result = await hooks.before_prompt_build(
1820+{ prompt: "what wings should i order? timeout boilerplate", messages: [] },
1821+{
1822+agentId: "main",
1823+trigger: "user",
1824+ sessionKey,
1825+messageProvider: "webchat",
1826+},
1827+);
1828+1829+expect(result).toBeUndefined();
1830+const lines = getActiveMemoryLines(sessionKey);
1831+expect(lines).toEqual([expect.stringContaining("🧩 Active Memory: status=timeout")]);
1832+expect(lines.some((line) => line.includes("timeout_partial"))).toBe(false);
1833+expect(lines.some((line) => line.includes("LLM request timed out"))).toBe(false);
1834+});
1835+17921836it("returns partial transcript text when an aborted subagent rejects before the race timeout wins", async () => {
17931837__testing.setMinimumTimeoutMsForTests(1);
17941838api.pluginConfig = {
@@ -2258,6 +2302,171 @@ describe("active-memory plugin", () => {
22582302expect(wallClockMs).toBeLessThan(CONFIGURED_TIMEOUT_MS + HARD_DEADLINE_MARGIN_MS);
22592303});
226023042305+it("fast-fails terminal zero-hit memory_search results without waiting for recall timeout", async () => {
2306+const CONFIGURED_TIMEOUT_MS = 1_000;
2307+__testing.setMinimumTimeoutMsForTests(1);
2308+__testing.setSetupGraceTimeoutMsForTests(0);
2309+api.pluginConfig = {
2310+agents: ["main"],
2311+timeoutMs: CONFIGURED_TIMEOUT_MS,
2312+logging: true,
2313+};
2314+plugin.register(api as unknown as OpenClawPluginApi);
2315+const sessionKey = "agent:main:terminal-zero-hit";
2316+hoisted.sessionStore[sessionKey] = { sessionId: "s-terminal-zero-hit", updatedAt: 0 };
2317+runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
2318+await writeTranscriptJsonl(params.sessionFile, [
2319+{
2320+message: {
2321+role: "toolResult",
2322+toolName: "memory_search",
2323+details: { results: [], debug: { backend: "qmd", hits: 0, searchMs: 8 } },
2324+},
2325+},
2326+]);
2327+await new Promise<never>(() => {});
2328+});
2329+2330+const startedAt = Date.now();
2331+const result = await hooks.before_prompt_build(
2332+{ prompt: "what food do i usually order? zero hit", messages: [] },
2333+{ agentId: "main", trigger: "user", sessionKey, messageProvider: "webchat" },
2334+);
2335+const wallClockMs = Date.now() - startedAt;
2336+2337+expect(result).toBeUndefined();
2338+expect(wallClockMs).toBeLessThan(CONFIGURED_TIMEOUT_MS);
2339+expect(getActiveMemoryLines(sessionKey)).toEqual([
2340+expect.stringContaining("🧩 Active Memory: status=empty"),
2341+expect.stringContaining("🔎 Active Memory Debug: backend=qmd searchMs=8 hits=0"),
2342+]);
2343+});
2344+2345+it("does not fast-fail memory_search results solely because debug hits is zero", async () => {
2346+__testing.setMinimumTimeoutMsForTests(1);
2347+__testing.setSetupGraceTimeoutMsForTests(0);
2348+api.pluginConfig = {
2349+agents: ["main"],
2350+timeoutMs: 500,
2351+logging: true,
2352+};
2353+plugin.register(api as unknown as OpenClawPluginApi);
2354+const sessionKey = "agent:main:terminal-zero-hit-with-results";
2355+hoisted.sessionStore[sessionKey] = {
2356+sessionId: "s-terminal-zero-hit-with-results",
2357+updatedAt: 0,
2358+};
2359+runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
2360+await writeTranscriptJsonl(params.sessionFile, [
2361+{
2362+message: {
2363+role: "toolResult",
2364+toolName: "memory_search",
2365+details: {
2366+results: [{ path: "memory/food.md", text: "User usually orders ramen." }],
2367+debug: { backend: "qmd", hits: 0, searchMs: 8 },
2368+},
2369+},
2370+},
2371+]);
2372+await new Promise((resolve) => setTimeout(resolve, 50));
2373+return { payloads: [{ text: "User usually orders ramen." }] };
2374+});
2375+2376+const result = await hooks.before_prompt_build(
2377+{ prompt: "what food do i usually order? zero hit with results", messages: [] },
2378+{ agentId: "main", trigger: "user", sessionKey, messageProvider: "webchat" },
2379+);
2380+2381+expect(result?.prependContext).toContain("User usually orders ramen.");
2382+expect(getActiveMemoryLines(sessionKey)).toEqual([
2383+expect.stringContaining("🧩 Active Memory: status=ok"),
2384+expect.stringContaining("🔎 Active Memory Debug: backend=qmd searchMs=8 hits=0"),
2385+]);
2386+});
2387+2388+it("fast-fails unavailable memory_search results without injecting provider errors", async () => {
2389+const CONFIGURED_TIMEOUT_MS = 1_000;
2390+__testing.setMinimumTimeoutMsForTests(1);
2391+__testing.setSetupGraceTimeoutMsForTests(0);
2392+api.pluginConfig = {
2393+agents: ["main"],
2394+timeoutMs: CONFIGURED_TIMEOUT_MS,
2395+logging: true,
2396+};
2397+plugin.register(api as unknown as OpenClawPluginApi);
2398+const sessionKey = "agent:main:terminal-unavailable";
2399+hoisted.sessionStore[sessionKey] = { sessionId: "s-terminal-unavailable", updatedAt: 0 };
2400+runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
2401+await writeTranscriptJsonl(params.sessionFile, [
2402+{
2403+message: {
2404+role: "toolResult",
2405+toolName: "memory_search",
2406+details: {
2407+disabled: true,
2408+warning: "Memory search is unavailable due to an embedding/provider error.",
2409+action: "Check the embedding provider configuration, then retry memory_search.",
2410+error: "embedding request failed",
2411+},
2412+},
2413+},
2414+]);
2415+await new Promise<never>(() => {});
2416+});
2417+2418+const startedAt = Date.now();
2419+const result = await hooks.before_prompt_build(
2420+{ prompt: "what food do i usually order? unavailable", messages: [] },
2421+{ agentId: "main", trigger: "user", sessionKey, messageProvider: "webchat" },
2422+);
2423+const wallClockMs = Date.now() - startedAt;
2424+2425+expect(result).toBeUndefined();
2426+expect(wallClockMs).toBeLessThan(CONFIGURED_TIMEOUT_MS);
2427+expect(getActiveMemoryLines(sessionKey)).toEqual([
2428+expect.stringContaining("🧩 Active Memory: status=empty"),
2429+expect.stringContaining(
2430+"🔎 Active Memory Debug: Memory search is unavailable due to an embedding/provider error. Check the embedding provider configuration, then retry memory_search.",
2431+),
2432+]);
2433+});
2434+2435+it("does not treat memory_get misses as terminal recall results", async () => {
2436+__testing.setMinimumTimeoutMsForTests(1);
2437+__testing.setSetupGraceTimeoutMsForTests(0);
2438+api.pluginConfig = {
2439+agents: ["main"],
2440+timeoutMs: 500,
2441+};
2442+plugin.register(api as unknown as OpenClawPluginApi);
2443+runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => {
2444+await writeTranscriptJsonl(params.sessionFile, [
2445+{
2446+message: {
2447+role: "toolResult",
2448+toolName: "memory_get",
2449+details: { path: "memory/missing.md", text: "", disabled: true, error: "not found" },
2450+},
2451+},
2452+]);
2453+await new Promise((resolve) => setTimeout(resolve, 50));
2454+return { payloads: [{ text: "User usually orders ramen after late flights." }] };
2455+});
2456+2457+const result = await hooks.before_prompt_build(
2458+{ prompt: "what food do i usually order? memory get miss", messages: [] },
2459+{
2460+agentId: "main",
2461+trigger: "user",
2462+sessionKey: "agent:main:memory-get-miss",
2463+messageProvider: "webchat",
2464+},
2465+);
2466+2467+expect(result?.prependContext).toContain("User usually orders ramen after late flights.");
2468+});
2469+22612470it("returns undefined instead of throwing when an unexpected error escapes prompt building", async () => {
22622471const result = await hooks.before_prompt_build(
22632472{ prompt: "what should i eat? escape test", messages: undefined as never },
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。