/** * runbookAsTool — turn a written operational procedure into a tool whose * every answer is EVIDENCE: who decided what, against which threshold, on * which rule version, with the recorded walk to check it against. * * The standard bridge from a footprintjs chart to the Agent's tool surface. * Where `flowchartAsTool` returns bare `JSON.stringify(values)`, * runbookAsTool supplies the missing middle — the honesty envelope every * hand-rolled triage tool was building for itself: * * THE MANDATORY SPINE (every runbook, whatever its shape): * - `af_coverage` — the three-list ledger, with every INNER tool's own * ledger folded upward through the run's dispatch, plus the sentence * naming the rule set and version; * - `result.af_provenance` — re-emitted FIRST (a carried `LOCAL SEED` * confession survives composition); * - `result.rule_version` — the declared rules' version, or the honest * `'undeclared'`; * - `result.walk` — the recorded walk's descriptor. The walk itself * ships as an artifact ticket (kind `recording/chart-walk`), never as * bytes; when it does not fit, the CONTROL FLOW survives and the * projection is declared. Opt into `walk: { recording: true }` and the * inner chart's own `{ snapshot, events, structure }` is filed beside * it (kind `recording/run`) and its ref rides the SAME descriptor as * `walk.recording_ref` — the row projection cannot be drawn, and this * is what makes the walk mountable as the flowchart it ran. * * THE OPTIONAL PROJECTION (selected by `resultKind: 'verdict/*'`): * verdict rows off the chart's `verdicts` state key, capped with * truthful counters, a pre-rendered table over the SAME rows, and * `verdict_meanings` GENERATED from the decider's declared branches + * the rule labels this run's decide() evidence carried. * * WHO RENDERS THE ROWSET (`presentation`, default `'prose'`): the bridge * cannot see which client it is in, so the caller says. Under `'prose'` * the model's words are the rowset's only surface — the table ships * pre-rendered and is output verbatim. Under `'panel'` the host draws * the rowset itself — no table ships, and the note says not to * reproduce rows the reader is already looking at. * * THREE OUTCOMES, honestly: a clean envelope; an inner ABSENCE passed * through verbatim (the framework still reads it as an absence); and * DECLINED rows counted into the ledger as not-checked ground. * * The procedure is a FACTORY invoked per call with the run's own tool * dispatch (`ctx.tools`) — fresh chart every run, stages close over the * dispatch, and every inner tool's honesty ledger folds into this tool's * answer. * * Reserved state keys the bridge reads off the final scope: * - `verdicts` — the rowset (verdict projection only); * - `coverage` — chart-declared coverage entries (`{checked?, not_checked?, * cannot_cover?}`); * - `report` — the app's own result fields, spread into `result` verbatim * BESIDE the spine, never over it: the envelope's own names (the spine * plus the projection this run assembled) are reserved, and a report * field spelling one of them is discarded and NAMED in `report_note`. * * Pause: NOT yet bridged. A paused inner chart throws with the checkpoint * attached, exactly like `flowchartAsTool` — the approval-gate integration * is the next phase of the runbook program. * * @example the smallest legal call — still yields the honest spine * const tool = runbookAsTool({ * name: 'restart_check', * description: 'Run the restart-safety procedure.', * procedure: () => * flowChart<{ safe: boolean }>('restart-safety', (scope) => { * scope.safe = true; * }, 'check').build(), * }); * * @example a triage runbook with rules, verdicts, and an inner tool * const triage = runbookAsTool({ * name: 'backup_triage', * description: 'Assess backup protection posture for every subject.', * resultKind: 'verdict/backup-posture', * rules: { name: 'health-signal', version: 'v1' }, * verdicts: { decider: 'posture' }, * composedOf: ['backup_inventory'], * procedure: (tools) => * flowChart('backup-triage', async (scope) => { * const inner = (await tools.call('backup_inventory', {})) as InventoryResult; * scope.subjects = inner.result.rows; * }, 'inventory') * .addDeciderFunction('Posture', postureDecider, 'posture') * .addFunctionBranch('protected', 'Protected', landProtected) * .addFunctionBranch('unprotected', 'Unprotected', landUnprotected) * .end() * .build(), * }); */ import { type Tool } from '../tools.js'; import type { RunbookAsToolOptions } from './types.js'; /** * Wrap a footprintjs procedure as a `Tool` whose every answer carries the * honesty spine. See the module header for the envelope; see * {@link RunbookAsToolOptions} for the full options bag. The smallest legal * call is `{ name, description, procedure }`. */ export declare function runbookAsTool(opts: RunbookAsToolOptions): Tool;