@bytespell/amux 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cli.js +10 -2171
- package/dist/bin/cli.js.map +1 -1
- package/dist/chunk-226DBKL3.js +26 -0
- package/dist/chunk-226DBKL3.js.map +1 -0
- package/dist/chunk-2NON2HR2.js +1602 -0
- package/dist/chunk-2NON2HR2.js.map +1 -0
- package/dist/chunk-5IPYOXBE.js +32 -0
- package/dist/chunk-5IPYOXBE.js.map +1 -0
- package/dist/chunk-L4DBPVMA.js +122 -0
- package/dist/chunk-L4DBPVMA.js.map +1 -0
- package/dist/chunk-OQ5K5ON2.js +319 -0
- package/dist/chunk-OQ5K5ON2.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-SX7NC3ZM.js +65 -0
- package/dist/chunk-SX7NC3ZM.js.map +1 -0
- package/dist/chunk-YYN3GXYP.js +333 -0
- package/dist/chunk-YYN3GXYP.js.map +1 -0
- package/dist/src/agents/eventStore.js +12 -344
- package/dist/src/agents/eventStore.js.map +1 -1
- package/dist/src/agents/manager.js +7 -1740
- package/dist/src/agents/manager.js.map +1 -1
- package/dist/src/db/index.js +5 -286
- package/dist/src/db/index.js.map +1 -1
- package/dist/src/lib/logger.js +18 -0
- package/dist/src/lib/logger.js.map +1 -0
- package/dist/src/server.js +10 -2168
- package/dist/src/server.js.map +1 -1
- package/dist/src/trpc/files.js +6 -396
- package/dist/src/trpc/files.js.map +1 -1
- package/dist/src/types.js +6 -19
- package/dist/src/types.js.map +1 -1
- package/package.json +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/agents/manager.ts","../../../src/db/index.ts","../../../src/lib/paths.ts","../../../src/db/schema.ts","../../../src/db/seed.ts","../../../src/types.ts","../../../src/agents/eventStore.ts","../../../src/agents/backends/acp.ts","../../../src/agents/process.ts","../../../src/agents/backends/mock.ts","../../../src/agents/backends/stress.ts","../../../src/stress/generators.ts","../../../src/stress/config.ts","../../../src/lib/mentions.ts"],"sourcesContent":["import { EventEmitter } from 'events';\nimport { db } from '../db/index.js';\nimport { sessions, agentConfigs } from '../db/schema.js';\nimport { eq } from 'drizzle-orm';\nimport type { WindowUpdate } from '../types.js';\nimport { isSessionUpdate } from '../types.js';\nimport { startTurn, storeEvent, endTurn, getEventsForSession } from './eventStore.js';\nimport { AcpBackend, MockBackend, StressBackend } from './backends/index.js';\nimport type { AgentBackend, AgentConfig, AgentSession } from './backends/index.js';\nimport { parseMessageToContentBlocks } from '../lib/mentions.js';\n\n// Generate a title from the first user message (fallback when agent doesn't send session_info_update)\nfunction generateTitleFromMessage(message: string): string {\n // Remove @mentions, trim, and take first line or first ~50 chars\n const cleaned = message\n .replace(/@[\\w./~-]+/g, '') // Remove @mentions\n .replace(/\\s+/g, ' ') // Normalize whitespace\n .trim();\n\n // Take first line if multi-line\n const firstLine = cleaned.split('\\n')[0] || cleaned;\n\n // Truncate to ~50 chars at word boundary\n if (firstLine.length <= 50) return firstLine;\n\n const truncated = firstLine.slice(0, 50);\n const lastSpace = truncated.lastIndexOf(' ');\n return lastSpace > 20 ? truncated.slice(0, lastSpace) + '...' : truncated + '...';\n}\n\n// Event emitted when a session update occurs\nexport interface SessionEvent {\n sessionId: string;\n update: WindowUpdate;\n}\n\ninterface AgentEntry {\n backend: AgentBackend;\n session: AgentSession;\n status: 'starting' | 'ready' | 'dead';\n}\n\nclass AgentManager extends EventEmitter {\n private backends: AgentBackend[];\n private agents = new Map<string, AgentEntry>();\n\n constructor() {\n super();\n const acpBackend = new AcpBackend();\n acpBackend.onSessionIdChanged = (sessionId, acpSessionId) => {\n db.update(sessions)\n .set({ acpSessionId })\n .where(eq(sessions.id, sessionId))\n .run();\n };\n\n this.backends = [acpBackend, new MockBackend(), new StressBackend()];\n }\n\n private getBackendForConfig(config: AgentConfig): AgentBackend {\n const backend = this.backends.find(b => b.matches(config));\n if (!backend) {\n throw new Error(`No backend for agent config: ${config.command}`);\n }\n return backend;\n }\n\n emitUpdate(sessionId: string, update: WindowUpdate) {\n storeEvent(sessionId, update);\n\n // Persist session title from session_info_update\n if (isSessionUpdate(update) && update.sessionUpdate === 'session_info_update') {\n const title = (update as { title?: string }).title;\n if (title !== undefined) {\n db.update(sessions)\n .set({ title })\n .where(eq(sessions.id, sessionId))\n .run();\n }\n }\n\n this.emit('update', { sessionId, update } satisfies SessionEvent);\n }\n\n async startForSession(sessionId: string): Promise<{\n acpSessionId: string;\n replayEvents: WindowUpdate[];\n models?: Array<{ modelId: string; name: string }>;\n modes?: Array<{ id: string; name: string; description?: string }>;\n }> {\n const existing = this.agents.get(sessionId);\n\n if (existing?.status === 'ready') {\n // Already ready - return stored events for hydration\n const replayEvents = getEventsForSession(sessionId);\n return {\n acpSessionId: existing.session.sessionId,\n replayEvents,\n models: existing.session.models,\n modes: existing.session.modes,\n };\n }\n\n if (existing?.status === 'starting') {\n return new Promise((resolve, reject) => {\n const checkReady = () => {\n const agent = this.agents.get(sessionId);\n if (agent?.status === 'ready') {\n const replayEvents = getEventsForSession(sessionId);\n resolve({\n acpSessionId: agent.session.sessionId,\n replayEvents,\n models: agent.session.models,\n modes: agent.session.modes,\n });\n } else if (agent?.status === 'dead') {\n reject(new Error('Agent failed to start'));\n } else {\n setTimeout(checkReady, 100);\n }\n };\n checkReady();\n });\n }\n\n const session = db.select().from(sessions).where(eq(sessions.id, sessionId)).get();\n if (!session) throw new Error(`Session ${sessionId} not found`);\n\n const dbConfig = db.select().from(agentConfigs).where(eq(agentConfigs.id, session.agentConfigId)).get();\n if (!dbConfig) throw new Error(`Agent config ${session.agentConfigId} not found`);\n\n const config: AgentConfig = {\n id: dbConfig.id,\n name: dbConfig.name,\n command: dbConfig.command,\n args: dbConfig.args ?? [],\n env: (dbConfig.env as Record<string, string>) ?? {},\n };\n\n const backend = this.getBackendForConfig(config);\n const emit = (update: WindowUpdate) => this.emitUpdate(sessionId, update);\n\n this.agents.set(sessionId, {\n backend,\n session: { sessionId: '' },\n status: 'starting',\n });\n\n // Get stored events for replay (returned in response, not emitted via WebSocket)\n const replayEvents = getEventsForSession(sessionId);\n\n try {\n const agentSession = await backend.start(sessionId, config, session.directory, session.acpSessionId ?? null, emit);\n\n this.agents.set(sessionId, {\n backend,\n session: agentSession,\n status: 'ready',\n });\n\n console.log(`[agents] Agent ready for session ${sessionId}`);\n\n return {\n acpSessionId: agentSession.sessionId,\n replayEvents,\n models: agentSession.models,\n modes: agentSession.modes,\n };\n } catch (err) {\n this.agents.set(sessionId, {\n backend,\n session: { sessionId: '' },\n status: 'dead',\n });\n throw err;\n }\n }\n\n // Legacy alias for compatibility during migration\n /** @deprecated Use startForSession instead */\n startForWindow = this.startForSession.bind(this);\n\n async prompt(sessionId: string, message: string): Promise<void> {\n console.log(`[agents] prompt() called for session ${sessionId}: \"${message.slice(0, 50)}...\"`);\n\n const agent = this.agents.get(sessionId);\n if (!agent || agent.status !== 'ready') {\n throw new Error(`Agent not ready for session ${sessionId}`);\n }\n\n // Get session directory for @mention path resolution\n const session = db.select().from(sessions).where(eq(sessions.id, sessionId)).get();\n if (!session) throw new Error(`Session ${sessionId} not found`);\n\n // Auto-generate title from first message if session has no title\n // (fallback for agents that don't send session_info_update)\n if (!session.title) {\n const title = generateTitleFromMessage(message);\n if (title) {\n db.update(sessions)\n .set({ title })\n .where(eq(sessions.id, sessionId))\n .run();\n\n // Emit session_info_update so client can update cache\n this.emitUpdate(sessionId, {\n sessionUpdate: 'session_info_update',\n title,\n } as WindowUpdate);\n }\n }\n\n startTurn(sessionId);\n\n // Signal turn start for streaming state\n this.emitUpdate(sessionId, { amuxEvent: 'turn_start' });\n\n // Emit raw message for UI display/persistence (preserves @mentions as typed)\n this.emitUpdate(sessionId, {\n sessionUpdate: 'user_message_chunk',\n content: { type: 'text', text: message },\n });\n\n // Parse @mentions into ContentBlock[] (converts @path to resource_link)\n const content = parseMessageToContentBlocks(message, session.directory);\n\n const emit = (update: WindowUpdate) => this.emitUpdate(sessionId, update);\n\n try {\n // Pass parsed content (with resource_links) to agent\n await agent.backend.prompt(sessionId, content, emit);\n } finally {\n endTurn(sessionId);\n this.emitUpdate(sessionId, { amuxEvent: 'turn_end' });\n }\n }\n\n async setMode(sessionId: string, modeId: string): Promise<void> {\n const agent = this.agents.get(sessionId);\n if (!agent || agent.status !== 'ready') {\n throw new Error(`Agent not ready for session ${sessionId}`);\n }\n if (!agent.backend.setMode) {\n throw new Error(`Backend ${agent.backend.type} does not support setMode`);\n }\n await agent.backend.setMode(sessionId, modeId);\n }\n\n async setModel(sessionId: string, modelId: string): Promise<void> {\n const agent = this.agents.get(sessionId);\n if (!agent || agent.status !== 'ready') {\n throw new Error(`Agent not ready for session ${sessionId}`);\n }\n if (!agent.backend.setModel) {\n throw new Error(`Backend ${agent.backend.type} does not support setModel`);\n }\n await agent.backend.setModel(sessionId, modelId);\n }\n\n async cancel(sessionId: string): Promise<void> {\n const agent = this.agents.get(sessionId);\n if (!agent?.backend.cancel) return;\n await agent.backend.cancel(sessionId);\n }\n\n respondPermission(sessionId: string, requestId: string, optionId: string): void {\n const agent = this.agents.get(sessionId);\n if (!agent) {\n // Agent may have stopped - silently ignore stale permission responses\n console.warn(`[AgentManager] No agent running for session ${sessionId}, ignoring permission response`);\n return;\n }\n if (!agent.backend.respondToPermission) {\n throw new Error(`Backend ${agent.backend.type} does not support permissions`);\n }\n agent.backend.respondToPermission(sessionId, requestId, optionId);\n }\n\n async stopForSession(sessionId: string): Promise<void> {\n const agent = this.agents.get(sessionId);\n if (agent) {\n await agent.backend.stop(sessionId);\n }\n this.agents.delete(sessionId);\n }\n\n // Legacy alias for compatibility during migration\n /** @deprecated Use stopForSession instead */\n stopForWindow = this.stopForSession.bind(this);\n\n async stopAll(): Promise<void> {\n const sessionIds = Array.from(this.agents.keys());\n await Promise.all(sessionIds.map(id => this.stopForSession(id)));\n }\n\n getForSession(sessionId: string): AgentEntry | undefined {\n return this.agents.get(sessionId);\n }\n\n getPendingPermission(sessionId: string): import('../types.js').PendingPermission | null {\n const agent = this.agents.get(sessionId);\n if (!agent?.backend.getPendingPermission) return null;\n return agent.backend.getPendingPermission(sessionId);\n }\n\n // Legacy alias for compatibility during migration\n /** @deprecated Use getForSession instead */\n getForWindow = this.getForSession.bind(this);\n\n registerBackend(backend: AgentBackend): void {\n this.backends.unshift(backend);\n }\n}\n\nexport const agentManager = new AgentManager();\n","import { randomUUID } from 'node:crypto';\nimport Database from 'better-sqlite3';\nimport { drizzle } from 'drizzle-orm/better-sqlite3';\nimport { getDbPath, ensureDir, getDataDir } from '../lib/paths.js';\nimport * as schema from './schema.js';\nimport { seedAgentConfigs } from './seed.js';\n\nensureDir(getDataDir());\n\nconst sqlite = new Database(getDbPath());\nsqlite.pragma('journal_mode = WAL');\nsqlite.pragma('foreign_keys = ON');\n\nexport const db = drizzle(sqlite, { schema });\n\n// Create tables if they don't exist\nsqlite.exec(`\n CREATE TABLE IF NOT EXISTS agent_configs (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n command TEXT NOT NULL,\n args TEXT DEFAULT '[]',\n env TEXT DEFAULT '{}',\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n );\n\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n directory TEXT NOT NULL,\n agent_config_id TEXT NOT NULL REFERENCES agent_configs(id),\n acp_session_id TEXT,\n title TEXT,\n model TEXT,\n mode TEXT,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n );\n\n CREATE TABLE IF NOT EXISTS app_state (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS session_events (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n turn_id TEXT NOT NULL,\n sequence_num INTEGER NOT NULL,\n event_kind TEXT NOT NULL,\n payload TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n );\n`);\n\n// Migration: rename old tables if they exist\ntry {\n // Check if old tables exist and new ones don't have data\n const oldExists = sqlite.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='client_windows'`).get();\n const newCount = sqlite.prepare(`SELECT COUNT(*) as count FROM sessions`).get() as { count: number };\n\n if (oldExists && newCount.count === 0) {\n console.log('[db] Migrating from client_windows/window_events to sessions/session_events...');\n\n // Migrate data from old tables to new tables\n sqlite.exec(`\n INSERT INTO sessions (id, directory, agent_config_id, acp_session_id, model, mode, created_at)\n SELECT id, directory, agent_config_id, session_id, model, mode, created_at\n FROM client_windows;\n\n INSERT INTO session_events (id, session_id, turn_id, sequence_num, event_kind, payload, created_at)\n SELECT id, window_id, turn_id, sequence_num, event_kind, payload, created_at\n FROM window_events;\n\n -- Migrate custom titles to app_state\n INSERT OR IGNORE INTO app_state (key, value)\n SELECT 'window_title_' || id, title\n FROM client_windows\n WHERE has_custom_title = 1 AND title != '';\n\n -- Migrate active window\n INSERT OR REPLACE INTO app_state (key, value)\n SELECT 'active_window_id', value\n FROM app_state\n WHERE key = 'active_window_id';\n `);\n\n // Drop old tables\n sqlite.exec(`\n DROP TABLE IF EXISTS window_events;\n DROP TABLE IF EXISTS client_windows;\n `);\n\n console.log('[db] Migration complete');\n }\n} catch (e) {\n // Migration not needed or already done\n}\n\n// Migration: add title column to sessions if missing\ntry {\n sqlite.exec(`ALTER TABLE sessions ADD COLUMN title TEXT`);\n} catch (e) {\n // Column already exists\n}\n\n// Fix orphaned turns from previous crash (turn_start without turn_end)\n// Using raw SQL to avoid circular dependency with eventStore\nconst orphanedTurns = sqlite.prepare(`\n SELECT DISTINCT turn_id, session_id\n FROM session_events\n WHERE event_kind = 'turn_start'\n AND turn_id NOT IN (\n SELECT turn_id FROM session_events WHERE event_kind = 'turn_end'\n )\n`).all() as { turn_id: string; session_id: string }[];\n\nif (orphanedTurns.length > 0) {\n const getMaxSeq = sqlite.prepare(`\n SELECT COALESCE(MAX(sequence_num), 0) + 1 as next_seq\n FROM session_events WHERE turn_id = ?\n `);\n const insert = sqlite.prepare(`\n INSERT INTO session_events (id, session_id, turn_id, sequence_num, event_kind, payload)\n VALUES (?, ?, ?, ?, 'turn_end', '{\"amuxEvent\":\"turn_end\"}')\n `);\n\n for (const turn of orphanedTurns) {\n const { next_seq } = getMaxSeq.get(turn.turn_id) as { next_seq: number };\n insert.run(randomUUID(), turn.session_id, turn.turn_id, next_seq);\n }\n\n console.log(`[db] Fixed ${orphanedTurns.length} orphaned turn(s) from previous session`);\n}\n\n// Seed defaults\nseedAgentConfigs();\n\nexport { schema };\n","import path from 'path';\nimport os from 'os';\nimport fs from 'fs';\n\n// Capture at module load time (before any chdir)\nconst STARTUP_CWD = process.cwd();\n\n/**\n * Get the working directory from when the server started\n */\nexport function getStartupCwd(): string {\n return STARTUP_CWD;\n}\n\n/**\n * Get the data directory following XDG Base Directory spec\n * - Linux: ~/.local/share/shella\n * - macOS: ~/Library/Application Support/shella\n * - Windows: %APPDATA%/shella\n */\nexport function getDataDir(): string {\n const home = os.homedir();\n\n let dataDir: string;\n\n switch (process.platform) {\n case 'darwin':\n dataDir = path.join(home, 'Library', 'Application Support', 'shella');\n break;\n case 'win32':\n dataDir = path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'shella');\n break;\n default:\n // Linux and others - follow XDG spec\n dataDir = path.join(process.env.XDG_DATA_HOME || path.join(home, '.local', 'share'), 'shella');\n }\n\n return dataDir;\n}\n\n/**\n * Get the config directory following XDG Base Directory spec\n * - Linux: ~/.config/shella\n * - macOS: ~/Library/Application Support/shella\n * - Windows: %APPDATA%/shella\n */\nexport function getConfigDir(): string {\n const home = os.homedir();\n\n let configDir: string;\n\n switch (process.platform) {\n case 'darwin':\n configDir = path.join(home, 'Library', 'Application Support', 'shella');\n break;\n case 'win32':\n configDir = path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'shella');\n break;\n default:\n // Linux and others - follow XDG spec\n configDir = path.join(process.env.XDG_CONFIG_HOME || path.join(home, '.config'), 'shella');\n }\n\n return configDir;\n}\n\n/**\n * Get the cache directory\n * - Linux: ~/.cache/shella\n * - macOS: ~/Library/Caches/shella\n * - Windows: %LOCALAPPDATA%/shella/cache\n */\nexport function getCacheDir(): string {\n const home = os.homedir();\n\n let cacheDir: string;\n\n switch (process.platform) {\n case 'darwin':\n cacheDir = path.join(home, 'Library', 'Caches', 'shella');\n break;\n case 'win32':\n cacheDir = path.join(process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'shella', 'cache');\n break;\n default:\n // Linux and others - follow XDG spec\n cacheDir = path.join(process.env.XDG_CACHE_HOME || path.join(home, '.cache'), 'shella');\n }\n\n return cacheDir;\n}\n\n/**\n * Check if running in mock mode\n */\nexport function isMockMode(): boolean {\n return process.env.SHELLA_MOCK_MODE === 'true';\n}\n\n/**\n * Get the path to the database file\n */\nexport function getDbPath(): string {\n const filename = isMockMode() ? 'shella.mock.db' : 'shella.db';\n return path.join(getDataDir(), filename);\n}\n\n/**\n * Ensure a directory exists, creating it if necessary\n */\nexport function ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\n/**\n * Ensure all shella directories exist\n */\nexport function ensureAllDirs(): void {\n ensureDir(getDataDir());\n ensureDir(getConfigDir());\n ensureDir(getCacheDir());\n}\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';\nimport { sql } from 'drizzle-orm';\n\n// User-configured agent types (e.g. claude-code-acp, opencode, etc.)\nexport const agentConfigs = sqliteTable('agent_configs', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n command: text('command').notNull(),\n args: text('args', { mode: 'json' }).$type<string[]>().default([]),\n env: text('env', { mode: 'json' }).$type<Record<string, string>>().default({}),\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\n .notNull()\n .default(sql`(unixepoch() * 1000)`),\n});\n\n// Agent sessions - each session is an independent agent instance.\nexport const sessions = sqliteTable('sessions', {\n id: text('id').primaryKey(),\n directory: text('directory').notNull(),\n agentConfigId: text('agent_config_id').notNull().references(() => agentConfigs.id),\n // ACP protocol session ID for resuming (internal to agent protocol)\n acpSessionId: text('acp_session_id'),\n // Title from agent (via session_info_update)\n title: text('title'),\n model: text('model'),\n mode: text('mode'),\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\n .notNull()\n .default(sql`(unixepoch() * 1000)`),\n});\n\nexport const appState = sqliteTable('app_state', {\n key: text('key').primaryKey(),\n value: text('value').notNull(),\n});\n\n// Persisted session events for replay on reconnect\nexport const sessionEvents = sqliteTable('session_events', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n turnId: text('turn_id').notNull(),\n sequenceNum: integer('sequence_num').notNull(),\n eventKind: text('event_kind').notNull(),\n payload: text('payload', { mode: 'json' }).notNull(),\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\n .notNull()\n .default(sql`(unixepoch() * 1000)`),\n});\n\nexport type AgentConfig = typeof agentConfigs.$inferSelect;\nexport type NewAgentConfig = typeof agentConfigs.$inferInsert;\nexport type Session = typeof sessions.$inferSelect;\nexport type NewSession = typeof sessions.$inferInsert;\nexport type StoredSessionEvent = typeof sessionEvents.$inferSelect;\nexport type NewStoredSessionEvent = typeof sessionEvents.$inferInsert;\n\n// Legacy aliases for compatibility during migration\n/** @deprecated Use Session instead */\nexport type ClientWindow = Session;\n/** @deprecated Use NewSession instead */\nexport type NewClientWindow = NewSession;\n","import { db } from './index.js';\nimport { agentConfigs, sessions } from './schema.js';\nimport { randomUUID } from 'crypto';\nimport { execSync } from 'child_process';\nimport { isMockMode } from '../lib/paths.js';\n\nconst MOCK_AGENT_ID = 'mock-agent';\n\n// Check if a command exists on the system\nfunction commandExists(cmd: string): boolean {\n try {\n execSync(`which ${cmd}`, { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\n// Detect available ACP-compatible agents\nfunction detectAgents(): Array<{ id: string; name: string; command: string; args: string[]; env: Record<string, string> }> {\n const agents: Array<{ id: string; name: string; command: string; args: string[]; env: Record<string, string> }> = [];\n\n // Claude - always available via npx\n agents.push({\n id: randomUUID(),\n name: 'Claude',\n command: 'npx',\n args: ['@zed-industries/claude-code-acp'],\n env: {},\n });\n\n // OpenCode - check if installed\n if (commandExists('opencode')) {\n agents.push({\n id: randomUUID(),\n name: 'OpenCode',\n command: 'opencode',\n args: ['acp'],\n env: {},\n });\n }\n\n return agents;\n}\n\n// Mock mode now uses the stress backend for full testing capabilities\nconst MOCK_CONFIGS = [\n {\n id: MOCK_AGENT_ID,\n name: 'Test Agent',\n command: '__stress__',\n args: [],\n env: {},\n },\n];\n\nexport function seedAgentConfigs() {\n if (isMockMode()) {\n // Mock mode: seed once as before\n const existing = db.select().from(agentConfigs).all();\n if (existing.length === 0) {\n for (const config of MOCK_CONFIGS) {\n db.insert(agentConfigs).values(config).run();\n }\n console.log('[db] Seeded mock agent configs');\n }\n seedMockSessions();\n return;\n }\n\n // Production: detect and add new agents on every launch\n const detected = detectAgents();\n const existing = db.select().from(agentConfigs).all();\n\n let added = 0;\n for (const agent of detected) {\n // Check if this agent already exists (by command + args)\n const alreadyExists = existing.some(\n (e) => e.command === agent.command &&\n JSON.stringify(e.args) === JSON.stringify(agent.args)\n );\n if (!alreadyExists) {\n db.insert(agentConfigs).values(agent).run();\n console.log(`[db] Detected new agent: ${agent.name}`);\n added++;\n }\n }\n\n if (added === 0 && detected.length > 0) {\n console.log(`[db] ${detected.length} known agent(s) already configured`);\n }\n}\n\nfunction seedMockSessions() {\n const existing = db.select().from(sessions).all();\n if (existing.length > 0) return;\n\n const count = getSessionCount(10);\n for (let i = 1; i <= count; i++) {\n db.insert(sessions).values({\n id: randomUUID(),\n directory: '~',\n agentConfigId: MOCK_AGENT_ID,\n }).run();\n }\n console.log(`[db] Seeded ${count} test sessions`);\n}\n\nfunction getSessionCount(defaultCount: number): number {\n // Support both new and legacy env var names\n const envCount = process.env.SHELLA_MOCK_SESSIONS ?? process.env.SHELLA_MOCK_WINDOWS;\n if (envCount) {\n const parsed = parseInt(envCount, 10);\n if (!isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n return defaultCount;\n}\n","// Re-export ACP types from SDK\nexport type {\n SessionUpdate,\n ContentBlock,\n ContentChunk,\n TextContent,\n ImageContent,\n AudioContent,\n EmbeddedResource,\n ResourceLink,\n PlanEntry as AcpPlanEntry,\n PlanEntryPriority,\n PlanEntryStatus,\n Plan,\n AvailableCommand,\n AvailableCommandsUpdate,\n CurrentModeUpdate,\n ToolCall,\n ToolCallUpdate,\n ToolCallStatus,\n PermissionOption,\n RequestPermissionRequest,\n RequestPermissionResponse,\n} from '@agentclientprotocol/sdk';\n\nimport type { PlanEntry as AcpPlanEntry, PlanEntryPriority, PlanEntryStatus, SessionUpdate } from '@agentclientprotocol/sdk';\n\n/**\n * Normalized PlanEntry that handles agent-specific quirks.\n *\n * Standard ACP: content, priority, status\n * Claude-style: content, status, activeForm (priority always \"medium\")\n *\n * The `activeForm` field provides a present-tense description (e.g., \"Analyzing tests\")\n * vs the imperative `content` (e.g., \"Analyze tests\"). When `activeForm` is present\n * and the status is \"in_progress\", UI should prefer displaying `activeForm`.\n */\nexport interface NormalizedPlanEntry {\n content: string;\n status: PlanEntryStatus;\n priority: PlanEntryPriority;\n /** Present-tense form for in_progress display (Claude-style). */\n activeForm?: string;\n}\n\n/**\n * Normalize an incoming ACP plan entry.\n * Extracts `activeForm` from Claude's `_meta.claudeCode` extension if present.\n */\nexport function normalizePlanEntry(entry: AcpPlanEntry): NormalizedPlanEntry {\n // Try to extract activeForm from _meta.claudeCode (if claude-acp sends it there)\n const meta = entry._meta as Record<string, unknown> | undefined;\n const claudeCode = meta?.claudeCode as Record<string, unknown> | undefined;\n const activeForm = claudeCode?.activeForm as string | undefined;\n\n // Also check if activeForm is directly on the entry (non-standard but possible)\n const directActiveForm = (entry as Record<string, unknown>).activeForm as string | undefined;\n\n return {\n content: entry.content,\n status: entry.status,\n priority: entry.priority,\n activeForm: activeForm ?? directActiveForm,\n };\n}\n\n// Model and mode info returned from agent session\nexport interface ModelInfo {\n modelId: string;\n name: string;\n}\n\nexport interface ModeInfo {\n id: string;\n name: string;\n description?: string;\n}\n\n// Permission types for WebSocket push\nexport interface PendingPermission {\n requestId: string;\n toolCallId?: string;\n title: string;\n options: Array<{ optionId: string; name: string; kind: string }>;\n}\n\n// amux events pushed via WebSocket subscription.\n// ACP blocking RPCs (requestPermission, prompt) need WebSocket notifications\n// so the UI can track state independently of RPC callbacks (e.g. after page reload).\n// Pattern: RPC start → notification, RPC complete → notification\nexport type AmuxEvent =\n | { amuxEvent: 'error'; message: string }\n | { amuxEvent: 'permission_request'; permission: PendingPermission }\n | { amuxEvent: 'permission_cleared' }\n | { amuxEvent: 'turn_start' }\n | { amuxEvent: 'turn_end' }\n | { amuxEvent: 'turn_cancelled' };\n\n// Combined type for session subscriptions\nexport type WindowUpdate = SessionUpdate | AmuxEvent;\n\n// Event emitted by amux when a session update occurs\nexport interface SessionEvent {\n sessionId: string;\n update: WindowUpdate;\n}\n\n// Type guard helpers\nexport function isSessionUpdate(update: WindowUpdate): update is SessionUpdate {\n return 'sessionUpdate' in update;\n}\n\nexport function isAmuxEvent(update: WindowUpdate): update is AmuxEvent {\n return 'amuxEvent' in update;\n}\n","import { randomUUID } from 'crypto';\nimport { db } from '../db/index.js';\nimport { sessionEvents } from '../db/schema.js';\nimport { eq, asc } from 'drizzle-orm';\nimport type { WindowUpdate } from '../types.js';\nimport { isSessionUpdate, isAmuxEvent } from '../types.js';\n\n// In-memory state for tracking current turn per session\nconst turnState = new Map<string, { turnId: string; seq: number }>();\n\n/**\n * Start a new prompt turn for a session\n */\nexport function startTurn(sessionId: string): string {\n const turnId = randomUUID();\n turnState.set(sessionId, { turnId, seq: 0 });\n return turnId;\n}\n\n/**\n * Store an event. Persists SessionUpdate events and turn_start/turn_end (for streaming state replay).\n * Other AmuxEvents (error, permission_request, permission_cleared) are transient.\n */\nexport function storeEvent(sessionId: string, update: WindowUpdate): void {\n // Determine event kind for storage\n let eventKind: string;\n if (isSessionUpdate(update)) {\n eventKind = update.sessionUpdate;\n } else if (isAmuxEvent(update) && (update.amuxEvent === 'turn_start' || update.amuxEvent === 'turn_end')) {\n eventKind = update.amuxEvent;\n } else {\n // Skip other amux events (error, permission_request, permission_cleared)\n return;\n }\n\n // Get or create turn state (handles events that arrive before startTurn)\n let state = turnState.get(sessionId);\n if (!state) {\n state = { turnId: randomUUID(), seq: 0 };\n turnState.set(sessionId, state);\n }\n\n const seq = state.seq++;\n\n db.insert(sessionEvents)\n .values({\n id: randomUUID(),\n sessionId,\n turnId: state.turnId,\n sequenceNum: seq,\n eventKind,\n payload: update as unknown as Record<string, unknown>,\n })\n .run();\n}\n\n/**\n * End the current turn for a session\n */\nexport function endTurn(sessionId: string): void {\n turnState.delete(sessionId);\n}\n\n/**\n * Get all stored events for a session, ordered for replay.\n * Returns both SessionUpdate events and turn_end events.\n */\nexport function getEventsForSession(sessionId: string): WindowUpdate[] {\n const rows = db\n .select()\n .from(sessionEvents)\n .where(eq(sessionEvents.sessionId, sessionId))\n .orderBy(asc(sessionEvents.createdAt), asc(sessionEvents.sequenceNum))\n .all();\n\n return rows.map((row) => row.payload as WindowUpdate);\n}\n\n/**\n * Clear all events for a session (e.g., when session is deleted)\n */\nexport function clearEventsForSession(sessionId: string): void {\n db.delete(sessionEvents).where(eq(sessionEvents.sessionId, sessionId)).run();\n}\n\n// Legacy aliases for compatibility during migration\n/** @deprecated Use getEventsForSession instead */\nexport const getEventsForWindow = getEventsForSession;\n/** @deprecated Use clearEventsForSession instead */\nexport const clearEventsForWindow = clearEventsForSession;\n","import { randomUUID } from 'crypto';\nimport { spawn as nodeSpawn, type ChildProcess } from 'child_process';\nimport { Readable, Writable } from 'stream';\nimport * as fs from 'fs/promises';\nimport {\n ClientSideConnection,\n ndJsonStream,\n Client,\n RequestPermissionRequest,\n RequestPermissionResponse,\n SessionNotification,\n ReadTextFileRequest,\n ReadTextFileResponse,\n WriteTextFileRequest,\n WriteTextFileResponse,\n CreateTerminalRequest,\n CreateTerminalResponse,\n TerminalOutputRequest,\n TerminalOutputResponse,\n WaitForTerminalExitRequest,\n WaitForTerminalExitResponse,\n KillTerminalCommandRequest,\n KillTerminalCommandResponse,\n ReleaseTerminalRequest,\n ReleaseTerminalResponse,\n} from '@agentclientprotocol/sdk';\nimport type { AgentBackend, AgentConfig, AgentSession, EmitFn } from './types.js';\nimport type { ContentBlock, PendingPermission, WindowUpdate } from '../../types.js';\nimport { spawn, type ManagedProcess } from '../process.js';\n\nconst INIT_TIMEOUT_MS = 30000;\n\n/**\n * Normalize ACP session updates.\n * \n * Amux passes through events as-is, but normalizes quirks from specific agents:\n * - Claude sends Edit/Write diffs as {newText, oldText, path} instead of unified diff\n * - We convert these to standard unified diff format so the UI doesn't need agent-specific logic\n */\nfunction normalizeSessionUpdate(update: Record<string, unknown>): Record<string, unknown> {\n // Only process tool_call and tool_call_update events with content arrays\n if (update.sessionUpdate !== 'tool_call' && update.sessionUpdate !== 'tool_call_update') {\n return update;\n }\n\n const content = update.content as Array<Record<string, unknown>> | undefined;\n if (!content || !Array.isArray(content)) {\n return update;\n }\n\n // Check for diff items that need normalization\n const normalizedContent = content.map(item => {\n if (item.type !== 'diff') return item;\n\n // Already a string diff - pass through\n if (typeof item.content === 'string') return item;\n\n // Claude-style diff: {type: 'diff', newText, oldText, path}\n // Convert to unified diff format\n const newText = item.newText as string | undefined;\n const oldText = item.oldText as string | null | undefined;\n const path = item.path as string | undefined;\n\n if (newText === undefined) return item;\n\n // Generate unified diff\n const filePath = path ?? 'file';\n const oldLines = oldText ? oldText.split('\\n') : [];\n const newLines = newText.split('\\n');\n\n let unifiedDiff = `Index: ${filePath}\\n`;\n unifiedDiff += '===================================================================\\n';\n unifiedDiff += `--- ${filePath}\\n`;\n unifiedDiff += `+++ ${filePath}\\n`;\n unifiedDiff += `@@ -${oldLines.length > 0 ? 1 : 0},${oldLines.length} +1,${newLines.length} @@\\n`;\n\n for (const line of oldLines) {\n unifiedDiff += `-${line}\\n`;\n }\n for (const line of newLines) {\n unifiedDiff += `+${line}\\n`;\n }\n\n // Return normalized diff item\n return {\n type: 'diff',\n content: unifiedDiff,\n };\n });\n\n return {\n ...update,\n content: normalizedContent,\n };\n}\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number, operation: string): Promise<T> {\n return Promise.race([\n promise,\n new Promise<T>((_, reject) =>\n setTimeout(() => reject(new Error(`${operation} timed out after ${ms}ms`)), ms)\n ),\n ]);\n}\n\ninterface Terminal {\n process: ChildProcess;\n output: string;\n exitCode: number | null;\n signal: string | null;\n truncated: boolean;\n outputByteLimit: number;\n}\n\ninterface AcpInstance {\n process: ManagedProcess;\n connection: ClientSideConnection;\n sessionId: string;\n pendingPermission: PendingPermission | null;\n permissionCallbacks: Map<string, {\n resolve: (optionId: string) => void;\n reject: (err: Error) => void;\n }>;\n emit: EmitFn;\n terminals: Map<string, Terminal>;\n}\n\nexport class AcpBackend implements AgentBackend {\n readonly type = 'acp';\n private instances = new Map<string, AcpInstance>();\n\n onSessionIdChanged?: (sessionId: string, acpSessionId: string) => void;\n\n matches(config: AgentConfig): boolean {\n return config.command !== '__mock__' && config.command !== '__stress__';\n }\n\n async start(\n sessionId: string,\n config: AgentConfig,\n cwd: string,\n existingAcpSessionId: string | null,\n emit: EmitFn\n ): Promise<AgentSession> {\n // Kill existing instance if any\n if (this.instances.has(sessionId)) {\n await this.stop(sessionId);\n }\n\n const args = config.args ?? [];\n const env = config.env ?? {};\n\n console.log(`[acp] Spawning: ${config.command} ${args.join(' ')} in ${cwd}`);\n\n const proc = spawn({\n command: config.command,\n args,\n cwd,\n env,\n });\n\n const instance: AcpInstance = {\n process: proc,\n connection: null!,\n sessionId: '',\n pendingPermission: null,\n permissionCallbacks: new Map(),\n emit,\n terminals: new Map(),\n };\n\n // Handle unexpected exit\n proc.wait().then(({ exitCode }) => {\n if (this.instances.has(sessionId)) {\n emit({ amuxEvent: 'error', message: `Agent process exited with code ${exitCode}` });\n this.instances.delete(sessionId);\n }\n });\n\n try {\n const input = Writable.toWeb(proc.stdin) as WritableStream<Uint8Array>;\n const output = Readable.toWeb(proc.stdout) as ReadableStream<Uint8Array>;\n const stream = ndJsonStream(input, output);\n\n const client = this.createClient(sessionId, instance);\n instance.connection = new ClientSideConnection(() => client, stream);\n\n const initResult = await withTimeout(\n instance.connection.initialize({\n protocolVersion: 1,\n clientCapabilities: {\n fs: { readTextFile: true, writeTextFile: true },\n terminal: true,\n },\n }),\n INIT_TIMEOUT_MS,\n 'Agent initialization'\n );\n\n console.log(`[acp] Initialized agent: ${initResult.agentInfo?.name} v${initResult.agentInfo?.version}`);\n\n const canResume = initResult.agentCapabilities?.sessionCapabilities?.resume !== undefined;\n let acpSessionId: string;\n let sessionResult: any;\n\n if (existingAcpSessionId && canResume) {\n let resumeSucceeded = false;\n try {\n console.log(`[acp] Resuming ACP session ${existingAcpSessionId}...`);\n sessionResult = await withTimeout(\n (instance.connection as any).unstable_resumeSession({\n sessionId: existingAcpSessionId,\n cwd,\n mcpServers: [],\n }),\n INIT_TIMEOUT_MS,\n 'Session resume'\n );\n\n await new Promise(resolve => setTimeout(resolve, 100));\n acpSessionId = existingAcpSessionId;\n console.log(`[acp] ACP session resumed successfully`);\n resumeSucceeded = true;\n } catch (resumeErr) {\n console.log(`[acp] Resume failed, creating new session:`, resumeErr);\n }\n\n if (!resumeSucceeded) {\n sessionResult = await withTimeout(\n instance.connection.newSession({ cwd, mcpServers: [] }),\n INIT_TIMEOUT_MS,\n 'New session creation'\n );\n acpSessionId = sessionResult.sessionId;\n console.log(`[acp] New ACP session created: ${acpSessionId}`);\n this.onSessionIdChanged?.(sessionId, acpSessionId);\n }\n } else {\n console.log(`[acp] Creating new ACP session in ${cwd}...`);\n sessionResult = await withTimeout(\n instance.connection.newSession({ cwd, mcpServers: [] }),\n INIT_TIMEOUT_MS,\n 'New session creation'\n );\n acpSessionId = sessionResult.sessionId!;\n console.log(`[acp] ACP session created: ${acpSessionId}`);\n this.onSessionIdChanged?.(sessionId, acpSessionId);\n }\n\n instance.sessionId = acpSessionId!;\n this.instances.set(sessionId, instance);\n\n const models = sessionResult?.models?.availableModels;\n const modes = sessionResult?.modes?.availableModes;\n\n if (sessionResult?.modes) {\n emit({\n sessionUpdate: 'current_mode_update',\n currentModeId: sessionResult.modes.currentModeId,\n });\n }\n\n return {\n sessionId: acpSessionId!,\n models,\n modes,\n };\n } catch (err) {\n console.error(`[acp] Error starting agent for session ${sessionId}:`, err);\n await proc.kill();\n throw err;\n }\n }\n\n private createClient(_sessionId: string, instance: AcpInstance): Client {\n return {\n async requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse> {\n const requestId = randomUUID();\n\n const permission = {\n requestId,\n toolCallId: params.toolCall.toolCallId,\n title: params.toolCall.title ?? 'Permission Required',\n options: params.options.map(o => ({\n optionId: o.optionId,\n name: o.name,\n kind: o.kind,\n })),\n };\n instance.pendingPermission = permission;\n instance.emit({ amuxEvent: 'permission_request', permission });\n\n return new Promise((resolve, reject) => {\n instance.permissionCallbacks.set(requestId, {\n resolve: (optionId) => {\n instance.pendingPermission = null;\n instance.emit({ amuxEvent: 'permission_cleared' });\n resolve({ outcome: { outcome: 'selected', optionId } });\n },\n reject,\n });\n });\n },\n\n async sessionUpdate(params: SessionNotification): Promise<void> {\n console.log(`[acp] sessionUpdate received:`, JSON.stringify(params));\n // Normalize the update before emitting (ACP normalization layer)\n const normalized = normalizeSessionUpdate(params.update) as WindowUpdate;\n instance.emit(normalized);\n },\n\n async readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse> {\n const content = await fs.readFile(params.path, 'utf-8');\n return { content };\n },\n\n async writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse> {\n await fs.writeFile(params.path, params.content);\n return {};\n },\n\n async createTerminal(params: CreateTerminalRequest): Promise<CreateTerminalResponse> {\n console.log(`[acp] createTerminal request:`, JSON.stringify(params));\n const terminalId = randomUUID();\n const outputByteLimit = params.outputByteLimit ?? 1024 * 1024; // Default 1MB\n\n const termProc = nodeSpawn(params.command, params.args ?? [], {\n cwd: params.cwd ?? undefined,\n env: params.env\n ? { ...process.env, ...Object.fromEntries(params.env.map(e => [e.name, e.value])) }\n : process.env,\n shell: true,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n const terminal: Terminal = {\n process: termProc,\n output: '',\n exitCode: null,\n signal: null,\n truncated: false,\n outputByteLimit,\n };\n\n const appendOutput = (data: Buffer) => {\n terminal.output += data.toString();\n // Truncate from beginning if over limit\n if (terminal.output.length > terminal.outputByteLimit) {\n terminal.output = terminal.output.slice(-terminal.outputByteLimit);\n terminal.truncated = true;\n }\n };\n\n termProc.stdout?.on('data', appendOutput);\n termProc.stderr?.on('data', appendOutput);\n\n termProc.on('exit', (code, signal) => {\n console.log(`[acp] Terminal ${terminalId} exited with code ${code}, signal ${signal}`);\n terminal.exitCode = code ?? null;\n terminal.signal = signal ?? null;\n });\n\n termProc.on('error', (err) => {\n console.error(`[acp] Terminal ${terminalId} error:`, err.message);\n terminal.output += `\\nError: ${err.message}`;\n terminal.exitCode = -1;\n });\n\n instance.terminals.set(terminalId, terminal);\n console.log(`[acp] Created terminal ${terminalId} for command: ${params.command}`);\n\n return { terminalId };\n },\n\n async terminalOutput(params: TerminalOutputRequest): Promise<TerminalOutputResponse> {\n console.log(`[acp] terminalOutput request for terminal ${params.terminalId}`);\n const terminal = instance.terminals.get(params.terminalId);\n if (!terminal) {\n throw new Error(`Terminal ${params.terminalId} not found`);\n }\n return {\n output: terminal.output,\n truncated: terminal.truncated,\n exitStatus: terminal.exitCode !== null || terminal.signal !== null ? {\n exitCode: terminal.exitCode,\n signal: terminal.signal,\n } : undefined,\n };\n },\n\n async waitForTerminalExit(params: WaitForTerminalExitRequest): Promise<WaitForTerminalExitResponse> {\n console.log(`[acp] waitForTerminalExit request for terminal ${params.terminalId}`);\n const terminal = instance.terminals.get(params.terminalId);\n if (!terminal) {\n throw new Error(`Terminal ${params.terminalId} not found`);\n }\n\n // If already exited, return immediately\n if (terminal.exitCode !== null || terminal.signal !== null) {\n return {\n exitCode: terminal.exitCode,\n signal: terminal.signal,\n };\n }\n\n // Wait for exit\n return new Promise((resolve) => {\n terminal.process.on('exit', (code, signal) => {\n resolve({\n exitCode: code ?? null,\n signal: signal ?? null,\n });\n });\n });\n },\n\n // Note: killTerminalCommand not in SDK Client interface yet, but we implement handlers\n // for completeness when the SDK adds support\n async killTerminal(params: KillTerminalCommandRequest): Promise<KillTerminalCommandResponse> {\n console.log(`[acp] killTerminal request for terminal ${params.terminalId}`);\n const terminal = instance.terminals.get(params.terminalId);\n if (!terminal) {\n throw new Error(`Terminal ${params.terminalId} not found`);\n }\n terminal.process.kill('SIGTERM');\n return {};\n },\n\n async releaseTerminal(params: ReleaseTerminalRequest): Promise<ReleaseTerminalResponse> {\n console.log(`[acp] releaseTerminal request for terminal ${params.terminalId}`);\n const terminal = instance.terminals.get(params.terminalId);\n if (terminal) {\n if (terminal.exitCode === null) {\n terminal.process.kill('SIGKILL');\n }\n instance.terminals.delete(params.terminalId);\n }\n return {};\n },\n };\n }\n\n async prompt(sessionId: string, content: ContentBlock[], _emit: EmitFn): Promise<void> {\n const instance = this.instances.get(sessionId);\n if (!instance) throw new Error(`No ACP instance for window ${sessionId}`);\n\n console.log(`[acp] Sending prompt to session ${instance.sessionId} with ${content.length} content block(s)...`);\n const result = await instance.connection.prompt({\n sessionId: instance.sessionId,\n prompt: content,\n });\n console.log(`[acp] Prompt complete, stopReason: ${result.stopReason}`);\n }\n\n async stop(sessionId: string): Promise<void> {\n const instance = this.instances.get(sessionId);\n if (!instance) return;\n\n // Reject pending permission callbacks\n for (const [, callback] of instance.permissionCallbacks) {\n callback.reject(new Error('Agent stopped'));\n }\n instance.permissionCallbacks.clear();\n instance.pendingPermission = null;\n\n // Remove from instances first to prevent exit handler re-entry\n this.instances.delete(sessionId);\n\n // Kill the process tree\n await instance.process.kill();\n }\n\n async stopAll(): Promise<void> {\n const sessionIds = [...this.instances.keys()];\n await Promise.all(sessionIds.map((id) => this.stop(id)));\n }\n\n isRunning(sessionId: string): boolean {\n return this.instances.has(sessionId);\n }\n\n respondToPermission(sessionId: string, requestId: string, optionId: string): void {\n const instance = this.instances.get(sessionId);\n const callback = instance?.permissionCallbacks.get(requestId);\n if (!callback) {\n throw new Error(`No pending permission request ${requestId}`);\n }\n callback.resolve(optionId);\n instance!.permissionCallbacks.delete(requestId);\n }\n\n getPendingPermission(sessionId: string): PendingPermission | null {\n const instance = this.instances.get(sessionId);\n return instance?.pendingPermission ?? null;\n }\n\n async cancel(sessionId: string): Promise<void> {\n const instance = this.instances.get(sessionId);\n if (!instance) return;\n\n await instance.connection.cancel({ sessionId: instance.sessionId });\n }\n\n async setMode(sessionId: string, modeId: string): Promise<void> {\n const instance = this.instances.get(sessionId);\n if (!instance) throw new Error(`No ACP instance for window ${sessionId}`);\n\n await instance.connection.setSessionMode({\n sessionId: instance.sessionId,\n modeId,\n });\n }\n\n async setModel(sessionId: string, modelId: string): Promise<void> {\n const instance = this.instances.get(sessionId);\n if (!instance) throw new Error(`No ACP instance for window ${sessionId}`);\n\n await (instance.connection as any).unstable_setSessionModel({\n sessionId: instance.sessionId,\n modelId,\n });\n }\n}\n","/**\n * Managed process wrapper with automatic tree cleanup.\n * Uses execa for process spawning and tree-kill for cleanup.\n */\n\nimport { execa } from 'execa';\nimport treeKill from 'tree-kill';\nimport type { Writable, Readable } from 'stream';\n\nexport interface ManagedProcess {\n pid: number;\n stdin: Writable;\n stdout: Readable;\n stderr: Readable;\n kill: (signal?: string) => Promise<void>;\n wait: () => Promise<{ exitCode: number | null }>;\n}\n\nexport interface SpawnOptions {\n command: string;\n args?: string[];\n cwd?: string;\n env?: Record<string, string>;\n timeoutMs?: number;\n}\n\n/**\n * Spawns a managed process with automatic tree cleanup.\n *\n * - Cross-platform process tree killing via tree-kill\n * - Automatic cleanup on parent exit\n * - Promise-based wait/kill\n */\nexport function spawn(options: SpawnOptions): ManagedProcess {\n const { command, args = [], cwd, env, timeoutMs } = options;\n\n const subprocess = execa(command, args, {\n cwd,\n env: { ...process.env, ...env },\n stdin: 'pipe',\n stdout: 'pipe',\n stderr: 'pipe',\n timeout: timeoutMs,\n cleanup: true, // Kill on parent exit\n windowsHide: true, // Hide console window on Windows\n });\n\n const pid = subprocess.pid;\n if (!pid) {\n throw new Error(`Failed to spawn process: ${command}`);\n }\n\n return {\n pid,\n stdin: subprocess.stdin!,\n stdout: subprocess.stdout!,\n stderr: subprocess.stderr!,\n\n kill: (signal = 'SIGTERM') => killTree(pid, signal),\n\n wait: async () => {\n try {\n const result = await subprocess;\n return { exitCode: result.exitCode };\n } catch (err: any) {\n return { exitCode: err.exitCode ?? 1 };\n }\n },\n };\n}\n\nfunction killTree(pid: number, signal: string): Promise<void> {\n return new Promise((resolve) => {\n treeKill(pid, signal, (err?: Error) => {\n if (err && !err.message.includes('No such process')) {\n console.error(`[process] kill error pid=${pid}:`, err.message);\n }\n resolve();\n });\n });\n}\n","import { randomUUID } from 'crypto';\nimport type { ContentBlock } from '../../types.js';\nimport type { AgentBackend, AgentConfig, AgentSession, EmitFn } from './types.js';\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nexport class MockBackend implements AgentBackend {\n readonly type = 'mock';\n private running = new Set<string>();\n\n matches(config: AgentConfig): boolean {\n return config.command === '__mock__';\n }\n\n async start(\n sessionId: string,\n _config: AgentConfig,\n _cwd: string,\n _existingAcpSessionId: string | null,\n emit: EmitFn\n ): Promise<AgentSession> {\n console.log(`[mock] Starting mock agent for session ${sessionId}`);\n\n emit({\n sessionUpdate: 'current_mode_update',\n currentModeId: 'mock',\n });\n\n console.log(`[mock] Mock agent ready for session ${sessionId}`);\n this.running.add(sessionId);\n\n return {\n sessionId: randomUUID(),\n models: [{ modelId: 'mock-model', name: 'Mock Model' }],\n modes: [{ id: 'mock', name: 'Mock Mode' }],\n };\n }\n\n async prompt(sessionId: string, content: ContentBlock[], emit: EmitFn): Promise<void> {\n // Extract text from content blocks for logging\n const text = content\n .filter((b): b is { type: 'text'; text: string } => b.type === 'text')\n .map(b => b.text)\n .join('');\n console.log(`[mock] Mock prompt for session ${sessionId}: \"${text.slice(0, 50)}...\"`);\n\n const words = [\n 'This', 'is', 'a', 'mock', 'response', 'from', 'the', 'mock', 'agent.',\n 'It', 'simulates', 'streaming', 'text', 'chunks', 'for', 'performance', 'testing.',\n 'The', 'response', 'arrives', 'in', 'small', 'pieces', 'to', 'test', 'UI', 'rendering.',\n ];\n\n for (let i = 0; i < words.length; i += 3) {\n const chunk = words.slice(i, i + 3).join(' ') + ' ';\n emit({\n sessionUpdate: 'agent_message_chunk',\n content: { type: 'text', text: chunk },\n });\n await delay(50);\n }\n }\n\n async stop(sessionId: string): Promise<void> {\n console.log(`[mock] Stopping mock agent for session ${sessionId}`);\n this.running.delete(sessionId);\n }\n\n async stopAll(): Promise<void> {\n const sessionIds = [...this.running];\n await Promise.all(sessionIds.map((id) => this.stop(id)));\n }\n\n isRunning(sessionId: string): boolean {\n return this.running.has(sessionId);\n }\n}\n","import { randomUUID } from 'crypto';\nimport type { ContentBlock } from '../../types.js';\nimport type { AgentBackend, AgentConfig, AgentSession, EmitFn } from './types.js';\nimport { generateRealisticTurn, type StressConfig } from '../../stress/generators.js';\nimport { getStressRuntimeConfig } from '../../stress/config.js';\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\ninterface RunningAgent {\n emit: EmitFn;\n stopped: boolean;\n}\n\nexport class StressBackend implements AgentBackend {\n readonly type = 'stress';\n private running = new Map<string, RunningAgent>();\n\n constructor() {}\n\n private getConfig(): StressConfig {\n const runtime = getStressRuntimeConfig();\n return {\n eventsPerTurn: runtime.eventsPerTurn,\n toolCallProbability: 0.4,\n thoughtProbability: 0.6,\n planProbability: 0.3,\n chunkSize: runtime.chunkSize,\n delayMs: runtime.eventDelay,\n };\n }\n\n matches(config: AgentConfig): boolean {\n return config.command === '__stress__';\n }\n\n async start(\n sessionId: string,\n _config: AgentConfig,\n _cwd: string,\n _existingAcpSessionId: string | null,\n emit: EmitFn\n ): Promise<AgentSession> {\n console.log(`[stress] Starting stress agent for session ${sessionId}`);\n\n emit({\n sessionUpdate: 'current_mode_update',\n currentModeId: 'stress',\n });\n\n // Track this agent\n const agent: RunningAgent = { emit, stopped: false };\n this.running.set(sessionId, agent);\n\n // Start background event generation\n this.runEventLoop(sessionId, agent);\n\n console.log(`[stress] Stress agent ready for session ${sessionId}`);\n\n return {\n sessionId: randomUUID(),\n models: [{ modelId: 'stress-model', name: 'Stress Model' }],\n modes: [{ id: 'stress', name: 'Stress Mode' }],\n };\n }\n\n private getTurnDelayMs(): number {\n const { turnDelay } = getStressRuntimeConfig();\n if (turnDelay === 0) {\n return 0; // No delay - max speed\n }\n // Add some randomness: +/- 50%\n const variance = turnDelay * 0.5;\n return (turnDelay - variance + Math.random() * variance * 2) * 1000;\n }\n\n private async runEventLoop(sessionId: string, agent: RunningAgent): Promise<void> {\n // Wait a bit before starting to let UI connect\n await delay(1000);\n\n let turnCount = 0;\n while (!agent.stopped) {\n turnCount++;\n\n // 1. Signal turn start for streaming state\n agent.emit({ amuxEvent: 'turn_start' });\n\n // 2. Emit synthetic user message\n agent.emit({\n sessionUpdate: 'user_message_chunk',\n content: { type: 'text', text: `[Stress test turn ${turnCount}]` },\n });\n\n // 3. Generate a realistic turn of events (reads config each time)\n const config = this.getConfig();\n const events = generateRealisticTurn(config);\n\n for (const event of events) {\n if (agent.stopped) break;\n agent.emit(event);\n\n // Delay between events if configured\n if (config.delayMs > 0) {\n await delay(config.delayMs);\n }\n }\n\n // 4. Emit turn_end to complete the turn\n if (!agent.stopped) {\n agent.emit({ amuxEvent: 'turn_end' });\n }\n\n // Wait between turns\n const turnDelay = this.getTurnDelayMs();\n await delay(turnDelay);\n }\n\n console.log(`[stress] Event loop stopped for session ${sessionId}`);\n }\n\n async prompt(sessionId: string, content: ContentBlock[], emit: EmitFn): Promise<void> {\n // In stress mode, prompts just trigger an immediate response turn\n const text = content\n .filter((b): b is { type: 'text'; text: string } => b.type === 'text')\n .map(b => b.text)\n .join('');\n console.log(`[stress] Prompt for session ${sessionId}: \"${text.slice(0, 50)}...\"`);\n\n const config = this.getConfig();\n const events = generateRealisticTurn(config);\n for (const event of events) {\n emit(event);\n if (config.delayMs > 0) {\n await delay(config.delayMs);\n }\n }\n }\n\n async stop(sessionId: string): Promise<void> {\n console.log(`[stress] Stopping stress agent for session ${sessionId}`);\n const agent = this.running.get(sessionId);\n if (agent) {\n agent.stopped = true;\n this.running.delete(sessionId);\n }\n }\n\n async stopAll(): Promise<void> {\n const sessionIds = [...this.running.keys()];\n await Promise.all(sessionIds.map((id) => this.stop(id)));\n }\n\n isRunning(sessionId: string): boolean {\n return this.running.has(sessionId);\n }\n}\n","import { randomUUID } from 'crypto';\nimport type { SessionUpdate, AcpPlanEntry, ToolCallStatus } from '../types.js';\n\nexport interface StressConfig {\n eventsPerTurn: number;\n toolCallProbability: number;\n thoughtProbability: number;\n planProbability: number;\n chunkSize: number;\n delayMs: number;\n}\n\nexport const DEFAULT_STRESS_CONFIG: StressConfig = {\n eventsPerTurn: 50,\n toolCallProbability: 0.4,\n thoughtProbability: 0.6,\n planProbability: 0.3,\n chunkSize: 50,\n delayMs: 0,\n};\n\n// ============================================================================\n// Markdown content samples for Streamdown testing\n// ============================================================================\n\nconst MARKDOWN_SAMPLES = [\n // Sample 1: Code block with TypeScript\n `Here's a React component that handles user authentication:\n\n\\`\\`\\`typescript\nimport { useState, useEffect } from 'react';\nimport { useAuth } from '@/hooks/useAuth';\n\ninterface AuthProviderProps {\n children: React.ReactNode;\n}\n\nexport function AuthProvider({ children }: AuthProviderProps) {\n const [isLoading, setIsLoading] = useState(true);\n const { user, login, logout } = useAuth();\n\n useEffect(() => {\n // Check for existing session\n const checkSession = async () => {\n try {\n await validateToken();\n } finally {\n setIsLoading(false);\n }\n };\n checkSession();\n }, []);\n\n if (isLoading) {\n return <LoadingSpinner />;\n }\n\n return (\n <AuthContext.Provider value={{ user, login, logout }}>\n {children}\n </AuthContext.Provider>\n );\n}\n\\`\\`\\`\n\nThis component wraps your app and provides authentication context.`,\n\n // Sample 2: Multiple code blocks\n `Let me show you how to set up the API client:\n\nFirst, install the dependencies:\n\n\\`\\`\\`bash\nnpm install @tanstack/react-query axios\n\\`\\`\\`\n\nThen create the client:\n\n\\`\\`\\`typescript\n// src/lib/api.ts\nimport axios from 'axios';\n\nexport const apiClient = axios.create({\n baseURL: process.env.NEXT_PUBLIC_API_URL,\n timeout: 10000,\n headers: {\n 'Content-Type': 'application/json',\n },\n});\n\n// Add auth interceptor\napiClient.interceptors.request.use((config) => {\n const token = localStorage.getItem('auth_token');\n if (token) {\n config.headers.Authorization = \\`Bearer \\${token}\\`;\n }\n return config;\n});\n\\`\\`\\`\n\nAnd set up React Query:\n\n\\`\\`\\`typescript\n// src/providers/QueryProvider.tsx\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n retry: 1,\n },\n },\n});\n\nexport function QueryProvider({ children }: { children: React.ReactNode }) {\n return (\n <QueryClientProvider client={queryClient}>\n {children}\n </QueryClientProvider>\n );\n}\n\\`\\`\\``,\n\n // Sample 3: Lists and inline code\n `Here's what I found in the codebase:\n\n1. **Components** - Located in \\`src/components/\\`\n - \\`Button.tsx\\` - Primary button component\n - \\`Input.tsx\\` - Form input with validation\n - \\`Modal.tsx\\` - Accessible modal dialog\n\n2. **Hooks** - Custom React hooks in \\`src/hooks/\\`\n - \\`useDebounce\\` - Debounces rapidly changing values\n - \\`useLocalStorage\\` - Syncs state with localStorage\n - \\`useMediaQuery\\` - Responsive breakpoint detection\n\n3. **Utils** - Helper functions in \\`src/lib/\\`\n - \\`cn()\\` - Class name merger (tailwind-merge + clsx)\n - \\`formatDate()\\` - Date formatting with Intl API\n - \\`validateEmail()\\` - Email validation regex\n\nThe main entry point is \\`src/App.tsx\\` which sets up routing and providers.`,\n\n // Sample 4: Table and code\n `Here's a comparison of the state management options:\n\n| Feature | Zustand | Redux | Jotai |\n|---------|---------|-------|-------|\n| Bundle size | 1.5kb | 7kb | 2kb |\n| Boilerplate | Low | High | Low |\n| DevTools | Yes | Yes | Yes |\n| TypeScript | Excellent | Good | Excellent |\n\nBased on your requirements, I recommend Zustand:\n\n\\`\\`\\`typescript\nimport { create } from 'zustand';\n\ninterface AppState {\n count: number;\n increment: () => void;\n decrement: () => void;\n}\n\nexport const useAppStore = create<AppState>((set) => ({\n count: 0,\n increment: () => set((state) => ({ count: state.count + 1 })),\n decrement: () => set((state) => ({ count: state.count - 1 })),\n}));\n\\`\\`\\``,\n\n // Sample 5: Error explanation with code\n `I found the issue! The error occurs because of a race condition:\n\n\\`\\`\\`typescript\n// ❌ Bug: Race condition\nuseEffect(() => {\n fetchData().then(setData);\n}, [id]);\n\n// ✅ Fix: Cleanup function prevents stale updates\nuseEffect(() => {\n let cancelled = false;\n\n fetchData().then((result) => {\n if (!cancelled) {\n setData(result);\n }\n });\n\n return () => {\n cancelled = true;\n };\n}, [id]);\n\\`\\`\\`\n\nThe fix adds a cleanup function that sets a \\`cancelled\\` flag when the effect re-runs or the component unmounts.`,\n];\n\nconst THOUGHT_PREFIXES = [\n 'Let me analyze', 'I should check', 'Looking at', 'Considering',\n 'The approach here is', 'Based on the code', 'I need to', 'First',\n 'Next', 'This suggests', 'The pattern shows', 'Examining',\n];\n\nconst LOREM_WORDS = [\n 'Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit',\n 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore',\n 'magna', 'aliqua', 'Ut', 'enim', 'ad', 'minim', 'veniam', 'quis', 'nostrud',\n];\n\nconst PLAN_TASKS = [\n 'Analyze the current implementation',\n 'Identify areas for improvement',\n 'Refactor the component structure',\n 'Update the test suite',\n 'Fix type errors',\n 'Add documentation',\n 'Optimize performance',\n 'Review dependencies',\n];\n\n// ============================================================================\n// Rich tool call data\n// ============================================================================\n\ninterface RichToolCallConfig {\n toolName: string;\n kind: 'read' | 'edit' | 'execute' | 'search' | 'think' | 'fetch' | 'other';\n title: string;\n input: Record<string, unknown>;\n output?: string;\n diff?: string;\n}\n\nconst RICH_TOOL_CALLS: RichToolCallConfig[] = [\n // Bash tool with command and output\n {\n toolName: 'Bash',\n kind: 'execute',\n title: 'List files in src directory',\n input: {\n command: 'ls -la src/',\n description: 'List files in src directory',\n },\n output: `total 48\ndrwxr-xr-x 8 user user 4096 Jan 10 14:30 .\ndrwxr-xr-x 12 user user 4096 Jan 10 14:25 ..\ndrwxr-xr-x 4 user user 4096 Jan 10 14:30 components\ndrwxr-xr-x 2 user user 4096 Jan 10 14:28 hooks\ndrwxr-xr-x 2 user user 4096 Jan 10 14:27 lib\n-rw-r--r-- 1 user user 1245 Jan 10 14:30 App.tsx\n-rw-r--r-- 1 user user 892 Jan 10 14:25 main.tsx\n-rw-r--r-- 1 user user 2341 Jan 10 14:29 index.css`,\n },\n // Read tool with file contents\n {\n toolName: 'Read',\n kind: 'read',\n title: 'Read ~/repos/project/src/utils.ts',\n input: {\n file_path: '/home/user/repos/project/src/utils.ts',\n },\n output: `export function cn(...classes: string[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\nexport function formatDate(date: Date): string {\n return new Intl.DateTimeFormat('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n }).format(date);\n}\n\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delay: number\n): T {\n let timeoutId: NodeJS.Timeout;\n return ((...args) => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => fn(...args), delay);\n }) as T;\n}`,\n },\n // Edit tool with diff\n {\n toolName: 'Edit',\n kind: 'edit',\n title: 'Edit ~/repos/project/src/config.ts',\n input: {\n file_path: '/home/user/repos/project/src/config.ts',\n old_string: 'timeout: 5000',\n new_string: 'timeout: 10000',\n },\n diff: `--- a/src/config.ts\n+++ b/src/config.ts\n@@ -5,7 +5,7 @@ export const config = {\n apiUrl: process.env.API_URL,\n environment: process.env.NODE_ENV,\n features: {\n- timeout: 5000,\n+ timeout: 10000,\n retries: 3,\n cacheEnabled: true,\n },`,\n },\n // Glob tool\n {\n toolName: 'Glob',\n kind: 'search',\n title: 'Glob \"**/*.test.ts\"',\n input: {\n pattern: '**/*.test.ts',\n path: '/home/user/repos/project',\n },\n output: `src/components/Button.test.ts\nsrc/components/Input.test.ts\nsrc/hooks/useAuth.test.ts\nsrc/lib/utils.test.ts\nsrc/lib/api.test.ts`,\n },\n // Grep tool\n {\n toolName: 'Grep',\n kind: 'search',\n title: 'Grep \"useState\" in src/',\n input: {\n pattern: 'useState',\n path: '/home/user/repos/project/src',\n },\n output: `src/components/Counter.tsx:3:import { useState } from 'react';\nsrc/components/Form.tsx:1:import { useState, useCallback } from 'react';\nsrc/hooks/useToggle.ts:1:import { useState } from 'react';\nsrc/App.tsx:2:import { useState, useEffect } from 'react';`,\n },\n // Task/subagent tool\n {\n toolName: 'Task',\n kind: 'think',\n title: 'Explore Task',\n input: {\n subagent_type: 'Explore',\n description: 'Find authentication implementation',\n prompt: 'Search for authentication-related files and understand the auth flow',\n },\n output: `Found 3 relevant files:\n- src/hooks/useAuth.ts (main auth hook)\n- src/context/AuthContext.tsx (auth provider)\n- src/lib/auth.ts (token management)\n\nThe app uses JWT tokens stored in localStorage with automatic refresh.`,\n },\n // Write tool\n {\n toolName: 'Write',\n kind: 'edit',\n title: 'Write ~/repos/project/src/newFile.ts',\n input: {\n file_path: '/home/user/repos/project/src/newFile.ts',\n content: 'export const VERSION = \"1.0.0\";',\n },\n output: 'File created successfully',\n },\n // WebFetch tool\n {\n toolName: 'WebFetch',\n kind: 'fetch',\n title: 'Fetch https://api.example.com/docs',\n input: {\n url: 'https://api.example.com/docs',\n prompt: 'Get the API documentation',\n },\n output: `# API Documentation\n\n## Authentication\nAll requests require a Bearer token in the Authorization header.\n\n## Endpoints\n- GET /users - List all users\n- POST /users - Create a user\n- GET /users/:id - Get user by ID`,\n },\n];\n\n// ============================================================================\n// Utility functions\n// ============================================================================\n\nfunction randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nfunction randomChoice<T>(arr: T[]): T {\n return arr[Math.floor(Math.random() * arr.length)];\n}\n\nfunction generateText(length: number): string {\n const words: string[] = [];\n while (words.join(' ').length < length) {\n words.push(randomChoice(LOREM_WORDS));\n }\n return words.join(' ').slice(0, length);\n}\n\n// ============================================================================\n// Event generators\n// ============================================================================\n\nexport function generateMessageChunks(\n totalLength: number,\n chunkSize: number,\n kind: 'user_message_chunk' | 'agent_message_chunk' = 'agent_message_chunk'\n): SessionUpdate[] {\n const text = generateText(totalLength);\n const chunks: SessionUpdate[] = [];\n\n for (let i = 0; i < text.length; i += chunkSize) {\n const chunk = text.slice(i, i + chunkSize);\n chunks.push({\n sessionUpdate: kind,\n content: { type: 'text', text: chunk },\n } as SessionUpdate);\n }\n\n return chunks;\n}\n\n/**\n * Generate markdown content chunks for testing Streamdown\n */\nexport function generateMarkdownChunks(chunkSize: number = 50): SessionUpdate[] {\n const markdown = randomChoice(MARKDOWN_SAMPLES);\n const chunks: SessionUpdate[] = [];\n\n for (let i = 0; i < markdown.length; i += chunkSize) {\n const chunk = markdown.slice(i, i + chunkSize);\n chunks.push({\n sessionUpdate: 'agent_message_chunk',\n content: { type: 'text', text: chunk },\n } as SessionUpdate);\n }\n\n return chunks;\n}\n\nexport function generateThoughtChunks(totalLength: number): SessionUpdate[] {\n const prefix = randomChoice(THOUGHT_PREFIXES);\n const rest = generateText(totalLength - prefix.length);\n const text = `${prefix} ${rest}`;\n\n const chunkSize = randomInt(30, 60);\n const chunks: SessionUpdate[] = [];\n\n for (let i = 0; i < text.length; i += chunkSize) {\n const chunk = text.slice(i, i + chunkSize);\n chunks.push({\n sessionUpdate: 'agent_thought_chunk',\n content: { type: 'text', text: chunk },\n } as SessionUpdate);\n }\n\n return chunks;\n}\n\n/**\n * Generate a rich tool call with full ACP data and Claude metadata\n */\nexport function generateRichToolCall(config?: RichToolCallConfig): {\n call: SessionUpdate;\n update: SessionUpdate;\n toolCallId: string;\n} {\n const toolCallId = randomUUID();\n const toolConfig = config ?? randomChoice(RICH_TOOL_CALLS);\n\n // Initial tool_call event with full metadata\n const call: SessionUpdate = {\n sessionUpdate: 'tool_call',\n toolCallId,\n title: toolConfig.title,\n status: 'in_progress' as ToolCallStatus,\n kind: toolConfig.kind,\n rawInput: toolConfig.input,\n _meta: {\n claudeCode: {\n toolName: toolConfig.toolName,\n },\n },\n } as SessionUpdate;\n\n // Build toolResponse for the update\n const toolResponse: Array<{ type: string; text: string }> = [];\n if (toolConfig.output) {\n toolResponse.push({ type: 'text', text: toolConfig.output });\n }\n\n // Build content array for diff\n const content: Array<{ type: string; content?: string }> = [];\n if (toolConfig.diff) {\n content.push({ type: 'diff', content: toolConfig.diff });\n }\n\n // tool_call_update with output and status\n const update: SessionUpdate = {\n sessionUpdate: 'tool_call_update',\n toolCallId,\n status: 'completed' as ToolCallStatus,\n // Title update (sometimes tools update their title)\n title: toolConfig.title.replace('...', ''),\n _meta: {\n claudeCode: {\n toolName: toolConfig.toolName,\n toolResponse: toolResponse.length > 0 ? toolResponse : undefined,\n },\n },\n ...(content.length > 0 && { content }),\n } as SessionUpdate;\n\n return { call, update, toolCallId };\n}\n\n/**\n * Legacy simple tool call generator (for backwards compatibility)\n */\nexport function generateToolCall(title?: string): {\n call: SessionUpdate;\n update: SessionUpdate;\n toolCallId: string;\n} {\n // Use rich tool call generator with optional override\n if (title) {\n return generateRichToolCall({\n toolName: 'Bash',\n kind: 'execute',\n title,\n input: { command: 'echo \"test\"' },\n output: 'test',\n });\n }\n return generateRichToolCall();\n}\n\nexport function generatePlan(entryCount: number): SessionUpdate {\n const entries: AcpPlanEntry[] = [];\n const completedCount = randomInt(0, Math.floor(entryCount / 2));\n\n for (let i = 0; i < entryCount; i++) {\n const status = i < completedCount ? 'completed' : (i === completedCount ? 'in_progress' : 'pending');\n entries.push({\n content: randomChoice(PLAN_TASKS),\n priority: randomChoice(['high', 'medium', 'low'] as const),\n status,\n } as AcpPlanEntry);\n }\n\n return {\n sessionUpdate: 'plan',\n entries,\n } as SessionUpdate;\n}\n\n/**\n * Generate a realistic turn with markdown content and rich tool calls\n */\nexport function generateRealisticTurn(config: StressConfig = DEFAULT_STRESS_CONFIG): SessionUpdate[] {\n const events: SessionUpdate[] = [];\n\n // 1. Sometimes start with thinking\n if (Math.random() < config.thoughtProbability) {\n const thoughtLength = randomInt(100, 300);\n events.push(...generateThoughtChunks(thoughtLength));\n }\n\n // 2. Maybe update the plan\n if (Math.random() < config.planProbability) {\n events.push(generatePlan(randomInt(3, 6)));\n }\n\n // 3. Tool calls with rich data\n if (Math.random() < config.toolCallProbability) {\n const toolCount = randomInt(1, 3);\n for (let i = 0; i < toolCount; i++) {\n const { call, update } = generateRichToolCall();\n events.push(call);\n\n // Sometimes add thinking between tool call and update\n if (Math.random() < 0.3) {\n events.push(...generateThoughtChunks(randomInt(50, 100)));\n }\n\n events.push(update);\n }\n }\n\n // 4. Final response with MARKDOWN content (tests Streamdown)\n if (Math.random() < 0.7) {\n // 70% chance of rich markdown\n events.push(...generateMarkdownChunks(config.chunkSize));\n } else {\n // 30% chance of plain text\n const responseLength = randomInt(100, config.eventsPerTurn * config.chunkSize);\n events.push(...generateMessageChunks(responseLength, config.chunkSize));\n }\n\n return events;\n}\n\nexport function generateBurst(eventCount: number, chunkSize: number = 20): SessionUpdate[] {\n const events: SessionUpdate[] = [];\n for (let i = 0; i < eventCount; i++) {\n events.push({\n sessionUpdate: 'agent_message_chunk',\n content: { type: 'text', text: generateText(chunkSize) },\n } as SessionUpdate);\n }\n return events;\n}\n\n/**\n * Generate a sequence of rich tool calls\n */\nexport function generateToolSequence(count: number): SessionUpdate[] {\n const events: SessionUpdate[] = [];\n for (let i = 0; i < count; i++) {\n const { call, update } = generateRichToolCall();\n events.push(call);\n events.push(update);\n }\n return events;\n}\n\n/**\n * Generate a demo turn that showcases all features\n */\nexport function generateDemoTurn(): SessionUpdate[] {\n const events: SessionUpdate[] = [];\n\n // 1. Start with a thought\n events.push(...generateThoughtChunks(150));\n\n // 2. Show a plan\n events.push(generatePlan(4));\n\n // 3. Run through different tool types\n for (const toolConfig of RICH_TOOL_CALLS.slice(0, 4)) {\n const { call, update } = generateRichToolCall(toolConfig);\n events.push(call);\n events.push(update);\n }\n\n // 4. End with markdown response\n events.push(...generateMarkdownChunks(30));\n\n return events;\n}\n","/**\n * Global stress test configuration.\n * Updated via API, read by StressBackend.\n */\n\nexport interface StressRuntimeConfig {\n turnDelay: number; // seconds between turns\n eventsPerTurn: number; // events per turn\n eventDelay: number; // ms between events (for streaming effect)\n chunkSize: number; // characters per text chunk\n}\n\nconst config: StressRuntimeConfig = {\n turnDelay: 5,\n eventsPerTurn: 50,\n eventDelay: 30, // 30ms between events for visible streaming\n chunkSize: 20, // smaller chunks look more like streaming\n};\n\nexport function getStressRuntimeConfig(): StressRuntimeConfig {\n return { ...config };\n}\n\nexport function setStressRuntimeConfig(updates: Partial<StressRuntimeConfig>): void {\n if (updates.turnDelay !== undefined) {\n config.turnDelay = Math.max(0, Math.min(60, updates.turnDelay));\n }\n if (updates.eventsPerTurn !== undefined) {\n config.eventsPerTurn = Math.max(10, Math.min(500, updates.eventsPerTurn));\n }\n if (updates.eventDelay !== undefined) {\n config.eventDelay = Math.max(0, Math.min(200, updates.eventDelay));\n }\n if (updates.chunkSize !== undefined) {\n config.chunkSize = Math.max(5, Math.min(100, updates.chunkSize));\n }\n}\n","import type { ContentBlock } from '../types.js';\nimport path from 'path';\n\n/**\n * Parse message text with @mentions into ContentBlock array.\n * @mentions become resource_link blocks, other text becomes text blocks.\n *\n * Examples:\n * - \"hello world\" → [{ type: 'text', text: 'hello world' }]\n * - \"@src/foo.ts\" → [{ type: 'resource_link', uri: 'file://...', name: 'src/foo.ts' }]\n * - \"check @src/foo.ts for bugs\" → text + resource_link + text\n */\nexport function parseMessageToContentBlocks(\n message: string,\n workingDir: string\n): ContentBlock[] {\n const blocks: ContentBlock[] = [];\n // Match @path/to/file.ts, @./local.ts, @../parent/file.ts\n // Allows leading ./ or ../ followed by path characters\n const mentionRegex = /@(\\.{0,2}[\\w\\/\\.\\-]+)/g;\n\n let lastIndex = 0;\n for (const match of message.matchAll(mentionRegex)) {\n // Add text before mention\n if (match.index! > lastIndex) {\n const text = message.slice(lastIndex, match.index);\n if (text.trim()) {\n blocks.push({ type: 'text', text });\n }\n }\n\n // Add resource_link for mention\n const relativePath = match[1];\n const absolutePath = path.resolve(workingDir, relativePath);\n blocks.push({\n type: 'resource_link',\n uri: `file://${absolutePath}`,\n name: relativePath, // Required per ACP spec - use the path the user typed\n });\n\n lastIndex = match.index! + match[0].length;\n }\n\n // Add remaining text\n if (lastIndex < message.length) {\n const text = message.slice(lastIndex);\n if (text.trim()) {\n blocks.push({ type: 'text', text });\n }\n }\n\n // If no mentions found, return single text block\n return blocks.length > 0 ? blocks : [{ type: 'text', text: message }];\n}\n"],"mappings":"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","names":["randomUUID","existing","config","randomUUID","eq","randomUUID","randomUUID","randomUUID","fs","path","config","randomUUID","randomUUID","config","text","randomUUID","randomUUID","text","config","config","delay","config","randomUUID","text","path","text","eq","config","replayEvents"]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/src/db/index.js
CHANGED
|
@@ -1,289 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// src/db/index.ts
|
|
8
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
9
|
-
import Database from "better-sqlite3";
|
|
10
|
-
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
11
|
-
|
|
12
|
-
// src/lib/paths.ts
|
|
13
|
-
import path from "path";
|
|
14
|
-
import os from "os";
|
|
15
|
-
import fs from "fs";
|
|
16
|
-
var STARTUP_CWD = process.cwd();
|
|
17
|
-
function getDataDir() {
|
|
18
|
-
const home = os.homedir();
|
|
19
|
-
let dataDir;
|
|
20
|
-
switch (process.platform) {
|
|
21
|
-
case "darwin":
|
|
22
|
-
dataDir = path.join(home, "Library", "Application Support", "shella");
|
|
23
|
-
break;
|
|
24
|
-
case "win32":
|
|
25
|
-
dataDir = path.join(process.env.APPDATA || path.join(home, "AppData", "Roaming"), "shella");
|
|
26
|
-
break;
|
|
27
|
-
default:
|
|
28
|
-
dataDir = path.join(process.env.XDG_DATA_HOME || path.join(home, ".local", "share"), "shella");
|
|
29
|
-
}
|
|
30
|
-
return dataDir;
|
|
31
|
-
}
|
|
32
|
-
function isMockMode() {
|
|
33
|
-
return process.env.SHELLA_MOCK_MODE === "true";
|
|
34
|
-
}
|
|
35
|
-
function getDbPath() {
|
|
36
|
-
const filename = isMockMode() ? "shella.mock.db" : "shella.db";
|
|
37
|
-
return path.join(getDataDir(), filename);
|
|
38
|
-
}
|
|
39
|
-
function ensureDir(dir) {
|
|
40
|
-
if (!fs.existsSync(dir)) {
|
|
41
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// src/db/schema.ts
|
|
46
|
-
var schema_exports = {};
|
|
47
|
-
__export(schema_exports, {
|
|
48
|
-
agentConfigs: () => agentConfigs,
|
|
49
|
-
appState: () => appState,
|
|
50
|
-
sessionEvents: () => sessionEvents,
|
|
51
|
-
sessions: () => sessions
|
|
52
|
-
});
|
|
53
|
-
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
54
|
-
import { sql } from "drizzle-orm";
|
|
55
|
-
var agentConfigs = sqliteTable("agent_configs", {
|
|
56
|
-
id: text("id").primaryKey(),
|
|
57
|
-
name: text("name").notNull(),
|
|
58
|
-
command: text("command").notNull(),
|
|
59
|
-
args: text("args", { mode: "json" }).$type().default([]),
|
|
60
|
-
env: text("env", { mode: "json" }).$type().default({}),
|
|
61
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().default(sql`(unixepoch() * 1000)`)
|
|
62
|
-
});
|
|
63
|
-
var sessions = sqliteTable("sessions", {
|
|
64
|
-
id: text("id").primaryKey(),
|
|
65
|
-
directory: text("directory").notNull(),
|
|
66
|
-
agentConfigId: text("agent_config_id").notNull().references(() => agentConfigs.id),
|
|
67
|
-
// ACP protocol session ID for resuming (internal to agent protocol)
|
|
68
|
-
acpSessionId: text("acp_session_id"),
|
|
69
|
-
// Title from agent (via session_info_update)
|
|
70
|
-
title: text("title"),
|
|
71
|
-
model: text("model"),
|
|
72
|
-
mode: text("mode"),
|
|
73
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().default(sql`(unixepoch() * 1000)`)
|
|
74
|
-
});
|
|
75
|
-
var appState = sqliteTable("app_state", {
|
|
76
|
-
key: text("key").primaryKey(),
|
|
77
|
-
value: text("value").notNull()
|
|
78
|
-
});
|
|
79
|
-
var sessionEvents = sqliteTable("session_events", {
|
|
80
|
-
id: text("id").primaryKey(),
|
|
81
|
-
sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
82
|
-
turnId: text("turn_id").notNull(),
|
|
83
|
-
sequenceNum: integer("sequence_num").notNull(),
|
|
84
|
-
eventKind: text("event_kind").notNull(),
|
|
85
|
-
payload: text("payload", { mode: "json" }).notNull(),
|
|
86
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().default(sql`(unixepoch() * 1000)`)
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// src/db/seed.ts
|
|
90
|
-
import { randomUUID } from "crypto";
|
|
91
|
-
import { execSync } from "child_process";
|
|
92
|
-
var MOCK_AGENT_ID = "mock-agent";
|
|
93
|
-
function commandExists(cmd) {
|
|
94
|
-
try {
|
|
95
|
-
execSync(`which ${cmd}`, { stdio: "ignore" });
|
|
96
|
-
return true;
|
|
97
|
-
} catch {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
function detectAgents() {
|
|
102
|
-
const agents = [];
|
|
103
|
-
agents.push({
|
|
104
|
-
id: randomUUID(),
|
|
105
|
-
name: "Claude",
|
|
106
|
-
command: "npx",
|
|
107
|
-
args: ["@zed-industries/claude-code-acp"],
|
|
108
|
-
env: {}
|
|
109
|
-
});
|
|
110
|
-
if (commandExists("opencode")) {
|
|
111
|
-
agents.push({
|
|
112
|
-
id: randomUUID(),
|
|
113
|
-
name: "OpenCode",
|
|
114
|
-
command: "opencode",
|
|
115
|
-
args: ["acp"],
|
|
116
|
-
env: {}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
return agents;
|
|
120
|
-
}
|
|
121
|
-
var MOCK_CONFIGS = [
|
|
122
|
-
{
|
|
123
|
-
id: MOCK_AGENT_ID,
|
|
124
|
-
name: "Test Agent",
|
|
125
|
-
command: "__stress__",
|
|
126
|
-
args: [],
|
|
127
|
-
env: {}
|
|
128
|
-
}
|
|
129
|
-
];
|
|
130
|
-
function seedAgentConfigs() {
|
|
131
|
-
if (isMockMode()) {
|
|
132
|
-
const existing2 = db.select().from(agentConfigs).all();
|
|
133
|
-
if (existing2.length === 0) {
|
|
134
|
-
for (const config of MOCK_CONFIGS) {
|
|
135
|
-
db.insert(agentConfigs).values(config).run();
|
|
136
|
-
}
|
|
137
|
-
console.log("[db] Seeded mock agent configs");
|
|
138
|
-
}
|
|
139
|
-
seedMockSessions();
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const detected = detectAgents();
|
|
143
|
-
const existing = db.select().from(agentConfigs).all();
|
|
144
|
-
let added = 0;
|
|
145
|
-
for (const agent of detected) {
|
|
146
|
-
const alreadyExists = existing.some(
|
|
147
|
-
(e) => e.command === agent.command && JSON.stringify(e.args) === JSON.stringify(agent.args)
|
|
148
|
-
);
|
|
149
|
-
if (!alreadyExists) {
|
|
150
|
-
db.insert(agentConfigs).values(agent).run();
|
|
151
|
-
console.log(`[db] Detected new agent: ${agent.name}`);
|
|
152
|
-
added++;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (added === 0 && detected.length > 0) {
|
|
156
|
-
console.log(`[db] ${detected.length} known agent(s) already configured`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function seedMockSessions() {
|
|
160
|
-
const existing = db.select().from(sessions).all();
|
|
161
|
-
if (existing.length > 0) return;
|
|
162
|
-
const count = getSessionCount(10);
|
|
163
|
-
for (let i = 1; i <= count; i++) {
|
|
164
|
-
db.insert(sessions).values({
|
|
165
|
-
id: randomUUID(),
|
|
166
|
-
directory: "~",
|
|
167
|
-
agentConfigId: MOCK_AGENT_ID
|
|
168
|
-
}).run();
|
|
169
|
-
}
|
|
170
|
-
console.log(`[db] Seeded ${count} test sessions`);
|
|
171
|
-
}
|
|
172
|
-
function getSessionCount(defaultCount) {
|
|
173
|
-
const envCount = process.env.SHELLA_MOCK_SESSIONS ?? process.env.SHELLA_MOCK_WINDOWS;
|
|
174
|
-
if (envCount) {
|
|
175
|
-
const parsed = parseInt(envCount, 10);
|
|
176
|
-
if (!isNaN(parsed) && parsed > 0) {
|
|
177
|
-
return parsed;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return defaultCount;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// src/db/index.ts
|
|
184
|
-
ensureDir(getDataDir());
|
|
185
|
-
var sqlite = new Database(getDbPath());
|
|
186
|
-
sqlite.pragma("journal_mode = WAL");
|
|
187
|
-
sqlite.pragma("foreign_keys = ON");
|
|
188
|
-
var db = drizzle(sqlite, { schema: schema_exports });
|
|
189
|
-
sqlite.exec(`
|
|
190
|
-
CREATE TABLE IF NOT EXISTS agent_configs (
|
|
191
|
-
id TEXT PRIMARY KEY,
|
|
192
|
-
name TEXT NOT NULL,
|
|
193
|
-
command TEXT NOT NULL,
|
|
194
|
-
args TEXT DEFAULT '[]',
|
|
195
|
-
env TEXT DEFAULT '{}',
|
|
196
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
200
|
-
id TEXT PRIMARY KEY,
|
|
201
|
-
directory TEXT NOT NULL,
|
|
202
|
-
agent_config_id TEXT NOT NULL REFERENCES agent_configs(id),
|
|
203
|
-
acp_session_id TEXT,
|
|
204
|
-
title TEXT,
|
|
205
|
-
model TEXT,
|
|
206
|
-
mode TEXT,
|
|
207
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
CREATE TABLE IF NOT EXISTS app_state (
|
|
211
|
-
key TEXT PRIMARY KEY,
|
|
212
|
-
value TEXT NOT NULL
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
CREATE TABLE IF NOT EXISTS session_events (
|
|
216
|
-
id TEXT PRIMARY KEY,
|
|
217
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
218
|
-
turn_id TEXT NOT NULL,
|
|
219
|
-
sequence_num INTEGER NOT NULL,
|
|
220
|
-
event_kind TEXT NOT NULL,
|
|
221
|
-
payload TEXT NOT NULL,
|
|
222
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
223
|
-
);
|
|
224
|
-
`);
|
|
225
|
-
try {
|
|
226
|
-
const oldExists = sqlite.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='client_windows'`).get();
|
|
227
|
-
const newCount = sqlite.prepare(`SELECT COUNT(*) as count FROM sessions`).get();
|
|
228
|
-
if (oldExists && newCount.count === 0) {
|
|
229
|
-
console.log("[db] Migrating from client_windows/window_events to sessions/session_events...");
|
|
230
|
-
sqlite.exec(`
|
|
231
|
-
INSERT INTO sessions (id, directory, agent_config_id, acp_session_id, model, mode, created_at)
|
|
232
|
-
SELECT id, directory, agent_config_id, session_id, model, mode, created_at
|
|
233
|
-
FROM client_windows;
|
|
234
|
-
|
|
235
|
-
INSERT INTO session_events (id, session_id, turn_id, sequence_num, event_kind, payload, created_at)
|
|
236
|
-
SELECT id, window_id, turn_id, sequence_num, event_kind, payload, created_at
|
|
237
|
-
FROM window_events;
|
|
238
|
-
|
|
239
|
-
-- Migrate custom titles to app_state
|
|
240
|
-
INSERT OR IGNORE INTO app_state (key, value)
|
|
241
|
-
SELECT 'window_title_' || id, title
|
|
242
|
-
FROM client_windows
|
|
243
|
-
WHERE has_custom_title = 1 AND title != '';
|
|
244
|
-
|
|
245
|
-
-- Migrate active window
|
|
246
|
-
INSERT OR REPLACE INTO app_state (key, value)
|
|
247
|
-
SELECT 'active_window_id', value
|
|
248
|
-
FROM app_state
|
|
249
|
-
WHERE key = 'active_window_id';
|
|
250
|
-
`);
|
|
251
|
-
sqlite.exec(`
|
|
252
|
-
DROP TABLE IF EXISTS window_events;
|
|
253
|
-
DROP TABLE IF EXISTS client_windows;
|
|
254
|
-
`);
|
|
255
|
-
console.log("[db] Migration complete");
|
|
256
|
-
}
|
|
257
|
-
} catch (e) {
|
|
258
|
-
}
|
|
259
|
-
try {
|
|
260
|
-
sqlite.exec(`ALTER TABLE sessions ADD COLUMN title TEXT`);
|
|
261
|
-
} catch (e) {
|
|
262
|
-
}
|
|
263
|
-
var orphanedTurns = sqlite.prepare(`
|
|
264
|
-
SELECT DISTINCT turn_id, session_id
|
|
265
|
-
FROM session_events
|
|
266
|
-
WHERE event_kind = 'turn_start'
|
|
267
|
-
AND turn_id NOT IN (
|
|
268
|
-
SELECT turn_id FROM session_events WHERE event_kind = 'turn_end'
|
|
269
|
-
)
|
|
270
|
-
`).all();
|
|
271
|
-
if (orphanedTurns.length > 0) {
|
|
272
|
-
const getMaxSeq = sqlite.prepare(`
|
|
273
|
-
SELECT COALESCE(MAX(sequence_num), 0) + 1 as next_seq
|
|
274
|
-
FROM session_events WHERE turn_id = ?
|
|
275
|
-
`);
|
|
276
|
-
const insert = sqlite.prepare(`
|
|
277
|
-
INSERT INTO session_events (id, session_id, turn_id, sequence_num, event_kind, payload)
|
|
278
|
-
VALUES (?, ?, ?, ?, 'turn_end', '{"amuxEvent":"turn_end"}')
|
|
279
|
-
`);
|
|
280
|
-
for (const turn of orphanedTurns) {
|
|
281
|
-
const { next_seq } = getMaxSeq.get(turn.turn_id);
|
|
282
|
-
insert.run(randomUUID2(), turn.session_id, turn.turn_id, next_seq);
|
|
283
|
-
}
|
|
284
|
-
console.log(`[db] Fixed ${orphanedTurns.length} orphaned turn(s) from previous session`);
|
|
285
|
-
}
|
|
286
|
-
seedAgentConfigs();
|
|
1
|
+
import {
|
|
2
|
+
db,
|
|
3
|
+
schema_exports
|
|
4
|
+
} from "../../chunk-OQ5K5ON2.js";
|
|
5
|
+
import "../../chunk-PZ5AY32C.js";
|
|
287
6
|
export {
|
|
288
7
|
db,
|
|
289
8
|
schema_exports as schema
|
package/dist/src/db/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/db/index.ts","../../../src/lib/paths.ts","../../../src/db/schema.ts","../../../src/db/seed.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport Database from 'better-sqlite3';\nimport { drizzle } from 'drizzle-orm/better-sqlite3';\nimport { getDbPath, ensureDir, getDataDir } from '../lib/paths.js';\nimport * as schema from './schema.js';\nimport { seedAgentConfigs } from './seed.js';\n\nensureDir(getDataDir());\n\nconst sqlite = new Database(getDbPath());\nsqlite.pragma('journal_mode = WAL');\nsqlite.pragma('foreign_keys = ON');\n\nexport const db = drizzle(sqlite, { schema });\n\n// Create tables if they don't exist\nsqlite.exec(`\n CREATE TABLE IF NOT EXISTS agent_configs (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n command TEXT NOT NULL,\n args TEXT DEFAULT '[]',\n env TEXT DEFAULT '{}',\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n );\n\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n directory TEXT NOT NULL,\n agent_config_id TEXT NOT NULL REFERENCES agent_configs(id),\n acp_session_id TEXT,\n title TEXT,\n model TEXT,\n mode TEXT,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n );\n\n CREATE TABLE IF NOT EXISTS app_state (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS session_events (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n turn_id TEXT NOT NULL,\n sequence_num INTEGER NOT NULL,\n event_kind TEXT NOT NULL,\n payload TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n );\n`);\n\n// Migration: rename old tables if they exist\ntry {\n // Check if old tables exist and new ones don't have data\n const oldExists = sqlite.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='client_windows'`).get();\n const newCount = sqlite.prepare(`SELECT COUNT(*) as count FROM sessions`).get() as { count: number };\n\n if (oldExists && newCount.count === 0) {\n console.log('[db] Migrating from client_windows/window_events to sessions/session_events...');\n\n // Migrate data from old tables to new tables\n sqlite.exec(`\n INSERT INTO sessions (id, directory, agent_config_id, acp_session_id, model, mode, created_at)\n SELECT id, directory, agent_config_id, session_id, model, mode, created_at\n FROM client_windows;\n\n INSERT INTO session_events (id, session_id, turn_id, sequence_num, event_kind, payload, created_at)\n SELECT id, window_id, turn_id, sequence_num, event_kind, payload, created_at\n FROM window_events;\n\n -- Migrate custom titles to app_state\n INSERT OR IGNORE INTO app_state (key, value)\n SELECT 'window_title_' || id, title\n FROM client_windows\n WHERE has_custom_title = 1 AND title != '';\n\n -- Migrate active window\n INSERT OR REPLACE INTO app_state (key, value)\n SELECT 'active_window_id', value\n FROM app_state\n WHERE key = 'active_window_id';\n `);\n\n // Drop old tables\n sqlite.exec(`\n DROP TABLE IF EXISTS window_events;\n DROP TABLE IF EXISTS client_windows;\n `);\n\n console.log('[db] Migration complete');\n }\n} catch (e) {\n // Migration not needed or already done\n}\n\n// Migration: add title column to sessions if missing\ntry {\n sqlite.exec(`ALTER TABLE sessions ADD COLUMN title TEXT`);\n} catch (e) {\n // Column already exists\n}\n\n// Fix orphaned turns from previous crash (turn_start without turn_end)\n// Using raw SQL to avoid circular dependency with eventStore\nconst orphanedTurns = sqlite.prepare(`\n SELECT DISTINCT turn_id, session_id\n FROM session_events\n WHERE event_kind = 'turn_start'\n AND turn_id NOT IN (\n SELECT turn_id FROM session_events WHERE event_kind = 'turn_end'\n )\n`).all() as { turn_id: string; session_id: string }[];\n\nif (orphanedTurns.length > 0) {\n const getMaxSeq = sqlite.prepare(`\n SELECT COALESCE(MAX(sequence_num), 0) + 1 as next_seq\n FROM session_events WHERE turn_id = ?\n `);\n const insert = sqlite.prepare(`\n INSERT INTO session_events (id, session_id, turn_id, sequence_num, event_kind, payload)\n VALUES (?, ?, ?, ?, 'turn_end', '{\"amuxEvent\":\"turn_end\"}')\n `);\n\n for (const turn of orphanedTurns) {\n const { next_seq } = getMaxSeq.get(turn.turn_id) as { next_seq: number };\n insert.run(randomUUID(), turn.session_id, turn.turn_id, next_seq);\n }\n\n console.log(`[db] Fixed ${orphanedTurns.length} orphaned turn(s) from previous session`);\n}\n\n// Seed defaults\nseedAgentConfigs();\n\nexport { schema };\n","import path from 'path';\nimport os from 'os';\nimport fs from 'fs';\n\n// Capture at module load time (before any chdir)\nconst STARTUP_CWD = process.cwd();\n\n/**\n * Get the working directory from when the server started\n */\nexport function getStartupCwd(): string {\n return STARTUP_CWD;\n}\n\n/**\n * Get the data directory following XDG Base Directory spec\n * - Linux: ~/.local/share/shella\n * - macOS: ~/Library/Application Support/shella\n * - Windows: %APPDATA%/shella\n */\nexport function getDataDir(): string {\n const home = os.homedir();\n\n let dataDir: string;\n\n switch (process.platform) {\n case 'darwin':\n dataDir = path.join(home, 'Library', 'Application Support', 'shella');\n break;\n case 'win32':\n dataDir = path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'shella');\n break;\n default:\n // Linux and others - follow XDG spec\n dataDir = path.join(process.env.XDG_DATA_HOME || path.join(home, '.local', 'share'), 'shella');\n }\n\n return dataDir;\n}\n\n/**\n * Get the config directory following XDG Base Directory spec\n * - Linux: ~/.config/shella\n * - macOS: ~/Library/Application Support/shella\n * - Windows: %APPDATA%/shella\n */\nexport function getConfigDir(): string {\n const home = os.homedir();\n\n let configDir: string;\n\n switch (process.platform) {\n case 'darwin':\n configDir = path.join(home, 'Library', 'Application Support', 'shella');\n break;\n case 'win32':\n configDir = path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'shella');\n break;\n default:\n // Linux and others - follow XDG spec\n configDir = path.join(process.env.XDG_CONFIG_HOME || path.join(home, '.config'), 'shella');\n }\n\n return configDir;\n}\n\n/**\n * Get the cache directory\n * - Linux: ~/.cache/shella\n * - macOS: ~/Library/Caches/shella\n * - Windows: %LOCALAPPDATA%/shella/cache\n */\nexport function getCacheDir(): string {\n const home = os.homedir();\n\n let cacheDir: string;\n\n switch (process.platform) {\n case 'darwin':\n cacheDir = path.join(home, 'Library', 'Caches', 'shella');\n break;\n case 'win32':\n cacheDir = path.join(process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'shella', 'cache');\n break;\n default:\n // Linux and others - follow XDG spec\n cacheDir = path.join(process.env.XDG_CACHE_HOME || path.join(home, '.cache'), 'shella');\n }\n\n return cacheDir;\n}\n\n/**\n * Check if running in mock mode\n */\nexport function isMockMode(): boolean {\n return process.env.SHELLA_MOCK_MODE === 'true';\n}\n\n/**\n * Get the path to the database file\n */\nexport function getDbPath(): string {\n const filename = isMockMode() ? 'shella.mock.db' : 'shella.db';\n return path.join(getDataDir(), filename);\n}\n\n/**\n * Ensure a directory exists, creating it if necessary\n */\nexport function ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\n/**\n * Ensure all shella directories exist\n */\nexport function ensureAllDirs(): void {\n ensureDir(getDataDir());\n ensureDir(getConfigDir());\n ensureDir(getCacheDir());\n}\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';\nimport { sql } from 'drizzle-orm';\n\n// User-configured agent types (e.g. claude-code-acp, opencode, etc.)\nexport const agentConfigs = sqliteTable('agent_configs', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n command: text('command').notNull(),\n args: text('args', { mode: 'json' }).$type<string[]>().default([]),\n env: text('env', { mode: 'json' }).$type<Record<string, string>>().default({}),\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\n .notNull()\n .default(sql`(unixepoch() * 1000)`),\n});\n\n// Agent sessions - each session is an independent agent instance.\nexport const sessions = sqliteTable('sessions', {\n id: text('id').primaryKey(),\n directory: text('directory').notNull(),\n agentConfigId: text('agent_config_id').notNull().references(() => agentConfigs.id),\n // ACP protocol session ID for resuming (internal to agent protocol)\n acpSessionId: text('acp_session_id'),\n // Title from agent (via session_info_update)\n title: text('title'),\n model: text('model'),\n mode: text('mode'),\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\n .notNull()\n .default(sql`(unixepoch() * 1000)`),\n});\n\nexport const appState = sqliteTable('app_state', {\n key: text('key').primaryKey(),\n value: text('value').notNull(),\n});\n\n// Persisted session events for replay on reconnect\nexport const sessionEvents = sqliteTable('session_events', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n turnId: text('turn_id').notNull(),\n sequenceNum: integer('sequence_num').notNull(),\n eventKind: text('event_kind').notNull(),\n payload: text('payload', { mode: 'json' }).notNull(),\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\n .notNull()\n .default(sql`(unixepoch() * 1000)`),\n});\n\nexport type AgentConfig = typeof agentConfigs.$inferSelect;\nexport type NewAgentConfig = typeof agentConfigs.$inferInsert;\nexport type Session = typeof sessions.$inferSelect;\nexport type NewSession = typeof sessions.$inferInsert;\nexport type StoredSessionEvent = typeof sessionEvents.$inferSelect;\nexport type NewStoredSessionEvent = typeof sessionEvents.$inferInsert;\n\n// Legacy aliases for compatibility during migration\n/** @deprecated Use Session instead */\nexport type ClientWindow = Session;\n/** @deprecated Use NewSession instead */\nexport type NewClientWindow = NewSession;\n","import { db } from './index.js';\nimport { agentConfigs, sessions } from './schema.js';\nimport { randomUUID } from 'crypto';\nimport { execSync } from 'child_process';\nimport { isMockMode } from '../lib/paths.js';\n\nconst MOCK_AGENT_ID = 'mock-agent';\n\n// Check if a command exists on the system\nfunction commandExists(cmd: string): boolean {\n try {\n execSync(`which ${cmd}`, { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\n// Detect available ACP-compatible agents\nfunction detectAgents(): Array<{ id: string; name: string; command: string; args: string[]; env: Record<string, string> }> {\n const agents: Array<{ id: string; name: string; command: string; args: string[]; env: Record<string, string> }> = [];\n\n // Claude - always available via npx\n agents.push({\n id: randomUUID(),\n name: 'Claude',\n command: 'npx',\n args: ['@zed-industries/claude-code-acp'],\n env: {},\n });\n\n // OpenCode - check if installed\n if (commandExists('opencode')) {\n agents.push({\n id: randomUUID(),\n name: 'OpenCode',\n command: 'opencode',\n args: ['acp'],\n env: {},\n });\n }\n\n return agents;\n}\n\n// Mock mode now uses the stress backend for full testing capabilities\nconst MOCK_CONFIGS = [\n {\n id: MOCK_AGENT_ID,\n name: 'Test Agent',\n command: '__stress__',\n args: [],\n env: {},\n },\n];\n\nexport function seedAgentConfigs() {\n if (isMockMode()) {\n // Mock mode: seed once as before\n const existing = db.select().from(agentConfigs).all();\n if (existing.length === 0) {\n for (const config of MOCK_CONFIGS) {\n db.insert(agentConfigs).values(config).run();\n }\n console.log('[db] Seeded mock agent configs');\n }\n seedMockSessions();\n return;\n }\n\n // Production: detect and add new agents on every launch\n const detected = detectAgents();\n const existing = db.select().from(agentConfigs).all();\n\n let added = 0;\n for (const agent of detected) {\n // Check if this agent already exists (by command + args)\n const alreadyExists = existing.some(\n (e) => e.command === agent.command &&\n JSON.stringify(e.args) === JSON.stringify(agent.args)\n );\n if (!alreadyExists) {\n db.insert(agentConfigs).values(agent).run();\n console.log(`[db] Detected new agent: ${agent.name}`);\n added++;\n }\n }\n\n if (added === 0 && detected.length > 0) {\n console.log(`[db] ${detected.length} known agent(s) already configured`);\n }\n}\n\nfunction seedMockSessions() {\n const existing = db.select().from(sessions).all();\n if (existing.length > 0) return;\n\n const count = getSessionCount(10);\n for (let i = 1; i <= count; i++) {\n db.insert(sessions).values({\n id: randomUUID(),\n directory: '~',\n agentConfigId: MOCK_AGENT_ID,\n }).run();\n }\n console.log(`[db] Seeded ${count} test sessions`);\n}\n\nfunction getSessionCount(defaultCount: number): number {\n // Support both new and legacy env var names\n const envCount = process.env.SHELLA_MOCK_SESSIONS ?? process.env.SHELLA_MOCK_WINDOWS;\n if (envCount) {\n const parsed = parseInt(envCount, 10);\n if (!isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n return defaultCount;\n}\n"],"mappings":"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","names":["randomUUID","existing","randomUUID"]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|