@ai-devkit/agent-manager 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +1 -342
- package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
- package/dist/__tests__/database/DurableAgentsDatabase.test.js +31 -3
- package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +1 -1
- package/dist/__tests__/durable/CodexCliProbe.test.js +129 -0
- package/dist/__tests__/durable/CodexCliProbe.test.js.map +1 -0
- package/dist/__tests__/durable/CodexDurableAgentRepository.test.js +84 -0
- package/dist/__tests__/durable/CodexDurableAgentRepository.test.js.map +1 -0
- package/dist/__tests__/durable/CodexPrintAgent.integration.test.js +126 -0
- package/dist/__tests__/durable/CodexPrintAgent.integration.test.js.map +1 -0
- package/dist/__tests__/durable/CodexPrintAgentService.test.js +271 -0
- package/dist/__tests__/durable/CodexPrintAgentService.test.js.map +1 -0
- package/dist/__tests__/durable/CodexPrintRunner.test.js +393 -0
- package/dist/__tests__/durable/CodexPrintRunner.test.js.map +1 -0
- package/dist/__tests__/print/ClaudeCliProbe.test.js +3 -7
- package/dist/__tests__/print/ClaudeCliProbe.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +25 -28
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintAgentService.test.js +65 -0
- package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintRunner.test.js +4 -10
- package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
- package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js +79 -0
- package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js.map +1 -0
- package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js +436 -0
- package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js.map +1 -0
- package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js +76 -0
- package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js.map +1 -0
- package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js +43 -0
- package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js.map +1 -0
- package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js +202 -0
- package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js.map +1 -0
- package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js +119 -0
- package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js.map +1 -0
- package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js +386 -0
- package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js.map +1 -0
- package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js +34 -0
- package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js.map +1 -0
- package/dist/__tests__/utils/ClaudeSessionParser.test.js +2 -2
- package/dist/__tests__/utils/ClaudeSessionParser.test.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/database/migrations/004_codex_durable_sessions.sql +1 -0
- package/dist/durable/DurableAgent.d.ts +23 -3
- package/dist/durable/DurableAgent.d.ts.map +1 -1
- package/dist/durable/DurableAgent.js +12 -0
- package/dist/durable/DurableAgent.js.map +1 -1
- package/dist/durable/DurableAgentRepository.d.ts +6 -7
- package/dist/durable/DurableAgentRepository.d.ts.map +1 -1
- package/dist/durable/DurableAgentRepository.js +73 -42
- package/dist/durable/DurableAgentRepository.js.map +1 -1
- package/dist/durable/process.d.ts +8 -0
- package/dist/durable/process.d.ts.map +1 -0
- package/dist/durable/process.js +40 -0
- package/dist/durable/process.js.map +1 -0
- package/dist/durable/run.d.ts +32 -0
- package/dist/durable/run.d.ts.map +1 -0
- package/dist/durable/run.js +46 -0
- package/dist/durable/run.js.map +1 -0
- package/dist/durable/utils.d.ts +19 -0
- package/dist/durable/utils.d.ts.map +1 -0
- package/dist/durable/utils.js +52 -0
- package/dist/durable/utils.js.map +1 -0
- package/dist/index.d.ts +17 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/providers/claude/ClaudeAgentMapper.d.ts +21 -0
- package/dist/providers/claude/ClaudeAgentMapper.d.ts.map +1 -0
- package/dist/providers/claude/ClaudeAgentMapper.js +42 -0
- package/dist/providers/claude/ClaudeAgentMapper.js.map +1 -0
- package/dist/providers/claude/ClaudeCodeAdapter.d.ts +29 -0
- package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -0
- package/dist/providers/claude/ClaudeCodeAdapter.js +129 -0
- package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -0
- package/dist/{adapters/ClaudeCodeAdapter.d.ts → providers/claude/ClaudeSessionLocator.d.ts} +68 -52
- package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -0
- package/dist/{adapters/ClaudeCodeAdapter.js → providers/claude/ClaudeSessionLocator.js} +71 -189
- package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -0
- package/dist/{utils → providers/claude}/ClaudeSessionParser.d.ts +2 -2
- package/dist/providers/claude/ClaudeSessionParser.d.ts.map +1 -0
- package/dist/{utils → providers/claude}/ClaudeSessionParser.js +1 -1
- package/dist/providers/claude/ClaudeSessionParser.js.map +1 -0
- package/dist/providers/claude/durable/ClaudeCliProbe.d.ts.map +1 -0
- package/dist/{durable → providers/claude/durable}/ClaudeCliProbe.js +4 -9
- package/dist/providers/claude/durable/ClaudeCliProbe.js.map +1 -0
- package/dist/{durable → providers/claude/durable}/ClaudePrintAgentService.d.ts +4 -9
- package/dist/providers/claude/durable/ClaudePrintAgentService.d.ts.map +1 -0
- package/dist/providers/claude/durable/ClaudePrintAgentService.js +45 -0
- package/dist/providers/claude/durable/ClaudePrintAgentService.js.map +1 -0
- package/dist/{durable → providers/claude/durable}/ClaudePrintRunner.d.ts +3 -3
- package/dist/providers/claude/durable/ClaudePrintRunner.d.ts.map +1 -0
- package/dist/providers/claude/durable/ClaudePrintRunner.js +75 -0
- package/dist/providers/claude/durable/ClaudePrintRunner.js.map +1 -0
- package/dist/providers/claude/durable/ClaudeStreamParser.d.ts +17 -0
- package/dist/providers/claude/durable/ClaudeStreamParser.d.ts.map +1 -0
- package/dist/providers/claude/durable/ClaudeStreamParser.js +69 -0
- package/dist/providers/claude/durable/ClaudeStreamParser.js.map +1 -0
- package/dist/providers/codex/durable/CodexCliProbe.d.ts +20 -0
- package/dist/providers/codex/durable/CodexCliProbe.d.ts.map +1 -0
- package/dist/providers/codex/durable/CodexCliProbe.js +61 -0
- package/dist/providers/codex/durable/CodexCliProbe.js.map +1 -0
- package/dist/providers/codex/durable/CodexPrintAgentService.d.ts +40 -0
- package/dist/providers/codex/durable/CodexPrintAgentService.d.ts.map +1 -0
- package/dist/providers/codex/durable/CodexPrintAgentService.js +50 -0
- package/dist/providers/codex/durable/CodexPrintAgentService.js.map +1 -0
- package/dist/providers/codex/durable/CodexPrintRunner.d.ts +34 -0
- package/dist/providers/codex/durable/CodexPrintRunner.d.ts.map +1 -0
- package/dist/providers/codex/durable/CodexPrintRunner.js +78 -0
- package/dist/providers/codex/durable/CodexPrintRunner.js.map +1 -0
- package/dist/providers/codex/durable/CodexStreamParser.d.ts +21 -0
- package/dist/providers/codex/durable/CodexStreamParser.d.ts.map +1 -0
- package/dist/providers/codex/durable/CodexStreamParser.js +86 -0
- package/dist/providers/codex/durable/CodexStreamParser.js.map +1 -0
- package/dist/providers/pi/durable/PiCliProbe.d.ts +20 -0
- package/dist/providers/pi/durable/PiCliProbe.d.ts.map +1 -0
- package/dist/providers/pi/durable/PiCliProbe.js +51 -0
- package/dist/providers/pi/durable/PiCliProbe.js.map +1 -0
- package/dist/providers/pi/durable/PiPrintAgentService.d.ts +39 -0
- package/dist/providers/pi/durable/PiPrintAgentService.d.ts.map +1 -0
- package/dist/providers/pi/durable/PiPrintAgentService.js +47 -0
- package/dist/providers/pi/durable/PiPrintAgentService.js.map +1 -0
- package/dist/providers/pi/durable/PiPrintProtocol.d.ts +4 -0
- package/dist/providers/pi/durable/PiPrintProtocol.d.ts.map +1 -0
- package/dist/providers/pi/durable/PiPrintProtocol.js +44 -0
- package/dist/providers/pi/durable/PiPrintProtocol.js.map +1 -0
- package/dist/providers/pi/durable/PiPrintRunner.d.ts +32 -0
- package/dist/providers/pi/durable/PiPrintRunner.d.ts.map +1 -0
- package/dist/providers/pi/durable/PiPrintRunner.js +61 -0
- package/dist/providers/pi/durable/PiPrintRunner.js.map +1 -0
- package/dist/providers/pi/durable/PiStreamParser.d.ts +21 -0
- package/dist/providers/pi/durable/PiStreamParser.d.ts.map +1 -0
- package/dist/providers/pi/durable/PiStreamParser.js +71 -0
- package/dist/providers/pi/durable/PiStreamParser.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +1 -307
- package/src/__tests__/database/DurableAgentsDatabase.test.ts +28 -3
- package/src/__tests__/durable/CodexCliProbe.test.ts +63 -0
- package/src/__tests__/durable/CodexDurableAgentRepository.test.ts +54 -0
- package/src/__tests__/durable/CodexPrintAgent.integration.test.ts +79 -0
- package/src/__tests__/durable/CodexPrintAgentService.test.ts +139 -0
- package/src/__tests__/durable/CodexPrintRunner.test.ts +189 -0
- package/src/__tests__/fixtures/fake-codex.cjs +45 -0
- package/src/__tests__/print/ClaudeCliProbe.test.ts +3 -7
- package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +11 -21
- package/src/__tests__/print/ClaudePrintAgentService.test.ts +33 -1
- package/src/__tests__/print/ClaudePrintRunner.test.ts +4 -10
- package/src/__tests__/providers/claude/ClaudeAgentMapper.test.ts +84 -0
- package/src/__tests__/providers/claude/ClaudeSessionLocator.test.ts +411 -0
- package/src/__tests__/providers/pi/durable/PiCliProbe.test.ts +38 -0
- package/src/__tests__/providers/pi/durable/PiDurableAgentRepository.test.ts +32 -0
- package/src/__tests__/providers/pi/durable/PiPrintAgentService.test.ts +69 -0
- package/src/__tests__/providers/pi/durable/PiPrintProtocol.test.ts +33 -0
- package/src/__tests__/providers/pi/durable/PiPrintRunner.test.ts +107 -0
- package/src/__tests__/providers/pi/durable/PiStreamParser.test.ts +24 -0
- package/src/__tests__/utils/ClaudeSessionParser.test.ts +2 -2
- package/src/adapters/index.ts +1 -1
- package/src/database/migrations/004_codex_durable_sessions.sql +1 -0
- package/src/durable/DurableAgent.ts +51 -3
- package/src/durable/DurableAgentRepository.ts +60 -31
- package/src/durable/process.ts +28 -0
- package/src/durable/run.ts +74 -0
- package/src/durable/utils.ts +61 -0
- package/src/index.ts +31 -15
- package/src/providers/claude/ClaudeAgentMapper.ts +62 -0
- package/src/providers/claude/ClaudeCodeAdapter.ts +160 -0
- package/src/{adapters/ClaudeCodeAdapter.ts → providers/claude/ClaudeSessionLocator.ts} +97 -239
- package/src/{utils → providers/claude}/ClaudeSessionParser.ts +2 -2
- package/src/{durable → providers/claude/durable}/ClaudeCliProbe.ts +4 -10
- package/src/providers/claude/durable/ClaudePrintAgentService.ts +66 -0
- package/src/providers/claude/durable/ClaudePrintRunner.ts +92 -0
- package/src/providers/claude/durable/ClaudeStreamParser.ts +79 -0
- package/src/providers/codex/durable/CodexCliProbe.ts +60 -0
- package/src/providers/codex/durable/CodexPrintAgentService.ts +65 -0
- package/src/providers/codex/durable/CodexPrintRunner.ts +94 -0
- package/src/providers/codex/durable/CodexStreamParser.ts +104 -0
- package/src/providers/pi/durable/PiCliProbe.ts +48 -0
- package/src/providers/pi/durable/PiPrintAgentService.ts +69 -0
- package/src/providers/pi/durable/PiPrintProtocol.ts +39 -0
- package/src/providers/pi/durable/PiPrintRunner.ts +88 -0
- package/src/providers/pi/durable/PiStreamParser.ts +84 -0
- package/dist/adapters/ClaudeCodeAdapter.d.ts.map +0 -1
- package/dist/adapters/ClaudeCodeAdapter.js.map +0 -1
- package/dist/durable/ClaudeCliProbe.d.ts.map +0 -1
- package/dist/durable/ClaudeCliProbe.js.map +0 -1
- package/dist/durable/ClaudePrintAgentService.d.ts.map +0 -1
- package/dist/durable/ClaudePrintAgentService.js +0 -66
- package/dist/durable/ClaudePrintAgentService.js.map +0 -1
- package/dist/durable/ClaudePrintRunner.d.ts.map +0 -1
- package/dist/durable/ClaudePrintRunner.js +0 -128
- package/dist/durable/ClaudePrintRunner.js.map +0 -1
- package/dist/utils/ClaudeSessionParser.d.ts.map +0 -1
- package/dist/utils/ClaudeSessionParser.js.map +0 -1
- package/src/durable/ClaudePrintAgentService.ts +0 -94
- package/src/durable/ClaudePrintRunner.ts +0 -139
- /package/dist/{durable → providers/claude/durable}/ClaudeCliProbe.d.ts +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/ClaudeSessionParser.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport type { ConversationMessage } from '../adapters/AgentAdapter.js';\nimport { AgentStatus } from '../adapters/AgentAdapter.js';\n\n/**\n * Content block within a Claude Code JSONL message entry.\n * Handles text, tool_use, and tool_result block types.\n */\nexport interface ContentBlock {\n type?: string;\n text?: string;\n content?: string;\n name?: string;\n input?: Record<string, unknown>;\n tool_use_id?: string;\n is_error?: boolean;\n}\n\n/**\n * A single line entry in a Claude Code session JSONL file.\n *\n * Each line is an independent JSON object with a type discriminator:\n * - \"user\" / \"assistant\" / \"system\" — conversation turns\n * - \"progress\" / \"thinking\" — intermediate agent state\n * - \"last-prompt\" / \"file-history-snapshot\" — metadata (not conversation state)\n */\nexport interface SessionEntry {\n type?: string;\n timestamp?: string;\n cwd?: string;\n message?: {\n content?: string | ContentBlock[];\n };\n}\n\n/**\n * Parsed session state extracted from a JSONL file.\n * Aggregates data from all entries into a single summary.\n */\nexport interface ClaudeSession {\n sessionId: string;\n projectPath: string;\n lastCwd?: string;\n sessionStart: Date;\n lastActive: Date;\n lastEntryType?: string;\n isInterrupted: boolean;\n lastUserMessage?: string;\n /** First meaningful user prompt in the session (post noise filter) */\n firstUserMessage?: string;\n}\n\n/**\n * Top-level JSONL entry types that represent conversation/agent state.\n *\n * Only these types update `lastEntryType` for status determination. All\n * other types Claude Code emits (`attachment`, `permission-mode`,\n * `ai-title`, `queued_command`, `tools_changed`, `model_changed`,\n * `hook_progress`, …) are UI-state events that must not overwrite the\n * last conversation turn — otherwise polling between writes lands on a\n * UI-state entry and `determineStatus` falls through to UNKNOWN.\n */\nconst CONVERSATION_ENTRY_TYPES = new Set([\n 'user',\n 'assistant',\n 'system',\n 'progress',\n 'thinking',\n]);\n\n/**\n * Parses Claude Code session JSONL files into structured data.\n *\n * Session files live at ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl\n * and contain one JSON object per line, each representing a conversation\n * event (user turn, assistant response, tool call, etc.).\n */\nexport class ClaudeSessionParser {\n /**\n * Parse a session JSONL file into a ClaudeSession summary.\n *\n * Iterates all lines to extract: session start time (from first entry),\n * last activity timestamp, last entry type (for status), whether the\n * session was interrupted, and the last meaningful user message.\n *\n * Returns null if the file is unreadable or empty.\n */\n readSession(filePath: string, projectPath: string): ClaudeSession | null {\n const sessionId = path.basename(filePath, '.jsonl');\n\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n\n const allLines = content.trim().split('\\n');\n if (allLines.length === 0) {\n return null;\n }\n\n const sessionStart = this.parseSessionStart(allLines[0]);\n\n let lastEntryType: string | undefined;\n let lastActive: Date | undefined;\n let lastCwd: string | undefined;\n let isInterrupted = false;\n let lastUserMessage: string | undefined;\n let firstUserMessage: string | undefined;\n\n for (const line of allLines) {\n try {\n const entry: SessionEntry = JSON.parse(line);\n\n if (entry.timestamp) {\n const ts = new Date(entry.timestamp);\n if (!Number.isNaN(ts.getTime())) {\n lastActive = ts;\n }\n }\n\n if (typeof entry.cwd === 'string' && entry.cwd.trim().length > 0) {\n lastCwd = entry.cwd;\n }\n\n if (entry.type && CONVERSATION_ENTRY_TYPES.has(entry.type)) {\n lastEntryType = entry.type;\n\n if (entry.type === 'user') {\n const msgContent = entry.message?.content;\n isInterrupted =\n Array.isArray(msgContent) &&\n msgContent.some(\n (c) =>\n (c.type === 'text' &&\n c.text?.includes('[Request interrupted')) ||\n (c.type === 'tool_result' &&\n c.content?.includes('[Request interrupted')),\n );\n\n const text = this.extractUserMessageText(msgContent);\n if (text) {\n lastUserMessage = text;\n if (!firstUserMessage) {\n firstUserMessage = text;\n }\n }\n } else {\n isInterrupted = false;\n }\n }\n } catch {\n continue;\n }\n }\n\n return {\n sessionId,\n projectPath: projectPath || lastCwd || '',\n lastCwd,\n sessionStart: sessionStart || lastActive || new Date(),\n lastActive: lastActive || new Date(),\n lastEntryType,\n isInterrupted,\n lastUserMessage,\n firstUserMessage,\n };\n }\n\n /**\n * Determine agent status from parsed session state.\n *\n * Status mapping:\n * - \"user\" + interrupted → WAITING (agent finished, awaiting new input)\n * - \"user\" + not interrupted → RUNNING (agent is processing)\n * - \"progress\" / \"thinking\" → RUNNING\n * - \"assistant\" → WAITING (agent responded, awaiting user)\n * - \"system\" → IDLE\n */\n determineStatus(session: ClaudeSession): AgentStatus {\n if (!session.lastEntryType) {\n return AgentStatus.UNKNOWN;\n }\n\n if (session.lastEntryType === 'user') {\n return session.isInterrupted\n ? AgentStatus.WAITING\n : AgentStatus.RUNNING;\n }\n\n if (\n session.lastEntryType === 'progress' ||\n session.lastEntryType === 'thinking'\n ) {\n return AgentStatus.RUNNING;\n }\n\n if (session.lastEntryType === 'assistant') {\n return AgentStatus.WAITING;\n }\n\n if (session.lastEntryType === 'system') {\n return AgentStatus.IDLE;\n }\n\n return AgentStatus.UNKNOWN;\n }\n\n /**\n * Read the full conversation from a session JSONL file.\n *\n * Default mode returns only text content from user/assistant/system messages.\n * Verbose mode also includes tool_use and tool_result blocks.\n */\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n const verbose = options?.verbose ?? false;\n\n let content: string;\n try {\n content = fs.readFileSync(sessionFilePath, 'utf-8');\n } catch {\n return [];\n }\n\n const lines = content.trim().split('\\n');\n const messages: ConversationMessage[] = [];\n\n for (const line of lines) {\n let entry: SessionEntry;\n try {\n entry = JSON.parse(line);\n } catch {\n continue;\n }\n\n let role: ConversationMessage['role'];\n if (entry.type === 'user') {\n role = 'user';\n } else if (entry.type === 'assistant') {\n role = 'assistant';\n } else if (entry.type === 'system') {\n role = 'system';\n } else {\n continue;\n }\n\n const text = this.extractConversationContent(entry.message?.content, role, verbose);\n if (!text) continue;\n\n messages.push({\n role,\n content: text,\n timestamp: entry.timestamp,\n });\n }\n\n return messages;\n }\n\n /**\n * Parse session start time from the first JSONL line.\n *\n * Claude Code may emit a \"file-history-snapshot\" as the first entry,\n * which stores its timestamp inside \"snapshot.timestamp\" rather than\n * at the root level.\n */\n private parseSessionStart(firstLine: string): Date | null {\n try {\n const firstEntry = JSON.parse(firstLine);\n const rawTs: string | undefined =\n firstEntry.timestamp || firstEntry.snapshot?.timestamp;\n if (rawTs) {\n const ts = new Date(rawTs);\n if (!Number.isNaN(ts.getTime())) {\n return ts;\n }\n }\n } catch {\n /* malformed first line */\n }\n return null;\n }\n\n /**\n * Extract meaningful text from a user message content field.\n *\n * Handles multiple formats:\n * - Plain string content\n * - Array of content blocks (extracts first text block)\n * - Skill slash-commands (<command-message> tags)\n * - Expanded skill content (extracts ARGUMENTS line)\n * - Filters noise messages (interruptions, tool loaded, session continued)\n */\n private extractUserMessageText(\n content: string | Array<{ type?: string; text?: string }> | undefined,\n ): string | undefined {\n if (!content) {\n return undefined;\n }\n\n let raw: string | undefined;\n\n if (typeof content === 'string') {\n raw = content.trim();\n } else if (Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text' && block.text?.trim()) {\n raw = block.text.trim();\n break;\n }\n }\n }\n\n if (!raw) {\n return undefined;\n }\n\n if (raw.startsWith('<command-message>')) {\n return this.parseCommandMessage(raw);\n }\n\n if (raw.startsWith('Base directory for this skill:')) {\n const argsMatch = raw.match(/\\nARGUMENTS:\\s*(.+)/);\n return argsMatch?.[1]?.trim() || undefined;\n }\n\n if (isNoiseMessage(raw)) {\n return undefined;\n }\n\n return raw;\n }\n\n /**\n * Parse a <command-message> string into \"/command args\" format.\n */\n private parseCommandMessage(raw: string): string | undefined {\n const nameMatch = raw.match(/<command-name>([^<]+)<\\/command-name>/);\n const argsMatch = raw.match(/<command-args>([^<]+)<\\/command-args>/);\n const name = nameMatch?.[1]?.trim();\n if (!name) {\n return undefined;\n }\n const args = argsMatch?.[1]?.trim();\n return args ? `${name} ${args}` : name;\n }\n\n /**\n * Extract displayable content from a message content field for conversation output.\n */\n private extractConversationContent(\n content: string | ContentBlock[] | undefined,\n role: ConversationMessage['role'],\n verbose: boolean,\n ): string | undefined {\n if (!content) return undefined;\n\n if (typeof content === 'string') {\n const cleaned = stripHarnessTags(content);\n if (role === 'user' && isNoiseMessage(cleaned)) return undefined;\n return cleaned || undefined;\n }\n\n if (!Array.isArray(content)) return undefined;\n\n const parts: string[] = [];\n\n for (const block of content) {\n if (block.type === 'text' && block.text?.trim()) {\n const cleaned = stripHarnessTags(block.text);\n if (!cleaned) continue;\n if (role === 'user' && isNoiseMessage(cleaned)) continue;\n parts.push(cleaned);\n } else if (block.type === 'tool_use' && verbose) {\n const inputSummary = block.input?.file_path || block.input?.pattern || block.input?.command || '';\n parts.push(`[Tool: ${block.name}]${inputSummary ? ' ' + inputSummary : ''}`);\n } else if (block.type === 'tool_result' && verbose) {\n const truncated = truncateToolResult(block.content || '');\n const prefix = block.is_error ? '[Tool Error]' : '[Tool Result]';\n parts.push(`${prefix} ${truncated}`);\n }\n }\n\n return parts.length > 0 ? parts.join('\\n') : undefined;\n }\n}\n\n/**\n * Tags whose entire block (including content) should be dropped — they are\n * harness-injected prompt context (system reminders, hook output, command\n * stdout), not meaningful conversation content.\n */\nconst HARNESS_DROP_TAGS = [\n 'system-reminder',\n 'local-command-stdout',\n 'local-command-stderr',\n 'user-prompt-submit-hook',\n 'command-stdout',\n 'command-stderr',\n 'bash-input',\n 'bash-stdout',\n 'bash-stderr',\n 'command-message',\n] as const;\n\nconst HARNESS_DROP_RE = new RegExp(\n `<(${HARNESS_DROP_TAGS.join('|')})>[\\\\s\\\\S]*?</\\\\1>`,\n 'g',\n);\n\nconst COMMAND_INVOCATION_RE =\n /<command-name>([^<]+)<\\/command-name>(?:\\s*<command-args>([\\s\\S]*?)<\\/command-args>)?/g;\n\n/**\n * Remove harness-injected XML blocks from message text and collapse\n * <command-name>/<command-args> pairs into a \"/name args\" shorthand.\n *\n * Returns the cleaned, trimmed text. Returns an empty string if nothing\n * survives stripping.\n */\nfunction stripHarnessTags(text: string): string {\n let out = text.replace(HARNESS_DROP_RE, '');\n\n out = out.replace(COMMAND_INVOCATION_RE, (_match, rawName: string, rawArgs?: string) => {\n const name = rawName.trim();\n const args = rawArgs?.trim();\n return args ? `${name} ${args}` : name;\n });\n\n return out.replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n\n/** Check if a message is noise (not a meaningful user intent). */\nfunction isNoiseMessage(text: string): boolean {\n return (\n text.startsWith('[Request interrupted') ||\n text === 'Tool loaded.' ||\n text.startsWith('This session is being continued')\n );\n}\n\nfunction truncateToolResult(content: string, maxLength = 200): string {\n const firstLine = content.split('\\n')[0] || '';\n if (firstLine.length <= maxLength) return firstLine;\n return firstLine.slice(0, maxLength - 3) + '...';\n}\n"],"names":["fs","path","AgentStatus","CONVERSATION_ENTRY_TYPES","Set","ClaudeSessionParser","readSession","filePath","projectPath","sessionId","basename","content","readFileSync","allLines","trim","split","length","sessionStart","parseSessionStart","lastEntryType","lastActive","lastCwd","isInterrupted","lastUserMessage","firstUserMessage","line","entry","JSON","parse","timestamp","ts","Date","Number","isNaN","getTime","cwd","type","has","msgContent","message","Array","isArray","some","c","text","includes","extractUserMessageText","determineStatus","session","UNKNOWN","WAITING","RUNNING","IDLE","getConversation","sessionFilePath","options","verbose","lines","messages","role","extractConversationContent","push","firstLine","firstEntry","rawTs","snapshot","undefined","raw","block","startsWith","parseCommandMessage","argsMatch","match","isNoiseMessage","nameMatch","name","args","cleaned","stripHarnessTags","parts","inputSummary","input","file_path","pattern","command","truncated","truncateToolResult","prefix","is_error","join","HARNESS_DROP_TAGS","HARNESS_DROP_RE","RegExp","COMMAND_INVOCATION_RE","out","replace","_match","rawName","rawArgs","maxLength","slice"],"mappings":"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"}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { DurableAgent, ProcessIdentity } from './DurableAgent.js';
|
|
2
|
-
import { ClaudePrintError, DurableAgentNotFoundError } from './DurableAgent.js';
|
|
3
|
-
import { ClaudeCliProbe } from './ClaudeCliProbe.js';
|
|
4
|
-
import { ClaudePrintRunner, type ClaudePrintRunResult } from './ClaudePrintRunner.js';
|
|
5
|
-
import { DurableAgentRepository, type CreateDurableAgentInput, type DurableRunCompletion } from './DurableAgentRepository.js';
|
|
6
|
-
|
|
7
|
-
interface RepositoryLike {
|
|
8
|
-
create(input: CreateDurableAgentInput): Promise<DurableAgent>;
|
|
9
|
-
list(): Promise<DurableAgent[]>;
|
|
10
|
-
resolve(reference: string): Promise<DurableAgent | DurableAgent[] | null>;
|
|
11
|
-
acquireRun(id: string): Promise<{ agent: DurableAgent; token: string }>;
|
|
12
|
-
recordProviderProcess(id: string, token: string, identity: ProcessIdentity): Promise<void>;
|
|
13
|
-
completeRun(id: string, token: string, result: DurableRunCompletion): Promise<DurableAgent>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface ProbeLike { validate(): Promise<{ executable: string; version: string }> }
|
|
17
|
-
interface RunnerLike { run(request: Parameters<ClaudePrintRunner['run']>[0]): Promise<ClaudePrintRunResult> }
|
|
18
|
-
|
|
19
|
-
export interface ClaudePrintAgentServiceOptions {
|
|
20
|
-
repository?: RepositoryLike;
|
|
21
|
-
probe?: ProbeLike;
|
|
22
|
-
runner?: RunnerLike;
|
|
23
|
-
executable?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ClaudePrintSendResult extends ClaudePrintRunResult {
|
|
27
|
-
agentId: string;
|
|
28
|
-
agentName: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class ClaudePrintAgentService {
|
|
32
|
-
readonly repository: RepositoryLike;
|
|
33
|
-
private readonly probe: ProbeLike;
|
|
34
|
-
private readonly runner: RunnerLike;
|
|
35
|
-
private readonly executable?: string;
|
|
36
|
-
|
|
37
|
-
constructor(options: ClaudePrintAgentServiceOptions = {}) {
|
|
38
|
-
this.repository = options.repository ?? new DurableAgentRepository();
|
|
39
|
-
this.probe = options.probe ?? new ClaudeCliProbe();
|
|
40
|
-
this.runner = options.runner ?? new ClaudePrintRunner();
|
|
41
|
-
this.executable = options.executable;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async create(input: CreateDurableAgentInput): Promise<DurableAgent> {
|
|
45
|
-
await this.probe.validate();
|
|
46
|
-
return this.repository.create(input);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async send(reference: string, prompt: string): Promise<ClaudePrintSendResult> {
|
|
50
|
-
const resolved = await this.repository.resolve(reference);
|
|
51
|
-
if (!resolved) throw new DurableAgentNotFoundError(reference);
|
|
52
|
-
if (Array.isArray(resolved)) {
|
|
53
|
-
throw new ClaudePrintError(`Multiple durable agents match "${reference}".`, 'DURABLE_AGENT_AMBIGUOUS');
|
|
54
|
-
}
|
|
55
|
-
const acquired = await this.repository.acquireRun(resolved.id);
|
|
56
|
-
try {
|
|
57
|
-
const result = await this.runner.run({
|
|
58
|
-
agent: acquired.agent,
|
|
59
|
-
prompt,
|
|
60
|
-
executable: this.executable,
|
|
61
|
-
firstRun: acquired.agent.sessionHealth === 'uninitialized',
|
|
62
|
-
onSpawn: (identity) => this.repository.recordProviderProcess(resolved.id, acquired.token, identity),
|
|
63
|
-
});
|
|
64
|
-
await this.repository.completeRun(resolved.id, acquired.token, {
|
|
65
|
-
status: 'succeeded',
|
|
66
|
-
exitCode: result.exitCode,
|
|
67
|
-
summary: sanitize(result.result, 4096),
|
|
68
|
-
sessionHealth: 'healthy',
|
|
69
|
-
});
|
|
70
|
-
return { ...result, agentId: resolved.id, agentName: resolved.name };
|
|
71
|
-
} catch (error) {
|
|
72
|
-
const failure = error instanceof Error ? error : new Error(String(error));
|
|
73
|
-
const sessionHealth = error instanceof ClaudePrintError && error.code === 'CLAUDE_SESSION_MISMATCH'
|
|
74
|
-
? 'mismatch' as const
|
|
75
|
-
: 'unknown' as const;
|
|
76
|
-
await this.repository.completeRun(resolved.id, acquired.token, {
|
|
77
|
-
status: 'failed',
|
|
78
|
-
exitCode: null,
|
|
79
|
-
summary: sanitize(failure.message, 4096),
|
|
80
|
-
sessionHealth,
|
|
81
|
-
});
|
|
82
|
-
throw error;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function sanitize(value: string, max: number): string {
|
|
88
|
-
return Array.from(value, (character) => {
|
|
89
|
-
const code = character.charCodeAt(0);
|
|
90
|
-
return (code <= 8 || code === 11 || code === 12 || (code >= 14 && code <= 31) || code === 127)
|
|
91
|
-
? ' '
|
|
92
|
-
: character;
|
|
93
|
-
}).join('').trim().slice(0, max);
|
|
94
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { spawn as nodeSpawn, type ChildProcessWithoutNullStreams, type SpawnOptionsWithoutStdio } from 'child_process';
|
|
2
|
-
import type { DurableAgent, ProcessIdentity } from './DurableAgent.js';
|
|
3
|
-
import { ClaudePrintError } from './DurableAgent.js';
|
|
4
|
-
import { LocalProcessInspector, type ProcessInspector } from './DurableAgentRepository.js';
|
|
5
|
-
|
|
6
|
-
type Spawn = (
|
|
7
|
-
command: string,
|
|
8
|
-
args: readonly string[],
|
|
9
|
-
options: SpawnOptionsWithoutStdio & { stdio: ['pipe', 'pipe', 'pipe'] },
|
|
10
|
-
) => ChildProcessWithoutNullStreams;
|
|
11
|
-
|
|
12
|
-
export interface ClaudePrintRunRequest {
|
|
13
|
-
agent: DurableAgent;
|
|
14
|
-
prompt: string;
|
|
15
|
-
executable?: string;
|
|
16
|
-
firstRun: boolean;
|
|
17
|
-
onSpawn(identity: ProcessIdentity): Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ClaudePrintRunResult {
|
|
21
|
-
sessionId: string;
|
|
22
|
-
result: string;
|
|
23
|
-
exitCode: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ClaudePrintRunnerOptions {
|
|
27
|
-
spawn?: Spawn;
|
|
28
|
-
processInspector?: ProcessInspector;
|
|
29
|
-
maxLineBytes?: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class ClaudePrintRunner {
|
|
33
|
-
private readonly spawn: Spawn;
|
|
34
|
-
private readonly processInspector: ProcessInspector;
|
|
35
|
-
private readonly maxLineBytes: number;
|
|
36
|
-
|
|
37
|
-
constructor(options: ClaudePrintRunnerOptions = {}) {
|
|
38
|
-
this.spawn = options.spawn ?? (nodeSpawn as Spawn);
|
|
39
|
-
this.processInspector = options.processInspector ?? new LocalProcessInspector();
|
|
40
|
-
this.maxLineBytes = options.maxLineBytes ?? 1024 * 1024;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async run(request: ClaudePrintRunRequest): Promise<ClaudePrintRunResult> {
|
|
44
|
-
const sessionArgs = request.firstRun
|
|
45
|
-
? ['--session-id', request.agent.providerSessionId]
|
|
46
|
-
: ['--resume', request.agent.providerSessionId];
|
|
47
|
-
const args = ['-p', ...sessionArgs, '--output-format', 'stream-json', '--verbose'];
|
|
48
|
-
const child = this.spawn(request.executable ?? 'claude', args, {
|
|
49
|
-
cwd: request.agent.cwd,
|
|
50
|
-
shell: false,
|
|
51
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
52
|
-
});
|
|
53
|
-
if (!child.pid) {
|
|
54
|
-
child.kill();
|
|
55
|
-
throw new ClaudePrintError('Claude process did not provide a PID.', 'CLAUDE_PROCESS_IDENTITY');
|
|
56
|
-
}
|
|
57
|
-
const identity = this.processInspector.getIdentity(child.pid);
|
|
58
|
-
if (!identity) {
|
|
59
|
-
child.kill();
|
|
60
|
-
throw new ClaudePrintError('Cannot verify Claude process identity.', 'CLAUDE_PROCESS_IDENTITY');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
let buffer = Buffer.alloc(0);
|
|
64
|
-
let terminal: ClaudePrintRunResult | null = null;
|
|
65
|
-
let protocolError: ClaudePrintError | null = null;
|
|
66
|
-
|
|
67
|
-
child.stdout.on('data', (chunk: Buffer | string) => {
|
|
68
|
-
if (protocolError) return;
|
|
69
|
-
buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
|
|
70
|
-
if (buffer.length > this.maxLineBytes && buffer.indexOf(0x0a) < 0) {
|
|
71
|
-
protocolError = new ClaudePrintError('Claude stream line exceeded the safety limit.', 'CLAUDE_STREAM_OVERSIZED');
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
let newline: number;
|
|
75
|
-
while ((newline = buffer.indexOf(0x0a)) >= 0) {
|
|
76
|
-
const line = buffer.subarray(0, newline);
|
|
77
|
-
buffer = buffer.subarray(newline + 1);
|
|
78
|
-
if (line.length === 0) continue;
|
|
79
|
-
if (line.length > this.maxLineBytes) {
|
|
80
|
-
protocolError = new ClaudePrintError('Claude stream line exceeded the safety limit.', 'CLAUDE_STREAM_OVERSIZED');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
try {
|
|
84
|
-
const value = JSON.parse(line.toString('utf8')) as unknown;
|
|
85
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
86
|
-
throw new ClaudePrintError('Claude emitted a non-object stream message.', 'CLAUDE_STREAM_INVALID');
|
|
87
|
-
}
|
|
88
|
-
const event = value as Record<string, unknown>;
|
|
89
|
-
if (typeof event.session_id === 'string' && event.session_id !== request.agent.providerSessionId) {
|
|
90
|
-
throw new ClaudePrintError('Claude returned a different session identity.', 'CLAUDE_SESSION_MISMATCH');
|
|
91
|
-
}
|
|
92
|
-
if (event.type === 'result') {
|
|
93
|
-
if (terminal) throw new ClaudePrintError('Claude emitted more than one terminal result.', 'CLAUDE_STREAM_INVALID');
|
|
94
|
-
if (typeof event.session_id !== 'string' || typeof event.result !== 'string') {
|
|
95
|
-
throw new ClaudePrintError('Claude emitted an invalid terminal result.', 'CLAUDE_STREAM_INVALID');
|
|
96
|
-
}
|
|
97
|
-
terminal = { sessionId: event.session_id, result: event.result, exitCode: 0 };
|
|
98
|
-
}
|
|
99
|
-
} catch (error) {
|
|
100
|
-
protocolError = error instanceof ClaudePrintError
|
|
101
|
-
? error
|
|
102
|
-
: new ClaudePrintError('Claude emitted malformed stream JSON.', 'CLAUDE_STREAM_INVALID');
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
// Drain provider diagnostics without reflecting potentially sensitive prompt/tool data.
|
|
108
|
-
child.stderr.resume();
|
|
109
|
-
|
|
110
|
-
const closed = new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {
|
|
111
|
-
child.once('error', reject);
|
|
112
|
-
child.once('close', (code, signal) => resolve({ code, signal }));
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
await request.onSpawn(identity);
|
|
117
|
-
} catch (error) {
|
|
118
|
-
child.kill();
|
|
119
|
-
throw error;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
child.stdin.end(request.prompt);
|
|
123
|
-
const { code, signal } = await closed;
|
|
124
|
-
|
|
125
|
-
if (protocolError) throw protocolError;
|
|
126
|
-
if (buffer.length > 0) {
|
|
127
|
-
throw new ClaudePrintError('Claude stream ended with incomplete JSON.', 'CLAUDE_STREAM_INVALID');
|
|
128
|
-
}
|
|
129
|
-
if (code !== 0) {
|
|
130
|
-
throw new ClaudePrintError(
|
|
131
|
-
`Claude print run failed${signal ? ` (${signal})` : '.'}`,
|
|
132
|
-
'CLAUDE_PROCESS_FAILED',
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
if (!terminal) throw new ClaudePrintError('Claude stream ended without a terminal result.', 'CLAUDE_RESULT_MISSING');
|
|
136
|
-
const finalResult = terminal as ClaudePrintRunResult;
|
|
137
|
-
return { sessionId: finalResult.sessionId, result: finalResult.result, exitCode: code };
|
|
138
|
-
}
|
|
139
|
-
}
|
|
File without changes
|