rea-agents 2.4.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 (186) 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/BinarySession.js +15 -4
  7. package/dist/application/BinarySessionStatus.js +31 -1
  8. package/dist/application/BrowserEvidence.js +14 -0
  9. package/dist/application/BrowserScenarioCapturePort.js +1 -0
  10. package/dist/application/BrowserScenarioCaptureService.js +53 -0
  11. package/dist/application/BrowserScenarioEvidence.js +40 -0
  12. package/dist/application/CallPathTracing.js +160 -0
  13. package/dist/application/CapabilityInventory.js +68 -12
  14. package/dist/application/CompositeProvider.js +10 -1
  15. package/dist/application/DirectAnalysis.js +7 -2
  16. package/dist/application/EnhancedLiteralTracing.js +118 -0
  17. package/dist/application/EnhancedTools.js +35 -121
  18. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
  19. package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
  20. package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
  21. package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
  22. package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
  23. package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
  24. package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
  25. package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
  26. package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
  27. package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
  28. package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
  29. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
  30. package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
  31. package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
  32. package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
  33. package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
  34. package/dist/application/ProcessReactiveEffects.js +6 -0
  35. package/dist/application/ProcessReactiveHarness.js +16 -1
  36. package/dist/application/ProjectPermissionStore.js +1 -0
  37. package/dist/application/ReconstructionObligationAuthority.js +72 -0
  38. package/dist/application/ReconstructionObligationCandidates.js +265 -0
  39. package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
  40. package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
  41. package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
  42. package/dist/application/ReconstructionObligationLedgerService.js +72 -0
  43. package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
  44. package/dist/application/ReconstructionObligationPolicies.js +132 -0
  45. package/dist/application/ReconstructionReadinessService.js +48 -0
  46. package/dist/artifacts/ArtifactProvider.js +87 -22
  47. package/dist/browser/BrowserScenarioSecrets.js +53 -0
  48. package/dist/browser/BrowserScenarioSessionPort.js +1 -0
  49. package/dist/browser/CdpBrowserProvider.js +2 -2
  50. package/dist/browser/CdpCaptureDocuments.js +57 -0
  51. package/dist/browser/CdpCaptureStorage.js +71 -29
  52. package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
  53. package/dist/browser/CdpEndpoint.js +8 -6
  54. package/dist/browser/CdpPageCapture.js +2 -0
  55. package/dist/browser/JavaScriptRuntimeScope.js +38 -0
  56. package/dist/browser/PlaywrightBrowserScenarioProvider.js +253 -0
  57. package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
  58. package/dist/browser/PlaywrightScenarioActions.js +62 -0
  59. package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
  60. package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
  61. package/dist/browser/PlaywrightScenarioEvents.js +301 -0
  62. package/dist/browser/PlaywrightScenarioSession.js +300 -0
  63. package/dist/browser/V8InspectorCaptureProjection.js +110 -0
  64. package/dist/browser/V8InspectorEndpoint.js +72 -0
  65. package/dist/browser/V8InspectorProvider.js +267 -0
  66. package/dist/catalogIdentity.js +8 -0
  67. package/dist/cli/artifactCommands.js +39 -0
  68. package/dist/cli/coreAnalysisCommands.js +37 -0
  69. package/dist/cli.js +4 -0
  70. package/dist/cliApplicationCommands.js +30 -2
  71. package/dist/cliBrowserAdvancedCommands.js +17 -5
  72. package/dist/cliBrowserCommands.js +1 -0
  73. package/dist/cliBrowserScenarioCommands.js +43 -0
  74. package/dist/cliCommandNames.js +8 -0
  75. package/dist/cliJavaScriptRuntimeCommands.js +164 -0
  76. package/dist/cliObservationOptions.js +4 -0
  77. package/dist/cliPolicyCommands.js +1 -0
  78. package/dist/config/environment.js +12 -0
  79. package/dist/config/parseConfig.js +88 -17
  80. package/dist/contracts/analysisSearchContract.js +14 -0
  81. package/dist/contracts/applicationToolContracts.js +86 -2
  82. package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
  83. package/dist/contracts/artifactToolContracts.js +22 -0
  84. package/dist/contracts/browserProviderToolContracts.js +7 -0
  85. package/dist/contracts/browserScenarioToolContracts.js +81 -0
  86. package/dist/contracts/browserToolContracts.js +88 -98
  87. package/dist/contracts/enhancedInputs.js +25 -6
  88. package/dist/contracts/functionInstructionContract.js +8 -0
  89. package/dist/contracts/hopperMemoryContracts.js +22 -0
  90. package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
  91. package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
  92. package/dist/contracts/reconstructionReadinessExample.js +436 -0
  93. package/dist/contracts/toolContractExamples.js +5 -0
  94. package/dist/contracts/toolContractTypes.js +1 -0
  95. package/dist/contracts/toolContracts.js +13 -21
  96. package/dist/contracts/toolEffects.js +21 -0
  97. package/dist/contracts/toolOutputSchemaGroups.js +70 -20
  98. package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
  99. package/dist/domain/analysisErrorProjection.js +8 -0
  100. package/dist/domain/artifactInspection.js +347 -0
  101. package/dist/domain/browserCaptureComparison.js +82 -0
  102. package/dist/domain/browserObservation.js +8 -1
  103. package/dist/domain/browserObservationErrors.js +1 -0
  104. package/dist/domain/browserObservationSchemas.js +26 -0
  105. package/dist/domain/browserScenario.js +165 -0
  106. package/dist/domain/browserScenarioCapture.js +57 -0
  107. package/dist/domain/browserScenarioCaptureValues.js +204 -0
  108. package/dist/domain/browserScenarioDiff.js +324 -0
  109. package/dist/domain/browserScenarioDiffValues.js +157 -0
  110. package/dist/domain/browserScenarioNormalization.js +45 -0
  111. package/dist/domain/browserScenarioValues.js +372 -0
  112. package/dist/domain/hopperValues.js +10 -0
  113. package/dist/domain/javascriptRuntimeObservation.js +170 -0
  114. package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
  115. package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
  116. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
  117. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  118. package/dist/domain/javascriptSemanticAnalysis.js +5 -4
  119. package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
  120. package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
  121. package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
  122. package/dist/domain/javascriptSemanticDataEffects.js +284 -0
  123. package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
  124. package/dist/domain/javascriptSemanticFingerprints.js +174 -0
  125. package/dist/domain/javascriptSemanticIr.js +21 -1
  126. package/dist/domain/javascriptSemanticObjects.js +115 -0
  127. package/dist/domain/javascriptSemanticPromises.js +290 -0
  128. package/dist/domain/javascriptSemanticResources.js +136 -0
  129. package/dist/domain/javascriptStaticAnalysis.js +4 -3
  130. package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
  131. package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
  132. package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
  133. package/dist/domain/processCapture.js +8 -2
  134. package/dist/domain/processReactiveScenario.js +1 -0
  135. package/dist/domain/processReactiveTransition.js +9 -4
  136. package/dist/domain/providerCleanupError.js +11 -0
  137. package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
  138. package/dist/domain/reconstructionReadiness.js +121 -0
  139. package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
  140. package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
  141. package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
  142. package/dist/domain/reconstructionReadinessSchemas.js +240 -0
  143. package/dist/domain/sourceToBundleComparison.js +335 -0
  144. package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
  145. package/dist/domain/sourceToBundleSignals.js +202 -0
  146. package/dist/domain/webCaptureDiff.js +141 -27
  147. package/dist/domain/webScreenshot.js +1 -1
  148. package/dist/generatedPackageMetadata.js +1 -1
  149. package/dist/ghidra/GhidraDefaults.js +1 -1
  150. package/dist/ghidra/GhidraFunctionValues.js +13 -1
  151. package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
  152. package/dist/hopper/HopperCleanup.js +158 -0
  153. package/dist/hopper/HopperClient.js +69 -99
  154. package/dist/hopper/HopperProvider.js +20 -1
  155. package/dist/hopper/HopperRequestQueue.js +187 -0
  156. package/dist/hopper/HopperResponseStream.js +46 -0
  157. package/dist/hopper/protocol.js +1 -0
  158. package/dist/main/transport.js +24 -24
  159. package/dist/main.js +6 -0
  160. package/dist/mcpDoctor.js +25 -2
  161. package/dist/process/ProcessCommandIdentity.js +19 -0
  162. package/dist/process/ProcessOwnership.js +11 -8
  163. package/dist/server/DynamicToolAvailability.js +70 -0
  164. package/dist/server/createServer.js +46 -1
  165. package/dist/server/lifecycleProgress.js +10 -0
  166. package/dist/server/permissionFailure.js +12 -0
  167. package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
  168. package/dist/server/registerApplicationTools/helpers.js +11 -7
  169. package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
  170. package/dist/server/registerApplicationTools/readiness.js +55 -0
  171. package/dist/server/registerApplicationTools.js +6 -0
  172. package/dist/server/registerArtifactTools.js +9 -1
  173. package/dist/server/registerBrowserScenarioTool.js +25 -0
  174. package/dist/server/registerBrowserTools.js +2 -2
  175. package/dist/server/registerCloseLifecycleTool.js +57 -0
  176. package/dist/server/registerEnhancedTools.js +16 -5
  177. package/dist/server/registerEvidenceResources.js +4 -0
  178. package/dist/server/registerEvidenceTools.js +9 -2
  179. package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
  180. package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
  181. package/dist/server/registerReconstructionReadinessResource.js +39 -0
  182. package/dist/server/registerSessionStatusTool.js +11 -8
  183. package/dist/server/registerSessionTools.js +11 -57
  184. package/dist/server/sessionAvailabilityPolicy.js +10 -8
  185. package/package.json +12 -6
  186. package/skills/reverse-engineer-anything/SKILL.md +2 -2
@@ -2,9 +2,10 @@ import { z } from "zod";
2
2
  import { processCaptureComparisonSchema, processCaptureSchema, } from "../domain/processCapture.js";
3
3
  import { evidenceBundleSchema } from "../domain/evidenceBundle.js";
4
4
  import { residualUnknownSchema } from "../domain/residualUnknown.js";
5
- import { referenceKindSchema } from "../domain/hopperValues.js";
5
+ import { functionInstructionWindowSchema, referenceKindSchema, } from "../domain/hopperValues.js";
6
6
  import { demangleSwiftSchema, inspectMachoSchema, inspectPlistSchema, inspectSignatureSchema, listArchitecturesSchema, } from "../domain/nativeInspection.js";
7
7
  import { artifactExtractionResultSchema, artifactInventoryResultSchema, } from "../domain/artifactGraph.js";
8
+ import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
8
9
  import { managedArtifactInspectionSchema, managedMemberInspectionSchema, managedNativeBoundaryInspectionSchema, } from "../domain/managedArtifact.js";
9
10
  import { managedMemberComparisonResultSchema } from "../domain/managedMemberComparison.js";
10
11
  import { managedNativeVerificationResultSchema } from "../domain/managedNativeVerification.js";
@@ -20,7 +21,7 @@ import { staticRuntimeCorrelationResultSchema } from "../domain/staticRuntimeCor
20
21
  import { reconstructionVerificationResultSchema } from "../domain/reconstructionVerification.js";
21
22
  import { replayMachineRunOutputSchema } from "../domain/replayMachineRun.js";
22
23
  import { analysisErrorProjectionSchema } from "./errorSchemas.js";
23
- import { addressList, addressedEntry, bounded, containingProcedureResolution, functionDossierOutput, graphNode, lifecycleResultOf, nullableText, pageOutput, procedureIdentity, procedureInfoOutput, evidenceResultOf as resultOf, searchPageOutput, segmentOutput, sessionProvider, providerIdentity, toolAvailability, symbolDiscoveryOutput, targetFormatSchema, targetKindSchema, } from "./toolOutputSchemaPrimitives.js";
24
+ import { addressList, analysisActivity, addressedEntry, bounded, containingProcedureResolution, clientFeatureAvailabilitySchema, functionDossierOutput, graphNode, lifecycleResultOf, nullableText, pageOutput, procedureIdentity, procedureInfoOutput, evidenceResultOf as resultOf, searchPageOutput, segmentOutput, sessionProvider, providerIdentity, toolAvailability, symbolDiscoveryOutput, targetFormatSchema, targetKindSchema, } from "./toolOutputSchemaPrimitives.js";
24
25
  /** Exact structured-content schemas shared by direct analysis providers. */
25
26
  export const officialOutputSchemas = {
26
27
  address_name: resultOf(nullableText),
@@ -43,6 +44,18 @@ export const officialOutputSchemas = {
43
44
  procedure_callees: resultOf(addressList),
44
45
  procedure_callers: resultOf(addressList),
45
46
  procedure_info: procedureInfoOutput,
47
+ read_function_instructions: resultOf(functionInstructionWindowSchema),
48
+ read_bytes: resultOf(z.object({
49
+ address: z.string(),
50
+ requested_bytes: z.number().int().min(1).max(4_096),
51
+ returned_bytes: z.number().int().min(0).max(4_096),
52
+ bytes_hex: z.string().regex(/^(?:[a-f0-9]{2})*$/u),
53
+ complete: z.boolean(),
54
+ })),
55
+ address_to_file_offset: resultOf(z.object({
56
+ address: z.string(),
57
+ file_offset: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
58
+ })),
46
59
  procedure_references: resultOf(z.object({
47
60
  procedure: procedureIdentity,
48
61
  direction: z.enum(["incoming", "outgoing"]),
@@ -69,6 +82,55 @@ export const officialOutputSchemas = {
69
82
  unset_bookmark: resultOf(z.boolean()),
70
83
  xrefs: resultOf(addressList),
71
84
  };
85
+ const literalTraceOutput = resultOf(z.object({
86
+ query: z.string(),
87
+ search_mode: z.literal("literal"),
88
+ operations_used: z.number().int().min(0),
89
+ operation_budget: z.number().int().min(1),
90
+ matches: z.array(z.object({
91
+ type: z.enum(["string", "procedure"]),
92
+ address: z.string(),
93
+ value: z.string(),
94
+ })),
95
+ references: z.array(z.object({
96
+ target_address: z.string(),
97
+ source_address: z.string(),
98
+ containing_procedure: containingProcedureResolution,
99
+ })),
100
+ truncated: z.boolean(),
101
+ residual_unknowns: z.array(z.string()),
102
+ }));
103
+ const callPathTraceOutput = resultOf(z.object({
104
+ start: z.string(),
105
+ goal: z.string().nullable(),
106
+ direction: z.enum(["forward", "backward"]),
107
+ goal_status: z.enum(["not_requested", "reached", "not_reached"]),
108
+ nodes: z.array(z.object({
109
+ address: z.string(),
110
+ depth: z.number().int().min(0),
111
+ })),
112
+ edges: z.array(z.object({
113
+ source_address: z.string(),
114
+ target_address: z.string(),
115
+ discovery_depth: z.number().int().min(1),
116
+ })),
117
+ traversal_path: z.array(z.string()),
118
+ failures: z.array(z.object({
119
+ address: z.string(),
120
+ error: analysisErrorProjectionSchema,
121
+ })),
122
+ frontier: z.array(z.string()),
123
+ limits: z.object({
124
+ max_depth: z.number().int().min(1),
125
+ max_nodes: z.number().int().min(1),
126
+ max_operations: z.number().int().min(1),
127
+ nodes_visited: z.number().int().min(1),
128
+ operations_used: z.number().int().min(0),
129
+ }),
130
+ truncated: z.boolean(),
131
+ residual_unknowns: z.array(z.string()),
132
+ limitations: z.array(z.string()),
133
+ }));
72
134
  /** Exact structured-content schemas for composed analysis workflows. */
73
135
  export const enhancedOutputSchemas = {
74
136
  swift_classes: symbolDiscoveryOutput("classes"),
@@ -126,24 +188,9 @@ export const enhancedOutputSchemas = {
126
188
  string_count: z.number().int().min(0),
127
189
  })),
128
190
  analyze_function: functionDossierOutput,
129
- trace_feature: resultOf(z.object({
130
- query: z.string(),
131
- search_mode: z.literal("literal"),
132
- operations_used: z.number().int().min(0),
133
- operation_budget: z.number().int().min(1),
134
- matches: z.array(z.object({
135
- type: z.enum(["string", "procedure"]),
136
- address: z.string(),
137
- value: z.string(),
138
- })),
139
- references: z.array(z.object({
140
- target_address: z.string(),
141
- source_address: z.string(),
142
- containing_procedure: containingProcedureResolution,
143
- })),
144
- truncated: z.boolean(),
145
- residual_unknowns: z.array(z.string()),
146
- })),
191
+ trace_feature: literalTraceOutput,
192
+ find_code_for_string: literalTraceOutput,
193
+ trace_call_path: callPathTraceOutput,
147
194
  };
148
195
  /** Exact Evidence v2 schemas for provider-neutral native inspection. */
149
196
  export const nativeOutputSchemas = {
@@ -156,6 +203,7 @@ export const nativeOutputSchemas = {
156
203
  /** Exact Evidence v2 schemas for provider-neutral artifact graph operations. */
157
204
  export const artifactOutputSchemas = {
158
205
  inventory_artifact: resultOf(artifactInventoryResultSchema),
206
+ inspect_artifact: resultOf(artifactInspectionResultSchema),
159
207
  extract_artifact: resultOf(artifactExtractionResultSchema),
160
208
  };
161
209
  /** Exact Evidence v2 schema for execution-free managed static analysis. */
@@ -210,6 +258,7 @@ export const sessionOutputSchemas = {
210
258
  reasons: z.array(z.string()),
211
259
  remediation: z.string().nullable(),
212
260
  }),
261
+ analysis_activity: analysisActivity,
213
262
  recommended_actions: z.array(z.string()),
214
263
  }),
215
264
  z.object({
@@ -226,6 +275,7 @@ export const sessionOutputSchemas = {
226
275
  next_cursor: z.number().int().min(0).nullable(),
227
276
  has_more: z.boolean(),
228
277
  }),
278
+ client_features: clientFeatureAvailabilitySchema,
229
279
  }),
230
280
  z.union([
231
281
  sessionProvider.extend({
@@ -111,6 +111,22 @@ const analysisRun = z
111
111
  ]),
112
112
  })
113
113
  .nullable();
114
+ export const analysisActivity = z.object({
115
+ status: z.enum(["not_observed", "idle", "busy", "timed_out_busy"]),
116
+ providers: z.array(z.object({
117
+ provider: providerIdentity,
118
+ active: z
119
+ .object({
120
+ request_id: z.number().int().min(1),
121
+ operation: z.string().min(1),
122
+ elapsed_ms: z.number().int().min(0),
123
+ timeout_ms: z.number().int().min(0),
124
+ caller_state: z.enum(["waiting", "timed_out", "cancelled"]),
125
+ })
126
+ .nullable(),
127
+ queued_requests: z.number().int().min(0),
128
+ })),
129
+ });
114
130
  const providerRejectionCode = z.enum(PROVIDER_REJECTION_CODES);
115
131
  const providerDiagnostics = z.record(z.string(), z.json());
116
132
  const providerAvailability = z.discriminatedUnion("status", [
@@ -153,12 +169,26 @@ const providerTargetSupport = z.discriminatedUnion("status", [
153
169
  diagnostics: providerDiagnostics,
154
170
  }),
155
171
  ]);
172
+ const clientFeatureNameSchema = z.enum([
173
+ "elicitation_form",
174
+ "elicitation_url",
175
+ "roots",
176
+ "sampling",
177
+ ]);
178
+ /** Negotiated MCP client features that affect REA workflow availability. */
179
+ export const clientFeatureAvailabilitySchema = z.object({
180
+ elicitation_form: z.boolean(),
181
+ elicitation_url: z.boolean(),
182
+ roots: z.boolean(),
183
+ sampling: z.boolean(),
184
+ });
156
185
  export const toolAvailability = z.object({
157
186
  name: z.string(),
158
187
  surface: z.string(),
159
188
  available: z.boolean(),
160
189
  reason: z.enum([
161
190
  "available",
191
+ "client_capability_missing",
162
192
  "target_required",
163
193
  "provider_missing",
164
194
  "provider_unavailable",
@@ -167,6 +197,12 @@ export const toolAvailability = z.object({
167
197
  "policy_disabled",
168
198
  ]),
169
199
  remediation: z.string().nullable(),
200
+ client_requirements: z.object({
201
+ required: z.array(clientFeatureNameSchema),
202
+ optional: z.array(clientFeatureNameSchema),
203
+ missing_required: z.array(clientFeatureNameSchema),
204
+ missing_optional: z.array(clientFeatureNameSchema),
205
+ }),
170
206
  effects: z.strictObject({
171
207
  mutatesTarget: z.boolean(),
172
208
  mutatesSession: z.boolean(),
@@ -189,6 +225,7 @@ export const sessionProvider = z.object({
189
225
  providers: z.array(providerIdentity),
190
226
  capabilities: z.array(providerCapability),
191
227
  analysis_run: analysisRun,
228
+ analysis_activity: analysisActivity,
192
229
  analysis_provider_binding: z
193
230
  .object({
194
231
  provider: providerIdentity,
@@ -21,6 +21,8 @@ export const projectAnalysisError = (error) => {
21
21
  };
22
22
  };
23
23
  const errorCode = (error) => {
24
+ if (error.cleanupIncomplete)
25
+ return "cleanup_incomplete";
24
26
  if (error instanceof ReplayPlanStaleError)
25
27
  return "plan_stale";
26
28
  if (error instanceof PermissionRequiredError)
@@ -221,6 +223,12 @@ const providerErrorDetails = (error) => {
221
223
  return {
222
224
  provider_id: error.providerId,
223
225
  operation: error.operation,
226
+ ...(error.cleanupIncomplete
227
+ ? {
228
+ cleanup: "incomplete",
229
+ resources: [...error.cleanupResources],
230
+ }
231
+ : {}),
224
232
  ...(error.diagnostics === undefined
225
233
  ? {}
226
234
  : { diagnostics: error.diagnostics }),
@@ -0,0 +1,347 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { artifactInventoryResultSchema, } from "./artifactGraph.js";
5
+ import { evidenceSchema, parseEvidence } from "./evidence.js";
6
+ import { jsonValueSchema } from "./jsonValue.js";
7
+ const boundedTextSchema = z.string().min(1).max(4_096);
8
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
9
+ const artifactIdSchema = z.string().regex(/^art_[a-f0-9]{64}$/u);
10
+ const inspectionLimitsSchema = z.strictObject({
11
+ max_observations: z.number().int().min(1).max(500).default(100),
12
+ max_relationships: z.number().int().min(1).max(500).default(100),
13
+ max_hypotheses: z.number().int().min(1).max(100).default(20),
14
+ max_unexplored_branches: z.number().int().min(1).max(100).default(20),
15
+ max_next_probes: z.number().int().min(1).max(50).default(20),
16
+ max_substeps: z.literal(1).default(1),
17
+ });
18
+ export const artifactInspectionLimitsSchema = inspectionLimitsSchema;
19
+ const inspectionObservationSchema = z.strictObject({
20
+ observation_id: z.string().regex(/^aio_[a-f0-9]{64}$/u),
21
+ kind: z.enum(["root-manifest", "artifact", "occurrence", "integrity"]),
22
+ subject: boundedTextSchema,
23
+ value: jsonValueSchema,
24
+ evidence_id: evidenceIdSchema,
25
+ });
26
+ const inspectionRelationshipSchema = z.strictObject({
27
+ relationship_id: z.string().regex(/^air_[a-f0-9]{64}$/u),
28
+ relation: z.enum([
29
+ "contains",
30
+ "extracts",
31
+ "slice-of",
32
+ "embeds",
33
+ "loads",
34
+ "maps-source",
35
+ "derived-from",
36
+ ]),
37
+ source_artifact_id: artifactIdSchema,
38
+ target_artifact_id: artifactIdSchema,
39
+ occurrence_id: z.string().regex(/^occ_[a-f0-9]{64}$/u),
40
+ logical_path: boundedTextSchema.nullable(),
41
+ evidence_id: evidenceIdSchema,
42
+ });
43
+ const inspectionHypothesisSchema = z.strictObject({
44
+ hypothesis_id: z.string().regex(/^aih_[a-f0-9]{64}$/u),
45
+ statement: boundedTextSchema,
46
+ confidence: z.enum(["medium", "low"]),
47
+ basis_evidence_ids: z.array(evidenceIdSchema).min(1).max(8),
48
+ limitation: boundedTextSchema,
49
+ });
50
+ const inspectionContradictionSchema = z.strictObject({
51
+ contradiction_id: z.string().regex(/^aic_[a-f0-9]{64}$/u),
52
+ statement: boundedTextSchema,
53
+ occurrence_id: z.string().regex(/^occ_[a-f0-9]{64}$/u),
54
+ declared_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
55
+ observed_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
56
+ evidence_id: evidenceIdSchema,
57
+ });
58
+ const nextProbeSchema = z.strictObject({
59
+ operation: z.string().min(1).max(128),
60
+ rationale: boundedTextSchema,
61
+ authority: z.enum([
62
+ "artifact-bytes",
63
+ "managed-static-analysis",
64
+ "native-analysis-provider",
65
+ "ast-static-analysis",
66
+ ]),
67
+ });
68
+ const unexploredBranchSchema = z.strictObject({
69
+ branch_id: z.string().regex(/^aib_[a-f0-9]{64}$/u),
70
+ reason: z.enum([
71
+ "page-limit",
72
+ "format-specific-analysis-required",
73
+ "unknown-format",
74
+ ]),
75
+ detail: boundedTextSchema,
76
+ next_probe: nextProbeSchema.nullable(),
77
+ evidence_id: evidenceIdSchema,
78
+ });
79
+ /** Provider-neutral inspection result with its atomic source Evidence. */
80
+ export const artifactInspectionResultSchema = z.strictObject({
81
+ schema_version: z.literal(1),
82
+ inspection_id: z.string().regex(/^ai_[a-f0-9]{64}$/u),
83
+ subject: z.strictObject({
84
+ manifest_id: z.string().regex(/^agm_[a-f0-9]{64}$/u),
85
+ root_artifact_id: artifactIdSchema,
86
+ root_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
87
+ root_format: z.string().min(1).max(128),
88
+ }),
89
+ substeps: z
90
+ .array(z.strictObject({
91
+ substep_id: z.string().regex(/^ais_[a-f0-9]{64}$/u),
92
+ operation: z.literal("inventory_artifact"),
93
+ status: z.literal("completed"),
94
+ evidence_id: evidenceIdSchema,
95
+ evidence: evidenceSchema,
96
+ }))
97
+ .min(1)
98
+ .max(1),
99
+ observations: z.array(inspectionObservationSchema).max(500),
100
+ derived_relationships: z.array(inspectionRelationshipSchema).max(500),
101
+ hypotheses: z.array(inspectionHypothesisSchema).max(100),
102
+ contradictions: z.array(inspectionContradictionSchema).max(100),
103
+ unexplored_branches: z.array(unexploredBranchSchema).max(100),
104
+ next_probes: z.array(nextProbeSchema).max(50),
105
+ coverage: z.strictObject({
106
+ status: z.enum(["complete-within-substeps", "partial", "truncated"]),
107
+ substeps_completed: z.literal(1),
108
+ observations_retained: z.number().int().min(0),
109
+ observations_omitted: z.number().int().min(0),
110
+ relationships_retained: z.number().int().min(0),
111
+ relationships_omitted: z.number().int().min(0),
112
+ hypotheses_retained: z.number().int().min(0),
113
+ hypotheses_omitted: z.number().int().min(0),
114
+ unexplored_branches_retained: z.number().int().min(0),
115
+ unexplored_branches_omitted: z.number().int().min(0),
116
+ next_probes_retained: z.number().int().min(0),
117
+ next_probes_omitted: z.number().int().min(0),
118
+ }),
119
+ evidence_links: z.array(evidenceIdSchema).min(1).max(1),
120
+ limitations: z.array(boundedTextSchema).max(1_000),
121
+ });
122
+ /** Project one successful inventory Evidence record into an inspection report. */
123
+ export const createArtifactInspection = (inventoryInput, limits) => {
124
+ const inventoryEvidence = parseInventoryEvidence(inventoryInput);
125
+ const inventory = artifactInventoryResultSchema.parse(inventoryEvidence.normalized_result);
126
+ const observations = allObservations(inventory, inventoryEvidence.evidence_id);
127
+ const relationships = allRelationships(inventory, inventoryEvidence.evidence_id);
128
+ const hypotheses = allHypotheses(inventory, inventoryEvidence.evidence_id);
129
+ const nextProbes = probesFor(inventory);
130
+ const branches = allUnexploredBranches(inventory, inventoryEvidence.evidence_id, nextProbes);
131
+ const retained = {
132
+ observations: observations.slice(0, limits.max_observations),
133
+ relationships: relationships.slice(0, limits.max_relationships),
134
+ hypotheses: hypotheses.slice(0, limits.max_hypotheses),
135
+ branches: branches.slice(0, limits.max_unexplored_branches),
136
+ probes: nextProbes.slice(0, limits.max_next_probes),
137
+ };
138
+ const omissions = {
139
+ observations: observations.length - retained.observations.length,
140
+ relationships: relationships.length - retained.relationships.length,
141
+ hypotheses: hypotheses.length - retained.hypotheses.length,
142
+ branches: branches.length - retained.branches.length,
143
+ probes: nextProbes.length - retained.probes.length,
144
+ };
145
+ const semantic = {
146
+ schema_version: 1,
147
+ subject: {
148
+ manifest_id: inventory.manifest.manifest_id,
149
+ root_artifact_id: inventory.manifest.root_artifact_id,
150
+ root_sha256: inventory.manifest.root_sha256,
151
+ root_format: inventory.manifest.root_format,
152
+ },
153
+ substeps: [
154
+ {
155
+ substep_id: `ais_${digestCanonical({
156
+ operation: "inventory_artifact",
157
+ evidence_id: inventoryEvidence.evidence_id,
158
+ })}`,
159
+ operation: "inventory_artifact",
160
+ status: "completed",
161
+ evidence_id: inventoryEvidence.evidence_id,
162
+ evidence: inventoryEvidence,
163
+ },
164
+ ],
165
+ observations: retained.observations,
166
+ derived_relationships: retained.relationships,
167
+ hypotheses: retained.hypotheses,
168
+ contradictions: contradictions(inventory, inventoryEvidence.evidence_id),
169
+ unexplored_branches: retained.branches,
170
+ next_probes: retained.probes,
171
+ coverage: {
172
+ status: inspectionCoverage(inventory, omissions),
173
+ substeps_completed: 1,
174
+ observations_retained: retained.observations.length,
175
+ observations_omitted: omissions.observations,
176
+ relationships_retained: retained.relationships.length,
177
+ relationships_omitted: omissions.relationships,
178
+ hypotheses_retained: retained.hypotheses.length,
179
+ hypotheses_omitted: omissions.hypotheses,
180
+ unexplored_branches_retained: retained.branches.length,
181
+ unexplored_branches_omitted: omissions.branches,
182
+ next_probes_retained: retained.probes.length,
183
+ next_probes_omitted: omissions.probes,
184
+ },
185
+ evidence_links: [inventoryEvidence.evidence_id],
186
+ limitations: uniqueSorted([
187
+ ...inventory.limitations,
188
+ "Inspection derives bounded static hypotheses and next probes; it does not execute the artifact or claim format-specific semantics.",
189
+ ]),
190
+ };
191
+ return artifactInspectionResultSchema.parse({
192
+ ...semantic,
193
+ inspection_id: `ai_${digestCanonical(semantic)}`,
194
+ });
195
+ };
196
+ const parseInventoryEvidence = (input) => {
197
+ const evidence = parseEvidence(input);
198
+ if (evidence.operation !== "inventory_artifact")
199
+ throw new TypeError("Artifact inspection substep requires inventory_artifact Evidence");
200
+ return evidence;
201
+ };
202
+ const allObservations = (inventory, evidenceId) => [
203
+ observation("root-manifest", inventory.manifest.root_artifact_id, inventory.manifest, evidenceId),
204
+ ...inventory.nodes.items.map((node) => observation("artifact", node.artifact_id, node, evidenceId)),
205
+ ...inventory.occurrences.items.map((occurrence) => observation("occurrence", occurrence.occurrence_id, occurrence, evidenceId)),
206
+ ...inventory.integrity_contradictions.map((value) => observation("integrity", value.contradiction_id, value, evidenceId)),
207
+ ];
208
+ const observation = (kind, subject, value, evidenceId) => {
209
+ const semantic = {
210
+ kind,
211
+ subject,
212
+ value: jsonValueSchema.parse(value),
213
+ evidence_id: evidenceId,
214
+ };
215
+ return { observation_id: `aio_${digestCanonical(semantic)}`, ...semantic };
216
+ };
217
+ const allRelationships = (inventory, evidenceId) => inventory.edges.items.map((edge) => {
218
+ const semantic = {
219
+ relation: edge.relation,
220
+ source_artifact_id: edge.parent_artifact_id,
221
+ target_artifact_id: edge.child_artifact_id,
222
+ occurrence_id: edge.occurrence_id,
223
+ logical_path: edge.logical_path,
224
+ evidence_id: evidenceId,
225
+ };
226
+ return {
227
+ relationship_id: `air_${digestCanonical(semantic)}`,
228
+ ...semantic,
229
+ };
230
+ });
231
+ const allHypotheses = (inventory, evidenceId) => {
232
+ const statements = inventory.manifest.root_format === "pe"
233
+ ? [
234
+ "The PE artifact may contain managed CLI metadata or native code; inventory bytes alone do not distinguish them.",
235
+ ]
236
+ : ["asar", "javascript-bundle"].includes(inventory.manifest.root_format)
237
+ ? [
238
+ "The artifact may contain a recoverable JavaScript application graph.",
239
+ ]
240
+ : [];
241
+ return statements.map((statement) => {
242
+ const semantic = {
243
+ statement,
244
+ confidence: "low",
245
+ basis_evidence_ids: [evidenceId],
246
+ limitation: "Format classification proposes a bounded follow-up; it is not semantic proof.",
247
+ };
248
+ return { hypothesis_id: `aih_${digestCanonical(semantic)}`, ...semantic };
249
+ });
250
+ };
251
+ const contradictions = (inventory, evidenceId) => inventory.integrity_contradictions.map((value) => {
252
+ const semantic = {
253
+ statement: `Declared integrity for ${value.logical_path} contradicts observed bytes.`,
254
+ occurrence_id: value.occurrence_id,
255
+ declared_sha256: value.declared_sha256,
256
+ observed_sha256: value.observed_sha256,
257
+ evidence_id: evidenceId,
258
+ };
259
+ return {
260
+ contradiction_id: `aic_${digestCanonical(semantic)}`,
261
+ ...semantic,
262
+ };
263
+ });
264
+ const probesFor = (inventory) => {
265
+ const format = inventory.manifest.root_format;
266
+ if (["asar", "javascript-bundle", "directory"].includes(format))
267
+ return [
268
+ {
269
+ operation: "analyze_javascript_application",
270
+ rationale: "Recover bounded JavaScript modules, source maps, Electron boundaries, and application relationships.",
271
+ authority: "ast-static-analysis",
272
+ },
273
+ ];
274
+ if (format === "pe")
275
+ return [
276
+ {
277
+ operation: "inspect_managed_artifact",
278
+ rationale: "Check execution-free CLI metadata before choosing a deep native provider.",
279
+ authority: "managed-static-analysis",
280
+ },
281
+ {
282
+ operation: "open_binary",
283
+ rationale: "Open as native code only when managed inspection reports a native or mixed boundary.",
284
+ authority: "native-analysis-provider",
285
+ },
286
+ ];
287
+ if (["mach-o", "mach-o-universal", "elf"].includes(format))
288
+ return [
289
+ {
290
+ operation: "open_binary",
291
+ rationale: "Bind one deep native provider before function-level analysis.",
292
+ authority: "native-analysis-provider",
293
+ },
294
+ ];
295
+ if (format === "plist")
296
+ return [
297
+ {
298
+ operation: "inspect_plist",
299
+ rationale: "Parse property-list values without executing the artifact.",
300
+ authority: "artifact-bytes",
301
+ },
302
+ ];
303
+ return [];
304
+ };
305
+ const allUnexploredBranches = (inventory, evidenceId, nextProbes) => {
306
+ const pages = [
307
+ ["nodes", inventory.nodes.next_offset],
308
+ ["occurrences", inventory.occurrences.next_offset],
309
+ ["relationships", inventory.edges.next_offset],
310
+ ];
311
+ const branches = pages.flatMap(([collection, nextOffset]) => nextOffset === null
312
+ ? []
313
+ : [
314
+ branch("page-limit", `${collection} continue at offset ${String(nextOffset)}.`, null, evidenceId),
315
+ ]);
316
+ branches.push(...nextProbes.map((probe) => branch("format-specific-analysis-required", `${probe.operation} was not run by provider-neutral artifact inspection.`, probe, evidenceId)));
317
+ if (inventory.manifest.root_format === "unknown")
318
+ branches.push(branch("unknown-format", "Artifact bytes did not match an admitted format classifier.", null, evidenceId));
319
+ return branches;
320
+ };
321
+ const branch = (reason, detail, nextProbe, evidenceId) => {
322
+ const semantic = {
323
+ reason,
324
+ detail,
325
+ next_probe: nextProbe,
326
+ evidence_id: evidenceId,
327
+ };
328
+ return { branch_id: `aib_${digestCanonical(semantic)}`, ...semantic };
329
+ };
330
+ const inspectionCoverage = (inventory, omissions) => {
331
+ if (Object.values(omissions).some((count) => count > 0))
332
+ return "truncated";
333
+ return [
334
+ inventory.nodes.next_offset,
335
+ inventory.occurrences.next_offset,
336
+ inventory.edges.next_offset,
337
+ ].some((offset) => offset !== null)
338
+ ? "partial"
339
+ : "complete-within-substeps";
340
+ };
341
+ const digestCanonical = (value) => {
342
+ const encoded = canonicalize(value);
343
+ if (encoded === undefined)
344
+ throw new TypeError("Artifact inspection could not canonicalize");
345
+ return createHash("sha256").update(encoded).digest("hex");
346
+ };
347
+ const uniqueSorted = (values) => [...new Set(values)].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
@@ -0,0 +1,82 @@
1
+ import { z } from "zod";
2
+ import { browserScenarioDiffSchema, compareBrowserScenarios, compareBrowserScenariosInputSchema, } from "./browserScenarioDiff.js";
3
+ import { compareWebCaptures, captureSnapshotSchema, webCaptureDiffSchema, } from "./webCaptureDiff.js";
4
+ /** Top-level MCP shape for legacy page or step-indexed scenario comparison. */
5
+ export const browserCaptureComparisonWireSchema = z
6
+ .strictObject({
7
+ before: captureSnapshotSchema
8
+ .optional()
9
+ .describe("Earlier passive web-page capture; pair with after."),
10
+ after: captureSnapshotSchema
11
+ .optional()
12
+ .describe("Later passive web-page capture; pair with before."),
13
+ before_scenario: compareBrowserScenariosInputSchema.shape.before_scenario
14
+ .optional()
15
+ .describe("Earlier browser scenario capture; pair with after_scenario."),
16
+ after_scenario: compareBrowserScenariosInputSchema.shape.after_scenario
17
+ .optional()
18
+ .describe("Later browser scenario capture; pair with before_scenario."),
19
+ normalization: compareBrowserScenariosInputSchema.shape.normalization.describe("Scenario-only exact-literal normalization policy."),
20
+ max_changes: z
21
+ .number()
22
+ .int()
23
+ .min(1)
24
+ .max(20_000)
25
+ .default(2_000)
26
+ .describe("Maximum normalized change records to retain."),
27
+ })
28
+ .superRefine((input, context) => {
29
+ const hasPassive = input.before !== undefined || input.after !== undefined;
30
+ const hasScenario = input.before_scenario !== undefined || input.after_scenario !== undefined;
31
+ if (hasPassive === hasScenario)
32
+ context.addIssue({
33
+ code: "custom",
34
+ message: "Provide exactly one complete pair: before/after or before_scenario/after_scenario",
35
+ });
36
+ if (hasPassive && (input.before === undefined || input.after === undefined))
37
+ context.addIssue({
38
+ code: "custom",
39
+ message: "Passive comparison requires both before and after",
40
+ });
41
+ if (hasScenario &&
42
+ (input.before_scenario === undefined ||
43
+ input.after_scenario === undefined))
44
+ context.addIssue({
45
+ code: "custom",
46
+ message: "Browser scenario comparison requires both before_scenario and after_scenario",
47
+ });
48
+ if (hasPassive && input.normalization.rules.length > 0)
49
+ context.addIssue({
50
+ code: "custom",
51
+ path: ["normalization"],
52
+ message: "Literal normalization rules apply only to browser scenario captures",
53
+ });
54
+ });
55
+ /** Parsed legacy page snapshots or step-indexed browser scenarios. */
56
+ export const browserCaptureComparisonInputSchema = browserCaptureComparisonWireSchema.transform((input) => {
57
+ if (input.before_scenario !== undefined &&
58
+ input.after_scenario !== undefined)
59
+ return {
60
+ before_scenario: input.before_scenario,
61
+ after_scenario: input.after_scenario,
62
+ normalization: input.normalization,
63
+ max_changes: input.max_changes,
64
+ };
65
+ if (input.before !== undefined && input.after !== undefined)
66
+ return {
67
+ before: input.before,
68
+ after: input.after,
69
+ max_changes: input.max_changes,
70
+ };
71
+ throw new TypeError("Capture comparison pair refinement failed");
72
+ });
73
+ /** Result from passive page or browser scenario comparison. */
74
+ export const browserCaptureComparisonSchema = z.union([
75
+ browserScenarioDiffSchema,
76
+ webCaptureDiffSchema,
77
+ ]);
78
+ /** Dispatch a parsed capture comparison to its pure domain comparator. */
79
+ export const compareBrowserCaptures = (input) => isScenarioComparison(input)
80
+ ? compareBrowserScenarios(input)
81
+ : compareWebCaptures(input);
82
+ const isScenarioComparison = (input) => "before_scenario" in input;