mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-07 22:44:16 +00:00
fix(agents): increment compaction counter on overflow-triggered compaction (#39123)
Co-authored-by: MumuTW <clothl47364@gmail.com>
This commit is contained in:
@@ -263,6 +263,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Discord/model picker persistence check: add a short post-dispatch settle delay before reading back session model state so picker confirmations stop reporting false mismatch warnings after successful model switches. (#39105) Thanks @akropp.
|
||||
- Agents/OpenAI WS compat store flag: omit `store` from `response.create` payloads when model compat sets `supportsStore: false`, preventing strict OpenAI-compatible providers from rejecting websocket requests with unknown-field errors. (#39113) Thanks @scoootscooob.
|
||||
- Config/validation log sanitization: sanitize config-validation issue paths/messages before logging so control characters and ANSI escape sequences cannot inject misleading terminal output from crafted config content. (#39116) Thanks @powermaster888.
|
||||
- Agents/compaction counter accuracy: count successful overflow-triggered auto-compactions (`willRetry=true`) in the compaction counter while still excluding aborted/no-result events, so `/status` reflects actual safeguard compaction activity. (#39123) Thanks @MumuTW.
|
||||
|
||||
## 2026.3.2
|
||||
|
||||
|
||||
@@ -40,11 +40,17 @@ export function handleAutoCompactionStart(ctx: EmbeddedPiSubscribeContext) {
|
||||
|
||||
export function handleAutoCompactionEnd(
|
||||
ctx: EmbeddedPiSubscribeContext,
|
||||
evt: AgentEvent & { willRetry?: unknown },
|
||||
evt: AgentEvent & { willRetry?: unknown; result?: unknown; aborted?: unknown },
|
||||
) {
|
||||
ctx.state.compactionInFlight = false;
|
||||
const willRetry = Boolean(evt.willRetry);
|
||||
if (!willRetry) {
|
||||
// Increment counter whenever compaction actually produced a result,
|
||||
// regardless of willRetry. Overflow-triggered compaction sets willRetry=true
|
||||
// (the framework retries the LLM request), but the compaction itself succeeded
|
||||
// and context was trimmed — the counter must reflect that. (#38905)
|
||||
const hasResult = evt.result != null;
|
||||
const wasAborted = Boolean(evt.aborted);
|
||||
if (hasResult && !wasAborted) {
|
||||
ctx.incrementCompactionCount?.();
|
||||
}
|
||||
if (willRetry) {
|
||||
|
||||
@@ -38,11 +38,26 @@ describe("subscribeEmbeddedPiSession", () => {
|
||||
emit({ type: "auto_compaction_start" });
|
||||
expect(subscription.getCompactionCount()).toBe(0);
|
||||
|
||||
emit({ type: "auto_compaction_end", willRetry: true });
|
||||
// willRetry with result — counter IS incremented (overflow compaction succeeded)
|
||||
emit({ type: "auto_compaction_end", willRetry: true, result: { summary: "s" } });
|
||||
expect(subscription.getCompactionCount()).toBe(1);
|
||||
|
||||
// willRetry=false with result — counter incremented again
|
||||
emit({ type: "auto_compaction_end", willRetry: false, result: { summary: "s2" } });
|
||||
expect(subscription.getCompactionCount()).toBe(2);
|
||||
});
|
||||
|
||||
it("does not count compaction when result is absent", async () => {
|
||||
const { emit, subscription } = createSubscribedSessionHarness({
|
||||
runId: "run-compaction-no-result",
|
||||
});
|
||||
|
||||
// No result (e.g. aborted or cancelled) — counter stays at 0
|
||||
emit({ type: "auto_compaction_end", willRetry: false, result: undefined });
|
||||
expect(subscription.getCompactionCount()).toBe(0);
|
||||
|
||||
emit({ type: "auto_compaction_end", willRetry: false });
|
||||
expect(subscription.getCompactionCount()).toBe(1);
|
||||
emit({ type: "auto_compaction_end", willRetry: false, aborted: true });
|
||||
expect(subscription.getCompactionCount()).toBe(0);
|
||||
});
|
||||
|
||||
it("emits compaction events on the agent event bus", async () => {
|
||||
|
||||
@@ -100,6 +100,7 @@ describe("compaction hook wiring", () => {
|
||||
{
|
||||
type: "auto_compaction_end",
|
||||
willRetry: false,
|
||||
result: { summary: "compacted" },
|
||||
} as never,
|
||||
);
|
||||
|
||||
@@ -122,7 +123,7 @@ describe("compaction hook wiring", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not call runAfterCompaction when willRetry is true", () => {
|
||||
it("does not call runAfterCompaction when willRetry is true but still increments counter", () => {
|
||||
hookMocks.runner.hasHooks.mockReturnValue(true);
|
||||
|
||||
const ctx = {
|
||||
@@ -132,7 +133,8 @@ describe("compaction hook wiring", () => {
|
||||
noteCompactionRetry: vi.fn(),
|
||||
resetForCompactionRetry: vi.fn(),
|
||||
maybeResolveCompactionWait: vi.fn(),
|
||||
getCompactionCount: () => 0,
|
||||
incrementCompactionCount: vi.fn(),
|
||||
getCompactionCount: () => 1,
|
||||
};
|
||||
|
||||
handleAutoCompactionEnd(
|
||||
@@ -140,10 +142,13 @@ describe("compaction hook wiring", () => {
|
||||
{
|
||||
type: "auto_compaction_end",
|
||||
willRetry: true,
|
||||
result: { summary: "compacted" },
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(hookMocks.runner.runAfterCompaction).not.toHaveBeenCalled();
|
||||
// Counter is incremented even with willRetry — compaction succeeded (#38905)
|
||||
expect(ctx.incrementCompactionCount).toHaveBeenCalledTimes(1);
|
||||
expect(ctx.noteCompactionRetry).toHaveBeenCalledTimes(1);
|
||||
expect(ctx.resetForCompactionRetry).toHaveBeenCalledTimes(1);
|
||||
expect(ctx.maybeResolveCompactionWait).not.toHaveBeenCalled();
|
||||
@@ -154,6 +159,75 @@ describe("compaction hook wiring", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not increment counter when compaction was aborted", () => {
|
||||
const ctx = {
|
||||
params: { runId: "r3b", session: { messages: [] } },
|
||||
state: { compactionInFlight: true },
|
||||
log: { debug: vi.fn(), warn: vi.fn() },
|
||||
maybeResolveCompactionWait: vi.fn(),
|
||||
incrementCompactionCount: vi.fn(),
|
||||
getCompactionCount: () => 0,
|
||||
};
|
||||
|
||||
handleAutoCompactionEnd(
|
||||
ctx as never,
|
||||
{
|
||||
type: "auto_compaction_end",
|
||||
willRetry: false,
|
||||
result: undefined,
|
||||
aborted: true,
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(ctx.incrementCompactionCount).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not increment counter when compaction has result but was aborted", () => {
|
||||
const ctx = {
|
||||
params: { runId: "r3b2", session: { messages: [] } },
|
||||
state: { compactionInFlight: true },
|
||||
log: { debug: vi.fn(), warn: vi.fn() },
|
||||
maybeResolveCompactionWait: vi.fn(),
|
||||
incrementCompactionCount: vi.fn(),
|
||||
getCompactionCount: () => 0,
|
||||
};
|
||||
|
||||
handleAutoCompactionEnd(
|
||||
ctx as never,
|
||||
{
|
||||
type: "auto_compaction_end",
|
||||
willRetry: false,
|
||||
result: { summary: "compacted" },
|
||||
aborted: true,
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(ctx.incrementCompactionCount).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not increment counter when result is undefined", () => {
|
||||
const ctx = {
|
||||
params: { runId: "r3c", session: { messages: [] } },
|
||||
state: { compactionInFlight: true },
|
||||
log: { debug: vi.fn(), warn: vi.fn() },
|
||||
maybeResolveCompactionWait: vi.fn(),
|
||||
incrementCompactionCount: vi.fn(),
|
||||
getCompactionCount: () => 0,
|
||||
};
|
||||
|
||||
handleAutoCompactionEnd(
|
||||
ctx as never,
|
||||
{
|
||||
type: "auto_compaction_end",
|
||||
willRetry: false,
|
||||
result: undefined,
|
||||
aborted: false,
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(ctx.incrementCompactionCount).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("resets stale assistant usage after final compaction", () => {
|
||||
const messages = [
|
||||
{ role: "user", content: "hello" },
|
||||
@@ -183,6 +257,7 @@ describe("compaction hook wiring", () => {
|
||||
{
|
||||
type: "auto_compaction_end",
|
||||
willRetry: false,
|
||||
result: { summary: "compacted" },
|
||||
} as never,
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user