rea-agents 1.2.0 → 1.4.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 (107) hide show
  1. package/README.md +95 -27
  2. package/bridge/hopper_bridge.py +172 -14
  3. package/dist/application/AnalysisSnapshotCache.js +131 -0
  4. package/dist/application/AnalysisSnapshotFiles.js +31 -0
  5. package/dist/application/ArtifactGraphConstruction.js +1 -1
  6. package/dist/application/ArtifactInventory.js +100 -24
  7. package/dist/application/AuthorizedArtifactInventory.js +16 -0
  8. package/dist/application/BinarySession.js +149 -36
  9. package/dist/application/BinarySessionPort.js +1 -0
  10. package/dist/application/BoundedJsonFiles.js +109 -0
  11. package/dist/application/CapabilityInventory.js +127 -0
  12. package/dist/application/ClientRegistrationStatus.js +69 -0
  13. package/dist/application/CrossVersionInventory.js +109 -0
  14. package/dist/application/CrossVersionInvestigation.js +358 -0
  15. package/dist/application/DirectAnalysis.js +205 -24
  16. package/dist/application/Doctor.js +104 -3
  17. package/dist/application/EvidenceBundleFiles.js +15 -108
  18. package/dist/application/EvidenceLedger.js +3 -2
  19. package/dist/application/InvestigationProviders.js +48 -0
  20. package/dist/application/InvestigationWorkspaceStore.js +214 -0
  21. package/dist/application/LinuxHopper.js +72 -48
  22. package/dist/application/PermissionAuthority.js +183 -0
  23. package/dist/application/PermissionConfiguration.js +26 -0
  24. package/dist/application/ProcessCaptureAuthority.js +2 -2
  25. package/dist/application/ProcessCaptureError.js +25 -0
  26. package/dist/application/ProcessCaptureLifecycle.js +22 -2
  27. package/dist/application/ProcessCli.js +111 -31
  28. package/dist/application/ProcessHarness.js +6 -3
  29. package/dist/application/ProgressReporter.js +30 -0
  30. package/dist/application/ProjectPermissionStore.js +110 -0
  31. package/dist/application/ReferenceSourceImportEntries.js +18 -3
  32. package/dist/application/ReferenceSourceImportTypes.js +32 -0
  33. package/dist/application/Setup.js +35 -101
  34. package/dist/application/SetupClients.js +19 -0
  35. package/dist/application/SetupInstallFailure.js +53 -0
  36. package/dist/application/SetupPlan.js +31 -0
  37. package/dist/application/SupportedClients.js +41 -0
  38. package/dist/application/Uninstall.js +34 -24
  39. package/dist/application/UnknownEvidence.js +33 -0
  40. package/dist/application/Upgrade.js +3 -1
  41. package/dist/application/runtime.js +2 -2
  42. package/dist/artifacts/ArtifactProvider.js +15 -8
  43. package/dist/catalogIdentity.js +121 -0
  44. package/dist/cli.js +76 -13
  45. package/dist/cliEvidenceCommands.js +52 -12
  46. package/dist/cliInvestigationCommands.js +137 -0
  47. package/dist/cliLogging.js +20 -3
  48. package/dist/cliOutput.js +41 -0
  49. package/dist/cliPolicyCommands.js +115 -0
  50. package/dist/config.js +102 -27
  51. package/dist/contracts/artifactToolContracts.js +14 -1
  52. package/dist/contracts/errorSchemas.js +98 -0
  53. package/dist/contracts/promptContracts.js +256 -0
  54. package/dist/contracts/sessionLifecycleInputs.js +11 -0
  55. package/dist/contracts/toolContractTypes.js +1 -0
  56. package/dist/contracts/toolContracts.js +20 -7
  57. package/dist/contracts/toolOutputSchemas.js +70 -9
  58. package/dist/domain/analysisSnapshot.js +149 -0
  59. package/dist/domain/artifactComparison.js +38 -11
  60. package/dist/domain/artifactGraph.js +25 -1
  61. package/dist/domain/artifactInventoryEvidence.js +18 -0
  62. package/dist/domain/changedBehavior.js +35 -10
  63. package/dist/domain/errors.js +384 -32
  64. package/dist/domain/evidence.js +4 -2
  65. package/dist/domain/evidenceBundle.js +28 -0
  66. package/dist/domain/hopperStartupFailure.js +55 -0
  67. package/dist/domain/investigationWorkspace.js +341 -0
  68. package/dist/domain/jsonValue.js +2 -0
  69. package/dist/domain/nativeInspection.js +3 -2
  70. package/dist/domain/permissionPolicy.js +157 -0
  71. package/dist/domain/processCapture.js +5 -4
  72. package/dist/domain/processComparison.js +6 -4
  73. package/dist/domain/reconstructionVerification.js +1 -1
  74. package/dist/domain/reconstructionVerificationSchemas.js +1 -0
  75. package/dist/domain/staticRuntimeCorrelation.js +5 -1
  76. package/dist/generatedPackageMetadata.js +9 -0
  77. package/dist/hopper/BridgeLauncher.js +51 -20
  78. package/dist/hopper/HopperClient.js +45 -3
  79. package/dist/hopper/HopperProvider.js +1 -0
  80. package/dist/identity.js +10 -3
  81. package/dist/logger.js +2 -2
  82. package/dist/main.js +139 -28
  83. package/dist/server/createServer.js +54 -5
  84. package/dist/server/mcpProgress.js +23 -0
  85. package/dist/server/promptCompletion.js +144 -0
  86. package/dist/server/registerArtifactComparisonTool.js +6 -2
  87. package/dist/server/registerBundleComparisonTool.js +6 -2
  88. package/dist/server/registerEnhancedTools.js +20 -7
  89. package/dist/server/registerEvidenceResources.js +302 -0
  90. package/dist/server/registerEvidenceTools.js +59 -8
  91. package/dist/server/registerFunctionComparisonTool.js +7 -3
  92. package/dist/server/registerInvestigationTools.js +111 -18
  93. package/dist/server/registerOfficialTools.js +25 -14
  94. package/dist/server/registerProcessComparisonTool.js +8 -10
  95. package/dist/server/registerPrompts.js +67 -0
  96. package/dist/server/registerSessionStatusTool.js +52 -0
  97. package/dist/server/registerSessionTools.js +178 -22
  98. package/dist/server/runDerivedOperation.js +35 -0
  99. package/dist/server/sessionEvidence.js +2 -8
  100. package/dist/server/sessionToolPolicies.js +1 -42
  101. package/dist/server/toolResult.js +34 -6
  102. package/dist/serverIdentity.js +70 -0
  103. package/install.sh +11 -11
  104. package/package.json +10 -6
  105. package/scripts/hopper-demo-x11.py +349 -0
  106. package/scripts/rea.mjs +6 -3
  107. package/skills/rea-analysis/SKILL.md +9 -2
@@ -1,7 +1,8 @@
1
+ import { runCrossVersionInvestigation } from "../application/CrossVersionInvestigation.js";
1
2
  import { buildCallPath, callPathInputSchema } from "../domain/callPath.js";
2
3
  import { correlateStaticAndRuntime, staticRuntimeCorrelationInputSchema, } from "../domain/staticRuntimeCorrelation.js";
3
4
  import { reconstructionVerificationInputSchema, verifyReconstruction, } from "../domain/reconstructionVerification.js";
4
- import { changedBehaviorInputSchema, findChangedBehavior, } from "../domain/changedBehavior.js";
5
+ import { changedBehaviorResultSchema, changedBehaviorInputSchema, findChangedBehavior, } from "../domain/changedBehavior.js";
5
6
  import { createEvidence } from "../domain/evidence.js";
6
7
  import { EvidenceIntegrityError } from "../domain/errors.js";
7
8
  import { jsonValueSchema } from "../domain/jsonValue.js";
@@ -9,21 +10,105 @@ import { err, ok } from "../domain/result.js";
9
10
  import { CALL_PATH_PROVIDER, CHANGED_BEHAVIOR_PROVIDER, RECONSTRUCTION_PROVIDER, STATIC_RUNTIME_PROVIDER, } from "./sessionToolPolicies.js";
10
11
  import { toCallToolResult } from "./toolResult.js";
11
12
  import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
13
+ import { AnalysisProtocolError, PermissionRequiredError, } from "../domain/errors.js";
14
+ import { mcpProgressReporter } from "./mcpProgress.js";
15
+ import { runDerivedOperation } from "./runDerivedOperation.js";
12
16
  /** Register Evidence-composed differential investigation workflows. */
13
- export const registerInvestigationTools = (server, session, contracts) => {
14
- registerChangedBehavior(server, session, contracts[0]);
17
+ export const registerInvestigationTools = (server, session, contracts, policies) => {
18
+ registerChangedBehavior(server, session, contracts[0], policies);
15
19
  registerCallPath(server, session, contracts[1]);
16
20
  registerStaticRuntime(server, session, contracts[2]);
17
21
  registerReconstruction(server, session, contracts[3]);
18
22
  };
19
- const registerChangedBehavior = (server, session, contract) => {
20
- server.registerTool(contract.name, contractOptions(contract), (input) => {
23
+ const investigationExecution = (session, signal, inputRoots, integrityContinueEnabled, progress) => ({
24
+ session,
25
+ signal,
26
+ inputRoots,
27
+ integrityContinueEnabled,
28
+ ...(progress === undefined ? {} : { progress }),
29
+ });
30
+ const isIncomplete = (status) => status === "unknown" || status === "truncated";
31
+ const registerChangedBehavior = (server, session, contract, policies) => {
32
+ server.registerTool(contract.name, contractOptions(contract), async (input, context) => {
21
33
  const parsed = changedBehaviorInputSchema.parse(input);
34
+ if (parsed.investigation_run !== undefined) {
35
+ const progress = mcpProgressReporter(context);
36
+ if (policies.permissionAuthority !== undefined) {
37
+ const scopes = [
38
+ {
39
+ capability: "investigation_workspace_read",
40
+ roots: [parsed.investigation_run.workspace_path],
41
+ },
42
+ {
43
+ capability: "investigation_workspace_write",
44
+ roots: [parsed.investigation_run.workspace_path],
45
+ },
46
+ {
47
+ capability: "investigation_input",
48
+ roots: [
49
+ parsed.investigation_run.left_path,
50
+ parsed.investigation_run.right_path,
51
+ ],
52
+ },
53
+ ];
54
+ for (const scope of scopes) {
55
+ const authorized = await policies.permissionAuthority.authorize({
56
+ ...scope,
57
+ executables: [],
58
+ environment_names: [],
59
+ network: "none",
60
+ mount: false,
61
+ operation_identity: `${contract.name}:${scope.capability}:${scope.roots.join(":")}`,
62
+ }, scope.capability === "investigation_input" ? "read" : "write");
63
+ if (!authorized.ok)
64
+ return toCallToolResult(err(authorized.error instanceof PermissionRequiredError
65
+ ? authorized.error
66
+ : new AnalysisProtocolError(authorized.error.message, {
67
+ cause: authorized.error,
68
+ })), contract);
69
+ }
70
+ }
71
+ const investigated = await runCrossVersionInvestigation(parsed.investigation_run, policies.evidenceFiles, investigationExecution(session, context.mcpReq.signal, policies.inputRoots, policies.integrityContinueEnabled?.() ?? false, progress));
72
+ if (!investigated.ok)
73
+ return toCallToolResult(investigated, contract);
74
+ const workspace = investigated.value.workspace;
75
+ if (session.retainInvestigationWorkspace(workspace) === "added")
76
+ server.sendResourceListChanged();
77
+ const result = changedBehaviorResultSchema.parse(investigated.value.evidence.normalized_result);
78
+ const toolResult = toCallToolResult(recordWorkflowEvidence(session, investigated.value.evidence, parsed.unknown_registry_approved, isIncomplete(result.behavior_status), {
79
+ question: `Automatic changed behavior run ${result.investigation_run?.run_id ?? "unknown"} remains ${result.behavior_status}`,
80
+ domain: "changed-behavior",
81
+ requiredAuthority: "controlled-replay",
82
+ requiredConfidence: "observed",
83
+ probes: [
84
+ {
85
+ operation: "capture_process_scenario",
86
+ rationale: "Capture both versions under the same bounded scenario and environment.",
87
+ },
88
+ ],
89
+ }), contract);
90
+ return {
91
+ ...toolResult,
92
+ content: [
93
+ ...toolResult.content,
94
+ {
95
+ type: "resource_link",
96
+ uri: `rea://workspace/${workspace.workspace_id}/revision/${String(workspace.revision)}`,
97
+ name: `${workspace.workspace_id} revision ${String(workspace.revision)}`,
98
+ description: "Immutable CAS-linked investigation workspace revision",
99
+ mimeType: "application/json",
100
+ },
101
+ ],
102
+ };
103
+ }
22
104
  const closure = evidenceClosure(session, comparisonClosure(parsed.comparisons));
23
105
  if (!closure.ok)
24
106
  return toCallToolResult(closure, contract);
25
107
  const links = closure.value;
26
- const result = findChangedBehavior(parsed.comparisons, parsed.offset, parsed.limit);
108
+ const computed = await runDerivedOperation(context, contract.name, () => findChangedBehavior(parsed.comparisons, parsed.offset, parsed.limit));
109
+ if (!computed.ok)
110
+ return toCallToolResult(computed, contract);
111
+ const result = computed.value;
27
112
  const evidence = createEvidence(undefined, CHANGED_BEHAVIOR_PROVIDER, {
28
113
  predicateType: "rea.changed-behavior/v1",
29
114
  operation: contract.name,
@@ -38,9 +123,8 @@ const registerChangedBehavior = (server, session, contract) => {
38
123
  limitations: result.limitations,
39
124
  evidenceLinks: links,
40
125
  });
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}`,
126
+ const recorded = recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, isIncomplete(result.behavior_status), {
127
+ question: "Did both versions behave the same under a complete controlled replay?",
44
128
  domain: "changed-behavior",
45
129
  requiredAuthority: "controlled-replay",
46
130
  requiredConfidence: "observed",
@@ -55,13 +139,16 @@ const registerChangedBehavior = (server, session, contract) => {
55
139
  });
56
140
  };
57
141
  const registerCallPath = (server, session, contract) => {
58
- server.registerTool(contract.name, contractOptions(contract), (input) => {
142
+ server.registerTool(contract.name, contractOptions(contract), async (input, context) => {
59
143
  const parsed = callPathInputSchema.parse(input);
60
144
  const closure = evidenceClosure(session, functionEvidenceIds(parsed.functions));
61
145
  if (!closure.ok)
62
146
  return toCallToolResult(closure, contract);
63
147
  const links = closure.value;
64
- const result = buildCallPath(parsed);
148
+ const computed = await runDerivedOperation(context, contract.name, () => buildCallPath(parsed));
149
+ if (!computed.ok)
150
+ return toCallToolResult(computed, contract);
151
+ const result = computed.value;
65
152
  const evidence = createEvidence(undefined, CALL_PATH_PROVIDER, {
66
153
  predicateType: "rea.call-path/v1",
67
154
  operation: contract.name,
@@ -80,7 +167,7 @@ const registerCallPath = (server, session, contract) => {
80
167
  evidenceLinks: links,
81
168
  });
82
169
  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}`,
170
+ question: "Can the requested call path be established from complete analysis?",
84
171
  domain: "call-path",
85
172
  requiredAuthority: "shipped-artifact",
86
173
  requiredConfidence: "derived",
@@ -95,7 +182,7 @@ const registerCallPath = (server, session, contract) => {
95
182
  });
96
183
  };
97
184
  const registerStaticRuntime = (server, session, contract) => {
98
- server.registerTool(contract.name, contractOptions(contract), (input) => {
185
+ server.registerTool(contract.name, contractOptions(contract), async (input, context) => {
99
186
  const parsed = staticRuntimeCorrelationInputSchema.parse(input);
100
187
  const closure = evidenceClosure(session, comparisonClosure([
101
188
  ...parsed.static_comparisons,
@@ -104,7 +191,10 @@ const registerStaticRuntime = (server, session, contract) => {
104
191
  if (!closure.ok)
105
192
  return toCallToolResult(closure, contract);
106
193
  const links = closure.value;
107
- const result = correlateStaticAndRuntime(parsed);
194
+ const computed = await runDerivedOperation(context, contract.name, () => correlateStaticAndRuntime(parsed));
195
+ if (!computed.ok)
196
+ return toCallToolResult(computed, contract);
197
+ const result = computed.value;
108
198
  const evidence = createEvidence(undefined, STATIC_RUNTIME_PROVIDER, {
109
199
  predicateType: "rea.static-runtime-correlation/v1",
110
200
  operation: contract.name,
@@ -120,7 +210,7 @@ const registerStaticRuntime = (server, session, contract) => {
120
210
  evidenceLinks: links,
121
211
  });
122
212
  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}`,
213
+ question: "Does runtime behavior match the available static analysis?",
124
214
  domain: "static-runtime-correlation",
125
215
  requiredAuthority: null,
126
216
  requiredConfidence: "derived",
@@ -134,7 +224,7 @@ const registerStaticRuntime = (server, session, contract) => {
134
224
  });
135
225
  };
136
226
  const registerReconstruction = (server, session, contract) => {
137
- server.registerTool(contract.name, contractOptions(contract), (input) => {
227
+ server.registerTool(contract.name, contractOptions(contract), async (input, context) => {
138
228
  const parsed = reconstructionVerificationInputSchema.parse(input);
139
229
  const owned = session.exportEvidenceBundle();
140
230
  const ownedUnknowns = new Set(owned.unknowns.map(({ revision_digest: digest }) => digest));
@@ -142,7 +232,10 @@ const registerReconstruction = (server, session, contract) => {
142
232
  if (parsed.evidence_bundle.records.some(({ evidence_id: id }) => !ownedEvidenceIds.has(id)) ||
143
233
  parsed.evidence_bundle.unknowns.some(({ revision_digest: digest }) => !ownedUnknowns.has(digest)))
144
234
  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);
235
+ const computed = await runDerivedOperation(context, contract.name, () => verifyReconstruction(parsed.specification, owned, parsed.offset, parsed.limit));
236
+ if (!computed.ok)
237
+ return toCallToolResult(computed, contract);
238
+ const result = computed.value;
146
239
  const closure = evidenceClosure(session, result.evidence_links);
147
240
  if (!closure.ok)
148
241
  return toCallToolResult(closure, contract);
@@ -163,7 +256,7 @@ const registerReconstruction = (server, session, contract) => {
163
256
  evidenceLinks: links,
164
257
  });
165
258
  return toCallToolResult(recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.status === "unknown", {
166
- question: `Reconstruction ${result.specification_sha256} has unknown claims`,
259
+ question: "Does the reconstruction satisfy every declared claim?",
167
260
  domain: "reconstruction-verification",
168
261
  requiredAuthority: null,
169
262
  requiredConfidence: "derived",
@@ -1,9 +1,10 @@
1
1
  import { OFFICIAL_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
2
- import { jsonValueSchema } from "../domain/jsonValue.js";
2
+ import { jsonObjectSchema, 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
6
  import { AnalysisCapabilityUnavailableError, UnknownRegistryError, } from "../domain/errors.js";
7
+ import { mcpProgressReporter } from "./mcpProgress.js";
7
8
  /** Register direct bridge proxies, preserving MCP cancellation and typed errors. */
8
9
  export const registerOfficialTools = (server, analysis, options) => {
9
10
  for (const contract of OFFICIAL_TOOL_CONTRACTS) {
@@ -23,9 +24,24 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
23
24
  annotations: contract.annotations,
24
25
  }, async (input, context) => {
25
26
  const arguments_ = projectOfficialArguments(contract, input);
27
+ const progress = mcpProgressReporter(context);
28
+ await progress.report({
29
+ phase: contract.name,
30
+ completed: 0,
31
+ total: 1,
32
+ message: "started",
33
+ });
26
34
  const result = await logToolExecution(registration.logger, contract.name, () => analysis.execute(contract.name, arguments_, {
27
35
  signal: context.mcpReq.signal,
36
+ progress,
28
37
  }));
38
+ await progress.report({
39
+ phase: contract.name,
40
+ completed: 1,
41
+ total: 1,
42
+ message: result.ok ? "completed" : "failed",
43
+ terminal: true,
44
+ });
29
45
  if (result.ok) {
30
46
  const evidence = createEvidence(result.value.subject ?? registration.activeTarget?.(), result.value.provider, {
31
47
  operation: contract.name,
@@ -38,16 +54,18 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
38
54
  const recorded = registration.recordEvidence?.(evidence);
39
55
  if (recorded !== undefined && !recorded.ok)
40
56
  return toCallToolResult(recorded, contract);
41
- return toCallToolResult({ ok: true, value: evidence }, contract);
57
+ return toCallToolResult({ ok: true, value: evidence }, contract, {
58
+ evidenceResourcesAvailable: recorded !== undefined,
59
+ });
42
60
  }
43
61
  if (result.error instanceof AnalysisCapabilityUnavailableError &&
44
62
  approvedUnknownTracking(input) &&
45
63
  registration.recordUnknown !== undefined) {
46
64
  const unknown = registration.recordUnknown({
47
65
  approved: true,
48
- question: `${contract.name} is unavailable: ${result.error.reason}`,
66
+ question: "The requested analysis is unavailable for the current target.",
49
67
  severity: "medium",
50
- domain: "provider-capability",
68
+ domain: "analysis-capability",
51
69
  supporting_evidence_ids: [],
52
70
  contradicting_evidence_ids: [],
53
71
  required_authority: "shipped-artifact",
@@ -56,7 +74,7 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
56
74
  recommended_probes: [
57
75
  {
58
76
  operation: contract.name,
59
- rationale: "Use a provider that declares this capability available.",
77
+ rationale: "Choose another analysis or target that can answer this question.",
60
78
  },
61
79
  ],
62
80
  relationships: [],
@@ -70,19 +88,12 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
70
88
  });
71
89
  };
72
90
  const projectOfficialArguments = (contract, input) => {
73
- const parsed = jsonValueSchema.safeParse(input);
74
- if (!parsed.success ||
75
- typeof parsed.data !== "object" ||
76
- parsed.data === null ||
77
- Array.isArray(parsed.data)) {
78
- // The SDK validates this with the same schema before invoking the callback.
79
- throw new Error("Validated MCP tool input was not a JSON object");
80
- }
91
+ const parsed = jsonObjectSchema.parse(contract.inputSchema.parse(input));
81
92
  const projected = {};
82
93
  for (const key of Object.keys(contract.inputSchema.shape)) {
83
94
  if (key === "unknown_registry_approved")
84
95
  continue;
85
- projected[key] = parsed.data[key] ?? null;
96
+ projected[key] = jsonValueSchema.parse(parsed[key] ?? null);
86
97
  }
87
98
  return projected;
88
99
  };
@@ -10,9 +10,10 @@ import { err } from "../domain/result.js";
10
10
  import { PROCESS_PROVIDER } from "./sessionToolPolicies.js";
11
11
  import { toCallToolResult } from "./toolResult.js";
12
12
  import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
13
+ import { runDerivedOperation } from "./runDerivedOperation.js";
13
14
  /** Register deterministic process-capture comparison and contradiction tracking. */
14
15
  export const registerProcessComparisonTool = (server, session, contract, now = Date.now) => {
15
- server.registerTool(contract.name, toolRegistrationOptions(contract), (input) => {
16
+ server.registerTool(contract.name, toolRegistrationOptions(contract), async (input, context) => {
16
17
  const parsed = z
17
18
  .object({
18
19
  left_evidence_id: z.string(),
@@ -28,12 +29,15 @@ export const registerProcessComparisonTool = (server, session, contract, now = D
28
29
  return toCallToolResult(validated, contract);
29
30
  let comparison;
30
31
  try {
31
- comparison = compareProcessCaptures(parsed.left, parsed.right, {
32
+ const computed = await runDerivedOperation(context, contract.name, () => compareProcessCaptures(parsed.left, parsed.right, {
32
33
  ...(parsed.max_capture_age_ms === undefined
33
34
  ? {}
34
35
  : { maxCaptureAgeMs: parsed.max_capture_age_ms }),
35
36
  now,
36
- });
37
+ }));
38
+ if (!computed.ok)
39
+ return toCallToolResult(computed, contract);
40
+ comparison = computed.value;
37
41
  }
38
42
  catch (cause) {
39
43
  return toCallToolResult(err(new EvidenceIntegrityError(cause instanceof Error
@@ -80,17 +84,11 @@ const validateCaptureSources = (session, parsed) => {
80
84
  evidence.confidence !== "observed" ||
81
85
  evidence.authority !== "controlled-replay" ||
82
86
  evidenceCapture === undefined ||
83
- canonicalJson(evidenceCapture) !== canonicalJson(capture))
87
+ canonicalize(evidenceCapture) !== canonicalize(capture))
84
88
  return err(new EvidenceIntegrityError("Process comparison payload does not match its source Evidence"));
85
89
  }
86
90
  return { ok: true, value: null };
87
91
  };
88
- const canonicalJson = (value) => {
89
- const encoded = canonicalize(value);
90
- if (encoded === undefined)
91
- throw new TypeError("Process capture could not be canonicalized");
92
- return encoded;
93
- };
94
92
  const comparisonUnknownInput = (parsed, comparison) => {
95
93
  if (parsed.unknown_registry_approved !== true ||
96
94
  comparison.status === "unchanged")
@@ -0,0 +1,67 @@
1
+ import { completable, } from "@modelcontextprotocol/server";
2
+ import { z } from "zod";
3
+ import { PROMPT_CONTRACTS, renderGuidedPrompt, } from "../contracts/promptContracts.js";
4
+ import { createPromptCompletionSource, } from "./promptCompletion.js";
5
+ const promptValuesSchema = z.record(z.string(), z.string());
6
+ /** Register every guided workflow and its session-scoped argument completers. */
7
+ export const registerGuidedPrompts = (server, analysis, session) => {
8
+ const completion = createPromptCompletionSource(analysis, session);
9
+ const prompts = new Map();
10
+ for (const contract of PROMPT_CONTRACTS)
11
+ prompts.set(contract.name, registerPrompt(server, contract, completion));
12
+ return {
13
+ update(contract) {
14
+ const prompt = prompts.get(contract.name);
15
+ if (prompt === undefined)
16
+ throw new RangeError(`Unknown guided prompt ${contract.name}`);
17
+ const argsSchema = promptArgumentsSchema(contract, completion);
18
+ prompt.update({
19
+ title: contract.title,
20
+ description: contract.description,
21
+ argsSchema,
22
+ callback: (arguments_) => promptResult(contract, arguments_),
23
+ });
24
+ },
25
+ };
26
+ };
27
+ const registerPrompt = (server, contract, completion) => {
28
+ const argsSchema = promptArgumentsSchema(contract, completion);
29
+ return server.registerPrompt(contract.name, {
30
+ title: contract.title,
31
+ description: contract.description,
32
+ argsSchema,
33
+ }, (arguments_) => promptResult(contract, arguments_));
34
+ };
35
+ const promptArgumentsSchema = (contract, completion) => {
36
+ const shape = {};
37
+ for (const [name, argument] of Object.entries(contract.arguments))
38
+ shape[name] = argumentSchema(argument, completion);
39
+ return z.object(shape);
40
+ };
41
+ const argumentSchema = (argument, completion) => {
42
+ const value = z
43
+ .string()
44
+ .trim()
45
+ .min(1)
46
+ .max(4_096)
47
+ .describe(argument.description);
48
+ const kind = argument.completion;
49
+ const completed = kind === undefined
50
+ ? value
51
+ : completable(value, async (partial, context) => [
52
+ ...(await completion.complete(kind, partial, context)),
53
+ ]);
54
+ return argument.required ? completed : completed.optional();
55
+ };
56
+ const promptResult = (contract, arguments_) => ({
57
+ description: contract.description,
58
+ messages: [
59
+ {
60
+ role: "user",
61
+ content: {
62
+ type: "text",
63
+ text: renderGuidedPrompt(contract, promptValuesSchema.parse(arguments_)),
64
+ },
65
+ },
66
+ ],
67
+ });
@@ -0,0 +1,52 @@
1
+ import { binarySessionInputSchema, SESSION_TOOL_CONTRACTS, } from "../contracts/toolContracts.js";
2
+ import { toCallToolResult } from "./toolResult.js";
3
+ import { jsonObjectSchema } from "../domain/jsonValue.js";
4
+ import { createServerIdentity } from "../serverIdentity.js";
5
+ import { buildCapabilityInventory } from "../application/CapabilityInventory.js";
6
+ /** Register the read-only provider and target status operation. */
7
+ export const registerSessionStatusTool = (server, session, contract, startedAt, availabilityPolicy) => {
8
+ server.registerTool(contract.name, {
9
+ description: contract.description,
10
+ inputSchema: contract.inputSchema,
11
+ outputSchema: contract.outputSchema,
12
+ annotations: contract.annotations,
13
+ }, (input) => {
14
+ const parsed = binarySessionInputSchema.parse(input);
15
+ const client = server.server.getClientVersion();
16
+ const clientCapabilities = server.server.getClientCapabilities();
17
+ const status = session.status();
18
+ return toCallToolResult({
19
+ ok: true,
20
+ value: {
21
+ ...jsonObjectSchema.parse(status),
22
+ tool_availability: buildCapabilityInventory(status, availabilityPolicy()),
23
+ client_features: {
24
+ elicitation_form: clientCapabilities?.elicitation?.form !== undefined,
25
+ elicitation_url: clientCapabilities?.elicitation?.url !== undefined,
26
+ roots: clientCapabilities?.roots !== undefined,
27
+ sampling: clientCapabilities?.sampling !== undefined,
28
+ },
29
+ server_identity: createServerIdentity({
30
+ startedAt,
31
+ expected: {
32
+ ...(parsed.expected_package_version === undefined
33
+ ? {}
34
+ : { package_version: parsed.expected_package_version }),
35
+ ...(parsed.expected_catalog_digest === undefined
36
+ ? {}
37
+ : { catalog_digest: parsed.expected_catalog_digest }),
38
+ ...(parsed.expected_server_path === undefined
39
+ ? {}
40
+ : { server_path: parsed.expected_server_path }),
41
+ },
42
+ ...(client === undefined ? {} : { client }),
43
+ ...(server.server.getNegotiatedProtocolVersion() === undefined
44
+ ? {}
45
+ : {
46
+ protocolVersion: server.server.getNegotiatedProtocolVersion(),
47
+ }),
48
+ }),
49
+ },
50
+ }, contract);
51
+ });
52
+ };