rea-agents 5.0.0 → 6.0.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 (275) hide show
  1. package/README.md +157 -650
  2. package/bridge/mitmproxy/capture.py +210 -0
  3. package/bridge/native/rea_lldb_tracer.py +303 -0
  4. package/bridge/process/ProcessRunTokenReader.swift +263 -0
  5. package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
  6. package/bridge/pwndbg/core_context.py +101 -0
  7. package/bridge/pwndbg/launch.py +39 -0
  8. package/bridge/pwndbg/launch_impl.py +34 -0
  9. package/bridge/pwntools/decoder_runtime.py +59 -0
  10. package/bridge/pwntools/layout.py +39 -0
  11. package/bridge/pwntools/layout_impl.py +309 -0
  12. package/bridge/pwntools/recorded_crash.py +39 -0
  13. package/bridge/pwntools/recorded_crash_impl.py +143 -0
  14. package/dist/android/JadxProvider.js +1 -0
  15. package/dist/application/CapabilityInventory.js +21 -0
  16. package/dist/application/DirectAnalysis.js +97 -23
  17. package/dist/application/EvidenceBundleFiles.js +1 -1
  18. package/dist/application/ReconstructionObligationCandidates.js +5 -1
  19. package/dist/application/WebNetworkCapturePort.js +1 -0
  20. package/dist/application/WebNetworkCaptureService.js +132 -0
  21. package/dist/application/WindowsCapabilities.js +1 -1
  22. package/dist/application/binary/AnalysisSnapshotCache.js +49 -6
  23. package/dist/application/binary/BinarySession.js +38 -10
  24. package/dist/application/binary/BinarySessionOpen.js +16 -3
  25. package/dist/application/binary/BinarySessionRecords.js +20 -0
  26. package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
  27. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +57 -0
  28. package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
  29. package/dist/application/binaryDiagnostics/RecordedCrashService.js +60 -0
  30. package/dist/application/evm/EvmInterfacePort.js +1 -0
  31. package/dist/application/evm/EvmInterfaceService.js +62 -0
  32. package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -2
  33. package/dist/application/javascript/JavaScriptApplicationService.js +15 -2
  34. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +42 -23
  35. package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
  36. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +74 -24
  37. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +23 -10
  38. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
  39. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +145 -70
  40. package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
  41. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
  42. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
  43. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
  44. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
  45. package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
  46. package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
  47. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +5 -5
  48. package/dist/application/{ProcessCli.js → process/ProcessCli.js} +10 -10
  49. package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +4 -4
  50. package/dist/artifacts/ArtifactProvider.js +30 -3
  51. package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
  52. package/dist/artifacts/ZipArtifactReader.js +3 -0
  53. package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
  54. package/dist/artifacts/apple/MachoLoadCommandReader.js +294 -0
  55. package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
  56. package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +3 -3
  57. package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
  58. package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
  59. package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
  60. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +3 -3
  61. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
  62. package/dist/artifacts/readStableArtifact.js +20 -4
  63. package/dist/browser/CdpCaptureEventHandlers.js +90 -2
  64. package/dist/browser/CdpCaptureEvents.js +9 -0
  65. package/dist/browser/PlaywrightElectronActiveProvider.js +12 -3
  66. package/dist/browser/history/CaptureFailures.js +101 -0
  67. package/dist/browser/history/CaptureFormatError.js +10 -0
  68. package/dist/browser/history/CaptureRedaction.js +65 -0
  69. package/dist/browser/history/CaptureRelease.js +12 -0
  70. package/dist/browser/history/HarCapture.js +165 -0
  71. package/dist/browser/history/HarCaptureAdapter.js +20 -0
  72. package/dist/browser/history/HarCaptureProcess.js +57 -0
  73. package/dist/browser/history/HarJson.js +91 -0
  74. package/dist/browser/history/HarSchema.js +72 -0
  75. package/dist/browser/history/HistoricalCaptureDecoder.js +186 -0
  76. package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
  77. package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
  78. package/dist/cli/artifactCommands.js +26 -1
  79. package/dist/cli/binaryDiagnosticsCommands.js +39 -0
  80. package/dist/cli/coreAnalysisCommands.js +2 -2
  81. package/dist/cli/coreBinaryCommands.js +8 -8
  82. package/dist/cli/electronCommands.js +16 -9
  83. package/dist/cli/evmCommands.js +24 -0
  84. package/dist/cli/firmwareCommands.js +6 -3
  85. package/dist/cli/javascriptApplicationAnalysis.js +16 -3
  86. package/dist/cli/jsonOutput.js +133 -0
  87. package/dist/cli/nativeCallCommands.js +35 -0
  88. package/dist/cli/processCommands.js +1 -1
  89. package/dist/cli/streamedJsonOutput.js +56 -0
  90. package/dist/cli/utilityCommands.js +2 -0
  91. package/dist/cli/webNetworkCaptureCommands.js +40 -0
  92. package/dist/cli.js +9 -3
  93. package/dist/cliBrowserScenarioCommands.js +2 -2
  94. package/dist/cliCommandNames.js +6 -0
  95. package/dist/cliJsonInput.js +30 -1
  96. package/dist/cliOutput.js +21 -6
  97. package/dist/composition/binaryDiagnostics.js +8 -0
  98. package/dist/composition/evm.js +4 -0
  99. package/dist/composition/webNetworkCaptures.js +6 -0
  100. package/dist/contracts/artifactToolContracts.js +6 -0
  101. package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
  102. package/dist/contracts/browserToolContracts.js +4 -3
  103. package/dist/contracts/errorSchemas.js +8 -1
  104. package/dist/contracts/evm/evmToolContracts.js +20 -0
  105. package/dist/contracts/investigationExamples.js +2 -2
  106. package/dist/contracts/managed/managedToolContracts.js +7 -3
  107. package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
  108. package/dist/contracts/native/nativeToolContracts.js +17 -1
  109. package/dist/contracts/officialToolContracts.js +12 -9
  110. package/dist/contracts/process/processCaptureExample.js +2 -0
  111. package/dist/contracts/sessionLifecycleInputs.js +22 -5
  112. package/dist/contracts/sessionStatusContract.js +10 -2
  113. package/dist/contracts/sessionToolContracts.js +8 -1
  114. package/dist/contracts/sessionToolSchemas.js +10 -3
  115. package/dist/contracts/toolContractExamples.js +1 -1
  116. package/dist/contracts/toolContracts.js +6 -0
  117. package/dist/contracts/toolEffects.js +30 -0
  118. package/dist/contracts/toolOutputSchemaGroups.js +5 -1
  119. package/dist/contracts/toolOutputSchemaPrimitives.js +1 -1
  120. package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
  121. package/dist/domain/analysisErrorBase.js +15 -0
  122. package/dist/domain/analysisErrorCore.js +59 -5
  123. package/dist/domain/analysisErrorPresentation.js +29 -1
  124. package/dist/domain/analysisErrorProjection.js +52 -2
  125. package/dist/domain/analysisSnapshot.js +166 -2
  126. package/dist/domain/apple/dyldPaths.js +81 -0
  127. package/dist/domain/apple/dylibResolution.js +367 -0
  128. package/dist/domain/apple/dylibResolutionFindings.js +75 -0
  129. package/dist/domain/artifactPathSyntax.js +1 -1
  130. package/dist/domain/browserObservation.js +1 -1
  131. package/dist/domain/browserObservationSchemas.js +13 -0
  132. package/dist/domain/browserScenarioValues.js +9 -2
  133. package/dist/domain/callPathSchemas.js +1 -1
  134. package/dist/domain/canonicalDigest.js +13 -1
  135. package/dist/domain/changedBehavior.js +3 -3
  136. package/dist/domain/comparisonStatus.js +2 -30
  137. package/dist/domain/conformanceTrustGate.js +0 -16
  138. package/dist/domain/evidence.js +17 -5
  139. package/dist/domain/evm/evmInterface.js +63 -0
  140. package/dist/domain/explicitSensitiveFailure.js +92 -0
  141. package/dist/domain/explicitSensitiveValues.js +10 -0
  142. package/dist/domain/firmware/firmwareAnalysis.js +8 -1
  143. package/dist/domain/javascript/electronActiveObservation.js +9 -4
  144. package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
  145. package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
  146. package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
  147. package/dist/domain/javascript/javascriptSemanticGraph.js +21 -5
  148. package/dist/domain/jsonValue.js +4 -0
  149. package/dist/domain/localPath.js +2 -0
  150. package/dist/domain/native/binaryLayout.js +357 -0
  151. package/dist/domain/native/nativeCallObservation.js +164 -0
  152. package/dist/domain/native/objcSwiftMetadata.js +0 -39
  153. package/dist/domain/native/recordedCrash.js +266 -0
  154. package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
  155. package/dist/domain/{processCapture.js → process/processCapture.js} +139 -2
  156. package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
  157. package/dist/domain/process/processCaptureParsing.js +54 -0
  158. package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
  159. package/dist/domain/{processComparison.js → process/processComparison.js} +1 -1
  160. package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
  161. package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
  162. package/dist/domain/providerSelectionError.js +4 -2
  163. package/dist/domain/reconstructionVerification.js +3 -3
  164. package/dist/domain/reconstructionVerificationSchemas.js +1 -1
  165. package/dist/domain/residualUnknown.js +3 -2
  166. package/dist/domain/staticRuntimeCorrelation.js +3 -3
  167. package/dist/domain/webCaptureDiff.js +1 -0
  168. package/dist/domain/webNetworkCapture.js +118 -0
  169. package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
  170. package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
  171. package/dist/dotnet/ManagedMemberInspector.js +4 -2
  172. package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
  173. package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
  174. package/dist/dotnet/ManagedMemberRows.js +14 -3
  175. package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
  176. package/dist/dotnet/ManagedMetadataInventory.js +38 -28
  177. package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
  178. package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
  179. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
  180. package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
  181. package/dist/dotnet/ManagedPeReader.js +11 -0
  182. package/dist/evm/EvmBytecodeCarrier.js +21 -0
  183. package/dist/evm/EvmWorkerLimits.js +40 -0
  184. package/dist/evm/EvmoleFailures.js +126 -0
  185. package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
  186. package/dist/evm/EvmoleInterfaceProvider.js +273 -0
  187. package/dist/evm/EvmoleInterfaceWorker.js +87 -0
  188. package/dist/evm/EvmoleRelease.js +16 -0
  189. package/dist/firmware/FirmwareCommand.js +1 -0
  190. package/dist/generatedPackageMetadata.js +2 -2
  191. package/dist/ghidra/GhidraLauncher.js +1 -0
  192. package/dist/ghidra/GhidraProvider.js +2 -2
  193. package/dist/ghidra/GhidraProviderCapabilities.js +1 -1
  194. package/dist/hopper/BridgeLauncher.js +2 -0
  195. package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
  196. package/dist/javascript/recovery/WakaruCommand.js +3 -0
  197. package/dist/javascript/sourceMaps/SourceMapCommand.js +1 -0
  198. package/dist/native/CommandRunner.js +2 -1
  199. package/dist/native/LldbCallTracer.js +278 -0
  200. package/dist/native/NativeCallObservation.js +135 -0
  201. package/dist/native/NativeMacOSProvider.js +19 -3
  202. package/dist/native/NativeMacOSProviderMetadata.js +8 -3
  203. package/dist/native/pwndbg/PwndbgCoreContext.js +160 -0
  204. package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
  205. package/dist/native/pwntools/PwntoolsFailures.js +180 -0
  206. package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
  207. package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
  208. package/dist/native/pwntools/PwntoolsRelease.js +19 -0
  209. package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
  210. package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
  211. package/dist/native/pwntools/RecordedCrashStructureBindings.js +101 -0
  212. package/dist/process/DarwinProcessRunTokenReader.js +294 -0
  213. package/dist/process/OwnedCommand.js +113 -0
  214. package/dist/process/ProcessOwnership.js +263 -33
  215. package/dist/process/ProcessOwnershipObservation.js +161 -24
  216. package/dist/process/ProviderProcess.js +30 -3
  217. package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
  218. package/dist/{application → process/capture}/ProcessCaptureError.js +17 -1
  219. package/dist/process/capture/ProcessCaptureLifecycle.js +615 -0
  220. package/dist/{application → process/capture}/ProcessHarness.js +195 -29
  221. package/dist/{application → process/capture}/ProcessSampling.js +2 -2
  222. package/dist/server/createServer.js +22 -0
  223. package/dist/server/promptCompletion.js +10 -3
  224. package/dist/server/registerBinaryDiagnosticsTools.js +17 -0
  225. package/dist/server/registerEvidenceTools.js +1 -1
  226. package/dist/server/registerEvmTools.js +17 -0
  227. package/dist/server/registerProcessComparisonTool.js +1 -1
  228. package/dist/server/registerRecordedCrashTools.js +17 -0
  229. package/dist/server/registerSessionTools.js +2 -2
  230. package/dist/server/registerWebNetworkCaptureTool.js +28 -0
  231. package/dist/server/sessionAvailabilityPolicy.js +3 -0
  232. package/dist/server/sessionToolPolicies.js +1 -1
  233. package/dist/server/toolResult.js +1 -1
  234. package/native/windows/build/manifest.json +2 -2
  235. package/native/windows/build/rea-windows-x64.node +0 -0
  236. package/package.json +37 -9
  237. package/scripts/rea.mjs +11 -3
  238. package/skills/reverse-engineer-anything/SKILL.md +23 -3
  239. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
  240. package/third_party/README.md +60 -0
  241. package/third_party/evmole/LICENSE +21 -0
  242. package/third_party/evmole/README.md +22 -0
  243. package/third_party/pwndbg/LICENSE.md +21 -0
  244. package/third_party/pwndbg/README.md +23 -0
  245. package/third_party/pwntools/README.md +40 -0
  246. package/dist/application/AuthorizedArtifactInventory.js +0 -7
  247. package/dist/application/ProcessCaptureLifecycle.js +0 -299
  248. package/dist/application/StoreFileLock.js +0 -124
  249. package/dist/contracts/processCaptureExample.js +0 -2
  250. package/dist/domain/bytecodeProvider.js +0 -178
  251. package/dist/domain/customProtocolCapture.js +0 -137
  252. package/dist/domain/eventProcessTree.js +0 -225
  253. package/dist/domain/mobileApplicationInvestigation.js +0 -159
  254. package/dist/domain/packageAnalysis.js +0 -123
  255. package/dist/domain/peInspection.js +0 -197
  256. package/dist/domain/processCaptureParsing.js +0 -21
  257. package/dist/domain/protocolCapture.js +0 -217
  258. package/dist/domain/runtimeIdentification.js +0 -212
  259. package/dist/generatedMcpToolCatalog.js +0 -37
  260. /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
  261. /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
  262. /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
  263. /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
  264. /package/dist/domain/{processScenario.js → process/processScenario.js} +0 -0
  265. /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
  266. /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
  267. /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
  268. /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
  269. /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
  270. /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
  271. /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
  272. /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
  273. /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
  274. /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
  275. /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
@@ -11,6 +11,7 @@ import { access } from "node:fs/promises";
11
11
  import { readAnalysisSnapshot, writeAnalysisSnapshot, } from "./binary/AnalysisSnapshotFiles.js";
12
12
  import { parseBinaryTarget } from "./BinaryTargetResolver.js";
13
13
  import { snapshotEvidenceForQuery, snapshotMatchesTarget, } from "../domain/analysisSnapshot.js";
14
+ import { analysisProfileSchema, committedProviderSchema, } from "../domain/analysisProfile.js";
14
15
  import { err, ok } from "../domain/result.js";
15
16
  import { REA_WORKFLOW_PROVIDER, workflowAnalysisProfile, } from "./InvestigationProviders.js";
16
17
  import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
@@ -82,7 +83,42 @@ const runAnalysis = async (dependencies, path, tool, arguments_, options) => {
82
83
  if (!prepared.ok)
83
84
  return cliError(prepared.error);
84
85
  const { snapshot } = prepared.value;
85
- const opened = await session.open(path, {
86
+ let resolvedTarget;
87
+ if (snapshot !== undefined && prepared.value.target !== undefined) {
88
+ const preview = await session.previewTarget(prepared.value.target, {
89
+ signal,
90
+ snapshot,
91
+ ...(options.formatHint === undefined
92
+ ? {}
93
+ : { formatHint: options.formatHint }),
94
+ ...(options.providerId === undefined
95
+ ? {}
96
+ : { providerId: options.providerId }),
97
+ });
98
+ if (!preview.ok)
99
+ return cliError(preview.error);
100
+ resolvedTarget = preview.value;
101
+ const route = preview.value.route;
102
+ const bindingProfile = route.profile ?? undefined;
103
+ const evidenceProfile = analysisProfileForRoute(route, tool);
104
+ if (bindingProfile !== undefined &&
105
+ evidenceProfile !== undefined &&
106
+ allowsSnapshotReplay(route, tool)) {
107
+ const cached = snapshotEvidenceForQuery(snapshot, {
108
+ target: preview.value.target,
109
+ bindingProfile,
110
+ operation: tool,
111
+ parameters: arguments_,
112
+ provider: isWorkflowEvidenceTool(tool)
113
+ ? REA_WORKFLOW_PROVIDER
114
+ : providerIdentityForRoute(route, tool),
115
+ evidenceProfile,
116
+ });
117
+ if (cached !== undefined)
118
+ return cached;
119
+ }
120
+ }
121
+ const openOptions = {
86
122
  signal,
87
123
  ...(options.formatHint === undefined
88
124
  ? {}
@@ -91,26 +127,13 @@ const runAnalysis = async (dependencies, path, tool, arguments_, options) => {
91
127
  ...(options.providerId === undefined
92
128
  ? {}
93
129
  : { providerId: options.providerId }),
94
- });
130
+ };
131
+ const opened = resolvedTarget === undefined
132
+ ? await session.open(path, openOptions)
133
+ : await session.openResolvedTarget(resolvedTarget, openOptions);
95
134
  if (!opened.ok)
96
135
  return cliError(opened.error);
97
136
  const evidenceProfile = analysisProfileForEvidence(session, tool);
98
- const bindingProfile = session.analysisProfile();
99
- if (canReplayDirectTool(session, tool) &&
100
- snapshot !== undefined &&
101
- evidenceProfile !== undefined &&
102
- bindingProfile !== undefined) {
103
- const cached = snapshotEvidenceForQuery(snapshot, {
104
- target: opened.value,
105
- bindingProfile,
106
- operation: tool,
107
- parameters: arguments_,
108
- provider: replayProviderFor(session, tool),
109
- evidenceProfile,
110
- });
111
- if (cached !== undefined)
112
- return cached;
113
- }
114
137
  const { output, evidence } = await executeAnalysisTool({
115
138
  session,
116
139
  openedTarget: opened.value,
@@ -121,6 +144,37 @@ const runAnalysis = async (dependencies, path, tool, arguments_, options) => {
121
144
  });
122
145
  if (evidence !== undefined)
123
146
  session.recordEvidence(evidence);
147
+ if (isWorkflowEvidenceTool(tool) &&
148
+ tool !== "trace_native_ui_action" &&
149
+ snapshotPath !== undefined &&
150
+ evidence !== undefined &&
151
+ "analysis_profile" in evidence &&
152
+ session.allowsSnapshotReplay(tool)) {
153
+ const recorded = session.recordWorkflowSnapshot({
154
+ operation: tool,
155
+ parameters: arguments_,
156
+ execution: {
157
+ result: evidence.normalized_result,
158
+ rawResult: evidence.raw_result,
159
+ provider: committedProviderSchema.parse(evidence.provider),
160
+ analysisProfile: analysisProfileSchema.parse(evidence.analysis_profile),
161
+ limitations: evidence.limitations,
162
+ locations: evidence.locations,
163
+ subject: evidence.subject === null
164
+ ? null
165
+ : {
166
+ path: evidence.subject.local_path,
167
+ sha256: evidence.subject.digest.sha256,
168
+ format: evidence.subject.format,
169
+ ...(evidence.subject.architecture === null
170
+ ? {}
171
+ : { architecture: evidence.subject.architecture }),
172
+ },
173
+ },
174
+ });
175
+ if (!recorded.ok)
176
+ return cliError(recorded.error);
177
+ }
124
178
  if (tool !== "trace_native_ui_action" &&
125
179
  snapshotPath !== undefined &&
126
180
  evidence !== undefined) {
@@ -137,7 +191,6 @@ const runAnalysis = async (dependencies, path, tool, arguments_, options) => {
137
191
  await session.close();
138
192
  }
139
193
  };
140
- const canReplayDirectTool = (session, tool) => tool !== "trace_native_ui_action" && session.allowsSnapshotReplay(tool);
141
194
  const executeAnalysisTool = async (input) => {
142
195
  const { session, tool, signal, evidenceProfile } = input;
143
196
  if (tool === "analyze_function") {
@@ -212,11 +265,32 @@ const prepareSnapshot = async (options) => {
212
265
  return target;
213
266
  if (!snapshotMatchesTarget(loaded.value.target, target.value))
214
267
  return err(new EvidenceIntegrityError("Analysis snapshot target does not match the requested binary"));
215
- return ok({ snapshot: loaded.value });
268
+ return ok({ snapshot: loaded.value, target: target.value });
269
+ };
270
+ const allowsSnapshotReplay = (route, tool) => {
271
+ if (tool === "trace_native_ui_action")
272
+ return false;
273
+ const descriptor = route.capabilities?.get(tool);
274
+ return descriptor === undefined
275
+ ? ![...(route.capabilities?.values() ?? [])].some(({ cachePolicy }) => cachePolicy === "live")
276
+ : descriptor.cachePolicy !== "live";
277
+ };
278
+ const analysisProfileForRoute = (route, tool) => {
279
+ const profile = route.profile;
280
+ if (profile === null || profile === undefined)
281
+ return undefined;
282
+ if (isWorkflowEvidenceTool(tool))
283
+ return workflowAnalysisProfile(profile);
284
+ const provider = providerIdentityForRoute(route, tool);
285
+ return provider.id === profile.provider.id ? profile : undefined;
286
+ };
287
+ /** Mirror BinarySession.providerIdentity for a route not opened yet. */
288
+ const providerIdentityForRoute = (route, operation) => {
289
+ const selected = route.capabilities?.get(operation)?.provider ?? route.identity;
290
+ return route.profile?.provider.id === selected.id
291
+ ? route.profile.provider
292
+ : selected;
216
293
  };
217
- const replayProviderFor = (session, tool) => isWorkflowEvidenceTool(tool)
218
- ? REA_WORKFLOW_PROVIDER
219
- : session.providerIdentity(tool);
220
294
  const analysisProfileForEvidence = (session, tool) => {
221
295
  if (!isWorkflowEvidenceTool(tool))
222
296
  return session.analysisProfile(tool);
@@ -1,7 +1,7 @@
1
1
  import { parseEvidenceBundle, serializeEvidenceBundle, } from "../domain/evidenceBundle.js";
2
2
  import { EvidenceFileError, EvidenceIntegrityError, } from "../domain/evidenceErrors.js";
3
3
  import { err, ok } from "../domain/result.js";
4
- import { parseProcessCapture } from "../domain/processCapture.js";
4
+ import { parseProcessCapture } from "../domain/process/processCapture.js";
5
5
  import { readJsonFile, writeTextFile } from "./JsonFiles.js";
6
6
  /** Read and validate an evidence bundle at the caller-supplied path. */
7
7
  export const readEvidenceBundle = async (path) => {
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import canonicalize from "canonicalize";
3
3
  import { parseEvidenceBundle, } from "../domain/evidenceBundle.js";
4
- import { parseProcessCapture } from "../domain/processCapture.js";
4
+ import { parseProcessCapture } from "../domain/process/processCapture.js";
5
5
  import { parseApplicationGraphEvidence } from "./javascript/JavaScriptApplicationEvidenceGraph.js";
6
6
  import { applicationObligationPolicy, semanticObligationPolicy, } from "./ReconstructionObligationPolicies.js";
7
7
  /** Derive conservative candidates from graph, process, and reviewed inputs. */
@@ -101,6 +101,10 @@ const deriveProcessCandidate = (evidence, candidates, limitations) => {
101
101
  ]
102
102
  : []),
103
103
  ];
104
+ if (capture.manifest.executable_sha256 === null) {
105
+ limitations.add(`Process capture ${evidence.evidence_id} has no validated executable artifact identity; packaged-process lifecycle obligation was not bound to an artifact.`);
106
+ return;
107
+ }
104
108
  addCandidate(candidates, {
105
109
  obligation_id: obligationId(`process:${capture.manifest.executable_sha256}:${capture.manifest.full_scenario_sha256}`),
106
110
  obligation_version: 1,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,132 @@
1
+ import { redactExplicitFailure } from "../domain/explicitSensitiveFailure.js";
2
+ import { excludeCaptureCoordinates } from "../domain/webNetworkCaptureCoordinates.js";
3
+ import { redactExplicitText } from "../domain/explicitSensitiveValues.js";
4
+ import { isAbsolute } from "node:path";
5
+ import { AnalysisCancelledError, AnalysisInputError, AnalysisOutputError, } from "../domain/analysisErrorCore.js";
6
+ import { createEvidence } from "../domain/evidence.js";
7
+ import { projectInputIssues } from "../domain/inputIssueProjection.js";
8
+ import { jsonObjectSchema } from "../domain/jsonValue.js";
9
+ import { err, ok } from "../domain/result.js";
10
+ import { inspectWebNetworkCaptureInputSchema, webNetworkCaptureSchema, } from "../domain/webNetworkCapture.js";
11
+ const OPERATION = "inspect_web_network_capture";
12
+ /** Shared CLI/MCP historical inspection preserves caller selection and producer record identity. */
13
+ export class WebNetworkCaptureService {
14
+ port;
15
+ constructor(port) {
16
+ this.port = port;
17
+ }
18
+ /** Return selected complete records inline, without inventing live browser attribution. */
19
+ async inspect(rawInput, options) {
20
+ if (options?.signal?.aborted)
21
+ return err(new AnalysisCancelledError(OPERATION));
22
+ const declarations = inspectWebNetworkCaptureInputSchema.shape.sensitive_values.safeParse(typeof rawInput === "object" &&
23
+ rawInput !== null &&
24
+ "sensitive_values" in rawInput
25
+ ? rawInput.sensitive_values
26
+ : undefined);
27
+ const failure = (error) => err(redactExplicitFailure(error, declarations.success ? declarations.data : []));
28
+ const parsed = inspectWebNetworkCaptureInputSchema.safeParse(rawInput);
29
+ if (!parsed.success)
30
+ return failure(new AnalysisInputError(OPERATION, { cause: parsed.error }, projectInputIssues(parsed.error.issues, rawInput)));
31
+ const input = parsed.data;
32
+ if (!isAbsolute(input.capture_path))
33
+ return failure(new AnalysisInputError(OPERATION, undefined, [
34
+ {
35
+ path: ["capture_path"],
36
+ reason: "invalid_format",
37
+ message: "Expected an absolute filesystem path on this host.",
38
+ },
39
+ ]));
40
+ if (input.record_ordinals !== undefined &&
41
+ new Set(input.record_ordinals).size !== input.record_ordinals.length)
42
+ return failure(new AnalysisInputError(OPERATION, undefined, [
43
+ {
44
+ path: ["record_ordinals"],
45
+ reason: "invalid_format",
46
+ message: "Each requested producer ordinal must occur once.",
47
+ },
48
+ ]));
49
+ const loaded = await this.port.inspect(input, options);
50
+ if (!loaded.ok)
51
+ return failure(loaded.error);
52
+ if (options?.signal?.aborted)
53
+ return failure(new AnalysisCancelledError(OPERATION));
54
+ const report = webNetworkCaptureSchema.safeParse(loaded.value);
55
+ if (!report.success)
56
+ return failure(new AnalysisOutputError(OPERATION, "Capture adapter returned malformed historical evidence."));
57
+ const value = report.data;
58
+ const artifactSha256 = value.artifact.sha256;
59
+ if (artifactSha256 === null)
60
+ return failure(new AnalysisOutputError(OPERATION, "Capture adapter omitted its observed artifact digest before explicit projection."));
61
+ if (value.artifact.path !== input.capture_path ||
62
+ value.format !== input.format ||
63
+ value.total_records !== value.records.length ||
64
+ value.records.some((record, index) => record.ordinal !== index))
65
+ return failure(new AnalysisOutputError(OPERATION, "Capture adapter changed the selected artifact, format or original record sequence."));
66
+ const safeValue = excludeCaptureCoordinates(value, input.sensitive_values);
67
+ const redactText = (text) => redactExplicitText(text, input.sensitive_values);
68
+ const selected = input.record_ordinals ?? value.records.map(({ ordinal }) => ordinal);
69
+ const records = [];
70
+ for (const ordinal of selected) {
71
+ const record = safeValue.records[ordinal];
72
+ if (record === undefined)
73
+ return failure(new AnalysisInputError(OPERATION, undefined, [
74
+ {
75
+ path: ["record_ordinals"],
76
+ reason: "invalid_format",
77
+ message: `Producer ordinal ${ordinal} is outside the retained capture (${value.total_records} records).`,
78
+ },
79
+ ]));
80
+ records.push({
81
+ ...record,
82
+ limitations: record.limitations.map(redactText),
83
+ });
84
+ }
85
+ const safePath = input.sensitive_values.some((literal) => input.capture_path.includes(literal))
86
+ ? ""
87
+ : input.capture_path;
88
+ const safeSha256 = input.sensitive_values.some((literal) => artifactSha256.includes(literal))
89
+ ? null
90
+ : artifactSha256;
91
+ const subjectUnavailableReason = redactText("The explicitly sensitive artifact digest is excluded; its cryptographic identity is unknown in this Evidence.");
92
+ const limitations = [
93
+ ...safeValue.limitations,
94
+ ...(safePath === ""
95
+ ? [
96
+ "The explicitly sensitive artifact path is excluded; its size remains available.",
97
+ ]
98
+ : []),
99
+ ].map(redactText);
100
+ const parameters = jsonObjectSchema.parse({
101
+ capture_path: safePath,
102
+ ...(input.sensitive_values.some((literal) => input.format.includes(literal))
103
+ ? {}
104
+ : { format: input.format }),
105
+ record_ordinals: selected,
106
+ sensitive_value_count: input.sensitive_values.length,
107
+ });
108
+ return ok(createEvidence(safeSha256 === null
109
+ ? undefined
110
+ : {
111
+ path: safePath,
112
+ sha256: safeSha256,
113
+ format: "file",
114
+ }, value.decoder, {
115
+ operation: OPERATION,
116
+ parameters,
117
+ result: {
118
+ ...safeValue,
119
+ artifact: { ...value.artifact, path: safePath, sha256: safeSha256 },
120
+ records,
121
+ limitations: safeSha256 === null
122
+ ? [...limitations, subjectUnavailableReason]
123
+ : limitations,
124
+ },
125
+ confidence: "observed",
126
+ authority: "historical-reference",
127
+ limitations,
128
+ ...(safeSha256 === null ? { subjectUnavailableReason } : {}),
129
+ locations: safePath === "" ? [] : [{ kind: "artifact-path", path: safePath }],
130
+ }));
131
+ }
132
+ }
@@ -3,7 +3,7 @@ import { mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { windowsNativeCapabilities, } from "../process/WindowsAuthority.js";
6
- import { probeProcessCaptureCapability, } from "./ProcessCaptureCapability.js";
6
+ import { probeProcessCaptureCapability, } from "../process/capture/ProcessCaptureCapability.js";
7
7
  /** Probe named prerequisites without converting missing controls into support. */
8
8
  export const probeWindowsCapabilities = async (dependencies = systemDependencies()) => {
9
9
  const [symlinkCreation, pty] = await Promise.all([
@@ -2,7 +2,7 @@ import { EvidenceIntegrityError } from "../../domain/evidenceErrors.js";
2
2
  import { NoBinaryOpenError } from "../../domain/configurationErrors.js";
3
3
  import {} from "../../domain/analysisErrorBase.js";
4
4
  import { err, ok } from "../../domain/result.js";
5
- import { analysisQueryId, createAnalysisSnapshotEntry, snapshotBinding, snapshotMatchesProfile, snapshotMatchesTarget, snapshotTarget, parseAnalysisSnapshot, } from "../../domain/analysisSnapshot.js";
5
+ import { analysisQueryId, createAnalysisSnapshotEntry, createAnalysisSnapshotWorkflowEntry, snapshotBinding, snapshotMatchesProfile, snapshotMatchesTarget, snapshotTarget, parseAnalysisSnapshot, } from "../../domain/analysisSnapshot.js";
6
6
  import { OFFICIAL_TOOL_CONTRACTS } from "../../contracts/officialToolContracts.js";
7
7
  const STATEFUL_OPERATIONS = new Set([
8
8
  "health",
@@ -24,6 +24,8 @@ const CURSOR_DEFAULT_OPERATIONS = new Set([
24
24
  const DOCUMENT_SCOPED_OPERATIONS = new Set(OFFICIAL_TOOL_CONTRACTS.map(({ name }) => name).filter((name) => name !== "current_document" &&
25
25
  name !== "list_documents" &&
26
26
  name !== "set_current_document"));
27
+ /** Maximum duplicated result bindings retained across direct and workflow queries. */
28
+ export const SNAPSHOT_CACHE_ENTRY_CEILING = 10_000;
27
29
  /** Whether an operation is immutable and independent of provider UI state. */
28
30
  export const isSnapshotCacheable = (operation, descriptor, parameters) => !STATEFUL_OPERATIONS.has(operation) &&
29
31
  (!DOCUMENT_SCOPED_OPERATIONS.has(operation) ||
@@ -37,6 +39,7 @@ export const isSnapshotCacheable = (operation, descriptor, parameters) => !STATE
37
39
  /** Bounded in-memory cache for one immutable binary identity. */
38
40
  export class AnalysisSnapshotCache {
39
41
  #entries = new Map();
42
+ #workflowEntries = new Map();
40
43
  #target;
41
44
  #binding;
42
45
  /** Whether staged entries belong to the supplied target and profile. */
@@ -51,7 +54,7 @@ export class AnalysisSnapshotCache {
51
54
  /** Replace cache state when the target or selected profile changes. */
52
55
  select(target, profile) {
53
56
  if (!this.matches(target, profile))
54
- this.#entries.clear();
57
+ this.#clearEntries();
55
58
  this.#target = snapshotTarget(target);
56
59
  this.#binding = snapshotBinding(profile);
57
60
  }
@@ -61,15 +64,22 @@ export class AnalysisSnapshotCache {
61
64
  JSON.stringify(this.#target) !== JSON.stringify(snapshot.target)) ||
62
65
  (this.#binding !== undefined &&
63
66
  !snapshotMatchesProfile(this.#binding, snapshot.binding.analysis_profile)))
64
- this.#entries.clear();
67
+ this.#clearEntries();
65
68
  this.#target = structuredClone(snapshot.target);
66
69
  this.#binding = structuredClone(snapshot.binding);
67
70
  let imported = 0;
68
71
  for (const entry of snapshot.entries) {
72
+ if (!this.#entries.has(entry.query_id) && !this.#hasCapacity())
73
+ continue;
69
74
  if (!this.#entries.has(entry.query_id))
70
75
  imported += 1;
71
76
  this.#entries.set(entry.query_id, structuredClone(entry));
72
77
  }
78
+ for (const entry of snapshot.workflow_entries ?? []) {
79
+ if (!this.#workflowEntries.has(entry.query_id) && !this.#hasCapacity())
80
+ continue;
81
+ this.#workflowEntries.set(entry.query_id, structuredClone(entry));
82
+ }
73
83
  return imported;
74
84
  }
75
85
  /** Build a complete snapshot for an active target. */
@@ -84,6 +94,9 @@ export class AnalysisSnapshotCache {
84
94
  target: snapshotTargetIdentity,
85
95
  binding,
86
96
  entries: this.entries(),
97
+ ...(this.workflowEntries().length === 0
98
+ ? {}
99
+ : { workflow_entries: this.workflowEntries() }),
87
100
  evidence_bundle: retainedEvidence,
88
101
  }));
89
102
  }
@@ -117,6 +130,27 @@ export class AnalysisSnapshotCache {
117
130
  .sort((left, right) => left.query_id.localeCompare(right.query_id))
118
131
  .map((entry) => structuredClone(entry));
119
132
  }
133
+ /** Return canonical composed-workflow entries for persistence. */
134
+ workflowEntries() {
135
+ return [...this.#workflowEntries.values()]
136
+ .sort((left, right) => left.query_id.localeCompare(right.query_id))
137
+ .map((entry) => structuredClone(entry));
138
+ }
139
+ /** Record one exact derived workflow result unless the shared cache is full. */
140
+ recordWorkflow(input) {
141
+ this.select(input.target, input.profile);
142
+ const queryId = analysisQueryId(snapshotTarget(input.target), snapshotBinding(input.profile), input.operation, input.parameters);
143
+ if (!this.#workflowEntries.has(queryId) && !this.#hasCapacity())
144
+ return;
145
+ const entry = createAnalysisSnapshotWorkflowEntry({
146
+ target: snapshotTarget(input.target),
147
+ binding: snapshotBinding(input.profile),
148
+ operation: input.operation,
149
+ parameters: input.parameters,
150
+ execution: input.execution,
151
+ });
152
+ this.#workflowEntries.set(entry.query_id, entry);
153
+ }
120
154
  /** Replay an exact provider-specific query, marking its cached provenance. */
121
155
  lookup(target, profile, operation, parameters) {
122
156
  const queryId = analysisQueryId(snapshotTarget(target), snapshotBinding(profile), operation, parameters);
@@ -153,9 +187,10 @@ export class AnalysisSnapshotCache {
153
187
  /** Record one successful immutable call unless the cache is full. */
154
188
  record(input) {
155
189
  const { target, profile, operation, parameters, execution } = input;
156
- if (this.#entries.size >= 10_000)
157
- return;
158
190
  this.select(target, profile);
191
+ const queryId = analysisQueryId(snapshotTarget(target), snapshotBinding(profile), operation, parameters);
192
+ if (!this.#entries.has(queryId) && !this.#hasCapacity())
193
+ return;
159
194
  const entry = createAnalysisSnapshotEntry({
160
195
  target: snapshotTarget(target),
161
196
  binding: snapshotBinding(profile),
@@ -167,8 +202,16 @@ export class AnalysisSnapshotCache {
167
202
  }
168
203
  /** Forget all target-bound entries. */
169
204
  clear() {
170
- this.#entries.clear();
205
+ this.#clearEntries();
171
206
  this.#target = undefined;
172
207
  this.#binding = undefined;
173
208
  }
209
+ #clearEntries() {
210
+ this.#entries.clear();
211
+ this.#workflowEntries.clear();
212
+ }
213
+ #hasCapacity() {
214
+ return (this.#entries.size + this.#workflowEntries.size <
215
+ SNAPSHOT_CACHE_ENTRY_CEILING);
216
+ }
174
217
  }
@@ -10,7 +10,7 @@ import { SessionProviderRouter, } from "./SessionProviderRouter.js";
10
10
  import { InvestigationRecords } from "../investigation/InvestigationRecords.js";
11
11
  import { BinarySessionRecords } from "./BinarySessionRecords.js";
12
12
  import { binarySessionStatus } from "./BinarySessionStatus.js";
13
- import { resolveSessionOpen, } from "./BinarySessionOpen.js";
13
+ import { resolveSessionOpen, resolveSessionTarget, validateResolvedSessionTarget, } from "./BinarySessionOpen.js";
14
14
  import { bindExecutionTarget, commitExecutionProfile, prepareSessionExecution, } from "./BinarySessionExecution.js";
15
15
  import { closeAnalysisClient } from "./AnalysisClientCleanup.js";
16
16
  const OFFICIAL_OPERATIONS = new Set(OFFICIAL_TOOL_CONTRACTS.map(({ name }) => name));
@@ -88,21 +88,47 @@ export class BinarySession extends BinarySessionRecords {
88
88
  this.#availabilityListeners.add(listener);
89
89
  return () => this.#availabilityListeners.delete(listener);
90
90
  }
91
+ /** Resolve a target and provider profile without creating a provider client. */
92
+ previewTarget(target, options = {}) {
93
+ if (isAborted(options.signal))
94
+ return Promise.resolve(err(new AnalysisCancelledError("open_binary")));
95
+ return resolveSessionTarget({
96
+ router: this.#providerRouter,
97
+ current: this.#active,
98
+ target,
99
+ options,
100
+ stagedSnapshotMatches: (target, profile) => this.matchesSnapshot(target, profile),
101
+ }).then((resolved) => isAborted(options.signal)
102
+ ? err(new AnalysisCancelledError("open_binary"))
103
+ : resolved);
104
+ }
91
105
  /**
92
106
  * Open or switch targets after draining calls against the current target.
93
107
  * Returns the switch failure even if best-effort restoration also fails.
94
108
  */
95
109
  open(path, options = {}) {
110
+ return this.#open(() => resolveSessionOpen({
111
+ router: this.#providerRouter,
112
+ current: this.#active,
113
+ path,
114
+ options,
115
+ stagedSnapshotMatches: (target, profile) => this.matchesSnapshot(target, profile),
116
+ }), options);
117
+ }
118
+ /** Open a previewed target without repeating parsing or provider profile discovery. */
119
+ openResolvedTarget(resolved, options = {}) {
120
+ return this.#open(() => Promise.resolve(validateResolvedSessionTarget({
121
+ ...resolved,
122
+ current: this.#active,
123
+ options,
124
+ stagedSnapshotMatches: (target, profile) => this.matchesSnapshot(target, profile),
125
+ })), options);
126
+ }
127
+ #open(resolve, options) {
96
128
  return this.#serialize(async () => {
97
129
  if (isAborted(options.signal))
98
130
  return err(new AnalysisCancelledError("open_binary"));
99
- const resolved = await resolveSessionOpen({
100
- router: this.#providerRouter,
101
- current: this.#active,
102
- path,
103
- options,
104
- stagedSnapshotMatches: (target, profile) => this.matchesSnapshot(target, profile),
105
- });
131
+ const resolved = await resolve();
106
132
  if (!resolved.ok)
107
133
  return resolved;
108
134
  const { target, route, sameTarget } = resolved.value;
@@ -232,7 +258,8 @@ export class BinarySession extends BinarySessionRecords {
232
258
  unboundOperationError: (operation, route) => this.#providerRouter.unboundOperationError(operation, route),
233
259
  lookupSnapshot: (target, profile, operation, parameters) => operation === "decode_interface_builder" ||
234
260
  operation === "inspect_asset_catalog" ||
235
- operation === "inspect_keyed_archive"
261
+ operation === "inspect_keyed_archive" ||
262
+ operation === "trace_dylib_resolution"
236
263
  ? undefined
237
264
  : this.lookupSnapshot(target, profile, operation, parameters),
238
265
  });
@@ -252,7 +279,8 @@ export class BinarySession extends BinarySessionRecords {
252
279
  profile !== undefined &&
253
280
  name !== "decode_interface_builder" &&
254
281
  name !== "inspect_asset_catalog" &&
255
- name !== "inspect_keyed_archive") {
282
+ name !== "inspect_keyed_archive" &&
283
+ name !== "trace_dylib_resolution") {
256
284
  const evidence = createEvidence(profiled.value.subject ?? active.target, profiled.value.provider, {
257
285
  operation: name,
258
286
  parameters: arguments_,
@@ -7,11 +7,15 @@ import { err } from "../../domain/result.js";
7
7
  import { SessionProviderRouter } from "./SessionProviderRouter.js";
8
8
  /** Parse a target, resolve its provider route, and validate snapshot binding. */
9
9
  export const resolveSessionOpen = async (input) => {
10
- const { router, current, path, options, stagedSnapshotMatches } = input;
10
+ const { path, options } = input;
11
11
  const parsed = await parseBinaryTarget(path, process.cwd(), process.arch, options.targetKind, options.formatHint);
12
12
  if (!parsed.ok)
13
13
  return parsed;
14
- const target = parsed.value;
14
+ return resolveSessionTarget({ ...input, target: parsed.value });
15
+ };
16
+ /** Resolve a known target's provider route and validate snapshot binding. */
17
+ export const resolveSessionTarget = async (input) => {
18
+ const { router, current, target, options } = input;
15
19
  const sameTarget = current?.target.path === target.path &&
16
20
  snapshotMatchesTarget(snapshotTarget(current.target), target);
17
21
  const resolvedRoute = sameTarget && options.providerId === undefined && current !== undefined
@@ -19,7 +23,16 @@ export const resolveSessionOpen = async (input) => {
19
23
  : await router.resolve(target, options.providerId, options.signal);
20
24
  if (!resolvedRoute.ok)
21
25
  return resolvedRoute;
22
- const route = resolvedRoute.value;
26
+ return validateResolvedSessionTarget({
27
+ ...input,
28
+ route: resolvedRoute.value,
29
+ });
30
+ };
31
+ /** Revalidate a previewed route against the current session and snapshot binding. */
32
+ export const validateResolvedSessionTarget = (input) => {
33
+ const { current, target, route, options, stagedSnapshotMatches } = input;
34
+ const sameTarget = current?.target.path === target.path &&
35
+ snapshotMatchesTarget(snapshotTarget(current.target), target);
23
36
  const snapshotError = validateSnapshot({
24
37
  snapshot: options.snapshot,
25
38
  target,
@@ -84,6 +84,26 @@ export class BinarySessionRecords {
84
84
  this.#snapshot.record(input);
85
85
  this.#emitSnapshotChanged();
86
86
  }
87
+ /** Retain one derived workflow result alongside its provider cache entries. */
88
+ recordWorkflowSnapshot(input) {
89
+ const active = this.activeAnalysisBinding();
90
+ if (active === undefined || active.profile === null)
91
+ return err(new EvidenceIntegrityError("Workflow snapshot entries require an active concrete provider profile"));
92
+ try {
93
+ this.#snapshot.recordWorkflow({
94
+ target: active.target,
95
+ profile: active.profile,
96
+ ...input,
97
+ });
98
+ this.#emitSnapshotChanged();
99
+ return ok(null);
100
+ }
101
+ catch (cause) {
102
+ return err(new EvidenceIntegrityError(cause instanceof Error
103
+ ? cause.message
104
+ : "Workflow snapshot entry validation failed", { cause }));
105
+ }
106
+ }
87
107
  invalidateSnapshot() {
88
108
  this.#snapshot.clear();
89
109
  this.#snapshotInvalidated = true;