agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
85 lines (84 loc) • 3.54 kB
TypeScript
/**
* 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()}`,
* });
*/
import type { WireRole } from '../../provider-contract/types.js';
import type { Injection, InjectionContext } from '../types.js';
import type { CachePolicy } from '../../cache/types.js';
interface DefineFactBase {
readonly id: string;
readonly description?: string;
/** The fact (data string) to inject. */
readonly data: string;
/**
* Trigger control. Defaults to always-on. For conditional facts
* (e.g., "only show user profile after iteration 3"), pass a
* predicate via `activeWhen`.
*/
readonly activeWhen?: (ctx: InjectionContext) => boolean;
/**
* Cache policy for this fact injection. Defaults to `'always'` —
* facts are typically static data the LLM should always have in mind.
* Override with `'never'` for facts containing volatile content
* (e.g., a `Current time:` fact); use `{ until }` for time-bounded
* facts.
*/
readonly cache?: CachePolicy;
}
/**
* `slot` and `role` are ONE decision, so the type makes them one choice.
*
* `slot: 'messages'` requires a `role` and there is deliberately no default:
* who appears to speak is a meaning the app owns. (Before 7.19.1 the option
* existed with `role` defaulting to `'system'` — a default that reached the
* model on OpenAI-family providers and vanished on Anthropic-family ones.
* Now the role is stated, and a provider that cannot carry it refuses at run
* start by name.) `slot: 'system-prompt'` forbids `role`, because there is no
* role in a system prompt to choose.
*/
export type DefineFactOptions = DefineFactBase & ({
/** The default. The one placement every provider delivers. */
readonly slot?: 'system-prompt';
readonly role?: never;
} | {
/**
* Deliver the fact into the conversation itself — it enters
* `scope.history` at the injection-engine boundary, so the window
* strategies, the slots and the wire all see the same past.
* Placed at the END of the window; if its role would collide with
* the turn already there, delivery defers to the next boundary and
* says so on `messagesDelivery.deferred`.
*/
readonly slot: 'messages';
/**
* Who appears to say it. Required — no default. Refused at run start
* when the attached provider does not carry it inside `messages`
* (the Anthropic family drops `'system'` there; the OpenAI family
* carries it). `'tool'` is refused outright: a tool message answers a
* specific call, and an injection has no call to answer.
*/
readonly role: WireRole;
});
export declare function defineFact(opts: DefineFactOptions): Injection;
export {};