rea-agents 0.3.0 → 0.5.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 (125) hide show
  1. package/README.md +139 -21
  2. package/bridge/hopper_bridge.py +156 -14
  3. package/dist/application/AnalysisProvider.js +12 -1
  4. package/dist/application/ArtifactExtraction.js +166 -0
  5. package/dist/application/ArtifactGraphConstruction.js +257 -0
  6. package/dist/application/ArtifactInventory.js +253 -0
  7. package/dist/application/BinarySession.js +174 -14
  8. package/dist/application/CompositeProvider.js +73 -0
  9. package/dist/application/DirectAnalysis.js +44 -3
  10. package/dist/application/Doctor.js +35 -6
  11. package/dist/application/EnhancedTools.js +10 -7
  12. package/dist/application/EvidenceBundleCommands.js +51 -0
  13. package/dist/application/EvidenceBundleFiles.js +127 -0
  14. package/dist/application/EvidenceLedger.js +225 -18
  15. package/dist/application/FilesystemSnapshot.js +124 -0
  16. package/dist/application/LinuxHopper.js +186 -0
  17. package/dist/application/LoopbackReplay.js +195 -37
  18. package/dist/application/ProcessHarness.js +200 -191
  19. package/dist/application/ProcessNormalization.js +44 -0
  20. package/dist/application/ProcessOwnership.js +105 -0
  21. package/dist/application/ProcessSampling.js +284 -0
  22. package/dist/application/RealHopperAssertions.js +116 -0
  23. package/dist/application/ReferenceSourceImport.js +182 -0
  24. package/dist/application/ReferenceSourceImportEntries.js +122 -0
  25. package/dist/application/ReferenceSourceImportPolicy.js +73 -0
  26. package/dist/application/ReferenceSourceImportTypes.js +18 -0
  27. package/dist/application/ReferenceSourceVcsAdapter.js +34 -0
  28. package/dist/application/Setup.js +192 -41
  29. package/dist/application/Uninstall.js +130 -0
  30. package/dist/application/runtime.js +8 -1
  31. package/dist/artifacts/ArtifactPaths.js +51 -0
  32. package/dist/artifacts/ArtifactProvider.js +130 -0
  33. package/dist/artifacts/ArtifactReader.js +9 -0
  34. package/dist/artifacts/AsarArtifactReader.js +62 -0
  35. package/dist/artifacts/DirectoryArtifactReader.js +107 -0
  36. package/dist/artifacts/MachOSliceArtifactReader.js +66 -0
  37. package/dist/artifacts/SafeOutputTree.js +199 -0
  38. package/dist/artifacts/StreamBytes.js +10 -0
  39. package/dist/artifacts/ZipArtifactReader.js +109 -0
  40. package/dist/cli.js +229 -21
  41. package/dist/cliEvidenceCommands.js +68 -0
  42. package/dist/cliLogging.js +21 -0
  43. package/dist/config.js +35 -1
  44. package/dist/contracts/artifactComparisonExample.js +95 -0
  45. package/dist/contracts/artifactToolContracts.js +84 -0
  46. package/dist/contracts/enhancedInputs.js +4 -0
  47. package/dist/contracts/functionComparisonExample.js +57 -0
  48. package/dist/contracts/investigationExamples.js +118 -0
  49. package/dist/contracts/nativeToolContracts.js +52 -0
  50. package/dist/contracts/processCaptureExample.js +25 -0
  51. package/dist/contracts/toolContractExamples.js +69 -0
  52. package/dist/contracts/toolContracts.js +99 -36
  53. package/dist/contracts/toolOutputSchemas.js +156 -82
  54. package/dist/contracts/unknownContractExamples.js +33 -0
  55. package/dist/domain/artifactComparison.js +273 -0
  56. package/dist/domain/artifactGraph.js +194 -0
  57. package/dist/domain/artifactInventoryEvidence.js +150 -0
  58. package/dist/domain/binaryTarget.js +55 -0
  59. package/dist/domain/bundleComparison.js +266 -0
  60. package/dist/domain/callPath.js +346 -0
  61. package/dist/domain/changedBehavior.js +294 -0
  62. package/dist/domain/errors.js +199 -5
  63. package/dist/domain/evidence.js +41 -10
  64. package/dist/domain/evidenceBundle.js +187 -6
  65. package/dist/domain/functionComparison.js +201 -0
  66. package/dist/domain/functionComparisonNormalization.js +112 -0
  67. package/dist/domain/functionComparisonResults.js +54 -0
  68. package/dist/domain/functionComparisonSchemas.js +82 -0
  69. package/dist/domain/functionDossierEvidence.js +171 -0
  70. package/dist/domain/hopperValues.js +35 -8
  71. package/dist/domain/nativeInspection.js +142 -0
  72. package/dist/domain/processCapture.js +152 -54
  73. package/dist/domain/processComparison.js +106 -0
  74. package/dist/domain/reconstructionUnknowns.js +90 -0
  75. package/dist/domain/reconstructionVerification.js +285 -0
  76. package/dist/domain/reconstructionVerificationSchemas.js +126 -0
  77. package/dist/domain/referenceSourceClassification.js +496 -0
  78. package/dist/domain/referenceSourceGraph.js +376 -0
  79. package/dist/domain/referenceSourceImportParsing.js +235 -0
  80. package/dist/domain/referenceSourcePolicy.js +1 -0
  81. package/dist/domain/residualUnknown.js +239 -0
  82. package/dist/domain/staticRuntimeCorrelation.js +375 -0
  83. package/dist/hopper/BridgeLauncher.js +39 -3
  84. package/dist/hopper/HopperClient.js +14 -5
  85. package/dist/hopper/HopperProvider.js +57 -22
  86. package/dist/hopper/protocol.js +13 -2
  87. package/dist/identity.js +1 -0
  88. package/dist/main.js +5 -1
  89. package/dist/native/CommandRunner.js +156 -0
  90. package/dist/native/NativeMacOSProvider.js +306 -0
  91. package/dist/native/NativeMachoInspection.js +135 -0
  92. package/dist/native/parsers/codesign.js +55 -0
  93. package/dist/native/parsers/demangle.js +26 -0
  94. package/dist/native/parsers/dyldInfo.js +25 -0
  95. package/dist/native/parsers/lipo.js +67 -0
  96. package/dist/native/parsers/otool.js +193 -0
  97. package/dist/native/parsers/plist.js +23 -0
  98. package/dist/reference/ReferenceSourceReader.js +73 -0
  99. package/dist/reference/ReferenceSourceReaderEntries.js +206 -0
  100. package/dist/reference/ReferenceSourceReaderErrors.js +19 -0
  101. package/dist/reference/ReferenceSourceReaderFile.js +119 -0
  102. package/dist/reference/ReferenceSourceReaderPaths.js +23 -0
  103. package/dist/reference/ReferenceSourceReaderTypes.js +2 -0
  104. package/dist/reference/ReferenceSourceReaderValidate.js +71 -0
  105. package/dist/server/createServer.js +31 -5
  106. package/dist/server/recordDerivedEvidence.js +10 -0
  107. package/dist/server/registerArtifactComparisonTool.js +62 -0
  108. package/dist/server/registerArtifactTools.js +6 -0
  109. package/dist/server/registerBundleComparisonTool.js +47 -0
  110. package/dist/server/registerEnhancedTools.js +64 -12
  111. package/dist/server/registerEvidenceTools.js +36 -0
  112. package/dist/server/registerFunctionComparisonTool.js +68 -0
  113. package/dist/server/registerInvestigationTools.js +224 -0
  114. package/dist/server/registerNativeTools.js +6 -0
  115. package/dist/server/registerOfficialTools.js +47 -14
  116. package/dist/server/registerProcessComparisonTool.js +106 -0
  117. package/dist/server/registerSessionTools.js +179 -70
  118. package/dist/server/sessionEvidence.js +28 -0
  119. package/dist/server/sessionToolPolicies.js +64 -0
  120. package/dist/server/toolRegistrationOptions.js +7 -0
  121. package/dist/server/toolResult.js +8 -5
  122. package/install.sh +198 -0
  123. package/package.json +18 -1
  124. package/scripts/rea.mjs +5 -1
  125. package/skills/rea-analysis/SKILL.md +77 -2
@@ -0,0 +1,224 @@
1
+ import { buildCallPath, callPathInputSchema } from "../domain/callPath.js";
2
+ import { correlateStaticAndRuntime, staticRuntimeCorrelationInputSchema, } from "../domain/staticRuntimeCorrelation.js";
3
+ import { reconstructionVerificationInputSchema, verifyReconstruction, } from "../domain/reconstructionVerification.js";
4
+ import { changedBehaviorInputSchema, findChangedBehavior, } from "../domain/changedBehavior.js";
5
+ import { createEvidence } from "../domain/evidence.js";
6
+ import { EvidenceIntegrityError } from "../domain/errors.js";
7
+ import { jsonValueSchema } from "../domain/jsonValue.js";
8
+ import { err, ok } from "../domain/result.js";
9
+ import { CALL_PATH_PROVIDER, CHANGED_BEHAVIOR_PROVIDER, RECONSTRUCTION_PROVIDER, STATIC_RUNTIME_PROVIDER, } from "./sessionToolPolicies.js";
10
+ import { toCallToolResult } from "./toolResult.js";
11
+ import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
12
+ /** Register Evidence-composed differential investigation workflows. */
13
+ export const registerInvestigationTools = (server, session, contracts) => {
14
+ registerChangedBehavior(server, session, contracts[0]);
15
+ registerCallPath(server, session, contracts[1]);
16
+ registerStaticRuntime(server, session, contracts[2]);
17
+ registerReconstruction(server, session, contracts[3]);
18
+ };
19
+ const registerChangedBehavior = (server, session, contract) => {
20
+ server.registerTool(contract.name, contractOptions(contract), (input) => {
21
+ const parsed = changedBehaviorInputSchema.parse(input);
22
+ const closure = evidenceClosure(session, comparisonClosure(parsed.comparisons));
23
+ if (!closure.ok)
24
+ return toCallToolResult(closure, contract);
25
+ const links = closure.value;
26
+ const result = findChangedBehavior(parsed.comparisons, parsed.offset, parsed.limit);
27
+ const evidence = createEvidence(undefined, CHANGED_BEHAVIOR_PROVIDER, {
28
+ predicateType: "rea.changed-behavior/v1",
29
+ operation: contract.name,
30
+ parameters: {
31
+ comparison_evidence_ids: parsed.comparisons.map(({ evidence_id: id }) => id),
32
+ offset: parsed.offset,
33
+ limit: parsed.limit,
34
+ },
35
+ result: jsonValueSchema.parse(result),
36
+ confidence: "derived",
37
+ authority: "analyst-inference",
38
+ limitations: result.limitations,
39
+ evidenceLinks: links,
40
+ });
41
+ const recorded = recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.behavior_status === "unknown" ||
42
+ result.behavior_status === "truncated", {
43
+ question: `Changed behavior result ${evidence.evidence_id} remains ${result.behavior_status}`,
44
+ domain: "changed-behavior",
45
+ requiredAuthority: "controlled-replay",
46
+ requiredConfidence: "observed",
47
+ probes: [
48
+ {
49
+ operation: "capture_process_scenario",
50
+ rationale: "Capture both versions under the same bounded scenario and environment.",
51
+ },
52
+ ],
53
+ });
54
+ return toCallToolResult(recorded, contract);
55
+ });
56
+ };
57
+ const registerCallPath = (server, session, contract) => {
58
+ server.registerTool(contract.name, contractOptions(contract), (input) => {
59
+ const parsed = callPathInputSchema.parse(input);
60
+ const closure = evidenceClosure(session, functionEvidenceIds(parsed.functions));
61
+ if (!closure.ok)
62
+ return toCallToolResult(closure, contract);
63
+ const links = closure.value;
64
+ const result = buildCallPath(parsed);
65
+ const evidence = createEvidence(undefined, CALL_PATH_PROVIDER, {
66
+ predicateType: "rea.call-path/v1",
67
+ operation: contract.name,
68
+ parameters: {
69
+ start: parsed.start.address,
70
+ goal: parsed.goal.address,
71
+ max_depth: parsed.max_depth,
72
+ max_paths: parsed.max_paths,
73
+ offset: parsed.offset,
74
+ limit: parsed.limit,
75
+ },
76
+ result: jsonValueSchema.parse(result),
77
+ confidence: "derived",
78
+ authority: "analyst-inference",
79
+ limitations: result.limitations,
80
+ evidenceLinks: links,
81
+ });
82
+ const recorded = recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.status === "unknown" || result.status === "truncated", {
83
+ question: `Call path ${parsed.start.address} → ${parsed.goal.address} is ${result.status}`,
84
+ domain: "call-path",
85
+ requiredAuthority: "shipped-artifact",
86
+ requiredConfidence: "derived",
87
+ probes: [
88
+ {
89
+ operation: "analyze_function",
90
+ rationale: "Collect complete callee dossiers for the reported frontier addresses.",
91
+ },
92
+ ],
93
+ });
94
+ return toCallToolResult(recorded, contract);
95
+ });
96
+ };
97
+ const registerStaticRuntime = (server, session, contract) => {
98
+ server.registerTool(contract.name, contractOptions(contract), (input) => {
99
+ const parsed = staticRuntimeCorrelationInputSchema.parse(input);
100
+ const closure = evidenceClosure(session, comparisonClosure([
101
+ ...parsed.static_comparisons,
102
+ ...parsed.runtime_comparisons,
103
+ ]));
104
+ if (!closure.ok)
105
+ return toCallToolResult(closure, contract);
106
+ const links = closure.value;
107
+ const result = correlateStaticAndRuntime(parsed);
108
+ const evidence = createEvidence(undefined, STATIC_RUNTIME_PROVIDER, {
109
+ predicateType: "rea.static-runtime-correlation/v1",
110
+ operation: contract.name,
111
+ parameters: {
112
+ mapping_count: parsed.mappings.length,
113
+ offset: parsed.offset,
114
+ limit: parsed.limit,
115
+ },
116
+ result: jsonValueSchema.parse(result),
117
+ confidence: "inferred",
118
+ authority: "analyst-inference",
119
+ limitations: result.limitations,
120
+ evidenceLinks: links,
121
+ });
122
+ return toCallToolResult(recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.status === "unknown" || result.status === "truncated", {
123
+ question: `Static/runtime correlation result ${evidence.evidence_id} is ${result.status}`,
124
+ domain: "static-runtime-correlation",
125
+ requiredAuthority: null,
126
+ requiredConfidence: "derived",
127
+ probes: [
128
+ {
129
+ operation: "capture_process_scenario",
130
+ rationale: "Repeat runtime observations and complete the mapped static comparison Evidence.",
131
+ },
132
+ ],
133
+ }), contract);
134
+ });
135
+ };
136
+ const registerReconstruction = (server, session, contract) => {
137
+ server.registerTool(contract.name, contractOptions(contract), (input) => {
138
+ const parsed = reconstructionVerificationInputSchema.parse(input);
139
+ const owned = session.exportEvidenceBundle();
140
+ const ownedUnknowns = new Set(owned.unknowns.map(({ revision_digest: digest }) => digest));
141
+ const ownedEvidenceIds = new Set(owned.records.map(({ evidence_id: id }) => id));
142
+ if (parsed.evidence_bundle.records.some(({ evidence_id: id }) => !ownedEvidenceIds.has(id)) ||
143
+ parsed.evidence_bundle.unknowns.some(({ revision_digest: digest }) => !ownedUnknowns.has(digest)))
144
+ return toCallToolResult(err(new EvidenceIntegrityError("Reconstruction input unknown history is not present in this session")), contract);
145
+ const result = verifyReconstruction(parsed.specification, owned, parsed.offset, parsed.limit);
146
+ const closure = evidenceClosure(session, result.evidence_links);
147
+ if (!closure.ok)
148
+ return toCallToolResult(closure, contract);
149
+ const links = closure.value;
150
+ const evidence = createEvidence(undefined, RECONSTRUCTION_PROVIDER, {
151
+ predicateType: "rea.reconstruction-verification/v1",
152
+ operation: contract.name,
153
+ parameters: {
154
+ specification_sha256: result.specification_sha256,
155
+ claim_ids: parsed.specification.claims.map(({ claim_id: id }) => id),
156
+ offset: parsed.offset,
157
+ limit: parsed.limit,
158
+ },
159
+ result: jsonValueSchema.parse(result),
160
+ confidence: "derived",
161
+ authority: "analyst-inference",
162
+ limitations: result.limitations,
163
+ evidenceLinks: links,
164
+ });
165
+ return toCallToolResult(recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.status === "unknown", {
166
+ question: `Reconstruction ${result.specification_sha256} has unknown claims`,
167
+ domain: "reconstruction-verification",
168
+ requiredAuthority: null,
169
+ requiredConfidence: "derived",
170
+ probes: result.recommended_probes.map(({ operation, rationale }) => ({
171
+ operation,
172
+ rationale,
173
+ })),
174
+ }), contract);
175
+ });
176
+ };
177
+ const contractOptions = (contract) => ({
178
+ description: contract.description,
179
+ inputSchema: contract.inputSchema,
180
+ outputSchema: contract.outputSchema,
181
+ annotations: contract.annotations,
182
+ });
183
+ const comparisonClosure = (comparisons) => uniqueIds(comparisons.flatMap((evidence) => [
184
+ evidence.evidence_id,
185
+ ...evidence.evidence_links,
186
+ ]));
187
+ const functionEvidenceIds = (groups) => uniqueIds(groups.flatMap((group) => (Array.isArray(group) ? group : [group]).map(({ evidence_id: id }) => id)));
188
+ const uniqueIds = (ids) => [...new Set(ids)].sort((left, right) => left.localeCompare(right));
189
+ const evidenceClosure = (session, seedIds) => {
190
+ const records = new Map(session
191
+ .exportEvidenceBundle()
192
+ .records.map((evidence) => [evidence.evidence_id, evidence]));
193
+ const visited = new Set();
194
+ const pending = [...seedIds];
195
+ while (pending.length > 0) {
196
+ const evidenceId = pending.pop();
197
+ if (evidenceId === undefined || visited.has(evidenceId))
198
+ continue;
199
+ const evidence = records.get(evidenceId);
200
+ if (evidence === undefined)
201
+ return err(new EvidenceIntegrityError("Investigation input has a dangling Evidence link"));
202
+ visited.add(evidenceId);
203
+ pending.push(...evidence.evidence_links);
204
+ }
205
+ return ok(uniqueIds([...visited]));
206
+ };
207
+ const recordWorkflowEvidence = (...[session, evidence, approved, unresolved, input]) => {
208
+ if (approved !== true || !unresolved) {
209
+ return recordDerivedEvidence(session, evidence, undefined);
210
+ }
211
+ return recordDerivedEvidence(session, evidence, {
212
+ approved: true,
213
+ question: input.question,
214
+ severity: "high",
215
+ domain: input.domain,
216
+ supporting_evidence_ids: [evidence.evidence_id],
217
+ contradicting_evidence_ids: [],
218
+ required_authority: input.requiredAuthority,
219
+ required_confidence: input.requiredConfidence,
220
+ required_environment: null,
221
+ recommended_probes: [...input.probes],
222
+ relationships: [],
223
+ });
224
+ };
@@ -0,0 +1,6 @@
1
+ import { NATIVE_TOOL_CONTRACTS } from "../contracts/nativeToolContracts.js";
2
+ import { registerEvidenceTools } from "./registerEvidenceTools.js";
3
+ /** Register provider-neutral static inspection operations. */
4
+ export const registerNativeTools = (server, analysis, options) => {
5
+ registerEvidenceTools(server, analysis, NATIVE_TOOL_CONTRACTS, options);
6
+ };
@@ -1,20 +1,17 @@
1
- import { OFFICIAL_TOOL_CONTRACTS, } from "../contracts/toolContracts.js";
1
+ import { OFFICIAL_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
2
2
  import { jsonValueSchema } from "../domain/jsonValue.js";
3
3
  import { toCallToolResult } from "./toolResult.js";
4
4
  import { logToolExecution } from "./toolLogging.js";
5
5
  import { createEvidence } from "../domain/evidence.js";
6
+ import { AnalysisCapabilityUnavailableError, UnknownRegistryError, } from "../domain/errors.js";
6
7
  /** Register direct bridge proxies, preserving MCP cancellation and typed errors. */
7
- export const registerOfficialTools = (server, analysis, logger, activeTarget, provider = {
8
- id: "unidentified",
9
- name: "Unidentified provider",
10
- version: null,
11
- }, recordEvidence) => {
8
+ export const registerOfficialTools = (server, analysis, options) => {
12
9
  for (const contract of OFFICIAL_TOOL_CONTRACTS) {
13
10
  registerOfficialTool(server, analysis, contract, {
14
- logger,
15
- activeTarget,
16
- provider,
17
- recordEvidence,
11
+ logger: options.logger,
12
+ activeTarget: options.activeTarget,
13
+ recordEvidence: options.recordEvidence,
14
+ recordUnknown: options.recordUnknown,
18
15
  });
19
16
  }
20
17
  };
@@ -30,15 +27,45 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
30
27
  signal: context.mcpReq.signal,
31
28
  }));
32
29
  if (result.ok) {
33
- const evidence = createEvidence(registration.activeTarget?.(), registration.provider, {
30
+ const evidence = createEvidence(result.value.subject ?? registration.activeTarget?.(), result.value.provider, {
34
31
  operation: contract.name,
35
32
  parameters: arguments_,
36
- result: result.value,
37
- redactedRawPayload: result.value,
33
+ result: result.value.result,
34
+ rawResult: result.value.rawResult,
35
+ limitations: result.value.limitations,
36
+ locations: result.value.locations,
38
37
  });
39
- registration.recordEvidence?.(evidence);
38
+ const recorded = registration.recordEvidence?.(evidence);
39
+ if (recorded !== undefined && !recorded.ok)
40
+ return toCallToolResult(recorded, contract);
40
41
  return toCallToolResult({ ok: true, value: evidence }, contract);
41
42
  }
43
+ if (result.error instanceof AnalysisCapabilityUnavailableError &&
44
+ approvedUnknownTracking(input) &&
45
+ registration.recordUnknown !== undefined) {
46
+ const unknown = registration.recordUnknown({
47
+ approved: true,
48
+ question: `${contract.name} is unavailable: ${result.error.reason}`,
49
+ severity: "medium",
50
+ domain: "provider-capability",
51
+ supporting_evidence_ids: [],
52
+ contradicting_evidence_ids: [],
53
+ required_authority: "shipped-artifact",
54
+ required_confidence: "observed",
55
+ required_environment: null,
56
+ recommended_probes: [
57
+ {
58
+ operation: contract.name,
59
+ rationale: "Use a provider that declares this capability available.",
60
+ },
61
+ ],
62
+ relationships: [],
63
+ });
64
+ if (!unknown.ok &&
65
+ !(unknown.error instanceof UnknownRegistryError &&
66
+ unknown.error.reason === "already-exists"))
67
+ return toCallToolResult(unknown, contract);
68
+ }
42
69
  return toCallToolResult(result, contract);
43
70
  });
44
71
  };
@@ -53,7 +80,13 @@ const projectOfficialArguments = (contract, input) => {
53
80
  }
54
81
  const projected = {};
55
82
  for (const key of Object.keys(contract.inputSchema.shape)) {
83
+ if (key === "unknown_registry_approved")
84
+ continue;
56
85
  projected[key] = parsed.data[key] ?? null;
57
86
  }
58
87
  return projected;
59
88
  };
89
+ const approvedUnknownTracking = (input) => typeof input === "object" &&
90
+ input !== null &&
91
+ "unknown_registry_approved" in input &&
92
+ input.unknown_registry_approved === true;
@@ -0,0 +1,106 @@
1
+ import canonicalize from "canonicalize";
2
+ import { z } from "zod";
3
+ import { SESSION_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
4
+ import { createEvidence } from "../domain/evidence.js";
5
+ import { EvidenceIntegrityError } from "../domain/errors.js";
6
+ import { jsonValueSchema } from "../domain/jsonValue.js";
7
+ import { compareProcessCaptures, processCaptureSchema, } from "../domain/processCapture.js";
8
+ import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
9
+ import { err } from "../domain/result.js";
10
+ import { PROCESS_PROVIDER } from "./sessionToolPolicies.js";
11
+ import { toCallToolResult } from "./toolResult.js";
12
+ import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
13
+ /** Register deterministic process-capture comparison and contradiction tracking. */
14
+ export const registerProcessComparisonTool = (server, session, contract) => {
15
+ server.registerTool(contract.name, toolRegistrationOptions(contract), (input) => {
16
+ const parsed = z
17
+ .object({
18
+ left_evidence_id: z.string(),
19
+ left: processCaptureSchema,
20
+ right_evidence_id: z.string(),
21
+ right: processCaptureSchema,
22
+ unknown_registry_approved: z.literal(true).optional(),
23
+ })
24
+ .parse(input);
25
+ const validated = validateCaptureSources(session, parsed);
26
+ if (!validated.ok)
27
+ return toCallToolResult(validated, contract);
28
+ const comparison = compareProcessCaptures(parsed.left, parsed.right);
29
+ const evidence = createEvidence(undefined, PROCESS_PROVIDER, {
30
+ predicateType: "rea.process-comparison/v1",
31
+ operation: contract.name,
32
+ parameters: {
33
+ left_evidence_id: parsed.left_evidence_id,
34
+ right_evidence_id: parsed.right_evidence_id,
35
+ left_normalization: parsed.left.normalization,
36
+ right_normalization: parsed.right.normalization,
37
+ },
38
+ result: jsonValueSchema.parse(comparison),
39
+ confidence: "derived",
40
+ authority: "analyst-inference",
41
+ limitations: comparison.limitations,
42
+ evidenceLinks: [parsed.left_evidence_id, parsed.right_evidence_id],
43
+ });
44
+ return toCallToolResult(recordDerivedEvidence(session, evidence, comparisonUnknownInput(parsed, comparison)), contract);
45
+ });
46
+ };
47
+ const validateCaptureSources = (session, parsed) => {
48
+ for (const [evidenceId, capture] of [
49
+ [parsed.left_evidence_id, parsed.left],
50
+ [parsed.right_evidence_id, parsed.right],
51
+ ]) {
52
+ const evidence = session.evidenceById(evidenceId);
53
+ const result = processCaptureSchema.safeParse(evidence?.normalized_result);
54
+ if (evidence === undefined ||
55
+ evidence.operation !== "capture_process_scenario" ||
56
+ evidence.predicate_type !== "rea.process-capture/v2" ||
57
+ evidence.provider.id !== PROCESS_PROVIDER.id ||
58
+ evidence.provider.name !== PROCESS_PROVIDER.name ||
59
+ evidence.provider.version !== PROCESS_PROVIDER.version ||
60
+ evidence.confidence !== "observed" ||
61
+ evidence.authority !== "controlled-replay" ||
62
+ !result.success ||
63
+ canonicalJson(result.data) !== canonicalJson(capture))
64
+ return err(new EvidenceIntegrityError("Process comparison payload does not match its source Evidence"));
65
+ }
66
+ return { ok: true, value: null };
67
+ };
68
+ const canonicalJson = (value) => {
69
+ const encoded = canonicalize(value);
70
+ if (encoded === undefined)
71
+ throw new TypeError("Process capture could not be canonicalized");
72
+ return encoded;
73
+ };
74
+ const comparisonUnknownInput = (parsed, comparison) => {
75
+ if (parsed.unknown_registry_approved !== true ||
76
+ comparison.status === "unchanged")
77
+ return undefined;
78
+ const differingScopes = [
79
+ ["terminal", comparison.terminal],
80
+ ["exit", comparison.exit],
81
+ ["filesystem", comparison.filesystem],
82
+ ["protocol", comparison.protocol],
83
+ ["process", comparison.process],
84
+ ]
85
+ .filter(([, status]) => status !== "unchanged")
86
+ .map(([scope]) => scope)
87
+ .join(", ");
88
+ return {
89
+ approved: true,
90
+ question: `Process captures disagree across: ${differingScopes}`,
91
+ severity: "high",
92
+ domain: "process-comparison",
93
+ supporting_evidence_ids: [parsed.left_evidence_id],
94
+ contradicting_evidence_ids: [parsed.right_evidence_id],
95
+ required_authority: "controlled-replay",
96
+ required_confidence: "observed",
97
+ required_environment: null,
98
+ recommended_probes: [
99
+ {
100
+ operation: "capture_process_scenario",
101
+ rationale: "Repeat both scenarios under the same controlled environment.",
102
+ },
103
+ ],
104
+ relationships: [],
105
+ };
106
+ };