/** * Shared wire types for the crouter web client/server split. * * The SPA imports the pi payload types it needs from this module — never from * broker-protocol internals. The type-only re-exports below keep the web * package's shared payload shapes in one place. */ export type { UserMessage, AssistantMessage, ToolResultMessage, TextContent, ToolCall, ImageContent, } from '@earendil-works/pi-ai'; export type { ThinkingLevel } from '@earendil-works/pi-agent-core'; export type { RpcExtensionUIRequest, RpcExtensionUIResponse, RpcSessionState, } from '@earendil-works/pi-coding-agent'; import type { AgentMessage } from '@earendil-works/pi-agent-core'; import type { RpcSessionState } from '@earendil-works/pi-coding-agent'; import type { DeterministicCommandExpansion } from '../../../../core/runtime/command-expansion.js'; /** * An `AgentMessage` enriched with a client-side origin tag. * Only `role:'user'` messages can carry `origin:'inbox'`; all others leave * `origin` undefined. `'human'` is reserved for future explicit human-origin * tagging (currently unset — absence means human by default). * * Defined as an intersection (not `interface extends`) because `AgentMessage` * is a union type and TypeScript does not allow extending unions. */ export type FoldedMessage = AgentMessage & { /** Set to `'inbox'` when this user message is recognized as injected by the * canvas-inbox-watcher extension (a coalesced inbox digest). */ origin?: 'inbox' | 'human'; }; /** base session vs orchestrator. */ export type NodeMode = 'base' | 'orchestrator'; /** terminal (reaps when done) vs resident (root) node. */ export type NodeLifecycle = 'terminal' | 'resident'; /** Canvas lifecycle status. */ export type NodeLifeStatus = 'active' | 'idle' | 'done' | 'dead' | 'canceled'; /** broker-hosted (enterable) vs tmux-pane (shown, non-enterable). */ export type HostKind = 'broker' | 'tmux'; /** One row from `crtr canvas snapshot --json` — node identity + runtime, no chrome. */ export interface NodeSummary { node_id: string; name: string; /** Node kind (developer/explore/…) — open set. */ kind: string; mode: NodeMode; lifecycle: NodeLifecycle; status: NodeLifeStatus; cwd: string; /** Spine parent node id, or null for a root. */ parent: string | null; /** ISO-8601 creation timestamp. */ created: string; host_kind: HostKind; /** True iff `host_kind === 'broker'` (the web UI can open a live session). */ enterable: boolean; /** Count of pending human asks (blocked-on-human indicator). */ attention_count: number; /** Canvas cycle count for this node (revive/yield generations). Optional for * back-compat with snapshots produced before the field existed. */ cycles?: number; /** ISO-8601 of the node's most recent work (session activity), distinct from * `created`. Optional/back-compat; falls back to `created` when absent. */ last_activity?: string; } /** input/output token burn, with cache reads where the provider reports them. */ export interface TokenBurn { input: number; output: number; cache?: number; } /** Live context-window usage. */ export interface ContextUsage { tokens: number; window: number; percent: number; } /** Coarse session stats for chrome (distinct from pi's full `SessionStats`). */ export interface SessionStatsSummary { turns: number; user_messages: number; assistant_messages: number; cost?: number; } /** Viewer/controller presence for a node. */ export interface Presence { viewers: number; controller: string | null; } /** Git working-tree change counts for the meta strip. */ export interface GitStatus { added: number; modified: number; deleted: number; untracked: number; } /** `crtr node inspect show --json`'s node payload — `NodeSummary` plus the per-node chrome. */ export interface NodeDetail extends NodeSummary { branch: string | null; model: string | null; tokens: TokenBurn | null; context: ContextUsage | null; tool_calls: number | null; stats: SessionStatsSummary | null; presence: Presence | null; /** True while a broker is live; false for a dormant (last-known) view. */ live: boolean; /** Working-tree change counts for the meta strip (null when unavailable). */ git_status?: GitStatus | null; } /** A slash command in a node's palette (from the broker `get_commands` reply). */ export interface Command { name: string; description: string; source: 'builtin' | 'command' | 'template'; location?: 'user' | 'project' | 'path'; path?: string; argument_hint?: string; /** Raw file-template body used to disclose the exact expanded prompt before submission. */ template?: string; /** Deterministic extension-command prompt expansion; absent for immediate UI, * dynamic, and side-effect commands. */ expansion?: DeterministicCommandExpansion; } /** Metadata for one resolvable inline memory reference (design: inline memory * references). Hand-mirrored field-for-field from `RefMeta` in * `core/runtime/broker-protocol.ts` rather than imported — this module stays * free of any dependency on broker-protocol so it can be shared by both the * browser bundle and server-side code (browser convention, D11). `name` is * the canonical name a token resolves to and what `crtr memory read ` * expects; `scope` is the winning scope after precedence dedup (display * only). Metadata only — no document body or source path. */ export interface RefMeta { name: string; kind: 'knowledge' | 'preference'; scope: 'node' | 'project' | 'profile' | 'user' | 'builtin'; shortForm: string; } /** `crtr canvas snapshot --json` body. */ export interface CanvasSnapshot { nodes: NodeSummary[]; /** ISO-8601. */ generated_at: string; } /** `crtr node new --json` success. */ export interface SpawnResponse { node_id: string; name: string; window?: string; session?: string; status: string; follow_up: string; } /** `crtr node message send --json` success. */ export interface MessageResponse { mode: string; armed: boolean; target: string; delivered?: boolean; revived?: boolean; guidance: string; } /** `crtr node lifecycle revive --json` success. */ export interface ReviveResponse { window: null; session: string | null; resumed: boolean; ready: boolean; } /** `crtr node lifecycle close --json` success. */ export interface CloseResponse { closed: boolean; node_id: string; count: number; closed_ids: string[]; spared: string[]; } /** `crtr node new` body. Always headless; `root` toggles a resident node. */ export interface SpawnRequest { prompt: string; kind: string; mode?: NodeMode; root?: boolean; cwd?: string; name?: string; model?: string; parent?: string; } /** `crtr node message send` body. */ export interface MessageRequest { body: string; tier?: string; } /** `crtr node lifecycle revive` body. */ export interface ReviveRequest { fresh?: boolean; } /** * The five resolution flows (design §5.2). humanloop's `InteractionKind` also * has `review`; the deck loader normalizes it (and any unknown/absent kind) * onto one of these five so a deck always renders through a known flow. */ export type DeckKind = 'notify' | 'validation' | 'decision' | 'context' | 'error'; /** One option of a decision/validation interaction (humanloop InteractionOption). */ export interface DeckOption { id: string; label: string; /** The option's consequence/explanation (humanloop `description`). */ description?: string; } /** One interaction within a deck (humanloop Interaction, normalized for the web). */ export interface DeckInteraction { id: string; title: string; subtitle: string; /** Markdown body (bodyPath already inlined by `crtr human deck`). */ body?: string; kind: DeckKind; options: DeckOption[]; multiSelect: boolean; allowFreetext: boolean; freetextLabel?: string; } /** * A pending ask, ranked in the inbox list (design §5.2). Provenance carries * private node routing and an optional authored source label for presentation. */ export interface DeckSummary { /** Opaque, stable inbox-entry id (base64url of the interaction dir). */ id: string; /** The interaction job id (basename of the interaction dir). */ job_id: string; /** The first interaction's kind — the glyph + flow for the row. */ kind: DeckKind; title: string; subtitle: string; /** ISO-8601 — when the ask started blocking (drives the wait duration + rank). */ blocked_since: string; /** Humanloop's current claim owner, when a person/session is handling it. */ claim_owner?: string; /** Authored display label: source sessionName, then askedBy. */ source_label?: string; /** The conversation (spine root) this ask belongs to. */ conversation_id: string; conversation_title: string; /** The node that raised the ask. */ asking_node_id: string; asking_node_name: string; /** The asking node's cwd — sub-DAG scoping. */ cwd: string; /** How many interactions the deck holds (a multi-question deck). */ interaction_count: number; } /** The full deck for a resolution flow (`crtr human deck --json`). */ export interface DeckDetail extends DeckSummary { interactions: DeckInteraction[]; } /** One interaction's answer (humanloop InteractionResponse). */ export interface DeckAnswer { id: string; selectedOptionId?: string; selectedOptionIds?: string[]; freetext?: string; optionComments?: Record; } /** `crtr human resolve --json` body. */ export interface ResolveDeckRequest { responses: DeckAnswer[]; } /** `crtr human resolve --json` success. */ export interface ResolveDeckResponse { resolved: true; job_id: string; delivered: boolean; } /** * Engine state carried in `snapshot.state` — pi's OWN `get_state` payload, not a * mirror of it. `BrokerSnapshot['state']` is this same type, so the welcome frame * passes through the SPA untranslated. The SPA's streaming/idle indicator reads * `isStreaming` (spec C.10). */ export type SessionState = RpcSessionState; /** Web role of one browser tab's session connection. */ export type WebRole = 'observer' | 'controller'; /** Upstream broker connectivity, surfaced to the tab (spec §6.2). */ export type BrokerStatus = 'connected' | 'reconnecting' | 'down' | 'revived'; /** Response payload to an extension dialog (`RpcExtensionUIResponse` minus id/type). */ export type DialogResponseValue = { value: string; } | { confirmed: boolean; } | { cancelled: true; }; /** * A typed content block within a view tab. Discriminated union of three * block kinds: inline/sourced markdown, a KPI grid, and a bar-list chart. */ export type ViewBlock = { kind: 'markdown'; source: { node_id: string; path: string; } | { inline: string; }; } | { kind: 'kpis'; items: { label: string; value: string; unit?: string; sub?: string; }[]; } | { kind: 'barlist'; title: string; rows: { label: string; value: number; max?: number; note?: string; }[]; };