Files
orca/mobile/src/session/MobileNativeChatPromptCard.tsx
T
Brennan BensonandMerge Sim f55b7ba680 fix(native-chat): cancel pending prompts precisely (#20601)
* fix(native-chat): hide activity while awaiting input

* fix(native-chat): keep approval turns cancellable

* test(native-chat): satisfy split PR quality gate

* fix(native-chat): catalog approval cancellation label

* fix(native-chat): include approval cancellation runtime label

* fix(codex): settle prompts when cancelled turns complete

* fix(codex): settle prompt registry fallbacks

* test(native-chat): cover pending interaction fallbacks

* test(native-chat): split prompt state coverage

* test(native-chat): keep prompt state isolated

* fix(native-chat): bound prompt turn backfill

* refactor(codex): centralize prompt registry bounds

* fix(native-chat): cancel pending prompts precisely

* fix(native-chat): consolidate capability imports

* fix(native-chat): harden precise prompt cancellation

* fix claude cancellation teardown races

* retry claude prompt lifecycle admission

* bound claude prompt cancellation retry work

* fix(codex): bound prompt turn identity on registration

* fix(native-chat): route rejected late dispatch settlements

* fix(codex): retain exact cancellable prompt turn ids

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-14 14:59:03 -07:00

79 lines
2.5 KiB
TypeScript

import type { AskAnswerSelection, AskPrompt } from '../../../src/shared/native-chat-ask'
import { MobileNativeChatAsk } from './MobileNativeChatAsk'
import { MobileNativeChatPermission } from './MobileNativeChatPermission'
import type { MobileChatPermission } from './mobile-native-chat-permission'
import { MobileNativeChatQuestion } from './MobileNativeChatQuestion'
import { mobileChatQuestionKey, type MobileChatQuestion } from './mobile-native-chat-question'
/** The one pending agent prompt shown above the composer: a structured
* AskUserQuestion wins, then a heuristic permission, then a heuristic question.
* The controller owns dismissal (it must survive this subtree unmounting on a
* view toggle); `ask` arrives already nulled while dismissed. */
export function MobileNativeChatPromptCard({
ask,
askKey,
onDismissAsk,
onAnswerAsk,
onCancelAsk,
onCancelPrompt,
permission,
onRespondPermission,
question,
onAnswerQuestion
}: {
ask?: AskPrompt | null
askKey?: string | null
onDismissAsk?: () => void
onAnswerAsk?: (prompt: AskPrompt, selections: AskAnswerSelection[]) => Promise<boolean>
onCancelAsk?: () => Promise<boolean>
onCancelPrompt?: (prompt?: NonNullable<MobileChatPermission['prompt']>) => Promise<boolean>
permission?: MobileChatPermission | null
onRespondPermission?: (send: string) => Promise<boolean>
question?: MobileChatQuestion | null
onAnswerQuestion?: (text: string) => Promise<boolean>
}): React.JSX.Element | null {
if (ask) {
return (
<MobileNativeChatAsk
key={askKey ?? 'ask'}
prompt={ask}
onAnswer={async (selections) => {
const accepted = (await onAnswerAsk?.(ask, selections)) ?? false
if (accepted) {
onDismissAsk?.()
}
return accepted
}}
onCancel={async () => {
const accepted = (await onCancelAsk?.()) ?? false
if (accepted) {
onDismissAsk?.()
}
return accepted
}}
/>
)
}
if (permission) {
return (
<MobileNativeChatPermission
key={JSON.stringify(permission)}
permission={permission}
onRespond={async (send) => (await onRespondPermission?.(send)) ?? false}
onCancel={onCancelPrompt}
/>
)
}
if (question) {
return (
<MobileNativeChatQuestion
key={mobileChatQuestionKey(question)}
question={question}
onAnswer={async (text) => (await onAnswerQuestion?.(text)) ?? false}
onCancel={onCancelPrompt}
/>
)
}
return null
}