openfox 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +68 -0
  2. package/dist/branch.api-WDHKZJS6.js +48 -0
  3. package/dist/branch.api-WDHKZJS6.js.map +1 -0
  4. package/dist/chunk-26NRL3HX.js +116 -0
  5. package/dist/chunk-26NRL3HX.js.map +1 -0
  6. package/dist/chunk-43SWNGYW.js +273 -0
  7. package/dist/chunk-43SWNGYW.js.map +1 -0
  8. package/dist/chunk-4Z2OZ6NX.js +132 -0
  9. package/dist/chunk-4Z2OZ6NX.js.map +1 -0
  10. package/dist/chunk-6MOGZMGX.js +218 -0
  11. package/dist/chunk-6MOGZMGX.js.map +1 -0
  12. package/dist/chunk-AF7XS4OI.js +55 -0
  13. package/dist/chunk-AF7XS4OI.js.map +1 -0
  14. package/dist/chunk-EI46G5WO.js +138 -0
  15. package/dist/chunk-EI46G5WO.js.map +1 -0
  16. package/dist/chunk-FF6Z2MC3.js +844 -0
  17. package/dist/chunk-FF6Z2MC3.js.map +1 -0
  18. package/dist/chunk-IKAWIOXM.js +5817 -0
  19. package/dist/chunk-IKAWIOXM.js.map +1 -0
  20. package/dist/chunk-IVHCPQO2.js +29 -0
  21. package/dist/chunk-IVHCPQO2.js.map +1 -0
  22. package/dist/chunk-MTX5Y3V3.js +46 -0
  23. package/dist/chunk-MTX5Y3V3.js.map +1 -0
  24. package/dist/chunk-N2XYX4ZA.js +83 -0
  25. package/dist/chunk-N2XYX4ZA.js.map +1 -0
  26. package/dist/chunk-QY7BMXWT.js +124 -0
  27. package/dist/chunk-QY7BMXWT.js.map +1 -0
  28. package/dist/chunk-U62ZECSQ.js +54 -0
  29. package/dist/chunk-U62ZECSQ.js.map +1 -0
  30. package/dist/chunk-YQ36AK5A.js +5570 -0
  31. package/dist/chunk-YQ36AK5A.js.map +1 -0
  32. package/dist/cli/dev.d.ts +1 -0
  33. package/dist/cli/dev.js +14 -0
  34. package/dist/cli/dev.js.map +1 -0
  35. package/dist/cli/index.d.ts +1 -0
  36. package/dist/cli/index.js +14 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/command-defaults/defaults/commit-push.command.md +13 -0
  39. package/dist/command-defaults/defaults/init.command.md +18 -0
  40. package/dist/command-defaults/defaults/test-ui.command.md +16 -0
  41. package/dist/config-6GJ4SVFH.js +33 -0
  42. package/dist/config-6GJ4SVFH.js.map +1 -0
  43. package/dist/init-UVZDOHMX.js +185 -0
  44. package/dist/init-UVZDOHMX.js.map +1 -0
  45. package/dist/paths-4BPXWB2W.js +17 -0
  46. package/dist/paths-4BPXWB2W.js.map +1 -0
  47. package/dist/project-creator-CWPKRIKI.js +69 -0
  48. package/dist/project-creator-CWPKRIKI.js.map +1 -0
  49. package/dist/protocol-BJsm88OW.d.ts +760 -0
  50. package/dist/provider-LCE6WZA6.js +343 -0
  51. package/dist/provider-LCE6WZA6.js.map +1 -0
  52. package/dist/serve-27DZK34K.js +177 -0
  53. package/dist/serve-27DZK34K.js.map +1 -0
  54. package/dist/server/index.d.ts +479 -0
  55. package/dist/server/index.js +20 -0
  56. package/dist/server/index.js.map +1 -0
  57. package/dist/settings-D5O3TMLD.js +17 -0
  58. package/dist/settings-D5O3TMLD.js.map +1 -0
  59. package/dist/shared/index.d.ts +19 -0
  60. package/dist/shared/index.js +17 -0
  61. package/dist/shared/index.js.map +1 -0
  62. package/dist/skill-defaults/defaults/playwright-cli.skill.md +38 -0
  63. package/dist/tools-R4YQAYUF.js +46 -0
  64. package/dist/tools-R4YQAYUF.js.map +1 -0
  65. package/dist/web/assets/html2canvas.esm-QH1iLAAe.js +22 -0
  66. package/dist/web/assets/index-BMNDCsbJ.js +120 -0
  67. package/dist/web/assets/index-BSlSWZrM.css +1 -0
  68. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  69. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  70. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  71. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  72. package/dist/web/assets/jetbrains-mono-cyrillic-600-normal-8K4wrrwR.woff +0 -0
  73. package/dist/web/assets/jetbrains-mono-cyrillic-600-normal-EVf6-Yzo.woff2 +0 -0
  74. package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  75. package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  76. package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  77. package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  78. package/dist/web/assets/jetbrains-mono-greek-600-normal-H7WoG9Et.woff2 +0 -0
  79. package/dist/web/assets/jetbrains-mono-greek-600-normal-mc2nkWzM.woff +0 -0
  80. package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  81. package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  82. package/dist/web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  83. package/dist/web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  84. package/dist/web/assets/jetbrains-mono-latin-600-normal-BfsvjouI.woff +0 -0
  85. package/dist/web/assets/jetbrains-mono-latin-600-normal-C8RAYTDA.woff2 +0 -0
  86. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  87. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  88. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  89. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  90. package/dist/web/assets/jetbrains-mono-latin-ext-600-normal-BfB_LPfz.woff2 +0 -0
  91. package/dist/web/assets/jetbrains-mono-latin-ext-600-normal-DObL3zCW.woff +0 -0
  92. package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  93. package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  94. package/dist/web/assets/jetbrains-mono-vietnamese-600-normal-OWROknRo.woff +0 -0
  95. package/dist/web/assets/openfox.png +0 -0
  96. package/dist/web/fox.svg +25 -0
  97. package/dist/web/index.html +14 -0
  98. package/dist/web/sounds/achievement.mp3 +0 -0
  99. package/dist/web/sounds/intervention.mp3 +0 -0
  100. package/dist/web/sounds/notification.mp3 +0 -0
  101. package/dist/web/sounds/waiting-for-user.mp3 +0 -0
  102. package/package.json +101 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/tools/read.ts","../src/server/tools/types.ts","../src/server/tools/tool-helpers.ts","../src/server/tools/path-security.ts","../src/server/ws/protocol.ts","../src/server/tools/file-tracker.ts","../src/server/tools/write.ts","../src/server/tools/diagnostics.ts","../src/server/tools/edit.ts","../src/server/tools/edit-context.ts","../src/server/tools/shell.ts","../src/server/utils/process-tree.ts","../src/server/tools/glob.ts","../src/server/tools/grep.ts","../src/server/tools/git.ts","../src/server/utils/async.ts","../src/server/tools/ask.ts","../src/server/tools/criterion.ts","../src/server/tools/planner-criteria.ts","../src/server/tools/todo.ts","../src/server/agents/registry.ts","../src/server/config.ts","../src/server/runtime-config.ts","../src/server/chat/prompts.ts","../src/server/chat/stats.ts","../src/server/chat/stream-pure.ts","../src/server/chat/tool-streaming.ts","../src/server/events/store.ts","../src/server/events/folding.ts","../src/server/events/session.ts","../src/server/context/instructions.ts","../src/server/context/compactor.ts","../src/server/chat/request-context.ts","../src/server/context/auto-compaction.ts","../src/server/skills/registry.ts","../src/server/chat/agent-loop.ts","../src/server/sub-agents/manager.ts","../src/server/tools/sub-agent.ts","../src/server/tools/load-skill.ts","../src/server/tools/return-value.ts","../src/server/tools/web-fetch.ts","../src/server/tools/index.ts"],"sourcesContent":["import { readFile, stat } from 'node:fs/promises'\nimport { extname } from 'node:path'\nimport { OUTPUT_LIMITS } from './types.js'\nimport { createTool } from './tool-helpers.js'\nimport { computeFileHash } from './file-tracker.js'\n\ninterface ReadFileArgs {\n path: string\n offset?: number\n limit?: number\n}\n\n// Image file extensions and their MIME types\nconst IMAGE_MIME_TYPES: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.bmp': 'image/bmp',\n '.svg': 'image/svg+xml',\n}\n\n// Magic byte signatures for image formats\nconst IMAGE_SIGNATURES: Array<{ mimeType: string; signature: Buffer; extension: string }> = [\n { mimeType: 'image/png', signature: Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]), extension: '.png' },\n { mimeType: 'image/jpeg', signature: Buffer.from([0xFF, 0xD8, 0xFF]), extension: '.jpg' },\n { mimeType: 'image/gif', signature: Buffer.from([0x47, 0x49, 0x46, 0x38]), extension: '.gif' },\n { mimeType: 'image/webp', signature: Buffer.from([]), extension: '.webp' }, // WebP handled specially\n { mimeType: 'image/bmp', signature: Buffer.from([0x42, 0x4D]), extension: '.bmp' },\n]\n\n/**\n * Detect if a buffer is an image by checking magic byte signatures.\n * Falls back to extension-based detection if no signature matches.\n */\nfunction detectImageType(buffer: Buffer, filePath: string): string | null {\n // First try magic byte detection\n for (const { mimeType, signature } of IMAGE_SIGNATURES) {\n if (mimeType === 'image/webp') {\n // WebP signature check (bytes 0-3 = RIFF, 8-11 = WEBP)\n if (buffer.length >= 12 &&\n buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46 &&\n buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) {\n return mimeType\n }\n } else if (buffer.length >= signature.length) {\n let matches = true\n for (let i = 0; i < signature.length; i++) {\n if (buffer[i] !== signature[i]) {\n matches = false\n break\n }\n }\n if (matches) {\n return mimeType\n }\n }\n }\n \n // Fall back to extension-based detection\n const ext = extname(filePath).toLowerCase()\n const mimeType = IMAGE_MIME_TYPES[ext]\n if (mimeType) {\n return mimeType\n }\n \n // Check for SVG text content\n if (buffer.length > 0) {\n const textStart = buffer.subarray(0, Math.min(100, buffer.length)).toString('utf-8')\n if (textStart.includes('<svg')) {\n return 'image/svg+xml'\n }\n }\n \n return null\n}\n\nexport const readFileTool = createTool<ReadFileArgs>(\n 'read_file',\n {\n type: 'function',\n function: {\n name: 'read_file',\n description: 'Read the contents of a file. For text files: returns line-numbered content. For images (PNG, JPEG, GIF, WebP, BMP, SVG): returns base64-encoded data with MIME type metadata.',\n parameters: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Path to the file (relative to workdir or absolute)',\n },\n offset: {\n type: 'number',\n description: 'Line number to start from (1-indexed). Default: 1. Only applies to text files.',\n },\n limit: {\n type: 'number',\n description: `Maximum number of lines to read. Default: ${OUTPUT_LIMITS.read_file.maxLines}. Only applies to text files.`,\n },\n },\n required: ['path'],\n },\n },\n },\n async (args, context, helpers) => {\n const startTime = Date.now()\n const offset = args.offset ?? 1\n const limit = Math.min(\n args.limit ?? OUTPUT_LIMITS.read_file.maxLines,\n OUTPUT_LIMITS.read_file.maxLines\n )\n \n const fullPath = helpers.resolvePath(args.path)\n await helpers.checkPathAccess([fullPath])\n \n // Check if file exists and is not a directory\n try {\n const stats = await stat(fullPath)\n if (stats.isDirectory()) {\n return helpers.error(`Path is a directory, not a file: ${args.path}`)\n }\n \n // Check image size limit before reading\n if (stats.size > OUTPUT_LIMITS.read_file.maxImageBytes) {\n return helpers.error(`File size (${stats.size} bytes) exceeds image size limit (2MB). Use shell command to process large files.`)\n }\n } catch {\n return helpers.error(`File not found: ${args.path}`)\n }\n \n // Read file as binary first to detect type\n const rawBuffer = await readFile(fullPath)\n \n // Detect if this is an image file\n const mimeType = detectImageType(rawBuffer, args.path)\n \n if (mimeType) {\n // Handle as image\n const base64Data = rawBuffer.toString('base64')\n \n // Record file read with content hash for write validation\n const contentHash = await computeFileHash(fullPath)\n if (contentHash) {\n context.sessionManager.recordFileRead(context.sessionId, fullPath, contentHash)\n }\n \n return {\n success: true,\n durationMs: Date.now() - startTime,\n truncated: false,\n metadata: {\n mimeType,\n size: rawBuffer.length,\n base64Data,\n path: fullPath,\n },\n }\n }\n \n // Handle as text file\n const content = rawBuffer.toString('utf-8')\n const lines = content.split('\\n')\n const totalLines = lines.length\n \n // Apply offset and limit\n const startLine = Math.max(1, offset)\n const endLine = Math.min(startLine + limit - 1, totalLines)\n const selectedLines = lines.slice(startLine - 1, endLine)\n \n // Format with line numbers\n const formatted = selectedLines\n .map((line, i) => `${startLine + i}: ${line}`)\n .join('\\n')\n \n // Check if truncated\n const truncated = endLine < totalLines\n let output = formatted\n \n if (truncated) {\n output += `\\n\\n[Showing lines ${startLine}-${endLine} of ${totalLines} total. Use offset to read more.]`\n }\n \n // Check byte limit\n if (output.length > OUTPUT_LIMITS.read_file.maxBytes) {\n output = output.slice(0, OUTPUT_LIMITS.read_file.maxBytes)\n output += '\\n\\n[Output truncated due to size limit]'\n }\n \n // Record file read with content hash for write validation\n const contentHash = await computeFileHash(fullPath)\n if (contentHash) {\n context.sessionManager.recordFileRead(context.sessionId, fullPath, contentHash)\n }\n \n return helpers.success(output, truncated)\n }\n)\n","import type { ToolResult, StatsIdentity } from '../../shared/types.js'\nexport type { ToolResult } from '../../shared/types.js'\nimport type { ServerMessage } from '../../shared/protocol.js'\nimport type { LLMToolDefinition } from '../llm/types.js'\nimport type { LspManagerInterface } from '../lsp/types.js'\nimport type { SessionManager } from '../session/manager.js'\nimport type { LLMClientWithModel } from '../llm/client.js'\n\nexport interface ToolContext {\n workdir: string\n sessionId: string\n sessionManager: SessionManager // Injected dependency (replaces singleton import)\n signal?: AbortSignal | undefined // For cancelling long-running operations (e.g., shell commands)\n onProgress?: ((message: string) => void) | undefined\n onEvent?: ((event: ServerMessage) => void) | undefined // For sending events to client (e.g., path confirmation)\n lspManager?: LspManagerInterface | undefined // Optional LSP manager for file diagnostics\n llmClient?: LLMClientWithModel | undefined // For tools that need to spawn LLM calls (e.g., call_sub_agent)\n statsIdentity?: StatsIdentity | undefined // For tools that track metrics\n}\n\nexport interface Tool {\n name: string\n definition: LLMToolDefinition\n execute: (args: Record<string, unknown>, context: ToolContext) => Promise<ToolResult>\n}\n\nexport interface ToolRegistry {\n tools: Tool[]\n definitions: LLMToolDefinition[]\n execute: (name: string, args: Record<string, unknown>, context: ToolContext) => Promise<ToolResult>\n}\n\n// Output limits to prevent context overflow\nexport const OUTPUT_LIMITS = {\n read_file: {\n maxLines: 2000,\n maxBytes: 100_000,\n maxImageBytes: 2_097_152, // 2MB for images\n },\n run_command: {\n maxLines: 2000,\n maxBytes: 50_000,\n },\n glob: {\n maxResults: 500,\n },\n grep: {\n maxMatches: 200,\n },\n web_fetch: {\n maxBytes: 100_000,\n },\n}\n","import { resolve, isAbsolute } from 'node:path'\nimport type { ToolResult } from '../../shared/types.js'\nimport type { Tool, ToolContext } from './types.js'\nimport type { LLMToolDefinition } from '../llm/types.js'\nimport { requestPathAccess, PathAccessDeniedError } from './path-security.js'\n\n/**\n * Helper utilities provided to tool handlers by createTool.\n * These encapsulate common patterns like path resolution and result formatting.\n */\nexport interface ToolHelpers {\n /** Resolve a path relative to workdir (or return as-is if absolute) */\n resolvePath: (path: string) => string\n \n /** Check path access and request user confirmation if needed. Throws PathAccessDeniedError if denied. */\n checkPathAccess: (paths: string[]) => Promise<void>\n \n /** Create an error result with timing */\n error: (message: string, truncated?: boolean) => ToolResult\n \n /** Create a success result with timing */\n success: (output: string, truncated?: boolean, extra?: Partial<ToolResult>) => ToolResult\n}\n\n/**\n * Handler function type for tools created with createTool.\n * Receives typed args, context, and helper utilities.\n */\nexport type ToolHandler<TArgs> = (\n args: TArgs,\n context: ToolContext,\n helpers: ToolHelpers\n) => Promise<ToolResult>\n\n/**\n * Create a tool with common boilerplate handled automatically:\n * - Timing (durationMs)\n * - Error handling with PathAccessDeniedError re-throw\n * - Path resolution helper\n * - Success/error result helpers\n * \n * @param name - Tool name\n * @param definition - LLM tool definition\n * @param handler - Tool implementation receiving typed args and helpers\n * @returns Tool object ready for registration\n * \n * @example\n * ```ts\n * interface ReadFileArgs { path: string; offset?: number }\n * \n * export const readFileTool = createTool<ReadFileArgs>(\n * 'read_file',\n * definition,\n * async (args, context, helpers) => {\n * const fullPath = helpers.resolvePath(args.path)\n * await helpers.checkPathAccess([fullPath])\n * const content = await readFile(fullPath, 'utf-8')\n * return helpers.success(content)\n * }\n * )\n * ```\n */\nexport function createTool<TArgs>(\n name: string,\n definition: LLMToolDefinition,\n handler: ToolHandler<TArgs>\n): Tool {\n return {\n name,\n definition,\n async execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {\n const startTime = Date.now()\n \n // Create helpers with timing closure\n const helpers: ToolHelpers = {\n resolvePath: (path: string) => \n isAbsolute(path) ? path : resolve(context.workdir, path),\n \n checkPathAccess: async (paths: string[]) => {\n if (context.onEvent) {\n await requestPathAccess(\n paths,\n context.workdir,\n context.sessionId,\n crypto.randomUUID(),\n name,\n context.onEvent\n )\n }\n },\n \n error: (message: string, truncated = false) => ({\n success: false,\n error: message,\n durationMs: Date.now() - startTime,\n truncated,\n }),\n \n success: (output: string, truncated = false, extra?: Partial<ToolResult>) => ({\n success: true,\n output,\n durationMs: Date.now() - startTime,\n truncated,\n ...extra,\n }),\n }\n \n try {\n return await handler(args as TArgs, context, helpers)\n } catch (error) {\n // Re-throw path access errors for orchestrator to handle with helpful message\n if (error instanceof PathAccessDeniedError) {\n throw error\n }\n \n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error in tool execution',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n },\n }\n}\n","import { realpath } from 'node:fs/promises'\nimport { resolve, normalize, join } from 'node:path'\nimport { homedir } from 'node:os'\nimport type { ServerMessage } from '../../shared/protocol.js'\nimport { createChatPathConfirmationMessage } from '../ws/protocol.js'\n\n// ===========================================================================\n// Constants\n// ===========================================================================\n\n/** Safe device paths that don't need confirmation */\nconst SAFE_PATHS = new Set([\n '/dev/null',\n '/dev/zero',\n '/dev/random',\n '/dev/urandom',\n '/dev/stdin',\n '/dev/stdout',\n '/dev/stderr',\n '/dev/fd/0',\n '/dev/fd/1',\n '/dev/fd/2',\n])\n\n/** Directories that are always allowed (in addition to workdir) */\nconst ALLOWED_ROOTS = ['/tmp', '/var/tmp']\n\n/**\n * Patterns for files that may contain secrets and require confirmation\n * regardless of whether they're inside the workdir.\n * \n * Note: .env.example is excluded as it typically contains placeholder values.\n */\nconst SENSITIVE_FILE_PATTERNS: RegExp[] = [\n // Dotenv files (but not .envrc, .env-example, .env.example)\n /^\\.env$/, // .env\n /^\\.env\\.(?!example)[a-zA-Z0-9_.-]+$/, // .env.local, .env.production (not .env.example)\n \n // Credential files\n /^credentials\\.json$/i,\n /^secrets?\\.(?:json|ya?ml|toml)$/i,\n \n // Private keys\n /\\.pem$/i,\n /\\.key$/i,\n /^id_rsa/,\n /^id_ed25519/,\n /^id_ecdsa/,\n /^id_dsa/,\n \n // Auth configs\n /^\\.netrc$/,\n]\n\n/**\n * Check if a path points to a sensitive file that may contain secrets.\n * Checks the basename of the path against known sensitive patterns.\n * \n * @param path - The path to check (can be relative or absolute)\n * @returns true if the file matches a sensitive pattern\n */\nexport function isSensitivePath(path: string): boolean {\n // Extract basename from path\n const basename = path.split('/').pop() ?? ''\n if (!basename) return false\n \n return SENSITIVE_FILE_PATTERNS.some(pattern => pattern.test(basename))\n}\n\n// ===========================================================================\n// Session Allowlist (paths approved by user)\n// ===========================================================================\n\n/** Per-session set of paths that user has approved for access */\nconst sessionAllowedPaths = new Map<string, Set<string>>()\n\n/**\n * Add a path to the session's allowlist (user approved it)\n */\nexport function addAllowedPath(sessionId: string, path: string): void {\n if (!sessionAllowedPaths.has(sessionId)) {\n sessionAllowedPaths.set(sessionId, new Set())\n }\n sessionAllowedPaths.get(sessionId)!.add(normalize(path))\n}\n\n/**\n * Add multiple paths to the session's allowlist\n */\nexport function addAllowedPaths(sessionId: string, paths: string[]): void {\n for (const path of paths) {\n addAllowedPath(sessionId, path)\n }\n}\n\n/**\n * Check if a path is in the session's allowlist\n */\nexport function isPathAllowed(sessionId: string, path: string): boolean {\n const allowed = sessionAllowedPaths.get(sessionId)\n if (!allowed) return false\n return allowed.has(normalize(path))\n}\n\n/**\n * Clear the session's allowlist (e.g., on session delete)\n */\nexport function clearAllowedPaths(sessionId: string): void {\n sessionAllowedPaths.delete(sessionId)\n}\n\n// ===========================================================================\n// Path Validation\n// ===========================================================================\n\n/**\n * Safely resolve a path, following symlinks if possible.\n * Falls back to normalize() if realpath fails (e.g., broken symlink, nonexistent).\n */\nasync function safeRealpath(path: string): Promise<string> {\n try {\n return await realpath(path)\n } catch {\n // For broken symlinks or nonexistent paths, resolve what we can\n // and treat the target as the path to check\n return normalize(resolve(path))\n }\n}\n\n/**\n * Check if a resolved path is within the sandbox (workdir or allowed roots).\n * Resolves symlinks to prevent escape via symlink chains.\n * Also checks the session's allowlist for user-approved paths.\n * \n * @param path - The path to check (can be relative or absolute)\n * @param workdir - The session's working directory\n * @param sessionId - Optional session ID to check allowlist\n * @returns Object with `allowed` boolean and `resolvedPath` for error messages\n */\nexport async function isPathWithinSandbox(\n path: string,\n workdir: string,\n sessionId?: string\n): Promise<{ allowed: boolean; resolvedPath: string }> {\n // Normalize and resolve both paths\n const normalizedWorkdir = normalize((await safeRealpath(workdir)).replace(/\\/+$/, ''))\n \n // For the path, we need to handle the case where it might be a symlink\n // pointing outside the workdir\n let resolvedPath: string\n try {\n // Try to follow symlinks fully\n resolvedPath = normalize(await realpath(path))\n } catch {\n // If realpath fails (broken symlink, nonexistent), try to resolve what we can\n // For a broken symlink, we read where it points and check that\n try {\n const { readlink } = await import('node:fs/promises')\n const linkTarget = await readlink(path)\n // Resolve the link target relative to the link's directory\n const linkDir = resolve(path, '..')\n resolvedPath = normalize(resolve(linkDir, linkTarget))\n } catch {\n // Not a symlink or can't read - just normalize the path\n resolvedPath = normalize(resolve(path))\n }\n }\n\n // Remove trailing slashes for consistent comparison\n resolvedPath = resolvedPath.replace(/\\/+$/, '')\n\n // Check if in workdir\n if (resolvedPath === normalizedWorkdir ||\n resolvedPath.startsWith(normalizedWorkdir + '/')) {\n return { allowed: true, resolvedPath }\n }\n\n // Check if in allowed roots (/tmp, /var/tmp)\n for (const root of ALLOWED_ROOTS) {\n const normalizedRoot = normalize(root)\n if (resolvedPath === normalizedRoot ||\n resolvedPath.startsWith(normalizedRoot + '/')) {\n return { allowed: true, resolvedPath }\n }\n }\n\n // Check if path was previously approved by user for this session\n if (sessionId && isPathAllowed(sessionId, resolvedPath)) {\n return { allowed: true, resolvedPath }\n }\n\n return { allowed: false, resolvedPath }\n}\n\n// ===========================================================================\n// Command Path Extraction\n// ===========================================================================\n\n/**\n * Extract absolute paths from a shell command (heuristic).\n * Handles: /absolute/paths, ~/tilde/paths, quoted paths.\n * Filters out safe paths (/dev/*) and URLs.\n * \n * @param command - The shell command to parse\n * @returns Array of absolute paths found (deduplicated)\n */\nexport function extractAbsolutePathsFromCommand(command: string): string[] {\n if (!command.trim()) {\n return []\n }\n\n const paths: string[] = []\n const home = homedir()\n\n // Remove URL schemes to avoid false positives\n // Replace http://, https://, ftp://, file:// with markers\n let sanitized = command\n .replace(/https?:\\/\\/[^\\s'\"]+/g, ' __URL__ ')\n .replace(/ftp:\\/\\/[^\\s'\"]+/g, ' __URL__ ')\n \n // Handle file:// URLs specially - extract the path\n const fileUrlMatches = command.matchAll(/file:\\/\\/([^\\s'\"]+)/g)\n for (const match of fileUrlMatches) {\n const filePath = match[1]\n if (filePath && !isSafePath(filePath)) {\n paths.push(normalize(filePath))\n }\n }\n sanitized = sanitized.replace(/file:\\/\\/[^\\s'\"]+/g, ' __FILEURL__ ')\n\n // Pattern 1: Tilde paths ~/... or just ~\n // Match ~ followed by optional /path, at word boundary\n const tildePattern = /(?:^|[\\s='\"(])~(\\/[^\\s'\"()]*)?(?=[\\s'\"()]|$)/g\n let match\n while ((match = tildePattern.exec(sanitized)) !== null) {\n const pathPart = match[1] ?? ''\n // Concatenate home + pathPart then resolve to handle ~/../etc properly\n // ~/../etc/passwd with home=/home/user becomes /home/user/../etc/passwd -> /etc/passwd\n const fullPath = home + pathPart\n const resolved = normalize(resolve(fullPath))\n if (!isSafePath(resolved)) {\n paths.push(resolved)\n }\n }\n\n // Pattern 2: Quoted strings (may contain paths with spaces)\n const quotedPattern = /[\"']([^\"']+)[\"']/g\n while ((match = quotedPattern.exec(sanitized)) !== null) {\n const content = match[1]!\n \n // Check if it looks like a regex pattern (starts and ends with /)\n if (content.startsWith('/') && content.endsWith('/')) {\n continue // Skip regex patterns\n }\n \n // Check for absolute path\n if (content.startsWith('/')) {\n const resolved = normalize(content)\n if (!isSafePath(resolved)) {\n paths.push(resolved)\n }\n }\n // Check for tilde path\n else if (content.startsWith('~')) {\n const pathPart = content.slice(1) // Remove ~\n const fullPath = join(home, pathPart)\n const resolved = normalize(resolve(fullPath))\n if (!isSafePath(resolved)) {\n paths.push(resolved)\n }\n }\n }\n\n // Pattern 3: Unquoted absolute paths\n // Match / followed by path characters, at word boundary\n // Be careful not to match paths already in quotes or parts of URLs\n const absolutePattern = /(?:^|[\\s=(])(\\/?\\/[a-zA-Z0-9_\\-./]+)/g\n while ((match = absolutePattern.exec(sanitized)) !== null) {\n const pathCandidate = match[1]!\n \n // Skip if it's a URL marker\n if (pathCandidate.includes('__URL__') || pathCandidate.includes('__FILEURL__')) {\n continue\n }\n \n // Skip if it looks like a regex (surrounded by /)\n if (pathCandidate.endsWith('/') && pathCandidate.split('/').length <= 2) {\n continue\n }\n \n const resolved = normalize(pathCandidate)\n if (!isSafePath(resolved)) {\n paths.push(resolved)\n }\n }\n\n // Deduplicate\n return [...new Set(paths)]\n}\n\n/**\n * Extract sensitive file paths from a shell command.\n * Unlike extractAbsolutePathsFromCommand, this looks for relative paths\n * that match sensitive file patterns (like .env, credentials.json, etc.).\n * \n * @param command - The shell command to parse\n * @returns Array of sensitive paths found (deduplicated)\n */\nexport function extractSensitivePathsFromCommand(command: string): string[] {\n if (!command.trim()) {\n return []\n }\n\n const paths: string[] = []\n\n // Pattern to match potential file paths (relative or in subdirectories)\n // Matches: .env, config/.env, \"./file\", 'file', paths with common extensions\n // Word boundaries are tricky in shell, so we look for common delimiters\n \n // Pattern 1: Quoted strings that might contain sensitive paths\n const quotedPattern = /[\"']([^\"']+)[\"']/g\n let match\n while ((match = quotedPattern.exec(command)) !== null) {\n const content = match[1]!\n if (isSensitivePath(content)) {\n paths.push(content)\n }\n }\n\n // Pattern 2: Unquoted paths - look for tokens that could be file paths\n // Split by common shell delimiters and check each token\n // Remove quoted sections first to avoid double-matching\n const unquoted = command\n .replace(/[\"'][^\"']*[\"']/g, ' ') // Remove quoted strings\n .replace(/[|&;><]/g, ' ') // Replace shell operators with spaces\n \n // Split by whitespace and check each token\n const tokens = unquoted.split(/\\s+/).filter(Boolean)\n \n for (const token of tokens) {\n // Skip flags (start with -)\n if (token.startsWith('-')) continue\n \n // Skip URLs\n if (token.includes('://')) continue\n \n // Check if token is or contains a sensitive file\n if (isSensitivePath(token)) {\n paths.push(token)\n }\n }\n\n // Deduplicate\n return [...new Set(paths)]\n}\n\n/**\n * Check if a path is a \"safe\" device path that doesn't need confirmation\n */\nfunction isSafePath(path: string): boolean {\n const normalized = normalize(path)\n return SAFE_PATHS.has(normalized)\n}\n\n// ===========================================================================\n// Batch Path Checking\n// ===========================================================================\n\nexport interface PathAccessResult {\n needsConfirmation: boolean\n deniedPaths: string[] // Paths outside the sandbox\n sensitivePaths: string[] // Paths matching sensitive file patterns (may overlap with deniedPaths)\n}\n\n/**\n * Check multiple paths and return which ones need confirmation\n * \n * @param paths - Array of paths to check\n * @param workdir - The session's working directory\n * @param sessionId - Optional session ID to check allowlist\n * @returns Object with confirmation status and list of denied paths\n */\nexport async function checkPathsAccess(\n paths: string[],\n workdir: string,\n sessionId?: string\n): Promise<PathAccessResult> {\n if (paths.length === 0) {\n return { needsConfirmation: false, deniedPaths: [], sensitivePaths: [] }\n }\n\n const deniedPaths: string[] = []\n const sensitivePaths: string[] = []\n\n for (const path of paths) {\n const result = await isPathWithinSandbox(path, workdir, sessionId)\n \n // Check if path is outside sandbox\n if (!result.allowed) {\n deniedPaths.push(result.resolvedPath)\n }\n \n // Check if path is sensitive (regardless of sandbox status)\n // But only if not already in session allowlist\n if (isSensitivePath(path) && !isPathAllowed(sessionId ?? '', path)) {\n // Use the resolved path for consistency\n sensitivePaths.push(result.resolvedPath)\n }\n }\n\n // Deduplicate\n const uniqueDenied = [...new Set(deniedPaths)]\n const uniqueSensitive = [...new Set(sensitivePaths)]\n\n return {\n needsConfirmation: uniqueDenied.length > 0 || uniqueSensitive.length > 0,\n deniedPaths: uniqueDenied,\n sensitivePaths: uniqueSensitive,\n }\n}\n\n// ===========================================================================\n// Request Path Access (Promise-based flow)\n// ===========================================================================\n\n/**\n * Request access to paths outside the sandbox or sensitive files.\n * If paths require confirmation, sends a confirmation event to the client and suspends\n * tool execution until the user responds.\n * \n * @param paths - Array of paths to check\n * @param workdir - The session's working directory\n * @param sessionId - Session ID for allowlist tracking\n * @param callId - Unique ID for this confirmation request\n * @param tool - Name of the tool requesting access\n * @param onEvent - Callback to send events to the client\n * @throws PathAccessDeniedError if user denies access\n */\nexport async function requestPathAccess(\n paths: string[],\n workdir: string,\n sessionId: string,\n callId: string,\n tool: string,\n onEvent: (event: ServerMessage) => void\n): Promise<void> {\n // Check which paths need confirmation\n const result = await checkPathsAccess(paths, workdir, sessionId)\n \n if (!result.needsConfirmation) {\n // All paths allowed\n return\n }\n \n // Combine all paths that need confirmation (may overlap)\n const allPathsNeedingConfirmation = [...new Set([...result.deniedPaths, ...result.sensitivePaths])]\n \n // Determine reason based on which arrays have entries\n const hasDenied = result.deniedPaths.length > 0\n const hasSensitive = result.sensitivePaths.length > 0\n const reason = hasDenied && hasSensitive ? 'both' as const\n : hasDenied ? 'outside_workdir' as const\n : 'sensitive_file' as const\n \n // Create the confirmation Promise and send event to client\n const confirmationPromise = registerPathConfirmation(callId, allPathsNeedingConfirmation, sessionId)\n \n // Send path confirmation event to client (this shows the modal)\n onEvent(createChatPathConfirmationMessage(callId, tool, allPathsNeedingConfirmation, workdir, reason))\n \n // Suspend tool execution until user responds\n const approved = await confirmationPromise\n \n if (!approved) {\n throw new PathAccessDeniedError(allPathsNeedingConfirmation, tool, reason)\n }\n \n // If approved, paths have already been added to allowlist by providePathConfirmation\n}\n\n// ===========================================================================\n// Error Classes\n// ===========================================================================\n\nexport type PathDenialReason = 'outside_workdir' | 'sensitive_file' | 'both'\n\n/**\n * Error thrown when user denies path access.\n * This causes the agent run to abort.\n */\nexport class PathAccessDeniedError extends Error {\n constructor(\n public readonly paths: string[],\n public readonly tool: string,\n public readonly reason: PathDenialReason = 'outside_workdir'\n ) {\n const reasonText = reason === 'sensitive_file' \n ? 'sensitive files (may contain secrets)'\n : reason === 'both'\n ? 'paths outside workdir and sensitive files'\n : 'paths outside workdir'\n super(`User denied access to ${reasonText}: ${paths.join(', ')}`)\n this.name = 'PathAccessDeniedError'\n }\n}\n\n/**\n * @deprecated Use requestPathAccess() instead which handles the Promise flow internally.\n * Kept for backward compatibility during transition.\n */\nexport class PathConfirmationInterrupt extends Error {\n constructor(\n public readonly callId: string,\n public readonly paths: string[],\n public readonly tool: string,\n public readonly workdir: string\n ) {\n super('Path confirmation required')\n this.name = 'PathConfirmationInterrupt'\n }\n}\n\n// ===========================================================================\n// Confirmation State Management\n// ===========================================================================\n\n/** Pending path confirmations, keyed by callId */\nconst pendingConfirmations = new Map<string, {\n resolve: (approved: boolean) => void\n reject: (error: Error) => void\n paths: string[]\n sessionId: string\n}>()\n\n/**\n * Register a pending path confirmation.\n * Stores the paths and sessionId so they can be added to allowlist on approval.\n */\nexport function registerPathConfirmation(\n callId: string,\n paths: string[],\n sessionId: string\n): Promise<boolean> {\n return new Promise((resolve, reject) => {\n pendingConfirmations.set(callId, { resolve, reject, paths, sessionId })\n })\n}\n\n/**\n * Provide a response to a pending path confirmation.\n * Called by the WebSocket handler when user responds.\n * If approved, adds the paths to the session's allowlist.\n * \n * @param callId - The confirmation's unique ID\n * @param approved - Whether the user approved the access\n * @returns Object with found status, and if approved, the sessionId for re-triggering chat\n */\nexport function providePathConfirmation(callId: string, approved: boolean): {\n found: boolean\n sessionId?: string\n approved?: boolean\n} {\n const pending = pendingConfirmations.get(callId)\n if (!pending) {\n return { found: false }\n }\n\n if (approved) {\n // Add paths to session's allowlist\n addAllowedPaths(pending.sessionId, pending.paths)\n }\n\n pending.resolve(approved)\n pendingConfirmations.delete(callId)\n \n return { found: true, sessionId: pending.sessionId, approved }\n}\n\n/**\n * Cancel a pending path confirmation (e.g., on session abort).\n * \n * @param callId - The confirmation's unique ID\n * @param reason - Reason for cancellation\n * @returns true if confirmation was found and cancelled, false otherwise\n */\nexport function cancelPathConfirmation(callId: string, reason: string): boolean {\n const pending = pendingConfirmations.get(callId)\n if (!pending) {\n return false\n }\n\n pending.reject(new Error(reason))\n pendingConfirmations.delete(callId)\n return true\n}\n\nexport function cancelPathConfirmationsForSession(sessionId: string, reason: string): number {\n let cancelledCount = 0\n\n for (const [callId, pending] of pendingConfirmations.entries()) {\n if (pending.sessionId !== sessionId) {\n continue\n }\n\n pending.reject(new Error(reason))\n pendingConfirmations.delete(callId)\n cancelledCount += 1\n }\n\n return cancelledCount\n}\n\n/**\n * Check if there's a pending path confirmation.\n * \n * @param callId - The confirmation's unique ID\n * @returns true if confirmation is pending\n */\nexport function hasPendingPathConfirmation(callId: string): boolean {\n return pendingConfirmations.has(callId)\n}\n","import type {\n ClientMessage,\n ServerMessage,\n ProjectCreatePayload,\n ProjectCreateWithDirPayload,\n ProjectLoadPayload,\n ProjectUpdatePayload,\n ProjectDeletePayload,\n SessionCreatePayload,\n SessionLoadPayload,\n SessionSetProviderPayload,\n ChatSendPayload,\n ModeSwitchPayload,\n CriteriaEditPayload,\n ProjectStatePayload,\n ProjectListPayload,\n SessionStatePayload,\n SessionListPayload,\n SessionRunningPayload,\n SessionNameGeneratedPayload,\n ChatDeltaPayload,\n ChatThinkingPayload,\n ChatToolPreparingPayload,\n ChatToolCallPayload,\n ChatToolOutputPayload,\n ChatToolResultPayload,\n ChatTodoPayload,\n ChatSummaryPayload,\n ChatProgressPayload,\n ChatFormatRetryPayload,\n ChatMessagePayload,\n ChatMessageUpdatedPayload,\n ChatDonePayload,\n ChatErrorPayload,\n ChatPathConfirmationPayload,\n PathConfirmPayload,\n ModeChangedPayload,\n PhaseChangedPayload,\n CriteriaUpdatedPayload,\n ContextStatePayload,\n ErrorPayload,\n SettingsGetPayload,\n SettingsSetPayload,\n SettingsValuePayload,\n QueueAsapPayload,\n QueueCompletionPayload,\n QueueCancelPayload,\n QueueStatePayload,\n QueuedMessage,\n} from '../../shared/protocol.js'\nimport { isClientMessage, createServerMessage } from '../../shared/protocol.js'\nimport type { Project, Session, SessionSummary, SessionMode, SessionPhase, Criterion, Todo, ToolResult, Message, ContextState, ToolCall } from '../../shared/types.js'\n\n/**\n * Enrich messages by attaching tool results to their parent toolCalls.\n * This ensures frontend receives consistent data shape whether streaming or loading from DB.\n * \n * Tool results are stored in separate 'tool' role messages, but for display purposes\n * the frontend expects results attached to the toolCall objects on assistant messages.\n */\nfunction enrichMessagesWithToolResults(messages: Message[]): Message[] {\n // Build toolCallId → toolResult lookup from tool messages\n const resultMap = new Map<string, ToolResult>()\n for (const msg of messages) {\n if (msg.role === 'tool' && msg.toolCallId && msg.toolResult) {\n resultMap.set(msg.toolCallId, msg.toolResult)\n }\n }\n \n // If no tool results, return as-is\n if (resultMap.size === 0) return messages\n \n // Attach results to toolCalls on assistant messages\n return messages.map(msg => {\n if (msg.role !== 'assistant' || !msg.toolCalls?.length) return msg\n \n // Check if any toolCalls need enrichment\n const needsEnrichment = msg.toolCalls.some(tc => !tc.result && resultMap.has(tc.id))\n if (!needsEnrichment) return msg\n \n return {\n ...msg,\n toolCalls: msg.toolCalls.map((tc): ToolCall => {\n if (tc.result) return tc\n const result = resultMap.get(tc.id)\n // Only add result if it exists (satisfies exactOptionalPropertyTypes)\n return result ? { ...tc, result } : tc\n })\n }\n })\n}\n\nexport function parseClientMessage(data: string): ClientMessage | null {\n try {\n const parsed = JSON.parse(data)\n if (isClientMessage(parsed)) {\n return parsed\n }\n return null\n } catch {\n return null\n }\n}\n\nexport function serializeServerMessage(message: ServerMessage): string {\n return JSON.stringify(message)\n}\n\n// Error message\nexport function createErrorMessage(code: string, message: string, correlationId?: string): ServerMessage<ErrorPayload> {\n return createServerMessage('error', { code, message }, correlationId)\n}\n\n// Session messages\nexport function createSessionStateMessage(session: Session, messages: Message[], correlationId?: string): ServerMessage<SessionStatePayload> {\n // Enrich messages so toolCalls have their results attached\n const enrichedMessages = enrichMessagesWithToolResults(messages)\n return createServerMessage('session.state', { session, messages: enrichedMessages }, correlationId)\n}\n\nexport function createSessionListMessage(sessions: SessionSummary[], correlationId?: string): ServerMessage<SessionListPayload> {\n return createServerMessage('session.list', { sessions }, correlationId)\n}\n\nexport function createSessionRunningMessage(isRunning: boolean): ServerMessage<SessionRunningPayload> {\n return createServerMessage('session.running', { isRunning })\n}\n\n// Project messages\nexport function createProjectStateMessage(project: Project, correlationId?: string): ServerMessage<ProjectStatePayload> {\n return createServerMessage('project.state', { project }, correlationId)\n}\n\nexport function createProjectListMessage(projects: Project[], correlationId?: string): ServerMessage<ProjectListPayload> {\n return createServerMessage('project.list', { projects }, correlationId)\n}\n\n// Chat messages - all include messageId to identify which message to update\nexport function createChatDeltaMessage(messageId: string, content: string): ServerMessage<ChatDeltaPayload> {\n return createServerMessage('chat.delta', { messageId, content })\n}\n\nexport function createChatThinkingMessage(messageId: string, content: string): ServerMessage<ChatThinkingPayload> {\n return createServerMessage('chat.thinking', { messageId, content })\n}\n\nexport function createChatToolPreparingMessage(messageId: string, index: number, name: string): ServerMessage<ChatToolPreparingPayload> {\n return createServerMessage('chat.tool_preparing', { messageId, index, name })\n}\n\nexport function createChatToolCallMessage(messageId: string, callId: string, tool: string, args: Record<string, unknown>): ServerMessage<ChatToolCallPayload> {\n return createServerMessage('chat.tool_call', { messageId, callId, tool, args })\n}\n\nexport function createChatToolResultMessage(messageId: string, callId: string, tool: string, result: ToolResult): ServerMessage<ChatToolResultPayload> {\n return createServerMessage('chat.tool_result', { messageId, callId, tool, result })\n}\n\nexport function createChatToolOutputMessage(\n messageId: string,\n callId: string,\n output: string,\n stream: 'stdout' | 'stderr'\n): ServerMessage<ChatToolOutputPayload> {\n return createServerMessage('chat.tool_output', { messageId, callId, output, stream })\n}\n\nexport function createChatTodoMessage(todos: Todo[]): ServerMessage<ChatTodoPayload> {\n return createServerMessage('chat.todo', { todos })\n}\n\nexport function createChatSummaryMessage(summary: string): ServerMessage<ChatSummaryPayload> {\n return createServerMessage('chat.summary', { summary })\n}\n\nexport function createChatProgressMessage(\n message: string,\n phase?: 'summary' | 'mode_switch' | 'starting' | 'context_warning' | 'context_error'\n): ServerMessage<ChatProgressPayload> {\n return createServerMessage('chat.progress', { message, ...(phase ? { phase } : {}) })\n}\n\nexport function createChatFormatRetryMessage(\n attempt: number,\n maxAttempts: number\n): ServerMessage<ChatFormatRetryPayload> {\n return createServerMessage('chat.format_retry', { attempt, maxAttempts })\n}\n\nexport function createChatMessageMessage(message: Message): ServerMessage<ChatMessagePayload> {\n return createServerMessage('chat.message', { message })\n}\n\nexport function createChatMessageUpdatedMessage(\n messageId: string,\n updates: ChatMessageUpdatedPayload['updates']\n): ServerMessage<ChatMessageUpdatedPayload> {\n return createServerMessage('chat.message_updated', { messageId, updates })\n}\n\nexport function createChatDoneMessage(\n messageId: string,\n reason: 'complete' | 'stopped' | 'error' | 'waiting_for_user',\n stats?: ChatDonePayload['stats']\n): ServerMessage<ChatDonePayload> {\n return createServerMessage('chat.done', { messageId, reason, ...(stats ? { stats } : {}) })\n}\n\nexport function createChatErrorMessage(error: string, recoverable: boolean): ServerMessage<ChatErrorPayload> {\n return createServerMessage('chat.error', { error, recoverable })\n}\n\n// Path confirmation messages\nexport function createChatPathConfirmationMessage(\n callId: string,\n tool: string,\n paths: string[],\n workdir: string,\n reason: ChatPathConfirmationPayload['reason']\n): ServerMessage<ChatPathConfirmationPayload> {\n return createServerMessage('chat.path_confirmation', { callId, tool, paths, workdir, reason })\n}\n\n// Mode messages\nexport function createModeChangedMessage(mode: SessionMode, auto: boolean, reason?: string): ServerMessage<ModeChangedPayload> {\n return createServerMessage('mode.changed', { mode, auto, ...(reason ? { reason } : {}) })\n}\n\n// Phase messages\nexport function createPhaseChangedMessage(phase: SessionPhase): ServerMessage<PhaseChangedPayload> {\n return createServerMessage('phase.changed', { phase })\n}\n\n// Criteria messages\nexport function createCriteriaUpdatedMessage(criteria: Criterion[], changedId?: string): ServerMessage<CriteriaUpdatedPayload> {\n return createServerMessage('criteria.updated', { criteria, ...(changedId ? { changedId } : {}) })\n}\n\n// Context messages\nexport function createContextStateMessage(context: ContextState): ServerMessage<ContextStatePayload> {\n return createServerMessage('context.state', { context })\n}\n\n// Session name messages\nexport function createSessionNameGeneratedMessage(name: string, sessionId?: string): ServerMessage<SessionNameGeneratedPayload> {\n const msg: ServerMessage<SessionNameGeneratedPayload> = {\n type: 'session.name_generated',\n payload: { name },\n }\n if (sessionId) {\n msg.sessionId = sessionId\n }\n return msg\n}\n\n// Type guards for payloads\n\n// Project payloads\nexport function isProjectCreatePayload(payload: unknown): payload is ProjectCreatePayload {\n return typeof payload === 'object' && payload !== null && 'name' in payload && 'workdir' in payload\n}\n\nexport function isProjectCreateWithDirPayload(payload: unknown): payload is ProjectCreateWithDirPayload {\n return typeof payload === 'object' && payload !== null && 'name' in payload && typeof payload.name === 'string'\n}\n\nexport function isProjectLoadPayload(payload: unknown): payload is ProjectLoadPayload {\n return typeof payload === 'object' && payload !== null && 'projectId' in payload\n}\n\nexport function isProjectUpdatePayload(payload: unknown): payload is ProjectUpdatePayload {\n return typeof payload === 'object' && payload !== null && 'projectId' in payload\n}\n\nexport function isProjectDeletePayload(payload: unknown): payload is ProjectDeletePayload {\n return typeof payload === 'object' && payload !== null && 'projectId' in payload\n}\n\n// Session payloads\nexport function isSessionCreatePayload(payload: unknown): payload is SessionCreatePayload {\n return typeof payload === 'object' && payload !== null && 'projectId' in payload\n}\n\nexport function isSessionLoadPayload(payload: unknown): payload is SessionLoadPayload {\n return typeof payload === 'object' && payload !== null && 'sessionId' in payload\n}\n\n// Chat payloads\nexport function isChatSendPayload(payload: unknown): payload is ChatSendPayload {\n return typeof payload === 'object' && payload !== null && 'content' in payload\n}\n\nexport function isModeSwitchPayload(payload: unknown): payload is ModeSwitchPayload {\n return typeof payload === 'object' && payload !== null && 'mode' in payload\n}\n\nexport function isCriteriaEditPayload(payload: unknown): payload is CriteriaEditPayload {\n return typeof payload === 'object' && payload !== null && 'criteria' in payload\n}\n\n// Path confirmation payloads\nexport function isPathConfirmPayload(payload: unknown): payload is PathConfirmPayload {\n return typeof payload === 'object' && payload !== null && 'callId' in payload && 'approved' in payload\n}\n\n// Settings payloads\nexport function isSettingsGetPayload(payload: unknown): payload is SettingsGetPayload {\n return typeof payload === 'object' && payload !== null && 'key' in payload\n}\n\nexport function isSettingsSetPayload(payload: unknown): payload is SettingsSetPayload {\n return typeof payload === 'object' && payload !== null && 'key' in payload && 'value' in payload\n}\n\n// Session provider payload\nexport function isSessionSetProviderPayload(payload: unknown): payload is SessionSetProviderPayload {\n return typeof payload === 'object' && payload !== null && 'providerId' in payload\n}\n\nexport function createSettingsValueMessage(key: string, value: string | null, correlationId?: string): ServerMessage {\n return createServerMessage<SettingsValuePayload>('settings.value', { key, value }, correlationId)\n}\n\n// Queue messages\nexport function createQueueStateMessage(messages: QueuedMessage[]): ServerMessage<QueueStatePayload> {\n return createServerMessage('queue.state', { messages })\n}\n\n// Queue payload type guards\nexport function isQueueAsapPayload(payload: unknown): payload is QueueAsapPayload {\n return typeof payload === 'object' && payload !== null && 'content' in payload && typeof (payload as QueueAsapPayload).content === 'string'\n}\n\nexport function isQueueCompletionPayload(payload: unknown): payload is QueueCompletionPayload {\n return typeof payload === 'object' && payload !== null && 'content' in payload && typeof (payload as QueueCompletionPayload).content === 'string'\n}\n\nexport function isQueueCancelPayload(payload: unknown): payload is QueueCancelPayload {\n return typeof payload === 'object' && payload !== null && 'queueId' in payload && typeof (payload as QueueCancelPayload).queueId === 'string'\n}\n\n// ============================================================================\n// Event Store → Server Message Conversion\n// ============================================================================\n\nimport type { StoredEvent, TurnEvent } from '../events/types.js'\n\n/**\n * Convert a StoredEvent from EventStore to a ServerMessage for WebSocket.\n * This bridges the new event sourcing layer with the existing frontend protocol.\n * \n * Returns null for events that don't have a direct WebSocket equivalent\n * (e.g., turn.snapshot events are used for efficient loading, not streaming).\n */\nexport function storedEventToServerMessage(event: StoredEvent): ServerMessage | null {\n switch (event.type) {\n case 'message.start': {\n const data = event.data as Extract<TurnEvent, { type: 'message.start' }>['data']\n // Create a minimal Message object for chat.message\n // User messages have content upfront and are not streaming\n // Assistant messages start streaming (content builds up via deltas)\n const isUserOrSystem = data.role === 'user' || data.role === 'system'\n const message: Message = {\n id: data.messageId,\n role: data.role,\n content: data.content ?? '',\n timestamp: new Date(event.timestamp).toISOString(),\n tokenCount: 0,\n isStreaming: !isUserOrSystem, // Only assistant messages stream\n ...(data.contextWindowId ? { contextWindowId: data.contextWindowId } : {}),\n ...(data.subAgentId ? { subAgentId: data.subAgentId } : {}),\n ...(data.subAgentType ? { subAgentType: data.subAgentType } : {}),\n ...(data.isSystemGenerated ? { isSystemGenerated: data.isSystemGenerated } : {}),\n ...(data.messageKind ? { messageKind: data.messageKind } : {}),\n ...(data.isCompactionSummary ? { isCompactionSummary: data.isCompactionSummary } : {}),\n }\n return createChatMessageMessage(message)\n }\n\n case 'message.delta': {\n const data = event.data as Extract<TurnEvent, { type: 'message.delta' }>['data']\n return createChatDeltaMessage(data.messageId, data.content)\n }\n\n case 'message.thinking': {\n const data = event.data as Extract<TurnEvent, { type: 'message.thinking' }>['data']\n return createChatThinkingMessage(data.messageId, data.content)\n }\n\n case 'message.done': {\n const data = event.data as Extract<TurnEvent, { type: 'message.done' }>['data']\n // This maps to chat.message_updated with isStreaming: false and optionally stats\n const updates: {\n isStreaming: false\n partial?: true\n stats?: typeof data.stats\n promptContext?: typeof data.promptContext\n } = { isStreaming: false }\n if (data.partial) {\n updates.partial = true\n }\n if (data.stats) {\n updates.stats = data.stats\n }\n if (data.promptContext) {\n updates.promptContext = data.promptContext\n }\n return createChatMessageUpdatedMessage(data.messageId, updates)\n }\n\n case 'tool.preparing': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.preparing' }>['data']\n return createChatToolPreparingMessage(data.messageId, data.index, data.name)\n }\n\n case 'tool.call': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.call' }>['data']\n return createChatToolCallMessage(\n data.messageId,\n data.toolCall.id,\n data.toolCall.name,\n data.toolCall.arguments\n )\n }\n\n case 'tool.output': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.output' }>['data']\n return createChatToolOutputMessage('', data.toolCallId, data.content, data.stream)\n }\n\n case 'tool.result': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.result' }>['data']\n // Need messageId for the result - we include it in the event\n return createChatToolResultMessage(\n data.messageId,\n data.toolCallId,\n '', // Tool name not available in event, but not used by frontend for matching\n data.result\n )\n }\n\n case 'phase.changed': {\n const data = event.data as Extract<TurnEvent, { type: 'phase.changed' }>['data']\n return createPhaseChangedMessage(data.phase)\n }\n\n case 'task.completed': {\n const data = event.data as Extract<TurnEvent, { type: 'task.completed' }>['data']\n return createServerMessage('task.completed', data)\n }\n\n case 'mode.changed': {\n const data = event.data as Extract<TurnEvent, { type: 'mode.changed' }>['data']\n return createModeChangedMessage(data.mode, data.auto, data.reason)\n }\n\n case 'running.changed': {\n const data = event.data as Extract<TurnEvent, { type: 'running.changed' }>['data']\n return createSessionRunningMessage(data.isRunning)\n }\n\n case 'criteria.set': {\n const data = event.data as Extract<TurnEvent, { type: 'criteria.set' }>['data']\n return createCriteriaUpdatedMessage(data.criteria)\n }\n\n case 'criterion.updated': {\n // No direct equivalent - would need to fetch full criteria list\n // For now, skip this event in streaming (frontend gets full state on load)\n return null\n }\n\n case 'context.state': {\n const data = event.data as ContextState\n return createContextStateMessage(data)\n }\n\n case 'session.name_generated': {\n const data = event.data as { name: string }\n return {\n type: 'session.name_generated',\n payload: { name: data.name },\n sessionId: event.sessionId,\n }\n }\n\n case 'todo.updated': {\n const data = event.data as Extract<TurnEvent, { type: 'todo.updated' }>['data']\n return createChatTodoMessage(data.todos)\n }\n\n case 'chat.done': {\n const data = event.data as Extract<TurnEvent, { type: 'chat.done' }>['data']\n return createChatDoneMessage(data.messageId, data.reason, data.stats)\n }\n\n case 'chat.error': {\n const data = event.data as Extract<TurnEvent, { type: 'chat.error' }>['data']\n return createChatErrorMessage(data.error, data.recoverable)\n }\n\n case 'format.retry': {\n const data = event.data as Extract<TurnEvent, { type: 'format.retry' }>['data']\n return createChatFormatRetryMessage(data.attempt, data.maxAttempts)\n }\n\n case 'turn.snapshot':\n case 'context.compacted':\n // These are internal events, not sent to frontend in real-time\n return null\n\n default:\n // Unknown event type - log and skip\n return null\n }\n}\n","import { createHash } from 'node:crypto'\nimport { readFile, stat } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport type { FileReadEntry } from '../../shared/types.js'\n\n// ============================================================================\n// Error Classes\n// ============================================================================\n\nexport class FileNotReadError extends Error {\n constructor(path: string) {\n super(`File \"${path}\" must be read before writing. Use read_file first.`)\n this.name = 'FileNotReadError'\n }\n}\n\nexport class FileChangedExternallyError extends Error {\n constructor(path: string) {\n super(`File \"${path}\" was modified externally and must be read before writing. Use read_file to see current contents.`)\n this.name = 'FileChangedExternallyError'\n }\n}\n\n// ============================================================================\n// Hash Computation\n// ============================================================================\n\n/**\n * Compute SHA-256 hash of a file's content.\n * Returns null if the file doesn't exist.\n */\nexport async function computeFileHash(filePath: string): Promise<string | null> {\n try {\n const content = await readFile(filePath)\n const hash = createHash('sha256')\n hash.update(content)\n return hash.digest('hex')\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n return null\n }\n throw error\n }\n}\n\n// ============================================================================\n// Validation\n// ============================================================================\n\nexport interface ValidationResult {\n valid: boolean\n error?: FileNotReadError | FileChangedExternallyError\n}\n\n/**\n * Validate that a file can be written to.\n * \n * Rules:\n * - New files (don't exist on disk) → allowed\n * - Existing files must be in readFiles map with matching hash\n * - If file changed externally (hash mismatch) → treated same as not read\n */\nexport async function validateFileForWrite(\n filePath: string,\n readFiles: Record<string, FileReadEntry>\n): Promise<ValidationResult> {\n // Normalize path for consistent comparison\n const normalizedPath = resolve(filePath)\n \n // Check if file exists on disk\n const currentHash = await computeFileHash(normalizedPath)\n \n // New file - no read required\n if (currentHash === null) {\n return { valid: true }\n }\n \n // File exists - check if it was read\n const readEntry = readFiles[normalizedPath]\n \n if (!readEntry) {\n return {\n valid: false,\n error: new FileNotReadError(filePath),\n }\n }\n \n // Check if file changed since read\n if (readEntry.hash !== currentHash) {\n return {\n valid: false,\n error: new FileChangedExternallyError(filePath),\n }\n }\n \n return { valid: true }\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && 'code' in error\n}\n","import { writeFile, mkdir } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport type { Diagnostic } from '../../shared/types.js'\nimport { createTool } from './tool-helpers.js'\nimport { formatDiagnosticsForLLM } from './diagnostics.js'\nimport { validateFileForWrite, computeFileHash } from './file-tracker.js'\n\ninterface WriteFileArgs {\n path: string\n content: string\n}\n\nexport const writeFileTool = createTool<WriteFileArgs>(\n 'write_file',\n {\n type: 'function',\n function: {\n name: 'write_file',\n description: 'Write content to a file. Creates the file if it does not exist, or overwrites if it does. Creates parent directories as needed.',\n parameters: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Path to the file (relative to workdir or absolute)',\n },\n content: {\n type: 'string',\n description: 'Content to write to the file',\n },\n },\n required: ['path', 'content'],\n },\n },\n },\n async (args, context, helpers) => {\n const fullPath = helpers.resolvePath(args.path)\n await helpers.checkPathAccess([fullPath])\n \n // Validate file was read before writing (only for existing files)\n const readFiles = context.sessionManager.getReadFiles(context.sessionId)\n const validation = await validateFileForWrite(fullPath, readFiles)\n if (!validation.valid) {\n return helpers.error(validation.error?.message ?? 'File validation failed')\n }\n \n // Ensure parent directory exists\n const dir = dirname(fullPath)\n await mkdir(dir, { recursive: true })\n \n // Write file\n await writeFile(fullPath, args.content, 'utf-8')\n \n const lineCount = args.content.split('\\n').length\n const byteCount = Buffer.byteLength(args.content, 'utf-8')\n \n let output = `Successfully wrote ${lineCount} lines (${byteCount} bytes) to ${args.path}`\n let diagnostics: Diagnostic[] = []\n \n // Get LSP diagnostics if available\n if (context.lspManager) {\n diagnostics = await context.lspManager.notifyFileChange(fullPath, args.content)\n output += formatDiagnosticsForLLM(diagnostics)\n }\n \n // Update file hash after write so subsequent writes don't require re-reading\n const newHash = await computeFileHash(fullPath)\n if (newHash) {\n context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash)\n }\n \n return helpers.success(\n output,\n false,\n diagnostics.length > 0 ? { diagnostics } : undefined\n )\n }\n)\n","import type { Diagnostic } from '../../shared/types.js'\n\n/**\n * Format diagnostics for LLM consumption (plain text in output).\n * This is appended to tool output so the LLM can see and respond to errors.\n */\nexport function formatDiagnosticsForLLM(diagnostics: Diagnostic[]): string {\n if (diagnostics.length === 0) return ''\n \n const errors = diagnostics.filter(d => d.severity === 'error')\n const warnings = diagnostics.filter(d => d.severity === 'warning')\n \n let output = '\\n\\nLSP found '\n const parts: string[] = []\n if (errors.length > 0) parts.push(`${errors.length} error(s)`)\n if (warnings.length > 0) parts.push(`${warnings.length} warning(s)`)\n output += parts.join(', ') + ':\\n'\n \n // Limit to 10 most severe diagnostics\n const sorted = [...diagnostics].sort((a, b) => {\n const severityOrder = { error: 0, warning: 1, info: 2, hint: 3 }\n return severityOrder[a.severity] - severityOrder[b.severity]\n })\n \n for (const d of sorted.slice(0, 10)) {\n output += `- Line ${d.range.start.line + 1}: [${d.severity}] ${d.message}\\n`\n }\n \n if (diagnostics.length > 10) {\n output += `... and ${diagnostics.length - 10} more\\n`\n }\n \n return output\n}\n","import { readFile, writeFile } from 'node:fs/promises'\nimport type { Diagnostic, EditContextRegion } from '../../shared/types.js'\nimport { createTool } from './tool-helpers.js'\nimport { formatDiagnosticsForLLM } from './diagnostics.js'\nimport { validateFileForWrite, computeFileHash } from './file-tracker.js'\nimport { extractEditContext } from './edit-context.js'\n\ninterface EditFileArgs {\n path: string\n old_string: string\n new_string: string\n replace_all?: boolean\n}\n\nexport const editFileTool = createTool<EditFileArgs>(\n 'edit_file',\n {\n type: 'function',\n function: {\n name: 'edit_file',\n description: 'Replace specific text in a file. Use this for surgical edits. The old_string must match exactly (including whitespace and indentation).',\n parameters: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Path to the file (relative to workdir or absolute)',\n },\n old_string: {\n type: 'string',\n description: 'Exact text to find and replace. Must match exactly including whitespace.',\n },\n new_string: {\n type: 'string',\n description: 'Replacement text',\n },\n replace_all: {\n type: 'boolean',\n description: 'Replace all occurrences (default: false). If false and multiple matches found, the operation fails.',\n },\n },\n required: ['path', 'old_string', 'new_string'],\n },\n },\n },\n async (args, context, helpers) => {\n const replaceAll = args.replace_all ?? false\n \n const fullPath = helpers.resolvePath(args.path)\n await helpers.checkPathAccess([fullPath])\n \n // Validate file was read before editing\n const readFiles = context.sessionManager.getReadFiles(context.sessionId)\n const validation = await validateFileForWrite(fullPath, readFiles)\n if (!validation.valid) {\n return helpers.error(validation.error?.message ?? 'File validation failed')\n }\n \n // Read file\n let content: string\n try {\n content = await readFile(fullPath, 'utf-8')\n } catch {\n return helpers.error(`File not found: ${args.path}`)\n }\n \n // Count occurrences\n const occurrences = content.split(args.old_string).length - 1\n \n if (occurrences === 0) {\n const preview = args.old_string.length > 100 \n ? args.old_string.slice(0, 100) + '...' \n : args.old_string\n \n return helpers.error(\n `old_string not found in file.\\n\\nSearched for:\\n${preview}\\n\\nMake sure whitespace and indentation match exactly.`\n )\n }\n \n if (occurrences > 1 && !replaceAll) {\n return helpers.error(\n `Found ${occurrences} matches for old_string. Use replace_all: true to replace all, or provide more context to make the match unique.`\n )\n }\n \n // Extract edit context BEFORE making the replacement\n const contextResult = extractEditContext(content, args.old_string, args.new_string, replaceAll)\n \n // Convert to shared types\n const editContextRegions: EditContextRegion[] = contextResult.regions.map(region => ({\n beforeContext: region.beforeContext.map(line => ({\n lineNumber: line.lineNumber,\n content: line.content,\n })),\n afterContext: region.afterContext.map(line => ({\n lineNumber: line.lineNumber,\n content: line.content,\n })),\n startLine: region.startLine,\n endLine: region.endLine,\n oldContent: region.oldContent,\n newContent: region.newContent,\n edits: region.edits.map(edit => ({\n startLine: edit.startLine,\n endLine: edit.endLine,\n oldContent: edit.oldContent,\n newContent: edit.newContent,\n })),\n }))\n \n // Perform replacement\n const newContent = replaceAll \n ? content.replaceAll(args.old_string, args.new_string)\n : content.replace(args.old_string, args.new_string)\n \n // Write file\n await writeFile(fullPath, newContent, 'utf-8')\n \n let output = `Successfully replaced ${replaceAll ? occurrences : 1} occurrence(s) in ${args.path}`\n let diagnostics: Diagnostic[] = []\n \n // Get LSP diagnostics if available\n if (context.lspManager) {\n diagnostics = await context.lspManager.notifyFileChange(fullPath, newContent)\n output += formatDiagnosticsForLLM(diagnostics)\n }\n \n // Update file hash after edit so subsequent edits don't require re-reading\n const newHash = await computeFileHash(fullPath)\n if (newHash) {\n context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash)\n }\n \n return helpers.success(output, false, {\n ...(diagnostics.length > 0 && { diagnostics }),\n ...(editContextRegions.length > 0 && { editContext: { regions: editContextRegions } }),\n })\n }\n)\n","/**\n * Edit Context Extraction\n * \n * Computes context lines (4 before, 4 after) for edit_file operations.\n * Handles multiple replacements by merging overlapping context regions.\n */\n\nconst CONTEXT_LINES = 4\n\nexport interface ContextLine {\n lineNumber: number // 1-indexed\n content: string\n}\n\nexport interface SingleEdit {\n startLine: number // 1-indexed line where old content starts\n endLine: number // 1-indexed line where old content ends (inclusive)\n oldContent: string\n newContent: string\n}\n\n/** \n * A region represents a contiguous section of the file with context.\n * For single edits, there's one edit in the region.\n * For replace_all with overlapping contexts, multiple edits are merged.\n */\nexport interface EditRegion {\n beforeContext: ContextLine[]\n afterContext: ContextLine[]\n // For single edit (backwards compat)\n startLine: number\n endLine: number\n oldContent: string\n newContent: string\n // For merged multiple edits\n edits: SingleEdit[]\n}\n\nexport interface EditContextResult {\n regions: EditRegion[]\n}\n\n/**\n * Find all occurrences of a substring in content.\n * Returns array of { index, line } for each match.\n */\nfunction findAllMatches(content: string, searchString: string): Array<{ index: number; line: number }> {\n const matches: Array<{ index: number; line: number }> = []\n let pos = 0\n \n while (true) {\n const index = content.indexOf(searchString, pos)\n if (index === -1) break\n \n // Count lines up to this position\n const beforeMatch = content.slice(0, index)\n const line = beforeMatch.split('\\n').length\n \n matches.push({ index, line })\n pos = index + 1 // Move past this match to find next\n }\n \n return matches\n}\n\n/**\n * Get the line number where a character index falls.\n * Returns 1-indexed line number.\n */\nfunction getLineNumber(content: string, charIndex: number): number {\n return content.slice(0, charIndex).split('\\n').length\n}\n\n/**\n * Get the end line number for a multi-line string starting at a given line.\n */\nfunction getEndLine(startLine: number, str: string): number {\n const lineCount = str.split('\\n').length\n return startLine + lineCount - 1\n}\n\n/**\n * Extract context lines around edit positions.\n * \n * @param content - Original file content\n * @param oldString - String being replaced\n * @param newString - Replacement string\n * @param replaceAll - If true, find all occurrences\n * @returns EditContextResult with regions containing context and edit info\n */\nexport function extractEditContext(\n content: string,\n oldString: string,\n newString: string,\n replaceAll: boolean = false\n): EditContextResult {\n if (!content || !oldString) {\n return { regions: [] }\n }\n \n // Find all matches\n const matches = findAllMatches(content, oldString)\n \n if (matches.length === 0) {\n return { regions: [] }\n }\n \n // If not replaceAll, only use first match\n const relevantMatches = replaceAll ? matches : [matches[0]!]\n \n // Split content into lines for context extraction\n const lines = content.split('\\n')\n \n // Create initial edit regions for each match\n const edits: SingleEdit[] = relevantMatches.map(match => ({\n startLine: match.line,\n endLine: getEndLine(match.line, oldString),\n oldContent: oldString,\n newContent: newString,\n }))\n \n // Merge overlapping regions\n const regions = mergeEditRegions(edits, lines)\n \n return { regions }\n}\n\n/**\n * Merge edits into regions, combining those whose contexts overlap.\n */\nfunction mergeEditRegions(edits: SingleEdit[], lines: string[]): EditRegion[] {\n if (edits.length === 0) return []\n \n const totalLines = lines.length\n const regions: EditRegion[] = []\n \n // Sort edits by start line\n const sortedEdits = [...edits].sort((a, b) => a.startLine - b.startLine)\n \n let currentRegion: {\n edits: SingleEdit[]\n contextStart: number // First line of context (1-indexed)\n contextEnd: number // Last line of context (1-indexed)\n } | null = null\n \n for (const edit of sortedEdits) {\n const editContextStart = Math.max(1, edit.startLine - CONTEXT_LINES)\n const editContextEnd = Math.min(totalLines, edit.endLine + CONTEXT_LINES)\n \n if (currentRegion === null) {\n // Start new region\n currentRegion = {\n edits: [edit],\n contextStart: editContextStart,\n contextEnd: editContextEnd,\n }\n } else if (editContextStart <= currentRegion.contextEnd + 1) {\n // Overlaps with current region - merge\n currentRegion.edits.push(edit)\n currentRegion.contextEnd = Math.max(currentRegion.contextEnd, editContextEnd)\n } else {\n // No overlap - finalize current region and start new one\n regions.push(buildRegion(currentRegion.edits, currentRegion.contextStart, currentRegion.contextEnd, lines))\n currentRegion = {\n edits: [edit],\n contextStart: editContextStart,\n contextEnd: editContextEnd,\n }\n }\n }\n \n // Finalize last region\n if (currentRegion) {\n regions.push(buildRegion(currentRegion.edits, currentRegion.contextStart, currentRegion.contextEnd, lines))\n }\n \n return regions\n}\n\n/**\n * Build an EditRegion from a set of edits and their combined context bounds.\n */\nfunction buildRegion(\n edits: SingleEdit[],\n contextStart: number,\n contextEnd: number,\n lines: string[]\n): EditRegion {\n const firstEdit = edits[0]!\n const lastEdit = edits[edits.length - 1]!\n \n // Before context: lines from contextStart up to (but not including) first edit\n const beforeContext: ContextLine[] = []\n for (let i = contextStart; i < firstEdit.startLine; i++) {\n beforeContext.push({\n lineNumber: i,\n content: lines[i - 1] ?? '', // lines array is 0-indexed\n })\n }\n \n // After context: lines after last edit up to contextEnd\n const afterContext: ContextLine[] = []\n for (let i = lastEdit.endLine + 1; i <= contextEnd; i++) {\n afterContext.push({\n lineNumber: i,\n content: lines[i - 1] ?? '', // lines array is 0-indexed\n })\n }\n \n return {\n beforeContext,\n afterContext,\n // Primary edit info (first edit for backwards compat)\n startLine: firstEdit.startLine,\n endLine: lastEdit.endLine,\n oldContent: firstEdit.oldContent,\n newContent: firstEdit.newContent,\n // All edits in this region\n edits,\n }\n}\n","import { spawn } from 'node:child_process'\nimport { resolve, isAbsolute } from 'node:path'\nimport { OUTPUT_LIMITS } from './types.js'\nimport { createTool } from './tool-helpers.js'\nimport {\n extractAbsolutePathsFromCommand,\n extractSensitivePathsFromCommand,\n} from './path-security.js'\nimport { terminateProcessTree } from '../utils/process-tree.js'\n\nconst DANGEROUS_PATTERNS = [\n /rm\\s+(-rf?|--recursive)\\s+[\\/~]/,\n /sudo\\s/,\n /chmod\\s+777/,\n />\\s*\\/dev\\/sd/,\n /mkfs\\s/,\n /dd\\s+if=/,\n /:\\(\\)\\s*\\{\\s*:\\s*\\|\\s*:\\s*&\\s*\\}\\s*;/, // Fork bomb\n]\n\ninterface RunCommandArgs {\n command: string\n cwd?: string\n timeout?: number\n}\n\nexport const runCommandTool = createTool<RunCommandArgs>(\n 'run_command',\n {\n type: 'function',\n function: {\n name: 'run_command',\n description: 'Execute a shell command. Returns stdout, stderr, and exit code.',\n parameters: {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: 'The command to execute',\n },\n cwd: {\n type: 'string',\n description: 'Working directory for the command (default: session workdir)',\n },\n timeout: {\n type: 'number',\n description: 'Timeout in milliseconds (default: 120000, max: 300000)',\n },\n },\n required: ['command'],\n },\n },\n },\n async (args, context, helpers) => {\n const timeout = Math.min(args.timeout ?? 120_000, 300_000)\n \n // Check for dangerous commands\n for (const pattern of DANGEROUS_PATTERNS) {\n if (pattern.test(args.command)) {\n return helpers.error(\n `Command appears dangerous and was blocked: ${args.command}\\n\\nIf you really need to run this, ask the user for confirmation.`\n )\n }\n }\n \n // Resolve working directory\n const workingDir = args.cwd \n ? helpers.resolvePath(args.cwd)\n : context.workdir\n \n // Collect all paths that need checking\n const pathsToCheck: string[] = [workingDir]\n \n // Extract absolute paths from command (including ~ expansion)\n const commandPaths = extractAbsolutePathsFromCommand(args.command)\n for (const cmdPath of commandPaths) {\n const resolved = isAbsolute(cmdPath) ? cmdPath : resolve(workingDir, cmdPath)\n pathsToCheck.push(resolved)\n }\n \n // Extract sensitive file paths from command (like .env, credentials.json)\n const sensitivePaths = extractSensitivePathsFromCommand(args.command)\n for (const sensitivePath of sensitivePaths) {\n const resolved = isAbsolute(sensitivePath) ? sensitivePath : resolve(workingDir, sensitivePath)\n pathsToCheck.push(resolved)\n }\n \n // Check all paths - request confirmation for paths outside workdir or sensitive files\n await helpers.checkPathAccess(pathsToCheck)\n \n // Execute command\n const result = await executeCommand(args.command, workingDir, timeout, context.signal, context.onProgress)\n \n // Format output\n let output = ''\n \n if (result.stdout) {\n output += result.stdout\n }\n \n if (result.stderr) {\n if (output) output += '\\n\\n'\n output += `[stderr]\\n${result.stderr}`\n }\n \n output += `\\n\\n[Exit code: ${result.exitCode}]`\n \n // Check truncation\n let truncated = false\n if (output.length > OUTPUT_LIMITS.run_command.maxBytes) {\n output = output.slice(0, OUTPUT_LIMITS.run_command.maxBytes)\n output += '\\n\\n[Output truncated due to size limit]'\n truncated = true\n }\n \n const lines = output.split('\\n').length\n if (lines > OUTPUT_LIMITS.run_command.maxLines) {\n const limitedLines = output.split('\\n').slice(0, OUTPUT_LIMITS.run_command.maxLines)\n output = limitedLines.join('\\n')\n output += '\\n\\n[Output truncated due to line limit]'\n truncated = true\n }\n \n // Check if this was an interrupted command (marker in output, exit code 130)\n const wasInterrupted = output.includes('[interrupted by user]')\n \n return helpers.success(output, truncated, {\n // Mark as not successful if non-zero exit (unless interrupted)\n success: result.exitCode === 0,\n // Don't set error for interrupted commands - the marker is sufficient\n ...(result.exitCode !== 0 && !wasInterrupted ? { error: `Command exited with code ${result.exitCode}` } : {}),\n })\n }\n)\n\ninterface CommandResult {\n stdout: string\n stderr: string\n exitCode: number\n}\n\nfunction executeCommand(\n command: string,\n cwd: string,\n timeout: number,\n signal?: AbortSignal,\n onProgress?: (message: string) => void\n): Promise<CommandResult> {\n return new Promise((resolve, reject) => {\n // Check if already aborted before starting\n if (signal?.aborted) {\n reject(new Error('Command aborted before execution'))\n return\n }\n \n const proc = spawn('sh', ['-c', command], {\n cwd,\n env: { ...process.env, FORCE_COLOR: '0' },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: true, // Create new process group so we can kill all children\n })\n \n let stdout = ''\n let stderr = ''\n let killed = false\n let aborted = false\n let exited = false\n \n const timer = setTimeout(() => {\n killed = true\n void terminateProcessTree(proc, { exited: () => exited })\n reject(new Error(`Command timed out after ${timeout}ms`))\n }, timeout)\n \n // Handle abort signal - kill entire process group (like Ctrl+C)\n const onAbort = () => {\n if (!killed && !aborted) {\n aborted = true\n void terminateProcessTree(proc, { exited: () => exited })\n }\n }\n signal?.addEventListener('abort', onAbort)\n \n proc.stdout.on('data', (data: Buffer) => {\n const chunk = data.toString()\n stdout += chunk\n onProgress?.(`[stdout] ${chunk}`)\n })\n \n proc.stderr.on('data', (data: Buffer) => {\n const chunk = data.toString()\n stderr += chunk\n onProgress?.(`[stderr] ${chunk}`)\n })\n \n proc.on('close', (code) => {\n exited = true\n clearTimeout(timer)\n signal?.removeEventListener('abort', onAbort)\n \n if (killed) {\n // Already rejected by timeout\n return\n }\n \n if (aborted) {\n // Return partial output with interrupted marker (not an error)\n let output = stdout.trim()\n if (output) output += '\\n\\n'\n output += '[interrupted by user]'\n resolve({\n stdout: output,\n stderr: stderr.trim(),\n exitCode: 130, // Standard SIGINT exit code\n })\n return\n }\n \n resolve({\n stdout: stdout.trim(),\n stderr: stderr.trim(),\n exitCode: code ?? 1,\n })\n })\n \n proc.on('error', (error) => {\n clearTimeout(timer)\n signal?.removeEventListener('abort', onAbort)\n reject(error)\n })\n })\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { setTimeout as sleep } from 'node:timers/promises'\n\nconst SIGKILL_TIMEOUT_MS = 200\n\nexport async function terminateProcessTree(\n proc: ChildProcess,\n options?: { exited?: () => boolean }\n): Promise<void> {\n const pid = proc.pid\n if (!pid || options?.exited?.()) {\n return\n }\n\n if (process.platform === 'win32') {\n await new Promise<void>((resolve) => {\n const killer = spawn('taskkill', ['/pid', String(pid), '/f', '/t'], {\n stdio: 'ignore',\n windowsHide: true,\n })\n killer.once('exit', () => resolve())\n killer.once('error', () => resolve())\n })\n return\n }\n\n try {\n process.kill(-pid, 'SIGTERM')\n await sleep(SIGKILL_TIMEOUT_MS)\n if (!options?.exited?.()) {\n process.kill(-pid, 'SIGKILL')\n }\n } catch {\n proc.kill('SIGTERM')\n await sleep(SIGKILL_TIMEOUT_MS)\n if (!options?.exited?.()) {\n proc.kill('SIGKILL')\n }\n }\n}\n","import fg from 'fast-glob'\nimport { OUTPUT_LIMITS } from './types.js'\nimport { createTool } from './tool-helpers.js'\n\ninterface GlobArgs {\n pattern: string\n cwd?: string\n}\n\nexport const globTool = createTool<GlobArgs>(\n 'glob',\n {\n type: 'function',\n function: {\n name: 'glob',\n description: 'Find files matching a glob pattern. Returns list of matching file paths.',\n parameters: {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'Glob pattern (e.g., \"**/*.ts\", \"src/**/*.{js,jsx}\")',\n },\n cwd: {\n type: 'string',\n description: 'Base directory for the search (default: session workdir)',\n },\n },\n required: ['pattern'],\n },\n },\n },\n async (args, context, helpers) => {\n // Resolve working directory\n const baseDir = args.cwd \n ? helpers.resolvePath(args.cwd)\n : context.workdir\n \n await helpers.checkPathAccess([baseDir])\n \n // Execute glob\n const files = await fg(args.pattern, {\n cwd: baseDir,\n onlyFiles: true,\n ignore: [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/build/**',\n '**/.next/**',\n '**/coverage/**',\n ],\n followSymbolicLinks: false,\n suppressErrors: true,\n })\n \n // Sort alphabetically\n files.sort()\n \n // Apply limit\n const truncated = files.length > OUTPUT_LIMITS.glob.maxResults\n const limitedFiles = files.slice(0, OUTPUT_LIMITS.glob.maxResults)\n \n // Format output\n let output = limitedFiles.join('\\n')\n \n if (truncated) {\n output += `\\n\\n[Showing first ${OUTPUT_LIMITS.glob.maxResults} of ${files.length} matches]`\n } else {\n output += `\\n\\n[${files.length} file(s) found]`\n }\n \n return helpers.success(output, truncated, {\n metadata: {\n pattern: args.pattern,\n cwd: args.cwd,\n totalFound: files.length,\n shownCount: limitedFiles.length,\n truncated,\n },\n })\n }\n)\n","import fg from 'fast-glob'\nimport { readFile } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { OUTPUT_LIMITS } from './types.js'\nimport { createTool } from './tool-helpers.js'\n\ninterface GrepArgs {\n pattern: string\n include?: string\n cwd?: string\n}\n\nexport const grepTool = createTool<GrepArgs>(\n 'grep',\n {\n type: 'function',\n function: {\n name: 'grep',\n description: 'Search for a pattern in files. Returns matching lines with file paths and line numbers.',\n parameters: {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'Regex pattern to search for',\n },\n include: {\n type: 'string',\n description: 'File pattern to include (e.g., \"*.ts\", \"*.{js,jsx}\")',\n },\n cwd: {\n type: 'string',\n description: 'Base directory for the search (default: session workdir)',\n },\n },\n required: ['pattern'],\n },\n },\n },\n async (args, context, helpers) => {\n // Resolve working directory\n const baseDir = args.cwd \n ? helpers.resolvePath(args.cwd)\n : context.workdir\n \n await helpers.checkPathAccess([baseDir])\n \n // Create regex\n let regex: RegExp\n try {\n regex = new RegExp(args.pattern, 'gi')\n } catch {\n return helpers.error(`Invalid regex pattern: ${args.pattern}`)\n }\n \n // Find files to search\n const globPattern = args.include ?? '**/*'\n const files = await fg(globPattern, {\n cwd: baseDir,\n onlyFiles: true,\n ignore: [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/build/**',\n '**/.next/**',\n '**/coverage/**',\n '**/*.min.js',\n '**/*.map',\n '**/package-lock.json',\n '**/yarn.lock',\n '**/pnpm-lock.yaml',\n ],\n followSymbolicLinks: false,\n suppressErrors: true,\n })\n \n // Search files\n const matches: { file: string; line: number; content: string }[] = []\n \n for (const file of files) {\n if (matches.length >= OUTPUT_LIMITS.grep.maxMatches) break\n \n try {\n const fullPath = resolve(baseDir, file)\n const content = await readFile(fullPath, 'utf-8')\n const lines = content.split('\\n')\n \n for (let i = 0; i < lines.length; i++) {\n if (matches.length >= OUTPUT_LIMITS.grep.maxMatches) break\n \n const line = lines[i]!\n if (regex.test(line)) {\n matches.push({\n file,\n line: i + 1,\n content: line.length > 200 ? line.slice(0, 200) + '...' : line,\n })\n }\n \n // Reset regex lastIndex since we're reusing it\n regex.lastIndex = 0\n }\n } catch {\n // Skip files that can't be read (binary files, etc.)\n }\n }\n \n // Format output\n const truncated = matches.length >= OUTPUT_LIMITS.grep.maxMatches\n \n let output = matches\n .map(m => `${m.file}:${m.line}: ${m.content}`)\n .join('\\n')\n \n if (output) {\n if (truncated) {\n output += `\\n\\n[Showing first ${OUTPUT_LIMITS.grep.maxMatches} matches. Refine your search for more specific results.]`\n } else {\n output += `\\n\\n[${matches.length} match(es) found]`\n }\n } else {\n output = 'No matches found.'\n }\n \n return helpers.success(output, truncated, {\n metadata: {\n pattern: args.pattern,\n include: args.include,\n cwd: args.cwd,\n totalMatches: matches.length,\n shownCount: matches.length,\n truncated,\n },\n })\n }\n)\n","import { spawn } from 'node:child_process'\nimport { resolve, isAbsolute } from 'node:path'\nimport type { ToolResult } from '../../shared/types.js'\nimport type { Tool, ToolContext } from './types.js'\nimport { OUTPUT_LIMITS } from './types.js'\nimport { terminateProcessTree } from '../utils/process-tree.js'\n\nexport const gitTool: Tool = {\n name: 'git',\n definition: {\n type: 'function',\n function: {\n name: 'git',\n description: 'Execute a git command to inspect repository state. Use for checking status, diffs, logs, branches, etc. Cannot modify the repository.',\n parameters: {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: 'The git command to execute (e.g., \"git status\", \"git diff\", \"git log --oneline -5\")',\n },\n cwd: {\n type: 'string',\n description: 'Working directory for the command (default: session workdir). Must be within a git repository.',\n },\n },\n required: ['command'],\n },\n },\n },\n \n async execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {\n const startTime = Date.now()\n \n try {\n const command = args['command'] as string\n const cwd = args['cwd'] as string | undefined\n \n // Validate command starts with git\n if (!command || typeof command !== 'string') {\n return {\n success: false,\n error: 'command is required',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n \n if (!command.startsWith('git')) {\n return {\n success: false,\n error: `Command must start with \"git\". Received: ${command}`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n \n // Block destructive git commands\n const destructivePatterns = [\n /git\\s+(reset\\s+--hard|reset\\s+--hard\\s+HEAD)/,\n /git\\s+(clean\\s+-[a-z]*f[a-z]*|-f.*clean)/,\n /git\\s+push\\s+--force/,\n /git\\s+branch\\s+-D/,\n /git\\s+update-ref\\s+-d/,\n ]\n \n for (const pattern of destructivePatterns) {\n if (pattern.test(command)) {\n return {\n success: false,\n error: `Destructive git command blocked: ${command}\\n\\nThe git tool is read-only. Use builder mode for repository modifications.`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n }\n \n // Resolve working directory\n const workingDir = cwd \n ? (isAbsolute(cwd) ? cwd : resolve(context.workdir, cwd))\n : context.workdir\n \n // Execute git command\n const result = await executeGitCommand(command, workingDir, context.signal)\n \n // Format output\n let output = ''\n \n if (result.stdout) {\n output += result.stdout\n }\n \n if (result.stderr) {\n if (output) output += '\\n\\n'\n output += `[stderr]\\n${result.stderr}`\n }\n \n output += `\\n\\n[Exit code: ${result.exitCode}]`\n \n // Check truncation\n let truncated = false\n if (output.length > OUTPUT_LIMITS.run_command.maxBytes) {\n output = output.slice(0, OUTPUT_LIMITS.run_command.maxBytes)\n output += '\\n\\n[Output truncated due to size limit]'\n truncated = true\n }\n \n const lines = output.split('\\n').length\n if (lines > OUTPUT_LIMITS.run_command.maxLines) {\n const limitedLines = output.split('\\n').slice(0, OUTPUT_LIMITS.run_command.maxLines)\n output = limitedLines.join('\\n')\n output += '\\n\\n[Output truncated due to line limit]'\n truncated = true\n }\n \n return {\n success: result.exitCode === 0,\n output,\n ...(result.exitCode !== 0 ? { error: `Git command exited with code ${result.exitCode}` } : {}),\n durationMs: Date.now() - startTime,\n truncated,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error executing git command',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n },\n}\n\ninterface GitResult {\n stdout: string\n stderr: string\n exitCode: number\n}\n\nfunction executeGitCommand(\n command: string,\n cwd: string,\n signal?: AbortSignal\n): Promise<GitResult> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error('Git command aborted before execution'))\n return\n }\n \n const proc = spawn('sh', ['-c', command], {\n cwd,\n env: { ...process.env, FORCE_COLOR: '0' },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: true,\n })\n \n let stdout = ''\n let stderr = ''\n let aborted = false\n let exited = false\n\n const onAbort = () => {\n if (!aborted) {\n aborted = true\n void terminateProcessTree(proc, { exited: () => exited })\n }\n }\n signal?.addEventListener('abort', onAbort)\n \n proc.stdout.on('data', (data: Buffer) => {\n stdout += data.toString()\n })\n \n proc.stderr.on('data', (data: Buffer) => {\n stderr += data.toString()\n })\n \n proc.on('close', (code) => {\n exited = true\n signal?.removeEventListener('abort', onAbort)\n \n if (aborted) {\n resolve({\n stdout: stdout.trim() + '\\n[interrupted by user]',\n stderr: stderr.trim(),\n exitCode: 130,\n })\n return\n }\n \n resolve({\n stdout: stdout.trim(),\n stderr: stderr.trim(),\n exitCode: code ?? 1,\n })\n })\n \n proc.on('error', (error) => {\n signal?.removeEventListener('abort', onAbort)\n reject(error)\n })\n })\n}\n","export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport interface RetryOptions {\n maxRetries: number\n backoffMs: number[]\n shouldRetry?: (error: unknown) => boolean\n}\n\nexport async function withRetry<T>(\n operation: () => Promise<T>,\n options: RetryOptions\n): Promise<T> {\n let lastError: unknown\n \n for (let attempt = 0; attempt <= options.maxRetries; attempt++) {\n try {\n return await operation()\n } catch (error) {\n lastError = error\n \n if (options.shouldRetry && !options.shouldRetry(error)) {\n throw error\n }\n \n if (attempt < options.maxRetries) {\n const backoff = options.backoffMs[attempt] ?? options.backoffMs[options.backoffMs.length - 1]!\n await sleep(backoff)\n }\n }\n }\n \n throw lastError\n}\n\nexport function createDeferred<T>(): {\n promise: Promise<T>\n resolve: (value: T) => void\n reject: (error: unknown) => void\n} {\n let resolve!: (value: T) => void\n let reject!: (error: unknown) => void\n \n const promise = new Promise<T>((res, rej) => {\n resolve = res\n reject = rej\n })\n \n return { promise, resolve, reject }\n}\n\nexport type Unsubscribe = () => void\n\nexport class EventEmitter<T extends Record<string, unknown[]>> {\n private listeners = new Map<keyof T, Set<(...args: unknown[]) => void>>()\n \n on<K extends keyof T>(event: K, listener: (...args: T[K]) => void): Unsubscribe {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set())\n }\n \n const listeners = this.listeners.get(event)!\n listeners.add(listener as (...args: unknown[]) => void)\n \n return () => {\n listeners.delete(listener as (...args: unknown[]) => void)\n }\n }\n \n emit<K extends keyof T>(event: K, ...args: T[K]): void {\n const listeners = this.listeners.get(event)\n if (listeners) {\n for (const listener of listeners) {\n listener(...args)\n }\n }\n }\n \n removeAllListeners(): void {\n this.listeners.clear()\n }\n}\n","import type { ToolResult } from '../../shared/types.js'\nimport type { Tool, ToolContext } from './types.js'\nimport { createDeferred } from '../utils/async.js'\n\n// Store pending questions by call ID\nconst pendingQuestions = new Map<string, {\n resolve: (answer: string) => void\n reject: (error: Error) => void\n sessionId: string\n}>()\n\nexport const askUserTool: Tool = {\n name: 'ask_user',\n definition: {\n type: 'function',\n function: {\n name: 'ask_user',\n description: 'Pause execution and ask the user a question. Use this when you need clarification or user input before proceeding.',\n parameters: {\n type: 'object',\n properties: {\n question: {\n type: 'string',\n description: 'The question to ask the user',\n },\n },\n required: ['question'],\n },\n },\n },\n \n async execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {\n const startTime = Date.now()\n const question = args['question'] as string\n \n // Generate a unique ID for this question\n const callId = crypto.randomUUID()\n \n // Create a deferred promise for the answer\n const deferred = createDeferred<string>()\n void deferred.promise.catch(() => {})\n \n pendingQuestions.set(callId, {\n resolve: deferred.resolve,\n reject: deferred.reject,\n sessionId: context.sessionId,\n })\n \n // The agent runner will see this and pause execution,\n // sending an event to the client with the question and callId\n \n // This is a special case - we throw a custom error that the agent runner catches\n throw new AskUserInterrupt(callId, question)\n },\n}\n\n// Custom error class for ask_user interrupts\nexport class AskUserInterrupt extends Error {\n constructor(\n public readonly callId: string,\n public readonly question: string\n ) {\n super('Ask user interrupt')\n this.name = 'AskUserInterrupt'\n }\n}\n\n// Function to provide the answer (called by the WebSocket handler)\nexport function provideAnswer(callId: string, answer: string): boolean {\n const pending = pendingQuestions.get(callId)\n if (!pending) {\n return false\n }\n \n pending.resolve(answer)\n pendingQuestions.delete(callId)\n return true\n}\n\n// Function to cancel a pending question\nexport function cancelQuestion(callId: string, reason: string): boolean {\n const pending = pendingQuestions.get(callId)\n if (!pending) {\n return false\n }\n \n pending.reject(new Error(reason))\n pendingQuestions.delete(callId)\n return true\n}\n\nexport function cancelQuestionsForSession(sessionId: string, reason: string): number {\n let cancelledCount = 0\n\n for (const [callId, pending] of pendingQuestions.entries()) {\n if (pending.sessionId !== sessionId) {\n continue\n }\n\n pending.reject(new Error(reason))\n pendingQuestions.delete(callId)\n cancelledCount += 1\n }\n\n return cancelledCount\n}\n\n// Check if there's a pending question\nexport function hasPendingQuestion(callId: string): boolean {\n return pendingQuestions.has(callId)\n}\n","import type { ToolResult, CriterionStatus } from '../../shared/types.js'\nimport type { Tool, ToolContext } from './types.js'\n\n/**\n * complete_criterion - Builder marks a criterion as completed (awaiting verification)\n */\nexport const completeCriterionTool: Tool = {\n name: 'complete_criterion',\n definition: {\n type: 'function',\n function: {\n name: 'complete_criterion',\n description: 'Mark an acceptance criterion as completed. Call this when you have finished implementing a criterion. The verifier will later confirm it passes.',\n parameters: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'The criterion ID to mark as completed',\n },\n reason: {\n type: 'string',\n description: 'Brief explanation of how the criterion was satisfied',\n },\n },\n required: ['id'],\n },\n },\n },\n \n async execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {\n const startTime = Date.now()\n \n try {\n const id = args['id'] as string\n const reason = args['reason'] as string | undefined\n \n const session = context.sessionManager.requireSession(context.sessionId)\n const criterion = session.criteria.find(c => c.id === id)\n \n if (!criterion) {\n return {\n success: false,\n error: `Criterion not found: ${id}. Available: ${session.criteria.map(c => c.id).join(', ')}`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n \n // Update status to completed\n const status: CriterionStatus = {\n type: 'completed',\n completedAt: new Date().toISOString(),\n ...(reason ? { reason } : {}),\n }\n \n context.sessionManager.updateCriterionStatus(context.sessionId, id, status)\n \n return {\n success: true,\n output: `Criterion \"${id}\" marked as completed.${reason ? ` Reason: ${reason}` : ''}`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n },\n}\n\n/**\n * pass_criterion - Verifier confirms a criterion passes verification\n */\nexport const passCriterionTool: Tool = {\n name: 'pass_criterion',\n definition: {\n type: 'function',\n function: {\n name: 'pass_criterion',\n description: 'Confirm that a criterion passes verification. Call this after verifying the implementation meets the requirement.',\n parameters: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'The criterion ID that passes verification',\n },\n reason: {\n type: 'string',\n description: 'How you verified this criterion passes',\n },\n },\n required: ['id'],\n },\n },\n },\n \n async execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {\n const startTime = Date.now()\n \n try {\n const id = args['id'] as string\n const reason = args['reason'] as string | undefined\n \n const session = context.sessionManager.requireSession(context.sessionId)\n const criterion = session.criteria.find(c => c.id === id)\n \n if (!criterion) {\n return {\n success: false,\n error: `Criterion not found: ${id}`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n \n const status: CriterionStatus = {\n type: 'passed',\n verifiedAt: new Date().toISOString(),\n ...(reason ? { reason } : {}),\n }\n \n context.sessionManager.updateCriterionStatus(context.sessionId, id, status)\n \n // Record the verification attempt\n context.sessionManager.addCriterionAttempt(context.sessionId, id, {\n attemptNumber: criterion.attempts.length + 1,\n status: 'passed',\n timestamp: new Date().toISOString(),\n ...(reason && { details: reason }),\n })\n \n return {\n success: true,\n output: `Criterion \"${id}\" verified as PASSED.${reason ? ` Verification: ${reason}` : ''}`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n },\n}\n\n/**\n * fail_criterion - Verifier marks a criterion as failed\n */\nexport const failCriterionTool: Tool = {\n name: 'fail_criterion',\n definition: {\n type: 'function',\n function: {\n name: 'fail_criterion',\n description: 'Mark a criterion as failed during verification. Provide a clear reason so the builder can fix it.',\n parameters: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'The criterion ID that failed verification',\n },\n reason: {\n type: 'string',\n description: 'Why the criterion failed verification - be specific so it can be fixed',\n },\n },\n required: ['id', 'reason'],\n },\n },\n },\n \n async execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {\n const startTime = Date.now()\n \n try {\n const id = args['id'] as string\n const reason = args['reason'] as string\n \n const session = context.sessionManager.requireSession(context.sessionId)\n const criterion = session.criteria.find(c => c.id === id)\n \n if (!criterion) {\n return {\n success: false,\n error: `Criterion not found: ${id}`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n \n const status: CriterionStatus = {\n type: 'failed',\n failedAt: new Date().toISOString(),\n reason,\n }\n \n context.sessionManager.updateCriterionStatus(context.sessionId, id, status)\n \n // Record the verification attempt\n context.sessionManager.addCriterionAttempt(context.sessionId, id, {\n attemptNumber: criterion.attempts.length + 1,\n status: 'failed',\n timestamp: new Date().toISOString(),\n details: reason,\n })\n \n return {\n success: true,\n output: `Criterion \"${id}\" marked as FAILED. Reason: ${reason}`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n },\n}\n","import type { Criterion } from '../../shared/types.js'\nimport type { Tool } from './types.js'\n\nfunction formatCriteriaList(criteria: Criterion[]): string {\n if (criteria.length === 0) return 'No criteria defined.'\n return criteria.map((c, i) => `${i + 1}. [${c.id}] ${c.description}`).join('\\n')\n}\n\nexport const getCriteriaTool: Tool = {\n name: 'get_criteria',\n definition: {\n type: 'function',\n function: {\n name: 'get_criteria',\n description: 'Get the current acceptance criteria list, including any user edits. Always call this before modifying criteria.',\n parameters: {\n type: 'object',\n properties: {},\n },\n },\n },\n execute: async (_args, context) => {\n const session = context.sessionManager.requireSession(context.sessionId)\n const criteria = session.criteria\n \n return {\n success: true,\n output: criteria.length === 0\n ? 'No criteria defined yet.'\n : JSON.stringify(criteria.map(c => ({\n id: c.id,\n description: c.description,\n })), null, 2),\n durationMs: 0,\n truncated: false,\n }\n },\n}\n\nexport const addCriterionTool: Tool = {\n name: 'add_criterion',\n definition: {\n type: 'function',\n function: {\n name: 'add_criterion',\n description: 'Add a new acceptance criterion.',\n parameters: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'Unique semantic identifier (e.g., \"tests-pass\", \"api-returns-jwt\")',\n },\n description: {\n type: 'string',\n description: 'Self-contained, verifiable requirement. Include how to verify it.',\n },\n },\n required: ['id', 'description'],\n },\n },\n },\n execute: async (args, context) => {\n const { id, description } = args as { id: string; description: string }\n \n if (!id || typeof id !== 'string') {\n return { success: false, error: 'id is required', durationMs: 0, truncated: false }\n }\n if (!description || typeof description !== 'string') {\n return { success: false, error: 'description is required', durationMs: 0, truncated: false }\n }\n \n context.sessionManager.requireSession(context.sessionId)\n \n const criterion: Criterion = {\n id,\n description,\n status: { type: 'pending' },\n attempts: [],\n }\n \n const result = context.sessionManager.addCriterion(context.sessionId, criterion)\n \n if ('error' in result) {\n return { success: false, error: result.error, durationMs: 0, truncated: false }\n }\n \n const idNote = result.actualId !== id \n ? ` (requested ID \"${id}\" was in use, using \"${result.actualId}\" instead)`\n : ''\n \n return {\n success: true,\n output: `Added criterion \"${result.actualId}\"${idNote}. Current criteria:\\n${formatCriteriaList(result.criteria)}`,\n durationMs: 0,\n truncated: false,\n }\n },\n}\n\nexport const updateCriterionTool: Tool = {\n name: 'update_criterion',\n definition: {\n type: 'function',\n function: {\n name: 'update_criterion',\n description: 'Update an existing criterion by ID.',\n parameters: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'ID of the criterion to update',\n },\n description: {\n type: 'string',\n description: 'New description',\n },\n },\n required: ['id'],\n },\n },\n },\n execute: async (args, context) => {\n const { id, description } = args as { id: string; description?: string }\n \n if (!id) {\n return { success: false, error: 'id is required', durationMs: 0, truncated: false }\n }\n \n const session = context.sessionManager.requireSession(context.sessionId)\n \n if (!session.criteria.find(c => c.id === id)) {\n return { success: false, error: `criterion \"${id}\" not found`, durationMs: 0, truncated: false }\n }\n \n if (!description) {\n return { success: false, error: 'description is required for update', durationMs: 0, truncated: false }\n }\n \n const criteria = context.sessionManager.updateCriterionFull(context.sessionId, id, { description })\n return {\n success: true,\n output: `Updated criterion \"${id}\". Current criteria:\\n${formatCriteriaList(criteria)}`,\n durationMs: 0,\n truncated: false,\n }\n },\n}\n\nexport const removeCriterionTool: Tool = {\n name: 'remove_criterion',\n definition: {\n type: 'function',\n function: {\n name: 'remove_criterion',\n description: 'Remove a criterion by ID.',\n parameters: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'ID of the criterion to remove',\n },\n },\n required: ['id'],\n },\n },\n },\n execute: async (args, context) => {\n const { id } = args as { id: string }\n \n if (!id) {\n return { success: false, error: 'id is required', durationMs: 0, truncated: false }\n }\n \n const session = context.sessionManager.requireSession(context.sessionId)\n \n if (!session.criteria.find(c => c.id === id)) {\n return { success: false, error: `criterion \"${id}\" not found`, durationMs: 0, truncated: false }\n }\n \n const criteria = context.sessionManager.removeCriterion(context.sessionId, id)\n return {\n success: true,\n output: criteria.length === 0\n ? `Removed criterion \"${id}\". No criteria remaining.`\n : `Removed criterion \"${id}\". Current criteria:\\n${formatCriteriaList(criteria)}`,\n durationMs: 0,\n truncated: false,\n }\n },\n}\n","import type { ToolResult, Todo } from '../../shared/types.js'\nimport type { Tool, ToolContext } from './types.js'\n\n// Store todos per session (in-memory for now, could be persisted later)\nconst sessionTodos = new Map<string, Todo[]>()\n\n// Callback to emit todo updates (set by the chat handler)\nlet onTodoUpdate: ((sessionId: string, todos: Todo[]) => void) | null = null\n\nexport function setTodoUpdateCallback(callback: (sessionId: string, todos: Todo[]) => void): void {\n onTodoUpdate = callback\n}\n\nexport function getTodos(sessionId: string): Todo[] {\n return sessionTodos.get(sessionId) ?? []\n}\n\nexport function clearTodos(sessionId: string): void {\n sessionTodos.delete(sessionId)\n}\n\n/**\n * todo_write - Update the task list (visible in chat)\n */\nexport const todoWriteTool: Tool = {\n name: 'todo_write',\n definition: {\n type: 'function',\n function: {\n name: 'todo_write',\n description: 'Update your task list. This helps you plan and track your work. The list is visible to the user in the chat.',\n parameters: {\n type: 'object',\n properties: {\n todos: {\n type: 'array',\n description: 'The complete updated todo list',\n items: {\n type: 'object',\n properties: {\n content: {\n type: 'string',\n description: 'Brief description of the task',\n },\n status: {\n type: 'string',\n enum: ['pending', 'in_progress', 'completed'],\n description: 'Current status of the task',\n },\n },\n required: ['content', 'status'],\n },\n },\n },\n required: ['todos'],\n },\n },\n },\n \n async execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {\n const startTime = Date.now()\n \n try {\n const todos = args['todos'] as Todo[]\n \n // Validate todos\n if (!Array.isArray(todos)) {\n return {\n success: false,\n error: 'todos must be an array',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n \n for (const todo of todos) {\n if (!todo.content || !todo.status) {\n return {\n success: false,\n error: 'Each todo must have content and status',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n if (!['pending', 'in_progress', 'completed'].includes(todo.status)) {\n return {\n success: false,\n error: `Invalid status: ${todo.status}. Must be pending, in_progress, or completed`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n }\n \n // Store todos\n sessionTodos.set(context.sessionId, todos)\n \n // Emit update if callback is set\n if (onTodoUpdate) {\n onTodoUpdate(context.sessionId, todos)\n }\n \n const pending = todos.filter(t => t.status === 'pending').length\n const inProgress = todos.filter(t => t.status === 'in_progress').length\n const completed = todos.filter(t => t.status === 'completed').length\n \n return {\n success: true,\n output: `Task list updated: ${completed} completed, ${inProgress} in progress, ${pending} pending`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n },\n}\n","/**\n * Agent Registry\n *\n * Discovers, loads, and manages agent definitions from .agent.md files.\n * Follows the same pattern as commands and skills registries.\n *\n * Built-in agents ship in src/server/agents/defaults/.\n * User agents live in ~/.openfox/agents/.\n * User definitions override built-in by id.\n */\n\nimport { readdir, readFile, writeFile, copyFile, mkdir, access, unlink } from 'node:fs/promises'\nimport { join, dirname } from 'node:path'\nimport { constants } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport matter from 'gray-matter'\nimport type { AgentDefinition, AgentMetadata } from './types.js'\nimport { logger } from '../utils/logger.js'\nimport { getRuntimeConfig } from '../runtime-config.js'\nimport { getGlobalConfigDir } from '../../cli/paths.js'\n\nconst __bundleDir = dirname(fileURLToPath(import.meta.url))\nconst DEFAULTS_DIR = join(__bundleDir, 'defaults')\nconst DEFAULTS_DIR_ALT = join(__bundleDir, 'agent-defaults')\nconst AGENT_EXTENSION = '.agent.md'\n\n// ============================================================================\n// Directory Helpers\n// ============================================================================\n\nfunction getAgentsDir(configDir: string): string {\n return join(configDir, 'agents')\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.R_OK)\n return true\n } catch {\n return false\n }\n}\n\n// ============================================================================\n// Default Agents Installation\n// ============================================================================\n\nexport async function ensureDefaultAgents(configDir: string): Promise<void> {\n const agentsDir = getAgentsDir(configDir)\n\n if (!await pathExists(agentsDir)) {\n await mkdir(agentsDir, { recursive: true })\n }\n\n let defaultFiles: string[]\n let sourceDir: string\n try {\n defaultFiles = (await readdir(DEFAULTS_DIR)).filter(f => f.endsWith(AGENT_EXTENSION))\n sourceDir = DEFAULTS_DIR\n } catch {\n try {\n defaultFiles = (await readdir(DEFAULTS_DIR_ALT)).filter(f => f.endsWith(AGENT_EXTENSION))\n sourceDir = DEFAULTS_DIR_ALT\n } catch {\n logger.warn('No bundled agent defaults found', { dir: DEFAULTS_DIR })\n return\n }\n }\n\n for (const file of defaultFiles) {\n const targetPath = join(agentsDir, file)\n if (!await pathExists(targetPath)) {\n try {\n await copyFile(join(sourceDir, file), targetPath)\n logger.info('Installed default agent', { file })\n } catch (err) {\n logger.error('Failed to copy default agent', { file, error: err instanceof Error ? err.message : String(err) })\n }\n }\n }\n}\n\n// ============================================================================\n// Parsing\n// ============================================================================\n\nfunction parseAgentFile(raw: string, filename: string): AgentDefinition | undefined {\n const { data, content } = matter(raw)\n const meta = data as Record<string, unknown>\n\n if (!meta['id'] || !content.trim()) {\n logger.warn('Skipping invalid agent file', { filename })\n return undefined\n }\n\n const metadata: AgentMetadata = {\n id: String(meta['id']),\n name: String(meta['name'] ?? meta['id']),\n description: String(meta['description'] ?? ''),\n subagent: meta['subagent'] === true,\n tools: Array.isArray(meta['tools']) ? meta['tools'].map(String) : [],\n ...(typeof meta['color'] === 'string' ? { color: meta['color'] } : {}),\n }\n\n return { metadata, prompt: content.trim() }\n}\n\n// ============================================================================\n// Loading\n// ============================================================================\n\nasync function loadAgentsFromDir(dir: string): Promise<AgentDefinition[]> {\n if (!await pathExists(dir)) {\n return []\n }\n\n let files: string[]\n try {\n files = (await readdir(dir)).filter(f => f.endsWith(AGENT_EXTENSION))\n } catch {\n return []\n }\n\n const agents: AgentDefinition[] = []\n for (const file of files) {\n try {\n const raw = await readFile(join(dir, file), 'utf-8')\n const agent = parseAgentFile(raw, file)\n if (agent) {\n agents.push(agent)\n }\n } catch (err) {\n logger.warn('Failed to parse agent file', { file, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n return agents\n}\n\n/**\n * Load all agents from both built-in defaults and user config.\n * User definitions override built-in by id.\n */\nexport async function loadAllAgents(configDir: string): Promise<AgentDefinition[]> {\n const [builtinAgents, userAgents] = await Promise.all([\n loadBuiltinAgents(),\n loadAgentsFromDir(getAgentsDir(configDir)),\n ])\n\n const agentMap = new Map<string, AgentDefinition>()\n for (const agent of builtinAgents) {\n agentMap.set(agent.metadata.id, agent)\n }\n for (const agent of userAgents) {\n agentMap.set(agent.metadata.id, agent)\n }\n\n return Array.from(agentMap.values())\n}\n\n/**\n * Load all agents using the global config directory.\n * Convenience wrapper that resolves configDir from runtime config.\n * Use this from code that doesn't have configDir readily available.\n */\nexport async function loadAllAgentsDefault(): Promise<AgentDefinition[]> {\n try {\n const configDir = getGlobalConfigDir(getRuntimeConfig().mode ?? 'production')\n return await loadAllAgents(configDir)\n } catch {\n return loadBuiltinAgents()\n }\n}\n\n/**\n * Load only built-in agents (from the bundled defaults directory).\n */\nexport async function loadBuiltinAgents(): Promise<AgentDefinition[]> {\n const agents = await loadAgentsFromDir(DEFAULTS_DIR)\n if (agents.length > 0) return agents\n return loadAgentsFromDir(DEFAULTS_DIR_ALT)\n}\n\n// ============================================================================\n// Default Restoration\n// ============================================================================\n\n/**\n * Get the list of agent IDs that have bundled defaults.\n */\nexport async function getDefaultAgentIds(): Promise<string[]> {\n for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {\n try {\n const files = (await readdir(dir)).filter(f => f.endsWith(AGENT_EXTENSION))\n return files.map(f => f.replace(AGENT_EXTENSION, ''))\n } catch { /* try next */ }\n }\n return []\n}\n\n/**\n * Restore a single agent to its bundled default.\n * For agents, this deletes the user override so the built-in shows through.\n * Returns true if the agent had a bundled default.\n */\nexport async function restoreDefaultAgent(configDir: string, agentId: string): Promise<boolean> {\n const defaultIds = await getDefaultAgentIds()\n if (!defaultIds.includes(agentId)) return false\n\n // Delete user override if it exists\n const userPath = join(getAgentsDir(configDir), `${agentId}${AGENT_EXTENSION}`)\n try {\n await unlink(userPath)\n } catch { /* no user override to remove */ }\n return true\n}\n\n/**\n * Return the IDs of default agents whose user copy differs from the bundled version.\n * For agents, a user file existing for a built-in ID means it's been overridden.\n */\nexport async function getModifiedDefaultAgentIds(configDir: string): Promise<string[]> {\n const defaultIds = await getDefaultAgentIds()\n const modified: string[] = []\n\n for (const id of defaultIds) {\n const filename = `${id}${AGENT_EXTENSION}`\n const userPath = join(getAgentsDir(configDir), filename)\n\n // Find bundled source\n let bundledContent: string | null = null\n for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {\n try {\n bundledContent = await readFile(join(dir, filename), 'utf-8')\n break\n } catch { /* try next */ }\n }\n if (!bundledContent) continue\n\n try {\n const userContent = await readFile(userPath, 'utf-8')\n if (userContent !== bundledContent) {\n modified.push(id)\n }\n } catch {\n // No user override — not modified\n }\n }\n\n return modified\n}\n\n/**\n * Restore all agents to their bundled defaults.\n */\nexport async function restoreAllDefaultAgents(configDir: string): Promise<number> {\n const ids = await getDefaultAgentIds()\n let count = 0\n for (const id of ids) {\n if (await restoreDefaultAgent(configDir, id)) count++\n }\n return count\n}\n\n// ============================================================================\n// Lookup Helpers\n// ============================================================================\n\nexport function findAgentById(agentId: string, agents: AgentDefinition[]): AgentDefinition | undefined {\n return agents.find(a => a.metadata.id === agentId)\n}\n\nexport function getSubAgents(agents: AgentDefinition[]): AgentDefinition[] {\n return agents.filter(a => a.metadata.subagent)\n}\n\nexport function getTopLevelAgents(agents: AgentDefinition[]): AgentDefinition[] {\n return agents.filter(a => !a.metadata.subagent)\n}\n\n// ============================================================================\n// CRUD\n// ============================================================================\n\nexport async function agentExists(configDir: string, agentId: string): Promise<boolean> {\n const filePath = join(getAgentsDir(configDir), `${agentId}${AGENT_EXTENSION}`)\n return pathExists(filePath)\n}\n\nexport async function saveAgent(configDir: string, agent: AgentDefinition): Promise<void> {\n const agentsDir = getAgentsDir(configDir)\n if (!await pathExists(agentsDir)) {\n await mkdir(agentsDir, { recursive: true })\n }\n const filePath = join(agentsDir, `${agent.metadata.id}${AGENT_EXTENSION}`)\n const content = matter.stringify(agent.prompt, agent.metadata)\n await writeFile(filePath, content, 'utf-8')\n}\n\nexport async function deleteAgent(configDir: string, agentId: string): Promise<boolean> {\n const filePath = join(getAgentsDir(configDir), `${agentId}${AGENT_EXTENSION}`)\n try {\n await unlink(filePath)\n return true\n } catch {\n return false\n }\n}\n","import { z } from 'zod'\nimport type { Config, LlmBackend } from '../shared/types.js'\n\nconst backendSchema = z.enum(['auto', 'vllm', 'sglang', 'ollama', 'llamacpp']).default('auto')\n\nconst envSchema = z.object({\n // New env var name, with fallback to old name for backward compatibility\n OPENFOX_LLM_URL: z.string().url().optional(),\n OPENFOX_VLLM_URL: z.string().url().optional(),\n OPENFOX_BACKEND: backendSchema,\n OPENFOX_MODEL_NAME: z.string().default('qwen3.5-122b-int4-autoround'),\n OPENFOX_MAX_CONTEXT: z.coerce.number().default(200000),\n OPENFOX_PORT: z.coerce.number().default(10369),\n OPENFOX_HOST: z.string().default('0.0.0.0'),\n OPENFOX_WORKDIR: z.string().optional(),\n OPENFOX_DB_PATH: z.string().default('./openfox.db'),\n OPENFOX_LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n OPENFOX_DISABLE_THINKING: z.coerce.boolean().default(false),\n OPENFOX_DEV: z.coerce.boolean().default(false),\n})\n\nexport function loadConfig(): Config {\n const env = envSchema.parse(process.env)\n \n // Use new env var, fall back to old one, then default\n const llmUrl = env.OPENFOX_LLM_URL ?? env.OPENFOX_VLLM_URL ?? 'http://localhost:8000/v1'\n \n // Workdir from env only (serve.ts will merge with global config)\n const workdir = env.OPENFOX_WORKDIR ?? process.cwd()\n \n return {\n llm: {\n baseUrl: llmUrl,\n model: env.OPENFOX_MODEL_NAME,\n timeout: 300_000, // 5 minutes (deprecated, kept for backward compatibility)\n idleTimeout: 300_000, // 5 minutes of inactivity\n backend: env.OPENFOX_BACKEND as LlmBackend | 'auto',\n disableThinking: env.OPENFOX_DISABLE_THINKING,\n },\n context: {\n maxTokens: env.OPENFOX_MAX_CONTEXT,\n compactionThreshold: 0.85,\n compactionTarget: 0.60,\n },\n agent: {\n maxIterations: 10,\n maxConsecutiveFailures: 3,\n toolTimeout: 120_000,\n },\n server: {\n port: env.OPENFOX_PORT,\n host: env.OPENFOX_HOST,\n },\n database: {\n path: env.OPENFOX_DB_PATH,\n },\n mode: env.OPENFOX_DEV ? 'development' : 'production',\n workdir,\n }\n}\n\nexport type { Config }\n","import type { Config } from '../shared/types.js'\nimport { loadConfig } from './config.js'\n\nlet runtimeConfig: Config | null = null\n\nexport function setRuntimeConfig(config: Config): void {\n runtimeConfig = config\n}\n\nexport function getRuntimeConfig(): Config {\n return runtimeConfig ?? loadConfig()\n}\n","import type { SkillMetadata } from '../skills/types.js'\nimport type { AgentDefinition } from '../agents/types.js'\n\n// ============================================================================\n// Base Prompt (shared by all agents)\n// ============================================================================\n\n/**\n * Core system prompt shared by ALL agents (top-level and sub-agents).\n * Contains: environment, core behavior, tone, guardrails, skills.\n * Does NOT contain: agent-specific instructions, sub-agents list.\n */\nexport function buildBasePrompt(workdir: string, customInstructions?: string, skills?: SkillMetadata[]): string {\n const instructionsSection = customInstructions\n ? `\\n\\n## CUSTOM INSTRUCTIONS\\n\\n${customInstructions}`\n : ''\n\n return `You are OpenFox, an agentic assistant.\n\nToday's date is ${new Date().toISOString().split('T')[0]!.replace(/-/g, '/')}\n\n## ENVIRONMENT\nWorking directory: ${workdir}\nPlatform: ${process.platform} (${process.arch})\n\n## CORE BEHAVIOR\n- Help the user complete any tasks safely and efficiently.\n- Do everything in your capacity to satisfy user requirements.\n- Read code before changing it.\n- Prefer precise, minimal changes.\n- Use available tools when needed.\n- Explain tradeoffs clearly when requirements are ambiguous.\n- Follow repository and project instructions exactly.\n\n## MODE CONTROL\n- OpenFox may append system-generated runtime control messages as USER-role messages wrapped in <system-reminder>...</system-reminder>.\n- These reminders are authoritative framework instructions from OpenFox.\n- Treat them as higher-priority operational constraints than normal user task wording for the current turn.\n- Do not describe them as \"the user reminded me\"; they are runtime mode/control metadata injected by OpenFox.\n\n## WORKFLOW\n- If the current runtime reminder says planning mode, focus on understanding, exploration, clarification, and criteria quality.\n- If the current runtime reminder says build mode, focus on implementation, verification, and completing approved criteria.\n- Respect tool and permission constraints enforced by the server even if the conversation suggests otherwise.\n\n## IMPORTANT GUARDRAILS\n- NEVER delete/git checkout an already modified file: that would result in a data loss.\n\n\n# Tone and style\nYou should be concise, direct, and to the point. When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system).\nRemember that your output will be displayed on a command line interface. Your responses can use GitHub-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification.\nOutput text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session.\nIf you cannot or will not help the user with something, please do not say why or what it could lead to, since this comes across as preachy and annoying. Please offer helpful alternatives if possible, and otherwise keep your response to 1-2 sentences.\nOnly use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked.\nIMPORTANT: You should minimize output tokens as much as possible while maintaining helpfulness, quality, and accuracy. Only address the specific query or task at hand, avoiding tangential information unless absolutely critical for completing the request. If you can answer in 1-3 sentences or a short paragraph, please do.\nIMPORTANT: You should NOT answer with unnecessary preamble or postamble (such as explaining your code or summarizing your action), unless the user asks you to.\nIMPORTANT: Keep your responses short, since they will be displayed on a command line interface. You MUST answer concisely with fewer than 4 lines (not including tool use or code generation), unless user asks for detail. Answer the user's question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. You MUST avoid text before/after your response, such as \"The answer is <answer>.\", \"Here is the content of the file...\" or \"Based on the information provided, the answer is...\" or \"Here is what I will do next...\". Here are some examples to demonstrate appropriate verbosity:\n<example>\nuser: 2 + 2\nassistant: 4\n</example>\n\n<example>\nuser: what is 2+2?\nassistant: 4\n</example>\n\n<example>\nuser: is 11 a prime number?\nassistant: Yes\n</example>\n\n<example>\nuser: what command should I run to list files in the current directory?\nassistant: ls\n</example>\n\n<example>\nuser: what command should I run to watch files in the current directory?\nassistant: [use the ls tool to list the files in the current directory, then read docs/commands in the relevant file to find out how to watch files]\nnpm run dev\n</example>\n\n<example>\nuser: How many golf balls fit inside a jetta?\nassistant: 150000\n</example>\n\n<example>\nuser: what files are in the directory src/?\nassistant: [runs ls and sees foo.c, bar.c, baz.c]\nuser: which file contains the implementation of foo?\nassistant: src/foo.c\n</example>\n\n<example>\nuser: write tests for new feature\nassistant: [uses grep and glob search tools to find where similar tests are defined, uses concurrent read file tool use blocks in one tool call to read relevant files at the same time, uses edit file tool to write new tests]\n</example>\n\n# Proactiveness\nYou are allowed to be proactive, but only when the user asks you to do something. You should strive to strike a balance between:\n1. Doing the right thing when asked, including taking actions and follow-up actions\n2. Not surprising the user with actions you take without asking\nFor example, if the user asks you how to approach something, you should do your best to answer their question first, and not immediately jump into taking actions.\n3. Do not add additional code explanation summary unless requested by the user. After working on a file, just stop, rather than providing an explanation of what you did.\n\n# Following conventions\nWhen making changes to files, first understand the file's code conventions. Mimic code style, use existing libraries and utilities, and follow existing patterns.\n- NEVER assume that a given library is available, even if it is well known. Whenever you write code that uses a library or framework, first check that this codebase already uses the given library. For example, you might look at neighboring files, or check the package.json (or cargo.toml, and so on depending on the language).\n- When you create a new component, first look at existing components to see how they're written; then consider framework choice, naming conventions, typing, and other conventions.\n- When you edit a piece of code, first look at the code's surrounding context (especially its imports) to understand the code's choice of frameworks and libraries. Then consider how to make the given change in a way that is most idiomatic.\n- Always follow security best practices. Never introduce code that exposes or logs secrets and keys. Never commit secrets or keys to the repository.\n\n# Code style\n- IMPORTANT: DO NOT ADD ***ANY*** COMMENTS unless asked\n\n# Doing tasks\nThe user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:\n- Use the available search tools to understand the codebase and the user's query. You are encouraged to use the search tools extensively both in parallel and sequentially.\n- Implement the solution using all tools available to you\n- Verify the solution if possible with tests. NEVER assume specific test framework or test script. Check the README or search codebase to determine the testing approach.\n- VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (e.g. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask the user for the command to run and if they supply it, proactively suggest writing it to AGENTS.md so that you will know to run it next time.\nNEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive.\n\n- Tool results and user messages may include <system-reminder> tags. <system-reminder> tags contain useful information and reminders. They are NOT part of the user's provided input or the tool result.\n\n# Tool usage policy\n- When doing file search, prefer to use the Task tool in order to reduce context usage.\n- You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. When making multiple bash tool calls, you MUST send a single message with multiple tools calls to run the calls in parallel. For example, if you need to run \"git status\" and \"git diff\", send a single message with two tool calls to run the calls in parallel.\n\nYou MUST answer concisely with fewer than 4 lines of text (not including tool use or code generation), unless user asks for detail.\n\n${instructionsSection}\n${buildSkillsSection(skills)}\n`\n}\n\n// ============================================================================\n// Dynamic Sections\n// ============================================================================\n\nfunction buildSkillsSection(skills?: SkillMetadata[]): string {\n if (!skills || skills.length === 0) return ''\n\n const listing = skills\n .map((s, i) => `${i + 1}. **${s.id}** - ${s.description}`)\n .join('\\n')\n\n return `\n## AVAILABLE SKILLS\n\nYou can load specialized knowledge using the load_skill tool. Only load a skill when you need its instructions for the current task.\n\n${listing}\n\nTo load a skill, call load_skill with the skill ID. The skill's detailed instructions will be returned as a tool result.\n`\n}\n\n/**\n * Build the \"Available Sub-Agents\" section dynamically from agent definitions.\n */\nexport function buildSubAgentsSection(subAgentDefs: AgentDefinition[]): string {\n if (subAgentDefs.length === 0) return ''\n\n const listing = subAgentDefs\n .map((agent, i) => {\n const tools = agent.metadata.tools.join(', ')\n return `${i + 1}. **${agent.metadata.id}** - ${agent.metadata.description}\n - Has access to: ${tools}`\n })\n .join('\\n\\n')\n\n return `\n## AVAILABLE SUB-AGENTS\n\nYou can call specialized sub-agents for specific tasks using the call_sub_agent tool:\n\n${listing}\n\nTo call a sub-agent, use the call_sub_agent tool with:\n- subAgentType: The ID of the sub-agent\n- prompt: Clear description of what you need\n`\n}\n\n// ============================================================================\n// System Prompt Builders\n// ============================================================================\n\n/**\n * System prompt for top-level agents (planner, builder, custom).\n * Identical for all top-level agents to preserve KV cache.\n * Agent-specific behavior comes from the runtime reminder.\n */\nexport function buildTopLevelSystemPrompt(\n workdir: string,\n customInstructions?: string,\n skills?: SkillMetadata[],\n subAgentDefs?: AgentDefinition[],\n): string {\n const base = buildBasePrompt(workdir, customInstructions, skills)\n const subAgents = subAgentDefs ? buildSubAgentsSection(subAgentDefs) : ''\n return base + subAgents\n}\n\n/**\n * System prompt for sub-agents.\n * Base prompt + agent-specific instructions baked in (no mode switching).\n */\nexport function buildSubAgentSystemPrompt(\n workdir: string,\n agentDef: AgentDefinition,\n skills?: SkillMetadata[],\n): string {\n const base = buildBasePrompt(workdir, undefined, skills)\n return base + '\\n\\n' + agentDef.prompt\n}\n\n/**\n * Build a runtime reminder from an agent definition's prompt body.\n * Used for top-level agents to inject mode-specific behavior via user messages.\n */\nexport function buildAgentReminder(agentDef: AgentDefinition): string {\n return `<system-reminder>\\n${agentDef.prompt}\\n</system-reminder>`\n}\n\n// ============================================================================\n// Utility Prompts\n// ============================================================================\n\nexport const SUMMARY_REQUEST_PROMPT = `Write a 2-3 sentence summary of what the user wants to accomplish. Focus on WHAT and WHY, not HOW. Output only the summary, no preamble.`\n\nexport const BUILDER_KICKOFF_PROMPT = (criteriaCount: number) =>\n `Implement the task and make sure you fulfil the ${criteriaCount} criteria.`\n\nexport const VERIFIER_KICKOFF_PROMPT = 'Verify each criterion marked [NEEDS VERIFICATION]. Read the code, run tests if applicable, then call pass_criterion or fail_criterion for each.'\n\nexport const COMPACTION_PROMPT = `Summarize the conversation history concisely, preserving:\n1. All file modifications made (file paths and what changed)\n2. All errors encountered and how they were resolved\n3. Current progress on each task\n4. Any important decisions or learnings\n\nBe thorough but concise. Output as a structured summary.\n\nIMPORTANT: Do NOT use any tools. Do NOT output XML tags like <tool_call>, <function=>. Only respond with plain text summary.`\n\nexport const FORMAT_CORRECTION_PROMPT = `IMPORTANT: You MUST use the JSON function calling API. Do NOT output XML tags like <tool_call>, <function=>, or <parameter=>. Your previous attempt was stopped because you used the wrong format. Use the proper tool_calls format.`\n\nexport const MAX_FORMAT_RETRIES = 10\n","/**\n * Message stats computation - single source of truth for the formula.\n */\n\nimport type { LLMCallStats, MessageStats, StatsIdentity, ToolMode } from '../../shared/types.js'\nimport type { StreamTiming } from '../llm/streaming.js'\n\nconst roundTo1 = (n: number): number => Math.round(n * 10) / 10\n\nfunction buildCallStats(input: {\n identity: StatsIdentity\n callIndex: number\n timing: StreamTiming\n promptTokens: number\n completionTokens: number\n timestamp?: string\n}): LLMCallStats {\n const { identity, callIndex, timing, promptTokens, completionTokens, timestamp } = input\n return {\n ...identity,\n callIndex,\n promptTokens,\n completionTokens,\n ttft: timing.ttft,\n completionTime: timing.completionTime,\n prefillSpeed: timing.ttft > 0 ? roundTo1(promptTokens / timing.ttft) : 0,\n generationSpeed: timing.completionTime > 0 ? roundTo1(completionTokens / timing.completionTime) : 0,\n totalTime: roundTo1(timing.ttft + timing.completionTime),\n ...(timestamp ? { timestamp } : {}),\n }\n}\n\nexport interface StatsInput {\n identity: StatsIdentity\n mode: ToolMode\n timing: StreamTiming\n usage: { promptTokens: number; completionTokens: number }\n /** Tool execution time in seconds (default: 0) */\n toolTime?: number\n /** Override totalTime instead of computing from timing + toolTime */\n totalTimeOverride?: number\n timestamp?: string\n}\n\n/**\n * Compute message stats from LLM timing and usage data.\n * \n * For single LLM calls: totalTime = ttft + completionTime + toolTime\n * For multi-call flows: pass totalTimeOverride with wall clock time\n */\nexport function computeMessageStats(input: StatsInput): MessageStats {\n const { identity, mode, timing, usage, toolTime = 0, totalTimeOverride, timestamp } = input\n \n const totalTime = totalTimeOverride ?? (timing.ttft + timing.completionTime + toolTime)\n \n return {\n ...identity,\n mode,\n totalTime,\n toolTime,\n prefillTokens: usage.promptTokens,\n prefillSpeed: timing.ttft > 0 ? roundTo1(usage.promptTokens / timing.ttft) : 0,\n generationTokens: usage.completionTokens,\n generationSpeed: timing.completionTime > 0 ? roundTo1(usage.completionTokens / timing.completionTime) : 0,\n llmCalls: [buildCallStats({\n identity,\n callIndex: 1,\n timing,\n promptTokens: usage.promptTokens,\n completionTokens: usage.completionTokens,\n ...(timestamp ? { timestamp } : {}),\n })],\n }\n}\n\n/**\n * Compute stats from aggregated multi-call data (e.g., TurnMetrics).\n * Speeds are computed as averages across all calls.\n */\nexport function computeAggregatedStats(input: {\n identity: StatsIdentity\n mode: ToolMode\n totalPrefillTokens: number\n totalGenTokens: number\n totalPrefillTime: number // sum of ttft across all calls\n totalGenTime: number // sum of completionTime across all calls\n totalToolTime: number // seconds\n totalTime: number // wall clock seconds\n llmCalls?: LLMCallStats[]\n}): MessageStats {\n const { identity, mode, totalPrefillTokens, totalGenTokens, totalPrefillTime, totalGenTime, totalToolTime, totalTime, llmCalls } = input\n \n return {\n ...identity,\n mode,\n totalTime,\n toolTime: totalToolTime,\n prefillTokens: totalPrefillTokens,\n prefillSpeed: totalPrefillTime > 0 ? roundTo1(totalPrefillTokens / totalPrefillTime) : 0,\n generationTokens: totalGenTokens,\n generationSpeed: totalGenTime > 0 ? roundTo1(totalGenTokens / totalGenTime) : 0,\n ...(llmCalls ? { llmCalls } : {}),\n }\n}\n","/**\n * Pure LLM Streaming Generator\n *\n * This module provides pure generator functions that yield TurnEvents.\n * No side effects, no persistence - just transforms LLM streams to events.\n *\n * The orchestrator is responsible for:\n * - Appending events to EventStore\n * - Handling tool execution\n * - Managing session state\n */\n\nimport type { PromptContext, ToolCall, MessageSegment, MessageStats, StatsIdentity, ToolResult, Attachment } from '../../shared/types.js'\nimport type { LLMClientWithModel } from '../llm/client.js'\nimport type { LLMToolDefinition } from '../llm/types.js'\nimport type { StreamTiming } from '../llm/streaming.js'\nimport type { TurnEvent } from '../events/types.js'\nimport { streamWithSegments } from '../llm/streaming.js'\nimport { computeAggregatedStats } from './stats.js'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PureStreamOptions {\n messageId: string\n systemPrompt: string\n llmClient: LLMClientWithModel\n messages: Array<{\n role: 'system' | 'user' | 'assistant' | 'tool'\n content: string\n toolCalls?: ToolCall[]\n toolCallId?: string\n attachments?: Attachment[]\n }>\n tools?: LLMToolDefinition[]\n toolChoice?: 'auto' | 'none' | 'required'\n signal?: AbortSignal | undefined\n disableThinking?: boolean\n}\n\nexport interface PureStreamResult {\n content: string\n thinkingContent?: string\n toolCalls: ToolCall[]\n segments: MessageSegment[]\n usage: { promptTokens: number; completionTokens: number }\n timing: StreamTiming\n aborted: boolean\n xmlFormatError: boolean\n}\n\n// ============================================================================\n// Pure Streaming Generator\n// ============================================================================\n\n/**\n * Pure generator that streams an LLM response and yields TurnEvents.\n *\n * Does NOT:\n * - Create or update messages in database\n * - Emit WebSocket messages\n * - Execute tools\n *\n * DOES:\n * - Yield events for message deltas, thinking, tool preparation\n * - Return the final result with content, tool calls, usage stats\n *\n * @example\n * ```typescript\n * const gen = streamLLMPure(options)\n * for await (const event of gen) {\n * eventStore.append(sessionId, event)\n * }\n * const result = gen.value // Available after iteration completes\n * ```\n */\nexport async function* streamLLMPure(\n options: PureStreamOptions\n): AsyncGenerator<TurnEvent, PureStreamResult> {\n const { messageId, systemPrompt, llmClient, messages, tools, toolChoice, signal, disableThinking } = options\n\n // Build LLM messages\n const llmMessages = [{ role: 'system' as const, content: systemPrompt }, ...messages]\n\n // Start streaming\n const stream = streamWithSegments(llmClient, {\n messages: llmMessages,\n ...(tools && { tools }),\n ...(tools && { toolChoice: toolChoice ?? 'auto' }),\n disableThinking: disableThinking ?? false,\n })\n\n // Track tool call indices we've emitted preparing events for\n const seenToolIndices = new Set<number>()\n const toolNames = new Map<number, string>()\n const toolIds = new Map<number, string>()\n // Accumulate raw JSON arguments for return_value to extract content\n const returnValueArgs = new Map<number, string>()\n\n let result: Awaited<ReturnType<typeof stream.next>>['value'] = null\n let aborted = false\n let xmlFormatError = false\n\n try {\n while (true) {\n if (signal?.aborted) {\n aborted = true\n break\n }\n\n const { value, done } = await stream.next()\n\n if (done) {\n result = value\n break\n }\n\n // Transform streaming events to TurnEvents\n switch (value.type) {\n case 'text_delta':\n yield {\n type: 'message.delta',\n data: { messageId, content: value.content },\n }\n break\n\n case 'thinking_delta':\n yield {\n type: 'message.thinking',\n data: { messageId, content: value.content },\n }\n break\n\n case 'tool_call_delta': {\n // Accumulate tool name and id if provided\n if (value.name) {\n const existingName = toolNames.get(value.index) ?? ''\n toolNames.set(value.index, existingName + value.name)\n }\n if (value.id) {\n toolIds.set(value.index, value.id)\n }\n\n // Emit preparing event on first delta for this index (when we have a complete name)\n const fullName = toolNames.get(value.index)\n if (!seenToolIndices.has(value.index) && fullName) {\n seenToolIndices.add(value.index)\n yield {\n type: 'tool.preparing',\n data: { messageId, index: value.index, name: fullName },\n }\n }\n\n // Stream return_value content fragments as tool.output for live display\n if (fullName === 'return_value' && value.arguments) {\n const toolCallId = toolIds.get(value.index)\n if (toolCallId) {\n const prevRaw = returnValueArgs.get(value.index) ?? ''\n const newRaw = prevRaw + value.arguments\n returnValueArgs.set(value.index, newRaw)\n\n // Extract the \"content\" value from partial JSON: {\"content\":\"...text...\"}\n // Find the start of the content string value\n const contentStart = newRaw.indexOf('\"content\"')\n if (contentStart >= 0) {\n // Find the opening quote of the value (after \"content\":)\n const colonPos = newRaw.indexOf(':', contentStart + 9)\n if (colonPos >= 0) {\n const valueStart = newRaw.indexOf('\"', colonPos + 1)\n if (valueStart >= 0) {\n // Everything after the opening quote (minus trailing `\"}` if complete)\n const prevContent = prevRaw.length > valueStart + 1\n ? prevRaw.slice(valueStart + 1).replace(/\"\\s*\\}\\s*$/, '')\n : ''\n const currentContent = newRaw.slice(valueStart + 1).replace(/\"\\s*\\}\\s*$/, '')\n // Emit only the new delta\n const delta = currentContent.slice(prevContent.length)\n if (delta) {\n // Unescape JSON string escapes\n const unescaped = delta.replace(/\\\\n/g, '\\n').replace(/\\\\t/g, '\\t').replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\')\n yield {\n type: 'tool.output',\n data: { toolCallId, stream: 'stdout' as const, content: unescaped },\n }\n }\n }\n }\n }\n }\n }\n break\n }\n\n case 'xml_tool_abort':\n xmlFormatError = true\n break\n\n case 'error':\n yield {\n type: 'chat.error',\n data: { error: value.error, recoverable: true },\n }\n break\n }\n\n // If XML format error, break out\n if (xmlFormatError) {\n break\n }\n }\n } catch (error) {\n if (error instanceof Error && error.message === 'Aborted') {\n aborted = true\n } else {\n throw error\n }\n }\n\n // Return result (available via generator.value after iteration)\n if (!result) {\n return {\n content: '',\n toolCalls: [],\n segments: [],\n usage: { promptTokens: 0, completionTokens: 0 },\n timing: { ttft: 0, completionTime: 0, tps: 0, prefillTps: 0 },\n aborted,\n xmlFormatError,\n }\n }\n\n const baseResult: PureStreamResult = {\n content: result.content,\n toolCalls: result.toolCalls,\n segments: result.segments,\n usage: {\n promptTokens: result.response.usage.promptTokens,\n completionTokens: result.response.usage.completionTokens,\n },\n timing: result.timing,\n aborted,\n xmlFormatError,\n }\n\n // Only include thinkingContent if it has content\n if (result.thinkingContent) {\n baseResult.thinkingContent = result.thinkingContent\n }\n\n return baseResult\n}\n\n// ============================================================================\n// Turn Metrics (pure, no side effects)\n// ============================================================================\n\n/**\n * Tracks aggregated metrics across a full turn (multiple LLM calls + tool executions).\n * Pure data structure - no side effects.\n */\nexport class TurnMetrics {\n private startTime: number\n private totalPrefillTokens = 0\n private totalPrefillTime = 0 // seconds\n private totalGenTokens = 0\n private totalGenTime = 0 // seconds\n private totalToolTime = 0 // seconds\n private llmCalls: Array<Omit<NonNullable<MessageStats['llmCalls']>[number], 'providerId' | 'providerName' | 'backend' | 'model'>> = []\n\n constructor() {\n this.startTime = performance.now()\n }\n\n /** Add metrics from an LLM call */\n addLLMCall(timing: StreamTiming, promptTokens: number, completionTokens: number): void {\n const callIndex = this.llmCalls.length + 1\n this.totalPrefillTokens += promptTokens\n this.totalPrefillTime += timing.ttft\n this.totalGenTokens += completionTokens\n this.totalGenTime += timing.completionTime\n this.llmCalls = [\n ...this.llmCalls,\n {\n callIndex,\n promptTokens,\n completionTokens,\n ttft: timing.ttft,\n completionTime: timing.completionTime,\n prefillSpeed: timing.ttft > 0 ? Math.round((promptTokens / timing.ttft) * 10) / 10 : 0,\n generationSpeed: timing.completionTime > 0 ? Math.round((completionTokens / timing.completionTime) * 10) / 10 : 0,\n totalTime: Math.round((timing.ttft + timing.completionTime) * 10) / 10,\n timestamp: new Date().toISOString(),\n },\n ]\n }\n\n /** Add tool execution time (in milliseconds) */\n addToolTime(durationMs: number): void {\n this.totalToolTime += durationMs / 1000\n }\n\n /** Build final stats object */\n buildStats(identity: StatsIdentity, mode: string): MessageStats {\n return computeAggregatedStats({\n identity,\n mode,\n totalPrefillTokens: this.totalPrefillTokens,\n totalGenTokens: this.totalGenTokens,\n totalPrefillTime: this.totalPrefillTime,\n totalGenTime: this.totalGenTime,\n totalToolTime: this.totalToolTime,\n totalTime: (performance.now() - this.startTime) / 1000,\n llmCalls: this.llmCalls.map((call) => ({\n ...identity,\n ...call,\n })),\n })\n }\n}\n\n// ============================================================================\n// Event Helpers\n// ============================================================================\n\n/**\n * Create a message.start event\n */\nexport function createMessageStartEvent(\n messageId: string,\n role: 'user' | 'assistant' | 'system',\n content?: string,\n options?: {\n contextWindowId?: string\n subAgentId?: string\n subAgentType?: string\n isSystemGenerated?: boolean\n messageKind?: 'correction' | 'auto-prompt' | 'context-reset' | 'task-completed' | 'workflow-started' | 'command'\n }\n): TurnEvent {\n return {\n type: 'message.start',\n data: {\n messageId,\n role,\n ...(content !== undefined && { content }),\n ...(options?.contextWindowId && { contextWindowId: options.contextWindowId }),\n ...(options?.subAgentId && { subAgentId: options.subAgentId }),\n ...(options?.subAgentType && { subAgentType: options.subAgentType }),\n ...(options?.isSystemGenerated && { isSystemGenerated: options.isSystemGenerated }),\n ...(options?.messageKind && { messageKind: options.messageKind }),\n },\n }\n}\n\n/**\n * Create a message.done event\n */\nexport function createMessageDoneEvent(\n messageId: string,\n options?: {\n stats?: MessageStats\n segments?: MessageSegment[]\n partial?: boolean\n promptContext?: PromptContext\n }\n): TurnEvent {\n return {\n type: 'message.done',\n data: {\n messageId,\n ...(options?.stats && { stats: options.stats }),\n ...(options?.segments && { segments: options.segments }),\n ...(options?.partial && { partial: options.partial }),\n ...(options?.promptContext && { promptContext: options.promptContext }),\n },\n }\n}\n\n/**\n * Create a tool.call event\n */\nexport function createToolCallEvent(messageId: string, toolCall: ToolCall): TurnEvent {\n return {\n type: 'tool.call',\n data: { messageId, toolCall },\n }\n}\n\n/**\n * Create a tool.result event\n */\nexport function createToolResultEvent(messageId: string, toolCallId: string, result: ToolResult): TurnEvent {\n return {\n type: 'tool.result',\n data: { messageId, toolCallId, result },\n }\n}\n\n/**\n * Create a chat.done event\n */\nexport function createChatDoneEvent(\n messageId: string,\n reason: 'complete' | 'stopped' | 'error' | 'waiting_for_user',\n stats?: MessageStats\n): TurnEvent {\n return {\n type: 'chat.done',\n data: {\n messageId,\n reason,\n ...(stats && { stats }),\n },\n }\n}\n\n/**\n * Create a format.retry event\n */\nexport function createFormatRetryEvent(attempt: number, maxAttempts: number): TurnEvent {\n return {\n type: 'format.retry',\n data: { attempt, maxAttempts },\n }\n}\n\n// ============================================================================\n// Consumer Helper\n// ============================================================================\n\n/**\n * Consume a pure stream generator, yielding events and returning the final result.\n * This properly extracts the return value from the async generator.\n */\nexport async function consumeStreamGenerator(\n gen: AsyncGenerator<TurnEvent, PureStreamResult>,\n onEvent: (event: TurnEvent) => void\n): Promise<PureStreamResult> {\n let result: IteratorResult<TurnEvent, PureStreamResult>\n\n while (true) {\n result = await gen.next()\n if (result.done) {\n return result.value\n }\n onEvent(result.value)\n }\n}\n","/**\n * Tool streaming utilities\n * \n * Handles conversion of tool onProgress callbacks to chat.tool_output events.\n * Used for streaming shell command output to the client in real-time.\n */\n\nimport type { ServerMessage } from '../../shared/protocol.js'\nimport { createChatToolOutputMessage } from '../ws/protocol.js'\n\nexport interface ParsedProgress {\n stream: 'stdout' | 'stderr'\n content: string\n}\n\n/**\n * Parse a progress message from the shell tool.\n * Shell tool emits messages in format: \"[stdout] content\" or \"[stderr] content\"\n * \n * @returns Parsed progress or null if format doesn't match\n */\nexport function parseProgressMessage(message: string): ParsedProgress | null {\n const match = message.match(/^\\[(stdout|stderr)\\] (.*)$/s)\n if (!match) return null\n \n return {\n stream: match[1] as 'stdout' | 'stderr',\n content: match[2]!,\n }\n}\n\n/**\n * Create an onProgress handler that converts progress messages to chat.tool_output events.\n * \n * @param messageId - The assistant message ID this tool call belongs to\n * @param callId - The tool call ID\n * @param onMessage - Callback to send server messages to the client\n * @returns Progress handler function to pass to tool context\n */\nexport function createToolProgressHandler(\n messageId: string,\n callId: string,\n onMessage: (msg: ServerMessage) => void\n): (message: string) => void {\n return (message: string) => {\n const parsed = parseProgressMessage(message)\n if (!parsed) return\n \n onMessage(createChatToolOutputMessage(messageId, callId, parsed.content, parsed.stream))\n }\n}\n","/**\n * EventStore - Single source of truth for session events\n *\n * Responsibilities:\n * - Persist events to SQLite with per-session sequence numbers\n * - Provide event retrieval and replay\n * - Manage live subscriptions with async iterators\n * - Handle snapshots for efficient session loading\n *\n * Design:\n * - All events are append-only and immutable\n * - Sequence numbers are per-session (1, 2, 3...)\n * - Subscribers receive events in real-time via async iterators\n * - Snapshots enable efficient replay (skip to snapshot, replay from there)\n */\n\nimport type Database from 'better-sqlite3'\nimport type { TurnEvent, StoredEvent, SessionSnapshot } from './types.js'\nimport { logger } from '../utils/logger.js'\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface Subscriber {\n sessionId: string\n callback: (event: StoredEvent) => void\n close: () => void // Function to close the iterator\n closed: boolean\n}\n\ninterface EventRow {\n id: number\n session_id: string\n seq: number\n timestamp: number\n event_type: string\n payload: string\n}\n\n// ============================================================================\n// EventStore Implementation\n// ============================================================================\n\nexport class EventStore {\n private db: Database.Database\n private subscribers: Map<string, Set<Subscriber>> = new Map()\n\n constructor(db: Database.Database) {\n this.db = db\n this.initSchema()\n }\n\n // --------------------------------------------------------------------------\n // Schema\n // --------------------------------------------------------------------------\n\n private initSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n timestamp INTEGER NOT NULL,\n event_type TEXT NOT NULL,\n payload TEXT NOT NULL,\n UNIQUE(session_id, seq)\n )\n `)\n\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_events_session_seq \n ON events(session_id, seq)\n `)\n\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_events_session_type \n ON events(session_id, event_type)\n `)\n }\n\n // --------------------------------------------------------------------------\n // Append\n // --------------------------------------------------------------------------\n\n /**\n * Append a single event to a session\n */\n append(sessionId: string, event: TurnEvent): StoredEvent {\n const timestamp = Date.now()\n const seq = this.getNextSeq(sessionId)\n const payload = JSON.stringify(event.data)\n\n this.db\n .prepare(\n `INSERT INTO events (session_id, seq, timestamp, event_type, payload)\n VALUES (?, ?, ?, ?, ?)`\n )\n .run(sessionId, seq, timestamp, event.type, payload)\n\n const stored: StoredEvent = {\n seq,\n timestamp,\n sessionId,\n type: event.type,\n data: event.data,\n }\n\n this.notifySubscribers(sessionId, stored)\n\n return stored\n }\n\n /**\n * Append multiple events atomically\n */\n appendBatch(sessionId: string, events: TurnEvent[]): StoredEvent[] {\n if (events.length === 0) return []\n\n const timestamp = Date.now()\n let seq = this.getNextSeq(sessionId)\n const results: StoredEvent[] = []\n\n const insert = this.db.prepare(\n `INSERT INTO events (session_id, seq, timestamp, event_type, payload)\n VALUES (?, ?, ?, ?, ?)`\n )\n\n const transaction = this.db.transaction(() => {\n for (const event of events) {\n const payload = JSON.stringify(event.data)\n insert.run(sessionId, seq, timestamp, event.type, payload)\n\n const stored: StoredEvent = {\n seq,\n timestamp,\n sessionId,\n type: event.type,\n data: event.data,\n }\n results.push(stored)\n seq++\n }\n })\n\n transaction()\n\n // Notify after transaction commits\n for (const stored of results) {\n this.notifySubscribers(sessionId, stored)\n }\n\n return results\n }\n\n private getNextSeq(sessionId: string): number {\n const row = this.db\n .prepare(`SELECT MAX(seq) as max_seq FROM events WHERE session_id = ?`)\n .get(sessionId) as { max_seq: number | null } | undefined\n\n return (row?.max_seq ?? 0) + 1\n }\n\n // --------------------------------------------------------------------------\n // Retrieval\n // --------------------------------------------------------------------------\n\n /**\n * Get all events for a session, optionally starting from a specific seq\n */\n getEvents(sessionId: string, fromSeq?: number): StoredEvent[] {\n const query =\n fromSeq !== undefined\n ? `SELECT * FROM events WHERE session_id = ? AND seq >= ? ORDER BY seq`\n : `SELECT * FROM events WHERE session_id = ? ORDER BY seq`\n\n const rows =\n fromSeq !== undefined\n ? (this.db.prepare(query).all(sessionId, fromSeq) as EventRow[])\n : (this.db.prepare(query).all(sessionId) as EventRow[])\n\n return rows.map((row) => this.rowToStoredEvent(row))\n }\n\n /**\n * Get the latest sequence number for a session\n */\n getLatestSeq(sessionId: string): number | undefined {\n const row = this.db\n .prepare(`SELECT MAX(seq) as max_seq FROM events WHERE session_id = ?`)\n .get(sessionId) as { max_seq: number | null } | undefined\n\n return row?.max_seq ?? undefined\n }\n\n /**\n * Get the latest snapshot event for a session\n */\n getLatestSnapshot(sessionId: string): StoredEvent<Extract<TurnEvent, { type: 'turn.snapshot' }>> | undefined {\n const row = this.db\n .prepare(\n `SELECT * FROM events \n WHERE session_id = ? AND event_type = 'turn.snapshot' \n ORDER BY seq DESC LIMIT 1`\n )\n .get(sessionId) as EventRow | undefined\n\n if (!row) return undefined\n\n return this.rowToStoredEvent(row) as StoredEvent<Extract<TurnEvent, { type: 'turn.snapshot' }>>\n }\n\n /**\n * Get the latest snapshot and all events since it\n * This is the primary method for loading a session efficiently\n */\n getEventsSinceSnapshot(sessionId: string): { snapshot: SessionSnapshot | undefined; events: StoredEvent[] } {\n const snapshotEvent = this.getLatestSnapshot(sessionId)\n\n if (!snapshotEvent) {\n // No snapshot, return all events\n return {\n snapshot: undefined,\n events: this.getEvents(sessionId),\n }\n }\n\n // Get events AFTER the snapshot (seq > snapshotEvent.seq)\n const events = this.getEvents(sessionId, snapshotEvent.seq + 1)\n\n return {\n snapshot: snapshotEvent.data,\n events,\n }\n }\n\n private rowToStoredEvent(row: EventRow): StoredEvent {\n return {\n seq: row.seq,\n timestamp: row.timestamp,\n sessionId: row.session_id,\n type: row.event_type as TurnEvent['type'],\n data: JSON.parse(row.payload),\n }\n }\n\n // --------------------------------------------------------------------------\n // Subscriptions\n // --------------------------------------------------------------------------\n\n /**\n * Subscribe to events for a session\n * Optionally replay events from a specific seq\n *\n * Returns an async iterator that yields events and an unsubscribe function\n */\n subscribe(\n sessionId: string,\n fromSeq?: number\n ): { iterator: AsyncIterableIterator<StoredEvent>; unsubscribe: () => void } {\n const queue: StoredEvent[] = []\n let resolveNext: ((value: IteratorResult<StoredEvent>) => void) | null = null\n let closed = false\n\n const closeIterator = () => {\n closed = true\n if (resolveNext) {\n resolveNext({ value: undefined, done: true })\n resolveNext = null\n }\n }\n\n const subscriber: Subscriber = {\n sessionId,\n callback: (event: StoredEvent) => {\n if (closed) return\n\n if (resolveNext) {\n resolveNext({ value: event, done: false })\n resolveNext = null\n } else {\n queue.push(event)\n }\n },\n close: closeIterator,\n closed: false,\n }\n\n // Add to subscribers\n let sessionSubs = this.subscribers.get(sessionId)\n if (!sessionSubs) {\n sessionSubs = new Set()\n this.subscribers.set(sessionId, sessionSubs)\n }\n sessionSubs.add(subscriber)\n\n // Replay events if fromSeq is provided\n if (fromSeq !== undefined) {\n const replayEvents = this.getEvents(sessionId, fromSeq)\n queue.push(...replayEvents)\n }\n\n const iterator: AsyncIterableIterator<StoredEvent> = {\n [Symbol.asyncIterator]() {\n return this\n },\n async next(): Promise<IteratorResult<StoredEvent>> {\n if (closed) {\n return { value: undefined, done: true }\n }\n\n const queued = queue.shift()\n if (queued) {\n return { value: queued, done: false }\n }\n\n // Wait for next event\n return new Promise((resolve) => {\n resolveNext = resolve\n })\n },\n async return(): Promise<IteratorResult<StoredEvent>> {\n closed = true\n subscriber.closed = true\n return { value: undefined, done: true }\n },\n }\n\n const unsubscribe = () => {\n subscriber.closed = true\n closeIterator()\n sessionSubs?.delete(subscriber)\n }\n\n return { iterator, unsubscribe }\n }\n\n private notifySubscribers(sessionId: string, event: StoredEvent): void {\n const sessionSubs = this.subscribers.get(sessionId)\n if (!sessionSubs) return\n\n for (const subscriber of sessionSubs) {\n if (!subscriber.closed) {\n subscriber.callback(event)\n }\n }\n }\n\n // --------------------------------------------------------------------------\n // Cleanup\n // --------------------------------------------------------------------------\n\n /**\n * Delete all events for a session\n */\n deleteSession(sessionId: string): void {\n this.db.prepare(`DELETE FROM events WHERE session_id = ?`).run(sessionId)\n\n // Close all subscribers for this session\n const sessionSubs = this.subscribers.get(sessionId)\n if (sessionSubs) {\n for (const subscriber of sessionSubs) {\n subscriber.closed = true\n subscriber.close() // Resolve any pending next() calls\n }\n this.subscribers.delete(sessionId)\n }\n }\n\n /**\n * Delete all events up to (and including) a given sequence number.\n * This is used to clean up events that are now contained in a snapshot.\n * \n * @param sessionId - The session ID\n * @param upToSeq - The sequence number to delete up to (inclusive)\n * @returns The number of events deleted\n */\n deleteEventsUpToSeq(sessionId: string, upToSeq: number): number {\n const result = this.db\n .prepare(`DELETE FROM events WHERE session_id = ? AND seq <= ?`)\n .run(sessionId, upToSeq)\n\n return result.changes as number\n }\n\n /**\n * Clean up old events, keeping only:\n * - session.initialized event (seq 1)\n * - All snapshot events\n * - State-changing events (criteria.set, criterion.updated, mode.changed, phase.changed, context.state, etc.)\n * - Events after the latest snapshot (current window)\n * \n * This is the recommended cleanup method that preserves all snapshots and state.\n * \n * @param sessionId - The session ID\n * @returns The number of events deleted\n */\n cleanupOldEvents(sessionId: string): number {\n // Get the latest snapshot sequence\n const latestSnapshotSeq = this.getLatestSnapshotSeq(sessionId)\n \n if (latestSnapshotSeq === 0) {\n // No snapshots yet, nothing to clean up\n return 0\n }\n\n // Delete all events before the latest snapshot, except:\n // - seq 1 (session.initialized)\n // - State-changing events that define session state\n // Old snapshots are also deleted — the latest snapshot is always a\n // superset of all previous ones (messages are cumulative).\n const result = this.db\n .prepare(`\n DELETE FROM events\n WHERE session_id = ? AND seq > 1 AND seq < ?\n AND event_type NOT IN (\n 'criteria.set',\n 'criterion.updated',\n 'mode.changed',\n 'phase.changed',\n 'todo.updated',\n 'context.state'\n )\n `)\n .run(sessionId, latestSnapshotSeq)\n\n return result.changes as number\n }\n\n /**\n * Get the latest snapshot sequence number for a session\n * @returns The sequence number of the latest snapshot, or 0 if none\n */\n getLatestSnapshotSeq(sessionId: string): number {\n const row = this.db\n .prepare(`\n SELECT seq FROM events \n WHERE session_id = ? AND event_type = 'turn.snapshot' \n ORDER BY seq DESC LIMIT 1\n `)\n .get(sessionId) as { seq: number } | undefined\n\n return row?.seq ?? 0\n }\n\n /**\n * One-time storage optimization: delete old snapshots and strip\n * promptContext.messages from remaining snapshots across all sessions.\n * Safe to run multiple times (idempotent).\n */\n optimizeStorage(): { deletedSnapshots: number; strippedSnapshots: number } {\n let deletedSnapshots = 0\n let strippedSnapshots = 0\n\n // 1. Delete all non-latest snapshots per session\n const deleteResult = this.db\n .prepare(`\n DELETE FROM events\n WHERE event_type = 'turn.snapshot'\n AND id NOT IN (\n SELECT e1.id FROM events e1\n WHERE e1.event_type = 'turn.snapshot'\n AND e1.seq = (\n SELECT MAX(e2.seq) FROM events e2\n WHERE e2.session_id = e1.session_id\n AND e2.event_type = 'turn.snapshot'\n )\n )\n `)\n .run()\n deletedSnapshots = deleteResult.changes as number\n\n // 2. Strip promptContext.messages from all but the last assistant message\n // in each remaining snapshot\n const snapshots = this.db\n .prepare(`SELECT id, payload FROM events WHERE event_type = 'turn.snapshot'`)\n .all() as Array<{ id: number; payload: string }>\n\n const updateStmt = this.db.prepare(`UPDATE events SET payload = ? WHERE id = ?`)\n\n for (const row of snapshots) {\n const data = JSON.parse(row.payload)\n const messages = data.messages as Array<{ role: string; promptContext?: { messages?: unknown[] } }>\n if (!messages) continue\n\n // Find last assistant message with promptContext\n let lastAssistantIdx = -1\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (!msg) continue\n if (msg.role === 'assistant' && msg.promptContext) {\n lastAssistantIdx = i\n break\n }\n }\n\n let changed = false\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n if (!msg) continue\n const pc = msg.promptContext\n if (pc?.messages && pc.messages.length > 0 && i !== lastAssistantIdx) {\n pc.messages = []\n changed = true\n }\n }\n\n if (changed) {\n updateStmt.run(JSON.stringify(data), row.id)\n strippedSnapshots++\n }\n }\n\n return { deletedSnapshots, strippedSnapshots }\n }\n}\n\n// ============================================================================\n// Singleton instance (will be initialized with the main database)\n// ============================================================================\n\nlet eventStoreInstance: EventStore | null = null\n\nexport function initEventStore(db: Database.Database): EventStore {\n eventStoreInstance = new EventStore(db)\n\n // Reset stale running states from previous server runs.\n // Sessions cannot actually be running when server starts - any session\n // that shows as running was interrupted (crash, restart, etc.).\n resetStaleRunningSessions(eventStoreInstance, db)\n\n // Optimize storage: remove old snapshots and strip bloated promptContext data.\n // Idempotent — fast no-op on already-optimized databases.\n const result = eventStoreInstance.optimizeStorage()\n if (result.deletedSnapshots > 0 || result.strippedSnapshots > 0) {\n logger.info('Storage optimized', result)\n }\n\n return eventStoreInstance\n}\n\n/**\n * Find sessions that would fold to isRunning=true and emit running.changed=false\n * to clean up stale running states from server crashes/restarts.\n */\nfunction resetStaleRunningSessions(eventStore: EventStore, db: Database.Database): void {\n // Get all session IDs\n const sessions = db.prepare(`SELECT id FROM sessions`).all() as { id: string }[]\n\n let resetCount = 0\n for (const { id: sessionId } of sessions) {\n // Get the last running.changed event for this session\n const lastRunningEvent = db.prepare(`\n SELECT payload FROM events \n WHERE session_id = ? AND event_type = 'running.changed'\n ORDER BY seq DESC LIMIT 1\n `).get(sessionId) as { payload: string } | undefined\n\n if (lastRunningEvent) {\n const data = JSON.parse(lastRunningEvent.payload) as { isRunning: boolean }\n if (data.isRunning === true) {\n // This session was left in running state - emit false to reset\n eventStore.append(sessionId, {\n type: 'running.changed',\n data: { isRunning: false },\n })\n resetCount++\n }\n }\n }\n\n if (resetCount > 0) {\n logger.info('EventStore reset stale running sessions', { count: resetCount })\n }\n}\n\nexport function getEventStore(): EventStore {\n if (!eventStoreInstance) {\n throw new Error('EventStore not initialized. Call initEventStore first.')\n }\n return eventStoreInstance\n}\n","/**\n * Event Folding Functions\n *\n * Pure functions that reconstruct state from events.\n * All state is derived from events - no external data sources.\n */\n\nimport type {\n Message,\n Criterion,\n CriterionStatus,\n SessionMode,\n SessionPhase,\n ContextState,\n Todo,\n ToolCall,\n Attachment,\n} from '../../shared/types.js'\nimport type {\n StoredEvent,\n TurnEvent,\n SessionSnapshot,\n SnapshotMessage,\n ToolCallWithResult,\n ReadFileEntry,\n} from './types.js'\nimport { getRuntimeConfig } from '../runtime-config.js'\n\nfunction cloneMessage(message: Message): Message {\n return {\n ...message,\n ...(message.attachments ? { attachments: [...message.attachments] } : {}),\n ...(message.toolCalls ? {\n toolCalls: message.toolCalls.map((toolCall) => ({\n ...toolCall,\n ...(toolCall.streamingOutput ? { streamingOutput: [...toolCall.streamingOutput] } : {}),\n ...(toolCall.result ? { result: { ...toolCall.result } } : {}),\n })),\n } : {}),\n ...(message.segments ? { segments: [...message.segments] } : {}),\n ...(message.preparingToolCalls ? { preparingToolCalls: [...message.preparingToolCalls] } : {}),\n }\n}\n\nfunction snapshotMessageToMessage(message: SnapshotMessage): Message {\n return cloneMessage({\n id: message.id,\n role: message.role,\n content: message.content,\n timestamp: new Date(message.timestamp).toISOString(),\n ...(message.tokenCount !== undefined && { tokenCount: message.tokenCount }),\n ...(message.isStreaming !== undefined && { isStreaming: message.isStreaming }),\n ...(message.contextWindowId !== undefined && { contextWindowId: message.contextWindowId }),\n ...(message.subAgentId !== undefined && { subAgentId: message.subAgentId }),\n ...(message.subAgentType !== undefined && { subAgentType: message.subAgentType }),\n ...(message.isSystemGenerated !== undefined && { isSystemGenerated: message.isSystemGenerated }),\n ...(message.messageKind !== undefined && { messageKind: message.messageKind }),\n ...(message.isCompactionSummary !== undefined && { isCompactionSummary: message.isCompactionSummary }),\n ...(message.attachments !== undefined && { attachments: message.attachments }),\n ...(message.thinkingContent !== undefined && { thinkingContent: message.thinkingContent }),\n ...(message.toolCalls !== undefined && { toolCalls: message.toolCalls }),\n ...(message.segments !== undefined && { segments: message.segments }),\n ...(message.stats !== undefined && { stats: message.stats }),\n ...(message.partial !== undefined && { partial: message.partial }),\n ...(message.promptContext !== undefined && { promptContext: message.promptContext }),\n })\n}\n\nfunction shouldIncludeContextMessage(\n message: Pick<SnapshotMessage, 'role' | 'contextWindowId' | 'subAgentType'>,\n windowId?: string,\n options?: ContextMessageBuildOptions,\n): boolean {\n const includeVerifier = options?.includeVerifier ?? true\n\n return message.role !== 'system'\n && (windowId === undefined || message.contextWindowId === windowId)\n && (includeVerifier || message.subAgentType !== 'verifier')\n}\n\nfunction appendSnapshotMessageContext(\n result: ContextMessage[],\n message: SnapshotMessage,\n): void {\n const contextMsg: ContextMessage = {\n role: message.role as 'user' | 'assistant',\n content: message.content,\n }\n\n if (message.toolCalls && message.toolCalls.length > 0) {\n contextMsg.toolCalls = message.toolCalls.map((toolCall) => ({\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n }))\n }\n\n if (message.attachments !== undefined) {\n contextMsg.attachments = message.attachments\n }\n\n result.push(contextMsg)\n\n if (!message.toolCalls) {\n return\n }\n\n for (const toolCall of message.toolCalls) {\n if (!toolCall.result) {\n continue\n }\n\n result.push({\n role: 'tool',\n content: toolCall.result.success\n ? (toolCall.result.output ?? 'Success')\n : `Error: ${toolCall.result.error}`,\n toolCallId: toolCall.id,\n })\n }\n}\n\nfunction applyStoredMessageEvents(initialMessages: Message[], events: StoredEvent[]): Message[] {\n const messages = new Map(initialMessages.map((message) => [message.id, cloneMessage(message)]))\n\n for (const event of events) {\n switch (event.type) {\n case 'message.start': {\n const data = event.data as Extract<TurnEvent, { type: 'message.start' }>['data']\n const isUserOrSystem = data.role === 'user' || data.role === 'system'\n messages.set(data.messageId, {\n id: data.messageId,\n role: data.role,\n content: data.content ?? '',\n timestamp: new Date(event.timestamp).toISOString(),\n tokenCount: data.tokenCount ?? 0,\n isStreaming: !isUserOrSystem,\n ...(data.contextWindowId !== undefined && { contextWindowId: data.contextWindowId }),\n ...(data.subAgentId !== undefined && { subAgentId: data.subAgentId }),\n ...(data.subAgentType !== undefined && { subAgentType: data.subAgentType }),\n ...(data.isSystemGenerated !== undefined && { isSystemGenerated: data.isSystemGenerated }),\n ...(data.messageKind !== undefined && { messageKind: data.messageKind }),\n ...(data.isCompactionSummary !== undefined && { isCompactionSummary: data.isCompactionSummary }),\n ...(data.attachments !== undefined && { attachments: data.attachments }),\n })\n break\n }\n case 'message.delta': {\n const data = event.data as Extract<TurnEvent, { type: 'message.delta' }>['data']\n const msg = messages.get(data.messageId)\n if (msg) {\n msg.content += data.content\n }\n break\n }\n case 'message.thinking': {\n const data = event.data as Extract<TurnEvent, { type: 'message.thinking' }>['data']\n const msg = messages.get(data.messageId)\n if (msg) {\n msg.thinkingContent = (msg.thinkingContent ?? '') + data.content\n }\n break\n }\n case 'message.done': {\n const data = event.data as Extract<TurnEvent, { type: 'message.done' }>['data']\n const msg = messages.get(data.messageId)\n if (msg) {\n msg.isStreaming = false\n if (data.stats) msg.stats = data.stats\n if (data.segments) msg.segments = data.segments\n if (data.partial) msg.partial = data.partial\n if (data.promptContext) msg.promptContext = data.promptContext\n if (data.tokenCount !== undefined) msg.tokenCount = data.tokenCount\n }\n break\n }\n case 'tool.call': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.call' }>['data']\n const msg = messages.get(data.messageId)\n if (msg) {\n const existingToolCalls = msg.toolCalls ?? []\n msg.toolCalls = [...existingToolCalls, data.toolCall]\n }\n break\n }\n case 'tool.result': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.result' }>['data']\n const msg = messages.get(data.messageId)\n if (msg?.toolCalls) {\n const toolCall = msg.toolCalls.find((tc) => tc.id === data.toolCallId)\n if (toolCall) {\n toolCall.result = data.result\n }\n }\n break\n }\n case 'session.initialized':\n case 'turn.snapshot':\n case 'phase.changed':\n case 'mode.changed':\n case 'running.changed':\n case 'criteria.set':\n case 'criterion.updated':\n case 'context.state':\n case 'context.compacted':\n case 'file.read':\n case 'todo.updated':\n case 'chat.done':\n case 'chat.error':\n case 'format.retry':\n case 'tool.preparing':\n case 'tool.output':\n break\n }\n }\n\n return Array.from(messages.values())\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ContextMessage {\n role: 'user' | 'assistant' | 'tool'\n content: string\n toolCalls?: Array<{ id: string; name: string; arguments: Record<string, unknown> }>\n toolCallId?: string\n attachments?: Attachment[]\n}\n\nexport interface ContextMessageBuildOptions {\n includeVerifier?: boolean\n}\n\ntype EventLike = Pick<StoredEvent, 'type' | 'data'> & Partial<Pick<StoredEvent, 'timestamp'>>\n\nfunction cloneSnapshotMessage(message: SnapshotMessage): SnapshotMessage {\n return {\n ...message,\n ...(message.attachments ? { attachments: [...message.attachments] } : {}),\n ...(message.toolCalls ? {\n toolCalls: message.toolCalls.map((toolCall) => ({\n ...toolCall,\n ...(toolCall.streamingOutput ? { streamingOutput: [...toolCall.streamingOutput] } : {}),\n ...(toolCall.result ? { result: { ...toolCall.result } } : {}),\n })),\n } : {}),\n ...(message.segments ? { segments: [...message.segments] } : {}),\n }\n}\n\nfunction applyTurnEventsToSnapshotMessages(\n initialMessages: SnapshotMessage[],\n events: EventLike[]\n): SnapshotMessage[] {\n const messages = new Map(initialMessages.map((message) => [message.id, cloneSnapshotMessage(message)]))\n\n for (const event of events) {\n switch (event.type) {\n case 'message.start': {\n const data = event.data as Extract<TurnEvent, { type: 'message.start' }>['data']\n const msg: SnapshotMessage = {\n id: data.messageId,\n role: data.role,\n content: data.content ?? '',\n timestamp: typeof event.timestamp === 'number' ? event.timestamp : Date.now(),\n isStreaming: true,\n }\n if (data.tokenCount !== undefined) msg.tokenCount = data.tokenCount\n if (data.contextWindowId !== undefined) msg.contextWindowId = data.contextWindowId\n if (data.subAgentId !== undefined) msg.subAgentId = data.subAgentId\n if (data.subAgentType !== undefined) msg.subAgentType = data.subAgentType\n if (data.isSystemGenerated !== undefined) msg.isSystemGenerated = data.isSystemGenerated\n if (data.messageKind !== undefined) msg.messageKind = data.messageKind\n if (data.isCompactionSummary !== undefined) msg.isCompactionSummary = data.isCompactionSummary\n if (data.attachments !== undefined) msg.attachments = data.attachments\n messages.set(data.messageId, msg)\n break\n }\n case 'message.delta': {\n const data = event.data as Extract<TurnEvent, { type: 'message.delta' }>['data']\n const msg = messages.get(data.messageId)\n if (msg) {\n msg.content += data.content\n }\n break\n }\n case 'message.thinking': {\n const data = event.data as Extract<TurnEvent, { type: 'message.thinking' }>['data']\n const msg = messages.get(data.messageId)\n if (msg) {\n msg.thinkingContent = (msg.thinkingContent ?? '') + data.content\n }\n break\n }\n case 'message.done': {\n const data = event.data as Extract<TurnEvent, { type: 'message.done' }>['data']\n const msg = messages.get(data.messageId)\n if (msg) {\n msg.isStreaming = false\n if (data.stats) msg.stats = data.stats\n if (data.segments) msg.segments = data.segments\n if (data.partial) msg.partial = data.partial\n if (data.promptContext) msg.promptContext = data.promptContext\n if (data.tokenCount !== undefined) msg.tokenCount = data.tokenCount\n }\n break\n }\n case 'tool.call': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.call' }>['data']\n const msg = messages.get(data.messageId)\n if (msg) {\n const toolCalls = msg.toolCalls ?? []\n msg.toolCalls = [...toolCalls, data.toolCall as ToolCallWithResult]\n }\n break\n }\n case 'tool.result': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.result' }>['data']\n const msg = messages.get(data.messageId)\n if (msg?.toolCalls) {\n const toolCall = msg.toolCalls.find((tc) => tc.id === data.toolCallId)\n if (toolCall) {\n toolCall.result = data.result\n }\n }\n break\n }\n }\n }\n\n return Array.from(messages.values())\n}\n\n/**\n * Full session state derived entirely from events\n */\nexport interface FoldedSessionState {\n mode: SessionMode\n phase: SessionPhase\n isRunning: boolean\n messages: SnapshotMessage[]\n criteria: Criterion[]\n todos: Todo[]\n contextState: ContextState\n currentContextWindowId: string\n readFiles: ReadFileEntry[]\n}\n\n// ============================================================================\n// Message Folding\n// ============================================================================\n\n/**\n * Build Message[] from stored events (for backward compatibility with shared types)\n * \n * If a snapshot exists, messages are extracted from it since individual message events\n * may have been deleted to save space.\n */\nexport function buildMessagesFromStoredEvents(events: StoredEvent[]): Message[] {\n // Check if there's a snapshot - if so, use the latest one and replay newer events\n const snapshotEvent = [...events].reverse().find((event) => event.type === 'turn.snapshot')\n if (snapshotEvent) {\n const snapshot = snapshotEvent.data as import('./types.js').SessionSnapshot\n const snapshotMessages = snapshot.messages.map(snapshotMessageToMessage)\n const laterEvents = events.filter((event) => event.seq > snapshotEvent.seq)\n return applyStoredMessageEvents(snapshotMessages, laterEvents)\n }\n\n // Fallback: build from individual message events (for sessions without snapshots)\n return applyStoredMessageEvents([], events)\n}\n\n/**\n * Build context messages for LLM from stored events.\n * When windowId is provided, only messages in that context window are included.\n * \n * If events are missing (deleted after snapshot), this function will not find them.\n * Callers should ensure they have access to the latest snapshot for complete message history.\n */\nexport function buildContextMessagesFromStoredEvents(\n events: StoredEvent[],\n windowId?: string,\n options?: ContextMessageBuildOptions,\n): ContextMessage[] {\n const includeVerifier = options?.includeVerifier ?? true\n const messages: Array<ContextMessage & { id: string }> = []\n const messageMap = new Map<string, ContextMessage & { id: string }>()\n\n for (const event of events) {\n switch (event.type) {\n case 'message.start': {\n const data = event.data as Extract<TurnEvent, { type: 'message.start' }>['data']\n if (\n data.role !== 'system'\n && (windowId === undefined || data.contextWindowId === windowId)\n && (includeVerifier || data.subAgentType !== 'verifier')\n ) {\n const message: ContextMessage & { id: string } = {\n id: data.messageId,\n role: data.role as 'user' | 'assistant',\n content: data.content ?? '',\n ...(data.attachments !== undefined && { attachments: data.attachments }),\n }\n messageMap.set(data.messageId, message)\n messages.push(message)\n }\n break\n }\n case 'message.delta': {\n const data = event.data as Extract<TurnEvent, { type: 'message.delta' }>['data']\n const msg = messageMap.get(data.messageId)\n if (msg) {\n msg.content += data.content\n }\n break\n }\n case 'tool.call': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.call' }>['data']\n const msg = messageMap.get(data.messageId)\n if (msg) {\n if (!msg.toolCalls) msg.toolCalls = []\n msg.toolCalls.push(data.toolCall)\n }\n break\n }\n case 'tool.result': {\n const data = event.data as Extract<TurnEvent, { type: 'tool.result' }>['data']\n if (messageMap.has(data.messageId)) {\n messages.push({\n id: `tool-${data.toolCallId}`,\n role: 'tool',\n content: data.result.success \n ? (data.result.output ?? 'Success')\n : data.result.output \n ? `${data.result.output}\\n\\nError: ${data.result.error}`\n : `Error: ${data.result.error}`,\n toolCallId: data.toolCallId,\n })\n }\n break\n }\n }\n }\n\n return messages.map(({ id: _id, ...message }) => message)\n}\n\nexport function buildContextMessagesFromEventHistory(\n events: StoredEvent[],\n windowId?: string,\n options?: ContextMessageBuildOptions,\n): ContextMessage[] {\n const snapshotEvent = [...events].reverse().find((event) => event.type === 'turn.snapshot')\n if (!snapshotEvent) {\n return buildContextMessagesFromStoredEvents(events, windowId, options)\n }\n\n const snapshot = snapshotEvent.data as SessionSnapshot\n const snapshotMessages = snapshot.messages.reduce<ContextMessage[]>((result, message) => {\n if (!shouldIncludeContextMessage(message, windowId, options)) {\n return result\n }\n\n appendSnapshotMessageContext(result, message)\n return result\n }, [])\n const laterEvents = events.filter((event) => event.seq > snapshotEvent.seq)\n\n return [\n ...snapshotMessages,\n ...buildContextMessagesFromStoredEvents(laterEvents, windowId, options),\n ]\n}\n\n/**\n * Fold events into SnapshotMessage[] for snapshots\n */\nexport function foldTurnEventsToSnapshotMessages(events: EventLike[]): SnapshotMessage[] {\n return applyTurnEventsToSnapshotMessages([], events)\n}\n\n// ============================================================================\n// Criteria Folding\n// ============================================================================\n\n/**\n * Fold criteria state from events\n */\nexport function foldCriteria(events: EventLike[]): Criterion[] {\n let criteria: Criterion[] = []\n\n for (const event of events) {\n switch (event.type) {\n case 'criteria.set': {\n const data = event.data as Extract<TurnEvent, { type: 'criteria.set' }>['data']\n criteria = data.criteria\n break\n }\n case 'criterion.updated': {\n const data = event.data as Extract<TurnEvent, { type: 'criterion.updated' }>['data']\n criteria = criteria.map((c) =>\n c.id === data.criterionId ? { ...c, status: data.status } : c\n )\n break\n }\n }\n }\n\n return criteria\n}\n\n// ============================================================================\n// Todos Folding\n// ============================================================================\n\n/**\n * Fold todos state from events\n */\nexport function foldTodos(events: EventLike[]): Todo[] {\n let todos: Todo[] = []\n\n for (const event of events) {\n if (event.type === 'todo.updated') {\n const data = event.data as Extract<TurnEvent, { type: 'todo.updated' }>['data']\n todos = data.todos\n }\n }\n\n return todos\n}\n\n// ============================================================================\n// Context State Folding\n// ============================================================================\n\ninterface ContextFoldResult {\n currentContextWindowId: string\n compactionCount: number\n readFiles: ReadFileEntry[]\n // Latest context.state from LLM (real promptTokens)\n latestContextState: ContextState | null\n}\n\n/**\n * Fold context state from events\n */\nexport function foldContextState(events: EventLike[], initialWindowId: string): ContextFoldResult {\n let currentContextWindowId = initialWindowId\n let compactionCount = 0\n let latestContextState: ContextState | null = null\n const readFilesMap = new Map<string, ReadFileEntry>()\n\n for (const event of events) {\n switch (event.type) {\n case 'session.initialized': {\n const data = event.data as Extract<TurnEvent, { type: 'session.initialized' }>['data']\n currentContextWindowId = data.contextWindowId\n break\n }\n case 'turn.snapshot': {\n const data = event.data as SessionSnapshot\n currentContextWindowId = data.currentContextWindowId\n compactionCount = data.contextState.compactionCount\n latestContextState = data.contextState\n readFilesMap.clear()\n for (const entry of data.readFiles) {\n readFilesMap.set(entry.path, { ...entry })\n }\n break\n }\n case 'context.state': {\n // Use the real promptTokens from LLM\n const data = event.data as ContextState\n latestContextState = data\n break\n }\n case 'context.compacted': {\n const data = event.data as Extract<TurnEvent, { type: 'context.compacted' }>['data']\n currentContextWindowId = data.newWindowId\n compactionCount++\n // Clear read files cache on compaction (new window)\n readFilesMap.clear()\n // Reset context state after compaction (will be updated by next LLM call)\n latestContextState = null\n break\n }\n case 'file.read': {\n const data = event.data as Extract<TurnEvent, { type: 'file.read' }>['data']\n // Only track reads for current window\n if (data.contextWindowId === currentContextWindowId) {\n readFilesMap.set(data.path, {\n path: data.path,\n tokenCount: data.tokenCount,\n })\n }\n break\n }\n }\n }\n\n return {\n currentContextWindowId,\n compactionCount,\n readFiles: Array.from(readFilesMap.values()),\n latestContextState,\n }\n}\n\n// ============================================================================\n// Session State Folding\n// ============================================================================\n\n/**\n * Fold mode from events (returns latest mode)\n */\nexport function foldMode(events: EventLike[]): SessionMode {\n let mode: SessionMode = 'planner'\n\n for (const event of events) {\n if (event.type === 'mode.changed') {\n const data = event.data as Extract<TurnEvent, { type: 'mode.changed' }>['data']\n mode = data.mode\n }\n }\n\n return mode\n}\n\n/**\n * Fold phase from events (returns latest phase)\n */\nexport function foldPhase(events: EventLike[]): SessionPhase {\n let phase: SessionPhase = 'plan'\n\n for (const event of events) {\n if (event.type === 'phase.changed') {\n const data = event.data as Extract<TurnEvent, { type: 'phase.changed' }>['data']\n phase = data.phase\n }\n }\n\n return phase\n}\n\n/**\n * Fold running state from events\n */\nexport function foldIsRunning(events: EventLike[]): boolean {\n let isRunning = false\n\n for (const event of events) {\n if (event.type === 'running.changed') {\n const data = event.data as Extract<TurnEvent, { type: 'running.changed' }>['data']\n isRunning = data.isRunning\n }\n }\n\n return isRunning\n}\n\n/**\n * Fold full session state from events\n */\nexport function foldSessionState(events: EventLike[], initialWindowId: string): FoldedSessionState {\n const maxTokens = getRuntimeConfig().context.maxTokens\n const mode = foldMode(events)\n const phase = foldPhase(events)\n const isRunning = foldIsRunning(events)\n const messages = foldTurnEventsToSnapshotMessages(events)\n const criteria = foldCriteria(events)\n const todos = foldTodos(events)\n const contextResult = foldContextState(events, initialWindowId)\n\n // Use real promptTokens from latest context.state event if available\n // This is the accurate value from the LLM, not an estimate\n const baseContextState = contextResult.latestContextState ?? {\n currentTokens: 0,\n maxTokens,\n compactionCount: contextResult.compactionCount,\n dangerZone: false,\n canCompact: false,\n }\n\n // Ensure compactionCount is up-to-date from events (in case compaction happened after last context.state)\n const contextState: ContextState = baseContextState.compactionCount !== contextResult.compactionCount\n ? { ...baseContextState, compactionCount: contextResult.compactionCount }\n : baseContextState\n\n return {\n mode,\n phase,\n isRunning,\n messages,\n criteria,\n todos,\n contextState,\n currentContextWindowId: contextResult.currentContextWindowId,\n readFiles: contextResult.readFiles,\n }\n}\n\n// ============================================================================\n// Snapshot Building\n// ============================================================================\n\n/**\n * Build a snapshot from folded session state\n */\nexport function buildSnapshot(\n foldedState: FoldedSessionState,\n latestSeq: number,\n snapshotAt: number = Date.now()\n): SessionSnapshot {\n return {\n mode: foldedState.mode,\n phase: foldedState.phase,\n isRunning: foldedState.isRunning,\n messages: foldedState.messages,\n criteria: foldedState.criteria,\n contextState: foldedState.contextState,\n currentContextWindowId: foldedState.currentContextWindowId,\n todos: foldedState.todos,\n readFiles: foldedState.readFiles,\n snapshotSeq: latestSeq,\n snapshotAt,\n }\n}\n\n/**\n * Legacy compatibility wrapper - builds snapshot from session state object.\n * @deprecated Use foldSessionState + buildSnapshot instead\n */\ninterface LegacySessionState {\n mode: SessionMode\n phase: SessionPhase\n isRunning: boolean\n criteria: Criterion[]\n executionState?: { currentTokenCount?: number; compactionCount?: number } | null\n}\n\n/**\n * Strip the `messages` array from `promptContext` on all but the last assistant message.\n * Each promptContext.messages contains the full conversation history at that turn,\n * so storing it on every message causes O(n²) snapshot growth.\n * Mutates the array in place.\n */\nfunction stripPromptContextMessages(messages: SnapshotMessage[]): void {\n // Find the last assistant message with promptContext\n let lastAssistantIdx = -1\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (!msg) continue\n if (msg.role === 'assistant' && msg.promptContext) {\n lastAssistantIdx = i\n break\n }\n }\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n if (!msg) continue\n const pc = msg.promptContext\n if (pc && i !== lastAssistantIdx) {\n // Keep everything except the heavy messages array\n const { messages: _msgs, ...rest } = pc\n msg.promptContext = { ...rest, messages: [] }\n }\n }\n}\n\nexport function buildSnapshotFromSessionState(input: {\n session: LegacySessionState\n events: EventLike[]\n latestSeq: number\n snapshotAt?: number\n}): SessionSnapshot {\n const { session, events, latestSeq, snapshotAt = Date.now() } = input\n\n // Get initial context window ID from session.initialized event or generate one\n let initialWindowId = ''\n for (const event of events) {\n if (event.type === 'session.initialized') {\n const data = event.data as Extract<TurnEvent, { type: 'session.initialized' }>['data']\n initialWindowId = data.contextWindowId\n break\n }\n }\n if (!initialWindowId) {\n initialWindowId = 'legacy-window-1'\n }\n\n const foldedState = foldSessionState(events, initialWindowId)\n const latestSnapshotIndex = events.map((event) => event.type).lastIndexOf('turn.snapshot')\n const latestSnapshotEvent = latestSnapshotIndex >= 0 ? events[latestSnapshotIndex] : undefined\n const messages = latestSnapshotEvent\n ? applyTurnEventsToSnapshotMessages(\n (latestSnapshotEvent.data as SessionSnapshot).messages,\n events.slice(latestSnapshotIndex + 1),\n )\n : foldedState.messages\n\n // Strip the conversation history from promptContext on older messages.\n // Each promptContext.messages duplicates the full conversation up to that turn,\n // causing O(n²) growth. We keep the lightweight metadata (systemPrompt, tools,\n // injectedFiles, requestOptions, userMessage) for the Prompt Inspector UI.\n stripPromptContextMessages(messages)\n\n // Override with legacy session values where provided\n return {\n mode: session.mode,\n phase: session.phase,\n isRunning: session.isRunning,\n messages,\n criteria: session.criteria,\n contextState: {\n currentTokens: session.executionState?.currentTokenCount ?? foldedState.contextState.currentTokens,\n maxTokens: foldedState.contextState.maxTokens,\n compactionCount: session.executionState?.compactionCount ?? foldedState.contextState.compactionCount,\n dangerZone: foldedState.contextState.dangerZone,\n canCompact: foldedState.contextState.canCompact,\n },\n currentContextWindowId: foldedState.currentContextWindowId,\n todos: foldedState.todos,\n readFiles: foldedState.readFiles,\n snapshotSeq: latestSeq,\n snapshotAt,\n }\n}\n\n/**\n * Get messages for a specific context window\n */\nexport function getMessagesForWindow(\n messages: SnapshotMessage[],\n windowId: string\n): SnapshotMessage[] {\n return messages.filter((m) => m.contextWindowId === windowId)\n}\n\n/**\n * Build context messages for LLM from messages in current window\n */\nexport function buildContextMessagesFromMessages(\n messages: SnapshotMessage[],\n windowId: string\n): ContextMessage[] {\n return getMessagesForWindow(messages, windowId).reduce<ContextMessage[]>((result, message) => {\n if (!shouldIncludeContextMessage(message, windowId)) {\n return result\n }\n\n appendSnapshotMessageContext(result, message)\n return result\n }, [])\n}\n","/**\n * Session State API (Event-Sourced)\n *\n * This module provides the primary API for interacting with session state.\n * All state changes go through EventStore - this is the single source of truth.\n *\n * Usage:\n * ```typescript\n * import { emitUserMessage, emitModeChanged, getSessionState } from './events/session.js'\n *\n * // Emit events\n * const messageId = emitUserMessage(sessionId, 'Hello')\n * emitModeChanged(sessionId, 'builder', false, 'User switched to builder')\n *\n * // Get current state\n * const state = getSessionState(sessionId)\n * ```\n */\n\nimport type {\n SessionMode,\n SessionPhase,\n Criterion,\n CriterionStatus,\n ToolCall,\n ToolResult,\n MessageStats,\n Todo,\n MessageSegment,\n PromptContext,\n Attachment,\n} from '../../shared/types.js'\nimport type { SessionSnapshot, SnapshotMessage, ReadFileEntry } from './types.js'\nimport { getEventStore } from './store.js'\nimport {\n foldSessionState,\n foldTurnEventsToSnapshotMessages,\n foldCriteria,\n foldTodos,\n foldMode,\n foldPhase,\n foldIsRunning,\n foldContextState,\n buildContextMessagesFromEventHistory,\n buildContextMessagesFromStoredEvents,\n buildMessagesFromStoredEvents,\n type ContextMessage,\n type FoldedSessionState,\n} from './folding.js'\n\nfunction toSnapshotMessage(message: import('../../shared/types.js').Message): SnapshotMessage {\n return {\n id: message.id,\n role: message.role,\n content: message.content,\n timestamp: new Date(message.timestamp).getTime(),\n ...(message.thinkingContent !== undefined && { thinkingContent: message.thinkingContent }),\n ...(message.toolCalls !== undefined && { toolCalls: message.toolCalls }),\n ...(message.segments !== undefined && { segments: message.segments }),\n ...(message.stats !== undefined && { stats: message.stats }),\n ...(message.tokenCount !== undefined && { tokenCount: message.tokenCount }),\n ...(message.isStreaming !== undefined && { isStreaming: message.isStreaming }),\n ...(message.partial !== undefined && { partial: message.partial }),\n ...(message.subAgentId !== undefined && { subAgentId: message.subAgentId }),\n ...(message.subAgentType !== undefined && { subAgentType: message.subAgentType }),\n ...(message.isSystemGenerated !== undefined && { isSystemGenerated: message.isSystemGenerated }),\n ...(message.messageKind !== undefined && { messageKind: message.messageKind }),\n ...(message.contextWindowId !== undefined && { contextWindowId: message.contextWindowId }),\n ...(message.isCompactionSummary !== undefined && { isCompactionSummary: message.isCompactionSummary }),\n ...(message.promptContext !== undefined && { promptContext: message.promptContext }),\n ...(message.attachments !== undefined && { attachments: message.attachments }),\n }\n}\n\n// ============================================================================\n// Session State Retrieval\n// ============================================================================\n\n/**\n * Get full session state by folding all events.\n * Returns undefined if no session.initialized event exists.\n * \n * If a snapshot exists, messages are loaded from the snapshot instead of\n * reconstructing from individual events (which may have been deleted).\n */\nexport function getSessionState(sessionId: string): FoldedSessionState | undefined {\n const eventStore = getEventStore()\n \n // Check for the latest snapshot first\n const latestSnapshotEvent = eventStore.getLatestSnapshot(sessionId)\n \n // Get all events (snapshots + current window events)\n const events = eventStore.getEvents(sessionId)\n\n if (events.length === 0) {\n return undefined\n }\n\n // Find initial context window ID from session.initialized event\n let initialWindowId: string | undefined\n for (const event of events) {\n if (event.type === 'session.initialized') {\n const data = event.data as { contextWindowId: string }\n initialWindowId = data.contextWindowId\n break\n }\n }\n\n if (!initialWindowId) {\n return undefined\n }\n\n // If we have a snapshot, use it as the base for messages and replay newer events\n if (latestSnapshotEvent) {\n const state = foldSessionState(events, initialWindowId)\n \n // Override folded messages with the latest snapshot plus replayed events.\n return {\n ...state,\n messages: buildMessagesFromStoredEvents(events).map(toSnapshotMessage),\n }\n }\n\n return foldSessionState(events, initialWindowId)\n}\n\n/**\n * Get messages for the current context window (for LLM context building)\n * \n * If a snapshot exists, messages are loaded from the snapshot.\n * Otherwise, they're built from events.\n */\nexport function getCurrentWindowMessages(sessionId: string): SnapshotMessage[] {\n // Get current context window ID from events (not from snapshot, as snapshot may be stale)\n const currentWindowId = getCurrentContextWindowId(sessionId)\n if (!currentWindowId) return []\n\n const state = getSessionState(sessionId)\n if (!state) return []\n \n return state.messages.filter((m) => m.contextWindowId === currentWindowId)\n}\n\n/**\n * Get context messages for LLM from current window\n * \n * If a snapshot exists, messages are loaded from the snapshot.\n * Otherwise, they're built from events.\n */\nexport function getContextMessages(sessionId: string): ContextMessage[] {\n const eventStore = getEventStore()\n // Get current context window ID from events (not from snapshot, as snapshot may be stale)\n const currentWindowId = getCurrentContextWindowId(sessionId)\n if (!currentWindowId) return []\n\n const events = eventStore.getEvents(sessionId)\n if (events.length === 0) return []\n\n return buildContextMessagesFromEventHistory(events, currentWindowId, { includeVerifier: false })\n}\n\n/**\n * Get current context window ID\n */\nexport function getCurrentContextWindowId(sessionId: string): string | undefined {\n const eventStore = getEventStore()\n const events = eventStore.getEvents(sessionId)\n\n const contextResult = foldContextState(events, '')\n return contextResult.currentContextWindowId || undefined\n}\n\n/**\n * Get read files cache for current window\n */\nexport function getReadFilesCache(sessionId: string): ReadFileEntry[] {\n const state = getSessionState(sessionId)\n return state?.readFiles ?? []\n}\n\n/**\n * Check if a file is in the read cache for current window\n */\nexport function isFileInCache(sessionId: string, path: string): boolean {\n const cache = getReadFilesCache(sessionId)\n return cache.some((f) => f.path === path)\n}\n\n// ============================================================================\n// Event Emission Helpers\n// ============================================================================\n\n/**\n * Emit session.initialized event (called once when session is created)\n */\nexport function emitSessionInitialized(\n sessionId: string,\n projectId: string,\n workdir: string,\n contextWindowId: string,\n title?: string\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'session.initialized',\n data: {\n projectId,\n workdir,\n contextWindowId,\n ...(title !== undefined && { title }),\n },\n })\n}\n\n/**\n * Emit a user message. Returns the message ID.\n */\nexport function emitUserMessage(\n sessionId: string,\n content: string,\n options?: {\n contextWindowId?: string\n isSystemGenerated?: boolean\n messageKind?: 'correction' | 'auto-prompt' | 'context-reset' | 'task-completed' | 'workflow-started' | 'command'\n isCompactionSummary?: boolean\n tokenCount?: number\n attachments?: Attachment[] // Optional image attachments\n subAgentId?: string\n subAgentType?: string\n }\n): string {\n const eventStore = getEventStore()\n const messageId = crypto.randomUUID()\n\n eventStore.append(sessionId, {\n type: 'message.start',\n data: {\n messageId,\n role: 'user',\n content,\n ...(options?.contextWindowId !== undefined && { contextWindowId: options.contextWindowId }),\n ...(options?.isSystemGenerated !== undefined && { isSystemGenerated: options.isSystemGenerated }),\n ...(options?.messageKind !== undefined && { messageKind: options.messageKind }),\n ...(options?.isCompactionSummary !== undefined && { isCompactionSummary: options.isCompactionSummary }),\n ...(options?.tokenCount !== undefined && { tokenCount: options.tokenCount }),\n ...(options?.attachments !== undefined && { attachments: options.attachments }),\n ...(options?.subAgentId !== undefined && { subAgentId: options.subAgentId }),\n ...(options?.subAgentType !== undefined && { subAgentType: options.subAgentType }),\n },\n })\n\n eventStore.append(sessionId, {\n type: 'message.done',\n data: { messageId },\n })\n\n return messageId\n}\n\n/**\n * Emit assistant message start. Returns the message ID.\n */\nexport function emitAssistantMessageStart(\n sessionId: string,\n options?: {\n contextWindowId?: string\n subAgentId?: string\n subAgentType?: string\n }\n): string {\n const eventStore = getEventStore()\n const messageId = crypto.randomUUID()\n\n eventStore.append(sessionId, {\n type: 'message.start',\n data: {\n messageId,\n role: 'assistant',\n ...(options?.contextWindowId !== undefined && { contextWindowId: options.contextWindowId }),\n ...(options?.subAgentId !== undefined && { subAgentId: options.subAgentId }),\n ...(options?.subAgentType !== undefined && { subAgentType: options.subAgentType }),\n },\n })\n\n return messageId\n}\n\n/**\n * Emit message content delta (streaming)\n */\nexport function emitMessageDelta(sessionId: string, messageId: string, content: string): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'message.delta',\n data: { messageId, content },\n })\n}\n\n/**\n * Emit message thinking content (streaming)\n */\nexport function emitMessageThinking(sessionId: string, messageId: string, content: string): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'message.thinking',\n data: { messageId, content },\n })\n}\n\n/**\n * Emit message done\n */\nexport function emitMessageDone(\n sessionId: string,\n messageId: string,\n options?: {\n stats?: MessageStats\n segments?: MessageSegment[]\n partial?: boolean\n promptContext?: PromptContext\n tokenCount?: number\n }\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'message.done',\n data: {\n messageId,\n ...(options?.stats !== undefined && { stats: options.stats }),\n ...(options?.segments !== undefined && { segments: options.segments }),\n ...(options?.partial !== undefined && { partial: options.partial }),\n ...(options?.promptContext !== undefined && { promptContext: options.promptContext }),\n ...(options?.tokenCount !== undefined && { tokenCount: options.tokenCount }),\n },\n })\n}\n\n/**\n * Emit tool preparing (early in stream when tool name is known but args not complete)\n */\nexport function emitToolPreparing(\n sessionId: string,\n messageId: string,\n index: number,\n name: string\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'tool.preparing',\n data: { messageId, index, name },\n })\n}\n\n/**\n * Emit tool call (when tool call is complete and ready to execute)\n */\nexport function emitToolCall(sessionId: string, messageId: string, toolCall: ToolCall): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'tool.call',\n data: { messageId, toolCall },\n })\n}\n\n/**\n * Emit tool output (streaming stdout/stderr from run_command)\n */\nexport function emitToolOutput(\n sessionId: string,\n toolCallId: string,\n stream: 'stdout' | 'stderr',\n content: string\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'tool.output',\n data: { toolCallId, stream, content },\n })\n}\n\n/**\n * Emit tool result\n */\nexport function emitToolResult(\n sessionId: string,\n messageId: string,\n toolCallId: string,\n result: ToolResult\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'tool.result',\n data: { messageId, toolCallId, result },\n })\n}\n\n/**\n * Emit mode changed\n */\nexport function emitModeChanged(\n sessionId: string,\n mode: SessionMode,\n auto: boolean,\n reason?: string\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'mode.changed',\n data: {\n mode,\n auto,\n ...(reason !== undefined && { reason }),\n },\n })\n}\n\n/**\n * Emit phase changed\n */\nexport function emitPhaseChanged(sessionId: string, phase: SessionPhase): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'phase.changed',\n data: { phase },\n })\n}\n\n/**\n * Emit running state changed\n */\nexport function emitRunningChanged(sessionId: string, isRunning: boolean): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'running.changed',\n data: { isRunning },\n })\n}\n\n/**\n * Emit criteria set (replace all criteria)\n */\nexport function emitCriteriaSet(sessionId: string, criteria: Criterion[]): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'criteria.set',\n data: { criteria },\n })\n}\n\n/**\n * Emit criterion updated\n */\nexport function emitCriterionUpdated(\n sessionId: string,\n criterionId: string,\n status: CriterionStatus\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'criterion.updated',\n data: { criterionId, status },\n })\n}\n\n/**\n * Emit todos updated\n */\nexport function emitTodosUpdated(sessionId: string, todos: Todo[]): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'todo.updated',\n data: { todos },\n })\n}\n\n/**\n * Emit file read (for cache tracking)\n */\nexport function emitFileRead(\n sessionId: string,\n path: string,\n tokenCount: number,\n contextWindowId: string\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'file.read',\n data: { path, tokenCount, contextWindowId },\n })\n}\n\n/**\n * Emit context compacted (closes current window, creates new one)\n */\nexport function emitContextCompacted(\n sessionId: string,\n closedWindowId: string,\n newWindowId: string,\n beforeTokens: number,\n afterTokens: number,\n summary: string\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'context.compacted',\n data: {\n closedWindowId,\n newWindowId,\n beforeTokens,\n afterTokens,\n summary,\n },\n })\n}\n\n/**\n * Emit context state update\n */\nexport function emitContextState(\n sessionId: string,\n currentTokens: number,\n maxTokens: number,\n compactionCount: number,\n dangerZone: boolean,\n canCompact: boolean\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'context.state',\n data: {\n currentTokens,\n maxTokens,\n compactionCount,\n dangerZone,\n canCompact,\n },\n })\n}\n\n/**\n * Emit chat done\n */\nexport function emitChatDone(\n sessionId: string,\n messageId: string,\n reason: 'complete' | 'stopped' | 'error' | 'waiting_for_user',\n stats?: MessageStats\n): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'chat.done',\n data: {\n messageId,\n reason,\n ...(stats !== undefined && { stats }),\n },\n })\n}\n\n/**\n * Emit chat error\n */\nexport function emitChatError(sessionId: string, error: string, recoverable: boolean): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'chat.error',\n data: { error, recoverable },\n })\n}\n\n/**\n * Emit format retry\n */\nexport function emitFormatRetry(sessionId: string, attempt: number, maxAttempts: number): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'format.retry',\n data: { attempt, maxAttempts },\n })\n}\n\n/**\n * Emit turn snapshot\n */\nexport function emitTurnSnapshot(sessionId: string, snapshot: SessionSnapshot): void {\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'turn.snapshot',\n data: snapshot,\n })\n}\n\n// ============================================================================\n// Convenience Helpers\n// ============================================================================\n\n/**\n * Create a compaction summary message and emit context compaction\n */\nexport function compactContext(\n sessionId: string,\n summary: string,\n beforeTokens: number\n): { newWindowId: string; summaryMessageId: string } {\n const state = getSessionState(sessionId)\n if (!state) {\n throw new Error('Session not found')\n }\n\n const closedWindowId = state.currentContextWindowId\n const newWindowId = crypto.randomUUID()\n const summaryMessageId = crypto.randomUUID()\n\n // Emit summary message\n const eventStore = getEventStore()\n eventStore.append(sessionId, {\n type: 'message.start',\n data: {\n messageId: summaryMessageId,\n role: 'assistant',\n content: summary,\n contextWindowId: closedWindowId,\n isCompactionSummary: true,\n },\n })\n eventStore.append(sessionId, {\n type: 'message.done',\n data: { messageId: summaryMessageId },\n })\n\n // Emit compaction event\n emitContextCompacted(sessionId, closedWindowId, newWindowId, beforeTokens, 0, summary)\n\n return { newWindowId, summaryMessageId }\n}\n\n// ============================================================================\n// Recent User Prompts\n// ============================================================================\n\n/**\n * Get the most recent user prompts for a session.\n * Queries the events table directly for efficiency, returning only necessary fields.\n * \n * @param sessionId - The session ID\n * @param limit - Maximum number of prompts to return (default: 10)\n * @returns Array of recent user prompts with id, content, and timestamp\n */\nexport function getRecentUserPromptsForSession(sessionId: string, limit: number = 10): { id: string, content: string, timestamp: string }[] {\n try {\n const eventStore = getEventStore()\n const db = (eventStore as any).db as import('better-sqlite3').Database | undefined\n\n // If no db available (e.g., in tests), return empty array\n if (!db) {\n return []\n }\n\n const isRealUserMessage = (msg: { role: string, isSystemGenerated?: boolean, messageKind?: string, subAgentType?: string }) =>\n msg.role === 'user' && !msg.isSystemGenerated && !msg.messageKind && !msg.subAgentType\n\n // Collect user prompts from both snapshots and message.start events.\n // After a snapshot is created, older message.start events may be deleted,\n // so we must extract messages from the latest snapshot as well.\n const promptMap = new Map<string, { id: string, content: string, timestamp: string }>()\n\n // 1. Extract user messages from the latest snapshot (if any)\n const snapshotRow = db\n .prepare(`\n SELECT payload, timestamp\n FROM events\n WHERE session_id = ? AND event_type = 'turn.snapshot'\n ORDER BY timestamp DESC\n LIMIT 1\n `)\n .get(sessionId) as { payload: string, timestamp: number } | undefined\n\n if (snapshotRow) {\n const snapshot = JSON.parse(snapshotRow.payload) as { messages: Array<{ id: string, role: string, content: string, timestamp: number, isSystemGenerated?: boolean, messageKind?: string, subAgentType?: string }> }\n for (const msg of snapshot.messages) {\n if (isRealUserMessage(msg)) {\n promptMap.set(msg.id, {\n id: msg.id,\n content: msg.content,\n timestamp: new Date(msg.timestamp).toISOString(),\n })\n }\n }\n }\n\n // 2. Add/override with message.start events (these may be newer than the snapshot)\n const rows = db\n .prepare(`\n SELECT payload, timestamp\n FROM events\n WHERE session_id = ? AND event_type = 'message.start'\n AND json_extract(payload, '$.role') = 'user'\n AND json_extract(payload, '$.isSystemGenerated') IS NULL\n AND json_extract(payload, '$.messageKind') IS NULL\n AND json_extract(payload, '$.subAgentType') IS NULL\n ORDER BY timestamp DESC\n LIMIT ?\n `)\n .all(sessionId, limit) as { payload: string, timestamp: number }[]\n\n for (const row of rows) {\n const message = JSON.parse(row.payload) as { messageId: string, content: string }\n promptMap.set(message.messageId, {\n id: message.messageId,\n content: message.content,\n timestamp: new Date(row.timestamp).toISOString(),\n })\n }\n\n // Sort by timestamp descending (newest first) and take top N\n return [...promptMap.values()]\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, limit)\n } catch (error) {\n // If any error occurs (e.g., in tests), return empty array\n return []\n }\n}\n","import { readFile, access } from 'node:fs/promises'\nimport { join, dirname } from 'node:path'\nimport { constants } from 'node:fs'\nimport { getSetting, SETTINGS_KEYS } from '../db/settings.js'\nimport { getProject } from '../db/projects.js'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface InstructionFile {\n path: string\n source: 'agents-md' | 'global' | 'project'\n content?: string\n}\n\nexport interface AllInstructions {\n content: string\n files: InstructionFile[]\n}\n\n// Filenames to look for (in order of priority within same directory)\nconst INSTRUCTION_FILENAMES = ['AGENTS.md', 'CLAUDE.md']\n\n// ============================================================================\n// Discovery\n// ============================================================================\n\n/**\n * Find instruction files by walking up the directory tree from workdir.\n * Returns files ordered from root to workdir (parent directories first),\n * so that files closer to the working directory can override parent instructions.\n */\nexport async function findInstructionFiles(workdir: string): Promise<InstructionFile[]> {\n const foundFiles: InstructionFile[] = []\n const pathsToCheck: string[] = []\n \n // Walk up the directory tree\n let currentDir = workdir\n while (true) {\n pathsToCheck.unshift(currentDir) // Add to front (we want root-first order)\n \n const parentDir = dirname(currentDir)\n // Stop if we've reached the root (dirname returns same path)\n if (parentDir === currentDir) {\n break\n }\n currentDir = parentDir\n }\n \n // Check each directory for instruction files\n for (const dir of pathsToCheck) {\n for (const filename of INSTRUCTION_FILENAMES) {\n const filePath = join(dir, filename)\n if (await fileExists(filePath)) {\n foundFiles.push({\n path: filePath,\n source: 'agents-md',\n })\n }\n }\n }\n \n return foundFiles\n}\n\n/**\n * Load instruction content from files.\n * Each file's content is prefixed with a comment showing its source path.\n */\nexport async function loadInstructions(files: InstructionFile[]): Promise<string> {\n const contents: string[] = []\n \n for (const file of files) {\n try {\n const content = await readFile(file.path, 'utf-8')\n contents.push(`Instructions from: ${file.path}\\n${content}`)\n } catch {\n // File doesn't exist or can't be read - skip silently\n continue\n }\n }\n \n return contents.join('\\n')\n}\n\n/**\n * Convenience function to find and load all instruction files for a workdir.\n * Only includes AGENTS.md files, not global or project instructions.\n */\nexport async function getInstructionsForWorkdir(workdir: string): Promise<{\n content: string\n files: InstructionFile[]\n}> {\n const files = await findInstructionFiles(workdir)\n const content = await loadInstructions(files)\n return { content, files }\n}\n\n/**\n * Load ALL instructions from all sources for a session.\n * Order: global → project → AGENTS.md files\n * This is the primary function that should be used when building prompts.\n */\nexport async function getAllInstructions(\n workdir: string,\n projectId: string\n): Promise<AllInstructions> {\n const sections: string[] = []\n const allFiles: InstructionFile[] = []\n \n // 1. Global instructions (from settings)\n const globalInstructions = getSetting(SETTINGS_KEYS.GLOBAL_INSTRUCTIONS)\n if (globalInstructions) {\n sections.push(`## GLOBAL INSTRUCTIONS\\n\\n${globalInstructions}`)\n allFiles.push({ path: 'Global Instructions', source: 'global', content: globalInstructions })\n }\n \n // 2. Project instructions (from project record)\n const project = getProject(projectId)\n if (project?.customInstructions) {\n sections.push(`## PROJECT INSTRUCTIONS\\n\\n${project.customInstructions}`)\n allFiles.push({ path: `Project: ${project.name}`, source: 'project', content: project.customInstructions })\n }\n \n // 3. AGENTS.md files (from filesystem)\n const agentFiles = await findInstructionFiles(workdir)\n if (agentFiles.length > 0) {\n const agentContent = await loadInstructions(agentFiles)\n if (agentContent) {\n sections.push(`## FILE INSTRUCTIONS\\n\\n${agentContent}`)\n // Load content for each file\n for (const file of agentFiles) {\n try {\n const content = await readFile(file.path, 'utf-8')\n allFiles.push({ ...file, content })\n } catch {\n allFiles.push(file)\n }\n }\n }\n }\n \n return {\n content: sections.join('\\n\\n'),\n files: allFiles,\n }\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.R_OK)\n return true\n } catch {\n return false\n }\n}\n","/**\n * Context compaction utilities.\n * \n * The actual compaction is now handled by streamLLMResponse with the COMPACTION_PROMPT\n * from chat/prompts.ts. This module provides helper functions for deciding when to compact.\n */\n\n/**\n * Check if automatic compaction should be triggered.\n */\nexport function shouldCompact(\n currentTokens: number,\n maxTokens: number,\n threshold: number\n): boolean {\n return currentTokens > maxTokens * threshold\n}\n\n/**\n * Calculate the target token count for compaction.\n */\nexport function getCompactionTarget(\n maxTokens: number,\n targetRatio: number\n): number {\n return Math.floor(maxTokens * targetRatio)\n}\n","import type {\n Attachment,\n InjectedFile,\n PromptContext,\n PromptContextMessage,\n PromptContextTool,\n PromptRequestOptions,\n} from '../../shared/types.js'\nimport type { LLMToolDefinition } from '../llm/types.js'\nimport type { SkillMetadata } from '../skills/types.js'\nimport type { AgentDefinition } from '../agents/types.js'\nimport {\n buildTopLevelSystemPrompt,\n buildSubAgentSystemPrompt,\n buildAgentReminder,\n} from './prompts.js'\n\nexport type RequestContextMessage = PromptContextMessage\n\nexport interface BaseAssemblyInput {\n workdir: string\n messages: RequestContextMessage[]\n injectedFiles: InjectedFile[]\n customInstructions?: string\n skills?: SkillMetadata[]\n includeRuntimeReminder?: boolean\n promptTools: LLMToolDefinition[]\n requestTools?: LLMToolDefinition[]\n toolChoice?: PromptRequestOptions['toolChoice']\n disableThinking?: boolean\n}\n\ninterface AssemblyResult {\n systemPrompt: string\n messages: Array<{\n role: 'user' | 'assistant' | 'tool'\n content: string\n toolCalls?: Array<{ id: string; name: string; arguments: Record<string, unknown> }>\n toolCallId?: string\n attachments?: Attachment[]\n }>\n promptContext: PromptContext\n}\n\nexport function createPromptContext(input: {\n systemPrompt: string\n messages: RequestContextMessage[]\n injectedFiles: InjectedFile[]\n requestTools: LLMToolDefinition[]\n toolChoice: PromptRequestOptions['toolChoice']\n disableThinking: boolean\n userMessage?: string\n}): PromptContext {\n return {\n systemPrompt: input.systemPrompt,\n injectedFiles: input.injectedFiles,\n userMessage: input.userMessage ?? getTriggerUserMessage(input.messages),\n messages: input.messages.map((message) => ({\n role: message.role,\n content: message.content,\n source: message.source,\n ...(message.toolCalls ? { toolCalls: message.toolCalls } : {}),\n ...(message.toolCallId ? { toolCallId: message.toolCallId } : {}),\n ...(message.attachments ? { attachments: message.attachments } : {}),\n })),\n tools: input.requestTools.map<PromptContextTool>((tool) => ({\n name: tool.function.name,\n description: tool.function.description,\n parameters: tool.function.parameters,\n })),\n requestOptions: {\n toolChoice: input.toolChoice,\n disableThinking: input.disableThinking,\n },\n }\n}\n\nfunction createAssemblyResult(input: {\n systemPrompt: string\n messages: RequestContextMessage[]\n runtimeReminder?: string\n injectedFiles: InjectedFile[]\n requestTools: LLMToolDefinition[]\n toolChoice: PromptRequestOptions['toolChoice']\n disableThinking: boolean\n}): AssemblyResult {\n const triggerUserMessage = getTriggerUserMessage(input.messages)\n const messages = input.runtimeReminder\n ? injectRuntimeReminder(input.messages, input.runtimeReminder)\n : input.messages\n\n return {\n systemPrompt: input.systemPrompt,\n messages: messages.map((message) => ({\n role: message.role,\n content: message.content,\n ...(message.toolCalls ? { toolCalls: message.toolCalls } : {}),\n ...(message.toolCallId ? { toolCallId: message.toolCallId } : {}),\n ...(message.attachments ? { attachments: message.attachments } : {}),\n })),\n promptContext: createPromptContext({\n ...input,\n messages,\n userMessage: triggerUserMessage,\n }),\n }\n}\n\nfunction injectRuntimeReminder(messages: RequestContextMessage[], runtimeReminder: string): RequestContextMessage[] {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]\n if (message?.role !== 'user' || message.source !== 'history') {\n continue\n }\n\n return messages.map((entry, entryIndex) => {\n if (entryIndex !== index) {\n return entry\n }\n\n return {\n ...entry,\n content: `${entry.content}\\n\\n${runtimeReminder}`,\n }\n })\n }\n\n return [...messages, { role: 'user', content: runtimeReminder, source: 'runtime' }]\n}\n\nfunction getTriggerUserMessage(messages: RequestContextMessage[]): string {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]\n if (message?.role === 'user' && message.source === 'history') {\n return message.content\n }\n }\n\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]\n if (message?.role === 'user') {\n return message.content\n }\n }\n\n return ''\n}\n\n// ============================================================================\n// Unified Agent Request Assembly\n// ============================================================================\n\nexport interface AgentAssemblyInput extends BaseAssemblyInput {\n agentDef: AgentDefinition\n subAgentDefs?: AgentDefinition[]\n}\n\n/**\n * Unified request assembly for any agent type.\n *\n * Top-level agents (subagent: false):\n * - System prompt = buildTopLevelSystemPrompt() (shared, cacheable)\n * - Runtime reminder = agent's prompt body (injected into user message)\n *\n * Sub-agents (subagent: true):\n * - System prompt = buildSubAgentSystemPrompt() (base + agent body)\n * - No runtime reminder\n */\nexport function assembleAgentRequest(input: AgentAssemblyInput): AssemblyResult {\n const { agentDef, subAgentDefs, ...baseInput } = input\n\n if (agentDef.metadata.subagent) {\n const systemPrompt = buildSubAgentSystemPrompt(\n baseInput.workdir,\n agentDef,\n baseInput.skills,\n )\n return createAssemblyResult({\n systemPrompt,\n messages: baseInput.messages,\n injectedFiles: baseInput.injectedFiles,\n requestTools: baseInput.requestTools ?? baseInput.promptTools,\n toolChoice: baseInput.toolChoice ?? 'auto',\n disableThinking: baseInput.disableThinking ?? false,\n })\n }\n\n const systemPrompt = buildTopLevelSystemPrompt(\n baseInput.workdir,\n baseInput.customInstructions,\n baseInput.skills,\n subAgentDefs,\n )\n const runtimeReminder = baseInput.includeRuntimeReminder === false\n ? undefined\n : buildAgentReminder(agentDef)\n\n return createAssemblyResult({\n systemPrompt,\n messages: baseInput.messages,\n ...(runtimeReminder ? { runtimeReminder } : {}),\n injectedFiles: baseInput.injectedFiles,\n requestTools: baseInput.requestTools ?? baseInput.promptTools,\n toolChoice: baseInput.toolChoice ?? 'auto',\n disableThinking: baseInput.disableThinking ?? false,\n })\n}\n","import type { Attachment, InjectedFile, Provider, StatsIdentity } from '../../shared/types.js'\nimport type { LLMClientWithModel } from '../llm/client.js'\nimport type { SessionManager } from '../session/index.js'\nimport { getEventStore, getContextMessages, getCurrentContextWindowId } from '../events/index.js'\nimport { getAllInstructions } from './instructions.js'\nimport { shouldCompact } from './compactor.js'\nimport { COMPACTION_PROMPT, FORMAT_CORRECTION_PROMPT, MAX_FORMAT_RETRIES } from '../chat/prompts.js'\nimport { assembleAgentRequest, type RequestContextMessage } from '../chat/request-context.js'\nimport {\n TurnMetrics,\n createMessageStartEvent,\n createMessageDoneEvent,\n createChatDoneEvent,\n} from '../chat/stream-pure.js'\nimport { streamLLMPure, consumeStreamGenerator } from '../chat/stream-pure.js'\nimport { loadAllAgentsDefault, findAgentById, getSubAgents } from '../agents/registry.js'\nimport { getRuntimeConfig } from '../runtime-config.js'\nimport { logger } from '../utils/logger.js'\n\n// ============================================================================\n// Compaction failure cooldown (prevents retry storms)\n// ============================================================================\n\nconst MIN_SUMMARY_LENGTH = 50\n\ninterface CompactionCooldown {\n failedAt: number\n attempts: number\n}\n\nconst compactionCooldowns = new Map<string, CompactionCooldown>()\nconst COOLDOWN_BASE_MS = 30_000 // 30 seconds\nconst MAX_COOLDOWN_MS = 300_000 // 5 minutes\n\nfunction isInCooldown(sessionId: string): boolean {\n const cooldown = compactionCooldowns.get(sessionId)\n if (!cooldown) return false\n const backoff = Math.min(COOLDOWN_BASE_MS * Math.pow(2, cooldown.attempts - 1), MAX_COOLDOWN_MS)\n return Date.now() - cooldown.failedAt < backoff\n}\n\nfunction recordCooldown(sessionId: string): void {\n const prev = compactionCooldowns.get(sessionId)\n compactionCooldowns.set(sessionId, {\n failedAt: Date.now(),\n attempts: (prev?.attempts ?? 0) + 1,\n })\n}\n\nfunction clearCooldown(sessionId: string): void {\n compactionCooldowns.delete(sessionId)\n}\n\nexport function clearCompactionCooldown(sessionId: string): void {\n clearCooldown(sessionId)\n}\n\nfunction getCurrentWindowMessageOptions(sessionId: string): { contextWindowId: string } | undefined {\n const contextWindowId = getCurrentContextWindowId(sessionId)\n return contextWindowId ? { contextWindowId } : undefined\n}\n\nfunction toRequestContextMessages(messages: Array<{\n role: 'user' | 'assistant' | 'tool'\n content: string\n toolCalls?: Array<{ id: string; name: string; arguments: Record<string, unknown> }>\n toolCallId?: string\n attachments?: Attachment[]\n}>): RequestContextMessage[] {\n return messages.map((message) => ({\n role: message.role,\n content: message.content,\n source: 'history',\n ...(message.toolCalls ? { toolCalls: message.toolCalls } : {}),\n ...(message.toolCallId ? { toolCallId: message.toolCallId } : {}),\n ...(message.attachments ? { attachments: message.attachments } : {}),\n }))\n}\n\ninterface ContextCompactionOptions {\n sessionManager: SessionManager\n sessionId: string\n llmClient: LLMClientWithModel\n statsIdentity: StatsIdentity\n signal?: AbortSignal\n}\n\nexport async function maybeAutoCompactContext(options: ContextCompactionOptions): Promise<boolean> {\n const config = getRuntimeConfig()\n const contextState = options.sessionManager.getContextState(options.sessionId)\n if (!shouldCompact(contextState.currentTokens, contextState.maxTokens, config.context.compactionThreshold)) {\n return false\n }\n\n if (isInCooldown(options.sessionId)) {\n logger.warn('Skipping auto-compaction (in cooldown after previous failure)', {\n sessionId: options.sessionId,\n currentTokens: contextState.currentTokens,\n })\n return false\n }\n\n try {\n await performContextCompaction({\n ...options,\n tokenCountAtClose: contextState.currentTokens,\n trigger: 'auto',\n })\n clearCooldown(options.sessionId)\n return true\n } catch (error) {\n // Abort errors should still propagate (user cancelled)\n if (error instanceof Error && error.message === 'Aborted') {\n throw error\n }\n\n logger.error('Auto-compaction failed, continuing without compaction', {\n sessionId: options.sessionId,\n error: error instanceof Error ? error.message : String(error),\n currentTokens: contextState.currentTokens,\n maxTokens: contextState.maxTokens,\n })\n\n recordCooldown(options.sessionId)\n\n // Emit a visible warning so the user knows compaction failed\n const eventStore = getEventStore()\n eventStore.append(options.sessionId, {\n type: 'chat.error',\n data: {\n error: `Auto-compaction failed: ${error instanceof Error ? error.message : 'Unknown error'}. Continuing with full context.`,\n recoverable: true,\n },\n })\n\n return false\n }\n}\n\nexport async function performManualContextCompaction(options: ContextCompactionOptions & {\n tokenCountAtClose: number\n}): Promise<void> {\n await performContextCompaction({\n ...options,\n trigger: 'manual',\n })\n}\n\nasync function performContextCompaction(options: ContextCompactionOptions & {\n tokenCountAtClose: number\n trigger: 'auto' | 'manual'\n}): Promise<void> {\n const { sessionManager, sessionId, llmClient, statsIdentity, signal, tokenCountAtClose, trigger } = options\n const eventStore = getEventStore()\n const session = sessionManager.requireSession(sessionId)\n const { content: instructions, files } = await getAllInstructions(session.workdir, session.projectId)\n const injectedFiles: InjectedFile[] = files.map((file) => ({\n path: file.path,\n content: file.content ?? '',\n source: file.source,\n }))\n let requestMessages = toRequestContextMessages(getContextMessages(sessionId))\n\n // Truncate context for the compaction call to avoid overwhelming the model.\n // Models with limited effective context (e.g. Ollama models with rope scaling)\n // may produce empty/1-token responses when fed too many tokens.\n const config = getRuntimeConfig()\n const safeInputLimit = Math.floor(config.context.maxTokens * 0.75)\n if (tokenCountAtClose > safeInputLimit && requestMessages.length > 4) {\n const avgTokensPerMessage = tokenCountAtClose / requestMessages.length\n const messagesToDrop = Math.ceil((tokenCountAtClose - safeInputLimit) / avgTokensPerMessage)\n if (messagesToDrop > 0 && messagesToDrop < requestMessages.length - 2) {\n const kept = requestMessages.length - messagesToDrop\n logger.info('Truncating compaction input to fit model context', {\n sessionId,\n originalMessages: requestMessages.length,\n keptMessages: kept,\n estimatedTokensSaved: Math.round(messagesToDrop * avgTokensPerMessage),\n })\n // Keep first message (task setup context) + most recent messages (current state)\n requestMessages = [requestMessages[0]!, ...requestMessages.slice(-kept + 1)]\n }\n }\n\n const allAgents = await loadAllAgentsDefault()\n const plannerDef = findAgentById('planner', allAgents)!\n const subAgentDefs = getSubAgents(allAgents)\n\n const assembledRequest = assembleAgentRequest({\n agentDef: plannerDef,\n subAgentDefs,\n workdir: session.workdir,\n messages: requestMessages,\n includeRuntimeReminder: false,\n injectedFiles,\n promptTools: [],\n requestTools: [],\n toolChoice: 'none',\n disableThinking: true,\n ...(instructions ? { customInstructions: instructions } : {}),\n })\n\n const compactPromptMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(compactPromptMsgId, 'user', COMPACTION_PROMPT, {\n ...(getCurrentWindowMessageOptions(sessionId) ?? {}),\n isSystemGenerated: true,\n messageKind: 'auto-prompt',\n }))\n eventStore.append(sessionId, { type: 'message.done', data: { messageId: compactPromptMsgId } })\n\n const assistantMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(assistantMsgId, 'assistant', undefined, getCurrentWindowMessageOptions(sessionId)))\n\n const turnMetrics = new TurnMetrics()\n const correctionMessages: Array<{ role: 'user'; content: string }> = []\n let result: Awaited<ReturnType<typeof consumeStreamGenerator>> | null = null\n\n for (let attempt = 0; attempt < MAX_FORMAT_RETRIES; attempt++) {\n const streamGen = streamLLMPure({\n messageId: assistantMsgId,\n systemPrompt: assembledRequest.systemPrompt,\n llmClient,\n messages: [...assembledRequest.messages, ...correctionMessages],\n tools: [],\n toolChoice: 'none',\n disableThinking: true,\n ...(signal ? { signal } : {}),\n })\n result = await consumeStreamGenerator(streamGen, event => {\n eventStore.append(sessionId, event)\n })\n\n if (result.aborted) {\n throw new Error('Aborted')\n }\n\n if (!result.xmlFormatError) {\n break\n }\n\n logger.warn('Compaction XML tool format error, retrying', {\n sessionId,\n attempt: attempt + 1,\n maxAttempts: MAX_FORMAT_RETRIES,\n })\n\n correctionMessages.push({ role: 'user', content: FORMAT_CORRECTION_PROMPT })\n }\n\n if (!result || result.xmlFormatError) {\n throw new Error('Compaction summary generation failed due to XML tool format output')\n }\n\n turnMetrics.addLLMCall(result.timing, result.usage.promptTokens, result.usage.completionTokens)\n const compactionStats = turnMetrics.buildStats(statsIdentity, 'planner')\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n segments: result.segments,\n stats: compactionStats,\n promptContext: assembledRequest.promptContext,\n }))\n eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, 'complete', compactionStats))\n\n // Use thinking content as fallback if text content is empty\n // (Ollama models may produce thinking via <think> tags even with disableThinking)\n let summary = (result.content ?? '').trim()\n if (!summary && result.thinkingContent) {\n logger.info('Using thinking content as compaction summary (text content was empty)', { sessionId })\n summary = result.thinkingContent.trim()\n }\n\n if (!summary || summary.length < MIN_SUMMARY_LENGTH) {\n throw new Error(\n `Compaction produced insufficient summary (length=${summary.length}, completionTokens=${result.usage.completionTokens}). `\n + 'The model may not support the required context size.'\n )\n }\n\n sessionManager.compactContext(sessionId, summary, tokenCountAtClose)\n\n logger.info(`${trigger === 'auto' ? 'Auto' : 'Manual'} compaction complete`, {\n sessionId,\n trigger,\n tokensBefore: tokenCountAtClose,\n summaryLength: summary.length,\n summaryTokens: result.usage.completionTokens,\n })\n}\n\nexport function resolveCompactionStatsIdentity(\n llmClient: LLMClientWithModel,\n getActiveProvider?: () => Provider | undefined,\n): StatsIdentity {\n const provider = getActiveProvider?.()\n const model = llmClient.getModel()\n const backend = provider?.backend ?? (llmClient.getBackend() === 'unknown' ? 'unknown' : llmClient.getBackend())\n\n return {\n providerId: provider?.id ?? `provider:${model}`,\n providerName: provider?.name ?? 'Unknown Provider',\n backend,\n model,\n }\n}\n","/**\n * Skill Registry\n *\n * Discovers, loads, and manages skills from the skills directory.\n * Enable/disable state is stored in the SQLite settings table.\n */\n\nimport { readdir, readFile, writeFile, copyFile, mkdir, access, unlink } from 'node:fs/promises'\nimport { join, dirname } from 'node:path'\nimport { constants } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport matter from 'gray-matter'\nimport type { SkillDefinition, SkillMetadata } from './types.js'\nimport { getSetting, setSetting } from '../db/settings.js'\nimport { logger } from '../utils/logger.js'\n\nconst __bundleDir = dirname(fileURLToPath(import.meta.url))\nconst DEFAULTS_DIR = join(__bundleDir, 'defaults')\nconst DEFAULTS_DIR_ALT = join(__bundleDir, 'skill-defaults')\nconst SKILL_EXTENSION = '.skill.md'\nconst SKILL_SETTING_PREFIX = 'skill.enabled.'\n\n// ============================================================================\n// Directory Helpers\n// ============================================================================\n\nfunction getSkillsDir(configDir: string): string {\n return join(configDir, 'skills')\n}\n\nasync function dirExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.R_OK)\n return true\n } catch {\n return false\n }\n}\n\n// ============================================================================\n// Default Skills Installation\n// ============================================================================\n\n/**\n * Copy bundled default skills to the config skills directory if they don't already exist.\n */\nexport async function ensureDefaultSkills(configDir: string): Promise<void> {\n const skillsDir = getSkillsDir(configDir)\n\n // Ensure skills directory exists\n if (!await dirExists(skillsDir)) {\n await mkdir(skillsDir, { recursive: true })\n }\n\n // Find bundled defaults (try dev path first, then production path)\n let defaultFiles: string[]\n let sourceDir: string\n try {\n defaultFiles = (await readdir(DEFAULTS_DIR)).filter(f => f.endsWith(SKILL_EXTENSION))\n sourceDir = DEFAULTS_DIR\n } catch {\n try {\n defaultFiles = (await readdir(DEFAULTS_DIR_ALT)).filter(f => f.endsWith(SKILL_EXTENSION))\n sourceDir = DEFAULTS_DIR_ALT\n } catch {\n logger.warn('No bundled skill defaults found', { dir: DEFAULTS_DIR })\n return\n }\n }\n\n // Copy each default that doesn't already exist in the target\n for (const file of defaultFiles) {\n const targetPath = join(skillsDir, file)\n if (!await dirExists(targetPath)) {\n try {\n await copyFile(join(sourceDir, file), targetPath)\n logger.info('Installed default skill', { file })\n } catch (err) {\n logger.error('Failed to copy default skill', { file, error: err instanceof Error ? err.message : String(err) })\n }\n }\n }\n}\n\n// ============================================================================\n// Skill Loading\n// ============================================================================\n\n/**\n * Load all skills from the skills directory.\n */\nexport async function loadAllSkills(configDir: string): Promise<SkillDefinition[]> {\n const skillsDir = getSkillsDir(configDir)\n\n if (!await dirExists(skillsDir)) {\n return []\n }\n\n let files: string[]\n try {\n files = (await readdir(skillsDir)).filter(f => f.endsWith(SKILL_EXTENSION))\n } catch {\n return []\n }\n\n const skills: SkillDefinition[] = []\n for (const file of files) {\n try {\n const raw = await readFile(join(skillsDir, file), 'utf-8')\n const { data, content } = matter(raw)\n const metadata = data as SkillMetadata\n if (metadata.id && content.trim()) {\n skills.push({ metadata, prompt: content.trim() })\n } else {\n logger.warn('Skipping invalid skill file', { file })\n }\n } catch (err) {\n logger.warn('Failed to parse skill file', { file, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n return skills\n}\n\n/**\n * Load only enabled skills.\n */\nexport async function getEnabledSkills(configDir: string): Promise<SkillDefinition[]> {\n const all = await loadAllSkills(configDir)\n return all.filter(s => isSkillEnabled(s.metadata.id))\n}\n\n/**\n * Get metadata for all enabled skills (for system prompt listing).\n */\nexport async function getEnabledSkillMetadata(configDir: string): Promise<SkillMetadata[]> {\n const enabled = await getEnabledSkills(configDir)\n return enabled.map(s => s.metadata)\n}\n\n// ============================================================================\n// Enable / Disable\n// ============================================================================\n\n/**\n * Check if a skill is enabled. Defaults to true if no setting exists.\n */\nexport function isSkillEnabled(skillId: string): boolean {\n const value = getSetting(`${SKILL_SETTING_PREFIX}${skillId}`)\n if (value === null) return true // enabled by default\n return value === 'true'\n}\n\n/**\n * Set whether a skill is enabled.\n */\nexport function setSkillEnabled(skillId: string, enabled: boolean): void {\n setSetting(`${SKILL_SETTING_PREFIX}${skillId}`, String(enabled))\n}\n\n// ============================================================================\n// Default Restoration\n// ============================================================================\n\n/**\n * Get the list of skill IDs that have bundled defaults.\n */\nexport async function getDefaultSkillIds(): Promise<string[]> {\n for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {\n try {\n const files = (await readdir(dir)).filter(f => f.endsWith(SKILL_EXTENSION))\n return files.map(f => f.replace(SKILL_EXTENSION, ''))\n } catch { /* try next */ }\n }\n return []\n}\n\n/**\n * Restore a single skill to its bundled default by re-copying from defaults.\n * Returns true if the default was found and restored.\n */\nexport async function restoreDefaultSkill(configDir: string, skillId: string): Promise<boolean> {\n const filename = `${skillId}${SKILL_EXTENSION}`\n for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {\n const sourcePath = join(dir, filename)\n if (await dirExists(sourcePath)) {\n const targetPath = join(getSkillsDir(configDir), filename)\n await copyFile(sourcePath, targetPath)\n return true\n }\n }\n return false\n}\n\n/**\n * Return the IDs of default skills whose user copy differs from the bundled version.\n */\nexport async function getModifiedDefaultSkillIds(configDir: string): Promise<string[]> {\n const defaultIds = await getDefaultSkillIds()\n const modified: string[] = []\n\n for (const id of defaultIds) {\n const filename = `${id}${SKILL_EXTENSION}`\n const userPath = join(getSkillsDir(configDir), filename)\n\n // Find bundled source\n let bundledContent: string | null = null\n for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {\n try {\n bundledContent = await readFile(join(dir, filename), 'utf-8')\n break\n } catch { /* try next */ }\n }\n if (!bundledContent) continue\n\n try {\n const userContent = await readFile(userPath, 'utf-8')\n if (userContent !== bundledContent) {\n modified.push(id)\n }\n } catch {\n // User file doesn't exist — treat as not modified (will be re-created)\n }\n }\n\n return modified\n}\n\n/**\n * Restore all skills to their bundled defaults.\n */\nexport async function restoreAllDefaultSkills(configDir: string): Promise<number> {\n const ids = await getDefaultSkillIds()\n let count = 0\n for (const id of ids) {\n if (await restoreDefaultSkill(configDir, id)) count++\n }\n return count\n}\n\n// ============================================================================\n// Skill Lookup\n// ============================================================================\n\n/**\n * Find a skill by ID from a list of loaded skills.\n */\nexport function findSkillById(skillId: string, skills: SkillDefinition[]): SkillDefinition | undefined {\n return skills.find(s => s.metadata.id === skillId)\n}\n\n// ============================================================================\n// Skill CRUD\n// ============================================================================\n\n/**\n * Check if a skill file exists.\n */\nexport async function skillExists(configDir: string, skillId: string): Promise<boolean> {\n const filePath = join(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`)\n return dirExists(filePath)\n}\n\n/**\n * Save a skill definition to disk.\n */\nexport async function saveSkill(configDir: string, skill: SkillDefinition): Promise<void> {\n const skillsDir = getSkillsDir(configDir)\n if (!await dirExists(skillsDir)) {\n await mkdir(skillsDir, { recursive: true })\n }\n const filePath = join(skillsDir, `${skill.metadata.id}${SKILL_EXTENSION}`)\n const content = matter.stringify(skill.prompt, skill.metadata)\n await writeFile(filePath, content, 'utf-8')\n}\n\n/**\n * Delete a skill from disk and clean up its enabled setting.\n */\nexport async function deleteSkill(configDir: string, skillId: string): Promise<boolean> {\n const filePath = join(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`)\n try {\n await unlink(filePath)\n // Clean up the enabled setting\n const { deleteSetting } = await import('../db/settings.js')\n deleteSetting(`${SKILL_SETTING_PREFIX}${skillId}`)\n return true\n } catch {\n return false\n }\n}\n","/**\n * Unified Agent Execution Loop\n *\n * Extracts the shared execution logic from runPlannerTurn, runBuilderTurn,\n * and executeSubAgent into reusable helpers.\n *\n * - executeToolBatch(): shared tool execution (used by all agent types)\n * - runTopLevelAgentLoop(): replaces duplicated planner/builder turns\n */\n\nimport type { Attachment, InjectedFile, PromptContext, StatsIdentity, ToolCall, ToolMode, ToolResult } from '../../shared/types.js'\nimport type { ServerMessage } from '../../shared/protocol.js'\nimport type { LLMClientWithModel } from '../llm/client.js'\nimport type { LLMToolDefinition } from '../llm/types.js'\nimport type { SessionManager } from '../session/index.js'\nimport type { ToolRegistry } from '../tools/types.js'\nimport type { RequestContextMessage } from './request-context.js'\nimport { PathAccessDeniedError } from '../tools/path-security.js'\nimport { createToolProgressHandler } from './tool-streaming.js'\nimport {\n streamLLMPure,\n consumeStreamGenerator,\n TurnMetrics,\n createMessageStartEvent,\n createMessageDoneEvent,\n createToolCallEvent,\n createToolResultEvent,\n createChatDoneEvent,\n createFormatRetryEvent,\n} from './stream-pure.js'\nimport type { PureStreamResult } from './stream-pure.js'\nimport { getEventStore, getContextMessages, getCurrentContextWindowId } from '../events/index.js'\nimport { maybeAutoCompactContext } from '../context/auto-compaction.js'\nimport { getAllInstructions } from '../context/instructions.js'\nimport { getEnabledSkillMetadata } from '../skills/registry.js'\nimport { getRuntimeConfig } from '../runtime-config.js'\nimport { getGlobalConfigDir } from '../../cli/paths.js'\nimport { createQueueStateMessage } from '../ws/protocol.js'\nimport { logger } from '../utils/logger.js'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ToolBatchContext {\n toolRegistry: ToolRegistry\n sessionManager: SessionManager\n sessionId: string\n workdir: string\n turnMetrics: TurnMetrics\n signal?: AbortSignal | undefined\n onMessage?: ((msg: ServerMessage) => void) | undefined\n llmClient?: LLMClientWithModel | undefined\n statsIdentity?: StatsIdentity | undefined\n onToolExecuted?: ((toolCall: ToolCall, result: ToolResult) => void) | undefined\n}\n\nexport interface ToolBatchResult {\n toolMessages: RequestContextMessage[]\n criteriaChanged: boolean\n returnValueContent?: string | undefined\n}\n\nexport interface TopLevelLoopConfig {\n mode: ToolMode\n sessionManager: SessionManager\n sessionId: string\n llmClient: LLMClientWithModel\n statsIdentity: StatsIdentity\n signal?: AbortSignal | undefined\n onMessage?: ((msg: ServerMessage) => void) | undefined\n assembleRequest: (input: {\n workdir: string\n messages: RequestContextMessage[]\n injectedFiles: InjectedFile[]\n promptTools: LLMToolDefinition[]\n toolChoice: 'auto' | 'none' | 'required'\n customInstructions?: string\n skills?: import('../skills/types.js').SkillMetadata[]\n }) => {\n systemPrompt: string\n messages: Array<{\n role: 'user' | 'assistant' | 'tool'\n content: string\n toolCalls?: Array<{ id: string; name: string; arguments: Record<string, unknown> }>\n toolCallId?: string\n attachments?: Attachment[]\n }>\n promptContext: PromptContext\n }\n getToolRegistry: () => ToolRegistry\n onToolExecuted?: ((toolCall: ToolCall, result: ToolResult) => void) | undefined\n injectKickoff?: (() => void) | undefined\n}\n\n// ============================================================================\n// Shared Tool Execution\n// ============================================================================\n\nfunction getCurrentWindowMessageOptions(sessionId: string): { contextWindowId: string } | undefined {\n const contextWindowId = getCurrentContextWindowId(sessionId)\n return contextWindowId ? { contextWindowId } : undefined\n}\n\nexport async function executeToolBatch(\n assistantMsgId: string,\n toolCalls: ToolCall[],\n ctx: ToolBatchContext,\n): Promise<ToolBatchResult> {\n const eventStore = getEventStore()\n const toolMessages: RequestContextMessage[] = []\n let criteriaChanged = false\n let returnValueContent: string | undefined\n let session = ctx.sessionManager.requireSession(ctx.sessionId)\n\n for (const toolCall of toolCalls) {\n if (ctx.signal?.aborted) {\n throw new Error('Aborted')\n }\n\n eventStore.append(ctx.sessionId, createToolCallEvent(assistantMsgId, toolCall))\n\n if (toolCall.parseError) {\n const toolResult: ToolResult = {\n success: false,\n error: `Failed to parse tool call arguments: ${toolCall.parseError}. Please ensure your JSON function call arguments are valid.`,\n durationMs: 0,\n truncated: false,\n }\n ctx.turnMetrics.addToolTime(toolResult.durationMs)\n eventStore.append(ctx.sessionId, createToolResultEvent(assistantMsgId, toolCall.id, toolResult))\n toolMessages.push({\n role: 'tool',\n content: `Error: ${toolResult.error}`,\n source: 'history',\n toolCallId: toolCall.id,\n })\n continue\n }\n\n const onProgress = ctx.onMessage ? createToolProgressHandler(assistantMsgId, toolCall.id, ctx.onMessage) : undefined\n\n let toolResult: ToolResult\n try {\n toolResult = await ctx.toolRegistry.execute(toolCall.name, toolCall.arguments, {\n sessionManager: ctx.sessionManager,\n workdir: ctx.workdir,\n sessionId: ctx.sessionId,\n signal: ctx.signal,\n llmClient: ctx.llmClient,\n statsIdentity: ctx.statsIdentity,\n lspManager: ctx.sessionManager.getLspManager(ctx.sessionId),\n onEvent: ctx.onMessage,\n onProgress,\n })\n } catch (error) {\n if (error instanceof PathAccessDeniedError) {\n toolResult = {\n success: false,\n error: `User denied access to ${error.paths.join(', ')}. If you need this file, explain why and ask for permission.`,\n durationMs: 0,\n truncated: false,\n }\n } else {\n throw error\n }\n }\n\n ctx.turnMetrics.addToolTime(toolResult.durationMs)\n eventStore.append(ctx.sessionId, createToolResultEvent(assistantMsgId, toolCall.id, toolResult))\n\n ctx.onToolExecuted?.(toolCall, toolResult)\n\n if (toolCall.name === 'return_value' && !toolCall.parseError) {\n returnValueContent = (toolCall.arguments as Record<string, unknown>)['content'] as string\n }\n\n toolMessages.push({\n role: 'tool',\n content: toolResult.success \n ? (toolResult.output ?? 'Success')\n : toolResult.output \n ? `${toolResult.output}\\n\\nError: ${toolResult.error}`\n : `Error: ${toolResult.error}`,\n source: 'history',\n toolCallId: toolCall.id,\n })\n\n const updatedSession = ctx.sessionManager.requireSession(ctx.sessionId)\n if (JSON.stringify(updatedSession.criteria) !== JSON.stringify(session.criteria)) {\n eventStore.append(ctx.sessionId, { type: 'criteria.set', data: { criteria: updatedSession.criteria } })\n session = updatedSession\n criteriaChanged = true\n }\n }\n\n return { toolMessages, criteriaChanged, returnValueContent }\n}\n\n// ============================================================================\n// Top-Level Agent Loop (replaces runPlannerTurn / runBuilderTurn)\n// ============================================================================\n\nconst MAX_FORMAT_RETRIES = 10\nconst FORMAT_CORRECTION_PROMPT = `IMPORTANT: You MUST use the JSON function calling API. Do NOT output XML tags like <tool_call>, <function=>, or <parameter=>. Your previous attempt was stopped because you used the wrong format. Use the proper tool_calls format.`\n\nfunction toRequestContextMessages(messages: Array<{\n role: 'user' | 'assistant' | 'tool'\n content: string\n toolCalls?: ToolCall[]\n toolCallId?: string\n attachments?: Attachment[]\n}>): RequestContextMessage[] {\n return messages.map((message) => ({\n role: message.role,\n content: message.content,\n source: 'history' as const,\n ...(message.toolCalls ? { toolCalls: message.toolCalls.map((toolCall) => ({ id: toolCall.id, name: toolCall.name, arguments: toolCall.arguments })) } : {}),\n ...(message.toolCallId ? { toolCallId: message.toolCallId } : {}),\n ...(message.attachments ? { attachments: message.attachments } : {}),\n }))\n}\n\nexport async function runTopLevelAgentLoop(\n config: TopLevelLoopConfig,\n turnMetrics: TurnMetrics,\n): Promise<void> {\n const { mode, sessionManager, sessionId, llmClient, signal, onMessage, statsIdentity } = config\n const eventStore = getEventStore()\n\n let formatRetryCount = 0\n\n for (;;) {\n await maybeAutoCompactContext({\n sessionManager,\n sessionId,\n llmClient,\n statsIdentity,\n ...(signal ? { signal } : {}),\n })\n\n const session = sessionManager.requireSession(sessionId)\n\n // Inject kickoff prompt (e.g., builder kickoff) on first iteration\n if (formatRetryCount === 0) {\n config.injectKickoff?.()\n }\n\n const { content: instructionContent, files } = await getAllInstructions(session.workdir, session.projectId)\n const injectedFiles: InjectedFile[] = files.map(f => ({\n path: f.path,\n content: f.content ?? '',\n source: f.source,\n }))\n\n const toolRegistry = config.getToolRegistry()\n const currentWindowMessageOptions = getCurrentWindowMessageOptions(sessionId)\n\n const requestMessages = toRequestContextMessages(getContextMessages(sessionId))\n\n if (formatRetryCount > 0) {\n const correctionMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(correctionMsgId, 'user', FORMAT_CORRECTION_PROMPT, {\n ...(currentWindowMessageOptions ?? {}),\n isSystemGenerated: true,\n messageKind: 'correction',\n }))\n eventStore.append(sessionId, createFormatRetryEvent(formatRetryCount, MAX_FORMAT_RETRIES))\n requestMessages.push({ role: 'user', content: FORMAT_CORRECTION_PROMPT, source: 'runtime' })\n }\n\n const configDir = getGlobalConfigDir(getRuntimeConfig().mode ?? 'production')\n const skills = await getEnabledSkillMetadata(configDir)\n\n const assembledRequest = config.assembleRequest({\n workdir: session.workdir,\n messages: requestMessages,\n injectedFiles,\n promptTools: toolRegistry.definitions,\n toolChoice: 'auto',\n ...(instructionContent ? { customInstructions: instructionContent } : {}),\n ...(skills.length > 0 ? { skills } : {}),\n })\n\n const assistantMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(assistantMsgId, 'assistant', undefined, currentWindowMessageOptions))\n\n const streamGen = streamLLMPure({\n messageId: assistantMsgId,\n systemPrompt: assembledRequest.systemPrompt,\n llmClient,\n messages: assembledRequest.messages,\n tools: toolRegistry.definitions,\n toolChoice: 'auto',\n signal,\n })\n\n const result = await consumeStreamGenerator(streamGen, event => {\n eventStore.append(sessionId, event)\n })\n\n if (result.xmlFormatError) {\n if (formatRetryCount < MAX_FORMAT_RETRIES) {\n formatRetryCount += 1\n continue\n } else {\n eventStore.append(sessionId, {\n type: 'chat.error',\n data: { error: 'Model repeatedly used XML tool format after 10 retries', recoverable: false },\n })\n eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, 'error'))\n throw new Error('XML tool format retry limit exceeded')\n }\n }\n\n if (result.aborted) {\n const stats = turnMetrics.buildStats(statsIdentity, mode)\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n stats,\n partial: true,\n promptContext: assembledRequest.promptContext,\n }))\n eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, 'stopped', stats))\n throw new Error('Aborted')\n }\n\n turnMetrics.addLLMCall(result.timing, result.usage.promptTokens, result.usage.completionTokens)\n sessionManager.setCurrentContextSize(sessionId, result.usage.promptTokens)\n\n if (result.toolCalls.length > 0) {\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n segments: result.segments,\n promptContext: assembledRequest.promptContext,\n }))\n\n try {\n await executeToolBatch(assistantMsgId, result.toolCalls, {\n toolRegistry,\n sessionManager,\n sessionId,\n workdir: session.workdir,\n turnMetrics,\n signal,\n onMessage,\n llmClient,\n statsIdentity,\n onToolExecuted: config.onToolExecuted,\n })\n } catch (error) {\n if (error instanceof Error && error.message === 'Aborted') {\n const stats = turnMetrics.buildStats(statsIdentity, mode)\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n stats,\n partial: true,\n promptContext: assembledRequest.promptContext,\n }))\n eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, 'stopped', stats))\n throw error\n }\n throw error\n }\n\n if (signal?.aborted) {\n const stats = turnMetrics.buildStats(statsIdentity, mode)\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n stats,\n partial: true,\n promptContext: assembledRequest.promptContext,\n }))\n eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, 'stopped', stats))\n throw new Error('Aborted')\n }\n\n const asapMessages = sessionManager.drainAsapMessages(sessionId)\n for (const asap of asapMessages) {\n const asapMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(asapMsgId, 'user', asap.content, {\n ...getCurrentWindowMessageOptions(sessionId),\n ...(asap.attachments ? { attachments: asap.attachments } : {}),\n }))\n eventStore.append(sessionId, { type: 'message.done', data: { messageId: asapMsgId } })\n }\n if (asapMessages.length > 0) {\n onMessage?.(createQueueStateMessage(sessionManager.getQueueState(sessionId)))\n }\n\n formatRetryCount = 0\n continue\n }\n\n const stats = turnMetrics.buildStats(statsIdentity, mode)\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n segments: result.segments,\n stats,\n promptContext: assembledRequest.promptContext,\n }))\n eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, 'complete', stats))\n break\n }\n}\n","/**\n * Sub-Agent Manager\n *\n * Executes sub-agents with isolated context and restricted tool sets.\n * Uses the agent registry (.agent.md files) for agent definitions and\n * standalone context builders for fresh context assembly.\n */\n\nimport type { Criterion, InjectedFile, StatsIdentity, ToolResult } from '../../shared/types.js'\nimport type { SessionManager } from '../session/index.js'\nimport type { LLMClientWithModel } from '../llm/client.js'\nimport type { ToolRegistry } from '../tools/types.js'\nimport type { ServerMessage } from '../../shared/protocol.js'\nimport type { AgentDefinition } from '../agents/types.js'\nimport { loadAllAgentsDefault, findAgentById } from '../agents/registry.js'\nimport { buildSubAgentSystemPrompt } from '../chat/prompts.js'\nimport { streamLLMPure, consumeStreamGenerator, TurnMetrics, createMessageStartEvent, createMessageDoneEvent, createChatDoneEvent } from '../chat/stream-pure.js'\nimport { executeToolBatch } from '../chat/agent-loop.js'\nimport { assembleAgentRequest, type RequestContextMessage } from '../chat/request-context.js'\nimport { getAllInstructions } from '../context/instructions.js'\nimport { getEnabledSkillMetadata } from '../skills/registry.js'\nimport { getRuntimeConfig } from '../runtime-config.js'\nimport { getGlobalConfigDir } from '../../cli/paths.js'\nimport { getEventStore, getCurrentContextWindowId } from '../events/index.js'\nimport { logger } from '../utils/logger.js'\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst RETURN_VALUE_INSTRUCTION = `\n\n## RETURN VALUE\nAs the very last thing you do, call \\`return_value\\` with a structured summary of your work. This is how your findings get passed back to the calling agent. Do not finish without calling return_value.`\n\nconst RETURN_VALUE_NUDGE = 'You must call return_value with a summary of your findings before finishing. Call return_value now.'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface NudgeConfig {\n maxConsecutiveNudges: number\n getCriteriaAwaiting: (criteria: Criterion[]) => Criterion[]\n buildNudgeContent: (criteria: Criterion[]) => string\n buildRestartContent: (criteria: Criterion[]) => string\n}\n\nexport interface SubAgentExecutionOptions {\n subAgentType: string\n prompt: string\n sessionManager: SessionManager\n sessionId: string\n llmClient: LLMClientWithModel\n toolRegistry: ToolRegistry\n turnMetrics: TurnMetrics\n statsIdentity: StatsIdentity\n signal?: AbortSignal | undefined\n onMessage?: ((msg: ServerMessage) => void) | undefined\n nudgeConfig?: NudgeConfig | undefined\n}\n\nexport interface SubAgentResult {\n content: string\n allPassed?: boolean\n failed?: Array<{ id: string; reason: string }>\n}\n\n// ============================================================================\n// Agent Definition Resolution\n// ============================================================================\n\nasync function resolveAgentDef(agentId: string): Promise<AgentDefinition> {\n const allAgents = await loadAllAgentsDefault()\n const def = findAgentById(agentId, allAgents)\n if (!def) {\n throw new Error(`Unknown sub-agent type: ${agentId}`)\n }\n if (!def.metadata.subagent) {\n throw new Error(`Agent '${agentId}' is not a sub-agent`)\n }\n return def\n}\n\n// ============================================================================\n// Manager\n// ============================================================================\n\nfunction getCurrentWindowMessageOptions(sessionId: string): { contextWindowId: string } | undefined {\n const contextWindowId = getCurrentContextWindowId(sessionId)\n return contextWindowId ? { contextWindowId } : undefined\n}\n\nexport async function executeSubAgent(options: SubAgentExecutionOptions): Promise<SubAgentResult> {\n const {\n subAgentType,\n prompt,\n sessionManager,\n sessionId,\n llmClient,\n toolRegistry,\n turnMetrics,\n statsIdentity,\n signal,\n onMessage,\n nudgeConfig,\n } = options\n\n const agentDef = await resolveAgentDef(subAgentType)\n\n const eventStore = getEventStore()\n const subAgentId = crypto.randomUUID()\n let session = sessionManager.requireSession(sessionId)\n const currentWindowMessageOptions = getCurrentWindowMessageOptions(sessionId)\n\n // Build initial context messages — prompt arrives pre-resolved with template variables\n const contextMessages: RequestContextMessage[] = [\n { role: 'user', content: prompt, source: 'runtime' },\n ]\n\n logger.debug('Sub-agent starting', { subAgentType, subAgentId, sessionId })\n\n // Emit context reset marker\n const resetMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(resetMsgId, 'user', `Fresh Context - ${agentDef.metadata.name} Sub-Agent`, {\n ...(currentWindowMessageOptions ?? {}),\n isSystemGenerated: true,\n messageKind: 'context-reset',\n subAgentId,\n subAgentType,\n }))\n eventStore.append(sessionId, { type: 'message.done', data: { messageId: resetMsgId } })\n\n // Emit context messages as events for the UI feed\n for (const msg of contextMessages) {\n const msgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(msgId, 'user', msg.content, {\n ...(currentWindowMessageOptions ?? {}),\n isSystemGenerated: true,\n messageKind: 'auto-prompt',\n subAgentId,\n subAgentType,\n }))\n eventStore.append(sessionId, { type: 'message.done', data: { messageId: msgId } })\n }\n\n // Load instructions and skills for the sub-agent system prompt\n const { content: instructionContent, files } = await getAllInstructions(session.workdir, session.projectId)\n const injectedFiles: InjectedFile[] = files.map(file => ({\n path: file.path,\n content: file.content ?? '',\n source: file.source,\n }))\n\n const configDir = getGlobalConfigDir(getRuntimeConfig().mode ?? 'production')\n const skills = await getEnabledSkillMetadata(configDir)\n\n // Build system prompt from the agent definition\n const systemPrompt = buildSubAgentSystemPrompt(\n session.workdir,\n agentDef,\n skills.length > 0 ? skills : undefined,\n )\n\n // Build custom messages for isolated context\n let customMessages: RequestContextMessage[] = [...contextMessages]\n\n let consecutiveEmptyStops = 0\n let finalContent = ''\n let returnValueContent: string | null = null\n let returnValueNudged = false\n\n for (;;) {\n if (signal?.aborted) {\n throw new Error('Aborted')\n }\n\n // Create assistant message\n const assistantMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(assistantMsgId, 'assistant', undefined, {\n ...(currentWindowMessageOptions ?? {}),\n subAgentId,\n subAgentType,\n }))\n\n // Build prompt context for diagnostics\n const promptContext = {\n systemPrompt,\n injectedFiles,\n userMessage: prompt,\n messages: customMessages.map(m => ({ role: m.role, content: m.content, source: m.source })),\n tools: toolRegistry.definitions.map(t => ({ name: t.function.name, description: t.function.description, parameters: t.function.parameters })),\n requestOptions: { toolChoice: 'auto' as const, disableThinking: true },\n }\n\n // Stream LLM response — append return_value instruction to all subagent prompts\n const streamGen = streamLLMPure({\n messageId: assistantMsgId,\n systemPrompt: systemPrompt + RETURN_VALUE_INSTRUCTION,\n llmClient,\n messages: customMessages.map(m => ({\n role: m.role,\n content: m.content,\n ...(m.toolCalls ? { toolCalls: m.toolCalls.map(tc => ({ id: tc.id, name: tc.name, arguments: tc.arguments })) } : {}),\n ...(m.toolCallId ? { toolCallId: m.toolCallId } : {}),\n })),\n tools: toolRegistry.definitions,\n toolChoice: 'auto',\n signal,\n disableThinking: true,\n })\n\n const result = await consumeStreamGenerator(streamGen, event => {\n eventStore.append(sessionId, event)\n })\n\n if (result.aborted) {\n const stats = turnMetrics.buildStats(statsIdentity, 'verifier')\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n stats,\n partial: true,\n promptContext,\n }))\n eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, 'stopped', stats))\n throw new Error('Aborted')\n }\n\n // Track metrics\n turnMetrics.addLLMCall(result.timing, result.usage.promptTokens, result.usage.completionTokens)\n\n finalContent = result.content\n\n // Add assistant response to custom context\n customMessages.push({\n role: 'assistant',\n content: result.content,\n source: 'history',\n ...(result.toolCalls.length > 0 && { toolCalls: result.toolCalls }),\n })\n\n session = sessionManager.requireSession(sessionId)\n\n // If no tool calls, check nudge/stall logic or finish\n if (result.toolCalls.length === 0) {\n if (nudgeConfig) {\n const criteriaAwaiting = nudgeConfig.getCriteriaAwaiting(session.criteria)\n if (criteriaAwaiting.length > 0) {\n if (consecutiveEmptyStops < nudgeConfig.maxConsecutiveNudges) {\n consecutiveEmptyStops += 1\n const nudgeContent = nudgeConfig.buildNudgeContent(criteriaAwaiting)\n const nudgeMsgId = crypto.randomUUID()\n\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n segments: result.segments,\n promptContext,\n }))\n eventStore.append(sessionId, createMessageStartEvent(nudgeMsgId, 'user', nudgeContent, {\n ...(currentWindowMessageOptions ?? {}),\n isSystemGenerated: true,\n messageKind: 'correction',\n subAgentId,\n subAgentType,\n }))\n eventStore.append(sessionId, { type: 'message.done', data: { messageId: nudgeMsgId } })\n customMessages = [...customMessages, { role: 'user', content: nudgeContent, source: 'runtime' }]\n continue\n }\n\n // Stalled — emit restart message\n const stalledMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageStartEvent(stalledMsgId, 'user', nudgeConfig.buildRestartContent(criteriaAwaiting), {\n ...(currentWindowMessageOptions ?? {}),\n isSystemGenerated: true,\n messageKind: 'correction',\n subAgentId,\n subAgentType,\n }))\n eventStore.append(sessionId, { type: 'message.done', data: { messageId: stalledMsgId } })\n }\n }\n\n // Nudge once if return_value was never called\n if (!returnValueContent && !returnValueNudged) {\n returnValueNudged = true\n const nudgeMsgId = crypto.randomUUID()\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n segments: result.segments,\n promptContext,\n }))\n eventStore.append(sessionId, createMessageStartEvent(nudgeMsgId, 'user', RETURN_VALUE_NUDGE, {\n ...(currentWindowMessageOptions ?? {}),\n isSystemGenerated: true,\n messageKind: 'correction',\n subAgentId,\n subAgentType,\n }))\n eventStore.append(sessionId, { type: 'message.done', data: { messageId: nudgeMsgId } })\n customMessages = [...customMessages, { role: 'user', content: RETURN_VALUE_NUDGE, source: 'runtime' }]\n continue\n }\n\n const stats = turnMetrics.buildStats(statsIdentity, 'verifier')\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n segments: result.segments,\n stats,\n promptContext,\n }))\n eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, 'complete', stats))\n break\n }\n\n // Emit message done (intermediate, no stats)\n eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {\n segments: result.segments,\n promptContext,\n }))\n\n // Execute tool calls using shared helper\n const batchResult = await executeToolBatch(assistantMsgId, result.toolCalls, {\n toolRegistry,\n sessionManager,\n sessionId,\n workdir: session.workdir,\n turnMetrics,\n signal,\n onMessage,\n })\n\n // Capture return_value content\n if (batchResult.returnValueContent) {\n returnValueContent = batchResult.returnValueContent\n }\n\n // Add tool results to custom context\n customMessages = [...customMessages, ...batchResult.toolMessages]\n\n // Check criteria changes\n session = sessionManager.requireSession(sessionId)\n\n // Reset nudge counter when tools were called\n if (nudgeConfig) {\n consecutiveEmptyStops = 0\n }\n }\n\n logger.debug('Sub-agent execution complete', {\n subAgentType,\n subAgentId,\n resultLength: finalContent.length,\n })\n\n // Build result — for verifier, include pass/fail info\n if (subAgentType === 'verifier') {\n session = sessionManager.requireSession(sessionId)\n const remaining = nudgeConfig?.getCriteriaAwaiting(session.criteria) ?? []\n const failed = session.criteria\n .filter(c => c.status.type === 'failed')\n .map(c => ({\n id: c.id,\n reason: c.status.type === 'failed' ? c.status.reason : 'unknown',\n }))\n return {\n content: returnValueContent ?? finalContent,\n allPassed: failed.length === 0 && remaining.length === 0,\n failed,\n }\n }\n\n return { content: returnValueContent ?? finalContent }\n}\n\n// Backward-compatible factory (used by sub-agent.ts)\nexport function createSubAgentManager() {\n return { executeSubAgent }\n}\n","/**\n * Call Sub-Agent Tool\n *\n * Allows main agents to invoke specialized sub-agents for specific tasks.\n * Resolves sub-agent definitions from the agent registry (.agent.md files).\n */\n\nimport type { Tool, ToolResult, ToolContext } from './types.js'\nimport type { SubAgentType } from '../sub-agents/types.js'\nimport { executeSubAgent } from '../sub-agents/manager.js'\nimport { TurnMetrics } from '../chat/stream-pure.js'\nimport { loadAllAgentsDefault, getSubAgents, findAgentById } from '../agents/registry.js'\n\nexport const callSubAgentTool: Tool = {\n name: 'call_sub_agent',\n definition: {\n type: 'function',\n function: {\n name: 'call_sub_agent',\n description: 'Call a sub-agent to perform a specialized task. Available sub-agents: verifier (verify criteria), code_reviewer (review code quality), test_generator (generate tests), debugger (analyze errors). The sub-agent will execute with isolated context and return a text result.',\n parameters: {\n type: 'object',\n properties: {\n subAgentType: {\n type: 'string',\n description: 'Type of sub-agent to call',\n },\n prompt: {\n type: 'string',\n description: 'Task description for the sub-agent. Be specific about what you need.',\n },\n },\n required: ['subAgentType', 'prompt'],\n },\n },\n },\n async execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {\n const startTime = Date.now()\n\n if (!args['subAgentType']) {\n return {\n success: false,\n error: 'Missing required parameter: subAgentType',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n\n if (!args['prompt']) {\n return {\n success: false,\n error: 'Missing required parameter: prompt',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n\n const subAgentType = args['subAgentType'] as string\n const prompt = args['prompt'] as string\n\n // Resolve agent definition from the registry (built-in + user-defined)\n const agents = await loadAllAgentsDefault()\n const agentDef = findAgentById(subAgentType, agents)\n\n if (!agentDef || !agentDef.metadata.subagent) {\n const validIds = getSubAgents(agents).map(a => a.metadata.id)\n return {\n success: false,\n error: `Unknown sub-agent type: ${subAgentType}. Available types: ${validIds.join(', ')}`,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n\n const { sessionId, sessionManager, llmClient, statsIdentity } = context\n\n if (!sessionId || !sessionManager || !llmClient) {\n return {\n success: false,\n error: 'Missing required context: sessionId, sessionManager, or llmClient',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n\n try {\n // Build tool registry from the agent definition's tools list\n const { getToolRegistryForAgent } = await import('../tools/index.js')\n const toolRegistry = getToolRegistryForAgent(agentDef)\n\n const turnMetrics = new TurnMetrics()\n\n const result = await executeSubAgent({\n subAgentType: subAgentType as SubAgentType,\n prompt,\n sessionManager,\n sessionId,\n llmClient,\n toolRegistry,\n turnMetrics,\n statsIdentity: statsIdentity ?? {\n providerId: 'unknown',\n providerName: 'Unknown',\n backend: 'unknown',\n model: llmClient.getModel(),\n },\n signal: context.signal,\n onMessage: context.onEvent,\n })\n\n return {\n success: true,\n output: result.content,\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error during sub-agent execution',\n durationMs: Date.now() - startTime,\n truncated: false,\n }\n }\n },\n}\n","/**\n * Load Skill Tool\n *\n * Allows agents to load a skill's full instructions into context on demand.\n */\n\nimport type { ToolResult } from './types.js'\nimport { createTool, type ToolHandler } from './tool-helpers.js'\nimport { loadAllSkills, findSkillById, isSkillEnabled } from '../skills/registry.js'\nimport { getRuntimeConfig } from '../runtime-config.js'\nimport { getGlobalConfigDir } from '../../cli/paths.js'\n\ninterface LoadSkillArgs {\n skillId: string\n}\n\nconst handler: ToolHandler<LoadSkillArgs> = async (args, _context, helpers): Promise<ToolResult> => {\n const { skillId } = args\n\n if (!skillId) {\n return helpers.error('Missing required parameter: skillId')\n }\n\n if (!isSkillEnabled(skillId)) {\n return helpers.error(`Skill \"${skillId}\" is not enabled.`)\n }\n\n const config = getRuntimeConfig()\n const configDir = getGlobalConfigDir(config.mode ?? 'production')\n const allSkills = await loadAllSkills(configDir)\n const skill = findSkillById(skillId, allSkills)\n\n if (!skill) {\n const available = allSkills.map(s => s.metadata.id).join(', ')\n return helpers.error(`Skill \"${skillId}\" not found. Available skills: ${available}`)\n }\n\n return helpers.success(skill.prompt)\n}\n\nexport const loadSkillTool = createTool<LoadSkillArgs>(\n 'load_skill',\n {\n type: 'function',\n function: {\n name: 'load_skill',\n description: 'Load a skill\\'s detailed instructions into context. Use this when you need domain-specific knowledge for a task. Call with the skill ID shown in the AVAILABLE SKILLS section.',\n parameters: {\n type: 'object',\n properties: {\n skillId: {\n type: 'string',\n description: 'ID of the skill to load (e.g. \"playwright-cli\")',\n },\n },\n required: ['skillId'],\n },\n },\n },\n handler\n)\n","/**\n * Return Value Tool\n *\n * Allows sub-agents to explicitly set a structured return value\n * that gets passed back to the calling agent. The sub-agent manager\n * intercepts calls to this tool and captures the content.\n */\n\nimport type { Tool, ToolResult, ToolContext } from './types.js'\n\nexport const returnValueTool: Tool = {\n name: 'return_value',\n definition: {\n type: 'function',\n function: {\n name: 'return_value',\n description:\n 'Set the return value for this sub-agent execution. Call this once at the end of your work with a structured summary of your findings. The content will be passed back to the calling agent.',\n parameters: {\n type: 'object',\n properties: {\n content: {\n type: 'string',\n description:\n 'The return value content — a structured summary of findings, results, or recommendations',\n },\n },\n required: ['content'],\n },\n },\n },\n async execute(_args: Record<string, unknown>, _context: ToolContext): Promise<ToolResult> {\n return {\n success: true,\n output: 'Return value recorded.',\n durationMs: 0,\n truncated: false,\n }\n },\n}\n","/**\n * Web Fetch Tool\n *\n * Fetches content from a URL and returns it as text, markdown, or HTML.\n * Ported from opencode's WebFetchTool with adaptations for Node.js.\n */\n\nimport TurndownService from 'turndown'\nimport { createTool } from './tool-helpers.js'\nimport { OUTPUT_LIMITS } from './types.js'\n\nconst MAX_RESPONSE_SIZE = 5 * 1024 * 1024 // 5MB\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst MAX_TIMEOUT_MS = 120_000\n\nconst USER_AGENT =\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36'\n\ninterface WebFetchArgs {\n url: string\n format?: 'text' | 'markdown' | 'html'\n timeout?: number\n}\n\nfunction buildAcceptHeader(format: string): string {\n switch (format) {\n case 'markdown':\n return 'text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1'\n case 'text':\n return 'text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1'\n case 'html':\n return 'text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1'\n default:\n return 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'\n }\n}\n\nfunction buildSignal(timeoutMs: number, contextSignal?: AbortSignal): AbortSignal {\n const timeoutSignal = AbortSignal.timeout(timeoutMs)\n if (contextSignal) {\n return AbortSignal.any([timeoutSignal, contextSignal])\n }\n return timeoutSignal\n}\n\nfunction convertHTMLToMarkdown(html: string): string {\n const turndown = new TurndownService({\n headingStyle: 'atx',\n hr: '---',\n bulletListMarker: '-',\n codeBlockStyle: 'fenced',\n emDelimiter: '*',\n })\n turndown.remove(['script', 'style', 'meta', 'link'])\n return turndown.turndown(html)\n}\n\nfunction stripHTMLTags(html: string): string {\n // Remove script/style/noscript blocks entirely\n let text = html\n .replace(/<(script|style|noscript|iframe|object|embed)[^>]*>[\\s\\S]*?<\\/\\1>/gi, '')\n // Remove all remaining HTML tags\n .replace(/<[^>]+>/g, ' ')\n // Decode common HTML entities\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&nbsp;/g, ' ')\n // Collapse whitespace\n .replace(/\\s+/g, ' ')\n .trim()\n return text\n}\n\nexport const webFetchTool = createTool<WebFetchArgs>(\n 'web_fetch',\n {\n type: 'function',\n function: {\n name: 'web_fetch',\n description:\n 'Fetch content from a URL and return it as text, markdown, or HTML. Use this to retrieve and analyze web content such as documentation, API references, or web pages.',\n parameters: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'The URL to fetch content from (must start with http:// or https://)',\n },\n format: {\n type: 'string',\n enum: ['text', 'markdown', 'html'],\n description: 'Output format. Defaults to markdown.',\n },\n timeout: {\n type: 'number',\n description: 'Optional timeout in seconds (max 120). Defaults to 30.',\n },\n },\n required: ['url'],\n },\n },\n },\n async (args, context, helpers) => {\n const url = args.url\n const format = args.format ?? 'markdown'\n\n // Validate URL\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n return helpers.error('URL must start with http:// or https://')\n }\n\n // Build timeout signal\n const timeoutMs = Math.min(\n (args.timeout ?? DEFAULT_TIMEOUT_MS / 1000) * 1000,\n MAX_TIMEOUT_MS\n )\n const signal = buildSignal(timeoutMs, context.signal)\n\n const headers = {\n 'User-Agent': USER_AGENT,\n 'Accept': buildAcceptHeader(format),\n 'Accept-Language': 'en-US,en;q=0.9',\n }\n\n // Initial fetch\n const initial = await fetch(url, { signal, headers })\n\n // Retry with honest UA if blocked by Cloudflare bot detection (TLS fingerprint mismatch)\n const response =\n initial.status === 403 && initial.headers.get('cf-mitigated') === 'challenge'\n ? await fetch(url, { signal, headers: { ...headers, 'User-Agent': 'openfox' } })\n : initial\n\n if (!response.ok) {\n return helpers.error(`Request failed with status code: ${response.status}`)\n }\n\n // Check content length before downloading body\n const contentLength = response.headers.get('content-length')\n if (contentLength && parseInt(contentLength) > MAX_RESPONSE_SIZE) {\n return helpers.error('Response too large (exceeds 5MB limit)')\n }\n\n const arrayBuffer = await response.arrayBuffer()\n if (arrayBuffer.byteLength > MAX_RESPONSE_SIZE) {\n return helpers.error('Response too large (exceeds 5MB limit)')\n }\n\n const contentType = response.headers.get('content-type') || ''\n const mime = contentType.split(';')[0]?.trim().toLowerCase() || ''\n\n // Image handling — return base64 in metadata (like read_file does)\n const isImage =\n mime.startsWith('image/') &&\n mime !== 'image/svg+xml' &&\n mime !== 'image/vnd.fastbidsheet'\n\n if (isImage) {\n const base64Data = Buffer.from(arrayBuffer).toString('base64')\n return helpers.success('Image fetched successfully', false, {\n metadata: {\n mimeType: mime,\n base64Data,\n url,\n contentType,\n },\n })\n }\n\n // Text content\n const rawContent = new TextDecoder().decode(arrayBuffer)\n let output: string\n\n switch (format) {\n case 'markdown':\n output = contentType.includes('text/html')\n ? convertHTMLToMarkdown(rawContent)\n : rawContent\n break\n case 'text':\n output = contentType.includes('text/html')\n ? stripHTMLTags(rawContent)\n : rawContent\n break\n case 'html':\n output = rawContent\n break\n default:\n output = rawContent\n }\n\n // Truncate if needed\n const maxBytes = OUTPUT_LIMITS.web_fetch.maxBytes\n let truncated = false\n if (output.length > maxBytes) {\n output = output.slice(0, maxBytes) + '\\n\\n[Output truncated due to size limit]'\n truncated = true\n }\n\n return helpers.success(output, truncated, {\n metadata: { url, contentType },\n })\n }\n)\n","import type { ToolResult } from '../../shared/types.js'\nimport type { Tool, ToolRegistry, ToolContext } from './types.js'\nimport type { AgentDefinition } from '../agents/types.js'\nimport { readFileTool } from './read.js'\nimport { writeFileTool } from './write.js'\nimport { editFileTool } from './edit.js'\nimport { runCommandTool } from './shell.js'\nimport { globTool } from './glob.js'\nimport { grepTool } from './grep.js'\nimport { gitTool } from './git.js'\nimport { askUserTool, AskUserInterrupt, provideAnswer, cancelQuestion } from './ask.js'\nimport {\n PathAccessDeniedError,\n providePathConfirmation,\n cancelPathConfirmation,\n} from './path-security.js'\nimport { completeCriterionTool, passCriterionTool, failCriterionTool } from './criterion.js'\nimport { getCriteriaTool, addCriterionTool, updateCriterionTool, removeCriterionTool } from './planner-criteria.js'\nimport { todoWriteTool, setTodoUpdateCallback, getTodos, clearTodos } from './todo.js'\nimport { callSubAgentTool } from './sub-agent.js'\nimport { loadSkillTool } from './load-skill.js'\nimport { returnValueTool } from './return-value.js'\nimport { webFetchTool } from './web-fetch.js'\nimport { logger } from '../utils/logger.js'\n\n// ============================================================================\n// Registry Creation\n// ============================================================================\n\nfunction createRegistryFromTools(tools: Tool[]): ToolRegistry {\n const toolMap = new Map<string, Tool>()\n\n for (const tool of tools) {\n toolMap.set(tool.name, tool)\n }\n\n return {\n tools,\n definitions: tools.map(t => t.definition),\n\n async execute(\n name: string,\n args: Record<string, unknown>,\n context: ToolContext\n ): Promise<ToolResult> {\n const tool = toolMap.get(name)\n\n if (!tool) {\n return {\n success: false,\n error: `Unknown tool: ${name}. Available tools: ${tools.map(t => t.name).join(', ')}`,\n durationMs: 0,\n truncated: false,\n }\n }\n\n logger.debug('Executing tool', { tool: name, args })\n\n try {\n const result = await tool.execute(args, context)\n\n logger.debug('Tool completed', {\n tool: name,\n success: result.success,\n durationMs: result.durationMs,\n })\n\n return result\n } catch (error) {\n if (error instanceof AskUserInterrupt) {\n throw error\n }\n if (error instanceof PathAccessDeniedError) {\n throw error\n }\n\n logger.error('Tool execution error', { tool: name, error })\n\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n durationMs: 0,\n truncated: false,\n }\n }\n },\n }\n}\n\n// All tools by name for dynamic registry creation\nconst allToolsByName = new Map<string, Tool>([\n ...[\n readFileTool, writeFileTool, editFileTool, runCommandTool,\n globTool, grepTool, gitTool, askUserTool,\n completeCriterionTool, passCriterionTool, failCriterionTool,\n getCriteriaTool, addCriterionTool, updateCriterionTool, removeCriterionTool,\n todoWriteTool, callSubAgentTool, loadSkillTool, returnValueTool, webFetchTool,\n ].map(t => [t.name, t] as const),\n])\n\n// ============================================================================\n// Agent-Based Registry Creation\n// ============================================================================\n\n/**\n * Create a tool registry for a subagent from a list of tool names.\n * Sub-agents automatically get return_value added.\n */\nexport function getToolRegistryForSubAgent(toolNames: string[]): ToolRegistry {\n const tools: Tool[] = []\n for (const name of toolNames) {\n const tool = allToolsByName.get(name)\n if (tool) {\n tools.push(tool)\n }\n }\n if (!tools.some(t => t.name === 'return_value')) {\n const rv = allToolsByName.get('return_value')\n if (rv) tools.push(rv)\n }\n return createRegistryFromTools(tools)\n}\n\n/**\n * Create a tool registry for an agent definition.\n * Uses the agent's tools list to filter from the global tool registry.\n * Sub-agents automatically get return_value added.\n */\nexport function getToolRegistryForAgent(agentDef: AgentDefinition): ToolRegistry {\n if (agentDef.metadata.subagent) {\n return getToolRegistryForSubAgent(agentDef.metadata.tools)\n }\n const tools: Tool[] = []\n for (const name of agentDef.metadata.tools) {\n const tool = allToolsByName.get(name)\n if (tool) {\n tools.push(tool)\n }\n }\n return createRegistryFromTools(tools)\n}\n\n/**\n * Create a generic tool registry with all available tools.\n */\nexport function createToolRegistry(): ToolRegistry {\n return createRegistryFromTools(Array.from(allToolsByName.values()))\n}\n\n// Re-export types and utilities\nexport type { Tool, ToolRegistry, ToolContext } from './types.js'\nexport { AskUserInterrupt, provideAnswer, cancelQuestion, cancelQuestionsForSession } from './ask.js'\nexport { setTodoUpdateCallback, getTodos, clearTodos } from './todo.js'\nexport {\n PathAccessDeniedError,\n providePathConfirmation,\n cancelPathConfirmation,\n cancelPathConfirmationsForSession,\n addAllowedPaths,\n clearAllowedPaths,\n requestPathAccess,\n} from './path-security.js'\n"],"mappings":"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","names":["readFile","stat","resolve","match","resolve","handler","resolve","resolve","mimeType","stat","readFile","contentHash","readFile","writeFile","readFile","writeFile","spawn","resolve","isAbsolute","resolve","isAbsolute","resolve","spawn","fg","readFile","resolve","fg","resolve","readFile","spawn","resolve","isAbsolute","isAbsolute","resolve","spawn","resolve","readFile","writeFile","mkdir","join","dirname","dirname","join","mkdir","readFile","join","mkdir","writeFile","resolve","readFile","access","join","dirname","constants","dirname","join","readFile","readFile","access","constants","systemPrompt","readdir","readFile","writeFile","copyFile","mkdir","access","unlink","join","dirname","constants","fileURLToPath","matter","__bundleDir","dirname","fileURLToPath","DEFAULTS_DIR","join","DEFAULTS_DIR_ALT","access","constants","mkdir","readdir","copyFile","readFile","matter","writeFile","unlink","getCurrentWindowMessageOptions","toolResult","MAX_FORMAT_RETRIES","FORMAT_CORRECTION_PROMPT","toRequestContextMessages","stats","getCurrentWindowMessageOptions","getToolRegistryForAgent"]}