(props: {
args: AskUserQuestionToolArgs;
result: AskUserQuestionToolResult | null;
status: ToolStatus;
toolCallId: string;
workspaceId?: string;
})
| 217 | } |
| 218 | |
| 219 | export function AskUserQuestionToolCall(props: { |
| 220 | args: AskUserQuestionToolArgs; |
| 221 | result: AskUserQuestionToolResult | null; |
| 222 | status: ToolStatus; |
| 223 | toolCallId: string; |
| 224 | workspaceId?: string; |
| 225 | }): JSX.Element { |
| 226 | const { api } = useAPI(); |
| 227 | |
| 228 | // A live, blocking question must never be hidden behind a collapsed "tools" |
| 229 | // preference, so force it open while executing. forceExpanded only overrides the |
| 230 | // initial seed; once answered the row follows the sticky preference like any tool. |
| 231 | const { expanded, toggleExpanded } = useToolExpansion(false, { |
| 232 | forceExpanded: props.status === "executing", |
| 233 | }); |
| 234 | const statusDisplay = getStatusDisplay(props.status); |
| 235 | |
| 236 | const argsAnswers = props.args.answers ?? {}; |
| 237 | |
| 238 | // Restore from cache if available (survives workspace switches) |
| 239 | const cachedState = draftStateCache.get(props.toolCallId); |
| 240 | |
| 241 | const [activeIndex, setActiveIndex] = useState(() => cachedState?.activeIndex ?? 0); |
| 242 | const [isSubmitting, setIsSubmitting] = useState(false); |
| 243 | const [submitError, setSubmitError] = useState<string | null>(null); |
| 244 | |
| 245 | const workspaceStore = useWorkspaceStoreRaw(); |
| 246 | const workspaceState = useSyncExternalStore( |
| 247 | (listener) => { |
| 248 | if (!props.workspaceId) { |
| 249 | return () => undefined; |
| 250 | } |
| 251 | |
| 252 | return workspaceStore.subscribeKey(props.workspaceId, listener); |
| 253 | }, |
| 254 | () => { |
| 255 | if (!props.workspaceId) { |
| 256 | return null; |
| 257 | } |
| 258 | |
| 259 | // Some render paths (nested tool rendering) do not have workspace context. |
| 260 | // Fail-soft so ask_user_question still renders instead of crashing the tree. |
| 261 | try { |
| 262 | return workspaceStore.getWorkspaceState(props.workspaceId); |
| 263 | } catch { |
| 264 | return null; |
| 265 | } |
| 266 | } |
| 267 | ); |
| 268 | const autoRetryRollbackWorkspaceIdRef = useRef<string | null>(null); |
| 269 | const autoRetryRollbackPendingRef = useRef(false); |
| 270 | const autoRetryRollbackArmedRef = useRef(false); |
| 271 | const autoRetryRollbackBaselineMessageCountRef = useRef<number | null>(null); |
| 272 | const isMountedRef = useRef(true); |
| 273 | const apiRef = useRef(api); |
| 274 | |
| 275 | useEffect(() => { |
| 276 | apiRef.current = api; |
nothing calls this directly
no test coverage detected