mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 18:52:48 +00:00
fix(browser): preserve caller cancellation through CDP observations
This commit is contained in:
1 parent
9aca543f46
commit
7f4e7d5577
15 files changed
+678
-48
No files matched your search
@@ -197,3 +197,21 @@ it("retains completed bundle facts as bundle Evidence after cancellation", async
|
||||
normalized_result: bundle,
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves a prior caller reason without admitting browser work", async () => {
|
||||
const reason = new AnalysisOutputError(
|
||||
"inspect_web_page",
|
||||
"caller supplied reason",
|
||||
);
|
||||
const result = await listBrowserTargets(
|
||||
provider(async () => {
|
||||
throw new Error("Cancelled inventory must not start");
|
||||
}),
|
||||
input,
|
||||
{ signal: AbortSignal.abort(reason) },
|
||||
);
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) throw new Error("Expected cancelled inventory");
|
||||
expect(result.error._tag).toBe("AnalysisCancelledError");
|
||||
expect(result.error.cause).toBe(reason);
|
||||
});
|
||||
@@ -38,7 +38,11 @@ export const listBrowserTargets = async (
|
||||
const ready = requireBrowser(browser, "list_browser_targets");
|
||||
if (!ready.ok) return ready;
|
||||
if (options.signal?.aborted)
|
||||
return err(new AnalysisCancelledError("list_browser_targets"));
|
||||
return err(
|
||||
new AnalysisCancelledError("list_browser_targets", {
|
||||
cause: options.signal.reason,
|
||||
}),
|
||||
);
|
||||
const result = await ready.value.listTargets(input, options);
|
||||
return result.ok
|
||||
? browserEvidence(
|
||||
@@ -60,7 +64,11 @@ export const inspectWebPage = async (
|
||||
const ready = requireBrowser(browser, "inspect_web_page");
|
||||
if (!ready.ok) return ready;
|
||||
if (options.signal?.aborted)
|
||||
return err(new AnalysisCancelledError("inspect_web_page"));
|
||||
return err(
|
||||
new AnalysisCancelledError("inspect_web_page", {
|
||||
cause: options.signal.reason,
|
||||
}),
|
||||
);
|
||||
const result = await ready.value.inspectPage(input, options);
|
||||
if (!result.ok)
|
||||
return retainBrowserObservation(
|
||||
@@ -89,7 +97,11 @@ export const analyzeWebBundle = async (
|
||||
const ready = requireBrowser(browser, "analyze_web_bundle");
|
||||
if (!ready.ok) return ready;
|
||||
if (options.signal?.aborted)
|
||||
return err(new AnalysisCancelledError("analyze_web_bundle"));
|
||||
return err(
|
||||
new AnalysisCancelledError("analyze_web_bundle", {
|
||||
cause: options.signal.reason,
|
||||
}),
|
||||
);
|
||||
const analyzed = await ready.value.analyzeBundle(input, options);
|
||||
if (!analyzed.ok)
|
||||
return retainBrowserObservation(
|
||||
@@ -116,7 +128,11 @@ export const observeWebSession = async (
|
||||
const ready = requireBrowser(browser, "observe_web_session");
|
||||
if (!ready.ok) return ready;
|
||||
if (options.signal?.aborted)
|
||||
return err(new AnalysisCancelledError("observe_web_session"));
|
||||
return err(
|
||||
new AnalysisCancelledError("observe_web_session", {
|
||||
cause: options.signal.reason,
|
||||
}),
|
||||
);
|
||||
const observed = await ready.value.observeSession(input, options);
|
||||
return observed.ok
|
||||
? browserEvidence(
|
||||
@@ -138,7 +154,11 @@ export const discoverWebMcpTools = async (
|
||||
const ready = requireBrowser(browser, "discover_webmcp_tools");
|
||||
if (!ready.ok) return ready;
|
||||
if (options.signal?.aborted)
|
||||
return err(new AnalysisCancelledError("discover_webmcp_tools"));
|
||||
return err(
|
||||
new AnalysisCancelledError("discover_webmcp_tools", {
|
||||
cause: options.signal.reason,
|
||||
}),
|
||||
);
|
||||
const discovered = await ready.value.discoverWebMcpTools(input, options);
|
||||
return discovered.ok
|
||||
? browserEvidence(
|
||||
@@ -180,7 +200,11 @@ export const captureWebScreenshot = async (
|
||||
const ready = requireBrowser(browser, "capture_web_screenshot");
|
||||
if (!ready.ok) return ready;
|
||||
if (options.signal?.aborted)
|
||||
return err(new AnalysisCancelledError("capture_web_screenshot"));
|
||||
return err(
|
||||
new AnalysisCancelledError("capture_web_screenshot", {
|
||||
cause: options.signal.reason,
|
||||
}),
|
||||
);
|
||||
const captured = await ready.value.captureScreenshot(input, options);
|
||||
return captured.ok
|
||||
? browserEvidence(
|
||||
|
||||
@@ -29,10 +29,15 @@ it("rejects malformed input and prior cancellation without calling a provider",
|
||||
if (malformed.ok)
|
||||
throw new Error("Invalid endpoint must fail before execution");
|
||||
expect(malformed.error._tag).toBe("AnalysisInputError");
|
||||
const cancelled = await service.inspect({}, { signal: AbortSignal.abort() });
|
||||
const reason = { requested: "stop before provider admission" };
|
||||
const cancelled = await service.inspect(
|
||||
{},
|
||||
{ signal: AbortSignal.abort(reason) },
|
||||
);
|
||||
if (cancelled.ok)
|
||||
throw new Error("Prior cancellation must fail before inspection");
|
||||
expect(cancelled.error._tag).toBe("AnalysisCancelledError");
|
||||
expect(cancelled.error.cause).toBe(reason);
|
||||
});
|
||||
|
||||
it("preserves actual provider failures and rejects changed target/selector identities", async () => {
|
||||
|
||||
@@ -187,7 +187,9 @@ const parseInput = <T>(
|
||||
options: ExecutionOptions | undefined,
|
||||
): Result<T, AnalysisError> => {
|
||||
if (options?.signal?.aborted)
|
||||
return err(new AnalysisCancelledError(operation));
|
||||
return err(
|
||||
new AnalysisCancelledError(operation, { cause: options.signal.reason }),
|
||||
);
|
||||
const parsed = schema.safeParse(rawInput);
|
||||
return parsed.success
|
||||
? ok(parsed.data)
|
||||
|
||||
Reference in new issue
Block a user