refactor(artifacts): consolidate inventory into inspection

This commit is contained in:
morluto committed 2026-09-29 09:22:24 +08:00
1 parent a66e43a99d
commit 0fe96d92df
28 files changed
+375 -280

No files matched your search

+2 -2
View File
@@ -2,7 +2,7 @@ import type { BinaryTarget } from "../domain/binaryTarget.js";
import type { OfficialToolName } from "../contracts/toolContracts.js";
import type { EnhancedToolName } from "../contracts/enhancedInputs.js";
import type { NativeToolName } from "../contracts/nativeToolContracts.js";
import type { ArtifactToolName } from "../contracts/artifactToolContracts.js";
import type { ArtifactAnalysisOperation } from "../contracts/artifactToolContracts.js";
import type { ManagedToolName } from "../contracts/managedToolContracts.js";
import type { AnalysisError } from "../domain/errors.js";
import type { AnalysisProfileCommitment } from "../domain/analysisProfile.js";
@@ -25,7 +25,7 @@ export type AnalysisOperation =
| OfficialToolName
| EnhancedToolName
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| "health";
@@ -126,19 +126,23 @@ export const resolveCompareApplicationVersionsRequestValidated = (
input: CompareApplicationVersionsRequest,
lookup?: EvidenceLookup,
): Result<ComparisonInput, AnalysisError> => {
const left = graphEvidence(
"left" in input
? { application: input.left }
: { application_evidence_id: input.left_evidence_id },
lookup,
);
const leftReference = input.left ?? input.left_evidence_id;
if (leftReference === undefined)
return err(
new AnalysisInputError("compare_application_versions", undefined, [
{ path: ["left"], reason: "missing_argument" },
]),
);
const left = resolveApplicationGraphReference(leftReference, lookup);
if (!left.ok) return left;
const right = graphEvidence(
"right" in input
? { application: input.right }
: { application_evidence_id: input.right_evidence_id },
lookup,
);
const rightReference = input.right ?? input.right_evidence_id;
if (rightReference === undefined)
return err(
new AnalysisInputError("compare_application_versions", undefined, [
{ path: ["right"], reason: "missing_argument" },
]),
);
const right = resolveApplicationGraphReference(rightReference, lookup);
if (!right.ok) return right;
const leftNative = resolveEvidenceReferences(
lookup,
@@ -265,6 +269,14 @@ const graphEvidence = (
return resolved.ok ? ok(resolved.value[0]) : resolved;
};
const resolveApplicationGraphReference = (
reference: Evidence | string,
lookup: EvidenceLookup | undefined,
) =>
typeof reference === "string"
? graphEvidence({ application_evidence_id: reference }, lookup)
: graphEvidence({ application: reference }, lookup);
const invalid = <Value>(
operation: string,
cause: z.ZodError,
+2
View File
@@ -37,6 +37,7 @@ export const binarySessionStatus = ({
const provider = providerSummary(configuredProvider);
const providers = router.providerIdentities(route).map(providerSummary);
const capabilities = [...(route.capabilities?.values() ?? [])]
.filter(({ operation }) => operation !== "inventory_artifact")
.sort((left, right) => left.operation.localeCompare(right.operation))
.map((descriptor) => {
const unavailable = runtimeUnavailability.get(descriptor.operation);
@@ -66,6 +67,7 @@ export const binarySessionStatus = ({
selected: candidate.selected,
capabilities: candidate.capabilities
.slice()
.filter(({ operation }) => operation !== "inventory_artifact")
.sort((left, right) => left.operation.localeCompare(right.operation))
.map(capabilityStatus),
}));
+10 -10
View File
@@ -7,7 +7,7 @@ import { silentLogger, type Logger } from "../logger.js";
import { createEvidence } from "../domain/evidence.js";
import type { Evidence } from "../domain/evidence.js";
import type { NativeToolName } from "../contracts/nativeToolContracts.js";
import type { ArtifactToolName } from "../contracts/artifactToolContracts.js";
import type { ArtifactAnalysisOperation } from "../contracts/artifactToolContracts.js";
import type { ManagedToolName } from "../contracts/managedToolContracts.js";
import {
EvidenceIntegrityError,
@@ -92,7 +92,7 @@ export const runDirectAnalysis = async (
export const runProviderAnalysis = async (
...[path, tool, arguments_, logger = silentLogger, signal]: readonly [
path: string,
tool: NativeToolName | ArtifactToolName | ManagedToolName,
tool: NativeToolName | ArtifactAnalysisOperation | ManagedToolName,
arguments_: Readonly<Record<string, JsonValue>>,
logger?: Logger,
signal?: AbortSignal,
@@ -110,7 +110,7 @@ const authorizeAnalysis = async (
authority: PermissionAuthority,
tool:
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| DirectAnalysisTool,
arguments_: Readonly<Record<string, JsonValue>>,
@@ -170,7 +170,7 @@ const authorizeSnapshotAccess = async (
authority: PermissionAuthority,
tool:
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| DirectAnalysisTool,
snapshotPath: string | undefined,
@@ -196,7 +196,7 @@ const authorizeAnalysisRun = async (input: {
readonly suppliedAuthority: PermissionAuthority | undefined;
readonly tool:
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| DirectAnalysisTool;
readonly arguments: Readonly<Record<string, JsonValue>>;
@@ -226,7 +226,7 @@ const runAnalysis = async (
path: string,
tool:
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| DirectAnalysisTool,
arguments_: Readonly<Record<string, JsonValue>>,
@@ -316,7 +316,7 @@ const executeAnalysisTool = async (input: {
readonly openedTarget: BinaryTarget;
readonly tool:
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| DirectAnalysisTool;
readonly arguments: Readonly<Record<string, JsonValue>>;
@@ -421,7 +421,7 @@ const replayProviderFor = (
session: ReturnType<typeof createBinarySession>,
tool:
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| DirectAnalysisTool,
) =>
@@ -433,7 +433,7 @@ const analysisProfileForEvidence = (
session: ReturnType<typeof createBinarySession>,
tool:
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| DirectAnalysisTool,
): AnalysisProfileCommitment | undefined => {
@@ -445,7 +445,7 @@ const analysisProfileForEvidence = (
const isWorkflowEvidenceTool = (
tool:
| NativeToolName
| ArtifactToolName
| ArtifactAnalysisOperation
| ManagedToolName
| DirectAnalysisTool,
): boolean =>
+11 -10
View File
@@ -10,11 +10,10 @@ import {
import { inventoryArtifactFully } from "../application/ArtifactInventory.js";
import { extractArtifact } from "../application/ArtifactExtraction.js";
import {
ARTIFACT_TOOL_CONTRACTS,
artifactInspectionInputSchema,
ARTIFACT_ANALYSIS_OPERATIONS,
artifactInventoryInputSchema,
artifactExtractionExecutionSchema,
type ArtifactToolName,
type ArtifactAnalysisOperation,
} from "../contracts/artifactToolContracts.js";
import type { BinaryTarget } from "../domain/binaryTarget.js";
import {
@@ -46,10 +45,10 @@ export class ArtifactProvider implements AnalysisProvider {
) {}
readonly #capabilities: readonly CapabilityDescriptor[] = Object.freeze(
ARTIFACT_TOOL_CONTRACTS.map((contract) =>
ARTIFACT_ANALYSIS_OPERATIONS.map((operation) =>
Object.freeze({
provider: IDENTITY,
operation: contract.name,
operation,
available: true as const,
reason: null,
effects: Object.freeze({
@@ -57,7 +56,7 @@ export class ArtifactProvider implements AnalysisProvider {
launchesProcess: true,
mayShowUi: false,
mayAccessNetwork: false,
mayWriteFilesystem: contract.name === "extract_artifact",
mayWriteFilesystem: operation === "extract_artifact",
changesPermissions: false,
requiresRoot: false,
}),
@@ -169,7 +168,7 @@ class ArtifactClient implements AnalysisClient {
parameters: Readonly<Record<string, JsonValue>>,
options?: ExecutionOptions,
) {
const parsed = artifactInspectionInputSchema.parse(parameters);
const parsed = artifactInventoryInputSchema.parse(parameters);
const inventoryParameters = artifactInventoryInputSchema.parse({
native_mount_approved: parsed.native_mount_approved,
integrity_policy: parsed.integrity_policy,
@@ -256,11 +255,13 @@ const DEFAULT_ARTIFACT_LIMITS: ArtifactLimits = {
const isArtifactOperation = (
operation: AnalysisOperation,
): operation is ArtifactToolName =>
ARTIFACT_TOOL_CONTRACTS.some(({ name }) => name === operation);
): operation is ArtifactAnalysisOperation =>
ARTIFACT_ANALYSIS_OPERATIONS.includes(
operation as (typeof ARTIFACT_ANALYSIS_OPERATIONS)[number],
);
const translateFailure = (
operation: ArtifactToolName,
operation: ArtifactAnalysisOperation,
cause: unknown,
): AnalysisError => {
if (cause instanceof ArtifactReaderFailure)
+30 -46
View File
@@ -11,52 +11,10 @@ export const registerArtifactCommands = (
cli: CliInstance,
logger: Logger,
): void => {
registerInventoryCommand(cli, logger);
registerInspectionCommand(cli, logger);
registerExtractionCommand(cli, logger);
};
const registerInventoryCommand = (cli: CliInstance, logger: Logger): void => {
cli.command(CLI_COMMANDS.inventoryArtifact, {
description: "Build a complete deterministic artifact graph",
args: z.object({
path: z.string().describe("Application or package path"),
}),
options: z.object({
integrityPolicy: z
.enum(["fail", "record-and-continue"])
.default("fail")
.describe("Behavior when declared artifact integrity does not match"),
integrityContinueApproved: z
.boolean()
.default(false)
.describe("Approve continuing after recorded integrity mismatches"),
nativeMountApproved: z
.boolean()
.default(false)
.describe("Approve read-only native mounting when required"),
}),
alias: {
integrityPolicy: "integrity-policy",
integrityContinueApproved: "integrity-continue-approved",
nativeMountApproved: "native-mount-approved",
},
run: ({ args, options }) =>
logCliCommand(logger, "inventory-artifact", () =>
runProviderAnalysis(
args.path,
"inventory_artifact",
{
integrity_policy: options.integrityPolicy,
integrity_continue_approved: options.integrityContinueApproved,
native_mount_approved: options.nativeMountApproved,
},
logger,
),
),
});
};
const registerExtractionCommand = (cli: CliInstance, logger: Logger): void => {
cli.command(CLI_COMMANDS.extractArtifact, {
description: "Extract explicitly selected artifact occurrences safely",
@@ -67,7 +25,6 @@ const registerExtractionCommand = (cli: CliInstance, logger: Logger): void => {
occurrenceIds: z
.array(z.string().regex(/^occ_[a-f0-9]{64}$/u))
.min(1)
.max(500)
.describe("Exact artifact occurrence IDs selected for extraction"),
}),
alias: { occurrenceIds: "occurrence-ids" },
@@ -93,10 +50,37 @@ const registerInspectionCommand = (cli: CliInstance, logger: Logger): void => {
args: z.object({
path: z.string().describe("Application or package path"),
}),
options: z.object({}),
run: ({ args }) =>
options: z.object({
integrityPolicy: z
.enum(["fail", "record-and-continue"])
.default("fail")
.describe("Behavior when declared artifact integrity does not match"),
integrityContinueApproved: z
.boolean()
.default(false)
.describe("Approve continuing after recorded integrity mismatches"),
nativeMountApproved: z
.boolean()
.default(false)
.describe("Approve read-only native mounting when required"),
}),
alias: {
integrityPolicy: "integrity-policy",
integrityContinueApproved: "integrity-continue-approved",
nativeMountApproved: "native-mount-approved",
},
run: ({ args, options }) =>
logCliCommand(logger, "inspect-artifact", () =>
runProviderAnalysis(args.path, "inspect_artifact", {}, logger),
runProviderAnalysis(
args.path,
"inspect_artifact",
{
integrity_policy: options.integrityPolicy,
integrity_continue_approved: options.integrityContinueApproved,
native_mount_approved: options.nativeMountApproved,
},
logger,
),
),
});
};
+11 -19
View File
@@ -35,18 +35,8 @@ export const artifactExtractionInputSchema = z.object({
export const artifactExtractionExecutionSchema =
artifactExtractionInputSchema.extend({ output_root: z.string().min(1) });
/** Bounded provider-neutral inspection using one atomic inventory substep. */
const artifactInspectionFacts = {
native_mount_approved: z.boolean().default(false),
} as const;
export const artifactInspectionInputSchema = z.union([
z.object({ ...artifactInspectionFacts, ...integrityInput.fail }),
z.object({ ...artifactInspectionFacts, ...integrityInput.continue }),
]);
const exampleInputSchema = z.record(z.string(), jsonValueSchema);
const examples: Readonly<Record<string, Readonly<Record<string, unknown>>>> = {
inventory_artifact: {},
inspect_artifact: {},
extract_artifact: {
occurrence_ids: [`occ_${"0".repeat(64)}`],
@@ -80,15 +70,10 @@ const artifact = <
/** Artifact-graph inventory and safe extraction contracts. */
export const ARTIFACT_TOOL_CONTRACTS = [
artifact(
"inventory_artifact",
"After open_binary binds a local archive, application package, or other artifact—or when one is already active—inventory the complete content-addressed artifact graph in one call. This tool accepts no path; in a target-free session open the target first. It does not extract or mount by default.",
artifactInventoryInputSchema,
),
artifact(
"inspect_artifact",
"After open_binary binds a local archive, application package, or other artifact—or when one is already active—inspect the complete artifact inventory in one cancellable substep. This tool accepts no path; in a target-free session open the target first. Returns the full substep Evidence, observations, derived relationships, hypotheses, contradictions, unexplored branches, limitations, and format-specific next probes. Any substep failure fails the whole call.",
artifactInspectionInputSchema,
"Inspect an active archive or application package in one call. Returns the complete content-addressed artifact graph, source Evidence, observations, relationships, integrity contradictions, hypotheses, unexplored branches, limitations, and next probes inline. It does not extract files; use extract_artifact for selected occurrences.",
artifactInventoryInputSchema,
),
artifact(
"extract_artifact",
@@ -97,5 +82,12 @@ export const ARTIFACT_TOOL_CONTRACTS = [
),
] as const satisfies readonly ToolContract[];
/** Names of provider-neutral artifact graph operations. */
export type ArtifactToolName = (typeof ARTIFACT_TOOL_CONTRACTS)[number]["name"];
/** Provider operations; inventory remains an internal primitive of inspection. */
export const ARTIFACT_ANALYSIS_OPERATIONS = [
"inventory_artifact",
...ARTIFACT_TOOL_CONTRACTS.map(({ name }) => name),
] as const;
/** Provider-level operations including the inventory primitive used internally. */
export type ArtifactAnalysisOperation =
(typeof ARTIFACT_ANALYSIS_OPERATIONS)[number];
+15 -6
View File
@@ -10,6 +10,7 @@ import {
type IntegrityContradiction,
} from "./artifactGraph.js";
import { parseEvidence, type Evidence } from "./evidence.js";
import { artifactInspectionResultSchema } from "./artifactInspection.js";
/** Complete or partially assembled inventory used by comparison workflows. */
export interface InventorySet {
@@ -34,17 +35,25 @@ export const parseArtifactInventoryEvidence = (
assembleInventorySet(
(Array.isArray(input) ? input : [input]).map((value) => {
const evidence = parseEvidence(value);
if (evidence.operation !== "inventory_artifact")
const inventoryEvidence =
evidence.operation === "inventory_artifact"
? evidence
: evidence.operation === "inspect_artifact"
? artifactInspectionResultSchema.parse(evidence.normalized_result)
.substeps[0]?.evidence
: undefined;
if (inventoryEvidence === undefined)
throw new TypeError(
"Artifact comparison requires inventory_artifact Evidence",
"Artifact comparison requires inspect_artifact Evidence",
);
const inventory = artifactInventoryResultSchema.parse(
evidence.normalized_result,
inventoryEvidence.normalized_result,
);
if (
evidence.subject === null ||
evidence.subject.digest.sha256 !== inventory.manifest.root_sha256 ||
evidence.subject.format !== inventory.manifest.root_format
inventoryEvidence.subject === null ||
inventoryEvidence.subject.digest.sha256 !==
inventory.manifest.root_sha256 ||
inventoryEvidence.subject.format !== inventory.manifest.root_format
)
throw new TypeError(
"Artifact inventory Evidence subject does not match its root digest",
+1 -1
View File
@@ -30,7 +30,7 @@ interface GeneratedPayload {
}
const GENERATED_PAYLOAD_JSON =
'{"auxiliaryProviders":[{"capabilities":[{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["DMG child inventory is macOS-only and requires per-call approval plus operator policy; PKG remains root-hash-only.","ASAR files discovered in filesystem-backed inventories are expanded without bulk extraction; other nested containers remain recorded only."],"operation":"inventory_artifact","provider":{"id":"rea-artifact-graph","name":"REA safe artifact graph provider","version":"1"},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["DMG child inventory is macOS-only and requires per-call approval plus operator policy; PKG remains root-hash-only.","ASAR files discovered in filesystem-backed inventories are expanded without bulk extraction; other nested containers remain recorded only."],"operation":"inspect_artifact","provider":{"id":"rea-artifact-graph","name":"REA safe artifact graph provider","version":"1"},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":true,"mutatesArtifact":false,"requiresRoot":false},"limitations":["DMG child inventory is macOS-only and requires per-call approval plus operator policy; PKG remains root-hash-only.","ASAR files discovered in filesystem-backed inventories are expanded without bulk extraction; other nested containers remain recorded only."],"operation":"extract_artifact","provider":{"id":"rea-artifact-graph","name":"REA safe artifact graph provider","version":"1"},"reason":null}],"identity":{"id":"rea-artifact-graph","name":"REA safe artifact graph provider","version":"1"}},{"capabilities":[{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"inspect_macho","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"inspect_signature","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"inspect_plist","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"list_architectures","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"demangle_swift","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null}],"identity":{"id":"native-macos","name":"macOS native inspection utilities","version":null}},{"capabilities":[{"available":true,"effects":{"changesPermissions":false,"launchesProcess":false,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["This capability inventories PE/CLI identity only; inspect_managed_members admits bounded metadata members, signatures, and CIL anchors.","It never loads the target assembly, resolves dependencies through a CLR, decompiles C#, or executes target code."],"operation":"inspect_managed_artifact","provider":{"id":"rea-dotnet-static","name":"REA managed static analysis provider","version":"1"},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":false,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesysLine truncated
'{"auxiliaryProviders":[{"capabilities":[{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["DMG child inventory is macOS-only and requires per-call approval plus operator policy; PKG remains root-hash-only.","ASAR files discovered in filesystem-backed inventories are expanded without bulk extraction; other nested containers remain recorded only."],"operation":"inventory_artifact","provider":{"id":"rea-artifact-graph","name":"REA safe artifact graph provider","version":"1"},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["DMG child inventory is macOS-only and requires per-call approval plus operator policy; PKG remains root-hash-only.","ASAR files discovered in filesystem-backed inventories are expanded without bulk extraction; other nested containers remain recorded only."],"operation":"inspect_artifact","provider":{"id":"rea-artifact-graph","name":"REA safe artifact graph provider","version":"1"},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":true,"mutatesArtifact":false,"requiresRoot":false},"limitations":["DMG child inventory is macOS-only and requires per-call approval plus operator policy; PKG remains root-hash-only.","ASAR files discovered in filesystem-backed inventories are expanded without bulk extraction; other nested containers remain recorded only."],"operation":"extract_artifact","provider":{"id":"rea-artifact-graph","name":"REA safe artifact graph provider","version":"1"},"reason":null}],"identity":{"id":"rea-artifact-graph","name":"REA safe artifact graph provider","version":"1"}},{"capabilities":[{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"inspect_macho","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"inspect_signature","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"inspect_plist","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"list_architectures","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":true,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["Availability and textual formats depend on the installed macOS/Xcode toolchain."],"operation":"demangle_swift","provider":{"id":"native-macos","name":"macOS native inspection utilities","version":null},"reason":null}],"identity":{"id":"native-macos","name":"macOS native inspection utilities","version":null}},{"capabilities":[{"available":true,"effects":{"changesPermissions":false,"launchesProcess":false,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesystem":false,"mutatesArtifact":false,"requiresRoot":false},"limitations":["This capability inventories PE/CLI identity only; inspect_managed_members admits bounded metadata members, signatures, and CIL anchors.","It never loads the target assembly, resolves dependencies through a CLR, decompiles C#, or executes target code."],"operation":"inspect_managed_artifact","provider":{"id":"rea-dotnet-static","name":"REA managed static analysis provider","version":"1"},"reason":null},{"available":true,"effects":{"changesPermissions":false,"launchesProcess":false,"mayAccessNetwork":false,"mayShowUi":false,"mayWriteFilesysLine truncated
const generatedPayload: unknown = JSON.parse(GENERATED_PAYLOAD_JSON);
if (!isGeneratedPayload(generatedPayload))
throw new TypeError("Generated MCP catalog payload is invalid");
+16 -4
View File
@@ -4,6 +4,7 @@ import type { AnalysisOperationPort } from "../application/AnalysisProvider.js";
import type { BinarySessionPort } from "../application/BinarySession.js";
import type { PromptCompletionKind } from "../contracts/promptContracts.js";
import { artifactInventoryResultSchema } from "../domain/artifactGraph.js";
import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
import { processCaptureSchema } from "../domain/processCapture.js";
const documentListSchema = z.array(z.string().min(1));
@@ -145,10 +146,21 @@ const evidenceValues = (
if (kind === "evidence") return [evidence.evidence_id];
if (kind === "capture")
return isProcessCaptureEvidence(evidence) ? [evidence.evidence_id] : [];
if (evidence.operation !== "inventory_artifact") return [];
const inventory = artifactInventoryResultSchema.safeParse(
evidence.normalized_result,
);
const inventoryResult =
evidence.operation === "inventory_artifact"
? evidence.normalized_result
: evidence.operation === "inspect_artifact"
? (() => {
const inspection = artifactInspectionResultSchema.safeParse(
evidence.normalized_result,
);
return inspection.success
? inspection.data.substeps[0]?.evidence.normalized_result
: undefined;
})()
: undefined;
if (inventoryResult === undefined) return [];
const inventory = artifactInventoryResultSchema.safeParse(inventoryResult);
if (!inventory.success) return [];
if (kind === "manifest") return [inventory.data.manifest.manifest_id];
return inventory.data.occurrences.map(({ occurrence_id: id }) => id);
+1 -1
View File
@@ -24,7 +24,7 @@ export const registerArtifactComparisonTool = (
toolRegistrationOptions(contract),
async (input, context) => {
const expected = {
operation: "inventory_artifact",
operation: ["inspect_artifact", "inventory_artifact"],
predicate: "rea.analysis/v2",
};
const left = resolveSessionEvidenceIds(
+11 -4
View File
@@ -13,9 +13,16 @@ type EvidenceAuthorityResult = Result<Evidence[], EvidenceIntegrityError>;
export const resolveSessionEvidenceIds = (
session: BinarySessionPort,
evidenceIds: readonly string[],
expected: { readonly operation: string; readonly predicate: string },
expected: {
readonly operation: string | readonly string[];
readonly predicate: string;
},
): EvidenceAuthorityResult => {
const records: Evidence[] = [];
const expectedOperations = Array.isArray(expected.operation)
? expected.operation
: [expected.operation];
const expectedOperationLabel = expectedOperations.join(" or ");
for (const evidenceId of evidenceIds) {
const record = session.evidenceById(evidenceId);
if (record === undefined)
@@ -23,16 +30,16 @@ export const resolveSessionEvidenceIds = (
new EvidenceReferenceError(
evidenceId,
"missing",
expected.operation,
expectedOperationLabel,
null,
),
);
if (record.operation !== expected.operation)
if (!expectedOperations.includes(record.operation))
return err(
new EvidenceReferenceError(
evidenceId,
"wrong_operation",
expected.operation,
expectedOperationLabel,
record.operation,
),
);