{
  "version": 3,
  "sources": ["../../../src/hooks/ts/lib/worker-session.ts", "../../../src/hooks/ts/subagent-stop/subagent-stop-hook.ts"],
  "sourcesContent": ["/**\n * Worker-session detection.\n *\n * A disposable headless worker (a `claude -p` run started by an orchestrating\n * session, possibly against a different model provider with a different\n * context window) shares the project directory and the hook configuration\n * with the real session that spawned it. Left alone, the hooks treat it as a\n * session in its own right: they inject project context into it, create and\n * rename a numbered session note for it, autosave it, and enqueue a\n * model-written handover for it. Its compactions also land in the project's\n * transcript folder, where they are indistinguishable from a real session's\n * and drag the measured compaction trigger down (observed 2026-09-17: two\n * workers compacting at ~151k tokens pulled a project's trigger from ~784k\n * to ~151k, and the real session's handover fired at ~50k tokens).\n *\n * The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does\n * per-session bookkeeping returns immediately when this predicate is true.\n * Deliberately NOT guarded: the security validator (a worker's shell\n * commands must still be checked) and observability capture.\n */\nexport function isWorkerSession(env: NodeJS.ProcessEnv = process.env): boolean {\n  return env.PAI_WORKER === \"1\";\n}\n", "#!/usr/bin/env node\n\nimport { isWorkerSession } from \"../lib/worker-session.js\";\nimport { readFileSync, existsSync } from 'fs';\n\nasync function delay(ms: number): Promise<void> {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nasync function findTaskResult(transcriptPath: string, maxAttempts: number = 10): Promise<{ result: string | null, agentType: string | null }> {\n  console.error(`Looking for Task result in transcript: ${transcriptPath}`);\n\n  for (let attempt = 0; attempt < maxAttempts; attempt++) {\n    if (attempt > 0) {\n      // Wait progressively longer between attempts\n      await delay(100 * attempt);\n    }\n\n    if (!existsSync(transcriptPath)) {\n      console.error(`Transcript file doesn't exist yet (attempt ${attempt + 1}/${maxAttempts})`);\n      continue;\n    }\n\n    try {\n      const transcript = readFileSync(transcriptPath, 'utf-8');\n      const lines = transcript.trim().split('\\n');\n\n      // Search from the end of the transcript backwards\n      for (let i = lines.length - 1; i >= 0; i--) {\n        try {\n          const entry = JSON.parse(lines[i]);\n\n          // Look for assistant messages that contain Task tool_use\n          if (entry.type === 'assistant' && entry.message?.content) {\n            for (const content of entry.message.content) {\n              if (content.type === 'tool_use' && content.name === 'Task') {\n                console.error(`Found Task invocation with subagent: ${content.input?.subagent_type}`);\n                // Found a Task invocation, now look for its result\n                // The result should be in a subsequent user message\n                for (let j = i + 1; j < lines.length; j++) {\n                  const resultEntry = JSON.parse(lines[j]);\n                  if (resultEntry.type === 'user' && resultEntry.message?.content) {\n                    for (const resultContent of resultEntry.message.content) {\n                      if (resultContent.type === 'tool_result' && resultContent.tool_use_id === content.id) {\n                        // Found the matching Task result\n                        const taskOutput = resultContent.content;\n\n                        // Extract agent type from the output\n                        let agentType = 'default';\n                        const agentMatch = taskOutput.match(/Sub-agent\\s+(\\w+)\\s+completed/i);\n                        if (agentMatch) {\n                          agentType = agentMatch[1].toLowerCase();\n                        }\n\n                        return { result: taskOutput, agentType };\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        } catch (e) {\n          // Invalid JSON line, skip\n        }\n      }\n    } catch (e) {\n      // Error reading file, will retry\n    }\n  }\n\n  return { result: null, agentType: null };\n}\n\nfunction extractCompletionMessage(taskOutput: string): { message: string | null, agentType: string | null } {\n  // Look for the COMPLETED section in the agent's output\n  // Priority is given to [AGENT:type] format\n  const agentPatterns = [\n    // Handle markdown formatting with asterisks\n    /\\*+COMPLETED:\\*+\\s*\\[AGENT:(\\w+)\\]\\s*I\\s+completed\\s+(.+?)(?:\\n|$)/is,\n    // Non-markdown patterns\n    /COMPLETED:\\s*\\[AGENT:(\\w+)\\]\\s*I\\s+completed\\s+(.+?)(?:\\n|$)/is,\n    /\\[AGENT:(\\w+)\\]\\s*I\\s+completed\\s+(.+?)(?:\\.|!|\\n|$)/is,\n  ];\n\n  // First try to match agent-specific patterns\n  for (const pattern of agentPatterns) {\n    const match = taskOutput.match(pattern);\n    if (match && match[1] && match[2]) {\n      const agentType = match[1].toLowerCase();\n      let message = match[2].trim();\n\n      // Clean up the message\n      message = message.replace(/\\*+/g, '');\n      message = message.replace(/\\s+/g, ' ');\n\n      // Prepend agent name for spoken message\n      const agentName = agentType.charAt(0).toUpperCase() + agentType.slice(1);\n      const fullMessage = `${agentName} completed ${message}`;\n\n      console.error(`FOUND AGENT MATCH: [${agentType}] ${fullMessage}`);\n\n      return { message: fullMessage, agentType };\n    }\n  }\n\n  // Fall back to generic patterns but try to extract agent type\n  const genericPatterns = [\n    // Handle markdown formatting\n    /\\*+COMPLETED:\\*+\\s*(.+?)(?:\\n|$)/i,\n    // Non-markdown patterns\n    /COMPLETED:\\s*(.+?)(?:\\n|$)/i,\n    /Sub-agent\\s+\\w+\\s+completed\\s+(.+?)(?:\\.|!|\\n|$)/i,\n    /Agent\\s+completed\\s+(.+?)(?:\\.|!|\\n|$)/i\n  ];\n\n  for (const pattern of genericPatterns) {\n    const match = taskOutput.match(pattern);\n    if (match && match[1]) {\n      let message = match[1].trim();\n\n      // Clean up the message\n      message = message.replace(/^(the\\s+)?requested\\s+task$/i, '');\n      message = message.replace(/\\*+/g, '');\n      message = message.replace(/\\s+/g, ' ');\n\n      // Only return if it's not a generic message\n      if (message &&\n          !message.match(/^(the\\s+)?requested\\s+task$/i) &&\n          !message.match(/^task$/i) &&\n          message.length > 5) {\n\n        // Try to detect agent type from context\n        let agentType = null;\n        const agentMatch = taskOutput.match(/Sub-agent\\s+(\\w+)\\s+completed/i);\n        if (agentMatch) {\n          agentType = agentMatch[1].toLowerCase();\n        }\n\n        return { message, agentType };\n      }\n    }\n  }\n\n  return { message: null, agentType: null };\n}\n\nasync function main() {\n  if (isWorkerSession()) return; // disposable worker: no per-session bookkeeping\n  console.error('SubagentStop hook started');\n  // Read input from stdin with timeout\n  let input = '';\n  try {\n    const decoder = new TextDecoder();\n\n    const timeoutPromise = new Promise<void>((resolve) => {\n      setTimeout(() => resolve(), 500);\n    });\n\n    const readPromise = (async () => {\n      for await (const chunk of process.stdin) {\n        input += decoder.decode(chunk, { stream: true });\n      }\n    })();\n\n    await Promise.race([readPromise, timeoutPromise]);\n  } catch (e) {\n    console.error('Failed to read input:', e);\n    process.exit(0);\n  }\n\n  if (!input) {\n    console.log('No input received');\n    process.exit(0);\n  }\n\n  let transcriptPath: string;\n  try {\n    const parsed = JSON.parse(input);\n    transcriptPath = parsed.transcript_path;\n  } catch (e) {\n    console.error('Invalid input JSON:', e);\n    process.exit(0);\n  }\n\n  if (!transcriptPath) {\n    console.log('No transcript path provided');\n    process.exit(0);\n  }\n\n  // Wait for and find the Task result\n  const { result: taskOutput, agentType } = await findTaskResult(transcriptPath);\n\n  if (!taskOutput) {\n    console.log('No Task result found in transcript after waiting');\n    process.exit(0);\n  }\n\n  // Extract the completion message and agent type\n  const { message: completionMessage, agentType: extractedAgentType } = extractCompletionMessage(taskOutput);\n\n  if (!completionMessage) {\n    console.log('No specific completion message found in Task output');\n    process.exit(0);\n  }\n\n  // Use extracted agent type if available, otherwise use the one from task analysis\n  const finalAgentType = extractedAgentType || agentType || 'default';\n  const agentName = finalAgentType.charAt(0).toUpperCase() + finalAgentType.slice(1);\n\n  console.log(`[${agentName}] ${completionMessage}`);\n}\n\nmain().catch(console.error);\n"],
  "mappings": "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",
  "names": []
}
