@evident-ai/cli 0.2.1-dev.fab83f9 → 3.0.1-dev.284117a
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/README.md +10 -3
- package/dist/index.js +1288 -1161
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/config.ts","../src/lib/api.ts","../src/lib/keychain.ts","../src/utils/ui.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../src/commands/run.ts","../../../packages/types/src/telemetry/index.ts","../../../packages/types/src/tunnel/index.ts","../src/lib/telemetry.ts","../src/lib/auth.ts","../src/lib/opencode/health.ts","../src/lib/opencode/process.ts","../src/lib/opencode/install.ts","../src/lib/opencode/session.ts","../src/lib/tunnel/connection.ts","../src/lib/tunnel/forwarding.ts","../src/lib/tunnel/events.ts"],"sourcesContent":["/**\n * Evident CLI\n *\n * Run OpenCode locally and connect it to the Evident platform.\n */\n\nimport { Command } from 'commander';\nimport { login } from './commands/login.js';\nimport { logout } from './commands/logout.js';\nimport { whoami } from './commands/whoami.js';\nimport { run } from './commands/run.js';\nimport { setEnvironment, type Environment } from './lib/config.js';\n\nconst program = new Command();\n\nprogram\n .name('evident')\n .description('Run OpenCode locally and connect it to Evident')\n .version('0.1.0')\n .option('-e, --env <environment>', 'Environment to use (local, dev, production)', 'production')\n .hook('preAction', (thisCommand) => {\n const env = thisCommand.opts().env as Environment;\n if (env) {\n setEnvironment(env);\n }\n });\n\n// Login command\nprogram\n .command('login')\n .description('Authenticate with Evident')\n .option('--token', 'Use token-based authentication (for CI/CD)')\n .option('--no-browser', 'Do not open the browser automatically')\n .action(login);\n\n// Logout command\nprogram.command('logout').description('Remove stored credentials').action(logout);\n\n// Whoami command\nprogram.command('whoami').description('Show the currently logged in user').action(whoami);\n\n// Run command (unified - connects to Evident and processes messages)\nprogram\n .command('run')\n .description('Connect to Evident and process messages')\n .requiredOption('-a, --agent <id>', 'Agent ID to connect to')\n .option('-p, --port <port>', 'OpenCode port (default: 4096)', '4096')\n .option('-v, --verbose', 'Show detailed request/response information')\n .option('-c, --conversation <id>', 'Process only this specific conversation')\n .option('--idle-timeout <seconds>', 'Exit after N seconds idle')\n .option('--json', 'Output in JSON format')\n .action(\n (options: {\n agent: string;\n port: string;\n verbose?: boolean;\n conversation?: string;\n idleTimeout?: string;\n json?: boolean;\n }) => {\n run({\n agent: options.agent,\n port: parseInt(options.port, 10),\n verbose: options.verbose,\n conversation: options.conversation,\n idleTimeout: options.idleTimeout ? parseInt(options.idleTimeout, 10) : undefined,\n json: options.json,\n });\n },\n );\n\n// Parse arguments\nprogram.parse();\n","/**\n * Login Command\n *\n * Authenticates the user using OAuth Device Flow.\n * See ADR-0018 for details.\n */\n\nimport open from 'open';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport { api } from '../lib/api.js';\nimport { storeToken } from '../lib/keychain.js';\nimport { printSuccess, printError, blank, waitForEnter, sleep } from '../utils/ui.js';\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n}\n\ninterface TokenPollResponse {\n status: 'pending' | 'complete' | 'expired';\n access_token?: string;\n expires_at?: string;\n user?: {\n id: string;\n email: string;\n };\n}\n\ninterface LoginOptions {\n token?: boolean;\n noBrowser?: boolean;\n}\n\n/**\n * Start device flow authentication\n */\nasync function deviceFlowLogin(options: LoginOptions): Promise<void> {\n // Step 1: Request device code\n let deviceAuth: DeviceAuthResponse;\n try {\n deviceAuth = await api.post<DeviceAuthResponse>('/auth/device');\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError(`Failed to start authentication: ${message}`);\n process.exit(1);\n }\n\n const { device_code, user_code, verification_uri, interval } = deviceAuth;\n\n // Step 2: Display instructions\n blank();\n console.log(chalk.bold('To authenticate, visit:'));\n console.log();\n console.log(` ${chalk.cyan(verification_uri)}`);\n console.log();\n console.log(chalk.bold('And enter this code:'));\n console.log();\n console.log(` ${chalk.yellow.bold(user_code)}`);\n blank();\n\n // Step 3: Open browser (unless --no-browser)\n if (!options.noBrowser) {\n await waitForEnter('Press Enter to open the browser...');\n try {\n await open(verification_uri);\n } catch {\n console.log(chalk.dim('Could not open browser. Please visit the URL manually.'));\n }\n }\n\n // Step 4: Poll for completion\n const spinner = ora('Waiting for authentication...').start();\n\n const pollIntervalMs = (interval || 5) * 1000;\n const maxAttempts = 60; // 5 minutes max at 5s intervals\n let attempts = 0;\n\n while (attempts < maxAttempts) {\n await sleep(pollIntervalMs);\n attempts++;\n\n try {\n const result = await api.post<TokenPollResponse>('/auth/device/token', {\n device_code,\n });\n\n if (result.status === 'complete' && result.access_token && result.user) {\n // Success! Store the token\n await storeToken({\n token: result.access_token,\n user: result.user,\n expiresAt: result.expires_at,\n });\n\n spinner.stop();\n blank();\n printSuccess(`Logged in as ${chalk.bold(result.user.email)}`);\n return;\n }\n\n if (result.status === 'expired') {\n spinner.stop();\n blank();\n printError('Authentication expired. Please try again.');\n process.exit(1);\n }\n\n // Still pending, continue polling\n } catch (error) {\n // Network error, continue polling\n const message = error instanceof Error ? error.message : 'Unknown error';\n spinner.text = `Waiting for authentication... (${message})`;\n }\n }\n\n spinner.stop();\n blank();\n printError('Authentication timed out. Please try again.');\n process.exit(1);\n}\n\n/**\n * Token-based login (for CI/CD)\n */\nasync function tokenLogin(): Promise<void> {\n console.log('Token login mode.');\n console.log('Visit your Evident dashboard to generate a CLI token.');\n blank();\n\n // Read token from stdin\n process.stdout.write('Paste token: ');\n\n const token = await new Promise<string>((resolve) => {\n let data = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk) => {\n data += chunk;\n });\n process.stdin.on('end', () => {\n resolve(data.trim());\n });\n // For TTY, read a single line\n if (process.stdin.isTTY) {\n process.stdin.once('data', (chunk) => {\n process.stdin.pause();\n resolve(chunk.toString().trim());\n });\n process.stdin.resume();\n }\n });\n\n if (!token) {\n printError('No token provided.');\n process.exit(1);\n }\n\n // Validate the token\n const spinner = ora('Validating token...').start();\n\n try {\n interface ValidateResponse {\n user: { id: string; email: string };\n expires_at?: string;\n }\n\n const result = await api.post<ValidateResponse>('/auth/token/validate', { token });\n\n await storeToken({\n token,\n user: result.user,\n expiresAt: result.expires_at,\n });\n\n spinner.stop();\n printSuccess(`Logged in as ${chalk.bold(result.user.email)}`);\n } catch (error) {\n spinner.stop();\n const message = error instanceof Error ? error.message : 'Invalid token';\n printError(`Authentication failed: ${message}`);\n process.exit(1);\n }\n}\n\n/**\n * Login command handler\n */\nexport async function login(options: LoginOptions): Promise<void> {\n if (options.token) {\n await tokenLogin();\n } else {\n await deviceFlowLogin(options);\n }\n}\n","/**\n * CLI Configuration\n *\n * Manages configuration values and file-based credential storage.\n * Supports multiple environments: local, dev, production\n */\n\nimport Conf from 'conf';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// Supported environments\nexport type Environment = 'local' | 'dev' | 'production';\n\n// Configuration schema\ninterface ConfigSchema {\n apiUrl: string;\n tunnelUrl: string;\n}\n\n// Credentials schema (stored separately with stricter permissions)\ninterface CredentialsSchema {\n token?: string;\n user?: {\n id: string;\n email: string;\n };\n expiresAt?: string;\n}\n\n// Environment presets\n// Domain format: {service}.{env}.evident.run (with aliases for production)\n// API URLs include /v1 prefix as all endpoints are versioned\nconst environmentPresets: Record<Environment, ConfigSchema> = {\n local: {\n apiUrl: 'http://localhost:3001/v1',\n tunnelUrl: 'ws://localhost:8787',\n },\n dev: {\n apiUrl: 'https://api.dev.evident.run/v1',\n tunnelUrl: 'wss://tunnel.dev.evident.run',\n },\n production: {\n // Production URLs also have aliases: api.evident.run, tunnel.evident.run\n apiUrl: 'https://api.production.evident.run/v1',\n tunnelUrl: 'wss://tunnel.production.evident.run',\n },\n};\n\n// Default to production\nconst defaults: ConfigSchema = environmentPresets.production;\n\n// Current environment (can be set via --env flag or EVIDENT_ENV)\nlet currentEnvironment: Environment = 'production';\n\n/**\n * Set the current environment\n */\nexport function setEnvironment(env: Environment): void {\n currentEnvironment = env;\n}\n\n/**\n * Get the current environment\n */\nexport function getEnvironment(): Environment {\n // Environment variable takes precedence\n const envVar = process.env.EVIDENT_ENV as Environment | undefined;\n if (envVar && environmentPresets[envVar]) {\n return envVar;\n }\n return currentEnvironment;\n}\n\n/**\n * Get configuration for current environment\n */\nfunction getEnvConfig(): ConfigSchema {\n return environmentPresets[getEnvironment()];\n}\n\n// Environment overrides (env vars take highest precedence)\nfunction getApiUrl(): string {\n return process.env.EVIDENT_API_URL ?? getEnvConfig().apiUrl;\n}\n\nfunction getTunnelUrl(): string {\n return process.env.EVIDENT_TUNNEL_URL ?? getEnvConfig().tunnelUrl;\n}\n\n// Configuration store\nconst config = new Conf<ConfigSchema>({\n projectName: 'evident',\n projectSuffix: '',\n defaults,\n});\n\n// Credentials store (separate file with restricted access)\nconst credentials = new Conf<CredentialsSchema>({\n projectName: 'evident',\n projectSuffix: '',\n configName: 'credentials',\n defaults: {},\n});\n\n/**\n * Get the configuration directory path\n */\nexport function getConfigDir(): string {\n // XDG_CONFIG_HOME on Linux, ~/.config on others\n const xdgConfig = process.env.XDG_CONFIG_HOME;\n if (xdgConfig) {\n return join(xdgConfig, 'evident');\n }\n return join(homedir(), '.config', 'evident');\n}\n\n/**\n * Get the API URL\n */\nexport function getApiUrlConfig(): string {\n return getApiUrl();\n}\n\n/**\n * Get the tunnel WebSocket URL\n */\nexport function getTunnelUrlConfig(): string {\n return getTunnelUrl();\n}\n\n/**\n * Get stored credentials\n */\nexport function getCredentials(): CredentialsSchema {\n return {\n token: credentials.get('token'),\n user: credentials.get('user'),\n expiresAt: credentials.get('expiresAt'),\n };\n}\n\n/**\n * Store credentials\n */\nexport function setCredentials(creds: CredentialsSchema): void {\n if (creds.token) credentials.set('token', creds.token);\n if (creds.user) credentials.set('user', creds.user);\n if (creds.expiresAt) credentials.set('expiresAt', creds.expiresAt);\n}\n\n/**\n * Clear stored credentials\n */\nexport function clearCredentials(): void {\n credentials.clear();\n}\n\n/**\n * Check if we have valid credentials\n */\nexport function hasValidCredentials(): boolean {\n const creds = getCredentials();\n\n if (!creds.token) {\n return false;\n }\n\n if (creds.expiresAt) {\n const expiresAt = new Date(creds.expiresAt);\n if (expiresAt < new Date()) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Get the CLI command name based on how it was invoked.\n * Returns 'evident' for normal usage, or the actual invocation for dev/npx usage.\n */\nexport function getCliName(): string {\n // Check if running via npx - multiple detection methods\n // 1. npm_execpath contains npx\n // 2. npm_command is 'exec' (npx sets this)\n // 3. Running from a global npx cache directory\n const argv1 = process.argv[1] || '';\n const isNpx =\n process.env.npm_execpath?.includes('npx') ||\n process.env.npm_command === 'exec' ||\n argv1.includes('_npx') ||\n argv1.includes('.npm/_cacache');\n\n if (isNpx) {\n return 'npx @evident-ai/cli@latest';\n }\n\n // Check if running via tsx (development)\n if (argv1.includes('tsx') || argv1.includes('ts-node')) {\n return 'pnpm --filter @evident-ai/cli dev:run';\n }\n\n // Default to 'evident' for normal installed CLI\n return 'evident';\n}\n\nexport { config, credentials };\n","/**\n * API Client\n *\n * Handles HTTP requests to the Evident backend API.\n */\n\nimport { getApiUrlConfig, getCredentials } from './config.js';\n\ninterface ApiRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n body?: unknown;\n headers?: Record<string, string>;\n authenticated?: boolean;\n}\n\ninterface ApiError {\n message: string;\n statusCode: number;\n error?: string;\n}\n\nexport class ApiClient {\n private baseUrl: string;\n\n constructor(baseUrl?: string) {\n this.baseUrl = baseUrl ?? getApiUrlConfig();\n }\n\n /**\n * Make an API request\n */\n async request<T>(path: string, options: ApiRequestOptions = {}): Promise<T> {\n const { method = 'GET', body, headers = {}, authenticated = false } = options;\n\n const url = `${this.baseUrl}${path}`;\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headers,\n };\n\n // Add authentication header if requested\n if (authenticated) {\n const creds = getCredentials();\n if (!creds.token) {\n throw new Error('Not authenticated. Run the `login` command first.');\n }\n requestHeaders['Authorization'] = `Bearer ${creds.token}`;\n }\n\n const response = await fetch(url, {\n method,\n headers: requestHeaders,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n // Handle errors\n if (!response.ok) {\n let errorData: ApiError;\n try {\n errorData = (await response.json()) as ApiError;\n } catch {\n errorData = {\n message: response.statusText,\n statusCode: response.status,\n };\n }\n\n const error = new Error(errorData.message) as Error & { statusCode: number };\n error.statusCode = response.status;\n throw error;\n }\n\n // Handle empty responses\n const contentType = response.headers.get('Content-Type');\n if (!contentType?.includes('application/json')) {\n return {} as T;\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * GET request\n */\n async get<T>(path: string, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<T> {\n return this.request<T>(path, { ...options, method: 'GET' });\n }\n\n /**\n * POST request\n */\n async post<T>(\n path: string,\n body?: unknown,\n options: Omit<ApiRequestOptions, 'method'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'POST', body });\n }\n\n /**\n * PUT request\n */\n async put<T>(\n path: string,\n body?: unknown,\n options: Omit<ApiRequestOptions, 'method'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'PUT', body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(\n path: string,\n options: Omit<ApiRequestOptions, 'method' | 'body'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'DELETE' });\n }\n}\n\n// Lazy API client instance - created on first use after env is set\nlet _api: ApiClient | null = null;\nexport const api = {\n get<T>(path: string, options?: Parameters<ApiClient['get']>[1]) {\n if (!_api) _api = new ApiClient();\n return _api.get<T>(path, options);\n },\n post<T>(path: string, body?: unknown, options?: Parameters<ApiClient['post']>[2]) {\n if (!_api) _api = new ApiClient();\n return _api.post<T>(path, body, options);\n },\n put<T>(path: string, body?: unknown, options?: Parameters<ApiClient['put']>[2]) {\n if (!_api) _api = new ApiClient();\n return _api.put<T>(path, body, options);\n },\n delete<T>(path: string, options?: Parameters<ApiClient['delete']>[1]) {\n if (!_api) _api = new ApiClient();\n return _api.delete<T>(path, options);\n },\n};\n","/**\n * Keychain Storage\n *\n * Provides secure credential storage using the system keychain.\n * Falls back to file-based storage if keychain is unavailable.\n */\n\nimport { getCredentials, setCredentials, clearCredentials } from './config.js';\n\nconst SERVICE_NAME = 'evident-cli';\nconst ACCOUNT_NAME = 'default';\n\n// Dynamic import for keytar (optional dependency)\nasync function getKeytar(): Promise<typeof import('keytar') | null> {\n try {\n const keytar = await import('keytar');\n // Verify keytar is actually functional (has the expected methods)\n if (typeof keytar.setPassword !== 'function') {\n return null;\n }\n return keytar;\n } catch {\n // keytar not available (e.g., missing native dependencies)\n return null;\n }\n}\n\nexport interface StoredCredentials {\n token: string;\n user: {\n id: string;\n email: string;\n };\n expiresAt?: string;\n}\n\n/**\n * Store credentials in the system keychain\n */\nexport async function storeToken(credentials: StoredCredentials): Promise<void> {\n const keytar = await getKeytar();\n\n if (keytar) {\n // Store in system keychain\n await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(credentials));\n } else {\n // Fallback to file-based storage\n setCredentials({\n token: credentials.token,\n user: credentials.user,\n expiresAt: credentials.expiresAt,\n });\n }\n}\n\n/**\n * Retrieve credentials from the system keychain\n */\nexport async function getToken(): Promise<StoredCredentials | null> {\n const keytar = await getKeytar();\n\n if (keytar) {\n // Try system keychain first\n const stored = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);\n if (stored) {\n try {\n return JSON.parse(stored) as StoredCredentials;\n } catch {\n // Invalid JSON, clear it\n await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);\n return null;\n }\n }\n }\n\n // Fallback to file-based storage\n const creds = getCredentials();\n if (creds.token && creds.user) {\n return {\n token: creds.token,\n user: creds.user,\n expiresAt: creds.expiresAt,\n };\n }\n\n return null;\n}\n\n/**\n * Delete credentials from the system keychain\n */\nexport async function deleteToken(): Promise<void> {\n const keytar = await getKeytar();\n\n if (keytar) {\n await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);\n }\n\n // Always clear file-based storage too\n clearCredentials();\n}\n\n/**\n * Check if credentials are valid (not expired)\n */\nexport async function hasValidToken(): Promise<boolean> {\n const credentials = await getToken();\n\n if (!credentials) {\n return false;\n }\n\n if (credentials.expiresAt) {\n const expiresAt = new Date(credentials.expiresAt);\n if (expiresAt < new Date()) {\n return false;\n }\n }\n\n return true;\n}\n","/**\n * CLI UI Utilities\n *\n * Common formatting and display functions.\n */\n\nimport chalk from 'chalk';\n\n/**\n * Format success message\n */\nexport function success(message: string): string {\n return `${chalk.green('✓')} ${message}`;\n}\n\n/**\n * Format error message\n */\nexport function error(message: string): string {\n return `${chalk.red('✗')} ${message}`;\n}\n\n/**\n * Format warning message\n */\nexport function warning(message: string): string {\n return `${chalk.yellow('!')} ${message}`;\n}\n\n/**\n * Format info message\n */\nexport function info(message: string): string {\n return `${chalk.blue('i')} ${message}`;\n}\n\n/**\n * Print success message\n */\nexport function printSuccess(message: string): void {\n console.log(success(message));\n}\n\n/**\n * Print error message\n */\nexport function printError(message: string): void {\n console.error(error(message));\n}\n\n/**\n * Print warning message\n */\nexport function printWarning(message: string): void {\n console.log(warning(message));\n}\n\n/**\n * Print info message\n */\nexport function printInfo(message: string): void {\n console.log(info(message));\n}\n\n/**\n * Format a key-value pair for display\n */\nexport function keyValue(key: string, value: string): string {\n return `${chalk.dim(key + ':')} ${value}`;\n}\n\n/**\n * Print a blank line\n */\nexport function blank(): void {\n console.log();\n}\n\n/**\n * Wait for user to press Enter\n */\nexport function waitForEnter(prompt = 'Press Enter to continue...'): Promise<void> {\n return new Promise((resolve) => {\n process.stdout.write(chalk.dim(prompt));\n\n const handler = (): void => {\n process.stdin.removeListener('data', handler);\n process.stdin.setRawMode?.(false);\n process.stdin.pause();\n console.log();\n resolve();\n };\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode?.(true);\n }\n process.stdin.resume();\n process.stdin.once('data', handler);\n });\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Logout Command\n *\n * Removes stored credentials.\n */\n\nimport { deleteToken, getToken } from '../lib/keychain.js';\nimport { printSuccess, printWarning } from '../utils/ui.js';\n\n/**\n * Logout command handler\n */\nexport async function logout(): Promise<void> {\n const credentials = await getToken();\n\n if (!credentials) {\n printWarning('You are not logged in.');\n return;\n }\n\n await deleteToken();\n printSuccess('Logged out successfully.');\n}\n","/**\n * Whoami Command\n *\n * Displays the currently logged in user.\n */\n\nimport chalk from 'chalk';\nimport { getToken } from '../lib/keychain.js';\nimport { printError, keyValue, blank } from '../utils/ui.js';\n\n/**\n * Whoami command handler\n */\nexport async function whoami(): Promise<void> {\n const credentials = await getToken();\n\n if (!credentials) {\n printError('Not logged in. Run the `login` command to authenticate.');\n process.exit(1);\n }\n\n blank();\n console.log(keyValue('User', chalk.bold(credentials.user.email)));\n console.log(keyValue('User ID', credentials.user.id));\n\n if (credentials.expiresAt) {\n const expiresAt = new Date(credentials.expiresAt);\n const now = new Date();\n\n if (expiresAt < now) {\n console.log(keyValue('Status', chalk.red('Token expired')));\n } else {\n const daysRemaining = Math.ceil(\n (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),\n );\n console.log(keyValue('Expires', `${daysRemaining} days`));\n }\n }\n\n blank();\n}\n","/**\n * Run Command\n *\n * Unified command that connects to Evident and processes messages.\n *\n * Usage:\n * evident run --agent <id> # Interactive mode\n * evident run --agent <id> --conversation <id> # Process single conversation\n * evident run --agent <id> --idle-timeout 30 # Exit after 30s idle\n *\n * Features:\n * - Connects tunnel to Evident\n * - Processes pending messages from queue\n * - Forwards incoming requests to local OpenCode\n * - Rich interactive UI when running interactively\n * - JSON output mode for CI/CD\n */\n\nimport { ChildProcess } from 'child_process';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { select } from '@inquirer/prompts';\nimport { getApiUrlConfig, getCliName } from '../lib/config.js';\nimport { printError, blank } from '../utils/ui.js';\nimport {\n telemetry,\n EventTypes,\n shutdownTelemetry,\n emitAgentConnected,\n emitAgentDisconnected,\n emitAgentMessageProcessing,\n emitAgentMessageDone,\n emitAgentMessageFailed,\n} from '../lib/telemetry.js';\nimport {\n getAuthCredentials,\n getAuthHeader,\n isInteractive,\n getToken,\n type AuthCredentials,\n} from '../lib/auth.js';\nimport {\n checkOpenCodeHealth,\n waitForOpenCodeHealth,\n startOpenCode,\n stopOpenCode,\n findHealthyOpenCodeInstances,\n isPortInUse,\n findAvailablePort,\n isOpenCodeInstalled,\n promptOpenCodeInstall,\n createOpenCodeSession,\n sendMessageToOpenCode,\n type OpenCodeQuestion,\n type OpenCodePermission,\n} from '../lib/opencode/index.js';\nimport { connectTunnel, getReconnectDelay, type TunnelConnection } from '../lib/tunnel/index.js';\nimport { login } from './login.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RunOptions {\n agent?: string;\n port?: number;\n verbose?: boolean;\n conversation?: string;\n idleTimeout?: number;\n json?: boolean;\n}\n\ninterface ActivityLogEntry {\n timestamp: Date;\n type: 'request' | 'response' | 'error' | 'info';\n method?: string;\n path?: string;\n status?: number;\n durationMs?: number;\n error?: string;\n message?: string;\n requestId?: string;\n}\n\ninterface RunState {\n agentId: string;\n agentName: string | null;\n port: number;\n verbose: boolean;\n conversationFilter: string | null;\n idleTimeout: number | null;\n json: boolean;\n interactive: boolean;\n\n // Connection state\n connected: boolean;\n opencodeConnected: boolean;\n opencodeVersion: string | null;\n reconnectAttempt: number;\n\n // Process management\n opencodeProcess: ChildProcess | null;\n tunnelConnection: TunnelConnection | null;\n running: boolean;\n\n // Activity tracking\n activityLog: ActivityLogEntry[];\n displayInitialized: boolean;\n lastActivity: Date;\n pendingRequests: Map<string, { startTime: number; method: string; path: string }>;\n\n // Queue processing\n sessions: Map<string, string>; // conversationId -> sessionId\n messageCount: number;\n\n // Lock management\n lockCorrelationId: string;\n lockedConversations: Set<string>;\n lockHeartbeatTimer: ReturnType<typeof setInterval> | null;\n\n // Reconnection management\n consecutiveFetchFailures: number;\n reconnecting: boolean;\n reconnectPromise: Promise<void> | null;\n\n // Authentication (mutable — updated on re-auth)\n authHeader: string;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst MAX_ACTIVITY_LOG_ENTRIES = 10;\nconst MESSAGE_POLL_INTERVAL_MS = 2000;\nconst MAX_CONSECUTIVE_FETCH_FAILURES = 3;\nconst LOCK_HEARTBEAT_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n\n// ============================================================================\n// API Calls\n// ============================================================================\n\ninterface AgentInfo {\n id: string;\n name: string;\n sandbox_type: 'local' | 'github_actions';\n status: string;\n}\n\ninterface PendingConversation {\n id: string;\n agent_id: string;\n opencode_session_id: string | null;\n pending_message_count: number;\n oldest_pending_at: string;\n}\n\ninterface QueuedMessage {\n id: string;\n thread_id: string;\n content: string;\n status: string;\n created_at: string;\n opencode_agent: string | null;\n opencode_model: string | null;\n}\n\n/**\n * Resolve the agent ID from an agent key via the /v1/me endpoint.\n * Only works when authenticated with EVIDENT_AGENT_KEY (agent_key auth type).\n */\nasync function resolveAgentIdFromKey(\n authHeader: string,\n): Promise<{ agent_id?: string; error?: string }> {\n const apiUrl = getApiUrlConfig();\n try {\n const response = await fetch(`${apiUrl}/me`, {\n headers: { Authorization: authHeader },\n });\n\n if (!response.ok) {\n return { error: `Failed to resolve agent from key: HTTP ${response.status}` };\n }\n\n const data = (await response.json()) as { auth_type: string; agent_id?: string };\n if (data.auth_type === 'agent_key' && data.agent_id) {\n return { agent_id: data.agent_id };\n }\n\n return {\n error:\n 'Cannot resolve agent ID: auth type is not agent_key. Please provide --agent explicitly.',\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return { error: `Failed to resolve agent from key: ${message}` };\n }\n}\n\n/**\n * Validate agent exists and get its info\n */\nasync function getAgentInfo(\n agentId: string,\n authHeader: string,\n): Promise<{ valid: boolean; agent?: AgentInfo; error?: string; authFailed?: boolean }> {\n const apiUrl = getApiUrlConfig();\n\n try {\n const response = await fetch(`${apiUrl}/agents/${agentId}`, {\n headers: { Authorization: authHeader },\n });\n\n if (response.status === 404) {\n return { valid: false, error: 'Agent not found' };\n }\n\n if (response.status === 401) {\n return { valid: false, error: 'Authentication failed', authFailed: true };\n }\n\n if (!response.ok) {\n return { valid: false, error: `API error: ${response.status}` };\n }\n\n const agent = (await response.json()) as AgentInfo;\n\n if (agent.sandbox_type !== 'local' && agent.sandbox_type !== 'github_actions') {\n return {\n valid: false,\n error: `Agent is type '${agent.sandbox_type}', must be 'local' or 'github_actions' for CLI connection`,\n };\n }\n\n return { valid: true, agent };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return { valid: false, error: `Failed to validate agent: ${message}` };\n }\n}\n\n/**\n * Custom error class for authentication failures\n * This allows queue processing to detect auth issues and handle them appropriately\n */\nclass AuthenticationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Check if a response indicates an authentication failure\n */\nfunction checkAuthResponse(response: Response, context: string): void {\n if (response.status === 401 || response.status === 403) {\n throw new AuthenticationError(\n `Authentication failed during ${context}: HTTP ${response.status}. Your session may have expired.`,\n );\n }\n}\n\n/**\n * Get conversations with pending messages\n */\nasync function getPendingConversations(\n agentId: string,\n authHeader: string,\n conversationFilter?: string,\n): Promise<PendingConversation[]> {\n const apiUrl = getApiUrlConfig();\n const response = await fetch(`${apiUrl}/agents/${agentId}/conversations/pending`, {\n headers: { Authorization: authHeader },\n });\n\n checkAuthResponse(response, 'fetching pending conversations');\n\n if (!response.ok) {\n throw new Error(`Failed to get pending conversations: HTTP ${response.status}`);\n }\n\n const data = (await response.json()) as { conversations: PendingConversation[] };\n let conversations = data.conversations;\n\n // Filter by conversation ID if specified\n if (conversationFilter) {\n conversations = conversations.filter((c) => c.id === conversationFilter);\n }\n\n return conversations;\n}\n\n/**\n * Get pending messages for a conversation\n */\nasync function getPendingMessages(\n agentId: string,\n conversationId: string,\n authHeader: string,\n): Promise<QueuedMessage[]> {\n const apiUrl = getApiUrlConfig();\n const response = await fetch(\n `${apiUrl}/agents/${agentId}/threads/${conversationId}/messages?status=pending`,\n { headers: { Authorization: authHeader } },\n );\n\n checkAuthResponse(response, 'fetching pending messages');\n\n if (!response.ok) {\n throw new Error(`Failed to get messages: HTTP ${response.status}`);\n }\n\n return response.json() as Promise<QueuedMessage[]>;\n}\n\n/**\n * Mark a message as processing\n */\nasync function markMessageProcessing(\n agentId: string,\n conversationId: string,\n messageId: string,\n authHeader: string,\n): Promise<boolean> {\n const apiUrl = getApiUrlConfig();\n const response = await fetch(\n `${apiUrl}/agents/${agentId}/threads/${conversationId}/messages/${messageId}`,\n {\n method: 'PATCH',\n headers: { Authorization: authHeader, 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'processing' }),\n },\n );\n\n checkAuthResponse(response, 'marking message as processing');\n\n return response.ok;\n}\n\n/**\n * Report a pending question or permission to the API so it can be surfaced\n * to the user via their connected channel (e.g. Slack).\n */\nasync function reportInteractiveEvent(\n agentId: string,\n conversationId: string,\n type: 'question' | 'permission',\n data: OpenCodeQuestion | OpenCodePermission,\n authHeader: string,\n): Promise<void> {\n const apiUrl = getApiUrlConfig();\n const response = await fetch(\n `${apiUrl}/agents/${agentId}/threads/${conversationId}/interactive-event`,\n {\n method: 'POST',\n headers: { Authorization: authHeader, 'Content-Type': 'application/json' },\n body: JSON.stringify({ type, data }),\n },\n );\n\n checkAuthResponse(response, 'reporting interactive event');\n\n if (!response.ok) {\n throw new Error(`Failed to report interactive event: HTTP ${response.status}`);\n }\n}\n\n/**\n * Mark a message as done\n */\nasync function markMessageDone(\n agentId: string,\n conversationId: string,\n messageId: string,\n authHeader: string,\n sessionId?: string,\n): Promise<void> {\n const apiUrl = getApiUrlConfig();\n const body: Record<string, unknown> = { status: 'done' };\n if (sessionId) {\n body.opencode_session_id = sessionId;\n }\n const response = await fetch(\n `${apiUrl}/agents/${agentId}/threads/${conversationId}/messages/${messageId}`,\n {\n method: 'PATCH',\n headers: { Authorization: authHeader, 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n },\n );\n\n checkAuthResponse(response, 'marking message as done');\n}\n\n/**\n * Mark a message as failed\n */\nasync function markMessageFailed(\n agentId: string,\n conversationId: string,\n messageId: string,\n authHeader: string,\n): Promise<void> {\n const apiUrl = getApiUrlConfig();\n const response = await fetch(\n `${apiUrl}/agents/${agentId}/threads/${conversationId}/messages/${messageId}`,\n {\n method: 'PATCH',\n headers: { Authorization: authHeader, 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'failed' }),\n },\n );\n\n checkAuthResponse(response, 'marking message as failed');\n}\n\n/**\n * Acquire a lock on a conversation\n */\nasync function acquireConversationLock(\n agentId: string,\n conversationId: string,\n correlationId: string,\n authHeader: string,\n): Promise<{ acquired: boolean; error?: string }> {\n const apiUrl = getApiUrlConfig();\n try {\n const response = await fetch(`${apiUrl}/agents/${agentId}/threads/${conversationId}/lock`, {\n method: 'POST',\n headers: { Authorization: authHeader, 'Content-Type': 'application/json' },\n body: JSON.stringify({ correlation_id: correlationId }),\n });\n\n checkAuthResponse(response, 'acquiring conversation lock');\n\n if (response.status === 409) {\n return { acquired: false, error: 'Conversation already locked by another runner' };\n }\n\n if (!response.ok) {\n return { acquired: false, error: `Failed to acquire lock: HTTP ${response.status}` };\n }\n\n return { acquired: true };\n } catch (error) {\n if (error instanceof AuthenticationError) throw error;\n return { acquired: false, error: String(error) };\n }\n}\n\n/**\n * Extend a lock on a conversation (heartbeat)\n */\nasync function extendConversationLock(\n agentId: string,\n conversationId: string,\n correlationId: string,\n authHeader: string,\n): Promise<boolean> {\n const apiUrl = getApiUrlConfig();\n try {\n const response = await fetch(\n `${apiUrl}/agents/${agentId}/threads/${conversationId}/lock/extend`,\n {\n method: 'POST',\n headers: { Authorization: authHeader, 'Content-Type': 'application/json' },\n body: JSON.stringify({ correlation_id: correlationId }),\n },\n );\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Release a lock on a conversation\n */\nasync function releaseConversationLock(\n agentId: string,\n conversationId: string,\n correlationId: string,\n authHeader: string,\n): Promise<void> {\n const apiUrl = getApiUrlConfig();\n try {\n await fetch(\n `${apiUrl}/agents/${agentId}/threads/${conversationId}/lock?correlation_id=${encodeURIComponent(correlationId)}`,\n {\n method: 'DELETE',\n headers: { Authorization: authHeader },\n },\n );\n } catch {\n // Best effort — don't fail on lock release errors\n }\n}\n\n/**\n * Update conversation with OpenCode session ID\n */\nasync function updateConversationSession(\n agentId: string,\n conversationId: string,\n sessionId: string,\n authHeader: string,\n): Promise<void> {\n const apiUrl = getApiUrlConfig();\n const response = await fetch(`${apiUrl}/agents/${agentId}/threads/${conversationId}`, {\n method: 'PATCH',\n headers: { Authorization: authHeader, 'Content-Type': 'application/json' },\n body: JSON.stringify({ opencode_session_id: sessionId }),\n });\n\n checkAuthResponse(response, 'updating conversation session');\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(\n `Failed to update conversation session: HTTP ${response.status}${text ? `: ${text}` : ''}`,\n );\n }\n}\n\n/**\n * Update conversation title from OpenCode session\n */\nasync function updateConversationTitle(\n agentId: string,\n conversationId: string,\n title: string,\n authHeader: string,\n): Promise<void> {\n const apiUrl = getApiUrlConfig();\n const response = await fetch(`${apiUrl}/agents/${agentId}/threads/${conversationId}`, {\n method: 'PATCH',\n headers: { Authorization: authHeader, 'Content-Type': 'application/json' },\n body: JSON.stringify({ title }),\n });\n\n checkAuthResponse(response, 'updating conversation title');\n}\n\n// ============================================================================\n// Logging\n// ============================================================================\n\nfunction log(state: RunState, message: string, isError = false): void {\n if (state.json) {\n console.log(\n JSON.stringify({\n timestamp: new Date().toISOString(),\n level: isError ? 'error' : 'info',\n message,\n }),\n );\n } else if (!state.interactive) {\n // Non-interactive, non-JSON: simple output\n const prefix = isError ? chalk.red('✗') : chalk.green('•');\n console.log(`${prefix} ${message}`);\n }\n // In interactive mode, we use the activity log instead\n}\n\nfunction logActivity(state: RunState, entry: Omit<ActivityLogEntry, 'timestamp'>): void {\n const fullEntry: ActivityLogEntry = {\n ...entry,\n timestamp: new Date(),\n };\n\n state.activityLog.push(fullEntry);\n\n if (state.activityLog.length > MAX_ACTIVITY_LOG_ENTRIES) {\n state.activityLog.shift();\n }\n\n state.lastActivity = fullEntry.timestamp;\n\n // In non-interactive mode, also log to console immediately\n if (!state.interactive) {\n if (entry.type === 'error') {\n log(state, entry.error ?? 'Unknown error', true);\n } else if (entry.type === 'info' && entry.message) {\n log(state, entry.message);\n }\n }\n}\n\n// ============================================================================\n// Display (Interactive Mode)\n// ============================================================================\n\nconst ANSI = {\n moveUp: (n: number) => `\\x1b[${n}A`,\n};\n\nconst STATUS_DISPLAY_HEIGHT = 22;\n\nfunction colorizeStatus(status: number): string {\n if (status >= 200 && status < 300) {\n return chalk.green(status.toString());\n } else if (status >= 300 && status < 400) {\n return chalk.yellow(status.toString());\n } else if (status >= 400 && status < 500) {\n return chalk.red(status.toString());\n } else if (status >= 500) {\n return chalk.bgRed.white(` ${status} `);\n }\n return status.toString();\n}\n\nfunction formatActivityEntry(entry: ActivityLogEntry): string {\n const time = entry.timestamp.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n });\n\n switch (entry.type) {\n case 'request': {\n const duration = entry.durationMs ? ` (${entry.durationMs}ms)` : '';\n const status = entry.status ? ` -> ${colorizeStatus(entry.status)}` : ' ...';\n return ` ${chalk.dim(`[${time}]`)} ${chalk.cyan('<-')} ${entry.method} ${entry.path}${status}${duration}`;\n }\n\n case 'response': {\n const duration = entry.durationMs ? ` (${entry.durationMs}ms)` : '';\n return ` ${chalk.dim(`[${time}]`)} ${chalk.green('->')} ${entry.method} ${entry.path} ${colorizeStatus(entry.status!)}${duration}`;\n }\n\n case 'error': {\n const errorMsg = entry.error || 'Unknown error';\n const path = entry.path ? ` ${entry.method} ${entry.path}` : '';\n return ` ${chalk.dim(`[${time}]`)} ${chalk.red('x')}${path} - ${chalk.red(errorMsg)}`;\n }\n\n case 'info': {\n return ` ${chalk.dim(`[${time}]`)} ${chalk.blue('*')} ${entry.message}`;\n }\n\n default:\n return ` ${chalk.dim(`[${time}]`)} ${entry.message || 'Unknown'}`;\n }\n}\n\nfunction displayStatus(state: RunState): void {\n if (!state.interactive) return;\n\n const lines: string[] = [];\n\n // Header\n lines.push(chalk.bold('Evident'));\n lines.push(chalk.dim('-'.repeat(60)));\n lines.push('');\n\n // Agent info\n if (state.agentName) {\n lines.push(` Agent: ${state.agentName}`);\n }\n lines.push(` ID: ${state.agentId}`);\n if (state.conversationFilter) {\n lines.push(` Filter: conversation ${state.conversationFilter.slice(0, 8)}...`);\n }\n lines.push('');\n\n // Status indicators\n if (state.connected) {\n lines.push(` ${chalk.green('*')} Tunnel: ${chalk.green('Connected to Evident')}`);\n } else {\n if (state.reconnectAttempt > 0) {\n lines.push(\n ` ${chalk.yellow('o')} Tunnel: ${chalk.yellow(`Reconnecting... (attempt ${state.reconnectAttempt})`)}`,\n );\n } else {\n lines.push(` ${chalk.yellow('o')} Tunnel: ${chalk.yellow('Connecting...')}`);\n }\n }\n\n if (state.opencodeConnected) {\n const version = state.opencodeVersion ? `, v${state.opencodeVersion}` : '';\n lines.push(\n ` ${chalk.green('*')} OpenCode: ${chalk.green(`Running on port ${state.port}${version}`)}`,\n );\n } else {\n lines.push(` ${chalk.red('o')} OpenCode: ${chalk.red(`Not connected (port ${state.port})`)}`);\n }\n\n lines.push('');\n\n // Messages processed\n if (state.messageCount > 0) {\n lines.push(` Messages: ${state.messageCount} processed`);\n lines.push('');\n }\n\n // Activity log\n if (state.activityLog.length > 0) {\n lines.push(chalk.bold(' Activity:'));\n for (const entry of state.activityLog) {\n lines.push(formatActivityEntry(entry));\n }\n } else {\n lines.push(chalk.dim(' No activity yet. Waiting for requests...'));\n }\n\n lines.push('');\n lines.push(chalk.dim('-'.repeat(60)));\n\n if (state.verbose) {\n lines.push(chalk.dim(' Verbose mode: ON'));\n }\n\n lines.push('');\n lines.push(\n chalk.dim(` Tip: Run \\`opencode attach http://localhost:${state.port}\\` to see live activity`),\n );\n lines.push(chalk.dim(' Press Ctrl+C to disconnect'));\n\n while (lines.length < STATUS_DISPLAY_HEIGHT) {\n lines.push('');\n }\n\n if (!state.displayInitialized) {\n console.log('');\n console.log(chalk.dim('='.repeat(60)));\n console.log('');\n for (const line of lines) {\n console.log(line);\n }\n state.displayInitialized = true;\n } else {\n process.stdout.write(ANSI.moveUp(STATUS_DISPLAY_HEIGHT + 3));\n console.log(chalk.dim('='.repeat(60)));\n console.log('');\n for (const line of lines) {\n process.stdout.write('\\x1b[2K');\n console.log(line);\n }\n }\n}\n\n// ============================================================================\n// Authentication Helpers\n// ============================================================================\n\nasync function promptForLogin(\n promptMessage: string,\n successMessage: string,\n): Promise<AuthCredentials> {\n const action = await select({\n message: promptMessage,\n choices: [\n {\n name: 'Yes, log me in',\n value: 'login',\n description: 'Opens a browser to authenticate with Evident',\n },\n {\n name: 'No, exit',\n value: 'exit',\n description: 'Exit without logging in',\n },\n ],\n });\n\n if (action === 'exit') {\n console.log(chalk.dim(`\\nYou can log in later by running: ${getCliName()} login`));\n process.exit(0);\n }\n\n await login({ noBrowser: false });\n\n const credentials = await getToken();\n if (!credentials) {\n printError('Login failed. Please try again.');\n process.exit(1);\n }\n\n blank();\n console.log(chalk.green(successMessage));\n blank();\n\n return { token: credentials.token, authType: 'bearer', user: credentials.user };\n}\n\n// ============================================================================\n// OpenCode Setup\n// ============================================================================\n\nasync function ensureOpenCodeRunning(state: RunState): Promise<void> {\n const healthCheck = await checkOpenCodeHealth(state.port);\n\n if (healthCheck.healthy) {\n state.opencodeConnected = true;\n state.opencodeVersion = healthCheck.version ?? null;\n return;\n }\n\n // Check if running on different port\n const runningInstances = await findHealthyOpenCodeInstances();\n\n if (runningInstances.length > 0) {\n if (!state.interactive) {\n throw new Error(\n `OpenCode not found on port ${state.port}, but running on port ${runningInstances[0].port}. ` +\n `Use --port ${runningInstances[0].port}`,\n );\n }\n\n blank();\n console.log(chalk.yellow('Found OpenCode running on different port(s):'));\n for (const instance of runningInstances) {\n const ver = instance.version ? ` (v${instance.version})` : '';\n const cwd = instance.cwd ? ` in ${instance.cwd}` : '';\n console.log(chalk.dim(` * Port ${instance.port}${ver}${cwd}`));\n }\n blank();\n\n if (runningInstances.length === 1) {\n console.log(chalk.yellow('Tip: Run with the correct port:'));\n console.log(\n chalk.dim(\n ` ${getCliName()} run --agent ${state.agentId} --port ${runningInstances[0].port}`,\n ),\n );\n }\n blank();\n throw new Error(`OpenCode not running on port ${state.port}`);\n }\n\n // OpenCode not running anywhere\n if (!isOpenCodeInstalled()) {\n if (!state.interactive) {\n throw new Error('OpenCode is not installed. Install it with: npm install -g opencode-ai');\n }\n\n const result = await promptOpenCodeInstall(true);\n if (result === 'exit') {\n process.exit(0);\n }\n\n if (result === 'installed' || isOpenCodeInstalled()) {\n // Try to start it\n } else {\n throw new Error('OpenCode is not installed');\n }\n }\n\n // Offer to start OpenCode\n if (state.interactive) {\n // Check if port is in use\n let actualPort = state.port;\n if (isPortInUse(state.port)) {\n console.log(chalk.yellow(`\\nPort ${state.port} is already in use.`));\n const alternativePort = findAvailablePort(state.port + 1);\n if (alternativePort) {\n const useAlternative = await select({\n message: `Use port ${alternativePort} instead?`,\n choices: [\n { name: `Yes, use port ${alternativePort}`, value: 'yes' },\n { name: 'No, I will free the port manually', value: 'no' },\n ],\n });\n if (useAlternative === 'yes') {\n actualPort = alternativePort;\n state.port = actualPort;\n } else {\n throw new Error(`Port ${state.port} is in use`);\n }\n }\n }\n\n const action = await select({\n message: 'OpenCode is not running. What would you like to do?',\n choices: [\n {\n name: 'Start OpenCode for me',\n value: 'start',\n description: `Run 'opencode serve --port ${actualPort}'`,\n },\n {\n name: 'Show me the command',\n value: 'manual',\n description: 'Display the command to run manually',\n },\n {\n name: 'Continue without OpenCode',\n value: 'continue',\n description: 'Requests will fail until OpenCode starts',\n },\n ],\n });\n\n if (action === 'manual') {\n blank();\n console.log(chalk.bold('Run this command in another terminal:'));\n blank();\n console.log(` ${chalk.cyan(`opencode serve --port ${actualPort}`)}`);\n blank();\n throw new Error('Please start OpenCode manually');\n }\n\n if (action === 'start') {\n const spinner = ora('Starting OpenCode...').start();\n state.opencodeProcess = await startOpenCode(actualPort);\n const health = await waitForOpenCodeHealth(actualPort, 30000);\n\n if (!health.healthy) {\n spinner.fail('Failed to start OpenCode');\n throw new Error('OpenCode failed to start');\n }\n\n spinner.succeed(\n `OpenCode running on port ${actualPort}${health.version ? ` (v${health.version})` : ''}`,\n );\n state.opencodeConnected = true;\n state.opencodeVersion = health.version ?? null;\n }\n } else {\n // Non-interactive (CI): auto-start OpenCode rather than failing.\n // GitHub Actions runners have opencode-ai installed but don't pre-start it,\n // so the CLI must start it on their behalf.\n log(state, `OpenCode is not running on port ${state.port}. Starting it automatically...`);\n state.opencodeProcess = await startOpenCode(state.port);\n const health = await waitForOpenCodeHealth(state.port, 30000);\n\n if (!health.healthy) {\n throw new Error(\n `OpenCode failed to start on port ${state.port}. Install with: npm install -g opencode-ai`,\n );\n }\n\n log(\n state,\n `OpenCode started on port ${state.port}${health.version ? ` (v${health.version})` : ''}`,\n );\n state.opencodeConnected = true;\n state.opencodeVersion = health.version ?? null;\n }\n}\n\n// ============================================================================\n// Queue Processing\n// ============================================================================\n\n/** Exit code for authentication expiration in non-interactive mode */\nconst AUTH_EXPIRED_EXIT_CODE = 77;\n\n/**\n * Result of handling an authentication error\n */\ninterface AuthErrorResult {\n /** Whether authentication was successfully refreshed */\n success: boolean;\n /** New auth header if re-authenticated */\n newAuthHeader?: string;\n}\n\n/**\n * Handle authentication errors during queue processing.\n * In interactive mode, prompts for re-authentication.\n * In non-interactive mode, exits with a specific exit code.\n */\nasync function handleAuthError(\n state: RunState,\n error: AuthenticationError,\n): Promise<AuthErrorResult> {\n logActivity(state, {\n type: 'error',\n error: error.message,\n });\n if (state.interactive) displayStatus(state);\n\n if (!state.interactive) {\n // Non-interactive mode: log clear message and exit\n blank();\n console.log(chalk.red('Authentication expired'));\n console.log(chalk.dim('Your authentication token is no longer valid.'));\n blank();\n console.log(chalk.dim('To fix this:'));\n console.log(chalk.dim(` 1. Run '${getCliName()} login' to re-authenticate`));\n console.log(chalk.dim(' 2. Restart this command'));\n blank();\n await cleanup(state);\n await shutdownTelemetry();\n process.exit(AUTH_EXPIRED_EXIT_CODE);\n // Return to prevent fallthrough when process.exit is mocked in tests\n return { success: false };\n }\n\n // Interactive mode: prompt for re-authentication\n blank();\n console.log(chalk.yellow('Your authentication has expired.'));\n blank();\n\n try {\n const credentials = await promptForLogin(\n 'Would you like to log in again?',\n 'Re-authenticated successfully! Resuming...',\n );\n\n const newAuthHeader = getAuthHeader(credentials);\n return { success: true, newAuthHeader };\n } catch {\n // User declined to re-authenticate or login failed\n return { success: false };\n }\n}\n\n/**\n * Check if an error is a network/fetch failure that might be recoverable\n * through reconnection (as opposed to a logic error)\n */\nfunction isNetworkError(error: unknown): boolean {\n if (error instanceof Error) {\n const message = error.message.toLowerCase();\n return (\n message.includes('fetch failed') ||\n message.includes('network') ||\n message.includes('econnrefused') ||\n message.includes('econnreset') ||\n message.includes('etimedout') ||\n message.includes('socket hang up')\n );\n }\n return false;\n}\n\nasync function processQueue(\n state: RunState,\n authHeader: string,\n triggerReconnect: () => Promise<void>,\n): Promise<void> {\n // Number of consecutive poll cycles where the queue was empty.\n // Used to implement idle timeout without relying on wall-clock timing jitter.\n let idlePolls = 0;\n let currentAuthHeader = authHeader;\n\n while (state.running) {\n // Wait for any ongoing reconnection to complete\n if (state.reconnecting && state.reconnectPromise) {\n logActivity(state, {\n type: 'info',\n message: 'Waiting for tunnel reconnection...',\n });\n if (state.interactive) displayStatus(state);\n await state.reconnectPromise;\n }\n\n try {\n const conversations = await getPendingConversations(\n state.agentId,\n currentAuthHeader,\n state.conversationFilter ?? undefined,\n );\n\n // Reset consecutive failures on success\n state.consecutiveFetchFailures = 0;\n\n if (conversations.length > 0) {\n idlePolls = 0;\n\n for (const conv of conversations) {\n if (!state.running) break;\n\n // Acquire lock on the conversation before processing\n if (!state.lockedConversations.has(conv.id)) {\n const lockResult = await acquireConversationLock(\n state.agentId,\n conv.id,\n state.lockCorrelationId,\n currentAuthHeader,\n );\n if (!lockResult.acquired) {\n logActivity(state, {\n type: 'info',\n message: `Conversation ${conv.id.slice(0, 8)} locked by another runner — skipping`,\n });\n if (state.interactive) displayStatus(state);\n continue;\n }\n state.lockedConversations.add(conv.id);\n logActivity(state, {\n type: 'info',\n message: `Lock acquired on conversation ${conv.id.slice(0, 8)}`,\n });\n }\n\n logActivity(state, {\n type: 'info',\n message: `Processing conversation ${conv.id.slice(0, 8)}... (${conv.pending_message_count} pending)`,\n });\n if (state.interactive) displayStatus(state);\n\n // Get or create session\n let sessionId = state.sessions.get(conv.id);\n if (!sessionId) {\n if (conv.opencode_session_id) {\n sessionId = conv.opencode_session_id;\n } else {\n sessionId = await createOpenCodeSession(state.port);\n await updateConversationSession(state.agentId, conv.id, sessionId, currentAuthHeader);\n logActivity(state, {\n type: 'info',\n message: `Created session ${sessionId.slice(0, 8)}`,\n });\n }\n state.sessions.set(conv.id, sessionId);\n }\n\n // Process messages\n const messages = await getPendingMessages(state.agentId, conv.id, currentAuthHeader);\n\n for (const message of messages) {\n if (!state.running) break;\n\n logActivity(state, {\n type: 'info',\n message: `Processing message ${message.id.slice(0, 8)}...`,\n });\n if (state.interactive) displayStatus(state);\n\n const claimed = await markMessageProcessing(\n state.agentId,\n conv.id,\n message.id,\n currentAuthHeader,\n );\n if (!claimed) {\n logActivity(state, {\n type: 'info',\n message: `Message ${message.id.slice(0, 8)} already claimed`,\n });\n continue;\n }\n\n // Report to API for activity log (only after claim succeeds)\n emitAgentMessageProcessing(state.agentId, {\n message_id: message.id,\n conversation_id: conv.id,\n });\n\n try {\n const result = await sendMessageToOpenCode(\n state.port,\n sessionId,\n message.content,\n {\n agent: message.opencode_agent ?? undefined,\n model: message.opencode_model ?? undefined,\n },\n {\n onQuestion: async (question) => {\n try {\n await reportInteractiveEvent(\n state.agentId,\n conv.id,\n 'question',\n question,\n currentAuthHeader,\n );\n logActivity(state, {\n type: 'info',\n message: `Question surfaced to user (id: ${question.id.slice(0, 8)})`,\n });\n } catch (err) {\n logActivity(state, {\n type: 'error',\n error: `Failed to surface question: ${err}`,\n });\n }\n },\n onPermission: async (permission) => {\n try {\n await reportInteractiveEvent(\n state.agentId,\n conv.id,\n 'permission',\n permission,\n currentAuthHeader,\n );\n logActivity(state, {\n type: 'info',\n message: `Permission request surfaced to user (id: ${permission.id.slice(0, 8)})`,\n });\n } catch (err) {\n logActivity(state, {\n type: 'error',\n error: `Failed to surface permission: ${err}`,\n });\n }\n },\n },\n );\n\n // Sync OpenCode's session title to the conversation (best-effort)\n if (result.title) {\n try {\n await updateConversationTitle(\n state.agentId,\n conv.id,\n result.title,\n currentAuthHeader,\n );\n } catch {\n // Non-fatal: title sync failure should not fail the message\n }\n }\n await markMessageDone(\n state.agentId,\n conv.id,\n message.id,\n currentAuthHeader,\n sessionId,\n );\n state.messageCount++;\n logActivity(state, {\n type: 'info',\n message: `Message ${message.id.slice(0, 8)} processed`,\n });\n // Report to API for activity log\n emitAgentMessageDone(state.agentId, {\n message_id: message.id,\n conversation_id: conv.id,\n });\n } catch (error) {\n // Re-throw auth errors to be handled at the outer level\n if (error instanceof AuthenticationError) {\n throw error;\n }\n await markMessageFailed(state.agentId, conv.id, message.id, currentAuthHeader);\n logActivity(state, {\n type: 'error',\n error: `Message ${message.id.slice(0, 8)} failed: ${error}`,\n });\n // Report to API for activity log\n emitAgentMessageFailed(state.agentId, {\n message_id: message.id,\n conversation_id: conv.id,\n error: String(error),\n });\n }\n\n if (state.interactive) displayStatus(state);\n }\n }\n } else {\n // No pending messages — count idle poll cycles\n if (state.idleTimeout !== null) {\n idlePolls++;\n if (idlePolls === 1) {\n logActivity(state, {\n type: 'info',\n message: `Queue empty, waiting (timeout: ${state.idleTimeout}s)...`,\n });\n if (state.interactive) displayStatus(state);\n }\n }\n }\n\n // Sleep between polls. The idle check runs after the sleep so that a message\n // arriving just before the timeout gets at least one more poll cycle.\n await new Promise((resolve) => setTimeout(resolve, MESSAGE_POLL_INTERVAL_MS));\n\n // Check idle timeout after sleeping. Each idle poll represents MESSAGE_POLL_INTERVAL_MS\n // of idle time. We require at least 2 idle polls before firing — this guarantees\n // at least one additional poll runs after the queue first goes empty, giving messages\n // arriving just before the deadline a chance to be picked up.\n if (state.idleTimeout !== null && idlePolls >= 2) {\n const idleMs = idlePolls * MESSAGE_POLL_INTERVAL_MS;\n if (idleMs > state.idleTimeout * 1000) {\n logActivity(state, {\n type: 'info',\n message: 'Idle timeout reached',\n });\n if (state.interactive) displayStatus(state);\n break;\n }\n }\n } catch (error) {\n // Handle authentication errors specially\n if (error instanceof AuthenticationError) {\n const result = await handleAuthError(state, error);\n if (result.success && result.newAuthHeader) {\n currentAuthHeader = result.newAuthHeader;\n state.authHeader = result.newAuthHeader;\n logActivity(state, {\n type: 'info',\n message: 'Continuing with new credentials...',\n });\n if (state.interactive) displayStatus(state);\n continue; // Retry immediately with new credentials\n } else {\n // User declined re-auth, stop processing\n state.running = false;\n break;\n }\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error);\n logActivity(state, {\n type: 'error',\n error: `Queue processing error: ${errorMessage}`,\n });\n if (state.interactive) displayStatus(state);\n\n // Track consecutive fetch failures to detect connection issues\n if (isNetworkError(error)) {\n state.consecutiveFetchFailures++;\n\n if (state.consecutiveFetchFailures >= MAX_CONSECUTIVE_FETCH_FAILURES) {\n logActivity(state, {\n type: 'info',\n message: `Detected ${state.consecutiveFetchFailures} consecutive fetch failures, triggering reconnection...`,\n });\n if (state.interactive) displayStatus(state);\n\n // Trigger reconnection\n await triggerReconnect();\n state.consecutiveFetchFailures = 0;\n }\n }\n\n await new Promise((resolve) => setTimeout(resolve, MESSAGE_POLL_INTERVAL_MS));\n }\n }\n}\n\n// ============================================================================\n// Cleanup\n// ============================================================================\n\nasync function cleanup(state: RunState, authHeader?: string): Promise<void> {\n state.running = false;\n\n // Stop lock heartbeat\n if (state.lockHeartbeatTimer) {\n clearInterval(state.lockHeartbeatTimer);\n state.lockHeartbeatTimer = null;\n }\n\n // Release all locks\n if (authHeader && state.lockedConversations.size > 0) {\n for (const convId of state.lockedConversations) {\n await releaseConversationLock(state.agentId, convId, state.lockCorrelationId, authHeader);\n }\n if (state.interactive) {\n logActivity(state, {\n type: 'info',\n message: `Released ${state.lockedConversations.size} lock(s)`,\n });\n displayStatus(state);\n } else {\n log(state, `Released ${state.lockedConversations.size} lock(s)`);\n }\n state.lockedConversations.clear();\n }\n\n if (state.tunnelConnection) {\n state.tunnelConnection.close();\n state.tunnelConnection = null;\n }\n\n if (state.opencodeProcess) {\n stopOpenCode(state.opencodeProcess);\n if (state.interactive) {\n logActivity(state, { type: 'info', message: 'Stopped OpenCode process' });\n displayStatus(state);\n } else {\n log(state, 'Stopped OpenCode process');\n }\n state.opencodeProcess = null;\n }\n}\n\n// ============================================================================\n// Main Command Handler\n// ============================================================================\n\nexport async function run(options: RunOptions): Promise<void> {\n const interactive = isInteractive(options.json);\n\n // Initialize state\n const state: RunState = {\n agentId: options.agent || '',\n agentName: null,\n port: options.port ?? 4096,\n verbose: options.verbose ?? false,\n conversationFilter: options.conversation ?? null,\n idleTimeout: options.idleTimeout ?? null,\n json: options.json ?? false,\n interactive,\n\n connected: false,\n opencodeConnected: false,\n opencodeVersion: null,\n reconnectAttempt: 0,\n\n opencodeProcess: null,\n tunnelConnection: null,\n running: true,\n\n activityLog: [],\n displayInitialized: false,\n lastActivity: new Date(),\n pendingRequests: new Map(),\n\n sessions: new Map(),\n messageCount: 0,\n\n lockCorrelationId: `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n lockedConversations: new Set(),\n lockHeartbeatTimer: null,\n\n consecutiveFetchFailures: 0,\n reconnecting: false,\n reconnectPromise: null,\n\n authHeader: '',\n };\n\n if (state.idleTimeout === null && (process.env.GITHUB_ACTIONS || process.env.CI)) {\n log(\n state,\n 'Warning: No --idle-timeout set in CI environment. The runner will poll indefinitely until the job times out. Consider adding --idle-timeout 30 to avoid wasting runner minutes.',\n false,\n );\n }\n\n // Set up cleanup handlers\n const handleSignal = async () => {\n if (state.interactive) {\n logActivity(state, { type: 'info', message: 'Shutting down...' });\n displayStatus(state);\n } else {\n log(state, 'Shutting down...');\n }\n await cleanup(state, state.authHeader);\n await shutdownTelemetry();\n process.exit(0);\n };\n\n process.on('SIGINT', handleSignal);\n process.on('SIGTERM', handleSignal);\n\n try {\n // Step 1: Authenticate\n let credentials = await getAuthCredentials();\n\n if (!credentials) {\n if (!interactive) {\n printError('Authentication required');\n blank();\n console.log(chalk.dim('Set EVIDENT_AGENT_KEY environment variable for CI'));\n console.log(chalk.dim('Or run `evident login` for interactive authentication'));\n blank();\n process.exit(1);\n }\n\n blank();\n console.log(chalk.yellow('You are not logged in to Evident.'));\n blank();\n\n credentials = await promptForLogin(\n 'Would you like to log in now?',\n 'Login successful! Continuing...',\n );\n }\n\n state.authHeader = getAuthHeader(credentials);\n\n // Resolve agent ID from key if not provided explicitly\n if (!state.agentId) {\n if (credentials.authType === 'agent_key') {\n const resolved = await resolveAgentIdFromKey(state.authHeader);\n if (resolved.agent_id) {\n state.agentId = resolved.agent_id;\n log(state, `Resolved agent ID from key: ${state.agentId}`);\n } else {\n printError(resolved.error || 'Failed to resolve agent ID from key');\n process.exit(1);\n }\n } else {\n printError('--agent is required when not using EVIDENT_AGENT_KEY');\n blank();\n console.log(chalk.dim('Either provide --agent <id> or set EVIDENT_AGENT_KEY'));\n blank();\n process.exit(1);\n }\n }\n\n telemetry.info(\n EventTypes.CLI_COMMAND,\n 'Starting run command',\n {\n command: 'run',\n agentId: state.agentId,\n port: state.port,\n conversationFilter: state.conversationFilter,\n interactive,\n },\n state.agentId,\n );\n\n // Step 2: Validate agent\n if (interactive && !state.json) {\n blank();\n console.log(chalk.bold('Evident Run'));\n console.log(chalk.dim('-'.repeat(40)));\n }\n\n const spinner = interactive && !state.json ? ora('Validating agent...').start() : null;\n let validation = await getAgentInfo(state.agentId, state.authHeader);\n\n if (!validation.valid && validation.authFailed && interactive) {\n spinner?.fail('Authentication failed');\n blank();\n console.log(chalk.yellow('Your authentication token is invalid or expired.'));\n blank();\n\n credentials = await promptForLogin(\n 'Would you like to log in again?',\n 'Login successful! Retrying...',\n );\n\n state.authHeader = getAuthHeader(credentials);\n spinner?.start('Validating agent...');\n validation = await getAgentInfo(state.agentId, state.authHeader);\n }\n\n if (!validation.valid) {\n spinner?.fail(`Agent validation failed: ${validation.error}`);\n throw new Error(validation.error);\n }\n\n spinner?.succeed(`Agent: ${validation.agent!.name || state.agentId}`);\n state.agentName = validation.agent!.name;\n\n // Step 3: Ensure OpenCode is running\n const ocSpinner = interactive && !state.json ? ora('Checking OpenCode...').start() : null;\n\n try {\n await ensureOpenCodeRunning(state);\n const version = state.opencodeVersion ? ` (v${state.opencodeVersion})` : '';\n ocSpinner?.succeed(`OpenCode running on port ${state.port}${version}`);\n } catch (error) {\n ocSpinner?.fail((error as Error).message);\n throw error;\n }\n\n // Step 4: Connect tunnel\n const tunnelSpinner = interactive && !state.json ? ora('Connecting tunnel...').start() : null;\n\n /**\n * Connect to the tunnel with retry logic.\n * Can be called for initial connection or for reconnection after disconnect.\n * @param isReconnect - Whether this is a reconnection attempt (vs initial connection)\n */\n const connectWithRetry = async (isReconnect = false): Promise<void> => {\n // Prevent multiple simultaneous reconnection attempts\n if (isReconnect && state.reconnecting) {\n return;\n }\n\n state.reconnecting = true;\n\n // Close existing connection if any\n if (state.tunnelConnection) {\n try {\n state.tunnelConnection.close();\n } catch {\n // Ignore close errors\n }\n state.tunnelConnection = null;\n }\n\n while (state.running) {\n try {\n state.tunnelConnection = await connectTunnel({\n agentId: state.agentId,\n authHeader: state.authHeader,\n port: state.port,\n onConnected: (agentId) => {\n state.connected = true;\n state.reconnectAttempt = 0;\n state.reconnecting = false;\n state.consecutiveFetchFailures = 0;\n // Update state with server-resolved agent ID for consistency\n state.agentId = agentId;\n logActivity(state, {\n type: 'info',\n message: isReconnect\n ? `Tunnel reconnected (agent: ${agentId})`\n : `Tunnel connected (agent: ${agentId})`,\n });\n // Report to API for activity log\n emitAgentConnected(state.agentId, { port: state.port });\n if (state.interactive) displayStatus(state);\n },\n onDisconnected: (code, reason) => {\n state.connected = false;\n logActivity(state, {\n type: 'info',\n message: `Tunnel disconnected (code: ${code}, reason: ${reason})`,\n });\n // Report to API for activity log\n emitAgentDisconnected(state.agentId, { code, reason });\n if (state.interactive) displayStatus(state);\n\n // Auto-reconnect on unexpected disconnection (not manual shutdown)\n // Code 1000 = normal closure, 1001 = going away (expected)\n // Other codes indicate unexpected disconnection\n // Also skip if reconnection is already in progress (e.g., from fetch failures)\n if (state.running && code !== 1000 && !state.reconnecting) {\n logActivity(state, {\n type: 'info',\n message: 'Attempting automatic reconnection...',\n });\n if (state.interactive) displayStatus(state);\n\n // Trigger reconnection asynchronously\n state.reconnectPromise = connectWithRetry(true).catch((err) => {\n logActivity(state, {\n type: 'error',\n error: `Reconnection failed: ${err.message}`,\n });\n if (state.interactive) displayStatus(state);\n });\n }\n },\n onError: (error) => {\n logActivity(state, { type: 'error', error });\n if (state.interactive) displayStatus(state);\n },\n onRequest: (method, path, requestId) => {\n state.pendingRequests.set(requestId, {\n startTime: Date.now(),\n method,\n path,\n });\n logActivity(state, { type: 'request', method, path, requestId });\n if (state.interactive) displayStatus(state);\n },\n onResponse: (status, durationMs, requestId) => {\n const pending = state.pendingRequests.get(requestId);\n state.pendingRequests.delete(requestId);\n state.opencodeConnected = true;\n\n const lastEntry = state.activityLog[state.activityLog.length - 1];\n if (lastEntry && lastEntry.requestId === requestId) {\n lastEntry.type = 'response';\n lastEntry.status = status;\n lastEntry.durationMs = durationMs;\n } else if (pending) {\n logActivity(state, {\n type: 'response',\n method: pending.method,\n path: pending.path,\n status,\n durationMs,\n requestId,\n });\n }\n if (state.interactive) displayStatus(state);\n },\n onInfo: (message) => {\n logActivity(state, { type: 'info', message });\n if (state.interactive) displayStatus(state);\n },\n });\n\n if (!isReconnect) {\n tunnelSpinner?.succeed('Tunnel connected');\n }\n return;\n } catch (error) {\n state.reconnectAttempt++;\n const delay = getReconnectDelay(state.reconnectAttempt);\n\n if ((error as Error).message === 'Unauthorized') {\n state.reconnecting = false;\n if (!isReconnect) {\n tunnelSpinner?.fail('Unauthorized');\n }\n throw error;\n }\n\n logActivity(state, {\n type: 'error',\n error: `Connection failed, retrying in ${Math.round(delay / 1000)}s...`,\n });\n if (state.interactive) displayStatus(state);\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n state.reconnecting = false;\n };\n\n /**\n * Trigger reconnection from queue processing when fetch failures are detected.\n * Handles errors gracefully to avoid crashing processQueue.\n */\n const triggerReconnect = async (): Promise<void> => {\n if (!state.reconnecting) {\n state.reconnectPromise = connectWithRetry(true).catch((err) => {\n logActivity(state, {\n type: 'error',\n error: `Reconnection failed: ${err.message}`,\n });\n if (state.interactive) displayStatus(state);\n });\n }\n if (state.reconnectPromise) {\n await state.reconnectPromise;\n }\n };\n\n await connectWithRetry(false);\n\n // Step 5: Start lock heartbeat (extends locks every 5 minutes)\n state.lockHeartbeatTimer = setInterval(async () => {\n for (const convId of state.lockedConversations) {\n const extended = await extendConversationLock(\n state.agentId,\n convId,\n state.lockCorrelationId,\n state.authHeader,\n );\n if (!extended) {\n logActivity(state, {\n type: 'error',\n error: `Failed to extend lock on conversation ${convId.slice(0, 8)}`,\n });\n state.lockedConversations.delete(convId);\n }\n }\n }, LOCK_HEARTBEAT_INTERVAL_MS);\n\n // Step 6: Process queue and handle requests\n if (interactive && !state.json) {\n displayStatus(state);\n } else {\n log(state, 'Processing queue...');\n }\n\n await processQueue(state, state.authHeader, triggerReconnect);\n\n // Done\n await cleanup(state, state.authHeader);\n\n if (state.json) {\n console.log(\n JSON.stringify({\n status: 'success',\n messages_processed: state.messageCount,\n }),\n );\n } else if (!interactive) {\n log(state, `Completed. Processed ${state.messageCount} message(s).`);\n }\n\n await shutdownTelemetry();\n process.exit(0);\n } catch (error) {\n await cleanup(state, state.authHeader);\n\n const message = error instanceof Error ? error.message : String(error);\n\n if (state.json) {\n console.log(JSON.stringify({ status: 'error', error: message }));\n } else {\n printError(message);\n }\n\n telemetry.error(EventTypes.CLI_ERROR, `Run command failed: ${message}`, {\n command: 'run',\n agentId: options.agent,\n });\n await shutdownTelemetry();\n process.exit(1);\n }\n}\n","/**\n * Telemetry API types - shared between CLI and API\n *\n * These types define the contract for the telemetry endpoint.\n * Both CLI and API should use these types to ensure type safety.\n */\n\nimport { EventSeverity } from '../events/index.js';\n\n/** Client types that can send telemetry */\nexport type TelemetryClientType = 'cli' | 'sdk' | 'web';\n\n// ============================================================================\n// Event type constants\n// ============================================================================\n\nexport const TelemetryEventTypes = {\n // Agent activity events (shown in web UI activity log)\n AGENT_CONNECTED: 'agent.connected',\n AGENT_DISCONNECTED: 'agent.disconnected',\n AGENT_MESSAGE_PROCESSING: 'agent.message_processing',\n AGENT_MESSAGE_DONE: 'agent.message_done',\n AGENT_MESSAGE_FAILED: 'agent.message_failed',\n} as const;\n\nexport type TelemetryEventType = (typeof TelemetryEventTypes)[keyof typeof TelemetryEventTypes];\n\n// ============================================================================\n// Specific event types with typed metadata\n// ============================================================================\n\n/** Agent connected to Evident */\nexport interface AgentConnectedEvent {\n event_type: typeof TelemetryEventTypes.AGENT_CONNECTED;\n severity?: EventSeverity;\n message?: string;\n metadata: { port: number };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Agent disconnected from Evident */\nexport interface AgentDisconnectedEvent {\n event_type: typeof TelemetryEventTypes.AGENT_DISCONNECTED;\n severity?: EventSeverity;\n message?: string;\n metadata: { code: number; reason: string };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Agent started processing a message */\nexport interface AgentMessageProcessingEvent {\n event_type: typeof TelemetryEventTypes.AGENT_MESSAGE_PROCESSING;\n severity?: EventSeverity;\n message?: string;\n metadata: { message_id: string; conversation_id: string };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Agent finished processing a message successfully */\nexport interface AgentMessageDoneEvent {\n event_type: typeof TelemetryEventTypes.AGENT_MESSAGE_DONE;\n severity?: EventSeverity;\n message?: string;\n metadata: { message_id: string; conversation_id: string };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Agent failed to process a message */\nexport interface AgentMessageFailedEvent {\n event_type: typeof TelemetryEventTypes.AGENT_MESSAGE_FAILED;\n severity?: EventSeverity;\n message?: string;\n metadata: { message_id: string; conversation_id: string; reason?: string; error?: string };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Union of all specific telemetry events */\nexport type TelemetryEvent =\n | AgentConnectedEvent\n | AgentDisconnectedEvent\n | AgentMessageProcessingEvent\n | AgentMessageDoneEvent\n | AgentMessageFailedEvent;\n\n// ============================================================================\n// Generic event type (for API validation - accepts any event)\n// ============================================================================\n\n/**\n * Generic telemetry event request (used by API for validation).\n * Clients should use specific event types above for type safety.\n */\nexport interface TelemetryEventRequest {\n event_type: string;\n severity?: EventSeverity;\n message?: string;\n metadata?: Record<string, unknown>;\n agent_id?: string;\n timestamp?: string;\n}\n\n// ============================================================================\n// Request/Response types\n// ============================================================================\n\n/**\n * Batch request to submit multiple telemetry events\n */\nexport interface SubmitTelemetryEventsRequest {\n events: TelemetryEventRequest[];\n client_type: TelemetryClientType;\n client_version?: string;\n}\n\n/**\n * Response from submitting telemetry events\n */\nexport interface SubmitTelemetryEventsResponse {\n received: number;\n}\n","/**\n * Tunnel types - shared between API, Tunnel Relay (Cloudflare Worker), and CLI\n *\n * These types define the protocol for the WebSocket tunnel that connects\n * local OpenCode instances to the Evident platform.\n */\n\n// ============================================================================\n// API <-> Relay communication\n// ============================================================================\n\n/**\n * Token validation response from API to Relay\n * Sent when CLI connects and Relay validates the token with the API\n */\nexport interface TunnelTokenValidationResponse {\n agent_id: string;\n user_id: string;\n}\n\n/**\n * Token validation request from Relay to API\n */\nexport interface TunnelTokenValidationRequest {\n token: string;\n agent_id: string;\n auth_type: 'bearer' | 'sandbox_key';\n}\n\n/**\n * Status update from Relay to API\n * Sent when CLI connects or disconnects\n */\nexport interface TunnelStatusUpdate {\n agent_id: string;\n status: 'connected' | 'disconnected';\n close_code?: number;\n close_reason?: string;\n}\n\n/**\n * Internal request from API to forward to tunnel\n */\nexport interface TunnelForwardRequest {\n request_id: string;\n method: string;\n path: string;\n headers?: Record<string, string>;\n body?: unknown;\n timeout_ms?: number;\n}\n\n// ============================================================================\n// Relay <-> CLI communication (WebSocket messages)\n// ============================================================================\n\n/**\n * Message types from Relay to CLI\n */\nexport type RelayToCLIMessage =\n | { type: 'connected'; agent_id: string }\n | { type: 'error'; code: string; message: string }\n | { type: 'ping' }\n | { type: 'request'; id: string; payload: TunnelRequestPayload }\n | { type: 'subscribe_events'; id: string }\n | { type: 'unsubscribe_events'; id: string };\n\n/**\n * HTTP request payload forwarded to CLI\n */\nexport interface TunnelRequestPayload {\n method: string;\n path: string;\n headers?: Record<string, string>;\n body?: unknown;\n}\n\n/**\n * Message types from CLI to Relay\n */\nexport type CLIToRelayMessage =\n | { type: 'pong' }\n | { type: 'response'; id: string; payload: TunnelResponsePayload }\n | {\n type: 'response_start';\n id: string;\n total_chunks: number;\n total_size: number;\n payload: Omit<TunnelResponsePayload, 'body'>;\n }\n | { type: 'response_chunk'; id: string; chunk_index: number; data: string }\n | { type: 'response_end'; id: string }\n | { type: 'status'; status: 'ready' | 'busy' }\n | { type: 'event'; id: string; event: unknown }\n | { type: 'event_error'; id: string; error: string }\n | { type: 'event_end'; id: string };\n\n/**\n * Response payload from CLI\n */\nexport interface TunnelResponsePayload {\n status: number;\n headers?: Record<string, string>;\n body?: unknown;\n}\n\n// ============================================================================\n// Internal Relay state\n// ============================================================================\n\n/**\n * Tunnel connection metadata stored in the Durable Object\n */\nexport interface TunnelMetadata {\n agent_id: string;\n user_id: string;\n connected_at: string;\n last_activity: string;\n}\n\n// Note: TunnelStatus is defined in agents/index.ts as 'connected' | 'disconnected' | null\n// We re-use that type for tunnel operations\n\n// ============================================================================\n// Protocol constants\n// ============================================================================\n\n/** Responses larger than this are chunked (512KB) */\nexport const TUNNEL_CHUNK_THRESHOLD = 512 * 1024;\n\n/** Size of each chunk - fits under 1MB with base64 encoding (768KB) */\nexport const TUNNEL_CHUNK_SIZE = 768 * 1024;\n\n/** Maximum total response size (50MB) */\nexport const TUNNEL_MAX_RESPONSE_SIZE = 50 * 1024 * 1024;\n\n/** Timeout for receiving all chunks (30s) */\nexport const TUNNEL_CHUNK_TIMEOUT_MS = 30 * 1000;\n","/**\n * CLI Telemetry Client\n *\n * Captures and reports events to the Evident API for debugging and observability.\n * Events are batched and sent periodically to minimize network overhead.\n */\n\nimport {\n TelemetryEventRequest,\n SubmitTelemetryEventsRequest,\n TelemetryEventTypes,\n TelemetryEvent,\n AgentConnectedEvent,\n AgentDisconnectedEvent,\n AgentMessageProcessingEvent,\n AgentMessageDoneEvent,\n AgentMessageFailedEvent,\n EventSeverity,\n} from '@evident/types';\nimport { getApiUrlConfig } from './config.js';\nimport { getToken } from './keychain.js';\n\n// Get version from package.json at build time\nconst CLI_VERSION = process.env.npm_package_version || 'unknown';\n\n// Re-export for convenience\nexport type { EventSeverity } from '@evident/types';\n// Re-export event types from shared package\nexport { TelemetryEventTypes } from '@evident/types';\n\n// Event buffer for batching\nlet eventBuffer: TelemetryEventRequest[] = [];\nlet flushTimeout: NodeJS.Timeout | null = null;\nlet isShuttingDown = false;\n\n// Configuration\nconst FLUSH_INTERVAL_MS = 5000; // Flush every 5 seconds\nconst MAX_BUFFER_SIZE = 50; // Flush when buffer reaches this size\nconst FLUSH_TIMEOUT_MS = 3000; // Timeout for flush requests\n\n/**\n * Log a telemetry event\n * Events are buffered and sent in batches\n */\nexport function logEvent(\n eventType: string,\n options: {\n severity?: EventSeverity;\n message?: string;\n metadata?: Record<string, unknown>;\n agentId?: string;\n } = {},\n): void {\n const event: TelemetryEventRequest = {\n event_type: eventType,\n severity: options.severity || 'info',\n message: options.message,\n metadata: options.metadata,\n agent_id: options.agentId,\n timestamp: new Date().toISOString(),\n };\n\n eventBuffer.push(event);\n\n // Flush immediately for errors or if buffer is full\n if (options.severity === 'error' || eventBuffer.length >= MAX_BUFFER_SIZE) {\n void flushEvents();\n } else if (!flushTimeout && !isShuttingDown) {\n // Schedule a flush\n flushTimeout = setTimeout(() => {\n flushTimeout = null;\n void flushEvents();\n }, FLUSH_INTERVAL_MS);\n }\n}\n\n/**\n * Convenience methods for different severity levels\n */\nexport const telemetry = {\n debug: (\n eventType: string,\n message?: string,\n metadata?: Record<string, unknown>,\n agentId?: string,\n ) => logEvent(eventType, { severity: 'debug', message, metadata, agentId }),\n\n info: (\n eventType: string,\n message?: string,\n metadata?: Record<string, unknown>,\n agentId?: string,\n ) => logEvent(eventType, { severity: 'info', message, metadata, agentId }),\n\n warn: (\n eventType: string,\n message?: string,\n metadata?: Record<string, unknown>,\n agentId?: string,\n ) => logEvent(eventType, { severity: 'warning', message, metadata, agentId }),\n\n error: (\n eventType: string,\n message?: string,\n metadata?: Record<string, unknown>,\n agentId?: string,\n ) => logEvent(eventType, { severity: 'error', message, metadata, agentId }),\n};\n\n/**\n * Flush buffered events to the API\n */\nexport async function flushEvents(): Promise<void> {\n if (eventBuffer.length === 0) return;\n\n // Take current buffer and reset\n const events = eventBuffer;\n eventBuffer = [];\n\n // Clear any pending flush timeout\n if (flushTimeout) {\n clearTimeout(flushTimeout);\n flushTimeout = null;\n }\n\n try {\n const credentials = await getToken();\n if (!credentials) {\n // Not logged in, can't send telemetry\n return;\n }\n\n const apiUrl = getApiUrlConfig();\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);\n\n try {\n // Type-check the request against the shared contract\n const request: SubmitTelemetryEventsRequest = {\n events,\n client_type: 'cli',\n client_version: CLI_VERSION,\n };\n\n const response = await fetch(`${apiUrl}/telemetry/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${credentials.token}`,\n },\n body: JSON.stringify(request),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n // Log failure but don't throw - telemetry shouldn't break the CLI\n console.error(`Telemetry flush failed: ${response.status}`);\n }\n } finally {\n clearTimeout(timeout);\n }\n } catch (error) {\n // Silently ignore telemetry errors - don't disrupt the user\n if (process.env.DEBUG) {\n console.error('Telemetry error:', error);\n }\n }\n}\n\n/**\n * Shutdown telemetry - flush remaining events\n * Call this before the process exits\n */\nexport async function shutdownTelemetry(): Promise<void> {\n isShuttingDown = true;\n\n if (flushTimeout) {\n clearTimeout(flushTimeout);\n flushTimeout = null;\n }\n\n await flushEvents();\n}\n\n// ============================================================================\n// Type-safe event emitters for agent activity events\n// These ensure the correct metadata is provided for each event type\n// ============================================================================\n\nfunction emitEvent(event: TelemetryEvent): void {\n logEvent(event.event_type, {\n severity: event.severity,\n message: event.message,\n metadata: event.metadata,\n agentId: event.agent_id,\n });\n}\n\n/** Emit agent connected event */\nexport function emitAgentConnected(\n agentId: string,\n metadata: AgentConnectedEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_CONNECTED,\n severity: 'info',\n message: 'Agent CLI connected',\n metadata,\n agent_id: agentId,\n } satisfies AgentConnectedEvent);\n}\n\n/** Emit agent disconnected event */\nexport function emitAgentDisconnected(\n agentId: string,\n metadata: AgentDisconnectedEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_DISCONNECTED,\n severity: 'info',\n message: `Agent CLI disconnected (code: ${metadata.code})`,\n metadata,\n agent_id: agentId,\n } satisfies AgentDisconnectedEvent);\n}\n\n/** Emit agent message processing event */\nexport function emitAgentMessageProcessing(\n agentId: string,\n metadata: AgentMessageProcessingEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_MESSAGE_PROCESSING,\n severity: 'info',\n message: `Processing message ${metadata.message_id.slice(0, 8)}...`,\n metadata,\n agent_id: agentId,\n } satisfies AgentMessageProcessingEvent);\n}\n\n/** Emit agent message done event */\nexport function emitAgentMessageDone(\n agentId: string,\n metadata: AgentMessageDoneEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_MESSAGE_DONE,\n severity: 'info',\n message: `Message ${metadata.message_id.slice(0, 8)} processed`,\n metadata,\n agent_id: agentId,\n } satisfies AgentMessageDoneEvent);\n}\n\n/** Emit agent message failed event */\nexport function emitAgentMessageFailed(\n agentId: string,\n metadata: AgentMessageFailedEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_MESSAGE_FAILED,\n severity: 'error',\n message: metadata.error\n ? `Message ${metadata.message_id.slice(0, 8)} failed: ${metadata.error}`\n : `Message ${metadata.message_id.slice(0, 8)} ${metadata.reason || 'failed'}`,\n metadata,\n agent_id: agentId,\n } satisfies AgentMessageFailedEvent);\n}\n\n// ============================================================================\n// Legacy event types (for non-activity events like CLI lifecycle, auth, etc.)\n// ============================================================================\n\nexport const EventTypes = {\n // Tunnel lifecycle\n TUNNEL_STARTING: 'tunnel.starting',\n TUNNEL_CONNECTED: 'tunnel.connected',\n TUNNEL_DISCONNECTED: 'tunnel.disconnected',\n TUNNEL_RECONNECTING: 'tunnel.reconnecting',\n TUNNEL_ERROR: 'tunnel.error',\n\n // OpenCode communication\n OPENCODE_HEALTH_CHECK: 'opencode.health_check',\n OPENCODE_HEALTH_OK: 'opencode.health_ok',\n OPENCODE_HEALTH_FAILED: 'opencode.health_failed',\n OPENCODE_REQUEST_RECEIVED: 'opencode.request_received',\n OPENCODE_REQUEST_FORWARDED: 'opencode.request_forwarded',\n OPENCODE_RESPONSE_SENT: 'opencode.response_sent',\n OPENCODE_UNREACHABLE: 'opencode.unreachable',\n OPENCODE_ERROR: 'opencode.error',\n\n // Authentication\n AUTH_LOGIN_STARTED: 'auth.login_started',\n AUTH_LOGIN_SUCCESS: 'auth.login_success',\n AUTH_LOGIN_FAILED: 'auth.login_failed',\n AUTH_LOGOUT: 'auth.logout',\n\n // CLI lifecycle\n CLI_STARTED: 'cli.started',\n CLI_COMMAND: 'cli.command',\n CLI_ERROR: 'cli.error',\n} as const;\n","/**\n * Unified Authentication\n *\n * Provides authentication that works for both interactive (keychain) and CI (env vars) modes.\n *\n * Priority:\n * 1. EVIDENT_AGENT_KEY - API key for CI environments\n * 2. EVIDENT_TOKEN - User token (alternative to key)\n * 3. Keychain - Stored credentials from `evident login`\n */\n\nimport { getToken, StoredCredentials } from './keychain.js';\n\nexport type AuthType = 'agent_key' | 'bearer';\n\nexport interface AuthCredentials {\n token: string;\n authType: AuthType;\n /** User info (only available for keychain auth) */\n user?: {\n id: string;\n email: string;\n };\n}\n\n/**\n * Get the authentication credentials.\n *\n * Priority:\n * 1. EVIDENT_AGENT_KEY env var (CI mode)\n * 2. EVIDENT_TOKEN env var (CI mode)\n * 3. Keychain credentials (interactive mode)\n *\n * @returns Credentials if available, null otherwise\n */\nexport async function getAuthCredentials(): Promise<AuthCredentials | null> {\n // Check for agent key (CI environment)\n const agentKey = process.env.EVIDENT_AGENT_KEY;\n if (agentKey) {\n return { token: agentKey, authType: 'agent_key' };\n }\n\n // Check for user token (env var)\n const userToken = process.env.EVIDENT_TOKEN;\n if (userToken) {\n return { token: userToken, authType: 'bearer' };\n }\n\n // Fall back to keychain credentials\n const keychainCreds = await getToken();\n if (keychainCreds) {\n return {\n token: keychainCreds.token,\n authType: 'bearer',\n user: keychainCreds.user,\n };\n }\n\n // No credentials available\n return null;\n}\n\n/**\n * Get the Authorization header value for the given credentials\n */\nexport function getAuthHeader(credentials: AuthCredentials): string {\n if (credentials.authType === 'agent_key') {\n return `SandboxKey ${credentials.token}`;\n }\n return `Bearer ${credentials.token}`;\n}\n\n/**\n * Check if we're running in an interactive environment.\n *\n * Non-interactive if:\n * - CI environment variable is set\n * - GITHUB_ACTIONS environment variable is set\n * - stdin is not a TTY\n *\n * @param jsonOutput - If true, force non-interactive mode\n */\nexport function isInteractive(jsonOutput?: boolean): boolean {\n if (jsonOutput) return false;\n if (process.env.CI) return false;\n if (process.env.GITHUB_ACTIONS) return false;\n if (!process.stdin.isTTY) return false;\n return true;\n}\n\n// Re-export keychain functions for convenience\nexport { getToken, storeToken, deleteToken, hasValidToken } from './keychain.js';\nexport type { StoredCredentials };\n","/**\n * OpenCode Health Checking\n *\n * Functions for checking OpenCode health status and waiting for it to become healthy.\n */\n\nexport interface HealthCheckResult {\n healthy: boolean;\n version?: string;\n error?: string;\n}\n\n/**\n * Check if a port has a valid OpenCode instance by calling /global/health\n */\nexport async function checkOpenCodeHealth(port: number): Promise<HealthCheckResult> {\n try {\n const response = await fetch(`http://localhost:${port}/global/health`, {\n signal: AbortSignal.timeout(2000), // 2 second timeout\n });\n if (!response.ok) {\n return { healthy: false, error: `HTTP ${response.status}` };\n }\n const data = (await response.json().catch(() => ({}))) as { version?: string };\n return { healthy: true, version: data.version };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return { healthy: false, error: message };\n }\n}\n\n/**\n * Wait for OpenCode to be healthy\n */\nexport async function waitForOpenCodeHealth(\n port: number,\n timeoutMs: number = 30000,\n): Promise<HealthCheckResult> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeoutMs) {\n const health = await checkOpenCodeHealth(port);\n if (health.healthy) {\n return health;\n }\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n\n return { healthy: false, error: 'Timeout waiting for OpenCode to be healthy' };\n}\n","/**\n * OpenCode Process Management\n *\n * Functions for starting, stopping, and finding OpenCode processes.\n */\n\nimport { execSync, spawn, ChildProcess } from 'child_process';\nimport { checkOpenCodeHealth } from './health.js';\n\n// Common ports that OpenCode might run on\nconst OPENCODE_PORT_RANGE = [4096, 4097, 4098, 4099, 4100];\n\nexport interface OpenCodeInstance {\n pid: number;\n port: number;\n cwd?: string;\n version?: string;\n}\n\n/**\n * Get the working directory of a process\n */\nfunction getProcessCwd(pid: number): string | undefined {\n const platform = process.platform;\n\n try {\n if (platform === 'darwin') {\n // macOS: use lsof to get cwd\n const output = execSync(`lsof -a -p ${pid} -d cwd -Fn 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n // Output format: \"p<pid>\\nn<path>\"\n const lines = output.split('\\n');\n for (const line of lines) {\n if (line.startsWith('n') && !line.startsWith('n ')) {\n return line.slice(1); // Remove 'n' prefix\n }\n }\n } else if (platform === 'linux') {\n // Linux: read /proc/<pid>/cwd symlink\n const output = execSync(`readlink /proc/${pid}/cwd 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n if (output) return output;\n }\n } catch {\n // Failed to get cwd, return undefined\n }\n\n return undefined;\n}\n\n/**\n * Check if a port is in use by any process\n */\nexport function isPortInUse(port: number): boolean {\n const platform = process.platform;\n\n try {\n if (platform === 'darwin' || platform === 'linux') {\n execSync(`lsof -i :${port} -sTCP:LISTEN 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return true; // Command succeeded, port is in use\n }\n } catch {\n // lsof failed or returned empty, port is free\n }\n\n return false;\n}\n\n/**\n * Find the next available port starting from the given port\n */\nexport function findAvailablePort(startPort: number, maxAttempts: number = 10): number | null {\n for (let i = 0; i < maxAttempts; i++) {\n const port = startPort + i;\n if (!isPortInUse(port)) {\n return port;\n }\n }\n return null;\n}\n\n/**\n * Find running OpenCode processes by scanning the process list\n * Uses pgrep for more reliable process matching\n * Returns array of instances with their PIDs and ports\n */\nexport function findOpenCodeProcesses(): OpenCodeInstance[] {\n const instances: OpenCodeInstance[] = [];\n\n try {\n const platform = process.platform;\n\n if (platform === 'darwin' || platform === 'linux') {\n // Method 1: Use pgrep for more reliable process matching\n let pids: number[] = [];\n\n try {\n // pgrep -f matches against full command line\n const pgrepOutput = execSync('pgrep -f \"opencode serve|opencode-serve\"', {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (pgrepOutput) {\n pids = pgrepOutput\n .split('\\n')\n .map((p) => parseInt(p.trim(), 10))\n .filter((p) => !isNaN(p));\n }\n } catch {\n // pgrep found nothing or failed, try ps fallback\n try {\n const psOutput = execSync('ps aux | grep -E \"opencode (serve|--port)\" | grep -v grep', {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (psOutput) {\n for (const line of psOutput.split('\\n')) {\n const parts = line.trim().split(/\\s+/);\n if (parts.length >= 2) {\n const pid = parseInt(parts[1], 10);\n if (!isNaN(pid)) pids.push(pid);\n }\n }\n }\n } catch {\n // ps also failed, pids stays empty\n }\n }\n\n // For each PID, find what port it's listening on and get cwd\n for (const pid of pids) {\n try {\n const lsofOutput = execSync(`lsof -Pan -p ${pid} -i TCP -sTCP:LISTEN 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n for (const line of lsofOutput.split('\\n')) {\n // Parse port from lsof output (e.g., \"node 12345 user 23u IPv4 0x1234 0t0 TCP *:4096 (LISTEN)\")\n const portMatch = line.match(/:(\\d+)\\s+\\(LISTEN\\)/);\n if (portMatch) {\n const port = parseInt(portMatch[1], 10);\n if (!isNaN(port) && !instances.some((i) => i.port === port)) {\n const cwd = getProcessCwd(pid);\n instances.push({ pid, port, cwd });\n }\n }\n }\n } catch {\n // lsof failed for this PID, skip it\n }\n }\n }\n } catch {\n // Process detection failed, return empty array\n }\n\n return instances;\n}\n\n/**\n * Scan common OpenCode ports and check for healthy instances\n * This is a fallback when process-based detection fails\n */\nexport async function scanPortsForOpenCode(): Promise<OpenCodeInstance[]> {\n const instances: OpenCodeInstance[] = [];\n\n // Check each port in parallel for speed\n const checks = OPENCODE_PORT_RANGE.map(async (port) => {\n const health = await checkOpenCodeHealth(port);\n if (health.healthy) {\n // Try to find the PID for this port\n let pid = 0;\n try {\n const lsofOutput = execSync(`lsof -ti :${port} -sTCP:LISTEN 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n if (lsofOutput) {\n pid = parseInt(lsofOutput.split('\\n')[0], 10) || 0;\n }\n } catch {\n // Couldn't get PID, that's ok\n }\n\n const cwd = pid ? getProcessCwd(pid) : undefined;\n return { pid, port, cwd, version: health.version };\n }\n return null;\n });\n\n const results = await Promise.all(checks);\n for (const result of results) {\n if (result) {\n instances.push(result);\n }\n }\n\n return instances;\n}\n\n/**\n * Find all running OpenCode instances that are healthy\n * Uses process detection first, falls back to port scanning\n */\nexport async function findHealthyOpenCodeInstances(): Promise<OpenCodeInstance[]> {\n // First try process-based detection\n const processes = findOpenCodeProcesses();\n const healthy: OpenCodeInstance[] = [];\n\n for (const proc of processes) {\n const health = await checkOpenCodeHealth(proc.port);\n if (health.healthy) {\n healthy.push({ ...proc, version: health.version });\n }\n }\n\n // If process detection found nothing, fall back to port scanning\n if (healthy.length === 0) {\n const scanned = await scanPortsForOpenCode();\n return scanned;\n }\n\n return healthy;\n}\n\n/**\n * Start OpenCode as a child process\n */\nexport async function startOpenCode(port: number): Promise<ChildProcess> {\n // Try to find opencode command\n let command = 'opencode';\n let args = ['serve', '--port', port.toString()];\n\n try {\n execSync('which opencode', { stdio: 'ignore' });\n } catch {\n // opencode not in PATH, try npx\n command = 'npx';\n args = ['opencode', 'serve', '--port', port.toString()];\n }\n\n const child = spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n cwd: process.cwd(),\n });\n\n return child;\n}\n\n/**\n * Stop OpenCode process\n * Handles both POSIX (process groups with negative PID) and Windows (direct kill)\n */\nexport function stopOpenCode(opencodeProcess: ChildProcess | null): void {\n if (!opencodeProcess || !opencodeProcess.pid) {\n return;\n }\n\n try {\n if (process.platform === 'win32') {\n // Windows: kill the process directly (no process groups)\n opencodeProcess.kill('SIGTERM');\n } else {\n // POSIX: kill the process group (negative PID) since we spawned with detached: true\n process.kill(-opencodeProcess.pid, 'SIGTERM');\n }\n } catch {\n // Process may have already exited, ignore errors\n }\n}\n","/**\n * OpenCode Installation Detection and Prompts\n *\n * Functions for checking if OpenCode is installed and prompting for installation.\n */\n\nimport { execSync } from 'child_process';\nimport chalk from 'chalk';\nimport { select } from '@inquirer/prompts';\nimport { blank } from '../../utils/ui.js';\n\n// OpenCode installation URL\nconst OPENCODE_INSTALL_URL = 'https://opencode.ai';\n\n/**\n * Check if OpenCode is installed on the system.\n * Returns true if the `opencode` command is available in PATH.\n */\nexport function isOpenCodeInstalled(): boolean {\n try {\n const platform = process.platform;\n if (platform === 'win32') {\n execSync('where opencode', { stdio: 'ignore' });\n } else {\n execSync('which opencode', { stdio: 'ignore' });\n }\n return true;\n } catch {\n return false;\n }\n}\n\nexport type InstallPromptResult = 'installed' | 'continue' | 'exit';\n\n/**\n * Display OpenCode installation instructions and offer to install.\n * Returns 'installed' if user installed it, 'continue' to proceed anyway, or 'exit' to stop.\n *\n * @param interactive - If false, outputs JSON error and returns 'exit'\n */\nexport async function promptOpenCodeInstall(interactive: boolean): Promise<InstallPromptResult> {\n if (!interactive) {\n // In non-interactive mode, just output a JSON message and exit\n console.log(\n JSON.stringify({\n status: 'error',\n error: 'OpenCode is not installed',\n install_url: OPENCODE_INSTALL_URL,\n install_commands: {\n npm: 'npm install -g opencode-ai',\n curl: 'curl -fsSL https://opencode.ai/install.sh | sh',\n },\n }),\n );\n return 'exit';\n }\n\n blank();\n console.log(chalk.yellow('OpenCode is not installed on your system.'));\n blank();\n console.log(chalk.dim('OpenCode is an AI coding agent that runs locally on your machine.'));\n console.log(chalk.dim(`Learn more at: ${chalk.cyan(OPENCODE_INSTALL_URL)}`));\n blank();\n\n const action = await select({\n message: 'How would you like to proceed?',\n choices: [\n {\n name: 'Show installation instructions',\n value: 'instructions',\n description: 'Display commands to install OpenCode',\n },\n {\n name: 'Continue without OpenCode',\n value: 'continue',\n description: 'Connect anyway (requests will fail until OpenCode is installed)',\n },\n {\n name: 'Exit',\n value: 'exit',\n description: 'Exit and install OpenCode manually',\n },\n ],\n });\n\n if (action === 'instructions') {\n blank();\n console.log(chalk.bold('Install OpenCode using one of these methods:'));\n blank();\n console.log(chalk.dim(' # Option 1: Install via npm (recommended)'));\n console.log(` ${chalk.cyan('npm install -g opencode-ai')}`);\n blank();\n console.log(chalk.dim(' # Option 2: Install via curl'));\n console.log(` ${chalk.cyan('curl -fsSL https://opencode.ai/install.sh | sh')}`);\n blank();\n console.log(chalk.dim(`For more options, visit: ${chalk.cyan(OPENCODE_INSTALL_URL)}`));\n blank();\n\n const afterInstall = await select({\n message: 'After installing, what would you like to do?',\n choices: [\n {\n name: 'I installed it - continue',\n value: 'continue',\n description: 'Proceed with the run command',\n },\n {\n name: 'Exit',\n value: 'exit',\n description: 'Exit now and run the command again later',\n },\n ],\n });\n\n if (afterInstall === 'continue') {\n // Verify installation\n if (isOpenCodeInstalled()) {\n console.log(chalk.green('\\n✓ OpenCode detected!'));\n return 'installed';\n } else {\n console.log(chalk.yellow('\\nOpenCode still not detected in PATH.'));\n console.log(chalk.dim('You may need to restart your terminal or add it to your PATH.'));\n\n const proceed = await select({\n message: 'Continue anyway?',\n choices: [\n { name: 'Yes, continue', value: 'continue' },\n { name: 'No, exit', value: 'exit' },\n ],\n });\n return proceed === 'continue' ? 'continue' : 'exit';\n }\n }\n return 'exit';\n }\n\n return action as 'continue' | 'exit';\n}\n","/**\n * OpenCode Session Management\n *\n * Functions for creating and managing OpenCode sessions.\n */\n\n/**\n * Create a new OpenCode session\n */\nexport async function createOpenCodeSession(port: number): Promise<string> {\n const response = await fetch(`http://localhost:${port}/session`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create session: HTTP ${response.status}`);\n }\n\n const data = (await response.json()) as { id: string };\n return data.id;\n}\n\n/**\n * Optional OpenCode routing options for sendMessageToOpenCode\n */\nexport interface MessageOptions {\n /** OpenCode agent name (e.g. \"build\", \"plan\") */\n agent?: string;\n /** Model in provider/model format (e.g. \"anthropic/claude-opus-4-6\") */\n model?: string;\n}\n\nexport interface SendMessageResult {\n title?: string;\n}\n\n// Minimal types matching the OpenCode API shapes (avoiding API imports in CLI)\n\nexport interface OpenCodeQuestionOption {\n label: string;\n description: string;\n}\n\nexport interface OpenCodeQuestionInfo {\n question: string;\n header: string;\n options: OpenCodeQuestionOption[];\n}\n\nexport interface OpenCodeQuestion {\n id: string;\n sessionID: string;\n questions: OpenCodeQuestionInfo[];\n tool?: { messageID: string; callID: string };\n}\n\nexport interface OpenCodePermission {\n id: string;\n type: string;\n pattern?: string | string[];\n sessionID: string;\n messageID: string;\n callID?: string;\n title: string;\n metadata: Record<string, unknown>;\n time: { created: number };\n}\n\n/**\n * Hooks called while the message is being processed.\n * Each question/permission is reported at most once (tracked by ID).\n */\nexport interface SessionInteractiveHooks {\n onQuestion?: (question: OpenCodeQuestion) => Promise<void>;\n onPermission?: (permission: OpenCodePermission) => Promise<void>;\n}\n\n/**\n * Send a message to an OpenCode session and wait for it to complete.\n *\n * OpenCode uses a blocking HTTP endpoint: POST /session/:id/message holds the\n * connection open until processing completes (including any wait for the user\n * to answer an interactive question). While waiting, we poll for pending\n * questions and permissions every second so they can be surfaced without\n * blocking the main request.\n *\n * Throws on HTTP errors or when maxWaitMs is exceeded.\n */\nexport async function sendMessageToOpenCode(\n port: number,\n sessionId: string,\n content: string,\n options?: MessageOptions,\n hooks?: SessionInteractiveHooks,\n maxWaitMs: number = 10 * 60 * 1000,\n): Promise<SendMessageResult> {\n const body: Record<string, unknown> = {\n parts: [{ type: 'text', text: content }],\n };\n\n if (options?.agent) {\n body.agent = options.agent;\n }\n\n if (options?.model) {\n const slashIndex = options.model.indexOf('/');\n if (slashIndex !== -1) {\n body.model = {\n providerID: options.model.substring(0, slashIndex),\n modelID: options.model.substring(slashIndex + 1),\n };\n }\n }\n\n let pollDone = false;\n const reportedQuestions = new Set<string>();\n const reportedPermissions = new Set<string>();\n\n // Polls for interactive events while the message request is in-flight.\n const pollInteractive = async () => {\n while (!pollDone) {\n await new Promise<void>((resolve) => setTimeout(resolve, 1000));\n if (pollDone) break;\n\n if (hooks?.onQuestion) {\n try {\n const res = await fetch(`http://localhost:${port}/question`);\n if (res.ok) {\n const questions = (await res.json()) as OpenCodeQuestion[];\n for (const q of questions) {\n if (q.sessionID === sessionId && !reportedQuestions.has(q.id)) {\n reportedQuestions.add(q.id);\n await hooks.onQuestion(q);\n }\n }\n }\n } catch {\n // Non-fatal: interactive detection is best-effort\n }\n }\n\n if (hooks?.onPermission) {\n try {\n const res = await fetch(`http://localhost:${port}/permission`);\n if (res.ok) {\n const permissions = (await res.json()) as OpenCodePermission[];\n for (const p of permissions) {\n if (p.sessionID === sessionId && !reportedPermissions.has(p.id)) {\n reportedPermissions.add(p.id);\n await hooks.onPermission(p);\n }\n }\n }\n } catch {\n // Non-fatal: interactive detection is best-effort\n }\n }\n }\n };\n\n // Awaits the message endpoint; sets pollDone when done so the poll loop exits.\n const sendMessage = async (): Promise<SendMessageResult> => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), maxWaitMs);\n try {\n const res = await fetch(`http://localhost:${port}/session/${sessionId}/message`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`OpenCode message failed: HTTP ${res.status}${text ? `: ${text}` : ''}`);\n }\n // Fetch the updated session title after successful completion\n const sessionRes = await fetch(`http://localhost:${port}/session/${sessionId}`).catch(\n () => null,\n );\n const session = sessionRes?.ok ? ((await sessionRes.json()) as { title?: string }) : null;\n return { title: session?.title };\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error('Message processing timed out');\n }\n throw err;\n } finally {\n clearTimeout(timer);\n pollDone = true;\n }\n };\n\n const [result] = await Promise.all([sendMessage(), pollInteractive()]);\n return result;\n}\n","/**\n * Tunnel WebSocket Connection\n *\n * Functions for establishing and managing WebSocket tunnel connections.\n */\n\nimport WebSocket from 'ws';\nimport { getTunnelUrlConfig } from '../config.js';\nimport type { RelayToCLIMessage } from '@evident/types';\nimport { forwardToOpenCode, sendResponse } from './forwarding.js';\nimport { subscribeToOpenCodeEvents } from './events.js';\n\n// Re-export the message type\nexport type RelayMessage = RelayToCLIMessage;\n\n// Reconnection constants\nconst MAX_RECONNECT_DELAY = 30000; // 30 seconds\nconst BASE_RECONNECT_DELAY = 500; // 0.5 seconds\n\nexport interface TunnelConnectionOptions {\n agentId: string;\n authHeader: string;\n port: number;\n onConnected?: (agentId: string) => void;\n onDisconnected?: (code: number, reason: string) => void;\n onError?: (error: string) => void;\n onRequest?: (method: string, path: string, requestId: string) => void;\n onResponse?: (status: number, durationMs: number, requestId: string) => void;\n onInfo?: (message: string) => void;\n}\n\nexport interface TunnelConnection {\n ws: WebSocket;\n close: () => void;\n // Active event subscriptions: subscription_id -> AbortController\n activeEventSubscriptions: Map<string, AbortController>;\n}\n\n/**\n * Calculate reconnect delay with exponential backoff and jitter\n */\nexport function getReconnectDelay(attempt: number): number {\n const exponentialDelay = BASE_RECONNECT_DELAY * Math.pow(2, attempt);\n const jitter = Math.random() * 1000;\n return Math.min(exponentialDelay + jitter, MAX_RECONNECT_DELAY);\n}\n\n/**\n * Connect to the tunnel relay\n *\n * @returns A promise that resolves with the WebSocket connection when connected,\n * or rejects on connection failure\n */\nexport function connectTunnel(options: TunnelConnectionOptions): Promise<TunnelConnection> {\n const {\n agentId,\n authHeader,\n port,\n onConnected,\n onDisconnected,\n onError,\n onRequest,\n onResponse,\n onInfo,\n } = options;\n\n const tunnelUrl = getTunnelUrlConfig();\n const url = `${tunnelUrl}/tunnel/${agentId}/connect`;\n const activeEventSubscriptions = new Map<string, AbortController>();\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url, {\n headers: {\n Authorization: authHeader,\n },\n });\n\n const connectionTimeout = setTimeout(() => {\n ws.close();\n reject(new Error('Connection timeout'));\n }, 30000);\n\n ws.on('open', () => {\n onInfo?.('WebSocket connection established');\n });\n\n ws.on('message', async (data: WebSocket.RawData) => {\n try {\n const message: RelayMessage = JSON.parse(data.toString());\n\n switch (message.type) {\n case 'connected': {\n clearTimeout(connectionTimeout);\n const connectedAgentId = message.agent_id ?? agentId;\n onConnected?.(connectedAgentId);\n resolve({\n ws,\n close: () => ws.close(1000, 'CLI shutdown'),\n activeEventSubscriptions,\n });\n break;\n }\n\n case 'error':\n clearTimeout(connectionTimeout);\n onError?.(message.message || 'Unknown tunnel error');\n if (message.code === 'unauthorized') {\n ws.close();\n reject(new Error('Unauthorized'));\n }\n break;\n\n case 'ping':\n ws.send(JSON.stringify({ type: 'pong' }));\n break;\n\n case 'request':\n if (message.id && message.payload) {\n const startTime = Date.now();\n onRequest?.(message.payload.method, message.payload.path, message.id);\n\n const response = await forwardToOpenCode(port, message.payload);\n const durationMs = Date.now() - startTime;\n\n onResponse?.(response.status, durationMs, message.id);\n\n // Use sendResponse which handles chunking for large responses\n sendResponse(ws, message.id, response);\n }\n break;\n\n case 'subscribe_events':\n if (message.id) {\n const abortController = new AbortController();\n activeEventSubscriptions.set(message.id, abortController);\n onInfo?.(`Starting event subscription ${message.id.slice(0, 8)}`);\n\n subscribeToOpenCodeEvents(port, message.id, ws, abortController)\n .catch((error) => {\n if (!abortController.signal.aborted) {\n onError?.(`Event subscription failed: ${error.message}`);\n }\n })\n .finally(() => {\n activeEventSubscriptions.delete(message.id!);\n });\n }\n break;\n\n case 'unsubscribe_events':\n if (message.id) {\n const controller = activeEventSubscriptions.get(message.id);\n if (controller) {\n controller.abort();\n activeEventSubscriptions.delete(message.id);\n }\n }\n break;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n onError?.(`Failed to handle message: ${errorMessage}`);\n }\n });\n\n ws.on('error', (error: Error) => {\n clearTimeout(connectionTimeout);\n onError?.(`Connection error: ${error.message}`);\n reject(error);\n });\n\n ws.on('close', (code: number, reason: Buffer) => {\n const reasonStr = reason.toString() || 'No reason provided';\n onDisconnected?.(code, reasonStr);\n\n // Cancel all active event subscriptions\n for (const [, controller] of activeEventSubscriptions) {\n controller.abort();\n }\n activeEventSubscriptions.clear();\n });\n });\n}\n","/**\n * Tunnel Request Forwarding\n *\n * Functions for forwarding requests to OpenCode and handling responses.\n */\n\nimport WebSocket from 'ws';\n\n// Chunked response protocol constants (must match tunnel-relay)\nconst CHUNK_THRESHOLD = 512 * 1024; // 512KB - responses larger than this are chunked\nconst CHUNK_SIZE = 768 * 1024; // 768KB - size of each chunk\n\nexport interface ForwardRequest {\n method: string;\n path: string;\n headers?: Record<string, string>;\n body?: unknown;\n}\n\nexport interface ForwardResponse {\n status: number;\n headers?: Record<string, string>;\n body?: unknown;\n}\n\n/**\n * Forward request to local OpenCode instance\n */\nexport async function forwardToOpenCode(\n port: number,\n request: ForwardRequest,\n): Promise<ForwardResponse> {\n const url = `http://localhost:${port}${request.path}`;\n\n try {\n const response = await fetch(url, {\n method: request.method,\n headers: {\n 'Content-Type': 'application/json',\n ...request.headers,\n },\n body: request.body ? JSON.stringify(request.body) : undefined,\n });\n\n let body: unknown;\n const contentType = response.headers.get('Content-Type');\n const text = await response.text();\n\n // Handle empty responses (e.g., 204 No Content)\n if (!text || text.length === 0) {\n body = null;\n } else if (contentType?.includes('application/json')) {\n try {\n body = JSON.parse(text);\n } catch {\n // If JSON parsing fails, return as text\n body = text;\n }\n } else {\n body = text;\n }\n\n return {\n status: response.status,\n body,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n status: 502,\n body: { error: 'Failed to connect to OpenCode', message },\n };\n }\n}\n\n/**\n * Send a response back to the relay.\n * Automatically uses chunked protocol for large responses (>= 512KB body).\n */\nexport function sendResponse(ws: WebSocket, requestId: string, response: ForwardResponse): void {\n const bodyStr = JSON.stringify(response.body ?? null);\n const bodyBytes = Buffer.from(bodyStr, 'utf-8');\n\n if (bodyBytes.length < CHUNK_THRESHOLD) {\n // Small response: send as single message (common case)\n ws.send(\n JSON.stringify({\n type: 'response',\n id: requestId,\n payload: response,\n }),\n );\n return;\n }\n\n // Large response: use chunked protocol\n sendResponseAsChunks(ws, requestId, response, bodyBytes);\n}\n\n/**\n * Send a large response as multiple chunks.\n * Only called when response body exceeds CHUNK_THRESHOLD.\n */\nfunction sendResponseAsChunks(\n ws: WebSocket,\n requestId: string,\n response: { status: number; headers?: Record<string, string> },\n bodyBytes: Buffer,\n): void {\n const chunks = splitIntoChunks(bodyBytes, CHUNK_SIZE);\n\n // Send start message with metadata\n ws.send(\n JSON.stringify({\n type: 'response_start',\n id: requestId,\n total_chunks: chunks.length,\n total_size: bodyBytes.length,\n payload: {\n status: response.status,\n headers: response.headers,\n },\n }),\n );\n\n // Send each chunk\n for (let i = 0; i < chunks.length; i++) {\n ws.send(\n JSON.stringify({\n type: 'response_chunk',\n id: requestId,\n chunk_index: i,\n data: chunks[i].toString('base64'),\n }),\n );\n }\n\n // Send end message\n ws.send(\n JSON.stringify({\n type: 'response_end',\n id: requestId,\n }),\n );\n}\n\n/**\n * Split a buffer into chunks of the specified size\n */\nfunction splitIntoChunks(data: Buffer, chunkSize: number): Buffer[] {\n const chunks: Buffer[] = [];\n for (let i = 0; i < data.length; i += chunkSize) {\n chunks.push(data.subarray(i, i + chunkSize));\n }\n return chunks;\n}\n","/**\n * Tunnel SSE Event Subscription\n *\n * Functions for subscribing to OpenCode events and forwarding them through the WebSocket.\n */\n\nimport WebSocket from 'ws';\n\n/**\n * Subscribe to OpenCode events and forward them through the WebSocket\n *\n * @param port - OpenCode port\n * @param subscriptionId - Unique ID for this subscription\n * @param ws - WebSocket connection to relay\n * @param abortController - Controller to cancel the subscription\n */\nexport async function subscribeToOpenCodeEvents(\n port: number,\n subscriptionId: string,\n ws: WebSocket,\n abortController: AbortController,\n): Promise<void> {\n const url = `http://localhost:${port}/event`;\n\n try {\n const response = await fetch(url, {\n headers: { Accept: 'text/event-stream' },\n signal: abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to connect to OpenCode events: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error('No response body');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n // Stream ended\n ws.send(JSON.stringify({ type: 'event_end', id: subscriptionId }));\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n try {\n const event = JSON.parse(line.slice(6));\n // Forward event to relay\n ws.send(JSON.stringify({ type: 'event', id: subscriptionId, event }));\n } catch {\n // Ignore parse errors for individual events\n }\n }\n }\n }\n } catch (error) {\n if (abortController.signal.aborted) {\n // Subscription was cancelled, this is expected\n return;\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error';\n // Send error to relay\n ws.send(JSON.stringify({ type: 'event_error', id: subscriptionId, error: message }));\n throw error;\n }\n}\n"],"mappings":"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","names":["chalk","error","credentials","error","chalk","credentials","chalk","credentials","chalk","chalk","ora","select","credentials","error","credentials","error","execSync","chalk","execSync","chalk","error","error","error","error","chalk","select","credentials","ora"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/config.ts","../src/lib/api.ts","../src/lib/keychain.ts","../src/utils/ui.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../src/commands/run.ts","../../../packages/types/src/telemetry/index.ts","../../../packages/types/src/tunnel/index.ts","../src/lib/telemetry.ts","../src/lib/auth.ts","../src/lib/opencode/health.ts","../src/lib/opencode/process.ts","../src/lib/opencode/install.ts","../src/lib/opencode/session.ts","../src/lib/tunnel/connection.ts","../src/lib/tunnel/forwarding.ts","../src/lib/tunnel/runner-connection.ts","../src/lib/channels/driver.ts","../src/commands/ensure-opencode.ts","../src/commands/agent-lookup.ts"],"sourcesContent":["/**\n * Evident CLI\n *\n * Run OpenCode locally and connect it to the Evident platform.\n */\n\nimport { Command } from 'commander';\nimport { login } from './commands/login.js';\nimport { logout } from './commands/logout.js';\nimport { whoami } from './commands/whoami.js';\nimport { run } from './commands/run.js';\nimport { setEndpoint, setTunnelUrl } from './lib/config.js';\n\nconst program = new Command();\n\nprogram\n .name('evident')\n .description('Run OpenCode locally and connect it to Evident')\n .version('0.1.0')\n // The CLI targets production by default. Point it elsewhere (local dev, a\n // preview env, …) with --endpoint (REST API base URL) and, if needed, --tunnel.\n .option(\n '--endpoint <url>',\n 'Evident API base URL (default: production; e.g. http://localhost:3001)',\n )\n .option('--tunnel <url>', 'Tunnel WebSocket URL (default: production; e.g. ws://localhost:8787)')\n .hook('preAction', (thisCommand) => {\n const { endpoint, tunnel } = thisCommand.opts() as {\n endpoint?: string;\n tunnel?: string;\n };\n if (endpoint) {\n setEndpoint(endpoint);\n }\n if (tunnel) {\n setTunnelUrl(tunnel);\n }\n });\n\n// Login command\nprogram\n .command('login')\n .description('Authenticate with Evident')\n .option('--token', 'Use token-based authentication (for CI/CD)')\n .option('--no-browser', 'Do not open the browser automatically')\n .action(login);\n\n// Logout command\nprogram\n .command('logout')\n .description('Remove stored credentials for the current endpoint')\n .option('--all', 'Remove stored credentials for all endpoints')\n .action((options: { all?: boolean }) => logout({ all: options.all }));\n\n// Whoami command\nprogram.command('whoami').description('Show the currently logged in user').action(whoami);\n\n// Run command (unified - connects to Evident and processes messages)\nprogram\n .command('run')\n .description('Connect to Evident and process messages')\n // NOTE: This MUST remain `.option()` (not `.requiredOption()`). When EVIDENT_AGENT_KEY is set,\n // the CLI resolves the agent ID at runtime via GET /v1/me — requiring --agent at the Commander\n // argument-parsing level would block that path before the runtime logic ever runs.\n .option('-a, --agent [id]', 'Agent ID to connect to (optional when EVIDENT_AGENT_KEY is set)')\n .option('-p, --port <port>', 'OpenCode port (default: 4096)', '4096')\n .option('-v, --verbose', 'Show detailed request/response information')\n .option('-c, --conversation <id>', 'Process only this specific conversation')\n .option('--idle-timeout <seconds>', 'Exit after N seconds idle')\n .option('--json', 'Output in JSON format')\n .action(\n (options: {\n agent?: string;\n port: string;\n verbose?: boolean;\n conversation?: string;\n idleTimeout?: string;\n json?: boolean;\n }) => {\n run({\n agent: options.agent,\n port: parseInt(options.port, 10),\n verbose: options.verbose,\n conversation: options.conversation,\n idleTimeout: options.idleTimeout ? parseInt(options.idleTimeout, 10) : undefined,\n json: options.json,\n });\n },\n );\n\n// Parse arguments\nprogram.parse();\n","/**\n * Login Command\n *\n * Authenticates the user using OAuth Device Flow.\n * See ADR-0018 for details.\n */\n\nimport open from 'open';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport { api } from '../lib/api.js';\nimport { storeToken } from '../lib/keychain.js';\nimport { printSuccess, printError, blank, waitForEnter, sleep } from '../utils/ui.js';\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n}\n\ninterface TokenPollResponse {\n status: 'pending' | 'complete' | 'expired';\n access_token?: string;\n expires_at?: string;\n user?: {\n id: string;\n email: string;\n };\n}\n\ninterface LoginOptions {\n token?: boolean;\n noBrowser?: boolean;\n}\n\n/**\n * Start device flow authentication\n */\nasync function deviceFlowLogin(options: LoginOptions): Promise<void> {\n // Step 1: Request device code\n let deviceAuth: DeviceAuthResponse;\n try {\n deviceAuth = await api.post<DeviceAuthResponse>('/auth/device');\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError(`Failed to start authentication: ${message}`);\n process.exit(1);\n }\n\n const { device_code, user_code, verification_uri, interval } = deviceAuth;\n\n // Step 2: Display instructions\n blank();\n console.log(chalk.bold('To authenticate, visit:'));\n console.log();\n console.log(` ${chalk.cyan(verification_uri)}`);\n console.log();\n console.log(chalk.bold('And enter this code:'));\n console.log();\n console.log(` ${chalk.yellow.bold(user_code)}`);\n blank();\n\n // Step 3: Open browser (unless --no-browser)\n if (!options.noBrowser) {\n await waitForEnter('Press Enter to open the browser...');\n try {\n await open(verification_uri);\n } catch {\n console.log(chalk.dim('Could not open browser. Please visit the URL manually.'));\n }\n }\n\n // Step 4: Poll for completion\n const spinner = ora('Waiting for authentication...').start();\n\n const pollIntervalMs = (interval || 5) * 1000;\n const maxAttempts = 60; // 5 minutes max at 5s intervals\n let attempts = 0;\n\n while (attempts < maxAttempts) {\n await sleep(pollIntervalMs);\n attempts++;\n\n try {\n const result = await api.post<TokenPollResponse>('/auth/device/token', {\n device_code,\n });\n\n if (result.status === 'complete' && result.access_token && result.user) {\n // Success! Store the token\n await storeToken({\n token: result.access_token,\n user: result.user,\n expiresAt: result.expires_at,\n });\n\n spinner.stop();\n blank();\n printSuccess(`Logged in as ${chalk.bold(result.user.email)}`);\n return;\n }\n\n if (result.status === 'expired') {\n spinner.stop();\n blank();\n printError('Authentication expired. Please try again.');\n process.exit(1);\n }\n\n // Still pending, continue polling\n } catch (error) {\n // Network error, continue polling\n const message = error instanceof Error ? error.message : 'Unknown error';\n spinner.text = `Waiting for authentication... (${message})`;\n }\n }\n\n spinner.stop();\n blank();\n printError('Authentication timed out. Please try again.');\n process.exit(1);\n}\n\n/**\n * Token-based login (for CI/CD)\n */\nasync function tokenLogin(): Promise<void> {\n console.log('Token login mode.');\n console.log('Visit your Evident dashboard to generate a CLI token.');\n blank();\n\n // Read token from stdin\n process.stdout.write('Paste token: ');\n\n const token = await new Promise<string>((resolve) => {\n let data = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk) => {\n data += chunk;\n });\n process.stdin.on('end', () => {\n resolve(data.trim());\n });\n // For TTY, read a single line\n if (process.stdin.isTTY) {\n process.stdin.once('data', (chunk) => {\n process.stdin.pause();\n resolve(chunk.toString().trim());\n });\n process.stdin.resume();\n }\n });\n\n if (!token) {\n printError('No token provided.');\n process.exit(1);\n }\n\n // Validate the token\n const spinner = ora('Validating token...').start();\n\n try {\n interface ValidateResponse {\n user: { id: string; email: string };\n expires_at?: string;\n }\n\n const result = await api.post<ValidateResponse>('/auth/token/validate', { token });\n\n await storeToken({\n token,\n user: result.user,\n expiresAt: result.expires_at,\n });\n\n spinner.stop();\n printSuccess(`Logged in as ${chalk.bold(result.user.email)}`);\n } catch (error) {\n spinner.stop();\n const message = error instanceof Error ? error.message : 'Invalid token';\n printError(`Authentication failed: ${message}`);\n process.exit(1);\n }\n}\n\n/**\n * Login command handler\n */\nexport async function login(options: LoginOptions): Promise<void> {\n if (options.token) {\n await tokenLogin();\n } else {\n await deviceFlowLogin(options);\n }\n}\n","/**\n * CLI Configuration\n *\n * Manages configuration values and file-based credential storage.\n *\n * The CLI targets the PRODUCTION Evident platform by default. To point it at a\n * different backend (local dev, a preview environment, etc.) pass `--endpoint`\n * (REST API base URL) and, if needed, `--tunnel` (tunnel WebSocket URL) — or set\n * the `EVIDENT_API_URL` / `EVIDENT_TUNNEL_URL` env vars. There is no named\n * environment concept; URLs are the single source of truth, so the UI can\n * generate a `run` command that points at whatever backend it is itself using.\n */\n\nimport Conf from 'conf';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// Configuration schema\ninterface ConfigSchema {\n apiUrl: string;\n tunnelUrl: string;\n}\n\n// A single endpoint's credentials.\ninterface EndpointCredentials {\n token?: string;\n user?: {\n id: string;\n email: string;\n };\n expiresAt?: string;\n}\n\n// Credentials schema (stored separately with stricter permissions).\n//\n// Credentials are keyed by the resolved API endpoint so the CLI can hold a\n// distinct session per environment (dev, production, a preview env, …) at the\n// same time. `evident login --endpoint <a>` and `--endpoint <b>` no longer clobber\n// each other, and `run`/`whoami`/`logout` automatically pick the entry that\n// matches the endpoint they are pointed at.\ninterface CredentialsSchema {\n byEndpoint?: Record<string, EndpointCredentials>;\n}\n\n// Built-in defaults: the production Evident platform.\n// (Production URLs also have aliases: api.evident.run, tunnel.evident.run.)\n// API URLs include the /v1 prefix as all REST endpoints are versioned.\nconst PRODUCTION_API_URL = 'https://api.production.evident.run/v1';\nconst PRODUCTION_TUNNEL_URL = 'wss://tunnel.production.evident.run';\n\nconst defaults: ConfigSchema = {\n apiUrl: PRODUCTION_API_URL,\n tunnelUrl: PRODUCTION_TUNNEL_URL,\n};\n\n// Explicit endpoint overrides (set via --endpoint / --tunnel flags). These take\n// precedence over the production defaults so the UI can generate a command that\n// points at an exact API URL without hardcoding per-env URLs in two places.\nlet endpointOverride: string | undefined;\nlet tunnelOverride: string | undefined;\n\n/**\n * Override the API endpoint URL directly (from the `--endpoint` flag).\n *\n * Accepts a base URL with or without the trailing `/v1` (the platform's REST\n * routes are versioned). The `/v1` suffix is normalized so callers can paste the\n * plain origin shown in the UI (e.g. `http://localhost:3001`).\n */\nexport function setEndpoint(url: string | undefined): void {\n if (!url) {\n endpointOverride = undefined;\n return;\n }\n const trimmed = url.replace(/\\/+$/, '');\n endpointOverride = /\\/v1$/.test(trimmed) ? trimmed : `${trimmed}/v1`;\n}\n\n/**\n * Override the tunnel WebSocket URL directly (from the `--tunnel` flag).\n */\nexport function setTunnelUrl(url: string | undefined): void {\n tunnelOverride = url ? url.replace(/\\/+$/, '') : undefined;\n}\n\n// URL resolution precedence: explicit `--endpoint`/`--tunnel` flag > env var >\n// production default.\n//\n// The flag is the most specific, intentional signal a user can give for a single\n// invocation, so it MUST win. The env var (EVIDENT_API_URL / EVIDENT_TUNNEL_URL)\n// is an ambient default — useful in a dev shell (direnv) — but it must never\n// silently override an endpoint the user typed on the command line. Getting this\n// backwards means `--endpoint https://api.dev.evident.run` is silently ignored\n// when a local `EVIDENT_API_URL` is exported, sending the CLI to the wrong\n// backend with no feedback.\nfunction getApiUrl(): string {\n return endpointOverride ?? process.env.EVIDENT_API_URL ?? defaults.apiUrl;\n}\n\nfunction getTunnelUrl(): string {\n return tunnelOverride ?? process.env.EVIDENT_TUNNEL_URL ?? defaults.tunnelUrl;\n}\n\n// Configuration store\nconst config = new Conf<ConfigSchema>({\n projectName: 'evident',\n projectSuffix: '',\n defaults,\n});\n\n// Credentials store (separate file with restricted access)\nconst credentials = new Conf<CredentialsSchema>({\n projectName: 'evident',\n projectSuffix: '',\n configName: 'credentials',\n defaults: {},\n});\n\n/**\n * Get the configuration directory path\n */\nexport function getConfigDir(): string {\n // XDG_CONFIG_HOME on Linux, ~/.config on others\n const xdgConfig = process.env.XDG_CONFIG_HOME;\n if (xdgConfig) {\n return join(xdgConfig, 'evident');\n }\n return join(homedir(), '.config', 'evident');\n}\n\n/**\n * Get the API URL\n */\nexport function getApiUrlConfig(): string {\n return getApiUrl();\n}\n\n/**\n * Get the tunnel WebSocket URL\n */\nexport function getTunnelUrlConfig(): string {\n return getTunnelUrl();\n}\n\n/**\n * The key under which credentials for the current endpoint are stored. We key on\n * the fully-resolved API URL (including `/v1` and any env/flag override) so each\n * environment gets its own slot. A token is only ever valid for the backend that\n * minted it, so the endpoint is the natural identity for a credential.\n */\nfunction credentialsKey(): string {\n return getApiUrl();\n}\n\n/**\n * Get stored credentials for the current endpoint.\n */\nexport function getCredentials(): EndpointCredentials {\n const byEndpoint = credentials.get('byEndpoint') ?? {};\n return byEndpoint[credentialsKey()] ?? {};\n}\n\n/**\n * Store credentials for the current endpoint.\n */\nexport function setCredentials(creds: EndpointCredentials): void {\n const byEndpoint = credentials.get('byEndpoint') ?? {};\n byEndpoint[credentialsKey()] = {\n token: creds.token,\n user: creds.user,\n expiresAt: creds.expiresAt,\n };\n credentials.set('byEndpoint', byEndpoint);\n}\n\n/**\n * Clear stored credentials for the current endpoint only. Other endpoints'\n * sessions are preserved.\n */\nexport function clearCredentials(): void {\n const byEndpoint = credentials.get('byEndpoint') ?? {};\n delete byEndpoint[credentialsKey()];\n credentials.set('byEndpoint', byEndpoint);\n}\n\n/**\n * Clear all stored credentials across every endpoint.\n */\nexport function clearAllCredentials(): void {\n credentials.clear();\n}\n\n/**\n * Check if we have valid credentials\n */\nexport function hasValidCredentials(): boolean {\n const creds = getCredentials();\n\n if (!creds.token) {\n return false;\n }\n\n if (creds.expiresAt) {\n const expiresAt = new Date(creds.expiresAt);\n if (expiresAt < new Date()) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Get the CLI command name based on how it was invoked.\n * Returns 'evident' for normal usage, or the actual invocation for dev/npx usage.\n */\nexport function getCliName(): string {\n // Check if running via npx - multiple detection methods\n // 1. npm_execpath contains npx\n // 2. npm_command is 'exec' (npx sets this)\n // 3. Running from a global npx cache directory\n const argv1 = process.argv[1] || '';\n const isNpx =\n process.env.npm_execpath?.includes('npx') ||\n process.env.npm_command === 'exec' ||\n argv1.includes('_npx') ||\n argv1.includes('.npm/_cacache');\n\n if (isNpx) {\n return 'npx @evident-ai/cli@latest';\n }\n\n // Check if running via tsx (development)\n if (argv1.includes('tsx') || argv1.includes('ts-node')) {\n return 'pnpm --filter @evident-ai/cli dev:run';\n }\n\n // Default to 'evident' for normal installed CLI\n return 'evident';\n}\n\nexport { config, credentials };\n","/**\n * API Client\n *\n * Handles HTTP requests to the Evident backend API.\n */\n\nimport { getApiUrlConfig, getCredentials } from './config.js';\n\ninterface ApiRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n body?: unknown;\n headers?: Record<string, string>;\n authenticated?: boolean;\n}\n\ninterface ApiError {\n message: string;\n statusCode: number;\n error?: string;\n}\n\nexport class ApiClient {\n private baseUrl: string;\n\n constructor(baseUrl?: string) {\n this.baseUrl = baseUrl ?? getApiUrlConfig();\n }\n\n /**\n * Make an API request\n */\n async request<T>(path: string, options: ApiRequestOptions = {}): Promise<T> {\n const { method = 'GET', body, headers = {}, authenticated = false } = options;\n\n const url = `${this.baseUrl}${path}`;\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headers,\n };\n\n // Add authentication header if requested\n if (authenticated) {\n const creds = getCredentials();\n if (!creds.token) {\n throw new Error('Not authenticated. Run the `login` command first.');\n }\n requestHeaders['Authorization'] = `Bearer ${creds.token}`;\n }\n\n const response = await fetch(url, {\n method,\n headers: requestHeaders,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n // Handle errors\n if (!response.ok) {\n let errorData: ApiError;\n try {\n errorData = (await response.json()) as ApiError;\n } catch {\n errorData = {\n message: response.statusText,\n statusCode: response.status,\n };\n }\n\n const error = new Error(errorData.message) as Error & { statusCode: number };\n error.statusCode = response.status;\n throw error;\n }\n\n // Handle empty responses\n const contentType = response.headers.get('Content-Type');\n if (!contentType?.includes('application/json')) {\n return {} as T;\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * GET request\n */\n async get<T>(path: string, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<T> {\n return this.request<T>(path, { ...options, method: 'GET' });\n }\n\n /**\n * POST request\n */\n async post<T>(\n path: string,\n body?: unknown,\n options: Omit<ApiRequestOptions, 'method'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'POST', body });\n }\n\n /**\n * PUT request\n */\n async put<T>(\n path: string,\n body?: unknown,\n options: Omit<ApiRequestOptions, 'method'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'PUT', body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(\n path: string,\n options: Omit<ApiRequestOptions, 'method' | 'body'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'DELETE' });\n }\n}\n\n// Lazy API client instance - created on first use after env is set\nlet _api: ApiClient | null = null;\nexport const api = {\n get<T>(path: string, options?: Parameters<ApiClient['get']>[1]) {\n if (!_api) _api = new ApiClient();\n return _api.get<T>(path, options);\n },\n post<T>(path: string, body?: unknown, options?: Parameters<ApiClient['post']>[2]) {\n if (!_api) _api = new ApiClient();\n return _api.post<T>(path, body, options);\n },\n put<T>(path: string, body?: unknown, options?: Parameters<ApiClient['put']>[2]) {\n if (!_api) _api = new ApiClient();\n return _api.put<T>(path, body, options);\n },\n delete<T>(path: string, options?: Parameters<ApiClient['delete']>[1]) {\n if (!_api) _api = new ApiClient();\n return _api.delete<T>(path, options);\n },\n};\n","/**\n * Keychain Storage\n *\n * Provides secure credential storage using the system keychain.\n * Falls back to file-based storage if the keychain is unavailable.\n *\n * Credentials are keyed PER ENDPOINT in both backends so the CLI can hold a\n * distinct session per environment (dev, production, a preview env, …) at the\n * same time, and never clobber one when logging into another:\n * - keytar: the keychain \"account\" is the resolved API endpoint URL.\n * - file fallback: the on-disk store is a `{ byEndpoint }` map (see config.ts).\n * Both pick the entry that matches the endpoint the command is pointed at (via\n * `--endpoint` / `EVIDENT_API_URL`, else production).\n */\n\nimport {\n getCredentials,\n setCredentials,\n clearCredentials,\n clearAllCredentials,\n getApiUrlConfig,\n} from './config.js';\n\nconst SERVICE_NAME = 'evident-cli';\n\n// Dynamic import for keytar (optional dependency)\nasync function getKeytar(): Promise<typeof import('keytar') | null> {\n try {\n const keytar = await import('keytar');\n // Verify keytar is actually functional (has the expected methods)\n if (typeof keytar.setPassword !== 'function') {\n return null;\n }\n return keytar;\n } catch {\n // keytar not available (e.g., missing native dependencies)\n return null;\n }\n}\n\n/**\n * The keychain account for the current endpoint. We key on the fully-resolved\n * API URL (matching the file-store key) so a token is stored against the backend\n * that minted it.\n */\nfunction keychainAccount(): string {\n return getApiUrlConfig();\n}\n\nexport interface StoredCredentials {\n token: string;\n user: {\n id: string;\n email: string;\n };\n expiresAt?: string;\n}\n\n/**\n * Store credentials for the current endpoint in the system keychain.\n */\nexport async function storeToken(credentials: StoredCredentials): Promise<void> {\n const keytar = await getKeytar();\n\n if (keytar) {\n // Store in system keychain, keyed by endpoint.\n await keytar.setPassword(SERVICE_NAME, keychainAccount(), JSON.stringify(credentials));\n } else {\n // Fallback to file-based storage (also per-endpoint).\n setCredentials({\n token: credentials.token,\n user: credentials.user,\n expiresAt: credentials.expiresAt,\n });\n }\n}\n\n/**\n * Retrieve credentials for the current endpoint from the system keychain,\n * falling back to the file-based store.\n */\nexport async function getToken(): Promise<StoredCredentials | null> {\n const keytar = await getKeytar();\n\n if (keytar) {\n // Try system keychain first, for this endpoint.\n const account = keychainAccount();\n const stored = await keytar.getPassword(SERVICE_NAME, account);\n if (stored) {\n try {\n return JSON.parse(stored) as StoredCredentials;\n } catch {\n // Invalid JSON, clear it\n await keytar.deletePassword(SERVICE_NAME, account);\n return null;\n }\n }\n }\n\n // Fallback to file-based storage\n const creds = getCredentials();\n if (creds.token && creds.user) {\n return {\n token: creds.token,\n user: creds.user,\n expiresAt: creds.expiresAt,\n };\n }\n\n return null;\n}\n\n/**\n * Delete stored credentials.\n *\n * By default this clears credentials for the *current* endpoint only, preserving\n * sessions for other environments. Pass `{ all: true }` to wipe every stored\n * session across all endpoints.\n */\nexport async function deleteToken(options: { all?: boolean } = {}): Promise<void> {\n const keytar = await getKeytar();\n\n if (keytar) {\n if (options.all) {\n // Enumerate every account stored under our service and remove each one.\n const all = await keytar.findCredentials(SERVICE_NAME).catch(() => []);\n await Promise.all(\n all.map((entry) =>\n keytar.deletePassword(SERVICE_NAME, entry.account).catch(() => {\n /* best-effort */\n }),\n ),\n );\n } else {\n await keytar.deletePassword(SERVICE_NAME, keychainAccount());\n }\n }\n\n // Clear file-based storage: just the current endpoint, or everything.\n if (options.all) {\n clearAllCredentials();\n } else {\n clearCredentials();\n }\n}\n\n/**\n * Check if credentials for the current endpoint are valid (not expired).\n */\nexport async function hasValidToken(): Promise<boolean> {\n const credentials = await getToken();\n\n if (!credentials) {\n return false;\n }\n\n if (credentials.expiresAt) {\n const expiresAt = new Date(credentials.expiresAt);\n if (expiresAt < new Date()) {\n return false;\n }\n }\n\n return true;\n}\n","/**\n * CLI UI Utilities\n *\n * Common formatting and display functions.\n */\n\nimport chalk from 'chalk';\n\n/**\n * Format success message\n */\nexport function success(message: string): string {\n return `${chalk.green('✓')} ${message}`;\n}\n\n/**\n * Format error message\n */\nexport function error(message: string): string {\n return `${chalk.red('✗')} ${message}`;\n}\n\n/**\n * Format warning message\n */\nexport function warning(message: string): string {\n return `${chalk.yellow('!')} ${message}`;\n}\n\n/**\n * Format info message\n */\nexport function info(message: string): string {\n return `${chalk.blue('i')} ${message}`;\n}\n\n/**\n * Print success message\n */\nexport function printSuccess(message: string): void {\n console.log(success(message));\n}\n\n/**\n * Print error message\n */\nexport function printError(message: string): void {\n console.error(error(message));\n}\n\n/**\n * Print warning message\n */\nexport function printWarning(message: string): void {\n console.log(warning(message));\n}\n\n/**\n * Print info message\n */\nexport function printInfo(message: string): void {\n console.log(info(message));\n}\n\n/**\n * Format a key-value pair for display\n */\nexport function keyValue(key: string, value: string): string {\n return `${chalk.dim(key + ':')} ${value}`;\n}\n\n/**\n * Print a blank line\n */\nexport function blank(): void {\n console.log();\n}\n\n/**\n * Wait for user to press Enter\n */\nexport function waitForEnter(prompt = 'Press Enter to continue...'): Promise<void> {\n return new Promise((resolve) => {\n process.stdout.write(chalk.dim(prompt));\n\n const handler = (): void => {\n process.stdin.removeListener('data', handler);\n process.stdin.setRawMode?.(false);\n process.stdin.pause();\n console.log();\n resolve();\n };\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode?.(true);\n }\n process.stdin.resume();\n process.stdin.once('data', handler);\n });\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Logout Command\n *\n * Removes stored credentials.\n */\n\nimport { deleteToken, getToken } from '../lib/keychain.js';\nimport { getApiUrlConfig } from '../lib/config.js';\nimport { printSuccess, printWarning } from '../utils/ui.js';\n\ninterface LogoutOptions {\n /** Clear credentials for every endpoint, not just the current one. */\n all?: boolean;\n}\n\n/**\n * Logout command handler.\n *\n * Credentials are stored per endpoint, so by default this only signs you out of\n * the endpoint the command is pointed at (via `--endpoint` / `EVIDENT_API_URL`,\n * else production). Pass `--all` to clear every stored session.\n */\nexport async function logout(options: LogoutOptions = {}): Promise<void> {\n if (options.all) {\n await deleteToken({ all: true });\n printSuccess('Logged out of all endpoints.');\n return;\n }\n\n const credentials = await getToken();\n\n if (!credentials) {\n printWarning(`You are not logged in to ${getApiUrlConfig()}.`);\n return;\n }\n\n await deleteToken();\n printSuccess(`Logged out of ${getApiUrlConfig()}.`);\n}\n","/**\n * Whoami Command\n *\n * Displays the currently logged in user.\n */\n\nimport chalk from 'chalk';\nimport { getToken } from '../lib/keychain.js';\nimport { getApiUrlConfig } from '../lib/config.js';\nimport { printError, keyValue, blank } from '../utils/ui.js';\n\n/**\n * Whoami command handler.\n *\n * Sessions are stored per endpoint, so this reports the identity for the\n * endpoint the command is pointed at (via `--endpoint` / `EVIDENT_API_URL`,\n * else production).\n */\nexport async function whoami(): Promise<void> {\n const apiUrl = getApiUrlConfig();\n const credentials = await getToken();\n\n if (!credentials) {\n printError(`Not logged in to ${apiUrl}. Run the \\`login\\` command to authenticate.`);\n process.exit(1);\n }\n\n blank();\n console.log(keyValue('Endpoint', apiUrl));\n console.log(keyValue('User', chalk.bold(credentials.user.email)));\n console.log(keyValue('User ID', credentials.user.id));\n\n if (credentials.expiresAt) {\n const expiresAt = new Date(credentials.expiresAt);\n const now = new Date();\n\n if (expiresAt < now) {\n console.log(keyValue('Status', chalk.red('Token expired')));\n } else {\n const daysRemaining = Math.ceil(\n (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),\n );\n console.log(keyValue('Expires', `${daysRemaining} days`));\n }\n }\n\n blank();\n}\n","/**\n * Run Command (thinned — WI-THIN-1, ADR-0039)\n *\n * `evident run` is now a thin control-plane attach point:\n * authenticate → resolve agent → ensure `opencode serve` on loopback\n * → connect the streaming tunnel (which transparently proxies ALL web\n * traffic: HTML, JS bundle, /session, /event SSE)\n * → start the ChannelDriver loop (drive Slack/WhatsApp-originated messages,\n * detect completion, deliver replies + surface questions/permissions).\n *\n * Removed in this rewrite (now obsolete):\n * - conversation locks (acquire/extend/release + heartbeat) — one long-lived\n * `opencode serve` per run; no multi-runner serialization needed;\n * - idle-timeout-for-lock-release;\n * - the `/question` + `/permission` polling loops in run.ts — interactions\n * now flow through the ChannelDriver's interactive-event callback;\n * - the web-path queue processing — web traffic is the live streaming proxy,\n * the CLI does not poll/forward it anymore.\n *\n * Usage:\n * evident run --agent <id> # Interactive mode\n * evident run --agent <id> --conversation <id> # Drive a single conversation\n * evident run --agent <id> --idle-timeout 30 # Exit after 30s idle (CI)\n */\n\nimport { ChildProcess } from 'child_process';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { select } from '@inquirer/prompts';\nimport { getApiUrlConfig, getCliName } from '../lib/config.js';\nimport { printError, blank } from '../utils/ui.js';\nimport {\n telemetry,\n EventTypes,\n shutdownTelemetry,\n emitAgentConnected,\n emitAgentDisconnected,\n} from '../lib/telemetry.js';\nimport {\n getAuthCredentials,\n getAuthHeader,\n isInteractive,\n getToken,\n type AuthCredentials,\n} from '../lib/auth.js';\nimport { stopOpenCode } from '../lib/opencode/index.js';\nimport { RunnerConnection } from '../lib/tunnel/index.js';\nimport { ChannelDriver, ChannelAuthError } from '../lib/channels/driver.js';\nimport { ensureOpenCodeRunning } from './ensure-opencode.js';\nimport { resolveAgentIdFromKey, getAgentInfo } from './agent-lookup.js';\nimport { login } from './login.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RunOptions {\n agent?: string;\n port?: number;\n verbose?: boolean;\n conversation?: string;\n idleTimeout?: number;\n json?: boolean;\n}\n\ninterface ActivityLogEntry {\n timestamp: Date;\n type: 'error' | 'info';\n error?: string;\n message?: string;\n}\n\ninterface RunState {\n agentId: string;\n agentName: string | null;\n port: number;\n conversationFilter: string | null;\n idleTimeout: number | null;\n json: boolean;\n interactive: boolean;\n\n // Connection state\n connected: boolean;\n opencodeConnected: boolean;\n opencodeVersion: string | null;\n\n // Process management\n opencodeProcess: ChildProcess | null;\n connection: RunnerConnection | null;\n running: boolean;\n\n // Recent activity (for the minimal status line)\n activityLog: ActivityLogEntry[];\n\n // Channel driving\n messageCount: number;\n\n // Authentication (mutable — updated on re-auth)\n authHeader: string;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst MAX_ACTIVITY_LOG_ENTRIES = 10;\n/**\n * How often the ChannelDriver polls for pending channel messages. Overridable\n * via `EVIDENT_CHANNEL_POLL_INTERVAL_MS` (tests set it low for determinism).\n */\nconst CHANNEL_POLL_INTERVAL_MS = Number(process.env.EVIDENT_CHANNEL_POLL_INTERVAL_MS) || 2000;\n\n// ============================================================================\n// Logging\n// ============================================================================\n\nfunction log(state: RunState, message: string, isError = false): void {\n if (state.json) {\n console.log(\n JSON.stringify({\n timestamp: new Date().toISOString(),\n level: isError ? 'error' : 'info',\n message,\n }),\n );\n } else if (!state.interactive) {\n // Non-interactive, non-JSON: simple output\n const prefix = isError ? chalk.red('✗') : chalk.green('•');\n console.log(`${prefix} ${message}`);\n }\n // In interactive mode, we use the activity log instead\n}\n\nfunction logActivity(state: RunState, entry: Omit<ActivityLogEntry, 'timestamp'>): void {\n const fullEntry: ActivityLogEntry = {\n ...entry,\n timestamp: new Date(),\n };\n\n state.activityLog.push(fullEntry);\n\n if (state.activityLog.length > MAX_ACTIVITY_LOG_ENTRIES) {\n state.activityLog.shift();\n }\n\n // In non-interactive mode, also log to console immediately\n if (!state.interactive) {\n if (entry.type === 'error') {\n log(state, entry.error ?? 'Unknown error', true);\n } else if (entry.type === 'info' && entry.message) {\n log(state, entry.message);\n }\n }\n}\n\n// ============================================================================\n// Display (Interactive Mode)\n// ============================================================================\n\n/**\n * Minimal interactive status line. Each call prints the current tunnel /\n * opencode state plus the most recent activity entry. We deliberately avoid the\n * full-screen ANSI redraw the old runner used — a thin append-only status is\n * sufficient now that web traffic is rendered in the proxied opencode web UI.\n */\nfunction displayStatus(state: RunState): void {\n if (!state.interactive) return;\n\n const attempt = state.connection?.reconnectAttempt ?? 0;\n const tunnel = state.connected\n ? chalk.green('tunnel: connected')\n : attempt > 0\n ? chalk.yellow(`tunnel: reconnecting (#${attempt})`)\n : chalk.yellow('tunnel: connecting');\n const opencode = state.opencodeConnected\n ? chalk.green(`opencode: :${state.port}`)\n : chalk.red(`opencode: :${state.port} (down)`);\n const messages = state.messageCount > 0 ? chalk.dim(` · ${state.messageCount} processed`) : '';\n\n const last = state.activityLog[state.activityLog.length - 1];\n const detail = last\n ? chalk.dim(` · ${last.type === 'error' ? (last.error ?? '') : (last.message ?? '')}`)\n : '';\n\n const agent = state.agentName ?? state.agentId;\n console.log(\n `${chalk.bold('Evident')} ${chalk.dim(agent)} ${tunnel} ${opencode}${messages}${detail}`,\n );\n}\n\n// ============================================================================\n// Authentication Helpers\n// ============================================================================\n\nasync function promptForLogin(\n promptMessage: string,\n successMessage: string,\n): Promise<AuthCredentials> {\n const action = await select({\n message: promptMessage,\n choices: [\n {\n name: 'Yes, log me in',\n value: 'login',\n description: 'Opens a browser to authenticate with Evident',\n },\n {\n name: 'No, exit',\n value: 'exit',\n description: 'Exit without logging in',\n },\n ],\n });\n\n if (action === 'exit') {\n console.log(chalk.dim(`\\nYou can log in later by running: ${getCliName()} login`));\n process.exit(0);\n }\n\n await login({ noBrowser: false });\n\n const credentials = await getToken();\n if (!credentials) {\n printError('Login failed. Please try again.');\n process.exit(1);\n }\n\n blank();\n console.log(chalk.green(successMessage));\n blank();\n\n return { token: credentials.token, authType: 'bearer', user: credentials.user };\n}\n\n// ============================================================================\n// Channel driving\n// ============================================================================\n\n/** Exit code for authentication expiration in non-interactive mode */\nconst AUTH_EXPIRED_EXIT_CODE = 77;\n\n/**\n * Result of handling an authentication error\n */\ninterface AuthErrorResult {\n /** Whether authentication was successfully refreshed */\n success: boolean;\n /** New auth header if re-authenticated */\n newAuthHeader?: string;\n}\n\n/**\n * Handle authentication errors during channel driving.\n * In interactive mode, prompts for re-authentication.\n * In non-interactive mode, exits with a specific exit code.\n */\nasync function handleAuthError(state: RunState, error: ChannelAuthError): Promise<AuthErrorResult> {\n logActivity(state, {\n type: 'error',\n error: error.message,\n });\n if (state.interactive) displayStatus(state);\n\n if (!state.interactive) {\n // Non-interactive mode: log clear message and exit\n blank();\n console.log(chalk.red('Authentication expired'));\n console.log(chalk.dim('Your authentication token is no longer valid.'));\n blank();\n console.log(chalk.dim('To fix this:'));\n console.log(chalk.dim(` 1. Run '${getCliName()} login' to re-authenticate`));\n console.log(chalk.dim(' 2. Restart this command'));\n blank();\n await cleanup(state);\n await shutdownTelemetry();\n process.exit(AUTH_EXPIRED_EXIT_CODE);\n // Return to prevent fallthrough when process.exit is mocked in tests\n return { success: false };\n }\n\n // Interactive mode: prompt for re-authentication\n blank();\n console.log(chalk.yellow('Your authentication has expired.'));\n blank();\n\n try {\n const credentials = await promptForLogin(\n 'Would you like to log in again?',\n 'Re-authenticated successfully! Resuming...',\n );\n\n const newAuthHeader = getAuthHeader(credentials);\n return { success: true, newAuthHeader };\n } catch {\n // User declined to re-authenticate or login failed\n return { success: false };\n }\n}\n\n/**\n * Drive channel-originated messages (Slack/WhatsApp) through the ChannelDriver.\n *\n * Web traffic does NOT flow through here — it is transparently proxied by the\n * streaming tunnel. This loop only polls the server-side offline queue and\n * delegates each pending conversation to the driver, which sends the message to\n * loopback opencode, detects completion, and delivers the reply / surfaces any\n * question or permission via the existing combinedAuth thread routes.\n *\n * `drainPending()` on the driver is also invoked on tunnel (re)connect (WI-CHAN-4).\n */\nasync function driveChannels(state: RunState, driver: ChannelDriver): Promise<void> {\n // Number of consecutive poll cycles with no work — drives idle-timeout exit.\n let idlePolls = 0;\n\n while (state.running) {\n // Wait for any ongoing reconnection to complete before polling.\n if (state.connection?.reconnecting && state.connection.reconnectPromise) {\n logActivity(state, { type: 'info', message: 'Waiting for tunnel reconnection...' });\n if (state.interactive) displayStatus(state);\n await state.connection.reconnectPromise;\n }\n\n try {\n const processed = await driver.drainPending();\n state.messageCount += processed;\n\n if (processed > 0) {\n idlePolls = 0;\n if (state.interactive) displayStatus(state);\n } else if (state.idleTimeout !== null) {\n idlePolls++;\n if (idlePolls === 1) {\n logActivity(state, {\n type: 'info',\n message: `Queue empty, waiting (timeout: ${state.idleTimeout}s)...`,\n });\n if (state.interactive) displayStatus(state);\n }\n }\n } catch (error) {\n if (error instanceof ChannelAuthError) {\n const result = await handleAuthError(state, error);\n if (result.success && result.newAuthHeader) {\n state.authHeader = result.newAuthHeader;\n logActivity(state, { type: 'info', message: 'Continuing with new credentials...' });\n if (state.interactive) displayStatus(state);\n continue;\n }\n state.running = false;\n break;\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error);\n logActivity(state, { type: 'error', error: `Channel processing error: ${errorMessage}` });\n if (state.interactive) displayStatus(state);\n }\n\n // Sleep between polls. The idle check runs after the sleep so a message\n // arriving just before the timeout still gets one more poll cycle.\n await new Promise((resolve) => setTimeout(resolve, CHANNEL_POLL_INTERVAL_MS));\n\n if (state.idleTimeout !== null && idlePolls >= 2) {\n const idleMs = idlePolls * CHANNEL_POLL_INTERVAL_MS;\n if (idleMs > state.idleTimeout * 1000) {\n logActivity(state, { type: 'info', message: 'Idle timeout reached' });\n if (state.interactive) displayStatus(state);\n break;\n }\n }\n }\n}\n\n// ============================================================================\n// Cleanup\n// ============================================================================\n\nasync function cleanup(state: RunState): Promise<void> {\n state.running = false;\n\n if (state.connection) {\n state.connection.close();\n state.connection = null;\n }\n\n if (state.opencodeProcess) {\n stopOpenCode(state.opencodeProcess);\n if (state.interactive) {\n logActivity(state, { type: 'info', message: 'Stopped OpenCode process' });\n displayStatus(state);\n } else {\n log(state, 'Stopped OpenCode process');\n }\n state.opencodeProcess = null;\n }\n}\n\n// ============================================================================\n// Main Command Handler\n// ============================================================================\n\nexport async function run(options: RunOptions): Promise<void> {\n const interactive = isInteractive(options.json);\n\n // Initialize state\n const state: RunState = {\n agentId: options.agent || '',\n agentName: null,\n port: options.port ?? 4096,\n conversationFilter: options.conversation ?? null,\n idleTimeout: options.idleTimeout ?? null,\n json: options.json ?? false,\n interactive,\n\n connected: false,\n opencodeConnected: false,\n opencodeVersion: null,\n\n opencodeProcess: null,\n connection: null,\n running: true,\n\n activityLog: [],\n\n messageCount: 0,\n\n authHeader: '',\n };\n\n if (state.idleTimeout === null && (process.env.GITHUB_ACTIONS || process.env.CI)) {\n log(\n state,\n 'Warning: No --idle-timeout set in CI environment. The runner will poll indefinitely until the job times out. Consider adding --idle-timeout 30 to avoid wasting runner minutes.',\n false,\n );\n }\n\n // Set up cleanup handlers (SIGINT/SIGTERM): stop opencode + close tunnel.\n const handleSignal = async () => {\n if (state.interactive) {\n logActivity(state, { type: 'info', message: 'Shutting down...' });\n displayStatus(state);\n } else {\n log(state, 'Shutting down...');\n }\n await cleanup(state);\n await shutdownTelemetry();\n process.exit(0);\n };\n\n process.on('SIGINT', handleSignal);\n process.on('SIGTERM', handleSignal);\n\n try {\n // Step 1: Authenticate\n let credentials = await getAuthCredentials();\n\n if (!credentials) {\n if (!interactive) {\n printError('Authentication required');\n blank();\n console.log(chalk.dim('Set EVIDENT_AGENT_KEY environment variable for CI'));\n console.log(chalk.dim('Or run `evident login` for interactive authentication'));\n blank();\n process.exit(1);\n }\n\n blank();\n console.log(chalk.yellow('You are not logged in to Evident.'));\n blank();\n\n credentials = await promptForLogin(\n 'Would you like to log in now?',\n 'Login successful! Continuing...',\n );\n }\n\n state.authHeader = getAuthHeader(credentials);\n\n // Resolve agent ID from key if not provided explicitly\n if (!state.agentId) {\n if (credentials.authType === 'agent_key') {\n const resolved = await resolveAgentIdFromKey(state.authHeader);\n if (resolved.agent_id) {\n state.agentId = resolved.agent_id;\n log(state, `Resolved agent ID from key: ${state.agentId}`);\n // In interactive mode, log() is a no-op — surface the resolution visibly.\n if (state.interactive && !state.json) {\n logActivity(state, {\n type: 'info',\n message: `Agent ID resolved from key: ${state.agentId}`,\n });\n }\n } else {\n printError(resolved.error || 'Failed to resolve agent ID from key');\n process.exit(1);\n }\n } else {\n printError('--agent is required when not using EVIDENT_AGENT_KEY');\n blank();\n console.log(chalk.dim('Either provide --agent <id> or set EVIDENT_AGENT_KEY'));\n blank();\n process.exit(1);\n }\n }\n\n telemetry.info(\n EventTypes.CLI_COMMAND,\n 'Starting run command',\n {\n command: 'run',\n agentId: state.agentId,\n port: state.port,\n conversationFilter: state.conversationFilter,\n interactive,\n },\n state.agentId,\n );\n\n // Step 2: Validate agent\n if (interactive && !state.json) {\n blank();\n console.log(chalk.bold('Evident Run'));\n console.log(chalk.dim('-'.repeat(40)));\n }\n\n const spinner = interactive && !state.json ? ora('Validating agent...').start() : null;\n let validation = await getAgentInfo(state.agentId, state.authHeader);\n\n if (!validation.valid && validation.authFailed && interactive) {\n spinner?.fail('Authentication failed');\n blank();\n console.log(chalk.yellow('Your authentication token is invalid or expired.'));\n blank();\n\n credentials = await promptForLogin(\n 'Would you like to log in again?',\n 'Login successful! Retrying...',\n );\n\n state.authHeader = getAuthHeader(credentials);\n spinner?.start('Validating agent...');\n validation = await getAgentInfo(state.agentId, state.authHeader);\n }\n\n if (!validation.valid) {\n spinner?.fail(`Agent validation failed: ${validation.error}`);\n throw new Error(validation.error);\n }\n\n spinner?.succeed(`Agent: ${validation.agent!.name || state.agentId}`);\n state.agentName = validation.agent!.name;\n\n // Step 3: Ensure OpenCode is running (loopback only — RUN-1)\n const ocSpinner = interactive && !state.json ? ora('Checking OpenCode...').start() : null;\n\n try {\n const oc = await ensureOpenCodeRunning({\n port: state.port,\n interactive: state.interactive,\n agentId: state.agentId,\n log: (message) => log(state, message),\n });\n state.port = oc.port;\n state.opencodeProcess = oc.process;\n state.opencodeVersion = oc.version;\n state.opencodeConnected = oc.process !== null || oc.version !== null;\n const version = state.opencodeVersion ? ` (v${state.opencodeVersion})` : '';\n ocSpinner?.succeed(`OpenCode running on port ${state.port}${version}`);\n } catch (error) {\n ocSpinner?.fail((error as Error).message);\n throw error;\n }\n\n // Step 4: Connect tunnel (streaming forward handles ALL web traffic).\n // The channel driver owns Slack/WhatsApp message driving + completion\n // callbacks; web traffic is transparently proxied by the tunnel.\n const tunnelSpinner = interactive && !state.json ? ora('Connecting tunnel...').start() : null;\n\n const channelDriver = new ChannelDriver({\n agentId: state.agentId,\n port: state.port,\n apiUrl: getApiUrlConfig(),\n getAuthHeader: () => state.authHeader,\n conversationFilter: state.conversationFilter,\n log: (entry) =>\n logActivity(state, {\n type: entry.level === 'error' ? 'error' : 'info',\n message: entry.message,\n error: entry.level === 'error' ? entry.message : undefined,\n }),\n });\n\n const connection = new RunnerConnection({\n agentId: state.agentId,\n getAuthHeader: () => state.authHeader,\n port: state.port,\n isRunning: () => state.running,\n events: {\n onConnected: (agentId, isReconnect) => {\n state.connected = true;\n state.agentId = agentId;\n logActivity(state, {\n type: 'info',\n message: `Tunnel ${isReconnect ? 'reconnected' : 'connected'} (agent: ${agentId})`,\n });\n emitAgentConnected(state.agentId, { port: state.port });\n if (!isReconnect) tunnelSpinner?.succeed('Tunnel connected');\n if (state.interactive) displayStatus(state);\n // On (re)connect, immediately drain the server-side offline queue\n // (WI-CHAN-4). Best-effort — the steady-state poll loop also drains —\n // but surface failures: silently swallowing them here is exactly why a\n // queued message can look like it \"never ran\" with no clue as to why.\n channelDriver\n .drainPending()\n .then((processed) => {\n if (processed > 0) {\n state.messageCount += processed;\n logActivity(state, {\n type: 'info',\n message: `Drained ${processed} queued message(s) on connect`,\n });\n if (state.interactive) displayStatus(state);\n }\n })\n .catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n logActivity(state, {\n type: 'error',\n error: `Failed to drain queued messages on connect: ${message}`,\n });\n if (state.interactive) displayStatus(state);\n });\n },\n onDisconnected: (code, reason) => {\n state.connected = false;\n logActivity(state, {\n type: 'info',\n message: `Tunnel disconnected (code: ${code}, reason: ${reason})`,\n });\n emitAgentDisconnected(state.agentId, { code, reason });\n if (state.interactive) displayStatus(state);\n },\n onError: (error) => {\n logActivity(state, { type: 'error', error });\n if (state.interactive) displayStatus(state);\n },\n // Web traffic is proxied transparently; only note opencode is live.\n onResponse: () => {\n state.opencodeConnected = true;\n },\n // A channel message was queued and the api-worker pinged us over the\n // tunnel to drain immediately instead of waiting for the next poll tick.\n // Best-effort + non-fatal: mirror the on-connect drain block. A failed\n // drain here is logged and swallowed — the steady-state poll retries, so\n // a lost/failed ping can never orphan a message (§2 invariant).\n onDrainPing: () => {\n if (!state.running) return;\n logActivity(state, { type: 'info', message: 'Drain ping received — draining' });\n channelDriver\n .drainPending()\n .then((processed) => {\n if (processed > 0) {\n state.messageCount += processed;\n logActivity(state, {\n type: 'info',\n message: `Drained ${processed} queued message(s) on ping`,\n });\n if (state.interactive) displayStatus(state);\n }\n })\n .catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n logActivity(state, {\n type: 'error',\n error: `Failed to drain queued messages on ping: ${message}`,\n });\n if (state.interactive) displayStatus(state);\n });\n },\n onInfo: (message) => logActivity(state, { type: 'info', message }),\n },\n });\n state.connection = connection;\n\n try {\n await connection.connect();\n } catch (error) {\n if ((error as Error).message === 'Unauthorized') tunnelSpinner?.fail('Unauthorized');\n throw error;\n }\n\n // Step 5: Drive channel messages.\n // Note: in interactive mode the `onConnected` handler has already rendered\n // the status line by the time `connect()` resolves, so we must NOT call\n // `displayStatus` again here — doing so prints the same status line twice.\n if (!interactive || state.json) {\n log(state, 'Driving channel messages...');\n }\n\n await driveChannels(state, channelDriver);\n\n // Done\n await cleanup(state);\n\n if (state.json) {\n console.log(\n JSON.stringify({\n status: 'success',\n messages_processed: state.messageCount,\n }),\n );\n } else if (!interactive) {\n log(state, `Completed. Processed ${state.messageCount} message(s).`);\n }\n\n await shutdownTelemetry();\n process.exit(0);\n } catch (error) {\n await cleanup(state);\n\n const message = error instanceof Error ? error.message : String(error);\n\n if (state.json) {\n console.log(JSON.stringify({ status: 'error', error: message }));\n } else {\n printError(message);\n }\n\n telemetry.error(EventTypes.CLI_ERROR, `Run command failed: ${message}`, {\n command: 'run',\n agentId: options.agent,\n });\n await shutdownTelemetry();\n process.exit(1);\n }\n}\n","/**\n * Telemetry API types - shared between CLI and API\n *\n * These types define the contract for the telemetry endpoint.\n * Both CLI and API should use these types to ensure type safety.\n */\n\nimport { EventSeverity } from '../events/index.js';\n\n/** Client types that can send telemetry */\nexport type TelemetryClientType = 'cli' | 'sdk' | 'web';\n\n// ============================================================================\n// Event type constants\n// ============================================================================\n\nexport const TelemetryEventTypes = {\n // Agent activity events (shown in web UI activity log)\n AGENT_CONNECTED: 'agent.connected',\n AGENT_DISCONNECTED: 'agent.disconnected',\n AGENT_MESSAGE_PROCESSING: 'agent.message_processing',\n AGENT_MESSAGE_DONE: 'agent.message_done',\n AGENT_MESSAGE_FAILED: 'agent.message_failed',\n} as const;\n\nexport type TelemetryEventType = (typeof TelemetryEventTypes)[keyof typeof TelemetryEventTypes];\n\n// ============================================================================\n// Specific event types with typed metadata\n// ============================================================================\n\n/** Agent connected to Evident */\nexport interface AgentConnectedEvent {\n event_type: typeof TelemetryEventTypes.AGENT_CONNECTED;\n severity?: EventSeverity;\n message?: string;\n metadata: { port: number };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Agent disconnected from Evident */\nexport interface AgentDisconnectedEvent {\n event_type: typeof TelemetryEventTypes.AGENT_DISCONNECTED;\n severity?: EventSeverity;\n message?: string;\n metadata: { code: number; reason: string };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Agent started processing a message */\nexport interface AgentMessageProcessingEvent {\n event_type: typeof TelemetryEventTypes.AGENT_MESSAGE_PROCESSING;\n severity?: EventSeverity;\n message?: string;\n metadata: { message_id: string; conversation_id: string };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Agent finished processing a message successfully */\nexport interface AgentMessageDoneEvent {\n event_type: typeof TelemetryEventTypes.AGENT_MESSAGE_DONE;\n severity?: EventSeverity;\n message?: string;\n metadata: { message_id: string; conversation_id: string };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Agent failed to process a message */\nexport interface AgentMessageFailedEvent {\n event_type: typeof TelemetryEventTypes.AGENT_MESSAGE_FAILED;\n severity?: EventSeverity;\n message?: string;\n metadata: { message_id: string; conversation_id: string; reason?: string; error?: string };\n agent_id: string;\n timestamp?: string;\n}\n\n/** Union of all specific telemetry events */\nexport type TelemetryEvent =\n | AgentConnectedEvent\n | AgentDisconnectedEvent\n | AgentMessageProcessingEvent\n | AgentMessageDoneEvent\n | AgentMessageFailedEvent;\n\n// ============================================================================\n// Generic event type (for API validation - accepts any event)\n// ============================================================================\n\n/**\n * Generic telemetry event request (used by API for validation).\n * Clients should use specific event types above for type safety.\n */\nexport interface TelemetryEventRequest {\n event_type: string;\n severity?: EventSeverity;\n message?: string;\n metadata?: Record<string, unknown>;\n agent_id?: string;\n timestamp?: string;\n}\n\n// ============================================================================\n// Request/Response types\n// ============================================================================\n\n/**\n * Batch request to submit multiple telemetry events\n */\nexport interface SubmitTelemetryEventsRequest {\n events: TelemetryEventRequest[];\n client_type: TelemetryClientType;\n client_version?: string;\n}\n\n/**\n * Response from submitting telemetry events\n */\nexport interface SubmitTelemetryEventsResponse {\n received: number;\n}\n\n// ============================================================================\n// Server-side event type constants\n// ============================================================================\n\n/**\n * Event type strings for server-originated activity log entries.\n * Used by ActivityLogService in the API Worker.\n */\nexport const ServerEventTypes = {\n // GitHub Actions\n GHA_WORKFLOW_DISPATCHING: 'github_actions.workflow.dispatching',\n GHA_WORKFLOW_DISPATCHED: 'github_actions.workflow.dispatched',\n GHA_WORKFLOW_DISPATCH_FAILED: 'github_actions.workflow.dispatch_failed',\n GHA_WORKFLOW_FAILED: 'github_actions.workflow.failed',\n GHA_WORKFLOW_CANCELLED: 'github_actions.workflow.cancelled',\n GHA_WORKFLOW_TIMED_OUT: 'github_actions.workflow.timed_out',\n GHA_RUNNER_TIMEOUT: 'github_actions.runner.timeout',\n\n // Conversation lifecycle\n CONVERSATION_CREATED: 'conversation.created',\n CONVERSATION_MESSAGE_QUEUED: 'conversation.message.queued',\n CONVERSATION_MESSAGE_PROCESSING: 'conversation.message.processing',\n CONVERSATION_MESSAGE_COMPLETED: 'conversation.message.completed',\n CONVERSATION_MESSAGE_FAILED: 'conversation.message.failed',\n CONVERSATION_RUNNER_STARTED: 'conversation.runner.started',\n CONVERSATION_RUNNER_FAILED: 'conversation.runner.failed',\n CONVERSATION_RUNNER_CONNECTED: 'conversation.runner.connected',\n\n // Slack\n SLACK_MESSAGE_RECEIVED: 'slack.message.received',\n SLACK_MESSAGE_QUEUED: 'slack.message.queued',\n SLACK_MESSAGE_FORWARDED: 'slack.message.forwarded',\n SLACK_DRAIN_PING_SENT: 'slack.drain.ping_sent',\n SLACK_USER_NOT_CONFIGURED: 'slack.user.not_configured',\n SLACK_WORKSPACE_NOT_FOUND: 'slack.workspace.not_found',\n SLACK_QUESTION_ANSWERED: 'slack.question.answered',\n SLACK_PERMISSION_RESPONDED: 'slack.permission.responded',\n SLACK_NOTIFICATION_DELIVERED: 'slack.notification.delivered',\n SLACK_NOTIFICATION_FAILED: 'slack.notification.failed',\n\n // Tunnel\n TUNNEL_CONNECTED: 'tunnel.connected',\n TUNNEL_DISCONNECTED: 'tunnel.disconnected',\n\n // Cron\n CRON_STUCK_MESSAGES_RESET: 'cron.stuck_messages.reset',\n CRON_STUCK_RUNNER_FAILED: 'cron.stuck_runner.failed',\n CRON_LOCKS_REAPED: 'cron.locks.reaped',\n} as const;\n\n/** Union of all server-side event type strings. */\nexport type ServerEventType = (typeof ServerEventTypes)[keyof typeof ServerEventTypes];\n","/**\n * Tunnel types - shared between API, Tunnel Relay (Cloudflare Worker), and CLI\n *\n * These types define the protocol for the WebSocket tunnel that connects\n * local OpenCode instances to the Evident platform.\n */\n\n// ============================================================================\n// API <-> Relay communication\n// ============================================================================\n\n/**\n * Token validation response from API to Relay\n * Sent when CLI connects and Relay validates the token with the API\n */\nexport interface TunnelTokenValidationResponse {\n agent_id: string;\n user_id: string;\n}\n\n/**\n * Token validation request from Relay to API\n */\nexport interface TunnelTokenValidationRequest {\n token: string;\n agent_id: string;\n auth_type: 'bearer' | 'sandbox_key';\n}\n\n/**\n * Status update from Relay to API\n * Sent when CLI connects or disconnects\n */\nexport interface TunnelStatusUpdate {\n agent_id: string;\n status: 'connected' | 'disconnected';\n close_code?: number;\n close_reason?: string;\n}\n\n/**\n * Internal request from API to forward to tunnel\n */\nexport interface TunnelForwardRequest {\n request_id: string;\n method: string;\n path: string;\n headers?: Record<string, string>;\n body?: unknown;\n timeout_ms?: number;\n}\n\n// ============================================================================\n// Relay <-> CLI communication (WebSocket messages)\n// ============================================================================\n\n/**\n * Lightweight control messages from Relay to CLI.\n *\n * The buffered request/response variants were removed with the chunked protocol\n * (superseded by ADR-0039); HTTP traffic now flows over the streaming frame\n * protocol below (`StreamFrameToAgent` / `StreamFrameToEdge`). Only connection\n * lifecycle and heartbeat control messages remain.\n */\nexport type RelayToCLIMessage =\n | { type: 'connected'; agent_id: string }\n | { type: 'error'; code: string; message: string }\n | { type: 'ping' };\n\n/**\n * Lightweight control messages from CLI to Relay.\n *\n * The buffered/chunked response variants and the separate event-subscription\n * variants were removed with the chunked protocol (superseded by ADR-0039);\n * responses now flow over the streaming frame protocol below\n * (`StreamFrameToEdge`). Only heartbeat and client status remain.\n */\nexport type CLIToRelayMessage = { type: 'pong' } | { type: 'status'; status: 'ready' | 'busy' };\n\n// ============================================================================\n// Streaming frame protocol (ADR-0039) — multiplexed by `sid`\n// ============================================================================\n//\n// Replaces the buffer-and-resolve-once request/response model and the base64\n// chunk protocol (ADR-0027) with a multiplexed, streaming frame protocol over\n// the single per-agent WebSocket. Ported from `scripts/poc/tunnel-streaming-proxy.mjs`.\n//\n// Frame SHAPE and streaming SEMANTICS are taken verbatim from the PoC, but the\n// type names follow this repo's convention: a `type` discriminator (not the\n// PoC's `t:`) and snake_case fields (`has_body`, not the PoC's camelCase\n// `hasBody`) — matching `RelayToCLIMessage` / `CLIToRelayMessage` above.\n//\n// Each logical HTTP request/response is a stream identified by `sid`. Bodies are\n// never buffered whole: they are emitted as many small `req_data` / `res_data`\n// frames as bytes arrive (each base64-encoded in `b64`), respecting the\n// Cloudflare ~1MB WS-frame limit (see `MAX_FRAME_BYTES`). An infinite SSE\n// response is simply a stream that never sends `res_end`.\n\n/**\n * Headers carried on streaming frames.\n *\n * Matches the existing tunnel convention (`TunnelForwardRequest.headers`) — a\n * flat record of lowercased header name → value.\n */\nexport type StreamFrameHeaders = Record<string, string>;\n\n/**\n * Frames sent from the edge (Worker + relay DO) to the agent (CLI on the laptop).\n *\n * Multiplexed by `sid`; ported from the PoC's EDGE → AGENT frames.\n */\nexport type StreamFrameToAgent =\n | {\n type: 'open';\n sid: string;\n method: string;\n path: string;\n headers: StreamFrameHeaders;\n has_body: boolean;\n }\n | { type: 'req_data'; sid: string; b64: string }\n | { type: 'req_end'; sid: string }\n | { type: 'abort'; sid: string };\n\n/**\n * Frames sent from the agent (CLI on the laptop) to the edge (Worker + relay DO).\n *\n * Multiplexed by `sid`; ported from the PoC's AGENT → EDGE frames.\n */\nexport type StreamFrameToEdge =\n | { type: 'head'; sid: string; status: number; headers: StreamFrameHeaders }\n | { type: 'res_data'; sid: string; b64: string }\n | { type: 'res_end'; sid: string }\n | { type: 'res_err'; sid: string; message: string };\n\n/**\n * Maximum size (bytes) of a single streaming frame's decoded payload.\n *\n * Models the Cloudflare ~1MB WS-message limit with comfortable headroom for\n * base64 expansion (~33%). Bodies larger than this are split across multiple\n * `req_data` / `res_data` frames; a whole-body buffer is never required.\n */\nexport const MAX_FRAME_BYTES = 256 * 1024;\n\n/**\n * Reserved internal control path for the channel-message drain ping.\n *\n * When a channel (Slack) message is queued for a `connected` local agent, the\n * api-worker issues a best-effort `POST` to this path over the existing tunnel\n * `/forward` plumbing. The CLI's `StreamForwarder.handleOpen` intercepts this\n * path BEFORE it would fetch loopback opencode and instead triggers an\n * immediate, idempotent `drainPending()` — cutting latency vs. waiting for the\n * next steady-state poll tick.\n *\n * This is a **latency optimization only** (see the §2 invariant in\n * `docs/plans/slack-queue-always-ping-tasks.md`): a lost, delayed, or failed\n * ping NEVER orphans a message or changes its status/reaction. The steady-state\n * poll and the drain-on-(re)connect remain the correctness guarantee, so the\n * ping is removable without breaking delivery.\n *\n * The `/__evident/` prefix is reserved by Evident — opencode has no such\n * namespace, so the intercept match is unambiguous.\n */\nexport const TUNNEL_DRAIN_PING_PATH = '/__evident/drain';\n\n// ============================================================================\n// Internal Relay state\n// ============================================================================\n\n/**\n * Tunnel connection metadata stored in the Durable Object\n */\nexport interface TunnelMetadata {\n agent_id: string;\n user_id: string;\n connected_at: string;\n last_activity: string;\n}\n\n// Note: TunnelStatus is defined in agents/index.ts as 'connected' | 'disconnected' | null\n// We re-use that type for tunnel operations\n","/**\n * CLI Telemetry Client\n *\n * Captures and reports events to the Evident API for debugging and observability.\n * Events are batched and sent periodically to minimize network overhead.\n */\n\nimport {\n TelemetryEventRequest,\n SubmitTelemetryEventsRequest,\n TelemetryEventTypes,\n TelemetryEvent,\n AgentConnectedEvent,\n AgentDisconnectedEvent,\n AgentMessageProcessingEvent,\n AgentMessageDoneEvent,\n AgentMessageFailedEvent,\n EventSeverity,\n} from '@evident/types';\nimport { getApiUrlConfig } from './config.js';\nimport { getToken } from './keychain.js';\n\n// Get version from package.json at build time\nconst CLI_VERSION = process.env.npm_package_version || 'unknown';\n\n// Re-export for convenience\nexport type { EventSeverity } from '@evident/types';\n// Re-export event types from shared package\nexport { TelemetryEventTypes } from '@evident/types';\n\n// Event buffer for batching\nlet eventBuffer: TelemetryEventRequest[] = [];\nlet flushTimeout: NodeJS.Timeout | null = null;\nlet isShuttingDown = false;\n\n// Configuration\nconst FLUSH_INTERVAL_MS = 5000; // Flush every 5 seconds\nconst MAX_BUFFER_SIZE = 50; // Flush when buffer reaches this size\nconst FLUSH_TIMEOUT_MS = 3000; // Timeout for flush requests\n\n/**\n * Log a telemetry event\n * Events are buffered and sent in batches\n */\nexport function logEvent(\n eventType: string,\n options: {\n severity?: EventSeverity;\n message?: string;\n metadata?: Record<string, unknown>;\n agentId?: string;\n } = {},\n): void {\n const event: TelemetryEventRequest = {\n event_type: eventType,\n severity: options.severity || 'info',\n message: options.message,\n metadata: options.metadata,\n agent_id: options.agentId,\n timestamp: new Date().toISOString(),\n };\n\n eventBuffer.push(event);\n\n // Flush immediately for errors or if buffer is full\n if (options.severity === 'error' || eventBuffer.length >= MAX_BUFFER_SIZE) {\n void flushEvents();\n } else if (!flushTimeout && !isShuttingDown) {\n // Schedule a flush\n flushTimeout = setTimeout(() => {\n flushTimeout = null;\n void flushEvents();\n }, FLUSH_INTERVAL_MS);\n }\n}\n\n/**\n * Convenience methods for different severity levels\n */\nexport const telemetry = {\n debug: (\n eventType: string,\n message?: string,\n metadata?: Record<string, unknown>,\n agentId?: string,\n ) => logEvent(eventType, { severity: 'debug', message, metadata, agentId }),\n\n info: (\n eventType: string,\n message?: string,\n metadata?: Record<string, unknown>,\n agentId?: string,\n ) => logEvent(eventType, { severity: 'info', message, metadata, agentId }),\n\n warn: (\n eventType: string,\n message?: string,\n metadata?: Record<string, unknown>,\n agentId?: string,\n ) => logEvent(eventType, { severity: 'warning', message, metadata, agentId }),\n\n error: (\n eventType: string,\n message?: string,\n metadata?: Record<string, unknown>,\n agentId?: string,\n ) => logEvent(eventType, { severity: 'error', message, metadata, agentId }),\n};\n\n/**\n * Flush buffered events to the API\n */\nexport async function flushEvents(): Promise<void> {\n if (eventBuffer.length === 0) return;\n\n // Take current buffer and reset\n const events = eventBuffer;\n eventBuffer = [];\n\n // Clear any pending flush timeout\n if (flushTimeout) {\n clearTimeout(flushTimeout);\n flushTimeout = null;\n }\n\n try {\n const credentials = await getToken();\n if (!credentials) {\n // Not logged in, can't send telemetry\n return;\n }\n\n const apiUrl = getApiUrlConfig();\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);\n\n try {\n // Type-check the request against the shared contract\n const request: SubmitTelemetryEventsRequest = {\n events,\n client_type: 'cli',\n client_version: CLI_VERSION,\n };\n\n const response = await fetch(`${apiUrl}/telemetry/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${credentials.token}`,\n },\n body: JSON.stringify(request),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n // Log failure but don't throw - telemetry shouldn't break the CLI\n console.error(`Telemetry flush failed: ${response.status}`);\n }\n } finally {\n clearTimeout(timeout);\n }\n } catch (error) {\n // Silently ignore telemetry errors - don't disrupt the user\n if (process.env.DEBUG) {\n console.error('Telemetry error:', error);\n }\n }\n}\n\n/**\n * Shutdown telemetry - flush remaining events\n * Call this before the process exits\n */\nexport async function shutdownTelemetry(): Promise<void> {\n isShuttingDown = true;\n\n if (flushTimeout) {\n clearTimeout(flushTimeout);\n flushTimeout = null;\n }\n\n await flushEvents();\n}\n\n// ============================================================================\n// Type-safe event emitters for agent activity events\n// These ensure the correct metadata is provided for each event type\n// ============================================================================\n\nfunction emitEvent(event: TelemetryEvent): void {\n logEvent(event.event_type, {\n severity: event.severity,\n message: event.message,\n metadata: event.metadata,\n agentId: event.agent_id,\n });\n}\n\n/** Emit agent connected event */\nexport function emitAgentConnected(\n agentId: string,\n metadata: AgentConnectedEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_CONNECTED,\n severity: 'info',\n message: 'Agent CLI connected',\n metadata,\n agent_id: agentId,\n } satisfies AgentConnectedEvent);\n}\n\n/** Emit agent disconnected event */\nexport function emitAgentDisconnected(\n agentId: string,\n metadata: AgentDisconnectedEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_DISCONNECTED,\n severity: 'info',\n message: `Agent CLI disconnected (code: ${metadata.code})`,\n metadata,\n agent_id: agentId,\n } satisfies AgentDisconnectedEvent);\n}\n\n/** Emit agent message processing event */\nexport function emitAgentMessageProcessing(\n agentId: string,\n metadata: AgentMessageProcessingEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_MESSAGE_PROCESSING,\n severity: 'info',\n message: `Processing message ${metadata.message_id.slice(0, 8)}...`,\n metadata,\n agent_id: agentId,\n } satisfies AgentMessageProcessingEvent);\n}\n\n/** Emit agent message done event */\nexport function emitAgentMessageDone(\n agentId: string,\n metadata: AgentMessageDoneEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_MESSAGE_DONE,\n severity: 'info',\n message: `Message ${metadata.message_id.slice(0, 8)} processed`,\n metadata,\n agent_id: agentId,\n } satisfies AgentMessageDoneEvent);\n}\n\n/** Emit agent message failed event */\nexport function emitAgentMessageFailed(\n agentId: string,\n metadata: AgentMessageFailedEvent['metadata'],\n): void {\n emitEvent({\n event_type: TelemetryEventTypes.AGENT_MESSAGE_FAILED,\n severity: 'error',\n message: metadata.error\n ? `Message ${metadata.message_id.slice(0, 8)} failed: ${metadata.error}`\n : `Message ${metadata.message_id.slice(0, 8)} ${metadata.reason || 'failed'}`,\n metadata,\n agent_id: agentId,\n } satisfies AgentMessageFailedEvent);\n}\n\n// ============================================================================\n// Legacy event types (for non-activity events like CLI lifecycle, auth, etc.)\n// ============================================================================\n\nexport const EventTypes = {\n // Tunnel lifecycle\n TUNNEL_STARTING: 'tunnel.starting',\n TUNNEL_CONNECTED: 'tunnel.connected',\n TUNNEL_DISCONNECTED: 'tunnel.disconnected',\n TUNNEL_RECONNECTING: 'tunnel.reconnecting',\n TUNNEL_ERROR: 'tunnel.error',\n\n // OpenCode communication\n OPENCODE_HEALTH_CHECK: 'opencode.health_check',\n OPENCODE_HEALTH_OK: 'opencode.health_ok',\n OPENCODE_HEALTH_FAILED: 'opencode.health_failed',\n OPENCODE_REQUEST_RECEIVED: 'opencode.request_received',\n OPENCODE_REQUEST_FORWARDED: 'opencode.request_forwarded',\n OPENCODE_RESPONSE_SENT: 'opencode.response_sent',\n OPENCODE_UNREACHABLE: 'opencode.unreachable',\n OPENCODE_ERROR: 'opencode.error',\n\n // Authentication\n AUTH_LOGIN_STARTED: 'auth.login_started',\n AUTH_LOGIN_SUCCESS: 'auth.login_success',\n AUTH_LOGIN_FAILED: 'auth.login_failed',\n AUTH_LOGOUT: 'auth.logout',\n\n // CLI lifecycle\n CLI_STARTED: 'cli.started',\n CLI_COMMAND: 'cli.command',\n CLI_ERROR: 'cli.error',\n} as const;\n","/**\n * Unified Authentication\n *\n * Provides authentication that works for both interactive (keychain) and CI (env vars) modes.\n *\n * Priority:\n * 1. EVIDENT_AGENT_KEY - API key for CI environments\n * 2. EVIDENT_TOKEN - User token (alternative to key)\n * 3. Keychain - Stored credentials from `evident login`\n */\n\nimport { getToken, StoredCredentials } from './keychain.js';\n\nexport type AuthType = 'agent_key' | 'bearer';\n\nexport interface AuthCredentials {\n token: string;\n authType: AuthType;\n /** User info (only available for keychain auth) */\n user?: {\n id: string;\n email: string;\n };\n}\n\n/**\n * Get the authentication credentials.\n *\n * Priority:\n * 1. EVIDENT_AGENT_KEY env var (CI mode)\n * 2. EVIDENT_TOKEN env var (CI mode)\n * 3. Keychain credentials (interactive mode)\n *\n * @returns Credentials if available, null otherwise\n */\nexport async function getAuthCredentials(): Promise<AuthCredentials | null> {\n // Check for agent key (CI environment)\n const agentKey = process.env.EVIDENT_AGENT_KEY;\n if (agentKey) {\n return { token: agentKey, authType: 'agent_key' };\n }\n\n // Check for user token (env var)\n const userToken = process.env.EVIDENT_TOKEN;\n if (userToken) {\n return { token: userToken, authType: 'bearer' };\n }\n\n // Fall back to keychain credentials\n const keychainCreds = await getToken();\n if (keychainCreds) {\n return {\n token: keychainCreds.token,\n authType: 'bearer',\n user: keychainCreds.user,\n };\n }\n\n // No credentials available\n return null;\n}\n\n/**\n * Get the Authorization header value for the given credentials\n */\nexport function getAuthHeader(credentials: AuthCredentials): string {\n if (credentials.authType === 'agent_key') {\n return `SandboxKey ${credentials.token}`;\n }\n return `Bearer ${credentials.token}`;\n}\n\n/**\n * Check if we're running in an interactive environment.\n *\n * Non-interactive if:\n * - CI environment variable is set\n * - GITHUB_ACTIONS environment variable is set\n * - stdin is not a TTY\n *\n * @param jsonOutput - If true, force non-interactive mode\n */\nexport function isInteractive(jsonOutput?: boolean): boolean {\n if (jsonOutput) return false;\n if (process.env.CI) return false;\n if (process.env.GITHUB_ACTIONS) return false;\n if (!process.stdin.isTTY) return false;\n return true;\n}\n\n// Re-export keychain functions for convenience\nexport { getToken, storeToken, deleteToken, hasValidToken } from './keychain.js';\nexport type { StoredCredentials };\n","/**\n * OpenCode Health Checking\n *\n * Functions for checking OpenCode health status and waiting for it to become healthy.\n */\n\nexport interface HealthCheckResult {\n healthy: boolean;\n version?: string;\n error?: string;\n}\n\n/**\n * Check if a port has a valid OpenCode instance by calling /global/health.\n *\n * Hits `127.0.0.1` explicitly (not `localhost`) so health detection matches the\n * loopback-only bind in `startOpenCode` (`--hostname 127.0.0.1`). `localhost` can\n * resolve to IPv6 `::1`, on which opencode is NOT listening, which would make the\n * check spuriously fail.\n */\nexport async function checkOpenCodeHealth(port: number): Promise<HealthCheckResult> {\n try {\n const response = await fetch(`http://127.0.0.1:${port}/global/health`, {\n signal: AbortSignal.timeout(2000), // 2 second timeout\n });\n if (!response.ok) {\n return { healthy: false, error: `HTTP ${response.status}` };\n }\n const data = (await response.json().catch(() => ({}))) as { version?: string };\n return { healthy: true, version: data.version };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return { healthy: false, error: message };\n }\n}\n\n/**\n * Wait for OpenCode to be healthy\n */\nexport async function waitForOpenCodeHealth(\n port: number,\n timeoutMs: number = 30000,\n): Promise<HealthCheckResult> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeoutMs) {\n const health = await checkOpenCodeHealth(port);\n if (health.healthy) {\n return health;\n }\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n\n return { healthy: false, error: 'Timeout waiting for OpenCode to be healthy' };\n}\n","/**\n * OpenCode Process Management\n *\n * Functions for starting, stopping, and finding OpenCode processes.\n */\n\nimport { execSync, spawn, ChildProcess } from 'child_process';\nimport { checkOpenCodeHealth } from './health.js';\n\n// Common ports that OpenCode might run on\nconst OPENCODE_PORT_RANGE = [4096, 4097, 4098, 4099, 4100];\n\nexport interface OpenCodeInstance {\n pid: number;\n port: number;\n cwd?: string;\n version?: string;\n}\n\n/**\n * Get the working directory of a process\n */\nfunction getProcessCwd(pid: number): string | undefined {\n const platform = process.platform;\n\n try {\n if (platform === 'darwin') {\n // macOS: use lsof to get cwd\n const output = execSync(`lsof -a -p ${pid} -d cwd -Fn 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n // Output format: \"p<pid>\\nn<path>\"\n const lines = output.split('\\n');\n for (const line of lines) {\n if (line.startsWith('n') && !line.startsWith('n ')) {\n return line.slice(1); // Remove 'n' prefix\n }\n }\n } else if (platform === 'linux') {\n // Linux: read /proc/<pid>/cwd symlink\n const output = execSync(`readlink /proc/${pid}/cwd 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n if (output) return output;\n }\n } catch {\n // Failed to get cwd, return undefined\n }\n\n return undefined;\n}\n\n/**\n * Check if a port is in use by any process\n */\nexport function isPortInUse(port: number): boolean {\n const platform = process.platform;\n\n try {\n if (platform === 'darwin' || platform === 'linux') {\n execSync(`lsof -i :${port} -sTCP:LISTEN 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return true; // Command succeeded, port is in use\n }\n } catch {\n // lsof failed or returned empty, port is free\n }\n\n return false;\n}\n\n/**\n * Find the next available port starting from the given port\n */\nexport function findAvailablePort(startPort: number, maxAttempts: number = 10): number | null {\n for (let i = 0; i < maxAttempts; i++) {\n const port = startPort + i;\n if (!isPortInUse(port)) {\n return port;\n }\n }\n return null;\n}\n\n/**\n * Find running OpenCode processes by scanning the process list\n * Uses pgrep for more reliable process matching\n * Returns array of instances with their PIDs and ports\n */\nexport function findOpenCodeProcesses(): OpenCodeInstance[] {\n const instances: OpenCodeInstance[] = [];\n\n try {\n const platform = process.platform;\n\n if (platform === 'darwin' || platform === 'linux') {\n // Method 1: Use pgrep for more reliable process matching\n let pids: number[] = [];\n\n try {\n // pgrep -f matches against full command line\n const pgrepOutput = execSync('pgrep -f \"opencode serve|opencode-serve\"', {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (pgrepOutput) {\n pids = pgrepOutput\n .split('\\n')\n .map((p) => parseInt(p.trim(), 10))\n .filter((p) => !isNaN(p));\n }\n } catch {\n // pgrep found nothing or failed, try ps fallback\n try {\n const psOutput = execSync('ps aux | grep -E \"opencode (serve|--port)\" | grep -v grep', {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (psOutput) {\n for (const line of psOutput.split('\\n')) {\n const parts = line.trim().split(/\\s+/);\n if (parts.length >= 2) {\n const pid = parseInt(parts[1], 10);\n if (!isNaN(pid)) pids.push(pid);\n }\n }\n }\n } catch {\n // ps also failed, pids stays empty\n }\n }\n\n // For each PID, find what port it's listening on and get cwd\n for (const pid of pids) {\n try {\n const lsofOutput = execSync(`lsof -Pan -p ${pid} -i TCP -sTCP:LISTEN 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n for (const line of lsofOutput.split('\\n')) {\n // Parse port from lsof output (e.g., \"node 12345 user 23u IPv4 0x1234 0t0 TCP *:4096 (LISTEN)\")\n const portMatch = line.match(/:(\\d+)\\s+\\(LISTEN\\)/);\n if (portMatch) {\n const port = parseInt(portMatch[1], 10);\n if (!isNaN(port) && !instances.some((i) => i.port === port)) {\n const cwd = getProcessCwd(pid);\n instances.push({ pid, port, cwd });\n }\n }\n }\n } catch {\n // lsof failed for this PID, skip it\n }\n }\n }\n } catch {\n // Process detection failed, return empty array\n }\n\n return instances;\n}\n\n/**\n * Scan common OpenCode ports and check for healthy instances\n * This is a fallback when process-based detection fails\n */\nexport async function scanPortsForOpenCode(): Promise<OpenCodeInstance[]> {\n const instances: OpenCodeInstance[] = [];\n\n // Check each port in parallel for speed\n const checks = OPENCODE_PORT_RANGE.map(async (port) => {\n const health = await checkOpenCodeHealth(port);\n if (health.healthy) {\n // Try to find the PID for this port\n let pid = 0;\n try {\n const lsofOutput = execSync(`lsof -ti :${port} -sTCP:LISTEN 2>/dev/null`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n if (lsofOutput) {\n pid = parseInt(lsofOutput.split('\\n')[0], 10) || 0;\n }\n } catch {\n // Couldn't get PID, that's ok\n }\n\n const cwd = pid ? getProcessCwd(pid) : undefined;\n return { pid, port, cwd, version: health.version };\n }\n return null;\n });\n\n const results = await Promise.all(checks);\n for (const result of results) {\n if (result) {\n instances.push(result);\n }\n }\n\n return instances;\n}\n\n/**\n * Find all running OpenCode instances that are healthy\n * Uses process detection first, falls back to port scanning\n */\nexport async function findHealthyOpenCodeInstances(): Promise<OpenCodeInstance[]> {\n // First try process-based detection\n const processes = findOpenCodeProcesses();\n const healthy: OpenCodeInstance[] = [];\n\n for (const proc of processes) {\n const health = await checkOpenCodeHealth(proc.port);\n if (health.healthy) {\n healthy.push({ ...proc, version: health.version });\n }\n }\n\n // If process detection found nothing, fall back to port scanning\n if (healthy.length === 0) {\n const scanned = await scanPortsForOpenCode();\n return scanned;\n }\n\n return healthy;\n}\n\n/**\n * Start OpenCode as a child process.\n *\n * Binds `opencode serve` to loopback only (`--hostname 127.0.0.1`) per ADR-0039\n * (\"Resolved decisions\"): the browser never reaches opencode directly — it goes\n * through Evident's authed reverse proxy + tunnel. Loopback binding keeps other\n * network hosts out and removes the need for an `OPENCODE_SERVER_PASSWORD` on the\n * critical path, so we deliberately do NOT set one here.\n *\n * `--cors` is intentionally omitted: PoC #1 (`scripts/poc/opencode-web-proxy.mjs`,\n * verified against opencode 1.17.11) showed that when the SPA is served through a\n * same-origin reverse proxy, every API/EventSource call stays same-origin and there\n * are no CORS errors — so no `--cors <evident-origin>` flag is required. If a future\n * probe ever shows the SPA needs cross-origin access for the Evident origin, add\n * `--cors <evident-origin>` to BOTH arg arrays below; until then it stays off.\n */\nexport async function startOpenCode(port: number): Promise<ChildProcess> {\n // Try to find opencode command\n let command = 'opencode';\n let args = ['serve', '--port', port.toString(), '--hostname', '127.0.0.1'];\n\n try {\n execSync('which opencode', { stdio: 'ignore' });\n } catch {\n // opencode not in PATH, try npx (must also bind loopback only)\n command = 'npx';\n args = ['opencode', 'serve', '--port', port.toString(), '--hostname', '127.0.0.1'];\n }\n\n const child = spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n cwd: process.cwd(),\n });\n\n return child;\n}\n\n/**\n * Stop OpenCode process\n * Handles both POSIX (process groups with negative PID) and Windows (direct kill)\n */\nexport function stopOpenCode(opencodeProcess: ChildProcess | null): void {\n if (!opencodeProcess || !opencodeProcess.pid) {\n return;\n }\n\n try {\n if (process.platform === 'win32') {\n // Windows: kill the process directly (no process groups)\n opencodeProcess.kill('SIGTERM');\n } else {\n // POSIX: kill the process group (negative PID) since we spawned with detached: true\n process.kill(-opencodeProcess.pid, 'SIGTERM');\n }\n } catch {\n // Process may have already exited, ignore errors\n }\n}\n","/**\n * OpenCode Installation Detection and Prompts\n *\n * Functions for checking if OpenCode is installed and prompting for installation.\n */\n\nimport { execSync } from 'child_process';\nimport chalk from 'chalk';\nimport { select } from '@inquirer/prompts';\nimport { blank } from '../../utils/ui.js';\n\n// OpenCode installation URL\nconst OPENCODE_INSTALL_URL = 'https://opencode.ai';\n\n/**\n * Check if OpenCode is installed on the system.\n * Returns true if the `opencode` command is available in PATH.\n */\nexport function isOpenCodeInstalled(): boolean {\n try {\n const platform = process.platform;\n if (platform === 'win32') {\n execSync('where opencode', { stdio: 'ignore' });\n } else {\n execSync('which opencode', { stdio: 'ignore' });\n }\n return true;\n } catch {\n return false;\n }\n}\n\nexport type InstallPromptResult = 'installed' | 'continue' | 'exit';\n\n/**\n * Display OpenCode installation instructions and offer to install.\n * Returns 'installed' if user installed it, 'continue' to proceed anyway, or 'exit' to stop.\n *\n * @param interactive - If false, outputs JSON error and returns 'exit'\n */\nexport async function promptOpenCodeInstall(interactive: boolean): Promise<InstallPromptResult> {\n if (!interactive) {\n // In non-interactive mode, just output a JSON message and exit\n console.log(\n JSON.stringify({\n status: 'error',\n error: 'OpenCode is not installed',\n install_url: OPENCODE_INSTALL_URL,\n install_commands: {\n npm: 'npm install -g opencode-ai',\n curl: 'curl -fsSL https://opencode.ai/install.sh | sh',\n },\n }),\n );\n return 'exit';\n }\n\n blank();\n console.log(chalk.yellow('OpenCode is not installed on your system.'));\n blank();\n console.log(chalk.dim('OpenCode is an AI coding agent that runs locally on your machine.'));\n console.log(chalk.dim(`Learn more at: ${chalk.cyan(OPENCODE_INSTALL_URL)}`));\n blank();\n\n const action = await select({\n message: 'How would you like to proceed?',\n choices: [\n {\n name: 'Show installation instructions',\n value: 'instructions',\n description: 'Display commands to install OpenCode',\n },\n {\n name: 'Continue without OpenCode',\n value: 'continue',\n description: 'Connect anyway (requests will fail until OpenCode is installed)',\n },\n {\n name: 'Exit',\n value: 'exit',\n description: 'Exit and install OpenCode manually',\n },\n ],\n });\n\n if (action === 'instructions') {\n blank();\n console.log(chalk.bold('Install OpenCode using one of these methods:'));\n blank();\n console.log(chalk.dim(' # Option 1: Install via npm (recommended)'));\n console.log(` ${chalk.cyan('npm install -g opencode-ai')}`);\n blank();\n console.log(chalk.dim(' # Option 2: Install via curl'));\n console.log(` ${chalk.cyan('curl -fsSL https://opencode.ai/install.sh | sh')}`);\n blank();\n console.log(chalk.dim(`For more options, visit: ${chalk.cyan(OPENCODE_INSTALL_URL)}`));\n blank();\n\n const afterInstall = await select({\n message: 'After installing, what would you like to do?',\n choices: [\n {\n name: 'I installed it - continue',\n value: 'continue',\n description: 'Proceed with the run command',\n },\n {\n name: 'Exit',\n value: 'exit',\n description: 'Exit now and run the command again later',\n },\n ],\n });\n\n if (afterInstall === 'continue') {\n // Verify installation\n if (isOpenCodeInstalled()) {\n console.log(chalk.green('\\n✓ OpenCode detected!'));\n return 'installed';\n } else {\n console.log(chalk.yellow('\\nOpenCode still not detected in PATH.'));\n console.log(chalk.dim('You may need to restart your terminal or add it to your PATH.'));\n\n const proceed = await select({\n message: 'Continue anyway?',\n choices: [\n { name: 'Yes, continue', value: 'continue' },\n { name: 'No, exit', value: 'exit' },\n ],\n });\n return proceed === 'continue' ? 'continue' : 'exit';\n }\n }\n return 'exit';\n }\n\n return action as 'continue' | 'exit';\n}\n","/**\n * OpenCode Session Management\n *\n * Functions for creating and managing OpenCode sessions.\n */\n\n/**\n * Base URL for the local `opencode serve`.\n *\n * MUST be `127.0.0.1`, NOT `localhost`: `startOpenCode` binds opencode to the\n * loopback IPv4 address only (`--hostname 127.0.0.1`). On hosts where `localhost`\n * resolves to IPv6 `::1` first, `localhost` requests fail with an opaque\n * connection error — which previously made queued messages silently fail to run\n * (the drain created a session / sent a message that never reached opencode).\n * Mirrors the same rationale in `health.ts`.\n */\nfunction opencodeBase(port: number): string {\n return `http://127.0.0.1:${port}`;\n}\n\n/**\n * Resolve the directory `opencode serve` is rooted at via `GET /path`.\n *\n * opencode binds every session to a `directory`, and `opencode web` lists\n * sessions filtered by `?directory=<dir>&roots=true`. Evident's deep-link into a\n * session is built from the SAME `GET /path` value (persisted as\n * `agents.working_directory`, see proxy-link.ts), so to guarantee a\n * drain-created session is visible at the link we hand it back, we create it with\n * that exact directory rather than relying on whatever the server defaulted to.\n *\n * Best-effort: returns `null` if `/path` is unreachable or yields no usable\n * directory, in which case the caller falls back to a directory-less create.\n */\nexport async function getOpenCodeDirectory(port: number): Promise<string | null> {\n try {\n const res = await fetch(`${opencodeBase(port)}/path`);\n if (!res.ok) return null;\n const body = (await res.json()) as {\n directory?: unknown;\n worktree?: unknown;\n path?: { cwd?: unknown; directory?: unknown };\n };\n const dir =\n (typeof body.directory === 'string' && body.directory) ||\n (typeof body.worktree === 'string' && body.worktree) ||\n (typeof body.path?.cwd === 'string' && body.path.cwd) ||\n (typeof body.path?.directory === 'string' && body.path.directory) ||\n null;\n return dir && dir.trim() ? dir.trim() : null;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message-level turn-completion (the SINGLE correct completion signal)\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal shape of an entry returned by `GET /session/:id/message`.\n *\n * opencode exposes a message's role/time either at the top level\n * (`{ role, parts }`, legacy) or nested under `info`\n * (`{ info: { role, time }, parts }`, current). Tolerate both — see the chosen\n * rule below. The shape mirrors the server's source of truth,\n * `extractTextFromMessages` in\n * `apps/api-worker/src/services/conversation-notification.ts`.\n */\nexport interface OpenCodeMessage {\n info?: { role?: string; time?: { created?: number; completed?: number } };\n role?: string;\n parts?: unknown[];\n}\n\n/**\n * Resolve a message's role, tolerating both shapes (mirrors the server's\n * `roleOf`): top-level `{ role }` (legacy) or `{ info: { role } }` (current).\n */\nfunction roleOf(m: OpenCodeMessage | undefined | null): string | undefined {\n if (!m || typeof m !== 'object') return undefined;\n if (typeof m.role === 'string') return m.role;\n const infoRole = m.info?.role;\n return typeof infoRole === 'string' ? infoRole : undefined;\n}\n\n/** Resolve a message's `time.completed`, tolerating both shapes. */\nfunction completedOf(m: OpenCodeMessage | undefined | null): number | null | undefined {\n if (!m || typeof m !== 'object') return undefined;\n return m.info?.time?.completed;\n}\n\n/**\n * Fetch the messages for a session via `GET /session/:id/message`.\n *\n * Best-effort, like `getOpenCodeDirectory`: returns `null` on a non-OK response\n * or any throw so callers can treat \"unknown\" as \"not yet complete\" without\n * crashing. Uses the IPv4 loopback base (`127.0.0.1`, NOT `localhost`).\n */\nexport async function getSessionMessages(\n port: number,\n sessionId: string,\n): Promise<OpenCodeMessage[] | null> {\n try {\n const res = await fetch(`${opencodeBase(port)}/session/${sessionId}/message`);\n if (!res.ok) return null;\n const body = await res.json();\n return Array.isArray(body) ? (body as OpenCodeMessage[]) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Decide whether a session's turn is COMPLETE from its messages.\n *\n * CHOSEN RULE (documented in the plan): a paused turn is COMPLETE when the LAST\n * message returned by `GET /session/:id/message` is an ASSISTANT message whose\n * `info.time.completed` is set (non-null). opencode (v1.17.11, verified live)\n * never sets `completed` on the SESSION object — completion is per-message: a\n * finished assistant turn ends with an assistant message bearing\n * `info.time.completed` (its last part is `step-finish`).\n *\n * Taking the LAST message (not \"any completed assistant message\") avoids a\n * false-positive when a prior turn completed but a NEW one — triggered by the\n * user answering a question/permission — is mid-flight. Shape source of truth:\n * the server's `extractTextFromMessages`/`roleOf` in\n * `apps/api-worker/src/services/conversation-notification.ts`.\n *\n * Returns `false` for `null`/empty, when the last message is the user's message,\n * or when the last message is an in-flight assistant message (no `completed`).\n */\nexport function isTurnComplete(messages: OpenCodeMessage[] | null): boolean {\n if (!messages || messages.length === 0) return false;\n const last = messages[messages.length - 1];\n if (roleOf(last) !== 'assistant') return false;\n return completedOf(last) != null;\n}\n\n/**\n * Convenience composer: fetch a session's messages and apply `isTurnComplete`.\n * Best-effort — unreachable opencode (`getSessionMessages` → `null`) yields\n * `false` (not yet complete). The two primitives stay separately testable.\n */\nexport async function isSessionTurnComplete(port: number, sessionId: string): Promise<boolean> {\n const messages = await getSessionMessages(port, sessionId);\n return isTurnComplete(messages);\n}\n\n/**\n * Create a new OpenCode session.\n *\n * When `directory` is provided it is passed as `?directory=<dir>` so the session\n * is rooted at the project directory (and thus visible in `opencode web`'s\n * directory-filtered session list) rather than at the CLI process's cwd.\n */\nexport async function createOpenCodeSession(\n port: number,\n directory?: string | null,\n): Promise<string> {\n const url = new URL(`${opencodeBase(port)}/session`);\n if (directory && directory.trim()) {\n url.searchParams.set('directory', directory.trim());\n }\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Failed to create session: HTTP ${response.status}${text ? `: ${text}` : ''}`);\n }\n\n const data = (await response.json()) as { id: string };\n return data.id;\n}\n\n/**\n * Optional OpenCode routing options for sendMessageToOpenCode\n */\nexport interface MessageOptions {\n /** OpenCode agent name (e.g. \"build\", \"plan\") */\n agent?: string;\n /** Model in provider/model format (e.g. \"anthropic/claude-opus-4-6\") */\n model?: string;\n}\n\nexport interface SendMessageResult {\n title?: string;\n /**\n * WI-11 (C4): true when the blocking message request returned while the turn\n * is still PAUSED awaiting an interaction (a question/permission was surfaced\n * during this send AND the turn is NOT message-level complete — the last\n * message from `GET /session/:id/message` is not yet a completed assistant\n * message, i.e. `isTurnComplete` is false). The channel driver uses this to\n * SKIP marking the message `done` — a paused turn is not finished; it completes\n * once the user answers in the proxied opencode-web surface and the watcher\n * observes the completed assistant message.\n */\n awaitingInteraction?: boolean;\n}\n\n// Minimal types matching the OpenCode API shapes (avoiding API imports in CLI)\n\nexport interface OpenCodeQuestionOption {\n label: string;\n description: string;\n}\n\nexport interface OpenCodeQuestionInfo {\n question: string;\n header: string;\n options: OpenCodeQuestionOption[];\n}\n\nexport interface OpenCodeQuestion {\n id: string;\n sessionID: string;\n questions: OpenCodeQuestionInfo[];\n tool?: { messageID: string; callID: string };\n}\n\nexport interface OpenCodePermission {\n id: string;\n type: string;\n pattern?: string | string[];\n sessionID: string;\n messageID: string;\n callID?: string;\n title: string;\n metadata: Record<string, unknown>;\n time: { created: number };\n}\n\n/**\n * Hooks called while the message is being processed.\n * Each question/permission is reported at most once (tracked by ID).\n */\nexport interface SessionInteractiveHooks {\n onQuestion?: (question: OpenCodeQuestion) => Promise<void>;\n onPermission?: (permission: OpenCodePermission) => Promise<void>;\n}\n\n/**\n * Send a message to an OpenCode session and wait for it to complete.\n *\n * OpenCode uses a blocking HTTP endpoint: POST /session/:id/message holds the\n * connection open until processing completes (including any wait for the user\n * to answer an interactive question). While waiting, we poll for pending\n * questions and permissions every second so they can be surfaced without\n * blocking the main request.\n *\n * Throws on HTTP errors or when maxWaitMs is exceeded.\n */\nexport async function sendMessageToOpenCode(\n port: number,\n sessionId: string,\n content: string,\n options?: MessageOptions,\n hooks?: SessionInteractiveHooks,\n maxWaitMs: number = 10 * 60 * 1000,\n): Promise<SendMessageResult> {\n const body: Record<string, unknown> = {\n parts: [{ type: 'text', text: content }],\n };\n\n if (options?.agent) {\n body.agent = options.agent;\n }\n\n if (options?.model) {\n const slashIndex = options.model.indexOf('/');\n if (slashIndex !== -1) {\n body.model = {\n providerID: options.model.substring(0, slashIndex),\n modelID: options.model.substring(slashIndex + 1),\n };\n }\n }\n\n let pollDone = false;\n const reportedQuestions = new Set<string>();\n const reportedPermissions = new Set<string>();\n\n // Polls for interactive events while the message request is in-flight.\n const pollInteractive = async () => {\n while (!pollDone) {\n await new Promise<void>((resolve) => setTimeout(resolve, 1000));\n if (pollDone) break;\n\n if (hooks?.onQuestion) {\n try {\n const res = await fetch(`${opencodeBase(port)}/question`);\n if (res.ok) {\n const questions = (await res.json()) as OpenCodeQuestion[];\n for (const q of questions) {\n if (q.sessionID === sessionId && !reportedQuestions.has(q.id)) {\n reportedQuestions.add(q.id);\n await hooks.onQuestion(q);\n }\n }\n }\n } catch {\n // Non-fatal: interactive detection is best-effort\n }\n }\n\n if (hooks?.onPermission) {\n try {\n const res = await fetch(`${opencodeBase(port)}/permission`);\n if (res.ok) {\n const permissions = (await res.json()) as OpenCodePermission[];\n for (const p of permissions) {\n if (p.sessionID === sessionId && !reportedPermissions.has(p.id)) {\n reportedPermissions.add(p.id);\n await hooks.onPermission(p);\n }\n }\n }\n } catch {\n // Non-fatal: interactive detection is best-effort\n }\n }\n }\n };\n\n // Awaits the message endpoint; sets pollDone when done so the poll loop exits.\n const sendMessage = async (): Promise<SendMessageResult> => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), maxWaitMs);\n try {\n const res = await fetch(`${opencodeBase(port)}/session/${sessionId}/message`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`OpenCode message failed: HTTP ${res.status}${text ? `: ${text}` : ''}`);\n }\n // Fetch the session ONLY for its title (for display). The session object\n // never carries a `completed` field (opencode tracks completion\n // per-message), so we do NOT read completion from here.\n const sessionRes = await fetch(`${opencodeBase(port)}/session/${sessionId}`).catch(\n () => null,\n );\n const session = sessionRes?.ok ? ((await sessionRes.json()) as { title?: string }) : null;\n\n // WI-11 (C4): a turn is \"awaiting interaction\" when we surfaced a\n // question/permission during this send AND the turn is NOT message-level\n // complete. At pause time the last message is an in-flight assistant\n // message (no `info.time.completed`) — or the user's message — so\n // `isTurnComplete` is false and this reduces to `reportedInteraction`,\n // exactly preserving the prior paused-detection behaviour. If the turn is\n // genuinely complete, `isTurnComplete` is true and we do NOT keep it paused.\n const reportedInteraction = reportedQuestions.size > 0 || reportedPermissions.size > 0;\n const turnComplete = isTurnComplete(await getSessionMessages(port, sessionId));\n const awaitingInteraction = reportedInteraction && !turnComplete;\n\n return { title: session?.title, awaitingInteraction };\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error('Message processing timed out');\n }\n throw err;\n } finally {\n clearTimeout(timer);\n pollDone = true;\n }\n };\n\n const [result] = await Promise.all([sendMessage(), pollInteractive()]);\n return result;\n}\n","/**\n * Tunnel WebSocket Connection\n *\n * Functions for establishing and managing WebSocket tunnel connections.\n *\n * Carries the streaming frame protocol (ADR-0039): control messages\n * (`connected` / `error` / `ping`) plus the multiplexed `StreamFrameToAgent`\n * frames (`open` / `req_data` / `req_end` / `abort`) which are dispatched to a\n * per-connection `StreamForwarder` that streams responses back verbatim.\n */\n\nimport WebSocket from 'ws';\nimport { getTunnelUrlConfig } from '../config.js';\nimport { TUNNEL_DRAIN_PING_PATH, type StreamFrameToAgent } from '@evident/types';\nimport { StreamForwarder } from './forwarding.js';\n\n/**\n * Control messages the relay sends outside the streaming frame protocol.\n */\ntype TunnelControlMessage =\n | { type: 'connected'; agent_id?: string }\n | { type: 'error'; code?: string; message?: string }\n | { type: 'ping' };\n\n/**\n * Any message the relay can send to the CLI: a control message or a streaming\n * frame (multiplexed by `sid`).\n */\nexport type RelayMessage = TunnelControlMessage | StreamFrameToAgent;\n\n// Reconnection constants\nconst MAX_RECONNECT_DELAY = 30000; // 30 seconds\nconst BASE_RECONNECT_DELAY = 500; // 0.5 seconds\n\nexport interface TunnelConnectionOptions {\n agentId: string;\n authHeader: string;\n port: number;\n onConnected?: (agentId: string) => void;\n onDisconnected?: (code: number, reason: string) => void;\n onError?: (error: string) => void;\n onRequest?: (method: string, path: string, requestId: string) => void;\n onResponse?: (status: number, durationMs: number, requestId: string) => void;\n onInfo?: (message: string) => void;\n /**\n * Fired when the relay forwards an `open` frame for the reserved drain-ping\n * path. The CLI run loop wires this to an immediate, idempotent\n * `drainPending()`. Best-effort latency optimization only.\n */\n onDrainPing?: () => void;\n}\n\nexport interface TunnelConnection {\n ws: WebSocket;\n close: () => void;\n}\n\n/**\n * Calculate reconnect delay with exponential backoff and jitter\n */\nexport function getReconnectDelay(attempt: number): number {\n const exponentialDelay = BASE_RECONNECT_DELAY * Math.pow(2, attempt);\n const jitter = Math.random() * 1000;\n return Math.min(exponentialDelay + jitter, MAX_RECONNECT_DELAY);\n}\n\n/**\n * Translate a low-level WebSocket/socket error into an actionable, operator-\n * facing message that names the tunnel URL and the likely cause. Node attaches a\n * `code` (e.g. `ECONNREFUSED`) to system errors; the default `ws` error message\n * for these is empty or unhelpful, which is how we ended up staring at an opaque\n * `1006`.\n */\nexport function describeSocketError(error: Error, url: string): string {\n const code = (error as NodeJS.ErrnoException).code;\n switch (code) {\n case 'ECONNREFUSED':\n return `connection refused at ${url} — is the tunnel relay running? (ECONNREFUSED)`;\n case 'ENOTFOUND':\n return `host not found for ${url} — check the tunnel URL (ENOTFOUND)`;\n case 'ETIMEDOUT':\n return `connection timed out to ${url} (ETIMEDOUT)`;\n case 'ECONNRESET':\n return `connection reset by ${url} (ECONNRESET)`;\n default: {\n const base = error.message?.trim();\n const suffix = code ? ` (${code})` : '';\n return `${base && base.length > 0 ? base : 'socket error'}${suffix} connecting to ${url}`;\n }\n }\n}\n\n/**\n * Frame types that belong to the streaming protocol (edge→agent).\n */\nconst STREAM_FRAME_TYPES = new Set<StreamFrameToAgent['type']>([\n 'open',\n 'req_data',\n 'req_end',\n 'abort',\n]);\n\nfunction isStreamFrame(message: RelayMessage): message is StreamFrameToAgent {\n return STREAM_FRAME_TYPES.has(message.type as StreamFrameToAgent['type']);\n}\n\n/**\n * Connect to the tunnel relay\n *\n * @returns A promise that resolves with the WebSocket connection when connected,\n * or rejects on connection failure\n */\nexport function connectTunnel(options: TunnelConnectionOptions): Promise<TunnelConnection> {\n const {\n agentId,\n authHeader,\n port,\n onConnected,\n onDisconnected,\n onError,\n onRequest,\n onResponse,\n onInfo,\n onDrainPing,\n } = options;\n\n const tunnelUrl = getTunnelUrlConfig();\n const url = `${tunnelUrl}/tunnel/${agentId}/connect`;\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url, {\n headers: {\n Authorization: authHeader,\n },\n });\n\n // Tracks per-stream start time so we can report response duration once the\n // upstream `head` is observed.\n const streamStartTimes = new Map<string, number>();\n\n // Streams responses from loopback opencode back to the relay, frame-by-frame.\n const forwarder = new StreamForwarder(ws, port, {\n onOpen: (sid, method, path) => {\n // Suppress request/response activity-log bookkeeping for the reserved\n // drain-ping path: it is an internal control signal, not a real\n // opencode request, and must not pollute the request log. The forwarder\n // intercepts it (204 + onDrainPing) without ever fetching opencode.\n if (path === TUNNEL_DRAIN_PING_PATH) return;\n streamStartTimes.set(sid, Date.now());\n onRequest?.(method, path, sid);\n },\n onHead: (sid, status) => {\n const startedAt = streamStartTimes.get(sid);\n streamStartTimes.delete(sid);\n onResponse?.(status, startedAt ? Date.now() - startedAt : 0, sid);\n },\n onDrainPing: () => onDrainPing?.(),\n });\n\n const connectionTimeout = setTimeout(() => {\n ws.close();\n reject(new Error('Connection timeout'));\n }, 30000);\n\n // Captures the HTTP-level rejection detail (status + body) from a failed\n // WebSocket UPGRADE, so a relay/auth rejection surfaces a real reason instead\n // of the opaque `close code 1006` the browser/`ws` reports otherwise. Set by\n // the `unexpected-response` handler and consumed by `error`/`close`.\n let upgradeRejection: string | null = null;\n\n // The relay can reject the upgrade with a normal HTTP response (e.g. 401\n // invalid token, 502 failed to register with API). `ws` emits this as\n // `unexpected-response` with the raw `http.IncomingMessage`; without handling\n // it we only ever see a generic error + 1006. Read the status + body so the\n // operator learns WHY the tunnel was refused.\n ws.on('unexpected-response', (_req, res) => {\n clearTimeout(connectionTimeout);\n const chunks: Buffer[] = [];\n res.on('data', (chunk: Buffer) => chunks.push(chunk));\n res.on('end', () => {\n const bodyRaw = Buffer.concat(chunks).toString('utf8').trim();\n // Try to extract a friendly message from a JSON error body.\n let detail = bodyRaw;\n try {\n const parsed = JSON.parse(bodyRaw) as {\n error?: string;\n message?: string;\n details?: string;\n };\n detail = parsed.error ?? parsed.message ?? bodyRaw;\n if (parsed.details) detail += ` (${parsed.details})`;\n } catch {\n /* not JSON — keep the raw body */\n }\n const statusLine = `HTTP ${res.statusCode}${res.statusMessage ? ` ${res.statusMessage}` : ''}`;\n upgradeRejection = detail ? `${statusLine}: ${detail}` : statusLine;\n onError?.(`Tunnel refused by relay (${upgradeRejection})`);\n // `ws` will also emit `error` + `close` after this; rejecting here ensures\n // the connect promise fails fast with the real reason.\n reject(new Error(`Tunnel handshake rejected: ${upgradeRejection}`));\n });\n });\n\n ws.on('open', () => {\n onInfo?.('WebSocket connection established');\n });\n\n ws.on('message', (data: WebSocket.RawData) => {\n let message: RelayMessage;\n try {\n message = JSON.parse(data.toString());\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n onError?.(`Failed to handle message: ${errorMessage}`);\n return;\n }\n\n // Streaming frames: dispatch to the forwarder (it fires onOpen/onHead).\n if (isStreamFrame(message)) {\n forwarder.handleFrame(message);\n return;\n }\n\n switch (message.type) {\n case 'connected': {\n clearTimeout(connectionTimeout);\n const connectedAgentId = message.agent_id ?? agentId;\n onConnected?.(connectedAgentId);\n resolve({\n ws,\n close: () => ws.close(1000, 'CLI shutdown'),\n });\n break;\n }\n\n case 'error':\n clearTimeout(connectionTimeout);\n onError?.(message.message || 'Unknown tunnel error');\n if (message.code === 'unauthorized') {\n ws.close();\n reject(new Error('Unauthorized'));\n }\n break;\n\n case 'ping':\n ws.send(JSON.stringify({ type: 'pong' }));\n break;\n }\n });\n\n ws.on('error', (error: Error) => {\n clearTimeout(connectionTimeout);\n // Prefer the HTTP upgrade-rejection detail (if any). Otherwise translate the\n // low-level socket error into an ACTIONABLE message that names the URL and\n // the likely cause — a bare \"Connection error:\" / 1006 is useless to the\n // operator. Most common locally: the relay isn't running (ECONNREFUSED).\n const detail = upgradeRejection ?? describeSocketError(error, url);\n onError?.(`Connection error: ${detail}`);\n reject(upgradeRejection ? new Error(upgradeRejection) : new Error(detail));\n });\n\n ws.on('close', (code: number, reason: Buffer) => {\n // For an abnormal close (1006) the reason frame is empty; fall back to any\n // captured HTTP upgrade-rejection detail so the log explains the cause.\n const reasonStr =\n reason.toString() ||\n upgradeRejection ||\n (code === 1006 ? 'abnormal closure' : 'No reason provided');\n // Abort any in-flight forwarded streams.\n forwarder.abortAll();\n streamStartTimes.clear();\n onDisconnected?.(code, reasonStr);\n });\n });\n}\n","/**\n * Tunnel Request Forwarding (streaming frame protocol — ADR-0039)\n *\n * Mirrors the AGENT side of `scripts/poc/tunnel-streaming-proxy.mjs`.\n *\n * For each `open` frame we `fetch()` the loopback `opencode serve` instance and\n * stream the response back to the relay frame-by-frame (`head` + `res_data` +\n * `res_end`), VERBATIM — response status and headers are forwarded as-is (no\n * `Content-Type` override) and the body is never buffered whole. Request bodies\n * (when `has_body`) are streamed in from incoming `req_data` / `req_end` frames.\n *\n * Multiplexed by `sid`; many logical streams share the single per-agent\n * WebSocket. An `abort` frame cancels the in-flight upstream fetch for that `sid`.\n */\n\nimport WebSocket from 'ws';\nimport {\n MAX_FRAME_BYTES,\n TUNNEL_DRAIN_PING_PATH,\n type StreamFrameHeaders,\n type StreamFrameToAgent,\n type StreamFrameToEdge,\n} from '@evident/types';\n\n/**\n * Use the IPv4 loopback explicitly — `localhost` may resolve to IPv6 `::1`,\n * where `opencode serve --hostname 127.0.0.1` does NOT listen.\n */\nconst LOOPBACK_HOST = '127.0.0.1';\n\n/**\n * Hop-by-hop request headers that must not be forwarded to opencode.\n * Mirrors the PoC's `STRIP_REQ` set.\n */\nconst STRIP_REQ = new Set([\n 'host',\n 'connection',\n 'keep-alive',\n 'proxy-authorization',\n 'transfer-encoding',\n 'upgrade',\n 'content-length',\n]);\n\n/**\n * Hop-by-hop response headers that must not be forwarded back to the edge.\n * Mirrors the PoC's `STRIP_RES` set.\n */\nconst STRIP_RES = new Set([\n 'connection',\n 'keep-alive',\n 'transfer-encoding',\n 'content-encoding',\n 'content-length',\n]);\n\n/**\n * Per-stream state held by the agent while a request is in flight.\n */\ninterface InflightStream {\n /** Feed a chunk of the request body (from a `req_data` frame). */\n pushBody?: (buf: Buffer) => void;\n /** Signal the end of the request body (from a `req_end` frame). */\n endBody?: () => void;\n /** Abort the in-flight upstream fetch (from an `abort` frame). */\n abort: () => void;\n}\n\n/**\n * Manages the AGENT side of the streaming frame protocol over a single tunnel\n * WebSocket. Dispatches edge→agent frames and emits agent→edge frames.\n */\nexport interface StreamForwarderCallbacks {\n /** Fired when an `open` frame begins a new forwarded stream. */\n onOpen?: (sid: string, method: string, path: string) => void;\n /** Fired when the upstream `head` (status + headers) is forwarded back. */\n onHead?: (sid: string, status: number) => void;\n /**\n * Fired when an `open` frame targets the reserved drain-ping path\n * (`TUNNEL_DRAIN_PING_PATH`). The forwarder intercepts that path BEFORE any\n * loopback opencode fetch and invokes this callback (fire-and-forget) so the\n * runner can trigger an immediate, idempotent `drainPending()`. Latency\n * optimization ONLY — a lost/failed ping never orphans a message (see the §2\n * invariant in `docs/plans/slack-queue-always-ping-tasks.md`).\n */\n onDrainPing?: () => void;\n}\n\nexport class StreamForwarder {\n private readonly inflight = new Map<string, InflightStream>();\n\n constructor(\n private readonly ws: WebSocket,\n private readonly port: number,\n private readonly callbacks: StreamForwarderCallbacks = {},\n ) {}\n\n /**\n * Handle an edge→agent frame. Unknown frame types are ignored.\n */\n handleFrame(frame: StreamFrameToAgent): void {\n switch (frame.type) {\n case 'open':\n this.callbacks.onOpen?.(frame.sid, frame.method, frame.path);\n void this.handleOpen(frame);\n break;\n case 'req_data':\n this.inflight.get(frame.sid)?.pushBody?.(Buffer.from(frame.b64, 'base64'));\n break;\n case 'req_end':\n this.inflight.get(frame.sid)?.endBody?.();\n break;\n case 'abort':\n this.inflight.get(frame.sid)?.abort?.();\n break;\n }\n }\n\n /**\n * Abort every in-flight stream (e.g. on WebSocket close).\n */\n abortAll(): void {\n for (const stream of this.inflight.values()) {\n try {\n stream.abort();\n } catch {\n // ignore\n }\n }\n this.inflight.clear();\n }\n\n private send(frame: StreamFrameToEdge): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(frame));\n }\n }\n\n private async handleOpen(frame: Extract<StreamFrameToAgent, { type: 'open' }>): Promise<void> {\n const { sid, method, path, headers, has_body } = frame;\n\n // Reserved drain-ping path: intercept BEFORE any inflight registration,\n // body-collection setup, or loopback opencode fetch. This path is the\n // channel-message drain ping (`/__evident/drain`) — it must NOT reach\n // opencode. We trigger an immediate, idempotent `drainPending()` via the\n // injected callback and reply `204` (head + res_end).\n //\n // This intercept MUST be harmless for ANY method/body: the same path is\n // reachable via the browser web-proxy (agent-proxy forwards the request path\n // verbatim over this same `open`-frame plumbing), so we never assume a POST\n // or a JSON body — we ignore both and reply 204 regardless of the caller.\n //\n // No inflight entry is registered, so any trailing `req_data`/`req_end`\n // frames the relay sends for this `sid` (the ping carries a small JSON body)\n // hit `handleFrame`'s `this.inflight.get(sid)?.…` with `undefined` and are\n // silently no-ops — harmless and intended.\n if (path === TUNNEL_DRAIN_PING_PATH) {\n this.callbacks.onDrainPing?.();\n this.send({ type: 'head', sid, status: 204, headers: {} });\n this.send({ type: 'res_end', sid });\n return;\n }\n\n const ac = new AbortController();\n\n // Collect the request body from `req_data` frames and resolve once `req_end`\n // arrives. We BUFFER the full body before issuing the upstream fetch rather\n // than streaming it with `duplex: 'half'`: undici (Node's fetch) tears down\n // the connection (\"SocketError: other side closed\", surfaced to the user as\n // `TypeError: fetch failed`) when a hand-fed request-body stream doesn't\n // satisfy its backpressure expectations — which is exactly what happened with\n // POSTs carrying a JSON body (e.g. `prompt_async`). opencode's request bodies\n // are small JSON payloads, so buffering is the robust choice; the RESPONSE is\n // still streamed back frame-by-frame (the part that actually needs streaming).\n let bodyPromise: Promise<Buffer> | undefined;\n let pushBody: ((buf: Buffer) => void) | undefined;\n let endBody: (() => void) | undefined;\n if (has_body) {\n const chunks: Buffer[] = [];\n bodyPromise = new Promise<Buffer>((resolve) => {\n pushBody = (buf: Buffer) => {\n chunks.push(buf);\n };\n endBody = () => {\n resolve(Buffer.concat(chunks));\n };\n });\n }\n\n // Forward request headers verbatim, minus hop-by-hop headers.\n const fwdHeaders: StreamFrameHeaders = {};\n for (const [k, v] of Object.entries(headers ?? {})) {\n if (!STRIP_REQ.has(k.toLowerCase())) fwdHeaders[k] = v;\n }\n\n this.inflight.set(sid, { pushBody, endBody, abort: () => ac.abort() });\n\n // Wait for the complete body (if any) before fetching. The relay sends all\n // `req_data` frames followed by `req_end`, so this resolves promptly.\n const body = bodyPromise ? await bodyPromise : undefined;\n if (ac.signal.aborted) {\n this.inflight.delete(sid);\n return;\n }\n\n let upstream: Response;\n try {\n upstream = await fetch(`http://${LOOPBACK_HOST}:${this.port}${path}`, {\n method,\n headers: fwdHeaders,\n body,\n redirect: 'manual',\n signal: ac.signal,\n } as RequestInit);\n } catch (err) {\n this.inflight.delete(sid);\n if (!ac.signal.aborted) {\n this.send({ type: 'res_err', sid, message: `upstream fetch failed: ${String(err)}` });\n }\n return;\n }\n\n // Forward response status + headers VERBATIM, minus hop-by-hop headers.\n // No Content-Type override — the upstream content-type is preserved exactly.\n const resHeaders: StreamFrameHeaders = {};\n upstream.headers.forEach((value, key) => {\n if (!STRIP_RES.has(key.toLowerCase())) resHeaders[key] = value;\n });\n this.send({ type: 'head', sid, status: upstream.status, headers: resHeaders });\n this.callbacks.onHead?.(sid, upstream.status);\n\n // Stream the body frame-by-frame. NEVER buffer the whole body. Each chunk is\n // further split to respect MAX_FRAME_BYTES (the ~1MB CF WS-frame limit).\n try {\n if (upstream.body) {\n const reader = upstream.body.getReader();\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const chunk = Buffer.from(value);\n for (let i = 0; i < chunk.length; i += MAX_FRAME_BYTES) {\n const slice = chunk.subarray(i, i + MAX_FRAME_BYTES);\n this.send({ type: 'res_data', sid, b64: slice.toString('base64') });\n }\n }\n }\n this.send({ type: 'res_end', sid });\n } catch (err) {\n if (!ac.signal.aborted) {\n this.send({ type: 'res_err', sid, message: String(err) });\n }\n } finally {\n this.inflight.delete(sid);\n }\n }\n}\n","/**\n * Runner tunnel connection (WI-THIN-1, ADR-0039)\n *\n * Wraps `connectTunnel` with the runner's connect-with-retry + auto-reconnect\n * orchestration, extracted out of `commands/run.ts` to keep the command thin.\n *\n * The streaming tunnel transparently proxies ALL web traffic (HTML, JS bundle,\n * `/session`, `/event` SSE) — this module only owns the WebSocket lifecycle\n * (connect, exponential backoff, automatic reconnection on unexpected close)\n * and surfaces status transitions to the caller via callbacks.\n */\n\nimport { connectTunnel, getReconnectDelay, type TunnelConnection } from './connection.js';\n\nexport interface RunnerConnectionEvents {\n /** Tunnel established; carries the server-resolved agent id. */\n onConnected: (agentId: string, isReconnect: boolean) => void;\n /** Tunnel closed; `code === 1000` is a normal (expected) closure. */\n onDisconnected: (code: number, reason: string) => void;\n /** A transient relay/protocol error (non-fatal). */\n onError?: (error: string) => void;\n /** opencode answered a forwarded request (web traffic is live). */\n onResponse?: () => void;\n /**\n * The relay forwarded a channel-message drain ping over the tunnel. Wire this\n * to an immediate, idempotent `drainPending()`. Best-effort latency\n * optimization only — a lost ping never orphans a message.\n */\n onDrainPing?: () => void;\n /** Informational lifecycle message. */\n onInfo?: (message: string) => void;\n /** A reconnect attempt is starting (carries the 1-based attempt number). */\n onReconnecting?: (attempt: number) => void;\n}\n\nexport interface RunnerConnectionOptions {\n agentId: string;\n getAuthHeader: () => string;\n port: number;\n /** Liveness predicate — stop retrying once the runner is shutting down. */\n isRunning: () => boolean;\n events: RunnerConnectionEvents;\n sleep?: (ms: number) => Promise<void>;\n}\n\n/**\n * Manages a single agent's tunnel connection with automatic reconnection.\n *\n * `connect()` resolves once the initial connection succeeds (or rejects on an\n * `Unauthorized` error). Subsequent unexpected disconnects trigger a background\n * reconnect loop that the caller can await via `reconnectPromise`.\n */\nexport class RunnerConnection {\n private readonly opts: RunnerConnectionOptions;\n private readonly sleep: (ms: number) => Promise<void>;\n\n private connection: TunnelConnection | null = null;\n private resolvedAgentId: string;\n\n /** True while a (re)connect loop is in flight. */\n reconnecting = false;\n /** The in-flight reconnect promise, awaitable by the caller. */\n reconnectPromise: Promise<void> | null = null;\n /** 1-based count of the current reconnect attempt streak. */\n reconnectAttempt = 0;\n\n constructor(opts: RunnerConnectionOptions) {\n this.opts = opts;\n this.resolvedAgentId = opts.agentId;\n this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n }\n\n get agentId(): string {\n return this.resolvedAgentId;\n }\n\n /** Establish the initial tunnel connection (with retry/backoff). */\n async connect(): Promise<void> {\n await this.connectWithRetry(false);\n }\n\n /** Close the active connection (idempotent). */\n close(): void {\n if (this.connection) {\n try {\n this.connection.close();\n } catch {\n // Ignore close errors\n }\n this.connection = null;\n }\n }\n\n private async connectWithRetry(isReconnect: boolean): Promise<void> {\n if (isReconnect && this.reconnecting) return;\n this.reconnecting = true;\n this.close();\n\n const { events } = this.opts;\n\n while (this.opts.isRunning()) {\n try {\n this.connection = await connectTunnel({\n agentId: this.resolvedAgentId,\n authHeader: this.opts.getAuthHeader(),\n port: this.opts.port,\n onConnected: (agentId) => {\n this.reconnectAttempt = 0;\n this.reconnecting = false;\n this.resolvedAgentId = agentId;\n events.onConnected(agentId, isReconnect);\n },\n onDisconnected: (code, reason) => {\n events.onDisconnected(code, reason);\n // Auto-reconnect on unexpected closure (1000 = normal/manual).\n if (this.opts.isRunning() && code !== 1000 && !this.reconnecting) {\n this.reconnectPromise = this.connectWithRetry(true).catch((err) => {\n events.onError?.(`Reconnection failed: ${err.message}`);\n });\n }\n },\n onError: (error) => events.onError?.(error),\n onResponse: () => events.onResponse?.(),\n onDrainPing: () => events.onDrainPing?.(),\n onInfo: (message) => events.onInfo?.(message),\n });\n return;\n } catch (error) {\n this.reconnectAttempt++;\n if ((error as Error).message === 'Unauthorized') {\n this.reconnecting = false;\n throw error;\n }\n const delay = getReconnectDelay(this.reconnectAttempt);\n events.onReconnecting?.(this.reconnectAttempt);\n events.onError?.(`Connection failed, retrying in ${Math.round(delay / 1000)}s...`);\n await this.sleep(delay);\n }\n }\n\n this.reconnecting = false;\n }\n}\n","/**\n * Channel driver (WI-CHAN-1 / WI-CHAN-2 / WI-CHAN-3 / WI-CHAN-4).\n *\n * The CLI is the channel-driver for headless channels (Slack/WhatsApp): there\n * is no browser driving the session, so something co-located with `opencode`\n * must send the message, detect completion, fetch the reply, and notify Evident\n * — which delivers it back to the originating thread (ADR-0039, approach 2).\n *\n * This module extracts that slice out of `commands/run.ts`, decoupled from the\n * (deleted) lock / queue-heartbeat / event-subscription machinery. It owns:\n *\n * - the blocking `POST /session/:id/message` call to LOOPBACK `opencode serve`\n * (`http://127.0.0.1:<port>`, NOT `localhost` — IPv4 loopback only);\n * - the EXISTING `combinedAuth` completion callback\n * `PATCH /v1/agents/:agentId/threads/:threadId/messages/:messageId\n * {status:'done', opencode_session_id}` (which already fires\n * `notifyMessageCompleted`). It does NOT call the `X-Internal-Secret`-gated\n * `/internal/*` routes (unreachable by the CLI — locked decision 2);\n * - the EXISTING `combinedAuth` interaction callback\n * `POST /v1/agents/:agentId/threads/:threadId/interactive-event`;\n * - retrying idempotent callbacks (exponential backoff + jitter, capped, NO\n * on-disk persistence — WI-CHAN-2);\n * - local reconcile via `GET /session/:id/message` (message-level completion:\n * the last assistant message's `info.time.completed`) when the blocking\n * call's return is unreliable (WI-CHAN-2);\n * - draining the server-side offline queue on tunnel (re)connect (WI-CHAN-4).\n */\n\nimport {\n createOpenCodeSession,\n getOpenCodeDirectory,\n sendMessageToOpenCode,\n isTurnComplete,\n type OpenCodeMessage,\n type OpenCodeQuestion,\n type OpenCodePermission,\n} from '../opencode/index.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChannelDriverConfig {\n /** Agent ID this driver runs for. */\n agentId: string;\n /** Loopback port `opencode serve` is listening on (127.0.0.1:<port>). */\n port: number;\n /** Evident REST API base URL (e.g. `https://api.localhost/v1`). */\n apiUrl: string;\n /**\n * Authorization header value for the EXISTING combinedAuth thread routes\n * (`ct_` device token / `SandboxKey esk_`). Resolved lazily so a refreshed\n * token is always picked up.\n */\n getAuthHeader: () => string;\n /** Optional filter: only drive this conversation id. */\n conversationFilter?: string | null;\n /** Retry policy for the idempotent callbacks (test override). */\n retry?: Partial<RetryPolicy>;\n /** Structured logger (no-op by default). */\n log?: (entry: ChannelDriverLogEntry) => void;\n /**\n * Injectable `fetch` (test override). Defaults to the global `fetch`.\n */\n fetchImpl?: typeof fetch;\n /**\n * Sleep function (test override) so backoff waits can be made deterministic.\n */\n sleep?: (ms: number) => Promise<void>;\n /**\n * Poll interval (ms) for the paused-session watcher (WI-2-CLI). Test override.\n */\n pausedPollIntervalMs?: number;\n /**\n * Max time (ms) the paused-session watcher waits for the SAME turn to finish\n * before giving up and leaving the message `processing` for the cron safety\n * net (WI-2-CLI). MUST stay strictly below the 15-min cron reset. Test override.\n */\n pausedMaxWaitMs?: number;\n}\n\nexport interface ChannelDriverLogEntry {\n level: 'info' | 'error';\n message: string;\n conversation_id?: string;\n message_id?: string;\n}\n\nexport interface RetryPolicy {\n /** Maximum number of attempts (including the first). */\n maxAttempts: number;\n /** Base delay in ms for the first retry. */\n baseDelayMs: number;\n /** Hard cap on any single backoff delay. */\n maxDelayMs: number;\n}\n\nexport const DEFAULT_RETRY_POLICY: RetryPolicy = {\n maxAttempts: 6,\n baseDelayMs: 500,\n maxDelayMs: 30_000,\n};\n\n/** Default poll interval for the paused-session watcher (WI-2-CLI). */\nexport const DEFAULT_PAUSED_POLL_INTERVAL_MS = 2_000;\n\n/**\n * Default max wait for the paused-session watcher (WI-2-CLI): 10 minutes.\n *\n * Deliberately STRICTLY LESS than the 15-minute lifecycle cron reset\n * (`apps/api-worker/src/cron/lifecycle.ts`): if the watcher were allowed to live\n * up to (or beyond) the cron threshold, the cron could reset the still-`processing`\n * row to `pending` and RE-SEND the original message as a NEW turn while the\n * watcher is simultaneously about to complete the SAME turn — a double-drive race.\n * Capping at 10 minutes guarantees the watcher always settles (complete or give\n * up) before the cron can act, so the cron remains a pure last-resort safety net.\n */\nexport const DEFAULT_PAUSED_MAX_WAIT_MS = 10 * 60 * 1000;\n\ninterface PendingConversation {\n id: string;\n agent_id: string;\n opencode_session_id: string | null;\n pending_message_count: number;\n oldest_pending_at: string;\n}\n\ninterface QueuedMessage {\n id: string;\n content: string;\n status: string;\n opencode_agent: string | null;\n opencode_model: string | null;\n}\n\n/** Thrown when an Evident API call returns 401/403 (token expired). */\nexport class ChannelAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ChannelAuthError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Backoff helper\n// ---------------------------------------------------------------------------\n\n/**\n * Exponential backoff with full jitter, capped at `maxDelayMs`.\n * delay(attempt) = random(0, min(maxDelayMs, baseDelayMs * 2^attempt))\n * `attempt` is 0-based (0 = the delay before the FIRST retry).\n */\nexport function backoffDelay(attempt: number, policy: RetryPolicy): number {\n const exp = policy.baseDelayMs * Math.pow(2, attempt);\n const capped = Math.min(policy.maxDelayMs, exp);\n return Math.floor(Math.random() * capped);\n}\n\nfunction isRetryableStatus(status: number): boolean {\n // Retry on transient server errors + 429. 4xx (other than 429) are terminal.\n return status === 429 || (status >= 500 && status <= 599);\n}\n\n// ---------------------------------------------------------------------------\n// Channel driver\n// ---------------------------------------------------------------------------\n\nexport class ChannelDriver {\n private readonly agentId: string;\n private readonly port: number;\n private readonly apiUrl: string;\n private readonly getAuthHeader: () => string;\n private readonly conversationFilter: string | null;\n private readonly retry: RetryPolicy;\n private readonly log: (entry: ChannelDriverLogEntry) => void;\n private readonly fetchImpl: typeof fetch;\n private readonly sleep: (ms: number) => Promise<void>;\n private readonly pausedPollIntervalMs: number;\n private readonly pausedMaxWaitMs: number;\n\n /** Cache of conversationId → opencode sessionId. */\n private readonly sessions = new Map<string, string>();\n /**\n * Outstanding paused-session watchers, keyed by `message.id` (WI-2-CLI).\n * Single-flight per message: while a watcher is live for a message we never\n * start a second one. The message stays `processing` for the watcher's lifetime\n * so the `?status=pending` drain cannot double-claim it.\n */\n private readonly watchers = new Map<string, Promise<void>>();\n /**\n * Cache of the opencode root directory (from `GET /path`). Resolved lazily on\n * first session creation so drain-created sessions are rooted at the project\n * directory and thus visible in `opencode web`'s session list. `undefined` =\n * not yet resolved; `null` = resolved-but-unavailable (don't keep retrying).\n */\n private opencodeDirectory: string | null | undefined = undefined;\n /** Serialises drains so a reconnect during a drain doesn't double-process. */\n private draining = false;\n\n constructor(config: ChannelDriverConfig) {\n this.agentId = config.agentId;\n this.port = config.port;\n this.apiUrl = config.apiUrl.replace(/\\/$/, '');\n this.getAuthHeader = config.getAuthHeader;\n this.conversationFilter = config.conversationFilter ?? null;\n this.retry = { ...DEFAULT_RETRY_POLICY, ...config.retry };\n this.log = config.log ?? (() => {});\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.sleep = config.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n this.pausedPollIntervalMs = config.pausedPollIntervalMs ?? DEFAULT_PAUSED_POLL_INTERVAL_MS;\n this.pausedMaxWaitMs = config.pausedMaxWaitMs ?? DEFAULT_PAUSED_MAX_WAIT_MS;\n }\n\n /** The IPv4-loopback base URL for the local `opencode serve`. */\n private get opencodeBase(): string {\n return `http://127.0.0.1:${this.port}`;\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n\n /**\n * Drain all pending channel conversations once: poll → process → callback.\n * Called on tunnel `connected` (WI-CHAN-4) and on each poll tick by `run.ts`.\n * Re-entrant calls while a drain is in flight are skipped (return 0).\n *\n * @returns the number of messages processed.\n */\n async drainPending(): Promise<number> {\n if (this.draining) return 0;\n this.draining = true;\n let processed = 0;\n try {\n const conversations = await this.getPendingConversations();\n if (conversations.length > 0) {\n const total = conversations.reduce((sum, c) => sum + (c.pending_message_count ?? 0), 0);\n this.log({\n level: 'info',\n message: `Found ${total} pending message(s) across ${conversations.length} conversation(s) — draining`,\n });\n }\n for (const conv of conversations) {\n processed += await this.processConversation(conv);\n }\n } finally {\n this.draining = false;\n }\n return processed;\n }\n\n /**\n * Await all outstanding paused-session watchers (WI-2-CLI).\n *\n * In production the watchers are deliberately started-not-awaited so the drain\n * loop never blocks on them and process exit is not held up (the cron recovers\n * any abandoned ones). This helper exists primarily for deterministic tests\n * that need to observe the watcher's effect (the `done` PATCH or its giving up)\n * after a non-blocking `drainPending`. Watchers never reject, so this resolves.\n */\n async flushPausedWatchers(): Promise<void> {\n await Promise.all([...this.watchers.values()]);\n }\n\n // -------------------------------------------------------------------------\n // Conversation processing\n // -------------------------------------------------------------------------\n\n private async processConversation(conv: PendingConversation): Promise<number> {\n const sessionId = await this.ensureSession(conv);\n const messages = await this.getPendingMessages(conv.id);\n let processed = 0;\n\n for (const message of messages) {\n // Claim the message (pending → processing). If another worker already\n // claimed it, skip — the status gate makes this safe.\n const claimed = await this.markProcessing(conv.id, message.id);\n if (!claimed) {\n this.log({\n level: 'info',\n message: `Message ${message.id.slice(0, 8)} already claimed — skipping`,\n conversation_id: conv.id,\n message_id: message.id,\n });\n continue;\n }\n\n try {\n this.log({\n level: 'info',\n message: `Sending queued message ${message.id.slice(0, 8)} to OpenCode (session ${sessionId.slice(0, 8)})`,\n conversation_id: conv.id,\n message_id: message.id,\n });\n const result = await sendMessageToOpenCode(\n this.port,\n sessionId,\n message.content,\n {\n agent: message.opencode_agent ?? undefined,\n model: message.opencode_model ?? undefined,\n },\n {\n onQuestion: (question) => this.reportInteraction(conv.id, 'question', question),\n onPermission: (permission) => this.reportInteraction(conv.id, 'permission', permission),\n },\n );\n\n // WI-11 (C4): the blocking message endpoint ALSO returns when the turn\n // yields for a question/permission — so a plain return is NOT proof the\n // turn finished. If the send ended awaiting an interaction, we must NOT\n // mark the message `done` yet: doing so fires notifyMessageCompleted with\n // no assistant text → a stray \"✅ Done!\" while the user hasn't answered.\n //\n // WI-2-CLI: instead of leaving the row stuck `processing` until the 15-min\n // cron resets+RE-SENDS it as a NEW turn, start a NON-AWAITED watcher that\n // polls the SAME opencode session to completion and then completes the\n // SAME turn (no re-send). The message stays `processing` (we do NOT\n // re-`markProcessing`) so the `?status=pending` drain can't double-claim\n // it while the watcher runs. The watcher is started-not-awaited and\n // tracked by message id so `drainPending` returns promptly (it does NOT\n // hold `this.draining`) and other conversations keep draining.\n if (result.awaitingInteraction) {\n this.log({\n level: 'info',\n message: `Message ${message.id.slice(0, 8)} paused awaiting interaction — watching session for completion`,\n conversation_id: conv.id,\n message_id: message.id,\n });\n this.startPausedWatcher(conv, message, sessionId);\n continue;\n }\n\n // Local reconcile (WI-CHAN-2): the blocking call returning is the\n // primary \"done\" signal, but it can be unreliable (tunnel/process\n // flakiness). Confirm via GET /session/:id/message (the last assistant\n // message's `info.time.completed`) before we mark done — best-effort,\n // never blocks completion.\n await this.confirmCompletion(sessionId);\n\n // EXISTING combinedAuth completion route — fires notifyMessageCompleted.\n // Retried + idempotent (server status/processed_at gate dedups).\n await this.markDone(conv.id, message.id, sessionId);\n processed += 1;\n this.log({\n level: 'info',\n message: `Message ${message.id.slice(0, 8)} processed`,\n conversation_id: conv.id,\n message_id: message.id,\n });\n } catch (err) {\n if (err instanceof ChannelAuthError) throw err;\n await this.markFailed(conv.id, message.id).catch(() => {\n /* failure callback is best-effort */\n });\n this.log({\n level: 'error',\n message: `Message ${message.id.slice(0, 8)} failed: ${err instanceof Error ? err.message : String(err)}`,\n conversation_id: conv.id,\n message_id: message.id,\n });\n }\n }\n\n return processed;\n }\n\n private async ensureSession(conv: PendingConversation): Promise<string> {\n const cached = this.sessions.get(conv.id);\n if (cached) return cached;\n\n if (conv.opencode_session_id) {\n this.sessions.set(conv.id, conv.opencode_session_id);\n return conv.opencode_session_id;\n }\n\n // Root the new session at opencode's `GET /path` directory — the same value\n // Evident uses to build the deep-link into the session (proxy-link.ts) — so\n // the session is guaranteed to appear under `opencode web`'s\n // directory-filtered session list at the link we surface.\n const directory = await this.resolveOpenCodeDirectory();\n const sessionId = await createOpenCodeSession(this.port, directory);\n this.sessions.set(conv.id, sessionId);\n await this.persistSession(conv.id, sessionId).catch(() => {\n /* best-effort: the completion PATCH also carries opencode_session_id */\n });\n return sessionId;\n }\n\n /**\n * Lazily resolve (and cache) opencode's root directory via `GET /path`.\n * Resolved once per driver: `undefined` until first lookup, then the directory\n * string or `null` if unavailable (we don't keep retrying a missing `/path`).\n */\n private async resolveOpenCodeDirectory(): Promise<string | null> {\n if (this.opencodeDirectory !== undefined) return this.opencodeDirectory;\n this.opencodeDirectory = await getOpenCodeDirectory(this.port);\n if (!this.opencodeDirectory) {\n this.log({\n level: 'info',\n message:\n 'Could not determine opencode directory (GET /path) — new sessions may not appear in opencode web',\n });\n }\n return this.opencodeDirectory;\n }\n\n /**\n * Local reconcile: re-query `GET /session/:id/message` and check whether the\n * last assistant message is message-level complete (`isTurnComplete`).\n *\n * This is a PURE OBSERVABILITY probe on the normal (non-paused) path: the\n * blocking POST already returned, and `markDone` causes the server to re-fetch\n * the messages itself (via `extractTextFromMessages`) when delivering the\n * reply — so this round-trip never gates delivery. We keep it only to surface a\n * truthful diagnostic when opencode hasn't yet recorded a completed assistant\n * turn at reconcile time, then proceed to `markDone` regardless. Uses the\n * injected `fetchImpl` and reuses only the pure `isTurnComplete` predicate.\n */\n private async confirmCompletion(sessionId: string): Promise<void> {\n try {\n const res = await this.fetchImpl(`${this.opencodeBase}/session/${sessionId}/message`);\n if (!res.ok) return;\n const body = await res.json();\n const messages = Array.isArray(body) ? (body as OpenCodeMessage[]) : null;\n if (!isTurnComplete(messages)) {\n // Messages do not yet show a completed assistant turn despite the\n // blocking call returning. Surface for observability but do not block\n // delivery — the server re-fetches the reply on the `done` PATCH.\n this.log({\n level: 'info',\n message: `Session ${sessionId.slice(0, 8)} messages do not yet show a completed assistant turn on reconcile — delivering anyway`,\n });\n }\n } catch {\n // Non-fatal: reconcile is a best-effort confirmation.\n }\n }\n\n // -------------------------------------------------------------------------\n // Paused-session watcher (WI-2-CLI)\n // -------------------------------------------------------------------------\n\n /**\n * Start (but do NOT await) a watcher that resumes a paused turn to completion.\n *\n * Single-flight per message: if a watcher is already live for this message we\n * skip. The returned watcher promise is tracked in `this.watchers` and removed\n * when it settles; it never rejects (the body is fully guarded), so a failed\n * poll/markDone can never crash the run loop — the cron stays as the safety net.\n */\n private startPausedWatcher(\n conv: PendingConversation,\n message: QueuedMessage,\n sessionId: string,\n ): void {\n if (this.watchers.has(message.id)) return;\n const watcher = this.watchPausedSession(conv, message, sessionId).finally(() => {\n this.watchers.delete(message.id);\n });\n this.watchers.set(message.id, watcher);\n }\n\n /**\n * Poll `GET /session/:id/message` until the SAME paused turn is message-level\n * complete — the last message is an ASSISTANT message whose\n * `info.time.completed` is set (the user answered the question/permission in\n * opencode web and opencode finished the turn) — then complete it via the\n * EXISTING `markDone` PATCH — NO re-send of the original message. The server\n * re-fetches the assistant messages on completion, so the watcher does not\n * pass any reply text.\n *\n * Fetch seam: the poll uses the injected `this.fetchImpl` (preserving the\n * tests' injection) and reuses only the pure `isTurnComplete` predicate from\n * `session.ts` — we do NOT call `getSessionMessages` (which uses the global\n * `fetch`) here.\n *\n * Bounded by `pausedMaxWaitMs` (default 10 min, strictly < the 15-min cron\n * reset): on timeout we STOP and leave the message `processing` so the cron\n * remains the last-resort safety net. The whole body is wrapped so any\n * poll/markDone failure is logged and swallowed — a watcher MUST NEVER throw\n * out of the run loop.\n */\n private async watchPausedSession(\n conv: PendingConversation,\n message: QueuedMessage,\n sessionId: string,\n ): Promise<void> {\n const deadline = Date.now() + this.pausedMaxWaitMs;\n try {\n while (Date.now() < deadline) {\n await this.sleep(this.pausedPollIntervalMs);\n\n let completed = false;\n try {\n const res = await this.fetchImpl(`${this.opencodeBase}/session/${sessionId}/message`);\n if (res.ok) {\n const body = await res.json();\n const messages = Array.isArray(body) ? (body as OpenCodeMessage[]) : null;\n completed = isTurnComplete(messages);\n }\n } catch {\n // Non-fatal: a transient poll failure just means we try again next tick.\n continue;\n }\n\n if (!completed) continue;\n\n // The SAME turn finished after the user answered in web. Complete it via\n // the EXISTING completion path (no re-send). `confirmCompletion` is\n // redundant here (we already observed message-level completion) so we\n // skip it and go straight to the idempotent markDone, which fires\n // notifyMessageCompleted.\n this.log({\n level: 'info',\n message: `Paused session ${sessionId.slice(0, 8)} completed — marking message ${message.id.slice(0, 8)} done`,\n conversation_id: conv.id,\n message_id: message.id,\n });\n await this.markDone(conv.id, message.id, sessionId);\n return;\n }\n\n // Timed out: leave the message `processing` so the 15-min lifecycle cron is\n // the last-resort recovery. The watcher is the primary path; the cron is the\n // fallback, not the norm.\n this.log({\n level: 'info',\n message: `Paused session ${sessionId.slice(0, 8)} did not complete within the watch window — leaving message ${message.id.slice(0, 8)} for the cron safety net`,\n conversation_id: conv.id,\n message_id: message.id,\n });\n } catch (err) {\n // A watcher failure must NEVER crash the run loop — log and swallow; the\n // cron safety net still recovers the stuck `processing` row.\n this.log({\n level: 'error',\n message: `Paused-session watcher failed for message ${message.id.slice(0, 8)}: ${err instanceof Error ? err.message : String(err)}`,\n conversation_id: conv.id,\n message_id: message.id,\n });\n }\n }\n\n // -------------------------------------------------------------------------\n // Evident API calls (combinedAuth thread routes)\n // -------------------------------------------------------------------------\n\n private async getPendingConversations(): Promise<PendingConversation[]> {\n const res = await this.fetchImpl(\n `${this.apiUrl}/agents/${this.agentId}/conversations/pending`,\n {\n headers: { Authorization: this.getAuthHeader() },\n },\n );\n this.assertAuth(res, 'fetching pending conversations');\n if (!res.ok) {\n throw new Error(`Failed to get pending conversations: HTTP ${res.status}`);\n }\n const data = (await res.json()) as { conversations: PendingConversation[] };\n let conversations = data.conversations;\n if (this.conversationFilter) {\n conversations = conversations.filter((c) => c.id === this.conversationFilter);\n }\n return conversations;\n }\n\n private async getPendingMessages(conversationId: string): Promise<QueuedMessage[]> {\n const res = await this.fetchImpl(\n `${this.apiUrl}/agents/${this.agentId}/threads/${conversationId}/messages?status=pending`,\n { headers: { Authorization: this.getAuthHeader() } },\n );\n this.assertAuth(res, 'fetching pending messages');\n if (!res.ok) {\n throw new Error(`Failed to get messages: HTTP ${res.status}`);\n }\n return (await res.json()) as QueuedMessage[];\n }\n\n private async markProcessing(conversationId: string, messageId: string): Promise<boolean> {\n const res = await this.fetchImpl(\n `${this.apiUrl}/agents/${this.agentId}/threads/${conversationId}/messages/${messageId}`,\n {\n method: 'PATCH',\n headers: { Authorization: this.getAuthHeader(), 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'processing' }),\n },\n );\n this.assertAuth(res, 'marking message as processing');\n return res.ok;\n }\n\n /**\n * EXISTING combinedAuth completion route — idempotent + retried (WI-CHAN-2).\n * `PATCH .../messages/:id {status:'done', opencode_session_id}`. The server's\n * `queued_conversation_messages.status`/`processed_at` gate makes a re-call\n * for an already-`done` message a no-op (no double Slack post).\n */\n private async markDone(\n conversationId: string,\n messageId: string,\n sessionId: string,\n ): Promise<void> {\n await this.callWithRetry('marking message as done', () =>\n this.fetchImpl(\n `${this.apiUrl}/agents/${this.agentId}/threads/${conversationId}/messages/${messageId}`,\n {\n method: 'PATCH',\n headers: { Authorization: this.getAuthHeader(), 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'done', opencode_session_id: sessionId }),\n },\n ),\n );\n }\n\n private async markFailed(conversationId: string, messageId: string): Promise<void> {\n await this.callWithRetry('marking message as failed', () =>\n this.fetchImpl(\n `${this.apiUrl}/agents/${this.agentId}/threads/${conversationId}/messages/${messageId}`,\n {\n method: 'PATCH',\n headers: { Authorization: this.getAuthHeader(), 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'failed' }),\n },\n ),\n );\n }\n\n private async persistSession(conversationId: string, sessionId: string): Promise<void> {\n const res = await this.fetchImpl(\n `${this.apiUrl}/agents/${this.agentId}/threads/${conversationId}`,\n {\n method: 'PATCH',\n headers: { Authorization: this.getAuthHeader(), 'Content-Type': 'application/json' },\n body: JSON.stringify({ opencode_session_id: sessionId }),\n },\n );\n this.assertAuth(res, 'persisting session id');\n }\n\n /**\n * EXISTING combinedAuth interaction route (WI-CHAN-3) — idempotent + retried.\n * `POST .../interactive-event {type, data}`. The server persists the\n * interaction and posts a link to the proxied opencode-web conversation.\n */\n private async reportInteraction(\n conversationId: string,\n type: 'question' | 'permission',\n data: OpenCodeQuestion | OpenCodePermission,\n ): Promise<void> {\n try {\n await this.callWithRetry('reporting interactive event', () =>\n this.fetchImpl(\n `${this.apiUrl}/agents/${this.agentId}/threads/${conversationId}/interactive-event`,\n {\n method: 'POST',\n headers: { Authorization: this.getAuthHeader(), 'Content-Type': 'application/json' },\n body: JSON.stringify({ type, data }),\n },\n ),\n );\n this.log({\n level: 'info',\n message: `${type} surfaced to channel (id: ${data.id.slice(0, 8)})`,\n conversation_id: conversationId,\n });\n } catch (err) {\n if (err instanceof ChannelAuthError) throw err;\n // Interactions are surfaced best-effort: a failed callback must not abort\n // the in-flight blocking message request.\n this.log({\n level: 'error',\n message: `Failed to surface ${type}: ${err instanceof Error ? err.message : String(err)}`,\n conversation_id: conversationId,\n });\n }\n }\n\n // -------------------------------------------------------------------------\n // Retry wrapper\n // -------------------------------------------------------------------------\n\n /**\n * Invoke an Evident API call, retrying on transient failures (5xx / 429 /\n * network errors) with exponential backoff + jitter (capped). Auth failures\n * (401/403) are terminal and surface as `ChannelAuthError`; other 4xx are\n * terminal too. No on-disk persistence — a crash mid-retry drops the callback\n * (accepted by ADR-0039).\n */\n private async callWithRetry(context: string, call: () => Promise<Response>): Promise<void> {\n let lastError: unknown;\n for (let attempt = 0; attempt < this.retry.maxAttempts; attempt += 1) {\n let res: Response | undefined;\n try {\n res = await call();\n } catch (err) {\n // Network-level failure — retryable.\n lastError = err;\n if (attempt < this.retry.maxAttempts - 1) {\n await this.sleep(backoffDelay(attempt, this.retry));\n continue;\n }\n throw err;\n }\n\n if (res.status === 401 || res.status === 403) {\n throw new ChannelAuthError(\n `Authentication failed during ${context}: HTTP ${res.status}. Your session may have expired.`,\n );\n }\n\n if (res.ok) return;\n\n if (isRetryableStatus(res.status)) {\n lastError = new Error(`${context}: HTTP ${res.status}`);\n if (attempt < this.retry.maxAttempts - 1) {\n await this.sleep(backoffDelay(attempt, this.retry));\n continue;\n }\n }\n\n // Terminal non-retryable status (other 4xx) — fail immediately.\n throw new Error(`${context}: HTTP ${res.status}`);\n }\n\n throw lastError instanceof Error ? lastError : new Error(`${context}: exhausted retries`);\n }\n\n private assertAuth(res: Response, context: string): void {\n if (res.status === 401 || res.status === 403) {\n throw new ChannelAuthError(\n `Authentication failed during ${context}: HTTP ${res.status}. Your session may have expired.`,\n );\n }\n }\n}\n","/**\n * Ensure `opencode serve` is running on loopback (WI-THIN-1 / RUN-1).\n *\n * Extracted from `commands/run.ts` to keep the command thin. Detects a healthy\n * loopback `opencode serve`, and — depending on interactivity — auto-starts it\n * (CI) or guides the user through starting it (interactive). `startOpenCode`\n * binds `127.0.0.1` only, with no server password (ADR-0039).\n *\n * NOTE: this module imports the opencode helpers from the `../lib/opencode`\n * barrel so they are mockable as a unit in tests, while `run.ts` imports\n * `ensureOpenCodeRunning` from THIS module (not the barrel) so the real\n * orchestration runs.\n */\n\nimport { ChildProcess } from 'child_process';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { select } from '@inquirer/prompts';\nimport { getCliName } from '../lib/config.js';\nimport { blank } from '../utils/ui.js';\nimport {\n checkOpenCodeHealth,\n waitForOpenCodeHealth,\n startOpenCode,\n findHealthyOpenCodeInstances,\n isPortInUse,\n findAvailablePort,\n isOpenCodeInstalled,\n promptOpenCodeInstall,\n} from '../lib/opencode/index.js';\n\nexport interface EnsureOpenCodeContext {\n /** Desired loopback port. May be mutated by the interactive port-conflict flow. */\n port: number;\n interactive: boolean;\n agentId: string;\n /** Logger used for non-interactive progress lines. */\n log: (message: string) => void;\n}\n\nexport interface EnsureOpenCodeResult {\n /** The port opencode is (or will be) listening on — may differ from the input. */\n port: number;\n /** The spawned process, if this call started one (null if already running). */\n process: ChildProcess | null;\n /** The detected/started opencode version, if known. */\n version: string | null;\n}\n\n/**\n * Ensure a healthy loopback `opencode serve` is available.\n *\n * @throws if opencode cannot be reached/started (caller surfaces the error).\n */\nexport async function ensureOpenCodeRunning(\n ctx: EnsureOpenCodeContext,\n): Promise<EnsureOpenCodeResult> {\n const healthCheck = await checkOpenCodeHealth(ctx.port);\n if (healthCheck.healthy) {\n return { port: ctx.port, process: null, version: healthCheck.version ?? null };\n }\n\n // Already running on a different port? Guide the user to the right --port.\n const runningInstances = await findHealthyOpenCodeInstances();\n if (runningInstances.length > 0) {\n if (!ctx.interactive) {\n throw new Error(\n `OpenCode not found on port ${ctx.port}, but running on port ${runningInstances[0].port}. ` +\n `Use --port ${runningInstances[0].port}`,\n );\n }\n\n blank();\n console.log(chalk.yellow('Found OpenCode running on different port(s):'));\n for (const instance of runningInstances) {\n const ver = instance.version ? ` (v${instance.version})` : '';\n const cwd = instance.cwd ? ` in ${instance.cwd}` : '';\n console.log(chalk.dim(` * Port ${instance.port}${ver}${cwd}`));\n }\n blank();\n if (runningInstances.length === 1) {\n console.log(chalk.yellow('Tip: Run with the correct port:'));\n console.log(\n chalk.dim(\n ` ${getCliName()} run --agent ${ctx.agentId} --port ${runningInstances[0].port}`,\n ),\n );\n }\n blank();\n throw new Error(`OpenCode not running on port ${ctx.port}`);\n }\n\n // Not running anywhere — ensure it's installed.\n if (!isOpenCodeInstalled()) {\n if (!ctx.interactive) {\n throw new Error('OpenCode is not installed. Install it with: npm install -g opencode-ai');\n }\n const result = await promptOpenCodeInstall(true);\n if (result === 'exit') process.exit(0);\n if (result !== 'installed' && !isOpenCodeInstalled()) {\n throw new Error('OpenCode is not installed');\n }\n }\n\n if (!ctx.interactive) {\n // CI: auto-start rather than failing.\n ctx.log(`OpenCode is not running on port ${ctx.port}. Starting it automatically...`);\n const proc = await startOpenCode(ctx.port);\n const health = await waitForOpenCodeHealth(ctx.port, 30000);\n if (!health.healthy) {\n throw new Error(\n `OpenCode failed to start on port ${ctx.port}. Install with: npm install -g opencode-ai`,\n );\n }\n ctx.log(`OpenCode started on port ${ctx.port}${health.version ? ` (v${health.version})` : ''}`);\n return { port: ctx.port, process: proc, version: health.version ?? null };\n }\n\n // Interactive: resolve a port conflict, then offer to start / show / continue.\n let port = ctx.port;\n if (isPortInUse(port)) {\n console.log(chalk.yellow(`\\nPort ${port} is already in use.`));\n const alternativePort = findAvailablePort(port + 1);\n if (alternativePort) {\n const useAlternative = await select({\n message: `Use port ${alternativePort} instead?`,\n choices: [\n { name: `Yes, use port ${alternativePort}`, value: 'yes' },\n { name: 'No, I will free the port manually', value: 'no' },\n ],\n });\n if (useAlternative === 'yes') {\n port = alternativePort;\n } else {\n throw new Error(`Port ${ctx.port} is in use`);\n }\n }\n }\n\n const action = await select({\n message: 'OpenCode is not running. What would you like to do?',\n choices: [\n {\n name: 'Start OpenCode for me',\n value: 'start',\n description: `Run 'opencode serve --port ${port}'`,\n },\n {\n name: 'Show me the command',\n value: 'manual',\n description: 'Display the command to run manually',\n },\n {\n name: 'Continue without OpenCode',\n value: 'continue',\n description: 'Requests will fail until OpenCode starts',\n },\n ],\n });\n\n if (action === 'manual') {\n blank();\n console.log(chalk.bold('Run this command in another terminal:'));\n blank();\n console.log(` ${chalk.cyan(`opencode serve --port ${port}`)}`);\n blank();\n throw new Error('Please start OpenCode manually');\n }\n\n if (action === 'start') {\n const spinner = ora('Starting OpenCode...').start();\n const proc = await startOpenCode(port);\n const health = await waitForOpenCodeHealth(port, 30000);\n if (!health.healthy) {\n spinner.fail('Failed to start OpenCode');\n throw new Error('OpenCode failed to start');\n }\n // Stop (don't `succeed`) the transient progress spinner: the caller owns the\n // final \"OpenCode running on port ...\" line, so succeeding here would print\n // it twice (once here, once via the caller's spinner).\n spinner.stop();\n return { port, process: proc, version: health.version ?? null };\n }\n\n // 'continue' — proceed without a confirmed-healthy opencode.\n return { port, process: null, version: null };\n}\n","/**\n * Agent lookup helpers (WI-THIN-1).\n *\n * Small REST helpers used by `evident run` to resolve and validate the target\n * agent before connecting. Extracted from `commands/run.ts` to keep it thin.\n *\n * Error handling principle (development-workflow.mdc — \"Don't swallow errors\"):\n * these helpers always surface the *real* reason a request failed. The API\n * returns a JSON body with an `error`/`message` field; we read it and include it\n * in the message rather than collapsing every non-2xx into a vague, often\n * misleading label (e.g. rendering a 401 \"Invalid token\" as \"Agent not found\").\n */\n\nimport { getApiUrlConfig } from '../lib/config.js';\n\nexport interface AgentInfo {\n id: string;\n name: string;\n agent_type: 'local';\n status: string;\n}\n\n/**\n * Best-effort extraction of the human-readable error message from an API\n * response body. The api-worker returns `{ \"error\": \"...\" }`; the legacy\n * NestJS API returns `{ \"message\": \"...\", \"error\": \"...\", \"statusCode\": ... }`.\n * Falls back to the raw text, then to the HTTP status text.\n */\nasync function readErrorMessage(response: Response): Promise<string | undefined> {\n const text = await response.text().catch(() => '');\n if (!text) return response.statusText || undefined;\n\n try {\n const data = JSON.parse(text) as { error?: unknown; message?: unknown };\n const message = data.message ?? data.error;\n if (typeof message === 'string' && message.trim()) {\n return message;\n }\n } catch {\n // Not JSON — fall through to returning the raw body.\n }\n\n return text.trim() || response.statusText || undefined;\n}\n\n/**\n * Build a hint appended to auth-failure messages. A token that the server\n * rejects is most often a token minted against a *different* environment than\n * the one `--endpoint` points at (dev vs production each have their own token\n * store), or one that has been revoked/expired. Surfacing this turns an opaque\n * 401 into an actionable next step.\n */\nfunction authFailureHint(apiUrl: string, serverMessage?: string): string {\n const reason = serverMessage ? `: ${serverMessage}` : '';\n return (\n `Authentication failed${reason}. ` +\n `Your credentials were rejected by ${apiUrl}. ` +\n `This usually means you logged in against a different environment, or your ` +\n `session expired — log in again pointing at this endpoint and retry.`\n );\n}\n\n/**\n * Resolve the agent ID from an agent key via the /v1/me endpoint.\n * Only works when authenticated with EVIDENT_AGENT_KEY (agent_key auth type).\n */\nexport async function resolveAgentIdFromKey(\n authHeader: string,\n): Promise<{ agent_id?: string; error?: string; authFailed?: boolean }> {\n const apiUrl = getApiUrlConfig();\n try {\n const response = await fetch(`${apiUrl}/me`, {\n headers: { Authorization: authHeader },\n });\n\n if (response.status === 401) {\n const serverMessage = await readErrorMessage(response);\n return { error: authFailureHint(apiUrl, serverMessage), authFailed: true };\n }\n\n if (!response.ok) {\n const serverMessage = await readErrorMessage(response);\n return {\n error: `Failed to resolve agent from key (HTTP ${response.status})${\n serverMessage ? `: ${serverMessage}` : ''\n }`,\n };\n }\n\n const data = (await response.json()) as { auth_type: string; agent_id?: string };\n if (data.auth_type === 'agent_key' && data.agent_id) {\n return { agent_id: data.agent_id };\n }\n\n return {\n error:\n 'Cannot resolve agent ID: auth type is not agent_key. Please provide --agent explicitly.',\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return { error: `Failed to resolve agent from key: ${message}` };\n }\n}\n\n/**\n * Validate the agent exists and is a `local` agent reachable over the tunnel.\n */\nexport async function getAgentInfo(\n agentId: string,\n authHeader: string,\n): Promise<{ valid: boolean; agent?: AgentInfo; error?: string; authFailed?: boolean }> {\n const apiUrl = getApiUrlConfig();\n\n try {\n const response = await fetch(`${apiUrl}/agents/${agentId}`, {\n headers: { Authorization: authHeader },\n });\n\n // 401 — the credentials themselves were rejected. NEVER report this as\n // \"Agent not found\": the agent lookup never even ran. Surface the server's\n // real reason plus an environment-mismatch hint.\n if (response.status === 401) {\n const serverMessage = await readErrorMessage(response);\n return { valid: false, error: authFailureHint(apiUrl, serverMessage), authFailed: true };\n }\n\n // 403 — authenticated, but this identity isn't allowed to see the agent\n // (e.g. it belongs to a different team/org than the one the credentials\n // resolve to). Distinct from \"not found\"; surface the server's message.\n if (response.status === 403) {\n const serverMessage = await readErrorMessage(response);\n return {\n valid: false,\n error:\n serverMessage ??\n 'You do not have access to this agent (it may belong to a different team or organization).',\n };\n }\n\n if (response.status === 404) {\n const serverMessage = await readErrorMessage(response);\n return { valid: false, error: serverMessage ?? `Agent ${agentId} not found` };\n }\n\n if (!response.ok) {\n const serverMessage = await readErrorMessage(response);\n return {\n valid: false,\n error: `API error (HTTP ${response.status})${serverMessage ? `: ${serverMessage}` : ''}`,\n };\n }\n\n const agent = (await response.json()) as AgentInfo;\n\n if (agent.agent_type !== 'local') {\n return {\n valid: false,\n error: `Agent is type '${agent.agent_type}', must be 'local' for CLI connection`,\n };\n }\n\n return { valid: true, agent };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return { valid: false, error: `Failed to validate agent: ${message}` };\n }\n}\n"],"mappings":"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","names":["chalk","error","credentials","error","chalk","credentials","chalk","credentials","chalk","chalk","ora","select","credentials","error","credentials","error","execSync","chalk","execSync","chalk","WebSocket","error","WebSocket","error","config","chalk","ora","select","chalk","select","ora","error","chalk","select","credentials","error","ora"]}
|