* Creates a canUseTool function for in-process teammates that properly resolves * 'ask' permissions via the UI rather than treating them as denials. * * Always uses the leader's ToolUseConfirm dialog with a worker badge when * the bridge is available, giving teammates the same tool-specific UI
( identity: TeammateIdentity, abortController: AbortController, onPermissionWaitMs?: (waitMs: number) => void, )
| 126 | * in the teammate's own mailbox. |
| 127 | */ |
| 128 | function createInProcessCanUseTool( |
| 129 | identity: TeammateIdentity, |
| 130 | abortController: AbortController, |
| 131 | onPermissionWaitMs?: (waitMs: number) => void, |
| 132 | ): CanUseToolFn { |
| 133 | return async ( |
| 134 | tool, |
| 135 | input, |
| 136 | toolUseContext, |
| 137 | assistantMessage, |
| 138 | toolUseID, |
| 139 | forceDecision, |
| 140 | ) => { |
| 141 | const result = |
| 142 | forceDecision ?? |
| 143 | (await hasPermissionsToUseTool( |
| 144 | tool, |
| 145 | input, |
| 146 | toolUseContext, |
| 147 | assistantMessage, |
| 148 | toolUseID, |
| 149 | )) |
| 150 | |
| 151 | // Pass through allow/deny decisions directly |
| 152 | if (result.behavior !== 'ask') { |
| 153 | return result |
| 154 | } |
| 155 | |
| 156 | // For bash commands, try classifier auto-approval before showing leader dialog. |
| 157 | // Agents await the classifier result (rather than racing it against user |
| 158 | // interaction like the main agent). |
| 159 | if ( |
| 160 | feature('BASH_CLASSIFIER') && |
| 161 | tool.name === BASH_TOOL_NAME && |
| 162 | result.pendingClassifierCheck |
| 163 | ) { |
| 164 | const classifierDecision = await awaitClassifierAutoApproval( |
| 165 | result.pendingClassifierCheck, |
| 166 | abortController.signal, |
| 167 | toolUseContext.options.isNonInteractiveSession, |
| 168 | ) |
| 169 | if (classifierDecision) { |
| 170 | return { |
| 171 | behavior: 'allow', |
| 172 | updatedInput: input as Record<string, unknown>, |
| 173 | decisionReason: classifierDecision, |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | // Check if aborted before showing UI |
| 179 | if (abortController.signal.aborted) { |
| 180 | return { behavior: 'ask', message: SUBAGENT_REJECT_MESSAGE } |
| 181 | } |
| 182 | |
| 183 | const appState = toolUseContext.getAppState() |
| 184 | |
| 185 | const description = await (tool as Tool).description(input as never, { |
no test coverage detected