agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
216 lines (215 loc) • 10.4 kB
TypeScript
/**
* auditBounding — HOW the audit chain's bounded mode bounds a value: the
* markers, the sanitizer, the leaves and the combinators every shape is built
* from (`auditShapes.ts` says WHAT each event keeps; `auditKeptByContract.ts`
* names what a record keeps although it may carry a run's value).
*
* Pattern: small pure rules composed into shapes; every rule records what it is
* (`ruleKind`), so a test can walk a shape beside the type it bounds
* and hand every rule what its kind forbids.
* Role: the bounding layer `auditExport({ payloadMode: 'bounded' })` runs
* every payload and every record's meta through (`audit.ts`). No I/O,
* no clock.
* Emits: N/A.
*
* THE RULE, the one `otel.ts` maps spans by: **structure is kept, content is
* a marker**, bounding is CLOSED by default, and every kept value is CHECKED:
*
* - Every registered event has a shape that names every field of its
* payload type (`shapeOf` is exhaustive by type: a field added to a
* payload does not compile until it is classified).
* - A field a shape does not name, a value that is not the declared shape,
* and every field of an event type the registry does not declare, is a
* marker.
* - A type says what a field holds; it cannot say what ARRIVES. So nothing is
* kept on a type's word: a kept leaf checks at runtime the kind its type
* promises — `num` a number, `bool` a boolean, `str` a string, `oneOf` one
* of the words it lists — and anything else is a marker. No container is
* kept whole: an object is a `shapeOf` (or a `recordOf`), a list a
* `listOf`, every child ruled, so a child the type does not declare is a
* marker however it got there (structural typing lets an object carry one).
* The type walker (`test/type-regressions/AuditBoundingDepth.test.ts`)
* holds every rule to the type beneath it; the runtime sweep
* (`test/observability-providers/audit-bounded-sweep.test.ts`) hands every
* rule what its kind forbids.
* - Names are data. A key is kept as an own key of the record, whatever it is
* (a name `Object.prototype` holds included: an assignment would drop
* `__proto__`, or re-prototype the record), and no rule table is ever asked
* for a name it merely inherits.
*
* Content becomes `'[N chars]'` (a string), `'[type: …]'` (anything else),
* `'[keys: …]'` (a free-form record: its key names, never its values), or a
* count (`'[N messages]'`, `'[N blocks]'`, `'[N summaries]'`). A kept string is
* capped at 256 characters, a list at 32 items.
*/
/**
* Convert any value to a JSON-safe, size-bounded tree:
* strings capped, arrays capped with an overflow marker, cycles
* broken, depth capped, Date → ISO, bigint → string, non-finite →
* null, functions/symbols/undefined dropped (objects) or null
* (arrays). Every key an own key. Output always canonicalizes without
* throwing.
*/
export declare function sanitizeJson(value: unknown, depth?: number, seen?: Set<object>): unknown;
/** How the bounded mode writes one value. */
export type BoundRule = (value: unknown) => unknown;
/** The kind of a value, as a rule checks it at runtime. */
export type ValueKind = 'string' | 'number' | 'boolean' | 'null' | 'array' | 'object';
/** A value's kind — `undefined` for one JSON has no word for (a function, a symbol, a bigint). */
export declare function valueKindOf(value: unknown): ValueKind | undefined;
/**
* What a rule is, so a test can walk a shape beside the type it bounds and
* hand each rule what its kind forbids. Every rule this module builds has one;
* a function it did not build has none, and the walkers refuse it.
*/
export type RuleKind =
/** Keeps nothing of the value: its length, its key names, its type or its count. */
{
readonly kind: 'marker';
readonly marks: 'chars' | 'keys' | 'type' | 'count';
}
/** Keeps a value of this one kind (a string capped); anything else is a marker. */
| {
readonly kind: 'leaf';
readonly accepts: 'number' | 'boolean' | 'string';
}
/** Keeps a value that is one of these words; anything else is a marker. */
| {
readonly kind: 'oneOf';
readonly values: ReadonlySet<string | number | boolean>;
}
/** An object: each named field by its rule, any other field a marker. */
| {
readonly kind: 'shape';
readonly fields: ReadonlyMap<string, BoundRule>;
}
/** A list: each item by the rule. */
| {
readonly kind: 'list';
readonly item: BoundRule;
}
/** An object with open keys: every key kept, every value by the rule. */
| {
readonly kind: 'record';
readonly value: BoundRule;
}
/** One value or a list of them, each by the rule. */
| {
readonly kind: 'orList';
readonly item: BoundRule;
}
/** `null` kept; anything else by the rule. */
| {
readonly kind: 'nullOr';
readonly inner: BoundRule;
}
/** The first alternative that accepts the value's kind; none, a marker. */
| {
readonly kind: 'either';
readonly alternatives: readonly BoundRule[];
}
/** One rule or another, by a test of the value — the reason written down. */
| {
readonly kind: 'when';
readonly why: string;
readonly then: BoundRule;
readonly otherwise: BoundRule;
}
/** A position the record keeps although it may carry a run's value — named (`auditKeptByContract.ts`). */
| {
readonly kind: 'byContract';
readonly exception: string;
readonly inner: BoundRule;
};
/** What a rule is — `undefined` only for a function this module did not build. */
export declare function ruleKind(rule: BoundRule): RuleKind | undefined;
/**
* The value kinds a rule KEEPS something of at its own position — what
* `either` dispatches on. A marker accepts none: it keeps nothing of any value.
*
* @internal
*/
export declare function acceptedKinds(rule: BoundRule): ReadonlySet<ValueKind>;
export declare function typeofOf(value: unknown): string;
/** `'[23 chars]'` — length evidence without content. */
export declare function charsMarker(value: unknown): string;
/** `'[keys: a, b, c]'` — shape evidence for free-form Records. */
export declare function keysMarker(value: unknown): string;
/** `'[type: object]'` — what kind of value, nothing of it (a tool's result). */
export declare const typeMarker: BoundRule;
/** `'[3 messages]'` — a conversation's history. */
export declare const messagesMarker: BoundRule;
/** `'[2 blocks]'` — the model's thinking blocks. */
export declare const blocksMarker: BoundRule;
/** `'[4 summaries]'` — the previews of what a slot dropped. */
export declare const summariesMarker: BoundRule;
/** A count, a duration, a score, an index: kept when it IS a number (non-finite as `null`). */
export declare const num: BoundRule;
/** A flag or a verdict: kept when it IS a boolean. */
export declare const bool: BoundRule;
/**
* The record's own word — an id, a name, a code, a label the app declared: kept
* when it IS a string, capped. Where the type is a closed union of words, use
* `oneOf`, which keeps only those words.
*/
export declare const str: BoundRule;
/**
* One of the listed words (or numbers, or booleans): kept when the value IS one
* of them — so what it keeps is always a word this table wrote. Anything else
* is a marker.
*/
export declare function oneOf(...values: readonly (string | number | boolean)[]): BoundRule;
/**
* Every key of `T` — of EVERY member when `T` is a union. `keyof` of a union is
* only the keys all members share, so a field one member alone carries would
* escape the shape's exhaustiveness.
*/
export type AnyKeyOf<T> = T extends unknown ? keyof T : never;
/**
* An object of a known shape, every field ruled (exhaustive by type, so a field
* added to `T` does not compile until it is). A field the rules do not name is
* a marker: whatever the value carries at runtime is bounded until someone
* decides it is the record's own words. Anything but an object is a marker too.
*/
export declare function shapeOf<T>(rules: {
readonly [K in AnyKeyOf<T>]-?: BoundRule;
}): BoundRule;
/** A list: each entry by `rule`, capped at 32 like every list. Anything else is a marker. */
export declare function listOf(rule: BoundRule): BoundRule;
/** A map with open keys: every key kept (a key is a name), every value by `rule`. Anything else is a marker. */
export declare function recordOf(rule: BoundRule): BoundRule;
/** One value, or a list of them, each by `rule` — a guard's declared value is either. */
export declare function orList(rule: BoundRule): BoundRule;
/** `null` stays `null` (a declared "none"); anything else by `rule`. */
export declare function nullOr(rule: BoundRule): BoundRule;
/**
* A value of one of several kinds — a library word or a window, a facet word or
* a filter: the first alternative that accepts the value's kind bounds it. A
* value no alternative accepts is a marker.
*/
export declare function either(...alternatives: readonly BoundRule[]): BoundRule;
/**
* One rule or another, by a test of the value itself, the reason written down —
* the few places a field is structure only for some values (a name this
* strategy has seen run, the engine's own decision evidence).
*/
export declare function when(why: string, test: (value: unknown) => boolean, then: BoundRule, otherwise: BoundRule): BoundRule;
/**
* A position a bounded record keeps although its value may come from the run —
* one of the exceptions `auditKeptByContract.ts` names, in one place. The rule
* inside still checks the value like every other: an exception keeps a value of
* its declared kind, never whatever arrives.
*/
export declare function byContract(exception: string, rule: BoundRule): BoundRule;
/** An event type the registry does not declare (a consumer's own `emit`): every field a marker. */
export declare const unknownEventShape: BoundRule;
/**
* Tool names this strategy has seen RUN — a `stream.tool_end` without `error`,
* `notExecuted` or `notDispatched`, matched to its `tool_start` by
* `toolCallId` — so a tool this agent holds, by construction. What a
* `tryInsteadTool` name is recognized against (`otel.ts` keeps the same set).
*/
export declare function createToolRunTracker(): {
observe(type: string, payload: unknown): void;
ran(name: string): boolean;
};