agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
99 lines • 5.51 kB
TypeScript
/**
* skillIntent — the intent side of the turn-start routing cascade (SG-C):
* candidate projection, duplicate-example normalization, the graph's
* `TurnRoutingPlan` (what the agent's RouteTurn stage consumes), and the
* leave-one-out `checkupIntents` audit.
*
* One module owns how a declared `{ intent, examples }` becomes a scoreable
* {@link IntentCandidate}, so the router, the menu, the audit and the record
* can never project the same declaration differently. Pure over the entry
* declarations `skillGraph.ts` hands it; imports no engine machinery beyond
* the injection-context type.
*/
import { type InjectionContext } from './types.js';
import type { RouteWitness, SkillMatchData } from './skillMatch.js';
import type { IntentCandidate, IntentScorer } from './intentScorer.js';
import { type RoutingPolicy } from './routingPolicy.js';
import type { GraphCheckup, GraphProblem } from './skillGraphCheckup.js';
/** The slice of an entry declaration this module reads — structural, so
* `skillGraph.ts`'s module-private `EntryDecl` satisfies it unchanged. */
export interface IntentEntryDecl {
readonly id: string;
readonly when?: (ctx: InjectionContext) => boolean;
readonly match?: SkillMatchData;
/** The data matcher's EVIDENCE extractor (9.28.0), compiled beside `when` —
* called only on the rule that wins the turn. Absent for a `when` rule. */
readonly witness?: (ctx: InjectionContext) => RouteWitness | undefined;
}
/**
* The graph's turn-routing surface — everything the agent's RouteTurn stage
* needs from the graph, projected ONCE at build. Present on every flat graph
* (a decision `tree()` routes by predicate and has no turn start to route);
* `scorer` is present only when `.classify()` / `start.classify` configured.
*/
export interface TurnRoutingPlan {
/** The configured classifier — tier 2 over declared intents. */
readonly scorer?: IntentScorer;
/** The resolved tie policy the verdicts are judged under (recorded). */
readonly policy: RoutingPolicy;
/** Every entry id, declaration order — the `unmatched` menu. */
readonly entryIds: readonly string[];
/** Tier 1: the first NON-intent conditional entry whose predicate passes,
* declaration order — its id, plus the `witness` evidence when the rule was
* DATA and it yielded quotable text (9.28.0; a `when` rule has none).
* Unconditional entries are defaults, not rules — they never beat an
* inherited cursor. A throwing predicate is a no-match (dev-warned), the
* same law the cursor resolver applies. A rule for a skill the caller's role
* may not read (`ctx.hiddenSkillIds`) is never consulted: tier 1 obeys the
* same role filter as tiers 2 and 3. */
firstRuleMatch(ctx: InjectionContext): {
readonly id: string;
readonly witness?: RouteWitness;
} | undefined;
/** Tier-2 candidates: the declared intent entries, minus `exclude`. */
intentCandidates(exclude?: ReadonlySet<string>): readonly IntentCandidate[];
/** The incumbent as a candidate: its declared intent when it is an intent
* entry, else its description (never fabricated — `id` is a graph node). */
incumbentCandidate(id: string): IntentCandidate;
}
/** Build the plan. `describeFor` resolves a skill id to its description (the
* incumbent's fallback intent text). */
export declare function buildTurnRoutingPlan(input: {
readonly entries: readonly IntentEntryDecl[];
readonly describeFor: (id: string) => string | undefined;
readonly policy: RoutingPolicy;
readonly scorer?: IntentScorer;
}): TurnRoutingPlan;
/** Case/whitespace-normalize an example for the duplicate check — the SAME
* string under two intents is scorer luck, whatever its casing. */
export declare function normalizeExample(example: string): string;
/**
* The one PROVABLE intent-pair fact, decidable from the data alone: the same
* (normalized) example declared under two intents. Whichever wins is scorer
* luck, so it is named — both ids and the string. Everything else about two
* example sets needs the configured scorer ({@link runCheckupIntents}).
*/
export declare function findDuplicateIntentExamples(entries: readonly IntentEntryDecl[]): readonly GraphProblem[];
/**
* The leave-one-out intent audit behind `graph.checkupIntents()` — run each
* declared example through the CONFIGURED scorer (the router that will
* actually run; auditing with a different scorer would prove nothing about
* production) against all intent candidates, with the example's own intent
* represented by its REMAINING examples. An example whose top-1 is a
* different intent, or whose top-vs-own pairwise margin is inside
* `nearTieMargin`, is an `overlapping-intents` warning naming the example,
* both intents, both numbers, and the fix.
*
* Honesty boundaries, stated in the messages: only the configured scorer's
* view is checked; `when` predicates are opaque and never claimed checked;
* tier-1 regex/keyword rules that fire BEFORE tier 2 are named as unaudited
* shadowers when present. On an `llmClassifier` graph this costs one model
* call per example — the caller's docstring says so.
*/
export declare function runCheckupIntents(input: {
readonly entries: readonly IntentEntryDecl[];
readonly scorer: IntentScorer;
readonly policy: RoutingPolicy;
readonly signal?: AbortSignal;
}): Promise<GraphCheckup>;
//# sourceMappingURL=skillIntent.d.ts.map