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.
- package/README.md +95 -27
- package/bridge/hopper_bridge.py +172 -14
- package/dist/application/AnalysisSnapshotCache.js +131 -0
- package/dist/application/AnalysisSnapshotFiles.js +31 -0
- package/dist/application/ArtifactGraphConstruction.js +1 -1
- package/dist/application/ArtifactInventory.js +100 -24
- package/dist/application/AuthorizedArtifactInventory.js +16 -0
- package/dist/application/BinarySession.js +149 -36
- package/dist/application/BinarySessionPort.js +1 -0
- package/dist/application/BoundedJsonFiles.js +109 -0
- package/dist/application/CapabilityInventory.js +127 -0
- package/dist/application/ClientRegistrationStatus.js +69 -0
- package/dist/application/CrossVersionInventory.js +109 -0
- package/dist/application/CrossVersionInvestigation.js +358 -0
- package/dist/application/DirectAnalysis.js +205 -24
- package/dist/application/Doctor.js +104 -3
- package/dist/application/EvidenceBundleFiles.js +15 -108
- package/dist/application/EvidenceLedger.js +3 -2
- package/dist/application/InvestigationProviders.js +48 -0
- package/dist/application/InvestigationWorkspaceStore.js +214 -0
- package/dist/application/LinuxHopper.js +72 -48
- package/dist/application/PermissionAuthority.js +183 -0
- package/dist/application/PermissionConfiguration.js +26 -0
- package/dist/application/ProcessCaptureAuthority.js +2 -2
- package/dist/application/ProcessCaptureError.js +25 -0
- package/dist/application/ProcessCaptureLifecycle.js +22 -2
- package/dist/application/ProcessCli.js +111 -31
- package/dist/application/ProcessHarness.js +6 -3
- package/dist/application/ProgressReporter.js +30 -0
- package/dist/application/ProjectPermissionStore.js +110 -0
- package/dist/application/ReferenceSourceImportEntries.js +18 -3
- package/dist/application/ReferenceSourceImportTypes.js +32 -0
- package/dist/application/Setup.js +35 -101
- package/dist/application/SetupClients.js +19 -0
- package/dist/application/SetupInstallFailure.js +53 -0
- package/dist/application/SetupPlan.js +31 -0
- package/dist/application/SupportedClients.js +41 -0
- package/dist/application/Uninstall.js +34 -24
- package/dist/application/UnknownEvidence.js +33 -0
- package/dist/application/Upgrade.js +3 -1
- package/dist/application/runtime.js +2 -2
- package/dist/artifacts/ArtifactProvider.js +15 -8
- package/dist/catalogIdentity.js +121 -0
- package/dist/cli.js +76 -13
- package/dist/cliEvidenceCommands.js +52 -12
- package/dist/cliInvestigationCommands.js +137 -0
- package/dist/cliLogging.js +20 -3
- package/dist/cliOutput.js +41 -0
- package/dist/cliPolicyCommands.js +115 -0
- package/dist/config.js +102 -27
- package/dist/contracts/artifactToolContracts.js +14 -1
- package/dist/contracts/errorSchemas.js +98 -0
- package/dist/contracts/promptContracts.js +256 -0
- package/dist/contracts/sessionLifecycleInputs.js +11 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +20 -7
- package/dist/contracts/toolOutputSchemas.js +70 -9
- package/dist/domain/analysisSnapshot.js +149 -0
- package/dist/domain/artifactComparison.js +38 -11
- package/dist/domain/artifactGraph.js +25 -1
- package/dist/domain/artifactInventoryEvidence.js +18 -0
- package/dist/domain/changedBehavior.js +35 -10
- package/dist/domain/errors.js +384 -32
- package/dist/domain/evidence.js +4 -2
- package/dist/domain/evidenceBundle.js +28 -0
- package/dist/domain/hopperStartupFailure.js +55 -0
- package/dist/domain/investigationWorkspace.js +341 -0
- package/dist/domain/jsonValue.js +2 -0
- package/dist/domain/nativeInspection.js +3 -2
- package/dist/domain/permissionPolicy.js +157 -0
- package/dist/domain/processCapture.js +5 -4
- package/dist/domain/processComparison.js +6 -4
- package/dist/domain/reconstructionVerification.js +1 -1
- package/dist/domain/reconstructionVerificationSchemas.js +1 -0
- package/dist/domain/staticRuntimeCorrelation.js +5 -1
- package/dist/generatedPackageMetadata.js +9 -0
- package/dist/hopper/BridgeLauncher.js +51 -20
- package/dist/hopper/HopperClient.js +45 -3
- package/dist/hopper/HopperProvider.js +1 -0
- package/dist/identity.js +10 -3
- package/dist/logger.js +2 -2
- package/dist/main.js +139 -28
- package/dist/server/createServer.js +54 -5
- package/dist/server/mcpProgress.js +23 -0
- package/dist/server/promptCompletion.js +144 -0
- package/dist/server/registerArtifactComparisonTool.js +6 -2
- package/dist/server/registerBundleComparisonTool.js +6 -2
- package/dist/server/registerEnhancedTools.js +20 -7
- package/dist/server/registerEvidenceResources.js +302 -0
- package/dist/server/registerEvidenceTools.js +59 -8
- package/dist/server/registerFunctionComparisonTool.js +7 -3
- package/dist/server/registerInvestigationTools.js +111 -18
- package/dist/server/registerOfficialTools.js +25 -14
- package/dist/server/registerProcessComparisonTool.js +8 -10
- package/dist/server/registerPrompts.js +67 -0
- package/dist/server/registerSessionStatusTool.js +52 -0
- package/dist/server/registerSessionTools.js +178 -22
- package/dist/server/runDerivedOperation.js +35 -0
- package/dist/server/sessionEvidence.js +2 -8
- package/dist/server/sessionToolPolicies.js +1 -42
- package/dist/server/toolResult.js +34 -6
- package/dist/serverIdentity.js +70 -0
- package/install.sh +11 -11
- package/package.json +10 -6
- package/scripts/hopper-demo-x11.py +349 -0
- package/scripts/rea.mjs +6 -3
- package/skills/rea-analysis/SKILL.md +9 -2
package/dist/domain/errors.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { hopperStartupFailure, } from "./hopperStartupFailure.js";
|
|
2
|
+
export { hopperStartupFailure };
|
|
1
3
|
/** Stable tags exposed by safe analysis-error projections. */
|
|
2
|
-
|
|
4
|
+
const ANALYSIS_ERROR_TAGS = [
|
|
3
5
|
"AnalysisProtocolError",
|
|
4
6
|
"AnalysisInputError",
|
|
5
7
|
"AnalysisOutputError",
|
|
@@ -13,6 +15,7 @@ export const ANALYSIS_ERROR_TAGS = [
|
|
|
13
15
|
"EvidenceIntegrityError",
|
|
14
16
|
"EvidenceLimitError",
|
|
15
17
|
"EvidenceFileError",
|
|
18
|
+
"InvestigationWorkspaceError",
|
|
16
19
|
"UnknownRegistryError",
|
|
17
20
|
"HopperTimeoutError",
|
|
18
21
|
"HopperCancelledError",
|
|
@@ -23,9 +26,14 @@ export const ANALYSIS_ERROR_TAGS = [
|
|
|
23
26
|
"ConfigurationError",
|
|
24
27
|
"NoBinaryOpenError",
|
|
25
28
|
"BinaryTargetError",
|
|
29
|
+
"PermissionRequiredError",
|
|
26
30
|
];
|
|
27
31
|
/** Base class for expected analysis, provider, and session failures. */
|
|
28
32
|
export class AnalysisError extends Error {
|
|
33
|
+
userMessage = undefined;
|
|
34
|
+
userCategory = undefined;
|
|
35
|
+
cleanupIncomplete = false;
|
|
36
|
+
cleanupResources = [];
|
|
29
37
|
}
|
|
30
38
|
/** Base class for failures produced specifically by the Hopper provider. */
|
|
31
39
|
export class HopperError extends AnalysisError {
|
|
@@ -148,6 +156,17 @@ export class EvidenceFileError extends AnalysisError {
|
|
|
148
156
|
this.reason = reason;
|
|
149
157
|
}
|
|
150
158
|
}
|
|
159
|
+
/** Persistent investigation workspace access or CAS validation failed. */
|
|
160
|
+
export class InvestigationWorkspaceError extends AnalysisError {
|
|
161
|
+
operation;
|
|
162
|
+
reason;
|
|
163
|
+
_tag = "InvestigationWorkspaceError";
|
|
164
|
+
constructor(operation, reason, options) {
|
|
165
|
+
super(`Investigation workspace ${operation} failed: ${reason}`, options);
|
|
166
|
+
this.operation = operation;
|
|
167
|
+
this.reason = reason;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
151
170
|
/** Residual-unknown mutation failed a lifecycle, reference, or CAS invariant. */
|
|
152
171
|
export class UnknownRegistryError extends AnalysisError {
|
|
153
172
|
reason;
|
|
@@ -194,9 +213,14 @@ export class HopperRemoteError extends HopperError {
|
|
|
194
213
|
export class HopperProcessError extends HopperError {
|
|
195
214
|
exitCode;
|
|
196
215
|
_tag = "HopperProcessError";
|
|
216
|
+
failureCode;
|
|
217
|
+
userMessage;
|
|
197
218
|
constructor(exitCode) {
|
|
198
219
|
super(`Hopper bridge stopped unexpectedly with code ${String(exitCode)}`);
|
|
199
220
|
this.exitCode = exitCode;
|
|
221
|
+
const failure = hopperStartupFailure(exitCode);
|
|
222
|
+
this.failureCode = failure?.code;
|
|
223
|
+
this.userMessage = failure?.message;
|
|
200
224
|
}
|
|
201
225
|
}
|
|
202
226
|
/** Hopper or the repository bridge could not be started. */
|
|
@@ -226,57 +250,383 @@ export class BinaryTargetError extends AnalysisError {
|
|
|
226
250
|
this.path = path;
|
|
227
251
|
}
|
|
228
252
|
}
|
|
253
|
+
/** Exact denied authority used by both CLI and MCP remediation. */
|
|
254
|
+
export class PermissionRequiredError extends AnalysisError {
|
|
255
|
+
requested;
|
|
256
|
+
missing;
|
|
257
|
+
ceiling;
|
|
258
|
+
elicitationSupported;
|
|
259
|
+
restartRequired;
|
|
260
|
+
_tag = "PermissionRequiredError";
|
|
261
|
+
constructor(requested, missing, ceiling, elicitationSupported, restartRequired) {
|
|
262
|
+
super(`Permission required for ${requested.capability}`);
|
|
263
|
+
this.requested = requested;
|
|
264
|
+
this.missing = missing;
|
|
265
|
+
this.ceiling = ceiling;
|
|
266
|
+
this.elicitationSupported = elicitationSupported;
|
|
267
|
+
this.restartRequired = restartRequired;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
229
270
|
/** Project expected failures into exhaustive, secret-safe caller fields. */
|
|
230
271
|
export const projectAnalysisError = (error) => {
|
|
231
272
|
assertKnownTag(error._tag);
|
|
273
|
+
const code = errorCode(error);
|
|
274
|
+
const details = errorDetails(error);
|
|
232
275
|
return {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
276
|
+
code,
|
|
277
|
+
category: errorCategory(error),
|
|
278
|
+
message: userMessage(error),
|
|
279
|
+
retryable: RETRYABLE_CODES.has(code),
|
|
280
|
+
remediation: {
|
|
281
|
+
action: remediationAction(error),
|
|
282
|
+
restart_required: error instanceof PermissionRequiredError && error.restartRequired,
|
|
283
|
+
...(error instanceof PermissionRequiredError
|
|
284
|
+
? { elicitation_supported: error.elicitationSupported }
|
|
285
|
+
: {}),
|
|
286
|
+
},
|
|
287
|
+
...(details === undefined ? {} : { details }),
|
|
236
288
|
};
|
|
237
289
|
};
|
|
238
|
-
const
|
|
239
|
-
if (error instanceof
|
|
240
|
-
return
|
|
241
|
-
if (error instanceof
|
|
242
|
-
return
|
|
243
|
-
|
|
290
|
+
const errorCode = (error) => {
|
|
291
|
+
if (error instanceof PermissionRequiredError)
|
|
292
|
+
return "permission_required";
|
|
293
|
+
if (error instanceof ArtifactOperationError)
|
|
294
|
+
return error.reason === "integrity" && error.artifactDetails !== undefined
|
|
295
|
+
? "artifact_integrity_mismatch"
|
|
296
|
+
: error.reason === "limit"
|
|
297
|
+
? "truncated"
|
|
298
|
+
: error.reason === "cancelled"
|
|
299
|
+
? "cancelled"
|
|
300
|
+
: "artifact_operation_failed";
|
|
301
|
+
if (error instanceof EvidenceFileError)
|
|
302
|
+
return error.reason === "outside-root"
|
|
303
|
+
? "outside_approved_root"
|
|
304
|
+
: error.reason === "too-large"
|
|
305
|
+
? "truncated"
|
|
306
|
+
: error.reason === "disabled"
|
|
307
|
+
? "capability_unavailable"
|
|
308
|
+
: "execution_failure";
|
|
309
|
+
if (error instanceof InvestigationWorkspaceError) {
|
|
310
|
+
if (error.reason === "revision-conflict" ||
|
|
311
|
+
error.reason === "name-conflict")
|
|
312
|
+
return "revision_conflict";
|
|
313
|
+
if (error.reason === "too-large")
|
|
314
|
+
return "truncated";
|
|
315
|
+
if (error.reason === "disabled")
|
|
316
|
+
return "capability_unavailable";
|
|
317
|
+
if (error.reason === "outside-root")
|
|
318
|
+
return "outside_approved_root";
|
|
319
|
+
if (error.reason === "integrity" || error.reason === "invalid-json")
|
|
320
|
+
return "evidence_integrity_mismatch";
|
|
321
|
+
return "execution_failure";
|
|
322
|
+
}
|
|
323
|
+
if (error instanceof UnknownRegistryError)
|
|
324
|
+
return error.reason === "revision-conflict" ||
|
|
325
|
+
error.reason === "already-exists"
|
|
326
|
+
? "revision_conflict"
|
|
327
|
+
: error.reason === "limit"
|
|
328
|
+
? "truncated"
|
|
329
|
+
: error.reason === "integrity"
|
|
330
|
+
? "evidence_integrity_mismatch"
|
|
331
|
+
: "execution_failure";
|
|
332
|
+
if (error._tag === "ProcessCaptureError")
|
|
333
|
+
return error.cleanupIncomplete
|
|
334
|
+
? "cleanup_incomplete"
|
|
335
|
+
: error.userCategory === "cancelled"
|
|
336
|
+
? "cancelled"
|
|
337
|
+
: error.userCategory === "permission_required"
|
|
338
|
+
? "permission_required"
|
|
339
|
+
: "process_capture_failed";
|
|
340
|
+
switch (error._tag) {
|
|
341
|
+
case "AnalysisProtocolError":
|
|
342
|
+
case "AnalysisOutputError":
|
|
343
|
+
case "HopperProtocolError":
|
|
344
|
+
return "unreadable_output";
|
|
345
|
+
case "AnalysisInputError":
|
|
346
|
+
return "invalid_request";
|
|
347
|
+
case "AnalysisCapabilityUnavailableError":
|
|
348
|
+
case "ProviderSelectionError":
|
|
349
|
+
return "capability_unavailable";
|
|
350
|
+
case "AnalysisCancelledError":
|
|
351
|
+
case "HopperCancelledError":
|
|
352
|
+
return "cancelled";
|
|
353
|
+
case "AnalysisTimeoutError":
|
|
354
|
+
case "HopperTimeoutError":
|
|
355
|
+
return "provider_timeout";
|
|
356
|
+
case "HopperProcessError":
|
|
357
|
+
case "HopperStartError":
|
|
358
|
+
return "provider_unavailable";
|
|
359
|
+
case "ConfigurationError":
|
|
360
|
+
return "configuration_invalid";
|
|
361
|
+
case "NoBinaryOpenError":
|
|
362
|
+
case "BinaryTargetError":
|
|
363
|
+
return "target_unavailable";
|
|
364
|
+
case "EvidenceIntegrityError":
|
|
365
|
+
return "evidence_integrity_mismatch";
|
|
366
|
+
case "EvidenceLimitError":
|
|
367
|
+
return "truncated";
|
|
368
|
+
case "ProviderAdapterError":
|
|
369
|
+
case "HopperRemoteError":
|
|
370
|
+
return "execution_failure";
|
|
371
|
+
case "ArtifactOperationError":
|
|
372
|
+
case "EvidenceFileError":
|
|
373
|
+
case "InvestigationWorkspaceError":
|
|
374
|
+
case "UnknownRegistryError":
|
|
375
|
+
case "PermissionRequiredError":
|
|
376
|
+
throw new TypeError("Unhandled specialized analysis error");
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const errorDetails = (error) => {
|
|
380
|
+
if (error instanceof PermissionRequiredError)
|
|
244
381
|
return {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
382
|
+
capability: error.requested.capability,
|
|
383
|
+
requested: scopeDetails(error.requested),
|
|
384
|
+
missing: missingScopeDetails(error.missing),
|
|
385
|
+
ceiling: error.ceiling === null ? null : scopeDetails(error.ceiling),
|
|
386
|
+
};
|
|
387
|
+
if (error instanceof ArtifactOperationError && error.artifactDetails)
|
|
388
|
+
return {
|
|
389
|
+
logical_path: error.artifactDetails.logicalPath,
|
|
390
|
+
declared_sha256: error.artifactDetails.declaredSha256,
|
|
391
|
+
calculated_sha256: error.artifactDetails.calculatedSha256,
|
|
392
|
+
unpacked: error.artifactDetails.unpacked,
|
|
248
393
|
};
|
|
249
|
-
if (error instanceof AnalysisCancelledError)
|
|
250
|
-
return { operation: error.operation };
|
|
251
|
-
if (error instanceof AnalysisTimeoutError)
|
|
252
|
-
return { operation: error.operation, timeoutMs: error.timeoutMs };
|
|
253
|
-
if (error instanceof ProviderSelectionError)
|
|
254
|
-
return { operation: error.operation };
|
|
255
|
-
if (error instanceof ProviderAdapterError)
|
|
256
|
-
return { providerId: error.providerId, operation: error.operation };
|
|
257
394
|
if (error instanceof ArtifactOperationError)
|
|
258
395
|
return {
|
|
259
396
|
operation: error.operation,
|
|
260
397
|
reason: error.reason,
|
|
261
|
-
...error.
|
|
398
|
+
...(error.reason === "limit" ? { truncated: true } : {}),
|
|
262
399
|
};
|
|
400
|
+
if (error instanceof AnalysisCancelledError)
|
|
401
|
+
return { operation: error.operation, cleanup: "complete" };
|
|
402
|
+
if (error instanceof HopperCancelledError)
|
|
403
|
+
return { operation: "hopper", cleanup: "complete" };
|
|
404
|
+
if (error instanceof AnalysisTimeoutError)
|
|
405
|
+
return { operation: error.operation, timeout_ms: error.timeoutMs };
|
|
406
|
+
if (error instanceof HopperTimeoutError)
|
|
407
|
+
return { operation: "hopper", timeout_ms: error.timeoutMs };
|
|
263
408
|
if (error instanceof EvidenceLimitError)
|
|
264
|
-
return { limit: error.limit, maximum: error.maximum };
|
|
265
|
-
if (error instanceof
|
|
409
|
+
return { limit: error.limit, maximum: error.maximum, truncated: true };
|
|
410
|
+
if (error instanceof InvestigationWorkspaceError)
|
|
266
411
|
return { operation: error.operation, reason: error.reason };
|
|
267
412
|
if (error instanceof UnknownRegistryError)
|
|
268
413
|
return { reason: error.reason };
|
|
269
|
-
if (error instanceof
|
|
270
|
-
return {
|
|
414
|
+
if (error instanceof EvidenceFileError)
|
|
415
|
+
return { operation: error.operation, reason: error.reason };
|
|
416
|
+
if (error instanceof AnalysisCapabilityUnavailableError)
|
|
417
|
+
return { provider_id: error.providerId, operation: error.operation };
|
|
418
|
+
if (error instanceof ProviderAdapterError)
|
|
419
|
+
return { provider_id: error.providerId, operation: error.operation };
|
|
271
420
|
if (error instanceof HopperRemoteError)
|
|
421
|
+
return { provider_code: error.code, diagnostic_type: error.diagnosticType };
|
|
422
|
+
if (error instanceof HopperProcessError && error.failureCode !== undefined)
|
|
423
|
+
return { failure_code: error.failureCode, exit_code: error.exitCode };
|
|
424
|
+
if (error._tag === "ProcessCaptureError" && error.cleanupIncomplete)
|
|
272
425
|
return {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
diagnosticType: error.diagnosticType,
|
|
426
|
+
cleanup: "incomplete",
|
|
427
|
+
resources: [...error.cleanupResources],
|
|
276
428
|
};
|
|
277
|
-
if (error
|
|
278
|
-
|
|
279
|
-
|
|
429
|
+
if (error._tag === "ProcessCaptureError" &&
|
|
430
|
+
error.userCategory === "cancelled")
|
|
431
|
+
return { operation: "process_capture", cleanup: "complete" };
|
|
432
|
+
if (error instanceof BinaryTargetError)
|
|
433
|
+
return { path: error.path };
|
|
434
|
+
return undefined;
|
|
435
|
+
};
|
|
436
|
+
const scopeDetails = (scope) => ({
|
|
437
|
+
capability: scope.capability,
|
|
438
|
+
roots: [...scope.roots],
|
|
439
|
+
executables: [...scope.executables],
|
|
440
|
+
environment_names: [...scope.environment_names],
|
|
441
|
+
network: scope.network,
|
|
442
|
+
mount: scope.mount,
|
|
443
|
+
});
|
|
444
|
+
const missingScopeDetails = (scope) => ({
|
|
445
|
+
...(scope.roots === undefined ? {} : { roots: [...scope.roots] }),
|
|
446
|
+
...(scope.executables === undefined
|
|
447
|
+
? {}
|
|
448
|
+
: { executables: [...scope.executables] }),
|
|
449
|
+
...(scope.environment_names === undefined
|
|
450
|
+
? {}
|
|
451
|
+
: { environment_names: [...scope.environment_names] }),
|
|
452
|
+
...(scope.network === undefined ? {} : { network: scope.network }),
|
|
453
|
+
...(scope.mount === undefined ? {} : { mount: scope.mount }),
|
|
454
|
+
});
|
|
455
|
+
const RETRYABLE_CODES = new Set([
|
|
456
|
+
"provider_timeout",
|
|
457
|
+
"cancelled",
|
|
458
|
+
"revision_conflict",
|
|
459
|
+
"provider_unavailable",
|
|
460
|
+
]);
|
|
461
|
+
const remediationAction = (error) => {
|
|
462
|
+
if (error instanceof PermissionRequiredError)
|
|
463
|
+
return error.elicitationSupported
|
|
464
|
+
? "Approve the exact missing scope, then retry the operation."
|
|
465
|
+
: "Add the exact missing scope beneath the administrator ceiling, then retry.";
|
|
466
|
+
return userMessage(error);
|
|
467
|
+
};
|
|
468
|
+
const errorCategory = (error) => {
|
|
469
|
+
if (error instanceof PermissionRequiredError)
|
|
470
|
+
return "permission_required";
|
|
471
|
+
if (error._tag === "ProcessCaptureError")
|
|
472
|
+
return error.userCategory ?? "execution_failure";
|
|
473
|
+
if (error instanceof HopperRemoteError &&
|
|
474
|
+
error.diagnosticType === "authorization")
|
|
475
|
+
return "permission_required";
|
|
476
|
+
if (error instanceof ArtifactOperationError)
|
|
477
|
+
return artifactErrorCategory(error.reason);
|
|
478
|
+
if (error instanceof EvidenceFileError && error.reason === "disabled")
|
|
479
|
+
return "unavailable";
|
|
480
|
+
if (error instanceof InvestigationWorkspaceError)
|
|
481
|
+
return investigationWorkspaceCategory(error.reason);
|
|
482
|
+
return STATIC_ERROR_CATEGORIES[error._tag] ?? "execution_failure";
|
|
483
|
+
};
|
|
484
|
+
const investigationWorkspaceCategory = (reason) => {
|
|
485
|
+
if (reason === "disabled" ||
|
|
486
|
+
reason === "outside-root" ||
|
|
487
|
+
reason === "not-file")
|
|
488
|
+
return "unavailable";
|
|
489
|
+
if (reason === "too-large")
|
|
490
|
+
return "truncated";
|
|
491
|
+
if (reason === "invalid-json" || reason === "integrity")
|
|
492
|
+
return "integrity_mismatch";
|
|
493
|
+
return "execution_failure";
|
|
494
|
+
};
|
|
495
|
+
const artifactErrorCategory = (reason) => {
|
|
496
|
+
if (reason === "integrity")
|
|
497
|
+
return "integrity_mismatch";
|
|
498
|
+
if (reason === "limit")
|
|
499
|
+
return "truncated";
|
|
500
|
+
if (reason === "cancelled")
|
|
501
|
+
return "cancelled";
|
|
502
|
+
if (reason === "unavailable")
|
|
503
|
+
return "unavailable";
|
|
504
|
+
return "execution_failure";
|
|
505
|
+
};
|
|
506
|
+
const STATIC_ERROR_CATEGORIES = {
|
|
507
|
+
AnalysisInputError: "invalid_input",
|
|
508
|
+
AnalysisCapabilityUnavailableError: "unsupported_provider",
|
|
509
|
+
ProviderSelectionError: "unsupported_provider",
|
|
510
|
+
EvidenceIntegrityError: "integrity_mismatch",
|
|
511
|
+
EvidenceLimitError: "truncated",
|
|
512
|
+
AnalysisCancelledError: "cancelled",
|
|
513
|
+
HopperCancelledError: "cancelled",
|
|
514
|
+
AnalysisTimeoutError: "timeout",
|
|
515
|
+
HopperTimeoutError: "timeout",
|
|
516
|
+
NoBinaryOpenError: "unavailable",
|
|
517
|
+
BinaryTargetError: "unavailable",
|
|
518
|
+
};
|
|
519
|
+
const userMessage = (error) => {
|
|
520
|
+
if (error instanceof PermissionRequiredError)
|
|
521
|
+
return "This operation needs additional local permission. Review the requested scope and remediation.";
|
|
522
|
+
if (error instanceof AnalysisInputError)
|
|
523
|
+
return "Analysis input is invalid. Check the arguments and try again.";
|
|
524
|
+
if (error.userMessage !== undefined)
|
|
525
|
+
return error.userMessage;
|
|
526
|
+
if (UNREADABLE_OUTPUT_TAGS.has(error._tag))
|
|
527
|
+
return "Analysis returned an unreadable result. Retry once; if it continues, run `rea doctor`.";
|
|
528
|
+
if (UNSUPPORTED_PROVIDER_TAGS.has(error._tag))
|
|
529
|
+
return "This analysis is unavailable for the current target. Choose another analysis or target.";
|
|
530
|
+
if (CANCELLED_TAGS.has(error._tag))
|
|
531
|
+
return "Analysis was cancelled. Start it again when ready.";
|
|
532
|
+
if (TIMEOUT_TAGS.has(error._tag))
|
|
533
|
+
return "Analysis took too long. Try a smaller request, then run `rea doctor` if it continues.";
|
|
534
|
+
if (ADAPTER_FAILURE_TAGS.has(error._tag))
|
|
535
|
+
return "Analysis could not complete. Retry once; if it continues, run `rea doctor`.";
|
|
536
|
+
if (START_FAILURE_TAGS.has(error._tag))
|
|
537
|
+
return "Analysis could not start or stopped unexpectedly. Run `rea doctor`, then try again.";
|
|
538
|
+
if (error instanceof ArtifactOperationError)
|
|
539
|
+
return artifactMessage(error.reason);
|
|
540
|
+
if (error instanceof EvidenceIntegrityError)
|
|
541
|
+
return "Evidence is invalid or has changed. Recreate or re-import it, then try again.";
|
|
542
|
+
if (error instanceof EvidenceLimitError)
|
|
543
|
+
return "Evidence is too large for this session. Reduce the evidence set and try again.";
|
|
544
|
+
if (error instanceof EvidenceFileError)
|
|
545
|
+
return evidenceFileMessage(error.reason);
|
|
546
|
+
if (error instanceof InvestigationWorkspaceError)
|
|
547
|
+
return investigationWorkspaceMessage(error.reason);
|
|
548
|
+
if (error instanceof UnknownRegistryError)
|
|
549
|
+
return "Evidence state changed before the update completed. Refresh the current state and try again.";
|
|
550
|
+
if (error instanceof ConfigurationError)
|
|
551
|
+
return "REA configuration is invalid. Run `rea doctor` and fix the reported setting.";
|
|
552
|
+
if (error instanceof NoBinaryOpenError)
|
|
553
|
+
return error.message;
|
|
554
|
+
if (error instanceof BinaryTargetError)
|
|
555
|
+
return "REA could not open that app or binary. Check that the path exists, is readable, and points to a supported file.";
|
|
556
|
+
if (error._tag === "ProcessCaptureError")
|
|
557
|
+
return (error.userMessage ??
|
|
558
|
+
"Process capture could not complete. Run `rea doctor`, then review capture policy and try again.");
|
|
559
|
+
return "Analysis could not complete. Run `rea doctor`, then try again.";
|
|
560
|
+
};
|
|
561
|
+
const UNREADABLE_OUTPUT_TAGS = new Set([
|
|
562
|
+
"AnalysisProtocolError",
|
|
563
|
+
"AnalysisOutputError",
|
|
564
|
+
"HopperProtocolError",
|
|
565
|
+
]);
|
|
566
|
+
const UNSUPPORTED_PROVIDER_TAGS = new Set([
|
|
567
|
+
"AnalysisCapabilityUnavailableError",
|
|
568
|
+
"ProviderSelectionError",
|
|
569
|
+
]);
|
|
570
|
+
const CANCELLED_TAGS = new Set([
|
|
571
|
+
"AnalysisCancelledError",
|
|
572
|
+
"HopperCancelledError",
|
|
573
|
+
]);
|
|
574
|
+
const TIMEOUT_TAGS = new Set([
|
|
575
|
+
"AnalysisTimeoutError",
|
|
576
|
+
"HopperTimeoutError",
|
|
577
|
+
]);
|
|
578
|
+
const ADAPTER_FAILURE_TAGS = new Set([
|
|
579
|
+
"ProviderAdapterError",
|
|
580
|
+
"HopperRemoteError",
|
|
581
|
+
]);
|
|
582
|
+
const START_FAILURE_TAGS = new Set([
|
|
583
|
+
"HopperProcessError",
|
|
584
|
+
"HopperStartError",
|
|
585
|
+
]);
|
|
586
|
+
const artifactMessage = (reason) => {
|
|
587
|
+
if (reason === "cancelled")
|
|
588
|
+
return "Artifact operation was cancelled. Start it again when ready.";
|
|
589
|
+
if (reason === "limit")
|
|
590
|
+
return "Artifact is too large to process safely. Narrow the requested path or use a smaller artifact.";
|
|
591
|
+
if (reason === "path")
|
|
592
|
+
return "Artifact path is not allowed. Choose a path inside the artifact and try again.";
|
|
593
|
+
if (reason === "unavailable")
|
|
594
|
+
return "Artifact format is not available on this system. Choose another artifact or supported environment.";
|
|
595
|
+
if (reason === "format" || reason === "integrity")
|
|
596
|
+
return "Artifact is invalid or has changed. Get a fresh copy and try again.";
|
|
597
|
+
return "Artifact could not be read or written. Check file access and try again.";
|
|
598
|
+
};
|
|
599
|
+
const evidenceFileMessage = (reason) => {
|
|
600
|
+
if (reason === "disabled")
|
|
601
|
+
return "Evidence file access is disabled. Enable an evidence directory or use inline evidence.";
|
|
602
|
+
if (reason === "outside-root")
|
|
603
|
+
return "Evidence path is outside the allowed directory. Choose a path inside the configured evidence directory.";
|
|
604
|
+
if (reason === "not-file")
|
|
605
|
+
return "Evidence path does not point to a file. Choose an evidence file and try again.";
|
|
606
|
+
if (reason === "too-large")
|
|
607
|
+
return "Evidence file is too large. Reduce its size and try again.";
|
|
608
|
+
if (reason === "exists")
|
|
609
|
+
return "Evidence file already exists. Choose another path or allow overwrite.";
|
|
610
|
+
if (reason === "invalid-json")
|
|
611
|
+
return "Evidence file is not valid JSON. Repair or recreate the file and try again.";
|
|
612
|
+
return "Evidence file could not be accessed. Check file permissions and try again.";
|
|
613
|
+
};
|
|
614
|
+
const investigationWorkspaceMessage = (reason) => {
|
|
615
|
+
if (reason === "disabled")
|
|
616
|
+
return "Investigation workspace access is disabled. Configure a workspace directory and try again.";
|
|
617
|
+
if (reason === "outside-root")
|
|
618
|
+
return "Investigation workspace is outside the allowed directory. Choose a configured workspace directory and try again.";
|
|
619
|
+
if (reason === "not-file")
|
|
620
|
+
return "Investigation workspace path is not a file. Choose a workspace file and try again.";
|
|
621
|
+
if (reason === "too-large")
|
|
622
|
+
return "Investigation workspace is too large. Reduce its size and try again.";
|
|
623
|
+
if (reason === "invalid-json" || reason === "integrity")
|
|
624
|
+
return "Investigation workspace is invalid or has changed. Recreate or repair it, then try again.";
|
|
625
|
+
if (reason === "locked")
|
|
626
|
+
return "Another investigation update is in progress. Try again when it finishes.";
|
|
627
|
+
if (reason === "revision-conflict" || reason === "name-conflict")
|
|
628
|
+
return "Investigation workspace state changed. Refresh the current state and try again.";
|
|
629
|
+
return "Investigation workspace could not be accessed. Check file permissions and try again.";
|
|
280
630
|
};
|
|
281
631
|
const KNOWN_ERROR_TAGS = {
|
|
282
632
|
AnalysisProtocolError: true,
|
|
@@ -292,6 +642,7 @@ const KNOWN_ERROR_TAGS = {
|
|
|
292
642
|
EvidenceIntegrityError: true,
|
|
293
643
|
EvidenceLimitError: true,
|
|
294
644
|
EvidenceFileError: true,
|
|
645
|
+
InvestigationWorkspaceError: true,
|
|
295
646
|
UnknownRegistryError: true,
|
|
296
647
|
HopperTimeoutError: true,
|
|
297
648
|
HopperCancelledError: true,
|
|
@@ -302,6 +653,7 @@ const KNOWN_ERROR_TAGS = {
|
|
|
302
653
|
ConfigurationError: true,
|
|
303
654
|
NoBinaryOpenError: true,
|
|
304
655
|
BinaryTargetError: true,
|
|
656
|
+
PermissionRequiredError: true,
|
|
305
657
|
};
|
|
306
658
|
const assertKnownTag = (tag) => {
|
|
307
659
|
if (KNOWN_ERROR_TAGS[tag] !== true)
|
package/dist/domain/evidence.js
CHANGED
|
@@ -3,7 +3,8 @@ import canonicalize from "canonicalize";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { jsonValueSchema } from "./jsonValue.js";
|
|
5
5
|
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
6
|
-
|
|
6
|
+
/** Provider identity schema shared by evidence-bearing persistence formats. */
|
|
7
|
+
export const providerSchema = z.object({
|
|
7
8
|
id: z.string().min(1),
|
|
8
9
|
name: z.string().min(1),
|
|
9
10
|
version: z.string().nullable(),
|
|
@@ -36,7 +37,8 @@ const subjectSchema = z.object({
|
|
|
36
37
|
local_path: z.string(),
|
|
37
38
|
});
|
|
38
39
|
/** Source location attached to an evidence observation. */
|
|
39
|
-
|
|
40
|
+
/** Source-location schema shared by evidence-bearing persistence formats. */
|
|
41
|
+
export const evidenceLocationSchema = z.discriminatedUnion("kind", [
|
|
40
42
|
z.object({ kind: z.literal("address"), address: z.string().min(1) }),
|
|
41
43
|
z.object({
|
|
42
44
|
kind: z.literal("address-range"),
|
|
@@ -66,6 +66,34 @@ export const createEvidenceBundle = (records, unknowns = []) => {
|
|
|
66
66
|
records: sortedRecords,
|
|
67
67
|
};
|
|
68
68
|
};
|
|
69
|
+
/** Restrict a bundle to records and complete unknown histories for one artifact. */
|
|
70
|
+
export const evidenceBundleForTarget = (bundle, sha256) => {
|
|
71
|
+
const recordsById = new Map(bundle.records.map((record) => [record.evidence_id, record]));
|
|
72
|
+
let unknowns = bundle.unknowns.filter(({ scope_digest: scopeDigest }) => scopeDigest === sha256);
|
|
73
|
+
let changed = true;
|
|
74
|
+
while (changed) {
|
|
75
|
+
const ids = new Set(unknowns.map(({ unknown_id: id }) => id));
|
|
76
|
+
const retained = unknowns.filter((unknown) => unknown.relationships.every(({ unknown_id: id }) => ids.has(id)) &&
|
|
77
|
+
referencedEvidenceIds(unknown).every((id) => {
|
|
78
|
+
const record = recordsById.get(id);
|
|
79
|
+
return (record?.subject?.digest.sha256 === sha256 ||
|
|
80
|
+
(unknown.mutation_evidence_ids.includes(id) &&
|
|
81
|
+
record?.subject === null &&
|
|
82
|
+
record.predicate_type === "rea.residual-unknown-mutation/v1"));
|
|
83
|
+
}));
|
|
84
|
+
changed = retained.length !== unknowns.length;
|
|
85
|
+
unknowns = retained;
|
|
86
|
+
}
|
|
87
|
+
const mutationIds = new Set(unknowns.flatMap(({ mutation_evidence_ids: ids }) => ids));
|
|
88
|
+
return createEvidenceBundle(bundle.records.filter((record) => record.subject?.digest.sha256 === sha256 ||
|
|
89
|
+
mutationIds.has(record.evidence_id)), unknowns);
|
|
90
|
+
};
|
|
91
|
+
const referencedEvidenceIds = (unknown) => [
|
|
92
|
+
...unknown.supporting_evidence_ids,
|
|
93
|
+
...unknown.contradicting_evidence_ids,
|
|
94
|
+
...unknown.mutation_evidence_ids,
|
|
95
|
+
...(unknown.resolution?.evidence_ids ?? []),
|
|
96
|
+
];
|
|
69
97
|
/** Parse records, verify semantic IDs, and reject inconsistent manifests. */
|
|
70
98
|
export const parseEvidenceBundle = (input) => {
|
|
71
99
|
const parsed = evidenceBundleSchema.parse(input);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const failures = [
|
|
2
|
+
[
|
|
3
|
+
70,
|
|
4
|
+
"private_display_unavailable",
|
|
5
|
+
"REA could not start its private Linux display. Install Xvfb, then rerun rea doctor.",
|
|
6
|
+
],
|
|
7
|
+
[
|
|
8
|
+
71,
|
|
9
|
+
"x11_authorization_failed",
|
|
10
|
+
"REA could not authorize its private Linux display. Install xauth, then rerun rea doctor.",
|
|
11
|
+
],
|
|
12
|
+
[
|
|
13
|
+
72,
|
|
14
|
+
"unsupported_hopper_build",
|
|
15
|
+
"This Hopper build is not supported for unattended Linux analysis. Unset HOPPER_LAUNCHER_PATH and rerun rea setup --yes --install-hopper, or update REA.",
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
73,
|
|
19
|
+
"invalid_launch_command",
|
|
20
|
+
"REA refused an unexpected Hopper launch command. Check HOPPER_LAUNCHER_PATH and rerun rea doctor.",
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
74,
|
|
24
|
+
"process_ownership_mismatch",
|
|
25
|
+
"REA could not verify ownership of the launched Hopper process and stopped safely. Close other Hopper sessions and retry.",
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
75,
|
|
29
|
+
"hopper_exited_during_startup",
|
|
30
|
+
"Hopper exited before the REA bridge was ready. Run rea doctor and retry the analysis.",
|
|
31
|
+
],
|
|
32
|
+
[
|
|
33
|
+
76,
|
|
34
|
+
"unsupported_demo_dialog",
|
|
35
|
+
"This Hopper demo dialog is not supported for unattended Linux analysis. Update REA or use a supported Hopper build.",
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
77,
|
|
39
|
+
"unexpected_display_geometry",
|
|
40
|
+
"REA's private Linux display did not match the validated layout. Close custom display overrides and retry.",
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
78,
|
|
44
|
+
"x11_input_failed",
|
|
45
|
+
"REA could not activate Hopper's validated demo control. Install the XTEST runtime, then rerun rea doctor.",
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
79,
|
|
49
|
+
"runtime_dependency_unavailable",
|
|
50
|
+
"A Linux demo-session dependency is unavailable. Rerun rea setup --yes --install-hopper, then rea doctor.",
|
|
51
|
+
],
|
|
52
|
+
];
|
|
53
|
+
const HOPPER_STARTUP_FAILURES = new Map(failures.map(([exitCode, code, message]) => [exitCode, { code, message }]));
|
|
54
|
+
/** Return the stable public failure associated with a Linux demo adapter exit. */
|
|
55
|
+
export const hopperStartupFailure = (exitCode) => exitCode === null ? undefined : HOPPER_STARTUP_FAILURES.get(exitCode);
|