/** * Tool hooks of the Spotlight experiment: `read`, `bash` and `eval` pass their model-facing text through shaping * themselves, and every other tool's result passes through it when the agent's call returns * ({@link shapeCalledTool}, from the tool wrapper); each journals one `spotlight:shape` entry per result. Only the * tools `spotlight.experiment.tools` names are shaped and journaled. Shaping never fails a tool call: any error keeps * the unshaped text. * * A random share of the results shaping would cut is held out (`spotlight.experiment.holdout`): the model gets the * plain result for the call, and the trace (`holdout: true`) records the would-be cut. Of the rest, a random share * (`spotlight.experiment.scoreFree`) is shaped without Spotlight by the no-score rules (`scoreFree: true`). Every * trace sizes the plain result (`plainChars`, `plainTokens`); savings count against it. */ import type { AgentToolResult } from "@oh-my-pi/pi-agent-core"; import { type SpotlightDetails } from "@oh-my-pi/pi-tui/chat/spotlight"; import { type OutputMeta } from "@oh-my-pi/pi-tui/tools/output-meta"; import type { ToolSession } from "../tools/index.js"; import { SpotlightExperiment } from "./session.js"; import { type ShapedTool, type ShapeInput } from "./types.js"; /** One tool result to shape, and the command/code/`read ` that produced it. */ export interface ShapeRequest extends Omit { source: string; /** * The plain model-facing text for the same call, notices included (a spilled bash output's capped text, a read's * page): what savings count against. Defaults to `text`. */ plain?: string; /** Whether `plain`'s lines were cut to the sink's column cap: a short cut may then be longer than `plain`. */ plainWidthCut?: boolean; } /** Whether the experiment replaces pi's own read summarizer and bash minimizer: whenever it is on, for every tool. */ export declare function nativeShapingOff(session: Pick): boolean; /** * Whether the experiment rewrites `tool`'s output (the tools `spotlight.experiment.tools` names): a `read` without a * selector then pages the whole file, as the summarizer it replaces would have seen it, and shaping cuts it down. */ export declare function shapesOutput(session: Pick, tool: ShapedTool): boolean; /** The session's experiment when it shapes tool output, with the state shaping needs. */ export declare function shapingExperiment(session: Pick): SpotlightExperiment | undefined; /** A result's text after shaping, and the trace its details keep (`details.spotlight`). */ export interface ShapedText { text: string; /** * Set when the experiment shapes the tool, changed or not. When `text` is the unshaped input (kept, held out, * capped), the caller shows the model the plain result. */ spotlight?: SpotlightDetails; } /** * Shapes one tool result's text and journals it; the text is unchanged without the experiment, for a tool it does not * shape, when shaping keeps it, and when shaping fails. A changed text's original is saved as a session artifact the * trace points at. * * The result's coins are drawn before shaping. When the holdout coin lands and shaping changes the text, the text * stays unchanged and the journal entry and trace (`holdout: true`) describe the would-be cut, whose text is saved too * (`original.shapedUrl`); a result shaping keeps is never held out. When the score-free coin lands instead, shaping * runs without asking Spotlight and the entry and trace say `scoreFree: true`, changed or not. * * A cut saving fewer than `MIN_SAVED_TOKENS` against the plain text is never applied (nor held out, since applying it * would show the plain text too): the text stays unchanged, `steps` ends with `plain-cap`, and * `cappedChars`/`cappedTokens` size the would-be cut. A short cut of a result whose plain text was cut to the column * cap (`plainWidthCut`) is applied anyway, and its `savedTokens` is negative when it is longer than plain. Every result * the model gets plain (kept or capped) has the plain text's `outChars`/`outTokens`. */ export declare function shapeToolText(session: ToolSession, toolCallId: string, request: ShapeRequest, signal?: AbortSignal): Promise; /** Options of {@link shapeToolResult}. */ export interface ShapeResultOptions { /** A changed result loses its truncation notice, whose line ranges no longer match the text. */ dropTruncation?: boolean; /** * The result as the `plain` arm builds it, when the tool built `result` otherwise for the arm (a whole-file read * gathers past `plain`'s window): the trace sizes it as `plain`, and a result not cut (kept, held out, capped) is * this one. Without it, `result` is. */ plain?: () => Promise>; } /** * {@link shapeToolText} over a built result: shapes its text blocks (joined) and keeps its images. A result shaping * does not cut (kept, held out, capped) is `plain`'s result (`options.plain`, else `result` itself) with the trace; * savings count against that result's model-facing text, its output notice included. */ export declare function shapeToolResult(session: ToolSession, toolCallId: string, request: Omit, result: AgentToolResult, signal?: AbortSignal, options?: ShapeResultOptions): Promise>; /** One finished call the agent made to a tool that does not shape itself, as the tool wrapper hands it over. */ export interface CalledToolResult { /** The tool's name, as the agent called it. */ tool: string; toolCallId: string; /** The call's arguments. */ args: unknown; /** The tool's own result, before the wrapper spilled it to an artifact and appended its notices. */ own: AgentToolResult; /** The model-facing result the wrapper built from {@link own}: what the `plain` arm shows. */ result: AgentToolResult; signal?: AbortSignal; } /** Shapes the model-facing result of one of the agent's own tool calls; never throws. */ export type CalledToolShaper = (call: CalledToolResult) => Promise; /** * Shapes the result of a tool call the agent made, for every tool but those that shape themselves * ({@link SELF_SHAPED_TOOLS}) or are left out of `spotlight.experiment.tools`, and journals it like theirs. The * result's text blocks are shaped as command output, the source being the tool's name and arguments * ({@link callSummary}); images and other blocks stay. What `plain` shows is the wrapper's result, spill and notices * included. A result the wrapper spilled is shaped whole from the tool's own text, up to * {@link SPOTLIGHT_FULL_OUTPUT_MAX_BYTES}, and a cut of it no longer carries the spill's truncation. A result whose * details are not an object, or already carry a trace, is returned as is. Each call is shaped at most once. */ export declare function shapeCalledTool(session: ToolSession, call: CalledToolResult): Promise;