mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-19 20:44:16 +02:00
fix(runner): bundle the Claude ACP runtime and explain missing packages
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
bfe7c6357d
commit
2d81789c14
19 files changed
+180
-9
No files matched your search
@@ -1457,6 +1457,14 @@ Claude, which can reject unavailable models. Package/version verification is
|
||||
independent of model selection. Environment tests verify runtime installation;
|
||||
a successful provider run additionally verifies credentials and model access.
|
||||
|
||||
The server must install and bundle `@agentclientprotocol/claude-agent-acp`
|
||||
at the qualified version, including the patched bridge and pinned SDK. The
|
||||
vendor dependency check covers this dynamic lookup as well as ordinary imports.
|
||||
A missing provider package is reported as **Runtime missing**, with automatic
|
||||
retries stopped. Reinstall or rebuild Paperclip with its provider dependencies,
|
||||
restart the server, and run the agent's runtime test before retrying the task.
|
||||
Changing the model or signing in again cannot repair a missing runtime package.
|
||||
|
||||
ACPX Claude supports Linux x64 and macOS ARM64/x64 with pinned SDK executables.
|
||||
On macOS the launcher uses private verified module/executable snapshots instead
|
||||
of Linux `/proc` descriptors. Dependency isolation, process ownership, and
|
||||
|
||||
@@ -844,6 +844,7 @@ fn stderr_diagnostic_categories(value: &str) -> BTreeSet<&'static str> {
|
||||
|
||||
fn response_error_classification(error: &ResponseError) -> &'static str {
|
||||
match error.code.as_str() {
|
||||
"ACPX_RUNTIME_PACKAGE_MISSING" => return "runtime_package_missing",
|
||||
"ACP_MODEL_UNSUPPORTED" => return "requested_model_unsupported",
|
||||
"AGENT_STARTUP_FAILED" => return "agent_startup_failed",
|
||||
"AGENT_STARTUP_FAILED.UNVERIFIED_MODULE" => return "agent_startup_unverified_module",
|
||||
@@ -1038,6 +1039,7 @@ mod tests {
|
||||
#[test]
|
||||
fn candidate_auth_diagnostics_use_only_closed_codes_and_never_provider_text() {
|
||||
for (code, expected) in [
|
||||
("ACPX_RUNTIME_PACKAGE_MISSING", "runtime_package_missing"),
|
||||
("ACPX_TOOL_CALL_STALE", "provider_tool_call_retired"),
|
||||
("ACPX_TOOL_CALL_STALE_EXTRA", "unclassified"),
|
||||
("COPILOT_AUTH_REQUIRED", "authentication_required"),
|
||||
|
||||
@@ -186,3 +186,9 @@ describe("ACPX sidecar input sequencing", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves a missing runtime package category across wrapped bootstrap errors", () => {
|
||||
const error = Object.assign(new Error("private resolver stack"), { code: "ACPX_RUNTIME_PACKAGE_MISSING" });
|
||||
expect(acpxSidecarErrorCode(new Error("bootstrap failed", { cause: error }))).toBe("ACPX_RUNTIME_PACKAGE_MISSING");
|
||||
expect(acpxSidecarErrorCode(acpxBootstrapBlockedError(error, "session.read")!)).toBe("ACPX_RUNTIME_PACKAGE_MISSING");
|
||||
});
|
||||
@@ -25,6 +25,7 @@ export function acpxBootstrapBlockedError(
|
||||
return failure
|
||||
? new Error(
|
||||
`ACPX provider bootstrap failed before ${command}: ${failure.message}`,
|
||||
{ cause: failure },
|
||||
)
|
||||
: null;
|
||||
}
|
||||
@@ -130,6 +131,7 @@ const GENERIC_ACPX_OUTPUT_CODES = new Set([
|
||||
]);
|
||||
|
||||
const STABLE_ACPX_SIDECAR_CODES = new Set([
|
||||
"ACPX_RUNTIME_PACKAGE_MISSING",
|
||||
"COPILOT_POLICY_VIOLATION",
|
||||
"COPILOT_DETACHED_WORK_UNSUPPORTED",
|
||||
"ACP_MODEL_UNSUPPORTED",
|
||||
|
||||
@@ -54,6 +54,21 @@ afterEach(async () => {
|
||||
});
|
||||
|
||||
describe("ACPX installation integrity", () => {
|
||||
it("explains a missing Claude ACP installation without leaking the resolver stack", async () => {
|
||||
const missingPackage = Object.assign(new Error(
|
||||
"Cannot find module '@agentclientprotocol/claude-agent-acp/package.json'\nRequire stack: /srv/private/server.js",
|
||||
), { code: "MODULE_NOT_FOUND" });
|
||||
const error = await verifyQualifiedAcpxInstallation(
|
||||
resolveQualifiedAcpxProfile("claude", "claude-opus-5-5"),
|
||||
() => { throw missingPackage; },
|
||||
).catch(error => error);
|
||||
expect(error).toMatchObject({ code: "ACPX_RUNTIME_PACKAGE_MISSING" });
|
||||
expect(error.message).toContain("@agentclientprotocol/claude-agent-acp");
|
||||
expect(error.message).toContain("Reinstall or rebuild");
|
||||
expect(error.message).not.toContain("/srv/private");
|
||||
expect(error.message).not.toContain("Require stack");
|
||||
});
|
||||
|
||||
it.each([false, true])("admits only the declared qualified Codex platform in its npm-hoisted slot (published platform declaration: %s)", async (publishedDeclaration) => {
|
||||
const parent = await realpath(await mkdtemp(join(tmpdir(), "paperclip-codex-npm-hoist-")));
|
||||
temporaryDirectories.push(parent);
|
||||
|
||||
@@ -645,7 +645,22 @@ export async function verifyQualifiedAcpxInstallation(
|
||||
if (builtin && (profile.agentServerPackage !== "builtin:grok-acp" || profile.agentServerVersion !== "1" || profile.agentRuntimePackage !== "native:grok" || profile.agentRuntimeVersion !== "1.0.13")) {
|
||||
throw new Error("Grok builtin profile identity mismatch");
|
||||
}
|
||||
const serverPackageJsonPath = builtin ? null : await realpath(resolvePackageJson(profile.agentServerPackage));
|
||||
let serverPackageJsonPath: string | null = null;
|
||||
if (!builtin) {
|
||||
try {
|
||||
serverPackageJsonPath = await realpath(resolvePackageJson(profile.agentServerPackage));
|
||||
} catch (error) {
|
||||
if ((profile.agent === "claude" || profile.agent === "codex") &&
|
||||
error instanceof Error && "code" in error &&
|
||||
(error.code === "MODULE_NOT_FOUND" || error.code === "ERR_MODULE_NOT_FOUND")) {
|
||||
throw Object.assign(new Error(
|
||||
`The ACPX ${profile.agent} runtime is incomplete: required package ${profile.agentServerPackage} is missing. ` +
|
||||
"Reinstall or rebuild Paperclip with its bundled provider dependencies, restart the server, then test the agent's runtime before retrying.",
|
||||
), { code: "ACPX_RUNTIME_PACKAGE_MISSING" });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const serverPackage: AcpxPackageMetadata = builtin
|
||||
? { version: "1", bin: "launcher.cjs", type: "commonjs" }
|
||||
: await readPackageJson(serverPackageJsonPath!, profile.agentServerPackage);
|
||||
|
||||
@@ -176,6 +176,19 @@ test("published packages preserve the patched ACPX runtime", () => {
|
||||
assert.equal(cliEsbuildConfig.external.includes("acpx"), false);
|
||||
});
|
||||
|
||||
test("published Claude bridge pins its qualified SDK and provider dependency closure", () => {
|
||||
const published = materializePublishManifest(serverPackage);
|
||||
const { installManifest } = configureBundledProviderOverrides(
|
||||
createBundledInstallManifest(published, serverPackage.bundleDependencies),
|
||||
serverPackage.bundleDependencies, rootPackage, profileData,
|
||||
);
|
||||
assert.deepEqual(installManifest.overrides["@agentclientprotocol/claude-agent-acp@0.73.0"], {
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.286",
|
||||
"@agentclientprotocol/sdk": "1.4.0",
|
||||
zod: "4.4.3",
|
||||
});
|
||||
});
|
||||
|
||||
test("published Codex bridge uses its qualified producer closure without consumer overrides", () => {
|
||||
const published = materializePublishManifest(serverPackage);
|
||||
const { installManifest, profiles } = configureBundledProviderOverrides(
|
||||
|
||||
@@ -51,13 +51,14 @@ export function createBundledInstallManifest(publishManifest, bundledDependencie
|
||||
}
|
||||
|
||||
// npm consumers cannot inherit the workspace's pnpm overrides or patches.
|
||||
// Materialize the already-qualified Codex bridge closure in the tarball,
|
||||
// Materialize the already-qualified provider bridge closures in the tarball,
|
||||
// with overrides confined to the temporary, scripts-disabled producer graph.
|
||||
export function configureBundledProviderOverrides(installManifest, bundledDependencies, rootPackage, profileData) {
|
||||
const result = structuredClone(installManifest);
|
||||
const selected = [];
|
||||
for (const [agent, serverPackage, runtimePackage] of [
|
||||
["codex", "@agentclientprotocol/codex-acp", "@openai/codex"],
|
||||
["claude", "@agentclientprotocol/claude-agent-acp", "@anthropic-ai/claude-agent-sdk"],
|
||||
]) {
|
||||
if (!bundledDependencies.includes(serverPackage)) continue;
|
||||
const profile = profileData?.profiles?.[agent];
|
||||
@@ -75,9 +76,13 @@ export function configureBundledProviderOverrides(installManifest, bundledDepend
|
||||
if (result.overrides?.[npmSelector] !== undefined) {
|
||||
throw new Error(`Bundled ${agent} runtime has a conflicting producer override`);
|
||||
}
|
||||
const overrides = { [runtimePackage]: profile.agentRuntimeVersion };
|
||||
const overrides = { [runtimePackage]: profile.agentRuntimeVersion,
|
||||
...(agent === "claude" ? { "@agentclientprotocol/sdk": "1.4.0", zod: "4.4.3" } : {}),
|
||||
};
|
||||
result.overrides = { ...result.overrides, [npmSelector]: overrides };
|
||||
selected.push({ agent, ...profile });
|
||||
// Codex delegates its platform aliases below. Claude retains the SDK
|
||||
// platform dependencies in its ordinary bundled provider closure.
|
||||
if (agent === "codex") selected.push({ agent, ...profile });
|
||||
}
|
||||
return { installManifest: result, profiles: selected };
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"typecheck": "pnpm run prepare:runner-vendor && pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/claude-agent-acp": "0.73.0",
|
||||
"@agentclientprotocol/codex-acp": "1.6.2",
|
||||
"@ai-sdk/amazon-bedrock": "5.0.113",
|
||||
"@ai-sdk/anthropic": "4.0.78",
|
||||
@@ -113,6 +114,7 @@
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"bundleDependencies": [
|
||||
"@agentclientprotocol/claude-agent-acp",
|
||||
"@agentclientprotocol/codex-acp",
|
||||
"@chat-adapter/discord",
|
||||
"@chat-adapter/github",
|
||||
|
||||
@@ -100,6 +100,21 @@ export async function findRunnerExternalPackages(entryPoints) {
|
||||
return externalPackageNames;
|
||||
}
|
||||
|
||||
// Provider entrypoints are resolved with createRequire from profile metadata,
|
||||
// so esbuild cannot see them. These two bridges ship in the server package;
|
||||
// other ACP agents use separately provisioned provider packs.
|
||||
export async function findRunnerRuntimePackages(entryPoints, profileData) {
|
||||
const packages = await findRunnerExternalPackages(entryPoints);
|
||||
for (const agent of ["claude", "codex"]) {
|
||||
const profile = profileData?.profiles?.[agent];
|
||||
if (profileData?.schema !== "paperclip.acpx-profiles.v1" || !profile?.agentServerPackage) {
|
||||
throw new Error(`paperclip-runner vendor check: missing ${agent} provider profile.`);
|
||||
}
|
||||
packages.add(profile.agentServerPackage);
|
||||
}
|
||||
return packages;
|
||||
}
|
||||
|
||||
function readDependencyNames(packageJsonPath) {
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
||||
return new Map(Object.entries(packageJson.dependencies ?? {}));
|
||||
@@ -125,7 +140,8 @@ function explainMissingDependencies(missing, runnerDependencyNames) {
|
||||
|
||||
async function main() {
|
||||
const entryPoints = ENTRY_POINT_NAMES.map((name) => resolve(runnerDist, name));
|
||||
const externalPackageNames = await findRunnerExternalPackages(entryPoints);
|
||||
const profileData = JSON.parse(readFileSync(resolve(runnerRoot, "acpx-profiles.json"), "utf8"));
|
||||
const externalPackageNames = await findRunnerRuntimePackages(entryPoints, profileData);
|
||||
const serverDependencyNames = readDependencyNames(resolve(serverRoot, "package.json"));
|
||||
|
||||
const missing = findMissingVendorDependencies(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -7,6 +7,7 @@ import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
findMissingVendorDependencies,
|
||||
findRunnerExternalPackages,
|
||||
findRunnerRuntimePackages,
|
||||
} from "./verify-runner-vendor-dependencies.mjs";
|
||||
|
||||
describe("findMissingVendorDependencies", () => {
|
||||
@@ -87,6 +88,15 @@ describe("findRunnerExternalPackages", () => {
|
||||
expect(externalPackageNames).toEqual(new Set(["smol-toml"]));
|
||||
});
|
||||
|
||||
it("checks dynamically resolved bundled providers even without a JavaScript import", async () => {
|
||||
const entryPoints = writeFixture();
|
||||
const profiles = JSON.parse(readFileSync(new URL("../../packages/paperclip-runner/acpx-profiles.json", import.meta.url), "utf8"));
|
||||
const packages = await findRunnerRuntimePackages(entryPoints, profiles);
|
||||
expect(findMissingVendorDependencies(packages, new Set(["smol-toml", "@agentclientprotocol/codex-acp"])))
|
||||
.toEqual(["@agentclientprotocol/claude-agent-acp"]);
|
||||
expect(packages.has("pi-acp")).toBe(false);
|
||||
});
|
||||
|
||||
it("throws a clear, actionable error when an entry point is missing", async () => {
|
||||
fixtureDir = mkdtempSync(join(tmpdir(), "paperclip-runner-vendor-fixture-"));
|
||||
const missingEntryPoint = join(fixtureDir, "index.js");
|
||||
@@ -96,3 +106,12 @@ describe("findRunnerExternalPackages", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Claude is resolved through createRequire at runtime, outside esbuild's graph.
|
||||
it("ships the qualified Claude ACP server with the vendored runner", () => {
|
||||
const server = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
||||
const profiles = JSON.parse(readFileSync(new URL("../../packages/paperclip-runner/acpx-profiles.json", import.meta.url), "utf8"));
|
||||
const claude = profiles.profiles.claude;
|
||||
expect(server.dependencies[claude.agentServerPackage]).toBe(claude.agentServerVersion);
|
||||
expect(server.bundleDependencies).toContain(claude.agentServerPackage);
|
||||
});
|
||||
@@ -118,6 +118,13 @@ describe("native ACPX environment checks", () => {
|
||||
})]);
|
||||
});
|
||||
|
||||
it("identifies an incomplete installation and gives the operator a repair", async () => {
|
||||
probeInstallation.mockRejectedValue(Object.assign(new Error("The Claude ACP runtime package is missing. Reinstall or rebuild Paperclip."), { code: "ACPX_RUNTIME_PACKAGE_MISSING" }));
|
||||
const result = await requireServerAdapter("paperclip_runner").testEnvironment!(context);
|
||||
expect(result.status).toBe("fail");
|
||||
expect(result.checks[0]).toMatchObject({ code: "acpx_runtime_package_missing", message: expect.stringContaining("Reinstall or rebuild") });
|
||||
});
|
||||
|
||||
it("requires a successful installed runtime probe", async () => {
|
||||
const result = await requireServerAdapter("paperclip_runner").testEnvironment!(context);
|
||||
expect(result.status).toBe("pass");
|
||||
|
||||
@@ -447,7 +447,12 @@ const paperclipRunnerAdapter: ServerAdapterModule = {
|
||||
} catch (error) {
|
||||
return {
|
||||
adapterType: "paperclip_runner", status: "fail" as const, testedAt: new Date().toISOString(),
|
||||
checks: [{ code: "acpx_runtime_unavailable", level: "error" as const, message: error instanceof Error ? error.message : "ACPX Claude runtime could not be verified." }],
|
||||
checks: [{
|
||||
code: error instanceof Error && "code" in error && error.code === "ACPX_RUNTIME_PACKAGE_MISSING"
|
||||
? "acpx_runtime_package_missing" : "acpx_runtime_unavailable",
|
||||
level: "error" as const,
|
||||
message: error instanceof Error ? error.message : "The ACPX runtime could not be verified.",
|
||||
}],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13150,3 +13150,10 @@ it.each([
|
||||
),
|
||||
).toBe("native_event_replay_conflict");
|
||||
});
|
||||
|
||||
it("keeps an ACPX missing runtime package failure actionable and stops automatic retries", () => {
|
||||
const failure = new Error('PRP command session.open failed: {"result":{"code":"command_execution_failed","message":"failed to start ACPX provider: ACPX sidecar command session.open was rejected (retryable=false, classification=runtime_package_missing)"},"status":"failed"}');
|
||||
const code = nativeSessionFailureSourceCode(failure);
|
||||
expect(code).toBe("native_provider_runtime_missing");
|
||||
expect(nativeSessionFailureDisposition(1, new Date(), code)).toEqual({ phase: "terminal_failure", failureCode: code, nextAttemptAt: null });
|
||||
});
|
||||
@@ -6197,6 +6197,7 @@ export function nativeSessionFailureDisposition(
|
||||
sourceFailureCode?: ReturnType<typeof nativeSessionFailureSourceCode>,
|
||||
) {
|
||||
const permanentFailure =
|
||||
sourceFailureCode === "native_provider_runtime_missing" ||
|
||||
sourceFailureCode === "native_provider_model_rejected" ||
|
||||
sourceFailureCode === "native_provider_approval_required" ||
|
||||
sourceFailureCode === "native_event_replay_conflict" ||
|
||||
@@ -6270,6 +6271,7 @@ export function nativeSessionFailureSourceCode(
|
||||
| "native_runner_process_exited"
|
||||
| "planning_mode_unsupported"
|
||||
| "native_event_replay_conflict"
|
||||
| "native_provider_runtime_missing"
|
||||
| "native_provider_model_rejected"
|
||||
| "native_current_wake_comments_unread"
|
||||
| "native_current_wake_comments_changed_after_read"
|
||||
@@ -6292,6 +6294,8 @@ export function nativeSessionFailureSourceCode(
|
||||
if (error instanceof NativeSessionCleanupQuarantinedError)
|
||||
return "native_session_cleanup_quarantined";
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (/classification=runtime_package_missing\)/.test(message))
|
||||
return "native_provider_runtime_missing";
|
||||
if (/native_provider_model_rejected/i.test(message))
|
||||
return "native_provider_model_rejected";
|
||||
if (/native_adopted_runner_authentication_timeout/i.test(message)) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { flushSync } from "react-dom";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { expect, it } from "vitest";
|
||||
import { RuntimeTestCard } from "./RuntimeTestCard";
|
||||
|
||||
it("shows the installation repair directly on a missing runtime test", () => {
|
||||
const container = document.createElement("div");
|
||||
const root = createRoot(container);
|
||||
const message = "The ACPX claude runtime is incomplete: required package @agentclientprotocol/claude-agent-acp is missing. Reinstall or rebuild Paperclip with its bundled provider dependencies, restart the server, then test the agent's runtime before retrying.";
|
||||
try {
|
||||
flushSync(() => root.render(<RuntimeTestCard state="fail" onTest={() => {}} result={{
|
||||
adapterType: "paperclip_runner", status: "fail", testedAt: "2026-10-11T11:19:14Z",
|
||||
checks: [{ code: "acpx_runtime_package_missing", level: "error", message }],
|
||||
}} />));
|
||||
expect(container.querySelector('[role="alert"]')?.textContent).toContain("Runtime missing");
|
||||
expect(container.querySelector('[role="alert"]')?.textContent).toContain(message);
|
||||
expect(container.textContent).not.toContain("Check your model and provider connection");
|
||||
} finally {
|
||||
flushSync(() => root.unmount());
|
||||
}
|
||||
});
|
||||
@@ -61,7 +61,12 @@ export function RuntimeTestCard({
|
||||
disabled?: boolean;
|
||||
variant?: "connection" | "prerequisites";
|
||||
}) {
|
||||
const content = (variant === "prerequisites" ? prerequisiteCopy : copy)[state];
|
||||
const missingRuntime = state === "fail"
|
||||
? result?.checks.find(check => check.code === "acpx_runtime_package_missing")
|
||||
: undefined;
|
||||
const content = missingRuntime
|
||||
? { ...copy.fail, title: "Runtime missing", description: missingRuntime.message }
|
||||
: (variant === "prerequisites" ? prerequisiteCopy : copy)[state];
|
||||
const Icon =
|
||||
state === "running"
|
||||
? Loader2
|
||||
|
||||
@@ -4658,3 +4658,16 @@ describe("TaskChatThread composer execution controls", () => {
|
||||
expect(container.querySelector('[data-testid="task-chat-composer-stop"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it("explains a missing provider runtime with an installation repair", () => {
|
||||
render(<TaskChatThread comments={[]} onAdd={async () => {}} linkedRuns={[{
|
||||
runId: "missing-runtime", runtimeMode: "native", status: "failed", errorCode: "native_provider_runtime_missing",
|
||||
agentId: "agent-1", agentName: "Content Strategist", adapterType: "paperclip_runner",
|
||||
createdAt: "2026-10-11T11:19:08Z", startedAt: "2026-10-11T11:19:08Z", finishedAt: "2026-10-11T11:19:14Z",
|
||||
}]} />);
|
||||
expect(container.textContent).toContain("Runtime missing");
|
||||
const toggle = container.querySelector<HTMLButtonElement>('[data-testid="task-chat-collapsible-marker"] button[aria-expanded]')!;
|
||||
flushSync(() => toggle.click());
|
||||
expect(container.textContent).toContain("Reinstall or rebuild Paperclip");
|
||||
expect(container.textContent).toContain("test the agent's runtime");
|
||||
});
|
||||
@@ -1646,7 +1646,9 @@ export function TaskChatThread(props: TaskChatThreadProps) {
|
||||
? "native_runner_timed_out"
|
||||
: "native_runner_process_exited");
|
||||
const label =
|
||||
code === "native_provider_model_rejected" && source.status === "failed"
|
||||
code === "native_provider_runtime_missing" && source.status === "failed"
|
||||
? "Runtime missing"
|
||||
: code === "native_provider_model_rejected" && source.status === "failed"
|
||||
? "Model unavailable"
|
||||
: code === "native_provider_approval_required" && source.status === "failed"
|
||||
? "Approval required"
|
||||
@@ -1667,6 +1669,8 @@ export function TaskChatThread(props: TaskChatThreadProps) {
|
||||
? `The run was cancelled${responseBoundary}.`
|
||||
: source.status === "interrupted"
|
||||
? `The run was interrupted${responseBoundary}.`
|
||||
: code === "native_provider_runtime_missing"
|
||||
? "The selected provider runtime is missing a required package. Reinstall or rebuild Paperclip with its bundled provider dependencies, restart the server, then test the agent's runtime before retrying."
|
||||
: code === "native_provider_approval_required"
|
||||
? "This operation requires approval, but this runner has no interactive approval handler. Review the operation and update the agent's permission setting before retrying."
|
||||
: code === "native_provider_model_rejected"
|
||||
|
||||
Reference in new issue
Block a user