agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
77 lines (76 loc) • 3.54 kB
TypeScript
/**
* middleware/outcomes — PUBLIC. The three verbs a middleware speaks.
*
* Pattern: Smart constructors over a closed union.
* Role: core/ layer. `allow` / `deny` / `ask` are the entire vocabulary
* of both chains, which is why they are short: they are written
* inside every middleware body, several times, and a governance
* rule reads better as `deny('writes to prod need a ticket')` than
* as a namespaced ceremony.
* Emits: N/A.
*
* The `allow` overloads carry a rule: you can pass the value through
* untouched with no explanation, but you cannot CHANGE it without one. A
* transform is the only thing here that makes the trace and the wire
* disagree, so it is the only thing required to say why.
*/
import type { AllowOutcome, ArgumentOrigins, AskOutcome, AskPayload, DenyOutcome } from './types.js';
/** Pass the value through untouched. */
export declare function allow(): AllowOutcome<never>;
/**
* Pass the value through untouched, and say why you were comfortable.
*
* The row still reads `changed: false` — nothing moved — but it carries the
* reason, which is what a rule that remembers an earlier decision needs:
* "approved by dana@ops at 14:02" belongs in the record of the call it
* silently permitted, not only in the record of the call that asked.
*/
export declare function allow(value: undefined, why: string): AllowOutcome<never>;
/**
* Replace the value and say why.
*
* The `why` is mandatory and lands in the ledger next to the before/after
* pair, so a run that was scrubbed can be read back as a run that was
* scrubbed rather than as a run whose input was always that way.
*/
export declare function allow<T>(value: T, why: string): AllowOutcome<T>;
/**
* Replace the value, say why, and DECLARE where each rewritten argument's
* value came from — `'person'` (the person gave it, e.g. from a receipt of
* their answer), `'app'` (the app's own fact) or `'default'` (an app default
* nobody in the conversation chose). Recorded on the decision row. For an
* argument a tool's `askOrAssume` rules, the answer's standing reads a rewrite
* with no declared origin — or with `'default'` — as assumed.
*
* @example
* allow({ ...call.args, window: receipt.window }, 'window from the collected receipt', {
* from: { window: 'person' },
* });
*/
export declare function allow<T>(value: T, why: string, origin: {
readonly from: ArgumentOrigins;
}): AllowOutcome<T>;
/**
* Refuse the call.
*
* For a tool the reason reaches the model verbatim, as the tool's result,
* and the loop continues — the agent gets to adapt. For a message it
* surfaces as a `MessageDeniedError`.
*
* At the after-tool moment it means "the model does not get to read this" — the
* tool has already run, so the refusal replaces what the model reads while
* the run keeps the real result in the ledger. Refusing there hides an answer
* from the model; it cannot un-happen a side effect, and it does not pretend
* to.
*/
export declare function deny(reason: string): DenyOutcome;
/**
* Suspend the run and put the question to a person. Tool dispatch only —
* `MessageOutcome` has no `ask` arm, so this cannot be returned from a
* message middleware.
*
* The answer is a decision, not a result: approve and the chain continues
* and the REAL tool runs; decline and it becomes a denial the model reads.
* A middleware never gets to write the answer itself.
*/
export declare function ask(payload: AskPayload): AskOutcome;