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