@caupulican/pi-adaptative 0.81.39 → 0.81.40

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 (667) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +2 -0
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +2 -0
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -1
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +2 -0
  6. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -1
  7. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +3 -0
  8. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +9 -0
  9. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
  10. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +290 -107
  11. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
  12. package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +357 -122
  13. package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +2 -2
  14. package/dist/cli/args.d.ts +2 -0
  15. package/dist/cli/args.d.ts.map +1 -1
  16. package/dist/cli/args.js +9 -0
  17. package/dist/cli/args.js.map +1 -1
  18. package/dist/core/agent-directory-layout.d.ts +15 -0
  19. package/dist/core/agent-directory-layout.d.ts.map +1 -0
  20. package/dist/core/agent-directory-layout.js +72 -0
  21. package/dist/core/agent-directory-layout.js.map +1 -0
  22. package/dist/core/agent-paths.d.ts +19 -2
  23. package/dist/core/agent-paths.d.ts.map +1 -1
  24. package/dist/core/agent-paths.js +67 -4
  25. package/dist/core/agent-paths.js.map +1 -1
  26. package/dist/core/agent-session-runtime.d.ts +52 -8
  27. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  28. package/dist/core/agent-session-runtime.js +397 -79
  29. package/dist/core/agent-session-runtime.js.map +1 -1
  30. package/dist/core/agent-session-services.d.ts +1 -0
  31. package/dist/core/agent-session-services.d.ts.map +1 -1
  32. package/dist/core/agent-session-services.js +1 -0
  33. package/dist/core/agent-session-services.js.map +1 -1
  34. package/dist/core/agent-session.d.ts +71 -136
  35. package/dist/core/agent-session.d.ts.map +1 -1
  36. package/dist/core/agent-session.js +477 -1318
  37. package/dist/core/agent-session.js.map +1 -1
  38. package/dist/core/autonomy/approval-gate.d.ts +3 -2
  39. package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
  40. package/dist/core/autonomy/approval-gate.js +6 -28
  41. package/dist/core/autonomy/approval-gate.js.map +1 -1
  42. package/dist/core/autonomy/bounded-completion.d.ts +1 -1
  43. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  44. package/dist/core/autonomy/bounded-completion.js +1 -1
  45. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  46. package/dist/core/autonomy/contracts.d.ts +20 -12
  47. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  48. package/dist/core/autonomy/contracts.js.map +1 -1
  49. package/dist/core/autonomy/foreground-envelope.d.ts +8 -2
  50. package/dist/core/autonomy/foreground-envelope.d.ts.map +1 -1
  51. package/dist/core/autonomy/foreground-envelope.js +8 -25
  52. package/dist/core/autonomy/foreground-envelope.js.map +1 -1
  53. package/dist/core/autonomy/gates.d.ts.map +1 -1
  54. package/dist/core/autonomy/gates.js +8 -10
  55. package/dist/core/autonomy/gates.js.map +1 -1
  56. package/dist/core/autonomy/lane-private-paths.d.ts +2 -0
  57. package/dist/core/autonomy/lane-private-paths.d.ts.map +1 -0
  58. package/dist/core/autonomy/lane-private-paths.js +16 -0
  59. package/dist/core/autonomy/lane-private-paths.js.map +1 -0
  60. package/dist/core/autonomy/lane-tool-surface.d.ts +10 -0
  61. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  62. package/dist/core/autonomy/lane-tool-surface.js +46 -8
  63. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  64. package/dist/core/autonomy/lane-tracker.d.ts +14 -0
  65. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  66. package/dist/core/autonomy/lane-tracker.js +38 -0
  67. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  68. package/dist/core/autonomy/risk-assessment.d.ts +1 -2
  69. package/dist/core/autonomy/risk-assessment.d.ts.map +1 -1
  70. package/dist/core/autonomy/risk-assessment.js +1 -123
  71. package/dist/core/autonomy/risk-assessment.js.map +1 -1
  72. package/dist/core/autonomy/session-lane-record.d.ts +4 -4
  73. package/dist/core/autonomy/session-lane-record.d.ts.map +1 -1
  74. package/dist/core/autonomy/session-lane-record.js +16 -29
  75. package/dist/core/autonomy/session-lane-record.js.map +1 -1
  76. package/dist/core/autonomy/telemetry-events.d.ts +1 -1
  77. package/dist/core/autonomy/telemetry-events.d.ts.map +1 -1
  78. package/dist/core/autonomy/telemetry-events.js +1 -1
  79. package/dist/core/autonomy/telemetry-events.js.map +1 -1
  80. package/dist/core/autonomy-telemetry.d.ts +7 -7
  81. package/dist/core/autonomy-telemetry.d.ts.map +1 -1
  82. package/dist/core/autonomy-telemetry.js +9 -9
  83. package/dist/core/autonomy-telemetry.js.map +1 -1
  84. package/dist/core/background-lane-controller.d.ts +54 -236
  85. package/dist/core/background-lane-controller.d.ts.map +1 -1
  86. package/dist/core/background-lane-controller.js +140 -1273
  87. package/dist/core/background-lane-controller.js.map +1 -1
  88. package/dist/core/capability-contract.d.ts +4 -0
  89. package/dist/core/capability-contract.d.ts.map +1 -0
  90. package/dist/core/capability-contract.js +28 -0
  91. package/dist/core/capability-contract.js.map +1 -0
  92. package/dist/core/compaction-controller.d.ts +90 -0
  93. package/dist/core/compaction-controller.d.ts.map +1 -0
  94. package/dist/core/compaction-controller.js +452 -0
  95. package/dist/core/compaction-controller.js.map +1 -0
  96. package/dist/core/compaction-support.d.ts +12 -11
  97. package/dist/core/compaction-support.d.ts.map +1 -1
  98. package/dist/core/compaction-support.js.map +1 -1
  99. package/dist/core/context/context-artifacts.d.ts +6 -2
  100. package/dist/core/context/context-artifacts.d.ts.map +1 -1
  101. package/dist/core/context/context-artifacts.js +34 -2
  102. package/dist/core/context/context-artifacts.js.map +1 -1
  103. package/dist/core/context/context-store-retention.d.ts +8 -2
  104. package/dist/core/context/context-store-retention.d.ts.map +1 -1
  105. package/dist/core/context/context-store-retention.js +158 -35
  106. package/dist/core/context/context-store-retention.js.map +1 -1
  107. package/dist/core/context/okf-memory.d.ts.map +1 -1
  108. package/dist/core/context/okf-memory.js +9 -11
  109. package/dist/core/context/okf-memory.js.map +1 -1
  110. package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
  111. package/dist/core/context/sqlite-runtime-index.js +6 -8
  112. package/dist/core/context/sqlite-runtime-index.js.map +1 -1
  113. package/dist/core/context-gc.d.ts +3 -0
  114. package/dist/core/context-gc.d.ts.map +1 -1
  115. package/dist/core/context-gc.js +10 -5
  116. package/dist/core/context-gc.js.map +1 -1
  117. package/dist/core/context-pipeline.d.ts.map +1 -1
  118. package/dist/core/context-pipeline.js +9 -4
  119. package/dist/core/context-pipeline.js.map +1 -1
  120. package/dist/core/default-tool-surface.d.ts.map +1 -1
  121. package/dist/core/default-tool-surface.js +1 -0
  122. package/dist/core/default-tool-surface.js.map +1 -1
  123. package/dist/core/delegation/managed-lane-controller.d.ts +34 -0
  124. package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -0
  125. package/dist/core/delegation/managed-lane-controller.js +217 -0
  126. package/dist/core/delegation/managed-lane-controller.js.map +1 -0
  127. package/dist/core/delegation/session-worker-claim.d.ts +44 -0
  128. package/dist/core/delegation/session-worker-claim.d.ts.map +1 -0
  129. package/dist/core/delegation/session-worker-claim.js +98 -0
  130. package/dist/core/delegation/session-worker-claim.js.map +1 -0
  131. package/dist/core/delegation/worker-actions.d.ts +7 -5
  132. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  133. package/dist/core/delegation/worker-actions.js +20 -4
  134. package/dist/core/delegation/worker-actions.js.map +1 -1
  135. package/dist/core/delegation/{worker-result.d.ts → worker-claim.d.ts} +13 -13
  136. package/dist/core/delegation/worker-claim.d.ts.map +1 -0
  137. package/dist/core/delegation/{worker-result.js → worker-claim.js} +61 -51
  138. package/dist/core/delegation/worker-claim.js.map +1 -0
  139. package/dist/core/delegation/worker-delegation-controller.d.ts +110 -0
  140. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -0
  141. package/dist/core/delegation/worker-delegation-controller.js +861 -0
  142. package/dist/core/delegation/worker-delegation-controller.js.map +1 -0
  143. package/dist/core/delegation/worker-delegation-request.d.ts +11 -0
  144. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -0
  145. package/dist/core/delegation/worker-delegation-request.js +2 -0
  146. package/dist/core/delegation/worker-delegation-request.js.map +1 -0
  147. package/dist/core/delegation/worker-dispatch-scheduler.d.ts +40 -0
  148. package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -0
  149. package/dist/core/delegation/worker-dispatch-scheduler.js +93 -0
  150. package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -0
  151. package/dist/core/delegation/worker-execution-policy.d.ts +62 -0
  152. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -0
  153. package/dist/core/delegation/worker-execution-policy.js +160 -0
  154. package/dist/core/delegation/worker-execution-policy.js.map +1 -0
  155. package/dist/core/delegation/worker-lane-projection.d.ts +14 -0
  156. package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -0
  157. package/dist/core/delegation/worker-lane-projection.js +100 -0
  158. package/dist/core/delegation/worker-lane-projection.js.map +1 -0
  159. package/dist/core/delegation/worker-lifecycle.d.ts +97 -0
  160. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -0
  161. package/dist/core/delegation/worker-lifecycle.js +339 -0
  162. package/dist/core/delegation/worker-lifecycle.js.map +1 -0
  163. package/dist/core/delegation/worker-notification-coordinator.d.ts +36 -0
  164. package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -0
  165. package/dist/core/delegation/worker-notification-coordinator.js +90 -0
  166. package/dist/core/delegation/worker-notification-coordinator.js.map +1 -0
  167. package/dist/core/delegation/worker-profile-resolver.d.ts +55 -0
  168. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -0
  169. package/dist/core/delegation/worker-profile-resolver.js +113 -0
  170. package/dist/core/delegation/worker-profile-resolver.js.map +1 -0
  171. package/dist/core/delegation/worker-runner.d.ts +25 -14
  172. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  173. package/dist/core/delegation/worker-runner.js +122 -53
  174. package/dist/core/delegation/worker-runner.js.map +1 -1
  175. package/dist/core/delegation/worker-task-label.d.ts +3 -0
  176. package/dist/core/delegation/worker-task-label.d.ts.map +1 -0
  177. package/dist/core/delegation/worker-task-label.js +11 -0
  178. package/dist/core/delegation/worker-task-label.js.map +1 -0
  179. package/dist/core/doctor.d.ts +8 -0
  180. package/dist/core/doctor.d.ts.map +1 -1
  181. package/dist/core/doctor.js +32 -1
  182. package/dist/core/doctor.js.map +1 -1
  183. package/dist/core/extensions/index.d.ts +1 -1
  184. package/dist/core/extensions/index.d.ts.map +1 -1
  185. package/dist/core/extensions/index.js.map +1 -1
  186. package/dist/core/extensions/loader.d.ts +5 -1
  187. package/dist/core/extensions/loader.d.ts.map +1 -1
  188. package/dist/core/extensions/loader.js +50 -15
  189. package/dist/core/extensions/loader.js.map +1 -1
  190. package/dist/core/extensions/runner.d.ts.map +1 -1
  191. package/dist/core/extensions/runner.js +6 -0
  192. package/dist/core/extensions/runner.js.map +1 -1
  193. package/dist/core/extensions/storage.d.ts +8 -0
  194. package/dist/core/extensions/storage.d.ts.map +1 -0
  195. package/dist/core/extensions/storage.js +28 -0
  196. package/dist/core/extensions/storage.js.map +1 -0
  197. package/dist/core/extensions/types.d.ts +70 -21
  198. package/dist/core/extensions/types.d.ts.map +1 -1
  199. package/dist/core/extensions/types.js.map +1 -1
  200. package/dist/core/failure-corpus.d.ts +1 -15
  201. package/dist/core/failure-corpus.d.ts.map +1 -1
  202. package/dist/core/failure-corpus.js +9 -37
  203. package/dist/core/failure-corpus.js.map +1 -1
  204. package/dist/core/foreground-recovery-controller.d.ts +52 -0
  205. package/dist/core/foreground-recovery-controller.d.ts.map +1 -0
  206. package/dist/core/foreground-recovery-controller.js +111 -0
  207. package/dist/core/foreground-recovery-controller.js.map +1 -0
  208. package/dist/core/gateways/channel-provider.d.ts +3 -0
  209. package/dist/core/gateways/channel-provider.d.ts.map +1 -1
  210. package/dist/core/gateways/channel-provider.js +31 -6
  211. package/dist/core/gateways/channel-provider.js.map +1 -1
  212. package/dist/core/goals/goal-acceptance.d.ts +11 -0
  213. package/dist/core/goals/goal-acceptance.d.ts.map +1 -0
  214. package/dist/core/goals/goal-acceptance.js +25 -0
  215. package/dist/core/goals/goal-acceptance.js.map +1 -0
  216. package/dist/core/goals/goal-auto-continue-controller.d.ts +27 -0
  217. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -0
  218. package/dist/core/goals/goal-auto-continue-controller.js +93 -0
  219. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -0
  220. package/dist/core/goals/goal-continuation-controller.d.ts +1 -1
  221. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
  222. package/dist/core/goals/goal-continuation-controller.js +10 -0
  223. package/dist/core/goals/goal-continuation-controller.js.map +1 -1
  224. package/dist/core/goals/goal-continuation-prompt.d.ts +1 -1
  225. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  226. package/dist/core/goals/goal-continuation-prompt.js +6 -6
  227. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  228. package/dist/core/goals/goal-lifecycle.d.ts +20 -0
  229. package/dist/core/goals/goal-lifecycle.d.ts.map +1 -0
  230. package/dist/core/goals/goal-lifecycle.js +17 -0
  231. package/dist/core/goals/goal-lifecycle.js.map +1 -0
  232. package/dist/core/goals/goal-runtime-snapshot.d.ts +11 -9
  233. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
  234. package/dist/core/goals/goal-runtime-snapshot.js +10 -8
  235. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
  236. package/dist/core/goals/goal-state.d.ts +1 -0
  237. package/dist/core/goals/goal-state.d.ts.map +1 -1
  238. package/dist/core/goals/goal-state.js +7 -10
  239. package/dist/core/goals/goal-state.js.map +1 -1
  240. package/dist/core/goals/goal-tool-core.d.ts +8 -6
  241. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  242. package/dist/core/goals/goal-tool-core.js +11 -41
  243. package/dist/core/goals/goal-tool-core.js.map +1 -1
  244. package/dist/core/goals/session-goal-state.d.ts +2 -4
  245. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  246. package/dist/core/goals/session-goal-state.js +10 -33
  247. package/dist/core/goals/session-goal-state.js.map +1 -1
  248. package/dist/core/human-input.d.ts +100 -0
  249. package/dist/core/human-input.d.ts.map +1 -0
  250. package/dist/core/human-input.js +362 -0
  251. package/dist/core/human-input.js.map +1 -0
  252. package/dist/core/index.d.ts +9 -1
  253. package/dist/core/index.d.ts.map +1 -1
  254. package/dist/core/index.js +9 -1
  255. package/dist/core/index.js.map +1 -1
  256. package/dist/core/{telemetry.d.ts → install-telemetry.d.ts} +1 -1
  257. package/dist/core/install-telemetry.d.ts.map +1 -0
  258. package/dist/core/{telemetry.js → install-telemetry.js} +1 -1
  259. package/dist/core/install-telemetry.js.map +1 -0
  260. package/dist/core/keybindings.d.ts +17 -1
  261. package/dist/core/keybindings.d.ts.map +1 -1
  262. package/dist/core/keybindings.js +24 -6
  263. package/dist/core/keybindings.js.map +1 -1
  264. package/dist/core/learning/learning-audit.d.ts +2 -4
  265. package/dist/core/learning/learning-audit.d.ts.map +1 -1
  266. package/dist/core/learning/learning-audit.js +9 -29
  267. package/dist/core/learning/learning-audit.js.map +1 -1
  268. package/dist/core/learning/learning-gate.d.ts.map +1 -1
  269. package/dist/core/learning/learning-gate.js +1 -6
  270. package/dist/core/learning/learning-gate.js.map +1 -1
  271. package/dist/core/learning/session-learning-decision.d.ts +2 -4
  272. package/dist/core/learning/session-learning-decision.d.ts.map +1 -1
  273. package/dist/core/learning/session-learning-decision.js +9 -29
  274. package/dist/core/learning/session-learning-decision.js.map +1 -1
  275. package/dist/core/memory-controller.d.ts.map +1 -1
  276. package/dist/core/memory-controller.js +4 -4
  277. package/dist/core/memory-controller.js.map +1 -1
  278. package/dist/core/model-capability.d.ts.map +1 -1
  279. package/dist/core/model-capability.js +1 -0
  280. package/dist/core/model-capability.js.map +1 -1
  281. package/dist/core/model-resolver.d.ts +1 -1
  282. package/dist/core/model-resolver.d.ts.map +1 -1
  283. package/dist/core/model-resolver.js +6 -9
  284. package/dist/core/model-resolver.js.map +1 -1
  285. package/dist/core/model-router/intent-classifier.d.ts +1 -4
  286. package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
  287. package/dist/core/model-router/intent-classifier.js +1 -156
  288. package/dist/core/model-router/intent-classifier.js.map +1 -1
  289. package/dist/core/model-router-controller.d.ts.map +1 -1
  290. package/dist/core/model-router-controller.js +2 -2
  291. package/dist/core/model-router-controller.js.map +1 -1
  292. package/dist/core/model-selection-controller.d.ts +4 -4
  293. package/dist/core/model-selection-controller.d.ts.map +1 -1
  294. package/dist/core/model-selection-controller.js.map +1 -1
  295. package/dist/core/model-tool-protocol.d.ts +23 -0
  296. package/dist/core/model-tool-protocol.d.ts.map +1 -0
  297. package/dist/core/model-tool-protocol.js +43 -0
  298. package/dist/core/model-tool-protocol.js.map +1 -0
  299. package/dist/core/models/adaptation-store.d.ts +4 -6
  300. package/dist/core/models/adaptation-store.d.ts.map +1 -1
  301. package/dist/core/models/adaptation-store.js +95 -83
  302. package/dist/core/models/adaptation-store.js.map +1 -1
  303. package/dist/core/models/fitness-store.d.ts +2 -12
  304. package/dist/core/models/fitness-store.d.ts.map +1 -1
  305. package/dist/core/models/fitness-store.js +84 -57
  306. package/dist/core/models/fitness-store.js.map +1 -1
  307. package/dist/core/models/host-state-store.d.ts +34 -0
  308. package/dist/core/models/host-state-store.d.ts.map +1 -0
  309. package/dist/core/models/host-state-store.js +86 -0
  310. package/dist/core/models/host-state-store.js.map +1 -0
  311. package/dist/core/orchestration/agent-resume.d.ts +24 -0
  312. package/dist/core/orchestration/agent-resume.d.ts.map +1 -0
  313. package/dist/core/orchestration/agent-resume.js +90 -0
  314. package/dist/core/orchestration/agent-resume.js.map +1 -0
  315. package/dist/core/orchestration/capability-gateway.d.ts +61 -0
  316. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -0
  317. package/dist/core/orchestration/capability-gateway.js +152 -0
  318. package/dist/core/orchestration/capability-gateway.js.map +1 -0
  319. package/dist/core/orchestration/contracts.d.ts +343 -0
  320. package/dist/core/orchestration/contracts.d.ts.map +1 -0
  321. package/dist/core/orchestration/contracts.js +135 -0
  322. package/dist/core/orchestration/contracts.js.map +1 -0
  323. package/dist/core/orchestration/delegation-ledger.d.ts +56 -0
  324. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -0
  325. package/dist/core/orchestration/delegation-ledger.js +197 -0
  326. package/dist/core/orchestration/delegation-ledger.js.map +1 -0
  327. package/dist/core/orchestration/event-store.d.ts +83 -0
  328. package/dist/core/orchestration/event-store.d.ts.map +1 -0
  329. package/dist/core/orchestration/event-store.js +560 -0
  330. package/dist/core/orchestration/event-store.js.map +1 -0
  331. package/dist/core/orchestration/lane-tool-manifests.d.ts +9 -0
  332. package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -0
  333. package/dist/core/orchestration/lane-tool-manifests.js +37 -0
  334. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -0
  335. package/dist/core/orchestration/model-binding.d.ts +15 -0
  336. package/dist/core/orchestration/model-binding.d.ts.map +1 -0
  337. package/dist/core/orchestration/model-binding.js +29 -0
  338. package/dist/core/orchestration/model-binding.js.map +1 -0
  339. package/dist/core/orchestration/policy-compiler.d.ts +49 -0
  340. package/dist/core/orchestration/policy-compiler.d.ts.map +1 -0
  341. package/dist/core/orchestration/policy-compiler.js +191 -0
  342. package/dist/core/orchestration/policy-compiler.js.map +1 -0
  343. package/dist/core/orchestration/policy-gate.d.ts +18 -0
  344. package/dist/core/orchestration/policy-gate.d.ts.map +1 -0
  345. package/dist/core/orchestration/policy-gate.js +24 -0
  346. package/dist/core/orchestration/policy-gate.js.map +1 -0
  347. package/dist/core/orchestration/profile-registry.d.ts +51 -0
  348. package/dist/core/orchestration/profile-registry.d.ts.map +1 -0
  349. package/dist/core/orchestration/profile-registry.js +393 -0
  350. package/dist/core/orchestration/profile-registry.js.map +1 -0
  351. package/dist/core/orchestration/profile-store.d.ts +31 -0
  352. package/dist/core/orchestration/profile-store.d.ts.map +1 -0
  353. package/dist/core/orchestration/profile-store.js +136 -0
  354. package/dist/core/orchestration/profile-store.js.map +1 -0
  355. package/dist/core/orchestration/risk-budget.d.ts +9 -0
  356. package/dist/core/orchestration/risk-budget.d.ts.map +1 -0
  357. package/dist/core/orchestration/risk-budget.js +48 -0
  358. package/dist/core/orchestration/risk-budget.js.map +1 -0
  359. package/dist/core/orchestration/task-runtime.d.ts +168 -0
  360. package/dist/core/orchestration/task-runtime.d.ts.map +1 -0
  361. package/dist/core/orchestration/task-runtime.js +1600 -0
  362. package/dist/core/orchestration/task-runtime.js.map +1 -0
  363. package/dist/core/orchestration/work-state-projection.d.ts +32 -0
  364. package/dist/core/orchestration/work-state-projection.d.ts.map +1 -0
  365. package/dist/core/orchestration/work-state-projection.js +116 -0
  366. package/dist/core/orchestration/work-state-projection.js.map +1 -0
  367. package/dist/core/orchestration/worker-execution-contract.d.ts +21 -0
  368. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -0
  369. package/dist/core/orchestration/worker-execution-contract.js +179 -0
  370. package/dist/core/orchestration/worker-execution-contract.js.map +1 -0
  371. package/dist/core/orchestration/worker-result-adapter.d.ts +21 -0
  372. package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -0
  373. package/dist/core/orchestration/worker-result-adapter.js +109 -0
  374. package/dist/core/orchestration/worker-result-adapter.js.map +1 -0
  375. package/dist/core/process-identity.d.ts +11 -0
  376. package/dist/core/process-identity.d.ts.map +1 -0
  377. package/dist/core/process-identity.js +26 -0
  378. package/dist/core/process-identity.js.map +1 -0
  379. package/dist/core/process-matrix/codes.d.ts +14 -3
  380. package/dist/core/process-matrix/codes.d.ts.map +1 -1
  381. package/dist/core/process-matrix/codes.js.map +1 -1
  382. package/dist/core/process-matrix/resume-launcher.d.ts +26 -0
  383. package/dist/core/process-matrix/resume-launcher.d.ts.map +1 -0
  384. package/dist/core/process-matrix/resume-launcher.js +62 -0
  385. package/dist/core/process-matrix/resume-launcher.js.map +1 -0
  386. package/dist/core/process-matrix/runtime.d.ts +37 -22
  387. package/dist/core/process-matrix/runtime.d.ts.map +1 -1
  388. package/dist/core/process-matrix/runtime.js +399 -76
  389. package/dist/core/process-matrix/runtime.js.map +1 -1
  390. package/dist/core/process-matrix/store.d.ts +5 -2
  391. package/dist/core/process-matrix/store.d.ts.map +1 -1
  392. package/dist/core/process-matrix/store.js +123 -14
  393. package/dist/core/process-matrix/store.js.map +1 -1
  394. package/dist/core/process-matrix/supervisor.d.ts +10 -7
  395. package/dist/core/process-matrix/supervisor.d.ts.map +1 -1
  396. package/dist/core/process-matrix/supervisor.js +45 -14
  397. package/dist/core/process-matrix/supervisor.js.map +1 -1
  398. package/dist/core/profile-registry.d.ts.map +1 -1
  399. package/dist/core/profile-registry.js +6 -8
  400. package/dist/core/profile-registry.js.map +1 -1
  401. package/dist/core/reflection-controller.d.ts +4 -4
  402. package/dist/core/reflection-controller.d.ts.map +1 -1
  403. package/dist/core/reflection-controller.js +2 -1
  404. package/dist/core/reflection-controller.js.map +1 -1
  405. package/dist/core/reload-blockers.d.ts.map +1 -1
  406. package/dist/core/reload-blockers.js +6 -8
  407. package/dist/core/reload-blockers.js.map +1 -1
  408. package/dist/core/research/evidence-bundle.d.ts.map +1 -1
  409. package/dist/core/research/evidence-bundle.js +1 -6
  410. package/dist/core/research/evidence-bundle.js.map +1 -1
  411. package/dist/core/research/lane-model-resolver.d.ts +33 -0
  412. package/dist/core/research/lane-model-resolver.d.ts.map +1 -0
  413. package/dist/core/research/lane-model-resolver.js +65 -0
  414. package/dist/core/research/lane-model-resolver.js.map +1 -0
  415. package/dist/core/research/model-fitness-controller.d.ts +38 -0
  416. package/dist/core/research/model-fitness-controller.d.ts.map +1 -0
  417. package/dist/core/research/model-fitness-controller.js +106 -0
  418. package/dist/core/research/model-fitness-controller.js.map +1 -0
  419. package/dist/core/research/model-fitness.d.ts +1 -1
  420. package/dist/core/research/model-fitness.d.ts.map +1 -1
  421. package/dist/core/research/model-fitness.js +4 -4
  422. package/dist/core/research/model-fitness.js.map +1 -1
  423. package/dist/core/research/research-gate.d.ts.map +1 -1
  424. package/dist/core/research/research-gate.js +7 -7
  425. package/dist/core/research/research-gate.js.map +1 -1
  426. package/dist/core/research/research-lane-controller.d.ts +62 -0
  427. package/dist/core/research/research-lane-controller.d.ts.map +1 -0
  428. package/dist/core/research/research-lane-controller.js +292 -0
  429. package/dist/core/research/research-lane-controller.js.map +1 -0
  430. package/dist/core/research/session-evidence-bundle.d.ts +2 -4
  431. package/dist/core/research/session-evidence-bundle.d.ts.map +1 -1
  432. package/dist/core/research/session-evidence-bundle.js +10 -46
  433. package/dist/core/research/session-evidence-bundle.js.map +1 -1
  434. package/dist/core/resource-loader.d.ts.map +1 -1
  435. package/dist/core/resource-loader.js +7 -2
  436. package/dist/core/resource-loader.js.map +1 -1
  437. package/dist/core/risk-classifier.d.ts +6 -0
  438. package/dist/core/risk-classifier.d.ts.map +1 -0
  439. package/dist/core/risk-classifier.js +225 -0
  440. package/dist/core/risk-classifier.js.map +1 -0
  441. package/dist/core/runtime-builder.d.ts +26 -20
  442. package/dist/core/runtime-builder.d.ts.map +1 -1
  443. package/dist/core/runtime-builder.js +237 -178
  444. package/dist/core/runtime-builder.js.map +1 -1
  445. package/dist/core/sdk.d.ts +6 -3
  446. package/dist/core/sdk.d.ts.map +1 -1
  447. package/dist/core/sdk.js +61 -24
  448. package/dist/core/sdk.js.map +1 -1
  449. package/dist/core/session-analytics.d.ts.map +1 -1
  450. package/dist/core/session-analytics.js +28 -1
  451. package/dist/core/session-analytics.js.map +1 -1
  452. package/dist/core/session-image-store.d.ts +33 -0
  453. package/dist/core/session-image-store.d.ts.map +1 -0
  454. package/dist/core/session-image-store.js +283 -0
  455. package/dist/core/session-image-store.js.map +1 -0
  456. package/dist/core/session-role.d.ts +6 -4
  457. package/dist/core/session-role.d.ts.map +1 -1
  458. package/dist/core/session-role.js +10 -4
  459. package/dist/core/session-role.js.map +1 -1
  460. package/dist/core/session-snapshot.d.ts +34 -0
  461. package/dist/core/session-snapshot.d.ts.map +1 -0
  462. package/dist/core/session-snapshot.js +68 -0
  463. package/dist/core/session-snapshot.js.map +1 -0
  464. package/dist/core/session-supervision-runtime.d.ts +31 -0
  465. package/dist/core/session-supervision-runtime.d.ts.map +1 -0
  466. package/dist/core/session-supervision-runtime.js +97 -0
  467. package/dist/core/session-supervision-runtime.js.map +1 -0
  468. package/dist/core/settings-manager.d.ts +9 -4
  469. package/dist/core/settings-manager.d.ts.map +1 -1
  470. package/dist/core/settings-manager.js +80 -28
  471. package/dist/core/settings-manager.js.map +1 -1
  472. package/dist/core/tasks/session-task-state.d.ts +2 -4
  473. package/dist/core/tasks/session-task-state.d.ts.map +1 -1
  474. package/dist/core/tasks/session-task-state.js +10 -28
  475. package/dist/core/tasks/session-task-state.js.map +1 -1
  476. package/dist/core/tasks/task-projection.d.ts +20 -0
  477. package/dist/core/tasks/task-projection.d.ts.map +1 -0
  478. package/dist/core/tasks/task-projection.js +47 -0
  479. package/dist/core/tasks/task-projection.js.map +1 -0
  480. package/dist/core/tasks/task-state.d.ts +4 -0
  481. package/dist/core/tasks/task-state.d.ts.map +1 -1
  482. package/dist/core/tasks/task-state.js +28 -11
  483. package/dist/core/tasks/task-state.js.map +1 -1
  484. package/dist/core/tool-capability-policy.d.ts +13 -0
  485. package/dist/core/tool-capability-policy.d.ts.map +1 -0
  486. package/dist/core/tool-capability-policy.js +85 -0
  487. package/dist/core/tool-capability-policy.js.map +1 -0
  488. package/dist/core/tool-protocol-controller.d.ts +91 -0
  489. package/dist/core/tool-protocol-controller.d.ts.map +1 -0
  490. package/dist/core/tool-protocol-controller.js +512 -0
  491. package/dist/core/tool-protocol-controller.js.map +1 -0
  492. package/dist/core/tool-recovery-log-records.d.ts +2 -3
  493. package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
  494. package/dist/core/tool-recovery-log-records.js +9 -35
  495. package/dist/core/tool-recovery-log-records.js.map +1 -1
  496. package/dist/core/tool-selection/tool-performance-store.d.ts +3 -7
  497. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
  498. package/dist/core/tool-selection/tool-performance-store.js +97 -138
  499. package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
  500. package/dist/core/tools/ask-question.d.ts +124 -0
  501. package/dist/core/tools/ask-question.d.ts.map +1 -0
  502. package/dist/core/tools/ask-question.js +693 -0
  503. package/dist/core/tools/ask-question.js.map +1 -0
  504. package/dist/core/tools/delegate-status.d.ts +31 -3
  505. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  506. package/dist/core/tools/delegate-status.js +149 -29
  507. package/dist/core/tools/delegate-status.js.map +1 -1
  508. package/dist/core/tools/delegate.d.ts +15 -13
  509. package/dist/core/tools/delegate.d.ts.map +1 -1
  510. package/dist/core/tools/delegate.js +108 -23
  511. package/dist/core/tools/delegate.js.map +1 -1
  512. package/dist/core/tools/extensionify.d.ts.map +1 -1
  513. package/dist/core/tools/extensionify.js +1 -0
  514. package/dist/core/tools/extensionify.js.map +1 -1
  515. package/dist/core/tools/goal.d.ts +5 -5
  516. package/dist/core/tools/goal.d.ts.map +1 -1
  517. package/dist/core/tools/goal.js +10 -10
  518. package/dist/core/tools/goal.js.map +1 -1
  519. package/dist/core/tools/model-fitness.d.ts.map +1 -1
  520. package/dist/core/tools/model-fitness.js +2 -2
  521. package/dist/core/tools/model-fitness.js.map +1 -1
  522. package/dist/core/tools/orchestration-panel.d.ts +43 -0
  523. package/dist/core/tools/orchestration-panel.d.ts.map +1 -0
  524. package/dist/core/tools/orchestration-panel.js +168 -0
  525. package/dist/core/tools/orchestration-panel.js.map +1 -0
  526. package/dist/core/tools/run-process.d.ts +26 -0
  527. package/dist/core/tools/run-process.d.ts.map +1 -0
  528. package/dist/core/tools/run-process.js +138 -0
  529. package/dist/core/tools/run-process.js.map +1 -0
  530. package/dist/core/tools/task-steps.d.ts +2 -0
  531. package/dist/core/tools/task-steps.d.ts.map +1 -1
  532. package/dist/core/tools/task-steps.js +102 -0
  533. package/dist/core/tools/task-steps.js.map +1 -1
  534. package/dist/core/tools/tmux-dispatch.d.ts +3 -5
  535. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -1
  536. package/dist/core/tools/tmux-dispatch.js +4 -4
  537. package/dist/core/tools/tmux-dispatch.js.map +1 -1
  538. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -1
  539. package/dist/core/tools/windows-shell-engine.js +4 -6
  540. package/dist/core/tools/windows-shell-engine.js.map +1 -1
  541. package/dist/core/util/bounded-jsonl.d.ts +10 -0
  542. package/dist/core/util/bounded-jsonl.d.ts.map +1 -0
  543. package/dist/core/util/bounded-jsonl.js +75 -0
  544. package/dist/core/util/bounded-jsonl.js.map +1 -0
  545. package/dist/core/util/value-guards.d.ts +5 -0
  546. package/dist/core/util/value-guards.d.ts.map +1 -0
  547. package/dist/core/util/value-guards.js +12 -0
  548. package/dist/core/util/value-guards.js.map +1 -0
  549. package/dist/core/worktree-sync/store.d.ts +3 -3
  550. package/dist/core/worktree-sync/store.d.ts.map +1 -1
  551. package/dist/core/worktree-sync/store.js +10 -5
  552. package/dist/core/worktree-sync/store.js.map +1 -1
  553. package/dist/index.d.ts +13 -3
  554. package/dist/index.d.ts.map +1 -1
  555. package/dist/index.js +12 -2
  556. package/dist/index.js.map +1 -1
  557. package/dist/main.d.ts.map +1 -1
  558. package/dist/main.js +128 -44
  559. package/dist/main.js.map +1 -1
  560. package/dist/migrations.d.ts +4 -6
  561. package/dist/migrations.d.ts.map +1 -1
  562. package/dist/migrations.js +186 -28
  563. package/dist/migrations.js.map +1 -1
  564. package/dist/modes/interactive/clipboard-input.d.ts +7 -1
  565. package/dist/modes/interactive/clipboard-input.d.ts.map +1 -1
  566. package/dist/modes/interactive/clipboard-input.js +44 -6
  567. package/dist/modes/interactive/clipboard-input.js.map +1 -1
  568. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  569. package/dist/modes/interactive/components/settings-selector.js +2 -2
  570. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  571. package/dist/modes/interactive/config-backup.d.ts.map +1 -1
  572. package/dist/modes/interactive/config-backup.js +5 -4
  573. package/dist/modes/interactive/config-backup.js.map +1 -1
  574. package/dist/modes/interactive/extension-ui-host.d.ts +14 -0
  575. package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
  576. package/dist/modes/interactive/extension-ui-host.js +76 -15
  577. package/dist/modes/interactive/extension-ui-host.js.map +1 -1
  578. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  579. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  580. package/dist/modes/interactive/interactive-mode.js +63 -1
  581. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  582. package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
  583. package/dist/modes/interactive/key-handlers.js +1 -1
  584. package/dist/modes/interactive/key-handlers.js.map +1 -1
  585. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
  586. package/dist/modes/interactive/profile-menu-controller.js +7 -7
  587. package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
  588. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  589. package/dist/modes/interactive/report-commands.js +21 -0
  590. package/dist/modes/interactive/report-commands.js.map +1 -1
  591. package/dist/modes/interactive/session-flow-commands.d.ts +17 -2
  592. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  593. package/dist/modes/interactive/session-flow-commands.js +50 -17
  594. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  595. package/dist/modes/interactive/session-io-commands.d.ts.map +1 -1
  596. package/dist/modes/interactive/session-io-commands.js +16 -6
  597. package/dist/modes/interactive/session-io-commands.js.map +1 -1
  598. package/dist/modes/interactive/session-replacement-errors.d.ts +7 -0
  599. package/dist/modes/interactive/session-replacement-errors.d.ts.map +1 -0
  600. package/dist/modes/interactive/session-replacement-errors.js +15 -0
  601. package/dist/modes/interactive/session-replacement-errors.js.map +1 -0
  602. package/dist/modes/interactive/startup-checks.d.ts.map +1 -1
  603. package/dist/modes/interactive/startup-checks.js +1 -1
  604. package/dist/modes/interactive/startup-checks.js.map +1 -1
  605. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  606. package/dist/modes/rpc/rpc-mode.js +82 -23
  607. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  608. package/dist/modes/rpc/rpc-types.d.ts +15 -0
  609. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  610. package/dist/modes/rpc/rpc-types.js +138 -1
  611. package/dist/modes/rpc/rpc-types.js.map +1 -1
  612. package/dist/utils/clipboard-image.d.ts.map +1 -1
  613. package/dist/utils/clipboard-image.js +2 -13
  614. package/dist/utils/clipboard-image.js.map +1 -1
  615. package/dist/utils/clipboard-native.d.ts +1 -0
  616. package/dist/utils/clipboard-native.d.ts.map +1 -1
  617. package/dist/utils/clipboard-native.js.map +1 -1
  618. package/dist/utils/clipboard.d.ts +1 -0
  619. package/dist/utils/clipboard.d.ts.map +1 -1
  620. package/dist/utils/clipboard.js +52 -1
  621. package/dist/utils/clipboard.js.map +1 -1
  622. package/dist/utils/platform.d.ts +3 -0
  623. package/dist/utils/platform.d.ts.map +1 -0
  624. package/dist/utils/platform.js +8 -0
  625. package/dist/utils/platform.js.map +1 -0
  626. package/dist/utils/work-directory.d.ts +11 -0
  627. package/dist/utils/work-directory.d.ts.map +1 -1
  628. package/dist/utils/work-directory.js +30 -9
  629. package/dist/utils/work-directory.js.map +1 -1
  630. package/docs/UI_POLISH_ORCHESTRATION.md +44 -0
  631. package/docs/UI_QUALITY_ORCHESTRATION.md +30 -0
  632. package/docs/docs.json +4 -0
  633. package/docs/extensions.md +62 -2
  634. package/docs/harness-architecture.md +156 -0
  635. package/docs/keybindings.md +11 -1
  636. package/docs/process-matrix.md +59 -26
  637. package/docs/rpc.md +45 -2
  638. package/docs/sessions.md +15 -0
  639. package/docs/settings.md +125 -11
  640. package/docs/task-steps.md +8 -0
  641. package/docs/telemetry.md +19 -0
  642. package/docs/tmux-agent-manager.md +10 -3
  643. package/docs/ui-reference/README.md +9 -0
  644. package/docs/ui-reference/orchestration-after.txt +7 -0
  645. package/docs/ui-reference/orchestration-before.txt +7 -0
  646. package/docs/ui-reference/orchestration-reference.txt +6 -0
  647. package/docs/usage.md +14 -2
  648. package/docs/work-directory.md +19 -8
  649. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  650. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  651. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  652. package/examples/extensions/question.ts +34 -245
  653. package/examples/extensions/questionnaire.ts +41 -407
  654. package/examples/extensions/sandbox/package-lock.json +2 -2
  655. package/examples/extensions/sandbox/package.json +1 -1
  656. package/examples/extensions/with-deps/package-lock.json +2 -2
  657. package/examples/extensions/with-deps/package.json +1 -1
  658. package/npm-shrinkwrap.json +12 -12
  659. package/package.json +4 -4
  660. package/dist/core/delegation/session-worker-result.d.ts +0 -46
  661. package/dist/core/delegation/session-worker-result.d.ts.map +0 -1
  662. package/dist/core/delegation/session-worker-result.js +0 -113
  663. package/dist/core/delegation/session-worker-result.js.map +0 -1
  664. package/dist/core/delegation/worker-result.d.ts.map +0 -1
  665. package/dist/core/delegation/worker-result.js.map +0 -1
  666. package/dist/core/telemetry.d.ts.map +0 -1
  667. package/dist/core/telemetry.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { Type } from "typebox";
2
2
  import { addTaskStep, clearTaskSteps, compactTaskSteps, createTaskStepsState, findOpenDuplicateStep, formatTaskSteps, hasUnverifiedCompletedStep, MAX_TASK_STEPS, resolveTaskStepSelector, setTaskSteps, updateTaskStep, } from "../tasks/task-state.js";
3
+ import { emptyOrchestrationCall, OrchestrationPanelComponent, taskStepPanelRow, } from "./orchestration-panel.js";
3
4
  const statusSchema = Type.Union([
4
5
  Type.Literal("pending"),
5
6
  Type.Literal("in_progress"),
@@ -14,6 +15,10 @@ const stepInputSchema = Type.Object({
14
15
  status: Type.Optional(statusSchema),
15
16
  priority: Type.Optional(prioritySchema),
16
17
  owner: Type.Optional(Type.String({ maxLength: 200 })),
18
+ requirementIds: Type.Optional(Type.Array(Type.String({ minLength: 1, maxLength: 200 }), {
19
+ maxItems: 32,
20
+ description: "Goal requirement ids this foreground step advances.",
21
+ })),
17
22
  note: Type.Optional(Type.String({ maxLength: 4_000 })),
18
23
  evidence: Type.Optional(Type.Array(Type.String({ minLength: 1, maxLength: 2_000 }), { maxItems: 32 })),
19
24
  }, { additionalProperties: false });
@@ -39,6 +44,10 @@ const taskStepsSchema = Type.Object({
39
44
  status: Type.Optional(statusSchema),
40
45
  priority: Type.Optional(prioritySchema),
41
46
  owner: Type.Optional(Type.String({ maxLength: 200 })),
47
+ requirementIds: Type.Optional(Type.Array(Type.String({ minLength: 1, maxLength: 200 }), {
48
+ maxItems: 32,
49
+ description: "Goal requirement ids this foreground step advances; [] clears existing links.",
50
+ })),
42
51
  note: Type.Optional(Type.String({ maxLength: 4_000 })),
43
52
  evidence: Type.Optional(Type.Array(Type.String({ minLength: 1, maxLength: 2_000 }), { maxItems: 32 })),
44
53
  clearCompleted: Type.Optional(Type.Boolean({ description: "For list: compact completed and cancelled steps before rendering." })),
@@ -52,6 +61,7 @@ function toTaskStepInput(input) {
52
61
  status: input.status,
53
62
  priority: input.priority,
54
63
  owner: input.owner,
64
+ requirementIds: input.requirementIds,
55
65
  note: input.note,
56
66
  evidence: input.evidence,
57
67
  };
@@ -92,6 +102,74 @@ function errorResult(action, error, state) {
92
102
  details: { action, applied: false, error, state, ...(state ? counts(state) : {}) },
93
103
  };
94
104
  }
105
+ function taskStepsPanelModel(details, expanded) {
106
+ const state = details.state;
107
+ if (!state) {
108
+ return {
109
+ label: "task steps",
110
+ action: details.action,
111
+ status: details.error ? "error" : "idle",
112
+ emptyText: details.error ?? "No checklist state.",
113
+ };
114
+ }
115
+ const active = state.steps.filter((step) => step.status === "in_progress");
116
+ const blocked = state.steps.filter((step) => step.status === "blocked");
117
+ const pending = state.steps.filter((step) => step.status === "pending");
118
+ const completed = state.steps.filter((step) => step.status === "completed");
119
+ const cancelled = state.steps.filter((step) => step.status === "cancelled");
120
+ const open = [...active, ...blocked, ...pending];
121
+ const terminal = details.showCompleted ? [...completed, ...cancelled] : [];
122
+ const candidates = [...open, ...terminal];
123
+ const limit = expanded ? 24 : 8;
124
+ const rows = candidates.slice(0, limit).map(taskStepPanelRow);
125
+ const archivedDone = state.archive.completed + state.archive.cancelled;
126
+ const done = completed.length + state.archive.completed;
127
+ const summary = [
128
+ active.length ? `${active.length} working` : undefined,
129
+ `${open.length} open`,
130
+ `${done} done`,
131
+ blocked.length ? `${blocked.length} blocked` : undefined,
132
+ archivedDone ? `${archivedDone} archived` : undefined,
133
+ ].filter((value) => value !== undefined);
134
+ const notices = [];
135
+ if (!details.showCompleted && completed.length + cancelled.length > 0) {
136
+ notices.push({
137
+ status: "info",
138
+ text: `${completed.length + cancelled.length} finished step${completed.length + cancelled.length === 1 ? "" : "s"} hidden`,
139
+ });
140
+ }
141
+ if (details.duplicateOfStepId) {
142
+ notices.push({ status: "info", text: "Duplicate open step ignored." });
143
+ }
144
+ if (details.demotedStepIds?.length) {
145
+ notices.push({
146
+ status: "info",
147
+ text: `${details.demotedStepIds.length} previous working step${details.demotedStepIds.length === 1 ? "" : "s"} returned to pending.`,
148
+ });
149
+ }
150
+ if (details.verificationNudgeNeeded) {
151
+ notices.push({ status: "warning", text: "Completed work still needs attached evidence." });
152
+ }
153
+ const status = details.error
154
+ ? "error"
155
+ : blocked.length > 0
156
+ ? "warning"
157
+ : active.length > 0
158
+ ? "running"
159
+ : open.length === 0 && state.steps.length > 0
160
+ ? "success"
161
+ : "idle";
162
+ return {
163
+ label: "task steps",
164
+ action: details.action,
165
+ status,
166
+ summary,
167
+ rows,
168
+ notices,
169
+ emptyText: state.steps.length === 0 ? "No tracked steps." : "All open steps are done.",
170
+ hiddenRowCount: Math.max(0, candidates.length - rows.length),
171
+ };
172
+ }
95
173
  export function createTaskStepsToolDefinition(deps) {
96
174
  const now = deps.now ?? (() => new Date().toISOString());
97
175
  return {
@@ -104,10 +182,33 @@ export function createTaskStepsToolDefinition(deps) {
104
182
  "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.",
105
183
  "Mark steps completed as soon as evidence is gathered, and attach concise evidence or a blocker reason through update.",
106
184
  "Use action=intake with a complete steps array when preserving a raw multi-item dump; retain every item and do not silently drop entries.",
185
+ "When a goal is active, set requirementIds on each step that advances a goal requirement; do not rely on repeating requirement text.",
107
186
  "Drain task_steps before final responses: leave no stale in_progress step, and explicitly discuss or defer remaining pending or blocked work.",
108
187
  "Use goal for the durable goal ledger and delegate/delegate_status for worker lanes; do not emulate background execution inside task_steps.",
109
188
  ],
110
189
  parameters: taskStepsSchema,
190
+ renderShell: "self",
191
+ renderCall() {
192
+ return emptyOrchestrationCall();
193
+ },
194
+ renderResult(result, { expanded, isPartial }, theme) {
195
+ if (isPartial) {
196
+ return new OrchestrationPanelComponent(theme, {
197
+ label: "task steps",
198
+ action: "updating",
199
+ status: "running",
200
+ });
201
+ }
202
+ const details = result.details;
203
+ if (!details) {
204
+ return new OrchestrationPanelComponent(theme, {
205
+ label: "task steps",
206
+ status: "error",
207
+ emptyText: "No task-step details were returned.",
208
+ });
209
+ }
210
+ return new OrchestrationPanelComponent(theme, taskStepsPanelModel(details, expanded), expanded);
211
+ },
111
212
  async execute(_toolCallId, input) {
112
213
  const timestamp = now();
113
214
  const current = deps.getTaskStepsState();
@@ -147,6 +248,7 @@ export function createTaskStepsToolDefinition(deps) {
147
248
  status: input.status,
148
249
  priority: input.priority,
149
250
  owner: input.owner,
251
+ requirementIds: input.requirementIds,
150
252
  note: input.note,
151
253
  evidence: input.evidence,
152
254
  }, timestamp);
@@ -1 +1 @@
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"]}
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\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\";\nimport {\n\temptyOrchestrationCall,\n\tOrchestrationPanelComponent,\n\ttype OrchestrationPanelModel,\n\ttaskStepPanelRow,\n} from \"./orchestration-panel.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\trequirementIds: Type.Optional(\n\t\t\tType.Array(Type.String({ minLength: 1, maxLength: 200 }), {\n\t\t\t\tmaxItems: 32,\n\t\t\t\tdescription: \"Goal requirement ids this foreground step advances.\",\n\t\t\t}),\n\t\t),\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\trequirementIds: Type.Optional(\n\t\t\tType.Array(Type.String({ minLength: 1, maxLength: 200 }), {\n\t\t\t\tmaxItems: 32,\n\t\t\t\tdescription: \"Goal requirement ids this foreground step advances; [] clears existing links.\",\n\t\t\t}),\n\t\t),\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\trequirementIds: input.requirementIds,\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\nfunction taskStepsPanelModel(details: TaskStepsToolDetails, expanded: boolean): OrchestrationPanelModel {\n\tconst state = details.state;\n\tif (!state) {\n\t\treturn {\n\t\t\tlabel: \"task steps\",\n\t\t\taction: details.action,\n\t\t\tstatus: details.error ? \"error\" : \"idle\",\n\t\t\temptyText: details.error ?? \"No checklist state.\",\n\t\t};\n\t}\n\tconst active = state.steps.filter((step) => step.status === \"in_progress\");\n\tconst blocked = state.steps.filter((step) => step.status === \"blocked\");\n\tconst pending = state.steps.filter((step) => step.status === \"pending\");\n\tconst completed = state.steps.filter((step) => step.status === \"completed\");\n\tconst cancelled = state.steps.filter((step) => step.status === \"cancelled\");\n\tconst open = [...active, ...blocked, ...pending];\n\tconst terminal = details.showCompleted ? [...completed, ...cancelled] : [];\n\tconst candidates = [...open, ...terminal];\n\tconst limit = expanded ? 24 : 8;\n\tconst rows = candidates.slice(0, limit).map(taskStepPanelRow);\n\tconst archivedDone = state.archive.completed + state.archive.cancelled;\n\tconst done = completed.length + state.archive.completed;\n\tconst summary = [\n\t\tactive.length ? `${active.length} working` : undefined,\n\t\t`${open.length} open`,\n\t\t`${done} done`,\n\t\tblocked.length ? `${blocked.length} blocked` : undefined,\n\t\tarchivedDone ? `${archivedDone} archived` : undefined,\n\t].filter((value): value is string => value !== undefined);\n\tconst notices = [];\n\tif (!details.showCompleted && completed.length + cancelled.length > 0) {\n\t\tnotices.push({\n\t\t\tstatus: \"info\" as const,\n\t\t\ttext: `${completed.length + cancelled.length} finished step${completed.length + cancelled.length === 1 ? \"\" : \"s\"} hidden`,\n\t\t});\n\t}\n\tif (details.duplicateOfStepId) {\n\t\tnotices.push({ status: \"info\" as const, text: \"Duplicate open step ignored.\" });\n\t}\n\tif (details.demotedStepIds?.length) {\n\t\tnotices.push({\n\t\t\tstatus: \"info\" as const,\n\t\t\ttext: `${details.demotedStepIds.length} previous working step${details.demotedStepIds.length === 1 ? \"\" : \"s\"} returned to pending.`,\n\t\t});\n\t}\n\tif (details.verificationNudgeNeeded) {\n\t\tnotices.push({ status: \"warning\" as const, text: \"Completed work still needs attached evidence.\" });\n\t}\n\tconst status = details.error\n\t\t? \"error\"\n\t\t: blocked.length > 0\n\t\t\t? \"warning\"\n\t\t\t: active.length > 0\n\t\t\t\t? \"running\"\n\t\t\t\t: open.length === 0 && state.steps.length > 0\n\t\t\t\t\t? \"success\"\n\t\t\t\t\t: \"idle\";\n\treturn {\n\t\tlabel: \"task steps\",\n\t\taction: details.action,\n\t\tstatus,\n\t\tsummary,\n\t\trows,\n\t\tnotices,\n\t\temptyText: state.steps.length === 0 ? \"No tracked steps.\" : \"All open steps are done.\",\n\t\thiddenRowCount: Math.max(0, candidates.length - rows.length),\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\"When a goal is active, set requirementIds on each step that advances a goal requirement; do not rely on repeating requirement text.\",\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\trenderShell: \"self\",\n\t\trenderCall() {\n\t\t\treturn emptyOrchestrationCall();\n\t\t},\n\t\trenderResult(result, { expanded, isPartial }, theme) {\n\t\t\tif (isPartial) {\n\t\t\t\treturn new OrchestrationPanelComponent(theme, {\n\t\t\t\t\tlabel: \"task steps\",\n\t\t\t\t\taction: \"updating\",\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst details = result.details as TaskStepsToolDetails | undefined;\n\t\t\tif (!details) {\n\t\t\t\treturn new OrchestrationPanelComponent(theme, {\n\t\t\t\t\tlabel: \"task steps\",\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\temptyText: \"No task-step details were returned.\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn new OrchestrationPanelComponent(theme, taskStepsPanelModel(details, expanded), expanded);\n\t\t},\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\trequirementIds: input.requirementIds,\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"]}
@@ -16,11 +16,9 @@ export interface TmuxDispatchDeps {
16
16
  * live model turn. */
17
17
  createExtensionContext: () => ExtensionContext;
18
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.
19
+ * Confirm that the tmux extension's caller-chosen lane id (reconstructed below from the
20
+ * `fire_task` result's `details.job`) was durably registered by the host. The returned id is
21
+ * identity-preserving and is the same value `Requirement.boundLaneId` uses.
24
22
  */
25
23
  resolveManagedLaneId: (callerLaneId: string) => string | undefined;
26
24
  /** Active goal id, threaded onto the `fire_task` call so the dispatched lane is goal-tagged
@@ -1 +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"]}
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;;;;OAIG;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 * Confirm that the tmux extension's caller-chosen lane id (reconstructed below from the\n\t * `fire_task` result's `details.job`) was durably registered by the host. The returned id is\n\t * identity-preserving and is the same value `Requirement.boundLaneId` uses.\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 preserves as the canonical durable id; `resolveManagedLaneId` confirms that the\n\t// matching dispatch report reached the host before the goal binds it.\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 registeredLaneId = deps.resolveManagedLaneId(callerLaneId);\n\treturn registeredLaneId === callerLaneId ? { laneId: callerLaneId } : { skipReason: \"lane_correlation_failed\" };\n}\n"]}
@@ -78,14 +78,14 @@ export async function dispatchTmuxWorker(deps, args) {
78
78
  // Correlate: the extension reports its dispatch under its OWN caller-chosen laneId
79
79
  // (`tmux:${job.id}:${agent.id}`, reconstructed here from the tool result's `details.job` --
80
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.
81
+ // bridge preserves as the canonical durable id; `resolveManagedLaneId` confirms that the
82
+ // matching dispatch report reached the host before the goal binds it.
83
83
  const job = result.details?.job;
84
84
  const primary = job?.agents?.[0];
85
85
  if (!job?.id || !primary?.id)
86
86
  return { skipReason: "tmux_dispatch_incomplete" };
87
87
  const callerLaneId = `tmux:${job.id}:${primary.id}`;
88
- const laneId = deps.resolveManagedLaneId(callerLaneId);
89
- return laneId ? { laneId } : { skipReason: "lane_correlation_failed" };
88
+ const registeredLaneId = deps.resolveManagedLaneId(callerLaneId);
89
+ return registeredLaneId === callerLaneId ? { laneId: callerLaneId } : { skipReason: "lane_correlation_failed" };
90
90
  }
91
91
  //# sourceMappingURL=tmux-dispatch.js.map
@@ -1 +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"]}
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 * Confirm that the tmux extension's caller-chosen lane id (reconstructed below from the\n\t * `fire_task` result's `details.job`) was durably registered by the host. The returned id is\n\t * identity-preserving and is the same value `Requirement.boundLaneId` uses.\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 preserves as the canonical durable id; `resolveManagedLaneId` confirms that the\n\t// matching dispatch report reached the host before the goal binds it.\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 registeredLaneId = deps.resolveManagedLaneId(callerLaneId);\n\treturn registeredLaneId === callerLaneId ? { laneId: callerLaneId } : { skipReason: \"lane_correlation_failed\" };\n}\n"]}
@@ -1 +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"]}
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;AAEtF,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;AAmCD,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 { isRecordObject } from \"../util/value-guards.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\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 (!isRecordObject(raw)) return undefined;\n\t\tif (typeof raw.exitCode !== \"number\" || !Number.isInteger(raw.exitCode)) return undefined;\n\t\tif (typeof raw.cwd !== \"string\" || !isRecordObject(raw.envDelta)) return undefined;\n\t\tif (raw.unsupported !== null && !isRecordObject(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"]}
@@ -10,6 +10,7 @@ import { getBundledResourcesDir } from "../../config.js";
10
10
  import { spawnProcess, waitForChildProcessWithTermination } from "../../utils/child-process.js";
11
11
  import { getShellEnv, trackDetachedChildPid, untrackDetachedChildPid } from "../../utils/shell.js";
12
12
  import { ensurePythonRuntime } from "../python-runtime.js";
13
+ import { isRecordObject } from "../util/value-guards.js";
13
14
  import { applyEngineFrame, getOrCreateWindowsShellState, mergeEffectiveEnv, resolveEffectiveCwd, } from "./windows-shell-state.js";
14
15
  const ENGINE_FRAME_SENTINEL = 0x1e;
15
16
  export class WindowsShellEngineFailure extends Error {
@@ -24,9 +25,6 @@ function resolveEngineScriptPath() {
24
25
  return join(getBundledResourcesDir(), "runtimes", "pi-shell-engine", "main.py");
25
26
  }
26
27
  const MAX_CONTROL_FRAME_BYTES = 64 * 1024;
27
- function isRecord(value) {
28
- return typeof value === "object" && value !== null && !Array.isArray(value);
29
- }
30
28
  /** Parse the terminal frame from the dedicated control stream. Command output is never retained here. */
31
29
  function parseControlFrame(buffer) {
32
30
  if (buffer.length < 2 || buffer[buffer.length - 1] !== ENGINE_FRAME_SENTINEL)
@@ -36,13 +34,13 @@ function parseControlFrame(buffer) {
36
34
  return undefined;
37
35
  try {
38
36
  const raw = JSON.parse(buffer.subarray(openIndex + 1, buffer.length - 1).toString("utf8"));
39
- if (!isRecord(raw))
37
+ if (!isRecordObject(raw))
40
38
  return undefined;
41
39
  if (typeof raw.exitCode !== "number" || !Number.isInteger(raw.exitCode))
42
40
  return undefined;
43
- if (typeof raw.cwd !== "string" || !isRecord(raw.envDelta))
41
+ if (typeof raw.cwd !== "string" || !isRecordObject(raw.envDelta))
44
42
  return undefined;
45
- if (raw.unsupported !== null && !isRecord(raw.unsupported))
43
+ if (raw.unsupported !== null && !isRecordObject(raw.unsupported))
46
44
  return undefined;
47
45
  if (raw.unsupported !== null &&
48
46
  (raw.unsupported.code !== "unsupported" ||
@@ -1 +1 @@
1
- {"version":3,"file":"windows-shell-engine.js","sourceRoot":"","sources":["../../../src/core/tools/windows-shell-engine.ts"],"names":[],"mappings":"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","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"]}
1
+ {"version":3,"file":"windows-shell-engine.js","sourceRoot":"","sources":["../../../src/core/tools/windows-shell-engine.ts"],"names":[],"mappings":"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","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 { isRecordObject } from \"../util/value-guards.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\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 (!isRecordObject(raw)) return undefined;\n\t\tif (typeof raw.exitCode !== \"number\" || !Number.isInteger(raw.exitCode)) return undefined;\n\t\tif (typeof raw.cwd !== \"string\" || !isRecordObject(raw.envDelta)) return undefined;\n\t\tif (raw.unsupported !== null && !isRecordObject(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"]}
@@ -0,0 +1,10 @@
1
+ export interface BoundedJsonlLimits {
2
+ maxBytes: number;
3
+ targetBytes: number;
4
+ maxRecords: number;
5
+ }
6
+ /** Append one JSON record and rotate to the newest low-water tail under one exclusive lock. */
7
+ export declare function appendBoundedJsonLineSync(filePath: string, value: unknown, limits: BoundedJsonlLimits): void;
8
+ /** Async counterpart of {@link appendBoundedJsonLineSync}. */
9
+ export declare function appendBoundedJsonLine(filePath: string, value: unknown, limits: BoundedJsonlLimits): Promise<void>;
10
+ //# sourceMappingURL=bounded-jsonl.d.ts.map