UNPKG

agentfootprint

Version:

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

92 lines • 4.25 kB
import { defineTool } from '../tools/tools.js'; import { causeLimits } from './causeLimits.js'; import { causeGap } from './causeResponse.js'; import { loadCauseReader } from './causeLoad.js'; import { causeContextAssessment } from './causeContext.js'; const nodeId = { type: 'string', minLength: 1, maxLength: 2048, description: 'An opaque node ID returned by attention_list in THIS artifact scope, not an outer/inner stage ID.', }; const nodeQuestion = { type: 'object', properties: { nodeId }, required: ['nodeId'], additionalProperties: false, }; const CAUSE_OPERATIONS = { attention_list: { description: 'Find recorded abnormal nodes and proof IDs in this authorized graph, bounded and read-only. Start here; missing capture is unknown, never healthy.', inputSchema: { type: 'object', properties: { nodeIds: { type: 'array', items: nodeId, maxItems: 100 } }, additionalProperties: false, }, }, root_cause: { description: 'Explain one recorded node using public Foottrace cause analysis. Cite root/path/evidence IDs. Gaps mean not sure; alternatives are not proven roots. No provider internals are inferred.', inputSchema: nodeQuestion, }, impact: { description: 'Read the recorded forward impact of one node, with explicit gaps. Uses one bounded Foottrace query; it never reruns business work.', inputSchema: nodeQuestion, }, }; /** Model questions select IDs only. The host owns all resource controls. */ export function buildCauseTools(artifacts, nodes, options) { return toolsForContext({ kind: 'snapshot', artifacts, nodes }, options); } /** * Serve the three read-only cause questions over one host-authorized graph. * Saved evidence must be the exact object returned by `readStoredCauseArtifacts`; * copied display assessments do not grant saved-history authority. * No snapshot is indexed, no storage is fetched and no business work is rerun. */ export function traceCauseTools(cause, options) { return toolsForContext({ kind: 'graph', cause }, options); } function toolsForContext(context, options) { return Object.keys(CAUSE_OPERATIONS).map((operation) => defineTool({ name: operation, ...CAUSE_OPERATIONS[operation], execute: async (question, execution) => { const assessed = causeContextAssessment(context); if (assessed.kind === 'invalid') return causeGap(operation, 'invalid-data'); const capture = assessed.kind === 'stored' ? assessed.capture : context.kind === 'snapshot' && context.artifacts.cause === undefined ? true : undefined; const storage = assessed.kind === 'stored' ? assessed.admission.storage : undefined; if (execution.signal?.aborted && storage === undefined) return causeGap(operation, 'cancelled', capture); let limits; try { limits = causeLimits(options); } catch { return causeGap(operation, 'invalid-data', capture, storage, 512); } if (execution.signal?.aborted) return causeGap(operation, 'cancelled', capture, storage, limits.maxChars); const deadline = performance.now() + limits.timeoutMs; try { const loaded = await loadCauseReader(deadline, execution.signal); if ('gap' in loaded) return causeGap(operation, loaded.gap, capture, storage, limits.maxChars); return await loaded.reader.readCause(operation, context, question, limits, deadline, execution.signal); } catch { // Includes bounded loader refusals. Never echo an exception or disclose a private path. return causeGap(operation, execution.signal?.aborted ? 'cancelled' : performance.now() >= deadline ? 'timeout' : 'unavailable', capture, storage, limits.maxChars); } }, })); } //# sourceMappingURL=causeTools.js.map