feat: unified hook notifier, agent auto-detection, terminal transition UI
- Add hooks/notify.ps1 as single hook handler for all events - Refactor settings.local.json to use notify.ps1 instead of inline PS - Add Notification hook, auto-detect agent from session_id/transcript - Rename agent 'main' to 'claude' across server routes and terminal - Add loading overlay and error state for terminal switching transitions - Add transitionError ref to useTranscriptDebug composable
This commit is contained in:
@@ -96,12 +96,23 @@
|
|||||||
"agent-ui"
|
"agent-ui"
|
||||||
],
|
],
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
"SessionStart": [
|
||||||
|
{
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
|
"timeout": 5000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"UserPromptSubmit": [
|
"UserPromptSubmit": [
|
||||||
{
|
{
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
"timeout": 5000
|
"timeout": 5000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -113,7 +124,7 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
"timeout": 5000
|
"timeout": 5000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -125,7 +136,7 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
"timeout": 5000
|
"timeout": 5000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -137,7 +148,19 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
|
"timeout": 5000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Notification": [
|
||||||
|
{
|
||||||
|
"matcher": ".*",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
"timeout": 5000
|
"timeout": 5000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -148,18 +171,7 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
"timeout": 5000
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SessionStart": [
|
|
||||||
{
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "command",
|
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
|
||||||
"timeout": 5000
|
"timeout": 5000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -171,7 +183,7 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
"timeout": 5000
|
"timeout": 5000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -187,24 +199,12 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Notification": [
|
|
||||||
{
|
|
||||||
"matcher": ".*",
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "command",
|
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
|
||||||
"timeout": 5000
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Stop": [
|
"Stop": [
|
||||||
{
|
{
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "powershell -NoProfile -Command \"try{$b=[Console]::In.ReadToEnd();Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook?agent=claude' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3|Out-Null}catch{}\"",
|
"command": "powershell -NoProfile -File hooks/notify.ps1",
|
||||||
"timeout": 10000
|
"timeout": 10000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ const {
|
|||||||
selectedSessionId,
|
selectedSessionId,
|
||||||
conversation,
|
conversation,
|
||||||
loading,
|
loading,
|
||||||
|
transitioning,
|
||||||
|
transitionError,
|
||||||
error,
|
error,
|
||||||
isRealtime,
|
isRealtime,
|
||||||
processing,
|
processing,
|
||||||
@@ -702,6 +704,22 @@ onBeforeUnmount(() => {
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<AquaticBackground />
|
<AquaticBackground />
|
||||||
<div class="readability-overlay" :style="{ background: `rgba(0, 0, 0, ${overlayOpacity})` }" />
|
<div class="readability-overlay" :style="{ background: `rgba(0, 0, 0, ${overlayOpacity})` }" />
|
||||||
|
<Transition name="terminal-loading">
|
||||||
|
<div v-if="transitioning" class="terminal-loading-overlay">
|
||||||
|
<div class="terminal-loading-spinner" />
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
<Transition name="terminal-loading">
|
||||||
|
<div v-if="transitionError" class="terminal-error-overlay" @click="transitionError = null">
|
||||||
|
<div class="terminal-error-content">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f87171" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||||
|
</svg>
|
||||||
|
<span class="terminal-error-msg">{{ transitionError }}</span>
|
||||||
|
<span class="terminal-error-hint">Click to dismiss</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
<ChatContainer
|
<ChatContainer
|
||||||
ref="chatRef"
|
ref="chatRef"
|
||||||
v-if="conversation"
|
v-if="conversation"
|
||||||
@@ -1180,6 +1198,73 @@ onBeforeUnmount(() => {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Terminal switching loading overlay ── */
|
||||||
|
.terminal-loading-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-loading-spinner {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: 2.5px solid rgba(255, 255, 255, 0.15);
|
||||||
|
border-top-color: rgba(255, 255, 255, 0.7);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: tl-spin 0.7s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes tl-spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-error-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-error-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
max-width: 80%;
|
||||||
|
padding: 1.2rem 1.5rem;
|
||||||
|
background: rgba(30, 30, 30, 0.85);
|
||||||
|
border: 1px solid rgba(248, 113, 113, 0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-error-msg {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #fca5a5;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-error-hint {
|
||||||
|
font-size: 10px;
|
||||||
|
color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-loading-enter-active { transition: opacity 0.15s ease; }
|
||||||
|
.terminal-loading-leave-active { transition: opacity 0.25s ease; }
|
||||||
|
.terminal-loading-enter-from,
|
||||||
|
.terminal-loading-leave-to { opacity: 0; }
|
||||||
|
|
||||||
/* Override ChatContainer backgrounds: glass-transparent */
|
/* Override ChatContainer backgrounds: glass-transparent */
|
||||||
.content :deep(.chat-container) {
|
.content :deep(.chat-container) {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export function useTranscriptDebug() {
|
|||||||
const conversation = ref<ParsedConversation | null>(null)
|
const conversation = ref<ParsedConversation | null>(null)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const transitioning = ref(false)
|
const transitioning = ref(false)
|
||||||
|
const transitionError = ref<string | null>(null)
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
const isRealtime = ref(false)
|
const isRealtime = ref(false)
|
||||||
|
|
||||||
@@ -234,9 +235,15 @@ export function useTranscriptDebug() {
|
|||||||
async function switchToTerminal(transcriptSessionId: string) {
|
async function switchToTerminal(transcriptSessionId: string) {
|
||||||
if (transcriptSessionId === activeTerminalSessionId.value) return
|
if (transcriptSessionId === activeTerminalSessionId.value) return
|
||||||
|
|
||||||
|
transitioning.value = true
|
||||||
|
transitionError.value = null
|
||||||
|
|
||||||
// Park current
|
// Park current
|
||||||
parkCurrentTerminal()
|
parkCurrentTerminal()
|
||||||
|
|
||||||
|
// Small delay so the fade-out is visible before swapping content
|
||||||
|
await new Promise(r => setTimeout(r, 150))
|
||||||
|
|
||||||
// Find the entry — might be from another agent
|
// Find the entry — might be from another agent
|
||||||
const entry = serverRegistry.value.find(
|
const entry = serverRegistry.value.find(
|
||||||
e => e.transcriptSessionId === transcriptSessionId
|
e => e.transcriptSessionId === transcriptSessionId
|
||||||
@@ -247,12 +254,28 @@ export function useTranscriptDebug() {
|
|||||||
await fetchSessions()
|
await fetchSessions()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the target session's transcript
|
// Load the target session's transcript (skip for __new__ — no transcript yet)
|
||||||
selectedSessionId.value = transcriptSessionId
|
selectedSessionId.value = transcriptSessionId
|
||||||
await fetchSessionContent(transcriptSessionId)
|
if (transcriptSessionId === '__new__') {
|
||||||
|
// Brand-new session: no transcript to load, just clear conversation
|
||||||
|
rawContent.value = ''
|
||||||
|
conversation.value = null
|
||||||
|
error.value = null
|
||||||
|
} else {
|
||||||
|
const ok = await fetchSessionContent(transcriptSessionId)
|
||||||
|
if (!ok) {
|
||||||
|
transitionError.value = error.value || `Failed to load session "${transcriptSessionId}"`
|
||||||
|
transitioning.value = false
|
||||||
|
// Still connect to the terminal so the user can interact
|
||||||
|
connectToTerminal(transcriptSessionId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to the terminal
|
// Connect to the terminal
|
||||||
connectToTerminal(transcriptSessionId)
|
connectToTerminal(transcriptSessionId)
|
||||||
|
|
||||||
|
transitioning.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
async function disposeAllLocalTerminals() {
|
async function disposeAllLocalTerminals() {
|
||||||
@@ -583,6 +606,7 @@ export function useTranscriptDebug() {
|
|||||||
|
|
||||||
selectedAgent.value = agent
|
selectedAgent.value = agent
|
||||||
error.value = null
|
error.value = null
|
||||||
|
transitionError.value = null
|
||||||
loading.value = true
|
loading.value = true
|
||||||
transitioning.value = true
|
transitioning.value = true
|
||||||
|
|
||||||
@@ -618,6 +642,7 @@ export function useTranscriptDebug() {
|
|||||||
parkCurrentTerminal()
|
parkCurrentTerminal()
|
||||||
|
|
||||||
error.value = null
|
error.value = null
|
||||||
|
transitionError.value = null
|
||||||
loading.value = true
|
loading.value = true
|
||||||
transitioning.value = true
|
transitioning.value = true
|
||||||
|
|
||||||
@@ -927,6 +952,7 @@ export function useTranscriptDebug() {
|
|||||||
conversation,
|
conversation,
|
||||||
loading,
|
loading,
|
||||||
transitioning,
|
transitioning,
|
||||||
|
transitionError,
|
||||||
error,
|
error,
|
||||||
lineCount,
|
lineCount,
|
||||||
isRealtime,
|
isRealtime,
|
||||||
|
|||||||
7
hooks/notify.ps1
Normal file
7
hooks/notify.ps1
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Unified hook notifier - forwards all hook events to agent-ui server
|
||||||
|
# Claude Code pipes JSON via stdin with session_id, transcript_path, etc.
|
||||||
|
# The server routes to the correct agent automatically.
|
||||||
|
$b = [Console]::In.ReadToEnd()
|
||||||
|
try {
|
||||||
|
Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-hook' -Method POST -Body $b -ContentType 'application/json' -TimeoutSec 3 | Out-Null
|
||||||
|
} catch {}
|
||||||
@@ -2,16 +2,26 @@ import { jsonResponse, errorResponse } from '../utils/cors'
|
|||||||
import { PORT_TERMINAL } from '../config'
|
import { PORT_TERMINAL } from '../config'
|
||||||
import { existsSync, readFileSync } from 'fs'
|
import { existsSync, readFileSync } from 'fs'
|
||||||
import { setActiveSession, getIncrementalMessages } from '../services/transcript-engine'
|
import { setActiveSession, getIncrementalMessages } from '../services/transcript-engine'
|
||||||
import { deriveStatus, type HookPayload } from '../services/session-state'
|
import { deriveStatus, sessionState, type HookPayload } from '../services/session-state'
|
||||||
|
|
||||||
export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
||||||
if (req.method !== 'POST') return null
|
if (req.method !== 'POST') return null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = new URL(req.url)
|
const url = new URL(req.url)
|
||||||
const agent = url.searchParams.get('agent') || ''
|
let agent = url.searchParams.get('agent') || ''
|
||||||
const body = await req.json() as HookPayload
|
const body = await req.json() as HookPayload
|
||||||
|
|
||||||
|
// Auto-detect agent from session_id or transcript_path
|
||||||
|
if (!agent && body.session_id) {
|
||||||
|
agent = sessionState.findAgentBySessionId(body.session_id) || ''
|
||||||
|
}
|
||||||
|
if (!agent && body.transcript_path) {
|
||||||
|
const matches = sessionState.findAgentsByTranscript(body.transcript_path as string)
|
||||||
|
if (matches.length === 1) agent = matches[0]
|
||||||
|
}
|
||||||
|
if (!agent) agent = 'claude'
|
||||||
|
|
||||||
// On Stop events, extract last assistant response from transcript
|
// On Stop events, extract last assistant response from transcript
|
||||||
if (body.hook_event_name === 'Stop' && body.transcript_path) {
|
if (body.hook_event_name === 'Stop' && body.transcript_path) {
|
||||||
try {
|
try {
|
||||||
@@ -45,7 +55,7 @@ export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inject agent name into hook data for WS consumers
|
// Inject agent name into hook data for WS consumers
|
||||||
const hookData = { ...body, agent_name: agent || 'main' }
|
const hookData = { ...body, agent_name: agent }
|
||||||
|
|
||||||
// 1. Broadcast full hook data via WebSocket (always, even for subagents)
|
// 1. Broadcast full hook data via WebSocket (always, even for subagents)
|
||||||
try {
|
try {
|
||||||
@@ -78,7 +88,7 @@ export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
|||||||
await fetch(`http://localhost:${PORT_TERMINAL}/claude-status`, {
|
await fetch(`http://localhost:${PORT_TERMINAL}/claude-status`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ status: derived.status, tool: derived.tool, agent: agent || 'main' })
|
body: JSON.stringify({ status: derived.status, tool: derived.tool, agent })
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[claude-hook] Failed to forward status to terminal server:', e)
|
console.error('[claude-hook] Failed to forward status to terminal server:', e)
|
||||||
@@ -87,8 +97,7 @@ export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
|||||||
|
|
||||||
// 4. Incremental transcript reading for real-time chat
|
// 4. Incremental transcript reading for real-time chat
|
||||||
if (body.session_id && body.transcript_path) {
|
if (body.session_id && body.transcript_path) {
|
||||||
const agentName = agent || 'main'
|
setActiveSession(agent, body.session_id, body.transcript_path as string)
|
||||||
setActiveSession(agentName, body.session_id, body.transcript_path as string)
|
|
||||||
|
|
||||||
const newMessages = getIncrementalMessages(body.session_id, body.transcript_path as string)
|
const newMessages = getIncrementalMessages(body.session_id, body.transcript_path as string)
|
||||||
if (newMessages.length > 0) {
|
if (newMessages.length > 0) {
|
||||||
@@ -98,7 +107,7 @@ export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
|||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
sessionId: body.session_id,
|
sessionId: body.session_id,
|
||||||
agent: agentName,
|
agent,
|
||||||
messages: newMessages,
|
messages: newMessages,
|
||||||
hookEvent: body.hook_event_name
|
hookEvent: body.hook_event_name
|
||||||
})
|
})
|
||||||
@@ -109,7 +118,7 @@ export async function handleClaudeHook(req: Request): Promise<Response | null> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonResponse({ success: true, event: body.hook_event_name, agent: agent || 'main' })
|
return jsonResponse({ success: true, event: body.hook_event_name, agent })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return errorResponse('Invalid JSON body', 400)
|
return errorResponse('Invalid JSON body', 400)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export async function handleHooksApprovalPermission(req: Request): Promise<Respo
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
// Track in centralized session state → broadcasts patch to all clients
|
// Track in centralized session state → broadcasts patch to all clients
|
||||||
notifyAddApproval(body.agent_name || 'main', {
|
notifyAddApproval(body.agent_name || 'claude', {
|
||||||
requestId,
|
requestId,
|
||||||
type: 'permission',
|
type: 'permission',
|
||||||
toolName: body.tool_name,
|
toolName: body.tool_name,
|
||||||
@@ -189,7 +189,7 @@ export async function handleHooksApprovalPlan(req: Request): Promise<Response |
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
// Track in centralized session state → broadcasts patch to all clients
|
// Track in centralized session state → broadcasts patch to all clients
|
||||||
notifyAddApproval('main', {
|
notifyAddApproval(body.agent_name || 'claude', {
|
||||||
requestId,
|
requestId,
|
||||||
type: 'plan',
|
type: 'plan',
|
||||||
lastAssistantText,
|
lastAssistantText,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export function handleTranscriptSessions(): Response {
|
|||||||
export function handleTranscriptActive(req: Request, url: URL): Response {
|
export function handleTranscriptActive(req: Request, url: URL): Response {
|
||||||
if (req.method !== 'GET') return errorResponse('Method not allowed', 405)
|
if (req.method !== 'GET') return errorResponse('Method not allowed', 405)
|
||||||
|
|
||||||
const agent = url.searchParams.get('agent') || 'main'
|
const agent = url.searchParams.get('agent') || 'claude'
|
||||||
const activeSession = getActiveSession(agent)
|
const activeSession = getActiveSession(agent)
|
||||||
|
|
||||||
const sessionId = activeSession?.sessionId
|
const sessionId = activeSession?.sessionId
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface AgentTerminalState {
|
|||||||
export const agentSessions = new Map<string, AgentTerminalState>()
|
export const agentSessions = new Map<string, AgentTerminalState>()
|
||||||
|
|
||||||
const AGENT_COMMANDS: Record<string, string> = {
|
const AGENT_COMMANDS: Record<string, string> = {
|
||||||
'main': 'claude',
|
'claude': 'claude',
|
||||||
'ejecutor': 'ejecutor',
|
'ejecutor': 'ejecutor',
|
||||||
'nucleo000': 'nucleo000'
|
'nucleo000': 'nucleo000'
|
||||||
}
|
}
|
||||||
@@ -695,7 +695,7 @@ type ClaudeStatus = 'idle' | 'thinking' | 'toolUse' | 'reading' | 'writing' | 's
|
|||||||
|
|
||||||
// Broadcast Claude status to ALL clients across ALL sessions
|
// Broadcast Claude status to ALL clients across ALL sessions
|
||||||
export function broadcastClaudeStatus(status: ClaudeStatus, tool?: string, agent?: string) {
|
export function broadcastClaudeStatus(status: ClaudeStatus, tool?: string, agent?: string) {
|
||||||
const agentName = agent || 'main'
|
const agentName = agent || 'claude'
|
||||||
|
|
||||||
// Track agent running state
|
// Track agent running state
|
||||||
if (status === 'sessionStart') {
|
if (status === 'sessionStart') {
|
||||||
|
|||||||
Reference in New Issue
Block a user