UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

120 lines • 5.49 kB
/** * skillGraphDeclared — the AUTHOR'S skill map, projected for the record. * * Pattern: Pure projection over the structural graph `.skillGraph()` accepts. * Role: Turn what the builder was HANDED into what * `agentfootprint.skill.graph_declared` SAYS — the declared nodes and * edges as DATA, so a consumer drawing the graph from a recording * (the lens's `declaredSource: 'recording'` branch) never has to * reconstruct the topology from per-hop `routing[]` provenance, which * names an edge only once it FIRES and is therefore a lower bound. * Emits: N/A — the Agent dispatches what this returns, once per run, right * after the run-configuration manifest. * * ## The rule this file exists to keep * * The map carries EXACTLY what the author declared — the built graph's own * `nodes` and `edges` — and nothing inferred. Two consequences, both refusals * to guess: * * - a structurally-typed graph that carries no `nodes` yields `undefined` * (no event), because a map this file would have to invent is not the * author's map — the same forward-compat posture as `explainNextSkill` * ("a graph built before it existed still routes, it just cannot narrate"); * - an edge row that does not state its own `from` (the `.skillGraph()` * structural type required only `to` until 9.50.0) is SKIPPED, never * completed: `from: null` means the synthetic START and only the graph may * say so. * * Descriptions are the skills' catalog descriptions, verbatim — the text the * model reads — looked up by node id; absent when a skill declares none. * Titles (`defineSkill({ title })`) are looked up the same way and absent the * same way; a title is for a person reading a report and is never drawn in * place of `label`. */ /** * Project the declared map, or `undefined` when the graph cannot state one. * * `skills` supplies the catalog descriptions (the graph's compiled * injections); only `flavor: 'skill'` rows are consulted, and a node with no * matching skill (a predicate diamond) simply carries no description. */ export function buildSkillGraphDeclared(graph, skills) { const rawNodes = graph.nodes ?? []; const nodes = rawNodes.flatMap((n) => { if (typeof n.id !== 'string' || n.id.length === 0) return []; const skill = skills.find((s) => s.flavor === 'skill' && s.id === n.id); const description = skill?.description; // The skill's declared plain name (`defineSkill({ title })`), looked up // the way `description` is. Read by PRESENCE — absent = not declared, and // the node carries the bytes it always did. Never the drawn `label`. const title = skill?.title; return [ { id: n.id, kind: n.kind, ...(description !== undefined && { description }), ...(n.label !== undefined && { label: n.label }), ...(title !== undefined && { title }), }, ]; }); // No drawable nodes ⇒ no authored map on the record. Never invented from // edges or skills: the graph is the only party allowed to state its shape. if (nodes.length === 0) return undefined; const edges = (graph.edges ?? []).flatMap((e) => { // An edge that does not state its own `from` and `kind` (older/structural // graphs typed only `{ to }`) cannot be reported truthfully — `from: null` // is a CLAIM (the synthetic START) and a kind is the author's word, not a // default. Skipped, never completed. if (e.from === undefined || typeof e.kind !== 'string' || typeof e.to !== 'string') return []; const guard = projectGuard(e.guard); return [ { from: e.from, to: e.to, kind: e.kind, ...(e.label !== undefined && { label: e.label }), ...(guard !== undefined && { guard }), }, ]; }); return { nodes, edges }; } function isGuardValueRecord(v) { return v === null || typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean'; } /** * Project one edge's declared guard onto the record — verbatim rows, and only * when EVERY row has the compiled shape (`{ key, op, value }`, value plain * data). A structurally-typed graph carrying anything else gets NO guard on * the record rather than a repaired one: the map carries what the author * declared, never what this file could make of it. */ function projectGuard(guard) { if (guard === undefined || !Array.isArray(guard.conditions) || guard.conditions.length === 0) { return undefined; } const conditions = []; for (const row of guard.conditions) { if (row === null || typeof row !== 'object') return undefined; const { key, op, value } = row; if (typeof key !== 'string' || typeof op !== 'string') return undefined; if (Array.isArray(value)) { if (!value.every(isGuardValueRecord)) return undefined; conditions.push({ key, op, value: [...value] }); continue; } if (!isGuardValueRecord(value)) return undefined; conditions.push({ key, op, value }); } return { conditions }; } //# sourceMappingURL=skillGraphDeclared.js.map