feat(analysis): make resource budgets recoverable across CLI and MCP (#9706)

* refactor(analysis): centralize resource budgets and recovery facts

* feat(analysis): expose ASAR and Ghidra runtime admission controls

* feat(artifacts): expose reader admission and preserve limited recognition

* feat(javascript): expose recoverable semantic precision budgets

* feat(web): add recoverable source-map budgets and compact retained views

* feat(web): make historical capture decoding budgets recoverable

* fix(desktop): enforce selected view policies across capture and export

* feat(runtime): make Frida and LLDB retention budgets selectable

* feat(native): expose diagnostic budgets and preserve exhaustion causes

* feat(managed): expose recoverable snapshot and representation budgets

* feat(metadata): make Go and Rust inspection budgets recoverable

* feat(wasm): expose representation and producer admissions

* feat(evm): expose per-call worker resource budgets

* feat(android): make resource and string-pool omissions recoverable

* feat(producers): expose command and recorded-capture budgets

* docs(analysis): record recovery contracts and integrated validation

* fix(docs): link calibration oracle from published verification pages

* fix(docs): restore desktop task anchors and receipt links

* test(analysis): align fixtures with selectable budget contracts

* fix(analysis): remove source-location projector dependency cycle

* test(analysis): refresh budget recovery and verifier regressions

* test(analysis): preserve format and publication recovery expectations

* test(windows): align conformance with owned runtime admission

* fix(desktop): preserve selected alias kinds in exported state

* test(analysis): align provider fixtures with recoverable budgets

* test(desktop): validate emitted alias metadata structurally

* fix(ghidra): classify bounded Windows endpoint refusals

* test(cli): assert selectable artifact admission recovery

* fix(cli): preserve capture provenance in environment builds
This commit is contained in:
morluto authored and GitHub committed 2026-11-10 05:11:20 -05:00
1 parent 55b6d2ca20
commit cb4e5cfbff
515 files changed
+21066 -3637

No files matched your search

+24 -8
View File
@@ -45,6 +45,7 @@ import type {
ProviderProcessSupervisor,
} from "./ProviderProcess.js";
import { OwnedCommandFailure, ownedCommandResources } from "./OwnedCommand.js";
import { MAX_NODE_TIMER_DELAY_MS } from "../domain/analysisResourceLimits.js";
export type ArtifactAnalysisRuntime = Pick<
PrivateRuntimeRoot,
@@ -57,6 +58,7 @@ interface SelectedArtifactRead {
readonly allowanceName: string;
readonly observedOutput: AnalysisCapturedOutput | undefined;
readonly signal?: AbortSignal | undefined;
readonly budgetParameter?: "producer_limits.input_bytes";
}
export const artifactCommandOutput = (
@@ -207,7 +209,12 @@ export class ArtifactAnalysisResources {
{ input_bytes: maximumBytes },
{
cause,
...fixedProducerInputAllowance(maximumBytes, allowanceName),
...(input.budgetParameter === undefined
? fixedProducerInputAllowance(maximumBytes, allowanceName)
: {
retryable: false,
remediationAction: `Increase ${input.budgetParameter} above ${String(maximumBytes)} bytes on the next call to admit the same complete selected artifact; no REA restart is required. Do not truncate required input bytes. Output, parser, transport and inherited OS limits remain independent.`,
}),
},
);
if (cause instanceof ArtifactReaderFailure && cause.reason === "integrity")
@@ -501,6 +508,7 @@ export class ArtifactAnalysisResources {
},
effectiveLimits: LinuxResourceLimits | undefined,
operation = this.operation,
budgetParameter?: "producer_limits",
): AnalysisError {
const capturedOutput =
cause.snapshot === null
@@ -519,8 +527,11 @@ export class ArtifactAnalysisResources {
if (cause.reason === "timeout")
return new AnalysisTimeoutError(operation, limits.command_timeout_ms, {
...detail,
userMessage: `The owned producer command reached its fixed ${String(limits.command_timeout_ms)} ms deadline. This is not a client-side timeout; extending only the MCP client's deadline does not change this budget.`,
remediationAction: `REA's fixed producer command deadline is ${String(limits.command_timeout_ms)} ms and this release has no runtime override. Inspect any partial output and cleanup before retrying. Repeating the unchanged request reaches the same deadline.`,
userMessage: `The owned producer command reached its ${String(limits.command_timeout_ms)} ms deadline. This is not a client-side timeout; extending only the MCP client's deadline does not change this budget.`,
remediationAction:
budgetParameter === undefined
? `REA's fixed producer command deadline is ${String(limits.command_timeout_ms)} ms and this release has no runtime override. Inspect any partial output and cleanup before retrying. Repeating the unchanged request reaches the same deadline.`
: `${limits.command_timeout_ms < MAX_NODE_TIMER_DELAY_MS ? `Increase ${budgetParameter}.command_timeout_ms above ${String(limits.command_timeout_ms)} milliseconds on the next call (range 1..${String(MAX_NODE_TIMER_DELAY_MS)}); no restart is required.` : `${budgetParameter}.command_timeout_ms is already at the runtime timer maximum ${String(MAX_NODE_TIMER_DELAY_MS)} milliseconds; no higher deadline is supported. Narrow the selected work if that preserves the required observation.`} Inspect retained observations and confirm cleanup before retrying. This does not raise CPU, memory, output, parser or external client limits.`,
});
if (cause.reason === "output-limit")
return new AnalysisResourceConstraintError(
@@ -530,10 +541,15 @@ export class ArtifactAnalysisResources {
{ output_bytes: limits.output_bytes },
{
...detail,
...fixedAllowanceOptions(
`The retained command-output allowance of ${String(limits.output_bytes)} bytes (this release has no runtime override)`,
"capture",
),
...(budgetParameter === undefined
? fixedAllowanceOptions(
`The retained command-output allowance of ${String(limits.output_bytes)} bytes (this release has no runtime override)`,
"capture",
)
: {
retryable: false,
remediationAction: `Increase ${budgetParameter}.output_bytes above ${String(limits.output_bytes)} bytes on the next call; no restart is required. Inspect captured output and confirm cleanup before retrying. This controls collection, not parser, transport, host memory or inherited OS limits. Repeating the unchanged request cannot raise it.`,
}),
},
);
if (
@@ -556,7 +572,7 @@ export class ArtifactAnalysisResources {
{
...detail,
retryable: true,
remediationAction: `${reported} REA retains the tighter inherited limit. This release has no runtime override for that ceiling. A smaller artifact may finish inside the retained limit. The signal does not prove that ceiling was exhausted, so an unchanged retry may succeed or may fail the same way.`,
remediationAction: `${reported} REA retains the tighter inherited limit. ${budgetParameter === undefined ? "This release has no runtime override for that ceiling." : `Review ${budgetParameter}.${signal === "SIGXFSZ" ? "file_bytes" : "cpu_seconds"} for a new call; a higher requested policy cannot raise a tighter inherited soft or hard limit. No REA restart is needed for a per-call update.`} A smaller artifact may finish inside the retained limit. The signal does not prove that ceiling was exhausted, so an unchanged retry may succeed or may fail the same way.`,
},
);
}
+1 -1
View File
@@ -1,3 +1,4 @@
import { BOUNDED_PRODUCER_LIMITS } from "../domain/producerResourceLimits.js";
import { expect, it, onTestFinished, vi } from "vitest";
import { writeFile, type FileHandle } from "node:fs/promises";
import { join } from "node:path";
@@ -8,7 +9,6 @@ import { readStableArtifact } from "../artifacts/readStableArtifact.js";
import { ok } from "../domain/result.js";
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
import {
BOUNDED_PRODUCER_LIMITS,
clampLinuxResourceLimits,
prlimitArgumentVector,
prlimitCeilingArguments,
-10
View File
@@ -16,16 +16,6 @@ const rows = {
cpu_seconds: "Max cpu time",
} as const;
/** Bound the resident input copy and retained text before allocation/collection. */
export const BOUNDED_PRODUCER_LIMITS = {
input_bytes: 32 * 1024 * 1024,
output_bytes: 4 * 1024 * 1024,
command_timeout_ms: 30_000,
address_space_bytes: 1024 * 1024 * 1024,
file_bytes: 64 * 1024 * 1024,
cpu_seconds: 30,
} as const;
/**
* Pin soft and hard ceilings together, ending at the prlimit option terminator.
* Callers that append the producer command themselves use this prefix.