(toolCall: PendingToolCall)
| 751 | } |
| 752 | |
| 753 | private async handleToolCall(toolCall: PendingToolCall): Promise<string> { |
| 754 | const { onEvent, requestApproval, requestFollowup } = this.options.callbacks |
| 755 | |
| 756 | let args: Record<string, unknown> |
| 757 | try { |
| 758 | args = toolCall.arguments ? JSON.parse(toolCall.arguments) : {} |
| 759 | } catch (error) { |
| 760 | const message = `Invalid JSON arguments for ${toolCall.name}: ${(error as Error).message}` |
| 761 | onEvent({ |
| 762 | type: "tool-end", |
| 763 | id: toolCall.id, |
| 764 | name: toolCall.name, |
| 765 | summary: toolCall.name, |
| 766 | resultPreview: message, |
| 767 | isError: true, |
| 768 | }) |
| 769 | return message |
| 770 | } |
| 771 | |
| 772 | if (toolCall.name === "attempt_completion") { |
| 773 | onEvent({ type: "completion", result: String(args.result ?? "") }) |
| 774 | return "The user has been shown the completion result." |
| 775 | } |
| 776 | |
| 777 | if (toolCall.name === "ask_followup_question") { |
| 778 | const question = String(args.question ?? "") |
| 779 | const suggestions = (Array.isArray(args.follow_up) ? args.follow_up : []) |
| 780 | .map((s: { text?: string }) => ({ text: String(s?.text ?? "") })) |
| 781 | .filter((s: { text: string }) => s.text) |
| 782 | // Notification fires whenever OrbCode pauses to wait on the user. |
| 783 | if (this.hooks.hasHooks("Notification")) { |
| 784 | this.trackBackground(this.hooks.run("Notification", { |
| 785 | message: question || "OrbCode is asking a follow-up question.", |
| 786 | })) |
| 787 | } |
| 788 | const answer = await requestFollowup(question, suggestions) |
| 789 | return `<answer>\n${answer}\n</answer>` |
| 790 | } |
| 791 | |
| 792 | // PreToolUse runs before approval/execution. It can block the call, |
| 793 | // override the approval decision, rewrite the tool input, or add context. |
| 794 | let preContext = "" |
| 795 | let bypassApproval = false |
| 796 | let forceApproval = false |
| 797 | if (this.hooks.hasHooks("PreToolUse")) { |
| 798 | const pre = await this.hooks.run("PreToolUse", { tool_name: toolCall.name, tool_input: args }) |
| 799 | if (pre.stopAll || pre.blocked || pre.permissionDecision === "deny") { |
| 800 | const reason = |
| 801 | pre.blockReason || pre.stopReason || pre.permissionReason || "Blocked by a PreToolUse hook." |
| 802 | const blockedSummary = describeToolCall(toolCall.name, args) |
| 803 | onEvent({ type: "tool-start", id: toolCall.id, name: toolCall.name, summary: blockedSummary }) |
| 804 | onEvent({ |
| 805 | type: "tool-end", |
| 806 | id: toolCall.id, |
| 807 | name: toolCall.name, |
| 808 | summary: blockedSummary, |
| 809 | resultPreview: reason, |
| 810 | isError: true, |
no test coverage detected