fix(telemetry): surface AI sidecar and DOMException failure reasons in Sentry (#612)

AI sidecar failures reached Sentry as 'Error: Error': the scrubber type-onlys plain Errors and the tool wrappers threw them from result.error. The bridge now exports toSidecarError(), wrapping the sidecar reason in a SafeError (memory-allocation text classifies as operational, the rest as bug); all 14 wrappers use it, plus the dispatcher crash/stdin/spawn rejection paths and parseStdoutJson. toBgRemovalError from #535 delegates to the shared helper.

On the web side, DOMExceptions report their specific name via err.name, so the NATIVE_ERRORS allowlist dropped the whole family's browser-authored messages. It now carries the full WebIDL DOMException name table; messages still pass through url/path redaction.

Bridge-mocking test files switched to importOriginal passthrough mocks.
This commit is contained in:
SnapOtter
2026-07-21 23:18:50 +08:00
committed by GitHub
parent 82f5708193
commit 6a0768b39d
35 changed files with 502 additions and 73 deletions
+27
View File
@@ -27,6 +27,33 @@ describe("scrubBrowserMessage", () => {
it("drops messages for non-native error names", () => {
expect(scrubBrowserMessage("CustomerDataError", "contains secret.pdf")).toBeNull();
});
// DOMExceptions report their specific name ("NotFoundError"), not
// "DOMException", so listing only the base name dropped the diagnostic
// browser message for the whole family (WEB-3/4/6 showed as
// "NotFoundError: NotFoundError" with no way to tell which DOM call failed).
it("keeps messages for specific DOMException names, still redacted", () => {
expect(
scrubBrowserMessage(
"NotFoundError",
"Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.",
),
).toBe(
"Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.",
);
expect(scrubBrowserMessage("InvalidStateError", "The object is in an invalid state.")).toBe(
"The object is in an invalid state.",
);
expect(scrubBrowserMessage("NotAllowedError", "Write permission denied.")).toBe(
"Write permission denied.",
);
expect(scrubBrowserMessage("NotReadableError", "error reading /Users/bob/file.png")).toBe(
"error reading <path>",
);
expect(scrubBrowserMessage("DataCloneError", "could not be cloned.")).toBe(
"could not be cloned.",
);
});
});
describe("static filter lists", () => {