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
@@ -18,23 +18,24 @@ const MAX_CANONICAL_BYTES = 32 * 1024 * 1024;
18
18
  const providers = {
19
19
  behavioral: {
20
20
  operation: "compare_process_captures",
21
- predicate: "rea.process-comparison/v3",
21
+ predicates: ["rea.process-comparison/v3", "rea.process-comparison/v4"],
22
22
  id: "rea-process",
23
23
  name: "REA deterministic process harness",
24
24
  },
25
25
  "structural-function": {
26
26
  operation: "compare_functions",
27
- predicate: "rea.function-comparison/v1",
27
+ predicates: ["rea.function-comparison/v1"],
28
28
  id: "rea-function-comparison",
29
29
  name: "REA function comparison",
30
30
  },
31
31
  "structural-artifact": {
32
32
  operation: "compare_artifacts",
33
- predicate: "rea.artifact-comparison/v1",
33
+ predicates: ["rea.artifact-comparison/v1"],
34
34
  id: "rea-artifact-comparison",
35
35
  name: "REA artifact comparison",
36
36
  },
37
37
  };
38
+ const acceptsPredicate = (expected, actual) => expected.some((predicate) => predicate === actual);
38
39
  /** Verify only explicitly declared claims; no global equivalence is inferred. */
39
40
  export const verifyReconstruction = (specificationInput, bundleInput, offset, limit) => {
40
41
  const specification = reconstructionSpecificationSchema.parse(specificationInput);
@@ -157,7 +158,7 @@ const validateComparisonIdentity = (claim, evidence) => {
157
158
  parseEvidence(evidence);
158
159
  const expected = providers[claim.kind];
159
160
  if (evidence.operation !== expected.operation ||
160
- evidence.predicate_type !== expected.predicate ||
161
+ !acceptsPredicate(expected.predicates, evidence.predicate_type) ||
161
162
  evidence.provider.id !== expected.id ||
162
163
  evidence.provider.name !== expected.name ||
163
164
  evidence.provider.version !== (claim.kind === "behavioral" ? "3" : "1") ||
@@ -14,8 +14,17 @@ const sensitiveHeaderNames = new Set([
14
14
  "proxy-authorization",
15
15
  "set-cookie",
16
16
  ]);
17
+ const headerNameSchema = z
18
+ .string()
19
+ .min(1)
20
+ .max(256)
21
+ .regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u);
22
+ const headerValueSchema = z
23
+ .string()
24
+ .max(8_192)
25
+ .regex(/^[\t\x20-\x7e\x80-\xff]*$/u);
17
26
  const replayHeaderMatchersSchema = z
18
- .record(z.string().min(1).max(256), z.string().max(8_192))
27
+ .record(headerNameSchema, headerValueSchema)
19
28
  .superRefine((headers, context) => {
20
29
  const names = Object.keys(headers).map((name) => name.toLowerCase());
21
30
  if (new Set(names).size !== names.length)
@@ -77,7 +86,7 @@ const replayActionSchema = z.discriminatedUnion("type", [
77
86
  z.strictObject({
78
87
  type: z.literal("http_response"),
79
88
  status: z.number().int().min(100).max(599),
80
- headers: z.record(z.string(), z.string()).default({}),
89
+ headers: z.record(headerNameSchema, headerValueSchema).default({}),
81
90
  body: z.string().max(1_000_000),
82
91
  }),
83
92
  z.strictObject({
@@ -189,6 +198,13 @@ const validateReplayVariables = (machine, context) => {
189
198
  const capturedVariables = new Map();
190
199
  for (const [transitionIndex, transition] of machine.transitions.entries()) {
191
200
  for (const capture of transition.captures) {
201
+ if (capture.sensitive && capture.value.source === "action_json")
202
+ context.addIssue({
203
+ code: "custom",
204
+ message: "sensitive replay captures cannot read literal action JSON persisted in the replay plan",
205
+ path: ["transitions", transitionIndex, "captures"],
206
+ input: machine,
207
+ });
192
208
  const sensitivity = capturedVariables.get(capture.variable);
193
209
  if (sensitivity !== undefined && sensitivity !== capture.sensitive)
194
210
  context.addIssue({
@@ -0,0 +1,216 @@
1
+ import { z } from "zod";
2
+ import { replayMachineSchema } from "./replayMachine.js";
3
+ import { replayMachineEventSchema, ReplayMachineRuntime, } from "./replayMachineRuntime.js";
4
+ const refusalOutcomeSchema = z.enum([
5
+ "unmatched",
6
+ "invalid_state",
7
+ "guard_failed",
8
+ "transition_exhausted",
9
+ "invalid_capture",
10
+ "unexpected_reconnect",
11
+ "limit_exhausted",
12
+ ]);
13
+ const MAX_DIRECT_REPLAY_EVENTS = 10_000;
14
+ const MAX_DIRECT_REPLAY_ACTION_BYTES = 4 * 1_024 * 1_024;
15
+ const MAX_DIRECT_REPLAY_ACTION_FIELDS = 1_024;
16
+ const MAX_DIRECT_REPLAY_SENSITIVE_CAPTURES = 1_024;
17
+ const actionFieldCount = (machine) => machine.transitions.reduce((total, transition) => total +
18
+ transition.actions.reduce((fields, action) => fields +
19
+ (action.type === "http_response"
20
+ ? Object.keys(action.headers).length + 1
21
+ : action.type === "websocket_send"
22
+ ? 1
23
+ : 0), 0), 0);
24
+ const maximumSensitiveCapturesPerEvent = (machine) => Math.max(0, ...machine.transitions.map(({ captures }) => captures.filter(({ sensitive }) => sensitive).length));
25
+ /** Complete declarative input for one direct replay-machine run. */
26
+ export const replayMachineRunInputSchema = z
27
+ .strictObject({
28
+ machine: replayMachineSchema,
29
+ events: z.array(replayMachineEventSchema).max(MAX_DIRECT_REPLAY_EVENTS),
30
+ })
31
+ .superRefine(({ machine, events }, context) => {
32
+ const actionBytes = Buffer.byteLength(JSON.stringify(machine.transitions.map(({ id, actions }) => ({ id, actions }))));
33
+ if (actionBytes > MAX_DIRECT_REPLAY_ACTION_BYTES)
34
+ context.addIssue({
35
+ code: "custom",
36
+ path: ["machine", "transitions"],
37
+ message: "direct replay actions exceed the 4 MiB output boundary",
38
+ input: machine.transitions,
39
+ });
40
+ if (actionFieldCount(machine) > MAX_DIRECT_REPLAY_ACTION_FIELDS)
41
+ context.addIssue({
42
+ code: "custom",
43
+ path: ["machine", "transitions"],
44
+ message: "direct replay actions exceed the 1,024-field output boundary",
45
+ input: machine.transitions,
46
+ });
47
+ if (maximumSensitiveCapturesPerEvent(machine) * events.length >
48
+ MAX_DIRECT_REPLAY_SENSITIVE_CAPTURES)
49
+ context.addIssue({
50
+ code: "custom",
51
+ path: ["events"],
52
+ message: "direct replay events exceed the 1,024 sensitive-capture operation boundary",
53
+ input: events,
54
+ });
55
+ });
56
+ const replayMachineRunDecisionSchema = z.strictObject({
57
+ event_sequence: z.number().int().nonnegative(),
58
+ outcome: z.union([z.literal("matched"), refusalOutcomeSchema]),
59
+ transition_sequence: z.number().int().nonnegative().nullable(),
60
+ });
61
+ const replayRunActionSchema = z.discriminatedUnion("type", [
62
+ z.strictObject({
63
+ type: z.literal("http_response"),
64
+ status: z.number().int().min(100).max(599),
65
+ headers: z.record(z
66
+ .string()
67
+ .min(1)
68
+ .max(256)
69
+ .regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u), z.string().max(8_192)),
70
+ body: z.string().max(1_000_000),
71
+ }),
72
+ z.strictObject({
73
+ type: z.literal("websocket_send"),
74
+ data: z.string().max(1_000_000),
75
+ }),
76
+ z.strictObject({ type: z.literal("disconnect") }),
77
+ z.strictObject({
78
+ type: z.literal("delay"),
79
+ duration_ms: z.number().int().nonnegative().max(30_000),
80
+ }),
81
+ ]);
82
+ const replayRunIdentifierSchema = z
83
+ .string()
84
+ .regex(/^[A-Za-z][A-Za-z0-9_-]{0,63}$/u);
85
+ const replayMachineRunJournalEntrySchema = z.strictObject({
86
+ sequence: z.number().int().nonnegative(),
87
+ at_ms: z.number().int().nonnegative(),
88
+ transition_id: replayRunIdentifierSchema,
89
+ state_before: replayRunIdentifierSchema,
90
+ state_after: replayRunIdentifierSchema,
91
+ captured_aliases: z
92
+ .array(z.strictObject({
93
+ name: replayRunIdentifierSchema,
94
+ sensitive: z.boolean(),
95
+ }))
96
+ .max(32),
97
+ });
98
+ /** Capture-value-free result of directly evaluating a finite replay machine. */
99
+ export const replayMachineRunOutputSchema = z.strictObject({
100
+ schema_version: z.literal(1),
101
+ initial_state: replayRunIdentifierSchema,
102
+ final_state: replayRunIdentifierSchema,
103
+ terminal: z.boolean(),
104
+ decisions: z
105
+ .array(replayMachineRunDecisionSchema)
106
+ .max(MAX_DIRECT_REPLAY_EVENTS),
107
+ transition_journal: z
108
+ .array(replayMachineRunJournalEntrySchema)
109
+ .max(MAX_DIRECT_REPLAY_EVENTS),
110
+ transition_actions: z
111
+ .array(z.strictObject({
112
+ transition_id: replayRunIdentifierSchema,
113
+ actions: z.array(replayRunActionSchema).max(32),
114
+ }))
115
+ .max(2_000),
116
+ limits: z.strictObject({
117
+ configured: z.strictObject({
118
+ transitions: z.number().int().positive(),
119
+ connections: z.number().int().positive(),
120
+ messages: z.number().int().positive(),
121
+ bytes: z.number().int().positive(),
122
+ duration_ms: z.number().int().positive(),
123
+ }),
124
+ usage: z.strictObject({
125
+ offered_events: z.number().int().nonnegative(),
126
+ admitted_events: z.number().int().nonnegative(),
127
+ transitions: z.number().int().nonnegative(),
128
+ connections: z.number().int().nonnegative(),
129
+ messages: z.number().int().nonnegative(),
130
+ bytes: z.number().int().nonnegative(),
131
+ last_at_ms: z.number().int().nonnegative(),
132
+ }),
133
+ }),
134
+ });
135
+ const redactAction = (runtime, action) => {
136
+ switch (action.type) {
137
+ case "http_response":
138
+ return {
139
+ ...action,
140
+ headers: Object.fromEntries(Object.entries(action.headers).map(([name, value]) => [
141
+ name,
142
+ runtime.redactField(value),
143
+ ])),
144
+ body: runtime.redactField(action.body),
145
+ };
146
+ case "websocket_send":
147
+ return { ...action, data: runtime.redactField(action.data) };
148
+ case "delay":
149
+ case "disconnect":
150
+ return action;
151
+ }
152
+ };
153
+ /** Evaluate every supplied event in order without opening sockets or processes. */
154
+ export const runReplayMachine = (input) => {
155
+ const runtime = new ReplayMachineRuntime(input.machine);
156
+ const decisions = [];
157
+ const transitionJournal = [];
158
+ const usedTransitionIds = new Set();
159
+ for (const [eventSequence, event] of input.events.entries()) {
160
+ const decision = runtime.dispatch(event);
161
+ decisions.push({
162
+ event_sequence: eventSequence,
163
+ outcome: decision.outcome,
164
+ transition_sequence: decision.transition?.sequence ?? null,
165
+ });
166
+ if (decision.outcome !== "matched")
167
+ continue;
168
+ usedTransitionIds.add(decision.transition.transition_id);
169
+ const definition = input.machine.transitions.find(({ id }) => id === decision.transition.transition_id);
170
+ if (definition === undefined)
171
+ throw new TypeError("Replay runtime selected an unknown transition");
172
+ transitionJournal.push({
173
+ sequence: decision.transition.sequence,
174
+ at_ms: decision.transition.at_ms,
175
+ transition_id: decision.transition.transition_id,
176
+ state_before: decision.transition.state_before,
177
+ state_after: decision.transition.state_after,
178
+ captured_aliases: definition.captures.map(({ variable, sensitive }) => ({
179
+ name: variable,
180
+ sensitive,
181
+ })),
182
+ });
183
+ }
184
+ const usage = runtime.usage;
185
+ const transitionActions = input.machine.transitions
186
+ .filter(({ id }) => usedTransitionIds.has(id))
187
+ .map(({ id, actions }) => ({
188
+ transition_id: id,
189
+ actions: actions.map((action) => redactAction(runtime, action)),
190
+ }));
191
+ return replayMachineRunOutputSchema.parse({
192
+ schema_version: 1,
193
+ initial_state: input.machine.initial_state,
194
+ final_state: runtime.state,
195
+ terminal: input.machine.states.find(({ name }) => name === runtime.state)
196
+ ?.terminal ?? false,
197
+ decisions,
198
+ transition_journal: transitionJournal,
199
+ transition_actions: transitionActions,
200
+ limits: {
201
+ configured: {
202
+ transitions: input.machine.max_transitions,
203
+ ...input.machine.limits,
204
+ },
205
+ usage: {
206
+ offered_events: input.events.length,
207
+ admitted_events: usage.events,
208
+ transitions: usage.transitions,
209
+ connections: usage.connections,
210
+ messages: usage.messages,
211
+ bytes: usage.bytes,
212
+ last_at_ms: usage.last_at_ms,
213
+ },
214
+ },
215
+ });
216
+ };
@@ -1,4 +1,54 @@
1
1
  import canonicalize from "canonicalize";
2
+ import { z } from "zod";
3
+ /** One bounded inbound protocol event offered to a replay machine. */
4
+ const replayEventHeadersSchema = z
5
+ .record(z
6
+ .string()
7
+ .min(1)
8
+ .max(256)
9
+ .regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u), z
10
+ .string()
11
+ .max(8_192)
12
+ .regex(/^[\t\x20-\x7e\x80-\xff]*$/u))
13
+ .superRefine((headers, context) => {
14
+ const normalized = Object.keys(headers).map((name) => name.toLowerCase());
15
+ if (new Set(normalized).size !== normalized.length)
16
+ context.addIssue({
17
+ code: "custom",
18
+ message: "replay event headers must be unique ignoring case",
19
+ input: headers,
20
+ });
21
+ });
22
+ const replayEventFields = {
23
+ at_ms: z.number().int().nonnegative().safe(),
24
+ path: z.string().startsWith("/").max(8_192),
25
+ headers: replayEventHeadersSchema,
26
+ body: z.string().max(1_000_000),
27
+ };
28
+ export const replayMachineEventSchema = z.discriminatedUnion("protocol", [
29
+ z.strictObject({
30
+ protocol: z.literal("http"),
31
+ connection: z.literal("not_applicable"),
32
+ method: z
33
+ .string()
34
+ .min(1)
35
+ .max(16)
36
+ .transform((method) => method.toUpperCase()),
37
+ ...replayEventFields,
38
+ }),
39
+ z.strictObject({
40
+ protocol: z.literal("websocket_connect"),
41
+ connection: z.enum(["initial", "reconnect"]),
42
+ method: z.null(),
43
+ ...replayEventFields,
44
+ }),
45
+ z.strictObject({
46
+ protocol: z.literal("websocket_message"),
47
+ connection: z.enum(["initial", "reconnect"]),
48
+ method: z.null(),
49
+ ...replayEventFields,
50
+ }),
51
+ ]);
2
52
  const parseJson = (value) => {
3
53
  try {
4
54
  return JSON.parse(value);
@@ -76,6 +126,17 @@ const isJsonCompatible = (value) => {
76
126
  const replayValuesEqual = (left, right) => isJsonCompatible(left) &&
77
127
  isJsonCompatible(right) &&
78
128
  (Object.is(left, right) || canonicalize(left) === canonicalize(right));
129
+ const createTransitionRecord = ({ sequence, atMs, stateBefore, transition, captures, }) => ({
130
+ sequence,
131
+ at_ms: atMs,
132
+ transition_id: transition.id,
133
+ state_before: stateBefore,
134
+ state_after: transition.to,
135
+ sensitive_aliases: captures
136
+ .filter(({ sensitive }) => sensitive)
137
+ .map(({ variable }) => variable)
138
+ .sort(),
139
+ });
79
140
  /** Stateful evaluator for one validated replay-machine instance. */
80
141
  export class ReplayMachineRuntime {
81
142
  machine;
@@ -89,6 +150,7 @@ export class ReplayMachineRuntime {
89
150
  #messages = 0;
90
151
  #bytes = 0;
91
152
  #lastAtMs = 0;
153
+ #events = 0;
92
154
  constructor(machine) {
93
155
  this.machine = machine;
94
156
  this.#state = machine.initial_state;
@@ -102,6 +164,17 @@ export class ReplayMachineRuntime {
102
164
  get timeline() {
103
165
  return this.#timeline;
104
166
  }
167
+ /** Current committed use of the machine's declared limits. */
168
+ get usage() {
169
+ return {
170
+ events: this.#events,
171
+ transitions: this.#timeline.length,
172
+ connections: this.#connections,
173
+ messages: this.#messages,
174
+ bytes: this.#bytes,
175
+ last_at_ms: this.#lastAtMs,
176
+ };
177
+ }
105
178
  /** Replace captured secret values with stable aliases before persistence. */
106
179
  redact(value) {
107
180
  let redacted = value;
@@ -113,6 +186,14 @@ export class ReplayMachineRuntime {
113
186
  redacted = redacted.replaceAll(secret, `<secret:${variable}>`);
114
187
  return redacted;
115
188
  }
189
+ /** Replace a field containing captured secrets with one stable alias only. */
190
+ redactField(value) {
191
+ const alias = [...this.#sensitiveValues]
192
+ .filter(([, values]) => [...values].some((secret) => value.includes(secret)))
193
+ .map(([variable]) => variable)
194
+ .sort()[0];
195
+ return alias === undefined ? value : `<secret:${alias}>`;
196
+ }
116
197
  /** Evaluate and, when admitted, commit one protocol event. */
117
198
  dispatch(event) {
118
199
  const normalizedEvent = {
@@ -172,16 +253,13 @@ export class ReplayMachineRuntime {
172
253
  this.#sensitiveValues.set(capture.variable, values);
173
254
  }
174
255
  }
175
- const record = {
256
+ const record = createTransitionRecord({
176
257
  sequence: this.#timeline.length,
177
- transition_id: transition.id,
178
- state_before: this.#state,
179
- state_after: transition.to,
180
- sensitive_aliases: captures
181
- .filter(({ sensitive }) => sensitive)
182
- .map(({ variable }) => variable)
183
- .sort(),
184
- };
258
+ atMs: normalizedEvent.recorded_at_ms ?? normalizedEvent.at_ms,
259
+ stateBefore: this.#state,
260
+ transition,
261
+ captures,
262
+ });
185
263
  this.#uses.set(transition.id, used + 1);
186
264
  this.#stateVisits.set(transition.to, nextVisits);
187
265
  this.#state = transition.to;
@@ -207,6 +285,7 @@ export class ReplayMachineRuntime {
207
285
  this.#messages = messages;
208
286
  this.#bytes = bytes;
209
287
  this.#lastAtMs = event.at_ms;
288
+ this.#events += 1;
210
289
  return true;
211
290
  }
212
291
  #refusal(outcome) {
@@ -140,7 +140,7 @@ const EXPECTED = {
140
140
  "REA function comparison",
141
141
  ],
142
142
  compare_process_captures: [
143
- "rea.process-comparison/v3",
143
+ ["rea.process-comparison/v3", "rea.process-comparison/v4"],
144
144
  "rea-process",
145
145
  "REA deterministic process harness",
146
146
  ],
@@ -302,7 +302,9 @@ const parseRuntime = (input) => {
302
302
  const assertComparisonIdentity = (evidence) => {
303
303
  const expected = EXPECTED[evidence.operation];
304
304
  if (expected === undefined ||
305
- evidence.predicate_type !== expected[0] ||
305
+ !(Array.isArray(expected[0])
306
+ ? expected[0].includes(evidence.predicate_type)
307
+ : evidence.predicate_type === expected[0]) ||
306
308
  evidence.provider.id !== expected[1] ||
307
309
  evidence.provider.name !== expected[2] ||
308
310
  evidence.provider.version !==
@@ -26,6 +26,12 @@ const toolResultFailed = (item) => {
26
26
  const structured = record(result?.structured_content ?? result?.structuredContent);
27
27
  return result?.isError === true || structured?.error !== undefined;
28
28
  };
29
+ const toolResultErrorCode = (item) => {
30
+ const result = record(item.result ?? item.output);
31
+ const structured = record(result?.structured_content ?? result?.structuredContent);
32
+ const error = record(structured?.error ?? result?.error ?? item.error);
33
+ return textValue(error?.code) ?? null;
34
+ };
29
35
  const callFromItem = (value) => {
30
36
  const item = record(value);
31
37
  if (item?.type !== "mcp_tool_call")
@@ -40,6 +46,7 @@ const callFromItem = (value) => {
40
46
  arguments: item.arguments ?? item.input ?? {},
41
47
  evidenceIds: evidenceIdsFrom(item.result ?? item.output),
42
48
  error: toolResultFailed(item),
49
+ errorCode: toolResultErrorCode(item),
43
50
  };
44
51
  };
45
52
  const agentTextFromItem = (value) => {
@@ -102,6 +109,8 @@ export const evaluateCodexEvents = (events, expectedFirstTool, options = {}) =>
102
109
  signatures.set(signature, (signatures.get(signature) ?? 0) + 1);
103
110
  }
104
111
  const repeatedCallCount = [...signatures.values()].reduce((total, count) => total + Math.max(0, count - 1), 0);
112
+ const inputValidationFailureCount = reaCalls.filter(({ errorCode }) => errorCode === "invalid_request").length;
113
+ const requiredToolSubsequenceMet = containsOrderedSubsequence(reaCalls.filter(({ error }) => !error).map(({ tool }) => tool), options.requiredToolSubsequence ?? []);
105
114
  const finalMessage = state.finalMessages.at(-1) ?? "";
106
115
  const evidenceIds = [
107
116
  ...new Set(reaCalls.flatMap(({ evidenceIds: ids }) => ids)),
@@ -116,6 +125,8 @@ export const evaluateCodexEvents = (events, expectedFirstTool, options = {}) =>
116
125
  firstTool: reaCalls[0]?.tool ?? null,
117
126
  reaCalls,
118
127
  repeatedCallCount,
128
+ inputValidationFailureCount,
129
+ requiredToolSubsequenceMet,
119
130
  inputTokens: state.inputTokens,
120
131
  cachedInputTokens: state.cachedInputTokens,
121
132
  outputTokens: state.outputTokens,
@@ -126,7 +137,20 @@ export const evaluateCodexEvents = (events, expectedFirstTool, options = {}) =>
126
137
  completionQuality: finalMessage.trim().length >= 80 &&
127
138
  authorityHonesty &&
128
139
  contentCriteriaMet &&
140
+ requiredToolSubsequenceMet &&
141
+ (options.forbidInputValidationFailures !== true ||
142
+ inputValidationFailureCount === 0) &&
129
143
  (options.requireEvidence !== true || finalCitesEvidence),
130
144
  authorityHonesty,
131
145
  };
132
146
  };
147
+ const containsOrderedSubsequence = (values, required) => {
148
+ let index = 0;
149
+ for (const value of values) {
150
+ if (value === required[index])
151
+ index += 1;
152
+ if (index === required.length)
153
+ return true;
154
+ }
155
+ return required.length === 0;
156
+ };
@@ -1,7 +1,7 @@
1
1
  /** Generated from package.json and package-lock.json; do not edit. */
2
2
  export const PACKAGE_METADATA = {
3
3
  name: "rea-agents",
4
- version: "2.2.0", // x-release-please-version
4
+ version: "2.3.0", // x-release-please-version
5
5
  serverSdkVersion: "2.0.0-beta.4",
6
6
  clientSdkVersion: "2.0.0-beta.4",
7
7
  coreSdkVersion: "2.0.0-beta.4",
@@ -6,6 +6,7 @@ import { silentLogger } from "../logger.js";
6
6
  import { PendingOperations } from "../process/PendingOperations.js";
7
7
  import { PrivateRuntimeRoot } from "../process/PrivateRuntimeRoot.js";
8
8
  import { ProviderStartupDeadline } from "../process/ProviderDeadline.js";
9
+ import { ProviderRunLineage } from "../process/ProviderRunLineage.js";
9
10
  import { ProviderProcessSupervisor, } from "../process/ProviderProcess.js";
10
11
  import { GHIDRA_MAX_LINE_BYTES, GHIDRA_MAX_QUEUED_REQUESTS, GHIDRA_REQUEST_TIMEOUT_MS, GHIDRA_STARTUP_TIMEOUT_MS, } from "./GhidraDefaults.js";
11
12
  import { createGhidraDiagnostics } from "./GhidraDiagnostics.js";
@@ -33,6 +34,7 @@ export class GhidraClient {
33
34
  #snapshotPath;
34
35
  #token;
35
36
  #runId;
37
+ #lineage = new ProviderRunLineage();
36
38
  #nextId = 1;
37
39
  #closing = false;
38
40
  #processSnapshot;
@@ -145,6 +147,10 @@ export class GhidraClient {
145
147
  diagnostics() {
146
148
  return structuredClone(this.#diagnostics());
147
149
  }
150
+ /** Latest token-verified launcher lineage for the active run. */
151
+ runtimeLineage() {
152
+ return this.#lineage.snapshot();
153
+ }
148
154
  async #start(signal) {
149
155
  if (signal.aborted)
150
156
  return err(this.#failure("cancelled", "Ghidra startup was cancelled"));
@@ -188,7 +194,8 @@ export class GhidraClient {
188
194
  if (deadline.signal.aborted)
189
195
  return this.#startupInterrupted(deadline);
190
196
  this.#token = randomBytes(32).toString("hex");
191
- this.#runId = randomUUID();
197
+ this.#lineage.reset();
198
+ this.#runId = this.#options.runId ?? randomUUID();
192
199
  const launched = await this.#options.launcher
193
200
  .launch({
194
201
  runtimeRoot: this.#runtimeRoot.path,
@@ -221,7 +228,7 @@ export class GhidraClient {
221
228
  await this.#cleanup();
222
229
  return err(failure);
223
230
  }
224
- return completeGhidraStartupHandshake({
231
+ const completed = await completeGhidraStartupHandshake({
225
232
  deadline,
226
233
  request: (method, params, requestOptions) => this.#wire.request(method, params, requestOptions),
227
234
  parseSessionInfo: (value) => this.#parseSessionInfo(value),
@@ -229,6 +236,9 @@ export class GhidraClient {
229
236
  failure: this.#failure,
230
237
  startupTimeoutMs: this.#options.startupTimeoutMs,
231
238
  });
239
+ if (completed.ok)
240
+ await this.#lineage.observe(this.#launch);
241
+ return completed;
232
242
  }
233
243
  async #connect(endpoint, deadline) {
234
244
  const connected = await connectGhidraSocket({
@@ -373,6 +383,7 @@ export class GhidraClient {
373
383
  transport: this.#options.transport,
374
384
  providerVersion: this.#options.providerVersion,
375
385
  profileDigest: this.#options.profileDigest,
386
+ ...(this.#runId === undefined ? {} : { runId: this.#runId }),
376
387
  ...(this.#runtimeRoot === undefined
377
388
  ? {}
378
389
  : { runtimeRoot: this.#runtimeRoot.path }),
@@ -14,6 +14,7 @@ export const createGhidraDiagnostics = (options) => {
14
14
  transport: options.transport,
15
15
  provider_version: options.providerVersion,
16
16
  profile_digest: options.profileDigest,
17
+ ...(options.runId === undefined ? {} : { run_id: options.runId }),
17
18
  ...(options.runtimeRoot === undefined
18
19
  ? {}
19
20
  : {
@@ -91,9 +91,7 @@ export class GhidraHeadlessLauncher {
91
91
  }
92
92
  const spawned = started;
93
93
  return ok({
94
- process: spawned.process,
95
- ownsProcessLifetime: true,
96
- cleanup: () => cleanupStartedProcess(spawned, platform),
94
+ ...ownedGhidraProcess(spawned, platform),
97
95
  projectRoot: paths.projectRoot,
98
96
  ghidraLogPath: paths.ghidraLogPath,
99
97
  scriptLogPath: paths.scriptLogPath,
@@ -108,6 +106,12 @@ export class GhidraHeadlessLauncher {
108
106
  }
109
107
  }
110
108
  }
109
+ const ownedGhidraProcess = (spawned, platform) => ({
110
+ process: spawned.process,
111
+ ownsProcessLifetime: true,
112
+ ownership: spawned.ownership,
113
+ cleanup: () => cleanupStartedProcess(spawned, platform),
114
+ });
111
115
  /** Build a direct POSIX launch or a conservatively quoted Windows batch call. */
112
116
  export const ghidraHeadlessCommand = (options) => {
113
117
  if (options.platform !== "win32")
@@ -95,7 +95,7 @@ export class GhidraProvider {
95
95
  resolveAnalysisProfile(target, options) {
96
96
  return resolveGhidraAnalysisProfile(target, GHIDRA_PROVIDER_IDENTITY, this.#inspectInstallation(), options?.signal);
97
97
  }
98
- createClient(target, profile) {
98
+ createClient(target, profile, context) {
99
99
  const installation = this.#inspectInstallation();
100
100
  const prerequisites = ghidraClientPrerequisites(target, profile, installation);
101
101
  if (!prerequisites.ok)
@@ -118,6 +118,7 @@ export class GhidraProvider {
118
118
  : "unix-socket",
119
119
  providerVersion: prerequisites.value.providerVersion,
120
120
  profileDigest: committedProfile.digest,
121
+ ...(context === undefined ? {} : { runId: context.runId }),
121
122
  logger: this.logger.child({ layer: "ghidra-bridge" }),
122
123
  });
123
124
  return {
@@ -160,6 +161,12 @@ export class GhidraProvider {
160
161
  limitations: [...limitationsFor(operation), ...providerLimitations],
161
162
  }));
162
163
  },
164
+ runtimeLineageSnapshots: () => {
165
+ const observation = client.runtimeLineage?.() ?? null;
166
+ return observation === null
167
+ ? []
168
+ : [{ provider: committedProfile.provider, observation }];
169
+ },
163
170
  close: () => client.close(),
164
171
  };
165
172
  }