@caupulican/pi-adaptative 0.81.37 → 0.81.39

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 (451) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +1 -1
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +18 -1
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +131 -0
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -0
  6. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +196 -0
  7. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -0
  8. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +308 -0
  9. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +22 -2
  10. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
  11. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +585 -24
  12. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
  13. package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +749 -27
  14. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/__init__.py +43 -0
  15. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/fs.py +270 -0
  16. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/search.py +252 -0
  17. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/strings.py +399 -0
  18. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +575 -0
  19. package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +52 -0
  20. package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +49 -0
  21. package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +734 -0
  22. package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +238 -0
  23. package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +132 -0
  24. package/dist/bundled-resources/runtimes/pi-shell-engine/nodes.py +116 -0
  25. package/dist/bundled-resources/runtimes/pi-shell-engine/parser.py +287 -0
  26. package/dist/bundled-resources/runtimes/pi-shell-engine/proc.py +137 -0
  27. package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +100 -0
  28. package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +579 -0
  29. package/dist/bundled-resources/skills/tool-call-repair/SKILL.md +19 -9
  30. package/dist/bundled-resources/skills/tool-call-repair/references/failure-grammar.md +22 -5
  31. package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +5 -5
  32. package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
  33. package/dist/cli/args.d.ts +3 -0
  34. package/dist/cli/args.d.ts.map +1 -1
  35. package/dist/cli/args.js +15 -0
  36. package/dist/cli/args.js.map +1 -1
  37. package/dist/core/agent-paths.d.ts +49 -0
  38. package/dist/core/agent-paths.d.ts.map +1 -0
  39. package/dist/core/agent-paths.js +107 -0
  40. package/dist/core/agent-paths.js.map +1 -0
  41. package/dist/core/agent-session-services.d.ts.map +1 -1
  42. package/dist/core/agent-session-services.js +3 -3
  43. package/dist/core/agent-session-services.js.map +1 -1
  44. package/dist/core/agent-session.d.ts +234 -17
  45. package/dist/core/agent-session.d.ts.map +1 -1
  46. package/dist/core/agent-session.js +484 -62
  47. package/dist/core/agent-session.js.map +1 -1
  48. package/dist/core/autonomy/contracts.d.ts +9 -0
  49. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  50. package/dist/core/autonomy/contracts.js.map +1 -1
  51. package/dist/core/autonomy/lane-tracker.d.ts +10 -1
  52. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  53. package/dist/core/autonomy/lane-tracker.js +5 -1
  54. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  55. package/dist/core/background-lane-controller.d.ts +122 -6
  56. package/dist/core/background-lane-controller.d.ts.map +1 -1
  57. package/dist/core/background-lane-controller.js +447 -90
  58. package/dist/core/background-lane-controller.js.map +1 -1
  59. package/dist/core/bash-execution-controller.d.ts +4 -0
  60. package/dist/core/bash-execution-controller.d.ts.map +1 -1
  61. package/dist/core/bash-execution-controller.js +7 -1
  62. package/dist/core/bash-execution-controller.js.map +1 -1
  63. package/dist/core/compaction-support.d.ts +13 -3
  64. package/dist/core/compaction-support.d.ts.map +1 -1
  65. package/dist/core/compaction-support.js +43 -7
  66. package/dist/core/compaction-support.js.map +1 -1
  67. package/dist/core/context/context-audit.d.ts +33 -1
  68. package/dist/core/context/context-audit.d.ts.map +1 -1
  69. package/dist/core/context/context-audit.js +31 -5
  70. package/dist/core/context/context-audit.js.map +1 -1
  71. package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
  72. package/dist/core/context/sqlite-runtime-index.js +13 -7
  73. package/dist/core/context/sqlite-runtime-index.js.map +1 -1
  74. package/dist/core/context-gc.d.ts.map +1 -1
  75. package/dist/core/context-gc.js +16 -1
  76. package/dist/core/context-gc.js.map +1 -1
  77. package/dist/core/context-pipeline.d.ts +26 -4
  78. package/dist/core/context-pipeline.d.ts.map +1 -1
  79. package/dist/core/context-pipeline.js +137 -14
  80. package/dist/core/context-pipeline.js.map +1 -1
  81. package/dist/core/cost-guard.d.ts +19 -3
  82. package/dist/core/cost-guard.d.ts.map +1 -1
  83. package/dist/core/cost-guard.js +18 -3
  84. package/dist/core/cost-guard.js.map +1 -1
  85. package/dist/core/delegation/session-worker-result.d.ts +34 -4
  86. package/dist/core/delegation/session-worker-result.d.ts.map +1 -1
  87. package/dist/core/delegation/session-worker-result.js +64 -4
  88. package/dist/core/delegation/session-worker-result.js.map +1 -1
  89. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  90. package/dist/core/delegation/worker-actions.js +1 -1
  91. package/dist/core/delegation/worker-actions.js.map +1 -1
  92. package/dist/core/delegation/worker-result.d.ts +42 -1
  93. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  94. package/dist/core/delegation/worker-result.js +68 -0
  95. package/dist/core/delegation/worker-result.js.map +1 -1
  96. package/dist/core/extensions/loader.d.ts.map +1 -1
  97. package/dist/core/extensions/loader.js +23 -7
  98. package/dist/core/extensions/loader.js.map +1 -1
  99. package/dist/core/extensions/runner.d.ts.map +1 -1
  100. package/dist/core/extensions/runner.js +1 -0
  101. package/dist/core/extensions/runner.js.map +1 -1
  102. package/dist/core/extensions/types.d.ts +51 -0
  103. package/dist/core/extensions/types.d.ts.map +1 -1
  104. package/dist/core/extensions/types.js.map +1 -1
  105. package/dist/core/goal-loop-controller.d.ts +17 -1
  106. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  107. package/dist/core/goal-loop-controller.js +79 -11
  108. package/dist/core/goal-loop-controller.js.map +1 -1
  109. package/dist/core/goals/goal-continuation-controller.d.ts +48 -2
  110. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
  111. package/dist/core/goals/goal-continuation-controller.js +77 -0
  112. package/dist/core/goals/goal-continuation-controller.js.map +1 -1
  113. package/dist/core/goals/goal-continuation-defaults.d.ts +39 -0
  114. package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
  115. package/dist/core/goals/goal-continuation-defaults.js +42 -0
  116. package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
  117. package/dist/core/goals/goal-continuation-prompt.d.ts +4 -0
  118. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  119. package/dist/core/goals/goal-continuation-prompt.js +51 -10
  120. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  121. package/dist/core/goals/goal-runtime-snapshot.d.ts +101 -2
  122. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
  123. package/dist/core/goals/goal-runtime-snapshot.js +87 -4
  124. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
  125. package/dist/core/goals/goal-state.d.ts +89 -1
  126. package/dist/core/goals/goal-state.d.ts.map +1 -1
  127. package/dist/core/goals/goal-state.js +71 -5
  128. package/dist/core/goals/goal-state.js.map +1 -1
  129. package/dist/core/goals/goal-tool-core.d.ts +56 -2
  130. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  131. package/dist/core/goals/goal-tool-core.js +111 -5
  132. package/dist/core/goals/goal-tool-core.js.map +1 -1
  133. package/dist/core/goals/session-goal-state.d.ts +11 -2
  134. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  135. package/dist/core/goals/session-goal-state.js +32 -17
  136. package/dist/core/goals/session-goal-state.js.map +1 -1
  137. package/dist/core/keybindings.d.ts +10 -0
  138. package/dist/core/keybindings.d.ts.map +1 -1
  139. package/dist/core/keybindings.js +10 -2
  140. package/dist/core/keybindings.js.map +1 -1
  141. package/dist/core/learning/observation-store.d.ts +12 -3
  142. package/dist/core/learning/observation-store.d.ts.map +1 -1
  143. package/dist/core/learning/observation-store.js +30 -15
  144. package/dist/core/learning/observation-store.js.map +1 -1
  145. package/dist/core/learning/skill-curator.d.ts +5 -1
  146. package/dist/core/learning/skill-curator.d.ts.map +1 -1
  147. package/dist/core/learning/skill-curator.js +21 -19
  148. package/dist/core/learning/skill-curator.js.map +1 -1
  149. package/dist/core/local-runtime-controller.d.ts +65 -3
  150. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  151. package/dist/core/local-runtime-controller.js +186 -26
  152. package/dist/core/local-runtime-controller.js.map +1 -1
  153. package/dist/core/memory/providers/file-store.d.ts +1 -1
  154. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  155. package/dist/core/memory/providers/file-store.js +6 -6
  156. package/dist/core/memory/providers/file-store.js.map +1 -1
  157. package/dist/core/model-capability.d.ts +34 -0
  158. package/dist/core/model-capability.d.ts.map +1 -1
  159. package/dist/core/model-capability.js +42 -1
  160. package/dist/core/model-capability.js.map +1 -1
  161. package/dist/core/model-router/tool-escalation.d.ts +15 -0
  162. package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
  163. package/dist/core/model-router/tool-escalation.js +23 -1
  164. package/dist/core/model-router/tool-escalation.js.map +1 -1
  165. package/dist/core/model-router-controller.d.ts +34 -7
  166. package/dist/core/model-router-controller.d.ts.map +1 -1
  167. package/dist/core/model-router-controller.js +95 -16
  168. package/dist/core/model-router-controller.js.map +1 -1
  169. package/dist/core/models/adaptation-store.d.ts +7 -0
  170. package/dist/core/models/adaptation-store.d.ts.map +1 -1
  171. package/dist/core/models/adaptation-store.js +47 -11
  172. package/dist/core/models/adaptation-store.js.map +1 -1
  173. package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
  174. package/dist/core/models/default-model-suggestions.js +17 -0
  175. package/dist/core/models/default-model-suggestions.js.map +1 -1
  176. package/dist/core/models/fitness-store.d.ts +3 -0
  177. package/dist/core/models/fitness-store.d.ts.map +1 -1
  178. package/dist/core/models/fitness-store.js +11 -2
  179. package/dist/core/models/fitness-store.js.map +1 -1
  180. package/dist/core/models/llamacpp-runtime.d.ts +180 -0
  181. package/dist/core/models/llamacpp-runtime.d.ts.map +1 -0
  182. package/dist/core/models/llamacpp-runtime.js +475 -0
  183. package/dist/core/models/llamacpp-runtime.js.map +1 -0
  184. package/dist/core/models/local-registration.d.ts +40 -0
  185. package/dist/core/models/local-registration.d.ts.map +1 -1
  186. package/dist/core/models/local-registration.js +94 -5
  187. package/dist/core/models/local-registration.js.map +1 -1
  188. package/dist/core/models/local-runtime.d.ts.map +1 -1
  189. package/dist/core/models/local-runtime.js +9 -7
  190. package/dist/core/models/local-runtime.js.map +1 -1
  191. package/dist/core/models/model-ref.d.ts +7 -0
  192. package/dist/core/models/model-ref.d.ts.map +1 -1
  193. package/dist/core/models/model-ref.js +26 -0
  194. package/dist/core/models/model-ref.js.map +1 -1
  195. package/dist/core/models/needle-runtime.d.ts +257 -0
  196. package/dist/core/models/needle-runtime.d.ts.map +1 -0
  197. package/dist/core/models/needle-runtime.js +519 -0
  198. package/dist/core/models/needle-runtime.js.map +1 -0
  199. package/dist/core/models/prism-llamacpp-lifecycle.d.ts +89 -0
  200. package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -0
  201. package/dist/core/models/prism-llamacpp-lifecycle.js +121 -0
  202. package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -0
  203. package/dist/core/package-manager.d.ts.map +1 -1
  204. package/dist/core/package-manager.js +11 -10
  205. package/dist/core/package-manager.js.map +1 -1
  206. package/dist/core/process-matrix/codes.d.ts +72 -0
  207. package/dist/core/process-matrix/codes.d.ts.map +1 -0
  208. package/dist/core/process-matrix/codes.js +15 -0
  209. package/dist/core/process-matrix/codes.js.map +1 -0
  210. package/dist/core/process-matrix/runtime.d.ts +63 -0
  211. package/dist/core/process-matrix/runtime.d.ts.map +1 -0
  212. package/dist/core/process-matrix/runtime.js +310 -0
  213. package/dist/core/process-matrix/runtime.js.map +1 -0
  214. package/dist/core/process-matrix/store.d.ts +22 -0
  215. package/dist/core/process-matrix/store.d.ts.map +1 -0
  216. package/dist/core/process-matrix/store.js +80 -0
  217. package/dist/core/process-matrix/store.js.map +1 -0
  218. package/dist/core/process-matrix/supervisor.d.ts +72 -0
  219. package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
  220. package/dist/core/process-matrix/supervisor.js +130 -0
  221. package/dist/core/process-matrix/supervisor.js.map +1 -0
  222. package/dist/core/profile-registry.d.ts +7 -0
  223. package/dist/core/profile-registry.d.ts.map +1 -1
  224. package/dist/core/profile-registry.js +20 -0
  225. package/dist/core/profile-registry.js.map +1 -1
  226. package/dist/core/python-runtime.d.ts.map +1 -1
  227. package/dist/core/python-runtime.js +3 -3
  228. package/dist/core/python-runtime.js.map +1 -1
  229. package/dist/core/reflection-controller.d.ts +29 -5
  230. package/dist/core/reflection-controller.d.ts.map +1 -1
  231. package/dist/core/reflection-controller.js +215 -126
  232. package/dist/core/reflection-controller.js.map +1 -1
  233. package/dist/core/reload-blockers.d.ts +36 -0
  234. package/dist/core/reload-blockers.d.ts.map +1 -1
  235. package/dist/core/reload-blockers.js +44 -0
  236. package/dist/core/reload-blockers.js.map +1 -1
  237. package/dist/core/resource-loader.d.ts.map +1 -1
  238. package/dist/core/resource-loader.js +8 -7
  239. package/dist/core/resource-loader.js.map +1 -1
  240. package/dist/core/runtime-builder.d.ts +58 -5
  241. package/dist/core/runtime-builder.d.ts.map +1 -1
  242. package/dist/core/runtime-builder.js +209 -25
  243. package/dist/core/runtime-builder.js.map +1 -1
  244. package/dist/core/scout-controller.d.ts +6 -0
  245. package/dist/core/scout-controller.d.ts.map +1 -1
  246. package/dist/core/scout-controller.js +66 -52
  247. package/dist/core/scout-controller.js.map +1 -1
  248. package/dist/core/sdk.d.ts.map +1 -1
  249. package/dist/core/sdk.js +3 -3
  250. package/dist/core/sdk.js.map +1 -1
  251. package/dist/core/session-role.d.ts +31 -0
  252. package/dist/core/session-role.d.ts.map +1 -0
  253. package/dist/core/session-role.js +52 -0
  254. package/dist/core/session-role.js.map +1 -0
  255. package/dist/core/settings-manager.d.ts +42 -3
  256. package/dist/core/settings-manager.d.ts.map +1 -1
  257. package/dist/core/settings-manager.js +94 -60
  258. package/dist/core/settings-manager.js.map +1 -1
  259. package/dist/core/system-prompt-builder.d.ts +12 -0
  260. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  261. package/dist/core/system-prompt-builder.js +6 -0
  262. package/dist/core/system-prompt-builder.js.map +1 -1
  263. package/dist/core/system-prompt.d.ts +14 -1
  264. package/dist/core/system-prompt.d.ts.map +1 -1
  265. package/dist/core/system-prompt.js +20 -3
  266. package/dist/core/system-prompt.js.map +1 -1
  267. package/dist/core/tasks/session-task-state.d.ts +11 -2
  268. package/dist/core/tasks/session-task-state.d.ts.map +1 -1
  269. package/dist/core/tasks/session-task-state.js +27 -11
  270. package/dist/core/tasks/session-task-state.js.map +1 -1
  271. package/dist/core/tasks/task-contract-monitor.d.ts +45 -0
  272. package/dist/core/tasks/task-contract-monitor.d.ts.map +1 -0
  273. package/dist/core/tasks/task-contract-monitor.js +56 -0
  274. package/dist/core/tasks/task-contract-monitor.js.map +1 -0
  275. package/dist/core/tasks/task-state.d.ts +8 -0
  276. package/dist/core/tasks/task-state.d.ts.map +1 -1
  277. package/dist/core/tasks/task-state.js +28 -1
  278. package/dist/core/tasks/task-state.js.map +1 -1
  279. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  280. package/dist/core/tool-gate-controller.js +5 -0
  281. package/dist/core/tool-gate-controller.js.map +1 -1
  282. package/dist/core/tool-recovery-log-records.d.ts +7 -0
  283. package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
  284. package/dist/core/tool-recovery-log-records.js +14 -6
  285. package/dist/core/tool-recovery-log-records.js.map +1 -1
  286. package/dist/core/tool-selection/promotion.d.ts +54 -0
  287. package/dist/core/tool-selection/promotion.d.ts.map +1 -0
  288. package/dist/core/tool-selection/promotion.js +81 -0
  289. package/dist/core/tool-selection/promotion.js.map +1 -0
  290. package/dist/core/tool-selection/tool-performance-store.d.ts +37 -0
  291. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
  292. package/dist/core/tool-selection/tool-performance-store.js +87 -3
  293. package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
  294. package/dist/core/tool-selection/tool-selection-controller.d.ts +45 -0
  295. package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
  296. package/dist/core/tool-selection/tool-selection-controller.js +96 -0
  297. package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
  298. package/dist/core/tools/bash.d.ts +18 -0
  299. package/dist/core/tools/bash.d.ts.map +1 -1
  300. package/dist/core/tools/bash.js +97 -18
  301. package/dist/core/tools/bash.js.map +1 -1
  302. package/dist/core/tools/delegate-status.d.ts +14 -0
  303. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  304. package/dist/core/tools/delegate-status.js +82 -7
  305. package/dist/core/tools/delegate-status.js.map +1 -1
  306. package/dist/core/tools/delegate.d.ts.map +1 -1
  307. package/dist/core/tools/delegate.js +25 -8
  308. package/dist/core/tools/delegate.js.map +1 -1
  309. package/dist/core/tools/find.d.ts.map +1 -1
  310. package/dist/core/tools/find.js +52 -44
  311. package/dist/core/tools/find.js.map +1 -1
  312. package/dist/core/tools/goal.d.ts +94 -3
  313. package/dist/core/tools/goal.d.ts.map +1 -1
  314. package/dist/core/tools/goal.js +165 -15
  315. package/dist/core/tools/goal.js.map +1 -1
  316. package/dist/core/tools/grep.d.ts.map +1 -1
  317. package/dist/core/tools/grep.js +5 -4
  318. package/dist/core/tools/grep.js.map +1 -1
  319. package/dist/core/tools/model-fitness.d.ts +7 -0
  320. package/dist/core/tools/model-fitness.d.ts.map +1 -1
  321. package/dist/core/tools/model-fitness.js +2 -2
  322. package/dist/core/tools/model-fitness.js.map +1 -1
  323. package/dist/core/tools/render-utils.d.ts.map +1 -1
  324. package/dist/core/tools/render-utils.js +1 -1
  325. package/dist/core/tools/render-utils.js.map +1 -1
  326. package/dist/core/tools/shell-contract-router.d.ts +6 -1
  327. package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
  328. package/dist/core/tools/shell-contract-router.js +69 -13
  329. package/dist/core/tools/shell-contract-router.js.map +1 -1
  330. package/dist/core/tools/shell-session.d.ts +89 -0
  331. package/dist/core/tools/shell-session.d.ts.map +1 -0
  332. package/dist/core/tools/shell-session.js +432 -0
  333. package/dist/core/tools/shell-session.js.map +1 -0
  334. package/dist/core/tools/task-steps.d.ts +4 -0
  335. package/dist/core/tools/task-steps.d.ts.map +1 -1
  336. package/dist/core/tools/task-steps.js +63 -8
  337. package/dist/core/tools/task-steps.js.map +1 -1
  338. package/dist/core/tools/tmux-dispatch.d.ts +86 -0
  339. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -0
  340. package/dist/core/tools/tmux-dispatch.js +91 -0
  341. package/dist/core/tools/tmux-dispatch.js.map +1 -0
  342. package/dist/core/tools/windows-shell-engine.d.ts +42 -0
  343. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -0
  344. package/dist/core/tools/windows-shell-engine.js +153 -0
  345. package/dist/core/tools/windows-shell-engine.js.map +1 -0
  346. package/dist/core/tools/windows-shell-state.d.ts +40 -0
  347. package/dist/core/tools/windows-shell-state.d.ts.map +1 -0
  348. package/dist/core/tools/windows-shell-state.js +59 -0
  349. package/dist/core/tools/windows-shell-state.js.map +1 -0
  350. package/dist/core/tools/worktree-sync.d.ts +24 -0
  351. package/dist/core/tools/worktree-sync.d.ts.map +1 -0
  352. package/dist/core/tools/worktree-sync.js +338 -0
  353. package/dist/core/tools/worktree-sync.js.map +1 -0
  354. package/dist/core/trust-manager.d.ts +4 -1
  355. package/dist/core/trust-manager.d.ts.map +1 -1
  356. package/dist/core/trust-manager.js +20 -2
  357. package/dist/core/trust-manager.js.map +1 -1
  358. package/dist/core/util/atomic-file.d.ts +55 -0
  359. package/dist/core/util/atomic-file.d.ts.map +1 -0
  360. package/dist/core/util/atomic-file.js +255 -0
  361. package/dist/core/util/atomic-file.js.map +1 -0
  362. package/dist/core/util/minimatch-cache.d.ts +33 -0
  363. package/dist/core/util/minimatch-cache.d.ts.map +1 -0
  364. package/dist/core/util/minimatch-cache.js +0 -0
  365. package/dist/core/util/minimatch-cache.js.map +1 -0
  366. package/dist/core/worktree-sync/codes.d.ts +227 -0
  367. package/dist/core/worktree-sync/codes.d.ts.map +1 -0
  368. package/dist/core/worktree-sync/codes.js +14 -0
  369. package/dist/core/worktree-sync/codes.js.map +1 -0
  370. package/dist/core/worktree-sync/git-engine.d.ts +156 -0
  371. package/dist/core/worktree-sync/git-engine.d.ts.map +1 -0
  372. package/dist/core/worktree-sync/git-engine.js +1191 -0
  373. package/dist/core/worktree-sync/git-engine.js.map +1 -0
  374. package/dist/core/worktree-sync/lane-gate.d.ts +75 -0
  375. package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -0
  376. package/dist/core/worktree-sync/lane-gate.js +360 -0
  377. package/dist/core/worktree-sync/lane-gate.js.map +1 -0
  378. package/dist/core/worktree-sync/runtime.d.ts +47 -0
  379. package/dist/core/worktree-sync/runtime.d.ts.map +1 -0
  380. package/dist/core/worktree-sync/runtime.js +96 -0
  381. package/dist/core/worktree-sync/runtime.js.map +1 -0
  382. package/dist/core/worktree-sync/store.d.ts +69 -0
  383. package/dist/core/worktree-sync/store.d.ts.map +1 -0
  384. package/dist/core/worktree-sync/store.js +247 -0
  385. package/dist/core/worktree-sync/store.js.map +1 -0
  386. package/dist/core/worktree-sync/watcher.d.ts +29 -0
  387. package/dist/core/worktree-sync/watcher.d.ts.map +1 -0
  388. package/dist/core/worktree-sync/watcher.js +93 -0
  389. package/dist/core/worktree-sync/watcher.js.map +1 -0
  390. package/dist/main.d.ts.map +1 -1
  391. package/dist/main.js +94 -0
  392. package/dist/main.js.map +1 -1
  393. package/dist/migrations.d.ts +9 -0
  394. package/dist/migrations.d.ts.map +1 -1
  395. package/dist/migrations.js +38 -0
  396. package/dist/migrations.js.map +1 -1
  397. package/dist/modes/interactive/auto-learn-controller.d.ts +16 -1
  398. package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
  399. package/dist/modes/interactive/auto-learn-controller.js +50 -8
  400. package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
  401. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  402. package/dist/modes/interactive/components/profile-resource-editor.js +23 -1
  403. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  404. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  405. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  406. package/dist/modes/interactive/interactive-mode.js +4 -0
  407. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  408. package/dist/modes/interactive/local-model-commands.d.ts +43 -0
  409. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  410. package/dist/modes/interactive/local-model-commands.js +290 -3
  411. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  412. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  413. package/dist/modes/interactive/session-flow-commands.js +24 -4
  414. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  415. package/dist/utils/fs-watch.d.ts +11 -0
  416. package/dist/utils/fs-watch.d.ts.map +1 -1
  417. package/dist/utils/fs-watch.js +20 -2
  418. package/dist/utils/fs-watch.js.map +1 -1
  419. package/dist/utils/highlight-js-languages.d.ts +4 -0
  420. package/dist/utils/highlight-js-languages.d.ts.map +1 -0
  421. package/dist/utils/highlight-js-languages.js +573 -0
  422. package/dist/utils/highlight-js-languages.js.map +1 -0
  423. package/dist/utils/shell.d.ts +7 -1
  424. package/dist/utils/shell.d.ts.map +1 -1
  425. package/dist/utils/shell.js +39 -9
  426. package/dist/utils/shell.js.map +1 -1
  427. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  428. package/dist/utils/syntax-highlight.js +53 -5
  429. package/dist/utils/syntax-highlight.js.map +1 -1
  430. package/dist/utils/tools-manager.d.ts.map +1 -1
  431. package/dist/utils/tools-manager.js +112 -1
  432. package/dist/utils/tools-manager.js.map +1 -1
  433. package/docs/development.md +2 -0
  434. package/docs/packages.md +1 -1
  435. package/docs/process-matrix.md +120 -0
  436. package/docs/settings.md +5 -2
  437. package/docs/tmux-agent-manager.md +85 -2
  438. package/docs/windows.md +52 -3
  439. package/docs/work-directory.md +29 -0
  440. package/docs/worktree-sync.md +250 -0
  441. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  442. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  443. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  444. package/examples/extensions/sandbox/package-lock.json +2 -2
  445. package/examples/extensions/sandbox/package.json +1 -1
  446. package/examples/extensions/with-deps/package-lock.json +2 -2
  447. package/examples/extensions/with-deps/package.json +1 -1
  448. package/npm-shrinkwrap.json +12 -12
  449. package/package.json +10 -4
  450. package/docs/integration-sweep-builder-blueprint-2026-07-09.md +0 -365
  451. package/docs/integration-sweep-resume-2026-07-09.md +0 -407
@@ -1,5 +1,5 @@
1
1
  import { Type } from "typebox";
2
- import { addTaskStep, clearTaskSteps, compactTaskSteps, createTaskStepsState, formatTaskSteps, MAX_TASK_STEPS, setTaskSteps, updateTaskStep, } from "../tasks/task-state.js";
2
+ import { addTaskStep, clearTaskSteps, compactTaskSteps, createTaskStepsState, findOpenDuplicateStep, formatTaskSteps, hasUnverifiedCompletedStep, MAX_TASK_STEPS, resolveTaskStepSelector, setTaskSteps, updateTaskStep, } from "../tasks/task-state.js";
3
3
  const statusSchema = Type.Union([
4
4
  Type.Literal("pending"),
5
5
  Type.Literal("in_progress"),
@@ -61,9 +61,31 @@ function counts(state) {
61
61
  return {
62
62
  stepCount: state.steps.length,
63
63
  openStepCount,
64
- verificationNudgeNeeded: state.steps.some((step) => step.status === "completed" && step.evidence.length === 0),
64
+ verificationNudgeNeeded: hasUnverifiedCompletedStep(state),
65
65
  };
66
66
  }
67
+ /** Ids (from `resultSteps`, index-aligned with `inputs`) whose status was set explicitly by the caller. */
68
+ function explicitStatusStepIds(inputs, resultSteps) {
69
+ const ids = new Set();
70
+ inputs.forEach((stepInput, index) => {
71
+ if (stepInput.status !== undefined) {
72
+ const id = resultSteps[index]?.id;
73
+ if (id)
74
+ ids.add(id);
75
+ }
76
+ });
77
+ return ids;
78
+ }
79
+ /**
80
+ * Steps that were in_progress before this call and are pending after, excluding any step whose
81
+ * new status was set explicitly by the caller (an explicit change is not a "silent" demotion).
82
+ */
83
+ function computeDemotedStepIds(before, after, excludeIds) {
84
+ const beforeActiveIds = new Set(before.steps.filter((step) => step.status === "in_progress").map((step) => step.id));
85
+ return after.steps
86
+ .filter((step) => beforeActiveIds.has(step.id) && step.status === "pending" && !excludeIds.has(step.id))
87
+ .map((step) => step.id);
88
+ }
67
89
  function errorResult(action, error, state) {
68
90
  return {
69
91
  content: [{ type: "text", text: `task_steps ${action} failed: ${error}` }],
@@ -89,25 +111,36 @@ export function createTaskStepsToolDefinition(deps) {
89
111
  async execute(_toolCallId, input) {
90
112
  const timestamp = now();
91
113
  const current = deps.getTaskStepsState();
92
- let state = current ?? createTaskStepsState(timestamp);
114
+ const before = current ?? createTaskStepsState(timestamp);
115
+ let state = before;
116
+ let duplicateStepId;
117
+ let demotedStepIds = [];
93
118
  try {
94
119
  switch (input.action) {
95
120
  case "set":
96
121
  if (!input.steps)
97
122
  return errorResult(input.action, "set requires steps[].", current);
98
123
  state = setTaskSteps(state, input.steps, timestamp);
124
+ demotedStepIds = computeDemotedStepIds(before, state, explicitStatusStepIds(input.steps, state.steps));
99
125
  break;
100
126
  case "intake":
101
127
  if (!input.steps)
102
128
  return errorResult(input.action, "intake requires a complete steps[] list.", current);
103
129
  state = setTaskSteps(state, input.steps, timestamp);
130
+ demotedStepIds = computeDemotedStepIds(before, state, explicitStatusStepIds(input.steps, state.steps));
104
131
  break;
105
132
  case "add":
106
133
  state = addTaskStep(state, toTaskStepInput(input), timestamp);
134
+ if (state === before) {
135
+ // The reducer returned the unchanged state: an open step already carries this
136
+ // content, so nothing was created. Name the existing step in the response.
137
+ duplicateStepId = findOpenDuplicateStep(before.steps, input.content ?? "")?.id;
138
+ }
107
139
  break;
108
- case "update":
140
+ case "update": {
109
141
  if (!input.id?.trim())
110
142
  return errorResult(input.action, "update requires id or a unique selector.", current);
143
+ const selected = resolveTaskStepSelector(before.steps, input.id);
111
144
  state = updateTaskStep(state, input.id, {
112
145
  content: input.content,
113
146
  activeForm: input.activeForm,
@@ -117,7 +150,11 @@ export function createTaskStepsToolDefinition(deps) {
117
150
  note: input.note,
118
151
  evidence: input.evidence,
119
152
  }, timestamp);
153
+ // Exclude the explicitly targeted step: its own status change was requested by
154
+ // the caller, so it is never a "silent" demotion even if it moved to pending.
155
+ demotedStepIds = computeDemotedStepIds(before, state, new Set([selected.id]));
120
156
  break;
157
+ }
121
158
  case "clear":
122
159
  state = clearTaskSteps(state, timestamp);
123
160
  break;
@@ -129,25 +166,43 @@ export function createTaskStepsToolDefinition(deps) {
129
166
  state = compactTaskSteps(state, timestamp);
130
167
  break;
131
168
  }
132
- const mutated = input.action !== "list" || input.clearCompleted === true;
169
+ const isNoopDuplicateAdd = input.action === "add" && state === before;
170
+ const mutated = (input.action !== "list" || input.clearCompleted === true) && !isNoopDuplicateAdd;
133
171
  if (mutated)
134
172
  deps.saveTaskStepsState(state);
173
+ const stateCounts = counts(state);
174
+ const noticeLines = [];
175
+ if (duplicateStepId) {
176
+ noticeLines.push(`Duplicate open step ignored; existing ${duplicateStepId} already tracks this content.`);
177
+ }
178
+ if (demotedStepIds.length > 0) {
179
+ noticeLines.push(`Demoted to pending because another step became active: ${demotedStepIds.join(", ")}.`);
180
+ }
181
+ if (stateCounts.verificationNudgeNeeded) {
182
+ noticeLines.push("Reminder: a completed step has no evidence attached; attach evidence via update before treating it as verified.");
183
+ }
184
+ const notices = noticeLines.length > 0 ? `\n${noticeLines.join("\n")}` : "";
185
+ const headerAction = input.action === "list"
186
+ ? ""
187
+ : `task_steps ${input.action} ${duplicateStepId ? "ignored (duplicate)" : "recorded"}.\n`;
135
188
  return {
136
189
  content: [
137
190
  {
138
191
  type: "text",
139
- text: `${input.action === "list" ? "" : `task_steps ${input.action} recorded.\n`}${formatTaskSteps(state, {
192
+ text: `${headerAction}${formatTaskSteps(state, {
140
193
  includeTerminal: input.showCompleted,
141
194
  maxItems: input.maxItems,
142
- })}`,
195
+ })}${notices}`,
143
196
  },
144
197
  ],
145
198
  details: {
146
199
  action: input.action,
147
200
  applied: true,
148
201
  state,
149
- ...counts(state),
202
+ ...stateCounts,
150
203
  showCompleted: input.showCompleted,
204
+ duplicateOfStepId: duplicateStepId,
205
+ demotedStepIds: demotedStepIds.length > 0 ? demotedStepIds : undefined,
151
206
  },
152
207
  };
153
208
  }
@@ -1 +1 @@
1
- {"version":3,"file":"task-steps.js","sourceRoot":"","sources":["../../../src/core/tools/task-steps.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport {\n\taddTaskStep,\n\tclearTaskSteps,\n\tcompactTaskSteps,\n\tcreateTaskStepsState,\n\tformatTaskSteps,\n\tMAX_TASK_STEPS,\n\tsetTaskSteps,\n\ttype TaskStepInput,\n\ttype TaskStepsState,\n\tupdateTaskStep,\n} from \"../tasks/task-state.ts\";\n\nconst statusSchema = Type.Union([\n\tType.Literal(\"pending\"),\n\tType.Literal(\"in_progress\"),\n\tType.Literal(\"completed\"),\n\tType.Literal(\"blocked\"),\n\tType.Literal(\"cancelled\"),\n]);\n\nconst prioritySchema = Type.Union([Type.Literal(\"low\"), Type.Literal(\"normal\"), Type.Literal(\"high\")]);\n\nconst stepInputSchema = Type.Object(\n\t{\n\t\tcontent: Type.String({ minLength: 1, maxLength: 2_000, description: \"Imperative task step text.\" }),\n\t\tactiveForm: Type.Optional(\n\t\t\tType.String({ minLength: 1, maxLength: 2_000, description: \"Short present-progress label for active UI.\" }),\n\t\t),\n\t\tstatus: Type.Optional(statusSchema),\n\t\tpriority: Type.Optional(prioritySchema),\n\t\towner: Type.Optional(Type.String({ maxLength: 200 })),\n\t\tnote: Type.Optional(Type.String({ maxLength: 4_000 })),\n\t\tevidence: Type.Optional(Type.Array(Type.String({ minLength: 1, maxLength: 2_000 }), { maxItems: 32 })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst taskStepsSchema = Type.Object(\n\t{\n\t\taction: Type.Union(\n\t\t\t[\n\t\t\t\tType.Literal(\"set\"),\n\t\t\t\tType.Literal(\"add\"),\n\t\t\t\tType.Literal(\"update\"),\n\t\t\t\tType.Literal(\"list\"),\n\t\t\t\tType.Literal(\"clear\"),\n\t\t\t\tType.Literal(\"compact\"),\n\t\t\t\tType.Literal(\"intake\"),\n\t\t\t],\n\t\t\t{ description: \"Checklist action. Use list to read without mutation.\" },\n\t\t),\n\t\tsteps: Type.Optional(\n\t\t\tType.Array(stepInputSchema, {\n\t\t\t\tmaxItems: MAX_TASK_STEPS,\n\t\t\t\tdescription: \"Complete replacement list for set/intake. Intake preserves every supplied item in order.\",\n\t\t\t}),\n\t\t),\n\t\tid: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription: \"For update: current/active, exact step id, unique id prefix, or unique content selector.\",\n\t\t\t}),\n\t\t),\n\t\tcontent: Type.Optional(Type.String({ minLength: 1, maxLength: 2_000 })),\n\t\tactiveForm: Type.Optional(Type.String({ maxLength: 2_000 })),\n\t\tstatus: Type.Optional(statusSchema),\n\t\tpriority: Type.Optional(prioritySchema),\n\t\towner: Type.Optional(Type.String({ maxLength: 200 })),\n\t\tnote: Type.Optional(Type.String({ maxLength: 4_000 })),\n\t\tevidence: Type.Optional(Type.Array(Type.String({ minLength: 1, maxLength: 2_000 }), { maxItems: 32 })),\n\t\tclearCompleted: Type.Optional(\n\t\t\tType.Boolean({ description: \"For list: compact completed and cancelled steps before rendering.\" }),\n\t\t),\n\t\tshowCompleted: Type.Optional(Type.Boolean({ description: \"Include completed and cancelled steps in output.\" })),\n\t\tmaxItems: Type.Optional(Type.Integer({ minimum: 1, maximum: MAX_TASK_STEPS })),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type TaskStepsToolInput = Static<typeof taskStepsSchema>;\nexport type TaskStepsAction = TaskStepsToolInput[\"action\"];\n\nexport interface TaskStepsToolDetails {\n\taction: TaskStepsAction;\n\tapplied: boolean;\n\terror?: string;\n\tstate?: TaskStepsState;\n\tstepCount?: number;\n\topenStepCount?: number;\n\tverificationNudgeNeeded?: boolean;\n\tshowCompleted?: boolean;\n}\n\nexport interface TaskStepsToolDependencies {\n\tgetTaskStepsState: () => TaskStepsState | undefined;\n\tsaveTaskStepsState: (state: TaskStepsState) => void;\n\tnow?: () => string;\n}\n\nfunction toTaskStepInput(input: TaskStepsToolInput): TaskStepInput {\n\treturn {\n\t\tcontent: input.content ?? \"\",\n\t\tactiveForm: input.activeForm,\n\t\tstatus: input.status,\n\t\tpriority: input.priority,\n\t\towner: input.owner,\n\t\tnote: input.note,\n\t\tevidence: input.evidence,\n\t};\n}\n\nfunction counts(\n\tstate: TaskStepsState,\n): Pick<TaskStepsToolDetails, \"stepCount\" | \"openStepCount\" | \"verificationNudgeNeeded\"> {\n\tconst openStepCount = state.steps.filter(\n\t\t(step) => step.status !== \"completed\" && step.status !== \"cancelled\",\n\t).length;\n\treturn {\n\t\tstepCount: state.steps.length,\n\t\topenStepCount,\n\t\tverificationNudgeNeeded: state.steps.some((step) => step.status === \"completed\" && step.evidence.length === 0),\n\t};\n}\n\nfunction errorResult(action: TaskStepsAction, error: string, state?: TaskStepsState) {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text: `task_steps ${action} failed: ${error}` }],\n\t\tdetails: { action, applied: false, error, state, ...(state ? counts(state) : {}) } satisfies TaskStepsToolDetails,\n\t};\n}\n\nexport function createTaskStepsToolDefinition(deps: TaskStepsToolDependencies): ToolDefinition {\n\tconst now = deps.now ?? (() => new Date().toISOString());\n\treturn {\n\t\tname: \"task_steps\",\n\t\tlabel: \"Task Steps\",\n\t\tdescription:\n\t\t\t\"Maintain the current session's native ordered checklist for multi-step work. Supports pending, in_progress, completed, blocked, and cancelled steps with notes and evidence. State is isolated to and persisted with the active session; use native goal for durable outcome requirements and native delegate/delegate_status for background workers.\",\n\t\tpromptSnippet: \"Track and drain multi-step work with a native session checklist.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use task_steps for complex work, explicit task-tracking requests, and harness/self-improvement work; keep exactly one step in_progress while actively working.\",\n\t\t\t\"Always address the first open task step before unrelated work: start it, complete/block/cancel it with evidence, ask one clarifying question, or explicitly defer or reorder it.\",\n\t\t\t\"Mark steps completed as soon as evidence is gathered, and attach concise evidence or a blocker reason through update.\",\n\t\t\t\"Use action=intake with a complete steps array when preserving a raw multi-item dump; retain every item and do not silently drop entries.\",\n\t\t\t\"Drain task_steps before final responses: leave no stale in_progress step, and explicitly discuss or defer remaining pending or blocked work.\",\n\t\t\t\"Use goal for the durable goal ledger and delegate/delegate_status for worker lanes; do not emulate background execution inside task_steps.\",\n\t\t],\n\t\tparameters: taskStepsSchema,\n\t\tasync execute(_toolCallId, input: TaskStepsToolInput) {\n\t\t\tconst timestamp = now();\n\t\t\tconst current = deps.getTaskStepsState();\n\t\t\tlet state = current ?? createTaskStepsState(timestamp);\n\t\t\ttry {\n\t\t\t\tswitch (input.action) {\n\t\t\t\t\tcase \"set\":\n\t\t\t\t\t\tif (!input.steps) return errorResult(input.action, \"set requires steps[].\", current);\n\t\t\t\t\t\tstate = setTaskSteps(state, input.steps, timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"intake\":\n\t\t\t\t\t\tif (!input.steps)\n\t\t\t\t\t\t\treturn errorResult(input.action, \"intake requires a complete steps[] list.\", current);\n\t\t\t\t\t\tstate = setTaskSteps(state, input.steps, timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"add\":\n\t\t\t\t\t\tstate = addTaskStep(state, toTaskStepInput(input), timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"update\":\n\t\t\t\t\t\tif (!input.id?.trim())\n\t\t\t\t\t\t\treturn errorResult(input.action, \"update requires id or a unique selector.\", current);\n\t\t\t\t\t\tstate = updateTaskStep(\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t\tinput.id,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontent: input.content,\n\t\t\t\t\t\t\t\tactiveForm: input.activeForm,\n\t\t\t\t\t\t\t\tstatus: input.status,\n\t\t\t\t\t\t\t\tpriority: input.priority,\n\t\t\t\t\t\t\t\towner: input.owner,\n\t\t\t\t\t\t\t\tnote: input.note,\n\t\t\t\t\t\t\t\tevidence: input.evidence,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"clear\":\n\t\t\t\t\t\tstate = clearTaskSteps(state, timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"compact\":\n\t\t\t\t\t\tstate = compactTaskSteps(state, timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"list\":\n\t\t\t\t\t\tif (input.clearCompleted) state = compactTaskSteps(state, timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst mutated = input.action !== \"list\" || input.clearCompleted === true;\n\t\t\t\tif (mutated) deps.saveTaskStepsState(state);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `${input.action === \"list\" ? \"\" : `task_steps ${input.action} recorded.\\n`}${formatTaskSteps(\n\t\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tincludeTerminal: input.showCompleted,\n\t\t\t\t\t\t\t\t\tmaxItems: input.maxItems,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\taction: input.action,\n\t\t\t\t\t\tapplied: true,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\t...counts(state),\n\t\t\t\t\t\tshowCompleted: input.showCompleted,\n\t\t\t\t\t} satisfies TaskStepsToolDetails,\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\treturn errorResult(input.action, error instanceof Error ? error.message : String(error), current);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"task-steps.js","sourceRoot":"","sources":["../../../src/core/tools/task-steps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EACN,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,0BAA0B,EAC1B,cAAc,EACd,uBAAuB,EACvB,YAAY,EAIZ,cAAc,GACd,MAAM,wBAAwB,CAAC;AAEhC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC;IAC/B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;IACvB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;IACzB,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;IACvB,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAEvG,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAClC;IACC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IACnG,UAAU,EAAE,IAAI,CAAC,QAAQ,CACxB,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,6CAA6C,EAAE,CAAC,CAC3G;IACD,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;IACnC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;IACvC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACrD,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;CACtG,EACD,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAC/B,CAAC;AAEF,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAClC;IACC,MAAM,EAAE,IAAI,CAAC,KAAK,CACjB;QACC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACpB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QACrB,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;KACtB,EACD,EAAE,WAAW,EAAE,sDAAsD,EAAE,CACvE;IACD,KAAK,EAAE,IAAI,CAAC,QAAQ,CACnB,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE;QAC3B,QAAQ,EAAE,cAAc;QACxB,WAAW,EAAE,0FAA0F;KACvG,CAAC,CACF;IACD,EAAE,EAAE,IAAI,CAAC,QAAQ,CAChB,IAAI,CAAC,MAAM,CAAC;QACX,WAAW,EAAE,0FAA0F;KACvG,CAAC,CACF;IACD,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5D,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;IACnC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;IACvC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACrD,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IACtG,cAAc,EAAE,IAAI,CAAC,QAAQ,CAC5B,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,mEAAmE,EAAE,CAAC,CAClG;IACD,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC,CAAC;IAC/G,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;CAC9E,EACD,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAC/B,CAAC;AA0BF,SAAS,eAAe,CAAC,KAAyB,EAAiB;IAClE,OAAO;QACN,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE;QAC5B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACxB,CAAC;AAAA,CACF;AAED,SAAS,MAAM,CACd,KAAqB,EACmE;IACxF,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CACvC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,CACpE,CAAC,MAAM,CAAC;IACT,OAAO;QACN,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;QAC7B,aAAa;QACb,uBAAuB,EAAE,0BAA0B,CAAC,KAAK,CAAC;KAC1D,CAAC;AAAA,CACF;AAED,2GAA2G;AAC3G,SAAS,qBAAqB,CAAC,MAAgC,EAAE,WAAgC,EAAe;IAC/G,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC;QACpC,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YAClC,IAAI,EAAE;gBAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrB,CAAC;IAAA,CACD,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AAAA,CACX;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAC7B,MAAsB,EACtB,KAAqB,EACrB,UAA+B,EACpB;IACX,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACrH,OAAO,KAAK,CAAC,KAAK;SAChB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SACvG,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,CACzB;AAED,SAAS,WAAW,CAAC,MAAuB,EAAE,KAAa,EAAE,KAAsB,EAAE;IACpF,OAAO;QACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,cAAc,MAAM,YAAY,KAAK,EAAE,EAAE,CAAC;QACnF,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAiC;KACjH,CAAC;AAAA,CACF;AAED,MAAM,UAAU,6BAA6B,CAAC,IAA+B,EAAkB;IAC9F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IACzD,OAAO;QACN,IAAI,EAAE,YAAY;QAClB,KAAK,EAAE,YAAY;QACnB,WAAW,EACV,uVAAuV;QACxV,aAAa,EAAE,kEAAkE;QACjF,gBAAgB,EAAE;YACjB,gKAAgK;YAChK,kLAAkL;YAClL,uHAAuH;YACvH,0IAA0I;YAC1I,8IAA8I;YAC9I,4IAA4I;SAC5I;QACD,UAAU,EAAE,eAAe;QAC3B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,KAAyB,EAAE;YACrD,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,OAAO,IAAI,oBAAoB,CAAC,SAAS,CAAC,CAAC;YAC1D,IAAI,KAAK,GAAG,MAAM,CAAC;YACnB,IAAI,eAAmC,CAAC;YACxC,IAAI,cAAc,GAAsB,EAAE,CAAC;YAC3C,IAAI,CAAC;gBACJ,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;oBACtB,KAAK,KAAK;wBACT,IAAI,CAAC,KAAK,CAAC,KAAK;4BAAE,OAAO,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,uBAAuB,EAAE,OAAO,CAAC,CAAC;wBACrF,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;wBACpD,cAAc,GAAG,qBAAqB,CACrC,MAAM,EACN,KAAK,EACL,qBAAqB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAC/C,CAAC;wBACF,MAAM;oBACP,KAAK,QAAQ;wBACZ,IAAI,CAAC,KAAK,CAAC,KAAK;4BACf,OAAO,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,0CAA0C,EAAE,OAAO,CAAC,CAAC;wBACvF,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;wBACpD,cAAc,GAAG,qBAAqB,CACrC,MAAM,EACN,KAAK,EACL,qBAAqB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAC/C,CAAC;wBACF,MAAM;oBACP,KAAK,KAAK;wBACT,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,CAAC;wBAC9D,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;4BACtB,8EAA8E;4BAC9E,2EAA2E;4BAC3E,eAAe,GAAG,qBAAqB,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;wBAChF,CAAC;wBACD,MAAM;oBACP,KAAK,QAAQ,EAAE,CAAC;wBACf,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE;4BACpB,OAAO,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,0CAA0C,EAAE,OAAO,CAAC,CAAC;wBACvF,MAAM,QAAQ,GAAG,uBAAuB,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;wBACjE,KAAK,GAAG,cAAc,CACrB,KAAK,EACL,KAAK,CAAC,EAAE,EACR;4BACC,OAAO,EAAE,KAAK,CAAC,OAAO;4BACtB,UAAU,EAAE,KAAK,CAAC,UAAU;4BAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;4BACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;4BACxB,KAAK,EAAE,KAAK,CAAC,KAAK;4BAClB,IAAI,EAAE,KAAK,CAAC,IAAI;4BAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;yBACxB,EACD,SAAS,CACT,CAAC;wBACF,+EAA+E;wBAC/E,8EAA8E;wBAC9E,cAAc,GAAG,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;wBAC9E,MAAM;oBACP,CAAC;oBACD,KAAK,OAAO;wBACX,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;wBACzC,MAAM;oBACP,KAAK,SAAS;wBACb,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;wBAC3C,MAAM;oBACP,KAAK,MAAM;wBACV,IAAI,KAAK,CAAC,cAAc;4BAAE,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;wBACrE,MAAM;gBACR,CAAC;gBAED,MAAM,kBAAkB,GAAG,KAAK,CAAC,MAAM,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC;gBACtE,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,cAAc,KAAK,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC;gBAClG,IAAI,OAAO;oBAAE,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBAE5C,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBAClC,MAAM,WAAW,GAAa,EAAE,CAAC;gBACjC,IAAI,eAAe,EAAE,CAAC;oBACrB,WAAW,CAAC,IAAI,CACf,yCAAyC,eAAe,+BAA+B,CACvF,CAAC;gBACH,CAAC;gBACD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC/B,WAAW,CAAC,IAAI,CAAC,0DAA0D,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC1G,CAAC;gBACD,IAAI,WAAW,CAAC,uBAAuB,EAAE,CAAC;oBACzC,WAAW,CAAC,IAAI,CACf,iHAAiH,CACjH,CAAC;gBACH,CAAC;gBACD,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAE5E,MAAM,YAAY,GACjB,KAAK,CAAC,MAAM,KAAK,MAAM;oBACtB,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC,cAAc,KAAK,CAAC,MAAM,IAAI,eAAe,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC;gBAC5F,OAAO;oBACN,OAAO,EAAE;wBACR;4BACC,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,GAAG,YAAY,GAAG,eAAe,CAAC,KAAK,EAAE;gCAC9C,eAAe,EAAE,KAAK,CAAC,aAAa;gCACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;6BACxB,CAAC,GAAG,OAAO,EAAE;yBACd;qBACD;oBACD,OAAO,EAAE;wBACR,MAAM,EAAE,KAAK,CAAC,MAAM;wBACpB,OAAO,EAAE,IAAI;wBACb,KAAK;wBACL,GAAG,WAAW;wBACd,aAAa,EAAE,KAAK,CAAC,aAAa;wBAClC,iBAAiB,EAAE,eAAe;wBAClC,cAAc,EAAE,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS;qBACvC;iBAChC,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,OAAO,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;YACnG,CAAC;QAAA,CACD;KACD,CAAC;AAAA,CACF","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport {\n\taddTaskStep,\n\tclearTaskSteps,\n\tcompactTaskSteps,\n\tcreateTaskStepsState,\n\tfindOpenDuplicateStep,\n\tformatTaskSteps,\n\thasUnverifiedCompletedStep,\n\tMAX_TASK_STEPS,\n\tresolveTaskStepSelector,\n\tsetTaskSteps,\n\ttype TaskStep,\n\ttype TaskStepInput,\n\ttype TaskStepsState,\n\tupdateTaskStep,\n} from \"../tasks/task-state.ts\";\n\nconst statusSchema = Type.Union([\n\tType.Literal(\"pending\"),\n\tType.Literal(\"in_progress\"),\n\tType.Literal(\"completed\"),\n\tType.Literal(\"blocked\"),\n\tType.Literal(\"cancelled\"),\n]);\n\nconst prioritySchema = Type.Union([Type.Literal(\"low\"), Type.Literal(\"normal\"), Type.Literal(\"high\")]);\n\nconst stepInputSchema = Type.Object(\n\t{\n\t\tcontent: Type.String({ minLength: 1, maxLength: 2_000, description: \"Imperative task step text.\" }),\n\t\tactiveForm: Type.Optional(\n\t\t\tType.String({ minLength: 1, maxLength: 2_000, description: \"Short present-progress label for active UI.\" }),\n\t\t),\n\t\tstatus: Type.Optional(statusSchema),\n\t\tpriority: Type.Optional(prioritySchema),\n\t\towner: Type.Optional(Type.String({ maxLength: 200 })),\n\t\tnote: Type.Optional(Type.String({ maxLength: 4_000 })),\n\t\tevidence: Type.Optional(Type.Array(Type.String({ minLength: 1, maxLength: 2_000 }), { maxItems: 32 })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst taskStepsSchema = Type.Object(\n\t{\n\t\taction: Type.Union(\n\t\t\t[\n\t\t\t\tType.Literal(\"set\"),\n\t\t\t\tType.Literal(\"add\"),\n\t\t\t\tType.Literal(\"update\"),\n\t\t\t\tType.Literal(\"list\"),\n\t\t\t\tType.Literal(\"clear\"),\n\t\t\t\tType.Literal(\"compact\"),\n\t\t\t\tType.Literal(\"intake\"),\n\t\t\t],\n\t\t\t{ description: \"Checklist action. Use list to read without mutation.\" },\n\t\t),\n\t\tsteps: Type.Optional(\n\t\t\tType.Array(stepInputSchema, {\n\t\t\t\tmaxItems: MAX_TASK_STEPS,\n\t\t\t\tdescription: \"Complete replacement list for set/intake. Intake preserves every supplied item in order.\",\n\t\t\t}),\n\t\t),\n\t\tid: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription: \"For update: current/active, exact step id, unique id prefix, or unique content selector.\",\n\t\t\t}),\n\t\t),\n\t\tcontent: Type.Optional(Type.String({ minLength: 1, maxLength: 2_000 })),\n\t\tactiveForm: Type.Optional(Type.String({ maxLength: 2_000 })),\n\t\tstatus: Type.Optional(statusSchema),\n\t\tpriority: Type.Optional(prioritySchema),\n\t\towner: Type.Optional(Type.String({ maxLength: 200 })),\n\t\tnote: Type.Optional(Type.String({ maxLength: 4_000 })),\n\t\tevidence: Type.Optional(Type.Array(Type.String({ minLength: 1, maxLength: 2_000 }), { maxItems: 32 })),\n\t\tclearCompleted: Type.Optional(\n\t\t\tType.Boolean({ description: \"For list: compact completed and cancelled steps before rendering.\" }),\n\t\t),\n\t\tshowCompleted: Type.Optional(Type.Boolean({ description: \"Include completed and cancelled steps in output.\" })),\n\t\tmaxItems: Type.Optional(Type.Integer({ minimum: 1, maximum: MAX_TASK_STEPS })),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type TaskStepsToolInput = Static<typeof taskStepsSchema>;\nexport type TaskStepsAction = TaskStepsToolInput[\"action\"];\n\nexport interface TaskStepsToolDetails {\n\taction: TaskStepsAction;\n\tapplied: boolean;\n\terror?: string;\n\tstate?: TaskStepsState;\n\tstepCount?: number;\n\topenStepCount?: number;\n\tverificationNudgeNeeded?: boolean;\n\tshowCompleted?: boolean;\n\t/** Set when `add` was a no-op because an open step already carries this content. */\n\tduplicateOfStepId?: string;\n\t/** Step ids silently demoted to pending because another step became active in this call. */\n\tdemotedStepIds?: readonly string[];\n}\n\nexport interface TaskStepsToolDependencies {\n\tgetTaskStepsState: () => TaskStepsState | undefined;\n\tsaveTaskStepsState: (state: TaskStepsState) => void;\n\tnow?: () => string;\n}\n\nfunction toTaskStepInput(input: TaskStepsToolInput): TaskStepInput {\n\treturn {\n\t\tcontent: input.content ?? \"\",\n\t\tactiveForm: input.activeForm,\n\t\tstatus: input.status,\n\t\tpriority: input.priority,\n\t\towner: input.owner,\n\t\tnote: input.note,\n\t\tevidence: input.evidence,\n\t};\n}\n\nfunction counts(\n\tstate: TaskStepsState,\n): Pick<TaskStepsToolDetails, \"stepCount\" | \"openStepCount\" | \"verificationNudgeNeeded\"> {\n\tconst openStepCount = state.steps.filter(\n\t\t(step) => step.status !== \"completed\" && step.status !== \"cancelled\",\n\t).length;\n\treturn {\n\t\tstepCount: state.steps.length,\n\t\topenStepCount,\n\t\tverificationNudgeNeeded: hasUnverifiedCompletedStep(state),\n\t};\n}\n\n/** Ids (from `resultSteps`, index-aligned with `inputs`) whose status was set explicitly by the caller. */\nfunction explicitStatusStepIds(inputs: readonly TaskStepInput[], resultSteps: readonly TaskStep[]): Set<string> {\n\tconst ids = new Set<string>();\n\tinputs.forEach((stepInput, index) => {\n\t\tif (stepInput.status !== undefined) {\n\t\t\tconst id = resultSteps[index]?.id;\n\t\t\tif (id) ids.add(id);\n\t\t}\n\t});\n\treturn ids;\n}\n\n/**\n * Steps that were in_progress before this call and are pending after, excluding any step whose\n * new status was set explicitly by the caller (an explicit change is not a \"silent\" demotion).\n */\nfunction computeDemotedStepIds(\n\tbefore: TaskStepsState,\n\tafter: TaskStepsState,\n\texcludeIds: ReadonlySet<string>,\n): string[] {\n\tconst beforeActiveIds = new Set(before.steps.filter((step) => step.status === \"in_progress\").map((step) => step.id));\n\treturn after.steps\n\t\t.filter((step) => beforeActiveIds.has(step.id) && step.status === \"pending\" && !excludeIds.has(step.id))\n\t\t.map((step) => step.id);\n}\n\nfunction errorResult(action: TaskStepsAction, error: string, state?: TaskStepsState) {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text: `task_steps ${action} failed: ${error}` }],\n\t\tdetails: { action, applied: false, error, state, ...(state ? counts(state) : {}) } satisfies TaskStepsToolDetails,\n\t};\n}\n\nexport function createTaskStepsToolDefinition(deps: TaskStepsToolDependencies): ToolDefinition {\n\tconst now = deps.now ?? (() => new Date().toISOString());\n\treturn {\n\t\tname: \"task_steps\",\n\t\tlabel: \"Task Steps\",\n\t\tdescription:\n\t\t\t\"Maintain the current session's native ordered checklist for multi-step work. Supports pending, in_progress, completed, blocked, and cancelled steps with notes and evidence. State is isolated to and persisted with the active session; use native goal for durable outcome requirements and native delegate/delegate_status for background workers.\",\n\t\tpromptSnippet: \"Track and drain multi-step work with a native session checklist.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use task_steps for complex work, explicit task-tracking requests, and harness/self-improvement work; keep exactly one step in_progress while actively working.\",\n\t\t\t\"Always address the first open task step before unrelated work: start it, complete/block/cancel it with evidence, ask one clarifying question, or explicitly defer or reorder it.\",\n\t\t\t\"Mark steps completed as soon as evidence is gathered, and attach concise evidence or a blocker reason through update.\",\n\t\t\t\"Use action=intake with a complete steps array when preserving a raw multi-item dump; retain every item and do not silently drop entries.\",\n\t\t\t\"Drain task_steps before final responses: leave no stale in_progress step, and explicitly discuss or defer remaining pending or blocked work.\",\n\t\t\t\"Use goal for the durable goal ledger and delegate/delegate_status for worker lanes; do not emulate background execution inside task_steps.\",\n\t\t],\n\t\tparameters: taskStepsSchema,\n\t\tasync execute(_toolCallId, input: TaskStepsToolInput) {\n\t\t\tconst timestamp = now();\n\t\t\tconst current = deps.getTaskStepsState();\n\t\t\tconst before = current ?? createTaskStepsState(timestamp);\n\t\t\tlet state = before;\n\t\t\tlet duplicateStepId: string | undefined;\n\t\t\tlet demotedStepIds: readonly string[] = [];\n\t\t\ttry {\n\t\t\t\tswitch (input.action) {\n\t\t\t\t\tcase \"set\":\n\t\t\t\t\t\tif (!input.steps) return errorResult(input.action, \"set requires steps[].\", current);\n\t\t\t\t\t\tstate = setTaskSteps(state, input.steps, timestamp);\n\t\t\t\t\t\tdemotedStepIds = computeDemotedStepIds(\n\t\t\t\t\t\t\tbefore,\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t\texplicitStatusStepIds(input.steps, state.steps),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"intake\":\n\t\t\t\t\t\tif (!input.steps)\n\t\t\t\t\t\t\treturn errorResult(input.action, \"intake requires a complete steps[] list.\", current);\n\t\t\t\t\t\tstate = setTaskSteps(state, input.steps, timestamp);\n\t\t\t\t\t\tdemotedStepIds = computeDemotedStepIds(\n\t\t\t\t\t\t\tbefore,\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t\texplicitStatusStepIds(input.steps, state.steps),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"add\":\n\t\t\t\t\t\tstate = addTaskStep(state, toTaskStepInput(input), timestamp);\n\t\t\t\t\t\tif (state === before) {\n\t\t\t\t\t\t\t// The reducer returned the unchanged state: an open step already carries this\n\t\t\t\t\t\t\t// content, so nothing was created. Name the existing step in the response.\n\t\t\t\t\t\t\tduplicateStepId = findOpenDuplicateStep(before.steps, input.content ?? \"\")?.id;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"update\": {\n\t\t\t\t\t\tif (!input.id?.trim())\n\t\t\t\t\t\t\treturn errorResult(input.action, \"update requires id or a unique selector.\", current);\n\t\t\t\t\t\tconst selected = resolveTaskStepSelector(before.steps, input.id);\n\t\t\t\t\t\tstate = updateTaskStep(\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t\tinput.id,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontent: input.content,\n\t\t\t\t\t\t\t\tactiveForm: input.activeForm,\n\t\t\t\t\t\t\t\tstatus: input.status,\n\t\t\t\t\t\t\t\tpriority: input.priority,\n\t\t\t\t\t\t\t\towner: input.owner,\n\t\t\t\t\t\t\t\tnote: input.note,\n\t\t\t\t\t\t\t\tevidence: input.evidence,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// Exclude the explicitly targeted step: its own status change was requested by\n\t\t\t\t\t\t// the caller, so it is never a \"silent\" demotion even if it moved to pending.\n\t\t\t\t\t\tdemotedStepIds = computeDemotedStepIds(before, state, new Set([selected.id]));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"clear\":\n\t\t\t\t\t\tstate = clearTaskSteps(state, timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"compact\":\n\t\t\t\t\t\tstate = compactTaskSteps(state, timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"list\":\n\t\t\t\t\t\tif (input.clearCompleted) state = compactTaskSteps(state, timestamp);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst isNoopDuplicateAdd = input.action === \"add\" && state === before;\n\t\t\t\tconst mutated = (input.action !== \"list\" || input.clearCompleted === true) && !isNoopDuplicateAdd;\n\t\t\t\tif (mutated) deps.saveTaskStepsState(state);\n\n\t\t\t\tconst stateCounts = counts(state);\n\t\t\t\tconst noticeLines: string[] = [];\n\t\t\t\tif (duplicateStepId) {\n\t\t\t\t\tnoticeLines.push(\n\t\t\t\t\t\t`Duplicate open step ignored; existing ${duplicateStepId} already tracks this content.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (demotedStepIds.length > 0) {\n\t\t\t\t\tnoticeLines.push(`Demoted to pending because another step became active: ${demotedStepIds.join(\", \")}.`);\n\t\t\t\t}\n\t\t\t\tif (stateCounts.verificationNudgeNeeded) {\n\t\t\t\t\tnoticeLines.push(\n\t\t\t\t\t\t\"Reminder: a completed step has no evidence attached; attach evidence via update before treating it as verified.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst notices = noticeLines.length > 0 ? `\\n${noticeLines.join(\"\\n\")}` : \"\";\n\n\t\t\t\tconst headerAction =\n\t\t\t\t\tinput.action === \"list\"\n\t\t\t\t\t\t? \"\"\n\t\t\t\t\t\t: `task_steps ${input.action} ${duplicateStepId ? \"ignored (duplicate)\" : \"recorded\"}.\\n`;\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `${headerAction}${formatTaskSteps(state, {\n\t\t\t\t\t\t\t\tincludeTerminal: input.showCompleted,\n\t\t\t\t\t\t\t\tmaxItems: input.maxItems,\n\t\t\t\t\t\t\t})}${notices}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\taction: input.action,\n\t\t\t\t\t\tapplied: true,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\t...stateCounts,\n\t\t\t\t\t\tshowCompleted: input.showCompleted,\n\t\t\t\t\t\tduplicateOfStepId: duplicateStepId,\n\t\t\t\t\t\tdemotedStepIds: demotedStepIds.length > 0 ? demotedStepIds : undefined,\n\t\t\t\t\t} satisfies TaskStepsToolDetails,\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\treturn errorResult(input.action, error instanceof Error ? error.message : String(error), current);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,86 @@
1
+ import type { ExtensionContext, ToolDefinition } from "../extensions/types.ts";
2
+ import type { LaneWorkerRefusal } from "../model-capability.ts";
3
+ /**
4
+ * Dependencies for {@link dispatchTmuxWorker}, injected from the construction site
5
+ * (`RuntimeBuilder`) so the adapter itself stays a small, unit-testable, faux-tool-driven module
6
+ * with no direct dependency on `AgentSession`/`ExtensionRunner`/`BackgroundLaneController`.
7
+ */
8
+ export interface TmuxDispatchDeps {
9
+ /** Look up a registered tool by name -- the SAME seam the model's own tool calls resolve
10
+ * through (`AgentSession.getToolDefinition` -> `RuntimeBuilder.getToolDefinition`). `undefined`
11
+ * when the tmux extension is not loaded in this session -- an honest, non-fatal skip, not a
12
+ * crash and not a silent no-op. */
13
+ getToolDefinition: (name: string) => ToolDefinition | undefined;
14
+ /** Build a fresh, non-turn-bound `ExtensionContext` for this call
15
+ * (`ExtensionRunner.createContext()`) -- safe to invoke from the goal/idle path, outside any
16
+ * live model turn. */
17
+ createExtensionContext: () => ExtensionContext;
18
+ /**
19
+ * Resolve the tmux extension's own caller-chosen lane id (reconstructed below from the
20
+ * `fire_task` result's `details.job`) to the host's internal `LaneTracker` id -- the id
21
+ * `Requirement.boundLaneId` / `inFlightGoalLaneIds` actually match
22
+ * (`BackgroundLaneController.resolveManagedLaneId`). A deterministic keyed lookup against the
23
+ * dispatch this same call just minted, NOT a racy `getLaneRecords()` diff.
24
+ */
25
+ resolveManagedLaneId: (callerLaneId: string) => string | undefined;
26
+ /** Active goal id, threaded onto the `fire_task` call so the dispatched lane is goal-tagged
27
+ * (`ManagedLaneEvent.goalId`) -- `undefined` when no goal is active (dispatch still proceeds;
28
+ * the resulting lane is simply untagged). */
29
+ getGoalId: () => string | undefined;
30
+ /**
31
+ * Worktree-sync lane-first dispatch (opt-in -- wired ONLY inside `runtime-builder.ts`'s
32
+ * `worktreeSync.enabled` block, via the engine's `createLane`): when present, `dispatchTmuxWorker`
33
+ * creates a fresh worktree-sync lane for this requirement BEFORE issuing any `fire_task` call, so
34
+ * a creation refusal aborts cleanly before any tmux/pane side effect ever runs -- never a
35
+ * half-made lane, never a fabricated worktree. On success the dispatched agent's `cwd` is the new
36
+ * lane worktree and it carries `worktreeLane` (threaded to the tmux extension's launch profile --
37
+ * see `dispatch-grant.ts`'s `buildLaunchProfileFlags`). Absent dep -> existing byte-identical
38
+ * `params.agents` (no `cwd`/`worktreeLane`), exactly as before this field existed.
39
+ */
40
+ createLaneWorktree?: (args: {
41
+ goalId?: string;
42
+ requirementId: string;
43
+ }) => Promise<{
44
+ laneKey: string;
45
+ worktreePath: string;
46
+ } | {
47
+ skipReason: string;
48
+ }>;
49
+ /**
50
+ * Lane-worker capability eligibility (opt-in -- wired only from `runtime-builder.ts`, backed by
51
+ * `AgentSession.getLaneWorkerRefusal`): checked FIRST, before `createLaneWorktree`, so an
52
+ * ineligible model's dispatch is refused before any lane/pane side effect ever runs. This is the
53
+ * parent's best-effort check only -- the dispatched child session refuses authoritatively at its
54
+ * own startup (main.ts) regardless of what the parent decides here. Absent dep -> no capability
55
+ * check runs, byte-identical to before this field existed.
56
+ */
57
+ evaluateWorkerLaneRefusal?: () => LaneWorkerRefusal | undefined;
58
+ }
59
+ /**
60
+ * Classify a `fire_task` launch failure into an honest, stable skip reason. For an UNATTENDED
61
+ * caller (`ctx.hasUI === false`, always true from the goal/idle path) `authorizeLaunch`'s ONLY
62
+ * refusal throw is the no-standing-grant error (`tmux-agent-manager/index.ts`'s `authorizeLaunch`,
63
+ * message containing the stable phrase "no standing grant") -- matched here by substring since the
64
+ * extension throws a plain `Error`, not a tagged error code (documented follow-up hardening, not
65
+ * blocking). Every other thrown error (a bad jobId, a live session-name collision,
66
+ * an environment failure) maps onto the generic `"tmux_dispatch_failed"` -- still surfaced through
67
+ * the existing `dispatchSkipReason` contract, never a crash and never a silently-ignored failure.
68
+ */
69
+ export declare function classifyDispatchError(error: unknown): string;
70
+ /**
71
+ * Structurally dispatch ONE persistent tmux worker agent for a single goal requirement, by
72
+ * invoking the SAME `tmux_agent_manager` `fire_task` tool call the model itself would make. Core
73
+ * -- not model discretion -- decides WHEN this fires (the goal tool's `dispatchTarget:"tmux"`
74
+ * routing gates the call); the launch itself always still goes through the extension's own
75
+ * grant-gated `authorizeLaunch`, so an ungranted unattended call is honestly refused here (never a
76
+ * silent launch, never a fabricated laneId) and the refusal is surfaced through the existing
77
+ * `dispatchSkipReason` contract on the goal tool's response.
78
+ */
79
+ export declare function dispatchTmuxWorker(deps: TmuxDispatchDeps, args: {
80
+ requirementId: string;
81
+ instructions: string;
82
+ }): Promise<{
83
+ laneId?: string;
84
+ skipReason?: string;
85
+ }>;
86
+ //# sourceMappingURL=tmux-dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-dispatch.d.ts","sourceRoot":"","sources":["../../../src/core/tools/tmux-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAChC;;;uCAGmC;IACnC,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,cAAc,GAAG,SAAS,CAAC;IAChE;;0BAEsB;IACtB,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;IAC/C;;;;;;OAMG;IACH,oBAAoB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACnE;;iDAE6C;IAC7C,SAAS,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACpC;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,MAAM,CAAC;KACtB,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF;;;;;;;OAOG;IACH,yBAAyB,CAAC,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAC;CAChE;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG5D;AASD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACvC,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACnD,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAqEnD","sourcesContent":["import type { ExtensionContext, ToolDefinition } from \"../extensions/types.ts\";\nimport type { LaneWorkerRefusal } from \"../model-capability.ts\";\n\n/**\n * Dependencies for {@link dispatchTmuxWorker}, injected from the construction site\n * (`RuntimeBuilder`) so the adapter itself stays a small, unit-testable, faux-tool-driven module\n * with no direct dependency on `AgentSession`/`ExtensionRunner`/`BackgroundLaneController`.\n */\nexport interface TmuxDispatchDeps {\n\t/** Look up a registered tool by name -- the SAME seam the model's own tool calls resolve\n\t * through (`AgentSession.getToolDefinition` -> `RuntimeBuilder.getToolDefinition`). `undefined`\n\t * when the tmux extension is not loaded in this session -- an honest, non-fatal skip, not a\n\t * crash and not a silent no-op. */\n\tgetToolDefinition: (name: string) => ToolDefinition | undefined;\n\t/** Build a fresh, non-turn-bound `ExtensionContext` for this call\n\t * (`ExtensionRunner.createContext()`) -- safe to invoke from the goal/idle path, outside any\n\t * live model turn. */\n\tcreateExtensionContext: () => ExtensionContext;\n\t/**\n\t * Resolve the tmux extension's own caller-chosen lane id (reconstructed below from the\n\t * `fire_task` result's `details.job`) to the host's internal `LaneTracker` id -- the id\n\t * `Requirement.boundLaneId` / `inFlightGoalLaneIds` actually match\n\t * (`BackgroundLaneController.resolveManagedLaneId`). A deterministic keyed lookup against the\n\t * dispatch this same call just minted, NOT a racy `getLaneRecords()` diff.\n\t */\n\tresolveManagedLaneId: (callerLaneId: string) => string | undefined;\n\t/** Active goal id, threaded onto the `fire_task` call so the dispatched lane is goal-tagged\n\t * (`ManagedLaneEvent.goalId`) -- `undefined` when no goal is active (dispatch still proceeds;\n\t * the resulting lane is simply untagged). */\n\tgetGoalId: () => string | undefined;\n\t/**\n\t * Worktree-sync lane-first dispatch (opt-in -- wired ONLY inside `runtime-builder.ts`'s\n\t * `worktreeSync.enabled` block, via the engine's `createLane`): when present, `dispatchTmuxWorker`\n\t * creates a fresh worktree-sync lane for this requirement BEFORE issuing any `fire_task` call, so\n\t * a creation refusal aborts cleanly before any tmux/pane side effect ever runs -- never a\n\t * half-made lane, never a fabricated worktree. On success the dispatched agent's `cwd` is the new\n\t * lane worktree and it carries `worktreeLane` (threaded to the tmux extension's launch profile --\n\t * see `dispatch-grant.ts`'s `buildLaunchProfileFlags`). Absent dep -> existing byte-identical\n\t * `params.agents` (no `cwd`/`worktreeLane`), exactly as before this field existed.\n\t */\n\tcreateLaneWorktree?: (args: {\n\t\tgoalId?: string;\n\t\trequirementId: string;\n\t}) => Promise<{ laneKey: string; worktreePath: string } | { skipReason: string }>;\n\t/**\n\t * Lane-worker capability eligibility (opt-in -- wired only from `runtime-builder.ts`, backed by\n\t * `AgentSession.getLaneWorkerRefusal`): checked FIRST, before `createLaneWorktree`, so an\n\t * ineligible model's dispatch is refused before any lane/pane side effect ever runs. This is the\n\t * parent's best-effort check only -- the dispatched child session refuses authoritatively at its\n\t * own startup (main.ts) regardless of what the parent decides here. Absent dep -> no capability\n\t * check runs, byte-identical to before this field existed.\n\t */\n\tevaluateWorkerLaneRefusal?: () => LaneWorkerRefusal | undefined;\n}\n\n/**\n * Classify a `fire_task` launch failure into an honest, stable skip reason. For an UNATTENDED\n * caller (`ctx.hasUI === false`, always true from the goal/idle path) `authorizeLaunch`'s ONLY\n * refusal throw is the no-standing-grant error (`tmux-agent-manager/index.ts`'s `authorizeLaunch`,\n * message containing the stable phrase \"no standing grant\") -- matched here by substring since the\n * extension throws a plain `Error`, not a tagged error code (documented follow-up hardening, not\n * blocking). Every other thrown error (a bad jobId, a live session-name collision,\n * an environment failure) maps onto the generic `\"tmux_dispatch_failed\"` -- still surfaced through\n * the existing `dispatchSkipReason` contract, never a crash and never a silently-ignored failure.\n */\nexport function classifyDispatchError(error: unknown): string {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn message.includes(\"no standing grant\") ? \"no_standing_grant\" : \"tmux_dispatch_failed\";\n}\n\ninterface FireTaskResultDetails {\n\tjob?: {\n\t\tid?: string;\n\t\tagents?: ReadonlyArray<{ id?: string }>;\n\t};\n}\n\n/**\n * Structurally dispatch ONE persistent tmux worker agent for a single goal requirement, by\n * invoking the SAME `tmux_agent_manager` `fire_task` tool call the model itself would make. Core\n * -- not model discretion -- decides WHEN this fires (the goal tool's `dispatchTarget:\"tmux\"`\n * routing gates the call); the launch itself always still goes through the extension's own\n * grant-gated `authorizeLaunch`, so an ungranted unattended call is honestly refused here (never a\n * silent launch, never a fabricated laneId) and the refusal is surfaced through the existing\n * `dispatchSkipReason` contract on the goal tool's response.\n */\nexport async function dispatchTmuxWorker(\n\tdeps: TmuxDispatchDeps,\n\targs: { requirementId: string; instructions: string },\n): Promise<{ laneId?: string; skipReason?: string }> {\n\t// Capability check FIRST -- before the tool-definition lookup and before createLaneWorktree --\n\t// so an ineligible model's dispatch is refused with zero lane/pane/tool side effect. The coarse\n\t// \"worker_capability_insufficient\" skip reason is the stable contract code on the goal tool's\n\t// response; the granular reason (class/window/tool-calling) lives only in the refusal object\n\t// itself, surfaced by the caller for logging (see model-capability.ts's formatLaneWorkerRefusal).\n\tif (deps.evaluateWorkerLaneRefusal?.() !== undefined) {\n\t\treturn { skipReason: \"worker_capability_insufficient\" };\n\t}\n\n\tconst toolDef = deps.getToolDefinition(\"tmux_agent_manager\");\n\tif (!toolDef) return { skipReason: \"tmux_extension_not_loaded\" };\n\n\tconst goalId = deps.getGoalId();\n\tconst ctx = deps.createExtensionContext();\n\n\t// Lane-first: when worktree-sync is wired, create the lane BEFORE any fire_task call so a\n\t// creation refusal (max lanes, invalid key, git error, ...) aborts cleanly with no tmux/pane side\n\t// effect ever having run. The specific engine refusal code is deliberately collapsed onto the one\n\t// stable `worktree_create_failed` skip reason -- same narrow `{laneId?, skipReason?}` contract\n\t// every other skip path here already uses; the engine's own detail lives in its audit log, not in\n\t// this narrow return.\n\tlet laneWorktree: { laneKey: string; worktreePath: string } | undefined;\n\tif (deps.createLaneWorktree) {\n\t\tconst created = await deps.createLaneWorktree({ goalId, requirementId: args.requirementId });\n\t\tif (\"skipReason\" in created) return { skipReason: \"worktree_create_failed\" };\n\t\tlaneWorktree = created;\n\t}\n\n\t// Single-agent 1:1 mapping: a bare fire_task with no `agents` defaults to a THREE-agent\n\t// team (pi/agy/codex -- `DEFAULT_AGENT_PROVIDERS`), which would mint three lanes for one\n\t// requirement. A goal-bound dispatch must map to exactly one lane, so `agents` is always\n\t// passed explicitly here.\n\tconst params = {\n\t\taction: \"fire_task\",\n\t\ttask: args.instructions,\n\t\tgoalId,\n\t\tagents: [\n\t\t\tlaneWorktree\n\t\t\t\t? {\n\t\t\t\t\t\tprovider: \"pi\",\n\t\t\t\t\t\tname: \"goal-worker\",\n\t\t\t\t\t\tcwd: laneWorktree.worktreePath,\n\t\t\t\t\t\tworktreeLane: laneWorktree.laneKey,\n\t\t\t\t\t}\n\t\t\t\t: { provider: \"pi\", name: \"goal-worker\" },\n\t\t],\n\t};\n\tconst toolCallId = `goal-dispatch:${goalId ?? \"?\"}:${args.requirementId}`;\n\n\tlet result: Awaited<ReturnType<ToolDefinition[\"execute\"]>>;\n\ttry {\n\t\tresult = await toolDef.execute(toolCallId, params, ctx.signal, undefined, ctx);\n\t} catch (error) {\n\t\treturn { skipReason: classifyDispatchError(error) };\n\t}\n\n\t// Correlate: the extension reports its dispatch under its OWN caller-chosen laneId\n\t// (`tmux:${job.id}:${agent.id}`, reconstructed here from the tool result's `details.job` --\n\t// the same format `agentLaneId` builds extension-side) which the host's `recordManagedLane`\n\t// bridge keys `_managedLaneDispatches` by; `resolveManagedLaneId` reads that keyed map for the\n\t// internal id the reducer/continuation machinery actually match on.\n\tconst job = (result.details as FireTaskResultDetails | undefined)?.job;\n\tconst primary = job?.agents?.[0];\n\tif (!job?.id || !primary?.id) return { skipReason: \"tmux_dispatch_incomplete\" };\n\n\tconst callerLaneId = `tmux:${job.id}:${primary.id}`;\n\tconst laneId = deps.resolveManagedLaneId(callerLaneId);\n\treturn laneId ? { laneId } : { skipReason: \"lane_correlation_failed\" };\n}\n"]}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Classify a `fire_task` launch failure into an honest, stable skip reason. For an UNATTENDED
3
+ * caller (`ctx.hasUI === false`, always true from the goal/idle path) `authorizeLaunch`'s ONLY
4
+ * refusal throw is the no-standing-grant error (`tmux-agent-manager/index.ts`'s `authorizeLaunch`,
5
+ * message containing the stable phrase "no standing grant") -- matched here by substring since the
6
+ * extension throws a plain `Error`, not a tagged error code (documented follow-up hardening, not
7
+ * blocking). Every other thrown error (a bad jobId, a live session-name collision,
8
+ * an environment failure) maps onto the generic `"tmux_dispatch_failed"` -- still surfaced through
9
+ * the existing `dispatchSkipReason` contract, never a crash and never a silently-ignored failure.
10
+ */
11
+ export function classifyDispatchError(error) {
12
+ const message = error instanceof Error ? error.message : String(error);
13
+ return message.includes("no standing grant") ? "no_standing_grant" : "tmux_dispatch_failed";
14
+ }
15
+ /**
16
+ * Structurally dispatch ONE persistent tmux worker agent for a single goal requirement, by
17
+ * invoking the SAME `tmux_agent_manager` `fire_task` tool call the model itself would make. Core
18
+ * -- not model discretion -- decides WHEN this fires (the goal tool's `dispatchTarget:"tmux"`
19
+ * routing gates the call); the launch itself always still goes through the extension's own
20
+ * grant-gated `authorizeLaunch`, so an ungranted unattended call is honestly refused here (never a
21
+ * silent launch, never a fabricated laneId) and the refusal is surfaced through the existing
22
+ * `dispatchSkipReason` contract on the goal tool's response.
23
+ */
24
+ export async function dispatchTmuxWorker(deps, args) {
25
+ // Capability check FIRST -- before the tool-definition lookup and before createLaneWorktree --
26
+ // so an ineligible model's dispatch is refused with zero lane/pane/tool side effect. The coarse
27
+ // "worker_capability_insufficient" skip reason is the stable contract code on the goal tool's
28
+ // response; the granular reason (class/window/tool-calling) lives only in the refusal object
29
+ // itself, surfaced by the caller for logging (see model-capability.ts's formatLaneWorkerRefusal).
30
+ if (deps.evaluateWorkerLaneRefusal?.() !== undefined) {
31
+ return { skipReason: "worker_capability_insufficient" };
32
+ }
33
+ const toolDef = deps.getToolDefinition("tmux_agent_manager");
34
+ if (!toolDef)
35
+ return { skipReason: "tmux_extension_not_loaded" };
36
+ const goalId = deps.getGoalId();
37
+ const ctx = deps.createExtensionContext();
38
+ // Lane-first: when worktree-sync is wired, create the lane BEFORE any fire_task call so a
39
+ // creation refusal (max lanes, invalid key, git error, ...) aborts cleanly with no tmux/pane side
40
+ // effect ever having run. The specific engine refusal code is deliberately collapsed onto the one
41
+ // stable `worktree_create_failed` skip reason -- same narrow `{laneId?, skipReason?}` contract
42
+ // every other skip path here already uses; the engine's own detail lives in its audit log, not in
43
+ // this narrow return.
44
+ let laneWorktree;
45
+ if (deps.createLaneWorktree) {
46
+ const created = await deps.createLaneWorktree({ goalId, requirementId: args.requirementId });
47
+ if ("skipReason" in created)
48
+ return { skipReason: "worktree_create_failed" };
49
+ laneWorktree = created;
50
+ }
51
+ // Single-agent 1:1 mapping: a bare fire_task with no `agents` defaults to a THREE-agent
52
+ // team (pi/agy/codex -- `DEFAULT_AGENT_PROVIDERS`), which would mint three lanes for one
53
+ // requirement. A goal-bound dispatch must map to exactly one lane, so `agents` is always
54
+ // passed explicitly here.
55
+ const params = {
56
+ action: "fire_task",
57
+ task: args.instructions,
58
+ goalId,
59
+ agents: [
60
+ laneWorktree
61
+ ? {
62
+ provider: "pi",
63
+ name: "goal-worker",
64
+ cwd: laneWorktree.worktreePath,
65
+ worktreeLane: laneWorktree.laneKey,
66
+ }
67
+ : { provider: "pi", name: "goal-worker" },
68
+ ],
69
+ };
70
+ const toolCallId = `goal-dispatch:${goalId ?? "?"}:${args.requirementId}`;
71
+ let result;
72
+ try {
73
+ result = await toolDef.execute(toolCallId, params, ctx.signal, undefined, ctx);
74
+ }
75
+ catch (error) {
76
+ return { skipReason: classifyDispatchError(error) };
77
+ }
78
+ // Correlate: the extension reports its dispatch under its OWN caller-chosen laneId
79
+ // (`tmux:${job.id}:${agent.id}`, reconstructed here from the tool result's `details.job` --
80
+ // the same format `agentLaneId` builds extension-side) which the host's `recordManagedLane`
81
+ // bridge keys `_managedLaneDispatches` by; `resolveManagedLaneId` reads that keyed map for the
82
+ // internal id the reducer/continuation machinery actually match on.
83
+ const job = result.details?.job;
84
+ const primary = job?.agents?.[0];
85
+ if (!job?.id || !primary?.id)
86
+ return { skipReason: "tmux_dispatch_incomplete" };
87
+ const callerLaneId = `tmux:${job.id}:${primary.id}`;
88
+ const laneId = deps.resolveManagedLaneId(callerLaneId);
89
+ return laneId ? { laneId } : { skipReason: "lane_correlation_failed" };
90
+ }
91
+ //# sourceMappingURL=tmux-dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-dispatch.js","sourceRoot":"","sources":["../../../src/core/tools/tmux-dispatch.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExtensionContext, ToolDefinition } from \"../extensions/types.ts\";\nimport type { LaneWorkerRefusal } from \"../model-capability.ts\";\n\n/**\n * Dependencies for {@link dispatchTmuxWorker}, injected from the construction site\n * (`RuntimeBuilder`) so the adapter itself stays a small, unit-testable, faux-tool-driven module\n * with no direct dependency on `AgentSession`/`ExtensionRunner`/`BackgroundLaneController`.\n */\nexport interface TmuxDispatchDeps {\n\t/** Look up a registered tool by name -- the SAME seam the model's own tool calls resolve\n\t * through (`AgentSession.getToolDefinition` -> `RuntimeBuilder.getToolDefinition`). `undefined`\n\t * when the tmux extension is not loaded in this session -- an honest, non-fatal skip, not a\n\t * crash and not a silent no-op. */\n\tgetToolDefinition: (name: string) => ToolDefinition | undefined;\n\t/** Build a fresh, non-turn-bound `ExtensionContext` for this call\n\t * (`ExtensionRunner.createContext()`) -- safe to invoke from the goal/idle path, outside any\n\t * live model turn. */\n\tcreateExtensionContext: () => ExtensionContext;\n\t/**\n\t * Resolve the tmux extension's own caller-chosen lane id (reconstructed below from the\n\t * `fire_task` result's `details.job`) to the host's internal `LaneTracker` id -- the id\n\t * `Requirement.boundLaneId` / `inFlightGoalLaneIds` actually match\n\t * (`BackgroundLaneController.resolveManagedLaneId`). A deterministic keyed lookup against the\n\t * dispatch this same call just minted, NOT a racy `getLaneRecords()` diff.\n\t */\n\tresolveManagedLaneId: (callerLaneId: string) => string | undefined;\n\t/** Active goal id, threaded onto the `fire_task` call so the dispatched lane is goal-tagged\n\t * (`ManagedLaneEvent.goalId`) -- `undefined` when no goal is active (dispatch still proceeds;\n\t * the resulting lane is simply untagged). */\n\tgetGoalId: () => string | undefined;\n\t/**\n\t * Worktree-sync lane-first dispatch (opt-in -- wired ONLY inside `runtime-builder.ts`'s\n\t * `worktreeSync.enabled` block, via the engine's `createLane`): when present, `dispatchTmuxWorker`\n\t * creates a fresh worktree-sync lane for this requirement BEFORE issuing any `fire_task` call, so\n\t * a creation refusal aborts cleanly before any tmux/pane side effect ever runs -- never a\n\t * half-made lane, never a fabricated worktree. On success the dispatched agent's `cwd` is the new\n\t * lane worktree and it carries `worktreeLane` (threaded to the tmux extension's launch profile --\n\t * see `dispatch-grant.ts`'s `buildLaunchProfileFlags`). Absent dep -> existing byte-identical\n\t * `params.agents` (no `cwd`/`worktreeLane`), exactly as before this field existed.\n\t */\n\tcreateLaneWorktree?: (args: {\n\t\tgoalId?: string;\n\t\trequirementId: string;\n\t}) => Promise<{ laneKey: string; worktreePath: string } | { skipReason: string }>;\n\t/**\n\t * Lane-worker capability eligibility (opt-in -- wired only from `runtime-builder.ts`, backed by\n\t * `AgentSession.getLaneWorkerRefusal`): checked FIRST, before `createLaneWorktree`, so an\n\t * ineligible model's dispatch is refused before any lane/pane side effect ever runs. This is the\n\t * parent's best-effort check only -- the dispatched child session refuses authoritatively at its\n\t * own startup (main.ts) regardless of what the parent decides here. Absent dep -> no capability\n\t * check runs, byte-identical to before this field existed.\n\t */\n\tevaluateWorkerLaneRefusal?: () => LaneWorkerRefusal | undefined;\n}\n\n/**\n * Classify a `fire_task` launch failure into an honest, stable skip reason. For an UNATTENDED\n * caller (`ctx.hasUI === false`, always true from the goal/idle path) `authorizeLaunch`'s ONLY\n * refusal throw is the no-standing-grant error (`tmux-agent-manager/index.ts`'s `authorizeLaunch`,\n * message containing the stable phrase \"no standing grant\") -- matched here by substring since the\n * extension throws a plain `Error`, not a tagged error code (documented follow-up hardening, not\n * blocking). Every other thrown error (a bad jobId, a live session-name collision,\n * an environment failure) maps onto the generic `\"tmux_dispatch_failed\"` -- still surfaced through\n * the existing `dispatchSkipReason` contract, never a crash and never a silently-ignored failure.\n */\nexport function classifyDispatchError(error: unknown): string {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn message.includes(\"no standing grant\") ? \"no_standing_grant\" : \"tmux_dispatch_failed\";\n}\n\ninterface FireTaskResultDetails {\n\tjob?: {\n\t\tid?: string;\n\t\tagents?: ReadonlyArray<{ id?: string }>;\n\t};\n}\n\n/**\n * Structurally dispatch ONE persistent tmux worker agent for a single goal requirement, by\n * invoking the SAME `tmux_agent_manager` `fire_task` tool call the model itself would make. Core\n * -- not model discretion -- decides WHEN this fires (the goal tool's `dispatchTarget:\"tmux\"`\n * routing gates the call); the launch itself always still goes through the extension's own\n * grant-gated `authorizeLaunch`, so an ungranted unattended call is honestly refused here (never a\n * silent launch, never a fabricated laneId) and the refusal is surfaced through the existing\n * `dispatchSkipReason` contract on the goal tool's response.\n */\nexport async function dispatchTmuxWorker(\n\tdeps: TmuxDispatchDeps,\n\targs: { requirementId: string; instructions: string },\n): Promise<{ laneId?: string; skipReason?: string }> {\n\t// Capability check FIRST -- before the tool-definition lookup and before createLaneWorktree --\n\t// so an ineligible model's dispatch is refused with zero lane/pane/tool side effect. The coarse\n\t// \"worker_capability_insufficient\" skip reason is the stable contract code on the goal tool's\n\t// response; the granular reason (class/window/tool-calling) lives only in the refusal object\n\t// itself, surfaced by the caller for logging (see model-capability.ts's formatLaneWorkerRefusal).\n\tif (deps.evaluateWorkerLaneRefusal?.() !== undefined) {\n\t\treturn { skipReason: \"worker_capability_insufficient\" };\n\t}\n\n\tconst toolDef = deps.getToolDefinition(\"tmux_agent_manager\");\n\tif (!toolDef) return { skipReason: \"tmux_extension_not_loaded\" };\n\n\tconst goalId = deps.getGoalId();\n\tconst ctx = deps.createExtensionContext();\n\n\t// Lane-first: when worktree-sync is wired, create the lane BEFORE any fire_task call so a\n\t// creation refusal (max lanes, invalid key, git error, ...) aborts cleanly with no tmux/pane side\n\t// effect ever having run. The specific engine refusal code is deliberately collapsed onto the one\n\t// stable `worktree_create_failed` skip reason -- same narrow `{laneId?, skipReason?}` contract\n\t// every other skip path here already uses; the engine's own detail lives in its audit log, not in\n\t// this narrow return.\n\tlet laneWorktree: { laneKey: string; worktreePath: string } | undefined;\n\tif (deps.createLaneWorktree) {\n\t\tconst created = await deps.createLaneWorktree({ goalId, requirementId: args.requirementId });\n\t\tif (\"skipReason\" in created) return { skipReason: \"worktree_create_failed\" };\n\t\tlaneWorktree = created;\n\t}\n\n\t// Single-agent 1:1 mapping: a bare fire_task with no `agents` defaults to a THREE-agent\n\t// team (pi/agy/codex -- `DEFAULT_AGENT_PROVIDERS`), which would mint three lanes for one\n\t// requirement. A goal-bound dispatch must map to exactly one lane, so `agents` is always\n\t// passed explicitly here.\n\tconst params = {\n\t\taction: \"fire_task\",\n\t\ttask: args.instructions,\n\t\tgoalId,\n\t\tagents: [\n\t\t\tlaneWorktree\n\t\t\t\t? {\n\t\t\t\t\t\tprovider: \"pi\",\n\t\t\t\t\t\tname: \"goal-worker\",\n\t\t\t\t\t\tcwd: laneWorktree.worktreePath,\n\t\t\t\t\t\tworktreeLane: laneWorktree.laneKey,\n\t\t\t\t\t}\n\t\t\t\t: { provider: \"pi\", name: \"goal-worker\" },\n\t\t],\n\t};\n\tconst toolCallId = `goal-dispatch:${goalId ?? \"?\"}:${args.requirementId}`;\n\n\tlet result: Awaited<ReturnType<ToolDefinition[\"execute\"]>>;\n\ttry {\n\t\tresult = await toolDef.execute(toolCallId, params, ctx.signal, undefined, ctx);\n\t} catch (error) {\n\t\treturn { skipReason: classifyDispatchError(error) };\n\t}\n\n\t// Correlate: the extension reports its dispatch under its OWN caller-chosen laneId\n\t// (`tmux:${job.id}:${agent.id}`, reconstructed here from the tool result's `details.job` --\n\t// the same format `agentLaneId` builds extension-side) which the host's `recordManagedLane`\n\t// bridge keys `_managedLaneDispatches` by; `resolveManagedLaneId` reads that keyed map for the\n\t// internal id the reducer/continuation machinery actually match on.\n\tconst job = (result.details as FireTaskResultDetails | undefined)?.job;\n\tconst primary = job?.agents?.[0];\n\tif (!job?.id || !primary?.id) return { skipReason: \"tmux_dispatch_incomplete\" };\n\n\tconst callerLaneId = `tmux:${job.id}:${primary.id}`;\n\tconst laneId = deps.resolveManagedLaneId(callerLaneId);\n\treturn laneId ? { laneId } : { skipReason: \"lane_correlation_failed\" };\n}\n"]}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The `python-engine` tier: per-command spawn of the bundled `pi-shell-engine` (D3 in the
3
+ * blueprint). Wires the frozen §1.2/§1.3 stdin-JSON-request / 0x1e-framed-stdout protocol into a
4
+ * `BashOperations`, threads `WindowsShellState` through both directions (D4), and degrades to a
5
+ * named, actionable error when the Python runtime is not ready — the PowerShell floor keeps
6
+ * working regardless (the router never routes simple commands here when the engine is off).
7
+ */
8
+ import type { ChildProcess, SpawnOptions } from "node:child_process";
9
+ import { type PythonRuntimeOutcome } from "../python-runtime.ts";
10
+ import type { BashOperations } from "./bash.ts";
11
+ import { type WindowsShellState } from "./windows-shell-state.ts";
12
+ /** The full §1.3 control-frame shape. */
13
+ export interface WindowsShellEngineFrame {
14
+ exitCode: number;
15
+ cwd: string;
16
+ envDelta: Record<string, string | null>;
17
+ unsupported: {
18
+ code: "unsupported";
19
+ construct: string;
20
+ message: string;
21
+ } | null;
22
+ }
23
+ export declare class WindowsShellEngineFailure extends Error {
24
+ readonly capturedOutput: string;
25
+ constructor(message: string, capturedOutput: string);
26
+ }
27
+ export interface WindowsShellEngineOptions {
28
+ /** Override for tests: resolves the Python runtime outcome. Default: `ensurePythonRuntime`. */
29
+ resolveRuntime?: () => Promise<PythonRuntimeOutcome>;
30
+ /** Override for tests: absolute path to the engine's `main.py`. Default: the bundled runtime. */
31
+ engineScriptPath?: string;
32
+ /** Override for tests: the per-session state store lookup. Default: the shared module store. */
33
+ getState?: (sessionKey: string) => WindowsShellState;
34
+ /** Override for tests: spawns the engine child process. Default: `spawnProcess` (cross-spawn on win32). */
35
+ spawn?: (command: string, args: string[], options: SpawnOptions) => ChildProcess;
36
+ }
37
+ /**
38
+ * Create the `python-engine` tier `BashOperations` for one bash-tool session. `exec`'s `command`
39
+ * argument is the RAW Bash source from the `python-engine` route (never PowerShell-translated).
40
+ */
41
+ export declare function createWindowsShellEngineOperations(sessionKey: string, options?: WindowsShellEngineOptions): BashOperations;
42
+ //# sourceMappingURL=windows-shell-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows-shell-engine.d.ts","sourceRoot":"","sources":["../../../src/core/tools/windows-shell-engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKrE,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAKN,KAAK,iBAAiB,EACtB,MAAM,0BAA0B,CAAC;AAIlC,0CAAyC;AACzC,MAAM,WAAW,uBAAuB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACxC,WAAW,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAChF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IACnD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,YAAY,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAIlD;CACD;AAuCD,MAAM,WAAW,yBAAyB;IACzC,+FAA+F;IAC/F,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACrD,iGAAiG;IACjG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gGAAgG;IAChG,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,iBAAiB,CAAC;IACrD,2GAA2G;IAC3G,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,YAAY,KAAK,YAAY,CAAC;CACjF;AAUD;;;GAGG;AACH,wBAAgB,kCAAkC,CACjD,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,yBAA8B,GACrC,cAAc,CAwFhB","sourcesContent":["/**\n * The `python-engine` tier: per-command spawn of the bundled `pi-shell-engine` (D3 in the\n * blueprint). Wires the frozen §1.2/§1.3 stdin-JSON-request / 0x1e-framed-stdout protocol into a\n * `BashOperations`, threads `WindowsShellState` through both directions (D4), and degrades to a\n * named, actionable error when the Python runtime is not ready — the PowerShell floor keeps\n * working regardless (the router never routes simple commands here when the engine is off).\n */\n\nimport type { ChildProcess, SpawnOptions } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { getBundledResourcesDir } from \"../../config.ts\";\nimport { spawnProcess, waitForChildProcessWithTermination } from \"../../utils/child-process.ts\";\nimport { getShellEnv, trackDetachedChildPid, untrackDetachedChildPid } from \"../../utils/shell.ts\";\nimport { ensurePythonRuntime, type PythonRuntimeOutcome } from \"../python-runtime.ts\";\nimport type { BashOperations } from \"./bash.ts\";\nimport {\n\tapplyEngineFrame,\n\tgetOrCreateWindowsShellState,\n\tmergeEffectiveEnv,\n\tresolveEffectiveCwd,\n\ttype WindowsShellState,\n} from \"./windows-shell-state.ts\";\n\nconst ENGINE_FRAME_SENTINEL = 0x1e;\n\n/** The full §1.3 control-frame shape. */\nexport interface WindowsShellEngineFrame {\n\texitCode: number;\n\tcwd: string;\n\tenvDelta: Record<string, string | null>;\n\tunsupported: { code: \"unsupported\"; construct: string; message: string } | null;\n}\n\nexport class WindowsShellEngineFailure extends Error {\n\treadonly capturedOutput: string;\n\n\tconstructor(message: string, capturedOutput: string) {\n\t\tsuper(message);\n\t\tthis.name = \"WindowsShellEngineFailure\";\n\t\tthis.capturedOutput = capturedOutput;\n\t}\n}\n\nfunction resolveEngineScriptPath(): string {\n\treturn join(getBundledResourcesDir(), \"runtimes\", \"pi-shell-engine\", \"main.py\");\n}\n\nconst MAX_CONTROL_FRAME_BYTES = 64 * 1024;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Parse the terminal frame from the dedicated control stream. Command output is never retained here. */\nfunction parseControlFrame(buffer: Buffer): WindowsShellEngineFrame | undefined {\n\tif (buffer.length < 2 || buffer[buffer.length - 1] !== ENGINE_FRAME_SENTINEL) return undefined;\n\tconst openIndex = buffer.lastIndexOf(ENGINE_FRAME_SENTINEL, buffer.length - 2);\n\tif (openIndex === -1) return undefined;\n\ttry {\n\t\tconst raw: unknown = JSON.parse(buffer.subarray(openIndex + 1, buffer.length - 1).toString(\"utf8\"));\n\t\tif (!isRecord(raw)) return undefined;\n\t\tif (typeof raw.exitCode !== \"number\" || !Number.isInteger(raw.exitCode)) return undefined;\n\t\tif (typeof raw.cwd !== \"string\" || !isRecord(raw.envDelta)) return undefined;\n\t\tif (raw.unsupported !== null && !isRecord(raw.unsupported)) return undefined;\n\t\tif (\n\t\t\traw.unsupported !== null &&\n\t\t\t(raw.unsupported.code !== \"unsupported\" ||\n\t\t\t\ttypeof raw.unsupported.construct !== \"string\" ||\n\t\t\t\ttypeof raw.unsupported.message !== \"string\")\n\t\t)\n\t\t\treturn undefined;\n\t\tfor (const value of Object.values(raw.envDelta)) {\n\t\t\tif (value !== null && typeof value !== \"string\") return undefined;\n\t\t}\n\t\treturn raw as unknown as WindowsShellEngineFrame;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport interface WindowsShellEngineOptions {\n\t/** Override for tests: resolves the Python runtime outcome. Default: `ensurePythonRuntime`. */\n\tresolveRuntime?: () => Promise<PythonRuntimeOutcome>;\n\t/** Override for tests: absolute path to the engine's `main.py`. Default: the bundled runtime. */\n\tengineScriptPath?: string;\n\t/** Override for tests: the per-session state store lookup. Default: the shared module store. */\n\tgetState?: (sessionKey: string) => WindowsShellState;\n\t/** Override for tests: spawns the engine child process. Default: `spawnProcess` (cross-spawn on win32). */\n\tspawn?: (command: string, args: string[], options: SpawnOptions) => ChildProcess;\n}\n\nfunction degradationError(\n\toutcome: Extract<PythonRuntimeOutcome, { status: \"offline\" | \"uv-unavailable\" | \"python-unavailable\" }>,\n) {\n\treturn new Error(\n\t\t`The Windows shell engine (Python) is unavailable: ${outcome.reason} The simple-command PowerShell floor still works; fix the Python runtime (uv/network) to restore pipelines, redirection, expansion, and chaining.`,\n\t);\n}\n\n/**\n * Create the `python-engine` tier `BashOperations` for one bash-tool session. `exec`'s `command`\n * argument is the RAW Bash source from the `python-engine` route (never PowerShell-translated).\n */\nexport function createWindowsShellEngineOperations(\n\tsessionKey: string,\n\toptions: WindowsShellEngineOptions = {},\n): BashOperations {\n\tconst resolveRuntime = options.resolveRuntime ?? (() => ensurePythonRuntime({ silent: true }));\n\tconst engineScriptPath = options.engineScriptPath ?? resolveEngineScriptPath();\n\tconst getState = options.getState ?? getOrCreateWindowsShellState;\n\tconst spawn = options.spawn ?? spawnProcess;\n\n\treturn {\n\t\tasync exec(command, cwd, { onData, signal, timeout, env }) {\n\t\t\tconst runtime = await resolveRuntime();\n\t\t\tif (runtime.status !== \"ready\") throw degradationError(runtime);\n\n\t\t\tconst state = getState(sessionKey);\n\t\t\tconst effectiveCwd = resolveEffectiveCwd(state, cwd);\n\t\t\tconst effectiveEnv = mergeEffectiveEnv(state, env ?? getShellEnv());\n\t\t\tconst timeoutMs = timeout !== undefined && timeout > 0 ? timeout * 1000 : undefined;\n\t\t\t// The engine's own soft deadline must fire and emit its cooperative exit-124 frame (which\n\t\t\t// preserves partial output) BEFORE the hard tree-kill backstop below, or the hard kill\n\t\t\t// always wins the race and the soft path never gets to run. Give it a head start; the hard\n\t\t\t// kill still bounds a hung engine that never reads its own deadline.\n\t\t\tconst requestTimeoutMs = timeoutMs !== undefined ? Math.max(timeoutMs - 500, 500) : undefined;\n\n\t\t\tconst request = {\n\t\t\t\tcommand,\n\t\t\t\tcwd: effectiveCwd,\n\t\t\t\tenv: effectiveEnv,\n\t\t\t\t...(requestTimeoutMs !== undefined ? { timeoutMs: requestTimeoutMs } : {}),\n\t\t\t};\n\n\t\t\tconst child = spawn(runtime.pythonPath, [\"-B\", engineScriptPath], {\n\t\t\t\tcwd: effectiveCwd,\n\t\t\t\tenv: {\n\t\t\t\t\t...effectiveEnv,\n\t\t\t\t\tPYTHONDONTWRITEBYTECODE: \"1\",\n\t\t\t\t\tPYTHONIOENCODING: \"utf-8\",\n\t\t\t\t\tPYTHONUNBUFFERED: \"1\",\n\t\t\t\t\tPYTHONUTF8: \"1\",\n\t\t\t\t},\n\t\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\t});\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\n\t\t\tlet controlBuffer: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n\t\t\tlet controlOverflow = false;\n\n\t\t\t// Command output is the data stream: forward it immediately and never retain a\n\t\t\t// second full copy merely to find the terminal frame.\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tonData(chunk);\n\t\t\t});\n\t\t\t// stderr is the bounded control stream. The Python engine routes command stderr\n\t\t\t// into stdout, so this channel contains only the terminal frame/diagnostics.\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (controlOverflow) return;\n\t\t\t\tif (controlBuffer.length + chunk.length > MAX_CONTROL_FRAME_BYTES) {\n\t\t\t\t\tcontrolOverflow = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcontrolBuffer = Buffer.concat([controlBuffer, chunk]);\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tchild.stdin?.end(JSON.stringify(request), \"utf8\");\n\t\t\t\tconst terminal = await waitForChildProcessWithTermination(child, {\n\t\t\t\t\tsignal,\n\t\t\t\t\ttimeoutMs,\n\t\t\t\t\tkillGraceMs: 2_000,\n\t\t\t\t});\n\t\t\t\tif (signal?.aborted) throw new Error(\"aborted\");\n\n\t\t\t\tconst frame = controlOverflow ? undefined : parseControlFrame(controlBuffer);\n\t\t\t\tif (!frame) {\n\t\t\t\t\tconst capturedOutput = controlOverflow\n\t\t\t\t\t\t? `control frame exceeded ${MAX_CONTROL_FRAME_BYTES} bytes`\n\t\t\t\t\t\t: controlBuffer.toString(\"utf8\");\n\t\t\t\t\tthrow new WindowsShellEngineFailure(\n\t\t\t\t\t\t`Windows shell engine failed (process exit ${terminal.code ?? \"null\"}) without a parseable control frame.\\n${capturedOutput}`,\n\t\t\t\t\t\tcapturedOutput,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tapplyEngineFrame(state, frame);\n\t\t\t\tif (frame.unsupported) throw new Error(frame.unsupported.message);\n\t\t\t\treturn { exitCode: frame.exitCode };\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}