rea-agents 2.1.0 → 2.3.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 (193) hide show
  1. package/README.md +84 -18
  2. package/dist/application/AndroidApplicationService.js +17 -0
  3. package/dist/application/AppleApplicationService.js +17 -0
  4. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  5. package/dist/application/BinarySession.js +9 -2
  6. package/dist/application/BinarySessionStatus.js +39 -1
  7. package/dist/application/CapabilityInventory.js +15 -0
  8. package/dist/application/ClientRegistrationStatus.js +11 -4
  9. package/dist/application/CommandShimReplay.js +6 -3
  10. package/dist/application/CompositeProvider.js +5 -2
  11. package/dist/application/DirectAnalysis.js +1 -20
  12. package/dist/application/DirectAnalysisStatus.js +83 -0
  13. package/dist/application/Doctor.js +34 -25
  14. package/dist/application/DoctorDiagnostics.js +1 -4
  15. package/dist/application/DoctorProjection.js +43 -0
  16. package/dist/application/DoctorScope.js +102 -0
  17. package/dist/application/EvidenceReferenceResolver.js +26 -0
  18. package/dist/application/InventoryProjectionEvidence.js +41 -0
  19. package/dist/application/InvestigationProviders.js +12 -0
  20. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  21. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  22. package/dist/application/JavaScriptApplicationService.js +3 -2
  23. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  24. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  25. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  26. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  27. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  28. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  29. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  30. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  31. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  32. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  33. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  34. package/dist/application/LoopbackReplay.js +277 -133
  35. package/dist/application/LoopbackReplayRecorder.js +101 -0
  36. package/dist/application/PermissionAuthority.js +7 -3
  37. package/dist/application/ProcessCaptureJournal.js +95 -0
  38. package/dist/application/ProcessCaptureLifecycle.js +24 -13
  39. package/dist/application/ProcessCheckpoints.js +30 -4
  40. package/dist/application/ProcessCli.js +26 -4
  41. package/dist/application/ProcessHarness.js +20 -50
  42. package/dist/application/ProcessNormalization.js +3 -2
  43. package/dist/application/ProcessPairedExperiment.js +72 -0
  44. package/dist/application/ProcessReactiveCoordinator.js +218 -0
  45. package/dist/application/ProcessReactiveEffects.js +133 -0
  46. package/dist/application/ProcessReactiveObservations.js +20 -0
  47. package/dist/application/ProcessSampling.js +2 -0
  48. package/dist/application/SessionProviderRouter.js +7 -6
  49. package/dist/application/Setup.js +14 -13
  50. package/dist/application/SetupClientConfiguration.js +10 -6
  51. package/dist/application/SetupPlan.js +4 -3
  52. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  53. package/dist/application/SetupSkill.js +44 -40
  54. package/dist/application/TerminalRenderer.js +3 -1
  55. package/dist/application/Uninstall.js +7 -1
  56. package/dist/application/Upgrade.js +16 -1
  57. package/dist/browser/CdpEndpoint.js +37 -4
  58. package/dist/catalogIdentity.js +16 -7
  59. package/dist/cli/coreAnalysisCommands.js +34 -2
  60. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  61. package/dist/cli/setupCommands.js +84 -30
  62. package/dist/cli/utilityCommands.js +10 -2
  63. package/dist/cliApplicationCommands.js +79 -16
  64. package/dist/cliCommandNames.js +3 -0
  65. package/dist/cliElectronCommands.js +23 -33
  66. package/dist/cliObservationOptions.js +1 -1
  67. package/dist/cliOutput.js +56 -0
  68. package/dist/cliProcessCommands.js +32 -1
  69. package/dist/cliSetup.js +55 -29
  70. package/dist/contracts/applicationToolContracts.js +54 -7
  71. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  72. package/dist/contracts/electronToolContracts.js +73 -4
  73. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  74. package/dist/contracts/managedToolContracts.js +7 -2
  75. package/dist/contracts/processCaptureExample.js +1 -0
  76. package/dist/contracts/replayMachineExample.js +37 -0
  77. package/dist/contracts/sessionStatusContract.js +26 -0
  78. package/dist/contracts/toolContractExamples.js +2 -0
  79. package/dist/contracts/toolContractTypes.js +12 -1
  80. package/dist/contracts/toolContracts.js +8 -11
  81. package/dist/contracts/toolEffects.js +3 -0
  82. package/dist/contracts/toolOutputSchemaGroups.js +53 -9
  83. package/dist/contracts/toolOutputSchemaPrimitives.js +73 -32
  84. package/dist/doctorRuntime.js +41 -0
  85. package/dist/domain/analysisErrorPresentation.js +5 -3
  86. package/dist/domain/analysisErrorProjection.js +7 -1
  87. package/dist/domain/androidApplication.js +206 -0
  88. package/dist/domain/appleApplication.js +221 -0
  89. package/dist/domain/boundedCartesianProjection.js +15 -0
  90. package/dist/domain/changedBehavior.js +5 -4
  91. package/dist/domain/completionLedgerGeneration.js +190 -0
  92. package/dist/domain/errors.js +4 -2
  93. package/dist/domain/evidenceCompletionLedger.js +113 -0
  94. package/dist/domain/hopperStartupFailure.js +9 -0
  95. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  96. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  97. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  98. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  99. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  100. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  101. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  102. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  103. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  104. package/dist/domain/javascriptSemanticCalls.js +269 -0
  105. package/dist/domain/javascriptSemanticGraph.js +356 -0
  106. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  107. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  108. package/dist/domain/javascriptSemanticIr.js +14 -1
  109. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  110. package/dist/domain/javascriptSemanticProjection.js +36 -4
  111. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  112. package/dist/domain/javascriptSemanticQuery.js +294 -0
  113. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  114. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  115. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  116. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  117. package/dist/domain/javascriptSemanticReturns.js +187 -0
  118. package/dist/domain/javascriptSemanticState.js +32 -0
  119. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  120. package/dist/domain/javascriptSemanticValues.js +54 -70
  121. package/dist/domain/processCapture.js +36 -0
  122. package/dist/domain/processCaptureValidation.js +101 -12
  123. package/dist/domain/processComparison.js +96 -24
  124. package/dist/domain/processObservation.js +153 -0
  125. package/dist/domain/processPairedExperiment.js +128 -0
  126. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  127. package/dist/domain/processReactiveMatching.js +210 -0
  128. package/dist/domain/processReactiveRuntime.js +109 -0
  129. package/dist/domain/processReactiveScenario.js +361 -0
  130. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  131. package/dist/domain/processReactiveTransition.js +159 -0
  132. package/dist/domain/processScenario.js +12 -0
  133. package/dist/domain/processTraceComparison.js +43 -0
  134. package/dist/domain/processTraceDimensionProjection.js +50 -0
  135. package/dist/domain/processTraceEvaluation.js +312 -0
  136. package/dist/domain/processTraceSpecification.js +373 -0
  137. package/dist/domain/reconstructionVerification.js +5 -4
  138. package/dist/domain/replayMachine.js +332 -0
  139. package/dist/domain/replayMachineRun.js +216 -0
  140. package/dist/domain/replayMachineRuntime.js +294 -0
  141. package/dist/domain/runtimeIdentification.js +210 -0
  142. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  143. package/dist/evaluation/CodexAgentEval.js +156 -0
  144. package/dist/generatedPackageMetadata.js +2 -2
  145. package/dist/ghidra/GhidraClient.js +13 -2
  146. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  147. package/dist/ghidra/GhidraLauncher.js +7 -3
  148. package/dist/ghidra/GhidraProvider.js +8 -1
  149. package/dist/hopper/BridgeLauncher.js +56 -14
  150. package/dist/hopper/HopperClient.js +23 -10
  151. package/dist/hopper/HopperDiagnostics.js +14 -0
  152. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  153. package/dist/hopper/HopperProvider.js +8 -1
  154. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  155. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  156. package/dist/identity.js +2 -1
  157. package/dist/main/transport.js +2 -0
  158. package/dist/mcpDoctor.js +310 -0
  159. package/dist/process/ProcessOwnership.js +200 -53
  160. package/dist/process/ProviderRunLineage.js +21 -0
  161. package/dist/server/createServer.js +8 -2
  162. package/dist/server/javascriptApplicationResult.js +106 -0
  163. package/dist/server/registerApplicationTools/characterization.js +3 -3
  164. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  165. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  166. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  167. package/dist/server/registerApplicationTools/coverage.js +3 -3
  168. package/dist/server/registerApplicationTools/helpers.js +37 -25
  169. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  170. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  171. package/dist/server/registerApplicationTools.js +4 -0
  172. package/dist/server/registerElectronTools.js +19 -5
  173. package/dist/server/registerEvidenceResources.js +7 -1
  174. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  175. package/dist/server/registerManagedTools.js +18 -1
  176. package/dist/server/registerProcessComparisonTool.js +17 -7
  177. package/dist/server/registerReplayMachineTool.js +20 -0
  178. package/dist/server/registerSessionStatusTool.js +95 -25
  179. package/dist/server/registerSessionTools.js +12 -7
  180. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  181. package/dist/server/toolResult.js +10 -4
  182. package/dist/serverIdentity.js +19 -0
  183. package/package.json +62 -29
  184. package/scripts/hopper-demo-x11.py +459 -61
  185. package/scripts/prepack.mjs +55 -0
  186. package/scripts/prepare.mjs +28 -0
  187. package/scripts/rea.mjs +26 -5
  188. package/skills/reverse-engineer-anything/SKILL.md +69 -228
  189. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  190. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  191. package/skills/reverse-engineer-anything/references/javascript-applications.md +35 -0
  192. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  193. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
package/README.md CHANGED
@@ -36,17 +36,18 @@ Reverse engineering normally makes the operator choose a tool, learn its API, mo
36
36
 
37
37
  ## Just ask your agent
38
38
 
39
- Install the REA skill:
39
+ Run setup once. Agent integration installs an aligned MCP registration and the
40
+ bundled routing skill together:
40
41
 
41
42
  ```bash
42
- npx skills add morluto/rea
43
+ npx rea-agents setup
43
44
  ```
44
45
 
45
46
  Then ask:
46
47
 
47
48
  ```text
48
- Use REA to understand how search works in the Notes app, show me the
49
- evidence, and build a similar feature for my project.
49
+ Understand how search works in the Notes app, show me the evidence, and build a
50
+ similar feature for my project.
50
51
  ```
51
52
 
52
53
  Notes is only an example. Name any app you want to understand, or ask the agent to start with an overview.
@@ -97,9 +98,9 @@ shows its complete plan and asks before applying it. Setup does not update
97
98
  Homebrew, Node.js, or npm. `npx rea-agents setup` opens with the work it
98
99
  enables: investigate local apps from an agent, recover evidence through a
99
100
  deep-analysis provider, and reuse REA's guided workflow. It summarizes the
100
- detected agents, then asks which independent capabilities to set up: coding-agent
101
- access through MCP, the shared investigation skill, and—when needed—the Hopper
102
- provider. Nothing is preselected. Choosing MCP access opens a second empty
101
+ detected agents, then asks which capabilities to set up: agent integration
102
+ (MCP plus the matching guided workflow) and—when needed—the Hopper provider.
103
+ Nothing is preselected. Choosing agent integration opens a second empty
103
104
  checklist for the specific detected agents that should receive a registration.
104
105
 
105
106
  REA keeps the journey inline so its history remains in the terminal. Selecting
@@ -131,10 +132,12 @@ Pass installer options after `bash -s --`, for example `--dry-run`, `--no-setup`
131
132
  ### With an agent — recommended
132
133
 
133
134
  ```bash
134
- npx skills add morluto/rea
135
+ npx rea-agents setup
135
136
  ```
136
137
 
137
- Ask your agent to set up REA. It will check your supported host, explain anything it needs to install, ask for approval, and guide you through system prompts. After setup, restart the agent if it asks you to load the full REA toolset.
138
+ Choose Agent Integration in the reviewed setup plan. REA installs the pinned MCP
139
+ registration and its matching routing skill as one transaction. After setup,
140
+ restart the configured agent so it loads the aligned integration.
138
141
 
139
142
  Review the setup plan, approve it if appropriate, then describe the app or feature you want to understand. Hopper can run in its free demo mode; if it shows a first-run prompt, choose the demo or enter an existing license.
140
143
 
@@ -259,7 +262,7 @@ Uninstall preserves Hopper, Node.js, evidence, captures, external evidence roots
259
262
  | Inspect or decompile one part of an app from the Terminal | `rea analyze` or `rea decompile` |
260
263
  | Validate, canonicalize, or compare Evidence v2 bundles | `rea evidence-import`, `rea evidence-export`, or `rea compare` |
261
264
  | Run or resume a persistent two-version artifact analysis | `rea investigate-versions` |
262
- | Map a local JavaScript/Electron application without executing it | `rea analyze-javascript-application` |
265
+ | Map a local JavaScript/Electron application without executing it | `rea analyze PATH --approved` or `rea analyze-javascript-application` |
263
266
  | Reuse immutable analysis results without relaunching a provider | Pass `--snapshot /approved/path/analysis.json` to a deep-analysis command |
264
267
  | Import source as historical reference | `rea import-reference-source` |
265
268
  | Capture or compare controlled process behavior | `rea capture-process` or `rea compare-process-captures` |
@@ -273,9 +276,21 @@ rea evidence-import /absolute/path/to/evidence/bundle.json
273
276
  rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
274
277
  rea compare /absolute/path/to/evidence/left.json /absolute/path/to/evidence/right.json
275
278
  rea investigate-versions /absolute/path/to/releases/v1 /absolute/path/to/releases/v2 /absolute/path/to/evidence/releases.json --yes --workspace-name releases
279
+ rea analyze /absolute/path/to/releases/app.asar --approved --json
276
280
  rea analyze-javascript-application /absolute/path/to/releases/app.asar --approved --json
277
281
  ```
278
282
 
283
+ For a directory or `.asar`, generic `rea analyze` automatically selects the
284
+ static JavaScript application provider when neither `--provider` nor
285
+ `--snapshot` is supplied. The dedicated command remains available for explicit
286
+ format and resource-limit controls. Both routes require `--approved` and an
287
+ administrator-approved investigation input root.
288
+
289
+ If REA is registered with an MCP client, run approved `rea setup` while the
290
+ explicit investigation-root variable is set, then restart that client. Setup
291
+ copies this non-secret policy into the managed registration; changing the shell
292
+ environment alone cannot update an already-running MCP process.
293
+
279
294
  `investigate-versions` inventories both versions, checkpoints their observed
280
295
  Evidence, derives an artifact comparison, and records a changed-behavior report.
281
296
  Both input paths must resolve beneath `REA_INVESTIGATION_INPUT_ROOTS_JSON`;
@@ -365,8 +380,8 @@ REA handles the app analysis in steps 1–5. The agent performs step 6 with its
365
380
  | Managed PE/CLI | 8 | PE/CLI identity, metadata members, CIL hashes, P/Invoke/native-boundary declarations and verification, application-graph projection, decompiler reconstruction import, token remapping, runtime-correlation plans, and version comparison |
366
381
  | Browser observation | 8 | exact-origin CDP capture, bundle and source-map analysis, WebMCP discovery, session timelines, capture diff, and visual evidence |
367
382
  | Electron analysis | 4 | passive root-confined observation, bounded static application mapping, and evidence-backed static/runtime reconciliation |
368
- | Application workflows | 7 | bounded cross-layer traces, unique-only version matching, approved Linux-isolated extracted-module replay, managed-runtime characterization, and reconstruction coverage closure |
369
- | Workspace and observation | 18 | target lifecycle, Evidence v2 bundles, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
383
+ | Application workflows | 9 | bounded cross-layer traces, unique-only version matching, static export return-shape comparison, approved Linux-isolated extracted-module replay, managed-runtime characterization, and reconstruction coverage closure |
384
+ | Workspace and observation | 19 | target lifecycle, Evidence v2 bundles, direct finite replay-machine evaluation, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
370
385
 
371
386
  The public interface describes what the agent is trying to learn. Providers decide how to answer. macOS utilities handle common semantic inspection without launching Hopper; Hopper handles deeper native analysis; the process harness implements controlled behavioral capture.
372
387
 
@@ -381,7 +396,7 @@ REA is already useful for native application, browser, and Electron investigatio
381
396
  - Validate and canonically serialize a provider-neutral [JavaScript Application Graph v1](docs/javascript-application-graph.md) spanning packages, ASAR entries, Electron roles, JavaScript/source-map entities, browser/runtime instances, IPC, endpoints, storage, and native add-ons. This shipped domain contract performs no extraction or I/O by itself.
382
397
  - Reconstruct bounded static package, entrypoint, Webpack/Rspack module, import, worker, endpoint, storage, source-map, BrowserWindow, preload, contextBridge, IPC, utility-process, and native-add-on structure from an approved local directory or ASAR through `analyze_javascript_application` or `rea analyze-javascript-application`. The AST-only [application service](docs/javascript-artifact-reconstruction.md) never executes bootstrap code, pairs only unique exact literal IPC channels, and reports dynamic or ambiguous channels as unresolved.
383
398
  - Reconcile that static graph with existing passive web or Electron Evidence through `reconcile_javascript_runtime` or `rea reconcile-javascript-runtime`. Exact captured bytes outrank caller-declared file/URL mappings; target, frame, script, worker, cache, and asset ambiguity stays explicit, source-map authority stays separate, and a module resident in an observed bundle is never reported as executed. See [JavaScript static/runtime reconciliation](docs/javascript-runtime-reconciliation.md).
384
- - Trace a literal route, string, API, IPC channel, module, or native export through authenticated application Evidence with explicit traversal bounds, then hand exact native artifact digests and requested exports to retained Ghidra or Hopper Evidence without automatic provider switching. Compare application versions using unique-only digest, source-map, structural, and semantic tiers; duplicate, incomplete, and truncated matches stay unknown. See [cross-layer JavaScript application workflows](docs/javascript-application-workflows.md).
399
+ - Trace a literal route, string, API, IPC channel, module, or native export through authenticated application Evidence with explicit traversal bounds, then hand exact native artifact digests and requested exports to retained Ghidra or Hopper Evidence without automatic provider switching. Compare application versions using unique-only digest, source-map, structural, and semantic tiers; compare one exact JavaScript export's static return shapes through unique literal discriminants and bounded JSON Pointer changes. Duplicate, dynamic, incomplete, and truncated facts stay unknown. See [cross-layer JavaScript application workflows](docs/javascript-application-workflows.md).
385
400
  - Classify PE/CLI managed artifacts with `inspect_managed_artifact` / `rea inspect-managed-artifact`, inspect bounded metadata members, signatures, raw CIL hashes, limited decoded-instruction-tuple v1 hashes, separately reported exception regions, call edges, and field-access anchors with `inspect_managed_members` / `rea inspect-managed-members`, inventory declared ModuleRef/ImplMap/PInvoke and non-IL method boundary indicators with `inspect_managed_native_boundaries` / `rea inspect-managed-native-boundaries`, then compare two authenticated member observations with `compare_managed_members` / `rea compare-managed-members`. `verify_managed_native_boundaries` / `rea verify-managed-native-boundaries` checks managed P/Invoke declarations against authenticated native export or function Evidence while keeping verified, inferred, contradicted, and unresolved states distinct. The comparison treats build-local tokens as build-local and uses unique decoded-CIL/signature and structural method-shape tiers, never names alone; the v1 tuple hash does not itself resolve tokens or fully commit control flow. `project_managed_application_graph` / `rea project-managed-application-graph` projects authenticated managed artifact/member/native-boundary Evidence into the existing application graph for cross-layer feature tracing. `import_managed_reconstruction` / `rea import-managed-reconstruction` admits user-supplied decompiler C#/IL/pseudocode as analyst inference only after exact artifact SHA-256, MVID, signature, and the shipped v1 decoded-IL commitment match. Separately, `plan_managed_runtime_correlation` / `rea plan-managed-runtime-correlation` can admit a default-disabled, permission-gated runtime-correlation plan locked to the same build evidence. These paths never load the assembly, resolve CLR dependencies, execute target code, run a decompiler, or translate managed tokens into native addresses; full normalized-CIL v2 semantics, native-body bridge mapping, and an actual runtime executor remain future managed-code contracts.
386
401
  - Configure `REA_ILSPY_CMD_PATH=/absolute/path/to/ilspycmd` only when you want
387
402
  doctor and `verify:managed` to inspect a bring-your-own ILSpy command as a
@@ -394,6 +409,7 @@ REA is already useful for native application, browser, and Electron investigatio
394
409
  - Export and import evidence bundles across sessions.
395
410
  - Persist automatic cross-version artifact runs as canonical, lock-protected workspaces with tamper-evident revision commitments.
396
411
  - Capture approved PTY scenarios as Process Capture v4 Evidence, including committed run manifests, raw and rendered terminal frames, scripted interactions, descendant settlement, named filesystem checkpoints, deterministic command shims, and loopback HTTP/WebSocket exchanges.
412
+ - Validate finite replay machines without launching a target through `run_replay_machine` or `rea run-replay-machine`; ordered events return typed decisions, actions, captured aliases, transition journals, final state, and exact limit use without echoing request or captured values.
397
413
  - Compare complete artifact inventories by stable path, content, metadata, and relations; incomplete evidence never implies equivalence.
398
414
  - Compare explicit function dossiers across text, calls, references, strings, and address-normalized CFG topology with per-facet unknowns.
399
415
  - Compare canonical Evidence bundles by exact membership, explicit observation pairs, and residual-unknown histories without turning omissions into behavioral absence.
@@ -422,7 +438,7 @@ rea inspect-web-page http://127.0.0.1:9222 TARGET_ID --approved --json
422
438
 
423
439
  All eight browser tools expose the same Evidence v2 contracts over CLI and MCP. Inspection is passive: REA does not evaluate page JavaScript, navigate, click, close the page, or close the browser. Query values, credentials, cookies, authorization headers, storage values, and raw JSON or WebSocket values are never retained. Separately approved captures can retain bounded redacted console primitives, value-free JSON/WebSocket shapes, script sources, accessibility text, or screenshot pixels. Existing activity before attach is explicitly unavailable. See [Website observation with CDP](docs/browser-observation.md) for browser startup, schemas, limits, and the threat model.
424
440
 
425
- Exact package, tool-family, provider, setup-client, public schema-version, and CLI facts are generated from source in [`docs/product-catalog.json`](docs/product-catalog.json). `npm run docs:check` verifies that this catalog, the narrative documentation, TypeDoc, and generated schemas have not drifted.
441
+ Exact package, tool-family, provider, setup-client, public schema-version, and CLI facts are generated from source in [`docs/product-catalog.json`](docs/product-catalog.json). PR CI verifies this catalog, narrative documentation, generated schemas, and a clean TypeDoc render.
426
442
 
427
443
  ## Roadmap
428
444
 
@@ -456,17 +472,28 @@ Setup detects Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf,
456
472
 
457
473
  ### Manual MCP configuration
458
474
 
475
+ <!-- x-release-please-start-version -->
476
+
459
477
  ```json
460
478
  {
461
479
  "mcpServers": {
462
480
  "rea": {
463
481
  "command": "npx",
464
- "args": ["-y", "rea-agents@latest", "mcp"]
482
+ "args": ["-y", "rea-agents@2.3.0", "mcp"]
465
483
  }
466
484
  }
467
485
  }
468
486
  ```
469
487
 
488
+ <!-- x-release-please-end -->
489
+
490
+ Persistent registrations should use one exact package version. `rea setup`
491
+ maintains that pin, upgrades the bundled skill at the same time, and gives Codex
492
+ a 30-second startup allowance for a cold package-runner start. An interactive
493
+ `rea upgrade` opens the updated setup plan after installing the new executable;
494
+ structured or non-interactive upgrades tell you to run that sync explicitly.
495
+ Restart clients whose approved registration changed.
496
+
470
497
  MCP clients that support prompts can also discover six ordered investigation
471
498
  workflows through `prompts/list`. Their optional identifier arguments use the
472
499
  current session for bounded `completion/complete` suggestions; see
@@ -558,8 +585,15 @@ operations still work. An explicit unknown, unavailable, or unsupported
558
585
  provider fails with candidate IDs, stable rejection codes, and actionable local
559
586
  diagnostics. `binary_session`, `rea providers`, and `rea capabilities` expose
560
587
  the authoritative `analysis_provider_candidates` and
561
- `analysis_provider_binding` fields. Reopening the same target without a selector
562
- keeps its binding; runtime failure never selects another provider silently.
588
+ `analysis_provider_binding` fields. Each open target also has an
589
+ `analysis_run.run_id` allocated before provider startup. When dynamic providers
590
+ start, `analysis_run.process_lineage` changes from `not_observed` to `snapshots`
591
+ and retains one provider-attributed, token-verified observation per started
592
+ provider. Each observation carries `observed_at` and remains `unavailable` or
593
+ `verified`; a verified empty descendant list is distinct from both. Snapshots
594
+ describe bounded observations, not current live state or historical absence.
595
+ Reopening same target without a selector keeps its binding; runtime failure
596
+ never selects another provider silently.
563
597
  Ghidra can appear as an available, target-compatible candidate after doctor
564
598
  validates its exact installation. Its capability list contains the 18 admitted
565
599
  read-only inventory and function-analysis operations; selecting it still does
@@ -691,7 +725,39 @@ Any agent that can run a local MCP server can use the manual configuration. Setu
691
725
 
692
726
  ## Development
693
727
 
694
- See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, architecture, tests, and release instructions. Generated API documentation is available under [`docs/api`](docs/api/index.html).
728
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, architecture, tests, and release instructions. PR CI publishes generated API documentation as the `api-docs` workflow artifact.
729
+
730
+ `npm run verify:agent` runs brandless native, JavaScript-application, managed,
731
+ and browser prompts through a real local Codex CLI. Its JSON report measures
732
+ natural MCP use, first-tool routing, repeated calls, actual Codex token usage,
733
+ completion quality, and explicit treatment of authority and unknowns.
734
+
735
+ `npm run evidence:generate` regenerates the managed conformance manifest and
736
+ Evidence v2 completion ledger from live verifier output. `npm run
737
+ evidence:check` reruns the verifier and fails when artifacts, scenarios,
738
+ providers, schemas, claim counts, Evidence IDs, or the bundled skill have
739
+ drifted. Unsupported claims remain explicit and never count as passes.
740
+ Verifier JSON reports include an ephemeral `verifier_run` UUID allocated before
741
+ the verifier performs work and inherited by its child processes through
742
+ `REA_PROCESS_RUN_ID`. The final report includes the verifier and parent PIDs
743
+ plus `process_lineage` and its ISO `observed_at` timestamp: POSIX verifiers
744
+ report a token-verified, point-in-time launcher process group and live
745
+ descendants, while platforms without an owned
746
+ lineage primitive report `status: "unavailable"` and a reason. An empty verified
747
+ descendant list means no child was live during the final observation; it does
748
+ not claim the verifier launched no children earlier. Nested verifier entrypoints
749
+ in the same process reuse its UUID; each new verifier process replaces any
750
+ inherited parent token with a fresh run identity.
751
+ Generated completion commitments deliberately exclude this per-execution
752
+ identity, so check mode remains deterministic while live reports remain
753
+ attributable.
754
+
755
+ Owned Hopper shutdown logs retain the launcher PID, process-group ID, cleanup
756
+ status, and whether a verified group signal was required. They omit the run
757
+ token and unexpected exception text. The real-Hopper verifier also starts an
758
+ unrelated `Hopper`-named sentinel in a distinct process group and fails unless
759
+ that process survives every session close; the sentinel is removed only by the
760
+ verifier after the survival check.
695
761
 
696
762
  ## Project links
697
763
 
@@ -0,0 +1,17 @@
1
+ import { androidApplicationProjectionInputSchema, projectAndroidApplication, } from "../domain/androidApplication.js";
2
+ import { ANDROID_APPLICATION_PROVIDER } from "./InvestigationProviders.js";
3
+ import { projectInventoryEvidence } from "./InventoryProjectionEvidence.js";
4
+ const OPERATION = "project_android_application_graph";
5
+ /** Project authenticated APK inventory Evidence into Android application Evidence. */
6
+ export const projectAndroidApplicationEvidence = (rawInput) => {
7
+ return projectInventoryEvidence({
8
+ rawInput,
9
+ schema: androidApplicationProjectionInputSchema,
10
+ project: projectAndroidApplication,
11
+ operation: OPERATION,
12
+ predicateType: "rea.android-application-graph/v1",
13
+ provider: ANDROID_APPLICATION_PROVIDER,
14
+ subjectFormat: () => "apk",
15
+ protocolError: "Android application projection produced an invalid result",
16
+ });
17
+ };
@@ -0,0 +1,17 @@
1
+ import { appleApplicationProjectionInputSchema, projectAppleApplication, } from "../domain/appleApplication.js";
2
+ import { APPLE_APPLICATION_PROVIDER } from "./InvestigationProviders.js";
3
+ import { projectInventoryEvidence } from "./InventoryProjectionEvidence.js";
4
+ const OPERATION = "project_apple_application_graph";
5
+ /** Project authenticated IPA inventory Evidence into Apple application evidence. */
6
+ export const projectAppleApplicationEvidence = (rawInput) => {
7
+ return projectInventoryEvidence({
8
+ rawInput,
9
+ schema: appleApplicationProjectionInputSchema,
10
+ project: projectAppleApplication,
11
+ operation: OPERATION,
12
+ predicateType: "rea.apple-application-graph/v1",
13
+ provider: APPLE_APPLICATION_PROVIDER,
14
+ subjectFormat: () => "ipa",
15
+ protocolError: "Apple application projection produced an invalid result",
16
+ });
17
+ };
@@ -0,0 +1,162 @@
1
+ import { z } from "zod";
2
+ import { compareApplicationVersionsRequestSchema, compareJavaScriptExportShapesRequestSchema, traceApplicationFeatureRequestSchema, traceJavaScriptSemanticsRequestSchema, } from "../contracts/applicationWorkflowInputContracts.js";
3
+ import { AnalysisInputError } from "../domain/errors.js";
4
+ import { projectInputIssues } from "../domain/inputIssueProjection.js";
5
+ import { compareApplicationVersionsInputSchema } from "../domain/javascriptApplicationVersionComparisonSchemas.js";
6
+ import { compareJavaScriptExportShapesInputSchema } from "../domain/javascriptExportShapeComparisonSchemas.js";
7
+ import { traceApplicationFeatureInputSchema } from "../domain/javascriptFeatureTraceSchemas.js";
8
+ import { traceJavaScriptSemanticsInputSchema } from "../domain/javascriptSemanticTraceSchemas.js";
9
+ import { err, ok } from "../domain/result.js";
10
+ import { resolveEvidenceReferences, } from "./EvidenceReferenceResolver.js";
11
+ const APPLICATION_GRAPH_IDENTITIES = [
12
+ {
13
+ operation: "analyze_javascript_application",
14
+ predicate: "rea.javascript-application-analysis/v1",
15
+ },
16
+ {
17
+ operation: "analyze_javascript_application",
18
+ predicate: "rea.javascript-application-analysis/v2",
19
+ },
20
+ {
21
+ operation: "reconcile_javascript_runtime",
22
+ predicate: "rea.javascript-runtime-reconciliation/v1",
23
+ },
24
+ {
25
+ operation: "project_managed_application_graph",
26
+ predicate: "rea.managed-application-graph/v1",
27
+ },
28
+ ];
29
+ /** Parse and resolve one trace adapter request into its canonical domain input. */
30
+ export const resolveTraceApplicationFeatureRequest = (input, lookup) => {
31
+ const parsed = traceApplicationFeatureRequestSchema.safeParse(input);
32
+ return parsed.success
33
+ ? resolveTraceApplicationFeatureRequestValidated(parsed.data, lookup)
34
+ : invalid("trace_application_feature", parsed.error, input);
35
+ };
36
+ /** Resolve a trace request already parsed by the caller-facing contract. */
37
+ export const resolveTraceApplicationFeatureRequestValidated = (input, lookup) => {
38
+ const application = input.application === undefined
39
+ ? resolveEvidenceReferences(lookup, input.application_evidence_id === undefined
40
+ ? []
41
+ : [input.application_evidence_id], APPLICATION_GRAPH_IDENTITIES)
42
+ : ok([input.application]);
43
+ if (!application.ok)
44
+ return application;
45
+ const native = resolveEvidenceReferences(lookup, input.native_observation_evidence_ids);
46
+ if (!native.ok)
47
+ return native;
48
+ const raw = {
49
+ application: application.value[0],
50
+ native_observations: [...input.native_observations, ...native.value],
51
+ seed: input.seed,
52
+ direction: input.direction,
53
+ limits: input.limits,
54
+ };
55
+ const parsed = traceApplicationFeatureInputSchema.safeParse(raw);
56
+ return parsed.success
57
+ ? ok(parsed.data)
58
+ : invalid("trace_application_feature", parsed.error, raw);
59
+ };
60
+ /** Parse and resolve one semantic trace adapter request. */
61
+ export const resolveTraceJavaScriptSemanticsRequest = (input, lookup) => {
62
+ const parsed = traceJavaScriptSemanticsRequestSchema.safeParse(input);
63
+ return parsed.success
64
+ ? resolveTraceJavaScriptSemanticsRequestValidated(parsed.data, lookup)
65
+ : invalid("trace_javascript_semantics", parsed.error, input);
66
+ };
67
+ /** Resolve a semantic trace request already parsed by its public contract. */
68
+ export const resolveTraceJavaScriptSemanticsRequestValidated = (input, lookup) => {
69
+ const application = input.application === undefined
70
+ ? resolveEvidenceReferences(lookup, input.application_evidence_id === undefined
71
+ ? []
72
+ : [input.application_evidence_id], APPLICATION_GRAPH_IDENTITIES)
73
+ : ok([input.application]);
74
+ if (!application.ok)
75
+ return application;
76
+ const raw = { application: application.value[0], query: input.query };
77
+ const parsed = traceJavaScriptSemanticsInputSchema.safeParse(raw);
78
+ return parsed.success
79
+ ? ok(parsed.data)
80
+ : invalid("trace_javascript_semantics", parsed.error, raw);
81
+ };
82
+ /** Parse and resolve one comparison adapter request into canonical input. */
83
+ export const resolveCompareApplicationVersionsRequest = (input, lookup) => {
84
+ const parsed = compareApplicationVersionsRequestSchema.safeParse(input);
85
+ return parsed.success
86
+ ? resolveCompareApplicationVersionsRequestValidated(parsed.data, lookup)
87
+ : invalid("compare_application_versions", parsed.error, input);
88
+ };
89
+ /** Resolve a comparison request parsed by the caller-facing contract. */
90
+ export const resolveCompareApplicationVersionsRequestValidated = (input, lookup) => {
91
+ const left = graphEvidence(input.left, input.left_evidence_id, lookup);
92
+ if (!left.ok)
93
+ return left;
94
+ const right = graphEvidence(input.right, input.right_evidence_id, lookup);
95
+ if (!right.ok)
96
+ return right;
97
+ const leftNative = resolveEvidenceReferences(lookup, input.left_native_observation_evidence_ids);
98
+ if (!leftNative.ok)
99
+ return leftNative;
100
+ const rightNative = resolveEvidenceReferences(lookup, input.right_native_observation_evidence_ids);
101
+ if (!rightNative.ok)
102
+ return rightNative;
103
+ const raw = {
104
+ left: left.value,
105
+ right: right.value,
106
+ left_native_observations: [
107
+ ...input.left_native_observations,
108
+ ...leftNative.value,
109
+ ],
110
+ right_native_observations: [
111
+ ...input.right_native_observations,
112
+ ...rightNative.value,
113
+ ],
114
+ limits: input.limits,
115
+ ...(input.unknown_registry_approved === undefined
116
+ ? {}
117
+ : { unknown_registry_approved: input.unknown_registry_approved }),
118
+ };
119
+ const parsed = compareApplicationVersionsInputSchema.safeParse(raw);
120
+ return parsed.success
121
+ ? ok(parsed.data)
122
+ : invalid("compare_application_versions", parsed.error, raw);
123
+ };
124
+ /** Parse and resolve an export-shape adapter request into canonical input. */
125
+ export const resolveCompareJavaScriptExportShapesRequest = (input, lookup) => {
126
+ const parsed = compareJavaScriptExportShapesRequestSchema.safeParse(input);
127
+ return parsed.success
128
+ ? resolveCompareJavaScriptExportShapesRequestValidated(parsed.data, lookup)
129
+ : invalid("compare_javascript_export_shapes", parsed.error, input);
130
+ };
131
+ /** Resolve an export-shape request already parsed by the adapter contract. */
132
+ export const resolveCompareJavaScriptExportShapesRequestValidated = (input, lookup) => {
133
+ const left = graphEvidence(input.left, input.left_evidence_id, lookup);
134
+ if (!left.ok)
135
+ return left;
136
+ const right = graphEvidence(input.right, input.right_evidence_id, lookup);
137
+ if (!right.ok)
138
+ return right;
139
+ const raw = {
140
+ left: left.value,
141
+ right: right.value,
142
+ left_module_path: input.left_module_path,
143
+ left_export_name: input.left_export_name,
144
+ right_module_path: input.right_module_path,
145
+ right_export_name: input.right_export_name,
146
+ limits: input.limits,
147
+ ...(input.unknown_registry_approved === undefined
148
+ ? {}
149
+ : { unknown_registry_approved: input.unknown_registry_approved }),
150
+ };
151
+ const parsed = compareJavaScriptExportShapesInputSchema.safeParse(raw);
152
+ return parsed.success
153
+ ? ok(parsed.data)
154
+ : invalid("compare_javascript_export_shapes", parsed.error, raw);
155
+ };
156
+ const graphEvidence = (evidence, evidenceId, lookup) => {
157
+ if (evidence !== undefined)
158
+ return ok(evidence);
159
+ const resolved = resolveEvidenceReferences(lookup, evidenceId === undefined ? [] : [evidenceId], APPLICATION_GRAPH_IDENTITIES);
160
+ return resolved.ok ? ok(resolved.value[0]) : resolved;
161
+ };
162
+ const invalid = (operation, cause, input) => err(new AnalysisInputError(operation, undefined, projectInputIssues(cause.issues, input)));
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { analysisProfilesEqual, } from "../domain/analysisProfile.js";
2
3
  import { AnalysisCancelledError, ProviderAdapterError, } from "../domain/errors.js";
3
4
  import { err, ok } from "../domain/result.js";
@@ -116,7 +117,8 @@ export class BinarySession extends BinarySessionRecords {
116
117
  const previous = this.#active;
117
118
  this.#active = undefined;
118
119
  await previous?.client.close();
119
- const client = route.createClient(target);
120
+ const runId = randomUUID();
121
+ const client = route.createClient(target, { runId });
120
122
  const started = await client.execute("health", {}, options);
121
123
  if (!started.ok) {
122
124
  await client.close();
@@ -134,6 +136,7 @@ export class BinarySession extends BinarySessionRecords {
134
136
  profile,
135
137
  compatibility: structuredClone(compatibility),
136
138
  route,
139
+ runId,
137
140
  };
138
141
  this.#clearRuntimeAvailability();
139
142
  if (profile === null)
@@ -172,6 +175,8 @@ export class BinarySession extends BinarySessionRecords {
172
175
  route: this.#currentRoute(),
173
176
  router: this.#providerRouter,
174
177
  runtimeAvailability: this.#runtimeAvailability,
178
+ runId: this.#active?.runId,
179
+ runtimeLineageSnapshots: this.#active?.client.runtimeLineageSnapshots?.() ?? [],
175
180
  });
176
181
  }
177
182
  /** Return the immutable artifact identity captured before its provider started. */
@@ -300,7 +305,8 @@ export class BinarySession extends BinarySessionRecords {
300
305
  async #restore(previous) {
301
306
  if (previous === undefined)
302
307
  return;
303
- const client = previous.route.createClient(previous.target);
308
+ const runId = randomUUID();
309
+ const client = previous.route.createClient(previous.target, { runId });
304
310
  const started = await client.execute("health", {});
305
311
  if (started.ok)
306
312
  this.#active = {
@@ -309,6 +315,7 @@ export class BinarySession extends BinarySessionRecords {
309
315
  profile: previous.profile,
310
316
  compatibility: previous.compatibility,
311
317
  route: previous.route,
318
+ runId,
312
319
  };
313
320
  else
314
321
  await client.close();
@@ -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, }) => {
2
+ export const binarySessionStatus = ({ target, route, router, runtimeAvailability, runId, runtimeLineageSnapshots, }) => {
3
3
  const configuredProvider = router.configuredIdentity();
4
4
  const provider = providerSummary(configuredProvider);
5
5
  const providers = router.providerIdentities(route).map(providerSummary);
@@ -35,6 +35,12 @@ export const binarySessionStatus = ({ target, route, router, runtimeAvailability
35
35
  capabilities,
36
36
  analysis_provider_binding: analysisProviderBinding,
37
37
  analysis_provider_candidates: analysisProviderCandidates,
38
+ analysis_run: runId === undefined
39
+ ? null
40
+ : {
41
+ run_id: runId,
42
+ process_lineage: processLineageStatus(runtimeLineageSnapshots),
43
+ },
38
44
  };
39
45
  return target === undefined
40
46
  ? { open: false, ...common }
@@ -49,6 +55,38 @@ export const binarySessionStatus = ({ target, route, router, runtimeAvailability
49
55
  };
50
56
  };
51
57
  const providerSummary = ({ id, name, version, }) => ({ id, name, version });
58
+ const processLineageStatus = (snapshots) => snapshots.length === 0
59
+ ? { status: "not_observed" }
60
+ : {
61
+ status: "snapshots",
62
+ snapshots: snapshots
63
+ .slice()
64
+ .sort((left, right) => left.provider.id.localeCompare(right.provider.id))
65
+ .map(({ provider, observation }) => ({
66
+ provider: providerSummary(provider),
67
+ observation: observation.status === "unavailable"
68
+ ? {
69
+ status: observation.status,
70
+ observed_at: observation.observedAt,
71
+ launcher_pid: observation.launcherPid,
72
+ process_group_id: observation.processGroupId,
73
+ reason: observation.reason,
74
+ }
75
+ : {
76
+ status: observation.status,
77
+ observed_at: observation.observedAt,
78
+ schema_version: observation.lineage.schemaVersion,
79
+ launcher_pid: observation.lineage.launcherPid,
80
+ launcher_parent_pid: observation.lineage.launcherParentPid,
81
+ process_group_id: observation.lineage.processGroupId,
82
+ descendants: observation.lineage.descendants.map(({ pid, parentPid, processGroupId }) => ({
83
+ pid,
84
+ parent_pid: parentPid,
85
+ process_group_id: processGroupId,
86
+ })),
87
+ },
88
+ })),
89
+ };
52
90
  const capabilityStatus = (descriptor) => ({
53
91
  operation: descriptor.operation,
54
92
  available: descriptor.available,
@@ -62,6 +62,9 @@ export const buildCapabilityInventory = (sessionStatus, policy) => {
62
62
  }).sort((left, right) => left.name.localeCompare(right.name));
63
63
  };
64
64
  const availabilityFor = (context) => {
65
+ const javascriptApplication = javascriptApplicationAvailability(context);
66
+ if (javascriptApplication !== null)
67
+ return javascriptApplication;
65
68
  const policyDecision = policyAvailability(context);
66
69
  if (policyDecision !== null)
67
70
  return policyDecision;
@@ -70,6 +73,18 @@ const availabilityFor = (context) => {
70
73
  return targetDecision;
71
74
  return providerAvailability(context);
72
75
  };
76
+ const javascriptApplicationAvailability = ({ name, policy, }) => {
77
+ if (name === "analyze_javascript_application")
78
+ return policy.investigationInputRoots === 0
79
+ ? {
80
+ reason: "policy_disabled",
81
+ remediation: "Configure an exact REA_INVESTIGATION_INPUT_ROOTS_JSON root in the MCP registration, then restart the registered MCP server or client.",
82
+ }
83
+ : { reason: "available", remediation: null };
84
+ return name === "reconcile_javascript_runtime"
85
+ ? { reason: "available", remediation: null }
86
+ : null;
87
+ };
73
88
  const policyAvailability = ({ name, kind, policy, }) => {
74
89
  if (name === "capture_process_scenario" && !policy.processCaptureEnabled)
75
90
  return {
@@ -6,7 +6,11 @@ import { PRODUCT_IDENTITY } from "../identity.js";
6
6
  import { supportedClients } from "./SupportedClients.js";
7
7
  const objectSchema = z.record(z.string(), z.unknown());
8
8
  const registrationSchema = z
9
- .object({ command: z.string().min(1), args: z.array(z.string()).default([]) })
9
+ .object({
10
+ command: z.string().min(1),
11
+ args: z.array(z.string()).default([]),
12
+ startup_timeout_sec: z.number().positive().optional(),
13
+ })
10
14
  .passthrough();
11
15
  /** Inspect supported client registrations without reading their environment. */
12
16
  export const readClientRegistrationStatuses = async (home, currentCommandPath = resolve(process.argv[1] ?? "unknown")) => {
@@ -25,7 +29,7 @@ export const readClientRegistrationStatuses = async (home, currentCommandPath =
25
29
  }
26
30
  const registration = registrationSchema.parse(raw);
27
31
  const command = [registration.command, ...registration.args];
28
- statuses.push(status(client.name, client.configPath, command, registrationAligned(command, currentCommandPath)
32
+ statuses.push(status(client.name, client.configPath, command, registrationAligned(registration, client.name, currentCommandPath)
29
33
  ? "aligned"
30
34
  : "stale"));
31
35
  }
@@ -35,11 +39,14 @@ export const readClientRegistrationStatuses = async (home, currentCommandPath =
35
39
  }
36
40
  return statuses.sort((left, right) => left.client.localeCompare(right.client));
37
41
  };
38
- const registrationAligned = (command, currentCommandPath) => {
42
+ const registrationAligned = (registration, client, currentCommandPath) => {
43
+ const command = [registration.command, ...registration.args];
44
+ if (client === "codex" && registration.startup_timeout_sec !== 30)
45
+ return false;
39
46
  if (command.length === 4 &&
40
47
  command[0] === "npx" &&
41
48
  command[1] === "-y" &&
42
- command[2] === PRODUCT_IDENTITY.packageSpecifier &&
49
+ command[2] === PRODUCT_IDENTITY.registrationPackageSpecifier &&
43
50
  command[3] === "mcp")
44
51
  return true;
45
52
  return (command.length === 2 &&
@@ -27,7 +27,7 @@ const shellQuote = (value) => `'${value.replaceAll("'", `'\\''`)}'`;
27
27
  * would make an unmatched dependency call look authoritative. Unmatched and
28
28
  * exhausted routes therefore fail visibly and remain part of the capture.
29
29
  */
30
- export const startCommandShimReplay = async (scenario, temporaryRoot, started) => {
30
+ export const startCommandShimReplay = async (scenario, temporaryRoot, started, recordEvent = () => undefined) => {
31
31
  const binPath = join(temporaryRoot, "shims");
32
32
  await mkdir(binPath);
33
33
  const runnerPath = join(temporaryRoot, "shim-runner.mjs");
@@ -62,9 +62,10 @@ export const startCommandShimReplay = async (scenario, temporaryRoot, started) =
62
62
  : "matched";
63
63
  if (events.length >= scenario.limits.protocol_events)
64
64
  truncated = true;
65
- else
65
+ else {
66
+ const index = events.length;
66
67
  events.push({
67
- sequence: events.length,
68
+ sequence: index,
68
69
  at_ms: Math.max(0, Date.now() - started),
69
70
  command: invocation.command,
70
71
  route_index: routeIndex !== undefined && routeIndex >= 0 ? routeIndex : null,
@@ -72,6 +73,8 @@ export const startCommandShimReplay = async (scenario, temporaryRoot, started) =
72
73
  working_directory: invocation.working_directory,
73
74
  outcome,
74
75
  });
76
+ recordEvent("shim_events", index);
77
+ }
75
78
  if (outcome !== "matched" || route === undefined) {
76
79
  response.writeHead(409).end();
77
80
  return;
@@ -51,13 +51,13 @@ export class CompositeProvider {
51
51
  ? Promise.resolve(ok({ profile: null, compatibility: {} }))
52
52
  : resolve.call(this.#profileProvider, target, options);
53
53
  }
54
- createClient(target, profile) {
54
+ createClient(target, profile, context) {
55
55
  const clients = new Map();
56
56
  const clientFor = (provider) => {
57
57
  const existing = clients.get(provider);
58
58
  if (existing !== undefined)
59
59
  return existing;
60
- const created = provider.createClient(target, profile?.provider.id === provider.identity().id ? profile : undefined);
60
+ const created = provider.createClient(target, profile?.provider.id === provider.identity().id ? profile : undefined, context);
61
61
  clients.set(provider, created);
62
62
  return created;
63
63
  };
@@ -77,6 +77,9 @@ export class CompositeProvider {
77
77
  ? Promise.resolve(err(new AnalysisCapabilityUnavailableError(this.#identity.id, operation, "operation is not declared by this provider set")))
78
78
  : clientFor(provider).execute(operation, parameters, options);
79
79
  },
80
+ runtimeLineageSnapshots: () => [...clients.values()]
81
+ .flatMap((client) => client.runtimeLineageSnapshots?.() ?? [])
82
+ .sort((left, right) => left.provider.id.localeCompare(right.provider.id)),
80
83
  close: async () => {
81
84
  await Promise.allSettled([...clients.values()].map(async (client) => client.close()));
82
85
  },