/** * artifacts/placement — the placement threshold (Phase 2, Leg 3). * * An OPTIONAL operator dial on the agent's artifacts wiring: * `Agent.create({ artifacts: { store, placement: { maxInlineChars: N } } })`. * A tool result whose finalized text exceeds the threshold is checked into * the store (kind `tool-result/`, or the tool's declared * `resultKind` — see `placedResultKind`) and the model receives the claim * ticket instead — a SHORT substitute naming ref + meta + how to consume it. * The whole 879,073-token failure class, retired by configuration. * * PRECEDENCE with the other two ceilings, stated once and owned here: * 1. the tool's own `resultCeiling` (the AUTHOR's refusal) is judged FIRST, * at the execute boundary — a refused result is a short teaching * sentence, so placement never sees the payload at all; * 2. placement (the OPERATOR's ref-ing) is judged next, on what the * after-tool governance chain let through — a rule that already * summarized a huge result is measured on what it produced; * 3. the agent-level `maxToolResultChars` truncation net runs LAST — with * placement on it measures the ticket, so it should now rarely fire. * * A placed result is a TICKET, not a refusal: declared effects are still * judged and procedure steps advance normally — the result ARRIVED; it just * travels by reference. The decision is never silent: the mint lands as * `agentfootprint.artifacts.minted` (origin joining it to the tool call) and * the substitution states itself in the result the model reads. * * This module is the PURE half — the dial shape, its build-time refusals, * the measure rule (shared with both ceilings: strings as-is, everything * else `safeStringify`d by the caller), and the substitute composer. The * dispatch layer owns the mint and the events. */ import type { ArtifactMeta } from './types.js'; /** The dial. One field today; a plain object so field number two (a * per-kind override, say) can arrive additively. */ export interface ArtifactPlacement { /** * The threshold, in characters of the finalized result text. Over it, the * result is checked into the store and the model reads the claim ticket. * Positive whole number; anything else is refused at build. * * **It moves what routing predicates read.** The substitute replaces the * result string everywhere downstream — history, `lastToolResult`, * `toolResults` — which is where skill-graph `when` edges and `rule` * triggers look. Turning placement on, or changing this number, can * therefore change which edge fires for a graph that matches on result * TEXT. That is the layering (routing judges what the model was told, not a * string the conversation never contained), stated here because it is not * guessable from a number: an edge that must survive the dial should key on * the tool name (`onToolReturn`) or a declared `status` (`onToolStatus`). */ readonly maxInlineChars: number; } /** * Refuse a placement dial this library cannot honor, where it is configured * — never at the first oversized result of the first run. */ export declare function assertArtifactPlacement(site: string, placement: ArtifactPlacement | undefined): void; /** * The kind vocabulary a placement mint declares — THE one decision, and the * only place it is made. * * Default: `tool-result/`. Honest — it says exactly what the payload * is and which tool produced it — and it is what a `wants` declaration or a * `present` call names to consume the placed result. * * `declared` is the tool's own `Tool.resultKind` (9.70.0), and when a tool * declares one it WINS. The reason is the exact-match law on the consuming * end: `wants` matches kinds by exact string equality — no wildcards, no * hierarchy — so the framework's default vocabulary is a ticket a * `wants: { dataset: 'dataset/rows' }` argument must refuse. Rather than * loosen the matcher (a ticket would stop being a promise) or make consumers * re-mint at the seam (the framework declining to carry its own ref), the * MINT speaks the author's vocabulary. Absent → today's bytes exactly. */ export declare function placedResultKind(toolName: string, declared?: string): string; /** * The substitute the model reads in place of the payload — ONE shape, always * the object (the `TruncatedToolResult` law: a consumer branches on * `.placed` without parsing prose, and the model reads it as JSON on the * `role: 'tool'` message). */ export interface PlacedToolResult { /** Always `true`. The field a consumer branches on. */ readonly placed: true; readonly ref: string; /** The minted kind — what a consumer names to want it. The tool's declared * `Tool.resultKind` when it has one, `tool-result/` otherwise. */ readonly kind: string; readonly mediaType: string; /** The stored payload's true size — the chars the window did NOT pay. */ readonly bytes: number; /** What happened and what to do next: route the ref, never retype. */ readonly reason: string; } /** Type guard for consumers reading `tool_end.result` or a tool message. */ export declare function isPlacedToolResult(value: unknown): value is PlacedToolResult; /** Compose the ticket from the mint the dispatch layer performed. */ export declare function placedToolResult(toolName: string, meta: ArtifactMeta, sizeChars: number, maxInlineChars: number): PlacedToolResult; //# sourceMappingURL=placement.d.ts.map