fix #84857: skip CLI runtime harness preflight during compaction (#85862)

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 1dd8a88d21.
- Required merge gates passed before the squash merge.

Prepared head SHA: 1dd8a88d21
Review: https://github.com/openclaw/openclaw/pull/85862#issuecomment-4526794976

Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>
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>
This commit is contained in:
clawsweeper[bot]
2026-05-24 00:00:55 +00:00
committed by GitHub
parent 4ffbd07c06
commit fa39bef389
6 changed files with 51 additions and 5 deletions

View File

@@ -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.

View File

@@ -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();

View File

@@ -106,7 +106,7 @@ async function waitForPendingAutoStartsToSettle(
let timeout: ReturnType<typeof setTimeout> | undefined;
try {
return await Promise.race([
Promise.allSettled([...pendingStarts]).then(() => true),
Promise.allSettled(pendingStarts).then(() => true),
new Promise<boolean>((resolve) => {
timeout = setTimeout(() => resolve(false), AUTO_START_STOP_TIMEOUT_MS);
timeout.unref?.();

View File

@@ -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();

View File

@@ -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<EmbeddedPiCompactResult | undefined> {
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,

View File

@@ -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<PluginServicesHandle | null>>(async () => null);
const startGmailWatcherWithLogs = vi.fn(async () => {});
const loadInternalHooks = vi.fn(async () => 0);
const setInternalHooksEnabled = vi.fn();