(
workspaceId: string,
toolCallId: string,
answers: Record<string, string>
)
| 7598 | } |
| 7599 | |
| 7600 | async answerAskUserQuestion( |
| 7601 | workspaceId: string, |
| 7602 | toolCallId: string, |
| 7603 | answers: Record<string, string> |
| 7604 | ): Promise<Result<void>> { |
| 7605 | try { |
| 7606 | // Fast path: normal in-memory execution (stream still running, tool is awaiting input). |
| 7607 | askUserQuestionManager.answer(workspaceId, toolCallId, answers); |
| 7608 | return Ok(undefined); |
| 7609 | } catch (error) { |
| 7610 | // Fallback path: app restart (or other process death) means the in-memory |
| 7611 | // AskUserQuestionManager has no pending entry anymore. |
| 7612 | // |
| 7613 | // In that case we persist the tool result into partial.json or chat.jsonl, |
| 7614 | // then emit a synthetic tool-call-end so the renderer updates immediately. |
| 7615 | try { |
| 7616 | // Helper: update a message in-place if it contains this ask_user_question tool call. |
| 7617 | const tryFinalizeMessage = ( |
| 7618 | msg: MuxMessage |
| 7619 | ): Result<{ updated: MuxMessage; output: AskUserQuestionToolSuccessResult }> => { |
| 7620 | let foundToolCall = false; |
| 7621 | let output: AskUserQuestionToolSuccessResult | null = null; |
| 7622 | let errorMessage: string | null = null; |
| 7623 | |
| 7624 | const updatedParts = msg.parts.map((part) => { |
| 7625 | if (!isDynamicToolPart(part) || part.toolCallId !== toolCallId) { |
| 7626 | return part; |
| 7627 | } |
| 7628 | |
| 7629 | foundToolCall = true; |
| 7630 | |
| 7631 | if (part.toolName !== "ask_user_question") { |
| 7632 | errorMessage = `toolCallId=${toolCallId} is toolName=${part.toolName}, expected ask_user_question`; |
| 7633 | return part; |
| 7634 | } |
| 7635 | |
| 7636 | // Already answered - treat as idempotent. |
| 7637 | if (part.state === "output-available") { |
| 7638 | const parsedOutput = AskUserQuestionToolResultSchema.safeParse(part.output); |
| 7639 | if (!parsedOutput.success) { |
| 7640 | errorMessage = `ask_user_question output validation failed: ${parsedOutput.error.message}`; |
| 7641 | return part; |
| 7642 | } |
| 7643 | output = parsedOutput.data; |
| 7644 | return part; |
| 7645 | } |
| 7646 | |
| 7647 | const parsedArgs = AskUserQuestionToolArgsSchema.safeParse(part.input); |
| 7648 | if (!parsedArgs.success) { |
| 7649 | errorMessage = `ask_user_question input validation failed: ${parsedArgs.error.message}`; |
| 7650 | return part; |
| 7651 | } |
| 7652 | |
| 7653 | const nextOutput: AskUserQuestionToolSuccessResult = { |
| 7654 | summary: buildAskUserQuestionSummary(answers), |
| 7655 | ui_only: { |
| 7656 | ask_user_question: { |
| 7657 | questions: parsedArgs.data.questions, |
no test coverage detected