agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
86 lines (85 loc) • 4.4 kB
TypeScript
/**
* Structural lint rules (RFC-002 block C2) — the PLUGGABLE RULE PACK.
*
* Pattern: Strategy list — each rule is a plain `{ id, check }` object;
* `defaultStructuralRules` is OUR pack, and consumers add /
* remove / replace freely via `AnalyzeToolCatalogOptions.rules`.
* Parameterizable rules ship as FACTORIES (`descriptionRule`,
* `saysWhatNotWhenRule`, …) returning a configured `LintRule`.
* Role: `src/tool-lint/` leaf. Pure functions over `CatalogTool`;
* no embedder, no I/O.
*
* Every rule encodes a FIELD FINDING from real catalogs (the host-app SAN
* triage agent's 29-tool catalog was the seed corpus):
*
* 1. description-missing-or-short — the model can only guess from a name.
* 2. says-what-not-when — describes WHAT the tool returns but gives the
* model no cue for WHEN to pick it over a sibling (the #1 cause of
* twin-tool confusion: 'get_fcns_database' vs 'influx_get_fcns_database').
* 3. enum-in-prose — string params whose legal values are listed in prose
* ("avg_iops | peak_iops | mbps") instead of a JSON-Schema `enum` the
* model (and validators, see #9 tool-args validation) can act on.
* 4. optional-param-undocumented — optional params whose omission has
* meaning (fabric-wide sweep vs one switch) but whose schema never
* says so; the model can't reason about leaving them out.
*
* Honest claim: these are token/regex HEURISTICS. They flag review
* prompts, not certainties — expect (rare) false positives and tune via
* the factory options instead of deleting the rule.
*/
import type { LintRule } from './types.js';
export interface DescriptionRuleOptions {
/** Descriptions shorter than this (in chars) get a `warn`. Default 40. */
readonly minChars?: number;
}
/**
* Missing description → `error` (the model can only guess from the
* name). Present but shorter than `minChars` → `warn` (too short to
* differentiate from siblings).
*/
export declare function descriptionRule(options?: DescriptionRuleOptions): LintRule;
/** RFC-002 C2 heuristic cue list — temporal/conditional words whose
* presence suggests the description says WHEN to use the tool. */
export declare const DEFAULT_WHEN_CUES: readonly string[];
export interface SaysWhatNotWhenRuleOptions {
/** Cue tokens (whole-word, case-insensitive). Default `DEFAULT_WHEN_CUES`. */
readonly cueTokens?: readonly string[];
}
/**
* A description with NO temporal/conditional cue token usually describes
* WHAT the tool returns but never WHEN to pick it — the #1 cause of
* twin-tool confusion. Heuristic by design: tune `cueTokens` rather than
* dropping the rule. Skips tools with no description (rule 1's finding).
*/
export declare function saysWhatNotWhenRule(options?: SaysWhatNotWhenRuleOptions): LintRule;
/**
* A string param whose description enumerates its legal values in prose
* (pipe-separated literals, or comma lists behind "one of"/"allowed
* values") should declare a JSON-Schema `enum` instead — the model picks
* reliably from enums, and arg validators (#9) can enforce them. The
* field case: the host app's `influx_get_port_ranking.metric` =
* `"avg_iops | peak_iops | mbps"`.
*/
export declare function enumInProseRule(): LintRule;
/** Words that signal the description DOES say what omission means. */
export declare const DEFAULT_OMISSION_CUES: readonly string[];
export interface OptionalParamRuleOptions {
/** Cue tokens that satisfy the rule. Default `DEFAULT_OMISSION_CUES`. */
readonly omissionCues?: readonly string[];
}
/**
* An optional param's omission usually MEANS something (host app:
* `influx_get_interface_counters` without `switch_name` = fabric-wide
* sweep) — but the model can only reason about leaving a param out if
* the description says so. No description at all, or one with no
* omission cue, gets a `warn`.
*/
export declare function optionalParamRule(options?: OptionalParamRuleOptions): LintRule;
/**
* OUR rule pack, built with default options. Compose your own:
*
* rules: [...defaultStructuralRules, myRule] // add
* rules: defaultStructuralRules.filter(r => r.id !== '…') // remove
* rules: [descriptionRule({ minChars: 80 }), …] // re-tune
*/
export declare const defaultStructuralRules: readonly LintRule[];