fix(providers): preserve bridge limits through error attribution

This commit is contained in:
morluto committed 2026-11-11 20:16:24 +08:00
1 parent 57dd7f26b1
commit b3dcddc115
4 files changed
+71 -2

No files matched your search

+4 -1
View File
@@ -429,7 +429,10 @@ export class CutterBridgeClient {
cause.limit.resource,
cause.limit.reason,
cause.limit.reportedLimits,
resourceConstraintRecovery(cause.limit),
{
...resourceConstraintRecovery(cause.limit),
limit: cause.limit.limit,
},
);
}
return undefined;
@@ -1,3 +1,4 @@
import { bridgeRequestOversizeError } from "../config/bridgeMaxRequestBytes.js";
import { z } from "zod";
import { GHIDRA_LITERAL_CANDIDATES_LIMITATION } from "./GhidraLiteralSearch.js";
import {
@@ -1886,6 +1887,36 @@ describe("Ghidra decompiler failure projection", () => {
});
});
it("preserves a bridge request limit when reattributing a Ghidra session failure", async () => {
const refusal = bridgeRequestOversizeError(
"procedure_pseudo_code",
{ maximumBytes: 8192, source: "runtime" },
9000,
"Ghidra",
);
const ghidra = provider(installationHost(), () => ({
start: () => Promise.resolve(ok(sessionInfo())),
callTool: () =>
Promise.resolve(
err(
new GhidraSessionError(
"protocol",
"Bridge request refused",
{},
{ cause: refusal },
),
),
),
close: () => Promise.resolve(ok(null)),
}));
const client = await createElfClient(ghidra);
const result = await client.execute("procedure_pseudo_code", {
procedure: "main",
});
if (result.ok) throw new Error("Expected bridge limit refusal");
expect(result.error.limit).toEqual(refusal.limit);
});
describe("Ghidra decompile deadline for a finished invocation", () => {
const decompileDeadline = async (
seconds: number,
+5 -1
View File
@@ -1319,7 +1319,11 @@ const projectPrimarySessionError = (
physicalCause.resource,
physicalCause.reason,
physicalCause.reportedLimits,
{ ...context, ...resourceConstraintRecovery(physicalCause) },
{
...context,
...resourceConstraintRecovery(physicalCause),
limit: physicalCause.limit,
},
);
if (
origin === "owned-or-classified" &&
@@ -87,6 +87,37 @@ it("uses a runtime bridge ceiling for a Cutter client built from the launch valu
});
});
it("retains the runtime limit when an authenticated status response is oversized", async () => {
const directory = await privateDirectory();
const server = authenticatedServer(
TOKEN,
SESSION,
(_request, respond) => respond({ ok: true, padding: "x".repeat(8192) }),
true,
);
const port = await listen(server);
await writeDescriptor(directory, SESSION, port, TOKEN, {
maxRequestBytes: 8192,
});
const client = clientFor(directory, "8192");
await expect(
runWithAnalysisOperationSnapshot(
{ revision: 1, overrides: { "bridge.max_request_bytes": 8192 } },
() => client.listSessions(),
),
).rejects.toSatisfy((error: unknown) => {
expect(error).toBeInstanceOf(AnalysisResourceConstraintError);
if (!(error instanceof AnalysisResourceConstraintError)) return false;
expect(error.operation).toBe("list_cutter_sessions");
expect(error.limit).toMatchObject({
setting: "bridge.max_request_bytes",
effective_value: 8192,
source: "runtime",
});
return true;
});
});
it("rejects a bridge ceiling below the floor when the call has no override", async () => {
const client = new CutterBridgeClient({
REA_BRIDGE_MAX_REQUEST_BYTES: "7463",