import "./agent_run.css"; import type * as React from "react"; import { Collapsible } from "@base-ui/react/collapsible"; import { useRender } from "@base-ui/react/use-render"; import { type IconName } from "./icon"; import { type StyleProps } from "./style_props"; import { type AgentUIPart, type AgentStep, type AgentStepStatus } from "./agent_transform"; /** A resolved tool call as a `labelForCall` sees it: the RAW tool name, the * input it was invoked with, and its settle state. */ export interface AgentToolCall { toolName: string; input: unknown; state: AgentStepStatus; } /** A settled tool call as `renderToolOutput` sees it — only an output-carrying * step reaches the renderer. */ export interface AgentToolOutput { toolName: string; input: unknown; output: unknown; } /** THE TIMELINE, in place — the default the other two presentations hold. */ export interface AgentRunFeedProps extends StyleProps { variant?: "feed"; /** The agent message's ai-sdk `parts` — the ordered transcript. Chat passes * `message.parts`; an app passes `useAgentRun().parts`. */ parts: readonly AgentUIPart[]; /** Whole-run state. Defaults to `streaming` while any tool step is running. */ state?: "streaming" | "done" | "error"; /** A run-level BREAKING error that terminated the run — it lives OUTSIDE `parts` * (`useAgentRun().error`). A per-tool failure rides in that tool's own * `output-error` part. */ error?: string; /** Localize / override a TOOL step's display label from the CALL. Return * `undefined` to fall back to the built-in label. */ labelForCall?: (call: AgentToolCall) => string | undefined; /** Replace the DEFAULT `RawPayload` output rendering of an expanded tool step; * input + error rendering are unaffected. */ renderToolOutput?: (call: AgentToolOutput) => React.ReactNode | undefined; /** When the run ended on a terminal `error`, render a retry action under that * danger row. */ onRetry?: () => void; /** Fold a SETTLED run's work into ONE summary row (`splitTimeline`). Default * `true`; `false` where the run is ALREADY inside a collapsed shell, since a * fold behind a fold costs two presses to read one run. */ collapseProcess?: boolean; /** Name the whole run on that summary row. The default names the LAST step: * only the host knows which of its tools WROTE, and a kit-side guess would be * silently incomplete the moment a write tool is added. */ summarizeRun?: (steps: readonly AgentStep[]) => string | undefined; accessibilityLabel?: string; testID?: string; ref?: React.Ref; render?: useRender.RenderProp; } /** Resolve a tool's display label + icon: a caller override, then the locale's * `agentRun.tools`, then a prettified fallback. Exported so a host labelling a * run outside the feed says what the feed says. */ export declare function resolveToolMeta(call: AgentToolCall, labelForCall?: (call: AgentToolCall) => string | undefined, toolLabels?: Record): { label: string; icon: IconName; }; /** One option the agent offered. The answer's `value` IS the label — that is the * `ask_user_choice` wire shape, not a convenience. */ export interface AgentRunOption { label: string; description?: string; } /** One question the agent asked, exactly as `pendingChoice` carries it. Named so * a consumer can DECLARE one — a template or test builds these directly. */ export interface AgentRunQuestion { question: string; options: AgentRunOption[]; allow_custom?: boolean; } /** * What the `pane` needs from a run — structurally the shape `useAgentRun()` * returns, declared here rather than imported so `@lotics/ui` keeps its one-way * boundary and never depends on `@lotics/app-sdk`. Nothing enforces the match at * build time; the app's own typecheck is the detector. */ export interface AgentRunLike { status: "idle" | "streaming" | "awaiting_input" | "completed" | "error"; parts: readonly AgentUIPart[]; /** Non-null exactly while parked. */ pendingChoice: { questions: AgentRunQuestion[]; } | null; /** * GENERIC in what it resolves: naming the type would mean importing * `@lotics/app-sdk` and breaking the one-way boundary above. */ answerChoice: (answers: { value: string; custom: boolean; }[]) => Promise; /** * STOP the run — server-side, not just locally. `useAgentRun().cancel` is * exactly this; do NOT pass its `abort`, which detaches the listener and leaves * the run executing (and billing) to completion. */ cancel: () => void; /** The breaking error that killed the run. OPTIONAL, not merely widened to include * `undefined`: an optional property is not assignable to a required one however * wide. The pane normalizes absent, `undefined` and `null` alike. */ error?: string | null | undefined; } /** THE RUN AS THE SURFACE — a dialog body that swaps to the agent's question. */ export interface AgentRunPaneProps extends StyleProps { variant: "pane"; run: AgentRunLike; labelForCall?: AgentRunFeedProps["labelForCall"]; renderToolOutput?: AgentRunFeedProps["renderToolOutput"]; /** Abandon a parked question — the run is left for the operator to retry. */ onCancel: () => void; /** * How the ANSWERED leg ended. Required for correctness in any host that acts on a * run's result: this pane owns the `answerChoice` call, and the caller's own * `run()` promise already resolved — with `parked` — and never resolves again. * Re-fires for a follow-up ask. */ onLanding?: (landing: TLanding) => void; testID?: string; ref?: React.Ref; render?: useRender.RenderProp; } /** THE RUN OUT OF THE WAY — a floating pill that expands to the feed. */ export interface AgentRunPillProps extends StyleProps { variant: "pill"; /** The agent message's ai-sdk `parts` — the same shape the feed takes. */ parts: readonly AgentUIPart[]; state?: "streaming" | "done" | "error"; /** A run-level breaking error, forwarded to the expanded feed. */ error?: string; /** The collapsed pill's label. Defaults to the running step's label, else the * `agentProgress` locale slice's running / finished / stopped word. */ label?: string; /** Localize / override a tool step's label from the CALL — applied to both the * collapsed pill and the expanded feed. */ labelForCall?: AgentRunFeedProps["labelForCall"]; /** Replace a tool step's default output panel in the expanded feed. */ renderToolOutput?: AgentRunFeedProps["renderToolOutput"]; /** Retry action under the expanded feed's terminal error row. */ onRetry?: () => void; defaultExpanded?: boolean; testID?: string; ref?: React.Ref; render?: Collapsible.Root.Props["render"]; } export type AgentRunProps = AgentRunFeedProps | AgentRunPaneProps | AgentRunPillProps; /** * An agent's work, live — one entry in three presentations, and the choice is how * much of the screen the run is entitled to. `feed` renders the timeline IN PLACE; * `pane` gives the run a dialog body that swaps the transcript for the agent's * QUESTION, paired with `AgentRunDialog` + `AgentRunActions`; `pill` takes the run * off the screen altogether. */ export declare function AgentRun(props: AgentRunProps): React.JSX.Element; /** * The dialog a `pane` run lives in, so the run's verbs can be rendered in the * dialog's own footer rather than inside the scrolling body. */ export declare function AgentRunDialog({ children }: { children: React.ReactNode; }): React.JSX.Element; /** * The run's action bar, for a `DialogFooter`. The run decides what it holds, so * the host mounts it unconditionally: the wizard's Cancel/Back/Next/Submit while * a question is up, **Stop** while the run is streaming, nothing once it has * settled. * * A DISPATCHER, so it takes no style props of its own: it renders no box, and * each thing it can return already carries its own anatomy — * `.lotics-clarify-wizard__actions` for the wizard's verbs, `.lotics-button` for * Stop. Style those. * * Pinned outside the scroller is what decides that Stop lives here rather than * under the transcript: the feed FOLLOWS its newest part, so a control inside it * scrolls away from the reader exactly while the run is doing the thing they want * to stop. * * `onStop` is the host's way back — the same handler the pane's `onCancel` takes, * because abandoning a parked question and stopping a streaming run land the * operator in the same place; only the verb differs. */ export declare function AgentRunActions({ run, onStop }: { run: AgentRunLike; onStop?: () => void; }): React.JSX.Element | null;