@bastani/atomic 0.9.0-alpha.1 → 0.9.0-alpha.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 (211) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +6 -0
  3. package/dist/builtin/cursor/package.json +2 -2
  4. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  5. package/dist/builtin/intercom/package.json +2 -2
  6. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  7. package/dist/builtin/mcp/package.json +3 -3
  8. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  9. package/dist/builtin/subagents/package.json +4 -4
  10. package/dist/builtin/web-access/CHANGELOG.md +6 -0
  11. package/dist/builtin/web-access/package.json +2 -2
  12. package/dist/builtin/workflows/CHANGELOG.md +12 -0
  13. package/dist/builtin/workflows/README.md +189 -122
  14. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +30 -27
  15. package/dist/builtin/workflows/builtin/goal-runner.ts +10 -17
  16. package/dist/builtin/workflows/builtin/goal.ts +39 -44
  17. package/dist/builtin/workflows/builtin/index.d.ts +1 -0
  18. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +16 -17
  19. package/dist/builtin/workflows/builtin/open-claude-design.d.ts +1 -0
  20. package/dist/builtin/workflows/builtin/open-claude-design.ts +42 -50
  21. package/dist/builtin/workflows/builtin/ralph.ts +44 -41
  22. package/dist/builtin/workflows/package.json +2 -2
  23. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  24. package/dist/builtin/workflows/src/authoring/typebox-defaults.ts +217 -0
  25. package/dist/builtin/workflows/src/authoring/workflow.ts +184 -0
  26. package/dist/builtin/workflows/src/authoring.d.ts +14 -66
  27. package/dist/builtin/workflows/src/engine/graph-inference.ts +100 -0
  28. package/dist/builtin/workflows/src/engine/options.ts +40 -0
  29. package/dist/builtin/workflows/src/engine/primitives/chain.ts +29 -0
  30. package/dist/builtin/workflows/src/engine/primitives/exit.ts +2 -0
  31. package/dist/builtin/workflows/src/engine/primitives/parallel.ts +47 -0
  32. package/dist/builtin/workflows/src/engine/primitives/task.ts +108 -0
  33. package/dist/builtin/workflows/src/engine/primitives/ui.ts +41 -0
  34. package/dist/builtin/workflows/src/engine/primitives/workflow.ts +159 -0
  35. package/dist/builtin/workflows/src/engine/replay.ts +8 -0
  36. package/dist/builtin/workflows/src/engine/run.ts +356 -0
  37. package/dist/builtin/workflows/src/engine/runtime.ts +160 -0
  38. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +9 -3
  39. package/dist/builtin/workflows/src/extension/workflow-schema.ts +0 -18
  40. package/dist/builtin/workflows/src/index.ts +0 -2
  41. package/dist/builtin/workflows/src/runs/background/runner.ts +6 -3
  42. package/dist/builtin/workflows/src/runs/foreground/executor-child-boundary.ts +3 -3
  43. package/dist/builtin/workflows/src/runs/foreground/executor-child-helpers.ts +4 -4
  44. package/dist/builtin/workflows/src/runs/foreground/executor-child-workflow.ts +1 -158
  45. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +1 -1
  46. package/dist/builtin/workflows/src/runs/foreground/executor-outputs.ts +2 -2
  47. package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +1 -1
  48. package/dist/builtin/workflows/src/runs/foreground/executor-run.ts +1 -359
  49. package/dist/builtin/workflows/src/runs/foreground/executor-scheduler.ts +1 -1
  50. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +2 -5
  51. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +12 -4
  52. package/dist/builtin/workflows/src/runs/foreground/executor-stage-replay.ts +4 -3
  53. package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +9 -2
  54. package/dist/builtin/workflows/src/runs/foreground/executor-task-context.ts +2 -132
  55. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +2 -2
  56. package/dist/builtin/workflows/src/runs/shared/graph-inference.ts +2 -100
  57. package/dist/builtin/workflows/src/sdk-surface.ts +6 -9
  58. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +9 -3
  59. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +17 -3
  60. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +3 -33
  61. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +9 -81
  62. package/dist/builtin/workflows/src/shared/types.ts +25 -8
  63. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +49 -0
  64. package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +84 -0
  65. package/dist/builtin/workflows/src/workflows/registry.ts +7 -3
  66. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  67. package/dist/core/agent-session-auto-compaction.js +6 -1
  68. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  69. package/dist/core/agent-session-bash.d.ts.map +1 -1
  70. package/dist/core/agent-session-bash.js +0 -5
  71. package/dist/core/agent-session-bash.js.map +1 -1
  72. package/dist/core/agent-session-methods.d.ts +0 -2
  73. package/dist/core/agent-session-methods.d.ts.map +1 -1
  74. package/dist/core/agent-session-methods.js.map +1 -1
  75. package/dist/core/agent-session-services.d.ts +0 -1
  76. package/dist/core/agent-session-services.d.ts.map +1 -1
  77. package/dist/core/agent-session-services.js +0 -1
  78. package/dist/core/agent-session-services.js.map +1 -1
  79. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  80. package/dist/core/agent-session-tool-registry.js +0 -2
  81. package/dist/core/agent-session-tool-registry.js.map +1 -1
  82. package/dist/core/agent-session-types.d.ts +0 -2
  83. package/dist/core/agent-session-types.d.ts.map +1 -1
  84. package/dist/core/agent-session-types.js.map +1 -1
  85. package/dist/core/agent-session.d.ts +0 -2
  86. package/dist/core/agent-session.d.ts.map +1 -1
  87. package/dist/core/agent-session.js +0 -1
  88. package/dist/core/agent-session.js.map +1 -1
  89. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  90. package/dist/core/atomic-guide-command.js +1 -1
  91. package/dist/core/atomic-guide-command.js.map +1 -1
  92. package/dist/core/extensions/loader-core.d.ts +1 -3
  93. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  94. package/dist/core/extensions/loader-core.js +13 -6
  95. package/dist/core/extensions/loader-core.js.map +1 -1
  96. package/dist/core/extensions/loader-virtual-modules.d.ts +7 -1
  97. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  98. package/dist/core/extensions/loader-virtual-modules.js +34 -2
  99. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  100. package/dist/core/extensions/loader.d.ts +2 -1
  101. package/dist/core/extensions/loader.d.ts.map +1 -1
  102. package/dist/core/extensions/loader.js +2 -1
  103. package/dist/core/extensions/loader.js.map +1 -1
  104. package/dist/core/index.d.ts +0 -1
  105. package/dist/core/index.d.ts.map +1 -1
  106. package/dist/core/index.js +0 -1
  107. package/dist/core/index.js.map +1 -1
  108. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  109. package/dist/core/model-registry-builtins.js +6 -0
  110. package/dist/core/model-registry-builtins.js.map +1 -1
  111. package/dist/core/model-registry-schemas.d.ts +65 -13
  112. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  113. package/dist/core/model-registry-schemas.js +10 -0
  114. package/dist/core/model-registry-schemas.js.map +1 -1
  115. package/dist/core/resource-loader-core.d.ts +1 -0
  116. package/dist/core/resource-loader-core.d.ts.map +1 -1
  117. package/dist/core/resource-loader-core.js +2 -0
  118. package/dist/core/resource-loader-core.js.map +1 -1
  119. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  120. package/dist/core/resource-loader-extensions.js +3 -3
  121. package/dist/core/resource-loader-extensions.js.map +1 -1
  122. package/dist/core/resource-loader-internals.d.ts +1 -0
  123. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  124. package/dist/core/resource-loader-internals.js.map +1 -1
  125. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  126. package/dist/core/resource-loader-reload.js +6 -2
  127. package/dist/core/resource-loader-reload.js.map +1 -1
  128. package/dist/core/sdk-exports.d.ts +1 -1
  129. package/dist/core/sdk-exports.d.ts.map +1 -1
  130. package/dist/core/sdk-exports.js.map +1 -1
  131. package/dist/core/sdk-types.d.ts +0 -3
  132. package/dist/core/sdk-types.d.ts.map +1 -1
  133. package/dist/core/sdk-types.js.map +1 -1
  134. package/dist/core/sdk.d.ts.map +1 -1
  135. package/dist/core/sdk.js +0 -1
  136. package/dist/core/sdk.js.map +1 -1
  137. package/dist/core/session-manager-history.d.ts.map +1 -1
  138. package/dist/core/session-manager-history.js +2 -1
  139. package/dist/core/session-manager-history.js.map +1 -1
  140. package/dist/core/tools/bash.d.ts +0 -5
  141. package/dist/core/tools/bash.d.ts.map +1 -1
  142. package/dist/core/tools/bash.js +10 -11
  143. package/dist/core/tools/bash.js.map +1 -1
  144. package/dist/core/tools/edit-diff-preserve.d.ts +18 -0
  145. package/dist/core/tools/edit-diff-preserve.d.ts.map +1 -0
  146. package/dist/core/tools/edit-diff-preserve.js +85 -0
  147. package/dist/core/tools/edit-diff-preserve.js.map +1 -0
  148. package/dist/core/tools/edit-diff.d.ts +3 -2
  149. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  150. package/dist/core/tools/edit-diff.js +15 -18
  151. package/dist/core/tools/edit-diff.js.map +1 -1
  152. package/dist/core/tools/index.d.ts +0 -1
  153. package/dist/core/tools/index.d.ts.map +1 -1
  154. package/dist/core/tools/index.js +0 -1
  155. package/dist/core/tools/index.js.map +1 -1
  156. package/dist/index.d.ts +2 -2
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +1 -1
  159. package/dist/index.js.map +1 -1
  160. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  161. package/dist/modes/interactive/components/model-selector.js +2 -2
  162. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  163. package/dist/modes/interactive/model-search.d.ts +5 -0
  164. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  165. package/dist/modes/interactive/model-search.js +9 -0
  166. package/dist/modes/interactive/model-search.js.map +1 -1
  167. package/dist/utils/shell.d.ts +1 -0
  168. package/dist/utils/shell.d.ts.map +1 -1
  169. package/dist/utils/shell.js +12 -5
  170. package/dist/utils/shell.js.map +1 -1
  171. package/docs/custom-provider.md +4 -3
  172. package/docs/models.md +3 -2
  173. package/docs/packages.md +2 -2
  174. package/docs/quickstart.md +1 -1
  175. package/docs/sdk.md +2 -40
  176. package/docs/security.md +1 -1
  177. package/docs/workflows.md +238 -173
  178. package/package.json +5 -5
  179. package/dist/builtin/workflows/src/workflows/define-workflow.ts +0 -277
  180. package/dist/core/tools/bash-policy-compile.d.ts +0 -5
  181. package/dist/core/tools/bash-policy-compile.d.ts.map +0 -1
  182. package/dist/core/tools/bash-policy-compile.js +0 -241
  183. package/dist/core/tools/bash-policy-compile.js.map +0 -1
  184. package/dist/core/tools/bash-policy-evaluate.d.ts +0 -3
  185. package/dist/core/tools/bash-policy-evaluate.d.ts.map +0 -1
  186. package/dist/core/tools/bash-policy-evaluate.js +0 -92
  187. package/dist/core/tools/bash-policy-evaluate.js.map +0 -1
  188. package/dist/core/tools/bash-policy-format.d.ts +0 -5
  189. package/dist/core/tools/bash-policy-format.d.ts.map +0 -1
  190. package/dist/core/tools/bash-policy-format.js +0 -49
  191. package/dist/core/tools/bash-policy-format.js.map +0 -1
  192. package/dist/core/tools/bash-policy-parser.d.ts +0 -4
  193. package/dist/core/tools/bash-policy-parser.d.ts.map +0 -1
  194. package/dist/core/tools/bash-policy-parser.js +0 -155
  195. package/dist/core/tools/bash-policy-parser.js.map +0 -1
  196. package/dist/core/tools/bash-policy-segment.d.ts +0 -3
  197. package/dist/core/tools/bash-policy-segment.d.ts.map +0 -1
  198. package/dist/core/tools/bash-policy-segment.js +0 -275
  199. package/dist/core/tools/bash-policy-segment.js.map +0 -1
  200. package/dist/core/tools/bash-policy-shell.d.ts +0 -11
  201. package/dist/core/tools/bash-policy-shell.d.ts.map +0 -1
  202. package/dist/core/tools/bash-policy-shell.js +0 -267
  203. package/dist/core/tools/bash-policy-shell.js.map +0 -1
  204. package/dist/core/tools/bash-policy-types.d.ts +0 -146
  205. package/dist/core/tools/bash-policy-types.d.ts.map +0 -1
  206. package/dist/core/tools/bash-policy-types.js +0 -2
  207. package/dist/core/tools/bash-policy-types.js.map +0 -1
  208. package/dist/core/tools/bash-policy.d.ts +0 -6
  209. package/dist/core/tools/bash-policy.d.ts.map +0 -1
  210. package/dist/core/tools/bash-policy.js +0 -5
  211. package/dist/core/tools/bash-policy.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-history.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-history.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,sBAAsB,EACtB,sBAAsB,EACtB,SAAS,EACT,cAAc,EACd,YAAY,EAEZ,eAAe,EACf,MAAM,4BAA4B,CAAC;AAEpC,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,sBAAsB,GAAG,IAAI,CAQvG;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,sBAAsB,CAkBxF;AA0DD,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,sBAAsB,CA+DjG;AA0CD;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC/C,IAAI,EAAE,YAAY,EAAE,EACpB,gBAAgB,GAAE,sBAAmE,GACnF,YAAY,EAAE,CA4BhB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,YAAY,EAAE,EACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GAC9B,cAAc,CA8EhB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAChC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,YAAY,CAsBxE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,YAAY,EAAE,CAQhH;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9C,eAAe,EAAE,CAqCnB","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { createBranchSummaryMessage, createCustomMessage } from \"./messages.ts\";\nimport { contentArrayHasAssistantThinkingBlock } from \"./thinking-blocks.ts\";\nimport type {\n\tContextCompactionEntry,\n\tContextDeletionFilters,\n\tFileEntry,\n\tSessionContext,\n\tSessionEntry,\n\tSessionMessageEntry,\n\tSessionTreeNode,\n} from \"./session-manager-types.ts\";\n\nexport function getLatestCompactionBoundaryEntry(entries: SessionEntry[]): ContextCompactionEntry | null {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"context_compaction\") {\n\t\t\treturn entry;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Build raw deletion filters from persisted context_compaction entries.\n *\n * These raw filters do not apply replay-safety repair for latest assistant\n * thinking/redacted_thinking blocks or their paired tool results. Production\n * context rebuild paths should prefer `buildEffectiveContextDeletionFilters`\n * or `buildContextDeletionFilteredPath(path)` unless they intentionally need\n * the un-repaired historical deletion plan for diagnostics.\n */\nexport function buildContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"context_compaction\") continue;\n\t\tfor (const target of entry.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") {\n\t\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\t\texisting.add(target.blockIndex);\n\t\t\tdeletedContentBlocks.set(target.entryId, existing);\n\t\t}\n\t}\n\n\treturn { deletedEntryIds, deletedContentBlocks };\n}\n\nfunction getToolCallContentBlockId(block: unknown): string | undefined {\n\tif (!block || typeof block !== \"object\") return undefined;\n\tconst candidate = block as { type?: unknown; id?: unknown };\n\treturn candidate.type === \"toolCall\" && typeof candidate.id === \"string\" ? candidate.id : undefined;\n}\n\nfunction getToolResultCallId(message: AgentMessage): string | undefined {\n\tif (message.role !== \"toolResult\") return undefined;\n\tconst toolCallId = (message as { toolCallId?: unknown }).toolCallId;\n\treturn typeof toolCallId === \"string\" ? toolCallId : undefined;\n}\n\nfunction collectToolCallContentBlockIds(content: readonly unknown[]): Set<string> {\n\tconst toolCallIds = new Set<string>();\n\tfor (const block of content) {\n\t\tconst toolCallId = getToolCallContentBlockId(block);\n\t\tif (toolCallId) toolCallIds.add(toolCallId);\n\t}\n\treturn toolCallIds;\n}\n\nfunction addDeletionTarget(filters: ContextDeletionFilters, target: ContextCompactionEntry[\"deletedTargets\"][number]): void {\n\tif (target.kind === \"entry\") {\n\t\tfilters.deletedEntryIds.add(target.entryId);\n\t\treturn;\n\t}\n\tconst existing = filters.deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\texisting.add(target.blockIndex);\n\tfilters.deletedContentBlocks.set(target.entryId, existing);\n}\n\nfunction buildToolResultEntryIdsByCallId(path: SessionEntry[]): Map<string, Set<string>> {\n\tconst toolResultEntryIdsByCallId = new Map<string, Set<string>>();\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst toolCallId = getToolResultCallId(entry.message);\n\t\tif (!toolCallId) continue;\n\t\tconst existing = toolResultEntryIdsByCallId.get(toolCallId) ?? new Set<string>();\n\t\texisting.add(entry.id);\n\t\ttoolResultEntryIdsByCallId.set(toolCallId, existing);\n\t}\n\treturn toolResultEntryIdsByCallId;\n}\n\nfunction findRetainedThinkingAssistants(\n\tpath: SessionEntry[],\n\tdeletedEntryIds: ReadonlySet<string>,\n): SessionMessageEntry[] {\n\treturn path.filter((entry): entry is SessionMessageEntry => {\n\t\tif (entry.type !== \"message\") return false;\n\t\tif (deletedEntryIds.has(entry.id)) return false;\n\t\tif (entry.message.role !== \"assistant\") return false;\n\t\treturn contentArrayHasAssistantThinkingBlock(entry.message.content);\n\t});\n}\n\nexport function buildEffectiveContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst filters = buildContextDeletionFilters(path);\n\tif (!path.some((entry) => entry.type === \"context_compaction\")) return filters;\n\n\tconst rawDeletedEntryIds = new Set<string>();\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") rawDeletedEntryIds.add(target.entryId);\n\t\t}\n\t}\n\tconst retainedThinkingAssistants = findRetainedThinkingAssistants(path, rawDeletedEntryIds);\n\tconst retainedThinkingAssistantIds = new Set(retainedThinkingAssistants.map((entry) => entry.id));\n\tconst retainedThinkingAssistantById = new Map(retainedThinkingAssistants.map((entry) => [entry.id, entry]));\n\tconst toolResultEntryIdsByCallId = buildToolResultEntryIdsByCallId(path);\n\tconst effectiveFilters: ContextDeletionFilters = {\n\t\tdeletedEntryIds: new Set<string>(),\n\t\tdeletedContentBlocks: new Map<string, Set<number>>(),\n\t};\n\tconst allRestoredToolResultEntryIds = new Set<string>();\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind !== \"content_block\") continue;\n\t\t\tconst retainedThinkingAssistant = retainedThinkingAssistantById.get(target.entryId);\n\t\t\tif (!retainedThinkingAssistant) continue;\n\t\t\tconst content = (retainedThinkingAssistant.message as { content: readonly unknown[] }).content;\n\t\t\tfor (const toolCallId of collectToolCallContentBlockIds(content)) {\n\t\t\t\tfor (const entryId of toolResultEntryIdsByCallId.get(toolCallId) ?? []) {\n\t\t\t\t\tallRestoredToolResultEntryIds.add(entryId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tlet restoresRetainedThinkingAssistant = false;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\trestoresRetainedThinkingAssistant = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// When a stale persisted plan tried to partially filter a retained\n\t\t\t// thinking-bearing assistant, treat the same compaction entry as one\n\t\t\t// unsafe unit and restore its paired tool results. Later compaction\n\t\t\t// entries may still trim those restored multi-block results normally,\n\t\t\t// but whole-entry deletion of those paired results remains unsafe in any\n\t\t\t// later compaction because the assistant tool call is retained.\n\t\t\tif (restoresRetainedThinkingAssistant && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\tif (target.kind === \"entry\" && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\taddDeletionTarget(effectiveFilters, target);\n\t\t}\n\t}\n\n\treturn effectiveFilters;\n}\n\nfunction filterContentArray<T>(content: T[], deletedBlocks: ReadonlySet<number>): T[] {\n\treturn content.filter((_, index) => !deletedBlocks.has(index));\n}\n\nfunction filterMessageContentBlocks(\n\tmessage: AgentMessage,\n\tdeletedBlocks: ReadonlySet<number> | undefined,\n): AgentMessage | undefined {\n\tif (!deletedBlocks || deletedBlocks.size === 0) return message;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"toolResult\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"custom\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"bashExecution\":\n\t\tcase \"branchSummary\":\n\t\t\treturn message;\n\t}\n}\n\n/**\n * Return the active branch path after applying logical context-deletion entries.\n * Whole-entry deletions remove the entry from the path. Content-block deletions\n * clone only affected message/custom-message entries so retained blocks stay verbatim.\n * The optional filters parameter is for callers that already computed effective\n * filters with `buildEffectiveContextDeletionFilters(path)` and want to avoid\n * repeating the repair pass.\n */\nexport function buildContextDeletionFilteredPath(\n\tpath: SessionEntry[],\n\teffectiveFilters: ContextDeletionFilters = buildEffectiveContextDeletionFilters(path),\n): SessionEntry[] {\n\tconst filteredPath: SessionEntry[] = [];\n\n\tfor (const entry of path) {\n\t\tif (effectiveFilters.deletedEntryIds.has(entry.id)) continue;\n\n\t\tconst deletedBlocks = effectiveFilters.deletedContentBlocks.get(entry.id);\n\t\tif (!deletedBlocks || deletedBlocks.size === 0) {\n\t\t\tfilteredPath.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"message\") {\n\t\t\tconst message = filterMessageContentBlocks(entry.message, deletedBlocks);\n\t\t\tif (message) filteredPath.push({ ...entry, message });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"custom_message\" && Array.isArray(entry.content)) {\n\t\t\tconst content = filterContentArray(entry.content, deletedBlocks);\n\t\t\tif (content.length > 0) filteredPath.push({ ...entry, content });\n\t\t\tcontinue;\n\t\t}\n\n\t\tfilteredPath.push(entry);\n\t}\n\n\treturn filteredPath;\n}\n\n/**\n * Build the session context from entries using tree traversal.\n * If leafId is provided, walks from that entry to root.\n * Applies context-deletion filtering and includes branch summaries along the path.\n */\nexport function buildSessionContext(\n\tentries: SessionEntry[],\n\tleafId?: string | null,\n\tbyId?: Map<string, SessionEntry>,\n): SessionContext {\n\t// Build uuid index if not available\n\tif (!byId) {\n\t\tbyId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\t}\n\n\t// Find leaf\n\tlet leaf: SessionEntry | undefined;\n\tif (leafId === null) {\n\t\t// Explicitly null - return no messages (navigated to before first entry)\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\tif (leafId) {\n\t\tleaf = byId.get(leafId);\n\t}\n\tif (!leaf) {\n\t\t// Fallback to last entry (when leafId is undefined)\n\t\tleaf = entries[entries.length - 1];\n\t}\n\n\tif (!leaf) {\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\n\t// Walk from leaf to root, collecting path\n\tconst path = getBranchPath(leaf.id, byId);\n\n\t// Extract settings\n\tlet thinkingLevel = \"off\";\n\tlet contextWindow: number | undefined;\n\tlet model: { provider: string; modelId: string } | null = null;\n\n\tfor (const entry of path) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"context_window_change\") {\n\t\t\tcontextWindow = entry.contextWindow;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t}\n\t}\n\n\tconst filteredPath = buildContextDeletionFilteredPath(path);\n\n\t// Build active context messages from the filtered path. Legacy \"compaction\"\n\t// entries are archival metadata and intentionally inert here.\n\tconst messages: AgentMessage[] = [];\n\n\tconst appendMessage = (entry: SessionEntry) => {\n\t\tlet message: AgentMessage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tmessage = entry.message;\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessage = createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content,\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t\tentry.excludeFromContext,\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && entry.summary) {\n\t\t\tmessage = createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t\t}\n\n\t\tif (message) messages.push(message);\n\t};\n\n\tfor (const entry of filteredPath) {\n\t\tappendMessage(entry);\n\t}\n\n\treturn { messages, thinkingLevel, contextWindow, model };\n}\n\nexport interface SessionIndex {\n\tbyId: Map<string, SessionEntry>;\n\tlabelsById: Map<string, string>;\n\tlabelTimestampsById: Map<string, string>;\n\tleafId: string | null;\n}\n\nexport function buildSessionIndex(fileEntries: FileEntry[]): SessionIndex {\n\tconst byId = new Map<string, SessionEntry>();\n\tconst labelsById = new Map<string, string>();\n\tconst labelTimestampsById = new Map<string, string>();\n\tlet leafId: string | null = null;\n\n\tfor (const entry of fileEntries) {\n\t\tif (entry.type === \"session\") continue;\n\t\tbyId.set(entry.id, entry);\n\t\tleafId = entry.id;\n\t\tif (entry.type === \"label\") {\n\t\t\tif (entry.label) {\n\t\t\t\tlabelsById.set(entry.targetId, entry.label);\n\t\t\t\tlabelTimestampsById.set(entry.targetId, entry.timestamp);\n\t\t\t} else {\n\t\t\t\tlabelsById.delete(entry.targetId);\n\t\t\t\tlabelTimestampsById.delete(entry.targetId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { byId, labelsById, labelTimestampsById, leafId };\n}\n\nexport function getBranchPath(fromId: string | null | undefined, byId: Map<string, SessionEntry>): SessionEntry[] {\n\tconst path: SessionEntry[] = [];\n\tlet current = fromId ? byId.get(fromId) : undefined;\n\twhile (current) {\n\t\tpath.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn path;\n}\n\nexport function buildSessionTree(\n\tentries: SessionEntry[],\n\tlabelsById: ReadonlyMap<string, string>,\n\tlabelTimestampsById: ReadonlyMap<string, string>,\n): SessionTreeNode[] {\n\tconst nodeMap = new Map<string, SessionTreeNode>();\n\tconst roots: SessionTreeNode[] = [];\n\n\t// Create nodes with resolved labels\n\tfor (const entry of entries) {\n\t\tconst label = labelsById.get(entry.id);\n\t\tconst labelTimestamp = labelTimestampsById.get(entry.id);\n\t\tnodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });\n\t}\n\n\t// Build tree\n\tfor (const entry of entries) {\n\t\tconst node = nodeMap.get(entry.id)!;\n\t\tif (entry.parentId === null || entry.parentId === entry.id) {\n\t\t\troots.push(node);\n\t\t} else {\n\t\t\tconst parent = nodeMap.get(entry.parentId);\n\t\t\tif (parent) {\n\t\t\t\tparent.children.push(node);\n\t\t\t} else {\n\t\t\t\t// Orphan - treat as root\n\t\t\t\troots.push(node);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort children by timestamp (oldest first, newest at bottom)\n\t// Use iterative approach to avoid stack overflow on deep trees\n\tconst stack: SessionTreeNode[] = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop()!;\n\t\tnode.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());\n\t\tstack.push(...node.children);\n\t}\n\n\treturn roots;\n}\n"]}
1
+ {"version":3,"file":"session-manager-history.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-history.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,sBAAsB,EACtB,sBAAsB,EACtB,SAAS,EACT,cAAc,EACd,YAAY,EAEZ,eAAe,EACf,MAAM,4BAA4B,CAAC;AAEpC,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,sBAAsB,GAAG,IAAI,CAQvG;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,sBAAsB,CAkBxF;AA0DD,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,sBAAsB,CA+DjG;AA0CD;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC/C,IAAI,EAAE,YAAY,EAAE,EACpB,gBAAgB,GAAE,sBAAmE,GACnF,YAAY,EAAE,CA4BhB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,YAAY,EAAE,EACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GAC9B,cAAc,CA8EhB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAChC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,YAAY,CAsBxE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,YAAY,EAAE,CAShH;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9C,eAAe,EAAE,CAqCnB","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { createBranchSummaryMessage, createCustomMessage } from \"./messages.ts\";\nimport { contentArrayHasAssistantThinkingBlock } from \"./thinking-blocks.ts\";\nimport type {\n\tContextCompactionEntry,\n\tContextDeletionFilters,\n\tFileEntry,\n\tSessionContext,\n\tSessionEntry,\n\tSessionMessageEntry,\n\tSessionTreeNode,\n} from \"./session-manager-types.ts\";\n\nexport function getLatestCompactionBoundaryEntry(entries: SessionEntry[]): ContextCompactionEntry | null {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"context_compaction\") {\n\t\t\treturn entry;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Build raw deletion filters from persisted context_compaction entries.\n *\n * These raw filters do not apply replay-safety repair for latest assistant\n * thinking/redacted_thinking blocks or their paired tool results. Production\n * context rebuild paths should prefer `buildEffectiveContextDeletionFilters`\n * or `buildContextDeletionFilteredPath(path)` unless they intentionally need\n * the un-repaired historical deletion plan for diagnostics.\n */\nexport function buildContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"context_compaction\") continue;\n\t\tfor (const target of entry.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") {\n\t\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\t\texisting.add(target.blockIndex);\n\t\t\tdeletedContentBlocks.set(target.entryId, existing);\n\t\t}\n\t}\n\n\treturn { deletedEntryIds, deletedContentBlocks };\n}\n\nfunction getToolCallContentBlockId(block: unknown): string | undefined {\n\tif (!block || typeof block !== \"object\") return undefined;\n\tconst candidate = block as { type?: unknown; id?: unknown };\n\treturn candidate.type === \"toolCall\" && typeof candidate.id === \"string\" ? candidate.id : undefined;\n}\n\nfunction getToolResultCallId(message: AgentMessage): string | undefined {\n\tif (message.role !== \"toolResult\") return undefined;\n\tconst toolCallId = (message as { toolCallId?: unknown }).toolCallId;\n\treturn typeof toolCallId === \"string\" ? toolCallId : undefined;\n}\n\nfunction collectToolCallContentBlockIds(content: readonly unknown[]): Set<string> {\n\tconst toolCallIds = new Set<string>();\n\tfor (const block of content) {\n\t\tconst toolCallId = getToolCallContentBlockId(block);\n\t\tif (toolCallId) toolCallIds.add(toolCallId);\n\t}\n\treturn toolCallIds;\n}\n\nfunction addDeletionTarget(filters: ContextDeletionFilters, target: ContextCompactionEntry[\"deletedTargets\"][number]): void {\n\tif (target.kind === \"entry\") {\n\t\tfilters.deletedEntryIds.add(target.entryId);\n\t\treturn;\n\t}\n\tconst existing = filters.deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\texisting.add(target.blockIndex);\n\tfilters.deletedContentBlocks.set(target.entryId, existing);\n}\n\nfunction buildToolResultEntryIdsByCallId(path: SessionEntry[]): Map<string, Set<string>> {\n\tconst toolResultEntryIdsByCallId = new Map<string, Set<string>>();\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst toolCallId = getToolResultCallId(entry.message);\n\t\tif (!toolCallId) continue;\n\t\tconst existing = toolResultEntryIdsByCallId.get(toolCallId) ?? new Set<string>();\n\t\texisting.add(entry.id);\n\t\ttoolResultEntryIdsByCallId.set(toolCallId, existing);\n\t}\n\treturn toolResultEntryIdsByCallId;\n}\n\nfunction findRetainedThinkingAssistants(\n\tpath: SessionEntry[],\n\tdeletedEntryIds: ReadonlySet<string>,\n): SessionMessageEntry[] {\n\treturn path.filter((entry): entry is SessionMessageEntry => {\n\t\tif (entry.type !== \"message\") return false;\n\t\tif (deletedEntryIds.has(entry.id)) return false;\n\t\tif (entry.message.role !== \"assistant\") return false;\n\t\treturn contentArrayHasAssistantThinkingBlock(entry.message.content);\n\t});\n}\n\nexport function buildEffectiveContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst filters = buildContextDeletionFilters(path);\n\tif (!path.some((entry) => entry.type === \"context_compaction\")) return filters;\n\n\tconst rawDeletedEntryIds = new Set<string>();\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") rawDeletedEntryIds.add(target.entryId);\n\t\t}\n\t}\n\tconst retainedThinkingAssistants = findRetainedThinkingAssistants(path, rawDeletedEntryIds);\n\tconst retainedThinkingAssistantIds = new Set(retainedThinkingAssistants.map((entry) => entry.id));\n\tconst retainedThinkingAssistantById = new Map(retainedThinkingAssistants.map((entry) => [entry.id, entry]));\n\tconst toolResultEntryIdsByCallId = buildToolResultEntryIdsByCallId(path);\n\tconst effectiveFilters: ContextDeletionFilters = {\n\t\tdeletedEntryIds: new Set<string>(),\n\t\tdeletedContentBlocks: new Map<string, Set<number>>(),\n\t};\n\tconst allRestoredToolResultEntryIds = new Set<string>();\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind !== \"content_block\") continue;\n\t\t\tconst retainedThinkingAssistant = retainedThinkingAssistantById.get(target.entryId);\n\t\t\tif (!retainedThinkingAssistant) continue;\n\t\t\tconst content = (retainedThinkingAssistant.message as { content: readonly unknown[] }).content;\n\t\t\tfor (const toolCallId of collectToolCallContentBlockIds(content)) {\n\t\t\t\tfor (const entryId of toolResultEntryIdsByCallId.get(toolCallId) ?? []) {\n\t\t\t\t\tallRestoredToolResultEntryIds.add(entryId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tlet restoresRetainedThinkingAssistant = false;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\trestoresRetainedThinkingAssistant = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// When a stale persisted plan tried to partially filter a retained\n\t\t\t// thinking-bearing assistant, treat the same compaction entry as one\n\t\t\t// unsafe unit and restore its paired tool results. Later compaction\n\t\t\t// entries may still trim those restored multi-block results normally,\n\t\t\t// but whole-entry deletion of those paired results remains unsafe in any\n\t\t\t// later compaction because the assistant tool call is retained.\n\t\t\tif (restoresRetainedThinkingAssistant && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\tif (target.kind === \"entry\" && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\taddDeletionTarget(effectiveFilters, target);\n\t\t}\n\t}\n\n\treturn effectiveFilters;\n}\n\nfunction filterContentArray<T>(content: T[], deletedBlocks: ReadonlySet<number>): T[] {\n\treturn content.filter((_, index) => !deletedBlocks.has(index));\n}\n\nfunction filterMessageContentBlocks(\n\tmessage: AgentMessage,\n\tdeletedBlocks: ReadonlySet<number> | undefined,\n): AgentMessage | undefined {\n\tif (!deletedBlocks || deletedBlocks.size === 0) return message;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"toolResult\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"custom\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"bashExecution\":\n\t\tcase \"branchSummary\":\n\t\t\treturn message;\n\t}\n}\n\n/**\n * Return the active branch path after applying logical context-deletion entries.\n * Whole-entry deletions remove the entry from the path. Content-block deletions\n * clone only affected message/custom-message entries so retained blocks stay verbatim.\n * The optional filters parameter is for callers that already computed effective\n * filters with `buildEffectiveContextDeletionFilters(path)` and want to avoid\n * repeating the repair pass.\n */\nexport function buildContextDeletionFilteredPath(\n\tpath: SessionEntry[],\n\teffectiveFilters: ContextDeletionFilters = buildEffectiveContextDeletionFilters(path),\n): SessionEntry[] {\n\tconst filteredPath: SessionEntry[] = [];\n\n\tfor (const entry of path) {\n\t\tif (effectiveFilters.deletedEntryIds.has(entry.id)) continue;\n\n\t\tconst deletedBlocks = effectiveFilters.deletedContentBlocks.get(entry.id);\n\t\tif (!deletedBlocks || deletedBlocks.size === 0) {\n\t\t\tfilteredPath.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"message\") {\n\t\t\tconst message = filterMessageContentBlocks(entry.message, deletedBlocks);\n\t\t\tif (message) filteredPath.push({ ...entry, message });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"custom_message\" && Array.isArray(entry.content)) {\n\t\t\tconst content = filterContentArray(entry.content, deletedBlocks);\n\t\t\tif (content.length > 0) filteredPath.push({ ...entry, content });\n\t\t\tcontinue;\n\t\t}\n\n\t\tfilteredPath.push(entry);\n\t}\n\n\treturn filteredPath;\n}\n\n/**\n * Build the session context from entries using tree traversal.\n * If leafId is provided, walks from that entry to root.\n * Applies context-deletion filtering and includes branch summaries along the path.\n */\nexport function buildSessionContext(\n\tentries: SessionEntry[],\n\tleafId?: string | null,\n\tbyId?: Map<string, SessionEntry>,\n): SessionContext {\n\t// Build uuid index if not available\n\tif (!byId) {\n\t\tbyId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\t}\n\n\t// Find leaf\n\tlet leaf: SessionEntry | undefined;\n\tif (leafId === null) {\n\t\t// Explicitly null - return no messages (navigated to before first entry)\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\tif (leafId) {\n\t\tleaf = byId.get(leafId);\n\t}\n\tif (!leaf) {\n\t\t// Fallback to last entry (when leafId is undefined)\n\t\tleaf = entries[entries.length - 1];\n\t}\n\n\tif (!leaf) {\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\n\t// Walk from leaf to root, collecting path\n\tconst path = getBranchPath(leaf.id, byId);\n\n\t// Extract settings\n\tlet thinkingLevel = \"off\";\n\tlet contextWindow: number | undefined;\n\tlet model: { provider: string; modelId: string } | null = null;\n\n\tfor (const entry of path) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"context_window_change\") {\n\t\t\tcontextWindow = entry.contextWindow;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t}\n\t}\n\n\tconst filteredPath = buildContextDeletionFilteredPath(path);\n\n\t// Build active context messages from the filtered path. Legacy \"compaction\"\n\t// entries are archival metadata and intentionally inert here.\n\tconst messages: AgentMessage[] = [];\n\n\tconst appendMessage = (entry: SessionEntry) => {\n\t\tlet message: AgentMessage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tmessage = entry.message;\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessage = createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content,\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t\tentry.excludeFromContext,\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && entry.summary) {\n\t\t\tmessage = createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t\t}\n\n\t\tif (message) messages.push(message);\n\t};\n\n\tfor (const entry of filteredPath) {\n\t\tappendMessage(entry);\n\t}\n\n\treturn { messages, thinkingLevel, contextWindow, model };\n}\n\nexport interface SessionIndex {\n\tbyId: Map<string, SessionEntry>;\n\tlabelsById: Map<string, string>;\n\tlabelTimestampsById: Map<string, string>;\n\tleafId: string | null;\n}\n\nexport function buildSessionIndex(fileEntries: FileEntry[]): SessionIndex {\n\tconst byId = new Map<string, SessionEntry>();\n\tconst labelsById = new Map<string, string>();\n\tconst labelTimestampsById = new Map<string, string>();\n\tlet leafId: string | null = null;\n\n\tfor (const entry of fileEntries) {\n\t\tif (entry.type === \"session\") continue;\n\t\tbyId.set(entry.id, entry);\n\t\tleafId = entry.id;\n\t\tif (entry.type === \"label\") {\n\t\t\tif (entry.label) {\n\t\t\t\tlabelsById.set(entry.targetId, entry.label);\n\t\t\t\tlabelTimestampsById.set(entry.targetId, entry.timestamp);\n\t\t\t} else {\n\t\t\t\tlabelsById.delete(entry.targetId);\n\t\t\t\tlabelTimestampsById.delete(entry.targetId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { byId, labelsById, labelTimestampsById, leafId };\n}\n\nexport function getBranchPath(fromId: string | null | undefined, byId: Map<string, SessionEntry>): SessionEntry[] {\n\tconst path: SessionEntry[] = [];\n\tlet current = fromId ? byId.get(fromId) : undefined;\n\twhile (current) {\n\t\tpath.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tpath.reverse();\n\treturn path;\n}\n\nexport function buildSessionTree(\n\tentries: SessionEntry[],\n\tlabelsById: ReadonlyMap<string, string>,\n\tlabelTimestampsById: ReadonlyMap<string, string>,\n): SessionTreeNode[] {\n\tconst nodeMap = new Map<string, SessionTreeNode>();\n\tconst roots: SessionTreeNode[] = [];\n\n\t// Create nodes with resolved labels\n\tfor (const entry of entries) {\n\t\tconst label = labelsById.get(entry.id);\n\t\tconst labelTimestamp = labelTimestampsById.get(entry.id);\n\t\tnodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });\n\t}\n\n\t// Build tree\n\tfor (const entry of entries) {\n\t\tconst node = nodeMap.get(entry.id)!;\n\t\tif (entry.parentId === null || entry.parentId === entry.id) {\n\t\t\troots.push(node);\n\t\t} else {\n\t\t\tconst parent = nodeMap.get(entry.parentId);\n\t\t\tif (parent) {\n\t\t\t\tparent.children.push(node);\n\t\t\t} else {\n\t\t\t\t// Orphan - treat as root\n\t\t\t\troots.push(node);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort children by timestamp (oldest first, newest at bottom)\n\t// Use iterative approach to avoid stack overflow on deep trees\n\tconst stack: SessionTreeNode[] = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop()!;\n\t\tnode.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());\n\t\tstack.push(...node.children);\n\t}\n\n\treturn roots;\n}\n"]}
@@ -334,9 +334,10 @@ export function getBranchPath(fromId, byId) {
334
334
  const path = [];
335
335
  let current = fromId ? byId.get(fromId) : undefined;
336
336
  while (current) {
337
- path.unshift(current);
337
+ path.push(current);
338
338
  current = current.parentId ? byId.get(current.parentId) : undefined;
339
339
  }
340
+ path.reverse();
340
341
  return path;
341
342
  }
342
343
  export function buildSessionTree(entries, labelsById, labelTimestampsById) {
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-history.js","sourceRoot":"","sources":["../../src/core/session-manager-history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { createBranchSummaryMessage, createCustomMessage } from \"./messages.ts\";\nimport { contentArrayHasAssistantThinkingBlock } from \"./thinking-blocks.ts\";\nimport type {\n\tContextCompactionEntry,\n\tContextDeletionFilters,\n\tFileEntry,\n\tSessionContext,\n\tSessionEntry,\n\tSessionMessageEntry,\n\tSessionTreeNode,\n} from \"./session-manager-types.ts\";\n\nexport function getLatestCompactionBoundaryEntry(entries: SessionEntry[]): ContextCompactionEntry | null {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"context_compaction\") {\n\t\t\treturn entry;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Build raw deletion filters from persisted context_compaction entries.\n *\n * These raw filters do not apply replay-safety repair for latest assistant\n * thinking/redacted_thinking blocks or their paired tool results. Production\n * context rebuild paths should prefer `buildEffectiveContextDeletionFilters`\n * or `buildContextDeletionFilteredPath(path)` unless they intentionally need\n * the un-repaired historical deletion plan for diagnostics.\n */\nexport function buildContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"context_compaction\") continue;\n\t\tfor (const target of entry.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") {\n\t\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\t\texisting.add(target.blockIndex);\n\t\t\tdeletedContentBlocks.set(target.entryId, existing);\n\t\t}\n\t}\n\n\treturn { deletedEntryIds, deletedContentBlocks };\n}\n\nfunction getToolCallContentBlockId(block: unknown): string | undefined {\n\tif (!block || typeof block !== \"object\") return undefined;\n\tconst candidate = block as { type?: unknown; id?: unknown };\n\treturn candidate.type === \"toolCall\" && typeof candidate.id === \"string\" ? candidate.id : undefined;\n}\n\nfunction getToolResultCallId(message: AgentMessage): string | undefined {\n\tif (message.role !== \"toolResult\") return undefined;\n\tconst toolCallId = (message as { toolCallId?: unknown }).toolCallId;\n\treturn typeof toolCallId === \"string\" ? toolCallId : undefined;\n}\n\nfunction collectToolCallContentBlockIds(content: readonly unknown[]): Set<string> {\n\tconst toolCallIds = new Set<string>();\n\tfor (const block of content) {\n\t\tconst toolCallId = getToolCallContentBlockId(block);\n\t\tif (toolCallId) toolCallIds.add(toolCallId);\n\t}\n\treturn toolCallIds;\n}\n\nfunction addDeletionTarget(filters: ContextDeletionFilters, target: ContextCompactionEntry[\"deletedTargets\"][number]): void {\n\tif (target.kind === \"entry\") {\n\t\tfilters.deletedEntryIds.add(target.entryId);\n\t\treturn;\n\t}\n\tconst existing = filters.deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\texisting.add(target.blockIndex);\n\tfilters.deletedContentBlocks.set(target.entryId, existing);\n}\n\nfunction buildToolResultEntryIdsByCallId(path: SessionEntry[]): Map<string, Set<string>> {\n\tconst toolResultEntryIdsByCallId = new Map<string, Set<string>>();\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst toolCallId = getToolResultCallId(entry.message);\n\t\tif (!toolCallId) continue;\n\t\tconst existing = toolResultEntryIdsByCallId.get(toolCallId) ?? new Set<string>();\n\t\texisting.add(entry.id);\n\t\ttoolResultEntryIdsByCallId.set(toolCallId, existing);\n\t}\n\treturn toolResultEntryIdsByCallId;\n}\n\nfunction findRetainedThinkingAssistants(\n\tpath: SessionEntry[],\n\tdeletedEntryIds: ReadonlySet<string>,\n): SessionMessageEntry[] {\n\treturn path.filter((entry): entry is SessionMessageEntry => {\n\t\tif (entry.type !== \"message\") return false;\n\t\tif (deletedEntryIds.has(entry.id)) return false;\n\t\tif (entry.message.role !== \"assistant\") return false;\n\t\treturn contentArrayHasAssistantThinkingBlock(entry.message.content);\n\t});\n}\n\nexport function buildEffectiveContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst filters = buildContextDeletionFilters(path);\n\tif (!path.some((entry) => entry.type === \"context_compaction\")) return filters;\n\n\tconst rawDeletedEntryIds = new Set<string>();\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") rawDeletedEntryIds.add(target.entryId);\n\t\t}\n\t}\n\tconst retainedThinkingAssistants = findRetainedThinkingAssistants(path, rawDeletedEntryIds);\n\tconst retainedThinkingAssistantIds = new Set(retainedThinkingAssistants.map((entry) => entry.id));\n\tconst retainedThinkingAssistantById = new Map(retainedThinkingAssistants.map((entry) => [entry.id, entry]));\n\tconst toolResultEntryIdsByCallId = buildToolResultEntryIdsByCallId(path);\n\tconst effectiveFilters: ContextDeletionFilters = {\n\t\tdeletedEntryIds: new Set<string>(),\n\t\tdeletedContentBlocks: new Map<string, Set<number>>(),\n\t};\n\tconst allRestoredToolResultEntryIds = new Set<string>();\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind !== \"content_block\") continue;\n\t\t\tconst retainedThinkingAssistant = retainedThinkingAssistantById.get(target.entryId);\n\t\t\tif (!retainedThinkingAssistant) continue;\n\t\t\tconst content = (retainedThinkingAssistant.message as { content: readonly unknown[] }).content;\n\t\t\tfor (const toolCallId of collectToolCallContentBlockIds(content)) {\n\t\t\t\tfor (const entryId of toolResultEntryIdsByCallId.get(toolCallId) ?? []) {\n\t\t\t\t\tallRestoredToolResultEntryIds.add(entryId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tlet restoresRetainedThinkingAssistant = false;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\trestoresRetainedThinkingAssistant = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// When a stale persisted plan tried to partially filter a retained\n\t\t\t// thinking-bearing assistant, treat the same compaction entry as one\n\t\t\t// unsafe unit and restore its paired tool results. Later compaction\n\t\t\t// entries may still trim those restored multi-block results normally,\n\t\t\t// but whole-entry deletion of those paired results remains unsafe in any\n\t\t\t// later compaction because the assistant tool call is retained.\n\t\t\tif (restoresRetainedThinkingAssistant && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\tif (target.kind === \"entry\" && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\taddDeletionTarget(effectiveFilters, target);\n\t\t}\n\t}\n\n\treturn effectiveFilters;\n}\n\nfunction filterContentArray<T>(content: T[], deletedBlocks: ReadonlySet<number>): T[] {\n\treturn content.filter((_, index) => !deletedBlocks.has(index));\n}\n\nfunction filterMessageContentBlocks(\n\tmessage: AgentMessage,\n\tdeletedBlocks: ReadonlySet<number> | undefined,\n): AgentMessage | undefined {\n\tif (!deletedBlocks || deletedBlocks.size === 0) return message;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"toolResult\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"custom\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"bashExecution\":\n\t\tcase \"branchSummary\":\n\t\t\treturn message;\n\t}\n}\n\n/**\n * Return the active branch path after applying logical context-deletion entries.\n * Whole-entry deletions remove the entry from the path. Content-block deletions\n * clone only affected message/custom-message entries so retained blocks stay verbatim.\n * The optional filters parameter is for callers that already computed effective\n * filters with `buildEffectiveContextDeletionFilters(path)` and want to avoid\n * repeating the repair pass.\n */\nexport function buildContextDeletionFilteredPath(\n\tpath: SessionEntry[],\n\teffectiveFilters: ContextDeletionFilters = buildEffectiveContextDeletionFilters(path),\n): SessionEntry[] {\n\tconst filteredPath: SessionEntry[] = [];\n\n\tfor (const entry of path) {\n\t\tif (effectiveFilters.deletedEntryIds.has(entry.id)) continue;\n\n\t\tconst deletedBlocks = effectiveFilters.deletedContentBlocks.get(entry.id);\n\t\tif (!deletedBlocks || deletedBlocks.size === 0) {\n\t\t\tfilteredPath.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"message\") {\n\t\t\tconst message = filterMessageContentBlocks(entry.message, deletedBlocks);\n\t\t\tif (message) filteredPath.push({ ...entry, message });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"custom_message\" && Array.isArray(entry.content)) {\n\t\t\tconst content = filterContentArray(entry.content, deletedBlocks);\n\t\t\tif (content.length > 0) filteredPath.push({ ...entry, content });\n\t\t\tcontinue;\n\t\t}\n\n\t\tfilteredPath.push(entry);\n\t}\n\n\treturn filteredPath;\n}\n\n/**\n * Build the session context from entries using tree traversal.\n * If leafId is provided, walks from that entry to root.\n * Applies context-deletion filtering and includes branch summaries along the path.\n */\nexport function buildSessionContext(\n\tentries: SessionEntry[],\n\tleafId?: string | null,\n\tbyId?: Map<string, SessionEntry>,\n): SessionContext {\n\t// Build uuid index if not available\n\tif (!byId) {\n\t\tbyId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\t}\n\n\t// Find leaf\n\tlet leaf: SessionEntry | undefined;\n\tif (leafId === null) {\n\t\t// Explicitly null - return no messages (navigated to before first entry)\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\tif (leafId) {\n\t\tleaf = byId.get(leafId);\n\t}\n\tif (!leaf) {\n\t\t// Fallback to last entry (when leafId is undefined)\n\t\tleaf = entries[entries.length - 1];\n\t}\n\n\tif (!leaf) {\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\n\t// Walk from leaf to root, collecting path\n\tconst path = getBranchPath(leaf.id, byId);\n\n\t// Extract settings\n\tlet thinkingLevel = \"off\";\n\tlet contextWindow: number | undefined;\n\tlet model: { provider: string; modelId: string } | null = null;\n\n\tfor (const entry of path) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"context_window_change\") {\n\t\t\tcontextWindow = entry.contextWindow;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t}\n\t}\n\n\tconst filteredPath = buildContextDeletionFilteredPath(path);\n\n\t// Build active context messages from the filtered path. Legacy \"compaction\"\n\t// entries are archival metadata and intentionally inert here.\n\tconst messages: AgentMessage[] = [];\n\n\tconst appendMessage = (entry: SessionEntry) => {\n\t\tlet message: AgentMessage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tmessage = entry.message;\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessage = createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content,\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t\tentry.excludeFromContext,\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && entry.summary) {\n\t\t\tmessage = createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t\t}\n\n\t\tif (message) messages.push(message);\n\t};\n\n\tfor (const entry of filteredPath) {\n\t\tappendMessage(entry);\n\t}\n\n\treturn { messages, thinkingLevel, contextWindow, model };\n}\n\nexport interface SessionIndex {\n\tbyId: Map<string, SessionEntry>;\n\tlabelsById: Map<string, string>;\n\tlabelTimestampsById: Map<string, string>;\n\tleafId: string | null;\n}\n\nexport function buildSessionIndex(fileEntries: FileEntry[]): SessionIndex {\n\tconst byId = new Map<string, SessionEntry>();\n\tconst labelsById = new Map<string, string>();\n\tconst labelTimestampsById = new Map<string, string>();\n\tlet leafId: string | null = null;\n\n\tfor (const entry of fileEntries) {\n\t\tif (entry.type === \"session\") continue;\n\t\tbyId.set(entry.id, entry);\n\t\tleafId = entry.id;\n\t\tif (entry.type === \"label\") {\n\t\t\tif (entry.label) {\n\t\t\t\tlabelsById.set(entry.targetId, entry.label);\n\t\t\t\tlabelTimestampsById.set(entry.targetId, entry.timestamp);\n\t\t\t} else {\n\t\t\t\tlabelsById.delete(entry.targetId);\n\t\t\t\tlabelTimestampsById.delete(entry.targetId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { byId, labelsById, labelTimestampsById, leafId };\n}\n\nexport function getBranchPath(fromId: string | null | undefined, byId: Map<string, SessionEntry>): SessionEntry[] {\n\tconst path: SessionEntry[] = [];\n\tlet current = fromId ? byId.get(fromId) : undefined;\n\twhile (current) {\n\t\tpath.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn path;\n}\n\nexport function buildSessionTree(\n\tentries: SessionEntry[],\n\tlabelsById: ReadonlyMap<string, string>,\n\tlabelTimestampsById: ReadonlyMap<string, string>,\n): SessionTreeNode[] {\n\tconst nodeMap = new Map<string, SessionTreeNode>();\n\tconst roots: SessionTreeNode[] = [];\n\n\t// Create nodes with resolved labels\n\tfor (const entry of entries) {\n\t\tconst label = labelsById.get(entry.id);\n\t\tconst labelTimestamp = labelTimestampsById.get(entry.id);\n\t\tnodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });\n\t}\n\n\t// Build tree\n\tfor (const entry of entries) {\n\t\tconst node = nodeMap.get(entry.id)!;\n\t\tif (entry.parentId === null || entry.parentId === entry.id) {\n\t\t\troots.push(node);\n\t\t} else {\n\t\t\tconst parent = nodeMap.get(entry.parentId);\n\t\t\tif (parent) {\n\t\t\t\tparent.children.push(node);\n\t\t\t} else {\n\t\t\t\t// Orphan - treat as root\n\t\t\t\troots.push(node);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort children by timestamp (oldest first, newest at bottom)\n\t// Use iterative approach to avoid stack overflow on deep trees\n\tconst stack: SessionTreeNode[] = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop()!;\n\t\tnode.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());\n\t\tstack.push(...node.children);\n\t}\n\n\treturn roots;\n}\n"]}
1
+ {"version":3,"file":"session-manager-history.js","sourceRoot":"","sources":["../../src/core/session-manager-history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { createBranchSummaryMessage, createCustomMessage } from \"./messages.ts\";\nimport { contentArrayHasAssistantThinkingBlock } from \"./thinking-blocks.ts\";\nimport type {\n\tContextCompactionEntry,\n\tContextDeletionFilters,\n\tFileEntry,\n\tSessionContext,\n\tSessionEntry,\n\tSessionMessageEntry,\n\tSessionTreeNode,\n} from \"./session-manager-types.ts\";\n\nexport function getLatestCompactionBoundaryEntry(entries: SessionEntry[]): ContextCompactionEntry | null {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"context_compaction\") {\n\t\t\treturn entry;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Build raw deletion filters from persisted context_compaction entries.\n *\n * These raw filters do not apply replay-safety repair for latest assistant\n * thinking/redacted_thinking blocks or their paired tool results. Production\n * context rebuild paths should prefer `buildEffectiveContextDeletionFilters`\n * or `buildContextDeletionFilteredPath(path)` unless they intentionally need\n * the un-repaired historical deletion plan for diagnostics.\n */\nexport function buildContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"context_compaction\") continue;\n\t\tfor (const target of entry.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") {\n\t\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\t\texisting.add(target.blockIndex);\n\t\t\tdeletedContentBlocks.set(target.entryId, existing);\n\t\t}\n\t}\n\n\treturn { deletedEntryIds, deletedContentBlocks };\n}\n\nfunction getToolCallContentBlockId(block: unknown): string | undefined {\n\tif (!block || typeof block !== \"object\") return undefined;\n\tconst candidate = block as { type?: unknown; id?: unknown };\n\treturn candidate.type === \"toolCall\" && typeof candidate.id === \"string\" ? candidate.id : undefined;\n}\n\nfunction getToolResultCallId(message: AgentMessage): string | undefined {\n\tif (message.role !== \"toolResult\") return undefined;\n\tconst toolCallId = (message as { toolCallId?: unknown }).toolCallId;\n\treturn typeof toolCallId === \"string\" ? toolCallId : undefined;\n}\n\nfunction collectToolCallContentBlockIds(content: readonly unknown[]): Set<string> {\n\tconst toolCallIds = new Set<string>();\n\tfor (const block of content) {\n\t\tconst toolCallId = getToolCallContentBlockId(block);\n\t\tif (toolCallId) toolCallIds.add(toolCallId);\n\t}\n\treturn toolCallIds;\n}\n\nfunction addDeletionTarget(filters: ContextDeletionFilters, target: ContextCompactionEntry[\"deletedTargets\"][number]): void {\n\tif (target.kind === \"entry\") {\n\t\tfilters.deletedEntryIds.add(target.entryId);\n\t\treturn;\n\t}\n\tconst existing = filters.deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\texisting.add(target.blockIndex);\n\tfilters.deletedContentBlocks.set(target.entryId, existing);\n}\n\nfunction buildToolResultEntryIdsByCallId(path: SessionEntry[]): Map<string, Set<string>> {\n\tconst toolResultEntryIdsByCallId = new Map<string, Set<string>>();\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst toolCallId = getToolResultCallId(entry.message);\n\t\tif (!toolCallId) continue;\n\t\tconst existing = toolResultEntryIdsByCallId.get(toolCallId) ?? new Set<string>();\n\t\texisting.add(entry.id);\n\t\ttoolResultEntryIdsByCallId.set(toolCallId, existing);\n\t}\n\treturn toolResultEntryIdsByCallId;\n}\n\nfunction findRetainedThinkingAssistants(\n\tpath: SessionEntry[],\n\tdeletedEntryIds: ReadonlySet<string>,\n): SessionMessageEntry[] {\n\treturn path.filter((entry): entry is SessionMessageEntry => {\n\t\tif (entry.type !== \"message\") return false;\n\t\tif (deletedEntryIds.has(entry.id)) return false;\n\t\tif (entry.message.role !== \"assistant\") return false;\n\t\treturn contentArrayHasAssistantThinkingBlock(entry.message.content);\n\t});\n}\n\nexport function buildEffectiveContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst filters = buildContextDeletionFilters(path);\n\tif (!path.some((entry) => entry.type === \"context_compaction\")) return filters;\n\n\tconst rawDeletedEntryIds = new Set<string>();\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") rawDeletedEntryIds.add(target.entryId);\n\t\t}\n\t}\n\tconst retainedThinkingAssistants = findRetainedThinkingAssistants(path, rawDeletedEntryIds);\n\tconst retainedThinkingAssistantIds = new Set(retainedThinkingAssistants.map((entry) => entry.id));\n\tconst retainedThinkingAssistantById = new Map(retainedThinkingAssistants.map((entry) => [entry.id, entry]));\n\tconst toolResultEntryIdsByCallId = buildToolResultEntryIdsByCallId(path);\n\tconst effectiveFilters: ContextDeletionFilters = {\n\t\tdeletedEntryIds: new Set<string>(),\n\t\tdeletedContentBlocks: new Map<string, Set<number>>(),\n\t};\n\tconst allRestoredToolResultEntryIds = new Set<string>();\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind !== \"content_block\") continue;\n\t\t\tconst retainedThinkingAssistant = retainedThinkingAssistantById.get(target.entryId);\n\t\t\tif (!retainedThinkingAssistant) continue;\n\t\t\tconst content = (retainedThinkingAssistant.message as { content: readonly unknown[] }).content;\n\t\t\tfor (const toolCallId of collectToolCallContentBlockIds(content)) {\n\t\t\t\tfor (const entryId of toolResultEntryIdsByCallId.get(toolCallId) ?? []) {\n\t\t\t\t\tallRestoredToolResultEntryIds.add(entryId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const compaction of path) {\n\t\tif (compaction.type !== \"context_compaction\") continue;\n\t\tlet restoresRetainedThinkingAssistant = false;\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\trestoresRetainedThinkingAssistant = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (const target of compaction.deletedTargets) {\n\t\t\tif (target.kind === \"content_block\" && retainedThinkingAssistantIds.has(target.entryId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// When a stale persisted plan tried to partially filter a retained\n\t\t\t// thinking-bearing assistant, treat the same compaction entry as one\n\t\t\t// unsafe unit and restore its paired tool results. Later compaction\n\t\t\t// entries may still trim those restored multi-block results normally,\n\t\t\t// but whole-entry deletion of those paired results remains unsafe in any\n\t\t\t// later compaction because the assistant tool call is retained.\n\t\t\tif (restoresRetainedThinkingAssistant && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\tif (target.kind === \"entry\" && allRestoredToolResultEntryIds.has(target.entryId)) continue;\n\t\t\taddDeletionTarget(effectiveFilters, target);\n\t\t}\n\t}\n\n\treturn effectiveFilters;\n}\n\nfunction filterContentArray<T>(content: T[], deletedBlocks: ReadonlySet<number>): T[] {\n\treturn content.filter((_, index) => !deletedBlocks.has(index));\n}\n\nfunction filterMessageContentBlocks(\n\tmessage: AgentMessage,\n\tdeletedBlocks: ReadonlySet<number> | undefined,\n): AgentMessage | undefined {\n\tif (!deletedBlocks || deletedBlocks.size === 0) return message;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"toolResult\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"custom\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"bashExecution\":\n\t\tcase \"branchSummary\":\n\t\t\treturn message;\n\t}\n}\n\n/**\n * Return the active branch path after applying logical context-deletion entries.\n * Whole-entry deletions remove the entry from the path. Content-block deletions\n * clone only affected message/custom-message entries so retained blocks stay verbatim.\n * The optional filters parameter is for callers that already computed effective\n * filters with `buildEffectiveContextDeletionFilters(path)` and want to avoid\n * repeating the repair pass.\n */\nexport function buildContextDeletionFilteredPath(\n\tpath: SessionEntry[],\n\teffectiveFilters: ContextDeletionFilters = buildEffectiveContextDeletionFilters(path),\n): SessionEntry[] {\n\tconst filteredPath: SessionEntry[] = [];\n\n\tfor (const entry of path) {\n\t\tif (effectiveFilters.deletedEntryIds.has(entry.id)) continue;\n\n\t\tconst deletedBlocks = effectiveFilters.deletedContentBlocks.get(entry.id);\n\t\tif (!deletedBlocks || deletedBlocks.size === 0) {\n\t\t\tfilteredPath.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"message\") {\n\t\t\tconst message = filterMessageContentBlocks(entry.message, deletedBlocks);\n\t\t\tif (message) filteredPath.push({ ...entry, message });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"custom_message\" && Array.isArray(entry.content)) {\n\t\t\tconst content = filterContentArray(entry.content, deletedBlocks);\n\t\t\tif (content.length > 0) filteredPath.push({ ...entry, content });\n\t\t\tcontinue;\n\t\t}\n\n\t\tfilteredPath.push(entry);\n\t}\n\n\treturn filteredPath;\n}\n\n/**\n * Build the session context from entries using tree traversal.\n * If leafId is provided, walks from that entry to root.\n * Applies context-deletion filtering and includes branch summaries along the path.\n */\nexport function buildSessionContext(\n\tentries: SessionEntry[],\n\tleafId?: string | null,\n\tbyId?: Map<string, SessionEntry>,\n): SessionContext {\n\t// Build uuid index if not available\n\tif (!byId) {\n\t\tbyId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\t}\n\n\t// Find leaf\n\tlet leaf: SessionEntry | undefined;\n\tif (leafId === null) {\n\t\t// Explicitly null - return no messages (navigated to before first entry)\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\tif (leafId) {\n\t\tleaf = byId.get(leafId);\n\t}\n\tif (!leaf) {\n\t\t// Fallback to last entry (when leafId is undefined)\n\t\tleaf = entries[entries.length - 1];\n\t}\n\n\tif (!leaf) {\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\n\t// Walk from leaf to root, collecting path\n\tconst path = getBranchPath(leaf.id, byId);\n\n\t// Extract settings\n\tlet thinkingLevel = \"off\";\n\tlet contextWindow: number | undefined;\n\tlet model: { provider: string; modelId: string } | null = null;\n\n\tfor (const entry of path) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"context_window_change\") {\n\t\t\tcontextWindow = entry.contextWindow;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t}\n\t}\n\n\tconst filteredPath = buildContextDeletionFilteredPath(path);\n\n\t// Build active context messages from the filtered path. Legacy \"compaction\"\n\t// entries are archival metadata and intentionally inert here.\n\tconst messages: AgentMessage[] = [];\n\n\tconst appendMessage = (entry: SessionEntry) => {\n\t\tlet message: AgentMessage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tmessage = entry.message;\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessage = createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content,\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t\tentry.excludeFromContext,\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && entry.summary) {\n\t\t\tmessage = createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t\t}\n\n\t\tif (message) messages.push(message);\n\t};\n\n\tfor (const entry of filteredPath) {\n\t\tappendMessage(entry);\n\t}\n\n\treturn { messages, thinkingLevel, contextWindow, model };\n}\n\nexport interface SessionIndex {\n\tbyId: Map<string, SessionEntry>;\n\tlabelsById: Map<string, string>;\n\tlabelTimestampsById: Map<string, string>;\n\tleafId: string | null;\n}\n\nexport function buildSessionIndex(fileEntries: FileEntry[]): SessionIndex {\n\tconst byId = new Map<string, SessionEntry>();\n\tconst labelsById = new Map<string, string>();\n\tconst labelTimestampsById = new Map<string, string>();\n\tlet leafId: string | null = null;\n\n\tfor (const entry of fileEntries) {\n\t\tif (entry.type === \"session\") continue;\n\t\tbyId.set(entry.id, entry);\n\t\tleafId = entry.id;\n\t\tif (entry.type === \"label\") {\n\t\t\tif (entry.label) {\n\t\t\t\tlabelsById.set(entry.targetId, entry.label);\n\t\t\t\tlabelTimestampsById.set(entry.targetId, entry.timestamp);\n\t\t\t} else {\n\t\t\t\tlabelsById.delete(entry.targetId);\n\t\t\t\tlabelTimestampsById.delete(entry.targetId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { byId, labelsById, labelTimestampsById, leafId };\n}\n\nexport function getBranchPath(fromId: string | null | undefined, byId: Map<string, SessionEntry>): SessionEntry[] {\n\tconst path: SessionEntry[] = [];\n\tlet current = fromId ? byId.get(fromId) : undefined;\n\twhile (current) {\n\t\tpath.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tpath.reverse();\n\treturn path;\n}\n\nexport function buildSessionTree(\n\tentries: SessionEntry[],\n\tlabelsById: ReadonlyMap<string, string>,\n\tlabelTimestampsById: ReadonlyMap<string, string>,\n): SessionTreeNode[] {\n\tconst nodeMap = new Map<string, SessionTreeNode>();\n\tconst roots: SessionTreeNode[] = [];\n\n\t// Create nodes with resolved labels\n\tfor (const entry of entries) {\n\t\tconst label = labelsById.get(entry.id);\n\t\tconst labelTimestamp = labelTimestampsById.get(entry.id);\n\t\tnodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });\n\t}\n\n\t// Build tree\n\tfor (const entry of entries) {\n\t\tconst node = nodeMap.get(entry.id)!;\n\t\tif (entry.parentId === null || entry.parentId === entry.id) {\n\t\t\troots.push(node);\n\t\t} else {\n\t\t\tconst parent = nodeMap.get(entry.parentId);\n\t\t\tif (parent) {\n\t\t\t\tparent.children.push(node);\n\t\t\t} else {\n\t\t\t\t// Orphan - treat as root\n\t\t\t\troots.push(node);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort children by timestamp (oldest first, newest at bottom)\n\t// Use iterative approach to avoid stack overflow on deep trees\n\tconst stack: SessionTreeNode[] = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop()!;\n\t\tnode.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());\n\t\tstack.push(...node.children);\n\t}\n\n\treturn roots;\n}\n"]}
@@ -1,7 +1,6 @@
1
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
2
2
  import { type Static, Type } from "typebox";
3
3
  import type { ToolDefinition } from "../extensions/types.ts";
4
- import { type BashCommandPolicy } from "./bash-policy.ts";
5
4
  import { type TruncationResult } from "./truncate.ts";
6
5
  declare const bashSchema: Type.TObject<{
7
6
  command: Type.TString;
@@ -57,10 +56,6 @@ export interface BashToolOptions {
57
56
  shellPath?: string;
58
57
  /** Hook to adjust command, cwd, or env before execution */
59
58
  spawnHook?: BashSpawnHook;
60
- /** Optional command-level policy enforced before commandPrefix, spawnHook, or execution */
61
- policy?: BashCommandPolicy;
62
- /** Human-readable label used in policy denial errors */
63
- policyLabel?: string;
64
59
  }
65
60
  type BashRenderState = {
66
61
  startedAt: number | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAa5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAGN,KAAK,iBAAiB,EACtB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAoD,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAExG,QAAA,MAAM,UAAU;;;EAGd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACxB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CA4D1F;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;AAO5E,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,2FAA2F;IAC3F,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD,KAAK,eAAe,GAAG;IACtB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC;CACrC,CAAC;AAoHF,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,eAAe,CAAC,CAoLjF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Container, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { spawn } from \"child_process\";\nimport { type Static, Type } from \"typebox\";\nimport { APP_NAME } from \"../../config.ts\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tevaluateBashCommandPolicy,\n\tformatBashCommandPolicyRejection,\n\ttype BashCommandPolicy,\n} from \"./bash-policy.ts\";\nimport { OutputAccumulator } from \"./output-accumulator.ts\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst { shell, args } = getShellConfig(options?.shellPath);\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`);\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\n\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeout * 1000);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\tconst exitCode = await waitForChildProcess(child);\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t\t}\n\t\t\t\tif (timedOut) {\n\t\t\t\t\tthrow new Error(`timeout:${timeout}`);\n\t\t\t\t}\n\t\t\t\treturn { exitCode };\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {\n\tconst baseContext: BashSpawnContext = { command, cwd, env: { ...getShellEnv() } };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Optional explicit shell path from settings */\n\tshellPath?: string;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n\t/** Optional command-level policy enforced before commandPrefix, spawnHook, or execution */\n\tpolicy?: BashCommandPolicy;\n\t/** Human-readable label used in policy denial errors */\n\tpolicyLabel?: string;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\tconst totalSeconds = Math.floor(Math.max(0, ms) / 1000);\n\tconst hours = Math.floor(totalSeconds / 3600);\n\tconst minutes = Math.floor((totalSeconds % 3600) / 60);\n\tconst seconds = totalSeconds % 60;\n\tif (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;\n\tif (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n\treturn `${seconds}s`;\n}\n\nfunction formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\n\tconst timeout = args?.timeout as number | undefined;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn theme.fg(\"toolTitle\", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tlet output = getTextOutput(result, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"Expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst spawnHook = options?.spawnHook;\n\tconst policy = options?.policy;\n\tconst policyLabel = options?.policyLabel;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tparameters: bashSchema,\n\t\tmaxResultSizeChars: Infinity,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst policyDecision = evaluateBashCommandPolicy(command, policy);\n\t\t\tif (!policyDecision.allowed) {\n\t\t\t\tthrow new Error(formatBashCommandPolicyRejection(policyDecision, policyLabel));\n\t\t\t}\n\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: `${APP_NAME}-bash` });\n\t\t\tlet acceptingOutput = true;\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\tif (!acceptingOutput) return;\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\tacceptingOutput = false;\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: Awaited<ReturnType<typeof finishOutput>>, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\tconst appendStatus = (text: string, status: string) => `${text ? `${text}\\n\\n` : \"\"}${status}`;\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new Error(appendStatus(text, \"Command aborted\"));\n\t\t\t\t\t}\n\t\t\t\t\tif (err instanceof Error && err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\tthrow new Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatBashCall(args));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAa5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAItF,OAAO,EAAoD,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAExG,QAAA,MAAM,UAAU;;;EAGd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACxB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAiE1F;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;AAO5E,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,aAAa,CAAC;CAC1B;AAKD,KAAK,eAAe,GAAG;IACtB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC;CACrC,CAAC;AAoHF,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,eAAe,CAAC,CA8KjF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Container, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { spawn } from \"child_process\";\nimport { type Static, Type } from \"typebox\";\nimport { APP_NAME } from \"../../config.ts\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { OutputAccumulator } from \"./output-accumulator.ts\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst shellConfig = getShellConfig(options?.shellPath);\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`);\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeout * 1000);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\tconst exitCode = await waitForChildProcess(child);\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t\t}\n\t\t\t\tif (timedOut) {\n\t\t\t\t\tthrow new Error(`timeout:${timeout}`);\n\t\t\t\t}\n\t\t\t\treturn { exitCode };\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {\n\tconst baseContext: BashSpawnContext = { command, cwd, env: { ...getShellEnv() } };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Optional explicit shell path from settings */\n\tshellPath?: string;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\tconst totalSeconds = Math.floor(Math.max(0, ms) / 1000);\n\tconst hours = Math.floor(totalSeconds / 3600);\n\tconst minutes = Math.floor((totalSeconds % 3600) / 60);\n\tconst seconds = totalSeconds % 60;\n\tif (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;\n\tif (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n\treturn `${seconds}s`;\n}\n\nfunction formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\n\tconst timeout = args?.timeout as number | undefined;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn theme.fg(\"toolTitle\", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tlet output = getTextOutput(result, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"Expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tparameters: bashSchema,\n\t\tmaxResultSizeChars: Infinity,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\tbashCommand: BashToolInput,\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst { command, timeout } = bashCommand;\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: `${APP_NAME}-bash` });\n\t\t\tlet acceptingOutput = true;\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\tif (!acceptingOutput) return;\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\tacceptingOutput = false;\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: Awaited<ReturnType<typeof finishOutput>>, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\tconst appendStatus = (text: string, status: string) => `${text ? `${text}\\n\\n` : \"\"}${status}`;\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new Error(appendStatus(text, \"Command aborted\"));\n\t\t\t\t\t}\n\t\t\t\t\tif (err instanceof Error && err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\tthrow new Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatBashCall(args));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n"]}
@@ -9,7 +9,6 @@ import { truncateToVisualLines } from "../../modes/interactive/components/visual
9
9
  import { theme } from "../../modes/interactive/theme/theme.js";
10
10
  import { waitForChildProcess } from "../../utils/child-process.js";
11
11
  import { getShellConfig, getShellEnv, killProcessTree, trackDetachedChildPid, untrackDetachedChildPid, } from "../../utils/shell.js";
12
- import { evaluateBashCommandPolicy, formatBashCommandPolicyRejection, } from "./bash-policy.js";
13
12
  import { OutputAccumulator } from "./output-accumulator.js";
14
13
  import { getTextOutput, invalidArgText, str } from "./render-utils.js";
15
14
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
@@ -27,7 +26,7 @@ const bashSchema = Type.Object({
27
26
  export function createLocalBashOperations(options) {
28
27
  return {
29
28
  exec: async (command, cwd, { onData, signal, timeout, env }) => {
30
- const { shell, args } = getShellConfig(options?.shellPath);
29
+ const shellConfig = getShellConfig(options?.shellPath);
31
30
  try {
32
31
  await fsAccess(cwd, constants.F_OK);
33
32
  }
@@ -37,13 +36,18 @@ export function createLocalBashOperations(options) {
37
36
  if (signal?.aborted) {
38
37
  throw new Error("aborted");
39
38
  }
40
- const child = spawn(shell, [...args, command], {
39
+ const commandFromStdin = shellConfig.commandTransport === "stdin";
40
+ const child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {
41
41
  cwd,
42
42
  detached: process.platform !== "win32",
43
43
  env: env ?? getShellEnv(),
44
- stdio: ["ignore", "pipe", "pipe"],
44
+ stdio: [commandFromStdin ? "pipe" : "ignore", "pipe", "pipe"],
45
45
  windowsHide: true,
46
46
  });
47
+ if (commandFromStdin) {
48
+ child.stdin?.on("error", () => { });
49
+ child.stdin?.end(command);
50
+ }
47
51
  if (child.pid)
48
52
  trackDetachedChildPid(child.pid);
49
53
  let timedOut = false;
@@ -196,8 +200,6 @@ export function createBashToolDefinition(cwd, options) {
196
200
  const ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });
197
201
  const commandPrefix = options?.commandPrefix;
198
202
  const spawnHook = options?.spawnHook;
199
- const policy = options?.policy;
200
- const policyLabel = options?.policyLabel;
201
203
  return {
202
204
  name: "bash",
203
205
  label: "bash",
@@ -205,11 +207,8 @@ export function createBashToolDefinition(cwd, options) {
205
207
  promptSnippet: "Execute bash commands (ls, grep, find, etc.)",
206
208
  parameters: bashSchema,
207
209
  maxResultSizeChars: Infinity,
208
- async execute(_toolCallId, { command, timeout }, signal, onUpdate, _ctx) {
209
- const policyDecision = evaluateBashCommandPolicy(command, policy);
210
- if (!policyDecision.allowed) {
211
- throw new Error(formatBashCommandPolicyRejection(policyDecision, policyLabel));
212
- }
210
+ async execute(_toolCallId, bashCommand, signal, onUpdate, _ctx) {
211
+ const { command, timeout } = bashCommand;
213
212
  const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
214
213
  const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);
215
214
  const output = new OutputAccumulator({ tempFilePrefix: `${APP_NAME}-bash` });
@@ -1 +1 @@
1
- {"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,wDAAwD,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uDAAuD,CAAC;AAC9F,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EACN,cAAc,EACd,WAAW,EACX,eAAe,EACf,qBAAqB,EACrB,uBAAuB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,yBAAyB,EACzB,gCAAgC,GAEhC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,EAAyB,MAAM,eAAe,CAAC;AAExG,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;IAC9B,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;IAChE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC,CAAC;CACzG,CAAC,CAAC;AAiCH;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAgC;IACzE,OAAO;QACN,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE;YAC9D,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC3D,IAAI,CAAC;gBACJ,MAAM,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;YAAC,MAAM,CAAC;gBACR,MAAM,IAAI,KAAK,CAAC,qCAAqC,GAAG,iCAAiC,CAAC,CAAC;YAC5F,CAAC;YACD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE;gBAC9C,GAAG;gBACH,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;gBACtC,GAAG,EAAE,GAAG,IAAI,WAAW,EAAE;gBACzB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;gBACjC,WAAW,EAAE,IAAI;aACjB,CAAC,CAAC;YACH,IAAI,KAAK,CAAC,GAAG;gBAAE,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,IAAI,aAAyC,CAAC;YAC9C,MAAM,OAAO,GAAG,GAAG,EAAE;gBACpB,IAAI,KAAK,CAAC,GAAG;oBAAE,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC3C,CAAC,CAAC;YAEF,IAAI,CAAC;gBACJ,2BAA2B;gBAC3B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAC1C,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;wBAC/B,QAAQ,GAAG,IAAI,CAAC;wBAChB,IAAI,KAAK,CAAC,GAAG;4BAAE,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAC3C,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC;gBACpB,CAAC;gBACD,4BAA4B;gBAC5B,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBACjC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBACjC,0DAA0D;gBAC1D,IAAI,MAAM,EAAE,CAAC;oBACZ,IAAI,MAAM,CAAC,OAAO;wBAAE,OAAO,EAAE,CAAC;;wBACzB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChE,CAAC;gBACD,kFAAkF;gBAClF,2DAA2D;gBAC3D,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC;gBAClD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;oBACrB,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;gBAC5B,CAAC;gBACD,IAAI,QAAQ,EAAE,CAAC;oBACd,MAAM,IAAI,KAAK,CAAC,WAAW,OAAO,EAAE,CAAC,CAAC;gBACvC,CAAC;gBACD,OAAO,EAAE,QAAQ,EAAE,CAAC;YACrB,CAAC;oBAAS,CAAC;gBACV,IAAI,KAAK,CAAC,GAAG;oBAAE,uBAAuB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAClD,IAAI,aAAa;oBAAE,YAAY,CAAC,aAAa,CAAC,CAAC;gBAC/C,IAAI,MAAM;oBAAE,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC1D,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC;AAUD,SAAS,mBAAmB,CAAC,OAAe,EAAE,GAAW,EAAE,SAAyB;IACnF,MAAM,WAAW,GAAqB,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,GAAG,WAAW,EAAE,EAAE,EAAE,CAAC;IAClF,OAAO,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;AACzD,CAAC;AAiBD,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAC7B,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAcpC,MAAM,yBAA0B,SAAQ,SAAS;IAAjD;;QACC,UAAK,GAA0B;YAC9B,WAAW,EAAE,SAAS;YACtB,WAAW,EAAE,SAAS;YACtB,aAAa,EAAE,SAAS;SACxB,CAAC;IACH,CAAC;CAAA;AAED,SAAS,cAAc,CAAC,EAAU;IACjC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC;IAC1E,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC;IAChF,OAAO,GAAG,OAAO,GAAG,CAAC;AACtB,CAAC;AAED,SAAS,cAAc,CAAC,IAAwD;IAC/E,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,EAAE,OAA6B,CAAC;IACpD,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,MAAM,cAAc,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IACpH,OAAO,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,cAAc,EAAE,CAAC,CAAC,GAAG,aAAa,CAAC;AACjF,CAAC;AAED,SAAS,gCAAgC,CACxC,SAAoC,EACpC,MAGC,EACD,OAAgC,EAChC,UAAmB,EACnB,SAA6B,EAC7B,OAA2B;IAE3B,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;IAC9B,SAAS,CAAC,KAAK,EAAE,CAAC;IAElB,IAAI,MAAM,GAAG,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC;IAC9C,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC;IACtD,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,UAAU,EAAE,SAAS,IAAI,cAAc,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3F,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,WAAW,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YAC9E,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;QACjD,CAAC;IACF,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACZ,MAAM,YAAY,GAAG,MAAM;aACzB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;aAC3C,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACtB,SAAS,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACP,SAAS,CAAC,QAAQ,CAAC;gBAClB,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;oBACzB,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,KAAK,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;wBACpE,MAAM,OAAO,GAAG,qBAAqB,CAAC,YAAY,EAAE,kBAAkB,EAAE,KAAK,CAAC,CAAC;wBAC/E,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;wBACxC,KAAK,CAAC,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;wBAC3C,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC;oBAC3B,CAAC;oBACD,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;wBACpD,MAAM,IAAI,GACT,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,CAAC,aAAa,iBAAiB,CAAC;4BAC/D,IAAI,OAAO,CAAC,kBAAkB,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;wBACtE,OAAO,CAAC,EAAE,EAAE,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC;oBAChF,CAAC;oBACD,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC3C,CAAC;gBACD,UAAU,EAAE,GAAG,EAAE;oBAChB,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC;oBAC9B,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC;oBAC9B,KAAK,CAAC,aAAa,GAAG,SAAS,CAAC;gBACjC,CAAC;aACD,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,IAAI,UAAU,EAAE,SAAS,IAAI,cAAc,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,IAAI,cAAc,EAAE,CAAC;YACpB,QAAQ,CAAC,IAAI,CAAC,gBAAgB,cAAc,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,UAAU,EAAE,SAAS,EAAE,CAAC;YAC3B,IAAI,UAAU,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;gBACxC,QAAQ,CAAC,IAAI,CAAC,sBAAsB,UAAU,CAAC,WAAW,OAAO,UAAU,CAAC,UAAU,QAAQ,CAAC,CAAC;YACjG,CAAC;iBAAM,CAAC;gBACP,QAAQ,CAAC,IAAI,CACZ,cAAc,UAAU,CAAC,WAAW,iBAAiB,UAAU,CAAC,UAAU,CAAC,QAAQ,IAAI,iBAAiB,CAAC,SAAS,CAClH,CAAC;YACH,CAAC;QACF,CAAC;QACD,SAAS,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QACrD,MAAM,OAAO,GAAG,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACtC,SAAS,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,cAAc,CAAC,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjH,CAAC;AACF,CAAC;AAED,MAAM,UAAU,wBAAwB,CACvC,GAAW,EACX,OAAyB;IAEzB,MAAM,GAAG,GAAG,OAAO,EAAE,UAAU,IAAI,yBAAyB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAChG,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,CAAC;IAC7C,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,CAAC;IACrC,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;IAC/B,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,CAAC;IACzC,OAAO;QACN,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,mHAAmH,iBAAiB,aAAa,iBAAiB,GAAG,IAAI,0HAA0H;QAChT,aAAa,EAAE,8CAA8C;QAC7D,UAAU,EAAE,UAAU;QACtB,kBAAkB,EAAE,QAAQ;QAC5B,KAAK,CAAC,OAAO,CACZ,WAAW,EACX,EAAE,OAAO,EAAE,OAAO,EAAyC,EAC3D,MAAoB,EACpB,QAAS,EACT,IAAK;YAEL,MAAM,cAAc,GAAG,yBAAyB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAClE,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;YAChF,CAAC;YAED,MAAM,eAAe,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACjF,MAAM,YAAY,GAAG,mBAAmB,CAAC,eAAe,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;YAC1E,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,cAAc,EAAE,GAAG,QAAQ,OAAO,EAAE,CAAC,CAAC;YAC7E,IAAI,eAAe,GAAG,IAAI,CAAC;YAC3B,IAAI,WAAuC,CAAC;YAC5C,IAAI,WAAW,GAAG,KAAK,CAAC;YACxB,IAAI,YAAY,GAAG,CAAC,CAAC;YAErB,MAAM,gBAAgB,GAAG,GAAG,EAAE;gBAC7B,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW;oBAAE,OAAO;gBACtC,WAAW,GAAG,KAAK,CAAC;gBACpB,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/D,QAAQ,CAAC;oBACR,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;oBACzD,OAAO,EAAE;wBACR,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;wBAC3E,cAAc,EAAE,QAAQ,CAAC,cAAc;qBACvC;iBACD,CAAC,CAAC;YACJ,CAAC,CAAC;YAEF,MAAM,gBAAgB,GAAG,GAAG,EAAE;gBAC7B,IAAI,WAAW,EAAE,CAAC;oBACjB,YAAY,CAAC,WAAW,CAAC,CAAC;oBAC1B,WAAW,GAAG,SAAS,CAAC;gBACzB,CAAC;YACF,CAAC,CAAC;YAEF,MAAM,oBAAoB,GAAG,GAAG,EAAE;gBACjC,IAAI,CAAC,QAAQ;oBAAE,OAAO;gBACtB,WAAW,GAAG,IAAI,CAAC;gBACnB,MAAM,KAAK,GAAG,uBAAuB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,CAAC;gBACpE,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;oBAChB,gBAAgB,EAAE,CAAC;oBACnB,gBAAgB,EAAE,CAAC;oBACnB,OAAO;gBACR,CAAC;gBACD,WAAW,KAAK,UAAU,CAAC,GAAG,EAAE;oBAC/B,WAAW,GAAG,SAAS,CAAC;oBACxB,gBAAgB,EAAE,CAAC;gBACpB,CAAC,EAAE,KAAK,CAAC,CAAC;YACX,CAAC,CAAC;YAEF,IAAI,QAAQ,EAAE,CAAC;gBACd,QAAQ,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;YAC/C,CAAC;YAED,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;gBACnC,IAAI,CAAC,eAAe;oBAAE,OAAO;gBAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACpB,oBAAoB,EAAE,CAAC;YACxB,CAAC,CAAC;YAEF,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE;gBAC/B,eAAe,GAAG,KAAK,CAAC;gBACxB,MAAM,CAAC,MAAM,EAAE,CAAC;gBAChB,gBAAgB,EAAE,CAAC;gBACnB,gBAAgB,EAAE,CAAC;gBACnB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/D,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;gBAC7B,OAAO,QAAQ,CAAC;YACjB,CAAC,CAAC;YAEF,MAAM,YAAY,GAAG,CAAC,QAAkD,EAAE,SAAS,GAAG,aAAa,EAAE,EAAE;gBACtG,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;gBACvC,IAAI,IAAI,GAAG,QAAQ,CAAC,OAAO,IAAI,SAAS,CAAC;gBACzC,IAAI,OAAoC,CAAC;gBACzC,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;oBAC1B,OAAO,GAAG,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,CAAC;oBAClE,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,WAAW,GAAG,CAAC,CAAC;oBACrE,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC;oBACtC,IAAI,UAAU,CAAC,eAAe,EAAE,CAAC;wBAChC,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;wBAC3D,IAAI,IAAI,qBAAqB,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,OAAO,aAAa,YAAY,mBAAmB,QAAQ,CAAC,cAAc,GAAG,CAAC;oBAC1J,CAAC;yBAAM,IAAI,UAAU,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;wBAC/C,IAAI,IAAI,sBAAsB,SAAS,IAAI,OAAO,OAAO,UAAU,CAAC,UAAU,kBAAkB,QAAQ,CAAC,cAAc,GAAG,CAAC;oBAC5H,CAAC;yBAAM,CAAC;wBACP,IAAI,IAAI,sBAAsB,SAAS,IAAI,OAAO,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU,CAAC,iBAAiB,CAAC,yBAAyB,QAAQ,CAAC,cAAc,GAAG,CAAC;oBACrK,CAAC;gBACF,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAC1B,CAAC,CAAC;YAEF,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,MAAc,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC;YAE/F,IAAI,CAAC;gBACJ,IAAI,QAAuB,CAAC;gBAC5B,IAAI,CAAC;oBACJ,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,EAAE;wBACrE,MAAM,EAAE,UAAU;wBAClB,MAAM;wBACN,OAAO;wBACP,GAAG,EAAE,YAAY,CAAC,GAAG;qBACrB,CAAC,CAAC;oBACH,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;gBAC5B,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACd,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;oBACtC,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;oBAC5C,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;wBACvD,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC;oBACxD,CAAC;oBACD,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;wBAChE,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC9C,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,2BAA2B,WAAW,UAAU,CAAC,CAAC,CAAC;oBACvF,CAAC;oBACD,MAAM,GAAG,CAAC;gBACX,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;gBACtC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAC7D,IAAI,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;oBACzC,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,4BAA4B,QAAQ,EAAE,CAAC,CAAC,CAAC;gBACnF,CAAC;gBACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;YACnE,CAAC;oBAAS,CAAC;gBACV,gBAAgB,EAAE,CAAC;YACpB,CAAC;QACF,CAAC;QACD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO;YAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;YAC5B,IAAI,OAAO,CAAC,gBAAgB,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC/D,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC7B,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;YAC3B,CAAC;YACD,MAAM,IAAI,GAAI,OAAO,CAAC,aAAkC,IAAI,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/E,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;YACnC,OAAO,IAAI,CAAC;QACb,CAAC;QACD,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO;YAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;YAC5B,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBAC3E,KAAK,CAAC,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC;YAChE,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC3C,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC7B,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACpB,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAC9B,KAAK,CAAC,QAAQ,GAAG,SAAS,CAAC;gBAC5B,CAAC;YACF,CAAC;YACD,MAAM,SAAS,GACb,OAAO,CAAC,aAAuD,IAAI,IAAI,yBAAyB,EAAE,CAAC;YACrG,gCAAgC,CAC/B,SAAS,EACT,MAAM,EACN,OAAO,EACP,OAAO,CAAC,UAAU,EAClB,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,OAAO,CACb,CAAC;YACF,SAAS,CAAC,UAAU,EAAE,CAAC;YACvB,OAAO,SAAS,CAAC;QAClB,CAAC;KACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,OAAyB;IACpE,OAAO,kBAAkB,CAAC,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AACnE,CAAC","sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Container, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { spawn } from \"child_process\";\nimport { type Static, Type } from \"typebox\";\nimport { APP_NAME } from \"../../config.ts\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tevaluateBashCommandPolicy,\n\tformatBashCommandPolicyRejection,\n\ttype BashCommandPolicy,\n} from \"./bash-policy.ts\";\nimport { OutputAccumulator } from \"./output-accumulator.ts\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst { shell, args } = getShellConfig(options?.shellPath);\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`);\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\n\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeout * 1000);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\tconst exitCode = await waitForChildProcess(child);\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t\t}\n\t\t\t\tif (timedOut) {\n\t\t\t\t\tthrow new Error(`timeout:${timeout}`);\n\t\t\t\t}\n\t\t\t\treturn { exitCode };\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {\n\tconst baseContext: BashSpawnContext = { command, cwd, env: { ...getShellEnv() } };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Optional explicit shell path from settings */\n\tshellPath?: string;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n\t/** Optional command-level policy enforced before commandPrefix, spawnHook, or execution */\n\tpolicy?: BashCommandPolicy;\n\t/** Human-readable label used in policy denial errors */\n\tpolicyLabel?: string;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\tconst totalSeconds = Math.floor(Math.max(0, ms) / 1000);\n\tconst hours = Math.floor(totalSeconds / 3600);\n\tconst minutes = Math.floor((totalSeconds % 3600) / 60);\n\tconst seconds = totalSeconds % 60;\n\tif (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;\n\tif (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n\treturn `${seconds}s`;\n}\n\nfunction formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\n\tconst timeout = args?.timeout as number | undefined;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn theme.fg(\"toolTitle\", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tlet output = getTextOutput(result, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"Expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst spawnHook = options?.spawnHook;\n\tconst policy = options?.policy;\n\tconst policyLabel = options?.policyLabel;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tparameters: bashSchema,\n\t\tmaxResultSizeChars: Infinity,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst policyDecision = evaluateBashCommandPolicy(command, policy);\n\t\t\tif (!policyDecision.allowed) {\n\t\t\t\tthrow new Error(formatBashCommandPolicyRejection(policyDecision, policyLabel));\n\t\t\t}\n\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: `${APP_NAME}-bash` });\n\t\t\tlet acceptingOutput = true;\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\tif (!acceptingOutput) return;\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\tacceptingOutput = false;\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: Awaited<ReturnType<typeof finishOutput>>, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\tconst appendStatus = (text: string, status: string) => `${text ? `${text}\\n\\n` : \"\"}${status}`;\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new Error(appendStatus(text, \"Command aborted\"));\n\t\t\t\t\t}\n\t\t\t\t\tif (err instanceof Error && err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\tthrow new Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatBashCall(args));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Container, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { spawn } from \"child_process\";\nimport { type Static, Type } from \"typebox\";\nimport { APP_NAME } from \"../../config.ts\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { OutputAccumulator } from \"./output-accumulator.ts\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst shellConfig = getShellConfig(options?.shellPath);\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`);\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeout * 1000);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\tconst exitCode = await waitForChildProcess(child);\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t\t}\n\t\t\t\tif (timedOut) {\n\t\t\t\t\tthrow new Error(`timeout:${timeout}`);\n\t\t\t\t}\n\t\t\t\treturn { exitCode };\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {\n\tconst baseContext: BashSpawnContext = { command, cwd, env: { ...getShellEnv() } };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Optional explicit shell path from settings */\n\tshellPath?: string;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\tconst totalSeconds = Math.floor(Math.max(0, ms) / 1000);\n\tconst hours = Math.floor(totalSeconds / 3600);\n\tconst minutes = Math.floor((totalSeconds % 3600) / 60);\n\tconst seconds = totalSeconds % 60;\n\tif (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;\n\tif (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n\treturn `${seconds}s`;\n}\n\nfunction formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\n\tconst timeout = args?.timeout as number | undefined;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn theme.fg(\"toolTitle\", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tlet output = getTextOutput(result, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"Expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tparameters: bashSchema,\n\t\tmaxResultSizeChars: Infinity,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\tbashCommand: BashToolInput,\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst { command, timeout } = bashCommand;\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: `${APP_NAME}-bash` });\n\t\t\tlet acceptingOutput = true;\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\tif (!acceptingOutput) return;\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\tacceptingOutput = false;\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: Awaited<ReturnType<typeof finishOutput>>, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\tconst appendStatus = (text: string, status: string) => `${text ? `${text}\\n\\n` : \"\"}${status}`;\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new Error(appendStatus(text, \"Command aborted\"));\n\t\t\t\t\t}\n\t\t\t\t\tif (err instanceof Error && err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\tthrow new Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatBashCall(args));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n"]}
@@ -0,0 +1,18 @@
1
+ export interface TextReplacement {
2
+ matchIndex: number;
3
+ matchLength: number;
4
+ newText: string;
5
+ }
6
+ export declare function applyReplacements(content: string, replacements: TextReplacement[], offset?: number): string;
7
+ /**
8
+ * Apply replacements matched against `baseContent` to `originalContent` while
9
+ * preserving unchanged line blocks from the original.
10
+ *
11
+ * This is useful when `baseContent` is a normalized view of the original. Each
12
+ * replacement is widened to the lines it actually touches, those touched lines
13
+ * are rewritten from the normalized base, and all other lines are copied back
14
+ * from `originalContent`. The actual replacement ranges drive preservation so
15
+ * duplicate normalized lines cannot be aligned to the wrong occurrence.
16
+ */
17
+ export declare function applyReplacementsPreservingUnchangedLines(originalContent: string, baseContent: string, replacements: TextReplacement[]): string;
18
+ //# sourceMappingURL=edit-diff-preserve.d.ts.map