rea-agents 2.5.0 → 3.0.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 (88) hide show
  1. package/README.md +14 -4
  2. package/bridge/ghidra/ReaGhidraBridge.java +477 -4
  3. package/bridge/hopper_bridge.py +54 -4
  4. package/dist/application/AnalysisContextQueries.js +130 -0
  5. package/dist/application/AnalysisSnapshotCache.js +4 -2
  6. package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
  7. package/dist/application/BinarySession.js +5 -4
  8. package/dist/application/BinarySessionComposition.js +21 -0
  9. package/dist/application/BinarySessionRecords.js +79 -9
  10. package/dist/application/CapabilityInventory.js +16 -2
  11. package/dist/application/ClientRegistrationStatus.js +4 -1
  12. package/dist/application/DirectAnalysis.js +6 -2
  13. package/dist/application/EnhancedTools.js +37 -7
  14. package/dist/application/EvidenceLedger.js +45 -16
  15. package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
  16. package/dist/application/LazyAnalysisProvider.js +77 -0
  17. package/dist/application/LoopbackReplay.js +1 -1
  18. package/dist/application/NativeApiInspection.js +80 -0
  19. package/dist/application/SessionProviderRouter.js +6 -6
  20. package/dist/application/SetupClientConfiguration.js +4 -2
  21. package/dist/application/runtime.js +26 -9
  22. package/dist/browser/PlaywrightBrowserScenarioProvider.js +8 -2
  23. package/dist/catalogIdentity.js +3 -3
  24. package/dist/cli/artifactCommands.js +5 -3
  25. package/dist/cli/coreAnalysisCommands.js +41 -0
  26. package/dist/cliCommandNames.js +1 -0
  27. package/dist/contracts/artifactToolContracts.js +8 -3
  28. package/dist/contracts/enhancedInputs.js +10 -0
  29. package/dist/contracts/functionWorkflowToolContracts.js +30 -0
  30. package/dist/contracts/promptContracts.js +14 -15
  31. package/dist/contracts/toolContractExamples.js +3 -0
  32. package/dist/contracts/toolContracts.js +21 -4
  33. package/dist/contracts/toolEffects.js +4 -0
  34. package/dist/contracts/toolOutputSchemaGroups.js +50 -10
  35. package/dist/domain/analysisErrorPresentation.js +8 -2
  36. package/dist/domain/analysisErrorProjection.js +1 -0
  37. package/dist/domain/bytecodeProvider.js +179 -0
  38. package/dist/domain/conformancePackage.js +169 -0
  39. package/dist/domain/conformanceReplay.js +104 -0
  40. package/dist/domain/conformanceTrustGate.js +193 -0
  41. package/dist/domain/customProtocolCapture.js +137 -0
  42. package/dist/domain/eventProcessTree.js +214 -0
  43. package/dist/domain/evidenceBundle.js +47 -0
  44. package/dist/domain/hopperStartupFailure.js +1 -1
  45. package/dist/domain/hopperValues.js +4 -1
  46. package/dist/domain/inputIssueProjection.js +7 -1
  47. package/dist/domain/javascriptSemanticAnalysis.js +8 -12
  48. package/dist/domain/javascriptSourceParser.js +15 -0
  49. package/dist/domain/javascriptStaticAnalysis.js +8 -12
  50. package/dist/domain/mobileApplicationInvestigation.js +159 -0
  51. package/dist/domain/nativeApiBoundary.js +130 -0
  52. package/dist/domain/objcSwiftMetadata.js +150 -0
  53. package/dist/domain/packageAnalysis.js +123 -0
  54. package/dist/domain/peInspection.js +196 -0
  55. package/dist/domain/protocolCapture.js +243 -0
  56. package/dist/dotnet/ManagedStaticProvider.js +34 -5
  57. package/dist/generatedMcpToolCatalog.js +37 -0
  58. package/dist/generatedPackageMetadata.js +4 -4
  59. package/dist/ghidra/GhidraDefaults.js +1 -1
  60. package/dist/ghidra/GhidraFunctionValues.js +27 -1
  61. package/dist/ghidra/GhidraProviderCapabilities.js +40 -38
  62. package/dist/hopper/HopperClient.js +26 -2
  63. package/dist/hopper/HopperProvider.js +42 -30
  64. package/dist/hopper/HopperRequestQueue.js +32 -4
  65. package/dist/hopper/HopperResponseStream.js +2 -2
  66. package/dist/hopper/protocol.js +63 -21
  67. package/dist/main/transport.js +2 -5
  68. package/dist/main.js +14 -12
  69. package/dist/mcpDoctor.js +2 -2
  70. package/dist/mcpStartupPolicy.js +14 -0
  71. package/dist/server/LazyToolCatalog.js +134 -0
  72. package/dist/server/createServer.js +64 -171
  73. package/dist/server/hydrateServerTools.js +191 -0
  74. package/dist/server/registerEnhancedTools.js +13 -3
  75. package/dist/server/registerEvidenceResources.js +50 -101
  76. package/dist/server/registerInvestigationTools.js +1 -2
  77. package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
  78. package/dist/server/registerReconstructionObligationLedgerResource.js +0 -7
  79. package/dist/server/registerReconstructionReadinessResource.js +0 -7
  80. package/dist/server/registerSessionRecordTools.js +82 -12
  81. package/dist/server/registerSessionStatusTool.js +1 -1
  82. package/dist/server/registerSessionTools.js +21 -1
  83. package/dist/server/toolRegistrationOptions.js +21 -6
  84. package/dist/server/toolResult.js +12 -0
  85. package/package.json +21 -10
  86. package/scripts/package-runner-bootstrap.mjs +72 -0
  87. package/scripts/rea.mjs +11 -1
  88. package/skills/reverse-engineer-anything/SKILL.md +10 -3
@@ -1,4 +1,3 @@
1
- import { TOOL_CONTRACTS } from "./toolContracts.js";
2
1
  const optional = (description, completion) => ({
3
2
  description,
4
3
  required: false,
@@ -81,8 +80,8 @@ export const PROMPT_CONTRACTS = [
81
80
  instruction: "Build function dossiers and corroborate references and call relationships. Keep indirect or truncated paths unknown.",
82
81
  },
83
82
  {
84
- tools: ["export_evidence_bundle", "record_unknown"],
85
- instruction: "Cite retained Evidence IDs. Record residual unknowns only after separate explicit approval for registry mutation.",
83
+ tools: ["snapshot_evidence_bundle", "record_unknown"],
84
+ instruction: "Snapshot the canonical bundle, copy its opaque URI unchanged, and read that resource before citing retained Evidence IDs. Record residual unknowns only after separate explicit approval for registry mutation.",
86
85
  },
87
86
  ],
88
87
  },
@@ -100,8 +99,8 @@ export const PROMPT_CONTRACTS = [
100
99
  },
101
100
  steps: [
102
101
  {
103
- tools: ["inventory_artifact"],
104
- instruction: "Inventory each target independently before extraction. Follow nodes, occurrences, and edges to completion and retain every Evidence page for each manifest.",
102
+ tools: ["open_binary", "inventory_artifact"],
103
+ instruction: "For each supplied target path, open that target before inventory because inventory_artifact uses the active target and accepts no path. Follow nodes, occurrences, and edges to completion and retain every Evidence page for each manifest.",
105
104
  },
106
105
  {
107
106
  tools: [
@@ -112,8 +111,8 @@ export const PROMPT_CONTRACTS = [
112
111
  instruction: "For JavaScript/Electron versions, reconstruct each approved artifact independently, optionally reconcile separately retained passive runtime Evidence, then compare authenticated graphs with unique-only identity tiers. Keep ambiguous and incomplete matches unknown.",
113
112
  },
114
113
  {
115
- tools: ["export_evidence_bundle", "compare_artifacts"],
116
- instruction: "Resolve any supplied manifest IDs to retained inventory Evidence, validate graph commitments, then compare the complete left and right Evidence page sets.",
114
+ tools: ["snapshot_evidence_bundle", "compare_artifacts"],
115
+ instruction: "Snapshot and read the canonical bundle through its exact resource URI, resolve any supplied manifest IDs to retained inventory Evidence, validate graph commitments, then compare the complete left and right Evidence page sets.",
117
116
  },
118
117
  {
119
118
  tools: ["open_binary", "analyze_function", "compare_functions"],
@@ -141,8 +140,8 @@ export const PROMPT_CONTRACTS = [
141
140
  },
142
141
  steps: [
143
142
  {
144
- tools: ["export_evidence_bundle"],
145
- instruction: "Load the current canonical bundle and verify that every selected comparison Evidence ID is present and compatible with the intended claim.",
143
+ tools: ["snapshot_evidence_bundle"],
144
+ instruction: "Snapshot the current canonical bundle, copy its opaque URI unchanged, read the resource, and verify that every selected comparison Evidence ID is present and compatible with the intended claim.",
146
145
  },
147
146
  {
148
147
  tools: [
@@ -194,11 +193,11 @@ export const PROMPT_CONTRACTS = [
194
193
  },
195
194
  {
196
195
  tools: [
197
- "export_evidence_bundle",
196
+ "snapshot_evidence_bundle",
198
197
  "capture_process_scenario",
199
198
  "correlate_static_and_runtime",
200
199
  ],
201
- instruction: "Reuse a retained capture when supplied. Otherwise capture only after operator policy and per-call approval, then correlate through explicit hypotheses rather than timing or name coincidence.",
200
+ instruction: "Snapshot and read retained Evidence through the exact resource URI. Reuse a retained capture when supplied. Otherwise capture only after operator policy and per-call approval, then correlate through explicit hypotheses rather than timing or name coincidence.",
202
201
  },
203
202
  {
204
203
  tools: ["record_unknown"],
@@ -222,8 +221,8 @@ export const PROMPT_CONTRACTS = [
222
221
  instruction: "List current heads first and select only active, session-owned unknown IDs. Preserve their exact revision and requirements.",
223
222
  },
224
223
  {
225
- tools: ["export_evidence_bundle", "verify_unknown_resolution"],
226
- instruction: "Inspect cited supporting, contradicting, and mutation Evidence, then validate any resolved head against bundle integrity and authority requirements.",
224
+ tools: ["snapshot_evidence_bundle", "verify_unknown_resolution"],
225
+ instruction: "Snapshot and read the canonical bundle through its exact resource URI, inspect cited supporting, contradicting, and mutation Evidence, then validate any resolved head against bundle integrity and authority requirements.",
227
226
  },
228
227
  {
229
228
  tools: ["update_unknown"],
@@ -252,8 +251,8 @@ export const PROMPT_CONTRACTS = [
252
251
  instruction: "Inspect capture capability, declared effects, and limits before proposing execution. An unavailable capability is not permission to widen policy.",
253
252
  },
254
253
  {
255
- tools: ["export_evidence_bundle"],
256
- instruction: "Review any prior capture for unanswered dimensions, truncation, scenario commitments, and descendant settlement before designing a repeat.",
254
+ tools: ["snapshot_evidence_bundle"],
255
+ instruction: "Snapshot and read the canonical bundle through its exact resource URI, then review any prior capture for unanswered dimensions, truncation, scenario commitments, and descendant settlement before designing a repeat.",
257
256
  },
258
257
  {
259
258
  tools: ["capture_process_scenario"],
@@ -31,10 +31,13 @@ export const TOOL_EXAMPLE_OVERRIDES = {
31
31
  get_call_graph: { address: "0x1000" },
32
32
  find_xrefs_to_name: { name: "malloc" },
33
33
  analyze_function: { procedure: "main" },
34
+ inspect_native_api: { procedure: "main" },
34
35
  trace_feature: { query: "license" },
35
36
  find_code_for_string: { query: "authorization failed" },
36
37
  trace_call_path: { start: "0x1000", goal: "0x1100" },
37
38
  open_binary: { path: "/tmp/fixture" },
39
+ export_evidence_bundle: { path: "/tmp/evidence.json" },
40
+ inspect_address_context: { address: "0x1000" },
38
41
  import_evidence_bundle: { path: "evidence.json" },
39
42
  capture_process_scenario: {
40
43
  approved: true,
@@ -28,6 +28,7 @@ import { binarySessionInputSchema } from "./sessionStatusContract.js";
28
28
  import { functionInstructionInputSchema } from "./functionInstructionContract.js";
29
29
  import { HOPPER_MEMORY_TOOL_DEFINITIONS } from "./hopperMemoryContracts.js";
30
30
  import { analysisSearchInput } from "./analysisSearchContract.js";
31
+ import { FUNCTION_WORKFLOW_TOOL_CONTRACTS } from "./functionWorkflowToolContracts.js";
31
32
  export { binarySessionInputSchema } from "./sessionStatusContract.js";
32
33
  const document = z.string().optional().describe("The document name");
33
34
  const address = z
@@ -139,16 +140,27 @@ export const ENHANCED_TOOL_CONTRACTS = [
139
140
  enhanced("analyze_swift_types", "Categorize exhaustively paged procedure names into Swift classes, structs, enums, protocols, extensions, and other symbols. Scans at most 5,000 names and returns at most 50 entries per category.", enhancedInputSchemas.analyze_swift_types),
140
141
  enhanced("find_xrefs_to_name", "Resolve an exact name through the bound provider's exhaustively paged name inventory and return a resolved or unresolved result. Unresolved names use the stable name_not_found reason; this compact xref workflow returns address-only projections.", enhancedInputSchemas.find_xrefs_to_name),
141
142
  enhanced("binary_overview", "Use immediately after opening a target to summarize document, exhaustive procedure/string counts, and a bounded segment sample. detail controls segment fields and limit controls only the returned segment sample.", enhancedInputSchemas.binary_overview),
142
- enhanced("analyze_function", "Preferred bounded analysis for one procedure symbol or address. Returns identity, provider-specific pseudocode, optional assembly, comments, calls, typed-or-explicitly-unavailable references, referenced strings/names, and local CFG blocks with exact truncation metadata.", enhancedInputSchemas.analyze_function),
143
+ ...FUNCTION_WORKFLOW_TOOL_CONTRACTS,
143
144
  enhanced("trace_feature", "Trace a bounded literal feature query through matching strings and procedures, xrefs, and truthful containing-procedure resolution. Returns the operation budget, truncation, and residual unknowns; unknown_registry_approved: true records them durably without inferring reference kinds.", enhancedInputSchemas.trace_feature),
144
145
  enhanced("find_code_for_string", "Resolve one literal string query to bounded analyzed string entries, xrefs, and truthful containing-procedure candidates. Returns an Evidence ID, exact operation budget, truncation, and residual unknowns; it never infers reference kinds or runtime reachability.", enhancedInputSchemas.find_code_for_string),
145
146
  enhanced("trace_call_path", "Trace a deterministic bounded caller or callee path from one exact procedure address, optionally stopping at a goal. Returns visited nodes, direct-call edges, one shortest traversal path, failures, frontier, consumed limits, an Evidence ID, and explicit residual unknowns without claiming unresolved indirect calls are absent.", enhancedInputSchemas.trace_call_path),
146
147
  ];
147
148
  /** Session-owned Evidence bundle export options. */
148
149
  export const exportEvidenceBundleInputSchema = z.strictObject({
149
- path: z.string().min(1).optional(),
150
+ path: z.string().min(1),
150
151
  overwrite: z.boolean().default(false),
151
152
  });
153
+ /** Retain the current canonical Evidence bundle as a session resource. */
154
+ export const snapshotEvidenceBundleInputSchema = z.strictObject({});
155
+ /** Optional document selection for volatile navigation context. */
156
+ export const navigationContextInputSchema = z.strictObject({
157
+ document: z.string().min(1).optional(),
158
+ });
159
+ /** Explicit reproducible address context query. */
160
+ export const addressContextInputSchema = z.strictObject({
161
+ address: z.string().min(1),
162
+ document: z.string().min(1).optional(),
163
+ });
152
164
  /** Session-owned Evidence bundle import options. */
153
165
  export const importEvidenceBundleInputSchema = z.strictObject({
154
166
  path: z.string().min(1),
@@ -171,6 +183,8 @@ export const listUnknownsInputSchema = z.strictObject({
171
183
  .optional(),
172
184
  severity: z.enum(["low", "medium", "high", "critical"]).optional(),
173
185
  domain: z.string().trim().min(1).max(100).optional(),
186
+ offset: z.number().int().min(0).default(0),
187
+ limit: z.number().int().min(1).max(500).default(100),
174
188
  });
175
189
  /** Exact residual-unknown identity to revalidate. */
176
190
  export const verifyUnknownResolutionInputSchema = z.strictObject({
@@ -181,7 +195,7 @@ export const SESSION_TOOL_CONTRACTS = [
181
195
  session("open_binary", "Open a local executable, application bundle, archive, JavaScript, source map, plist, or analysis database after validation. provider_id selects one deep provider or deterministic auto selection; the binding remains stable until close or an explicit switch, with no failure fallback. An optional snapshot v2 is imported atomically and must match the binary identity, concrete provider, and canonical analysis profile exactly.", openBinaryInputSchema),
182
196
  session("close_binary", "Optionally write a provider-neutral analysis snapshot atomically, then close the active target and every provider resource started for it. Snapshot files require an operator-approved root and explicit overwrite; a failed save leaves the session open so cached analysis is not lost.", closeBinaryInputSchema),
183
197
  session("binary_session", "Report compact target, provider, and alignment state without starting analysis. The default summary is the routing check agents should use; detail=capabilities returns one family-filtered availability page, while detail=full is reserved for complete provider diagnostics.", binarySessionInputSchema),
184
- session("export_evidence_bundle", "Return the session's deterministic Evidence v2 bundle, or atomically write it beneath an operator-approved root. Existing files require overwrite: true; records and manifests use canonical byte-stable ordering.", exportEvidenceBundleInputSchema),
198
+ session("export_evidence_bundle", "Atomically write the session's deterministic Evidence v2 bundle beneath an operator-approved root. Existing files require overwrite: true; records and manifests use canonical byte-stable ordering. For an in-session read, use snapshot_evidence_bundle and read its exact resource URI instead.", exportEvidenceBundleInputSchema),
185
199
  session("import_evidence_bundle", "Read a bounded local JSON bundle beneath an operator-approved root, validate every Evidence v2 ID and canonical manifest, then atomically merge it. Imported content is data only and is never executed.", importEvidenceBundleInputSchema),
186
200
  session("capture_process_scenario", "Run one bounded process under a PTY using operator-approved executable and working roots. Produces Process Capture v4; legacy v3 captures cannot be upgraded and must be recaptured with this tool. Requires approved: true; unknown_registry_approved: true separately records capture residuals. Captures raw and xterm-rendered terminal frames, scripted interactions, lifecycle filesystem checkpoints, process ownership, declarative command shims, and loopback replay. Disabled unless operator policy enables it; not a security sandbox.", processScenarioSchema),
187
201
  session("compare_process_captures", "Compare two compatible Process Capture v4 observations across terminal, interaction, lifecycle, process, filesystem, command-shim, HTTP, and WebSocket evidence. Optional trace_spec validates exact events against an explicit partial order or finite trace language; concurrency is never inferred from timestamps or broad sorting. Missing, journal-free, or truncated observations are never treated as equivalent.", processComparisonInputSchema),
@@ -192,11 +206,14 @@ export const SESSION_TOOL_CONTRACTS = [
192
206
  session("build_call_path", "Build bounded shortest-first direct-callee paths from explicit analyze_function Evidence groups using exact canonical addresses. Missing dossiers, incomplete callee pages, provider mixing, and depth frontiers remain unknown; every node and edge cites source Evidence.", callPathInputSchema),
193
207
  session("correlate_static_and_runtime", "Evaluate explicit caller-declared hypotheses between exact static comparison findings and runtime comparison dimensions. Similar names or paths are never auto-matched, consistent cochange never proves causality, and unknown or truncated inputs remain unresolved.", staticRuntimeCorrelationInputSchema),
194
208
  session("verify_reconstruction", "Verify a finite typed behavioral and structural specification against a canonical Evidence bundle. Pass means every declared claim has complete comparable authority—not global source equivalence; changed claims fail and missing, limited, or unresolved evidence stays unknown.", reconstructionVerificationInputSchema),
195
- session("list_unknowns", "List current residual-unknown heads in deterministic ID order, with optional exact status, severity, and domain filters. This is read-only; unresolved, contradicted, and non-truth dispositions remain distinct.", listUnknownsInputSchema),
209
+ session("list_unknowns", "List current residual-unknown heads in deterministic ID order, with optional exact status, severity, and domain filters. Pages default to 100 items; while has_more is true, pass next_offset as offset and continue until false. This is read-only; unresolved, contradicted, and non-truth dispositions remain distinct.", listUnknownsInputSchema),
196
210
  session("record_unknown", "Create one deterministic residual unknown and immutable mutation evidence. Requires approved: true, validates all evidence and relationship references, and rejects duplicate stable identity.", recordUnknownInputSchema),
197
211
  session("update_unknown", "Append one immutable full-state revision and mutation evidence. Requires approved: true and exact expected_revision; stale concurrent writers fail instead of overwriting newer analysis.", updateUnknownInputSchema),
198
212
  session("verify_unknown_resolution", "Revalidate the current residual-unknown head against live bundled evidence, exact authority/confidence/environment requirements, and revision integrity. Withdrawn and out-of-scope dispositions are not truth claims.", verifyUnknownResolutionInputSchema),
199
213
  session("run_replay_machine", "Evaluate ordered HTTP and WebSocket events directly against one validated finite replay machine without opening sockets or launching a target. Returns every decision, a capture-value-free transition journal, one redacted action table entry per used transition, captured aliases, final state, and exact configured and consumed limits.", replayMachineRunInputSchema),
214
+ session("snapshot_evidence_bundle", "Retain the current canonical Evidence v2 bundle as an immutable session resource. Returns a compact digest summary and exact opaque URI; copy that URI unchanged and call MCP resources/read (Codex: read_mcp_resource) for the full bundle. Repeating an unchanged snapshot is idempotent.", snapshotEvidenceBundleInputSchema),
215
+ session("get_navigation_context", "Return the selected document, current address, and containing/current procedure in one coherent provider-neutral request. A cursor outside any procedure returns procedure: null. Scalar navigation getters remain available for evaluation and compatibility.", navigationContextInputSchema),
216
+ session("inspect_address_context", "Inspect one explicit reproducible address for its analyzed name, containing procedure, regular and inline comments, and matching bookmarks. Each unsupported facet returns a typed unavailable outcome; xrefs, assembly, and pseudocode remain separate bounded follow-ups.", addressContextInputSchema),
200
217
  ];
201
218
  /** Complete ordered public inventory used by registration and verification. */
202
219
  export const TOOL_CONTRACTS = [
@@ -67,6 +67,7 @@ export const TOOL_EFFECTS = {
67
67
  find_xrefs_to_name: evidence,
68
68
  binary_overview: evidence,
69
69
  analyze_function: evidence,
70
+ inspect_native_api: effects({ mutatesSession: true, idempotent: false }),
70
71
  trace_feature: effects({ mutatesSession: true, idempotent: false }),
71
72
  find_code_for_string: effects({ mutatesSession: true, idempotent: false }),
72
73
  trace_call_path: effects({ mutatesSession: true, idempotent: false }),
@@ -149,6 +150,9 @@ export const TOOL_EFFECTS = {
149
150
  writesFilesystem: true,
150
151
  mayDiscardData: true,
151
152
  }),
153
+ snapshot_evidence_bundle: effects({ mutatesSession: true }),
154
+ get_navigation_context: effects({ mutatesSession: true }),
155
+ inspect_address_context: effects({ mutatesSession: true }),
152
156
  import_evidence_bundle: effects({ mutatesSession: true }),
153
157
  capture_process_scenario: effects({
154
158
  mutatesSession: true,
@@ -1,8 +1,9 @@
1
1
  import { z } from "zod";
2
+ import { jsonValueSchema } from "../domain/jsonValue.js";
2
3
  import { processCaptureComparisonSchema, processCaptureSchema, } from "../domain/processCapture.js";
3
- import { evidenceBundleSchema } from "../domain/evidenceBundle.js";
4
4
  import { residualUnknownSchema } from "../domain/residualUnknown.js";
5
5
  import { functionInstructionWindowSchema, referenceKindSchema, } from "../domain/hopperValues.js";
6
+ import { nativeApiInspectionResultSchema } from "../domain/nativeApiBoundary.js";
6
7
  import { demangleSwiftSchema, inspectMachoSchema, inspectPlistSchema, inspectSignatureSchema, listArchitecturesSchema, } from "../domain/nativeInspection.js";
7
8
  import { artifactExtractionResultSchema, artifactInventoryResultSchema, } from "../domain/artifactGraph.js";
8
9
  import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
@@ -22,6 +23,14 @@ import { reconstructionVerificationResultSchema } from "../domain/reconstruction
22
23
  import { replayMachineRunOutputSchema } from "../domain/replayMachineRun.js";
23
24
  import { analysisErrorProjectionSchema } from "./errorSchemas.js";
24
25
  import { addressList, analysisActivity, addressedEntry, bounded, containingProcedureResolution, clientFeatureAvailabilitySchema, functionDossierOutput, graphNode, lifecycleResultOf, nullableText, pageOutput, procedureIdentity, procedureInfoOutput, evidenceResultOf as resultOf, searchPageOutput, segmentOutput, sessionProvider, providerIdentity, toolAvailability, symbolDiscoveryOutput, targetFormatSchema, targetKindSchema, } from "./toolOutputSchemaPrimitives.js";
26
+ const contextFacetSchema = z.discriminatedUnion("state", [
27
+ z.object({ state: z.literal("available"), value: jsonValueSchema }),
28
+ z.object({
29
+ state: z.literal("unavailable"),
30
+ reason: z.string(),
31
+ remediation: z.string(),
32
+ }),
33
+ ]);
25
34
  /** Exact structured-content schemas shared by direct analysis providers. */
26
35
  export const officialOutputSchemas = {
27
36
  address_name: resultOf(nullableText),
@@ -188,6 +197,7 @@ export const enhancedOutputSchemas = {
188
197
  string_count: z.number().int().min(0),
189
198
  })),
190
199
  analyze_function: functionDossierOutput,
200
+ inspect_native_api: resultOf(nativeApiInspectionResultSchema),
191
201
  trace_feature: literalTraceOutput,
192
202
  find_code_for_string: literalTraceOutput,
193
203
  trace_call_path: callPathTraceOutput,
@@ -293,14 +303,34 @@ export const sessionOutputSchemas = {
293
303
  }),
294
304
  ]),
295
305
  ])),
296
- export_evidence_bundle: lifecycleResultOf(z.union([
297
- evidenceBundleSchema,
298
- z.object({
299
- path: z.string(),
300
- bytes: z.number().int().min(0),
301
- records: z.number().int().min(0),
302
- }),
303
- ])),
306
+ export_evidence_bundle: lifecycleResultOf(z.object({
307
+ path: z.string(),
308
+ bytes: z.number().int().min(0),
309
+ records: z.number().int().min(0),
310
+ unknowns: z.number().int().min(0),
311
+ })),
312
+ snapshot_evidence_bundle: lifecycleResultOf(z.object({
313
+ bundle_digest: z.string().regex(/^[a-f0-9]{64}$/u),
314
+ bundle_version: z.literal(2),
315
+ bytes: z.number().int().min(0),
316
+ records: z.number().int().min(0),
317
+ unknowns: z.number().int().min(0),
318
+ bundle_uri: z.string().regex(/^rea:\/\/evidence-bundle\/[a-f0-9]{64}$/u),
319
+ })),
320
+ get_navigation_context: lifecycleResultOf(z.object({
321
+ document: z.string(),
322
+ address: z.string(),
323
+ procedure: z.union([z.string(), z.null()]),
324
+ })),
325
+ inspect_address_context: lifecycleResultOf(z.object({
326
+ address: z.string(),
327
+ document: z.string().nullable(),
328
+ name: contextFacetSchema,
329
+ procedure: contextFacetSchema,
330
+ comment: contextFacetSchema,
331
+ inline_comment: contextFacetSchema,
332
+ bookmarks: contextFacetSchema,
333
+ })),
304
334
  import_evidence_bundle: lifecycleResultOf(z.object({
305
335
  imported: z.number().int().min(0),
306
336
  total: z.number().int().min(0),
@@ -314,7 +344,17 @@ export const sessionOutputSchemas = {
314
344
  build_call_path: resultOf(callPathResultSchema),
315
345
  correlate_static_and_runtime: resultOf(staticRuntimeCorrelationResultSchema),
316
346
  verify_reconstruction: resultOf(reconstructionVerificationResultSchema),
317
- list_unknowns: lifecycleResultOf(z.array(residualUnknownSchema)),
347
+ list_unknowns: lifecycleResultOf(z.object({
348
+ items: z.array(z.object({
349
+ unknown: residualUnknownSchema,
350
+ uri: z.string().regex(/^rea:\/\/unknown\/unk_[a-f0-9]{64}$/u),
351
+ })),
352
+ offset: z.number().int().min(0),
353
+ limit: z.number().int().min(1).max(500),
354
+ total: z.number().int().min(0),
355
+ next_offset: z.number().int().min(0).nullable(),
356
+ has_more: z.boolean(),
357
+ })),
318
358
  record_unknown: lifecycleResultOf(residualUnknownSchema),
319
359
  update_unknown: lifecycleResultOf(residualUnknownSchema),
320
360
  verify_unknown_resolution: lifecycleResultOf(z.object({
@@ -2,6 +2,8 @@ import { AnalysisInputError, ArtifactOperationError, BinaryTargetError, BrowserO
2
2
  export const analysisErrorRemediationAction = (error) => {
3
3
  if (error instanceof AnalysisInputError)
4
4
  return "Correct the listed arguments and retry.";
5
+ if (error instanceof UnknownRegistryError && error.reason === "not-found")
6
+ return "Check that the unknown_id belongs to this session, then retry.";
5
7
  if (error instanceof ReplayPlanStaleError)
6
8
  return "Review the rebuilt replay plan and explicitly approve its new digest.";
7
9
  if (error instanceof PermissionRequiredError)
@@ -64,7 +66,7 @@ const artifactErrorCategory = (reason) => {
64
66
  return "truncated";
65
67
  if (reason === "cancelled")
66
68
  return "cancelled";
67
- if (reason === "unavailable")
69
+ if (reason === "policy" || reason === "unavailable")
68
70
  return "unavailable";
69
71
  return "execution_failure";
70
72
  };
@@ -103,6 +105,8 @@ export const analysisErrorUserMessage = (error) => {
103
105
  return evidenceFileMessage(error.reason);
104
106
  if (error instanceof InvestigationWorkspaceError)
105
107
  return investigationWorkspaceMessage(error.reason);
108
+ if (error instanceof UnknownRegistryError && error.reason === "not-found")
109
+ return "The requested residual unknown does not exist in this session. Check the unknown_id and try again.";
106
110
  if (error instanceof UnknownRegistryError)
107
111
  return "Evidence state changed before the update completed. Refresh the current state and try again.";
108
112
  if (error instanceof ConfigurationError)
@@ -163,8 +167,10 @@ const artifactMessage = (reason) => {
163
167
  return "Artifact is too large to process safely. Narrow the requested path or use a smaller artifact.";
164
168
  if (reason === "path")
165
169
  return "Artifact path is not allowed. Choose a path inside the artifact and try again.";
170
+ if (reason === "policy")
171
+ return "Artifact integrity continuation is disabled by policy. Configure REA_ARTIFACT_INTEGRITY_CONTINUE_ENABLED=true and retry only if continuing after mismatches is approved.";
166
172
  if (reason === "unavailable")
167
- return "Artifact format is not available on this system. Choose another artifact or supported environment.";
173
+ return "Artifact processing is unavailable for the current target or policy. Check artifact support and required approvals.";
168
174
  if (reason === "format" || reason === "integrity")
169
175
  return "Artifact is invalid or has changed. Get a fresh copy and try again.";
170
176
  return "Artifact could not be read or written. Check file access and try again.";
@@ -152,6 +152,7 @@ const requestErrorDetails = (error) => {
152
152
  issues: error.issues.map((issue) => ({
153
153
  path: [...issue.path],
154
154
  reason: issue.reason,
155
+ ...(issue.message === undefined ? {} : { message: issue.message }),
155
156
  ...(issue.expected === undefined ? {} : { expected: issue.expected }),
156
157
  ...(issue.minimum === undefined ? {} : { minimum: issue.minimum }),
157
158
  ...(issue.maximum === undefined ? {} : { maximum: issue.maximum }),
@@ -0,0 +1,179 @@
1
+ import { z } from "zod";
2
+ /** Supported managed-runtime bytecode families. */
3
+ export const bytecodeFamilySchema = z.enum(["jvm", "python"]);
4
+ /** Classification of bytecode provenance. */
5
+ export const bytecodeProvenanceSchema = z.enum([
6
+ "application",
7
+ "generated",
8
+ "vendored",
9
+ "bundled",
10
+ "standard_library",
11
+ ]);
12
+ /** A bytecode symbol discovered through static analysis. */
13
+ export const bytecodeSymbolSchema = z.strictObject({
14
+ /** Fully qualified name. */
15
+ name: z.string().min(1),
16
+ /** Raw bytecode location or offset. */
17
+ raw_location: z.string().min(1),
18
+ /** Normalized symbol type. */
19
+ kind: z.enum([
20
+ "class",
21
+ "interface",
22
+ "method",
23
+ "field",
24
+ "function",
25
+ "module",
26
+ "variable",
27
+ ]),
28
+ /** Bytecode version if known. */
29
+ bytecode_version: z.number().int().nullable(),
30
+ /** Provenance classification. */
31
+ provenance: bytecodeProvenanceSchema,
32
+ /** Whether the symbol is accessible (public). */
33
+ is_public: z.boolean().default(false),
34
+ });
35
+ /** A discovered bytecode artifact (class file, wheel, etc). */
36
+ export const bytecodeArtifactSchema = z.strictObject({
37
+ /** Artifact path relative to the analysis root. */
38
+ path: z.string().min(1),
39
+ /** Bytecode family. */
40
+ family: bytecodeFamilySchema,
41
+ /** Artifact format. */
42
+ format: z.enum(["class", "jar", "war", "ear", "pyc", "pyo", "whl", "zipapp"]),
43
+ /** Size in bytes. */
44
+ size: z.number().int().nonnegative(),
45
+ /** SHA-256 digest. */
46
+ digest: z.string().regex(/^[a-f0-9]{64}$/u),
47
+ /** Discovered symbols. */
48
+ symbols: z.array(bytecodeSymbolSchema).default([]),
49
+ /** Whether this is a standard library artifact. */
50
+ is_standard_library: z.boolean().default(false),
51
+ /** Whether this is a generated artifact. */
52
+ is_generated: z.boolean().default(false),
53
+ /** Whether this is a vendored artifact. */
54
+ is_vendored: z.boolean().default(false),
55
+ });
56
+ /** Static bytecode analysis result. */
57
+ export const bytecodeAnalysisSchema = z.strictObject({
58
+ /** Bytecode family. */
59
+ family: bytecodeFamilySchema,
60
+ /** All discovered artifacts. */
61
+ artifacts: z.array(bytecodeArtifactSchema).min(0).max(10_000),
62
+ /** Total symbols across all artifacts. */
63
+ total_symbols: z.number().int().nonnegative(),
64
+ /** Symbols classified as application code. */
65
+ application_symbols: z.number().int().nonnegative(),
66
+ /** Symbols classified as generated code. */
67
+ generated_symbols: z.number().int().nonnegative(),
68
+ /** Symbols classified as vendored code. */
69
+ vendored_symbols: z.number().int().nonnegative(),
70
+ /** Symbols classified as standard library. */
71
+ standard_library_symbols: z.number().int().nonnegative(),
72
+ });
73
+ /** Detect bytecode family from file extension. */
74
+ export function detectBytecodeFamily(path) {
75
+ const lower = path.toLowerCase();
76
+ if (lower.endsWith(".class") ||
77
+ lower.endsWith(".jar") ||
78
+ lower.endsWith(".war") ||
79
+ lower.endsWith(".ear")) {
80
+ return "jvm";
81
+ }
82
+ if (lower.endsWith(".pyc") ||
83
+ lower.endsWith(".pyo") ||
84
+ lower.endsWith(".whl") ||
85
+ lower.endsWith(".pyz") ||
86
+ lower.endsWith(".zipapp")) {
87
+ return "python";
88
+ }
89
+ return null;
90
+ }
91
+ /** Detect artifact format from file path. */
92
+ export function detectArtifactFormat(path) {
93
+ const lower = path.toLowerCase();
94
+ if (lower.endsWith(".class"))
95
+ return "class";
96
+ if (lower.endsWith(".jar"))
97
+ return "jar";
98
+ if (lower.endsWith(".war"))
99
+ return "war";
100
+ if (lower.endsWith(".ear"))
101
+ return "ear";
102
+ if (lower.endsWith(".pyc"))
103
+ return "pyc";
104
+ if (lower.endsWith(".pyo"))
105
+ return "pyo";
106
+ if (lower.endsWith(".whl"))
107
+ return "whl";
108
+ if (lower.endsWith(".pyz") || lower.endsWith(".zipapp"))
109
+ return "zipapp";
110
+ return null;
111
+ }
112
+ /** Classify bytecode symbol provenance. */
113
+ export function classifyProvenance(path, symbolName) {
114
+ const lower = path.toLowerCase();
115
+ // Standard library paths
116
+ if (lower.includes("/stdlib/") ||
117
+ lower.includes("/site-packages/") ||
118
+ lower.includes("java/") ||
119
+ lower.includes("javax/") ||
120
+ lower.includes("sun/") ||
121
+ symbolName.startsWith("java.") ||
122
+ symbolName.startsWith("javax.") ||
123
+ symbolName.startsWith("sun.") ||
124
+ symbolName.startsWith("__")) {
125
+ return "standard_library";
126
+ }
127
+ // Generated code
128
+ if (lower.includes("/generated/") ||
129
+ lower.includes("_generated") ||
130
+ lower.includes("/build/") ||
131
+ lower.includes("/target/") ||
132
+ lower.includes("/dist-packages/") ||
133
+ lower.includes("/__pycache__/") ||
134
+ symbolName.includes("Generated") ||
135
+ symbolName.includes("_generated")) {
136
+ return "generated";
137
+ }
138
+ // Vendored code
139
+ if (lower.includes("/vendor/") ||
140
+ lower.includes("/vendored/") ||
141
+ lower.includes("/third_party/") ||
142
+ lower.includes("/3rdparty/") ||
143
+ lower.includes("/node_modules/") ||
144
+ lower.includes("/vendor/")) {
145
+ return "vendored";
146
+ }
147
+ // Bundled code
148
+ if (lower.includes("/bundle/") ||
149
+ lower.includes("/bundled/") ||
150
+ lower.includes(".jar!") ||
151
+ lower.includes(".war!")) {
152
+ return "bundled";
153
+ }
154
+ return "application";
155
+ }
156
+ /** Analyze a set of bytecode artifacts and produce a summary. */
157
+ export function analyzeBytecodeArtifacts(artifacts, family) {
158
+ const applicationSymbols = artifacts
159
+ .flatMap((a) => a.symbols)
160
+ .filter((s) => s.provenance === "application").length;
161
+ const generatedSymbols = artifacts
162
+ .flatMap((a) => a.symbols)
163
+ .filter((s) => s.provenance === "generated").length;
164
+ const vendoredSymbols = artifacts
165
+ .flatMap((a) => a.symbols)
166
+ .filter((s) => s.provenance === "vendored").length;
167
+ const standardLibrarySymbols = artifacts
168
+ .flatMap((a) => a.symbols)
169
+ .filter((s) => s.provenance === "standard_library").length;
170
+ return {
171
+ family,
172
+ artifacts: artifacts.slice(0, 10_000),
173
+ total_symbols: artifacts.reduce((sum, a) => sum + a.symbols.length, 0),
174
+ application_symbols: applicationSymbols,
175
+ generated_symbols: generatedSymbols,
176
+ vendored_symbols: vendoredSymbols,
177
+ standard_library_symbols: standardLibrarySymbols,
178
+ };
179
+ }