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
@@ -0,0 +1,294 @@
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
+ ]);
52
+ const parseJson = (value) => {
53
+ try {
54
+ return JSON.parse(value);
55
+ }
56
+ catch {
57
+ return undefined;
58
+ }
59
+ };
60
+ const atPath = (value, path) => {
61
+ let current = value;
62
+ for (const segment of path) {
63
+ if (typeof segment === "number") {
64
+ if (!Array.isArray(current))
65
+ return undefined;
66
+ current = current[segment];
67
+ continue;
68
+ }
69
+ if (typeof current !== "object" ||
70
+ current === null ||
71
+ !Object.hasOwn(current, segment))
72
+ return undefined;
73
+ current = current[segment];
74
+ }
75
+ return current;
76
+ };
77
+ const actionJsonBody = (transition) => {
78
+ for (const action of transition.actions)
79
+ if (action.type === "http_response")
80
+ return action.body;
81
+ else if (action.type === "websocket_send")
82
+ return action.data;
83
+ return undefined;
84
+ };
85
+ const readValue = (source, event, transition) => {
86
+ switch (source.source) {
87
+ case "request_header":
88
+ return event.headers[source.name.toLowerCase()];
89
+ case "request_json":
90
+ return atPath(parseJson(event.body), source.path);
91
+ case "websocket_json":
92
+ return atPath(parseJson(event.body), source.path);
93
+ case "action_json": {
94
+ const body = actionJsonBody(transition);
95
+ return body === undefined
96
+ ? undefined
97
+ : atPath(parseJson(body), source.path);
98
+ }
99
+ }
100
+ };
101
+ const triggerMatches = (transition, event) => {
102
+ const trigger = transition.trigger;
103
+ if (trigger.protocol !== event.protocol || trigger.path !== event.path)
104
+ return false;
105
+ if ("headers" in trigger &&
106
+ Object.entries(trigger.headers).some(([name, value]) => event.headers[name.toLowerCase()] !== value))
107
+ return false;
108
+ if (trigger.protocol === "http")
109
+ return (trigger.method === event.method &&
110
+ (trigger.body === null || trigger.body === event.body));
111
+ if (trigger.protocol === "websocket_message")
112
+ return trigger.body === null || trigger.body === event.body;
113
+ return true;
114
+ };
115
+ const isJsonCompatible = (value) => {
116
+ if (value === null || typeof value === "string" || typeof value === "boolean")
117
+ return true;
118
+ if (typeof value === "number")
119
+ return Number.isFinite(value);
120
+ if (Array.isArray(value))
121
+ return value.every(isJsonCompatible);
122
+ if (typeof value !== "object")
123
+ return false;
124
+ return Object.values(value).every(isJsonCompatible);
125
+ };
126
+ const replayValuesEqual = (left, right) => isJsonCompatible(left) &&
127
+ isJsonCompatible(right) &&
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
+ });
140
+ /** Stateful evaluator for one validated replay-machine instance. */
141
+ export class ReplayMachineRuntime {
142
+ machine;
143
+ #variables = new Map();
144
+ #sensitiveValues = new Map();
145
+ #uses = new Map();
146
+ #stateVisits = new Map();
147
+ #timeline = [];
148
+ #state;
149
+ #connections = 0;
150
+ #messages = 0;
151
+ #bytes = 0;
152
+ #lastAtMs = 0;
153
+ #events = 0;
154
+ constructor(machine) {
155
+ this.machine = machine;
156
+ this.#state = machine.initial_state;
157
+ this.#stateVisits.set(machine.initial_state, 1);
158
+ }
159
+ /** Current machine state after all accepted transitions. */
160
+ get state() {
161
+ return this.#state;
162
+ }
163
+ /** Ordered, secret-free transition records. */
164
+ get timeline() {
165
+ return this.#timeline;
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
+ }
178
+ /** Replace captured secret values with stable aliases before persistence. */
179
+ redact(value) {
180
+ let redacted = value;
181
+ const secrets = [...this.#sensitiveValues]
182
+ .flatMap(([variable, values]) => [...values].map((secret) => ({ variable, secret })))
183
+ .sort((left, right) => right.secret.length - left.secret.length ||
184
+ left.variable.localeCompare(right.variable));
185
+ for (const { variable, secret } of secrets)
186
+ redacted = redacted.replaceAll(secret, `<secret:${variable}>`);
187
+ return redacted;
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
+ }
197
+ /** Evaluate and, when admitted, commit one protocol event. */
198
+ dispatch(event) {
199
+ const normalizedEvent = {
200
+ ...event,
201
+ headers: Object.fromEntries(Object.entries(event.headers).map(([name, value]) => [
202
+ name.toLowerCase(),
203
+ value,
204
+ ])),
205
+ };
206
+ if (!this.#admitEvent(normalizedEvent))
207
+ return this.#refusal("limit_exhausted");
208
+ const matching = this.machine.transitions
209
+ .map((transition, declarationOrder) => ({
210
+ transition,
211
+ declarationOrder,
212
+ }))
213
+ .filter(({ transition }) => triggerMatches(transition, normalizedEvent));
214
+ const eligible = matching
215
+ .filter(({ transition }) => transition.from === this.#state)
216
+ .sort((left, right) => left.transition.priority - right.transition.priority ||
217
+ left.declarationOrder - right.declarationOrder);
218
+ if (eligible.length === 0)
219
+ return this.#refusal(matching.length === 0
220
+ ? "unmatched"
221
+ : normalizedEvent.protocol === "websocket_connect" &&
222
+ normalizedEvent.connection === "reconnect"
223
+ ? "unexpected_reconnect"
224
+ : "invalid_state");
225
+ const guarded = eligible.filter(({ transition }) => transition.guards.every((guard) => this.#variables.has(guard.variable) &&
226
+ replayValuesEqual(this.#variables.get(guard.variable), readValue(guard.value, normalizedEvent, transition))));
227
+ if (guarded.length === 0)
228
+ return this.#refusal("guard_failed");
229
+ if (this.#timeline.length >= this.machine.max_transitions)
230
+ return this.#refusal("transition_exhausted");
231
+ const usable = guarded.find(({ transition }) => (this.#uses.get(transition.id) ?? 0) < transition.max_uses);
232
+ if (usable === undefined)
233
+ return this.#refusal("transition_exhausted");
234
+ const transition = usable.transition;
235
+ const used = this.#uses.get(transition.id) ?? 0;
236
+ const nextState = this.machine.states.find(({ name }) => name === transition.to);
237
+ const nextVisits = (this.#stateVisits.get(transition.to) ?? 0) + 1;
238
+ if (nextState === undefined || nextVisits > nextState.max_visits)
239
+ return this.#refusal("limit_exhausted");
240
+ const captures = transition.captures.map((capture) => ({
241
+ ...capture,
242
+ captured: readValue(capture.value, normalizedEvent, transition),
243
+ }));
244
+ if (captures.some(({ captured, sensitive }) => !isJsonCompatible(captured) ||
245
+ (sensitive && typeof captured !== "string")))
246
+ return this.#refusal("invalid_capture");
247
+ for (const capture of captures) {
248
+ this.#variables.set(capture.variable, capture.captured);
249
+ if (capture.sensitive && typeof capture.captured === "string") {
250
+ const values = this.#sensitiveValues.get(capture.variable) ?? new Set();
251
+ if (capture.captured.length > 0)
252
+ values.add(capture.captured);
253
+ this.#sensitiveValues.set(capture.variable, values);
254
+ }
255
+ }
256
+ const record = createTransitionRecord({
257
+ sequence: this.#timeline.length,
258
+ atMs: normalizedEvent.recorded_at_ms ?? normalizedEvent.at_ms,
259
+ stateBefore: this.#state,
260
+ transition,
261
+ captures,
262
+ });
263
+ this.#uses.set(transition.id, used + 1);
264
+ this.#stateVisits.set(transition.to, nextVisits);
265
+ this.#state = transition.to;
266
+ this.#timeline.push(record);
267
+ return {
268
+ outcome: "matched",
269
+ actions: transition.actions,
270
+ transition: record,
271
+ };
272
+ }
273
+ #admitEvent(event) {
274
+ const connections = this.#connections + (event.protocol === "websocket_connect" ? 1 : 0);
275
+ const messages = this.#messages + (event.protocol === "websocket_message" ? 1 : 0);
276
+ const bytes = this.#bytes + Buffer.byteLength(event.body);
277
+ if (!Number.isSafeInteger(event.at_ms) ||
278
+ event.at_ms < this.#lastAtMs ||
279
+ event.at_ms > this.machine.limits.duration_ms ||
280
+ connections > this.machine.limits.connections ||
281
+ messages > this.machine.limits.messages ||
282
+ bytes > this.machine.limits.bytes)
283
+ return false;
284
+ this.#connections = connections;
285
+ this.#messages = messages;
286
+ this.#bytes = bytes;
287
+ this.#lastAtMs = event.at_ms;
288
+ this.#events += 1;
289
+ return true;
290
+ }
291
+ #refusal(outcome) {
292
+ return { outcome, actions: [], transition: null };
293
+ }
294
+ }
@@ -0,0 +1,210 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { parseArtifactInventoryEvidence } from "./artifactInventoryEvidence.js";
5
+ import { evidenceSchema } from "./evidence.js";
6
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
7
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
8
+ const runtimeFamilySchema = z.enum([
9
+ "android",
10
+ "apple",
11
+ "javascript",
12
+ "jvm",
13
+ "native",
14
+ "webassembly",
15
+ ]);
16
+ const observationSchema = z.strictObject({
17
+ path: z.string().min(1).max(4_096),
18
+ artifact_id: z.string().regex(/^art_[a-f0-9]{64}$/u),
19
+ sha256: digestSchema,
20
+ format: z.string().min(1).max(100),
21
+ });
22
+ /** Authenticated artifact inventory pages to classify by runtime family. */
23
+ const runtimeIdentificationInputSchema = z.strictObject({
24
+ inventory_evidence: z.array(evidenceSchema).min(1).max(100),
25
+ limits: z
26
+ .strictObject({
27
+ max_observations: z.number().int().min(1).max(10_000).default(1_000),
28
+ })
29
+ .default({ max_observations: 1_000 }),
30
+ });
31
+ /** Provider-neutral runtime identification with explicit tooling availability. */
32
+ export const runtimeIdentificationResultSchema = z.strictObject({
33
+ schema_version: z.literal(1),
34
+ identification_id: z.string().regex(/^rid_[a-f0-9]{64}$/u),
35
+ root_sha256: digestSchema,
36
+ root_format: z.string().min(1).max(100),
37
+ source_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
38
+ runtimes: z.array(z.strictObject({
39
+ family: runtimeFamilySchema,
40
+ inspection: z.enum([
41
+ "available",
42
+ "provider-missing",
43
+ "provider-selection-required",
44
+ ]),
45
+ provider_id: z.string().min(1).max(200).nullable(),
46
+ reason: z.string().min(1).max(1_000).nullable(),
47
+ observations: z.array(observationSchema),
48
+ omitted_observations: z.number().int().min(0),
49
+ })),
50
+ coverage: z.strictObject({
51
+ status: z.enum(["complete-within-inventory", "partial", "truncated"]),
52
+ inventory_complete: z.boolean(),
53
+ omitted_observations: z.number().int().min(0),
54
+ }),
55
+ limitations: z.array(z.string().min(1).max(4_096)).max(100),
56
+ });
57
+ const PROVIDERS = {
58
+ android: {
59
+ inspection: "available",
60
+ id: "rea-android-application",
61
+ reason: null,
62
+ },
63
+ apple: { inspection: "available", id: "rea-apple-application", reason: null },
64
+ javascript: {
65
+ inspection: "available",
66
+ id: "rea-javascript-application",
67
+ reason: null,
68
+ },
69
+ jvm: {
70
+ inspection: "provider-missing",
71
+ id: null,
72
+ reason: "Install or configure a JVM bytecode-analysis provider.",
73
+ },
74
+ native: {
75
+ inspection: "provider-selection-required",
76
+ id: null,
77
+ reason: "Select an available native deep-analysis provider for this target and host.",
78
+ },
79
+ webassembly: {
80
+ inspection: "provider-missing",
81
+ id: null,
82
+ reason: "Install or configure a WebAssembly semantic-analysis provider.",
83
+ },
84
+ };
85
+ /** Identify runtime families without executing or semantically decoding target bytes. */
86
+ export const identifyRuntimes = (input) => {
87
+ const parsed = runtimeIdentificationInputSchema.parse(input);
88
+ const { evidence, inventory } = parseArtifactInventoryEvidence(parsed.inventory_evidence);
89
+ const nodes = new Map(inventory.nodes.map((node) => [node.artifact_id, node]));
90
+ const grouped = new Map();
91
+ const add = (family, observation) => {
92
+ const observations = grouped.get(family) ?? [];
93
+ observations.push(observation);
94
+ grouped.set(family, observations);
95
+ };
96
+ for (const occurrence of inventory.occurrences) {
97
+ if (occurrence.artifact_id === null)
98
+ continue;
99
+ const node = nodes.get(occurrence.artifact_id);
100
+ if (node === undefined)
101
+ throw new TypeError("Runtime observation has no artifact node");
102
+ const observation = {
103
+ path: occurrence.logical_path,
104
+ artifact_id: node.artifact_id,
105
+ sha256: node.sha256,
106
+ format: node.format,
107
+ };
108
+ for (const family of familiesFor(node.format, occurrence.logical_path))
109
+ add(family, observation);
110
+ }
111
+ const rootObservation = inventory.nodes.find(({ sha256 }) => sha256 === inventory.manifest.root_sha256);
112
+ if (rootObservation !== undefined)
113
+ for (const family of familiesFor(inventory.manifest.root_format, "."))
114
+ add(family, {
115
+ path: ".",
116
+ artifact_id: rootObservation.artifact_id,
117
+ sha256: rootObservation.sha256,
118
+ format: rootObservation.format,
119
+ });
120
+ let remaining = parsed.limits.max_observations;
121
+ let omitted = 0;
122
+ const runtimes = [...grouped.entries()]
123
+ .sort(([left], [right]) => compare(left, right))
124
+ .map(([family, values]) => {
125
+ const unique = deduplicate(values);
126
+ const observations = unique.slice(0, remaining);
127
+ remaining -= observations.length;
128
+ const omittedObservations = unique.length - observations.length;
129
+ omitted += omittedObservations;
130
+ const provider = providerFor(family, inventory.manifest.root_format);
131
+ return {
132
+ family,
133
+ inspection: provider.inspection,
134
+ provider_id: provider.id,
135
+ reason: provider.reason,
136
+ observations,
137
+ omitted_observations: omittedObservations,
138
+ };
139
+ });
140
+ const limitations = [
141
+ ...(!inventory.complete
142
+ ? [
143
+ "Source inventory pages are incomplete; absent runtimes remain unknown.",
144
+ ]
145
+ : []),
146
+ ...(omitted > 0
147
+ ? [`${String(omitted)} runtime observations were omitted by the limit.`]
148
+ : []),
149
+ "Runtime families are identified from exact artifact formats and paths; semantic claims require the named provider.",
150
+ ];
151
+ const withoutId = {
152
+ schema_version: 1,
153
+ root_sha256: inventory.manifest.root_sha256,
154
+ root_format: inventory.manifest.root_format,
155
+ source_evidence_ids: evidence
156
+ .map(({ evidence_id: id }) => id)
157
+ .sort(compare),
158
+ runtimes,
159
+ coverage: {
160
+ status: omitted > 0
161
+ ? "truncated"
162
+ : inventory.complete
163
+ ? "complete-within-inventory"
164
+ : "partial",
165
+ inventory_complete: inventory.complete,
166
+ omitted_observations: omitted,
167
+ },
168
+ limitations,
169
+ };
170
+ return runtimeIdentificationResultSchema.parse({
171
+ ...withoutId,
172
+ identification_id: `rid_${digest(withoutId)}`,
173
+ });
174
+ };
175
+ const providerFor = (family, rootFormat) => family === "android" && rootFormat !== "apk"
176
+ ? {
177
+ inspection: "provider-selection-required",
178
+ id: null,
179
+ reason: "Extract or select an APK root before using the Android application provider.",
180
+ }
181
+ : PROVIDERS[family];
182
+ const familiesFor = (format, path) => {
183
+ const families = new Set();
184
+ if (format === "apk" ||
185
+ format === "dex" ||
186
+ /(?:^|\/)[^/]+\.dex$/iu.test(path) ||
187
+ /(?:^|\/)AndroidManifest\.xml$/u.test(path))
188
+ families.add("android");
189
+ if (format === "ipa")
190
+ families.add("apple");
191
+ if (format === "javascript-bundle")
192
+ families.add("javascript");
193
+ if (format === "jvm-class" || /(?:^|\/)[^/]+\.class$/iu.test(path))
194
+ families.add("jvm");
195
+ if (["elf", "mach-o", "mach-o-universal", "pe"].includes(format))
196
+ families.add("native");
197
+ if (format === "webassembly" || /(?:^|\/)[^/]+\.wasm$/iu.test(path))
198
+ families.add("webassembly");
199
+ return [...families];
200
+ };
201
+ const deduplicate = (values) => [
202
+ ...new Map(values.map((value) => [`${value.path}\0${value.artifact_id}`, value])).values(),
203
+ ].sort((left, right) => compare(left.path, right.path));
204
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
205
+ const digest = (value) => {
206
+ const encoded = canonicalize(value);
207
+ if (encoded === undefined)
208
+ throw new TypeError("Runtime identification is not canonical JSON");
209
+ return createHash("sha256").update(encoded).digest("hex");
210
+ };
@@ -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 !==
@@ -0,0 +1,156 @@
1
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2
+ const record = (value) => isRecord(value) ? value : undefined;
3
+ const textValue = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
4
+ const numberValue = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
5
+ const canonicalValue = (value) => {
6
+ if (Array.isArray(value))
7
+ return value.map(canonicalValue);
8
+ const object = record(value);
9
+ if (object === undefined)
10
+ return value;
11
+ return Object.fromEntries(Object.entries(object)
12
+ .sort(([left], [right]) => left.localeCompare(right))
13
+ .map(([key, child]) => [key, canonicalValue(child)]));
14
+ };
15
+ const evidenceIdsFrom = (value) => {
16
+ const encoded = JSON.stringify(value);
17
+ if (encoded === undefined)
18
+ return [];
19
+ return [...new Set(encoded.match(/ev_[a-f0-9]{64}/gu) ?? [])];
20
+ };
21
+ const toolResultFailed = (item) => {
22
+ if (item.status === "failed" ||
23
+ (item.error !== undefined && item.error !== null))
24
+ return true;
25
+ const result = record(item.result ?? item.output);
26
+ const structured = record(result?.structured_content ?? result?.structuredContent);
27
+ return result?.isError === true || structured?.error !== undefined;
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
+ };
35
+ const callFromItem = (value) => {
36
+ const item = record(value);
37
+ if (item?.type !== "mcp_tool_call")
38
+ return undefined;
39
+ const tool = textValue(item.tool) ?? textValue(item.name);
40
+ if (tool === undefined)
41
+ return undefined;
42
+ return {
43
+ id: textValue(item.id) ?? null,
44
+ server: textValue(item.server) ?? textValue(item.server_name) ?? null,
45
+ tool,
46
+ arguments: item.arguments ?? item.input ?? {},
47
+ evidenceIds: evidenceIdsFrom(item.result ?? item.output),
48
+ error: toolResultFailed(item),
49
+ errorCode: toolResultErrorCode(item),
50
+ };
51
+ };
52
+ const agentTextFromItem = (value) => {
53
+ const item = record(value);
54
+ if (item?.type !== "agent_message")
55
+ return undefined;
56
+ const direct = textValue(item.text) ?? textValue(item.message);
57
+ if (direct !== undefined)
58
+ return direct;
59
+ if (!Array.isArray(item.content))
60
+ return undefined;
61
+ const parts = item.content.flatMap((part) => {
62
+ const object = record(part);
63
+ const text = object === undefined ? undefined : textValue(object.text);
64
+ return text === undefined ? [] : [text];
65
+ });
66
+ return parts.length === 0 ? undefined : parts.join("\n");
67
+ };
68
+ const consumeEvent = (state, value) => {
69
+ const event = record(value);
70
+ if (event === undefined)
71
+ return;
72
+ const completedItem = event.type === "item.completed" ? event.item : undefined;
73
+ const call = callFromItem(completedItem) ??
74
+ (event.type === "mcp_tool_call" ? callFromItem(event) : undefined);
75
+ if (call !== undefined) {
76
+ const identity = call.id ??
77
+ `${call.server ?? ""}:${call.tool}:${JSON.stringify(canonicalValue(call.arguments))}`;
78
+ if (!state.seenCallIds.has(identity)) {
79
+ state.seenCallIds.add(identity);
80
+ state.calls.push(call);
81
+ }
82
+ }
83
+ const message = agentTextFromItem(completedItem);
84
+ if (message !== undefined)
85
+ state.finalMessages.push(message);
86
+ if (event.type !== "turn.completed")
87
+ return;
88
+ const usage = record(event.usage);
89
+ state.inputTokens = Math.max(state.inputTokens, numberValue(usage?.input_tokens));
90
+ state.cachedInputTokens = Math.max(state.cachedInputTokens, numberValue(usage?.cached_input_tokens));
91
+ state.outputTokens = Math.max(state.outputTokens, numberValue(usage?.output_tokens));
92
+ };
93
+ /** Score agent routing, repetition, model usage, and epistemic completion. */
94
+ export const evaluateCodexEvents = (events, expectedFirstTool, options = {}) => {
95
+ const state = {
96
+ calls: [],
97
+ seenCallIds: new Set(),
98
+ finalMessages: [],
99
+ inputTokens: 0,
100
+ cachedInputTokens: 0,
101
+ outputTokens: 0,
102
+ };
103
+ for (const event of events)
104
+ consumeEvent(state, event);
105
+ const reaCalls = state.calls.filter(({ server }) => server === null || server.toLowerCase() === "rea");
106
+ const signatures = new Map();
107
+ for (const call of reaCalls) {
108
+ const signature = `${call.tool}:${JSON.stringify(canonicalValue(call.arguments))}`;
109
+ signatures.set(signature, (signatures.get(signature) ?? 0) + 1);
110
+ }
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 ?? []);
114
+ const finalMessage = state.finalMessages.at(-1) ?? "";
115
+ const evidenceIds = [
116
+ ...new Set(reaCalls.flatMap(({ evidenceIds: ids }) => ids)),
117
+ ];
118
+ const finalCitesEvidence = evidenceIds.some((evidenceId) => finalMessage.includes(evidenceId));
119
+ const normalizedFinalMessage = finalMessage.toLocaleLowerCase("en-US");
120
+ const contentCriteriaMet = (options.requiredAnswerTermGroups ?? []).every((terms) => terms.some((term) => normalizedFinalMessage.includes(term.toLocaleLowerCase("en-US"))));
121
+ const authorityHonesty = /\b(evidence|observed|inferred|unknown|unavailable|limitation|authority|not configured|could not|requires approval)\b/iu.test(finalMessage);
122
+ return {
123
+ naturalUse: reaCalls.length > 0,
124
+ correctFirstTool: reaCalls[0]?.tool === expectedFirstTool,
125
+ firstTool: reaCalls[0]?.tool ?? null,
126
+ reaCalls,
127
+ repeatedCallCount,
128
+ inputValidationFailureCount,
129
+ requiredToolSubsequenceMet,
130
+ inputTokens: state.inputTokens,
131
+ cachedInputTokens: state.cachedInputTokens,
132
+ outputTokens: state.outputTokens,
133
+ finalMessage,
134
+ evidenceIds,
135
+ finalCitesEvidence,
136
+ contentCriteriaMet,
137
+ completionQuality: finalMessage.trim().length >= 80 &&
138
+ authorityHonesty &&
139
+ contentCriteriaMet &&
140
+ requiredToolSubsequenceMet &&
141
+ (options.forbidInputValidationFailures !== true ||
142
+ inputValidationFailureCount === 0) &&
143
+ (options.requireEvidence !== true || finalCitesEvidence),
144
+ authorityHonesty,
145
+ };
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,9 +1,9 @@
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.1.0",
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",
8
- skillVersion: "22",
8
+ skillVersion: "23",
9
9
  };