agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
148 lines • 7.46 kB
TypeScript
/**
* time/bind — which of the person's windows a tool call carries (time
* design § 5.6, § 7.3): fill from one mention, bind by quote, and record a
* differing model window and run it.
*
* Pattern: Walker over recorded rows. The turn's WINDOWS are read from what
* the record already holds — this turn's `time-answer` rows (a
* mention the person settled in the time ask), a `model` reader's
* `time-reading` rows (a reading, never the person's) and the
* `clock` row's `control` window — never from words. A `rule`
* reading settles nothing: it is a proposal the person confirms
* (the owner's decision "Always confirm", time design TQ29). One call's decision
* ({@link callWindowOf}) is a pure function of its arguments, the
* tool's forms and those windows.
* Role: L0 leaf (the time layer). Imports `convert.ts`, `range.ts`,
* `rows.ts`, `resolve.ts`' types and `zone.ts` only. Asked by the
* inputs layer (`arguments/resolve.ts`), which files the argument
* rows and the `call-window` row the decision implies.
* Emits: N/A.
*
* ## The laws
*
* - **Fill only when the turn has exactly one window.** A message holding two
* mentions ("this morning vs yesterday morning") fills nothing: which one a
* call means is the model's to say, through the quote. A mention whose
* reading is still open (the person has not chosen) is counted and fills
* nothing; a refused mention (its quote was not in the message) is not the
* person's words and is not counted. A `control` window (`time.window`, set
* in a UI) counts as one more mention with no quote.
* - **Fill only what the model left out — exactly, else wider, never
* narrower.** Every argument of every form must be missing; the window goes
* into the first form that holds it exactly (`convert.ts` · `convertExact`),
* else the first that holds MORE (`convertWidened`: a whole day, the
* covering look-back — recorded with what it adds). No such form → the call
* is REFUSED, `no-form-holds` (`convert.ts` · `convertForTool`, the one
* answer): the tool's own rule never stands in for the person's window, so
* an assumed `1h` never silently reads a time the person did not ask about.
* - **Refuse before dispatch what the tool cannot honestly read (step
* T5b).** A window — the person's to fill, or the model's sent — outside
* the tool's declared `direction`, wholly beyond its `retention` or wider
* than its `maxRange`; a range across days for a tool whose every form is a
* `day`; a window of the person's no form can read (`no-form-holds`); a
* sent wall time the zone skips. A window only partly beyond `retention`
* runs, marked. An open reading the tool can read in NO reading is refused
* too, and never asked.
* - **A present window is never written over.** Bound by quote (the model's
* declared `user` quote names a recorded mention) or by value (the sent
* value, read back, IS a window of this turn); otherwise it runs as sent and
* is recorded `model-chosen` beside the person's window — a drill-down, a
* comparison and a baseline call are normal work.
*/
import { type Conversion, type PeriodFacts, type PeriodForm, type TimeRefusal, type WidenedConversion } from './convert.js';
import type { InstantText } from './instant.js';
import type { TimeRange } from './range.js';
import type { ZoneName } from './zone.js';
export { isOpenForPerson, offeredLookback, pendingQuotesOf, readerWindowsOf, turnWindowsOf, windowToConvert, type ReaderWindows, type RefusedWindow, type TurnWindow, type TurnWindows, type WindowSource, } from './windows.js';
import { type TurnWindow, type TurnWindows } from './windows.js';
/** What the inputs layer knows about one call to a tool that declares period forms. */
export interface CallToBind {
readonly args: Readonly<Record<string, unknown>>;
readonly forms: readonly PeriodForm[];
/** The argument is missing from the call (`tools/declare.ts` · `isMissing`). */
readonly isMissing: (argument: string) => boolean;
/**
* Under declared sources: the quotes the model declared (`source: 'user'`)
* for this call's period arguments. Absent when the arm is off.
*/
readonly quotes?: readonly string[];
/**
* The tool's declared facts (`direction`, `retention`, `maxRange`,
* `filtersToAsked`) — a call whose window breaks one is refused before
* dispatch (step T5b). Absent: nothing is checked.
*/
readonly facts?: PeriodFacts;
}
/** The clock and zones a decision reads. */
export interface BindContext {
readonly now: InstantText;
readonly zone: ZoneName;
readonly appZone?: ZoneName;
readonly granularityMs: number;
}
/** The decision for one call — see {@link callWindowOf}. */
export type CallWindow =
/** The model left every period argument out and the turn's one window went into `form` exactly. */
{
readonly how: 'filled';
readonly window: TurnWindow;
/** Exact, or — when no form holds the window exactly — WIDENED (it carries `sent` and `extra`). */
readonly conversion: Conversion | WidenedConversion;
/** Widened, and the tool declares `filtersToAsked`: it trims its rows to the asked window. */
readonly trimmedByTool?: true;
readonly partlyBeyondRetention?: true;
}
/** The sent window IS one of the turn's windows — named by the model's quote (`quote`) or equal in value (`value`). */
| {
readonly how: 'bound';
readonly form: number;
readonly asked: TimeRange;
readonly window: TurnWindow;
readonly by: 'quote' | 'value';
readonly partlyBeyondRetention?: true;
}
/** The sent window differs from the person's: it runs as sent (§ 7.3, the v1 law). `person` when one window is the person's. */
| {
readonly how: 'model-chosen';
readonly form: number;
readonly asked: TimeRange;
readonly person?: TurnWindow;
readonly partlyBeyondRetention?: true;
}
/** The sent window, and no window of the person's to compare it with. */
| {
readonly how: 'model';
readonly form: number;
readonly asked: TimeRange;
readonly partlyBeyondRetention?: true;
}
/**
* Refused before dispatch (step T5b): the window breaks one of the tool's
* facts, spans days for a tool that reads one, or a sent wall time is one
* the zone skips (`argument` names it). `asked` and `person` when known.
*/
| {
readonly how: 'refused';
readonly refused: TimeRefusal;
readonly form?: number;
readonly asked?: TimeRange;
readonly person?: TurnWindow;
readonly argument?: string;
}
/** A period argument was sent, and no form reads the call's arguments back as a range. */
| {
readonly how: 'unread';
}
/** The model left the period out and nothing was filled — why. */
| {
readonly how: 'not-filled';
readonly why: 'no-window' | 'several-mentions' | 'open-reading';
};
/**
* Which window one call carries (the module laws). Pure: the same call, forms
* and windows always give the same decision.
*/
export declare function callWindowOf(call: CallToBind, turn: TurnWindows, ctx: BindContext): CallWindow;
/** The range a decision says the call asks for — the person's on a fill, the sent one otherwise. */
export declare function askedRangeOf(decision: CallWindow): TimeRange | undefined;
//# sourceMappingURL=bind.d.ts.map