mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
refactor(errors): require a reason for every analysis cancellation (#10566)
* refactor(errors): require a reason for every analysis cancellation Add cancelledBy, the one constructor for AnalysisCancelledError. Its reason argument is required: an aborted signal, an earlier cancellation, a reason carried from an inner failure, or an OwnerCancellation when REA closed the work's owner itself. Closed adb, apktool, firmware, sqlite, gdb, web-bundle analyzer and binary-session record owners now report OwnerCancellation.ownerClosed instead of an absent reason. Firmware's predecessor wait returns its cancellation as a Result, removing an unreachable reason-less fallback. An architecture guard rejects new direct constructions, with a counted per-file baseline that can only shrink. * refactor(errors): share one aborted-signal predicate with cancelledBy * refactor(process): return a departing caller's reason from a shared startup wait * refactor(providers): build decompiler-provider cancellations with a required reason (#10587) Migrate every AnalysisCancelledError construction under src/ghidra, src/ida, src/cutter, src/jeb and src/kuna to cancelledBy and lower the guard baseline from 671 to 572. Closed Kuna target commands report OwnerCancellation.ownerClosed instead of an absent reason, and Kuna and JEB close abort their lifetimes with that typed reason. * fix(errors): read the reason of any signal-shaped cancellation source * test(ci): cover the counted-baseline comparison behind the architecture guard
This commit is contained in:
1 parent
2f4c588584
commit
85da0bd4ee
51 files changed
+900
-488
No files matched your search
@@ -8,6 +8,9 @@ import { jsonObjectSchema, type JsonValue } from "../domain/jsonValue.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisProtocolError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
OwnerCancellation,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import type { JebOperation } from "../domain/jeb/jebAnalysis.js";
|
||||
import {
|
||||
@@ -121,9 +124,7 @@ export const createStreamableHttpJebMcpConnection: JebMcpConnectionFactory = (
|
||||
: waited.value;
|
||||
if (outcome !== undefined && !outcome.ok) throw outcome.error;
|
||||
if (outcome === undefined || signal?.aborted === true)
|
||||
throw new AnalysisCancelledError("jeb_mcp_catalog", {
|
||||
cause: signal?.reason,
|
||||
});
|
||||
throw cancelledBy("jeb_mcp_catalog", carriedReason(signal?.reason));
|
||||
return outcome.value;
|
||||
};
|
||||
|
||||
@@ -184,10 +185,7 @@ export const createStreamableHttpJebMcpConnection: JebMcpConnectionFactory = (
|
||||
};
|
||||
|
||||
const connect = async (signal?: AbortSignal): Promise<readonly string[]> => {
|
||||
if (signal?.aborted === true)
|
||||
throw new AnalysisCancelledError("jeb_mcp_catalog", {
|
||||
cause: signal.reason,
|
||||
});
|
||||
if (signal?.aborted === true) throw cancelledBy("jeb_mcp_catalog", signal);
|
||||
if (closing !== undefined) await closing;
|
||||
if (closed) {
|
||||
if (state !== undefined) await dispose(state);
|
||||
@@ -338,7 +336,7 @@ export const createStreamableHttpJebMcpConnection: JebMcpConnectionFactory = (
|
||||
const owner = state;
|
||||
if (owner === undefined) return;
|
||||
owner.tools = undefined;
|
||||
owner.signal.abort();
|
||||
owner.signal.abort(OwnerCancellation.ownerClosed);
|
||||
if (owner.acquisition !== undefined)
|
||||
await owner.acquisition.promise.catch(() => undefined);
|
||||
if (state === owner) await dispose(owner);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SdkError, SdkErrorCode } from "@modelcontextprotocol/client";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisTimeoutError,
|
||||
cancelledBy,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { analysisSettingLimit } from "../domain/analysisSettingDefinitions.js";
|
||||
import { currentAnalysisOperationSnapshot } from "../domain/analysisOperationSnapshot.js";
|
||||
@@ -82,9 +82,7 @@ export class JebMcpDeadline {
|
||||
cause: this.#deadline.signal.reason,
|
||||
...jebMcpTimeoutGuidance(this.phase, this.timeoutMs, this.source),
|
||||
})
|
||||
: new AnalysisCancelledError(operation, {
|
||||
cause: this.#deadline.signal.reason,
|
||||
});
|
||||
: cancelledBy(operation, this.#deadline.signal);
|
||||
}
|
||||
if (cause instanceof SdkError && cause.code === SdkErrorCode.RequestTimeout)
|
||||
return new AnalysisTimeoutError(operation, this.timeoutMs, {
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisProtocolError,
|
||||
AnalysisTimeoutError,
|
||||
cancelledBy,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
||||
@@ -251,8 +252,7 @@ export class JebProvider implements JebAnalysisPort {
|
||||
): Promise<Result<AnalysisExecution, AnalysisError>> {
|
||||
if (options?.signal?.aborted === true)
|
||||
return err(
|
||||
new AnalysisCancelledError(request.operation, {
|
||||
cause: options.signal.reason,
|
||||
cancelledBy(request.operation, options.signal, {
|
||||
diagnostics: { endpoint: this.#selectedEndpoint },
|
||||
}),
|
||||
);
|
||||
@@ -269,8 +269,7 @@ export class JebProvider implements JebAnalysisPort {
|
||||
signal: AbortSignal | undefined,
|
||||
): Promise<Result<AnalysisExecution, AnalysisError>> {
|
||||
const cancelled = (operation: JebOperation) => {
|
||||
if (signal?.aborted === true)
|
||||
throw new AnalysisCancelledError(operation, { cause: signal.reason });
|
||||
if (signal?.aborted === true) throw cancelledBy(operation, signal);
|
||||
};
|
||||
switch (request.operation) {
|
||||
case "inspect_jeb_client": {
|
||||
@@ -354,8 +353,7 @@ export class JebProvider implements JebAnalysisPort {
|
||||
operation: JebOperation,
|
||||
signal: AbortSignal | undefined,
|
||||
) {
|
||||
if (signal?.aborted === true)
|
||||
throw new AnalysisCancelledError(operation, { cause: signal.reason });
|
||||
if (signal?.aborted === true) throw cancelledBy(operation, signal);
|
||||
const payload = await this.#connection.call(
|
||||
"get_client_information",
|
||||
{},
|
||||
@@ -555,7 +553,6 @@ export class JebProvider implements JebAnalysisPort {
|
||||
cause instanceof AnalysisTimeoutError
|
||||
) {
|
||||
const options = {
|
||||
cause: cause.cause,
|
||||
diagnostics: {
|
||||
...cause.diagnostics,
|
||||
endpoint: this.#selectedEndpoint,
|
||||
@@ -568,6 +565,7 @@ export class JebProvider implements JebAnalysisPort {
|
||||
cause instanceof AnalysisTimeoutError
|
||||
? new AnalysisTimeoutError(operation, cause.timeoutMs, {
|
||||
...options,
|
||||
cause: cause.cause,
|
||||
...(cause.userMessage === undefined
|
||||
? {}
|
||||
: { userMessage: cause.userMessage }),
|
||||
@@ -576,7 +574,7 @@ export class JebProvider implements JebAnalysisPort {
|
||||
: { remediationAction: cause.remediationAction }),
|
||||
...(cause.limit === undefined ? {} : { limit: cause.limit }),
|
||||
})
|
||||
: new AnalysisCancelledError(operation, options);
|
||||
: cancelledBy(operation, cause, options);
|
||||
return failure
|
||||
.bindCleanupDependencies([cause])
|
||||
.bindCleanupResourceOwner(cause);
|
||||
|
||||
Reference in new issue
Block a user