From bdd0dc6f39980ea18f3667016d4bc68f3b5b686c Mon Sep 17 00:00:00 2001 From: iliya Date: Fri, 27 Mar 2026 20:09:01 +0200 Subject: [PATCH] fix(team): add permission_request interception in stdout handler + diagnostic logging - Parse raw user text for permission_request in handleStreamJsonMessage (covers case where permission_request arrives without wrapper) - Add [PERM-TRACE] logger.warn diagnostics to trace the exact flow: where permission_request is detected, whether it reaches handleTeammatePermissionRequest, and whether relay or stdout interception triggers - These logs will help diagnose why ToolApprovalSheet may not appear --- .../services/team/TeamProvisioningService.ts | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index eeb92511..98b24a80 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -4055,6 +4055,9 @@ export class TeamProvisioningService { ); const permissionRequestIds = new Set(permissionRequestMsgs.map((m) => m.messageId)); if (permissionRequestMsgs.length > 0) { + logger.warn( + `[${run.teamName}] [PERM-TRACE] relay intercepted ${permissionRequestMsgs.length} permission_request(s) from inbox` + ); for (const msg of permissionRequestMsgs) { const perm = parsePermissionRequest(msg.text)!; this.handleTeammatePermissionRequest(run, perm, msg.timestamp); @@ -4907,6 +4910,24 @@ export class TeamProvisioningService { // {"type":"assistant","content":[{"type":"text","text":"..."},...]} // {"type":"result","subtype":"success",...} if (msg.type === 'user') { + // Check for permission_request in raw user message text BEFORE teammate-message parsing. + // The permission_request may arrive as plain JSON without wrapper, + // and handleNativeTeammateUserMessage only processes blocks. + const rawUserText = this.extractStreamUserText(msg); + if (rawUserText) { + const perm = parsePermissionRequest(rawUserText); + if (perm) { + logger.warn( + `[${run.teamName}] [PERM-TRACE] Intercepted permission_request from stdout user message: agent=${perm.agentId} tool=${perm.toolName} requestId=${perm.requestId}` + ); + this.handleTeammatePermissionRequest(run, perm, new Date().toISOString()); + } else if (rawUserText.includes('permission_request')) { + // Log near-miss: text contains "permission_request" but wasn't parsed + logger.warn( + `[${run.teamName}] [PERM-TRACE] stdout user message contains "permission_request" but parsePermissionRequest returned null. Text preview: ${rawUserText.slice(0, 300)}` + ); + } + } this.handleNativeTeammateUserMessage(run, msg); return; } @@ -5586,7 +5607,16 @@ export class TeamProvisioningService { messageTimestamp: string ): void { // Skip if already tracked (idempotency — relay can be called multiple times) - if (run.pendingApprovals.has(perm.requestId)) return; + if (run.pendingApprovals.has(perm.requestId)) { + logger.warn( + `[${run.teamName}] [PERM-TRACE] Duplicate permission_request skipped: ${perm.requestId}` + ); + return; + } + + logger.warn( + `[${run.teamName}] [PERM-TRACE] handleTeammatePermissionRequest: agent=${perm.agentId} tool=${perm.toolName} requestId=${perm.requestId}` + ); const approval: ToolApprovalRequest = { requestId: perm.requestId,