From 9bd6123f97b4058d77aed1296688e2b6fb3266cc Mon Sep 17 00:00:00 2001 From: josedario87 Date: Wed, 18 Feb 2026 23:55:09 -0600 Subject: [PATCH] feat: Add transcript-debug page with multi-agent support, hooks approval, and message selection - Transcript debug: JSONL viewer, parsed chat view, realtime WebSocket updates, session selector - Multi-agent: ejecutor, nucleo000, and claude (global ~/.claude/projects/) with agent switcher - Hooks approval: permission/plan request forwarding via PowerShell hooks, long-poll API, UI modals - Chat features: session ID copy, select mode with checkboxes, multi-select copy, select all/deselect all - File watchers for all agent transcript directories with polling fallback on Windows --- .claude-ejecutor/settings.json | 33 +- .claude-isolated/.gitignore | 10 - .claude-isolated/settings.json | 9 - .claude-nucleo000/settings.json | 106 ++- frontend/src/App.vue | 73 +++ .../src/components/HooksApprovalModal.vue | 260 ++++++++ frontend/src/components/Toolbar.vue | 9 + .../AssistantMessageBubble.vue | 189 ++++++ .../transcript-debug/ChatContainer.vue | 583 ++++++++++++++++ .../transcript-debug/PermissionApproval.vue | 213 ++++++ .../transcript-debug/PlanApproval.vue | 253 +++++++ .../transcript-debug/ProgressEvent.vue | 279 ++++++++ .../transcript-debug/RawJsonViewer.vue | 216 ++++++ .../transcript-debug/SessionSelector.vue | 110 ++++ .../transcript-debug/SystemMessage.vue | 90 +++ .../transcript-debug/ThinkingBlock.vue | 87 +++ .../transcript-debug/ToolCallBlock.vue | 390 +++++++++++ .../transcript-debug/ToolResultBlock.vue | 102 +++ .../components/transcript-debug/UserInput.vue | 166 +++++ .../transcript-debug/UserMessageBubble.vue | 122 ++++ .../src/components/transcript-debug/index.ts | 13 + .../src/composables/transcript-debug/index.ts | 2 + .../transcript-debug/useHooksApproval.ts | 111 ++++ .../transcript-debug/useTranscriptDebug.ts | 620 ++++++++++++++++++ frontend/src/composables/useGlobalApproval.ts | 199 ++++++ frontend/src/pages/TranscriptDebugPage.vue | 344 ++++++++++ frontend/src/router/index.ts | 5 + frontend/src/types/hooks-approval.ts | 17 + frontend/src/types/transcript-debug.ts | 227 +++++++ hooks/approval-permission.ps1 | 29 + hooks/approval-plan.ps1 | 31 + hooks/forward-hook.ps1 | 7 + server/routes/hooks-approval.ts | 330 ++++++++++ server/routes/index.ts | 52 ++ server/routes/transcript-debug.ts | 221 +++++++ .../handlers/transcript-debug-handler.ts | 160 +++++ server/services/sync-server.ts | 25 +- 37 files changed, 5663 insertions(+), 30 deletions(-) delete mode 100644 .claude-isolated/.gitignore delete mode 100644 .claude-isolated/settings.json create mode 100644 frontend/src/components/HooksApprovalModal.vue create mode 100644 frontend/src/components/transcript-debug/AssistantMessageBubble.vue create mode 100644 frontend/src/components/transcript-debug/ChatContainer.vue create mode 100644 frontend/src/components/transcript-debug/PermissionApproval.vue create mode 100644 frontend/src/components/transcript-debug/PlanApproval.vue create mode 100644 frontend/src/components/transcript-debug/ProgressEvent.vue create mode 100644 frontend/src/components/transcript-debug/RawJsonViewer.vue create mode 100644 frontend/src/components/transcript-debug/SessionSelector.vue create mode 100644 frontend/src/components/transcript-debug/SystemMessage.vue create mode 100644 frontend/src/components/transcript-debug/ThinkingBlock.vue create mode 100644 frontend/src/components/transcript-debug/ToolCallBlock.vue create mode 100644 frontend/src/components/transcript-debug/ToolResultBlock.vue create mode 100644 frontend/src/components/transcript-debug/UserInput.vue create mode 100644 frontend/src/components/transcript-debug/UserMessageBubble.vue create mode 100644 frontend/src/components/transcript-debug/index.ts create mode 100644 frontend/src/composables/transcript-debug/index.ts create mode 100644 frontend/src/composables/transcript-debug/useHooksApproval.ts create mode 100644 frontend/src/composables/transcript-debug/useTranscriptDebug.ts create mode 100644 frontend/src/composables/useGlobalApproval.ts create mode 100644 frontend/src/pages/TranscriptDebugPage.vue create mode 100644 frontend/src/types/hooks-approval.ts create mode 100644 frontend/src/types/transcript-debug.ts create mode 100644 hooks/approval-permission.ps1 create mode 100644 hooks/approval-plan.ps1 create mode 100644 hooks/forward-hook.ps1 create mode 100644 server/routes/hooks-approval.ts create mode 100644 server/routes/transcript-debug.ts create mode 100644 server/services/handlers/transcript-debug-handler.ts diff --git a/.claude-ejecutor/settings.json b/.claude-ejecutor/settings.json index cb7be00..744506d 100644 --- a/.claude-ejecutor/settings.json +++ b/.claude-ejecutor/settings.json @@ -26,7 +26,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=ejecutor' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 ejecutor", "timeout": 5000 } ] @@ -38,7 +38,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=ejecutor' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 ejecutor", "timeout": 5000 } ] @@ -50,7 +50,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=ejecutor' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 ejecutor", "timeout": 5000 } ] @@ -61,7 +61,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=ejecutor' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 ejecutor", "timeout": 5000 } ] @@ -73,7 +73,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=ejecutor' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 ejecutor", "timeout": 5000 } ] @@ -85,10 +85,20 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=ejecutor' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 ejecutor", "timeout": 5000 } ] + }, + { + "matcher": ".*", + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/approval-permission.ps1 ejecutor", + "timeout": 130000 + } + ] } ], "Stop": [ @@ -96,10 +106,19 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=ejecutor' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 ejecutor", "timeout": 10000 } ] + }, + { + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/approval-plan.ps1 ejecutor", + "timeout": 130000 + } + ] } ] } diff --git a/.claude-isolated/.gitignore b/.claude-isolated/.gitignore deleted file mode 100644 index ca50cc4..0000000 --- a/.claude-isolated/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# No versionar credenciales -.credentials.json -*.backup - -# Estado de sesión (regenerable) -.claude.json -.claude.json.backup - -# Archivos temporales -tmp/ diff --git a/.claude-isolated/settings.json b/.claude-isolated/settings.json deleted file mode 100644 index c9c0a74..0000000 --- a/.claude-isolated/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "permissions": { - "allow": [], - "deny": [] - }, - "env": { - "DISABLE_TELEMETRY": "1" - } -} diff --git a/.claude-nucleo000/settings.json b/.claude-nucleo000/settings.json index c9c0a74..07e342b 100644 --- a/.claude-nucleo000/settings.json +++ b/.claude-nucleo000/settings.json @@ -1,9 +1,111 @@ { + "env": { + "DISABLE_TELEMETRY": "1" + }, "permissions": { "allow": [], "deny": [] }, - "env": { - "DISABLE_TELEMETRY": "1" + "hooks": { + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 nucleo000", + "timeout": 5000 + } + ] + } + ], + "PreToolUse": [ + { + "matcher": ".*", + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 nucleo000", + "timeout": 5000 + } + ] + } + ], + "PostToolUse": [ + { + "matcher": ".*", + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 nucleo000", + "timeout": 5000 + } + ] + } + ], + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 nucleo000", + "timeout": 5000 + } + ] + } + ], + "Notification": [ + { + "matcher": ".*", + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 nucleo000", + "timeout": 5000 + } + ] + } + ], + "PermissionRequest": [ + { + "matcher": ".*", + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 nucleo000", + "timeout": 5000 + } + ] + }, + { + "matcher": ".*", + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/approval-permission.ps1 nucleo000", + "timeout": 130000 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/forward-hook.ps1 nucleo000", + "timeout": 10000 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -File hooks/approval-plan.ps1 nucleo000", + "timeout": 130000 + } + ] + } + ] } } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 332295e..e8d1bb7 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -8,6 +8,8 @@ import FloatingResponse from './components/FloatingResponse.vue' import FloatingVoice from './components/FloatingVoice.vue' import AgentBar from './components/AgentBar.vue' import PwaInstallBanner from './components/PwaInstallBanner.vue' +import HooksApprovalModal from './components/HooksApprovalModal.vue' +import { useGlobalApproval } from './composables/useGlobalApproval' import { initWebMCP, getWebMCP } from './services/webmcp' import { initTorch, destroyTorch } from './services/torch' import { endpoints } from './config/endpoints' @@ -68,6 +70,7 @@ const responseRef = ref | null>(null) const voiceRef = ref | null>(null) const canvasStore = useCanvasStore() const projectCanvasStore = useProjectCanvasStore() +const { totalPending, modalVisible, connect: connectApproval, disconnect: disconnectApproval, fetchPending: fetchApprovalPending } = useGlobalApproval() // Voice FAB push-to-talk state const voicePTTActive = ref(false) let voiceTouchStarted = false @@ -264,6 +267,10 @@ onMounted(async () => { // Connect to WebSocket for Claude status updates connectStatusWs() + // Connect global hooks approval WS + connectApproval() + fetchApprovalPending() + // Fire torch connection early (don't await yet) const torchReady = initTorch() @@ -349,6 +356,7 @@ onMounted(async () => { onUnmounted(() => { destroyTorch() + disconnectApproval() if (statusReconnectTimeout) clearTimeout(statusReconnectTimeout) if (processingTimeout) clearTimeout(processingTimeout) if (sessionStartTimeout) clearTimeout(sessionStartTimeout) @@ -398,6 +406,18 @@ watch(() => route.name, (newPage) => {
+ +
+ +
+ + +
+ + + + No pending approvals +
+
+ + + + + + + diff --git a/frontend/src/components/Toolbar.vue b/frontend/src/components/Toolbar.vue index 0fff2ea..b46ee8f 100644 --- a/frontend/src/components/Toolbar.vue +++ b/frontend/src/components/Toolbar.vue @@ -131,6 +131,15 @@ onMounted(() => { + + + + + + + + +
diff --git a/frontend/src/components/transcript-debug/AssistantMessageBubble.vue b/frontend/src/components/transcript-debug/AssistantMessageBubble.vue new file mode 100644 index 0000000..c71d2d3 --- /dev/null +++ b/frontend/src/components/transcript-debug/AssistantMessageBubble.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/ChatContainer.vue b/frontend/src/components/transcript-debug/ChatContainer.vue new file mode 100644 index 0000000..d65c9db --- /dev/null +++ b/frontend/src/components/transcript-debug/ChatContainer.vue @@ -0,0 +1,583 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/PermissionApproval.vue b/frontend/src/components/transcript-debug/PermissionApproval.vue new file mode 100644 index 0000000..dbf9dca --- /dev/null +++ b/frontend/src/components/transcript-debug/PermissionApproval.vue @@ -0,0 +1,213 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/PlanApproval.vue b/frontend/src/components/transcript-debug/PlanApproval.vue new file mode 100644 index 0000000..b699397 --- /dev/null +++ b/frontend/src/components/transcript-debug/PlanApproval.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/ProgressEvent.vue b/frontend/src/components/transcript-debug/ProgressEvent.vue new file mode 100644 index 0000000..99aa971 --- /dev/null +++ b/frontend/src/components/transcript-debug/ProgressEvent.vue @@ -0,0 +1,279 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/RawJsonViewer.vue b/frontend/src/components/transcript-debug/RawJsonViewer.vue new file mode 100644 index 0000000..44972d2 --- /dev/null +++ b/frontend/src/components/transcript-debug/RawJsonViewer.vue @@ -0,0 +1,216 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/SessionSelector.vue b/frontend/src/components/transcript-debug/SessionSelector.vue new file mode 100644 index 0000000..461e28f --- /dev/null +++ b/frontend/src/components/transcript-debug/SessionSelector.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/SystemMessage.vue b/frontend/src/components/transcript-debug/SystemMessage.vue new file mode 100644 index 0000000..f781a92 --- /dev/null +++ b/frontend/src/components/transcript-debug/SystemMessage.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/ThinkingBlock.vue b/frontend/src/components/transcript-debug/ThinkingBlock.vue new file mode 100644 index 0000000..289ccce --- /dev/null +++ b/frontend/src/components/transcript-debug/ThinkingBlock.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/ToolCallBlock.vue b/frontend/src/components/transcript-debug/ToolCallBlock.vue new file mode 100644 index 0000000..d123cef --- /dev/null +++ b/frontend/src/components/transcript-debug/ToolCallBlock.vue @@ -0,0 +1,390 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/ToolResultBlock.vue b/frontend/src/components/transcript-debug/ToolResultBlock.vue new file mode 100644 index 0000000..f1b3ffa --- /dev/null +++ b/frontend/src/components/transcript-debug/ToolResultBlock.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/frontend/src/components/transcript-debug/UserInput.vue b/frontend/src/components/transcript-debug/UserInput.vue new file mode 100644 index 0000000..6d7cf31 --- /dev/null +++ b/frontend/src/components/transcript-debug/UserInput.vue @@ -0,0 +1,166 @@ + + +