* Bridge an SDK ApprovalRequest through the ACP reverse-RPC * `session/request_permission`. * * Flow: * 1. Build the wire-level ToolCallUpdate so the client can * correlate the prompt with the tool card it already rendered * (uses the prefixed `${turnId}:${
(req: ApprovalRequest)
| 1227 | * needs human authorization to proceed with a tool call. |
| 1228 | */ |
| 1229 | private async handleApproval(req: ApprovalRequest): Promise<ApprovalResponse> { |
| 1230 | const toolCall = buildPermissionToolCallUpdate(this.currentTurnId, req); |
| 1231 | const options = approvalRequestToPermissionOptions(req); |
| 1232 | // Phase 13.2 telemetry breadcrumb: how many discrete options does |
| 1233 | // the plan_review surface carry? PII-free (just a count), matches |
| 1234 | // the Phase 11.2 telemetry discipline. |
| 1235 | if (req.display.kind === 'plan_review') { |
| 1236 | const count = req.display.options?.length ?? 0; |
| 1237 | this.emitTelemetry('plan_review_options_count', { count }); |
| 1238 | } |
| 1239 | try { |
| 1240 | // `requestPermission` is an awaitable JSON-RPC request (unlike |
| 1241 | // the fire-and-forget `sessionUpdate` notifications elsewhere in |
| 1242 | // this file), so the SDK call site naturally blocks on the |
| 1243 | // user's decision before the tool runs. |
| 1244 | const response = await this.conn.requestPermission({ |
| 1245 | sessionId: this.id, |
| 1246 | options: [...options], |
| 1247 | toolCall, |
| 1248 | }); |
| 1249 | // Map the discriminator first (pure mapper, easy to unit-test), |
| 1250 | // then stitch the matched option's human-readable name as |
| 1251 | // `selectedLabel` so the SDK can surface "approved as |
| 1252 | // 'Approve once'" in subsequent reasoning. `attachSelectedLabel` |
| 1253 | // is a no-op for `cancelled` outcomes, unknown optionIds, and |
| 1254 | // plan_* optionIds (Phase 13.2 — the plan_review branch attaches |
| 1255 | // selectedLabel inside `permissionResponseToApprovalResponse`). |
| 1256 | return attachSelectedLabel( |
| 1257 | response, |
| 1258 | permissionResponseToApprovalResponse(req, response), |
| 1259 | options, |
| 1260 | ); |
| 1261 | } catch (err) { |
| 1262 | log.warn('acp: requestPermission failed; rejecting', { |
| 1263 | sessionId: this.id, |
| 1264 | toolCallId: req.toolCallId, |
| 1265 | toolName: req.toolName, |
| 1266 | error: err instanceof Error ? err.message : String(err), |
| 1267 | }); |
| 1268 | return { decision: 'rejected' }; |
| 1269 | } |
| 1270 | } |
| 1271 | |
| 1272 | /** |
| 1273 | * Bridge an SDK {@link QuestionRequest} (the AskUserQuestion tool's |
no test coverage detected