rea-agents 2.2.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 (165) hide show
  1. package/README.md +56 -9
  2. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  3. package/dist/application/BinarySession.js +9 -2
  4. package/dist/application/BinarySessionStatus.js +39 -1
  5. package/dist/application/CapabilityInventory.js +15 -0
  6. package/dist/application/CommandShimReplay.js +6 -3
  7. package/dist/application/CompositeProvider.js +5 -2
  8. package/dist/application/DirectAnalysis.js +1 -20
  9. package/dist/application/DirectAnalysisStatus.js +83 -0
  10. package/dist/application/Doctor.js +34 -25
  11. package/dist/application/DoctorDiagnostics.js +1 -4
  12. package/dist/application/DoctorProjection.js +43 -0
  13. package/dist/application/DoctorScope.js +102 -0
  14. package/dist/application/EvidenceReferenceResolver.js +26 -0
  15. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  16. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  17. package/dist/application/JavaScriptApplicationService.js +3 -2
  18. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  19. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  20. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  21. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  22. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  23. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  24. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  25. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  26. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  27. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  28. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  29. package/dist/application/LoopbackReplay.js +277 -133
  30. package/dist/application/LoopbackReplayRecorder.js +101 -0
  31. package/dist/application/PermissionAuthority.js +7 -3
  32. package/dist/application/ProcessCaptureJournal.js +95 -0
  33. package/dist/application/ProcessCaptureLifecycle.js +24 -13
  34. package/dist/application/ProcessCheckpoints.js +30 -4
  35. package/dist/application/ProcessCli.js +26 -4
  36. package/dist/application/ProcessHarness.js +20 -50
  37. package/dist/application/ProcessNormalization.js +3 -2
  38. package/dist/application/ProcessReactiveCoordinator.js +218 -0
  39. package/dist/application/ProcessReactiveEffects.js +133 -0
  40. package/dist/application/ProcessReactiveObservations.js +20 -0
  41. package/dist/application/ProcessSampling.js +2 -0
  42. package/dist/application/SessionProviderRouter.js +7 -6
  43. package/dist/application/Setup.js +11 -10
  44. package/dist/application/SetupPlan.js +1 -1
  45. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  46. package/dist/application/TerminalRenderer.js +3 -1
  47. package/dist/browser/CdpEndpoint.js +37 -4
  48. package/dist/catalogIdentity.js +16 -7
  49. package/dist/cli/coreAnalysisCommands.js +34 -2
  50. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  51. package/dist/cli/setupCommands.js +81 -32
  52. package/dist/cli/utilityCommands.js +10 -2
  53. package/dist/cliApplicationCommands.js +79 -16
  54. package/dist/cliCommandNames.js +3 -0
  55. package/dist/cliElectronCommands.js +23 -33
  56. package/dist/cliObservationOptions.js +1 -1
  57. package/dist/cliOutput.js +56 -0
  58. package/dist/cliProcessCommands.js +32 -1
  59. package/dist/cliSetup.js +30 -21
  60. package/dist/contracts/applicationToolContracts.js +54 -7
  61. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  62. package/dist/contracts/electronToolContracts.js +53 -32
  63. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  64. package/dist/contracts/processCaptureExample.js +1 -0
  65. package/dist/contracts/replayMachineExample.js +37 -0
  66. package/dist/contracts/toolContractExamples.js +2 -0
  67. package/dist/contracts/toolContracts.js +5 -1
  68. package/dist/contracts/toolEffects.js +3 -0
  69. package/dist/contracts/toolOutputSchemaGroups.js +2 -0
  70. package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
  71. package/dist/doctorRuntime.js +41 -0
  72. package/dist/domain/analysisErrorPresentation.js +5 -3
  73. package/dist/domain/analysisErrorProjection.js +7 -1
  74. package/dist/domain/changedBehavior.js +5 -4
  75. package/dist/domain/completionLedgerGeneration.js +190 -0
  76. package/dist/domain/errors.js +4 -2
  77. package/dist/domain/evidenceCompletionLedger.js +113 -0
  78. package/dist/domain/hopperStartupFailure.js +9 -0
  79. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  80. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  81. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  82. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  83. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  84. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  85. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  86. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  87. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  88. package/dist/domain/javascriptSemanticCalls.js +269 -0
  89. package/dist/domain/javascriptSemanticGraph.js +356 -0
  90. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  91. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  92. package/dist/domain/javascriptSemanticIr.js +14 -1
  93. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  94. package/dist/domain/javascriptSemanticProjection.js +36 -4
  95. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  96. package/dist/domain/javascriptSemanticQuery.js +294 -0
  97. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  98. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  99. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  100. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  101. package/dist/domain/javascriptSemanticReturns.js +187 -0
  102. package/dist/domain/javascriptSemanticState.js +32 -0
  103. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  104. package/dist/domain/javascriptSemanticValues.js +54 -70
  105. package/dist/domain/processCapture.js +36 -0
  106. package/dist/domain/processCaptureValidation.js +101 -12
  107. package/dist/domain/processComparison.js +78 -25
  108. package/dist/domain/processObservation.js +153 -0
  109. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  110. package/dist/domain/processReactiveMatching.js +210 -0
  111. package/dist/domain/processReactiveRuntime.js +109 -0
  112. package/dist/domain/processReactiveScenario.js +361 -0
  113. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  114. package/dist/domain/processReactiveTransition.js +159 -0
  115. package/dist/domain/processScenario.js +12 -0
  116. package/dist/domain/processTraceComparison.js +43 -0
  117. package/dist/domain/processTraceDimensionProjection.js +50 -0
  118. package/dist/domain/processTraceEvaluation.js +312 -0
  119. package/dist/domain/processTraceSpecification.js +373 -0
  120. package/dist/domain/reconstructionVerification.js +5 -4
  121. package/dist/domain/replayMachine.js +18 -2
  122. package/dist/domain/replayMachineRun.js +216 -0
  123. package/dist/domain/replayMachineRuntime.js +88 -9
  124. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  125. package/dist/evaluation/CodexAgentEval.js +24 -0
  126. package/dist/generatedPackageMetadata.js +1 -1
  127. package/dist/ghidra/GhidraClient.js +13 -2
  128. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  129. package/dist/ghidra/GhidraLauncher.js +7 -3
  130. package/dist/ghidra/GhidraProvider.js +8 -1
  131. package/dist/hopper/BridgeLauncher.js +56 -14
  132. package/dist/hopper/HopperClient.js +23 -10
  133. package/dist/hopper/HopperDiagnostics.js +14 -0
  134. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  135. package/dist/hopper/HopperProvider.js +8 -1
  136. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  137. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  138. package/dist/main/transport.js +2 -0
  139. package/dist/mcpDoctor.js +310 -0
  140. package/dist/process/ProcessOwnership.js +200 -53
  141. package/dist/process/ProviderRunLineage.js +21 -0
  142. package/dist/server/createServer.js +4 -1
  143. package/dist/server/javascriptApplicationResult.js +22 -1
  144. package/dist/server/registerApplicationTools/characterization.js +3 -3
  145. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  146. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  147. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  148. package/dist/server/registerApplicationTools/coverage.js +3 -3
  149. package/dist/server/registerApplicationTools/helpers.js +37 -25
  150. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  151. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  152. package/dist/server/registerApplicationTools.js +4 -0
  153. package/dist/server/registerEvidenceResources.js +5 -1
  154. package/dist/server/registerProcessComparisonTool.js +17 -7
  155. package/dist/server/registerReplayMachineTool.js +20 -0
  156. package/dist/server/registerSessionTools.js +12 -7
  157. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  158. package/dist/serverIdentity.js +19 -0
  159. package/package.json +62 -30
  160. package/scripts/hopper-demo-x11.py +459 -61
  161. package/scripts/prepack.mjs +55 -0
  162. package/scripts/prepare.mjs +28 -0
  163. package/scripts/rea.mjs +26 -5
  164. package/skills/reverse-engineer-anything/SKILL.md +2 -2
  165. package/skills/reverse-engineer-anything/references/javascript-applications.md +10 -0
@@ -0,0 +1,218 @@
1
+ import { commitProcessReactiveProposal, createProcessReactiveSnapshot, reduceProcessReactiveScenario, } from "../domain/processReactiveRuntime.js";
2
+ const systemTimerHost = {
3
+ schedule: (callback, delayMs) => {
4
+ const timer = setTimeout(callback, delayMs);
5
+ return { cancel: () => clearTimeout(timer) };
6
+ },
7
+ };
8
+ /**
9
+ * Serializes live observations, deadlines, and effect commits for one run.
10
+ * Producer callbacks enqueue work without awaiting it, preventing reentrancy.
11
+ */
12
+ export class ProcessReactiveCoordinator {
13
+ #scenario;
14
+ #executor;
15
+ #timerHost;
16
+ #now;
17
+ #scenarioDeadlineAt;
18
+ #onDecision;
19
+ #snapshot;
20
+ #pendingInputs = [];
21
+ #tail = Promise.resolve();
22
+ #running = false;
23
+ #failure;
24
+ #stateTimer;
25
+ #scenarioTimer;
26
+ #stateDeadlineAt = 0;
27
+ #effectAbort;
28
+ #accepting = true;
29
+ #closed = false;
30
+ constructor(options) {
31
+ this.#scenario = options.scenario;
32
+ this.#executor = options.executor;
33
+ this.#timerHost = options.timerHost ?? systemTimerHost;
34
+ this.#now = options.now ?? Date.now;
35
+ this.#scenarioDeadlineAt = this.#now() + options.scenario.deadline_ms;
36
+ this.#onDecision = options.onDecision;
37
+ this.#snapshot = createProcessReactiveSnapshot(options.scenario);
38
+ this.#scheduleScenarioDeadline();
39
+ this.#scheduleStateDeadline();
40
+ }
41
+ /** Current immutable reducer snapshot after all completed turns. */
42
+ get snapshot() {
43
+ return this.#snapshot;
44
+ }
45
+ /** Enqueue producer work without creating a floating promise. */
46
+ enqueue(input) {
47
+ if (!this.#accepting)
48
+ return;
49
+ if (this.#interruptsEffects(input))
50
+ this.#effectAbort?.abort();
51
+ this.#enqueueOwned(input);
52
+ }
53
+ /** Submit one input and wait until every turn queued before it completes. */
54
+ async submit(input) {
55
+ this.enqueue(input);
56
+ await this.drain();
57
+ return this.#snapshot;
58
+ }
59
+ /** Wait for currently queued reducer/effect work and surface host failures. */
60
+ async drain() {
61
+ for (;;) {
62
+ const observedTail = this.#tail;
63
+ await observedTail;
64
+ if (observedTail === this.#tail)
65
+ break;
66
+ }
67
+ if (this.#failure !== undefined)
68
+ throw this.#failure;
69
+ }
70
+ /** Stop owned deadlines after queued work settles. */
71
+ async close() {
72
+ this.#accepting = false;
73
+ this.#clearTimers();
74
+ await this.drain();
75
+ this.#closed = true;
76
+ }
77
+ #enqueueOwned(input) {
78
+ if (this.#closed || this.#failure !== undefined)
79
+ return;
80
+ this.#pendingInputs.push(input);
81
+ this.#startWorker();
82
+ }
83
+ #startWorker() {
84
+ if (this.#running || this.#pendingInputs.length === 0)
85
+ return;
86
+ this.#running = true;
87
+ this.#tail = this.#runQueue()
88
+ .catch((cause) => this.#fail(cause))
89
+ .finally(() => {
90
+ this.#running = false;
91
+ if (this.#pendingInputs.length > 0)
92
+ this.#startWorker();
93
+ });
94
+ }
95
+ async #runQueue() {
96
+ for (;;) {
97
+ const input = this.#nextInput();
98
+ if (input === undefined)
99
+ return;
100
+ await this.#advance(input);
101
+ }
102
+ }
103
+ #nextInput() {
104
+ const terminalIndex = this.#pendingInputs.findIndex(({ kind }) => kind === "cancelled" || kind === "cleanup_failed");
105
+ if (terminalIndex >= 0)
106
+ return this.#pendingInputs.splice(terminalIndex, 1)[0];
107
+ let observationIndex = -1;
108
+ let captureOrder = Number.POSITIVE_INFINITY;
109
+ for (const [index, input] of this.#pendingInputs.entries())
110
+ if (input.kind === "observation" &&
111
+ input.observation.capture_order < captureOrder) {
112
+ observationIndex = index;
113
+ captureOrder = input.observation.capture_order;
114
+ }
115
+ return observationIndex >= 0
116
+ ? this.#pendingInputs.splice(observationIndex, 1)[0]
117
+ : this.#pendingInputs.shift();
118
+ }
119
+ #fail(cause) {
120
+ this.#failure ??= cause;
121
+ this.#accepting = false;
122
+ this.#closed = true;
123
+ this.#effectAbort?.abort();
124
+ this.#pendingInputs.length = 0;
125
+ this.#clearTimers();
126
+ }
127
+ async #advance(input) {
128
+ const before = this.#snapshot;
129
+ let decision = reduceProcessReactiveScenario(this.#scenario, before, input);
130
+ let effectResults = [];
131
+ if (decision.kind === "proposal") {
132
+ const effectAbort = new AbortController();
133
+ this.#effectAbort = effectAbort;
134
+ try {
135
+ effectResults = await this.#executor.execute(decision.effects, effectAbort.signal);
136
+ }
137
+ catch (cause) {
138
+ if (effectAbort.signal.aborted)
139
+ return;
140
+ throw cause;
141
+ }
142
+ finally {
143
+ if (this.#effectAbort === effectAbort)
144
+ this.#effectAbort = undefined;
145
+ }
146
+ if (effectAbort.signal.aborted)
147
+ return;
148
+ const expired = this.#expiredDeadlineInput();
149
+ if (expired !== undefined) {
150
+ this.#enqueueOwned(expired);
151
+ return;
152
+ }
153
+ decision = commitProcessReactiveProposal(this.#scenario, decision, effectResults);
154
+ }
155
+ this.#snapshot = decision.snapshot;
156
+ this.#onDecision?.(decision);
157
+ if (this.#snapshot.status === "finished")
158
+ this.#clearTimers();
159
+ else if (this.#accepting &&
160
+ (before.active_state !== this.#snapshot.active_state ||
161
+ before.state_entry_capture_order !==
162
+ this.#snapshot.state_entry_capture_order))
163
+ this.#scheduleStateDeadline();
164
+ for (const result of effectResults)
165
+ if (result.status === "succeeded")
166
+ this.#enqueueOwned({
167
+ kind: "observation",
168
+ observation: result.observation,
169
+ });
170
+ }
171
+ #interruptsEffects(input) {
172
+ if (input.kind === "cancelled" ||
173
+ input.kind === "cleanup_failed" ||
174
+ input.kind === "scenario_deadline")
175
+ return true;
176
+ return (input.kind === "state_deadline" &&
177
+ input.state_id === this.#snapshot.active_state &&
178
+ input.state_entry_capture_order ===
179
+ this.#snapshot.state_entry_capture_order);
180
+ }
181
+ #expiredDeadlineInput() {
182
+ const now = this.#now();
183
+ if (now >= this.#scenarioDeadlineAt)
184
+ return { kind: "scenario_deadline" };
185
+ if (now < this.#stateDeadlineAt)
186
+ return undefined;
187
+ return {
188
+ kind: "state_deadline",
189
+ state_id: this.#snapshot.active_state,
190
+ state_entry_capture_order: this.#snapshot.state_entry_capture_order,
191
+ };
192
+ }
193
+ #scheduleScenarioDeadline() {
194
+ if (!this.#accepting)
195
+ return;
196
+ this.#scenarioTimer = this.#timerHost.schedule(() => this.enqueue({ kind: "scenario_deadline" }), this.#scenario.deadline_ms);
197
+ }
198
+ #scheduleStateDeadline() {
199
+ if (!this.#accepting)
200
+ return;
201
+ this.#stateTimer?.cancel();
202
+ const state = this.#scenario.states.find(({ id }) => id === this.#snapshot.active_state);
203
+ if (state === undefined)
204
+ return;
205
+ this.#stateDeadlineAt = this.#now() + state.deadline_ms;
206
+ const identity = {
207
+ state_id: state.id,
208
+ state_entry_capture_order: this.#snapshot.state_entry_capture_order,
209
+ };
210
+ this.#stateTimer = this.#timerHost.schedule(() => this.enqueue({ kind: "state_deadline", ...identity }), state.deadline_ms);
211
+ }
212
+ #clearTimers() {
213
+ this.#stateTimer?.cancel();
214
+ this.#scenarioTimer?.cancel();
215
+ this.#stateTimer = undefined;
216
+ this.#scenarioTimer = undefined;
217
+ }
218
+ }
@@ -0,0 +1,133 @@
1
+ import { createProcessObservation } from "../domain/processObservation.js";
2
+ const elapsed = (host) => Math.max(0, host.now() - host.startedAtMs);
3
+ const interactionData = (action) => {
4
+ if (action.type === "send_input")
5
+ return action.sensitive
6
+ ? `<redacted-input:${String(Buffer.byteLength(action.data))}-bytes>`
7
+ : action.data;
8
+ if (action.type === "resize")
9
+ return `${String(action.columns)}x${String(action.rows)}`;
10
+ return action.signal;
11
+ };
12
+ const interactionType = (action) => {
13
+ if (action.type === "send_input")
14
+ return "input";
15
+ if (action.type === "resize")
16
+ return "resize";
17
+ return "signal";
18
+ };
19
+ const performTerminalAction = (terminal, renderer, action, atMs) => {
20
+ if (action.type === "send_input")
21
+ terminal.write(action.data);
22
+ else if (action.type === "resize") {
23
+ terminal.resize(action.columns, action.rows);
24
+ renderer.resize(action.columns, action.rows, atMs);
25
+ }
26
+ else
27
+ terminal.kill(action.signal);
28
+ };
29
+ const recordInteraction = (host, action, atMs, outcome) => {
30
+ const index = host.interactions.length;
31
+ const event = {
32
+ sequence: index,
33
+ scheduled_at_ms: atMs,
34
+ dispatched_at_ms: atMs,
35
+ type: interactionType(action),
36
+ data: interactionData(action),
37
+ outcome,
38
+ };
39
+ host.interactions.push(event);
40
+ const location = host.journal.record("interaction_events", index);
41
+ if (outcome !== "dispatched")
42
+ return { status: "rejected" };
43
+ return {
44
+ status: "succeeded",
45
+ observation: createProcessObservation({
46
+ source: "interaction",
47
+ source_sequence: index,
48
+ captured_at_ms: atMs,
49
+ subject_id: null,
50
+ location,
51
+ payload: event,
52
+ }),
53
+ };
54
+ };
55
+ const executeTerminalAction = (host, action) => {
56
+ if (action.type === "send_signal" && action.target.kind !== "root")
57
+ return { status: "rejected" };
58
+ const terminal = host.terminal();
59
+ if (terminal === undefined)
60
+ return { status: "target_lost" };
61
+ const atMs = elapsed(host);
62
+ let outcome = "dispatched";
63
+ try {
64
+ performTerminalAction(terminal, host.renderer, action, atMs);
65
+ }
66
+ catch {
67
+ outcome = "failed";
68
+ }
69
+ return recordInteraction(host, action, atMs, outcome);
70
+ };
71
+ const executeCheckpoint = async (host, action, signal) => {
72
+ try {
73
+ const { index, checkpoint } = await host.checkpoints.captureAndRead(action.name, signal);
74
+ const location = host.journal.entries.findLast((entry) => entry.collection === "filesystem_checkpoints" && entry.index === index);
75
+ if (location === undefined)
76
+ return { status: "rejected" };
77
+ return {
78
+ status: "succeeded",
79
+ observation: createProcessObservation({
80
+ source: "filesystem",
81
+ source_sequence: index,
82
+ captured_at_ms: checkpoint.at_ms,
83
+ subject_id: `checkpoint:${checkpoint.name}`,
84
+ location,
85
+ payload: checkpoint,
86
+ }),
87
+ };
88
+ }
89
+ catch {
90
+ return { status: "rejected" };
91
+ }
92
+ };
93
+ /** Execute proposed effects in order and stop after the first failure. */
94
+ export const executeProcessReactiveEffects = async (host, actions, signal) => {
95
+ const results = [];
96
+ for (const action of actions) {
97
+ if (signal.aborted)
98
+ return results;
99
+ const result = action.type === "checkpoint"
100
+ ? await executeCheckpoint(host, action, signal)
101
+ : executeTerminalAction(host, action);
102
+ results.push(result);
103
+ if (result.status !== "succeeded")
104
+ break;
105
+ }
106
+ return results;
107
+ };
108
+ const triggerSources = (trigger) => {
109
+ if (trigger.kind === "terminal_text")
110
+ return [];
111
+ if (trigger.kind === "event")
112
+ return ["terminal_raw", "interaction", "filesystem"].includes(trigger.source)
113
+ ? []
114
+ : [trigger.source];
115
+ return trigger.kind === "repeat"
116
+ ? triggerSources(trigger.trigger)
117
+ : trigger.triggers.flatMap(triggerSources);
118
+ };
119
+ /** Return unsupported sources, selectors, and checkpoint collisions pre-launch. */
120
+ export const unsupportedProcessReactiveFeatures = (scenario, reservedCheckpointNames = new Set([
121
+ "before",
122
+ "after_settlement",
123
+ ])) => [
124
+ ...scenario.states.flatMap((state) => state.on.flatMap((transition) => triggerSources(transition.when).map((source) => `${transition.id}:source:${source}`))),
125
+ ...scenario.states.flatMap((state) => state.on.flatMap((transition) => transition.actions.flatMap((action) => {
126
+ if (action.type === "send_signal" && action.target.kind !== "root")
127
+ return [`${transition.id}:target:${action.target.kind}`];
128
+ if (action.type === "checkpoint" &&
129
+ reservedCheckpointNames.has(action.name))
130
+ return [`${transition.id}:checkpoint:${action.name}`];
131
+ return [];
132
+ }))),
133
+ ];
@@ -0,0 +1,20 @@
1
+ import { createProcessObservation } from "../domain/processObservation.js";
2
+ /** Subscribe a reactive coordinator to normalized raw PTY journal records. */
3
+ export const subscribeProcessReactiveTerminalObservations = (options) => options.journal.subscribe((entry) => {
4
+ if (entry.collection !== "frames")
5
+ return;
6
+ const frame = options.frames[entry.index];
7
+ if (frame === undefined)
8
+ return;
9
+ options.sink.enqueue({
10
+ kind: "observation",
11
+ observation: createProcessObservation({
12
+ source: "terminal_raw",
13
+ source_sequence: frame.sequence,
14
+ captured_at_ms: frame.at_ms,
15
+ subject_id: null,
16
+ location: entry,
17
+ payload: frame,
18
+ }),
19
+ });
20
+ });
@@ -366,7 +366,9 @@ export const startProcessSampler = (options) => {
366
366
  continue;
367
367
  }
368
368
  lastObservations.set(value.pid, observation);
369
+ const index = samples.length;
369
370
  samples.push(value);
371
+ options.recordEvent?.("process_samples", index);
370
372
  }
371
373
  })
372
374
  .catch((cause) => {
@@ -86,7 +86,7 @@ export class SessionProviderRouter {
86
86
  compatibility: {},
87
87
  binding: null,
88
88
  selection: undefined,
89
- createClient: (target) => createLegacyClient(runtime, target, undefined),
89
+ createClient: (target, context) => createLegacyClient(runtime, target, undefined, context),
90
90
  };
91
91
  }
92
92
  /** Resolve target routes before any provider client is created. */
@@ -122,7 +122,7 @@ export class SessionProviderRouter {
122
122
  compatibility: normalized.value.compatibility,
123
123
  binding: null,
124
124
  selection: undefined,
125
- createClient: (openedTarget) => createLegacyClient(runtime, openedTarget, normalized.value.profile ?? undefined),
125
+ createClient: (openedTarget, context) => createLegacyClient(runtime, openedTarget, normalized.value.profile ?? undefined, context),
126
126
  });
127
127
  }
128
128
  /** Candidate status authoritative for the active or target-free route. */
@@ -156,12 +156,13 @@ const selectableRoute = (auxiliaryProviders, selection) => {
156
156
  compatibility: structuredClone(binding?.compatibility ?? {}),
157
157
  binding,
158
158
  selection,
159
- createClient: (target) => provider?.createClient(target, binding?.profile) ?? emptyClient(identity),
159
+ createClient: (target, context) => provider?.createClient(target, binding?.profile, context) ??
160
+ emptyClient(identity),
160
161
  };
161
162
  };
162
- const createLegacyClient = (runtime, target, profile) => typeof runtime.provider === "function"
163
- ? runtime.provider(target, profile)
164
- : runtime.provider.createClient(target, profile);
163
+ const createLegacyClient = (runtime, target, profile, context) => typeof runtime.provider === "function"
164
+ ? runtime.provider(target, profile, context)
165
+ : runtime.provider.createClient(target, profile, context);
165
166
  const emptyClient = (identity) => ({
166
167
  execute: (operation) => operation === "health"
167
168
  ? Promise.resolve(ok({
@@ -13,6 +13,8 @@ import { discoverSetupActions } from "./SetupPlan.js";
13
13
  import { clientConfigurationAligned, configureJsonClient, configureTomlClient, inspectClientConfiguration, } from "./SetupClientConfiguration.js";
14
14
  export { configureJsonClient, configureTomlClient };
15
15
  import { setupInstallFailure, } from "./SetupInstallFailure.js";
16
+ import { providerRegistrationEnvironment, registrationPermissionEnvironment, } from "./SetupRegistrationEnvironment.js";
17
+ export { registrationPermissionEnvironment } from "./SetupRegistrationEnvironment.js";
16
18
  export { canonicalSkillNeedsInstall, installCanonicalSkill };
17
19
  const registrationCommand = () => process.env.npm_command === "exec"
18
20
  ? PRODUCT_IDENTITY.mcpCommand.split(" ")
@@ -22,7 +24,7 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
22
24
  const appliedActions = [];
23
25
  let plannedActions = [];
24
26
  const clients = {};
25
- const fail = (remediation) => setupFailure(remediation, [host, plannedActions, appliedActions, clients]);
27
+ const fail = (remediation) => setupFailure(remediation, [host, plannedActions, appliedActions, clients], options.readinessScope);
26
28
  const unsupported = await hostRemediation(host, options.installHopper);
27
29
  if (unsupported !== undefined)
28
30
  return fail(unsupported);
@@ -36,6 +38,7 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
36
38
  proposeHopper: options.proposeHopper ?? true,
37
39
  selectedClientIds: options.clientIds,
38
40
  installSkillSelection: options.installSkill,
41
+ doctorScope: options.readinessScope,
39
42
  });
40
43
  if (discovery.blocker !== undefined)
41
44
  return fail(discovery.blocker);
@@ -61,6 +64,7 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
61
64
  clients,
62
65
  detectedClients,
63
66
  providerEnvironment,
67
+ doctorScope: options.readinessScope,
64
68
  });
65
69
  if ("failure" in install)
66
70
  return install.failure;
@@ -82,7 +86,7 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
82
86
  if (selection.installSkill &&
83
87
  !(await installSkillAction(host, options, appliedActions)))
84
88
  return fail("REA analysis skill could not be installed or verified. Check permissions for `~/.agents/skills`, then rerun setup.");
85
- const doctor = await host.doctor();
89
+ const doctor = await host.doctor(options.readinessScope);
86
90
  const remediation = finalSetupRemediation(host.platform, appliedActions.includes("installed_hopper"), doctor.healthy, hopperPath);
87
91
  return {
88
92
  status: remediation === undefined ? "ready" : "needs_human",
@@ -143,7 +147,7 @@ const installHopperAction = async (input) => {
143
147
  plannedActions: input.plannedActions,
144
148
  appliedActions: input.appliedActions,
145
149
  clients: input.clients,
146
- doctor: await input.host.doctor(),
150
+ doctor: await input.host.doctor(input.doctorScope),
147
151
  code: installed.code,
148
152
  remediation: installed.remediation,
149
153
  },
@@ -192,12 +196,12 @@ const installSkillAction = async (host, options, appliedActions) => {
192
196
  });
193
197
  return true;
194
198
  };
195
- const setupFailure = async (remediation, [host, plannedActions, appliedActions, clients]) => ({
199
+ const setupFailure = async (remediation, [host, plannedActions, appliedActions, clients], scope) => ({
196
200
  status: "needs_human",
197
201
  plannedActions,
198
202
  appliedActions,
199
203
  clients,
200
- doctor: await host.doctor(),
204
+ doctor: await host.doctor(scope),
201
205
  remediation,
202
206
  });
203
207
  const emitProgress = (options, event) => options.onProgress?.(event);
@@ -250,6 +254,7 @@ export const systemSetupHost = (doctorHost = systemDoctorHost()) => {
250
254
  const diagnosis = await runDoctor(undefined, doctorHost);
251
255
  return {
252
256
  ...providerRegistrationEnvironment(diagnosis.providerInspections ?? []),
257
+ ...registrationPermissionEnvironment(process.env),
253
258
  ...(diagnosis.hopperPath === undefined
254
259
  ? {}
255
260
  : { HOPPER_LAUNCHER_PATH: diagnosis.hopperPath }),
@@ -273,7 +278,7 @@ export const systemSetupHost = (doctorHost = systemDoctorHost()) => {
273
278
  inspectClientConfiguration: inspectClientConfiguration,
274
279
  skillNeedsInstall: () => canonicalSkillNeedsInstall(homedir()),
275
280
  installSkill: () => installCanonicalSkill(homedir()),
276
- doctor: () => runDoctor(undefined, doctorHost),
281
+ doctor: (scope) => runDoctor(undefined, doctorHost, scope),
277
282
  };
278
283
  };
279
284
  /** Detect supported agents from stable per-user installation markers. */
@@ -294,7 +299,3 @@ const exists = async (path) => {
294
299
  return false;
295
300
  }
296
301
  };
297
- const providerRegistrationEnvironment = (inspections) => Object.fromEntries(inspections
298
- .filter(({ available }) => available)
299
- .flatMap(({ registrationEnvironment }) => Object.entries(registrationEnvironment))
300
- .sort(([left], [right]) => left.localeCompare(right)));
@@ -59,7 +59,7 @@ const setupPlan = (input) => [
59
59
  /** Discover and preflight the complete set of selectable setup actions. */
60
60
  export const discoverSetupActions = async (input) => {
61
61
  const { host, providerEnvironment, command } = input;
62
- const initialDoctor = await host.doctor();
62
+ const initialDoctor = await host.doctor(input.doctorScope);
63
63
  const linuxHopperRepairNeeded = initialDoctor.checks.some(({ name, ok, detail }) => !ok &&
64
64
  (name === "hopper-demo-runtime" ||
65
65
  (name === "hopper-version" && detail === "/opt/hopper/bin/Hopper")));
@@ -0,0 +1,12 @@
1
+ /** Select explicit non-secret permission settings safe for managed MCP registrations. */
2
+ export const registrationPermissionEnvironment = (environment) => {
3
+ const investigationRoots = environment.REA_INVESTIGATION_INPUT_ROOTS_JSON;
4
+ return investigationRoots === undefined
5
+ ? {}
6
+ : { REA_INVESTIGATION_INPUT_ROOTS_JSON: investigationRoots };
7
+ };
8
+ /** Select exact non-secret settings published by available providers. */
9
+ export const providerRegistrationEnvironment = (inspections) => Object.fromEntries(inspections
10
+ .filter(({ available }) => available)
11
+ .flatMap(({ registrationEnvironment }) => Object.entries(registrationEnvironment))
12
+ .sort(([left], [right]) => left.localeCompare(right)));
@@ -77,8 +77,9 @@ export class TerminalRenderer {
77
77
  return;
78
78
  }
79
79
  this.#capturedBytes += bytes;
80
+ const sequence = this.#frames.length;
80
81
  this.#frames.push({
81
- sequence: this.#frames.length,
82
+ sequence,
82
83
  at_ms: atMs,
83
84
  columns: this.#terminal.cols,
84
85
  rows: this.#terminal.rows,
@@ -88,5 +89,6 @@ export class TerminalRenderer {
88
89
  lines,
89
90
  serialized_state: serializedState,
90
91
  });
92
+ this.options.recordEvent?.("rendered_frames", sequence);
91
93
  }
92
94
  }
@@ -40,25 +40,58 @@ export const discoverCdpEndpoint = async (endpoint, operation, signal) => {
40
40
  targets: targets.map((target) => ({
41
41
  id: target.id,
42
42
  type: target.type,
43
- title: sanitizeTargetTitle(target.title),
43
+ title: sanitizeTargetTitle(target.title, target.url),
44
44
  url: target.url,
45
45
  attached: target.attached,
46
46
  ...targetWebSocket(endpoint, target, operation),
47
47
  })),
48
48
  };
49
49
  };
50
- const sanitizeTargetTitle = (value) => {
50
+ const sanitizeTargetTitle = (value, targetUrl) => {
51
51
  let url;
52
52
  try {
53
53
  url = new URL(value);
54
54
  }
55
55
  catch {
56
- return value;
56
+ return sanitizeTargetUrlAlias(value, targetUrl);
57
57
  }
58
58
  return url.protocol === "http:" || url.protocol === "https:"
59
59
  ? sanitizeBrowserUrl(value).url.slice(0, 16_384)
60
- : value;
60
+ : sanitizeTargetUrlAlias(value, targetUrl);
61
+ };
62
+ const sanitizeTargetUrlAlias = (value, targetUrl) => {
63
+ let parsed;
64
+ try {
65
+ parsed = new URL(targetUrl);
66
+ }
67
+ catch {
68
+ return value;
69
+ }
70
+ const markers = [parsed.origin, `//${parsed.host}`, parsed.host];
71
+ if (parsed.pathname !== "/")
72
+ markers.push(parsed.pathname);
73
+ const candidatePattern = new RegExp(`(?:${markers.map(escapeRegExp).join("|")})[^\\s<>"']*`, "gu");
74
+ return value
75
+ .replace(candidatePattern, (candidate) => sanitizeSameOriginTitleUrl(candidate, parsed))
76
+ .slice(0, 16_384);
77
+ };
78
+ const sanitizeSameOriginTitleUrl = (candidate, target) => {
79
+ let parsed;
80
+ try {
81
+ parsed = candidate.startsWith("//")
82
+ ? new URL(`${target.protocol}${candidate}`)
83
+ : candidate.startsWith(target.host)
84
+ ? new URL(`${target.protocol}//${candidate}`)
85
+ : new URL(candidate, target.origin);
86
+ }
87
+ catch {
88
+ return candidate;
89
+ }
90
+ return parsed.origin === target.origin && parsed.search !== ""
91
+ ? sanitizeBrowserUrl(parsed.href).url
92
+ : candidate;
61
93
  };
94
+ const escapeRegExp = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/gu, "\\$&");
62
95
  /** Select a browser attachment socket or a direct socket for one page target. */
63
96
  export const cdpTargetWebSocket = (discovery, target, operation) => {
64
97
  const webSocket = availableCdpTargetWebSocket(discovery, target);
@@ -26,11 +26,13 @@ const promptCatalog = PROMPT_CONTRACTS.map((contract) => ({
26
26
  arguments: contract.arguments,
27
27
  steps: contract.steps,
28
28
  })).sort((left, right) => left.name.localeCompare(right.name));
29
- const resourceCatalog = [
29
+ /** Canonical fixed resources exposed by every target-free MCP session. */
30
+ export const MCP_RESOURCE_CATALOG = [
30
31
  { name: "server-identity", uri: "rea://server/identity" },
31
32
  { name: "active-residual-unknowns", uri: "rea://unknowns/active" },
32
33
  ];
33
- const resourceTemplateCatalog = [
34
+ /** Canonical resource templates exposed by every target-free MCP session. */
35
+ export const MCP_RESOURCE_TEMPLATE_CATALOG = [
34
36
  { name: "session-evidence", uri_template: "rea://evidence/{evidenceId}" },
35
37
  {
36
38
  name: "evidence-section",
@@ -57,6 +59,10 @@ const resourceTemplateCatalog = [
57
59
  name: "reconstruction-coverage-revision",
58
60
  uri_template: "rea://reconstruction-coverage/{workspaceId}/revision/{revision}",
59
61
  },
62
+ {
63
+ name: "javascript-application-graph-page",
64
+ uri_template: "rea://evidence/{evidenceId}/application-graph/{collection}/offset/{offset}/limit/{limit}",
65
+ },
60
66
  ];
61
67
  const digest = (value) => {
62
68
  const encoded = canonicalize(value);
@@ -70,19 +76,22 @@ export const CATALOG_IDENTITY = {
70
76
  cli_commands: CLI_COMMAND_NAMES.length,
71
77
  mcp_tools: toolCatalog.length,
72
78
  mcp_prompts: promptCatalog.length,
73
- mcp_resources: resourceCatalog.length,
74
- mcp_resource_templates: resourceTemplateCatalog.length,
79
+ mcp_resources: MCP_RESOURCE_CATALOG.length,
80
+ mcp_resource_templates: MCP_RESOURCE_TEMPLATE_CATALOG.length,
75
81
  },
76
82
  digests: {
77
83
  tools_sha256: digest(toolCatalog),
78
84
  prompts_sha256: digest(promptCatalog),
79
- resources_sha256: digest({ resourceCatalog, resourceTemplateCatalog }),
85
+ resources_sha256: digest({
86
+ resourceCatalog: MCP_RESOURCE_CATALOG,
87
+ resourceTemplateCatalog: MCP_RESOURCE_TEMPLATE_CATALOG,
88
+ }),
80
89
  combined_sha256: digest({
81
90
  cli: CLI_COMMAND_NAMES,
82
91
  tools: toolCatalog,
83
92
  prompts: promptCatalog,
84
- resources: resourceCatalog,
85
- resource_templates: resourceTemplateCatalog,
93
+ resources: MCP_RESOURCE_CATALOG,
94
+ resource_templates: MCP_RESOURCE_TEMPLATE_CATALOG,
86
95
  }),
87
96
  },
88
97
  tools: toolCatalog.map(({ name, surface, effects, annotations }) => ({