@contextgraph/agent 0.4.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/callback-server.ts","../src/credentials.ts","../src/auth-flow.ts","../src/workflows/auth.ts","../src/claude-sdk.ts","../src/plugin-setup.ts","../src/sdk-event-transformer.ts","../src/fetch-with-retry.ts","../src/log-transport.ts","../src/log-buffer.ts","../src/heartbeat-manager.ts","../src/workflows/prepare.ts","../src/workflows/execute.ts","../src/workflows/agent.ts","../src/api-client.ts","../src/workspace-prep.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { runAuth } from '../workflows/auth.js';\nimport { runPrepare } from '../workflows/prepare.js';\nimport { runExecute } from '../workflows/execute.js';\nimport { runLocalAgent } from '../workflows/agent.js';\nimport { loadCredentials, isExpired, isTokenExpired } from '../credentials.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, '../package.json'), 'utf-8')\n);\n\nconst program = new Command();\n\nprogram\n .name('contextgraph-agent')\n .description('Autonomous agent for contextgraph action execution')\n .version(packageJson.version);\n\nprogram\n .command('run')\n .description('Run continuous worker loop (claims and executes actions until Ctrl+C)')\n .action(async () => {\n try {\n await runLocalAgent();\n } catch (error) {\n if (error instanceof Error) {\n console.error('Error running agent:', error.message || '(no message)');\n if (error.stack) {\n console.error('\\nStack trace:');\n console.error(error.stack);\n }\n } else {\n console.error('Error running agent:', error);\n }\n process.exit(1);\n }\n });\n\nprogram\n .command('auth')\n .description('Authenticate with contextgraph.dev')\n .action(async () => {\n try {\n await runAuth();\n } catch (error) {\n console.error('Error during authentication:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram\n .command('prepare')\n .argument('<action-id>', 'Action ID to prepare')\n .description('Prepare a single action')\n .action(async (actionId: string) => {\n try {\n await runPrepare(actionId);\n } catch (error) {\n console.error('Error preparing action:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram\n .command('execute')\n .argument('<action-id>', 'Action ID to execute')\n .description('Execute a single action')\n .action(async (actionId: string) => {\n try {\n await runExecute(actionId);\n } catch (error) {\n console.error('Error executing action:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram\n .command('whoami')\n .description('Show current authentication status')\n .action(async () => {\n try {\n const credentials = await loadCredentials();\n\n if (!credentials) {\n console.log('Not authenticated. Run `contextgraph-agent auth` to authenticate.');\n process.exit(1);\n }\n\n if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {\n console.log('⚠️ Token expired. Run `contextgraph-agent auth` to re-authenticate.');\n console.log(`User ID: ${credentials.userId}`);\n console.log(`Expired at: ${credentials.expiresAt}`);\n process.exit(1);\n }\n\n console.log('✅ Authenticated');\n console.log(`User ID: ${credentials.userId}`);\n console.log(`Expires at: ${credentials.expiresAt}`);\n } catch (error) {\n console.error('Error checking authentication:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import http from 'http';\nimport { URL } from 'url';\nimport type { CallbackResult } from './types/actions.js';\n\nexport type CallbackServerResult = {\n port: number;\n waitForCallback: () => Promise<CallbackResult>;\n close: () => Promise<void>;\n};\n\nconst MIN_PORT = 3000;\nconst MAX_PORT = 3100;\n\nasync function findFreePort(): Promise<number> {\n for (let port = MIN_PORT; port <= MAX_PORT; port++) {\n const isAvailable = await checkPortAvailable(port);\n if (isAvailable) {\n return port;\n }\n }\n\n throw new Error(`No free ports found between ${MIN_PORT} and ${MAX_PORT}`);\n}\n\nfunction checkPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = http.createServer();\n\n server.once('error', () => {\n resolve(false);\n });\n\n server.once('listening', () => {\n server.close();\n resolve(true);\n });\n\n server.listen(port);\n });\n}\n\nexport async function startCallbackServer(): Promise<CallbackServerResult> {\n const port = await findFreePort();\n\n let callbackResolve: ((result: CallbackResult) => void) | null = null;\n\n const server = http.createServer((req, res) => {\n const url = new URL(req.url || '/', `http://localhost:${port}`);\n\n if (url.pathname === '/callback') {\n const token = url.searchParams.get('token');\n const userId = url.searchParams.get('userId');\n\n if (!token) {\n res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(getErrorPage('Missing token parameter'));\n return;\n }\n\n if (!userId) {\n res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(getErrorPage('Missing userId parameter'));\n return;\n }\n\n if (callbackResolve) {\n callbackResolve({ token, userId });\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(getSuccessPage());\n } else {\n res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(getNotFoundPage());\n }\n });\n\n await new Promise<void>((resolve) => {\n server.listen(port, resolve);\n });\n\n return {\n port,\n waitForCallback: () => {\n return new Promise((resolve) => {\n callbackResolve = resolve;\n });\n },\n close: () => {\n return new Promise<void>((resolve, reject) => {\n server.close((err) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n });\n },\n };\n}\n\nfunction getSuccessPage(): string {\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Authentication Successful</title>\n <style>\n body {\n font-family: system-ui, -apple-system, sans-serif;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n .icon {\n font-size: 4rem;\n margin-bottom: 1rem;\n }\n h1 {\n color: #667eea;\n margin: 0 0 1rem 0;\n font-size: 1.5rem;\n }\n p {\n color: #666;\n margin: 0;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"icon\">✅</div>\n <h1>Authentication successful!</h1>\n <p>You can close this window and return to your terminal.</p>\n </div>\n</body>\n</html>\n `.trim();\n}\n\nfunction getErrorPage(message: string): string {\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Authentication Error</title>\n <style>\n body {\n font-family: system-ui, -apple-system, sans-serif;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n .icon {\n font-size: 4rem;\n margin-bottom: 1rem;\n }\n h1 {\n color: #f5576c;\n margin: 0 0 1rem 0;\n font-size: 1.5rem;\n }\n p {\n color: #666;\n margin: 0;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"icon\">❌</div>\n <h1>Authentication error</h1>\n <p>${message}</p>\n </div>\n</body>\n</html>\n `.trim();\n}\n\nfunction getNotFoundPage(): string {\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Not Found</title>\n <style>\n body {\n font-family: system-ui, -apple-system, sans-serif;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n background: #f0f0f0;\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n text-align: center;\n max-width: 400px;\n }\n h1 {\n color: #666;\n margin: 0;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>404 Not Found</h1>\n </div>\n</body>\n</html>\n `.trim();\n}\n","import fs from 'fs/promises';\nimport path from 'path';\nimport os from 'os';\nimport type { Credentials } from './types/actions.js';\n\nfunction getCredentialsDir(): string {\n return process.env.CONTEXTGRAPH_CREDENTIALS_DIR || path.join(os.homedir(), '.contextgraph');\n}\n\nfunction getCredentialsPath(): string {\n return path.join(getCredentialsDir(), 'credentials.json');\n}\n\nexport const CREDENTIALS_DIR = getCredentialsDir();\nexport const CREDENTIALS_PATH = getCredentialsPath();\n\nexport async function saveCredentials(credentials: Credentials): Promise<void> {\n const dir = getCredentialsDir();\n const filePath = getCredentialsPath();\n\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n\n const content = JSON.stringify(credentials, null, 2);\n await fs.writeFile(filePath, content, { mode: 0o600 });\n}\n\nexport async function loadCredentials(): Promise<Credentials | null> {\n // Check for API token in environment variable first\n const apiToken = process.env.CONTEXTGRAPH_API_TOKEN;\n if (apiToken) {\n // Create credentials from API token\n // API tokens don't expire in the same way, set a far future date\n const farFuture = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(); // 1 year\n return {\n clerkToken: apiToken,\n userId: 'api-token-user', // Placeholder - server will resolve actual user\n expiresAt: farFuture,\n createdAt: new Date().toISOString(),\n };\n }\n\n // Fall back to file-based credentials\n const filePath = getCredentialsPath();\n\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(content) as Credentials;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n\n console.error('Error loading credentials:', error);\n return null;\n }\n}\n\nexport async function deleteCredentials(): Promise<void> {\n const filePath = getCredentialsPath();\n\n try {\n await fs.unlink(filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n}\n\nexport function isExpired(credentials: Credentials): boolean {\n return new Date(credentials.expiresAt) <= new Date();\n}\n\nexport function isTokenExpired(token: string): boolean {\n try {\n // API tokens (non-JWT format) don't expire - check with server\n // API tokens typically start with a prefix like \"cg_\" or similar\n const parts = token.split('.');\n if (parts.length !== 3) {\n // Not a JWT - assume it's an API token that doesn't expire\n return false;\n }\n\n // Decode JWT to check actual token expiration\n const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());\n const now = Math.floor(Date.now() / 1000);\n\n // Token without exp claim is considered expired\n if (!payload.exp) {\n return true;\n }\n\n // Check if token has expired (including exactly now)\n if (payload.exp <= now) {\n return true;\n }\n\n // Check if token is not yet valid\n if (payload.nbf && payload.nbf > now) {\n return true;\n }\n\n return false;\n } catch {\n // If we can't decode it as JWT, assume it's an API token\n return false;\n }\n}\n\nexport function getTokenExpiration(token: string): Date | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n return null;\n }\n\n const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());\n if (payload.exp) {\n return new Date(payload.exp * 1000);\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get an authenticated fetch function that includes the Clerk token\n *\n * This loads the credentials from disk and returns a fetch function\n * that automatically includes the x-authorization header (workaround for Vercel stripping Authorization).\n *\n * Throws an error if credentials are not found or expired.\n */\nexport async function getAuthenticatedFetch(): Promise<typeof fetch> {\n const credentials = await loadCredentials();\n\n if (!credentials) {\n throw new Error(\n 'No credentials found. Please run authentication first.'\n );\n }\n\n if (isTokenExpired(credentials.clerkToken)) {\n throw new Error(\n 'Your credentials have expired. Please re-authenticate.'\n );\n }\n\n // Return a fetch function that includes the x-authorization header\n // Use x-authorization instead of Authorization because Vercel strips Authorization header\n return async (url: string | URL | Request, init?: RequestInit) => {\n const headers = new Headers(init?.headers);\n headers.set('x-authorization', `Bearer ${credentials.clerkToken}`);\n\n return fetch(url, {\n ...init,\n headers,\n });\n };\n}\n","import { startCallbackServer } from './callback-server.js';\nimport { saveCredentials } from './credentials.js';\n\ntype AuthenticationResult =\n | {\n success: true;\n credentials: {\n token: string;\n userId: string;\n };\n }\n | {\n success: false;\n error: string;\n };\n\ntype AuthenticationOptions = {\n baseUrl?: string;\n timeout?: number;\n openBrowser?: (url: string) => Promise<void>;\n};\n\nconst DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes\nconst DEFAULT_BASE_URL = 'https://www.contextgraph.dev';\n\nasync function defaultOpenBrowser(url: string): Promise<void> {\n const open = (await import('open')).default;\n await open(url);\n}\n\nexport async function authenticateAgent(\n options: AuthenticationOptions = {}\n): Promise<AuthenticationResult> {\n const {\n baseUrl = DEFAULT_BASE_URL,\n timeout = DEFAULT_TIMEOUT,\n openBrowser = defaultOpenBrowser,\n } = options;\n\n let server;\n\n try {\n server = await startCallbackServer();\n const { port, waitForCallback, close } = server;\n\n const authUrl = `${baseUrl}/auth/cli-callback?port=${port}`;\n\n console.log(`Opening browser to: ${authUrl}`);\n await openBrowser(authUrl);\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error('Authentication timeout')), timeout);\n });\n\n const result = await Promise.race([waitForCallback(), timeoutPromise]);\n\n const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); // 24 hours\n\n await saveCredentials({\n clerkToken: result.token,\n userId: result.userId,\n expiresAt,\n createdAt: new Date().toISOString(),\n });\n\n await close();\n\n return {\n success: true,\n credentials: {\n token: result.token,\n userId: result.userId,\n },\n };\n } catch (error) {\n if (server) {\n await server.close();\n }\n\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n}\n","import { authenticateAgent } from '../auth-flow.js';\n\nexport async function runAuth(): Promise<void> {\n console.log('Starting authentication flow...\\n');\n\n const result = await authenticateAgent();\n\n if (result.success) {\n console.log('\\n✅ Authentication successful!');\n console.log(`User ID: ${result.credentials.userId}`);\n } else {\n console.error('\\n❌ Authentication failed:', result.error);\n process.exit(1);\n }\n}\n\n","import { query, type SDKMessage, type SDKAssistantMessage, type SDKResultMessage } from '@anthropic-ai/claude-agent-sdk';\nimport type { ClaudeResult, SpawnClaudeOptions } from './types/actions.js';\nimport { ensurePlugin } from './plugin-setup.js';\nimport { transformSDKMessage } from './sdk-event-transformer.js';\nimport type { LogEvent } from './log-transport.js';\n\n// Constants for timeouts and truncation\nconst EXECUTION_TIMEOUT_MS = 20 * 60 * 1000; // 20 minutes\nconst THINKING_TRUNCATE_LENGTH = 100;\nconst COMMAND_TRUNCATE_LENGTH = 60;\n\n// Helper types for SDK message content\ntype ToolInput =\n | { file_path: string; old_string?: string; new_string?: string } // Read, Edit, Write\n | { command: string; description?: string; timeout?: number } // Bash\n | { pattern: string; glob?: string; type?: string; output_mode?: string } // Grep\n | { pattern: string; path?: string } // Glob\n | Record<string, unknown>; // Other tools\n\ntype SDKMessageContent = {\n type: string;\n text?: string;\n name?: string;\n input?: ToolInput;\n thinking?: string;\n};\n\n/**\n * Format tool use for console output\n */\nfunction formatToolUse(content: SDKMessageContent): string {\n if (content.type === 'tool_use') {\n const name = content.name || 'unknown';\n const summary = formatToolInput(name, content.input);\n return ` 🔧 ${name}${summary}`;\n }\n if (content.type === 'thinking' && content.thinking) {\n const truncated = content.thinking.length > THINKING_TRUNCATE_LENGTH\n ? content.thinking.substring(0, THINKING_TRUNCATE_LENGTH) + '...'\n : content.thinking;\n return ` 💭 ${truncated}`;\n }\n return '';\n}\n\n/**\n * Format tool input parameters for display\n */\nfunction formatToolInput(toolName: string, input: any): string {\n if (!input) return '';\n\n switch (toolName) {\n case 'Read':\n return `: ${input.file_path}`;\n case 'Edit':\n case 'Write':\n return `: ${input.file_path}`;\n case 'Bash':\n const cmd = input.command || '';\n const truncated = cmd.length > COMMAND_TRUNCATE_LENGTH\n ? cmd.substring(0, COMMAND_TRUNCATE_LENGTH) + '...'\n : cmd;\n return `: ${truncated}`;\n case 'Grep':\n return `: \"${input.pattern}\"`;\n case 'Glob':\n return `: ${input.pattern}`;\n default:\n return '';\n }\n}\n\n/**\n * Format assistant message content for display\n */\nfunction formatAssistantMessage(content: Array<SDKMessageContent>): string {\n const lines: string[] = [];\n\n for (const item of content) {\n if (item.type === 'text' && item.text) {\n lines.push(` ${item.text}`);\n } else if (item.type === 'tool_use' || item.type === 'thinking') {\n const formatted = formatToolUse(item);\n if (formatted) lines.push(formatted);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format SDK message for console output\n */\nfunction formatMessage(message: SDKMessage): string | null {\n switch (message.type) {\n case 'system':\n if (message.subtype === 'init') {\n return '🚀 Claude session initialized';\n }\n return null;\n\n case 'assistant':\n const assistantMsg = message as SDKAssistantMessage;\n if (assistantMsg.message?.content && Array.isArray(assistantMsg.message.content)) {\n return formatAssistantMessage(assistantMsg.message.content as Array<SDKMessageContent>);\n }\n return null;\n\n case 'result':\n const resultMsg = message as SDKResultMessage;\n if (resultMsg.subtype === 'success') {\n const duration = resultMsg.duration_ms ? `${(resultMsg.duration_ms / 1000).toFixed(1)}s` : 'unknown';\n return `✅ Completed in ${duration}`;\n } else if (resultMsg.subtype.startsWith('error_')) {\n return '❌ Execution failed';\n }\n return null;\n\n default:\n return null;\n }\n}\n\n/**\n * Extended options for executeClaude with log streaming support\n */\nexport interface ExecuteClaudeOptions extends SpawnClaudeOptions {\n /** Callback for log events - called for each SDK message transformed into a LogEvent */\n onLogEvent?: (event: LogEvent) => void;\n}\n\n/**\n * Execute Claude using the Agent SDK\n *\n * This is a drop-in replacement for spawnClaude() that uses the SDK instead of spawning a CLI process.\n * It matches the same interface (SpawnClaudeOptions) and returns the same result type (ClaudeResult).\n *\n * Optionally accepts onLogEvent callback for real-time log streaming.\n */\nexport async function executeClaude(\n options: ExecuteClaudeOptions\n): Promise<ClaudeResult> {\n let sessionId: string | undefined;\n let totalCost = 0;\n let usage: any;\n\n // Create abort controller for timeout\n const abortController = new AbortController();\n const timeout = setTimeout(() => {\n abortController.abort();\n }, EXECUTION_TIMEOUT_MS);\n\n try {\n // Ensure the contextgraph plugin is available (clones from GitHub if missing)\n const pluginPath = await ensurePlugin();\n console.log('[Agent SDK] Loading plugin from:', pluginPath);\n console.log('[Agent SDK] Auth token available:', !!options.authToken);\n console.log('[Agent SDK] Anthropic API key available:', !!process.env.ANTHROPIC_API_KEY);\n console.log('[Agent SDK] Claude OAuth token available:', !!process.env.CLAUDE_CODE_OAUTH_TOKEN);\n\n // Create the query with SDK using the plugin\n const iterator = query({\n prompt: options.prompt,\n options: {\n cwd: options.cwd,\n abortController,\n permissionMode: 'bypassPermissions', // Allow MCP tools to execute automatically\n maxTurns: 100, // Reasonable limit\n env: {\n ...process.env,\n // Pass auth token through environment for MCP server\n CONTEXTGRAPH_AUTH_TOKEN: options.authToken || '',\n // Pass Anthropic API key for SDK authentication\n ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY || '',\n // Pass Claude OAuth token for SDK authentication (alternative to API key)\n CLAUDE_CODE_OAUTH_TOKEN: process.env.CLAUDE_CODE_OAUTH_TOKEN || '',\n },\n // Load the contextgraph plugin (provides MCP server URL and other config)\n plugins: [\n {\n type: 'local',\n path: pluginPath,\n }\n ]\n // Note: Auth is passed via CONTEXTGRAPH_AUTH_TOKEN environment variable above\n }\n });\n\n // Iterate through messages\n for await (const message of iterator) {\n // Capture session ID from first message\n if (!sessionId && message.session_id) {\n sessionId = message.session_id;\n }\n\n // Format and display the message (preserved console output)\n const formatted = formatMessage(message);\n if (formatted) {\n console.log(formatted);\n }\n\n // Transform and emit log event if callback is provided\n if (options.onLogEvent) {\n try {\n const logEvent = transformSDKMessage(message);\n if (logEvent) {\n options.onLogEvent(logEvent);\n }\n } catch (error) {\n // Log transformation errors but don't block execution\n console.error('[Log Transform]', error instanceof Error ? error.message : String(error));\n }\n }\n\n // Capture result metadata\n if (message.type === 'result') {\n const resultMsg = message as SDKResultMessage;\n totalCost = resultMsg.total_cost_usd || 0;\n usage = resultMsg.usage;\n\n // Check for errors\n if (resultMsg.subtype.startsWith('error_')) {\n clearTimeout(timeout);\n return {\n exitCode: 1,\n sessionId,\n usage,\n cost: totalCost,\n };\n }\n }\n }\n\n clearTimeout(timeout);\n\n // Return successful result\n return {\n exitCode: 0,\n sessionId,\n usage,\n cost: totalCost,\n };\n\n } catch (error) {\n clearTimeout(timeout);\n\n // Handle abort/timeout\n if (abortController.signal.aborted) {\n const timeoutMinutes = EXECUTION_TIMEOUT_MS / (60 * 1000);\n throw new Error(`Claude SDK execution timed out after ${timeoutMinutes} minutes`);\n }\n\n // Handle other errors\n throw new Error(`Failed to execute Claude SDK: ${(error as Error).message}`);\n }\n}\n","import { spawn } from 'child_process';\nimport { access, mkdir } from 'fs/promises';\nimport { join } from 'path';\nimport { homedir } from 'os';\n\nconst PLUGIN_REPO = 'https://github.com/contextgraph/claude-code-plugin.git';\nconst PLUGIN_DIR = join(homedir(), '.contextgraph', 'claude-code-plugin');\nconst PLUGIN_PATH = join(PLUGIN_DIR, 'plugins', 'contextgraph');\n\n/**\n * Get the path to the contextgraph plugin, cloning it if necessary\n */\nexport async function ensurePlugin(): Promise<string> {\n // Check if plugin already exists\n try {\n await access(PLUGIN_PATH);\n console.log(`📦 Using plugin: ${PLUGIN_PATH}`);\n return PLUGIN_PATH;\n } catch {\n // Plugin path doesn't exist, check if repo dir exists\n }\n\n // Check if repo directory exists but plugin path is missing (incomplete clone or wrong structure)\n let repoDirExists = false;\n try {\n await access(PLUGIN_DIR);\n repoDirExists = true;\n } catch {\n // Directory doesn't exist, will need to clone\n }\n\n if (repoDirExists) {\n // Directory exists but plugin path doesn't - try pulling latest\n console.log('📦 Plugin directory exists but incomplete, pulling latest...');\n await runCommand('git', ['pull'], PLUGIN_DIR);\n\n // Check again after pull\n try {\n await access(PLUGIN_PATH);\n console.log(`📦 Plugin ready: ${PLUGIN_PATH}`);\n return PLUGIN_PATH;\n } catch {\n throw new Error(`Plugin not found at ${PLUGIN_PATH} even after git pull. Check repository structure.`);\n }\n }\n\n console.log(`📦 Cloning plugin from ${PLUGIN_REPO}...`);\n\n // Ensure parent directory exists\n const contextgraphDir = join(homedir(), '.contextgraph');\n try {\n await mkdir(contextgraphDir, { recursive: true });\n } catch {\n // Directory might already exist\n }\n\n // Clone the repository\n await runCommand('git', ['clone', PLUGIN_REPO, PLUGIN_DIR]);\n\n // Verify plugin exists after clone\n try {\n await access(PLUGIN_PATH);\n console.log(`📦 Plugin installed: ${PLUGIN_PATH}`);\n return PLUGIN_PATH;\n } catch {\n throw new Error(`Plugin clone succeeded but plugin path not found at ${PLUGIN_PATH}`);\n }\n}\n\n/**\n * Update the plugin to latest version\n */\nexport async function updatePlugin(): Promise<void> {\n try {\n await access(PLUGIN_DIR);\n } catch {\n throw new Error('Plugin not installed. Run the agent first to auto-install.');\n }\n\n console.log('[Plugin Setup] Updating plugin...');\n await runCommand('git', ['pull'], PLUGIN_DIR);\n console.log('[Plugin Setup] Plugin updated');\n}\n\n/**\n * Get the plugin path (without ensuring it exists)\n */\nexport function getPluginPath(): string {\n return PLUGIN_PATH;\n}\n\nfunction runCommand(command: string, args: string[], cwd?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args, { cwd, stdio: 'inherit' });\n\n proc.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`${command} ${args[0]} failed with exit code ${code}`));\n }\n });\n\n proc.on('error', (err) => {\n reject(new Error(`Failed to spawn ${command}: ${err.message}`));\n });\n });\n}\n","/**\n * SDK Event Transformer - Transforms Claude SDK messages into agentLog event format\n *\n * This module provides a pure transformation function that converts SDK messages\n * into LogEvent objects for the log streaming infrastructure.\n *\n * IMPORTANT: Events are emitted in the same format as the Vercel sandbox agents\n * to ensure compatibility with the AgentEventMessage component. The full SDK\n * message is preserved in the `data` field without truncation.\n */\n\nimport type { LogEvent } from './log-transport.js';\nimport type { SDKMessage, SDKAssistantMessage, SDKResultMessage } from '@anthropic-ai/claude-agent-sdk';\n\n/**\n * Transform an SDK message into a LogEvent\n *\n * The transformation preserves the full SDK message in the `data` field,\n * matching the Vercel sandbox format for UI compatibility.\n *\n * @param message - The SDK message to transform\n * @returns A LogEvent or null if the message should be skipped\n */\nexport function transformSDKMessage(message: SDKMessage): LogEvent | null {\n const timestamp = new Date().toISOString();\n\n switch (message.type) {\n case 'system':\n return transformSystemMessage(message, timestamp);\n\n case 'assistant':\n return transformAssistantMessage(message as SDKAssistantMessage, timestamp);\n\n case 'result':\n return transformResultMessage(message as SDKResultMessage, timestamp);\n\n case 'user':\n // User messages with tool results\n return transformUserMessage(message, timestamp);\n\n default:\n // Skip unknown message types\n return null;\n }\n}\n\n/**\n * Transform a system message (initialization, etc.)\n */\nfunction transformSystemMessage(\n message: SDKMessage & { subtype?: string; content?: string },\n timestamp: string\n): LogEvent {\n // Emit in the format expected by AgentEventMessage\n return {\n eventType: 'claude_message',\n content: message.content || `System: ${message.subtype || 'initialization'}`,\n data: {\n type: 'system',\n subtype: message.subtype,\n content: message.content,\n session_id: message.session_id,\n },\n timestamp,\n };\n}\n\n/**\n * Transform an assistant message (text, tool use, thinking)\n *\n * Preserves the full SDK message in the data field for UI rendering.\n * This matches the Vercel sandbox format where the entire SDK JSON\n * is stored in event.data.\n */\nfunction transformAssistantMessage(\n message: SDKAssistantMessage,\n timestamp: string\n): LogEvent | null {\n const content = message.message?.content;\n if (!content || !Array.isArray(content)) {\n return null;\n }\n\n // Generate a human-readable summary for the content field\n const contentSummary = generateContentSummary(content);\n\n // Emit the full SDK message structure in data for UI compatibility\n // This matches sandbox-execution.ts line 512-522\n return {\n eventType: 'claude_message',\n content: contentSummary,\n data: {\n type: 'assistant',\n message: message.message,\n session_id: message.session_id,\n parent_tool_use_id: message.parent_tool_use_id,\n },\n timestamp,\n };\n}\n\n/**\n * Transform a result message (completion status)\n *\n * Emits as claude_message with type='result' to match sandbox format.\n */\nfunction transformResultMessage(\n message: SDKResultMessage,\n timestamp: string\n): LogEvent {\n const isSuccess = message.subtype === 'success';\n const durationSec = message.duration_ms\n ? (message.duration_ms / 1000).toFixed(1)\n : 'unknown';\n\n return {\n eventType: 'claude_message',\n content: isSuccess\n ? `Completed successfully in ${durationSec}s`\n : `Execution ${message.subtype}: ${durationSec}s`,\n data: {\n type: 'result',\n subtype: message.subtype,\n duration_ms: message.duration_ms,\n total_cost_usd: message.total_cost_usd,\n num_turns: message.num_turns,\n usage: message.usage,\n session_id: message.session_id,\n },\n timestamp,\n };\n}\n\n/**\n * Transform a user message (typically contains tool results)\n *\n * Preserves full tool result content for UI rendering.\n */\nfunction transformUserMessage(\n message: SDKMessage & { message?: { content?: unknown } },\n timestamp: string\n): LogEvent | null {\n const content = message.message?.content;\n if (!content || !Array.isArray(content)) {\n return null;\n }\n\n // Check if this contains tool results\n const hasToolResults = content.some(\n (block: any) => block.type === 'tool_result'\n );\n\n if (!hasToolResults) {\n return null;\n }\n\n // Generate summary for content field\n const summaries = content\n .filter((block: any) => block.type === 'tool_result')\n .map((block: any) => {\n const prefix = block.is_error ? '❌' : '✓';\n const resultText = extractToolResultText(block.content);\n return `${prefix} ${resultText.substring(0, 100)}${resultText.length > 100 ? '...' : ''}`;\n });\n\n // Emit full message structure in data for UI rendering\n return {\n eventType: 'claude_message',\n content: summaries.join('\\n'),\n data: {\n type: 'user',\n message: message.message,\n session_id: message.session_id,\n },\n timestamp,\n };\n}\n\n/**\n * Generate a human-readable summary from message content blocks\n */\nfunction generateContentSummary(content: unknown[]): string {\n const parts: string[] = [];\n\n for (const block of content as any[]) {\n if (block.type === 'text' && block.text) {\n // Include first 200 chars of text in summary\n const text = block.text.length > 200\n ? block.text.substring(0, 200) + '...'\n : block.text;\n parts.push(text);\n } else if (block.type === 'tool_use') {\n parts.push(`🔧 ${block.name}`);\n } else if (block.type === 'thinking') {\n parts.push('💭 [thinking]');\n }\n }\n\n return parts.join(' | ') || '[no content]';\n}\n\n/**\n * Extract text content from a tool result for summary purposes\n */\nfunction extractToolResultText(\n content: string | Array<{ type: string; text?: string }> | undefined\n): string {\n if (!content) return '';\n\n if (typeof content === 'string') {\n return content;\n }\n\n if (Array.isArray(content)) {\n return content\n .filter(block => block.type === 'text' && block.text)\n .map(block => block.text)\n .join('\\n');\n }\n\n return '';\n}\n\n/**\n * Batch transform multiple SDK messages\n * Useful for processing message arrays from SDK iterations\n *\n * @param messages - Array of SDK messages\n * @returns Array of LogEvents (excluding nulls)\n */\nexport function transformSDKMessages(messages: SDKMessage[]): LogEvent[] {\n return messages\n .map(transformSDKMessage)\n .filter((event): event is LogEvent => event !== null);\n}\n","export interface RetryOptions {\n maxRetries?: number;\n baseDelayMs?: number;\n maxDelayMs?: number;\n /** If true, will log retry attempts */\n verbose?: boolean;\n}\n\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n retryOptions: RetryOptions = {}\n): Promise<Response> {\n const { maxRetries = 3, baseDelayMs = 1000, maxDelayMs = 10000 } = retryOptions;\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, options);\n // Don't retry on client errors (4xx), only on server/network errors\n if (response.ok || (response.status >= 400 && response.status < 500)) {\n return response;\n }\n // Server error (5xx) - will retry\n lastError = new Error(`HTTP ${response.status}`);\n } catch (error) {\n // Network error - will retry\n lastError = error instanceof Error ? error : new Error(String(error));\n }\n\n if (attempt < maxRetries) {\n // Exponential backoff with jitter\n const delay = Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);\n const jitter = delay * 0.1 * Math.random();\n await new Promise((resolve) => setTimeout(resolve, delay + jitter));\n }\n }\n\n throw lastError ?? new Error('Request failed after retries');\n}\n","/**\n * LogTransportService - Handles sending log events to the platform API\n *\n * This service manages:\n * - Creating and updating runs\n * - Sending batches of log events\n * - Retry logic with exponential backoff\n */\n\n/**\n * Log event types supported by the platform\n */\nexport type LogEventType =\n | 'stdout'\n | 'stderr'\n | 'claude_message'\n | 'tool_use'\n | 'tool_result'\n | 'system';\n\n/**\n * A log event to be sent to the platform\n */\nexport interface LogEvent {\n eventType: LogEventType;\n content: string;\n data?: Record<string, unknown>;\n timestamp: string;\n}\n\n/**\n * Response from creating a run\n */\nexport interface CreateRunResponse {\n runId: string;\n}\n\n/**\n * Response from batch send\n */\nexport interface BatchSendResponse {\n success: boolean;\n eventsReceived?: number;\n}\n\n/**\n * Configuration for retry behavior\n */\nexport interface RetryConfig {\n maxRetries: number;\n initialDelayMs: number;\n backoffFactor: number;\n}\n\nconst DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxRetries: 3,\n initialDelayMs: 100,\n backoffFactor: 2,\n};\n\n/**\n * Service for sending log events to the platform API\n */\nexport class LogTransportService {\n private runId: string | null = null;\n private retryConfig: RetryConfig;\n\n constructor(\n private baseUrl: string,\n private authToken: string,\n runId?: string,\n retryConfig?: Partial<RetryConfig>\n ) {\n this.runId = runId ?? null;\n this.retryConfig = { ...DEFAULT_RETRY_CONFIG, ...retryConfig };\n }\n\n /**\n * Get the current run ID\n */\n getRunId(): string | null {\n return this.runId;\n }\n\n /**\n * Create a new run for an action\n * @param actionId - The action ID this run is executing\n * @returns The created run ID\n */\n async createRun(actionId: string): Promise<string> {\n const response = await this.makeRequest('/api/runs', {\n method: 'POST',\n body: JSON.stringify({\n actionId,\n state: 'queued',\n }),\n });\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || 'Failed to create run');\n }\n\n this.runId = result.data.runId;\n return this.runId;\n }\n\n /**\n * Start the run (transition to running state)\n * Called when execution begins\n */\n async startRun(): Promise<void> {\n if (!this.runId) {\n throw new Error('No run ID set. Call createRun() first.');\n }\n\n const response = await this.makeRequest(`/api/runs/${this.runId}/start`, {\n method: 'POST',\n body: JSON.stringify({}),\n });\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || 'Failed to start run');\n }\n }\n\n /**\n * Finish the run with an outcome\n * @param outcome - 'success' | 'error' | 'timeout' | 'incomplete'\n * @param metadata - Optional metadata (exitCode, errorMessage, cost, usage)\n */\n async finishRun(\n outcome: 'success' | 'error' | 'timeout' | 'incomplete',\n metadata?: {\n exitCode?: number;\n errorMessage?: string;\n cost?: number;\n usage?: Record<string, unknown>;\n }\n ): Promise<void> {\n if (!this.runId) {\n throw new Error('No run ID set. Call createRun() first.');\n }\n\n const response = await this.makeRequest(`/api/runs/${this.runId}/finish`, {\n method: 'POST',\n body: JSON.stringify({\n outcome,\n exitCode: metadata?.exitCode?.toString(),\n errorMessage: metadata?.errorMessage,\n }),\n });\n\n const result = await response.json();\n\n if (!result.success) {\n // If the run is already in a finishing state (summarizing, finished),\n // this is not an error - the server is already handling completion\n const error = result.error || 'Failed to finish run';\n if (error.includes('summarizing') || error.includes('finished')) {\n console.log('[LogTransport] Run is already being finished by server, skipping client finish');\n return;\n }\n throw new Error(error);\n }\n }\n\n /**\n * Update the state of the current run\n * @deprecated Use startRun() and finishRun() instead\n * @param state - New state for the run\n * @param metadata - Optional metadata to include with the state update\n */\n async updateRunState(\n state: string,\n metadata?: Record<string, unknown>\n ): Promise<void> {\n if (!this.runId) {\n throw new Error('No run ID set. Call createRun() first.');\n }\n\n // Map state to appropriate endpoint\n if (state === 'executing' || state === 'preparing' || state === 'running') {\n await this.startRun();\n } else if (state === 'completed' || state === 'failed') {\n const outcome = state === 'completed' ? 'success' : 'error';\n await this.finishRun(outcome, {\n exitCode: metadata?.exitCode as number | undefined,\n errorMessage: metadata?.error as string | undefined,\n cost: metadata?.cost as number | undefined,\n usage: metadata?.usage as Record<string, unknown> | undefined,\n });\n } else {\n // For unknown states, just log a warning\n console.warn(`[LogTransport] Unknown state '${state}' - no API call made`);\n }\n }\n\n /**\n * Send a batch of log events to the platform\n * @param events - Array of log events to send\n * @param workerId - Optional worker ID\n * @returns Success status and number of events received\n */\n async sendBatch(\n events: LogEvent[],\n workerId?: string\n ): Promise<BatchSendResponse> {\n if (!this.runId) {\n throw new Error('No run ID set. Call createRun() first.');\n }\n\n if (events.length === 0) {\n return { success: true, eventsReceived: 0 };\n }\n\n const response = await this.makeRequest('/api/agents/log/event', {\n method: 'POST',\n body: JSON.stringify({\n runId: this.runId,\n events,\n ...(workerId && { workerId }),\n }),\n });\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || 'Failed to send log batch');\n }\n\n return {\n success: true,\n eventsReceived: result.data?.eventsReceived ?? events.length,\n };\n }\n\n /**\n * Make an HTTP request with retry logic\n */\n private async makeRequest(\n path: string,\n options: RequestInit\n ): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n const headers = {\n 'x-authorization': `Bearer ${this.authToken}`,\n 'Content-Type': 'application/json',\n };\n\n let lastError: Error | null = null;\n let delay = this.retryConfig.initialDelayMs;\n\n for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {\n try {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...headers,\n ...(options.headers || {}),\n },\n });\n\n // Don't retry on client errors (4xx) - these won't succeed on retry\n if (response.status >= 400 && response.status < 500) {\n return response;\n }\n\n // Retry on server errors (5xx) or network issues\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Don't wait after the last attempt\n if (attempt < this.retryConfig.maxRetries) {\n await this.sleep(delay);\n delay *= this.retryConfig.backoffFactor;\n }\n }\n }\n\n throw new Error(\n `Request failed after ${this.retryConfig.maxRetries + 1} attempts: ${lastError?.message}`\n );\n }\n\n /**\n * Sleep for a given number of milliseconds\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * LogBuffer - Manages buffered, non-blocking log transmission\n *\n * Collects log events and flushes them periodically to avoid\n * blocking the main Claude execution flow.\n */\n\nimport { LogTransportService, type LogEvent } from './log-transport.js';\n\n// Buffer configuration for log streaming\nconst LOG_BUFFER_FLUSH_INTERVAL_MS = 500;\nconst LOG_BUFFER_MAX_SIZE = 50;\nconst LOG_BUFFER_MAX_QUEUE_SIZE = 1000;\n\nexport class LogBuffer {\n private buffer: LogEvent[] = [];\n private flushIntervalId: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n\n constructor(\n private transport: LogTransportService,\n private flushIntervalMs: number = LOG_BUFFER_FLUSH_INTERVAL_MS,\n private maxBufferSize: number = LOG_BUFFER_MAX_SIZE,\n private maxQueueSize: number = LOG_BUFFER_MAX_QUEUE_SIZE\n ) {}\n\n /**\n * Add an event to the buffer (fire-and-forget)\n * Handles backpressure by dropping oldest events if queue is full.\n */\n push(event: LogEvent): void {\n // Backpressure: drop oldest events if queue is too large\n if (this.buffer.length >= this.maxQueueSize) {\n this.buffer.shift();\n }\n this.buffer.push(event);\n\n // Trigger immediate flush if buffer is at max size\n if (this.buffer.length >= this.maxBufferSize) {\n this.flushAsync();\n }\n }\n\n /**\n * Start periodic flushing\n */\n start(): void {\n if (this.flushIntervalId !== null) return;\n\n this.flushIntervalId = setInterval(() => {\n this.flushAsync();\n }, this.flushIntervalMs);\n }\n\n /**\n * Stop periodic flushing and flush remaining events\n */\n async stop(): Promise<void> {\n if (this.flushIntervalId !== null) {\n clearInterval(this.flushIntervalId);\n this.flushIntervalId = null;\n }\n\n // Final flush of remaining events\n await this.flush();\n }\n\n /**\n * Async flush (fire-and-forget, non-blocking)\n */\n private flushAsync(): void {\n // Don't start a new flush if one is in progress\n if (this.isFlushing || this.buffer.length === 0) return;\n\n // Fire and forget - don't await\n this.flush().catch((error) => {\n console.error('[LogBuffer] Flush error:', error instanceof Error ? error.message : String(error));\n });\n }\n\n /**\n * Flush current buffer contents to transport\n */\n private async flush(): Promise<void> {\n if (this.isFlushing || this.buffer.length === 0) return;\n\n this.isFlushing = true;\n const eventsToSend = [...this.buffer];\n this.buffer = [];\n\n try {\n await this.transport.sendBatch(eventsToSend);\n } catch (error) {\n // Log errors but don't re-queue (to avoid infinite growth)\n console.error('[LogBuffer] Failed to send batch:', error instanceof Error ? error.message : String(error));\n } finally {\n this.isFlushing = false;\n }\n }\n}\n","/**\n * HeartbeatManager - Sends periodic liveness signals during execution\n *\n * This service manages:\n * - Periodic heartbeat signals to the platform\n * - Phase/progress updates without blocking execution\n * - Graceful error handling (log, don't throw)\n */\n\n/**\n * Execution phases for heartbeat reporting\n */\nexport type HeartbeatPhase = 'executing' | 'reading' | 'writing' | 'thinking';\n\n/**\n * Heartbeat payload sent to the platform\n */\nexport interface HeartbeatPayload {\n phase: HeartbeatPhase;\n progress?: number;\n timestamp: string;\n}\n\n/**\n * HeartbeatManager - Self-managing heartbeat service\n *\n * Sends periodic liveness signals to keep the platform informed\n * of worker status without blocking the main execution flow.\n */\nexport class HeartbeatManager {\n private intervalId: ReturnType<typeof setInterval> | null = null;\n private currentPhase: HeartbeatPhase = 'executing';\n private currentProgress: number | undefined = undefined;\n\n constructor(\n private baseUrl: string,\n private authToken: string,\n private runId: string\n ) {}\n\n /**\n * Start sending periodic heartbeats\n * @param intervalMs - Time between heartbeats in milliseconds (default: 30000)\n */\n start(intervalMs: number = 30000): void {\n // Clear any existing interval\n this.stop();\n\n // Send initial heartbeat immediately\n this.sendHeartbeat();\n\n // Set up periodic heartbeats\n this.intervalId = setInterval(() => {\n this.sendHeartbeat();\n }, intervalMs);\n }\n\n /**\n * Stop sending heartbeats\n */\n stop(): void {\n if (this.intervalId !== null) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n }\n\n /**\n * Update the current phase and optional progress\n * @param phase - Current execution phase\n * @param progress - Optional progress percentage (0-100)\n */\n updatePhase(phase: HeartbeatPhase, progress?: number): void {\n this.currentPhase = phase;\n this.currentProgress = progress;\n }\n\n /**\n * Check if heartbeat manager is currently running\n */\n isRunning(): boolean {\n return this.intervalId !== null;\n }\n\n /**\n * Send a heartbeat to the platform (internal method)\n * Errors are logged but not thrown to avoid blocking execution.\n * Includes one retry attempt for transient network failures.\n */\n private async sendHeartbeat(): Promise<void> {\n const payload: HeartbeatPayload = {\n phase: this.currentPhase,\n timestamp: new Date().toISOString(),\n };\n\n if (this.currentProgress !== undefined) {\n payload.progress = this.currentProgress;\n }\n\n const url = `${this.baseUrl}/api/runs/${this.runId}/heartbeat`;\n const requestOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'x-authorization': `Bearer ${this.authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n };\n\n // Try up to 2 times (initial + 1 retry)\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const response = await fetch(url, requestOptions);\n\n if (response.ok) {\n return; // Success\n }\n\n // Don't retry client errors (4xx)\n if (response.status >= 400 && response.status < 500) {\n console.error(\n `Heartbeat failed: HTTP ${response.status} ${response.statusText}`\n );\n return;\n }\n\n // Server error - will retry\n if (attempt === 0) {\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n } catch (error) {\n // Network error - retry once\n if (attempt === 0) {\n await new Promise((resolve) => setTimeout(resolve, 1000));\n } else {\n // Log on final failure\n console.error(\n 'Heartbeat error:',\n error instanceof Error ? error.message : String(error)\n );\n }\n }\n }\n }\n}\n","import { loadCredentials, isExpired, isTokenExpired } from '../credentials.js';\nimport { executeClaude } from '../claude-sdk.js';\nimport { fetchWithRetry } from '../fetch-with-retry.js';\nimport { LogTransportService } from '../log-transport.js';\nimport { LogBuffer } from '../log-buffer.js';\nimport { HeartbeatManager } from '../heartbeat-manager.js';\n\nconst API_BASE_URL = 'https://www.contextgraph.dev';\n\nexport interface WorkflowOptions {\n cwd?: string;\n}\n\nexport async function runPrepare(actionId: string, options?: WorkflowOptions): Promise<void> {\n const credentials = await loadCredentials();\n\n if (!credentials) {\n console.error('❌ Not authenticated. Run authentication first.');\n process.exit(1);\n }\n\n if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {\n console.error('❌ Token expired. Re-authenticate to continue.');\n process.exit(1);\n }\n\n console.log(`Fetching preparation instructions for action ${actionId}...\\n`);\n\n const response = await fetchWithRetry(\n `${API_BASE_URL}/api/prompts/prepare`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${credentials.clerkToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ actionId }),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to fetch prepare prompt: ${response.statusText}\\n${errorText}`);\n }\n\n const { prompt } = await response.json();\n\n // Initialize log streaming infrastructure\n const logTransport = new LogTransportService(API_BASE_URL, credentials.clerkToken);\n let runId: string | undefined;\n let heartbeatManager: HeartbeatManager | undefined;\n let logBuffer: LogBuffer | undefined;\n\n try {\n // Create run for this preparation phase\n console.log('[Log Streaming] Creating run for prepare phase...');\n runId = await logTransport.createRun(actionId);\n console.log(`[Log Streaming] Run created: ${runId}`);\n\n // Update run state to preparing\n await logTransport.updateRunState('preparing');\n\n // Start heartbeat manager\n heartbeatManager = new HeartbeatManager(API_BASE_URL, credentials.clerkToken, runId);\n heartbeatManager.start();\n console.log('[Log Streaming] Heartbeat started');\n\n // Set up log buffer for non-blocking transmission\n logBuffer = new LogBuffer(logTransport);\n logBuffer.start();\n\n console.log('Spawning Claude for preparation...\\n');\n\n const claudeResult = await executeClaude({\n prompt,\n cwd: options?.cwd || process.cwd(),\n authToken: credentials.clerkToken,\n onLogEvent: (event) => {\n logBuffer!.push(event);\n },\n });\n\n // Update run state based on execution result\n if (claudeResult.exitCode === 0) {\n await logTransport.finishRun('success', {\n exitCode: claudeResult.exitCode,\n cost: claudeResult.cost,\n usage: claudeResult.usage,\n });\n console.log('\\n✅ Preparation complete');\n } else {\n await logTransport.finishRun('error', {\n exitCode: claudeResult.exitCode,\n errorMessage: `Claude preparation failed with exit code ${claudeResult.exitCode}`,\n });\n console.error(`\\n❌ Claude preparation failed with exit code ${claudeResult.exitCode}`);\n process.exit(1);\n }\n\n } catch (error) {\n // Update run state to failed if we have a run\n if (runId) {\n try {\n await logTransport.finishRun('error', {\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n } catch (stateError) {\n console.error('[Log Streaming] Failed to update run state:', stateError);\n }\n }\n throw error;\n\n } finally {\n // Cleanup: stop heartbeat and flush remaining logs\n if (heartbeatManager) {\n heartbeatManager.stop();\n console.log('[Log Streaming] Heartbeat stopped');\n }\n\n if (logBuffer) {\n await logBuffer.stop();\n console.log('[Log Streaming] Logs flushed');\n }\n }\n}\n","import { loadCredentials, isExpired, isTokenExpired } from '../credentials.js';\nimport { executeClaude } from '../claude-sdk.js';\nimport { fetchWithRetry } from '../fetch-with-retry.js';\nimport { LogTransportService } from '../log-transport.js';\nimport { LogBuffer } from '../log-buffer.js';\nimport { HeartbeatManager } from '../heartbeat-manager.js';\n\nconst API_BASE_URL = 'https://www.contextgraph.dev';\n\nexport interface WorkflowOptions {\n cwd?: string;\n}\n\nexport async function runExecute(actionId: string, options?: WorkflowOptions): Promise<void> {\n const credentials = await loadCredentials();\n\n if (!credentials) {\n console.error('❌ Not authenticated. Run authentication first.');\n process.exit(1);\n }\n\n if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {\n console.error('❌ Token expired. Re-authenticate to continue.');\n process.exit(1);\n }\n\n console.log(`Fetching execution instructions for action ${actionId}...\\n`);\n\n const response = await fetchWithRetry(\n `${API_BASE_URL}/api/prompts/execute`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${credentials.clerkToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ actionId }),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to fetch execute prompt: ${response.statusText}\\n${errorText}`);\n }\n\n const { prompt } = await response.json();\n\n // Initialize log streaming infrastructure\n const logTransport = new LogTransportService(API_BASE_URL, credentials.clerkToken);\n let runId: string | undefined;\n let heartbeatManager: HeartbeatManager | undefined;\n let logBuffer: LogBuffer | undefined;\n\n try {\n // Create run for this execution\n console.log('[Log Streaming] Creating run...');\n runId = await logTransport.createRun(actionId);\n console.log(`[Log Streaming] Run created: ${runId}`);\n\n // Update run state to executing\n await logTransport.updateRunState('executing');\n\n // Start heartbeat manager\n heartbeatManager = new HeartbeatManager(API_BASE_URL, credentials.clerkToken, runId);\n heartbeatManager.start();\n console.log('[Log Streaming] Heartbeat started');\n\n // Set up log buffer for non-blocking transmission\n logBuffer = new LogBuffer(logTransport);\n logBuffer.start();\n\n console.log('Spawning Claude for execution...\\n');\n\n const claudeResult = await executeClaude({\n prompt,\n cwd: options?.cwd || process.cwd(),\n authToken: credentials.clerkToken,\n onLogEvent: (event) => {\n logBuffer!.push(event);\n },\n });\n\n // Update run state based on execution result\n if (claudeResult.exitCode === 0) {\n await logTransport.finishRun('success', {\n exitCode: claudeResult.exitCode,\n cost: claudeResult.cost,\n usage: claudeResult.usage,\n });\n console.log('\\n✅ Execution complete');\n } else {\n await logTransport.finishRun('error', {\n exitCode: claudeResult.exitCode,\n errorMessage: `Claude execution failed with exit code ${claudeResult.exitCode}`,\n });\n throw new Error(`Claude execution failed with exit code ${claudeResult.exitCode}`);\n }\n\n } catch (error) {\n // Update run state to failed if we have a run\n if (runId) {\n try {\n await logTransport.finishRun('error', {\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n } catch (stateError) {\n console.error('[Log Streaming] Failed to update run state:', stateError);\n }\n }\n throw error;\n\n } finally {\n // Cleanup: stop heartbeat and flush remaining logs\n if (heartbeatManager) {\n heartbeatManager.stop();\n console.log('[Log Streaming] Heartbeat stopped');\n }\n\n if (logBuffer) {\n await logBuffer.stop();\n console.log('[Log Streaming] Logs flushed');\n }\n }\n}\n","import { randomUUID } from 'crypto';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { ApiClient } from '../api-client.js';\nimport type { ActionNode, ActionMetadata } from '../types/actions.js';\nimport { findNextLeaf, type FindNextLeafResult } from '../next-action.js';\nimport { runPrepare } from './prepare.js';\nimport { runExecute } from './execute.js';\nimport { prepareWorkspace } from '../workspace-prep.js';\nimport { loadCredentials, isExpired, isTokenExpired } from '../credentials.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n// When built, this file is in dist/, so package.json is one level up\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, '../package.json'), 'utf-8')\n);\n\n// Polling configuration from environment variables\nconst INITIAL_POLL_INTERVAL = parseInt(process.env.WORKER_INITIAL_POLL_INTERVAL || '2000', 10); // 2 seconds default\nconst MAX_POLL_INTERVAL = parseInt(process.env.WORKER_MAX_POLL_INTERVAL || '5000', 10); // 5 seconds default\nconst BACKOFF_MULTIPLIER = 1.5;\nconst STATUS_INTERVAL_MS = 30000; // Show status every 30 seconds when idle\n\n// Retry configuration for transient API errors\n// For extended outages, we wait indefinitely with a ceiling on delay\nconst MAX_API_RETRIES = Infinity; // Never give up on transient errors\nconst INITIAL_RETRY_DELAY = 1000; // 1 second\nconst MAX_RETRY_DELAY = 60000; // 1 minute ceiling\nconst OUTAGE_WARNING_THRESHOLD = 5; // Warn user after this many retries\n\n// Module-scope state for graceful shutdown\nlet running = true;\nlet currentClaim: { actionId: string; claimId: string; workerId: string } | null = null;\nlet apiClient: ApiClient | null = null;\n\n// Stats tracking\nconst stats = {\n startTime: Date.now(),\n prepared: 0,\n executed: 0,\n errors: 0,\n};\n\nfunction formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n\n if (hours > 0) {\n return `${hours}h ${minutes % 60}m`;\n } else if (minutes > 0) {\n return `${minutes}m ${seconds % 60}s`;\n }\n return `${seconds}s`;\n}\n\nfunction printStatus(): void {\n const uptime = formatDuration(Date.now() - stats.startTime);\n const total = stats.prepared + stats.executed;\n console.log(`Status: ${total} actions (${stats.prepared} prepared, ${stats.executed} executed, ${stats.errors} errors) | Uptime: ${uptime}`);\n}\n\n/**\n * Get the next action to work on, handling tree depth truncation.\n * If the tree is truncated (children exist beyond depth limit), this function\n * will recursively re-fetch the tree starting from the truncated node.\n */\nasync function getNextAction(\n apiClient: ApiClient,\n rootId: string,\n depth: number = 0\n): Promise<ActionNode | null> {\n // Prevent infinite recursion in case of malformed data\n const maxDepth = 20;\n if (depth >= maxDepth) {\n console.error(`❌ Tree traversal exceeded maximum depth (${maxDepth}). Possible cycle or malformed data.`);\n return null;\n }\n\n const tree = await apiClient.fetchTree(rootId, false);\n\n if (tree.done) {\n if (depth === 0) {\n console.log('✅ Root action is already complete');\n }\n return null;\n }\n\n // Use local findNextLeaf to traverse tree and find next action\n const result = findNextLeaf(tree);\n\n // If we found an action, return it\n if (result.action) {\n return result.action;\n }\n\n // If tree was truncated, re-fetch starting from the truncated node\n if (result.truncatedAt) {\n console.log(`📊 Tree depth limit reached at action ${result.truncatedAt}. Fetching deeper...`);\n return getNextAction(apiClient, result.truncatedAt, depth + 1);\n }\n\n // No action found and no truncation - tree is complete or blocked\n return null;\n}\n\n/**\n * Clean up any claimed work and exit gracefully\n */\nasync function cleanupAndExit(): Promise<void> {\n if (currentClaim && apiClient) {\n try {\n console.log(`\\n🧹 Releasing claim on action ${currentClaim.actionId}...`);\n await apiClient.releaseClaim({\n action_id: currentClaim.actionId,\n worker_id: currentClaim.workerId,\n claim_id: currentClaim.claimId,\n });\n console.log('✅ Claim released successfully');\n } catch (error) {\n console.error('⚠️ Failed to release claim:', (error as Error).message);\n }\n }\n console.log('👋 Shutdown complete');\n process.exit(0);\n}\n\n/**\n * Set up signal handlers for graceful shutdown\n */\nfunction setupSignalHandlers(): void {\n process.on('SIGINT', async () => {\n console.log('\\n\\n⚠️ Received SIGINT (Ctrl+C). Shutting down gracefully...');\n running = false;\n await cleanupAndExit();\n });\n\n process.on('SIGTERM', async () => {\n console.log('\\n\\n⚠️ Received SIGTERM. Shutting down gracefully...');\n running = false;\n await cleanupAndExit();\n });\n}\n\n/**\n * Sleep for the specified number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Check if an error is likely transient and worth retrying\n */\nfunction isRetryableError(error: Error): boolean {\n const message = error.message.toLowerCase();\n // Retry on server errors (5xx), network errors, and timeouts\n return (\n message.includes('api error 5') ||\n message.includes('500') ||\n message.includes('502') ||\n message.includes('503') ||\n message.includes('504') ||\n message.includes('network') ||\n message.includes('timeout') ||\n message.includes('econnreset') ||\n message.includes('econnrefused') ||\n message.includes('socket hang up') ||\n message.includes('failed query') // Database query failures\n );\n}\n\nexport async function runLocalAgent(): Promise<void> {\n // Initialize module-scope apiClient for signal handlers\n apiClient = new ApiClient();\n\n // Set up graceful shutdown handlers\n setupSignalHandlers();\n\n // Load and validate credentials upfront\n const credentials = await loadCredentials();\n if (!credentials) {\n console.error('❌ Not authenticated.');\n console.error(' Set CONTEXTGRAPH_API_TOKEN environment variable or run `contextgraph-agent auth`');\n process.exit(1);\n }\n if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {\n console.error('❌ Token expired. Run `contextgraph-agent auth` to re-authenticate.');\n process.exit(1);\n }\n\n // Show authentication method\n const usingApiToken = !!process.env.CONTEXTGRAPH_API_TOKEN;\n if (usingApiToken) {\n console.log('🔐 Authenticated via CONTEXTGRAPH_API_TOKEN');\n }\n\n // Generate unique worker ID for this session\n const workerId = randomUUID();\n\n console.log(`🤖 ContextGraph Agent v${packageJson.version}`);\n console.log(`👷 Worker ID: ${workerId}`);\n console.log(`🔄 Starting continuous worker loop...\\n`);\n console.log(`💡 Press Ctrl+C to gracefully shutdown and release any claimed work\\n`);\n\n let currentPollInterval = INITIAL_POLL_INTERVAL;\n let lastStatusTime = Date.now();\n let consecutiveApiErrors = 0;\n let apiRetryDelay = INITIAL_RETRY_DELAY;\n\n while (running) {\n\n // Claim next action from worker queue with retry logic\n let actionDetail;\n try {\n actionDetail = await apiClient.claimNextAction(workerId);\n // Reset error tracking on success\n consecutiveApiErrors = 0;\n apiRetryDelay = INITIAL_RETRY_DELAY;\n } catch (error) {\n const err = error as Error;\n\n if (isRetryableError(err)) {\n consecutiveApiErrors++;\n\n // Show extended outage warning once\n if (consecutiveApiErrors === OUTAGE_WARNING_THRESHOLD) {\n console.warn(`\\n⚠️ API appears to be experiencing an outage.`);\n console.warn(` Will continue retrying indefinitely (every ${MAX_RETRY_DELAY / 1000}s max).`);\n console.warn(` Press Ctrl+C to stop.\\n`);\n }\n\n if (consecutiveApiErrors < OUTAGE_WARNING_THRESHOLD) {\n console.warn(`⚠️ API error (attempt ${consecutiveApiErrors}): ${err.message}`);\n } else if (consecutiveApiErrors % 10 === 0) {\n // Only log every 10th retry during extended outage to reduce noise\n console.warn(`⚠️ Still retrying... (attempt ${consecutiveApiErrors}, last error: ${err.message})`);\n }\n\n const delaySeconds = Math.round(apiRetryDelay / 1000);\n if (consecutiveApiErrors < OUTAGE_WARNING_THRESHOLD) {\n console.warn(` Retrying in ${delaySeconds}s...`);\n }\n\n await sleep(apiRetryDelay);\n apiRetryDelay = Math.min(apiRetryDelay * 2, MAX_RETRY_DELAY);\n continue;\n }\n\n // Non-retryable error - re-throw\n throw err;\n }\n\n if (!actionDetail) {\n // Show periodic status while waiting\n if (Date.now() - lastStatusTime >= STATUS_INTERVAL_MS) {\n printStatus();\n lastStatusTime = Date.now();\n }\n await sleep(currentPollInterval);\n currentPollInterval = Math.min(currentPollInterval * BACKOFF_MULTIPLIER, MAX_POLL_INTERVAL);\n continue;\n }\n\n // Reset poll interval on successful claim\n currentPollInterval = INITIAL_POLL_INTERVAL;\n\n console.log(`Working: ${actionDetail.title}`);\n\n // Track current claim for graceful shutdown\n if (actionDetail.claim_id) {\n currentClaim = {\n actionId: actionDetail.id,\n claimId: actionDetail.claim_id,\n workerId,\n };\n }\n\n const isPrepared = actionDetail.prepared !== false;\n\n // Prepare workspace - repository URL is required\n const repoUrl = actionDetail.resolved_repository_url || actionDetail.repository_url;\n const branch = actionDetail.resolved_branch || actionDetail.branch;\n\n if (!repoUrl) {\n console.error(`\\n❌ Action \"${actionDetail.title}\" has no repository_url set.`);\n console.error(` Actions must have a repository_url (directly or inherited from parent).`);\n console.error(` Action ID: ${actionDetail.id}`);\n console.error(` resolved_repository_url: ${actionDetail.resolved_repository_url}`);\n console.error(` repository_url: ${actionDetail.repository_url}`);\n process.exit(1);\n }\n\n let workspacePath: string;\n let cleanup: (() => Promise<void>) | undefined;\n\n try {\n const workspace = await prepareWorkspace(repoUrl, {\n branch: branch || undefined,\n authToken: credentials.clerkToken,\n });\n workspacePath = workspace.path;\n cleanup = workspace.cleanup;\n\n if (!isPrepared) {\n await runPrepare(actionDetail.id, { cwd: workspacePath });\n stats.prepared++;\n\n // Release claim after preparation\n if (currentClaim && apiClient) {\n try {\n await apiClient.releaseClaim({\n action_id: currentClaim.actionId,\n worker_id: currentClaim.workerId,\n claim_id: currentClaim.claimId,\n });\n } catch (releaseError) {\n console.error('⚠️ Failed to release claim after preparation:', (releaseError as Error).message);\n }\n }\n currentClaim = null;\n continue;\n }\n\n try {\n await runExecute(actionDetail.id, { cwd: workspacePath });\n stats.executed++;\n console.log(`Completed: ${actionDetail.title}`);\n } catch (executeError) {\n stats.errors++;\n console.error(`Error: ${(executeError as Error).message}. Continuing...`);\n } finally {\n // Release claim after execution completes (success or failure)\n if (currentClaim && apiClient) {\n try {\n await apiClient.releaseClaim({\n action_id: currentClaim.actionId,\n worker_id: currentClaim.workerId,\n claim_id: currentClaim.claimId,\n });\n } catch (releaseError) {\n console.error('⚠️ Failed to release claim:', (releaseError as Error).message);\n }\n }\n currentClaim = null;\n }\n } catch (workspaceError) {\n // Handle workspace preparation or other errors\n stats.errors++;\n console.error(`Error preparing workspace: ${(workspaceError as Error).message}. Continuing...`);\n\n // Release claim on workspace/preparation failure\n if (currentClaim && apiClient) {\n try {\n console.log(`🧹 Releasing claim due to workspace error...`);\n await apiClient.releaseClaim({\n action_id: currentClaim.actionId,\n worker_id: currentClaim.workerId,\n claim_id: currentClaim.claimId,\n });\n console.log('✅ Claim released');\n } catch (releaseError) {\n console.error('⚠️ Failed to release claim:', (releaseError as Error).message);\n }\n }\n currentClaim = null;\n } finally {\n if (cleanup) {\n await cleanup();\n }\n }\n }\n\n}\n\n","import { loadCredentials, isExpired, isTokenExpired } from './credentials.js';\nimport { fetchWithRetry } from './fetch-with-retry.js';\nimport type { ActionDetailResource, ActionNode } from './types/actions.js';\n\nexport class ApiClient {\n constructor(\n private baseUrl: string = 'https://www.contextgraph.dev'\n ) {}\n\n private async getAuthToken(): Promise<string> {\n const credentials = await loadCredentials();\n\n if (!credentials) {\n throw new Error('Not authenticated. Run authentication first.');\n }\n\n // Check both the stored metadata and the actual JWT expiration\n if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {\n throw new Error('Token expired. Re-authenticate to continue.');\n }\n\n return credentials.clerkToken;\n }\n\n async getActionDetail(actionId: string): Promise<ActionDetailResource> {\n const token = await this.getAuthToken();\n\n // Use both x-authorization header and query param for Vercel compatibility\n const response = await fetchWithRetry(\n `${this.baseUrl}/api/actions/${actionId}?token=${encodeURIComponent(token)}`,\n {\n headers: {\n 'x-authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status}`);\n }\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error);\n }\n\n return result.data;\n }\n\n async fetchTree(rootActionId: string, includeCompleted: boolean = false): Promise<ActionNode> {\n const token = await this.getAuthToken();\n\n // Use both x-authorization header and query param for Vercel compatibility\n const response = await fetchWithRetry(\n `${this.baseUrl}/api/tree/${rootActionId}?includeCompleted=${includeCompleted}&token=${encodeURIComponent(token)}`,\n {\n headers: {\n 'x-authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to fetch tree: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n if (!result.success) {\n throw new Error('Failed to fetch tree: API returned unsuccessful response');\n }\n\n // If no root actions, the tree is complete (all actions done)\n if (!result.data.rootActions?.[0]) {\n return { id: rootActionId, title: '', done: true, dependencies: [], children: [] };\n }\n\n return result.data.rootActions[0];\n }\n\n async claimNextAction(workerId: string): Promise<ActionDetailResource | null> {\n const token = await this.getAuthToken();\n\n const response = await fetchWithRetry(\n `${this.baseUrl}/api/worker/next?token=${encodeURIComponent(token)}`,\n {\n method: 'POST',\n headers: {\n 'x-authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ worker_id: workerId }),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error ${response.status}: ${errorText}`);\n }\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || 'API returned unsuccessful response');\n }\n\n // API returns null when no work is available\n return result.data;\n }\n\n async releaseClaim(params: { action_id: string; worker_id: string; claim_id: string }): Promise<void> {\n const token = await this.getAuthToken();\n\n const response = await fetchWithRetry(\n `${this.baseUrl}/api/worker/release?token=${encodeURIComponent(token)}`,\n {\n method: 'POST',\n headers: {\n 'x-authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(params),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error ${response.status}: ${errorText}`);\n }\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || 'API returned unsuccessful response');\n }\n }\n}\n","import { spawn } from 'child_process';\nimport { mkdtemp, rm } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\nimport { fetchWithRetry } from './fetch-with-retry.js';\nimport type { GitHubCredentials } from './types/actions.js';\n\nconst API_BASE_URL = 'https://www.contextgraph.dev';\n\nexport interface WorkspaceResult {\n path: string;\n cleanup: () => Promise<void>;\n}\n\nexport interface PrepareWorkspaceOptions {\n branch?: string;\n authToken: string;\n}\n\nasync function fetchGitHubCredentials(authToken: string): Promise<GitHubCredentials> {\n const response = await fetchWithRetry(`${API_BASE_URL}/api/cli/credentials`, {\n headers: {\n 'x-authorization': `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (response.status === 401) {\n throw new Error('Authentication failed. Please re-authenticate.');\n }\n\n if (response.status === 404) {\n throw new Error(\n 'GitHub not connected. Please connect your GitHub account at https://contextgraph.dev/settings.'\n );\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to fetch GitHub credentials: ${response.statusText}\\n${errorText}`);\n }\n\n return response.json() as Promise<GitHubCredentials>;\n}\n\nfunction runGitCommand(args: string[], cwd?: string): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n const proc = spawn('git', args, { cwd });\n let stdout = '';\n let stderr = '';\n\n proc.stdout.on('data', (data) => {\n stdout += data.toString();\n });\n\n proc.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n proc.on('close', (code) => {\n if (code === 0) {\n resolve({ stdout, stderr });\n } else {\n reject(new Error(`git ${args[0]} failed (exit ${code}): ${stderr || stdout}`));\n }\n });\n\n proc.on('error', (err) => {\n reject(new Error(`Failed to spawn git: ${err.message}`));\n });\n });\n}\n\nfunction buildAuthenticatedUrl(repoUrl: string, token: string): string {\n // Handle https://github.com/... URLs\n if (repoUrl.startsWith('https://github.com/')) {\n return repoUrl.replace('https://github.com/', `https://${token}@github.com/`);\n }\n\n // Handle https://github.com URLs without trailing slash\n if (repoUrl.startsWith('https://github.com')) {\n return repoUrl.replace('https://github.com', `https://${token}@github.com`);\n }\n\n // For other URLs, return as-is (might be SSH or other provider)\n return repoUrl;\n}\n\nexport async function prepareWorkspace(\n repoUrl: string,\n options: PrepareWorkspaceOptions\n): Promise<WorkspaceResult> {\n const { branch, authToken } = options;\n\n // Fetch GitHub credentials\n const credentials = await fetchGitHubCredentials(authToken);\n\n // Create temp directory\n const workspacePath = await mkdtemp(join(tmpdir(), 'cg-workspace-'));\n\n const cleanup = async () => {\n try {\n await rm(workspacePath, { recursive: true, force: true });\n } catch (error) {\n console.error(`Warning: Failed to cleanup workspace at ${workspacePath}:`, error);\n }\n };\n\n try {\n // Build authenticated clone URL\n const cloneUrl = buildAuthenticatedUrl(repoUrl, credentials.githubToken);\n\n // Clone the repository\n console.log(`📂 Cloning ${repoUrl}`);\n console.log(` → ${workspacePath}`);\n await runGitCommand(['clone', cloneUrl, workspacePath]);\n console.log(`✅ Repository cloned`);\n\n // Configure git identity if we have the info\n if (credentials.githubUsername) {\n await runGitCommand(['config', 'user.name', credentials.githubUsername], workspacePath);\n }\n if (credentials.githubEmail) {\n await runGitCommand(['config', 'user.email', credentials.githubEmail], workspacePath);\n }\n\n // Handle branch checkout if specified\n if (branch) {\n // Check if branch exists remotely\n const { stdout } = await runGitCommand(\n ['ls-remote', '--heads', 'origin', branch],\n workspacePath\n );\n\n const branchExists = stdout.trim().length > 0;\n\n if (branchExists) {\n // Checkout existing branch\n console.log(`🌿 Checking out branch: ${branch}`);\n await runGitCommand(['checkout', branch], workspacePath);\n } else {\n // Create new branch\n console.log(`🌱 Creating new branch: ${branch}`);\n await runGitCommand(['checkout', '-b', branch], workspacePath);\n }\n }\n\n return { path: workspacePath, cleanup };\n } catch (error) {\n // Cleanup on failure\n await cleanup();\n throw error;\n }\n}\n"],"mappings":"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","names":["readFileSync","fileURLToPath","dirname","join","path","API_BASE_URL","join","spawn","join","API_BASE_URL","spawn","join","__filename","__dirname","join","__filename","fileURLToPath","__dirname","dirname","packageJson","readFileSync","join"]}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Example: Worker Loop with Completion Context
3
+ *
4
+ * This example demonstrates how a worker processes actions WITHOUT needing to
5
+ * extract completion context from SDK responses. Claude handles completion
6
+ * automatically via MCP tool calls.
7
+ *
8
+ * Action ID: d52569f4-267d-4315-b401-db3e32b90318
9
+ */
10
+
11
+ import { loadCredentials, isExpired, isTokenExpired } from '../src/credentials.js';
12
+ import { executeClaude } from '../src/claude-sdk.js';
13
+ import type { Credentials } from '../src/types/actions.js';
14
+
15
+ const API_BASE_URL = 'https://www.contextgraph.dev';
16
+
17
+ /**
18
+ * Fetch execution prompt for an action from the backend
19
+ */
20
+ async function fetchExecutionPrompt(
21
+ actionId: string,
22
+ authToken: string
23
+ ): Promise<string> {
24
+ const response = await fetch(
25
+ `${API_BASE_URL}/api/prompts/execute`,
26
+ {
27
+ method: 'POST',
28
+ headers: {
29
+ 'Authorization': `Bearer ${authToken}`,
30
+ 'Content-Type': 'application/json',
31
+ },
32
+ body: JSON.stringify({ actionId }),
33
+ }
34
+ );
35
+
36
+ if (!response.ok) {
37
+ const errorText = await response.text();
38
+ throw new Error(`Failed to fetch execute prompt: ${response.statusText}\n${errorText}`);
39
+ }
40
+
41
+ const { prompt } = await response.json();
42
+ return prompt;
43
+ }
44
+
45
+ /**
46
+ * Claim a prepared action from the queue
47
+ *
48
+ * In the real worker loop, this would poll the backend for available work.
49
+ * For this example, we assume an action ID is provided.
50
+ */
51
+ async function claimAction(
52
+ credentials: Credentials
53
+ ): Promise<string | null> {
54
+ // TODO: Implement claim endpoint call
55
+ // For now, this is a placeholder
56
+ console.log('🔍 Checking for available work...');
57
+ console.log(' (In real worker: call /api/worker/claim)');
58
+ return null;
59
+ }
60
+
61
+ /**
62
+ * Process a single action: execute and let Claude handle completion
63
+ *
64
+ * This demonstrates the key insight: workers don't extract completion context.
65
+ * Claude observes changes and calls the MCP complete tool directly.
66
+ */
67
+ async function processAction(
68
+ actionId: string,
69
+ credentials: Credentials
70
+ ): Promise<boolean> {
71
+ console.log(`\n📋 Processing action: ${actionId}`);
72
+
73
+ try {
74
+ // Step 1: Fetch execution prompt
75
+ console.log('📥 Fetching execution prompt...');
76
+ const prompt = await fetchExecutionPrompt(actionId, credentials.clerkToken);
77
+
78
+ // Step 2: Execute with SDK
79
+ // The prompt instructs Claude to:
80
+ // 1. Perform the work
81
+ // 2. Observe changes (files, tests, etc.)
82
+ // 3. Call mcp__plugin_contextgraph_actions__complete with completion context
83
+ console.log('🤖 Spawning Claude for execution...\n');
84
+ const result = await executeClaude({
85
+ prompt,
86
+ cwd: process.cwd(),
87
+ authToken: credentials.clerkToken, // Enables MCP authentication
88
+ });
89
+
90
+ // Step 3: Check result
91
+ // Note: We only check exitCode - completion context is already sent to backend!
92
+ if (result.exitCode !== 0) {
93
+ console.error('\n❌ Execution failed');
94
+ console.error(' Action remains claimed and can be retried');
95
+ return false;
96
+ }
97
+
98
+ console.log('\n✅ Execution succeeded');
99
+ console.log(' Claude has already called MCP complete tool');
100
+ console.log(' Action is now marked done, claim is cleared');
101
+
102
+ // Log execution metadata (for monitoring/debugging)
103
+ if (result.sessionId) {
104
+ console.log(` Session ID: ${result.sessionId}`);
105
+ }
106
+ if (result.cost) {
107
+ console.log(` Cost: $${result.cost.toFixed(4)}`);
108
+ }
109
+ if (result.usage) {
110
+ console.log(` Tokens: ${result.usage.input_tokens} in, ${result.usage.output_tokens} out`);
111
+ }
112
+
113
+ return true;
114
+
115
+ } catch (error) {
116
+ console.error('\n💥 Error processing action:', (error as Error).message);
117
+ console.error(' Action remains claimed and can be retried');
118
+ return false;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Main worker loop (simplified for demonstration)
124
+ *
125
+ * The real worker would:
126
+ * 1. Poll continuously for work
127
+ * 2. Implement backoff when no work available
128
+ * 3. Handle graceful shutdown (Ctrl+C)
129
+ * 4. Track worker health metrics
130
+ */
131
+ async function workerLoop() {
132
+ console.log('🚀 Worker starting...\n');
133
+
134
+ // Load credentials
135
+ const credentials = await loadCredentials();
136
+ if (!credentials) {
137
+ console.error('❌ Not authenticated. Run authentication first.');
138
+ process.exit(1);
139
+ }
140
+
141
+ if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {
142
+ console.error('❌ Token expired. Re-authenticate to continue.');
143
+ process.exit(1);
144
+ }
145
+
146
+ console.log('✅ Authenticated');
147
+ console.log(` User ID: ${credentials.userId}`);
148
+
149
+ // Main loop
150
+ while (true) {
151
+ // Claim next action
152
+ const actionId = await claimAction(credentials);
153
+
154
+ if (!actionId) {
155
+ // No work available - implement backoff
156
+ console.log('⏸️ No work available, sleeping...');
157
+ await new Promise(resolve => setTimeout(resolve, 5000));
158
+ continue;
159
+ }
160
+
161
+ // Process the action
162
+ // Completion happens automatically via MCP tool - no extraction needed!
163
+ await processAction(actionId, credentials);
164
+
165
+ // Continue to next action
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Example: What NOT to do
171
+ *
172
+ * This shows the WRONG approach - trying to extract completion context from
173
+ * SDK responses. This is not needed because Claude sends it via MCP tool.
174
+ */
175
+ async function wrongApproach(actionId: string, credentials: Credentials) {
176
+ const prompt = await fetchExecutionPrompt(actionId, credentials.clerkToken);
177
+ const result = await executeClaude({
178
+ prompt,
179
+ cwd: process.cwd(),
180
+ authToken: credentials.clerkToken,
181
+ });
182
+
183
+ // ❌ WRONG: Trying to extract completion context from result
184
+ // The result only contains: exitCode, sessionId, usage, cost
185
+ // Completion context is NOT in the SDK response!
186
+ const completionContext = {
187
+ // ❌ This doesn't exist in result
188
+ technical_changes: (result as any).technical_changes, // undefined
189
+ outcomes: (result as any).outcomes, // undefined
190
+ challenges: (result as any).challenges, // undefined
191
+ };
192
+
193
+ // ❌ WRONG: Trying to call complete manually
194
+ // Claude already called it via MCP tool during execution!
195
+ // await callCompleteAPI(actionId, completionContext);
196
+ }
197
+
198
+ /**
199
+ * Example: What TO do
200
+ *
201
+ * This shows the CORRECT approach - just check exitCode.
202
+ * Claude handles completion automatically.
203
+ */
204
+ async function correctApproach(actionId: string, credentials: Credentials) {
205
+ const prompt = await fetchExecutionPrompt(actionId, credentials.clerkToken);
206
+ const result = await executeClaude({
207
+ prompt,
208
+ cwd: process.cwd(),
209
+ authToken: credentials.clerkToken,
210
+ });
211
+
212
+ // ✅ CORRECT: Just check if execution succeeded
213
+ if (result.exitCode !== 0) {
214
+ console.error('Execution failed');
215
+ return;
216
+ }
217
+
218
+ // ✅ That's it! Claude already:
219
+ // 1. Performed the work
220
+ // 2. Observed changes
221
+ // 3. Called mcp__plugin_contextgraph_actions__complete
222
+ // 4. Action is marked done, claim is cleared
223
+
224
+ console.log('Execution complete');
225
+ }
226
+
227
+ // Export for testing
228
+ export {
229
+ fetchExecutionPrompt,
230
+ claimAction,
231
+ processAction,
232
+ workerLoop,
233
+ correctApproach,
234
+ wrongApproach,
235
+ };
236
+
237
+ // If run directly, show usage
238
+ if (import.meta.url === `file://${process.argv[1]}`) {
239
+ console.log('This is an example file demonstrating completion context handling.');
240
+ console.log('Key takeaway: Workers do NOT extract completion context from SDK responses.');
241
+ console.log('Claude sends completion context directly to backend via MCP tool.');
242
+ console.log('\nSee docs/COMPLETION_CONTEXT_EXTRACTION.md for full documentation.');
243
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@contextgraph/agent",
3
+ "version": "0.4.0",
4
+ "description": "Autonomous agent for contextgraph action execution",
5
+ "type": "module",
6
+ "bin": {
7
+ "contextgraph-agent": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsup",
11
+ "dev": "tsup --watch",
12
+ "test": "jest"
13
+ },
14
+ "keywords": [
15
+ "contextgraph",
16
+ "agent",
17
+ "autonomous",
18
+ "cli"
19
+ ],
20
+ "author": "contextgraph",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/contextgraph/agent.git"
25
+ },
26
+ "homepage": "https://github.com/contextgraph/agent#readme",
27
+ "bugs": {
28
+ "url": "https://github.com/contextgraph/agent/issues"
29
+ },
30
+ "dependencies": {
31
+ "@anthropic-ai/claude-agent-sdk": "^0.1.50",
32
+ "commander": "^11.0.0",
33
+ "open": "^10.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@jest/globals": "^30.2.0",
37
+ "@types/jest": "^30.0.0",
38
+ "@types/node": "^20.0.0",
39
+ "jest": "^30.2.0",
40
+ "ts-jest": "^29.4.5",
41
+ "tsup": "^8.0.0",
42
+ "typescript": "^5.0.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ }
47
+ }