mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
Make Claude effort probe fall back conservatively on throw
A thrown capability probe (sandbox connection error, ENOENT spawning the binary) previously propagated past the unguarded call sites in execute.ts and test.ts, killing the run instead of degrading. Resolve the probe to null and drop the cache entry so the flag is passed through unchanged and the next lease retries. Add a regression test for the throw path.
This commit is contained in:
1 parent
43d99b03bf
commit
94067dc51d
3 files changed
+52
-3
No files matched your search
@@ -78,9 +78,12 @@ export async function claudeCommandSupportsEffortFlag(input: {
|
||||
const cached = effortFlagSupportCache.get(key);
|
||||
if (cached) return cached;
|
||||
|
||||
const probe = probeClaudeCommandSupportsEffortFlag(input).catch((error) => {
|
||||
// A thrown probe (e.g. sandbox connection error, ENOENT spawning the binary)
|
||||
// must degrade to the conservative fallback rather than killing the run, so we
|
||||
// resolve to null and drop the cache entry to retry on the next lease.
|
||||
const probe = probeClaudeCommandSupportsEffortFlag(input).catch(() => {
|
||||
effortFlagSupportCache.delete(key);
|
||||
throw error;
|
||||
return null;
|
||||
});
|
||||
effortFlagSupportCache.set(key, probe);
|
||||
return probe;
|
||||
|
||||
@@ -2,7 +2,10 @@ export { claudeSessionCwdMatchesExecutionTarget, execute, runClaudeLogin } from
|
||||
export { listClaudeSkills, syncClaudeSkills } from "./skills.js";
|
||||
export { listClaudeModels, refreshClaudeModels, resetClaudeModelsCacheForTests } from "./models.js";
|
||||
export { testEnvironment } from "./test.js";
|
||||
export { resetClaudeCliCapabilitiesCacheForTests } from "./cli-capabilities.js";
|
||||
export {
|
||||
claudeCommandSupportsEffortFlag,
|
||||
resetClaudeCliCapabilitiesCacheForTests,
|
||||
} from "./cli-capabilities.js";
|
||||
export {
|
||||
parseClaudeStreamJson,
|
||||
describeClaudeFailure,
|
||||
|
||||
@@ -4,6 +4,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { runChildProcess } from "@paperclipai/adapter-utils/server-utils";
|
||||
import {
|
||||
claudeCommandSupportsEffortFlag,
|
||||
claudeSessionCwdMatchesExecutionTarget,
|
||||
execute,
|
||||
resetClaudeCliCapabilitiesCacheForTests,
|
||||
@@ -947,6 +948,48 @@ describe("claude execute", () => {
|
||||
}
|
||||
}, 10_000);
|
||||
|
||||
it("degrades to the conservative fallback (returns null) when the sandbox probe throws, and retries on the next lease", async () => {
|
||||
let calls = 0;
|
||||
const throwingRunner = {
|
||||
execute: async () => {
|
||||
calls += 1;
|
||||
throw new Error("sandbox connection error");
|
||||
},
|
||||
};
|
||||
const target = {
|
||||
kind: "remote" as const,
|
||||
transport: "sandbox" as const,
|
||||
providerKey: "daytona",
|
||||
environmentId: "env-1",
|
||||
leaseId: "lease-1",
|
||||
remoteCwd: "/remote/workspace",
|
||||
timeoutMs: 30_000,
|
||||
runner: throwingRunner,
|
||||
};
|
||||
const probeInput = {
|
||||
runId: "run-probe-throws",
|
||||
command: "/usr/local/bin/claude",
|
||||
cwd: "/host/workspace",
|
||||
env: {},
|
||||
timeoutSec: 20,
|
||||
graceSec: 5,
|
||||
};
|
||||
|
||||
// A thrown probe must resolve to null (unknown) rather than reject and kill the run.
|
||||
await expect(
|
||||
claudeCommandSupportsEffortFlag({ ...probeInput, target }),
|
||||
).resolves.toBeNull();
|
||||
|
||||
// The failed result is not cached: a second lease re-probes instead of reusing the rejection.
|
||||
await expect(
|
||||
claudeCommandSupportsEffortFlag({
|
||||
...probeInput,
|
||||
target: { ...target, leaseId: "lease-2" },
|
||||
}),
|
||||
).resolves.toBeNull();
|
||||
expect(calls).toBe(2);
|
||||
});
|
||||
|
||||
it("allows remote session resumes when saved cwd is the host workspace", () => {
|
||||
expect(claudeSessionCwdMatchesExecutionTarget({
|
||||
runtimeSessionCwd: "/host/workspace",
|
||||
|
||||
Reference in new issue
Block a user