Files
hermes-agent/ui-tui/src/types.ts
T
teknium1 f6306d1920 feat(contracts): TypeScript consumes the generated contract; hand-typed wire shapes deleted
apps/shared/src/gateway-events.ts is now a thin layer over
gateway-contract.generated.ts (client-local synthetic events + the
GatewayEvent envelope); gateway-events.json, its two rendezvous tests and
the duplicated BillingBlock / SessionInfo / ProjectInfo hand copies are
gone. Desktop, TUI, web and shared typecheck against the generated
RpcMethods / ServerRequestMap / BackendGatewayEventMap.

What tsc found once the types were honest: three phantom fields the
backend never sent (tool.start.todos, error.reason,
voice.transcript.voice_stopped) - the TUI todo tests were driving the
list through the phantom and are retargeted to tool.complete, where the
wire actually carries it; nullable fields (`None` on the wire) were typed
as plain optionals in eight places and now coerce at the boundary;
SessionResumeResult had a stale generic.

Contract fixes from the consumer pass: TranscriptMessage is the gateway
projection (text/row_id/context/args), not the stored row; SkinPayload
matches HermesSkin (empty-string defaults, never null); SessionLiveInfo
model/tools/skills are required (always emitted); BillingBlock.billing_url
is required-nullable (dataclass asdict).

tui_gateway/AGENTS.md documents the declare -> regenerate -> tsc loop.
2026-09-14 06:12:19 -07:00

230 lines
6.0 KiB
TypeScript

import type { ProjectInfo, SessionLiveInfo, SubagentStatus } from '@hermes/shared/gateway-events'
export interface ActiveTool {
context?: string
id: string
name: string
verboseArgs?: string
startedAt?: number
}
export interface TodoItem {
content: string
id: string
/** Optional id of another item — renders this as a nested subtask. */
parent?: string
status: 'cancelled' | 'completed' | 'in_progress' | 'pending'
}
export interface ActivityItem {
id: number
text: string
tone: 'error' | 'info' | 'warn'
}
export interface SubagentProgress {
apiCalls?: number
costUsd?: number
/** Batch (delegation) id — tags `[n/N]` rows so concurrent/nested fan-outs
* are distinguishable. Absent on older gateways. */
delegationId?: string
depth: number
durationSeconds?: number
filesRead?: string[]
filesWritten?: string[]
goal: string
id: string
index: number
inputTokens?: number
iteration?: number
model?: string
notes: string[]
outputTail?: SubagentOutputEntry[]
outputTokens?: number
parentId: null | string
reasoningTokens?: number
startedAt?: number
status: SubagentStatus
summary?: string
taskCount: number
thinking: string[]
toolCount: number
tools: string[]
toolsets?: string[]
}
export interface SubagentOutputEntry {
isError: boolean
preview: string
tool: string
}
export interface SubagentNode {
aggregate: SubagentAggregate
children: SubagentNode[]
item: SubagentProgress
}
export interface SubagentAggregate {
activeCount: number
costUsd: number
descendantCount: number
filesTouched: number
hotness: number
inputTokens: number
maxDepthFromHere: number
outputTokens: number
totalDuration: number
totalTools: number
}
export interface DelegationStatus {
active: {
depth?: number
goal?: string
model?: null | string
parent_id?: null | string
started_at?: number
status?: string
subagent_id?: string
tool_count?: number
}[]
max_concurrent_children?: number
max_spawn_depth?: number
paused: boolean
}
export interface ApprovalReq {
// false when the backend won't honor a permanent allow (tirith warning) → hide "Always allow".
allowPermanent?: boolean
choices?: string[]
command: string
description: string
/** Server→client request id; the answer is the response frame for it. */
requestId: string
smartDenied?: boolean
}
export interface ConfirmReq {
cancelLabel?: string
confirmLabel?: string
danger?: boolean
detail?: string
onConfirm: () => void
title: string
}
export interface ClarifyBatchQuestion {
choices: string[] | null
multiSelect?: boolean
qid: string
question: string
}
export interface ClarifyReq {
choices: string[] | null
question: string
requestId: string
/** Batch (multi-question) clarify: present instead of question/choices. */
questions?: ClarifyBatchQuestion[]
/** Answers already locked server-side (qid → answer): seeded from the
* reconnect replay, updated as the user locks each question. */
answers?: Record<string, string>
}
export interface Msg {
info?: SessionInfo
kind?: 'diff' | 'event' | 'intro' | 'panel' | 'slash' | 'trail'
panelData?: PanelData
role: Role
text: string
// Unix seconds the message was authored (persisted transcript timestamp on
// rehydrate, wall clock at append time for live rows). Rendered as a dim
// [HH:MM] label when `display.timestamps` is on (#41531).
createdAt?: number
thinking?: string
// MoA reference-model output stored in `thinking` (see turnController's
// recordMoaReference): unlike ordinary model reasoning, this is the
// user-facing mixture-of-agents process the user opted into, so it stays
// visible even when `display.sections.thinking` is hidden.
isMoaReference?: boolean
// True only while this trail segment's reasoning is being streamed live by
// the current turn (see turnController's syncReasoningSegment). Sealed
// reasoning segments from earlier in the turn carry no flag, so the TUI can
// tell "the reasoning happening right now" apart from finished blocks.
isLiveReasoning?: boolean
thinkingTokens?: number
toolTokens?: number
tools?: string[]
todos?: TodoItem[]
todoIncomplete?: boolean
todoCollapsedByDefault?: boolean
}
export type Role = 'assistant' | 'system' | 'tool' | 'user'
export type DetailsMode = 'hidden' | 'collapsed' | 'expanded'
export type ThinkingMode = 'collapsed' | 'truncated' | 'full'
// Per-section overrides for the agent details accordion. Resolution order
// at lookup time is: explicit `display.sections.<name>` → built-in
// SECTION_DEFAULTS → global `details_mode`. Today the built-in defaults
// expand `thinking`/`tools` and hide `activity`; `subagents` falls through
// to the global mode. Any explicit value still wins for that one section.
export type SectionName = 'thinking' | 'tools' | 'subagents' | 'activity'
export type SectionVisibility = Partial<Record<SectionName, DetailsMode>>
export interface McpServerStatus {
connected: boolean
disabled?: boolean
status?: 'configured' | 'connecting' | 'connected' | 'disabled' | 'failed'
name: string
tools: number
transport: string
}
/** The gateway's `session.info` / resume `info` block — generated from `tui_gateway/contracts`. */
export type SessionInfo = SessionLiveInfo
export type { ProjectInfo }
export interface SudoReq {
requestId: string
}
export interface SecretReq {
envVar: string
prompt: string
requestId: string
}
/** External password-manager unlock (1Password / Bitwarden) — masked master-password prompt. */
export interface VaultUnlockReq {
backend: string
displayName: string
requestId: string
}
export interface PanelData {
sections: PanelSection[]
title: string
}
export interface PanelSection {
items?: string[]
rows?: [string, string][]
text?: string
title?: string
}
export interface SlashCatalog {
canon: Record<string, string>
categories: SlashCategory[]
pairs: [string, string][]
skillCount: number
sub: Record<string, string[]>
}
export interface SlashCategory {
name: string
pairs: [string, string][]
}