mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 02:27:43 +00:00
* fix(mobile): draw answered approvals and questions as receipts, and a pending question as its ask row
The phone projected a structured chat's journal with the host's plain
projection: an answered approval read "Allow Bash? {json} resolved", an
answered question listed its choices without the answer, and every
AskUserQuestion call stayed a raw tool line, also while its card was open
above the composer.
Desktop's question projection and receipt rules move to src/shared
(structured-agent-question-projection, native-chat-resolution-receipt,
with the receipt subject and answer heading and their words). The phone's
structured session now projects through them and hands each row its
receipt; new MobileNativeChatResolutionReceipt and
MobileNativeChatAwaitingInputRow draw them as desktop does. In a terminal
chat, a question call draws as "Awaiting user input:" until answered and
"Asked:" after.
* fix(mobile): say a question was asked, not awaited, once its agent has ended
A structured chat's question stays pending in the journal after the agent
that asked it ends (a restart, a crash, a new generation). The host stops
listing it as actionable, so the answer card goes away, but the phone's
row still read "Awaiting user input" and announced itself as live.
The session hook now returns the host's actionable prompt ids next to the
receipts, and each row asks the same shared
isActionableStructuredAgentSessionPrompt desktop's transcript row calls.
A question nobody waits on reads "Asked: …", as on desktop. The row
callback moves to its own hook so the chat view stays within its size.
* fix(mobile): decide a native chat's open question with desktop's selector
The phone decided whether a question call was still awaited with its own
rule (no answer yet and the turn working). Desktop asks the shared
selectActiveToolCall, which also reads the call's state, so a call marked
completed with no output reads "Asked" there and "Awaiting" on the phone.
Native chats now call the same selector. Terminal chats keep their rule.
* fix(mobile): zoom a receipt and its question row with the chat
Before receipts, an answered question or approval was a faint system line
that grew with the chat's pinch zoom. The receipt and the question row drew
at fixed sizes, so zooming no longer enlarged them. Both now take the
chat's zoom, through the same scaledType the notice row uses, which moves
next to the other row styles.
* fix(mobile): list a group's questions once nobody can answer them, and zoom the row's icon
A grouped prompt's row names only a count ("Asked: 2 questions"). Once it
is cancelled, or the agent that asked has ended, the answer card is gone
and the receipt lists no answers, so the phone showed the questions
nowhere. The row now lists them under its line, following desktop's
listsQuestions rule: the receipt asks for the list only when it shows no
answers, and the transcript's own question rows keep it off, as desktop's
tool run does. While the host waits, the card shows them.
The row's icon also grows with the chat's zoom, as the notice row's icon
does, so it stays level with its line.
* refactor(mobile): trim comments and simplify the question row and receipt
Comments that restated the code go. The answer heading takes the subject
its caller already has, dropping two conditions that never changed the
result, on desktop and the phone. The question row lists a group's
questions whenever its caller asks, as desktop's does, and the receipt
asks only while nobody waits. fontScale is required on both new rows.
119 lines
6.3 KiB
TypeScript
119 lines
6.3 KiB
TypeScript
import type { Dispatch, MutableRefObject, SetStateAction } from 'react'
|
|
import type {
|
|
AskAnswerSelection,
|
|
AskPrompt,
|
|
parseAskFromStatus
|
|
} from '../../../src/shared/native-chat-ask'
|
|
import type { detectAgentPermission } from './mobile-native-chat-permission'
|
|
import type { parseAgentQuestion } from './mobile-native-chat-question'
|
|
import type {
|
|
NativeChatLiveTurnIndicator,
|
|
NativeChatSettledTurns
|
|
} from '../../../src/shared/native-chat-turn-status'
|
|
import type { NativeChatTurnJournal } from '../../../src/shared/native-chat-turn-membership'
|
|
import type { NativeChatResolvedPrompt } from '../../../src/shared/native-chat-resolution-receipt'
|
|
import type { MobileNativeChatSendOutcome } from './mobile-native-chat-send'
|
|
import type { MobileNativeChatCommandRefusalCauses } from './use-mobile-native-chat-send-error'
|
|
import type { MobileStructuredQueuedMessageControls } from './use-mobile-structured-queued-message-controls'
|
|
import type { MobileStructuredBackgroundTasks } from './use-mobile-structured-background-tasks'
|
|
import type { MobileNativeChatPendingMessage } from './use-mobile-native-chat-drafts'
|
|
import type { useMobileNativeChatSession } from './use-mobile-native-chat-session'
|
|
import type { MobileNativeChatSessionOptionPickersProps } from './MobileNativeChatSessionOptionPickers'
|
|
import type { MobileNativeChatVisualSource } from './mobile-native-chat-visual-read'
|
|
|
|
export type MobileNativeChatController = {
|
|
/** Whether a tab's effective view is chat (per-tab override, else the default). */
|
|
isTabChatView: (tabId: string) => boolean
|
|
toggleTabChatView: (tabId: string) => void
|
|
showNativeChat: boolean
|
|
showNativeChatRef: MutableRefObject<boolean>
|
|
/** Resolved agent for the active chat tab (names the empty-state copy). */
|
|
nativeChatAgent: string | null
|
|
chatComposerText: string
|
|
setChatComposerText: Dispatch<SetStateAction<string>>
|
|
getChatComposerEditGeneration: () => number
|
|
chatPending: MobileNativeChatPendingMessage[]
|
|
chatImagePreviewsByMessageId: Record<string, string[]>
|
|
nativeChatSession: ReturnType<typeof useMobileNativeChatSession>
|
|
/** Structured lane: drives the per-turn status row and live tool progress. */
|
|
nativeChatStructured: boolean
|
|
/** Structured lane with a live client: where the transcript's visuals are read from. */
|
|
nativeChatVisualSource: MobileNativeChatVisualSource | null
|
|
nativeChatAgentWorking: boolean
|
|
/** What labels the live turn's one indicator row; null off the structured lane. */
|
|
nativeChatTurnIndicator: NativeChatLiveTurnIndicator | null
|
|
/** Structured lane: host-recorded turn timing for the per-turn status rows. */
|
|
nativeChatWorkingStartedAt: number | null
|
|
nativeChatSettledTurns: NativeChatSettledTurns | null
|
|
/** Structured lane: the journal that places each transcript row in its turn. */
|
|
nativeChatTurnJournal: NativeChatTurnJournal | null
|
|
nativeChatReceipts: ReadonlyMap<string, NativeChatResolvedPrompt> | null
|
|
nativeChatActionablePromptIds: readonly string[] | undefined
|
|
/** What a refused command's line stands on. */
|
|
nativeChatCommandRefusalCauses: MobileNativeChatCommandRefusalCauses
|
|
nativeChatCanStop: boolean
|
|
nativeChatStreamingText?: string
|
|
/** Agent mid-turn, regardless of whether chat is the visible view. */
|
|
nativeChatStreamLive: boolean
|
|
/** Host/workspace/tab/session scope for stateful streaming suppression. */
|
|
nativeChatStreamScopeKey: string
|
|
nativeChatPermission: ReturnType<typeof detectAgentPermission>
|
|
/** Terminal prompt occurrence; a replacement resets only the card's local submission state. */
|
|
nativeChatPromptKey: string | null
|
|
nativeChatQuestion: ReturnType<typeof parseAgentQuestion>
|
|
/** The pending ask, already null while dismissed (dismissal lives here so it
|
|
* survives the chat-view subtree unmounting on a view toggle). */
|
|
nativeChatAsk: ReturnType<typeof parseAskFromStatus>
|
|
/** Stable key for the current ask card (keys the card component). */
|
|
nativeChatAskKey: string | null
|
|
/** Hide the current ask until a genuinely different question arrives. */
|
|
dismissNativeChatAsk: () => void
|
|
handleNativeChatAnswerAsk: (
|
|
prompt: AskPrompt,
|
|
selections: AskAnswerSelection[]
|
|
) => Promise<boolean>
|
|
handleNativeChatCancelAsk: () => Promise<boolean>
|
|
/** Terminal lane: fold the occurrence to a strip and free Send, writing nothing. */
|
|
collapseNativeChatAsk: () => void
|
|
collapseNativeChatPrompt?: () => void
|
|
/** The collapsed terminal occurrence, shown as a strip above the composer. */
|
|
nativeChatCollapsedPrompt: { title: string; expand: () => void } | null
|
|
handleNativeChatCancelPrompt?: (prompt?: {
|
|
itemId: string
|
|
expectedRevision: number
|
|
}) => Promise<boolean>
|
|
handleNativeChatRespondPermission: (text: string) => Promise<boolean>
|
|
handleNativeChatStop: () => void
|
|
/** Host-held queued drafts shown as cards above the composer (structured lane; any host
|
|
* that publishes them). */
|
|
nativeChatQueued: MobileStructuredQueuedMessageControls
|
|
/** Running child work shown in a strip above the composer; null off the structured lane. */
|
|
nativeChatBackgroundTasks: MobileStructuredBackgroundTasks | null
|
|
nativeChatFilePaths: string[]
|
|
loadNativeChatFiles: (query: string) => void
|
|
handleNativeChatQuestionAnswer: (text: string) => Promise<boolean>
|
|
handleNativeChatSend: (text: string, images?: string[]) => Promise<boolean>
|
|
/** Outcome-preserving send: callers that pasted terminal input beforehand
|
|
* (image sends) must see 'unknown' to heal a possibly-orphaned paste. Such a
|
|
* caller passes its own `deadline` so the paste it already spent and this text
|
|
* body share one budget instead of holding the composer for two. */
|
|
handleNativeChatSendWithOutcome: (
|
|
text: string,
|
|
images?: string[],
|
|
deadline?: number,
|
|
attachments?: readonly {
|
|
id?: string
|
|
path: string
|
|
previewUri: string
|
|
}[]
|
|
) => Promise<MobileNativeChatSendOutcome>
|
|
/** Launch-context text still parked on the agent's TUI input line, or null.
|
|
* Image sends read it to size their leading clear (one Ctrl+U per line). */
|
|
readSeededLaunchDraft: () => string | null
|
|
/** Model/session-option pickers for the composer, or null when the active
|
|
* agent has no session-option catalog. */
|
|
nativeChatSessionOptions: MobileNativeChatSessionOptionPickersProps | null
|
|
/** Whether the composer offers images: a structured chat by its options report. */
|
|
nativeChatAcceptsImages: boolean
|
|
}
|