rea-agents 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +80 -18
  2. package/bridge/ghidra/ReaGhidraBridge.java +48 -1
  3. package/bridge/hopper_bridge.py +259 -24
  4. package/dist/application/AnalysisClientCleanup.js +14 -0
  5. package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
  6. package/dist/application/ArtifactExtraction.js +5 -1
  7. package/dist/application/ArtifactGraphConstruction.js +3 -15
  8. package/dist/application/ArtifactInventory/classify.js +5 -1
  9. package/dist/application/ArtifactInventory/reader.js +2 -0
  10. package/dist/application/BinarySession.js +15 -4
  11. package/dist/application/BinarySessionStatus.js +32 -1
  12. package/dist/application/BrowserEvidence.js +14 -0
  13. package/dist/application/BrowserScenarioCapturePort.js +1 -0
  14. package/dist/application/BrowserScenarioCaptureService.js +53 -0
  15. package/dist/application/BrowserScenarioEvidence.js +40 -0
  16. package/dist/application/CallPathTracing.js +160 -0
  17. package/dist/application/CapabilityInventory.js +79 -15
  18. package/dist/application/CompositeProvider.js +10 -1
  19. package/dist/application/DirectAnalysis.js +7 -2
  20. package/dist/application/EnhancedLiteralTracing.js +118 -0
  21. package/dist/application/EnhancedTools.js +35 -121
  22. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
  23. package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
  24. package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
  25. package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
  26. package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
  27. package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
  28. package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
  29. package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
  30. package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
  31. package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
  32. package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
  33. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
  34. package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
  35. package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
  36. package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
  37. package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
  38. package/dist/application/ProcessCaptureEnvironment.js +26 -0
  39. package/dist/application/ProcessCaptureLifecycle.js +1 -0
  40. package/dist/application/ProcessCheckpoints.js +5 -0
  41. package/dist/application/ProcessHarness.js +45 -34
  42. package/dist/application/ProcessNormalization.js +6 -0
  43. package/dist/application/ProcessReactiveCoordinator.js +10 -3
  44. package/dist/application/ProcessReactiveEffects.js +15 -1
  45. package/dist/application/ProcessReactiveHarness.js +100 -0
  46. package/dist/application/ProcessReactiveObservations.js +101 -16
  47. package/dist/application/ProjectPermissionStore.js +1 -0
  48. package/dist/application/ReconstructionObligationAuthority.js +72 -0
  49. package/dist/application/ReconstructionObligationCandidates.js +265 -0
  50. package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
  51. package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
  52. package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
  53. package/dist/application/ReconstructionObligationLedgerService.js +72 -0
  54. package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
  55. package/dist/application/ReconstructionObligationPolicies.js +132 -0
  56. package/dist/application/ReconstructionReadinessService.js +48 -0
  57. package/dist/artifacts/ArtifactProvider.js +87 -22
  58. package/dist/browser/BrowserScenarioSecrets.js +53 -0
  59. package/dist/browser/BrowserScenarioSessionPort.js +1 -0
  60. package/dist/browser/CdpBrowserProvider.js +2 -2
  61. package/dist/browser/CdpCaptureDocuments.js +57 -0
  62. package/dist/browser/CdpCaptureStorage.js +71 -29
  63. package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
  64. package/dist/browser/CdpEndpoint.js +8 -6
  65. package/dist/browser/CdpPageCapture.js +2 -0
  66. package/dist/browser/JavaScriptRuntimeScope.js +38 -0
  67. package/dist/browser/PlaywrightBrowserScenarioProvider.js +253 -0
  68. package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
  69. package/dist/browser/PlaywrightScenarioActions.js +62 -0
  70. package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
  71. package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
  72. package/dist/browser/PlaywrightScenarioEvents.js +301 -0
  73. package/dist/browser/PlaywrightScenarioSession.js +300 -0
  74. package/dist/browser/V8InspectorCaptureProjection.js +110 -0
  75. package/dist/browser/V8InspectorEndpoint.js +72 -0
  76. package/dist/browser/V8InspectorProvider.js +267 -0
  77. package/dist/catalogIdentity.js +8 -0
  78. package/dist/cli/artifactCommands.js +39 -0
  79. package/dist/cli/coreAnalysisCommands.js +37 -0
  80. package/dist/cli.js +4 -0
  81. package/dist/cliApplicationCommands.js +30 -2
  82. package/dist/cliBrowserAdvancedCommands.js +17 -5
  83. package/dist/cliBrowserCommands.js +1 -0
  84. package/dist/cliBrowserScenarioCommands.js +43 -0
  85. package/dist/cliCommandNames.js +8 -0
  86. package/dist/cliJavaScriptRuntimeCommands.js +164 -0
  87. package/dist/cliObservationOptions.js +4 -0
  88. package/dist/cliPolicyCommands.js +1 -0
  89. package/dist/config/environment.js +12 -0
  90. package/dist/config/parseConfig.js +88 -17
  91. package/dist/contracts/analysisSearchContract.js +14 -0
  92. package/dist/contracts/applicationToolContracts.js +86 -2
  93. package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
  94. package/dist/contracts/artifactToolContracts.js +22 -0
  95. package/dist/contracts/browserProviderToolContracts.js +7 -0
  96. package/dist/contracts/browserScenarioToolContracts.js +81 -0
  97. package/dist/contracts/browserToolContracts.js +88 -98
  98. package/dist/contracts/enhancedInputs.js +25 -6
  99. package/dist/contracts/functionInstructionContract.js +8 -0
  100. package/dist/contracts/hopperMemoryContracts.js +22 -0
  101. package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
  102. package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
  103. package/dist/contracts/reconstructionReadinessExample.js +436 -0
  104. package/dist/contracts/toolContractExamples.js +5 -0
  105. package/dist/contracts/toolContractTypes.js +1 -0
  106. package/dist/contracts/toolContracts.js +13 -21
  107. package/dist/contracts/toolEffects.js +21 -0
  108. package/dist/contracts/toolOutputSchemaGroups.js +70 -20
  109. package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
  110. package/dist/domain/analysisErrorProjection.js +8 -0
  111. package/dist/domain/analysisSnapshot.js +2 -0
  112. package/dist/domain/artifactGraph.js +2 -0
  113. package/dist/domain/artifactInspection.js +347 -0
  114. package/dist/domain/binaryTarget.js +3 -6
  115. package/dist/domain/browserCaptureComparison.js +82 -0
  116. package/dist/domain/browserObservation.js +8 -1
  117. package/dist/domain/browserObservationErrors.js +1 -0
  118. package/dist/domain/browserObservationSchemas.js +26 -0
  119. package/dist/domain/browserScenario.js +165 -0
  120. package/dist/domain/browserScenarioCapture.js +57 -0
  121. package/dist/domain/browserScenarioCaptureValues.js +204 -0
  122. package/dist/domain/browserScenarioDiff.js +324 -0
  123. package/dist/domain/browserScenarioDiffValues.js +157 -0
  124. package/dist/domain/browserScenarioNormalization.js +45 -0
  125. package/dist/domain/browserScenarioValues.js +372 -0
  126. package/dist/domain/evidence.js +2 -0
  127. package/dist/domain/hopperValues.js +10 -0
  128. package/dist/domain/javascriptRuntimeObservation.js +170 -0
  129. package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
  130. package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
  131. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
  132. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  133. package/dist/domain/javascriptSemanticAnalysis.js +5 -4
  134. package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
  135. package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
  136. package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
  137. package/dist/domain/javascriptSemanticDataEffects.js +284 -0
  138. package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
  139. package/dist/domain/javascriptSemanticFingerprints.js +174 -0
  140. package/dist/domain/javascriptSemanticIr.js +21 -1
  141. package/dist/domain/javascriptSemanticObjects.js +115 -0
  142. package/dist/domain/javascriptSemanticPromises.js +290 -0
  143. package/dist/domain/javascriptSemanticResources.js +136 -0
  144. package/dist/domain/javascriptStaticAnalysis.js +4 -3
  145. package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
  146. package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
  147. package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
  148. package/dist/domain/processCapture.js +71 -1
  149. package/dist/domain/processCaptureReactiveValidation.js +175 -0
  150. package/dist/domain/processCaptureValidation.js +2 -0
  151. package/dist/domain/processObservation.js +15 -6
  152. package/dist/domain/processReactiveRuntime.js +21 -10
  153. package/dist/domain/processReactiveScenario.js +1 -0
  154. package/dist/domain/processReactiveTransition.js +17 -10
  155. package/dist/domain/processScenario.js +21 -0
  156. package/dist/domain/providerCleanupError.js +11 -0
  157. package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
  158. package/dist/domain/reconstructionReadiness.js +121 -0
  159. package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
  160. package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
  161. package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
  162. package/dist/domain/reconstructionReadinessSchemas.js +240 -0
  163. package/dist/domain/sourceToBundleComparison.js +335 -0
  164. package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
  165. package/dist/domain/sourceToBundleSignals.js +202 -0
  166. package/dist/domain/webCaptureDiff.js +141 -27
  167. package/dist/domain/webScreenshot.js +1 -1
  168. package/dist/domain/zipPackageFormat.js +20 -0
  169. package/dist/generatedPackageMetadata.js +1 -1
  170. package/dist/ghidra/GhidraDefaults.js +1 -1
  171. package/dist/ghidra/GhidraFunctionValues.js +13 -1
  172. package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
  173. package/dist/hopper/HopperCleanup.js +158 -0
  174. package/dist/hopper/HopperClient.js +69 -99
  175. package/dist/hopper/HopperProvider.js +20 -1
  176. package/dist/hopper/HopperRequestQueue.js +187 -0
  177. package/dist/hopper/HopperResponseStream.js +46 -0
  178. package/dist/hopper/protocol.js +1 -0
  179. package/dist/main/transport.js +24 -24
  180. package/dist/main.js +6 -0
  181. package/dist/mcpDoctor.js +25 -2
  182. package/dist/native/NativeMacOSProvider.js +1 -0
  183. package/dist/process/ProcessCommandIdentity.js +19 -0
  184. package/dist/process/ProcessOwnership.js +11 -8
  185. package/dist/server/DynamicToolAvailability.js +70 -0
  186. package/dist/server/createServer.js +46 -1
  187. package/dist/server/lifecycleProgress.js +10 -0
  188. package/dist/server/permissionFailure.js +12 -0
  189. package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
  190. package/dist/server/registerApplicationTools/helpers.js +11 -7
  191. package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
  192. package/dist/server/registerApplicationTools/readiness.js +55 -0
  193. package/dist/server/registerApplicationTools.js +6 -0
  194. package/dist/server/registerArtifactTools.js +9 -1
  195. package/dist/server/registerBrowserScenarioTool.js +25 -0
  196. package/dist/server/registerBrowserTools.js +2 -2
  197. package/dist/server/registerCloseLifecycleTool.js +57 -0
  198. package/dist/server/registerEnhancedTools.js +16 -5
  199. package/dist/server/registerEvidenceResources.js +4 -0
  200. package/dist/server/registerEvidenceTools.js +9 -2
  201. package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
  202. package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
  203. package/dist/server/registerReconstructionReadinessResource.js +39 -0
  204. package/dist/server/registerSessionStatusTool.js +11 -8
  205. package/dist/server/registerSessionTools.js +11 -57
  206. package/dist/server/sessionAvailabilityPolicy.js +10 -8
  207. package/package.json +12 -6
  208. package/skills/reverse-engineer-anything/SKILL.md +4 -3
  209. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
@@ -1,5 +1,5 @@
1
1
  /** Project internal provider routing state into the caller-visible session status. */
2
- export const binarySessionStatus = ({ target, route, router, runtimeAvailability, runId, runtimeLineageSnapshots, }) => {
2
+ export const binarySessionStatus = ({ target, route, router, runtimeAvailability, runId, runtimeLineageSnapshots, requestActivitySnapshots, }) => {
3
3
  const configuredProvider = router.configuredIdentity();
4
4
  const provider = providerSummary(configuredProvider);
5
5
  const providers = router.providerIdentities(route).map(providerSummary);
@@ -41,6 +41,7 @@ export const binarySessionStatus = ({ target, route, router, runtimeAvailability
41
41
  run_id: runId,
42
42
  process_lineage: processLineageStatus(runtimeLineageSnapshots),
43
43
  },
44
+ analysis_activity: requestActivityStatus(requestActivitySnapshots),
44
45
  };
45
46
  return target === undefined
46
47
  ? { open: false, ...common }
@@ -54,6 +55,35 @@ export const binarySessionStatus = ({ target, route, router, runtimeAvailability
54
55
  architecture: target.architecture ?? null,
55
56
  };
56
57
  };
58
+ const requestActivityStatus = (snapshots) => {
59
+ const providers = snapshots
60
+ .slice()
61
+ .sort((left, right) => left.provider.id.localeCompare(right.provider.id))
62
+ .map(({ provider, active, queuedRequests }) => ({
63
+ provider: providerSummary(provider),
64
+ active: active === null
65
+ ? null
66
+ : {
67
+ request_id: active.requestId,
68
+ operation: active.operation,
69
+ elapsed_ms: active.elapsedMs,
70
+ timeout_ms: active.timeoutMs,
71
+ caller_state: active.callerState,
72
+ },
73
+ queued_requests: queuedRequests,
74
+ }));
75
+ const active = snapshots.flatMap((snapshot) => snapshot.active === null ? [] : [snapshot.active]);
76
+ return {
77
+ status: snapshots.length === 0
78
+ ? "not_observed"
79
+ : active.some(({ callerState }) => callerState === "timed_out")
80
+ ? "timed_out_busy"
81
+ : active.length > 0
82
+ ? "busy"
83
+ : "idle",
84
+ providers,
85
+ };
86
+ };
57
87
  const providerSummary = ({ id, name, version, }) => ({ id, name, version });
58
88
  const processLineageStatus = (snapshots) => snapshots.length === 0
59
89
  ? { status: "not_observed" }
@@ -91,6 +121,7 @@ const capabilityStatus = (descriptor) => ({
91
121
  operation: descriptor.operation,
92
122
  available: descriptor.available,
93
123
  reason: descriptor.reason,
124
+ availability_code: descriptor.availabilityCode ?? null,
94
125
  input_contract_version: descriptor.inputContractVersion,
95
126
  output_contract_version: descriptor.outputContractVersion,
96
127
  pagination: descriptor.pagination,
@@ -1,5 +1,6 @@
1
1
  import { createEvidence, } from "../domain/evidence.js";
2
2
  import { jsonValueSchema } from "../domain/jsonValue.js";
3
+ import { commitBrowserScenarioNormalization } from "../domain/browserScenarioNormalization.js";
3
4
  /** Create Evidence v2 for a policy-scoped external browser observation. */
4
5
  export const createBrowserEvidence = (operation, input, result, provider) => createEvidence(undefined, provider, {
5
6
  predicateType: browserPredicate(operation),
@@ -20,6 +21,18 @@ export const createBrowserEvidence = (operation, input, result, provider) => cre
20
21
  });
21
22
  const browserParameters = (input) => {
22
23
  if (!("cdp_endpoint" in input)) {
24
+ if ("before_scenario" in input &&
25
+ input.before_scenario !== undefined &&
26
+ input.after_scenario !== undefined)
27
+ return {
28
+ comparison_kind: "browser_scenario",
29
+ before_browser: input.before_scenario.browser,
30
+ after_browser: input.after_scenario.browser,
31
+ before_start_origin: input.before_scenario.scenario.start_origin,
32
+ after_start_origin: input.after_scenario.scenario.start_origin,
33
+ max_changes: input.max_changes,
34
+ normalization_sha256: commitBrowserScenarioNormalization(input.normalization).sha256,
35
+ };
23
36
  if ("max_changes" in input)
24
37
  return {
25
38
  before_target_id: input.before.inspection.target.target_id,
@@ -74,6 +87,7 @@ const browserParameters = (input) => {
74
87
  include_websocket_shapes: input.include_websocket_shapes,
75
88
  include_script_sources: input.include_script_sources,
76
89
  include_storage_keys: input.include_storage_keys,
90
+ include_storage_fingerprints: input.include_storage_fingerprints,
77
91
  limits: input.limits,
78
92
  ...(input.include_script_sources && "source_capture_approved" in input
79
93
  ? {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ import { createBrowserScenarioEvidence } from "./BrowserScenarioEvidence.js";
2
+ import { digestJson } from "./JavaScriptReplayPlanning.js";
3
+ import { AnalysisCapabilityUnavailableError, AnalysisProtocolError, PermissionRequiredError, } from "../domain/errors.js";
4
+ import { isLiteralLoopbackHostname } from "../domain/browserObservation.js";
5
+ import { err } from "../domain/result.js";
6
+ const OPERATION = "capture_browser_scenario";
7
+ const PROVIDER_ID = "rea-playwright-browser-scenario";
8
+ const networkScope = (origins) => origins.every((origin) => isLiteralLoopbackHostname(new URL(origin).hostname))
9
+ ? "loopback"
10
+ : "external";
11
+ const authorizeScenario = async (authority, scenario) => {
12
+ if (authority === undefined)
13
+ return err(new AnalysisCapabilityUnavailableError(PROVIDER_ID, OPERATION, "browser scenario permission policy is not configured"));
14
+ const authorized = await authority.authorize({
15
+ capability: "browser_automate",
16
+ roots: [],
17
+ executables: scenario.browser.mode === "launch"
18
+ ? [scenario.browser.executable_path]
19
+ : [],
20
+ environment_names: scenario.secrets.map(({ environment_variable: name }) => name),
21
+ origins: [
22
+ ...(scenario.browser.mode === "connect"
23
+ ? [scenario.browser.cdp_endpoint]
24
+ : []),
25
+ ...scenario.allowed_origins,
26
+ ],
27
+ network: networkScope(scenario.allowed_origins),
28
+ mount: false,
29
+ operation_identity: `${OPERATION}:${digestJson(scenario)}`,
30
+ }, "write");
31
+ if (!authorized.ok)
32
+ return err(authorized.error instanceof PermissionRequiredError
33
+ ? authorized.error
34
+ : new AnalysisProtocolError(authorized.error.message, {
35
+ cause: authorized.error,
36
+ }));
37
+ return { ok: true, value: true };
38
+ };
39
+ /** Authorize and execute one controlled browser scenario. */
40
+ export const captureBrowserScenario = async (provider, authority, scenario, options = {}) => {
41
+ const authorized = await authorizeScenario(authority, scenario);
42
+ if (!authorized.ok)
43
+ return authorized;
44
+ if (provider === undefined)
45
+ return err(new AnalysisCapabilityUnavailableError(PROVIDER_ID, OPERATION, "browser scenario provider is not configured"));
46
+ const captured = await provider.captureScenario(scenario, options);
47
+ return captured.ok
48
+ ? {
49
+ ok: true,
50
+ value: createBrowserScenarioEvidence(scenario, captured.value, provider.identity()),
51
+ }
52
+ : captured;
53
+ };
@@ -0,0 +1,40 @@
1
+ import { sanitizeBrowserUrl } from "../domain/browserObservation.js";
2
+ import { createEvidence, } from "../domain/evidence.js";
3
+ import { jsonValueSchema } from "../domain/jsonValue.js";
4
+ import { digestJson } from "./JavaScriptReplayPlanning.js";
5
+ const browserScenarioParameters = (scenario) => ({
6
+ scenario_sha256: digestJson(scenario),
7
+ browser_mode: scenario.browser.mode,
8
+ start_url: sanitizeBrowserUrl(scenario.start_url.url),
9
+ allowed_origins: scenario.allowed_origins,
10
+ actions: scenario.actions.map(({ step_id, action }) => ({
11
+ step_id,
12
+ action,
13
+ })),
14
+ secret_declarations: scenario.secrets.map(({ secret_id, environment_variable, purpose, redaction }) => ({
15
+ secret_id,
16
+ environment_variable,
17
+ purpose,
18
+ redaction,
19
+ })),
20
+ capture: scenario.capture,
21
+ limits: scenario.limits,
22
+ });
23
+ /** Create Evidence v2 without retaining resolved scenario secret values. */
24
+ export const createBrowserScenarioEvidence = (scenario, capture, provider) => createEvidence(undefined, provider, {
25
+ predicateType: "rea.browser-scenario-capture/v1",
26
+ operation: "capture_browser_scenario",
27
+ parameters: browserScenarioParameters(scenario),
28
+ result: jsonValueSchema.parse(capture),
29
+ confidence: "observed",
30
+ authority: "controlled-replay",
31
+ environment: {
32
+ id: `${capture.browser.product}@${capture.browser.version}`,
33
+ platform: process.platform,
34
+ architecture: process.arch,
35
+ isolation: capture.browser.process_ownership === "provider-owned"
36
+ ? "process"
37
+ : "none",
38
+ },
39
+ limitations: capture.limitations,
40
+ });
@@ -0,0 +1,160 @@
1
+ import { projectAnalysisError } from "../domain/errors.js";
2
+ import { parseRelatedAddresses } from "../domain/hopperValues.js";
3
+ import { ok } from "../domain/result.js";
4
+ /** Trace one deterministic bounded direct caller/callee path. */
5
+ export const traceCallPath = async (call, input, signal) => {
6
+ const state = createTraceState(input);
7
+ while (state.queueIndex < state.queue.length &&
8
+ state.traversalPath.length === 0) {
9
+ const address = state.queue[state.queueIndex++];
10
+ if (address === undefined)
11
+ break;
12
+ const current = state.visited.get(address);
13
+ if (current === undefined)
14
+ continue;
15
+ if (current.depth >= input.max_depth) {
16
+ state.frontier.add(address);
17
+ state.residual.add("Call-path traversal reached the depth limit.");
18
+ continue;
19
+ }
20
+ if (state.operations >= input.max_operations) {
21
+ recordPendingFrontier(state, input);
22
+ state.residual.add("Call-path traversal reached the operation budget.");
23
+ break;
24
+ }
25
+ state.operations += 1;
26
+ const found = await expandAddress(call, {
27
+ input,
28
+ state,
29
+ address,
30
+ current,
31
+ ...(signal === undefined ? {} : { signal }),
32
+ });
33
+ if (found)
34
+ break;
35
+ }
36
+ return ok(projectTrace(input, state));
37
+ };
38
+ const createTraceState = (input) => ({
39
+ visited: new Map([[input.start, { depth: 0, path: [input.start] }]]),
40
+ queue: [input.start],
41
+ edges: new Map(),
42
+ failures: [],
43
+ frontier: new Set(),
44
+ residual: new Set(),
45
+ operations: 0,
46
+ queueIndex: 0,
47
+ traversalPath: input.goal === input.start ? [input.start] : [],
48
+ });
49
+ const expandAddress = async (call, expansion) => {
50
+ const { input, state, address, current, signal } = expansion;
51
+ const relation = input.direction === "forward" ? "callees" : "callers";
52
+ const tool = input.direction === "forward" ? "procedure_callees" : "procedure_callers";
53
+ const result = await call(tool, { procedure: address }, signal);
54
+ if (!result.ok) {
55
+ state.failures.push({
56
+ address,
57
+ error: projectAnalysisError(result.error),
58
+ });
59
+ state.residual.add(`Call relationships were unavailable for ${address}.`);
60
+ return false;
61
+ }
62
+ const related = parseRelatedAddresses(result.value, relation);
63
+ if (!related.ok) {
64
+ state.failures.push({
65
+ address,
66
+ error: projectAnalysisError(related.error),
67
+ });
68
+ state.residual.add(`Call relationships were unreadable for ${address}.`);
69
+ return false;
70
+ }
71
+ for (const relatedAddress of sortedUniqueAddresses(related.value)) {
72
+ recordEdge(input, state, {
73
+ address,
74
+ relatedAddress,
75
+ discoveryDepth: current.depth + 1,
76
+ });
77
+ const path = state.visited.get(relatedAddress)?.path ?? [
78
+ ...current.path,
79
+ relatedAddress,
80
+ ];
81
+ if (state.visited.has(relatedAddress)) {
82
+ if (relatedAddress === input.goal) {
83
+ state.traversalPath = path;
84
+ return true;
85
+ }
86
+ continue;
87
+ }
88
+ if (state.visited.size >= input.max_nodes) {
89
+ if (state.frontier.size < input.max_nodes)
90
+ state.frontier.add(relatedAddress);
91
+ state.residual.add("Call-path traversal reached the node limit.");
92
+ continue;
93
+ }
94
+ state.visited.set(relatedAddress, {
95
+ depth: current.depth + 1,
96
+ path,
97
+ });
98
+ if (relatedAddress === input.goal) {
99
+ state.traversalPath = path;
100
+ return true;
101
+ }
102
+ state.queue.push(relatedAddress);
103
+ }
104
+ return false;
105
+ };
106
+ const recordEdge = (input, state, edge) => {
107
+ const sourceAddress = input.direction === "forward" ? edge.address : edge.relatedAddress;
108
+ const targetAddress = input.direction === "forward" ? edge.relatedAddress : edge.address;
109
+ const edgeKey = `${sourceAddress}\u0000${targetAddress}`;
110
+ if (state.edges.has(edgeKey))
111
+ return;
112
+ if (state.edges.size >= input.max_nodes) {
113
+ state.residual.add("Call-path traversal reached the edge limit.");
114
+ return;
115
+ }
116
+ state.edges.set(edgeKey, {
117
+ source_address: sourceAddress,
118
+ target_address: targetAddress,
119
+ discovery_depth: edge.discoveryDepth,
120
+ });
121
+ };
122
+ const recordPendingFrontier = (state, input) => {
123
+ for (const pending of state.queue.slice(state.queueIndex - 1))
124
+ if (state.frontier.size < input.max_nodes)
125
+ state.frontier.add(pending);
126
+ };
127
+ const projectTrace = (input, state) => ({
128
+ start: input.start,
129
+ goal: input.goal ?? null,
130
+ direction: input.direction,
131
+ goal_status: input.goal === undefined
132
+ ? "not_requested"
133
+ : state.traversalPath.length > 0
134
+ ? "reached"
135
+ : "not_reached",
136
+ nodes: [...state.visited.entries()]
137
+ .map(([address, { depth }]) => ({ address, depth }))
138
+ .sort((left, right) => left.depth - right.depth || compareAddress(left.address, right.address)),
139
+ edges: [...state.edges.values()].sort((left, right) => left.discovery_depth - right.discovery_depth ||
140
+ compareAddress(left.source_address, right.source_address) ||
141
+ compareAddress(left.target_address, right.target_address)),
142
+ traversal_path: state.traversalPath,
143
+ failures: state.failures,
144
+ frontier: [...state.frontier].sort(compareAddress),
145
+ limits: {
146
+ max_depth: input.max_depth,
147
+ max_nodes: input.max_nodes,
148
+ max_operations: input.max_operations,
149
+ nodes_visited: state.visited.size,
150
+ operations_used: state.operations,
151
+ },
152
+ truncated: state.residual.size > 0,
153
+ residual_unknowns: [...state.residual],
154
+ limitations: [
155
+ "Direct provider call relationships may omit unresolved indirect calls.",
156
+ "A not_reached goal is only absent from this bounded traversal, not from the complete program.",
157
+ ],
158
+ });
159
+ const compareAddress = (left, right) => left < right ? -1 : left > right ? 1 : 0;
160
+ const sortedUniqueAddresses = (addresses) => [...new Set(addresses)].sort(compareAddress);
@@ -8,8 +8,21 @@ const statusSchema = z.object({
8
8
  operation: z.string(),
9
9
  available: z.boolean(),
10
10
  reason: z.string().nullable(),
11
+ availability_code: z.string().nullable().optional(),
11
12
  })),
12
13
  });
14
+ const NO_CLIENT_FEATURES = {
15
+ elicitation_form: false,
16
+ elicitation_url: false,
17
+ roots: false,
18
+ sampling: false,
19
+ };
20
+ const CLIENT_FEATURE_REQUIREMENTS = {
21
+ capture_process_scenario: {
22
+ required: [],
23
+ optional: ["elicitation_form"],
24
+ },
25
+ };
13
26
  const ENHANCED_REQUIREMENTS = {
14
27
  swift_classes: ["list_procedures"],
15
28
  get_objc_classes: ["list_names"],
@@ -32,10 +45,17 @@ const ENHANCED_REQUIREMENTS = {
32
45
  "resolve_containing_procedure",
33
46
  ],
34
47
  };
35
- /** Build stable per-operation availability without hiding familiar tools. */
36
- export const buildCapabilityInventory = (sessionStatus, policy) => {
48
+ /** Build stable per-operation availability for discovery and tool visibility. */
49
+ export const buildCapabilityInventory = (sessionStatus, policy, clientFeatures = NO_CLIENT_FEATURES) => {
37
50
  const status = statusSchema.parse(sessionStatus);
38
- const descriptors = new Map(status.capabilities.map((descriptor) => [descriptor.operation, descriptor]));
51
+ const descriptors = new Map(status.capabilities.map((descriptor) => [
52
+ descriptor.operation,
53
+ {
54
+ available: descriptor.available,
55
+ reason: descriptor.reason,
56
+ availability_code: descriptor.availability_code ?? null,
57
+ },
58
+ ]));
39
59
  return TOOL_CONTRACTS.map((contract) => {
40
60
  const availability = availabilityFor({
41
61
  name: contract.name,
@@ -45,12 +65,19 @@ export const buildCapabilityInventory = (sessionStatus, policy) => {
45
65
  descriptors,
46
66
  policy,
47
67
  });
68
+ const clientRequirements = clientRequirementsFor(contract.name, clientFeatures);
69
+ const clientBlocked = clientRequirements.missing_required.length > 0;
48
70
  return {
49
71
  name: contract.name,
50
72
  surface: contract.kind,
51
- available: availability.reason === "available",
52
- reason: availability.reason,
53
- remediation: availability.remediation,
73
+ available: availability.reason === "available" && !clientBlocked,
74
+ reason: clientBlocked
75
+ ? "client_capability_missing"
76
+ : availability.reason,
77
+ remediation: clientBlocked
78
+ ? `Use an MCP client that supports: ${clientRequirements.missing_required.join(", ")}.`
79
+ : availability.remediation,
80
+ client_requirements: clientRequirements,
54
81
  effects: { ...contract.effects },
55
82
  annotations: {
56
83
  read_only: contract.annotations.readOnlyHint ?? false,
@@ -61,6 +88,18 @@ export const buildCapabilityInventory = (sessionStatus, policy) => {
61
88
  };
62
89
  }).sort((left, right) => left.name.localeCompare(right.name));
63
90
  };
91
+ const clientRequirementsFor = (name, clientFeatures) => {
92
+ const requirements = CLIENT_FEATURE_REQUIREMENTS[name] ?? {
93
+ required: [],
94
+ optional: [],
95
+ };
96
+ return {
97
+ required: [...requirements.required],
98
+ optional: [...requirements.optional],
99
+ missing_required: requirements.required.filter((feature) => !clientFeatures[feature]),
100
+ missing_optional: requirements.optional.filter((feature) => !clientFeatures[feature]),
101
+ };
102
+ };
64
103
  const availabilityFor = (context) => {
65
104
  const javascriptApplication = javascriptApplicationAvailability(context);
66
105
  if (javascriptApplication !== null)
@@ -86,6 +125,9 @@ const javascriptApplicationAvailability = ({ name, policy, }) => {
86
125
  : null;
87
126
  };
88
127
  const policyAvailability = ({ name, kind, policy, }) => {
128
+ const browser = browserPolicyAvailability(name, kind, policy);
129
+ if (browser !== null)
130
+ return browser;
89
131
  if (name === "capture_process_scenario" && !policy.processCaptureEnabled)
90
132
  return {
91
133
  reason: "policy_disabled",
@@ -113,26 +155,43 @@ const policyAvailability = ({ name, kind, policy, }) => {
113
155
  reason: "policy_disabled",
114
156
  remediation: "Configure or grant an evidence_read root.",
115
157
  };
116
- if (kind === "browser-provider")
117
- return policy.browserObservationEnabled === true
158
+ if (kind === "electron-provider")
159
+ return policy.electronObservationEnabled === true
118
160
  ? { reason: "available", remediation: null }
119
161
  : {
120
162
  reason: "policy_disabled",
121
- remediation: "Enable browser observation and configure exact CDP endpoint and page origins.",
163
+ remediation: "Enable Electron observation and configure a loopback CDP endpoint and canonical file roots.",
122
164
  };
123
- if (kind === "electron-provider")
124
- return policy.electronObservationEnabled === true
165
+ if (kind === "runtime-provider")
166
+ return policy.v8InspectorObservationEnabled === true
125
167
  ? { reason: "available", remediation: null }
126
168
  : {
127
169
  reason: "policy_disabled",
128
- remediation: "Enable Electron observation and configure a loopback CDP endpoint and canonical file roots.",
170
+ remediation: "Enable V8 Inspector observation and configure exact loopback endpoints plus canonical file roots or exact origins.",
129
171
  };
130
172
  if (kind === "session")
131
173
  return { reason: "available", remediation: null };
132
174
  return null;
133
175
  };
176
+ const browserPolicyAvailability = (name, kind, policy) => {
177
+ if (name === "capture_browser_scenario")
178
+ return policy.browserScenarioEnabled === true
179
+ ? { reason: "available", remediation: null }
180
+ : {
181
+ reason: "policy_disabled",
182
+ remediation: "Enable browser scenarios and configure exact executable roots or loopback CDP endpoints, page origins, and environment secret names.",
183
+ };
184
+ if (kind !== "browser-provider")
185
+ return null;
186
+ return policy.browserObservationEnabled === true
187
+ ? { reason: "available", remediation: null }
188
+ : {
189
+ reason: "policy_disabled",
190
+ remediation: "Enable browser observation and configure exact CDP endpoint and page origins.",
191
+ };
192
+ };
134
193
  const targetAvailability = ({ kind, targetKind, targetOpen, }) => {
135
- if (!targetOpen)
194
+ if (!targetOpen && (kind === "official-proxy" || kind === "enhanced"))
136
195
  return {
137
196
  reason: "target_required",
138
197
  remediation: "Call open_binary with a supported local target.",
@@ -151,13 +210,18 @@ const providerAvailability = ({ descriptors, kind, name, }) => {
151
210
  if (kind === "enhanced")
152
211
  return composedAvailability(name, descriptors);
153
212
  const descriptor = descriptors.get(name);
213
+ if (descriptor === undefined &&
214
+ (kind === "artifact-provider" ||
215
+ kind === "managed-provider" ||
216
+ kind === "native-provider"))
217
+ return { reason: "available", remediation: null };
154
218
  if (descriptor === undefined)
155
219
  return {
156
220
  reason: "provider_missing",
157
221
  remediation: "Install or configure a provider that declares this operation.",
158
222
  };
159
223
  if (!descriptor.available &&
160
- descriptor.reason?.toLowerCase().includes("require macos") === true)
224
+ descriptor.availability_code === "unsupported_host")
161
225
  return {
162
226
  reason: "unsupported_host",
163
227
  remediation: descriptor.reason,
@@ -187,7 +251,7 @@ const composedAvailability = (name, descriptors) => {
187
251
  .find((descriptor) => descriptor?.available === false);
188
252
  if (unavailable !== undefined)
189
253
  return {
190
- reason: unavailable.reason?.toLowerCase().includes("require macos") === true
254
+ reason: unavailable.availability_code === "unsupported_host"
191
255
  ? "unsupported_host"
192
256
  : "provider_unavailable",
193
257
  remediation: unavailable.reason ?? "Choose another target or configured provider.",
@@ -1,5 +1,6 @@
1
1
  import { AnalysisCapabilityUnavailableError } from "../domain/errors.js";
2
2
  import { err, ok } from "../domain/result.js";
3
+ import { closeAnalysisClient } from "./AnalysisClientCleanup.js";
3
4
  /** Synthetic compatibility identity for a deterministic provider set. */
4
5
  export const compositeProviderIdentity = (identities) => ({
5
6
  id: `composite:${identities
@@ -61,6 +62,10 @@ export class CompositeProvider {
61
62
  clients.set(provider, created);
62
63
  return created;
63
64
  };
65
+ const closeWithOutcome = async (options) => {
66
+ const outcomes = await Promise.all([...clients.entries()].map(([provider, client]) => closeAnalysisClient(client, provider.identity().id, options)));
67
+ return outcomes.find((outcome) => !outcome.ok) ?? ok(null);
68
+ };
64
69
  return {
65
70
  execute: (operation, parameters, options) => {
66
71
  if (operation === "health")
@@ -80,8 +85,12 @@ export class CompositeProvider {
80
85
  runtimeLineageSnapshots: () => [...clients.values()]
81
86
  .flatMap((client) => client.runtimeLineageSnapshots?.() ?? [])
82
87
  .sort((left, right) => left.provider.id.localeCompare(right.provider.id)),
88
+ requestActivitySnapshots: () => [...clients.values()]
89
+ .flatMap((client) => client.requestActivitySnapshots?.() ?? [])
90
+ .sort((left, right) => left.provider.id.localeCompare(right.provider.id)),
91
+ closeWithOutcome,
83
92
  close: async () => {
84
- await Promise.allSettled([...clients.values()].map(async (client) => client.close()));
93
+ await closeWithOutcome();
85
94
  },
86
95
  };
87
96
  }
@@ -12,6 +12,7 @@ import { err, ok } from "../domain/result.js";
12
12
  import { loadConfiguredPermissionAuthority } from "./PermissionConfiguration.js";
13
13
  import { authorizeFileReadWithDeferredWrite, } from "./DeferredFileAuthorization.js";
14
14
  import { REA_WORKFLOW_PROVIDER, workflowAnalysisProfile, } from "./InvestigationProviders.js";
15
+ import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
15
16
  export { runCapabilityStatus, runProviderStatus, } from "./DirectAnalysisStatus.js";
16
17
  /**
17
18
  * Open one binary, execute one tool, and always release provider resources.
@@ -60,7 +61,7 @@ const authorizeAnalysis = async (authority, tool, arguments_) => {
60
61
  cause: result.error,
61
62
  }));
62
63
  }
63
- if (tool === "inventory_artifact" &&
64
+ if (["inventory_artifact", "inspect_artifact"].includes(tool) &&
64
65
  arguments_.native_mount_approved === true) {
65
66
  const result = await authority.authorize({
66
67
  capability: "native_mount",
@@ -69,7 +70,7 @@ const authorizeAnalysis = async (authority, tool, arguments_) => {
69
70
  environment_names: [],
70
71
  network: "none",
71
72
  mount: true,
72
- operation_identity: "inventory_artifact:native_mount",
73
+ operation_identity: `${tool}:native_mount`,
73
74
  }, "read");
74
75
  if (!result.ok)
75
76
  return err(result.error instanceof PermissionRequiredError
@@ -212,6 +213,10 @@ const executeAnalysisTool = async (input) => {
212
213
  rawResult: result.value.rawResult,
213
214
  limitations: result.value.limitations,
214
215
  locations: result.value.locations,
216
+ evidenceLinks: tool === "inspect_artifact"
217
+ ? artifactInspectionResultSchema.parse(result.value.result)
218
+ .evidence_links
219
+ : [],
215
220
  });
216
221
  return { output: evidence, evidence };
217
222
  };