























@@ -334,6 +334,113 @@ describe("runMemoryFlushIfNeeded", () => {
334334]);
335335});
336336337+it("surfaces generic non-abort memory-flush failures so cron meta.error is populated (regression: #80755)", async () => {
338+const sessionEntry: SessionEntry = {
339+sessionId: "session",
340+updatedAt: Date.now(),
341+totalTokens: 80_000,
342+compactionCount: 1,
343+};
344+const visibleErrorPayloads: Array<{ text?: string; isError?: boolean }> = [];
345+runWithModelFallbackMock.mockRejectedValueOnce(
346+new Error("provider timed out after 60s while flushing memory"),
347+);
348+349+await runMemoryFlushIfNeeded({
350+cfg: { agents: { defaults: { compaction: { memoryFlush: {} } } } },
351+followupRun: createTestFollowupRun(),
352+sessionCtx: { Provider: "whatsapp" } as unknown as TemplateContext,
353+defaultModel: "anthropic/claude-opus-4-7",
354+agentCfgContextTokens: 100_000,
355+resolvedVerboseLevel: "off",
356+ sessionEntry,
357+sessionStore: { main: sessionEntry },
358+sessionKey: "main",
359+isHeartbeat: false,
360+replyOperation: createReplyOperation(),
361+onVisibleErrorPayloads: (payloads) => {
362+visibleErrorPayloads.push(...payloads);
363+},
364+});
365+366+expect(visibleErrorPayloads).toEqual([
367+{
368+text: "⚠️ provider timed out after 60s while flushing memory",
369+isError: true,
370+},
371+]);
372+});
373+374+it("redacts and caps generic visible memory-flush failures before delivery", async () => {
375+const sessionEntry: SessionEntry = {
376+sessionId: "session",
377+updatedAt: Date.now(),
378+totalTokens: 80_000,
379+compactionCount: 1,
380+};
381+const visibleErrorPayloads: Array<{ text?: string; isError?: boolean }> = [];
382+const token = "sk-abcdefghijklmnopqrstuv";
383+runWithModelFallbackMock.mockRejectedValueOnce(
384+new Error(`provider failed with Authorization: Bearer ${token} ${"x".repeat(800)}`),
385+);
386+387+await runMemoryFlushIfNeeded({
388+cfg: { agents: { defaults: { compaction: { memoryFlush: {} } } } },
389+followupRun: createTestFollowupRun(),
390+sessionCtx: { Provider: "whatsapp" } as unknown as TemplateContext,
391+defaultModel: "anthropic/claude-opus-4-7",
392+agentCfgContextTokens: 100_000,
393+resolvedVerboseLevel: "off",
394+ sessionEntry,
395+sessionStore: { main: sessionEntry },
396+sessionKey: "main",
397+isHeartbeat: false,
398+replyOperation: createReplyOperation(),
399+onVisibleErrorPayloads: (payloads) => {
400+visibleErrorPayloads.push(...payloads);
401+},
402+});
403+404+const [payload] = visibleErrorPayloads;
405+expect(payload?.isError).toBe(true);
406+expect(payload?.text).toMatch(/^⚠️ provider failed with Authorization: Bearer /);
407+expect(payload?.text).not.toContain(token);
408+expect(payload?.text?.length).toBeLessThanOrEqual(600);
409+expect(payload?.text?.endsWith("…")).toBe(true);
410+});
411+412+it("does not surface user-abort errors as visible payloads (regression: #80755)", async () => {
413+const sessionEntry: SessionEntry = {
414+sessionId: "session",
415+updatedAt: Date.now(),
416+totalTokens: 80_000,
417+compactionCount: 1,
418+};
419+const visibleErrorPayloads: Array<{ text?: string; isError?: boolean }> = [];
420+const abortErr = new Error("operation aborted by user");
421+abortErr.name = "AbortError";
422+runWithModelFallbackMock.mockRejectedValueOnce(abortErr);
423+424+await runMemoryFlushIfNeeded({
425+cfg: { agents: { defaults: { compaction: { memoryFlush: {} } } } },
426+followupRun: createTestFollowupRun(),
427+sessionCtx: { Provider: "whatsapp" } as unknown as TemplateContext,
428+defaultModel: "anthropic/claude-opus-4-7",
429+agentCfgContextTokens: 100_000,
430+resolvedVerboseLevel: "off",
431+ sessionEntry,
432+sessionStore: { main: sessionEntry },
433+sessionKey: "main",
434+isHeartbeat: false,
435+replyOperation: createReplyOperation(),
436+onVisibleErrorPayloads: (payloads) => {
437+visibleErrorPayloads.push(...payloads);
438+},
439+});
440+441+expect(visibleErrorPayloads).toEqual([]);
442+});
443+337444it("runs memory flush on the configured maintenance model without active fallbacks", async () => {
338445registerMemoryFlushPlanResolverForTest(() => ({
339446softThresholdTokens: 4_000,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。