Skip to content

zidane-ui/chat/interactions

Cards for the questions and plans an agent waits on.

import { … } from 'zidane-ui/chat/interactions'ui/src/chat/interactions/index.ts

Use this entry point to show ask_user forms and plan reviews. HostInteractions lists the interactions a ChatHost has pending for a session and sends the answers back. QuestionForm, PlanCard and the field components are exported when you build your own surface. See Composer for how the cards sit above the prompt box.

TSX
import type { ChatHost } from 'zidane-ui/chat/headless'
import { ChatProvider } from 'zidane-ui/chat'
import { HostInteractions } from 'zidane-ui/chat/interactions'

export function Questions({ host, id }: { host: ChatHost, id: string }) {
  return (
    <ChatProvider host={host}>
      <HostInteractions sessionId={id} autoFocus />
    </ChatProvider>
  )
}
InteractionCard: question
2 questions
InteractionCard: plan
Rate limit the chat endpoint

A token bucket per IP in front of /chat: 20 requests a minute, bursts of 5.

  1. 1.
    Add the limiter
    server/src/limits.ts, with tests
  2. 2.
    Guard the route
    Answer 429 with Retry-After when it trips
  3. 3.
    Document it

Components#

AnsweredQuestionsComponent#

An answered ask_user form, read-only: the same intro and question cards as the live form, each showing what was picked or typed, then the additional instructions when some were given. Used by the transcript (the call's details), so an answer reads the way it was given.

TypeScript
function AnsweredQuestions(props: AnsweredQuestionsProps): Element
Parameters
NameTypeDescription
propsAnsweredQuestionsProps

Component props: the payload that was asked, the answers, optional instructions, a unique id, and an optional Markdown renderer.

Element
AnsweredQuestions props
NameTypeDescription
answersReadonly<Record<string, AnswerValue>>

The submitted answers, keyed by question id.

className?string

Extra classes on the root element.

idstring

Unique per call, for the fields' ids.

instructions?string

The additional instructions the user gave. Shown only when not blank.

Markdown?ComponentType<MarkdownSlotProps>

Draws the intro; defaults to the transcript's Markdown slot.

onOpenSecrets?() => void

Opens the Secrets panel from an answered secret. Absent: no button.

payloadQuestionPayload

The questions that were asked.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

ui/src/chat/interactions/QuestionForm.tsx:279

HostInteractionsComponent#

Renders the requests a session is waiting on as cards, oldest first, answered through the ChatHost in context. Renders nothing while none wait.

TypeScript
function HostInteractions(props: HostInteractionsProps): Element | null
Parameters
NameTypeDescription
propsHostInteractionsProps

The HostInteractionsProps.

Element | null
HostInteractions props
NameTypeDefaultDescription
autoFocus?booleanfalse

Focuses the oldest card as it arrives.

className?string

Extra class names for the list.

Markdown?ComponentType<MarkdownSlotProps>

Draws an intro or a plan. Defaults to the transcript's Markdown slot.

sessionIdstring

Id of the session whose pending requests to show.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

ui/src/chat/interactions/HostInteractions.tsx:43

InboxPanelComponent#

Renders one session's pending requests as inbox cards, the head one open; an empty state while none wait.

TypeScript
function InboxPanel(props: InboxPanelProps): Element
Parameters
NameTypeDescription
propsInboxPanelProps

The InboxPanelProps.

Element
InboxPanel props
NameTypeDescription
className?string

Extra class names for the root.

focus?boolean

Hand keyboard focus to the head card once it is there (the panel's focus request).

inProgress?ReactNode

The "In progress" section's body: the plan being applied. Absent or null: no section.

Markdown?ComponentType<MarkdownSlotProps>

Draws an intro or a plan. Defaults to the transcript's Markdown slot.

recent?ReactNode

The "Recent" section's body: closed plans. Absent or null: no section.

sessionIdstring

The session whose requests to show.

style?CSSProperties

Inline styles on the root element.

onEscape?() => void

Esc inside the list: give the keyboard back (the composer). Absent: Esc does nothing here.

onFocusTaken?() => void

The head card took focus: clear the request.

ui/src/chat/interactions/InboxPanel.tsx:68

InteractionCardComponent#

Renders one agent request as a card: a header naming its kind and subject, then the form that answers it. A question gets a form, a plan gets an approve, revise, or reject review, and an unknown kind shows its payload.

TypeScript
function InteractionCard(props: InteractionCardProps): Element
Parameters
NameTypeDescription
propsInteractionCardProps

The InteractionCardProps.

Element
InteractionCard props
NameTypeDescription
autoFocus?boolean

Focus the card's first control.

className?string

Extra classes on the root element.

interactionChatInteraction

The pending request to show.

Markdown?ComponentType<MarkdownSlotProps>

Draws an intro or a plan; defaults to the transcript's Markdown slot.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

onAnswer(response: InteractionResponse) => Promise<void>

Called with the user's response. The card waits on the returned promise.

onDismiss?() => Promise<void>

Withdraw the request unanswered (a question's Dismiss).

ui/src/chat/interactions/InteractionCard.tsx:44

PlanBodyComponent#

The plan itself: its Markdown, then the numbered steps. Shared by this card and the transcript (a present_plan call's details), so a plan reads the same wherever it shows.

TypeScript
function PlanBody(props: PlanBodyProps): Element
Parameters
NameTypeDescription
propsPlanBodyProps

Component props: the payload and an optional Markdown renderer.

Element
PlanBody props
NameTypeDescription
Markdown?ComponentType<MarkdownSlotProps>

Draws the plan. Defaults to the transcript's Markdown slot.

payloadPlanProposal

The plan to show: its markdown and optional numbered steps.

ui/src/chat/interactions/PlanCard.tsx:159

PlanCardComponent#

Renders a present_plan request as a card the user approves, revises, or rejects. Revise and reject take an optional note.

TypeScript
function PlanCard(props: PlanCardProps): Element
Parameters
NameTypeDescription
propsPlanCardProps

The PlanCardProps.

Element
PlanCard props
NameTypeDescription
autoFocus?boolean

Focus Approve (the card just became the one to answer).

className?string

Extra classes on the root element.

Markdown?ComponentType<MarkdownSlotProps>

Draws the plan; defaults to the transcript's Markdown slot.

requestPlanRequest

The present_plan request: the plan's title, markdown, and steps.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

onAnswer(response: PlanResponse) => Promise<void>

Called with the decision (approve, revise, or reject) and an optional note. The card waits on the returned promise and shows a rejection's message.

ui/src/chat/interactions/PlanCard.tsx:43

QuestionFieldComponent#

Renders one question of an ask_user form as a card: the numbered prompt, its help, and the control its type takes.

TypeScript
function QuestionField(props: QuestionFieldProps): Element
Parameters
NameTypeDescription
propsQuestionFieldProps

Field props: index, question, value, onChange, requestId, labels, and readOnly.

Element
QuestionField props
NameTypeDescription
className?string

Extra classes on the root element.

indexnumber

Zero-based position of the question. Shown as a number before the prompt.

labels{ addDiagnosticToChat: (message: string) => string; addFavorite: string; additionalInstructions: string; additionalInstructionsHint: string; additionalInstructionsPlaceholder: string; address: string; addressPlaceholder: string; addToChat: string; agent: string; agentDraftNote: string; agentProfiles: string; archive: string; archived: string; attachFile: string; attachmentEmpty: (name: string) => string; attachmentExportPdf: (name: string) => string; attachmentNotices: string; attachmentReadFailed: (name: string) => string; attachments: string; attachmentTitle: (name: string, detail: string) => string; attachmentTooLarge: (name: string, size: string, limit: string) => string; attachmentUnreadableImage: (name: string) => string; attachmentUnsupported: (name: string) => string; authCancel: string; authCard: string; authCopied: string; authCopyDetails: string; authEnterCode: (code: string) => string; authFailed: (message: string) => string; authFixed: string; authHeadline: (auth: AuthFailure, label: string) => string; authOpeningBrowser: string; authOpenPage: string; authPaste: string; authPasteSend: string; authSettings: string; authSettingsHint: string; authSignedIn: string; authSignedInRetrying: string; authSignInAgain: string; authSwitching: (account: string) => string; authTryAgain: string; authUseAccount: (account: string) => string; authWaiting: string; autoDenied: string; back: string; badgeCopied: string; badgeNoComposer: string; badgeOpenIn: (app: string) => string; badgeOpenInBrowserPane: string; badgePathUnavailable: string; blockRunAgain: string; blockRunAttach: string; blockRunAttachmentName: string; blockRunCopy: string; blockRunFailed: (reason: string) => string; blockRunHere: string; blockRunInTerminal: string; blockRunMenu: string; blockRunNeedsTerminal: string; blockRunning: string; blockRunOutput: string; blockRunRemove: string; blockRunStop: string; blockRunStopped: string; blockRunTimedOut: string; blockRunTruncated: string; cancel: string; chats: string; chipBranch: (name: string) => string; chipChat: (name: string) => string; chipCommand: (name: string) => string; chipCommit: (sha: string) => string; chipFactAliases: string; chipFactBranch: string; chipFactChanges: string; chipFactContext: string; chipFactDate: string; chipFactModel: string; chipFactPrompts: string; chipFactSize: string; chipFactTurns: string; chipFactUpdated: string; chipLoading: string; chipNotAmongChats: string; chipNotFound: (title: string) => string; chipRemoteFile: (path: string, host: string) => string; chipSessionError: string; chipSessionRunning: string; chipSkill: (name: string) => string; chipTokens: (count: string) => string; chipUntitledChat: string; chooseModel: string; clickToAnswer: string; close: string; closeTab: (name: string) => string; code: string; collapseGroup: (label: string) => string; commandDetail: string; commandSuggestions: string; completionSuggestions: string; composerBadges: string; composerLabel: string; composerPlaceholder: string; composerPlaceholderQueue: string; composerPlaceholderSteer: string; confirm: string; confirmNo: string; confirmYes: string; contextActiveSkills: string; contextAutoCompact: (pct: number) => string; contextCacheRead: string; contextCacheWrite: string; contextClickToCopy: (hasDescription: boolean) => string; contextCopied: string; contextDeferred: string; contextDetail: string; contextEstimated: string; contextFreshInput: string; contextLastTurn: string; contextNoBreakdown: string; contextOutput: string; contextRunCount: (runs: number) => string; contextSessionCost: string; contextSummary: (used: string, window: string, pct: number) => string; contextSummaryCost: (summary: string, cost: string) => string; contextWindow: string; continue: string; continueHint: string; continueRun: string; copied: string; copy: string; copyCode: string; copyDiagramSource: string; copyImage: string; copyImageBase64: string; copyImageDataUrl: string; current: string; declineSecret: string; declineSecretHint: string; defaultAgent: string; delete: string; diagnosticsCheckedBy: (source: string) => string; diagnosticsChecking: string; diagnosticsNone: string; diagram: (type: string | null) => string; diagramError: (message: string, at: string) => string; diagramErrorAt: (line: number, column: number) => string; dismiss: string; dismissHint: string; dotAttached: string; dotAttention: string; dotCompleted: string; dotError: string; dotIdle: string; dotPending: string; dotRunning: string; earlierHistory: string; earlierLines: (count: number) => string; earlierMessages: (count: number) => string; editorConflict: string; editorConflictDetail: string; editorEmpty: string; editorOverwrite: string; editorReadFailed: (message: string) => string; editorReload: string; effortLevel: (level: string) => string; errorFailed: (region: string) => string; errorStack: string; errorTryAgain: string; estimateDue: string; estimateDueTitle: string; estimateOver: string; estimateOverTitle: string; estimatePausedTitle: (left: string) => string; estimateProblemTitle: (reason: string, message?: string) => string; estimateTitle: (left: string) => string; expandGroup: (label: string) => string; favorites: string; filePath: (path: string) => string; fileReadOnly: (path: string) => string; fileTree: string; fileUnsaved: string; fileUnsavedTitle: (path: string) => string; filterActivities: { '1d': string; '30d': string; '3d': string; '7d': string; all: string }; filterActivity: string; filterAgent: string; filterAllAgents: string; filterAllAgentsOption: string; filterAllProjects: string; filterAllProjectsOption: string; filterAny: string; filterNoAgents: string; filterNoProjects: string; filterOrigin: string; filterOrigins: { all: string; scheduled: string; unscheduled: string }; filterPicked: (count: number) => string; filterProject: string; filterSaveView: string; filterSessions: string; filterSort: string; filterSorts: { alphabetical: string; createdTime: string; recency: string }; filterSpaceDefault: string; filterStatus: string; filterStatuses: { active: string; all: string; archived: string }; filterView: string; filterViews: { agent: string; date: string; host: string; none: string; project: string; state: string }; findClose: string; findCloseHint: string; findInConversation: string; findInConversationClose: string; findInFile: string; findMatchCase: string; findNext: string; findNextHint: string; findNoResults: string; findPrevious: string; findPreviousHint: string; findReferences: string; fitDiagram: string; foldOutcomes: (ok: number, failed: number) => string; foldSteps: (count: number) => string; generatedImage: string; generatingImage: (model: string | null) => string; generatingImages: (count: number, model: string | null) => string; goToDefinition: string; goToStep: (step: string) => string; hostBadgeTitle: (host: string, status: string) => string; hostCollapse: (name: string) => string; hostExpand: (name: string) => string; hostOpenSettings: (host: string) => string; hostPicker: string; hostRunsOn: (name: string) => string; hostRunsOnSection: string; hostStatusError: (status: string, error: string) => string; hostStatuses: { 'auth-refused': string; connected: string; connecting: string; identity: string; local: string; offline: string; reconnecting: string; 'version-mismatch': string }; hostThisMachine: string; imageModelReferences: string; imageModelReferencesHint: string; imageModelsNone: string; imageModelsPill: string; imageModelTransparency: string; imageModelTransparencyHint: string; inboxEmpty: string; inboxInProgress: string; inboxNeedsYou: string; inboxPreviousSession: string; inboxRecent: string; inputCaption: string; interactionAnsweredBy: (who: string) => string; interactionApproved: string; interactionDismissed: string; interactionPlan: string; interactionQuestion: string; interactionRejected: string; interactionRevise: string; interactionsLabel: string; interactionWaitingAnswers: string; interactionWaitingDecision: string; justNow: string; loadEarlierHistory: string; loadFullOutput: string; loading: string; loadingEarlierHistory: string; loadingEarlierMessages: string; loadingFullOutput: string; mentionSuggestions: string; modalBack: string; modalFailed: string; modelAccountBlocked: (provider: string) => string; modelChain: string; modelChainNoModel: string; modelChainOrderHint: string; modelFallback: string; modelFallbackHint: string; modelFallbackPosition: (n: number) => string; modelFeatures: string; modelForNextChat: string; modelInUse: string; modelKinds: string; modelLegacy: string; modelLegacyHint: string; modelNotInCatalog: string; modelNotInCatalogHint: string; modelOptions: string; modelOptionsEmpty: string; modelOptionsHint: string; modelPrimary: string; modelPrimaryHint: string; modelReasoning: string; modelReasoningHint: string; modelRemoveFallback: (n: number) => string; modelRemoveFromChain: string; modelRemovePrimary: string; modelsChatTab: string; modelsCustomId: string; modelServing: (picked: string, serving: string) => string; modelServingFallback: string; modelsImageTab: string; modelsInUse: string; modelsJumpHint: (provider: string, digit: number | null) => string; modelsJumpToProvider: string; modelsLegacy: string; modelsLegacyHidden: (count: number) => string; modelsLoadFailed: (error: string) => string; modelsLoading: string; modelsNoMatch: (query: string) => string; modelsNoProviders: string; modelsSelected: (count: number) => string; modelsShowLegacy: string; modelsShowLegacyHint: string; modelsShownOf: (shown: number, total: number) => string; modelsTypeAnId: string; modelUnavailable: string; modelVerdictTitle: (approved: boolean, model: string, confidence: string | null) => string; modelVia: string; modelViaHint: (modelId: string, provider: string) => string; months: readonly string[]; moreActions: string; moreAttachments: (count: number) => string; newSession: string; newSessionIn: (label: string) => string; next: string; noAnswer: string; noMatches: (query: string) => string; noReferences: string; noSessions: string; noSuggestions: string; of: (index: number, count: number) => string; openFiles: string; openGroup: (label: string) => string; openImageFile: (path: string) => string; openInNewTab: string; openNamedSession: (title: string) => string; openSecrets: string; openSession: string; outputCaption: string; paletteBack: string; paletteBackHint: string; paletteChoose: string; paletteEmpty: string; paletteFilter: string; paletteLabel: string; paletteLoading: string; paletteNoMatch: (query: string) => string; paletteOpen: string; paletteRun: string; paletteScopeHint: string; paletteTurnOff: string; paletteTurnOn: string; pickModel: string; pickOneOrMore: string; planApprove: string; planReject: string; planRejectAndStop: string; planRejectNote: string; planRejectPlaceholder: string; planRevise: string; planReviseNote: string; planRevisePlaceholder: string; planSendRevision: string; previewFailed: (name: string) => string; previewTooLarge: (name: string, size: string) => string; previous: string; questionCount: (count: number) => string; queueHint: string; queueMessage: string; reasoningEffort: string; receiptAgent: string; receiptAgentSwitched: string; receiptCost: string; receiptDuration: string; receiptModel: string; receiptModelOthers: (others: number) => string; receiptSpeed: string; receiptTokens: (sent: string, received: string) => string; receiptTurns: (turns: number) => string; reload: string; removeAttachment: (name: string) => string; removeFavorite: string; rename: string; requiredHint: string; requiredMark: string; resetsAt: (time: string) => string; retry: string; retryHint: string; runningFor: (elapsed: string) => string; runReceipt: string; runRetrying: (attempt: number, max: number, source?: string) => string; runWaiting: string; saveSecurely: string; screenBack: (screen: string) => string; screenBackKey: string; scriptCallsDone: (done: number) => string; scriptCallsFailed: (failed: number) => string; scriptCallsOf: (done: number, total: number) => string; scriptCallsRunning: (running: number) => string; scriptLineCalls: (calls: number) => string; scriptLiveOutput: string; scriptLoop: string; scriptLoopRound: (round: number) => string; scriptLoopRounds: (rounds: number) => string; scriptLoopRuns: (runs: number) => string; scriptOptionOutput: string; scriptOptions: string; scriptOptionTimeout: string; scriptOptionTokens: (tokens: number) => string; scriptPercent: (percent: number) => string; scriptProgress: string; scriptWaitLeft: (seconds: number) => string; searchModels: string; searchModelsPlaceholder: string; searchSessions: string; secretDeclined: (name: string) => string; secretNote: (ttl: SecretTtl) => string; secretReused: (name: string) => string; secretSaved: (name: string, left: SecretTimeLeft) => string; secretSent: (name: string) => string; secretTtlLabel: string; secretTtlName: (ttl: SecretTtl) => string; secretValueLabel: string; secretValuePlaceholder: string; selectTurnCopied: string; selectTurnCopy: string; selectTurnDetails: string; selectTurnExit: string; selectTurnFork: string; selectTurnForkFailed: string; selectTurnMove: string; selectTurnNothingToCopy: string; selectTurnSelecting: string; sendAnswers: (count: number) => string; sendHint: string; sendMessage: string; sessionActions: string; sessionEventLabel: (title: string, verb: string, needsAttention: boolean) => string; sessions: string; setModelOptions: string; shellCancelled: string; shellExit: (code: number) => string; shellKilled: string; showDiagram: string; showDiagramSource: string; showMore: string; showMoreAttachments: (count: number) => string; showOutput: string; showSystemPrompt: string; signInCopyCode: string; signInDone: (account?: string) => string; signInEnterCode: (host: string) => string; signInFailed: string; signInInBrowser: (service: string) => string; signInNotOpened: string; signInOpenPage: string; signInPaste: string; signInStarting: string; signInSubmit: string; skip: string; spawnResumes: (childId: string) => string; spawnTask: string; stateCompleted: string; stateError: string; stateIdle: string; stateRunning: string; steerHint: string; steerMessage: string; stepDone: string; stepFailed: string; stepOf: (index: number, count: number) => string; stepResuming: string; stepRunning: string; steps: string; stepWaiting: string; stopCommand: string; stopHint: string; stopped: string; stoppedByYou: string; stopRun: string; switchAgent: string; switchingAgent: string; switchModel: string; terminal: string; terminalExited: (code: number) => string; terminalLoadFailed: (message: string) => string; thinkingLive: string; thoughtFor: (seconds: number) => string; thoughts: string; thoughtTimes: (count: number) => string; today: string; toolAttachments: string; transcriptLoading: string; turnMenuCopy: string; turnMenuCopySelection: string; turnMenuCopySelectionFailed: string; turnMenuDelete: string; turnMenuEdit: string; turnMenuEditNeedsComposer: string; turnMenuFork: string; turnMenuRewind: string; turnMenuSent: (age: string) => string; turnMenuStopFirst: (label: string) => string; turnsCompacted: string; unarchive: string; unsupportedInteraction: (tool: string) => string; viewAttachment: (name: string) => string; waitingForSlot: (max: number) => string; workerContinues: string; workerModel: string; workerSystemPrompt: string; workerSystemReplaced: string; workerType: string; working: string; wrapLines: string; yesterday: string; zoomFit: string; zoomInDiagram: string; zoomOutDiagram: string; zoomPercent: (percent: number) => string }

The chat labels used for the field's strings.

onChange(value: AnswerValue) => void

Called with the new answer when the user edits the field.

onOpenSecrets?() => void

Opens the Secrets panel from an answered secret. Absent: no button.

onSubmit?() => void

Enter in a secret's password field (the form saves).

questionQuestion

The question to draw. Its type picks the control.

readOnly?boolean

The answered form (transcript): the same card, showing what was picked, nothing editable.

requestIdstring

Id of the request. Builds unique element ids for the field.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

valueAnswerValue | undefined

The current answer, or undefined when none is given.

ui/src/chat/interactions/QuestionField.tsx:58

QuestionFormComponent#

Renders an ask_user request as a form: an optional intro, one card per question, and an "Additional instructions" field. Required questions block Send.

TypeScript
function QuestionForm(props: QuestionFormProps): Element
Parameters
NameTypeDescription
propsQuestionFormProps

The QuestionFormProps.

Element
QuestionForm props
NameTypeDescription
autoFocus?boolean

Focus the first control (the card just became the one to answer).

className?string

Extra classes on the root element.

Markdown?ComponentType<MarkdownSlotProps>

Draws the intro; defaults to the transcript's Markdown slot.

requestQuestionRequest

The ask_user request: its intro and questions.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

onAnswer(response: QuestionResponse) => Promise<void>

Called with the answers when the user sends. The form waits on the promise and shows a rejection's message under the controls.

onDismiss?() => Promise<void>

Withdraw the question unanswered. Absent: no Dismiss button.

ui/src/chat/interactions/QuestionForm.tsx:62

SecretInputComponent#

Renders a password field, a TTL picker and the note on where the value is kept.

TypeScript
function SecretInput(props: SecretInputProps): Element
Parameters
NameTypeDescription
propsSecretInputProps

The SecretInputProps.

Element
SecretInput props
NameTypeDescription
className?string

Extra classes on the root element.

describedBy?string

Ids of the elements that describe the field (the question's help).

idstring

Id of the password field: a <label htmlFor> names it.

inputRef?Ref<HTMLInputElement>

Ref to the password field.

onChange(value: string, ttl: SecretTtl) => void

Called on every keystroke with the typed value and the picked TTL. Keep the value out of anything logged or saved.

onSubmit?() => void

Called on Enter in the password field.

required?boolean

Marks the field required for assistive tech.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

ttlSecretTtl

The TTL picked.

ui/src/chat/interactions/SecretInput.tsx:41

Functions#

isRequiredFunction#

Same defaults as the TUI: select/confirm required, free text optional. A secret needs its value (Decline is the way out).

TypeScript
function isRequired(q: Question): boolean
Parameters
NameTypeDescription
qQuestion

The question. required wins; otherwise select, confirm and secret are required and free text is optional.

boolean

ui/src/chat/interactions/QuestionField.tsx:23

seedDefaultsFunction#

Seed the form from the payload: default for free text, selected pre-ticks for multi-select (an opt-out list arrives already ticked).

TypeScript
function seedDefaults(payload: QuestionPayload): Record<string, AnswerValue>
Parameters
NameTypeDescription
payloadQuestionPayload

The question payload to read defaults from.

Record<string, AnswerValue>

ui/src/chat/interactions/QuestionForm.tsx:328

useAllInteractionsFunction#

Every session's pending requests, in arrival order; empty when the host only lists them per session.

TypeScript
function useAllInteractions(): readonly ChatInteraction[]
readonly ChatInteraction[]

ui/src/chat/interactions/hooks.ts:45

useInteractionHeadFunction#

The oldest pending request of one session, or null. Re-renders only when that request changes, not when more queue behind it (a sidebar dot reads it per row).

TypeScript
function useInteractionHead(sessionId: string | null): ChatInteraction | null
Parameters
NameTypeDescription
sessionIdstring | null

The session, or null.

ui/src/chat/interactions/hooks.ts:34

usePendingInteractionsFunction#

One session's pending requests, oldest first; empty for no session or a host without interactions.

TypeScript
function usePendingInteractions(sessionId: string | null): readonly ChatInteraction[]
Parameters
NameTypeDescription
sessionIdstring | null

The session, or null.

readonly ChatInteraction[]

ui/src/chat/interactions/hooks.ts:19

Interfaces#

AnsweredQuestionsPropsInterface#

Props for AnsweredQuestions.

TypeScript
interface AnsweredQuestionsProps
AnsweredQuestionsProps properties
NameTypeDescription
answersReadonly<Record<string, AnswerValue>>

The submitted answers, keyed by question id.

className?string

Extra classes on the root element.

idstring

Unique per call, for the fields' ids.

instructions?string

The additional instructions the user gave. Shown only when not blank.

Markdown?ComponentType<MarkdownSlotProps>

Draws the intro; defaults to the transcript's Markdown slot.

onOpenSecrets?() => void

Opens the Secrets panel from an answered secret. Absent: no button.

payloadQuestionPayload

The questions that were asked.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

ui/src/chat/interactions/QuestionForm.tsx:252

HostInteractionsPropsInterface#

Props for HostInteractions.

TypeScript
interface HostInteractionsProps
HostInteractionsProps properties
NameTypeDefaultDescription
autoFocus?booleanfalse

Focuses the oldest card as it arrives.

className?string

Extra class names for the list.

Markdown?ComponentType<MarkdownSlotProps>

Draws an intro or a plan. Defaults to the transcript's Markdown slot.

sessionIdstring

Id of the session whose pending requests to show.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

ui/src/chat/interactions/HostInteractions.tsx:20

InboxPanelPropsInterface#

Props for InboxPanel.

TypeScript
interface InboxPanelProps
InboxPanelProps properties
NameTypeDescription
className?string

Extra class names for the root.

focus?boolean

Hand keyboard focus to the head card once it is there (the panel's focus request).

inProgress?ReactNode

The "In progress" section's body: the plan being applied. Absent or null: no section.

Markdown?ComponentType<MarkdownSlotProps>

Draws an intro or a plan. Defaults to the transcript's Markdown slot.

recent?ReactNode

The "Recent" section's body: closed plans. Absent or null: no section.

sessionIdstring

The session whose requests to show.

style?CSSProperties

Inline styles on the root element.

onEscape?() => void

Esc inside the list: give the keyboard back (the composer). Absent: Esc does nothing here.

onFocusTaken?() => void

The head card took focus: clear the request.

ui/src/chat/interactions/InboxPanel.tsx:36

InteractionCardPropsInterface#

Props for InteractionCard.

TypeScript
interface InteractionCardProps
InteractionCardProps properties
NameTypeDescription
autoFocus?boolean

Focus the card's first control.

className?string

Extra classes on the root element.

interactionChatInteraction

The pending request to show.

Markdown?ComponentType<MarkdownSlotProps>

Draws an intro or a plan; defaults to the transcript's Markdown slot.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

onAnswer(response: InteractionResponse) => Promise<void>

Called with the user's response. The card waits on the returned promise.

onDismiss?() => Promise<void>

Withdraw the request unanswered (a question's Dismiss).

ui/src/chat/interactions/InteractionCard.tsx:19

PlanBodyPropsInterface#

Props for PlanBody.

TypeScript
interface PlanBodyProps
PlanBodyProps properties
NameTypeDescription
Markdown?ComponentType<MarkdownSlotProps>

Draws the plan. Defaults to the transcript's Markdown slot.

payloadPlanProposal

The plan to show: its markdown and optional numbered steps.

ui/src/chat/interactions/PlanCard.tsx:145

PlanCardPropsInterface#

Props for PlanCard.

TypeScript
interface PlanCardProps
PlanCardProps properties
NameTypeDescription
autoFocus?boolean

Focus Approve (the card just became the one to answer).

className?string

Extra classes on the root element.

Markdown?ComponentType<MarkdownSlotProps>

Draws the plan; defaults to the transcript's Markdown slot.

requestPlanRequest

The present_plan request: the plan's title, markdown, and steps.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

onAnswer(response: PlanResponse) => Promise<void>

Called with the decision (approve, revise, or reject) and an optional note. The card waits on the returned promise and shows a rejection's message.

ui/src/chat/interactions/PlanCard.tsx:23

QuestionFieldPropsInterface#

Props for QuestionField.

TypeScript
interface QuestionFieldProps
QuestionFieldProps properties
NameTypeDescription
className?string

Extra classes on the root element.

indexnumber

Zero-based position of the question. Shown as a number before the prompt.

labels{ addDiagnosticToChat: (message: string) => string; addFavorite: string; additionalInstructions: string; additionalInstructionsHint: string; additionalInstructionsPlaceholder: string; address: string; addressPlaceholder: string; addToChat: string; agent: string; agentDraftNote: string; agentProfiles: string; archive: string; archived: string; attachFile: string; attachmentEmpty: (name: string) => string; attachmentExportPdf: (name: string) => string; attachmentNotices: string; attachmentReadFailed: (name: string) => string; attachments: string; attachmentTitle: (name: string, detail: string) => string; attachmentTooLarge: (name: string, size: string, limit: string) => string; attachmentUnreadableImage: (name: string) => string; attachmentUnsupported: (name: string) => string; authCancel: string; authCard: string; authCopied: string; authCopyDetails: string; authEnterCode: (code: string) => string; authFailed: (message: string) => string; authFixed: string; authHeadline: (auth: AuthFailure, label: string) => string; authOpeningBrowser: string; authOpenPage: string; authPaste: string; authPasteSend: string; authSettings: string; authSettingsHint: string; authSignedIn: string; authSignedInRetrying: string; authSignInAgain: string; authSwitching: (account: string) => string; authTryAgain: string; authUseAccount: (account: string) => string; authWaiting: string; autoDenied: string; back: string; badgeCopied: string; badgeNoComposer: string; badgeOpenIn: (app: string) => string; badgeOpenInBrowserPane: string; badgePathUnavailable: string; blockRunAgain: string; blockRunAttach: string; blockRunAttachmentName: string; blockRunCopy: string; blockRunFailed: (reason: string) => string; blockRunHere: string; blockRunInTerminal: string; blockRunMenu: string; blockRunNeedsTerminal: string; blockRunning: string; blockRunOutput: string; blockRunRemove: string; blockRunStop: string; blockRunStopped: string; blockRunTimedOut: string; blockRunTruncated: string; cancel: string; chats: string; chipBranch: (name: string) => string; chipChat: (name: string) => string; chipCommand: (name: string) => string; chipCommit: (sha: string) => string; chipFactAliases: string; chipFactBranch: string; chipFactChanges: string; chipFactContext: string; chipFactDate: string; chipFactModel: string; chipFactPrompts: string; chipFactSize: string; chipFactTurns: string; chipFactUpdated: string; chipLoading: string; chipNotAmongChats: string; chipNotFound: (title: string) => string; chipRemoteFile: (path: string, host: string) => string; chipSessionError: string; chipSessionRunning: string; chipSkill: (name: string) => string; chipTokens: (count: string) => string; chipUntitledChat: string; chooseModel: string; clickToAnswer: string; close: string; closeTab: (name: string) => string; code: string; collapseGroup: (label: string) => string; commandDetail: string; commandSuggestions: string; completionSuggestions: string; composerBadges: string; composerLabel: string; composerPlaceholder: string; composerPlaceholderQueue: string; composerPlaceholderSteer: string; confirm: string; confirmNo: string; confirmYes: string; contextActiveSkills: string; contextAutoCompact: (pct: number) => string; contextCacheRead: string; contextCacheWrite: string; contextClickToCopy: (hasDescription: boolean) => string; contextCopied: string; contextDeferred: string; contextDetail: string; contextEstimated: string; contextFreshInput: string; contextLastTurn: string; contextNoBreakdown: string; contextOutput: string; contextRunCount: (runs: number) => string; contextSessionCost: string; contextSummary: (used: string, window: string, pct: number) => string; contextSummaryCost: (summary: string, cost: string) => string; contextWindow: string; continue: string; continueHint: string; continueRun: string; copied: string; copy: string; copyCode: string; copyDiagramSource: string; copyImage: string; copyImageBase64: string; copyImageDataUrl: string; current: string; declineSecret: string; declineSecretHint: string; defaultAgent: string; delete: string; diagnosticsCheckedBy: (source: string) => string; diagnosticsChecking: string; diagnosticsNone: string; diagram: (type: string | null) => string; diagramError: (message: string, at: string) => string; diagramErrorAt: (line: number, column: number) => string; dismiss: string; dismissHint: string; dotAttached: string; dotAttention: string; dotCompleted: string; dotError: string; dotIdle: string; dotPending: string; dotRunning: string; earlierHistory: string; earlierLines: (count: number) => string; earlierMessages: (count: number) => string; editorConflict: string; editorConflictDetail: string; editorEmpty: string; editorOverwrite: string; editorReadFailed: (message: string) => string; editorReload: string; effortLevel: (level: string) => string; errorFailed: (region: string) => string; errorStack: string; errorTryAgain: string; estimateDue: string; estimateDueTitle: string; estimateOver: string; estimateOverTitle: string; estimatePausedTitle: (left: string) => string; estimateProblemTitle: (reason: string, message?: string) => string; estimateTitle: (left: string) => string; expandGroup: (label: string) => string; favorites: string; filePath: (path: string) => string; fileReadOnly: (path: string) => string; fileTree: string; fileUnsaved: string; fileUnsavedTitle: (path: string) => string; filterActivities: { '1d': string; '30d': string; '3d': string; '7d': string; all: string }; filterActivity: string; filterAgent: string; filterAllAgents: string; filterAllAgentsOption: string; filterAllProjects: string; filterAllProjectsOption: string; filterAny: string; filterNoAgents: string; filterNoProjects: string; filterOrigin: string; filterOrigins: { all: string; scheduled: string; unscheduled: string }; filterPicked: (count: number) => string; filterProject: string; filterSaveView: string; filterSessions: string; filterSort: string; filterSorts: { alphabetical: string; createdTime: string; recency: string }; filterSpaceDefault: string; filterStatus: string; filterStatuses: { active: string; all: string; archived: string }; filterView: string; filterViews: { agent: string; date: string; host: string; none: string; project: string; state: string }; findClose: string; findCloseHint: string; findInConversation: string; findInConversationClose: string; findInFile: string; findMatchCase: string; findNext: string; findNextHint: string; findNoResults: string; findPrevious: string; findPreviousHint: string; findReferences: string; fitDiagram: string; foldOutcomes: (ok: number, failed: number) => string; foldSteps: (count: number) => string; generatedImage: string; generatingImage: (model: string | null) => string; generatingImages: (count: number, model: string | null) => string; goToDefinition: string; goToStep: (step: string) => string; hostBadgeTitle: (host: string, status: string) => string; hostCollapse: (name: string) => string; hostExpand: (name: string) => string; hostOpenSettings: (host: string) => string; hostPicker: string; hostRunsOn: (name: string) => string; hostRunsOnSection: string; hostStatusError: (status: string, error: string) => string; hostStatuses: { 'auth-refused': string; connected: string; connecting: string; identity: string; local: string; offline: string; reconnecting: string; 'version-mismatch': string }; hostThisMachine: string; imageModelReferences: string; imageModelReferencesHint: string; imageModelsNone: string; imageModelsPill: string; imageModelTransparency: string; imageModelTransparencyHint: string; inboxEmpty: string; inboxInProgress: string; inboxNeedsYou: string; inboxPreviousSession: string; inboxRecent: string; inputCaption: string; interactionAnsweredBy: (who: string) => string; interactionApproved: string; interactionDismissed: string; interactionPlan: string; interactionQuestion: string; interactionRejected: string; interactionRevise: string; interactionsLabel: string; interactionWaitingAnswers: string; interactionWaitingDecision: string; justNow: string; loadEarlierHistory: string; loadFullOutput: string; loading: string; loadingEarlierHistory: string; loadingEarlierMessages: string; loadingFullOutput: string; mentionSuggestions: string; modalBack: string; modalFailed: string; modelAccountBlocked: (provider: string) => string; modelChain: string; modelChainNoModel: string; modelChainOrderHint: string; modelFallback: string; modelFallbackHint: string; modelFallbackPosition: (n: number) => string; modelFeatures: string; modelForNextChat: string; modelInUse: string; modelKinds: string; modelLegacy: string; modelLegacyHint: string; modelNotInCatalog: string; modelNotInCatalogHint: string; modelOptions: string; modelOptionsEmpty: string; modelOptionsHint: string; modelPrimary: string; modelPrimaryHint: string; modelReasoning: string; modelReasoningHint: string; modelRemoveFallback: (n: number) => string; modelRemoveFromChain: string; modelRemovePrimary: string; modelsChatTab: string; modelsCustomId: string; modelServing: (picked: string, serving: string) => string; modelServingFallback: string; modelsImageTab: string; modelsInUse: string; modelsJumpHint: (provider: string, digit: number | null) => string; modelsJumpToProvider: string; modelsLegacy: string; modelsLegacyHidden: (count: number) => string; modelsLoadFailed: (error: string) => string; modelsLoading: string; modelsNoMatch: (query: string) => string; modelsNoProviders: string; modelsSelected: (count: number) => string; modelsShowLegacy: string; modelsShowLegacyHint: string; modelsShownOf: (shown: number, total: number) => string; modelsTypeAnId: string; modelUnavailable: string; modelVerdictTitle: (approved: boolean, model: string, confidence: string | null) => string; modelVia: string; modelViaHint: (modelId: string, provider: string) => string; months: readonly string[]; moreActions: string; moreAttachments: (count: number) => string; newSession: string; newSessionIn: (label: string) => string; next: string; noAnswer: string; noMatches: (query: string) => string; noReferences: string; noSessions: string; noSuggestions: string; of: (index: number, count: number) => string; openFiles: string; openGroup: (label: string) => string; openImageFile: (path: string) => string; openInNewTab: string; openNamedSession: (title: string) => string; openSecrets: string; openSession: string; outputCaption: string; paletteBack: string; paletteBackHint: string; paletteChoose: string; paletteEmpty: string; paletteFilter: string; paletteLabel: string; paletteLoading: string; paletteNoMatch: (query: string) => string; paletteOpen: string; paletteRun: string; paletteScopeHint: string; paletteTurnOff: string; paletteTurnOn: string; pickModel: string; pickOneOrMore: string; planApprove: string; planReject: string; planRejectAndStop: string; planRejectNote: string; planRejectPlaceholder: string; planRevise: string; planReviseNote: string; planRevisePlaceholder: string; planSendRevision: string; previewFailed: (name: string) => string; previewTooLarge: (name: string, size: string) => string; previous: string; questionCount: (count: number) => string; queueHint: string; queueMessage: string; reasoningEffort: string; receiptAgent: string; receiptAgentSwitched: string; receiptCost: string; receiptDuration: string; receiptModel: string; receiptModelOthers: (others: number) => string; receiptSpeed: string; receiptTokens: (sent: string, received: string) => string; receiptTurns: (turns: number) => string; reload: string; removeAttachment: (name: string) => string; removeFavorite: string; rename: string; requiredHint: string; requiredMark: string; resetsAt: (time: string) => string; retry: string; retryHint: string; runningFor: (elapsed: string) => string; runReceipt: string; runRetrying: (attempt: number, max: number, source?: string) => string; runWaiting: string; saveSecurely: string; screenBack: (screen: string) => string; screenBackKey: string; scriptCallsDone: (done: number) => string; scriptCallsFailed: (failed: number) => string; scriptCallsOf: (done: number, total: number) => string; scriptCallsRunning: (running: number) => string; scriptLineCalls: (calls: number) => string; scriptLiveOutput: string; scriptLoop: string; scriptLoopRound: (round: number) => string; scriptLoopRounds: (rounds: number) => string; scriptLoopRuns: (runs: number) => string; scriptOptionOutput: string; scriptOptions: string; scriptOptionTimeout: string; scriptOptionTokens: (tokens: number) => string; scriptPercent: (percent: number) => string; scriptProgress: string; scriptWaitLeft: (seconds: number) => string; searchModels: string; searchModelsPlaceholder: string; searchSessions: string; secretDeclined: (name: string) => string; secretNote: (ttl: SecretTtl) => string; secretReused: (name: string) => string; secretSaved: (name: string, left: SecretTimeLeft) => string; secretSent: (name: string) => string; secretTtlLabel: string; secretTtlName: (ttl: SecretTtl) => string; secretValueLabel: string; secretValuePlaceholder: string; selectTurnCopied: string; selectTurnCopy: string; selectTurnDetails: string; selectTurnExit: string; selectTurnFork: string; selectTurnForkFailed: string; selectTurnMove: string; selectTurnNothingToCopy: string; selectTurnSelecting: string; sendAnswers: (count: number) => string; sendHint: string; sendMessage: string; sessionActions: string; sessionEventLabel: (title: string, verb: string, needsAttention: boolean) => string; sessions: string; setModelOptions: string; shellCancelled: string; shellExit: (code: number) => string; shellKilled: string; showDiagram: string; showDiagramSource: string; showMore: string; showMoreAttachments: (count: number) => string; showOutput: string; showSystemPrompt: string; signInCopyCode: string; signInDone: (account?: string) => string; signInEnterCode: (host: string) => string; signInFailed: string; signInInBrowser: (service: string) => string; signInNotOpened: string; signInOpenPage: string; signInPaste: string; signInStarting: string; signInSubmit: string; skip: string; spawnResumes: (childId: string) => string; spawnTask: string; stateCompleted: string; stateError: string; stateIdle: string; stateRunning: string; steerHint: string; steerMessage: string; stepDone: string; stepFailed: string; stepOf: (index: number, count: number) => string; stepResuming: string; stepRunning: string; steps: string; stepWaiting: string; stopCommand: string; stopHint: string; stopped: string; stoppedByYou: string; stopRun: string; switchAgent: string; switchingAgent: string; switchModel: string; terminal: string; terminalExited: (code: number) => string; terminalLoadFailed: (message: string) => string; thinkingLive: string; thoughtFor: (seconds: number) => string; thoughts: string; thoughtTimes: (count: number) => string; today: string; toolAttachments: string; transcriptLoading: string; turnMenuCopy: string; turnMenuCopySelection: string; turnMenuCopySelectionFailed: string; turnMenuDelete: string; turnMenuEdit: string; turnMenuEditNeedsComposer: string; turnMenuFork: string; turnMenuRewind: string; turnMenuSent: (age: string) => string; turnMenuStopFirst: (label: string) => string; turnsCompacted: string; unarchive: string; unsupportedInteraction: (tool: string) => string; viewAttachment: (name: string) => string; waitingForSlot: (max: number) => string; workerContinues: string; workerModel: string; workerSystemPrompt: string; workerSystemReplaced: string; workerType: string; working: string; wrapLines: string; yesterday: string; zoomFit: string; zoomInDiagram: string; zoomOutDiagram: string; zoomPercent: (percent: number) => string }

The chat labels used for the field's strings.

onChange(value: AnswerValue) => void

Called with the new answer when the user edits the field.

onOpenSecrets?() => void

Opens the Secrets panel from an answered secret. Absent: no button.

onSubmit?() => void

Enter in a secret's password field (the form saves).

questionQuestion

The question to draw. Its type picks the control.

readOnly?boolean

The answered form (transcript): the same card, showing what was picked, nothing editable.

requestIdstring

Id of the request. Builds unique element ids for the field.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

valueAnswerValue | undefined

The current answer, or undefined when none is given.

ui/src/chat/interactions/QuestionField.tsx:28

QuestionFormPropsInterface#

Props for QuestionForm.

TypeScript
interface QuestionFormProps
QuestionFormProps properties
NameTypeDescription
autoFocus?boolean

Focus the first control (the card just became the one to answer).

className?string

Extra classes on the root element.

Markdown?ComponentType<MarkdownSlotProps>

Draws the intro; defaults to the transcript's Markdown slot.

requestQuestionRequest

The ask_user request: its intro and questions.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

onAnswer(response: QuestionResponse) => Promise<void>

Called with the answers when the user sends. The form waits on the promise and shows a rejection's message under the controls.

onDismiss?() => Promise<void>

Withdraw the question unanswered. Absent: no Dismiss button.

ui/src/chat/interactions/QuestionForm.tsx:40

SecretInputPropsInterface#

Props for SecretInput.

TypeScript
interface SecretInputProps
SecretInputProps properties
NameTypeDescription
className?string

Extra classes on the root element.

describedBy?string

Ids of the elements that describe the field (the question's help).

idstring

Id of the password field: a <label htmlFor> names it.

inputRef?Ref<HTMLInputElement>

Ref to the password field.

onChange(value: string, ttl: SecretTtl) => void

Called on every keystroke with the typed value and the picked TTL. Keep the value out of anything logged or saved.

onSubmit?() => void

Called on Enter in the password field.

required?boolean

Marks the field required for assistive tech.

style?CSSProperties

Inline styles on the root element, merged after the component's own.

ttlSecretTtl

The TTL picked.

ui/src/chat/interactions/SecretInput.tsx:15