gsd-pi 2.8.0 → 2.8.2

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 (143) hide show
  1. package/dist/loader.js +5 -0
  2. package/node_modules/@gsd/pi-coding-agent/dist/config.d.ts +2 -0
  3. package/node_modules/@gsd/pi-coding-agent/dist/config.d.ts.map +1 -1
  4. package/node_modules/@gsd/pi-coding-agent/dist/config.js +4 -0
  5. package/node_modules/@gsd/pi-coding-agent/dist/config.js.map +1 -1
  6. package/node_modules/@gsd/pi-coding-agent/dist/core/artifact-manager.d.ts +52 -0
  7. package/node_modules/@gsd/pi-coding-agent/dist/core/artifact-manager.d.ts.map +1 -0
  8. package/node_modules/@gsd/pi-coding-agent/dist/core/artifact-manager.js +117 -0
  9. package/node_modules/@gsd/pi-coding-agent/dist/core/artifact-manager.js.map +1 -0
  10. package/node_modules/@gsd/pi-coding-agent/dist/core/bash-executor.js +2 -2
  11. package/node_modules/@gsd/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  12. package/node_modules/@gsd/pi-coding-agent/dist/core/blob-store.d.ts +31 -0
  13. package/node_modules/@gsd/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -0
  14. package/node_modules/@gsd/pi-coding-agent/dist/core/blob-store.js +97 -0
  15. package/node_modules/@gsd/pi-coding-agent/dist/core/blob-store.js.map +1 -0
  16. package/node_modules/@gsd/pi-coding-agent/dist/core/session-manager.d.ts +1 -0
  17. package/node_modules/@gsd/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  18. package/node_modules/@gsd/pi-coding-agent/dist/core/session-manager.js +112 -3
  19. package/node_modules/@gsd/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  20. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.d.ts +4 -0
  21. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  22. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.js +32 -22
  23. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  24. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.d.ts +1 -1
  25. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.d.ts.map +1 -1
  26. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.js +13 -2
  27. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.js.map +1 -1
  28. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.test.d.ts +2 -0
  29. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.test.d.ts.map +1 -0
  30. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.test.js +57 -0
  31. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.test.js.map +1 -0
  32. package/node_modules/@gsd/pi-coding-agent/dist/index.d.ts +3 -1
  33. package/node_modules/@gsd/pi-coding-agent/dist/index.d.ts.map +1 -1
  34. package/node_modules/@gsd/pi-coding-agent/dist/index.js +4 -1
  35. package/node_modules/@gsd/pi-coding-agent/dist/index.js.map +1 -1
  36. package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  37. package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -5
  38. package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  39. package/node_modules/@gsd/pi-coding-agent/dist/utils/shell.d.ts +7 -0
  40. package/node_modules/@gsd/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  41. package/node_modules/@gsd/pi-coding-agent/dist/utils/shell.js +11 -0
  42. package/node_modules/@gsd/pi-coding-agent/dist/utils/shell.js.map +1 -1
  43. package/node_modules/@gsd/pi-coding-agent/src/config.ts +5 -0
  44. package/node_modules/@gsd/pi-coding-agent/src/core/artifact-manager.ts +125 -0
  45. package/node_modules/@gsd/pi-coding-agent/src/core/bash-executor.ts +2 -2
  46. package/node_modules/@gsd/pi-coding-agent/src/core/blob-store.ts +106 -0
  47. package/node_modules/@gsd/pi-coding-agent/src/core/session-manager.ts +119 -3
  48. package/node_modules/@gsd/pi-coding-agent/src/core/tools/bash.ts +35 -22
  49. package/node_modules/@gsd/pi-coding-agent/src/core/tools/path-utils.test.ts +66 -0
  50. package/node_modules/@gsd/pi-coding-agent/src/core/tools/path-utils.ts +14 -2
  51. package/node_modules/@gsd/pi-coding-agent/src/index.ts +4 -1
  52. package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/interactive-mode.ts +6 -4
  53. package/node_modules/@gsd/pi-coding-agent/src/utils/shell.ts +11 -0
  54. package/package.json +6 -1
  55. package/packages/pi-coding-agent/dist/config.d.ts +2 -0
  56. package/packages/pi-coding-agent/dist/config.d.ts.map +1 -1
  57. package/packages/pi-coding-agent/dist/config.js +4 -0
  58. package/packages/pi-coding-agent/dist/config.js.map +1 -1
  59. package/packages/pi-coding-agent/dist/core/artifact-manager.d.ts +52 -0
  60. package/packages/pi-coding-agent/dist/core/artifact-manager.d.ts.map +1 -0
  61. package/packages/pi-coding-agent/dist/core/artifact-manager.js +117 -0
  62. package/packages/pi-coding-agent/dist/core/artifact-manager.js.map +1 -0
  63. package/packages/pi-coding-agent/dist/core/bash-executor.js +2 -2
  64. package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  65. package/packages/pi-coding-agent/dist/core/blob-store.d.ts +31 -0
  66. package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -0
  67. package/packages/pi-coding-agent/dist/core/blob-store.js +97 -0
  68. package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -0
  69. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +1 -0
  70. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  71. package/packages/pi-coding-agent/dist/core/session-manager.js +112 -3
  72. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  73. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +4 -0
  74. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  75. package/packages/pi-coding-agent/dist/core/tools/bash.js +32 -22
  76. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  77. package/packages/pi-coding-agent/dist/core/tools/path-utils.d.ts +1 -1
  78. package/packages/pi-coding-agent/dist/core/tools/path-utils.d.ts.map +1 -1
  79. package/packages/pi-coding-agent/dist/core/tools/path-utils.js +13 -2
  80. package/packages/pi-coding-agent/dist/core/tools/path-utils.js.map +1 -1
  81. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.d.ts +2 -0
  82. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.d.ts.map +1 -0
  83. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js +57 -0
  84. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js.map +1 -0
  85. package/packages/pi-coding-agent/dist/index.d.ts +3 -1
  86. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  87. package/packages/pi-coding-agent/dist/index.js +4 -1
  88. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  89. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  90. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -5
  91. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  92. package/packages/pi-coding-agent/dist/utils/shell.d.ts +7 -0
  93. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  94. package/packages/pi-coding-agent/dist/utils/shell.js +11 -0
  95. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  96. package/packages/pi-coding-agent/src/config.ts +5 -0
  97. package/packages/pi-coding-agent/src/core/artifact-manager.ts +125 -0
  98. package/packages/pi-coding-agent/src/core/bash-executor.ts +2 -2
  99. package/packages/pi-coding-agent/src/core/blob-store.ts +106 -0
  100. package/packages/pi-coding-agent/src/core/session-manager.ts +119 -3
  101. package/packages/pi-coding-agent/src/core/tools/bash.ts +35 -22
  102. package/packages/pi-coding-agent/src/core/tools/path-utils.test.ts +66 -0
  103. package/packages/pi-coding-agent/src/core/tools/path-utils.ts +14 -2
  104. package/packages/pi-coding-agent/src/index.ts +4 -1
  105. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +6 -4
  106. package/packages/pi-coding-agent/src/utils/shell.ts +11 -0
  107. package/src/resources/extensions/bg-shell/index.ts +2 -1
  108. package/src/resources/extensions/browser-tools/lifecycle.ts +6 -1
  109. package/src/resources/extensions/gsd/auto.ts +92 -49
  110. package/src/resources/extensions/gsd/dispatch-guard.ts +65 -0
  111. package/src/resources/extensions/gsd/docs/preferences-reference.md +76 -0
  112. package/src/resources/extensions/gsd/exit-command.ts +18 -0
  113. package/src/resources/extensions/gsd/files.ts +9 -40
  114. package/src/resources/extensions/gsd/git-service.ts +62 -17
  115. package/src/resources/extensions/gsd/gitignore.ts +28 -0
  116. package/src/resources/extensions/gsd/guided-flow.ts +49 -11
  117. package/src/resources/extensions/gsd/index.ts +111 -16
  118. package/src/resources/extensions/gsd/preferences.ts +8 -0
  119. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -2
  120. package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -3
  121. package/src/resources/extensions/gsd/prompts/discuss.md +27 -2
  122. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
  123. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -3
  124. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  125. package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -2
  126. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -3
  127. package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  128. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  129. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  130. package/src/resources/extensions/gsd/prompts/run-uat.md +4 -4
  131. package/src/resources/extensions/gsd/roadmap-slices.ts +50 -0
  132. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +102 -0
  133. package/src/resources/extensions/gsd/tests/exit-command.test.ts +50 -0
  134. package/src/resources/extensions/gsd/tests/git-service.test.ts +116 -39
  135. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +5 -5
  136. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
  137. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +59 -0
  138. package/src/resources/extensions/gsd/tests/run-uat.test.ts +2 -4
  139. package/src/resources/extensions/gsd/tests/write-gate.test.ts +122 -0
  140. package/src/resources/extensions/ttsr/index.ts +163 -0
  141. package/src/resources/extensions/ttsr/rule-loader.ts +121 -0
  142. package/src/resources/extensions/ttsr/ttsr-interrupt.md +6 -0
  143. package/src/resources/extensions/ttsr/ttsr-manager.ts +344 -0
@@ -1 +1 @@
1
- {"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport stripAnsi from \"strip-ansi\";\nimport { getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from \"../utils/shell.js\";\nimport type { BashOperations } from \"./tools/bash.js\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command with optional streaming and cancellation support.\n *\n * Features:\n * - Streams sanitized output via onChunk callback\n * - Writes large output to temp file for later retrieval\n * - Supports cancellation via AbortSignal\n * - Sanitizes output (strips ANSI, removes binary garbage, normalizes newlines)\n * - Truncates output if it exceeds the default max bytes\n *\n * @param command - The bash command to execute\n * @param options - Optional streaming callback and abort signal\n * @returns Promise resolving to execution result\n */\nexport function executeBash(command: string, options?: BashExecutorOptions): Promise<BashResult> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst { shell, args } = getShellConfig();\n\t\tconst child: ChildProcess = spawn(shell, [...args, command], {\n\t\t\tdetached: true,\n\t\t\tenv: getShellEnv(),\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\n\t\t// Track sanitized output for truncation\n\t\tconst outputChunks: string[] = [];\n\t\tlet outputBytes = 0;\n\t\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t// Temp file for large output\n\t\tlet tempFilePath: string | undefined;\n\t\tlet tempFileStream: WriteStream | undefined;\n\t\tlet totalBytes = 0;\n\n\t\t// Handle abort signal\n\t\tconst abortHandler = () => {\n\t\t\tif (child.pid) {\n\t\t\t\tkillProcessTree(child.pid);\n\t\t\t}\n\t\t};\n\n\t\tif (options?.signal) {\n\t\t\tif (options.signal.aborted) {\n\t\t\t\t// Already aborted, don't even start\n\t\t\t\tchild.kill();\n\t\t\t\tresolve({\n\t\t\t\t\toutput: \"\",\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler, { once: true });\n\t\t}\n\n\t\tconst decoder = new TextDecoder();\n\n\t\tconst handleData = (data: Buffer) => {\n\t\t\ttotalBytes += data.length;\n\n\t\t\t// Sanitize once at the source: strip ANSI, replace binary garbage, normalize newlines\n\t\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t\t// Start writing to temp file if exceeds threshold\n\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\t\t\ttempFilePath = join(tmpdir(), `pi-bash-${id}.log`);\n\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t// Write already-buffered chunks to temp file\n\t\t\t\tfor (const chunk of outputChunks) {\n\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.write(text);\n\t\t\t}\n\n\t\t\t// Keep rolling buffer of sanitized text\n\t\t\toutputChunks.push(text);\n\t\t\toutputBytes += text.length;\n\t\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\t\tconst removed = outputChunks.shift()!;\n\t\t\t\toutputBytes -= removed.length;\n\t\t\t}\n\n\t\t\t// Stream to callback if provided\n\t\t\tif (options?.onChunk) {\n\t\t\t\toptions.onChunk(text);\n\t\t\t}\n\t\t};\n\n\t\tchild.stdout?.on(\"data\", handleData);\n\t\tchild.stderr?.on(\"data\", handleData);\n\n\t\tchild.on(\"close\", (code) => {\n\t\t\t// Clean up abort listener\n\t\t\tif (options?.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\n\t\t\t// Combine buffered chunks for truncation (already sanitized)\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\n\t\t\t// code === null means killed (cancelled)\n\t\t\tconst cancelled = code === null;\n\n\t\t\tresolve({\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: cancelled ? undefined : code,\n\t\t\t\tcancelled,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t});\n\t\t});\n\n\t\tchild.on(\"error\", (err) => {\n\t\t\t// Clean up abort listener\n\t\t\tif (options?.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\n\t\t\treject(err);\n\t\t});\n\t});\n}\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\t\ttempFilePath = join(tmpdir(), `pi-bash-${id}.log`);\n\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\tfor (const chunk of outputChunks) {\n\t\t\t\ttempFileStream.write(chunk);\n\t\t\t}\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tthrow err;\n\t}\n}\n"]}
1
+ {"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport stripAnsi from \"strip-ansi\";\nimport { getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput, sanitizeCommand } from \"../utils/shell.js\";\nimport type { BashOperations } from \"./tools/bash.js\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command with optional streaming and cancellation support.\n *\n * Features:\n * - Streams sanitized output via onChunk callback\n * - Writes large output to temp file for later retrieval\n * - Supports cancellation via AbortSignal\n * - Sanitizes output (strips ANSI, removes binary garbage, normalizes newlines)\n * - Truncates output if it exceeds the default max bytes\n *\n * @param command - The bash command to execute\n * @param options - Optional streaming callback and abort signal\n * @returns Promise resolving to execution result\n */\nexport function executeBash(command: string, options?: BashExecutorOptions): Promise<BashResult> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst { shell, args } = getShellConfig();\n\t\tconst child: ChildProcess = spawn(shell, [...args, sanitizeCommand(command)], {\n\t\t\tdetached: true,\n\t\t\tenv: getShellEnv(),\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\n\t\t// Track sanitized output for truncation\n\t\tconst outputChunks: string[] = [];\n\t\tlet outputBytes = 0;\n\t\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t// Temp file for large output\n\t\tlet tempFilePath: string | undefined;\n\t\tlet tempFileStream: WriteStream | undefined;\n\t\tlet totalBytes = 0;\n\n\t\t// Handle abort signal\n\t\tconst abortHandler = () => {\n\t\t\tif (child.pid) {\n\t\t\t\tkillProcessTree(child.pid);\n\t\t\t}\n\t\t};\n\n\t\tif (options?.signal) {\n\t\t\tif (options.signal.aborted) {\n\t\t\t\t// Already aborted, don't even start\n\t\t\t\tchild.kill();\n\t\t\t\tresolve({\n\t\t\t\t\toutput: \"\",\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler, { once: true });\n\t\t}\n\n\t\tconst decoder = new TextDecoder();\n\n\t\tconst handleData = (data: Buffer) => {\n\t\t\ttotalBytes += data.length;\n\n\t\t\t// Sanitize once at the source: strip ANSI, replace binary garbage, normalize newlines\n\t\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t\t// Start writing to temp file if exceeds threshold\n\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\t\t\ttempFilePath = join(tmpdir(), `pi-bash-${id}.log`);\n\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t// Write already-buffered chunks to temp file\n\t\t\t\tfor (const chunk of outputChunks) {\n\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.write(text);\n\t\t\t}\n\n\t\t\t// Keep rolling buffer of sanitized text\n\t\t\toutputChunks.push(text);\n\t\t\toutputBytes += text.length;\n\t\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\t\tconst removed = outputChunks.shift()!;\n\t\t\t\toutputBytes -= removed.length;\n\t\t\t}\n\n\t\t\t// Stream to callback if provided\n\t\t\tif (options?.onChunk) {\n\t\t\t\toptions.onChunk(text);\n\t\t\t}\n\t\t};\n\n\t\tchild.stdout?.on(\"data\", handleData);\n\t\tchild.stderr?.on(\"data\", handleData);\n\n\t\tchild.on(\"close\", (code) => {\n\t\t\t// Clean up abort listener\n\t\t\tif (options?.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\n\t\t\t// Combine buffered chunks for truncation (already sanitized)\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\n\t\t\t// code === null means killed (cancelled)\n\t\t\tconst cancelled = code === null;\n\n\t\t\tresolve({\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: cancelled ? undefined : code,\n\t\t\t\tcancelled,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t});\n\t\t});\n\n\t\tchild.on(\"error\", (err) => {\n\t\t\t// Clean up abort listener\n\t\t\tif (options?.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\n\t\t\treject(err);\n\t\t});\n\t});\n}\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\t\ttempFilePath = join(tmpdir(), `pi-bash-${id}.log`);\n\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\tfor (const chunk of outputChunks) {\n\t\t\t\ttempFileStream.write(chunk);\n\t\t\t}\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tthrow err;\n\t}\n}\n"]}
@@ -0,0 +1,31 @@
1
+ export interface BlobPutResult {
2
+ hash: string;
3
+ path: string;
4
+ get ref(): string;
5
+ }
6
+ export declare class BlobStore {
7
+ readonly dir: string;
8
+ constructor(dir: string);
9
+ /** Write binary data to the blob store. Idempotent — same content → same hash. */
10
+ put(data: Buffer): BlobPutResult;
11
+ /** Read blob by hash, returns Buffer or null if not found. */
12
+ get(hash: string): Buffer | null;
13
+ /** Check if a blob exists. */
14
+ has(hash: string): boolean;
15
+ }
16
+ /** Check if a data string is a blob reference. */
17
+ export declare function isBlobRef(data: string): boolean;
18
+ /** Extract the SHA-256 hash from a blob reference string. Returns null if format is invalid. */
19
+ export declare function parseBlobRef(data: string): string | null;
20
+ /**
21
+ * Externalize an image's base64 data to the blob store, returning a blob reference.
22
+ * If the data is already a blob reference, returns it unchanged.
23
+ */
24
+ export declare function externalizeImageData(blobStore: BlobStore, base64Data: string): string;
25
+ /**
26
+ * Resolve a blob reference back to base64 data.
27
+ * If the data is not a blob reference, returns it unchanged.
28
+ * If the blob is missing, returns the ref unchanged.
29
+ */
30
+ export declare function resolveImageData(blobStore: BlobStore, data: string): string;
31
+ //# sourceMappingURL=blob-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-store.d.ts","sourceRoot":"","sources":["../../src/core/blob-store.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,GAAG,IAAI,MAAM,CAAC;CAClB;AAED,qBAAa,SAAS;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBACT,GAAG,EAAE,MAAM;IAKvB,kFAAkF;IAClF,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa;IAiBhC,8DAA8D;IAC9D,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAUhC,8BAA8B;IAC9B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAS1B;AAED,kDAAkD;AAClD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,gGAAgG;AAChG,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKxD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAKrF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ3E"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Content-addressed blob store for externalizing large binary data (images) from session JSONL files.
3
+ *
4
+ * Files are stored at `<dir>/<sha256-hex>` with no extension. The SHA-256 hash is computed
5
+ * over the raw binary data (not base64). Content-addressing makes writes idempotent and
6
+ * provides automatic deduplication across sessions.
7
+ */
8
+ import { createHash } from "node:crypto";
9
+ import { mkdirSync, readFileSync, writeFileSync, existsSync, accessSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ const BLOB_PREFIX = "blob:sha256:";
12
+ const SHA256_HEX_RE = /^[a-f0-9]{64}$/;
13
+ export class BlobStore {
14
+ constructor(dir) {
15
+ this.dir = dir;
16
+ mkdirSync(dir, { recursive: true });
17
+ }
18
+ /** Write binary data to the blob store. Idempotent — same content → same hash. */
19
+ put(data) {
20
+ const hash = createHash("sha256").update(data).digest("hex");
21
+ const blobPath = join(this.dir, hash);
22
+ const result = {
23
+ hash,
24
+ path: blobPath,
25
+ get ref() {
26
+ return `${BLOB_PREFIX}${hash}`;
27
+ },
28
+ };
29
+ if (!existsSync(blobPath)) {
30
+ writeFileSync(blobPath, data);
31
+ }
32
+ return result;
33
+ }
34
+ /** Read blob by hash, returns Buffer or null if not found. */
35
+ get(hash) {
36
+ if (!SHA256_HEX_RE.test(hash))
37
+ return null;
38
+ const blobPath = join(this.dir, hash);
39
+ try {
40
+ return readFileSync(blobPath);
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ /** Check if a blob exists. */
47
+ has(hash) {
48
+ if (!SHA256_HEX_RE.test(hash))
49
+ return false;
50
+ try {
51
+ accessSync(join(this.dir, hash));
52
+ return true;
53
+ }
54
+ catch {
55
+ return false;
56
+ }
57
+ }
58
+ }
59
+ /** Check if a data string is a blob reference. */
60
+ export function isBlobRef(data) {
61
+ return data.startsWith(BLOB_PREFIX);
62
+ }
63
+ /** Extract the SHA-256 hash from a blob reference string. Returns null if format is invalid. */
64
+ export function parseBlobRef(data) {
65
+ if (!data.startsWith(BLOB_PREFIX))
66
+ return null;
67
+ const hash = data.slice(BLOB_PREFIX.length);
68
+ if (!SHA256_HEX_RE.test(hash))
69
+ return null;
70
+ return hash;
71
+ }
72
+ /**
73
+ * Externalize an image's base64 data to the blob store, returning a blob reference.
74
+ * If the data is already a blob reference, returns it unchanged.
75
+ */
76
+ export function externalizeImageData(blobStore, base64Data) {
77
+ if (isBlobRef(base64Data))
78
+ return base64Data;
79
+ const buffer = Buffer.from(base64Data, "base64");
80
+ const { ref } = blobStore.put(buffer);
81
+ return ref;
82
+ }
83
+ /**
84
+ * Resolve a blob reference back to base64 data.
85
+ * If the data is not a blob reference, returns it unchanged.
86
+ * If the blob is missing, returns the ref unchanged.
87
+ */
88
+ export function resolveImageData(blobStore, data) {
89
+ const hash = parseBlobRef(data);
90
+ if (!hash)
91
+ return data;
92
+ const buffer = blobStore.get(hash);
93
+ if (!buffer)
94
+ return data; // Missing blob — return ref as-is
95
+ return buffer.toString("base64");
96
+ }
97
+ //# sourceMappingURL=blob-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-store.js","sourceRoot":"","sources":["../../src/core/blob-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Content-addressed blob store for externalizing large binary data (images) from session JSONL files.\n *\n * Files are stored at `<dir>/<sha256-hex>` with no extension. The SHA-256 hash is computed\n * over the raw binary data (not base64). Content-addressing makes writes idempotent and\n * provides automatic deduplication across sessions.\n */\nimport { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync, existsSync, accessSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst BLOB_PREFIX = \"blob:sha256:\";\nconst SHA256_HEX_RE = /^[a-f0-9]{64}$/;\n\nexport interface BlobPutResult {\n\thash: string;\n\tpath: string;\n\tget ref(): string;\n}\n\nexport class BlobStore {\n\treadonly dir: string;\n\tconstructor(dir: string) {\n\t\tthis.dir = dir;\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\n\t/** Write binary data to the blob store. Idempotent — same content → same hash. */\n\tput(data: Buffer): BlobPutResult {\n\t\tconst hash = createHash(\"sha256\").update(data).digest(\"hex\");\n\t\tconst blobPath = join(this.dir, hash);\n\t\tconst result: BlobPutResult = {\n\t\t\thash,\n\t\t\tpath: blobPath,\n\t\t\tget ref() {\n\t\t\t\treturn `${BLOB_PREFIX}${hash}`;\n\t\t\t},\n\t\t};\n\n\t\tif (!existsSync(blobPath)) {\n\t\t\twriteFileSync(blobPath, data);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/** Read blob by hash, returns Buffer or null if not found. */\n\tget(hash: string): Buffer | null {\n\t\tif (!SHA256_HEX_RE.test(hash)) return null;\n\t\tconst blobPath = join(this.dir, hash);\n\t\ttry {\n\t\t\treturn readFileSync(blobPath);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/** Check if a blob exists. */\n\thas(hash: string): boolean {\n\t\tif (!SHA256_HEX_RE.test(hash)) return false;\n\t\ttry {\n\t\t\taccessSync(join(this.dir, hash));\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\n/** Check if a data string is a blob reference. */\nexport function isBlobRef(data: string): boolean {\n\treturn data.startsWith(BLOB_PREFIX);\n}\n\n/** Extract the SHA-256 hash from a blob reference string. Returns null if format is invalid. */\nexport function parseBlobRef(data: string): string | null {\n\tif (!data.startsWith(BLOB_PREFIX)) return null;\n\tconst hash = data.slice(BLOB_PREFIX.length);\n\tif (!SHA256_HEX_RE.test(hash)) return null;\n\treturn hash;\n}\n\n/**\n * Externalize an image's base64 data to the blob store, returning a blob reference.\n * If the data is already a blob reference, returns it unchanged.\n */\nexport function externalizeImageData(blobStore: BlobStore, base64Data: string): string {\n\tif (isBlobRef(base64Data)) return base64Data;\n\tconst buffer = Buffer.from(base64Data, \"base64\");\n\tconst { ref } = blobStore.put(buffer);\n\treturn ref;\n}\n\n/**\n * Resolve a blob reference back to base64 data.\n * If the data is not a blob reference, returns it unchanged.\n * If the blob is missing, returns the ref unchanged.\n */\nexport function resolveImageData(blobStore: BlobStore, data: string): string {\n\tconst hash = parseBlobRef(data);\n\tif (!hash) return data;\n\n\tconst buffer = blobStore.get(hash);\n\tif (!buffer) return data; // Missing blob — return ref as-is\n\n\treturn buffer.toString(\"base64\");\n}\n"]}
@@ -167,6 +167,7 @@ export declare class SessionManager {
167
167
  private flushed;
168
168
  private fileEntries;
169
169
  private byId;
170
+ private blobStore;
170
171
  private labelsById;
171
172
  private leafId;
172
173
  private constructor();
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/core/session-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/core/session-manager.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,12 @@ import { randomUUID } from "crypto";
2
2
  import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, statSync, writeFileSync, } from "fs";
3
3
  import { readdir, readFile, stat } from "fs/promises";
4
4
  import { join, resolve } from "path";
5
- import { getAgentDir as getDefaultAgentDir, getSessionsDir } from "../config.js";
5
+ import { getAgentDir as getDefaultAgentDir, getBlobsDir, getSessionsDir } from "../config.js";
6
6
  import { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage, } from "./messages.js";
7
+ import { BlobStore, externalizeImageData, isBlobRef, resolveImageData } from "./blob-store.js";
8
+ const BLOB_EXTERNALIZE_THRESHOLD = 1024; // 1KB minimum to externalize
9
+ const MAX_PERSIST_CHARS = 500_000;
10
+ const TRUNCATION_NOTICE = "\n\n[Session persistence truncated large content]";
7
11
  export const CURRENT_SESSION_VERSION = 3;
8
12
  /** Generate a unique short ID (8 hex chars, collision-checked) */
9
13
  function generateId(byId) {
@@ -213,6 +217,107 @@ function getDefaultSessionDir(cwd) {
213
217
  }
214
218
  return sessionDir;
215
219
  }
220
+ function isImageBlock(value) {
221
+ return (typeof value === "object" &&
222
+ value !== null &&
223
+ "type" in value &&
224
+ value.type === "image" &&
225
+ "data" in value &&
226
+ typeof value.data === "string");
227
+ }
228
+ function truncateString(s, maxLength) {
229
+ if (s.length <= maxLength)
230
+ return s;
231
+ // Avoid splitting surrogate pairs
232
+ if (maxLength > 0 && s.charCodeAt(maxLength - 1) >= 0xd800 && s.charCodeAt(maxLength - 1) <= 0xdbff) {
233
+ return s.slice(0, maxLength - 1);
234
+ }
235
+ return s.slice(0, maxLength);
236
+ }
237
+ /**
238
+ * Prepare an entry for JSONL persistence: externalize large images to blob store,
239
+ * truncate oversized strings, strip transient fields.
240
+ */
241
+ function prepareForPersistence(obj, blobStore, key) {
242
+ if (obj === null || obj === undefined)
243
+ return obj;
244
+ if (typeof obj === "string") {
245
+ if (obj.length > MAX_PERSIST_CHARS) {
246
+ // Cryptographic signatures must be preserved exactly or cleared entirely
247
+ if (key === "thinkingSignature" || key === "thoughtSignature" || key === "textSignature") {
248
+ return "";
249
+ }
250
+ const limit = Math.max(0, MAX_PERSIST_CHARS - TRUNCATION_NOTICE.length);
251
+ return `${truncateString(obj, limit)}${TRUNCATION_NOTICE}`;
252
+ }
253
+ return obj;
254
+ }
255
+ if (Array.isArray(obj)) {
256
+ let changed = false;
257
+ const result = obj.map((item) => {
258
+ // Externalize oversized images to blob store
259
+ if (key === "content" && isImageBlock(item)) {
260
+ if (!isBlobRef(item.data) && item.data.length >= BLOB_EXTERNALIZE_THRESHOLD) {
261
+ changed = true;
262
+ const blobRef = externalizeImageData(blobStore, item.data);
263
+ return { ...item, data: blobRef };
264
+ }
265
+ }
266
+ const newItem = prepareForPersistence(item, blobStore, key);
267
+ if (newItem !== item)
268
+ changed = true;
269
+ return newItem;
270
+ });
271
+ return changed ? result : obj;
272
+ }
273
+ if (typeof obj === "object") {
274
+ let changed = false;
275
+ const result = {};
276
+ for (const [k, v] of Object.entries(obj)) {
277
+ // Strip transient properties
278
+ if (k === "partialJson" || k === "jsonlEvents") {
279
+ changed = true;
280
+ continue;
281
+ }
282
+ const newV = prepareForPersistence(v, blobStore, k);
283
+ result[k] = newV;
284
+ if (newV !== v)
285
+ changed = true;
286
+ }
287
+ // Update lineCount if content was truncated (for FileMentionFile)
288
+ if (changed && "lineCount" in result && "content" in result && typeof result.content === "string") {
289
+ result.lineCount = result.content.split("\n").length;
290
+ }
291
+ return changed ? result : obj;
292
+ }
293
+ return obj;
294
+ }
295
+ /**
296
+ * Resolve blob references in loaded entries, replacing `blob:sha256:<hash>` data
297
+ * fields with actual base64 content. Mutates entries in place.
298
+ */
299
+ function resolveBlobRefsInEntries(entries, blobStore) {
300
+ for (const entry of entries) {
301
+ if (entry.type === "session")
302
+ continue;
303
+ let contentArray;
304
+ if (entry.type === "message") {
305
+ const content = entry.message.content;
306
+ if (Array.isArray(content))
307
+ contentArray = content;
308
+ }
309
+ else if (entry.type === "custom_message" && Array.isArray(entry.content)) {
310
+ contentArray = entry.content;
311
+ }
312
+ if (!contentArray)
313
+ continue;
314
+ for (const block of contentArray) {
315
+ if (isImageBlock(block) && isBlobRef(block.data)) {
316
+ block.data = resolveImageData(blobStore, block.data);
317
+ }
318
+ }
319
+ }
320
+ }
216
321
  /** Exported for testing */
217
322
  export function loadEntriesFromFile(filePath) {
218
323
  if (!existsSync(filePath))
@@ -435,6 +540,7 @@ export class SessionManager {
435
540
  this.cwd = cwd;
436
541
  this.sessionDir = sessionDir;
437
542
  this.persist = persist;
543
+ this.blobStore = new BlobStore(getBlobsDir());
438
544
  if (persist && sessionDir && !existsSync(sessionDir)) {
439
545
  mkdirSync(sessionDir, { recursive: true });
440
546
  }
@@ -466,6 +572,7 @@ export class SessionManager {
466
572
  this._rewriteFile();
467
573
  }
468
574
  this._buildIndex();
575
+ resolveBlobRefsInEntries(this.fileEntries, this.blobStore);
469
576
  this.flushed = true;
470
577
  }
471
578
  else {
@@ -547,12 +654,14 @@ export class SessionManager {
547
654
  }
548
655
  if (!this.flushed) {
549
656
  for (const e of this.fileEntries) {
550
- appendFileSync(this.sessionFile, `${JSON.stringify(e)}\n`);
657
+ const prepared = prepareForPersistence(e, this.blobStore);
658
+ appendFileSync(this.sessionFile, `${JSON.stringify(prepared)}\n`);
551
659
  }
552
660
  this.flushed = true;
553
661
  }
554
662
  else {
555
- appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`);
663
+ const prepared = prepareForPersistence(entry, this.blobStore);
664
+ appendFileSync(this.sessionFile, `${JSON.stringify(prepared)}\n`);
556
665
  }
557
666
  }
558
667
  _appendEntry(entry) {