@cryer/star-cli 0.3.13 → 0.3.14

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 (32) hide show
  1. package/README.md +17 -5
  2. package/README.zh-CN.md +15 -5
  3. package/dist/{chunk-6PLZ3FLG.js → chunk-2ISBRSBA.js} +375 -235
  4. package/dist/chunk-2ISBRSBA.js.map +1 -0
  5. package/dist/{chunk-QELCMXQX.js → chunk-4HANB3DJ.js} +381 -212
  6. package/dist/chunk-4HANB3DJ.js.map +1 -0
  7. package/dist/{chunk-OF42OVM4.js → chunk-54KH4JSO.js} +204 -125
  8. package/dist/chunk-54KH4JSO.js.map +1 -0
  9. package/dist/{chunk-RD4S5LKM.js → chunk-CECMCDZG.js} +43 -30
  10. package/dist/chunk-CECMCDZG.js.map +1 -0
  11. package/dist/{chunk-MVAMPVTA.js → chunk-KILMI3S5.js} +13 -5
  12. package/dist/{chunk-MVAMPVTA.js.map → chunk-KILMI3S5.js.map} +1 -1
  13. package/dist/{chunk-NMB3TNQU.js → chunk-NRLGGA7L.js} +507 -175
  14. package/dist/chunk-NRLGGA7L.js.map +1 -0
  15. package/dist/loop-M64D5YII.js +17 -0
  16. package/dist/main.js +177 -131
  17. package/dist/main.js.map +1 -1
  18. package/dist/{provider-EWMUPBLH.js → provider-3Z4B7RMF.js} +2 -2
  19. package/dist/{repl-ZABBT33D.js → repl-N2H7JDSH.js} +617 -327
  20. package/dist/repl-N2H7JDSH.js.map +1 -0
  21. package/dist/{tools-4OLADVDR.js → tools-5RKM7ZI3.js} +3 -3
  22. package/package.json +1 -1
  23. package/dist/chunk-6PLZ3FLG.js.map +0 -1
  24. package/dist/chunk-NMB3TNQU.js.map +0 -1
  25. package/dist/chunk-OF42OVM4.js.map +0 -1
  26. package/dist/chunk-QELCMXQX.js.map +0 -1
  27. package/dist/chunk-RD4S5LKM.js.map +0 -1
  28. package/dist/loop-DWQ5WTPD.js +0 -17
  29. package/dist/repl-ZABBT33D.js.map +0 -1
  30. /package/dist/{loop-DWQ5WTPD.js.map → loop-M64D5YII.js.map} +0 -0
  31. /package/dist/{provider-EWMUPBLH.js.map → provider-3Z4B7RMF.js.map} +0 -0
  32. /package/dist/{tools-4OLADVDR.js.map → tools-5RKM7ZI3.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools/fs/edit.ts","../src/tools/fs/glob.ts","../src/tools/fs/grep.ts","../src/tools/fs/outline.ts","../src/tools/fs/read.ts","../src/tools/fs/read-image.ts","../src/tools/fs/write.ts","../src/tools/screenshot.ts","../src/core/screenshot.ts","../src/tools/tasks.ts","../src/tasks/format.ts","../src/tools/web/search.ts","../src/llm/debug.ts","../src/tools/registry.ts","../src/tools/index.ts"],"sourcesContent":["import { readFile, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport type { Tool } from \"../types\";\nimport {\n MAX_SNAPSHOT_CONTENT_BYTES,\n currentTurnMessageIndex,\n currentTurnSeq,\n nextSnapshotId,\n pushSnapshot,\n} from \"./snapshots\";\nimport { isSensitivePath, realpathForCheck } from \"./util\";\n\n// Same heuristic as read_file: a NUL in the first 8KB marks a binary file.\n// edit_file writes back as UTF-8, so an ASCII old_string that happens to\n// match inside a binary file would corrupt it irreversibly (U+FFFD\n// replacement on undecodable bytes), and snapshots cannot restore it.\nconst BINARY_PROBE_BYTES = 8192;\n\nfunction countOccurrences(haystack: string, needle: string): number {\n let count = 0;\n let idx = haystack.indexOf(needle);\n while (idx !== -1) {\n count += 1;\n idx = haystack.indexOf(needle, idx + needle.length);\n }\n return count;\n}\n\nfunction normalizeEol(text: string): string {\n return text.replace(/\\r\\n/g, \"\\n\");\n}\n\nfunction normalizedOffsets(text: string): number[] {\n const offsets = [0];\n for (let i = 0; i < text.length; i++) {\n if (text[i] === \"\\r\" && text[i + 1] === \"\\n\") i++;\n offsets.push(i + 1);\n }\n return offsets;\n}\n\nfunction replacePreservingEol(\n raw: string,\n normalized: string,\n offsets: number[],\n oldString: string,\n newString: string,\n replaceAll: boolean,\n useCrlf: boolean,\n): string {\n const replacement = useCrlf ? newString.replace(/\\n/g, \"\\r\\n\") : newString;\n let normalizedFrom = 0;\n let rawFrom = 0;\n let updated = \"\";\n while (true) {\n const match = normalized.indexOf(oldString, normalizedFrom);\n if (match < 0) break;\n const rawStart = offsets[match] ?? raw.length;\n const rawEnd = offsets[match + oldString.length] ?? raw.length;\n updated += raw.slice(rawFrom, rawStart) + replacement;\n rawFrom = rawEnd;\n normalizedFrom = match + oldString.length;\n if (!replaceAll) break;\n }\n return updated + raw.slice(rawFrom);\n}\n\nconst schema = z.object({\n path: z.string().describe(\"File path, absolute or relative to the working directory\"),\n old_string: z.string().min(1).describe(\"Exact text to replace\"),\n new_string: z.string().describe(\"Replacement text\"),\n replace_all: z\n .boolean()\n .optional()\n .describe(\"Replace every occurrence instead of requiring a unique match\"),\n});\n\nexport const editFileTool: Tool<typeof schema> = {\n name: \"edit_file\",\n description:\n \"Replace an exact string in a file. Fails if old_string is not found or matches multiple locations unless replace_all is set. Refuses binary files and files over 5MB.\",\n permission: \"write\",\n parameters: schema,\n async execute(args, ctx) {\n const filePath = path.resolve(ctx.cwd, args.path);\n // Same symlink defense as read_file/write_file: refuse when the resolved\n // target is a sensitive file, in-tool so yolo mode and hooks can't\n // bypass the gate's raw-path name check.\n const resolvedPath = await realpathForCheck(filePath);\n if (isSensitivePath(filePath) || (resolvedPath !== null && isSensitivePath(resolvedPath))) {\n return { content: `Refused to edit sensitive file: ${args.path}`, isError: true };\n }\n let content: string;\n let readMtimeMs: number;\n try {\n const st = await stat(filePath);\n // Past the snapshot content cap /undo keeps only metadata, so an edit\n // would be unrecoverable — refuse instead of writing blind.\n if (st.size > MAX_SNAPSHOT_CONTENT_BYTES) {\n return {\n content: `Refused to edit ${args.path}: file is ${st.size} bytes, over the 5MB limit (edits past it could not be restored by /undo)`,\n isError: true,\n };\n }\n readMtimeMs = st.mtimeMs;\n const buf = await readFile(filePath);\n if (buf.subarray(0, BINARY_PROBE_BYTES).includes(0)) {\n return { content: `Refused to edit binary file: ${args.path}`, isError: true };\n }\n content = buf.toString(\"utf8\");\n } catch (err) {\n return { content: `Failed to read ${args.path}: ${(err as Error).message}`, isError: true };\n }\n // Dual-path matching: exact first. When that finds nothing, retry in an\n // LF-normalized space — read_file strips \\r for display, so on a CRLF\n // file the model can only phrase a multi-line old_string with \\n, which\n // never matches the raw bytes. Uniqueness/replace_all counts are judged\n // in the normalized space. Fallback replacements use normalized-to-raw\n // offsets so line endings outside the edited ranges stay byte-identical.\n let working = content;\n let oldString = args.old_string;\n let newString = args.new_string;\n let offsets: number[] | null = null;\n let useCrlf = false;\n let count = countOccurrences(working, oldString);\n if (count === 0) {\n const normalizedContent = normalizeEol(content);\n const normalizedOld = normalizeEol(oldString);\n const normalizedCount = countOccurrences(normalizedContent, normalizedOld);\n if (normalizedCount > 0) {\n const crlfCount = countOccurrences(content, \"\\r\\n\");\n const loneLfCount = countOccurrences(content.replace(/\\r\\n/g, \"\"), \"\\n\");\n useCrlf = crlfCount > 0 && crlfCount >= loneLfCount;\n offsets = normalizedOffsets(content);\n working = normalizedContent;\n oldString = normalizedOld;\n newString = normalizeEol(newString);\n count = normalizedCount;\n }\n }\n if (count === 0) {\n return { content: `old_string not found in ${args.path}`, isError: true };\n }\n if (count > 1 && !args.replace_all) {\n return {\n content: `old_string matches ${count} locations in ${args.path}; provide more surrounding context or set replace_all`,\n isError: true,\n };\n }\n const updated = offsets\n ? replacePreservingEol(\n content,\n working,\n offsets,\n oldString,\n newString,\n args.replace_all === true,\n useCrlf,\n )\n : args.replace_all\n ? working.split(oldString).join(newString)\n : working.replace(oldString, () => newString);\n try {\n // Guard against a silent overwrite when the file changed on disk\n // between our read and our write.\n const now = await stat(filePath).catch(() => null);\n if (!now || now.mtimeMs !== readMtimeMs) {\n return {\n content: `Failed to edit ${args.path}: file changed since it was read; re-read and retry`,\n isError: true,\n };\n }\n await writeFile(filePath, updated, \"utf8\");\n } catch (err) {\n return { content: `Failed to write ${args.path}: ${(err as Error).message}`, isError: true };\n }\n const contentTooLarge = Buffer.byteLength(content, \"utf8\") > MAX_SNAPSHOT_CONTENT_BYTES;\n await pushSnapshot({\n id: nextSnapshotId(),\n path: filePath,\n existed: true,\n content: contentTooLarge ? null : content,\n contentTooLarge: contentTooLarge || undefined,\n toolName: \"edit_file\",\n timestamp: Date.now(),\n turn: ctx.snapshotContext?.turn ?? currentTurnSeq(),\n messageIndex: ctx.snapshotContext?.messageIndex ?? currentTurnMessageIndex(),\n owner: ctx.snapshotContext?.owner ?? \"root\",\n });\n return { content: `Edited ${args.path}: ${count} replacement${count > 1 ? \"s\" : \"\"}` };\n },\n};\n","import path from \"node:path\";\nimport { z } from \"zod\";\nimport type { Tool } from \"../types\";\nimport { createIgnorePredicate, matchesGlob, walkFiles } from \"./util\";\n\nconst MAX_RESULTS = 100;\n\nconst GLOB_MAGIC = /[*?[{]/;\n\n// Leading directory segments without glob magic (everything before the final\n// segment, which may name a file) let src/**/*.ts walk <root>/src directly\n// instead of descending the whole tree. Matched paths are re-prefixed before\n// matching and display so output stays relative to the requested root.\nfunction literalDirPrefix(pattern: string): string[] {\n const segments = pattern.replace(/\\\\/g, \"/\").split(\"/\").slice(0, -1);\n const out: string[] = [];\n for (const segment of segments) {\n if (segment === \"\" || GLOB_MAGIC.test(segment)) {\n break;\n }\n out.push(segment);\n }\n return out;\n}\n\nconst schema = z.object({\n pattern: z\n .string()\n .describe(\n \"Glob pattern, e.g. 'src/**/*.ts'. Supports **, *, ?, {a,b} braces and [abc] character classes. A bare pattern like '*.ts' matches basenames recursively.\",\n ),\n path: z.string().optional().describe(\"Directory to search, defaults to the working directory\"),\n});\n\n// A \"..\" segment would walk above the requested root (the literal-prefix\n// optimization resolves it against the search root), escaping the cwd\n// boundary the permission gate enforces on the path argument.\nfunction hasParentSegment(pattern: string): boolean {\n return pattern\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .some((segment) => segment === \"..\");\n}\n\nexport const globTool: Tool<typeof schema> = {\n name: \"glob\",\n description:\n \"Find files by glob pattern. Skips node_modules, .git and dist. Follows symlinks that stay inside the working directory. Returns paths sorted by modification time, newest first, up to 100 results.\",\n permission: \"read\",\n parameters: schema,\n async execute(args, ctx) {\n if (hasParentSegment(args.pattern)) {\n return {\n content: `Refused glob pattern with '..' segment: ${args.pattern}`,\n isError: true,\n };\n }\n const root = path.resolve(ctx.cwd, args.path ?? \".\");\n const prefixSegments = literalDirPrefix(args.pattern);\n const walkRoot = prefixSegments.length > 0 ? path.join(root, ...prefixSegments) : root;\n const prefix = prefixSegments.length > 0 ? `${prefixSegments.join(\"/\")}/` : \"\";\n let matched: Array<{ rel: string; mtimeMs: number }> = [];\n const { aborted } = await walkFiles(walkRoot, undefined, {\n ignore: createIgnorePredicate(ctx.cwd),\n signal: ctx.abortSignal,\n symlinkBoundary: ctx.cwd,\n nestedIgnore: true,\n collect: false,\n onBatch: (files) => {\n matched = matched\n .concat(files.filter((f) => matchesGlob(args.pattern, `${prefix}${f.rel}`)))\n .sort((a, b) => b.mtimeMs - a.mtimeMs || (a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0))\n .slice(0, MAX_RESULTS);\n },\n });\n if (matched.length === 0) {\n return {\n content: aborted\n ? `Search interrupted before any files matched pattern: ${args.pattern}`\n : `No files matched pattern: ${args.pattern}`,\n };\n }\n const lines = matched.map((f) => `${prefix}${f.rel}`);\n if (aborted) {\n lines.push(\"... (interrupted: partial results)\");\n }\n return { content: lines.join(\"\\n\") };\n },\n};\n","import { readFile, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport type { Tool } from \"../types\";\nimport {\n WALK_CONCURRENCY,\n type WalkedFile,\n createIgnorePredicate,\n isSensitivePath,\n matchesGlob,\n walkFiles,\n} from \"./util\";\n\nconst MAX_MATCHES = 250;\nconst MAX_LINE_CHARS = 500;\nconst MAX_PATTERN_CHARS = 200;\nconst MAX_FILE_BYTES = 5 * 1024 * 1024;\n\n// Very long patterns are usually pasted blobs, and a quantified group is the\n// classic catastrophic-backtracking shape — reject both before they hang the\n// process on a large tree.\nfunction isTooComplexPattern(pattern: string): boolean {\n return (\n pattern.length > MAX_PATTERN_CHARS || /\\)[+*]/.test(pattern) || /\\)\\{\\d+(,\\d*)?\\}/.test(pattern)\n );\n}\n\nconst schema = z.object({\n pattern: z.string().describe(\"Regular expression to search for\"),\n path: z\n .string()\n .optional()\n .describe(\"File or directory to search, defaults to the working directory\"),\n glob: z\n .string()\n .optional()\n .describe(\"Glob pattern to filter which files are searched, e.g. '*.ts'\"),\n ignoreCase: z.boolean().optional().describe(\"Case-insensitive matching\"),\n});\n\nexport const grepTool: Tool<typeof schema> = {\n name: \"grep\",\n description:\n \"Search file contents with a regular expression. Outputs 'file:line:content', up to 250 matches. Skips binary files, files over 5MB, sensitive files, node_modules and .git. Follows symlinks that stay inside the working directory.\",\n permission: \"read\",\n parameters: schema,\n async execute(args, ctx) {\n if (isTooComplexPattern(args.pattern)) {\n return {\n content: `pattern too complex: keep it under ${MAX_PATTERN_CHARS} characters and avoid quantified groups like (a+)+`,\n isError: true,\n };\n }\n let re: RegExp;\n try {\n re = new RegExp(args.pattern, args.ignoreCase ? \"i\" : \"\");\n } catch (err) {\n return { content: `Invalid regular expression: ${(err as Error).message}`, isError: true };\n }\n const root = path.resolve(ctx.cwd, args.path ?? \".\");\n let interrupted = false;\n const out: string[] = [];\n let truncated = false;\n let skippedLarge = 0;\n const scanFile = async (file: WalkedFile): Promise<string[] | null> => {\n const st = await stat(file.abs).catch(() => null);\n if (!st) return null;\n if (st.size > MAX_FILE_BYTES) {\n skippedLarge += 1;\n return null;\n }\n let buf: Buffer;\n try {\n buf = await readFile(file.abs);\n } catch {\n return null;\n }\n if (buf.includes(0)) return null;\n const hits: string[] = [];\n const lines = buf.toString(\"utf8\").split(\"\\n\");\n for (let i = 0; i < lines.length && hits.length < MAX_MATCHES; i += 1) {\n const line = lines[i] ?? \"\";\n if (re.test(line)) {\n const shown = line.length > MAX_LINE_CHARS ? `${line.slice(0, MAX_LINE_CHARS)}…` : line;\n hits.push(`${file.rel}:${i + 1}:${shown}`);\n }\n }\n return hits;\n };\n const scanBatch = async (batch: WalkedFile[]): Promise<boolean> => {\n const files = batch.filter(\n (f) =>\n !isSensitivePath(f.abs) &&\n (f.resolved === undefined || !isSensitivePath(f.resolved)) &&\n (!args.glob || matchesGlob(args.glob, f.rel)),\n );\n outer: for (let i = 0; i < files.length; i += WALK_CONCURRENCY) {\n if (ctx.abortSignal?.aborted) {\n interrupted = true;\n return false;\n }\n const chunk = await Promise.all(\n files.slice(i, i + WALK_CONCURRENCY).map((file) => scanFile(file)),\n );\n for (const hits of chunk) {\n for (const hit of hits ?? []) {\n out.push(hit);\n if (out.length >= MAX_MATCHES) {\n truncated = true;\n break outer;\n }\n }\n }\n }\n return !truncated && !ctx.abortSignal?.aborted;\n };\n try {\n const st = await stat(root);\n if (st.isFile()) {\n const resolved = await realpath(root).catch(() => null);\n if (isSensitivePath(root) || (resolved !== null && isSensitivePath(resolved))) {\n return {\n content: `Refused to grep sensitive file: ${args.path ?? \".\"}`,\n isError: true,\n };\n }\n await scanBatch([{ abs: root, rel: path.basename(root), mtimeMs: st.mtimeMs }]);\n } else {\n const walked = await walkFiles(root, undefined, {\n ignore: createIgnorePredicate(ctx.cwd),\n withMtime: false,\n signal: ctx.abortSignal,\n symlinkBoundary: ctx.cwd,\n nestedIgnore: true,\n collect: false,\n onBatch: scanBatch,\n });\n interrupted = walked.aborted;\n }\n } catch {\n return { content: `Path not found: ${args.path ?? \".\"}`, isError: true };\n }\n const skippedNote =\n skippedLarge > 0 ? `(skipped ${skippedLarge} file(s) larger than 5MB)` : null;\n if (out.length === 0) {\n return {\n content: `No matches for pattern: ${args.pattern}${skippedNote ? ` ${skippedNote}` : \"\"}${interrupted ? \" (search interrupted)\" : \"\"}`,\n };\n }\n if (truncated) {\n out.push(`... (truncated: more than ${MAX_MATCHES} matches)`);\n } else if (interrupted) {\n out.push(\"... (interrupted: partial results)\");\n }\n if (skippedNote) {\n out.push(`... ${skippedNote}`);\n }\n return { content: out.join(\"\\n\") };\n },\n};\n","import { readFile, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport type { Tool } from \"../types\";\nimport {\n WALK_CONCURRENCY,\n type WalkedFile,\n createIgnorePredicate,\n isSensitivePath,\n walkFiles,\n} from \"./util\";\n\nconst MAX_DIR_FILES = 60;\nconst MAX_FILE_BYTES = 2 * 1024 * 1024;\nconst MAX_SYMBOLS_PER_FILE = 150;\nconst MAX_LINE_CHARS = 200;\nconst MAX_OUT_CHARS = 30 * 1024;\n// Nesting is shown through original indentation, capped so deep nesting\n// cannot push the signature out of view.\nconst MAX_INDENT_CHARS = 8;\n\n// Declaration-spotting regexes per language family. This is deliberately a\n// lightweight skeleton extractor, not a parser: it misses some declarations\n// (multi-line signatures, macros) and that is fine — the outline exists so\n// the model can navigate to line ranges cheaply; read_file still owns exact\n// content. Patterns are anchored to declaration shapes (leading modifiers,\n// trailing \"{\"/\";\"/end-of-line) so ordinary calls and control flow don't\n// match, and are tested against the raw line because member rules key on\n// indentation (methods are indented, top-level declarations are not).\nconst KEYWORD_EXCLUSION =\n \"(?!if\\\\b|for\\\\b|while\\\\b|switch\\\\b|catch\\\\b|return\\\\b|else\\\\b|do\\\\b|new\\\\b|throw\\\\b)\";\n\nconst TS_MEMBER_MODIFIERS =\n \"(?:public|private|protected|static|async|readonly|get|set|override|abstract)\";\n\nconst TS_LIKE = [\n /^(?:export\\s+)?(?:default\\s+)?(?:async\\s+)?function\\*?\\s+[\\w$]+/,\n /^(?:export\\s+)?(?:abstract\\s+)?class\\s+[\\w$]+/,\n /^(?:export\\s+)?interface\\s+[\\w$]+/,\n /^(?:export\\s+)?type\\s+[\\w$]+\\s*=/,\n /^(?:export\\s+)?(?:const\\s+)?enum\\s+[\\w$]+/,\n // const handler = (a, b) => / const handler = async (a) => — top level\n // only; locals inside functions are navigation noise.\n /^(?:export\\s+)?(?:const|let|var)\\s+[\\w$]+\\s*=\\s*(?:async\\s*)?(?:\\([^)]*\\)|[\\w$]+)\\s*=>/,\n // Indented class/object members with a body: name<T>(args): ReturnType {\n // — control flow excluded by the keyword lookahead, dotted calls\n // (this.x()) fail the anchored name-then-paren shape.\n new RegExp(\n `^\\\\s+(?:${TS_MEMBER_MODIFIERS}\\\\s+)*${KEYWORD_EXCLUSION}[\\\\w$]+(?:<[^>]*>)?\\\\s*\\\\([^)]*\\\\)\\\\s*(?::\\\\s*[^{}=;]+?)?\\\\s*(?:\\\\{\\\\s*)?\\\\}?\\\\s*$`,\n ),\n // Bodyless signatures (interface members, overloads): only when the\n // parens carry type annotations or an explicit return type follows —\n // otherwise indented calls like clearTimeout(timer); or finalize();\n // would leak into the outline.\n new RegExp(\n `^\\\\s+(?:${TS_MEMBER_MODIFIERS}\\\\s+)*${KEYWORD_EXCLUSION}[\\\\w$]+(?:<[^>]*>)?\\\\s*(?:\\\\([^)]*:[^)]*\\\\)\\\\s*(?::\\\\s*[^{}=;]+?)?|\\\\([^)]*\\\\)\\\\s*:\\\\s*[^{}=;]+?)\\\\s*;\\\\s*$`,\n ),\n];\n\nconst LANGUAGE_PATTERNS: Record<string, RegExp[]> = {\n ts: TS_LIKE,\n python: [/^\\s*(?:async\\s+)?def\\s+\\w+\\s*\\(/, /^\\s*class\\s+\\w+/],\n go: [/^func\\s+(?:\\([^)]*\\)\\s*)?\\w+\\s*\\(/, /^type\\s+\\w+\\s+(?:struct|interface)\\b/],\n rust: [\n /^\\s*(?:pub(?:\\([^)]*\\))?\\s+)?(?:async\\s+)?fn\\s+\\w+/,\n /^\\s*(?:pub\\s+)?(?:struct|enum|trait|union)\\s+\\w+/,\n ],\n jvm: [\n /^\\s*(?:(?:public|private|protected|internal|static|final|abstract|sealed|open|suspend|inline|override|async|partial|virtual|extern)\\s+)*(?:class|interface|enum|struct|record|object)\\s+\\w+/,\n /^\\s*(?:(?:public|private|protected|internal|open|suspend|inline|override)\\s+)*fun\\s+\\w+\\s*\\(/,\n new RegExp(\n `^\\\\s+(?:(?:public|private|protected|internal|static|final|abstract|sealed|open|suspend|inline|override|async|partial|virtual)\\\\s+)*[\\\\w<>[\\\\],.?]+\\\\s+${KEYWORD_EXCLUSION}\\\\w+\\\\s*\\\\([^;{}]*\\\\)\\\\s*(?:\\\\{|;|=>|$)`,\n ),\n ],\n cpp: [\n /^\\s*(?:class|struct|enum|union|namespace)\\s+\\w+/,\n new RegExp(\n `^\\\\s*(?:(?:static|inline|virtual|constexpr|extern|friend|explicit)\\\\s+)*[\\\\w:<>,~*&\\\\s]+?\\\\s+${KEYWORD_EXCLUSION}[\\\\w:~]+\\\\s*\\\\([^;]*\\\\)\\\\s*(?:const\\\\s*)?(?:noexcept\\\\s*)?(?:\\\\{|;|$)`,\n ),\n ],\n ruby: [/^\\s*def\\s+[\\w.=!?]+/, /^\\s*(?:class|module)\\s+[\\w:]+/],\n php: [\n /^\\s*(?:(?:public|private|protected|static|abstract|final)\\s+)*function\\s+\\w+\\s*\\(/,\n /^\\s*(?:(?:abstract|final)\\s+)?(?:class|interface|trait|enum)\\s+\\w+/,\n ],\n shell: [/^\\s*(?:function\\s+)?[\\w.-]+\\s*\\(\\s*\\)\\s*(?:\\{|$)/],\n swift: [\n /^\\s*(?:(?:public|private|internal|open|fileprivate|static|class|override|mutating|final)\\s+)*func\\s+\\w+\\s*\\(/,\n /^\\s*(?:(?:public|private|internal|open|final)\\s+)*(?:class|struct|enum|protocol|extension)\\s+\\w+/,\n ],\n scala: [\n /^\\s*(?:(?:private|protected|override|final|abstract|implicit)\\s+)*def\\s+\\w+\\s*[(\\[]/,\n /^\\s*(?:(?:abstract|final|sealed)\\s+)*(?:class|trait|object)\\s+\\w+/,\n ],\n};\n\nconst EXT_TO_LANGUAGE: Record<string, keyof typeof LANGUAGE_PATTERNS> = {\n ts: \"ts\",\n tsx: \"ts\",\n mts: \"ts\",\n cts: \"ts\",\n js: \"ts\",\n jsx: \"ts\",\n mjs: \"ts\",\n cjs: \"ts\",\n py: \"python\",\n pyi: \"python\",\n go: \"go\",\n rs: \"rust\",\n java: \"jvm\",\n kt: \"jvm\",\n kts: \"jvm\",\n cs: \"jvm\",\n c: \"cpp\",\n h: \"cpp\",\n cc: \"cpp\",\n cpp: \"cpp\",\n cxx: \"cpp\",\n hpp: \"cpp\",\n hh: \"cpp\",\n rb: \"ruby\",\n php: \"php\",\n sh: \"shell\",\n bash: \"shell\",\n zsh: \"shell\",\n swift: \"swift\",\n scala: \"scala\",\n sc: \"scala\",\n};\n\nfunction languageOf(filePath: string): RegExp[] | undefined {\n const name = path.basename(filePath);\n // Minified and machine-generated bundles are declaration soup; outlining\n // a bundled dependency is never useful navigation.\n if (/\\.(min|bundle|generated)\\.[^.]+$/.test(name)) return undefined;\n const ext = name.split(\".\").pop()?.toLowerCase() ?? \"\";\n const lang = EXT_TO_LANGUAGE[ext];\n return lang === undefined ? undefined : LANGUAGE_PATTERNS[lang];\n}\n\ninterface SymbolLine {\n line: number;\n indent: string;\n text: string;\n}\n\nfunction extractSymbols(text: string, patterns: RegExp[]): SymbolLine[] {\n const out: SymbolLine[] = [];\n const lines = text.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const raw = (lines[i] ?? \"\").replace(/\\r$/, \"\");\n const trimmed = raw.trim();\n if (trimmed === \"\") continue;\n if (!patterns.some((re) => re.test(raw))) continue;\n const indent = raw.slice(0, raw.length - raw.trimStart().length).slice(0, MAX_INDENT_CHARS);\n const shown =\n trimmed.length > MAX_LINE_CHARS ? `${trimmed.slice(0, MAX_LINE_CHARS)}…` : trimmed;\n out.push({ line: i + 1, indent, text: shown });\n }\n return out;\n}\n\nasync function outlineFile(\n abs: string,\n): Promise<{ symbols: SymbolLine[]; total: number } | { binary: true } | { unreadable: true }> {\n const st = await stat(abs).catch(() => null);\n if (!st || st.size > MAX_FILE_BYTES) return { unreadable: true };\n const buf = await readFile(abs).catch(() => null);\n if (!buf) return { unreadable: true };\n if (buf.includes(0)) return { binary: true };\n const patterns = languageOf(abs);\n if (!patterns) return { unreadable: true };\n const all = extractSymbols(buf.toString(\"utf8\"), patterns);\n return { symbols: all.slice(0, MAX_SYMBOLS_PER_FILE), total: all.length };\n}\n\nfunction formatSymbols(symbols: SymbolLine[], total: number): string[] {\n const out = symbols.map((s) => `${s.line}\\t${s.indent}${s.text}`);\n if (total > symbols.length) {\n out.push(`... (${total - symbols.length} more declarations)`);\n }\n return out;\n}\n\nconst schema = z.object({\n path: z\n .string()\n .optional()\n .describe(\"Source file or directory to outline (default: the working directory)\"),\n maxFiles: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(`Directory mode: maximum files to include (default ${MAX_DIR_FILES})`),\n});\n\nexport const codeOutlineTool: Tool<typeof schema> = {\n name: \"code_outline\",\n description:\n \"List the declaration skeleton of a source file or directory (functions, classes, methods, types) with line numbers, without reading the bodies. Cheaper than read_file for navigating unfamiliar code: outline first, then read_file the exact line ranges you need. Supports TS/JS, Python, Go, Rust, Java/Kotlin/C#, C/C++, Ruby, PHP, Shell, Swift, Scala.\",\n permission: \"read\",\n parameters: schema,\n async execute(args, ctx) {\n const root = path.resolve(ctx.cwd, args.path ?? \".\");\n const st = await stat(root).catch(() => null);\n if (!st) {\n return { content: `Path not found: ${args.path ?? \".\"}`, isError: true };\n }\n\n if (st.isFile()) {\n // Same refusal as read_file: the raw name and the symlink target both\n // count, so a planted link cannot launder a sensitive basename.\n const resolved = await realpath(root).catch(() => null);\n if (isSensitivePath(root) || (resolved !== null && isSensitivePath(resolved))) {\n return { content: `Refused to outline sensitive file: ${args.path ?? \".\"}`, isError: true };\n }\n if (languageOf(root) === undefined) {\n return {\n content: `No supported source language for: ${args.path ?? \".\"} (supported: TS/JS, Python, Go, Rust, Java/Kotlin/C#, C/C++, Ruby, PHP, Shell, Swift, Scala)`,\n };\n }\n const result = await outlineFile(root);\n if (\"binary\" in result) {\n return { content: `Cannot outline binary file: ${args.path ?? \".\"}`, isError: true };\n }\n if (\"unreadable\" in result) {\n return {\n content: `Cannot outline ${args.path ?? \".\"}: unreadable or larger than 2MB`,\n isError: true,\n };\n }\n if (result.symbols.length === 0) {\n return { content: `(no recognizable declarations in ${args.path ?? \".\"})` };\n }\n return { content: formatSymbols(result.symbols, result.total).join(\"\\n\") };\n }\n\n const walked = await walkFiles(root, undefined, {\n ignore: createIgnorePredicate(ctx.cwd),\n withMtime: false,\n signal: ctx.abortSignal,\n symlinkBoundary: ctx.cwd,\n nestedIgnore: true,\n });\n const candidates = walked.files.filter(\n (f) =>\n languageOf(f.abs) !== undefined &&\n !isSensitivePath(f.abs) &&\n (f.resolved === undefined || !isSensitivePath(f.resolved)),\n );\n const maxFiles = args.maxFiles ?? MAX_DIR_FILES;\n\n const scan = async (\n file: WalkedFile,\n ): Promise<{ rel: string; lines: string[]; chars: number } | null> => {\n const result = await outlineFile(file.abs);\n if (\"symbols\" in result && result.symbols.length > 0) {\n const lines = formatSymbols(result.symbols, result.total);\n const chars = lines.reduce((n, l) => n + l.length + 1, file.rel.length + 2);\n return { rel: file.rel, lines, chars };\n }\n return null;\n };\n\n const out: string[] = [];\n let size = 0;\n let included = 0;\n let hitLimit = false;\n for (let i = 0; i < candidates.length && included < maxFiles; i += WALK_CONCURRENCY) {\n if (ctx.abortSignal?.aborted) break;\n const chunk = await Promise.all(\n candidates.slice(i, i + WALK_CONCURRENCY).map((file) => scan(file)),\n );\n for (const block of chunk) {\n if (!block) continue;\n if (size + block.chars > MAX_OUT_CHARS || included >= maxFiles) {\n hitLimit = true;\n break;\n }\n if (out.length > 0) out.push(\"\");\n out.push(`${block.rel}:`);\n out.push(...block.lines);\n size += block.chars;\n included += 1;\n }\n if (hitLimit) break;\n }\n if (included === 0) {\n return {\n content: `No recognizable declarations under ${args.path ?? \".\"} (${candidates.length} source file(s) scanned)`,\n };\n }\n if (hitLimit) {\n out.push(\"... (truncated: output limit reached, more files not outlined)\");\n } else if (walked.aborted || ctx.abortSignal?.aborted) {\n out.push(\"... (interrupted: partial results)\");\n }\n return { content: out.join(\"\\n\") };\n },\n};\n","import { open, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport { z } from \"zod\";\nimport type { Tool } from \"../types\";\nimport { isSensitivePath } from \"./util\";\n\nconst DEFAULT_LIMIT = 2000;\nconst MAX_CHARS = 100 * 1024;\nconst BINARY_PROBE_BYTES = 8192;\nconst READ_CHUNK_BYTES = 64 * 1024;\n\nconst schema = z.object({\n path: z.string().describe(\"File path, absolute or relative to the working directory\"),\n offset: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"1-based line number to start reading from\"),\n limit: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Maximum number of lines to read (default 2000)\"),\n});\n\n// Per-tool-instance cache of what was already sent to the model, so a re-read\n// of an unchanged file (same mtime + size) whose requested line range was\n// already returned answers with a one-line note instead of re-billing the\n// full content (aider's read-file dedup). Keyed by realpath when resolvable.\ninterface ReadCacheEntry {\n mtimeMs: number;\n size: number;\n totalLines: number;\n start: number;\n end: number;\n}\nconst READ_CACHE_MAX_ENTRIES = 200;\n\ninterface ReadWindow {\n totalLines: number;\n out: string[];\n lastShown: number;\n truncatedBySize: boolean;\n}\n\nasync function readWindow(\n handle: Awaited<ReturnType<typeof open>>,\n size: number,\n start: number,\n limit: number,\n): Promise<ReadWindow | { binary: true }> {\n const probe = Buffer.alloc(Math.min(size, BINARY_PROBE_BYTES));\n const { bytesRead: probeBytes } = await handle.read(probe, 0, probe.length, 0);\n const head = probe.subarray(0, probeBytes);\n const utf16le = head[0] === 0xff && head[1] === 0xfe;\n const utf16be = head[0] === 0xfe && head[1] === 0xff;\n if (!utf16le && !utf16be && head.includes(0)) return { binary: true };\n\n const decoder = new StringDecoder(utf16le || utf16be ? \"utf16le\" : \"utf8\");\n const requestedEnd = start - 1 + limit;\n const result: ReadWindow = {\n totalLines: 1,\n out: [],\n lastShown: start - 1,\n truncatedBySize: false,\n };\n let lineNumber = 1;\n let outputSize = 0;\n let pending = \"\";\n let beRemainder: number | undefined;\n const showLine = (raw: string) => {\n if (lineNumber >= start && lineNumber <= requestedEnd && !result.truncatedBySize) {\n const numbered = `${lineNumber}\\t${raw.replace(/\\r$/, \"\")}`;\n if (outputSize + numbered.length > MAX_CHARS) {\n result.truncatedBySize = true;\n } else {\n result.out.push(numbered);\n outputSize += numbered.length;\n result.lastShown = lineNumber;\n }\n }\n };\n const accept = (text: string) => {\n const parts = `${pending}${text}`.split(\"\\n\");\n for (let i = 0; i < parts.length - 1; i += 1) {\n showLine(parts[i] ?? \"\");\n lineNumber += 1;\n }\n pending = parts[parts.length - 1] ?? \"\";\n };\n\n let position = utf16le || utf16be ? 2 : 0;\n while (position < size) {\n const length = Math.min(READ_CHUNK_BYTES, size - position);\n const { bytesRead, buffer } = await handle.read(Buffer.alloc(length), 0, length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n let chunk = buffer.subarray(0, bytesRead);\n if (utf16be) {\n const combined =\n beRemainder === undefined ? chunk : Buffer.concat([Buffer.from([beRemainder]), chunk]);\n const pairedLength = combined.length - (combined.length % 2);\n const swapped = Buffer.allocUnsafe(pairedLength);\n for (let i = 0; i < pairedLength; i += 2) {\n swapped[i] = combined[i + 1] ?? 0;\n swapped[i + 1] = combined[i] ?? 0;\n }\n beRemainder = pairedLength < combined.length ? combined[combined.length - 1] : undefined;\n chunk = swapped;\n }\n accept(decoder.write(chunk));\n }\n accept(decoder.end());\n showLine(pending);\n result.totalLines = lineNumber;\n return result;\n}\n\nexport function createReadFileTool(): Tool<typeof schema> {\n const cache = new Map<string, ReadCacheEntry>();\n return {\n name: \"read_file\",\n description:\n \"Read a text file and return its contents with line numbers. Output is truncated to 2000 lines or 100KB by default. Re-reading an unchanged file range returns a short note instead of the content.\",\n permission: \"read\",\n parameters: schema,\n reset: () => cache.clear(),\n async execute(args, ctx) {\n const filePath = path.resolve(ctx.cwd, args.path);\n // Check both the given path and its symlink target: a repo can plant a\n // harmless-looking link (notes.txt -> ~/.ssh/id_rsa) whose resolved name\n // is on the sensitive list. Unresolvable paths keep the raw-path check.\n const resolvedPath = await realpath(filePath).catch(() => null);\n if (isSensitivePath(filePath) || (resolvedPath !== null && isSensitivePath(resolvedPath))) {\n return { content: `Refused to read sensitive file: ${args.path}`, isError: true };\n }\n const st = await stat(filePath).catch(() => null);\n if (!st) {\n return { content: `File not found: ${args.path}`, isError: true };\n }\n if (st.isDirectory()) {\n return { content: `Path is a directory, not a file: ${args.path}`, isError: true };\n }\n const start = args.offset ?? 1;\n const limit = args.limit ?? DEFAULT_LIMIT;\n const cacheKey = resolvedPath ?? filePath;\n const hit = cache.get(cacheKey);\n if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size) {\n const requestedEnd = Math.min(hit.totalLines, start - 1 + limit);\n if (start >= hit.start && requestedEnd <= hit.end && start <= hit.totalLines) {\n return {\n content: `[unchanged since last read: ${args.path} — lines ${start}-${requestedEnd} of ${hit.totalLines} are already in context and the file has not changed]`,\n };\n }\n }\n const handle = await open(filePath, \"r\").catch(() => null);\n if (!handle) {\n return { content: `Failed to read ${args.path}`, isError: true };\n }\n let decoded: ReadWindow | { binary: true };\n try {\n decoded = await readWindow(handle, st.size, start, limit);\n } catch {\n return { content: `Failed to read ${args.path}`, isError: true };\n } finally {\n await handle.close().catch(() => {});\n }\n if (\"binary\" in decoded) {\n return { content: `Cannot read binary file: ${args.path}`, isError: true };\n }\n const { totalLines, out, lastShown, truncatedBySize } = decoded;\n if (start > totalLines) {\n return { content: `(offset ${start} beyond end of file; file has ${totalLines} lines)` };\n }\n const end = Math.min(totalLines, start - 1 + limit);\n if (truncatedBySize) {\n out.push(\n `... (truncated: output exceeds 100KB, showing lines ${start}-${lastShown} of ${totalLines})`,\n );\n } else if (end < totalLines) {\n out.push(`... (truncated: showing lines ${start}-${end} of ${totalLines})`);\n }\n if (out.length > 0) {\n if (cache.size >= READ_CACHE_MAX_ENTRIES) cache.clear();\n const prev = hit?.mtimeMs === st.mtimeMs && hit.size === st.size ? hit : undefined;\n cache.set(cacheKey, {\n mtimeMs: st.mtimeMs,\n size: st.size,\n totalLines,\n // Extend the remembered range when the new read overlaps or abuts\n // the previous one; disjoint ranges replace it.\n start: prev && start <= prev.end + 1 ? Math.min(prev.start, start) : start,\n end: prev && lastShown >= prev.start - 1 ? Math.max(prev.end, lastShown) : lastShown,\n });\n }\n return { content: out.join(\"\\n\") };\n },\n };\n}\n\n// Shared instance for direct consumers (tests); the tool registry creates a\n// fresh one per agent loop so a subagent's cache never claims content that\n// only the parent's context holds.\nexport const readFileTool = createReadFileTool();\n","import { readFile, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { probeImageDimensions } from \"../../core/image\";\nimport { downsampleImageIfNeeded } from \"../../core/image-resize\";\nimport type { ImageInput } from \"../../core/messages\";\nimport type { Tool } from \"../types\";\nimport { isSensitivePath } from \"./util\";\n\n// Same ceiling as @-mention image reads (src/cli/mentions.ts).\nconst MAX_IMAGE_BYTES = 5 * 1024 * 1024;\n\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};\n\nconst schema = z.object({\n path: z\n .string()\n .describe(\n \"Image file path (png/jpg/jpeg/gif/webp), absolute or relative to the working directory\",\n ),\n});\n\n// Lets the model pull an image into the conversation mid-session (a screenshot\n// the user mentioned, a generated asset, a UI mock). The image itself is not\n// in the text result — the loop attaches it to the history as a user message\n// with image parts (see ToolResult.images), so every protocol sees it the same\n// way as a pasted image.\nexport const readImageTool: Tool<typeof schema> = {\n name: \"read_image\",\n description:\n \"Read an image file (png, jpg, gif, webp) and show it to the model. Use this whenever the user references a screenshot, diagram, or picture file you need to see. Images larger than 2000px are downsampled automatically.\",\n permission: \"read\",\n parameters: schema,\n async execute(args, ctx) {\n if (ctx.visionEnabled === false) {\n return {\n content:\n \"The active model does not support image input (vision = false), so the image was not read. Work from text instead, or ask the user to describe the image.\",\n isError: true,\n };\n }\n const filePath = path.resolve(ctx.cwd, args.path);\n // Same symlink-aware sensitive check as read_file.\n const resolvedPath = await realpath(filePath).catch(() => null);\n if (isSensitivePath(filePath) || (resolvedPath !== null && isSensitivePath(resolvedPath))) {\n return { content: `Refused to read sensitive file: ${args.path}`, isError: true };\n }\n const mimeType = IMAGE_MIME_TYPES[path.extname(filePath).toLowerCase()];\n if (!mimeType) {\n return {\n content: `Not a supported image (png/jpg/jpeg/gif/webp): ${args.path}`,\n isError: true,\n };\n }\n const st = await stat(filePath).catch(() => null);\n if (!st || !st.isFile()) {\n return { content: `File not found: ${args.path}`, isError: true };\n }\n if (st.size > MAX_IMAGE_BYTES) {\n return {\n content: `Image too large (${Math.round(st.size / 1024)}KB > ${MAX_IMAGE_BYTES / 1024}KB): ${args.path}`,\n isError: true,\n };\n }\n const buf = await readFile(filePath).catch(() => null);\n if (!buf || buf.length === 0) {\n return { content: `Failed to read ${args.path}`, isError: true };\n }\n if (!probeImageDimensions(buf)) {\n return {\n content: `File is not a valid image (unrecognized header): ${args.path}`,\n isError: true,\n };\n }\n const downsampled = await downsampleImageIfNeeded({\n path: args.path,\n mimeType,\n data: buf.toString(\"base64\"),\n });\n if (downsampled.oversized && !downsampled.resized) {\n return {\n content: `Image exceeds 2000px and could not be downsampled on this platform: ${args.path}`,\n isError: true,\n };\n }\n const image: ImageInput = downsampled.image;\n const dims = probeImageDimensions(Buffer.from(image.data, \"base64\"));\n const sizeKB = Math.round(image.data.length / 1024);\n const desc = dims ? `${dims.width}x${dims.height}` : \"unknown dimensions\";\n const resizedNote = downsampled.resized ? \" (downsampled to fit 2000px)\" : \"\";\n return {\n content: `Image loaded: ${args.path} (${desc}, ~${sizeKB}KB)${resizedNote}. The image is attached below — look at it now.`,\n images: [image],\n };\n },\n};\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport type { Tool } from \"../types\";\nimport { MAX_SNAPSHOT_CONTENT_BYTES, captureSnapshot, pushSnapshot } from \"./snapshots\";\nimport { isSensitivePath, realpathForCheck } from \"./util\";\n\nconst schema = z.object({\n path: z.string().describe(\"File path, absolute or relative to the working directory\"),\n content: z.string().describe(\"Full content to write to the file\"),\n});\n\nexport const writeFileTool: Tool<typeof schema> = {\n name: \"write_file\",\n description:\n \"Write content to a file, creating parent directories as needed. Overwrites existing files. Refuses content with NUL bytes (binary) or over 5MB.\",\n permission: \"write\",\n parameters: schema,\n async execute(args, ctx) {\n const filePath = path.resolve(ctx.cwd, args.path);\n // Same symlink defense as read_file: a repo can plant a harmless-looking\n // link (config.json -> .env) whose resolved name is on the sensitive\n // list, and the permission gate only name-checks the raw path — so this\n // runs in-tool, unreachable by yolo mode or hooks. Unresolvable (new)\n // paths resolve the parent and re-attach the basename.\n const resolvedPath = await realpathForCheck(filePath);\n if (isSensitivePath(filePath) || (resolvedPath !== null && isSensitivePath(resolvedPath))) {\n return { content: `Refused to write sensitive file: ${args.path}`, isError: true };\n }\n // write_file writes UTF-8 text: NUL bytes mean the model is pushing\n // binary content that would land corrupted. The size limit matches\n // edit_file and keeps snapshots bounded.\n if (args.content.includes(\"\\0\")) {\n return {\n content: `Refused to write ${args.path}: content contains NUL bytes (write_file is text-only)`,\n isError: true,\n };\n }\n const bytes = Buffer.byteLength(args.content, \"utf8\");\n if (bytes > MAX_SNAPSHOT_CONTENT_BYTES) {\n return {\n content: `Refused to write ${args.path}: content is ${bytes} bytes, over the 5MB limit (writes past it could not be restored by /undo)`,\n isError: true,\n };\n }\n const snapshot = await captureSnapshot(filePath, \"write_file\", ctx.snapshotContext);\n if (snapshot.existed && snapshot.content === null) {\n const reason = snapshot.contentTooLarge\n ? \"existing content is over the 5MB snapshot limit\"\n : \"existing content could not be read\";\n return {\n content: `Refused to overwrite ${args.path}: ${reason}, and no restorable snapshot is available`,\n isError: true,\n };\n }\n try {\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, args.content, \"utf8\");\n } catch (err) {\n return { content: `Failed to write ${args.path}: ${(err as Error).message}`, isError: true };\n }\n await pushSnapshot(snapshot);\n return { content: `Wrote ${bytes} bytes to ${args.path}` };\n },\n};\n","import { z } from \"zod\";\nimport { probeImageDimensions } from \"../core/image\";\nimport { type ScreenshotResult, captureScreenshot } from \"../core/screenshot\";\nimport type { Tool } from \"./types\";\n\nconst schema = z.object({});\n\n// Lets the model look at the actual screen mid-session — the visual debugging\n// counterpart of read_image: start a dev server or GUI program, screenshot,\n// then judge the real rendered output instead of guessing from code. The\n// capture function is injectable so tests never touch a real display.\nexport function createScreenshotTool(\n capture: () => Promise<ScreenshotResult> = captureScreenshot,\n): Tool<typeof schema> {\n return {\n name: \"screenshot\",\n description:\n \"Capture a screenshot of the primary display and see it. Use this to check the actual visual result of a running program (web page, GUI) — e.g. after starting a dev server — instead of inferring from code. The capture shows exactly what is visible on screen, including overlapping windows: content hidden behind other windows cannot be seen, so if a specific window matters ask the user to bring it to the front first. Note the capture may include anything else currently on screen.\",\n permission: \"read\",\n parameters: schema,\n async execute(_args, ctx) {\n if (ctx.visionEnabled === false) {\n return {\n content:\n \"The active model does not support image input (vision = false), so no screenshot was taken. Work from text instead, or ask the user to describe what is on screen.\",\n isError: true,\n };\n }\n const result = await capture();\n if (!result.ok) {\n return { content: `Screenshot failed: ${result.error}`, isError: true };\n }\n const image = result.image;\n const dims = probeImageDimensions(Buffer.from(image.data, \"base64\"));\n const sizeKB = Math.round(image.data.length / 1024);\n const desc = dims ? `${dims.width}x${dims.height}` : \"unknown dimensions\";\n return {\n content: `Screenshot captured (${desc}, ~${sizeKB}KB). The image is attached below — look at it now.`,\n images: [image],\n };\n },\n };\n}\n\nexport const screenshotTool = createScreenshotTool();\n","import { execFile } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { readFile, unlink } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { probeImageDimensions } from \"./image\";\nimport { downsampleImageIfNeeded } from \"./image-resize\";\nimport type { ImageInput } from \"./messages\";\n\nexport const SCREENSHOT_TIMEOUT_MS = 20_000;\n\nconst execFileAsync = promisify(execFile);\n\nexport interface ScreenshotCommand {\n command: string;\n args: string[];\n}\n\nexport type ScreenshotResult = { ok: true; image: ImageInput } | { ok: false; error: string };\n\n// Windows capture script: copies the primary screen into a PNG. The position\n// problem on Windows is DPI virtualization — when the process is not truly\n// per-monitor DPI aware, the Bounds Forms reports and the pixels CopyFromScreen\n// reads live in different coordinate spaces, so at 125%/150% scaling the\n// capture comes out offset and wrongly sized. Setting per-monitor-V2 awareness\n// before touching Forms/Drawing makes both sides physical pixels; the call\n// fails harmlessly when the host manifest already pinned awareness (system\n// awareness is still physically correct on the primary display).\nexport function windowsScreenshotScript(outputFile: string): string {\n const output = outputFile.replace(/'/g, \"''\");\n return [\n \"try { Add-Type -MemberDefinition '[DllImport(\\\"user32.dll\\\")] public static extern bool SetProcessDpiAwarenessContext(int v);' -Name Dpi -Namespace StarShot -ErrorAction Stop; [void][StarShot.Dpi]::SetProcessDpiAwarenessContext(-4) } catch { }\",\n \"Add-Type -AssemblyName System.Windows.Forms,System.Drawing\",\n \"$b = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds\",\n \"$bmp = New-Object System.Drawing.Bitmap($b.Width, $b.Height)\",\n \"$g = [System.Drawing.Graphics]::FromImage($bmp)\",\n \"$g.CopyFromScreen($b.Left, $b.Top, 0, 0, $bmp.Size)\",\n `$bmp.Save('${output}', [System.Drawing.Imaging.ImageFormat]::Png)`,\n \"$g.Dispose(); $bmp.Dispose()\",\n ].join(\"; \");\n}\n\n// Platform-native capture command writing a PNG to outputFile, or null when\n// this platform/session cannot take screenshots. Windows/macOS capture the\n// primary display only — one deterministic file and physically exact bounds,\n// even on mixed-DPI multi-monitor setups; Linux tools capture the whole\n// compositor/root window (all monitors composited).\nexport function screenshotPlan(\n platform: NodeJS.Platform,\n outputFile: string,\n env: NodeJS.ProcessEnv = process.env,\n tools: Record<string, boolean> = {},\n): ScreenshotCommand | null {\n if (platform === \"win32\") {\n return {\n command: \"powershell.exe\",\n args: [\"-NoProfile\", \"-NonInteractive\", \"-Command\", windowsScreenshotScript(outputFile)],\n };\n }\n if (platform === \"darwin\") {\n // -x silences the shutter sound; -D 1 pins the main display so a single\n // output file is deterministic no matter how many monitors are attached.\n return { command: \"screencapture\", args: [\"-x\", \"-D\", \"1\", outputFile] };\n }\n if (platform === \"linux\") {\n if (env.WAYLAND_DISPLAY) {\n if (tools.grim) return { command: \"grim\", args: [outputFile] };\n if (tools[\"gnome-screenshot\"]) {\n return { command: \"gnome-screenshot\", args: [\"-f\", outputFile] };\n }\n if (tools.spectacle) {\n return { command: \"spectacle\", args: [\"-b\", \"-n\", \"-o\", outputFile] };\n }\n return null;\n }\n if (env.DISPLAY) {\n if (tools.import) return { command: \"import\", args: [\"-window\", \"root\", outputFile] };\n if (tools.scrot) return { command: \"scrot\", args: [\"--overwrite\", outputFile] };\n if (tools[\"gnome-screenshot\"]) {\n return { command: \"gnome-screenshot\", args: [\"-f\", outputFile] };\n }\n if (tools.spectacle) {\n return { command: \"spectacle\", args: [\"-b\", \"-n\", \"-o\", outputFile] };\n }\n return null;\n }\n return null;\n }\n return null;\n}\n\nexport function noScreenshotToolMessage(\n platform: NodeJS.Platform,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n if (platform === \"linux\") {\n if (!env.WAYLAND_DISPLAY && !env.DISPLAY) {\n return \"no display server (neither DISPLAY nor WAYLAND_DISPLAY is set) — this session is headless (e.g. SSH), there is no screen to capture\";\n }\n return \"no screenshot tool found — install one of: grim or spectacle (Wayland), scrot or ImageMagick's import (X11), or gnome-screenshot\";\n }\n return `screenshots are not supported on this platform (${platform})`;\n}\n\nasync function commandExists(command: string): Promise<boolean> {\n const checker = process.platform === \"win32\" ? \"where\" : \"which\";\n try {\n await execFileAsync(checker, [command], { timeout: SCREENSHOT_TIMEOUT_MS });\n return true;\n } catch {\n return false;\n }\n}\n\nconst LINUX_TOOL_NAMES = [\"grim\", \"gnome-screenshot\", \"spectacle\", \"import\", \"scrot\"];\n\n// Captures the screen into a temp PNG via platform-native tools (no npm image\n// dependency), downsamples past 2000px like read_image, and returns the image\n// for the loop to attach to the history. Failures come back as messages the\n// model can act on instead of exceptions.\nexport async function captureScreenshot(): Promise<ScreenshotResult> {\n const outputFile = path.join(os.tmpdir(), `star-screenshot-${randomUUID()}.png`);\n try {\n const tools: Record<string, boolean> = {};\n if (process.platform === \"linux\") {\n const found = await Promise.all(LINUX_TOOL_NAMES.map(commandExists));\n for (const [i, name] of LINUX_TOOL_NAMES.entries()) {\n tools[name] = found[i] ?? false;\n }\n }\n const plan = screenshotPlan(process.platform, outputFile, process.env, tools);\n if (!plan) {\n return { ok: false, error: noScreenshotToolMessage(process.platform) };\n }\n try {\n await execFileAsync(plan.command, plan.args, { timeout: SCREENSHOT_TIMEOUT_MS });\n } catch (err) {\n const detail = err instanceof Error && err.message ? ` (${err.message})` : \"\";\n return { ok: false, error: `${plan.command} failed to capture the screen${detail}` };\n }\n const buf = await readFile(outputFile).catch(() => null);\n if (!buf || buf.length === 0) {\n return { ok: false, error: `${plan.command} produced no image file` };\n }\n if (!probeImageDimensions(buf)) {\n return { ok: false, error: `${plan.command} produced an unrecognized image file` };\n }\n const downsampled = await downsampleImageIfNeeded({\n path: \"screen\",\n mimeType: \"image/png\",\n data: buf.toString(\"base64\"),\n });\n if (downsampled.oversized && !downsampled.resized) {\n return {\n ok: false,\n error: \"screenshot exceeds 2000px and could not be downsampled on this platform\",\n };\n }\n return { ok: true, image: downsampled.image };\n } finally {\n await unlink(outputFile).catch(() => {});\n }\n}\n","import { z } from \"zod\";\nimport { type AgentTaskSnapshot, defaultAgentTasks } from \"../agent/agent-tasks\";\nimport { formatDuration, formatTaskLine, formatTaskList } from \"../tasks/format\";\nimport { type TaskSnapshot, defaultTaskManager } from \"../tasks/manager\";\nimport { MAX_OUTPUT } from \"./bash\";\nimport type { Tool, ToolContext } from \"./types\";\n\nconst idSchema = z.object({\n id: z.string().describe(\"Task id, e.g. task-1 or agent-1\"),\n});\n\n// Aligns with the foreground bash output cap: keep the tail, note the cut.\nfunction truncateOutput(s: string): string {\n if (s.length <= MAX_OUTPUT) return s;\n return `[... earlier output truncated; showing the last ${MAX_OUTPUT} characters ...]\\n${s.slice(-MAX_OUTPUT)}`;\n}\n\n// Shell tasks are owned by the agent that started them: root sees and\n// controls everything, a subagent only its own tasks — otherwise one\n// subagent could kill the parent's (or a sibling's) background work.\n// Unowned/foreign tasks answer \"Unknown task\" so their existence doesn't\n// leak either. Background subagents (agent-*) always belong to root:\n// subagents can't spawn subagents.\nfunction callerId(ctx: ToolContext): string {\n return ctx.agentId ?? \"root\";\n}\n\nfunction accessibleShellTask(ctx: ToolContext, id: string): TaskSnapshot | undefined {\n const task = defaultTaskManager.get(id);\n if (!task) return undefined;\n const caller = callerId(ctx);\n return caller === \"root\" || task.ownerId === caller ? task : undefined;\n}\n\nfunction formatAgentTaskLine(task: AgentTaskSnapshot): string {\n const duration = formatDuration(task.startedAt, task.endedAt);\n const label = task.description ?? task.prompt.slice(0, 60);\n const status =\n task.status === \"running\" ? `running ${duration}` : `${task.status} after ${duration}`;\n return `${task.id} [${status}] ${label}`;\n}\n\nexport const taskListTool: Tool = {\n name: \"task_list\",\n description: \"List background tasks (shell commands and subagents) with status and runtime.\",\n permission: \"read\",\n parameters: z.object({}),\n execute(_args, ctx) {\n const caller = callerId(ctx);\n const shell = formatTaskList(\n defaultTaskManager.list().filter((t) => caller === \"root\" || t.ownerId === caller),\n );\n const agents = caller === \"root\" ? defaultAgentTasks.list() : [];\n const agentLines =\n agents.length === 0\n ? \"No background subagents.\"\n : `Background subagents:\\n${agents.map(formatAgentTaskLine).join(\"\\n\")}`;\n return Promise.resolve({ content: `${shell}\\n${agentLines}` });\n },\n};\n\nexport const taskOutputTool: Tool<typeof idSchema> = {\n name: \"task_output\",\n description:\n \"Read the current output of a background task (shell command or subagent; works while still running).\",\n permission: \"read\",\n parameters: idSchema,\n execute(args, ctx) {\n const shell = accessibleShellTask(ctx, args.id);\n if (shell) {\n const body = truncateOutput(shell.output.replace(/\\s+$/, \"\") || \"(no output yet)\");\n return Promise.resolve({ content: `${formatTaskLine(shell)}\\n${body}` });\n }\n const agent = callerId(ctx) === \"root\" ? defaultAgentTasks.get(args.id) : undefined;\n if (agent) {\n const body =\n agent.status === \"running\"\n ? \"(still running)\"\n : truncateOutput(agent.result.replace(/\\s+$/, \"\") || \"(no report)\");\n return Promise.resolve({ content: `${formatAgentTaskLine(agent)}\\n${body}` });\n }\n return Promise.resolve({ content: `Unknown task: ${args.id}`, isError: true });\n },\n};\n\nexport const taskKillTool: Tool<typeof idSchema> = {\n name: \"task_kill\",\n description: \"Stop a running background task (shell command process tree or subagent).\",\n permission: \"exec\",\n parameters: idSchema,\n execute(args, ctx) {\n const shell = accessibleShellTask(ctx, args.id);\n const agent = shell || callerId(ctx) !== \"root\" ? undefined : defaultAgentTasks.get(args.id);\n const task = shell ?? agent;\n if (!task) {\n return Promise.resolve({ content: `Unknown task: ${args.id}`, isError: true });\n }\n if (task.status !== \"running\") {\n return Promise.resolve({\n content: `Task ${args.id} is not running (status: ${task.status}).`,\n isError: true,\n });\n }\n if (shell) {\n defaultTaskManager.kill(args.id);\n } else {\n defaultAgentTasks.kill(args.id);\n }\n return Promise.resolve({ content: `Task ${args.id} stopped.` });\n },\n};\n","import type { TaskSnapshot } from \"./manager\";\n\nexport function formatDuration(startedAt: number, endedAt?: number): string {\n const seconds = Math.max(0, Math.round(((endedAt ?? Date.now()) - startedAt) / 1000));\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m${seconds % 60}s`;\n return `${Math.floor(minutes / 60)}h${minutes % 60}m`;\n}\n\nfunction formatStatus(task: TaskSnapshot): string {\n const duration = formatDuration(task.startedAt, task.endedAt);\n if (task.status === \"running\") return `running ${duration}`;\n const exit =\n task.exitCode === null || task.exitCode === undefined ? \"\" : `, exit ${task.exitCode}`;\n return `${task.status}${exit} after ${duration}`;\n}\n\nexport function formatTaskLine(task: TaskSnapshot): string {\n const label = task.description ? `${task.description} (${task.command})` : task.command;\n return `${task.id} [${formatStatus(task)}] ${label}`;\n}\n\nexport function formatTaskList(tasks: TaskSnapshot[]): string {\n if (tasks.length === 0) return \"No background tasks.\";\n return `Background tasks:\\n${tasks.map(formatTaskLine).join(\"\\n\")}`;\n}\n\nexport function formatTaskStarted(task: TaskSnapshot): string {\n const label = task.description ? `${task.description} (${task.command})` : task.command;\n return `Background task ${task.id} started: ${label}`;\n}\n\nexport function formatTaskFinished(task: TaskSnapshot): string {\n const exit =\n task.exitCode === null || task.exitCode === undefined ? \"\" : ` (exit ${task.exitCode})`;\n switch (task.status) {\n case \"completed\":\n return `Background task ${task.id} finished${exit}: ${task.command}`;\n case \"failed\":\n return `Background task ${task.id} failed${exit}: ${task.command}`;\n case \"timed_out\":\n return `Background task ${task.id} timed out: ${task.command}`;\n case \"stopped\":\n return `Background task ${task.id} stopped: ${task.command}`;\n default:\n return `Background task ${task.id}: ${task.command}`;\n }\n}\n","import { z } from \"zod\";\nimport { debugStreamLog } from \"../../llm/debug\";\nimport type { Tool } from \"../types\";\nimport { decodeEntities } from \"./fetch\";\n\nconst TIMEOUT_MS = 15000;\nconst MAX_BODY_BYTES = 2 * 1024 * 1024;\nconst DEFAULT_MAX_RESULTS = 5;\nconst MAX_RESULTS_LIMIT = 10;\nconst USER_AGENT =\n \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36\";\n\nconst schema = z.object({\n query: z.string().min(1).describe(\"The search query\"),\n maxResults: z\n .number()\n .int()\n .min(1)\n .max(MAX_RESULTS_LIMIT)\n .optional()\n .describe(\n `Maximum number of results to return (default ${DEFAULT_MAX_RESULTS}, max ${MAX_RESULTS_LIMIT})`,\n ),\n});\n\nexport interface SearchResult {\n title: string;\n url: string;\n snippet: string;\n}\n\n// A search backend: builds the request URL for a query and parses its HTML\n// into the unified {title, url, snippet} shape. Sources are tried in order;\n// a network error, non-2xx status or zero parsed results falls through to\n// the next one.\ninterface SearchSource {\n name: string;\n buildUrl: (query: string) => string;\n parse: (html: string) => SearchResult[];\n}\n\nfunction stripTags(s: string): string {\n return decodeEntities(s.replace(/<[^>]+>/g, \" \"))\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction normalizeUrl(href: string): string {\n const decoded = decodeEntities(href);\n const uddg = decoded.match(/[?&]uddg=([^&]+)/);\n if (uddg?.[1]) {\n try {\n return decodeURIComponent(uddg[1]);\n } catch {\n return decoded;\n }\n }\n if (decoded.startsWith(\"//\")) {\n return `https:${decoded}`;\n }\n return decoded;\n}\n\nfunction hrefOf(tag: string): string | undefined {\n const match = tag.match(/href\\s*=\\s*\"([^\"]*)\"/i) ?? tag.match(/href\\s*=\\s*'([^']*)'/i);\n return match?.[1];\n}\n\nexport function parseResults(html: string): SearchResult[] {\n const results: SearchResult[] = [];\n const anchorRe = /<a\\b[^>]*\\bclass\\s*=\\s*\"[^\"]*\\bresult__a\\b[^\"]*\"[^>]*>[\\s\\S]*?<\\/a>/gi;\n const anchors = [...html.matchAll(anchorRe)];\n for (const [i, anchor] of anchors.entries()) {\n const full = anchor[0];\n const href = hrefOf(full);\n if (!href) {\n continue;\n }\n const url = normalizeUrl(href);\n const title = stripTags(full.replace(/^<a\\b[^>]*>/i, \"\").replace(/<\\/a>\\s*$/i, \"\"));\n const blockStart = (anchor.index ?? 0) + full.length;\n const blockEnd = i + 1 < anchors.length ? (anchors[i + 1]?.index ?? html.length) : html.length;\n const block = html.slice(blockStart, blockEnd);\n const snippetMatch = block.match(\n /<(?:a|div)\\b[^>]*\\bclass\\s*=\\s*\"[^\"]*\\bresult__snippet\\b[^\"]*\"[^>]*>([\\s\\S]*?)<\\/(?:a|div)>/i,\n );\n const snippet = snippetMatch?.[1] ? stripTags(snippetMatch[1]) : \"\";\n if (title && url) {\n results.push({ title, url, snippet });\n }\n }\n return results;\n}\n\n// lite.duckduckgo.com serves a different (table-based) layout: titles in\n// <a class=\"result-link\">, snippets in the following <td class=\"result-snippet\">.\nexport function parseDdgLiteResults(html: string): SearchResult[] {\n const results: SearchResult[] = [];\n const anchorRe =\n /<a\\b[^>]*\\bclass\\s*=\\s*[\"'][^\"']*\\bresult-link\\b[^\"']*[\"'][^>]*>[\\s\\S]*?<\\/a>/gi;\n const anchors = [...html.matchAll(anchorRe)];\n for (const [i, anchor] of anchors.entries()) {\n const full = anchor[0];\n const href = hrefOf(full);\n if (!href) {\n continue;\n }\n const url = normalizeUrl(href);\n const title = stripTags(full.replace(/^<a\\b[^>]*>/i, \"\").replace(/<\\/a>\\s*$/i, \"\"));\n const blockStart = (anchor.index ?? 0) + full.length;\n const blockEnd = i + 1 < anchors.length ? (anchors[i + 1]?.index ?? html.length) : html.length;\n const block = html.slice(blockStart, blockEnd);\n const snippetMatch = block.match(\n /<td\\b[^>]*\\bclass\\s*=\\s*[\"'][^\"']*\\bresult-snippet\\b[^\"']*[\"'][^>]*>([\\s\\S]*?)<\\/td>/i,\n );\n const snippet = snippetMatch?.[1] ? stripTags(snippetMatch[1]) : \"\";\n if (title && url) {\n results.push({ title, url, snippet });\n }\n }\n return results;\n}\n\n// www.bing.com/search needs no key: results are <li class=\"b_algo\"> blocks\n// with the title link in <h2><a> and the snippet in the block's first <p>.\nexport function parseBingResults(html: string): SearchResult[] {\n const results: SearchResult[] = [];\n const blockRe = /<li\\b[^>]*\\bclass\\s*=\\s*\"[^\"]*\\bb_algo\\b[^\"]*\"[^>]*>/gi;\n const blocks = [...html.matchAll(blockRe)];\n for (const [i, block] of blocks.entries()) {\n const start = block.index ?? 0;\n const end = i + 1 < blocks.length ? (blocks[i + 1]?.index ?? html.length) : html.length;\n const slice = html.slice(start, end);\n const headMatch = slice.match(\n /<h2\\b[^>]*>[\\s\\S]*?<a\\b[^>]*?\\shref\\s*=\\s*\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>[\\s\\S]*?<\\/h2>/i,\n );\n if (!headMatch) {\n continue;\n }\n const url = decodeEntities(headMatch[1] ?? \"\").trim();\n const title = stripTags(headMatch[2] ?? \"\");\n const snippetMatch = slice.match(/<p\\b[^>]*>([\\s\\S]*?)<\\/p>/i);\n const snippet = snippetMatch?.[1] ? stripTags(snippetMatch[1]) : \"\";\n if (title && /^https?:\\/\\//.test(url)) {\n results.push({ title, url, snippet });\n }\n }\n return results;\n}\n\nconst SEARCH_SOURCES: SearchSource[] = [\n {\n name: \"duckduckgo-html\",\n buildUrl: (query) => `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`,\n parse: parseResults,\n },\n {\n name: \"duckduckgo-lite\",\n buildUrl: (query) => `https://lite.duckduckgo.com/lite/?q=${encodeURIComponent(query)}`,\n parse: parseDdgLiteResults,\n },\n {\n name: \"bing\",\n buildUrl: (query) => `https://www.bing.com/search?q=${encodeURIComponent(query)}`,\n parse: parseBingResults,\n },\n];\n\nasync function readLimitedBody(res: Response): Promise<string> {\n if (!res.body) return \"\";\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let bytes = 0;\n let text = \"\";\n try {\n while (bytes < MAX_BODY_BYTES) {\n const { done, value } = await reader.read();\n if (done) break;\n bytes += value.byteLength;\n text += decoder.decode(value, { stream: true });\n }\n text += decoder.decode();\n } finally {\n await reader.cancel().catch(() => {});\n }\n return text;\n}\n\nexport const webSearchTool: Tool<typeof schema> = {\n name: \"web_search\",\n description:\n \"Search the web and return a numbered list of results with title, URL and snippet. Uses DuckDuckGo with automatic fallback to DuckDuckGo Lite and Bing when a source fails or returns nothing. No API key required. Use web_fetch to read a result's full page.\",\n permission: \"read\",\n parameters: schema,\n async execute(args, ctx) {\n const maxResults = Math.min(args.maxResults ?? DEFAULT_MAX_RESULTS, MAX_RESULTS_LIMIT);\n const failures: string[] = [];\n for (const source of SEARCH_SOURCES) {\n const timeoutSignal = AbortSignal.timeout(TIMEOUT_MS);\n const signal = ctx.abortSignal\n ? AbortSignal.any([timeoutSignal, ctx.abortSignal])\n : timeoutSignal;\n let res: Response;\n try {\n res = await fetch(source.buildUrl(args.query), {\n signal,\n redirect: \"follow\",\n headers: { \"user-agent\": USER_AGENT, accept: \"text/html\" },\n });\n } catch (err) {\n if (ctx.abortSignal?.aborted) {\n return { content: \"ERROR: request aborted\", isError: true };\n }\n const name = err instanceof Error ? err.name : \"\";\n if (timeoutSignal.aborted || name === \"TimeoutError\") {\n failures.push(`${source.name}: search timed out after ${TIMEOUT_MS / 1000}s`);\n } else {\n const message = err instanceof Error ? err.message : String(err);\n failures.push(`${source.name}: search request failed: ${message}`);\n }\n continue;\n }\n if (!res.ok) {\n await res.body?.cancel().catch(() => {});\n failures.push(`${source.name}: HTTP ${res.status} ${res.statusText}`);\n continue;\n }\n let html: string;\n try {\n html = await readLimitedBody(res);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n failures.push(`${source.name}: failed to read search response: ${message}`);\n continue;\n }\n const results = source.parse(html).slice(0, maxResults);\n if (results.length === 0) {\n failures.push(\n `${source.name}: no results found (the response may have been blocked or the page layout changed)`,\n );\n continue;\n }\n debugStreamLog(\"web_search\", {\n source: source.name,\n query: args.query,\n results: results.length,\n failures,\n });\n const lines = results.map(\n (r, i) => `${i + 1}. ${r.title}\\n ${r.url}${r.snippet ? `\\n ${r.snippet}` : \"\"}`,\n );\n return { content: lines.join(\"\\n\") };\n }\n debugStreamLog(\"web_search_failed\", { query: args.query, failures });\n return {\n content: `ERROR: web search failed for \"${args.query}\" — ${failures.join(\"; \")}`,\n isError: true,\n };\n },\n};\n","import { appendFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { starHome } from \"../config/paths\";\n\n// STAR_DEBUG-gated JSONL log of stream internals (~/.star-cli/debug.log):\n// what the provider actually sent — finish reasons, usage, error details —\n// so a misbehaving relay can be diagnosed from the client side. All write\n// failures are silent; logging must never break a turn.\nexport function debugStreamLog(event: string, data?: Record<string, unknown>): void {\n if (!process.env.STAR_DEBUG) return;\n try {\n const line = `${JSON.stringify({ ts: new Date().toISOString(), event, ...data })}\\n`;\n appendFileSync(path.join(starHome(), \"debug.log\"), line);\n } catch {\n // best-effort only\n }\n}\n","import { bashTool } from \"./bash\";\nimport { editFileTool } from \"./fs/edit\";\nimport { globTool } from \"./fs/glob\";\nimport { grepTool } from \"./fs/grep\";\nimport { codeOutlineTool } from \"./fs/outline\";\nimport { createReadFileTool } from \"./fs/read\";\nimport { readImageTool } from \"./fs/read-image\";\nimport { writeFileTool } from \"./fs/write\";\nimport { screenshotTool } from \"./screenshot\";\nimport { taskKillTool, taskListTool, taskOutputTool } from \"./tasks\";\nimport { type TodoStore, createTodoTools, sharedTodoStore } from \"./todo\";\nimport type { Tool } from \"./types\";\nimport { webFetchTool } from \"./web/fetch\";\nimport { webSearchTool } from \"./web/search\";\n\nexport class ToolRegistry {\n private tools = new Map<string, Tool>();\n\n // todoStore defaults to the module-level shared store; subagent loops pass\n // their own instance so a child's todo_write cannot clobber the parent list.\n // Exposed so the agent loop can watch open items for staleness.\n readonly todos: TodoStore;\n\n constructor(todoStore?: TodoStore) {\n this.todos = todoStore ?? sharedTodoStore();\n for (const tool of [\n // Fresh per registry (i.e. per agent loop): the unchanged-since-last-\n // read cache must not leak across loops, whose contexts differ.\n createReadFileTool(),\n readImageTool,\n screenshotTool,\n writeFileTool,\n editFileTool,\n globTool,\n grepTool,\n codeOutlineTool,\n bashTool,\n webFetchTool,\n webSearchTool,\n taskListTool,\n taskOutputTool,\n taskKillTool,\n ...createTodoTools(this.todos),\n ]) {\n this.register(tool);\n }\n }\n\n register(tool: Tool): void {\n this.tools.set(tool.name, tool);\n }\n\n // Drops every tool the predicate rejects. Used to restrict a read-only\n // subagent loop's tool set; the agent loop reads list() lazily per\n // request, so a retain right after loop construction still shapes the\n // first request. The permission gate stays as backstop for anything\n // attempted outside the model's tool map.\n retain(predicate: (tool: Tool) => boolean): void {\n for (const [name, tool] of this.tools) {\n if (!predicate(tool)) this.tools.delete(name);\n }\n }\n\n // Drops per-session volatile tool state after a wholesale history rewrite\n // (compaction, /undo, resume): anything a tool remembers as \"already sent\n // to the model\" may no longer be in the context.\n resetVolatileState(): void {\n for (const tool of this.tools.values()) tool.reset?.();\n }\n\n get(name: string): Tool | undefined {\n return this.tools.get(name);\n }\n\n list(): Tool[] {\n return [...this.tools.values()];\n }\n\n names(): string[] {\n return [...this.tools.keys()];\n }\n}\n","import { ToolRegistry } from \"./registry\";\nimport type { TodoStore } from \"./todo\";\n\nexport { ToolRegistry } from \"./registry\";\nexport { TodoStore, createTodoTools, setTodoPersistGuard } from \"./todo\";\nexport type { TodoItem } from \"./todo\";\nexport type { PermissionLevel, Tool, ToolContext, ToolResult } from \"./types\";\n\n// The registry constructor registers every built-in tool (todo tools\n// included); this helper only forwards the optional per-loop todo store.\nexport function createDefaultRegistry(todoStore?: TodoStore): ToolRegistry {\n return new ToolRegistry(todoStore);\n}\n"],"mappings":"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","names":["path","z","schema","z","path","readFile","stat","path","z","schema","z","path","stat","readFile","readFile","realpath","stat","path","z","MAX_FILE_BYTES","MAX_LINE_CHARS","path","stat","readFile","schema","z","realpath","realpath","stat","path","z","BINARY_PROBE_BYTES","schema","z","path","realpath","stat","readFile","realpath","stat","path","z","schema","z","path","realpath","stat","readFile","writeFile","path","z","schema","z","path","writeFile","z","readFile","path","path","readFile","schema","z","z","z","z","path","path","schema","z"]}
@@ -1,8 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  gitTreesDir,
4
- isSensitivePath
5
- } from "./chunk-6PLZ3FLG.js";
4
+ isSensitivePath,
5
+ sensitiveExcludePatterns
6
+ } from "./chunk-2ISBRSBA.js";
7
+
8
+ // src/core/cost.ts
9
+ function computeCostUsd(usage, model) {
10
+ if (model?.promptPrice === void 0 || model.completionPrice === void 0) {
11
+ return null;
12
+ }
13
+ const cached = Math.min(Math.max(usage.cachedPromptTokens ?? 0, 0), usage.promptTokens);
14
+ const promptUsd = (usage.promptTokens - cached) * model.promptPrice + cached * (model.cacheReadPrice ?? model.promptPrice) + (usage.cacheReadInputTokens ?? 0) * (model.cacheReadPrice ?? 0);
15
+ return (promptUsd + usage.completionTokens * model.completionPrice) / 1e6;
16
+ }
6
17
 
7
18
  // src/core/messages.ts
8
19
  function toStarMessage(message) {
@@ -190,6 +201,18 @@ var ConfigSchema = z.object({
190
201
  // lower value to compact earlier and keep headroom. Manual /compact ignores
191
202
  // this — it always compacts.
192
203
  compactThresholdTokens: z.number().int().positive().optional(),
204
+ // Stale-content elision (context/elision.ts) replaces old bulk — stale tool
205
+ // results, old tool-call arguments, old attached images — with placeholders
206
+ // BEFORE whole-turn compaction fires, keeping message count and order (and
207
+ // therefore /undo markers) intact. These tune it:
208
+ // fraction of the compaction threshold at which elision activates (0 = even
209
+ // in short sessions, 1 = only at the compaction point — effectively off).
210
+ elisionThresholdFraction: z.number().min(0).max(1).default(0.5),
211
+ // newest messages never elided — the model's active working set.
212
+ elisionKeepRecentMessages: z.number().int().min(0).default(24),
213
+ // tool results / tool-call args smaller than this are cheaper kept than
214
+ // re-fetched.
215
+ elisionMinTokens: z.number().int().min(0).default(250),
193
216
  // Seconds without any stream part before a stalled response is ended
194
217
  // gracefully (some relays never send the terminal chunks). Applies once
195
218
  // content has started streaming; the wait for the first content part gets
@@ -268,26 +291,6 @@ var GC_INTERVAL = 50;
268
291
  var TRACK_FAILURE_TTL_MS = 5 * 60 * 1e3;
269
292
  var MAX_PINNED_REFS = 50;
270
293
  var PINNED_REF_PREFIX = "refs/star/";
271
- var SENSITIVE_EXCLUDE_PATTERNS = [
272
- ".env",
273
- ".env.*",
274
- "!.env.example",
275
- "!.env.sample",
276
- "!.env.template",
277
- "id_rsa",
278
- "id_dsa",
279
- "id_ecdsa",
280
- "id_ed25519",
281
- ".npmrc",
282
- ".netrc",
283
- ".pgpass",
284
- "*.pem",
285
- "*.key",
286
- "*.p12",
287
- "*.pfx",
288
- "**/.aws/credentials",
289
- "**/.kube/config"
290
- ];
291
294
  var GIT_CONFIG_ARGS = [
292
295
  "-c",
293
296
  "core.autocrlf=false",
@@ -322,7 +325,6 @@ function runGit(args, timeoutMs = GIT_TIMEOUT_MS) {
322
325
  var gitAvailable = null;
323
326
  var trackFailureUntil = /* @__PURE__ */ new Map();
324
327
  var trackCounts = /* @__PURE__ */ new Map();
325
- var scrubbedRepos = /* @__PURE__ */ new Set();
326
328
  async function isGitAvailable() {
327
329
  if (gitAvailable === null) {
328
330
  gitAvailable = await runGit(["--version"]) !== null;
@@ -337,7 +339,6 @@ async function removeTreeRepo(cwd) {
337
339
  const dir = treeRepoDir(cwd);
338
340
  trackFailureUntil.delete(path.resolve(cwd));
339
341
  trackCounts.delete(dir);
340
- scrubbedRepos.delete(dir);
341
342
  await rm(dir, { recursive: true, force: true }).catch(() => {
342
343
  });
343
344
  }
@@ -375,10 +376,15 @@ async function ensureTreeRepo(cwd) {
375
376
  await mkdir(path.join(dir, "info"), { recursive: true });
376
377
  await writeFile(
377
378
  path.join(dir, "info", "exclude"),
378
- `${SENSITIVE_EXCLUDE_PATTERNS.join("\n")}
379
+ `${sensitiveExcludePatterns().join("\n")}
379
380
  `,
380
381
  "utf8"
381
382
  );
383
+ await writeFile(
384
+ path.join(dir, "info", "attributes"),
385
+ "* -text -filter -working-tree-encoding\n",
386
+ "utf8"
387
+ );
382
388
  } catch {
383
389
  return null;
384
390
  }
@@ -425,6 +431,14 @@ async function trackTreeGuarded(cwd) {
425
431
  return tree;
426
432
  }
427
433
  async function pinTree(base, tree) {
434
+ const existing = await runGit([
435
+ ...base,
436
+ "for-each-ref",
437
+ "--format=%(refname)",
438
+ `--points-at=${tree}`,
439
+ PINNED_REF_PREFIX
440
+ ]);
441
+ if (existing === null || existing !== "") return;
428
442
  const ref = `${PINNED_REF_PREFIX}${Date.now()}-${randomBytes(4).toString("hex")}`;
429
443
  if (await runGit([...base, "update-ref", ref, tree]) === null) return;
430
444
  const listed = await runGit([...base, "for-each-ref", "--format=%(refname)", PINNED_REF_PREFIX]);
@@ -438,10 +452,7 @@ async function trackTreeUncached(cwd) {
438
452
  const dir = await ensureTreeRepo(cwd);
439
453
  if (!dir) return null;
440
454
  const base = repoArgs(dir, cwd);
441
- if (!scrubbedRepos.has(dir)) {
442
- if (!await untrackExcluded(dir, cwd)) return null;
443
- scrubbedRepos.add(dir);
444
- }
455
+ if (!await untrackExcluded(dir, cwd)) return null;
445
456
  if (await runGit([...base, "add", "-A"]) === null) return null;
446
457
  const tree = await runGit([...base, "write-tree"]);
447
458
  if (tree === null) return null;
@@ -469,6 +480,7 @@ async function diffTreeNames(cwd, hash) {
469
480
  if (!await isGitAvailable()) return null;
470
481
  const dir = treeRepoDir(cwd);
471
482
  if (!existsSync(path.join(dir, "HEAD"))) return null;
483
+ if (!await ensureTreeRepo(cwd)) return null;
472
484
  const base = repoArgs(dir, cwd);
473
485
  const changed = await runGit([...base, "diff", "--name-only", hash]);
474
486
  const untracked = await runGit([...base, "ls-files", "--others", "--exclude-standard"]);
@@ -481,6 +493,7 @@ async function diffTreeNames(cwd, hash) {
481
493
  }
482
494
 
483
495
  export {
496
+ computeCostUsd,
484
497
  toStarMessage,
485
498
  toCoreMessages,
486
499
  isSyntheticUserMessage,
@@ -502,4 +515,4 @@ export {
502
515
  restoreTree,
503
516
  diffTreeNames
504
517
  };
505
- //# sourceMappingURL=chunk-RD4S5LKM.js.map
518
+ //# sourceMappingURL=chunk-CECMCDZG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/cost.ts","../src/core/messages.ts","../src/config/schema.ts","../src/snapshot/git-tree.ts"],"sourcesContent":["import type { ModelConfig } from \"../config/schema\";\n\nexport interface CostUsage {\n promptTokens: number;\n completionTokens: number;\n cachedPromptTokens?: number;\n cacheReadInputTokens?: number;\n}\n\nexport function computeCostUsd(usage: CostUsage, model: ModelConfig | undefined): number | null {\n if (model?.promptPrice === undefined || model.completionPrice === undefined) {\n return null;\n }\n const cached = Math.min(Math.max(usage.cachedPromptTokens ?? 0, 0), usage.promptTokens);\n const promptUsd =\n (usage.promptTokens - cached) * model.promptPrice +\n cached * (model.cacheReadPrice ?? model.promptPrice) +\n (usage.cacheReadInputTokens ?? 0) * (model.cacheReadPrice ?? 0);\n return (promptUsd + usage.completionTokens * model.completionPrice) / 1_000_000;\n}\n","import type { CoreMessage, ToolCallPart } from \"ai\";\n\nexport type { CoreMessage };\n\n// Side-band metadata the model never sees. It rides next to a persisted\n// message (the __starMeta jsonl key) so turn-boundary scans can tell a real\n// user turn from a loop-injected user message:\n// - \"nudge\": auto-continue / resume-after-cutoff steering injected mid-turn\n// - \"bg-report\": a finished background subagent's report, delivered mid-turn\n// - \"tool-image\": images a tool (read_image/screenshot) attached mid-turn\n// - \"steer\": a user prompt injected into the running turn (chosen at submit\n// time over queueing), delivered at the next step boundary\nexport interface MessageMeta {\n synthetic?: \"nudge\" | \"bg-report\" | \"tool-image\" | \"steer\";\n // Set by context/elision.ts when the message's bulky content (a stale tool\n // result, an old attached image) was replaced with a placeholder to free\n // the window — idempotency marker and debugging aid; the model never sees it.\n elided?: boolean;\n}\n\nexport interface StarMessage {\n message: CoreMessage;\n meta?: MessageMeta;\n}\n\nexport function toStarMessage(message: CoreMessage | StarMessage): StarMessage {\n return \"role\" in message ? { message } : message;\n}\n\nexport function toCoreMessages(messages: readonly StarMessage[]): CoreMessage[] {\n return messages.map((star) => star.message);\n}\n\n// A synthetic user message belongs to the turn that precedes it: it is never\n// a turn boundary for /undo, /rewind or compaction turn splitting.\nexport function isSyntheticUserMessage(star: StarMessage): boolean {\n return star.message.role === \"user\" && star.meta?.synthetic !== undefined;\n}\n\n// Plain-text content of a message: string content as-is; for array content,\n// the text parts joined with a space (tool calls, images and tool results\n// contribute nothing). Shared by the agent loop (/redo labels) and the CLI\n// (history rendering, conversation copy) so the extraction rule lives once.\nexport function coreMessageText(message: CoreMessage | undefined): string {\n if (!message) return \"\";\n const content = message.content;\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return content\n .filter((part) => part.type === \"text\")\n .map((part) => (\"text\" in part ? part.text : \"\"))\n .join(\" \");\n }\n return \"\";\n}\n\nexport interface ImageInput {\n path: string;\n mimeType: string;\n /** Base64-encoded image bytes. */\n data: string;\n}\n\nexport interface MultimodalInput {\n text: string;\n images: ImageInput[];\n}\n\nexport type ChatInput = string | MultimodalInput;\n\nexport interface ConversationState {\n messages: CoreMessage[];\n}\n\n// A tool call that has no real execution result must still be closed with a\n// synthetic tool message, or the stored history can no longer be sent to the\n// API. Three scenarios, three wordings:\n// - MISSING_TOOL_RESULT_TEXT: reconcileToolCalls/reconcileStarMessages repair\n// a persisted history whose session ended before the results were written\n// (crash, kill, older version) — the call may never have run at all.\n// - INTERRUPTED_TOOL_RESULT_TEXT: the user pressed Esc while the call was\n// still streaming in or executing (agent loop abort paths).\n// - UNFINISHED_TOOL_RESULT_TEXT: execution stopped for any other reason\n// (e.g. an error mid-batch) before the call produced a result.\nexport const MISSING_TOOL_RESULT_TEXT = \"tool result missing (session was interrupted)\";\nexport const INTERRUPTED_TOOL_RESULT_TEXT = \"Tool execution interrupted by user.\";\nexport const UNFINISHED_TOOL_RESULT_TEXT =\n \"Tool execution interrupted before a result was produced.\";\n\nexport function reconcileToolCalls(messages: CoreMessage[]): CoreMessage[] {\n const result: CoreMessage[] = [];\n let pending: { toolCallId: string; toolName: string }[] = [];\n // Non-tool messages that arrived while a tool-call batch was still open\n // (e.g. tool-attached image messages persisted by a version that\n // interleaved them between sibling tool results) are held back and emitted\n // once the batch closes: strict providers reject a user message sitting\n // inside the tool-result block (\"an assistant message with 'tool_calls'\n // must be followed by tool messages\"), and flushing synthetic fillers at\n // that point would also duplicate the real results that follow.\n let deferred: CoreMessage[] = [];\n\n const flushPending = () => {\n for (const call of pending) {\n result.push({\n role: \"tool\",\n content: [\n {\n type: \"tool-result\",\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n result: MISSING_TOOL_RESULT_TEXT,\n },\n ],\n });\n }\n pending = [];\n result.push(...deferred);\n deferred = [];\n };\n\n for (const message of messages) {\n if (message.role === \"assistant\" && Array.isArray(message.content)) {\n flushPending();\n pending = message.content\n .filter((part): part is ToolCallPart => part.type === \"tool-call\")\n .map((part) => ({ toolCallId: part.toolCallId, toolName: part.toolName }));\n result.push(message);\n } else if (message.role === \"tool\" && Array.isArray(message.content) && pending.length > 0) {\n const answered = new Set(message.content.map((part) => part.toolCallId));\n pending = pending.filter((call) => !answered.has(call.toolCallId));\n result.push(message);\n if (pending.length === 0) {\n result.push(...deferred);\n deferred = [];\n }\n } else if (pending.length > 0) {\n deferred.push(message);\n } else {\n result.push(message);\n }\n }\n flushPending();\n return result;\n}\n\n// StarMessage counterpart of reconcileToolCalls: same repair, preserving the\n// side-band meta of every surviving message. Synthetic filler results carry\n// no meta.\nexport function reconcileStarMessages(messages: readonly StarMessage[]): StarMessage[] {\n const result: StarMessage[] = [];\n let pending: { toolCallId: string; toolName: string }[] = [];\n let deferred: StarMessage[] = [];\n\n const flushPending = () => {\n for (const call of pending) {\n result.push({\n message: {\n role: \"tool\",\n content: [\n {\n type: \"tool-result\",\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n result: MISSING_TOOL_RESULT_TEXT,\n },\n ],\n },\n });\n }\n pending = [];\n result.push(...deferred);\n deferred = [];\n };\n\n for (const star of messages) {\n const message = star.message;\n if (message.role === \"assistant\" && Array.isArray(message.content)) {\n flushPending();\n pending = message.content\n .filter((part): part is ToolCallPart => part.type === \"tool-call\")\n .map((part) => ({ toolCallId: part.toolCallId, toolName: part.toolName }));\n result.push(star);\n } else if (message.role === \"tool\" && Array.isArray(message.content) && pending.length > 0) {\n const answered = new Set(message.content.map((part) => part.toolCallId));\n pending = pending.filter((call) => !answered.has(call.toolCallId));\n result.push(star);\n if (pending.length === 0) {\n result.push(...deferred);\n deferred = [];\n }\n } else if (pending.length > 0) {\n deferred.push(star);\n } else {\n result.push(star);\n }\n }\n flushPending();\n return result;\n}\n\n// Removes the last conversation turn: the final user message and everything\n// after it (assistant text, tool calls and results belonging to that turn).\n// Synthetic user messages (auto-continue nudges, background reports, tool\n// images) belong to the turn they sit in, so the scan skips them and the\n// boundary lands on the real user message that opened the turn. A leading\n// system message is never touched. Returns the trimmed list and how many\n// messages were dropped (0 = nothing to retract).\nexport function retractLastTurn(messages: StarMessage[]): {\n messages: StarMessage[];\n removed: number;\n} {\n for (let i = messages.length - 1; i >= 0; i--) {\n const star = messages[i];\n if (star && star.message.role === \"user\" && !isSyntheticUserMessage(star)) {\n return { messages: messages.slice(0, i), removed: messages.length - i };\n }\n }\n return { messages, removed: 0 };\n}\n","import { z } from \"zod\";\n\nexport const ProviderConfigSchema = z.object({\n name: z.string(),\n // openai-responses talks to the Responses API (/responses) instead of chat\n // completions — the wire format codex uses, and the native API of\n // reasoning-model relays that otherwise hide reasoning behind empty chat\n // completions. streamChat sends strictSchemas:false + store:false with it.\n protocol: z\n .enum([\"openai-compatible\", \"anthropic\", \"openai-responses\"])\n .default(\"openai-compatible\"),\n baseURL: z.string(),\n apiKeyEnv: z.string().optional(),\n apiKey: z.string().optional(),\n headers: z.record(z.string()).optional(),\n});\n\nexport const ModelConfigSchema = z.object({\n name: z.string(),\n provider: z.string(),\n model: z.string(),\n maxTokens: z.number().int().positive().optional(),\n // Per-model context window; overrides the top-level contextMaxTokens for\n // compaction and the status bar when set.\n contextMaxTokens: z.number().int().positive().optional(),\n // Reasoning intensity for thinking models, forwarded verbatim as\n // OpenAI-style reasoning_effort on every request (llm/provider.ts\n // reasoningEffortMetadata) — level naming varies by model/provider\n // (low|medium|high are common; some add minimal/max/xhigh/none), so any\n // non-empty string is accepted and the server validates it. Unset = the\n // request carries no effort field and the server-side default applies.\n // Anthropic maps effort to a thinking budget instead — ignored there.\n reasoningEffort: z.string().min(1).optional(),\n // Sampling temperature forwarded on every request. Unset = the AI SDK's\n // own default applies (ai@4 sends 0 — it does NOT omit the field). Some\n // endpoints reject anything but one explicit value (e.g. kimi-for-coding:\n // \"invalid temperature: only 1 is allowed\") — set it here. No upper\n // bound: ranges vary by provider and the server validates.\n temperature: z.number().min(0).optional(),\n // Per-model overrides of the top-level stream watchdog timeouts (unset =\n // the global values apply). Raise streamIdleTimeoutSec for relays that\n // buffer long generations and stall mid-stream between flushes.\n streamIdleTimeoutSec: z.number().positive().optional(),\n streamFirstChunkTimeoutSec: z.number().positive().optional(),\n // Text-only models: set false so image-producing tools (read_image,\n // screenshot) decline with a text error instead of attaching images the\n // endpoint would reject with an unrecoverable 4xx. Unset = images allowed.\n vision: z.boolean().optional(),\n // Cost estimation needs BOTH prices (USD per 1M tokens); with only one set\n // the model is treated as unpriced.\n promptPrice: z.number().nonnegative().optional(),\n completionPrice: z.number().nonnegative().optional(),\n // Optional price (USD per 1M tokens) for cache-read prompt tokens. When\n // unset, OpenAI-style cached tokens bill at promptPrice and Anthropic-style\n // cache reads are not billed — set this for accurate cache pricing.\n cacheReadPrice: z.number().nonnegative().optional(),\n});\n\nexport const PermissionsConfigSchema = z.object({\n allow: z.array(z.string()).default([]),\n deny: z.array(z.string()).default([]),\n // Rules that force a confirmation prompt in auto mode (same tool(glob)\n // syntax as allow/deny) — e.g. ask = [\"bash(git push *)\"]. Priority:\n // deny > ask > allow > mode default. Only meaningful in auto mode (ask\n // mode already prompts; readonly/plan are unaffected; yolo bypasses all).\n ask: z.array(z.string()).default([]),\n // Extra likely-secret file globs extending core/sensitive.ts's hardcoded\n // list (same single-* glob semantics as rules, matched case-insensitively\n // against the basename and the full normalized path). Global config only —\n // the project-config sandbox drops the whole [permissions] table.\n sensitive: z.array(z.string()).default([]),\n});\n\nexport const HookConfigSchema = z.object({\n event: z.enum([\"PreToolUse\", \"PostToolUse\", \"Stop\"]),\n matcher: z\n .string()\n .refine(\n (pattern) => {\n try {\n new RegExp(pattern);\n return true;\n } catch {\n return false;\n }\n },\n { message: \"invalid regular expression\" },\n )\n .optional(),\n command: z.string().min(1),\n timeoutSec: z.number().positive().default(30),\n});\n\nexport const ConfigSchema = z.object({\n defaultModel: z.string().default(\"\"),\n // Optional [[models]] name for cheap auxiliary LLM calls (compaction\n // summaries, session titles, turn completion checks) — Claude Code routes\n // these to a Haiku-class model. Unset = the active main model does them.\n smallModel: z.string().optional(),\n permissionMode: z.enum([\"auto\", \"ask\", \"readonly\", \"yolo\", \"plan\"]).default(\"ask\"),\n providers: z.array(ProviderConfigSchema).default([]),\n models: z.array(ModelConfigSchema).default([]),\n // Cap on model round-trips (\"steps\") per turn between progress checkpoints.\n // One step is a streamed reply plus every tool call it asked for — stream\n // retries and auto-continue nudges do not consume steps. Reaching the cap\n // while tools still execute resets the budget with a notice; the turn stops\n // only when no tool ran since the last checkpoint (the doom-loop\n // signature). 0 disables the cap entirely and is the default\n // (opencode/codex have none).\n maxSteps: z.number().int().min(0).default(0),\n contextMaxTokens: z.number().int().positive().default(200_000),\n // Token threshold at which the agent loop auto-compacts the history.\n // Defaults to the effective context window (compact only when full); set a\n // lower value to compact earlier and keep headroom. Manual /compact ignores\n // this — it always compacts.\n compactThresholdTokens: z.number().int().positive().optional(),\n // Stale-content elision (context/elision.ts) replaces old bulk — stale tool\n // results, old tool-call arguments, old attached images — with placeholders\n // BEFORE whole-turn compaction fires, keeping message count and order (and\n // therefore /undo markers) intact. These tune it:\n // fraction of the compaction threshold at which elision activates (0 = even\n // in short sessions, 1 = only at the compaction point — effectively off).\n elisionThresholdFraction: z.number().min(0).max(1).default(0.5),\n // newest messages never elided — the model's active working set.\n elisionKeepRecentMessages: z.number().int().min(0).default(24),\n // tool results / tool-call args smaller than this are cheaper kept than\n // re-fetched.\n elisionMinTokens: z.number().int().min(0).default(250),\n // Seconds without any stream part before a stalled response is ended\n // gracefully (some relays never send the terminal chunks). Applies once\n // content has started streaming; the wait for the first content part gets\n // a longer allowance (control/metadata and reasoning parts don't count as\n // content).\n streamIdleTimeoutSec: z.number().positive().default(60),\n // Seconds to wait for the very first content part before giving up; slow\n // thinking-model endpoints can stay silent for minutes after accepting\n // the request.\n streamFirstChunkTimeoutSec: z.number().positive().default(300),\n // Extra attempts per model request when a stream fails transiently\n // (network error, 429/5xx, idle watchdog cutoff) or comes back empty, so a\n // relay hiccup does not silently end a half-finished turn. 0 disables.\n // The wait between attempts honors Retry-After headers and otherwise backs\n // off exponentially with jitter (llm/retry.ts); retries after a\n // timeout/empty failure also scale both stream timeouts up (attempt number\n // ×, capped at 3x) since the relay is likely overloaded. Default 5 matches\n // opencode/Codex retry budgets and rides out flaky relays (中转站) better\n // than a minimal budget.\n streamMaxRetries: z.number().int().min(0).default(5),\n // How many consecutive unproductive replies may be nudged before the turn\n // is handed back: a text-only reply that announces pending work (\"我将…\",\n // \"I will…\") or answers a nudge with more words, or an empty reply that\n // survived the stream retry budget (steered with a nudge — a changed\n // request — since identical resends already failed). Tool calls reset the\n // count, so a productive task keeps going; exhaustion surfaces a notice\n // instead of stopping silently. Never fires in plan mode. 0 disables.\n maxAutoContinues: z.number().int().min(0).default(2),\n contextCompaction: z.enum([\"summary\", \"truncate\"]).default(\"summary\"),\n // Optional per-session dollar budget; when set, the REPL stops the session\n // once the estimated cost for the session reaches this amount.\n sessionBudgetUsd: z.number().positive().optional(),\n // Terminal bell (REPL only): ring when a turn takes longer than\n // notifyBellThresholdSec, and when a background task finishes.\n // STAR_NO_NOTIFY=1 disables without touching the config.\n notifyBell: z.boolean().default(true),\n notifyBellThresholdSec: z.number().positive().default(10),\n permissions: PermissionsConfigSchema.default({ allow: [], deny: [] }),\n hooks: z.array(HookConfigSchema).default([]),\n // Consecutive identical tool calls (same tool + same arguments) allowed\n // before the loop refuses to execute the repeat and tells the model to\n // change approach — a guard against agents stuck retrying a failing call\n // (opencode's doom_loop). 0 disables.\n doomLoopThreshold: z.number().int().min(0).default(3),\n // Track the whole working tree with an internal git repo (outside the\n // user's .git) at every turn boundary, so /undo and /redo can restore\n // changes made by ANY tool (bash included), not just write_file/edit_file.\n // Falls back silently to per-file snapshots when git is unavailable.\n gitSnapshots: z.boolean().default(true),\n // web_fetch SSRF guard escape hatch: by default target hosts are resolved\n // before connecting and private/reserved ranges are refused (loopback,\n // RFC1918, link-local incl. the cloud metadata address 169.254.169.254,\n // ULA, and localhost/.local/.internal names; redirect hops re-checked).\n // true disables the guard for intranet use. Global config only — not in\n // the project-config sandbox whitelist.\n webFetchAllowPrivateHosts: z.boolean().default(false),\n});\n\nexport type ProviderConfig = z.infer<typeof ProviderConfigSchema>;\nexport type ModelConfig = z.infer<typeof ModelConfigSchema>;\nexport type PermissionsConfig = z.infer<typeof PermissionsConfigSchema>;\nexport type HookConfig = z.infer<typeof HookConfigSchema>;\nexport type StarConfig = z.infer<typeof ConfigSchema>;\n\n// Effective context window for a model: its own contextMaxTokens when set,\n// otherwise the top-level config.contextMaxTokens.\nexport function contextWindowTokens(config: StarConfig, modelName: string): number {\n return (\n config.models.find((m) => m.name === modelName)?.contextMaxTokens ?? config.contextMaxTokens\n );\n}\n\n// Token threshold that triggers automatic compaction in the agent loop:\n// config.compactThresholdTokens when set, clamped to the window so a stale\n// value (e.g. after switching to a smaller model) never pushes compaction\n// past it; otherwise the window itself (current behavior).\nexport function resolveCompactThreshold(config: StarConfig, windowTokens: number): number {\n return Math.min(config.compactThresholdTokens ?? windowTokens, windowTokens);\n}\n\nexport interface CliOverrides {\n model?: string;\n permissionMode?: \"auto\" | \"ask\" | \"readonly\" | \"yolo\" | \"plan\";\n}\n","import { execFile } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readdir, rm, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { gitTreesDir } from \"../config/paths\";\nimport { isSensitivePath, sensitiveExcludePatterns } from \"../core/sensitive\";\n\nconst GIT_TIMEOUT_MS = 10_000;\n// gc repacks the object store and legitimately takes longer than the budget\n// every other git invocation gets; it runs rarely, so it can afford to wait.\nconst GC_TIMEOUT_MS = 60_000;\nconst GIT_MAX_BUFFER = 8 * 1024 * 1024;\n// `git gc --auto` runs after this many successful tree captures per repo\n// (each capture adds a tree plus blobs, and nothing else ever prunes them).\nconst GC_INTERVAL = 50;\n// The agent loop calls trackTree at the start of every turn; without a\n// negative cache a directory whose tracking fails (huge tree hitting the git\n// timeout, unreadable repo) would burn that timeout on every single turn.\nconst TRACK_FAILURE_TTL_MS = 5 * 60 * 1000;\n// Every captured tree is pinned under refs/star/<timestamp>-<random> so a\n// `gc --auto` prune (default gc.pruneExpire of two weeks) can never drop a\n// live /undo target. The namespace is trimmed to the newest this many refs,\n// matching the per-file snapshot stack cap.\nconst MAX_PINNED_REFS = 50;\nconst PINNED_REF_PREFIX = \"refs/star/\";\n\n// Forced config for every invocation: no CRLF translation (a file captured\n// with LF endings must restore byte-identically even on a machine whose\n// global config sets core.autocrlf=true) and no LFS filters rewriting blob\n// contents. Command-line -c beats repo/global/system config, so repos\n// created before this fix are covered too.\nconst GIT_CONFIG_ARGS = [\n \"-c\",\n \"core.autocrlf=false\",\n \"-c\",\n \"core.eol=native\",\n \"-c\",\n \"filter.lfs.process=\",\n \"-c\",\n \"filter.lfs.smudge=\",\n \"-c\",\n \"filter.lfs.clean=\",\n];\n\n// The child gets the parent environment minus every GIT_* variable (matched\n// case-insensitively: Windows env keys are). A star launched from a git hook\n// or rebase inherits GIT_DIR/GIT_INDEX_FILE pointing at the user's repo, and\n// GIT_INDEX_FILE redirects index writes even past an explicit --git-dir.\nfunction gitEnv(): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (value !== undefined && !key.toUpperCase().startsWith(\"GIT_\")) env[key] = value;\n }\n return env;\n}\n\n// Every git invocation goes through here: array arguments (never a shell), a\n// per-call timeout, and any failure — git missing, repo corrupt, timeout —\n// resolves null so the snapshot feature degrades silently instead of breaking\n// the agent loop.\nfunction runGit(args: string[], timeoutMs = GIT_TIMEOUT_MS): Promise<string | null> {\n return new Promise((resolve) => {\n execFile(\n \"git\",\n [...GIT_CONFIG_ARGS, ...args],\n { timeout: timeoutMs, maxBuffer: GIT_MAX_BUFFER, encoding: \"utf8\", env: gitEnv() },\n (error, stdout) => {\n resolve(error ? null : stdout.trim());\n },\n );\n });\n}\n\nlet gitAvailable: boolean | null = null;\n\n// Expiry timestamps (Date.now) of the per-directory failure negative cache.\nconst trackFailureUntil = new Map<string, number>();\n// Successful captures per repo since process start, driving the periodic gc.\nconst trackCounts = new Map<string, number>();\n\nexport async function isGitAvailable(): Promise<boolean> {\n if (gitAvailable === null) {\n gitAvailable = (await runGit([\"--version\"])) !== null;\n }\n return gitAvailable;\n}\n\n// Test hook: drop the cached availability probe so a mocked execFile takes\n// effect without reloading the module.\nexport function resetGitTreeAvailability(): void {\n gitAvailable = null;\n}\n\n// Test hook: drop every piece of cached state (availability probe, failure\n// negative cache, gc counters, capture chains).\nexport function resetGitTreeCaches(): void {\n gitAvailable = null;\n trackFailureUntil.clear();\n trackCounts.clear();\n trackChains.clear();\n}\n\n// One internal bare repo per project directory, named by the resolved path so\n// two directories never share a tree store.\nexport function treeRepoDir(cwd: string): string {\n const key = Buffer.from(path.resolve(cwd), \"utf8\").toString(\"base64url\");\n return path.join(gitTreesDir(), key);\n}\n\n// Removes a project's internal snapshot repo. Called when the project's last\n// session is cleared: the captured trees only make sense as /undo//redo\n// targets for session turns, and with no session left they are unreachable\n// disk usage. In-memory bookkeeping goes too, or a later session in the same\n// process would trust a failure cache / gc counter for a repo that no longer\n// exists.\nexport async function removeTreeRepo(cwd: string): Promise<void> {\n const dir = treeRepoDir(cwd);\n trackFailureUntil.delete(path.resolve(cwd));\n trackCounts.delete(dir);\n await rm(dir, { recursive: true, force: true }).catch(() => {});\n}\n\n// Deletes every tree repo whose owning project has no surviving session.\n// remainingCwds are the cwd values of the sessions still on disk; repos are\n// keyed by resolved path (see treeRepoDir), so a basename compare keeps\n// exactly those.\nexport async function pruneTreeReposExcept(remainingCwds: Iterable<string>): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(gitTreesDir());\n } catch {\n return;\n }\n const keep = new Set<string>();\n for (const cwd of remainingCwds) keep.add(path.basename(treeRepoDir(cwd)));\n await Promise.all(\n entries\n .filter((entry) => !keep.has(entry))\n .map((entry) =>\n rm(path.join(gitTreesDir(), entry), { recursive: true, force: true }).catch(() => {}),\n ),\n );\n}\n\n// Snapshotting the home directory or a filesystem root would track an entire\n// machine — refuse rather than let a stray session there index everything.\nfunction isGuardedDir(cwd: string): boolean {\n const resolved = path.resolve(cwd);\n const home = path.resolve(os.homedir());\n const same = (a: string, b: string) =>\n process.platform === \"win32\" ? a.toLowerCase() === b.toLowerCase() : a === b;\n return same(resolved, home) || same(resolved, path.parse(resolved).root);\n}\n\nasync function ensureTreeRepo(cwd: string): Promise<string | null> {\n const dir = treeRepoDir(cwd);\n try {\n // Owner-only like the session directories: tree objects hold file\n // contents the user never committed anywhere else.\n await mkdir(dir, { recursive: true, mode: 0o700 });\n if (!existsSync(path.join(dir, \"HEAD\"))) {\n const out = await runGit([\"init\", \"--bare\", dir]);\n if (out === null) return null;\n // Belt and braces for fresh repos; runGit's forced -c flags already\n // cover repos created before the repo-local setting existed.\n await runGit([`--git-dir=${dir}`, \"config\", \"core.autocrlf\", \"false\"]);\n }\n // Rewritten on every call so config changes immediately protect captures,\n // previews, restores and cleans, including in pre-existing repos.\n await mkdir(path.join(dir, \"info\"), { recursive: true });\n await writeFile(\n path.join(dir, \"info\", \"exclude\"),\n `${sensitiveExcludePatterns().join(\"\\n\")}\\n`,\n \"utf8\",\n );\n await writeFile(\n path.join(dir, \"info\", \"attributes\"),\n \"* -text -filter -working-tree-encoding\\n\",\n \"utf8\",\n );\n } catch {\n return null;\n }\n return dir;\n}\n\nfunction repoArgs(dir: string, cwd: string): string[] {\n return [`--git-dir=${dir}`, `--work-tree=${cwd}`];\n}\n\n// Drops index entries that match the exclude rules. Ignore patterns only keep\n// untracked files out of `git add`; a path already in the index — tracked by\n// a pre-exclusion star, or put back by read-tree of an old tree — stays until\n// explicitly removed, so both trackTree and restoreTree scrub it.\nasync function untrackExcluded(dir: string, cwd: string): Promise<boolean> {\n const base = repoArgs(dir, cwd);\n const listed = await runGit([...base, \"ls-files\", \"-z\", \"-c\", \"-i\", \"--exclude-standard\"]);\n if (listed === null) return false;\n const files = listed.split(\"\\0\").filter(Boolean);\n if (files.length === 0) return true;\n const removed = await runGit([\n ...base,\n \"rm\",\n \"--cached\",\n \"--ignore-unmatch\",\n \"-q\",\n \"--\",\n ...files.map((file) => `:(literal)${file}`),\n ]);\n return removed !== null;\n}\n\n// Captures the whole working tree (any change — write_file, edit_file, bash)\n// as a git tree object and returns its hash. The work tree's own .git is\n// skipped by git automatically; ignored files (node_modules) and excluded\n// likely-secret files never enter the tree, which is exactly what the restore\n// side relies on. A recent failure makes further attempts no-ops until\n// TRACK_FAILURE_TTL_MS has passed.\nexport function trackTree(cwd: string): Promise<string | null> {\n // Serialize captures per directory: the agent loop starts a turn's capture\n // concurrently and a quick second turn (or the /undo path) can arrive while\n // it still runs — two overlapping captures would race on the internal\n // repo's index.lock, and the loser's spurious failure would poison the\n // negative cache for five minutes. trackTreeUncached never rejects, so the\n // chain cannot wedge; each call still captures its own tree, in order.\n const key = path.resolve(cwd);\n const next = (trackChains.get(key) ?? Promise.resolve(null)).then(() => trackTreeGuarded(cwd));\n trackChains.set(key, next);\n return next;\n}\n\n// Per-cwd promise chains serializing trackTree (see above). Values stay\n// settled promises between captures; the map is keyed by directory, so it\n// stays tiny.\nconst trackChains = new Map<string, Promise<string | null>>();\n\nasync function trackTreeGuarded(cwd: string): Promise<string | null> {\n if (isGuardedDir(cwd)) return null;\n const key = path.resolve(cwd);\n const failedUntil = trackFailureUntil.get(key);\n if (failedUntil !== undefined && Date.now() < failedUntil) return null;\n const tree = await trackTreeUncached(cwd);\n if (tree === null) {\n trackFailureUntil.set(key, Date.now() + TRACK_FAILURE_TTL_MS);\n return null;\n }\n trackFailureUntil.delete(key);\n return tree;\n}\n\n// Pins a captured tree under refs/star/<timestamp>-<random> so gc --auto\n// never prunes a live /undo target, then trims the namespace to the newest\n// MAX_PINNED_REFS refs: for-each-ref sorts by refname, and equal-length\n// millisecond timestamps make that order chronological. Pure housekeeping —\n// a failure here never fails the capture.\nasync function pinTree(base: string[], tree: string): Promise<void> {\n const existing = await runGit([\n ...base,\n \"for-each-ref\",\n \"--format=%(refname)\",\n `--points-at=${tree}`,\n PINNED_REF_PREFIX,\n ]);\n if (existing === null || existing !== \"\") return;\n const ref = `${PINNED_REF_PREFIX}${Date.now()}-${randomBytes(4).toString(\"hex\")}`;\n if ((await runGit([...base, \"update-ref\", ref, tree])) === null) return;\n const listed = await runGit([...base, \"for-each-ref\", \"--format=%(refname)\", PINNED_REF_PREFIX]);\n if (listed === null) return;\n const refs = listed.split(\"\\n\").filter(Boolean);\n const stale = refs.slice(0, Math.max(0, refs.length - MAX_PINNED_REFS));\n await Promise.all(stale.map((name) => runGit([...base, \"update-ref\", \"-d\", name])));\n}\n\nasync function trackTreeUncached(cwd: string): Promise<string | null> {\n if (!(await isGitAvailable())) return null;\n const dir = await ensureTreeRepo(cwd);\n if (!dir) return null;\n const base = repoArgs(dir, cwd);\n if (!(await untrackExcluded(dir, cwd))) return null;\n if ((await runGit([...base, \"add\", \"-A\"])) === null) return null;\n const tree = await runGit([...base, \"write-tree\"]);\n if (tree === null) return null;\n await pinTree(base, tree);\n const count = (trackCounts.get(dir) ?? 0) + 1;\n trackCounts.set(dir, count);\n if (count % GC_INTERVAL === 0) {\n // Pure housekeeping: the result is ignored so a gc failure never fails\n // the turn, matching the module's silent-degradation style.\n await runGit([`--git-dir=${dir}`, \"gc\", \"--auto\"], GC_TIMEOUT_MS);\n }\n return tree;\n}\n\n// Restores the working tree to a captured tree: index reset to the tree,\n// every index file checked out over the work tree, then untracked\n// (post-snapshot) files cleaned. `clean` keeps ignored files, so dependencies\n// and excluded secrets survive. checkout-index -a only writes paths present\n// in the index, so scrubbing excluded entries after read-tree guarantees a\n// tree captured before the exclusion existed still cannot overwrite the\n// user's current .env (or delete it via clean, which also skips it).\nexport async function restoreTree(cwd: string, hash: string): Promise<boolean> {\n if (isGuardedDir(cwd)) return false;\n if (!(await isGitAvailable())) return false;\n const dir = await ensureTreeRepo(cwd);\n if (!dir) return false;\n const base = repoArgs(dir, cwd);\n if ((await runGit([...base, \"read-tree\", hash])) === null) return false;\n if (!(await untrackExcluded(dir, cwd))) return false;\n if ((await runGit([...base, \"checkout-index\", \"-f\", \"-a\"])) === null) return false;\n if ((await runGit([...base, \"clean\", \"-fd\"])) === null) return false;\n return true;\n}\n\n// Read-only list of files restoring a tree would touch: tracked paths that\n// differ from the tree, plus untracked (non-ignored) paths the clean step\n// would remove. Backs the /undo and /redo previews.\nexport async function diffTreeNames(cwd: string, hash: string): Promise<string[] | null> {\n if (!(await isGitAvailable())) return null;\n const dir = treeRepoDir(cwd);\n if (!existsSync(path.join(dir, \"HEAD\"))) return null;\n if (!(await ensureTreeRepo(cwd))) return null;\n const base = repoArgs(dir, cwd);\n const changed = await runGit([...base, \"diff\", \"--name-only\", hash]);\n const untracked = await runGit([...base, \"ls-files\", \"--others\", \"--exclude-standard\"]);\n if (changed === null || untracked === null) return null;\n const names = new Set<string>();\n for (const line of [...changed.split(\"\\n\"), ...untracked.split(\"\\n\")]) {\n // Restore never touches excluded likely-secret paths (trees captured\n // before the exclusion may still list them); keep the preview honest.\n if (line !== \"\" && !isSensitivePath(line)) names.add(line);\n }\n return [...names];\n}\n"],"mappings":"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","names":[]}
@@ -117,13 +117,21 @@ var SseNormalizeTransform = class extends TransformStream {
117
117
  return;
118
118
  }
119
119
  if (line.startsWith("data:")) {
120
- flushPending(controller);
121
- emitPayload(line.slice(5).replace(/^ /, ""), controller);
120
+ const payload = line.slice(5).replace(/^ /, "");
121
+ if (pending !== null && splitJsonPrefix(pending)) flushPending(controller);
122
+ if (pending === null) emitPayload(payload, controller);
123
+ else emitPayload(`${pending}
124
+ ${payload}`, controller);
122
125
  return;
123
126
  }
124
127
  if (line.startsWith("{") && pending !== null) {
125
- flushPending(controller);
126
- emitPayload(line, controller);
128
+ if (splitJsonPrefix(pending)) {
129
+ flushPending(controller);
130
+ emitPayload(line, controller);
131
+ } else {
132
+ emitPayload(`${pending}
133
+ ${line}`, controller);
134
+ }
127
135
  return;
128
136
  }
129
137
  flushPending(controller);
@@ -229,4 +237,4 @@ export {
229
237
  createModel,
230
238
  reasoningEffortMetadata
231
239
  };
232
- //# sourceMappingURL=chunk-MVAMPVTA.js.map
240
+ //# sourceMappingURL=chunk-KILMI3S5.js.map