From fa39bef3893d02ac7b261901f4a2aa4d7cf831d1 Mon Sep 17 00:00:00 2001 From: "clawsweeper[bot]" <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 00:00:55 +0000 Subject: [PATCH] fix #84857: skip CLI runtime harness preflight during compaction (#85862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: - The PR skips agent-harness compaction preflight for provider-owned or configured CLI runtime sessions, adds claude-cli regression coverage, includes a changelog entry, and applies small test/type cleanups. - Reproducibility: yes. at source level. Current main still routes provider-owned `claude-cli` runtime compaction preflight through harness selection, where `claude-cli` is not a registered embedded harness. Automerge notes: - PR branch already contained follow-up commit before automerge: fix #84857: skip CLI runtime harness preflight during compaction - PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8487… Validation: - ClawSweeper review passed for head 1dd8a88d218d464a4dbc191afbb0acdbd8f1cf02. - Required merge gates passed before the squash merge. Prepared head SHA: 1dd8a88d218d464a4dbc191afbb0acdbd8f1cf02 Review: https://github.com/openclaw/openclaw/pull/85862#issuecomment-4526794976 Co-authored-by: 张贵萍0668001030 Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com> --- CHANGELOG.md | 1 + extensions/meeting-notes/index.test.ts | 8 ++++-- extensions/meeting-notes/src/tool.ts | 2 +- src/agents/harness/selection.test.ts | 28 +++++++++++++++++++ src/agents/harness/selection.ts | 14 +++++++++- .../server-startup-post-attach.test.ts | 3 +- 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f498de92bde..9c6ae457f78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Agents/compaction: skip agent-harness preflight for provider-owned CLI runtime sessions so over-threshold Claude CLI sessions continue through normal compaction instead of failing on a missing harness. Fixes #84857. (#84878) Thanks @zhangguiping-xydt. - WebChat: summarize internal message-tool source replies so tool cards no longer duplicate the visible reply body. (#84773) Thanks @jason-allen-oneal. - WebChat: scope the visible attachment button to its own composer file input so clicking Upload reliably opens the file picker. (#83952, fixes #47983) Thanks @jason-allen-oneal. - Gateway: preserve deferred lifecycle-error cleanup across later non-terminal events so provider timeouts can persist failed session state instead of leaving sessions stuck running. (#85256, fixes #63819) Thanks @samzong. diff --git a/extensions/meeting-notes/index.test.ts b/extensions/meeting-notes/index.test.ts index 7b05440a8e1..3b5c40121b2 100644 --- a/extensions/meeting-notes/index.test.ts +++ b/extensions/meeting-notes/index.test.ts @@ -429,15 +429,19 @@ describe("meeting-notes plugin", () => { ], }); const logger = { debug: vi.fn(), error: vi.fn(), info: vi.fn(), warn: vi.fn() }; + const service = services[0]; + if (!service?.stop) { + throw new Error("Expected meeting notes service with stop hook"); + } - await services[0]?.start({ config: {}, logger, stateDir }); + await service.start({ config: {}, logger, stateDir }); await vi.waitFor(() => { expect(start).toHaveBeenCalledOnce(); }); const request = start.mock.calls[0]?.[0]; expect(request.abortSignal?.aborted).toBe(false); - await services[0]?.stop({ config: {}, logger, stateDir }); + await service.stop({ config: {}, logger, stateDir }); expect(request.abortSignal?.aborted).toBe(true); expect(stop).not.toHaveBeenCalled(); diff --git a/extensions/meeting-notes/src/tool.ts b/extensions/meeting-notes/src/tool.ts index c15ae11779f..6bf8d476a18 100644 --- a/extensions/meeting-notes/src/tool.ts +++ b/extensions/meeting-notes/src/tool.ts @@ -106,7 +106,7 @@ async function waitForPendingAutoStartsToSettle( let timeout: ReturnType | undefined; try { return await Promise.race([ - Promise.allSettled([...pendingStarts]).then(() => true), + Promise.allSettled(pendingStarts).then(() => true), new Promise((resolve) => { timeout = setTimeout(() => resolve(false), AUTO_START_STOP_TIMEOUT_MS); timeout.unref?.(); diff --git a/src/agents/harness/selection.test.ts b/src/agents/harness/selection.test.ts index 949db67b9af..755e3dbad29 100644 --- a/src/agents/harness/selection.test.ts +++ b/src/agents/harness/selection.test.ts @@ -639,6 +639,34 @@ describe("selectAgentHarness", () => { ).toBe("codex"); }); + it("skips harness compaction preflight for claude-cli runtime sessions", async () => { + await expect( + maybeCompactAgentHarnessSession({ + sessionId: "session-1", + sessionKey: "agent:main:main", + sessionFile: "/tmp/session.jsonl", + workspaceDir: "/tmp/workspace", + provider: "anthropic", + model: "claude-opus-4-7", + config: agentModelRuntimeConfig("anthropic/claude-opus-4-7", "claude-cli"), + }), + ).resolves.toBeUndefined(); + }); + + it("skips harness compaction preflight for claude-cli provider sessions", async () => { + await expect( + maybeCompactAgentHarnessSession({ + sessionId: "session-1", + sessionKey: "agent:main:main", + sessionFile: "/tmp/session.jsonl", + workspaceDir: "/tmp/workspace", + provider: "claude-cli", + model: "claude-opus-4-7", + config: providerRuntimeConfig("claude-cli", "claude-cli"), + }), + ).resolves.toBeUndefined(); + }); + it("ignores stale plugin pins during compaction when the provider no longer matches", async () => { registerFailingCodexHarness(); diff --git a/src/agents/harness/selection.ts b/src/agents/harness/selection.ts index f4da9c865ef..5dc28e298eb 100644 --- a/src/agents/harness/selection.ts +++ b/src/agents/harness/selection.ts @@ -1,7 +1,7 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { formatErrorMessage } from "../../infra/errors.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; -import { isCliRuntimeAliasForProvider } from "../model-runtime-aliases.js"; +import { isCliRuntimeAliasForProvider, isCliRuntimeProvider } from "../model-runtime-aliases.js"; import type { CompactEmbeddedPiSessionParams } from "../pi-embedded-runner/compact.types.js"; import type { EmbeddedRunAttemptParams, @@ -459,6 +459,18 @@ function logAgentHarnessSelection( export async function maybeCompactAgentHarnessSession( params: CompactEmbeddedPiSessionParams, ): Promise { + if (params.provider && isCliRuntimeProvider(params.provider)) { + return undefined; + } + const runtime = resolveConfiguredAgentHarnessPolicy({ + provider: params.provider, + modelId: params.model, + config: params.config, + sessionKey: params.sessionKey, + }).runtime; + if (isCliRuntimeAliasForProvider({ runtime, provider: params.provider })) { + return undefined; + } const harness = selectAgentHarness({ provider: params.provider ?? "", modelId: params.model, diff --git a/src/gateway/server-startup-post-attach.test.ts b/src/gateway/server-startup-post-attach.test.ts index 8a0f51d7550..5eb0f889496 100644 --- a/src/gateway/server-startup-post-attach.test.ts +++ b/src/gateway/server-startup-post-attach.test.ts @@ -6,10 +6,11 @@ import type { PluginHookGatewayContext, PluginHookGatewayStartEvent, } from "../plugins/hook-types.js"; +import type { PluginServicesHandle } from "../plugins/services.js"; import { withEnvAsync } from "../test-utils/env.js"; const hoisted = vi.hoisted(() => { - const startPluginServices = vi.fn(async () => null); + const startPluginServices = vi.fn<() => Promise>(async () => null); const startGmailWatcherWithLogs = vi.fn(async () => {}); const loadInternalHooks = vi.fn(async () => 0); const setInternalHooksEnabled = vi.fn();