UNPKG

agentfootprint

Version:

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

75 lines • 3.42 kB
"use strict"; /** * defineFact — sugar for context-style Injections (data, not behavior). * * Use for developer-supplied facts the LLM should see in addition to * user messages and tool results. Examples: user profile, env info, * computed conversation summary, cached config, current time. Distinct * from Skills (LLM-activated guidance) and Steering (always-on rules) * in INTENT — they share the engine. * * Produces an `Injection` with: * - flavor: `'fact'` * - trigger: configurable (default `'always'`) * - inject: `systemPrompt` (default), or `messages` with a declared role * * @example * const userProfile = defineFact({ * id: 'user-profile', * data: `Name: ${user.name}, Plan: ${user.plan}, Joined: ${user.joinedAt}`, * }); * * const turnTime = defineFact({ * id: 'turn-time', * data: `Current time: ${new Date().toISOString()}`, * }); */ Object.defineProperty(exports, "__esModule", { value: true }); exports.defineFact = void 0; const messagesSlotRefusal_js_1 = require("../messagesSlotRefusal.js"); const applyCachePolicy_js_1 = require("../../cache/applyCachePolicy.js"); function defineFact(opts) { if (!opts.id || opts.id.trim().length === 0) { throw new Error('defineFact: `id` is required and must be non-empty.'); } if (!opts.data || opts.data.length === 0) { throw new Error(`defineFact(${opts.id}): \`data\` is required.`); } // The runtime guards read a WIDENED view on purpose. The declared type // already pairs `slot` with `role` correctly, and TypeScript narrows so well // that `opts.role` is provably present inside a `slot === 'messages'` branch // — which makes the checks below unreachable *to the compiler* and therefore // invisible to the JavaScript callers and `as never` casts they exist for. A // refusal that only holds at compile time is not a refusal. const raw = opts; const toMessages = raw.slot === 'messages'; if (toMessages && !raw.role) { throw new Error(`defineFact('${opts.id}'): \`slot: 'messages'\` requires a \`role\` — ` + `'user', 'assistant', or 'system' on a provider that carries it. There is no ` + `default: who appears to speak is a meaning your app owns, not one the library picks.`); } if (toMessages && raw.role === 'tool') { throw new Error((0, messagesSlotRefusal_js_1.messagesToolRoleRefusal)(`defineFact('${opts.id}')`)); } const trigger = opts.activeWhen ? { kind: 'rule', activeWhen: opts.activeWhen } : { kind: 'always' }; const inject = toMessages ? { messages: [{ role: raw.role, content: opts.data }] } : { systemPrompt: opts.data }; const cacheMetadata = (0, applyCachePolicy_js_1.cachePolicyMetadata)('fact', opts.cache); // Two-stage cast (`as unknown as Injection`) is required because // `flavor: 'fact'` narrows tighter than `ContextSource`. Both stages // are type-safe at the call site — `flavor` IS a valid `ContextSource` // member; TypeScript just can't narrow back through the freeze. return Object.freeze({ id: opts.id, ...(opts.description && { description: opts.description }), flavor: 'fact', trigger, inject, metadata: Object.freeze(cacheMetadata), }); } exports.defineFact = defineFact; //# sourceMappingURL=defineFact.js.map