import { z } from "zod"; import { evidenceSchema, providerSchema } from "./evidence.js"; import { javascriptApplicationGraphSchema } from "./javascriptApplicationGraph.js"; const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u); const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u); const nodeIdSchema = z.string().regex(/^jag_node_[a-f0-9]{64}$/u); const edgeIdSchema = z.string().regex(/^jag_edge_[a-f0-9]{64}$/u); const boundedTextSchema = z.string().min(1); /** Literal starting point for one application feature trace. */ const applicationFeatureSeedSchema = z.strictObject({ kind: z.enum([ "node-id", "route", "string", "api", "channel", "module", "native-export", ]), value: boundedTextSchema, match: z.enum(["exact", "contains"]).nullable().default(null), case_sensitive: z.boolean().default(false), }); /** Evidence-backed application graph and feature seed. */ export const traceApplicationFeatureInputSchema = z .strictObject({ application: evidenceSchema, native_observations: z.array(evidenceSchema).default([]), seed: applicationFeatureSeedSchema, direction: z.enum(["outgoing", "incoming", "both"]).default("both"), }) .superRefine((input, context) => { const ids = input.native_observations.map(({ evidence_id: id }) => id); if (new Set(ids).size !== ids.length) context.addIssue({ code: "custom", path: ["native_observations"], message: "Native observations must be unique", }); }); const seedMatchSchema = z.strictObject({ node_id: nodeIdSchema, kind: z.string().min(1), basis: z.enum(["node-id", "label", "identity", "property"]), field: z.string().min(1), }); const tracePathSchema = z.strictObject({ path_id: z.string().regex(/^jatp_[a-f0-9]{64}$/u), start_node_id: nodeIdSchema, end_node_id: nodeIdSchema, end_kind: z.string().min(1), node_ids: z.array(nodeIdSchema).min(1), edge_ids: z.array(edgeIdSchema), authorities: z.array(z.string().min(1)), contains_inference: z.boolean(), }); const nativeHandoffSchema = z.strictObject({ native_node_id: nodeIdSchema, artifact_sha256: digestSchema, artifact_path: z.string().min(1).nullable(), export_node_ids: z.array(nodeIdSchema), requested_exports: z.array(boundedTextSchema), status: z.enum(["evidence-linked", "requires-provider-analysis"]), providers: z.array(providerSchema), evidence_ids: z.array(evidenceIdSchema), recommended_tools: z.array( z.enum([ "open_binary", "binary_overview", "search_procedures", "analyze_function", "xrefs", ]), ), }); /** Evidence-preserving complete reachable subgraph and native-analysis handoffs. */ export const applicationFeatureTraceResultSchema = z.strictObject({ trace_id: z.string().regex(/^jatr_[a-f0-9]{64}$/u), source_evidence_id: evidenceIdSchema, source_graph_id: z.string().regex(/^jag_[a-f0-9]{64}$/u), seed: applicationFeatureSeedSchema, direction: z.enum(["outgoing", "incoming", "both"]), seed_matches: z.array(seedMatchSchema), graph: javascriptApplicationGraphSchema.nullable(), paths: z.array(tracePathSchema), native_handoffs: z.array(nativeHandoffSchema), summary: z.strictObject({ matched_seeds: z.number().int().min(0), traced_nodes: z.number().int().min(0), traced_edges: z.number().int().min(0), terminal_paths: z.number().int().min(0), native_handoffs: z.number().int().min(0), observed_facts: z.number().int().min(0), inferred_facts: z.number().int().min(0), unknown_facts: z.number().int().min(0), unavailable_facts: z.number().int().min(0), }), coverage: z.strictObject({ status: z.enum(["complete-within-source", "partial", "no-match"]), source_graph_status: z.enum([ "complete", "partial", "unknown", "unavailable", ]), total_seed_matches: z.number().int().min(0), }), evidence_links: z.array(evidenceIdSchema).min(1), limitations: z.array(boundedTextSchema), }); export type TraceApplicationFeatureInput = z.infer< typeof traceApplicationFeatureInputSchema >; export type ApplicationFeatureSeed = z.infer< typeof applicationFeatureSeedSchema >; export type ApplicationFeatureTraceResult = z.infer< typeof applicationFeatureTraceResultSchema >;