@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foreground-recovery-controller.d.ts","sourceRoot":"","sources":["../../src/core/foreground-recovery-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAmB,MAAM,2BAA2B,CAAC;AAEpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,KAAK,uBAAuB,GACzB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACzG;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtF,MAAM,WAAW,gCAAgC;IAChD,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,EAAE,eAAe,CAAC;IACjC,aAAa,EAAE,aAAa,CAAC;IAC7B,aAAa,EAAE,qBAAqB,CAAC;IACrC,gBAAgB,IAAI,MAAM,CAAC;IAC3B,IAAI,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC3C,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,qBAAqB,IAAI,IAAI,CAAC;CAC9B;AAED,0FAA0F;AAC1F,qBAAa,4BAA4B;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA4B;IAC5D,OAAO,CAAC,oBAAoB,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IAExD,YAAY,IAAI,EAAE,gCAAgC,EA4BjD;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAErC;IAED,iBAAiB,IAAI,yBAAyB,CAE7C;IAED,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAQhD;IAED,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,EAAE;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC,GAAG,OAAO,CAQjF;IAEK,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CA4B3C;IAED,OAAO,CAAC,sBAAsB;CAQ9B","sourcesContent":["import type { Agent, AgentEvent, ClassifiedError } from \"@caupulican/pi-agent-core\";\nimport { classifyFailure, DEFAULT_RETRY_POLICY, RetryController } from \"@caupulican/pi-agent-core\";\nimport type { AssistantMessage } from \"@caupulican/pi-ai\";\nimport { isContextOverflow } from \"@caupulican/pi-ai\";\nimport { BillingFailoverController, ExhaustedProviderRegistry } from \"./billing-failover-controller.ts\";\nimport type { FailureCorpusRecorder } from \"./failure-corpus.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport type { ModelRouterFailoverStatus } from \"./model-router/status.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\ntype ForegroundRecoveryEvent =\n\t| { type: \"warning\"; message: string }\n\t| { type: \"auto_retry_start\"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }\n\t| { type: \"auto_retry_end\"; success: boolean; attempt: number; finalError?: string };\n\nexport interface ForegroundRecoveryControllerDeps {\n\tagent: Agent;\n\tsettingsManager: SettingsManager;\n\tmodelRegistry: ModelRegistry;\n\tfailureCorpus: FailureCorpusRecorder;\n\tgetContextWindow(): number;\n\temit(event: ForegroundRecoveryEvent): void;\n\tcheckCompaction(message: AssistantMessage): Promise<boolean>;\n\tonSuccessfulAssistant(): void;\n}\n\n/** Owns foreground retry/failover/compaction recovery ordering and its response latch. */\nexport class ForegroundRecoveryController {\n\tprivate readonly retry: RetryController;\n\tprivate readonly billingFailover: BillingFailoverController;\n\tprivate lastAssistantMessage: AssistantMessage | undefined;\n\tprivate readonly deps: ForegroundRecoveryControllerDeps;\n\n\tconstructor(deps: ForegroundRecoveryControllerDeps) {\n\t\tthis.deps = deps;\n\t\tthis.retry = new RetryController(\n\t\t\tdeps.agent,\n\t\t\t() => {\n\t\t\t\tconst retry = deps.settingsManager.getRetrySettings();\n\t\t\t\treturn {\n\t\t\t\t\tenabled: retry.enabled,\n\t\t\t\t\tmaxAttempts: retry.maxRetries,\n\t\t\t\t\tbaseDelayMs: retry.baseDelayMs,\n\t\t\t\t\tmaxDelayMs: DEFAULT_RETRY_POLICY.maxDelayMs,\n\t\t\t\t\tjitterRatio: 0,\n\t\t\t\t};\n\t\t\t},\n\t\t\t{\n\t\t\t\tonRetryStart: (info) => deps.emit({ type: \"auto_retry_start\", ...info }),\n\t\t\t\tonRetryEnd: (info) => deps.emit({ type: \"auto_retry_end\", ...info }),\n\t\t\t},\n\t\t\tdeps.getContextWindow,\n\t\t);\n\t\tthis.billingFailover = new BillingFailoverController({\n\t\t\tagent: deps.agent,\n\t\t\tmodelRegistry: deps.modelRegistry,\n\t\t\texhausted: new ExhaustedProviderRegistry(),\n\t\t\tsubscriptionHop: deps.settingsManager.getFailoverSettings().subscriptionHop,\n\t\t\temit: deps.emit,\n\t\t\trecordFailure: (record) => deps.failureCorpus.record(record),\n\t\t});\n\t}\n\n\tget attempt(): number {\n\t\treturn this.retry.attempt;\n\t}\n\n\tget isRetrying(): boolean {\n\t\treturn this.retry.isRetrying;\n\t}\n\n\tabortRetry(): void {\n\t\tthis.retry.abort();\n\t}\n\n\tisModelExhausted(ref: string): boolean {\n\t\treturn this.billingFailover.isExhausted(ref);\n\t}\n\n\tgetFailoverStatus(): ModelRouterFailoverStatus {\n\t\treturn this.billingFailover.getStatus();\n\t}\n\n\tobserveAssistant(message: AssistantMessage): void {\n\t\tthis.lastAssistantMessage = message;\n\t\tif (message.stopReason === \"error\") return;\n\t\tthis.deps.onSuccessfulAssistant();\n\t\tif (this.retry.attempt > 0) {\n\t\t\tthis.deps.emit({ type: \"auto_retry_end\", success: true, attempt: this.retry.attempt });\n\t\t\tthis.retry.reset();\n\t\t}\n\t}\n\n\twillRetryAfterAgentEnd(event: Extract<AgentEvent, { type: \"agent_end\" }>): boolean {\n\t\tconst settings = this.deps.settingsManager.getRetrySettings();\n\t\tif (!settings.enabled || this.retry.attempt >= settings.maxRetries) return false;\n\t\tfor (let index = event.messages.length - 1; index >= 0; index--) {\n\t\t\tconst message = event.messages[index];\n\t\t\tif (message.role === \"assistant\") return this.classifyAssistantError(message)?.retryable ?? false;\n\t\t}\n\t\treturn false;\n\t}\n\n\tasync handlePostAgentRun(): Promise<boolean> {\n\t\tconst message = this.lastAssistantMessage;\n\t\tthis.lastAssistantMessage = undefined;\n\t\tif (!message) return false;\n\n\t\tconst classified = this.classifyAssistantError(message);\n\t\tif (classified) {\n\t\t\tthis.deps.failureCorpus.record({\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodelId: message.model,\n\t\t\t\tmessage: message.errorMessage ?? \"\",\n\t\t\t\tclassified,\n\t\t\t});\n\t\t}\n\t\tif (classified?.retryable && (await this.retry.prepareRetry(message))) return true;\n\t\tif (await this.billingFailover.handleAssistantError(message, classified)) return false;\n\n\t\tif (message.stopReason === \"error\" && this.retry.attempt > 0) {\n\t\t\tthis.deps.emit({\n\t\t\t\ttype: \"auto_retry_end\",\n\t\t\t\tsuccess: false,\n\t\t\t\tattempt: this.retry.attempt,\n\t\t\t\tfinalError: message.errorMessage,\n\t\t\t});\n\t\t\tthis.retry.reset();\n\t\t}\n\t\tif (await this.deps.checkCompaction(message)) return true;\n\t\treturn this.deps.agent.hasQueuedMessages();\n\t}\n\n\tprivate classifyAssistantError(message: AssistantMessage): ClassifiedError | undefined {\n\t\tif (message.stopReason !== \"error\" || !message.errorMessage) return undefined;\n\t\treturn classifyFailure({\n\t\t\tmessage: message.errorMessage,\n\t\t\tcontextOverflow: isContextOverflow(message, this.deps.getContextWindow()),\n\t\t\tprovider: message.provider,\n\t\t});\n\t}\n}\n"]}
@@ -0,0 +1,111 @@
1
+ import { classifyFailure, DEFAULT_RETRY_POLICY, RetryController } from "@caupulican/pi-agent-core";
2
+ import { isContextOverflow } from "@caupulican/pi-ai";
3
+ import { BillingFailoverController, ExhaustedProviderRegistry } from "./billing-failover-controller.js";
4
+ /** Owns foreground retry/failover/compaction recovery ordering and its response latch. */
5
+ export class ForegroundRecoveryController {
6
+ retry;
7
+ billingFailover;
8
+ lastAssistantMessage;
9
+ deps;
10
+ constructor(deps) {
11
+ this.deps = deps;
12
+ this.retry = new RetryController(deps.agent, () => {
13
+ const retry = deps.settingsManager.getRetrySettings();
14
+ return {
15
+ enabled: retry.enabled,
16
+ maxAttempts: retry.maxRetries,
17
+ baseDelayMs: retry.baseDelayMs,
18
+ maxDelayMs: DEFAULT_RETRY_POLICY.maxDelayMs,
19
+ jitterRatio: 0,
20
+ };
21
+ }, {
22
+ onRetryStart: (info) => deps.emit({ type: "auto_retry_start", ...info }),
23
+ onRetryEnd: (info) => deps.emit({ type: "auto_retry_end", ...info }),
24
+ }, deps.getContextWindow);
25
+ this.billingFailover = new BillingFailoverController({
26
+ agent: deps.agent,
27
+ modelRegistry: deps.modelRegistry,
28
+ exhausted: new ExhaustedProviderRegistry(),
29
+ subscriptionHop: deps.settingsManager.getFailoverSettings().subscriptionHop,
30
+ emit: deps.emit,
31
+ recordFailure: (record) => deps.failureCorpus.record(record),
32
+ });
33
+ }
34
+ get attempt() {
35
+ return this.retry.attempt;
36
+ }
37
+ get isRetrying() {
38
+ return this.retry.isRetrying;
39
+ }
40
+ abortRetry() {
41
+ this.retry.abort();
42
+ }
43
+ isModelExhausted(ref) {
44
+ return this.billingFailover.isExhausted(ref);
45
+ }
46
+ getFailoverStatus() {
47
+ return this.billingFailover.getStatus();
48
+ }
49
+ observeAssistant(message) {
50
+ this.lastAssistantMessage = message;
51
+ if (message.stopReason === "error")
52
+ return;
53
+ this.deps.onSuccessfulAssistant();
54
+ if (this.retry.attempt > 0) {
55
+ this.deps.emit({ type: "auto_retry_end", success: true, attempt: this.retry.attempt });
56
+ this.retry.reset();
57
+ }
58
+ }
59
+ willRetryAfterAgentEnd(event) {
60
+ const settings = this.deps.settingsManager.getRetrySettings();
61
+ if (!settings.enabled || this.retry.attempt >= settings.maxRetries)
62
+ return false;
63
+ for (let index = event.messages.length - 1; index >= 0; index--) {
64
+ const message = event.messages[index];
65
+ if (message.role === "assistant")
66
+ return this.classifyAssistantError(message)?.retryable ?? false;
67
+ }
68
+ return false;
69
+ }
70
+ async handlePostAgentRun() {
71
+ const message = this.lastAssistantMessage;
72
+ this.lastAssistantMessage = undefined;
73
+ if (!message)
74
+ return false;
75
+ const classified = this.classifyAssistantError(message);
76
+ if (classified) {
77
+ this.deps.failureCorpus.record({
78
+ provider: message.provider,
79
+ modelId: message.model,
80
+ message: message.errorMessage ?? "",
81
+ classified,
82
+ });
83
+ }
84
+ if (classified?.retryable && (await this.retry.prepareRetry(message)))
85
+ return true;
86
+ if (await this.billingFailover.handleAssistantError(message, classified))
87
+ return false;
88
+ if (message.stopReason === "error" && this.retry.attempt > 0) {
89
+ this.deps.emit({
90
+ type: "auto_retry_end",
91
+ success: false,
92
+ attempt: this.retry.attempt,
93
+ finalError: message.errorMessage,
94
+ });
95
+ this.retry.reset();
96
+ }
97
+ if (await this.deps.checkCompaction(message))
98
+ return true;
99
+ return this.deps.agent.hasQueuedMessages();
100
+ }
101
+ classifyAssistantError(message) {
102
+ if (message.stopReason !== "error" || !message.errorMessage)
103
+ return undefined;
104
+ return classifyFailure({
105
+ message: message.errorMessage,
106
+ contextOverflow: isContextOverflow(message, this.deps.getContextWindow()),
107
+ provider: message.provider,
108
+ });
109
+ }
110
+ }
111
+ //# sourceMappingURL=foreground-recovery-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foreground-recovery-controller.js","sourceRoot":"","sources":["../../src/core/foreground-recovery-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentEvent, ClassifiedError } from \"@caupulican/pi-agent-core\";\nimport { classifyFailure, DEFAULT_RETRY_POLICY, RetryController } from \"@caupulican/pi-agent-core\";\nimport type { AssistantMessage } from \"@caupulican/pi-ai\";\nimport { isContextOverflow } from \"@caupulican/pi-ai\";\nimport { BillingFailoverController, ExhaustedProviderRegistry } from \"./billing-failover-controller.ts\";\nimport type { FailureCorpusRecorder } from \"./failure-corpus.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport type { ModelRouterFailoverStatus } from \"./model-router/status.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\ntype ForegroundRecoveryEvent =\n\t| { type: \"warning\"; message: string }\n\t| { type: \"auto_retry_start\"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }\n\t| { type: \"auto_retry_end\"; success: boolean; attempt: number; finalError?: string };\n\nexport interface ForegroundRecoveryControllerDeps {\n\tagent: Agent;\n\tsettingsManager: SettingsManager;\n\tmodelRegistry: ModelRegistry;\n\tfailureCorpus: FailureCorpusRecorder;\n\tgetContextWindow(): number;\n\temit(event: ForegroundRecoveryEvent): void;\n\tcheckCompaction(message: AssistantMessage): Promise<boolean>;\n\tonSuccessfulAssistant(): void;\n}\n\n/** Owns foreground retry/failover/compaction recovery ordering and its response latch. */\nexport class ForegroundRecoveryController {\n\tprivate readonly retry: RetryController;\n\tprivate readonly billingFailover: BillingFailoverController;\n\tprivate lastAssistantMessage: AssistantMessage | undefined;\n\tprivate readonly deps: ForegroundRecoveryControllerDeps;\n\n\tconstructor(deps: ForegroundRecoveryControllerDeps) {\n\t\tthis.deps = deps;\n\t\tthis.retry = new RetryController(\n\t\t\tdeps.agent,\n\t\t\t() => {\n\t\t\t\tconst retry = deps.settingsManager.getRetrySettings();\n\t\t\t\treturn {\n\t\t\t\t\tenabled: retry.enabled,\n\t\t\t\t\tmaxAttempts: retry.maxRetries,\n\t\t\t\t\tbaseDelayMs: retry.baseDelayMs,\n\t\t\t\t\tmaxDelayMs: DEFAULT_RETRY_POLICY.maxDelayMs,\n\t\t\t\t\tjitterRatio: 0,\n\t\t\t\t};\n\t\t\t},\n\t\t\t{\n\t\t\t\tonRetryStart: (info) => deps.emit({ type: \"auto_retry_start\", ...info }),\n\t\t\t\tonRetryEnd: (info) => deps.emit({ type: \"auto_retry_end\", ...info }),\n\t\t\t},\n\t\t\tdeps.getContextWindow,\n\t\t);\n\t\tthis.billingFailover = new BillingFailoverController({\n\t\t\tagent: deps.agent,\n\t\t\tmodelRegistry: deps.modelRegistry,\n\t\t\texhausted: new ExhaustedProviderRegistry(),\n\t\t\tsubscriptionHop: deps.settingsManager.getFailoverSettings().subscriptionHop,\n\t\t\temit: deps.emit,\n\t\t\trecordFailure: (record) => deps.failureCorpus.record(record),\n\t\t});\n\t}\n\n\tget attempt(): number {\n\t\treturn this.retry.attempt;\n\t}\n\n\tget isRetrying(): boolean {\n\t\treturn this.retry.isRetrying;\n\t}\n\n\tabortRetry(): void {\n\t\tthis.retry.abort();\n\t}\n\n\tisModelExhausted(ref: string): boolean {\n\t\treturn this.billingFailover.isExhausted(ref);\n\t}\n\n\tgetFailoverStatus(): ModelRouterFailoverStatus {\n\t\treturn this.billingFailover.getStatus();\n\t}\n\n\tobserveAssistant(message: AssistantMessage): void {\n\t\tthis.lastAssistantMessage = message;\n\t\tif (message.stopReason === \"error\") return;\n\t\tthis.deps.onSuccessfulAssistant();\n\t\tif (this.retry.attempt > 0) {\n\t\t\tthis.deps.emit({ type: \"auto_retry_end\", success: true, attempt: this.retry.attempt });\n\t\t\tthis.retry.reset();\n\t\t}\n\t}\n\n\twillRetryAfterAgentEnd(event: Extract<AgentEvent, { type: \"agent_end\" }>): boolean {\n\t\tconst settings = this.deps.settingsManager.getRetrySettings();\n\t\tif (!settings.enabled || this.retry.attempt >= settings.maxRetries) return false;\n\t\tfor (let index = event.messages.length - 1; index >= 0; index--) {\n\t\t\tconst message = event.messages[index];\n\t\t\tif (message.role === \"assistant\") return this.classifyAssistantError(message)?.retryable ?? false;\n\t\t}\n\t\treturn false;\n\t}\n\n\tasync handlePostAgentRun(): Promise<boolean> {\n\t\tconst message = this.lastAssistantMessage;\n\t\tthis.lastAssistantMessage = undefined;\n\t\tif (!message) return false;\n\n\t\tconst classified = this.classifyAssistantError(message);\n\t\tif (classified) {\n\t\t\tthis.deps.failureCorpus.record({\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodelId: message.model,\n\t\t\t\tmessage: message.errorMessage ?? \"\",\n\t\t\t\tclassified,\n\t\t\t});\n\t\t}\n\t\tif (classified?.retryable && (await this.retry.prepareRetry(message))) return true;\n\t\tif (await this.billingFailover.handleAssistantError(message, classified)) return false;\n\n\t\tif (message.stopReason === \"error\" && this.retry.attempt > 0) {\n\t\t\tthis.deps.emit({\n\t\t\t\ttype: \"auto_retry_end\",\n\t\t\t\tsuccess: false,\n\t\t\t\tattempt: this.retry.attempt,\n\t\t\t\tfinalError: message.errorMessage,\n\t\t\t});\n\t\t\tthis.retry.reset();\n\t\t}\n\t\tif (await this.deps.checkCompaction(message)) return true;\n\t\treturn this.deps.agent.hasQueuedMessages();\n\t}\n\n\tprivate classifyAssistantError(message: AssistantMessage): ClassifiedError | undefined {\n\t\tif (message.stopReason !== \"error\" || !message.errorMessage) return undefined;\n\t\treturn classifyFailure({\n\t\t\tmessage: message.errorMessage,\n\t\t\tcontextOverflow: isContextOverflow(message, this.deps.getContextWindow()),\n\t\t\tprovider: message.provider,\n\t\t});\n\t}\n}\n"]}
@@ -60,6 +60,9 @@ export declare class GatewayRegistry {
60
60
  private readonly schedulers;
61
61
  private started;
62
62
  private inboundHandler;
63
+ private readonly pendingLifecycle;
64
+ private trackLifecycle;
65
+ private drainLifecycle;
63
66
  registerChannel(provider: ChannelProvider): void;
64
67
  registerScheduler(provider: JobSchedulerProvider): void;
65
68
  getChannel(name: string): ChannelProvider | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"channel-provider.d.ts","sourceRoot":"","sources":["../../../src/core/gateways/channel-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC9B,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,mEAAmE;AACnE,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtF;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,KAAK,CAAC,SAAS,EAAE,qBAAqB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,gDAAgD;IAChD,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,4CAA4C;IAC5C,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,GAAG,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,KAAK,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2C;IACtE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAmC;IAEzD,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAM/C;IAED,iBAAiB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAKtD;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEpD;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,2FAA2F;IACrF,KAAK,CAAC,SAAS,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB3D;IAED,uFAAuF;IACjF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAa1B;CACD","sourcesContent":["/**\n * Interface-driven gateways & scheduling (adaptive-agent design R8).\n *\n * pi does NOT bake transports (Slack/Discord/email/webhooks) or a cron tick loop into the core — that\n * would bloat the agent and couple it to deployment concerns. Instead it exposes two provider contracts\n * that a deployment wrapper (server, headless runner) implements and registers; the core only manages\n * their lifecycle. This keeps the CLI/agent transport- and schedule-agnostic while still offering a\n * first-class, uniform extension point.\n */\n\n/** A message arriving from / sent to an external channel (gateway). */\nexport interface ChannelMessage {\n\t/** Stable conversation/session key for this channel (e.g. `slack_C123`). */\n\tconversationKey: string;\n\t/** Message text. */\n\ttext: string;\n\t/** Optional opaque metadata the provider round-trips. */\n\tmeta?: Record<string, unknown>;\n}\n\n/** Handler the core supplies to a channel for inbound messages. */\nexport type ChannelInboundHandler = (message: ChannelMessage) => void | Promise<void>;\n\n/**\n * A transport channel (Telegram/Slack/email/webhook/...). Implemented by a deployment wrapper and\n * registered via {@link GatewayRegistry}. The core starts it (handing it an inbound handler) and stops\n * it on shutdown; it never imports any transport SDK itself.\n */\nexport interface ChannelProvider {\n\treadonly name: string;\n\t/** Begin listening; deliver inbound messages to `onInbound`. */\n\tstart(onInbound: ChannelInboundHandler): void | Promise<void>;\n\t/** Send an outbound message on this channel. */\n\tsend(message: ChannelMessage): void | Promise<void>;\n\t/** Stop listening and release resources. */\n\tstop(): void | Promise<void>;\n}\n\n/** A scheduled job definition. */\nexport interface ScheduledJob {\n\tid: string;\n\t/** Cron expression or interval spec the provider understands. */\n\tschedule: string;\n\t/** Invoked when the job fires. */\n\trun: () => void | Promise<void>;\n}\n\n/**\n * A scheduler (cron-like). Implemented by a deployment wrapper and registered via\n * {@link GatewayRegistry}. The core registers jobs + starts/stops it; it owns no tick loop itself.\n */\nexport interface JobSchedulerProvider {\n\treadonly name: string;\n\tschedule(job: ScheduledJob): void;\n\tstart(): void | Promise<void>;\n\tstop(): void | Promise<void>;\n}\n\n/**\n * Holds registered channel + scheduler providers and drives their lifecycle. A session starts all\n * registered providers when it binds and stops them on dispose. Registration is additive and idempotent\n * by provider name (last registration wins).\n */\nexport class GatewayRegistry {\n\tprivate readonly channels = new Map<string, ChannelProvider>();\n\tprivate readonly schedulers = new Map<string, JobSchedulerProvider>();\n\tprivate started = false;\n\tprivate inboundHandler: ChannelInboundHandler = () => {};\n\n\tregisterChannel(provider: ChannelProvider): void {\n\t\t// Stop a same-named provider being replaced so its listeners/sockets don't leak (Bug #17).\n\t\tconst existing = this.channels.get(provider.name);\n\t\tif (existing && existing !== provider) void Promise.resolve(existing.stop()).catch(() => {});\n\t\tthis.channels.set(provider.name, provider);\n\t\tif (this.started) void Promise.resolve(provider.start(this.inboundHandler)).catch(() => {});\n\t}\n\n\tregisterScheduler(provider: JobSchedulerProvider): void {\n\t\tconst existing = this.schedulers.get(provider.name);\n\t\tif (existing && existing !== provider) void Promise.resolve(existing.stop()).catch(() => {});\n\t\tthis.schedulers.set(provider.name, provider);\n\t\tif (this.started) void Promise.resolve(provider.start()).catch(() => {});\n\t}\n\n\tgetChannel(name: string): ChannelProvider | undefined {\n\t\treturn this.channels.get(name);\n\t}\n\n\tget channelCount(): number {\n\t\treturn this.channels.size;\n\t}\n\n\tget schedulerCount(): number {\n\t\treturn this.schedulers.size;\n\t}\n\n\t/** Start every registered provider; inbound channel messages are routed to `onInbound`. */\n\tasync start(onInbound: ChannelInboundHandler): Promise<void> {\n\t\tif (this.started) return;\n\t\tthis.started = true;\n\t\tthis.inboundHandler = onInbound;\n\t\tfor (const channel of this.channels.values()) {\n\t\t\ttry {\n\t\t\t\tawait channel.start(onInbound);\n\t\t\t} catch {\n\t\t\t\t// a failing channel must not block the others\n\t\t\t}\n\t\t}\n\t\tfor (const scheduler of this.schedulers.values()) {\n\t\t\ttry {\n\t\t\t\tawait scheduler.start();\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\t/** Stop every registered provider. Best-effort; always leaves the registry stopped. */\n\tasync stop(): Promise<void> {\n\t\tif (!this.started) return;\n\t\tthis.started = false;\n\t\tfor (const channel of this.channels.values()) {\n\t\t\ttry {\n\t\t\t\tawait channel.stop();\n\t\t\t} catch {}\n\t\t}\n\t\tfor (const scheduler of this.schedulers.values()) {\n\t\t\ttry {\n\t\t\t\tawait scheduler.stop();\n\t\t\t} catch {}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"channel-provider.d.ts","sourceRoot":"","sources":["../../../src/core/gateways/channel-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC9B,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,mEAAmE;AACnE,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtF;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,KAAK,CAAC,SAAS,EAAE,qBAAqB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,gDAAgD;IAChD,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,4CAA4C;IAC5C,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,GAAG,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,KAAK,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2C;IACtE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4B;IAE7D,OAAO,CAAC,cAAc;YAWR,cAAc;IAM5B,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAM/C;IAED,iBAAiB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAKtD;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEpD;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,2FAA2F;IACrF,KAAK,CAAC,SAAS,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3D;IAED,uFAAuF;IACjF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAmB1B;CACD","sourcesContent":["/**\n * Interface-driven gateways & scheduling (adaptive-agent design R8).\n *\n * pi does NOT bake transports (Slack/Discord/email/webhooks) or a cron tick loop into the core — that\n * would bloat the agent and couple it to deployment concerns. Instead it exposes two provider contracts\n * that a deployment wrapper (server, headless runner) implements and registers; the core only manages\n * their lifecycle. This keeps the CLI/agent transport- and schedule-agnostic while still offering a\n * first-class, uniform extension point.\n */\n\n/** A message arriving from / sent to an external channel (gateway). */\nexport interface ChannelMessage {\n\t/** Stable conversation/session key for this channel (e.g. `slack_C123`). */\n\tconversationKey: string;\n\t/** Message text. */\n\ttext: string;\n\t/** Optional opaque metadata the provider round-trips. */\n\tmeta?: Record<string, unknown>;\n}\n\n/** Handler the core supplies to a channel for inbound messages. */\nexport type ChannelInboundHandler = (message: ChannelMessage) => void | Promise<void>;\n\n/**\n * A transport channel (Telegram/Slack/email/webhook/...). Implemented by a deployment wrapper and\n * registered via {@link GatewayRegistry}. The core starts it (handing it an inbound handler) and stops\n * it on shutdown; it never imports any transport SDK itself.\n */\nexport interface ChannelProvider {\n\treadonly name: string;\n\t/** Begin listening; deliver inbound messages to `onInbound`. */\n\tstart(onInbound: ChannelInboundHandler): void | Promise<void>;\n\t/** Send an outbound message on this channel. */\n\tsend(message: ChannelMessage): void | Promise<void>;\n\t/** Stop listening and release resources. */\n\tstop(): void | Promise<void>;\n}\n\n/** A scheduled job definition. */\nexport interface ScheduledJob {\n\tid: string;\n\t/** Cron expression or interval spec the provider understands. */\n\tschedule: string;\n\t/** Invoked when the job fires. */\n\trun: () => void | Promise<void>;\n}\n\n/**\n * A scheduler (cron-like). Implemented by a deployment wrapper and registered via\n * {@link GatewayRegistry}. The core registers jobs + starts/stops it; it owns no tick loop itself.\n */\nexport interface JobSchedulerProvider {\n\treadonly name: string;\n\tschedule(job: ScheduledJob): void;\n\tstart(): void | Promise<void>;\n\tstop(): void | Promise<void>;\n}\n\n/**\n * Holds registered channel + scheduler providers and drives their lifecycle. A session starts all\n * registered providers when it binds and stops them on dispose. Registration is additive and idempotent\n * by provider name (last registration wins).\n */\nexport class GatewayRegistry {\n\tprivate readonly channels = new Map<string, ChannelProvider>();\n\tprivate readonly schedulers = new Map<string, JobSchedulerProvider>();\n\tprivate started = false;\n\tprivate inboundHandler: ChannelInboundHandler = () => {};\n\tprivate readonly pendingLifecycle = new Set<Promise<void>>();\n\n\tprivate trackLifecycle(operation: () => void | Promise<void>): void {\n\t\tlet pending: Promise<void>;\n\t\ttry {\n\t\t\tpending = Promise.resolve(operation()).catch(() => {});\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tthis.pendingLifecycle.add(pending);\n\t\tvoid pending.finally(() => this.pendingLifecycle.delete(pending));\n\t}\n\n\tprivate async drainLifecycle(): Promise<void> {\n\t\twhile (this.pendingLifecycle.size > 0) {\n\t\t\tawait Promise.allSettled([...this.pendingLifecycle]);\n\t\t}\n\t}\n\n\tregisterChannel(provider: ChannelProvider): void {\n\t\t// Stop a same-named provider being replaced so its listeners/sockets don't leak (Bug #17).\n\t\tconst existing = this.channels.get(provider.name);\n\t\tif (existing && existing !== provider) this.trackLifecycle(() => existing.stop());\n\t\tthis.channels.set(provider.name, provider);\n\t\tif (this.started) this.trackLifecycle(() => provider.start(this.inboundHandler));\n\t}\n\n\tregisterScheduler(provider: JobSchedulerProvider): void {\n\t\tconst existing = this.schedulers.get(provider.name);\n\t\tif (existing && existing !== provider) this.trackLifecycle(() => existing.stop());\n\t\tthis.schedulers.set(provider.name, provider);\n\t\tif (this.started) this.trackLifecycle(() => provider.start());\n\t}\n\n\tgetChannel(name: string): ChannelProvider | undefined {\n\t\treturn this.channels.get(name);\n\t}\n\n\tget channelCount(): number {\n\t\treturn this.channels.size;\n\t}\n\n\tget schedulerCount(): number {\n\t\treturn this.schedulers.size;\n\t}\n\n\t/** Start every registered provider; inbound channel messages are routed to `onInbound`. */\n\tasync start(onInbound: ChannelInboundHandler): Promise<void> {\n\t\tif (this.started) {\n\t\t\tawait this.drainLifecycle();\n\t\t\treturn;\n\t\t}\n\t\tthis.started = true;\n\t\tthis.inboundHandler = onInbound;\n\t\tfor (const channel of this.channels.values()) {\n\t\t\ttry {\n\t\t\t\tawait channel.start(onInbound);\n\t\t\t} catch {\n\t\t\t\t// a failing channel must not block the others\n\t\t\t}\n\t\t}\n\t\tfor (const scheduler of this.schedulers.values()) {\n\t\t\ttry {\n\t\t\t\tawait scheduler.start();\n\t\t\t} catch {}\n\t\t}\n\t\tawait this.drainLifecycle();\n\t}\n\n\t/** Stop every registered provider. Best-effort; always leaves the registry stopped. */\n\tasync stop(): Promise<void> {\n\t\tif (!this.started) {\n\t\t\tawait this.drainLifecycle();\n\t\t\treturn;\n\t\t}\n\t\tthis.started = false;\n\t\t// Late-registration starts and replacement stops must settle before the final stop pass;\n\t\t// otherwise an async start can complete after shutdown and leak a listener/process.\n\t\tawait this.drainLifecycle();\n\t\tfor (const channel of this.channels.values()) {\n\t\t\ttry {\n\t\t\t\tawait channel.stop();\n\t\t\t} catch {}\n\t\t}\n\t\tfor (const scheduler of this.schedulers.values()) {\n\t\t\ttry {\n\t\t\t\tawait scheduler.stop();\n\t\t\t} catch {}\n\t\t}\n\t}\n}\n"]}
@@ -17,22 +17,39 @@ export class GatewayRegistry {
17
17
  schedulers = new Map();
18
18
  started = false;
19
19
  inboundHandler = () => { };
20
+ pendingLifecycle = new Set();
21
+ trackLifecycle(operation) {
22
+ let pending;
23
+ try {
24
+ pending = Promise.resolve(operation()).catch(() => { });
25
+ }
26
+ catch {
27
+ return;
28
+ }
29
+ this.pendingLifecycle.add(pending);
30
+ void pending.finally(() => this.pendingLifecycle.delete(pending));
31
+ }
32
+ async drainLifecycle() {
33
+ while (this.pendingLifecycle.size > 0) {
34
+ await Promise.allSettled([...this.pendingLifecycle]);
35
+ }
36
+ }
20
37
  registerChannel(provider) {
21
38
  // Stop a same-named provider being replaced so its listeners/sockets don't leak (Bug #17).
22
39
  const existing = this.channels.get(provider.name);
23
40
  if (existing && existing !== provider)
24
- void Promise.resolve(existing.stop()).catch(() => { });
41
+ this.trackLifecycle(() => existing.stop());
25
42
  this.channels.set(provider.name, provider);
26
43
  if (this.started)
27
- void Promise.resolve(provider.start(this.inboundHandler)).catch(() => { });
44
+ this.trackLifecycle(() => provider.start(this.inboundHandler));
28
45
  }
29
46
  registerScheduler(provider) {
30
47
  const existing = this.schedulers.get(provider.name);
31
48
  if (existing && existing !== provider)
32
- void Promise.resolve(existing.stop()).catch(() => { });
49
+ this.trackLifecycle(() => existing.stop());
33
50
  this.schedulers.set(provider.name, provider);
34
51
  if (this.started)
35
- void Promise.resolve(provider.start()).catch(() => { });
52
+ this.trackLifecycle(() => provider.start());
36
53
  }
37
54
  getChannel(name) {
38
55
  return this.channels.get(name);
@@ -45,8 +62,10 @@ export class GatewayRegistry {
45
62
  }
46
63
  /** Start every registered provider; inbound channel messages are routed to `onInbound`. */
47
64
  async start(onInbound) {
48
- if (this.started)
65
+ if (this.started) {
66
+ await this.drainLifecycle();
49
67
  return;
68
+ }
50
69
  this.started = true;
51
70
  this.inboundHandler = onInbound;
52
71
  for (const channel of this.channels.values()) {
@@ -63,12 +82,18 @@ export class GatewayRegistry {
63
82
  }
64
83
  catch { }
65
84
  }
85
+ await this.drainLifecycle();
66
86
  }
67
87
  /** Stop every registered provider. Best-effort; always leaves the registry stopped. */
68
88
  async stop() {
69
- if (!this.started)
89
+ if (!this.started) {
90
+ await this.drainLifecycle();
70
91
  return;
92
+ }
71
93
  this.started = false;
94
+ // Late-registration starts and replacement stops must settle before the final stop pass;
95
+ // otherwise an async start can complete after shutdown and leak a listener/process.
96
+ await this.drainLifecycle();
72
97
  for (const channel of this.channels.values()) {
73
98
  try {
74
99
  await channel.stop();
@@ -1 +1 @@
1
- {"version":3,"file":"channel-provider.js","sourceRoot":"","sources":["../../../src/core/gateways/channel-provider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Interface-driven gateways & scheduling (adaptive-agent design R8).\n *\n * pi does NOT bake transports (Slack/Discord/email/webhooks) or a cron tick loop into the core — that\n * would bloat the agent and couple it to deployment concerns. Instead it exposes two provider contracts\n * that a deployment wrapper (server, headless runner) implements and registers; the core only manages\n * their lifecycle. This keeps the CLI/agent transport- and schedule-agnostic while still offering a\n * first-class, uniform extension point.\n */\n\n/** A message arriving from / sent to an external channel (gateway). */\nexport interface ChannelMessage {\n\t/** Stable conversation/session key for this channel (e.g. `slack_C123`). */\n\tconversationKey: string;\n\t/** Message text. */\n\ttext: string;\n\t/** Optional opaque metadata the provider round-trips. */\n\tmeta?: Record<string, unknown>;\n}\n\n/** Handler the core supplies to a channel for inbound messages. */\nexport type ChannelInboundHandler = (message: ChannelMessage) => void | Promise<void>;\n\n/**\n * A transport channel (Telegram/Slack/email/webhook/...). Implemented by a deployment wrapper and\n * registered via {@link GatewayRegistry}. The core starts it (handing it an inbound handler) and stops\n * it on shutdown; it never imports any transport SDK itself.\n */\nexport interface ChannelProvider {\n\treadonly name: string;\n\t/** Begin listening; deliver inbound messages to `onInbound`. */\n\tstart(onInbound: ChannelInboundHandler): void | Promise<void>;\n\t/** Send an outbound message on this channel. */\n\tsend(message: ChannelMessage): void | Promise<void>;\n\t/** Stop listening and release resources. */\n\tstop(): void | Promise<void>;\n}\n\n/** A scheduled job definition. */\nexport interface ScheduledJob {\n\tid: string;\n\t/** Cron expression or interval spec the provider understands. */\n\tschedule: string;\n\t/** Invoked when the job fires. */\n\trun: () => void | Promise<void>;\n}\n\n/**\n * A scheduler (cron-like). Implemented by a deployment wrapper and registered via\n * {@link GatewayRegistry}. The core registers jobs + starts/stops it; it owns no tick loop itself.\n */\nexport interface JobSchedulerProvider {\n\treadonly name: string;\n\tschedule(job: ScheduledJob): void;\n\tstart(): void | Promise<void>;\n\tstop(): void | Promise<void>;\n}\n\n/**\n * Holds registered channel + scheduler providers and drives their lifecycle. A session starts all\n * registered providers when it binds and stops them on dispose. Registration is additive and idempotent\n * by provider name (last registration wins).\n */\nexport class GatewayRegistry {\n\tprivate readonly channels = new Map<string, ChannelProvider>();\n\tprivate readonly schedulers = new Map<string, JobSchedulerProvider>();\n\tprivate started = false;\n\tprivate inboundHandler: ChannelInboundHandler = () => {};\n\n\tregisterChannel(provider: ChannelProvider): void {\n\t\t// Stop a same-named provider being replaced so its listeners/sockets don't leak (Bug #17).\n\t\tconst existing = this.channels.get(provider.name);\n\t\tif (existing && existing !== provider) void Promise.resolve(existing.stop()).catch(() => {});\n\t\tthis.channels.set(provider.name, provider);\n\t\tif (this.started) void Promise.resolve(provider.start(this.inboundHandler)).catch(() => {});\n\t}\n\n\tregisterScheduler(provider: JobSchedulerProvider): void {\n\t\tconst existing = this.schedulers.get(provider.name);\n\t\tif (existing && existing !== provider) void Promise.resolve(existing.stop()).catch(() => {});\n\t\tthis.schedulers.set(provider.name, provider);\n\t\tif (this.started) void Promise.resolve(provider.start()).catch(() => {});\n\t}\n\n\tgetChannel(name: string): ChannelProvider | undefined {\n\t\treturn this.channels.get(name);\n\t}\n\n\tget channelCount(): number {\n\t\treturn this.channels.size;\n\t}\n\n\tget schedulerCount(): number {\n\t\treturn this.schedulers.size;\n\t}\n\n\t/** Start every registered provider; inbound channel messages are routed to `onInbound`. */\n\tasync start(onInbound: ChannelInboundHandler): Promise<void> {\n\t\tif (this.started) return;\n\t\tthis.started = true;\n\t\tthis.inboundHandler = onInbound;\n\t\tfor (const channel of this.channels.values()) {\n\t\t\ttry {\n\t\t\t\tawait channel.start(onInbound);\n\t\t\t} catch {\n\t\t\t\t// a failing channel must not block the others\n\t\t\t}\n\t\t}\n\t\tfor (const scheduler of this.schedulers.values()) {\n\t\t\ttry {\n\t\t\t\tawait scheduler.start();\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\t/** Stop every registered provider. Best-effort; always leaves the registry stopped. */\n\tasync stop(): Promise<void> {\n\t\tif (!this.started) return;\n\t\tthis.started = false;\n\t\tfor (const channel of this.channels.values()) {\n\t\t\ttry {\n\t\t\t\tawait channel.stop();\n\t\t\t} catch {}\n\t\t}\n\t\tfor (const scheduler of this.schedulers.values()) {\n\t\t\ttry {\n\t\t\t\tawait scheduler.stop();\n\t\t\t} catch {}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"channel-provider.js","sourceRoot":"","sources":["../../../src/core/gateways/channel-provider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Interface-driven gateways & scheduling (adaptive-agent design R8).\n *\n * pi does NOT bake transports (Slack/Discord/email/webhooks) or a cron tick loop into the core — that\n * would bloat the agent and couple it to deployment concerns. Instead it exposes two provider contracts\n * that a deployment wrapper (server, headless runner) implements and registers; the core only manages\n * their lifecycle. This keeps the CLI/agent transport- and schedule-agnostic while still offering a\n * first-class, uniform extension point.\n */\n\n/** A message arriving from / sent to an external channel (gateway). */\nexport interface ChannelMessage {\n\t/** Stable conversation/session key for this channel (e.g. `slack_C123`). */\n\tconversationKey: string;\n\t/** Message text. */\n\ttext: string;\n\t/** Optional opaque metadata the provider round-trips. */\n\tmeta?: Record<string, unknown>;\n}\n\n/** Handler the core supplies to a channel for inbound messages. */\nexport type ChannelInboundHandler = (message: ChannelMessage) => void | Promise<void>;\n\n/**\n * A transport channel (Telegram/Slack/email/webhook/...). Implemented by a deployment wrapper and\n * registered via {@link GatewayRegistry}. The core starts it (handing it an inbound handler) and stops\n * it on shutdown; it never imports any transport SDK itself.\n */\nexport interface ChannelProvider {\n\treadonly name: string;\n\t/** Begin listening; deliver inbound messages to `onInbound`. */\n\tstart(onInbound: ChannelInboundHandler): void | Promise<void>;\n\t/** Send an outbound message on this channel. */\n\tsend(message: ChannelMessage): void | Promise<void>;\n\t/** Stop listening and release resources. */\n\tstop(): void | Promise<void>;\n}\n\n/** A scheduled job definition. */\nexport interface ScheduledJob {\n\tid: string;\n\t/** Cron expression or interval spec the provider understands. */\n\tschedule: string;\n\t/** Invoked when the job fires. */\n\trun: () => void | Promise<void>;\n}\n\n/**\n * A scheduler (cron-like). Implemented by a deployment wrapper and registered via\n * {@link GatewayRegistry}. The core registers jobs + starts/stops it; it owns no tick loop itself.\n */\nexport interface JobSchedulerProvider {\n\treadonly name: string;\n\tschedule(job: ScheduledJob): void;\n\tstart(): void | Promise<void>;\n\tstop(): void | Promise<void>;\n}\n\n/**\n * Holds registered channel + scheduler providers and drives their lifecycle. A session starts all\n * registered providers when it binds and stops them on dispose. Registration is additive and idempotent\n * by provider name (last registration wins).\n */\nexport class GatewayRegistry {\n\tprivate readonly channels = new Map<string, ChannelProvider>();\n\tprivate readonly schedulers = new Map<string, JobSchedulerProvider>();\n\tprivate started = false;\n\tprivate inboundHandler: ChannelInboundHandler = () => {};\n\tprivate readonly pendingLifecycle = new Set<Promise<void>>();\n\n\tprivate trackLifecycle(operation: () => void | Promise<void>): void {\n\t\tlet pending: Promise<void>;\n\t\ttry {\n\t\t\tpending = Promise.resolve(operation()).catch(() => {});\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tthis.pendingLifecycle.add(pending);\n\t\tvoid pending.finally(() => this.pendingLifecycle.delete(pending));\n\t}\n\n\tprivate async drainLifecycle(): Promise<void> {\n\t\twhile (this.pendingLifecycle.size > 0) {\n\t\t\tawait Promise.allSettled([...this.pendingLifecycle]);\n\t\t}\n\t}\n\n\tregisterChannel(provider: ChannelProvider): void {\n\t\t// Stop a same-named provider being replaced so its listeners/sockets don't leak (Bug #17).\n\t\tconst existing = this.channels.get(provider.name);\n\t\tif (existing && existing !== provider) this.trackLifecycle(() => existing.stop());\n\t\tthis.channels.set(provider.name, provider);\n\t\tif (this.started) this.trackLifecycle(() => provider.start(this.inboundHandler));\n\t}\n\n\tregisterScheduler(provider: JobSchedulerProvider): void {\n\t\tconst existing = this.schedulers.get(provider.name);\n\t\tif (existing && existing !== provider) this.trackLifecycle(() => existing.stop());\n\t\tthis.schedulers.set(provider.name, provider);\n\t\tif (this.started) this.trackLifecycle(() => provider.start());\n\t}\n\n\tgetChannel(name: string): ChannelProvider | undefined {\n\t\treturn this.channels.get(name);\n\t}\n\n\tget channelCount(): number {\n\t\treturn this.channels.size;\n\t}\n\n\tget schedulerCount(): number {\n\t\treturn this.schedulers.size;\n\t}\n\n\t/** Start every registered provider; inbound channel messages are routed to `onInbound`. */\n\tasync start(onInbound: ChannelInboundHandler): Promise<void> {\n\t\tif (this.started) {\n\t\t\tawait this.drainLifecycle();\n\t\t\treturn;\n\t\t}\n\t\tthis.started = true;\n\t\tthis.inboundHandler = onInbound;\n\t\tfor (const channel of this.channels.values()) {\n\t\t\ttry {\n\t\t\t\tawait channel.start(onInbound);\n\t\t\t} catch {\n\t\t\t\t// a failing channel must not block the others\n\t\t\t}\n\t\t}\n\t\tfor (const scheduler of this.schedulers.values()) {\n\t\t\ttry {\n\t\t\t\tawait scheduler.start();\n\t\t\t} catch {}\n\t\t}\n\t\tawait this.drainLifecycle();\n\t}\n\n\t/** Stop every registered provider. Best-effort; always leaves the registry stopped. */\n\tasync stop(): Promise<void> {\n\t\tif (!this.started) {\n\t\t\tawait this.drainLifecycle();\n\t\t\treturn;\n\t\t}\n\t\tthis.started = false;\n\t\t// Late-registration starts and replacement stops must settle before the final stop pass;\n\t\t// otherwise an async start can complete after shutdown and leak a listener/process.\n\t\tawait this.drainLifecycle();\n\t\tfor (const channel of this.channels.values()) {\n\t\t\ttry {\n\t\t\t\tawait channel.stop();\n\t\t\t} catch {}\n\t\t}\n\t\tfor (const scheduler of this.schedulers.values()) {\n\t\t\ttry {\n\t\t\t\tawait scheduler.stop();\n\t\t\t} catch {}\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { GoalEvidenceRef, GoalState, Requirement } from "./goal-state.ts";
2
+ export declare function isTrustedGoalEvidence(evidence: GoalEvidenceRef): boolean;
3
+ export declare function getTrustedRequirementEvidence(state: GoalState, requirement: Requirement): GoalEvidenceRef[];
4
+ export declare function getUnprovenGoalRequirementIds(state: GoalState): string[];
5
+ /**
6
+ * Human/manual completion and the explicit evidence-gate opt-out are authoritative overrides.
7
+ * Completion events written before the flag existed are treated as legacy owner decisions so an
8
+ * already-completed persisted goal cannot strand durable work during resume.
9
+ */
10
+ export declare function hasGoalAcceptanceOverride(state: GoalState): boolean;
11
+ //# sourceMappingURL=goal-acceptance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-acceptance.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-acceptance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAExE;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,GAAG,eAAe,EAAE,CAG3G;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,EAAE,CAOxE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAQnE","sourcesContent":["import type { GoalEvidenceRef, GoalState, Requirement } from \"./goal-state.ts\";\n\nexport function isTrustedGoalEvidence(evidence: GoalEvidenceRef): boolean {\n\treturn evidence.kind === \"user\" || evidence.verified === true;\n}\n\nexport function getTrustedRequirementEvidence(state: GoalState, requirement: Requirement): GoalEvidenceRef[] {\n\tconst evidenceIds = new Set(requirement.evidenceIds);\n\treturn state.evidence.filter((evidence) => evidenceIds.has(evidence.id) && isTrustedGoalEvidence(evidence));\n}\n\nexport function getUnprovenGoalRequirementIds(state: GoalState): string[] {\n\treturn state.requirements\n\t\t.filter(\n\t\t\t(requirement) =>\n\t\t\t\trequirement.status !== \"satisfied\" || getTrustedRequirementEvidence(state, requirement).length === 0,\n\t\t)\n\t\t.map((requirement) => requirement.id);\n}\n\n/**\n * Human/manual completion and the explicit evidence-gate opt-out are authoritative overrides.\n * Completion events written before the flag existed are treated as legacy owner decisions so an\n * already-completed persisted goal cannot strand durable work during resume.\n */\nexport function hasGoalAcceptanceOverride(state: GoalState): boolean {\n\tconst completion = [...state.events]\n\t\t.reverse()\n\t\t.find((event) => event.type === \"complete_goal\" || event.type === \"complete_goal_manually\");\n\treturn (\n\t\tcompletion?.type === \"complete_goal_manually\" ||\n\t\t(completion?.type === \"complete_goal\" && completion.acceptanceOverride !== false)\n\t);\n}\n"]}
@@ -0,0 +1,25 @@
1
+ export function isTrustedGoalEvidence(evidence) {
2
+ return evidence.kind === "user" || evidence.verified === true;
3
+ }
4
+ export function getTrustedRequirementEvidence(state, requirement) {
5
+ const evidenceIds = new Set(requirement.evidenceIds);
6
+ return state.evidence.filter((evidence) => evidenceIds.has(evidence.id) && isTrustedGoalEvidence(evidence));
7
+ }
8
+ export function getUnprovenGoalRequirementIds(state) {
9
+ return state.requirements
10
+ .filter((requirement) => requirement.status !== "satisfied" || getTrustedRequirementEvidence(state, requirement).length === 0)
11
+ .map((requirement) => requirement.id);
12
+ }
13
+ /**
14
+ * Human/manual completion and the explicit evidence-gate opt-out are authoritative overrides.
15
+ * Completion events written before the flag existed are treated as legacy owner decisions so an
16
+ * already-completed persisted goal cannot strand durable work during resume.
17
+ */
18
+ export function hasGoalAcceptanceOverride(state) {
19
+ const completion = [...state.events]
20
+ .reverse()
21
+ .find((event) => event.type === "complete_goal" || event.type === "complete_goal_manually");
22
+ return (completion?.type === "complete_goal_manually" ||
23
+ (completion?.type === "complete_goal" && completion.acceptanceOverride !== false));
24
+ }
25
+ //# sourceMappingURL=goal-acceptance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-acceptance.js","sourceRoot":"","sources":["../../../src/core/goals/goal-acceptance.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,QAAyB,EAAW;IACzE,OAAO,QAAQ,CAAC,IAAI,KAAK,MAAM,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC;AAAA,CAC9D;AAED,MAAM,UAAU,6BAA6B,CAAC,KAAgB,EAAE,WAAwB,EAAqB;IAC5G,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAAA,CAC5G;AAED,MAAM,UAAU,6BAA6B,CAAC,KAAgB,EAAY;IACzE,OAAO,KAAK,CAAC,YAAY;SACvB,MAAM,CACN,CAAC,WAAW,EAAE,EAAE,CACf,WAAW,CAAC,MAAM,KAAK,WAAW,IAAI,6BAA6B,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,MAAM,KAAK,CAAC,CACrG;SACA,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;AAAA,CACvC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAgB,EAAW;IACpE,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;SAClC,OAAO,EAAE;SACT,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,CAAC,CAAC;IAC7F,OAAO,CACN,UAAU,EAAE,IAAI,KAAK,wBAAwB;QAC7C,CAAC,UAAU,EAAE,IAAI,KAAK,eAAe,IAAI,UAAU,CAAC,kBAAkB,KAAK,KAAK,CAAC,CACjF,CAAC;AAAA,CACF","sourcesContent":["import type { GoalEvidenceRef, GoalState, Requirement } from \"./goal-state.ts\";\n\nexport function isTrustedGoalEvidence(evidence: GoalEvidenceRef): boolean {\n\treturn evidence.kind === \"user\" || evidence.verified === true;\n}\n\nexport function getTrustedRequirementEvidence(state: GoalState, requirement: Requirement): GoalEvidenceRef[] {\n\tconst evidenceIds = new Set(requirement.evidenceIds);\n\treturn state.evidence.filter((evidence) => evidenceIds.has(evidence.id) && isTrustedGoalEvidence(evidence));\n}\n\nexport function getUnprovenGoalRequirementIds(state: GoalState): string[] {\n\treturn state.requirements\n\t\t.filter(\n\t\t\t(requirement) =>\n\t\t\t\trequirement.status !== \"satisfied\" || getTrustedRequirementEvidence(state, requirement).length === 0,\n\t\t)\n\t\t.map((requirement) => requirement.id);\n}\n\n/**\n * Human/manual completion and the explicit evidence-gate opt-out are authoritative overrides.\n * Completion events written before the flag existed are treated as legacy owner decisions so an\n * already-completed persisted goal cannot strand durable work during resume.\n */\nexport function hasGoalAcceptanceOverride(state: GoalState): boolean {\n\tconst completion = [...state.events]\n\t\t.reverse()\n\t\t.find((event) => event.type === \"complete_goal\" || event.type === \"complete_goal_manually\");\n\treturn (\n\t\tcompletion?.type === \"complete_goal_manually\" ||\n\t\t(completion?.type === \"complete_goal\" && completion.acceptanceOverride !== false)\n\t);\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { AgentSessionEvent, GoalContinuationLoopOptions, GoalContinuationLoopResult, PromptOptions } from "../agent-session.ts";
2
+ import { type ModelCapabilityProfile } from "../model-capability.ts";
3
+ import type { SettingsManager } from "../settings-manager.ts";
4
+ import type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from "./goal-runtime-snapshot.ts";
5
+ export interface GoalAutoContinueControllerDeps {
6
+ isDisposed(): boolean;
7
+ isGoalToolActive(): boolean;
8
+ getSettingsManager(): SettingsManager;
9
+ getModelCapabilityProfile(): ModelCapabilityProfile;
10
+ getGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;
11
+ hasInFlightLaneForGoal(goalId: string): boolean;
12
+ continueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;
13
+ emit(event: AgentSessionEvent): void;
14
+ }
15
+ /** Owns the single-flight goal continuation loop and its foreground-idle timer. */
16
+ export declare class GoalAutoContinueController {
17
+ private _timer;
18
+ private _isContinuing;
19
+ private readonly deps;
20
+ constructor(deps: GoalAutoContinueControllerDeps);
21
+ clearTimer(): void;
22
+ scheduleFromIdle(options?: PromptOptions): void;
23
+ continueExclusive(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;
24
+ private runScheduled;
25
+ private skippedResult;
26
+ }
27
+ //# sourceMappingURL=goal-auto-continue-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-auto-continue-controller.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-auto-continue-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,2BAA2B,EAC3B,0BAA0B,EAC1B,aAAa,EACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,sBAAsB,EAAyC,MAAM,wBAAwB,CAAC;AAC5G,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEnG,MAAM,WAAW,8BAA8B;IAC9C,UAAU,IAAI,OAAO,CAAC;IACtB,gBAAgB,IAAI,OAAO,CAAC;IAC5B,kBAAkB,IAAI,eAAe,CAAC;IACtC,yBAAyB,IAAI,sBAAsB,CAAC;IACpD,sBAAsB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,mBAAmB,CAAC;IACnF,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC5F,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACrC;AAED,mFAAmF;AACnF,qBAAa,0BAA0B;IACtC,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IAEtD,YAAY,IAAI,EAAE,8BAA8B,EAE/C;IAED,UAAU,IAAI,IAAI,CAKjB;IAED,gBAAgB,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAuB9C;IAEK,iBAAiB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAUjG;YAEa,YAAY;IAwB1B,OAAO,CAAC,aAAa;CAUrB","sourcesContent":["import type {\n\tAgentSessionEvent,\n\tGoalContinuationLoopOptions,\n\tGoalContinuationLoopResult,\n\tPromptOptions,\n} from \"../agent-session.ts\";\nimport { type ModelCapabilityProfile, scaleContinuationBudgetsForCapability } from \"../model-capability.ts\";\nimport type { SettingsManager } from \"../settings-manager.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goal-runtime-snapshot.ts\";\n\nexport interface GoalAutoContinueControllerDeps {\n\tisDisposed(): boolean;\n\tisGoalToolActive(): boolean;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\tgetGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;\n\thasInFlightLaneForGoal(goalId: string): boolean;\n\tcontinueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;\n\temit(event: AgentSessionEvent): void;\n}\n\n/** Owns the single-flight goal continuation loop and its foreground-idle timer. */\nexport class GoalAutoContinueController {\n\tprivate _timer: ReturnType<typeof setTimeout> | undefined;\n\tprivate _isContinuing = false;\n\tprivate readonly deps: GoalAutoContinueControllerDeps;\n\n\tconstructor(deps: GoalAutoContinueControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tclearTimer(): void {\n\t\tif (this._timer !== undefined) {\n\t\t\tclearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\tscheduleFromIdle(options?: PromptOptions): void {\n\t\tif (options?.autoContinueGoal === false || this._isContinuing || this.deps.isDisposed()) return;\n\t\tif (!this.deps.getModelCapabilityProfile().backgroundLanesEnabled) return;\n\n\t\tconst { maxStallTurns, goalAutoContinue, goalAutoContinueDelayMs } = this.deps\n\t\t\t.getSettingsManager()\n\t\t\t.getAutonomySettings();\n\t\tif (!goalAutoContinue) return;\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });\n\t\tif (snapshot.continuation.action !== \"continue\") return;\n\t\tconst activeGoalId = snapshot.goalState?.goalId;\n\t\tif (activeGoalId !== undefined && this.deps.hasInFlightLaneForGoal(activeGoalId)) return;\n\n\t\tthis.clearTimer();\n\t\tthis._timer = setTimeout(() => {\n\t\t\tthis._timer = undefined;\n\t\t\tvoid this.runScheduled();\n\t\t}, goalAutoContinueDelayMs);\n\t\tconst timer = this._timer;\n\t\tif (typeof timer === \"object\" && timer && \"unref\" in timer) {\n\t\t\tconst { unref } = timer as { unref?: () => void };\n\t\t\tunref?.call(timer);\n\t\t}\n\t}\n\n\tasync continueExclusive(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult> {\n\t\tif (this._isContinuing) return this.skippedResult(options, \"already_continuing\");\n\t\tif (this.deps.isDisposed()) return this.skippedResult(options, \"session_disposed\");\n\t\tif (!this.deps.isGoalToolActive()) return this.skippedResult(options, \"goal_tool_unavailable\");\n\t\tthis._isContinuing = true;\n\t\ttry {\n\t\t\treturn await this.deps.continueGoalLoop(options);\n\t\t} finally {\n\t\t\tthis._isContinuing = false;\n\t\t}\n\t}\n\n\tprivate async runScheduled(): Promise<void> {\n\t\tif (this._isContinuing || this.deps.isDisposed()) return;\n\t\tconst { maxStallTurns, goalContinueTurns, goalContinueMaxWallClockMinutes, goalAutoContinue } = this.deps\n\t\t\t.getSettingsManager()\n\t\t\t.getAutonomySettings();\n\t\tif (!goalAutoContinue) return;\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });\n\t\tif (snapshot.continuation.action !== \"continue\") return;\n\t\tconst scaled = scaleContinuationBudgetsForCapability(this.deps.getModelCapabilityProfile(), {\n\t\t\tmaxTurns: goalContinueTurns,\n\t\t\tmaxWallClockMinutes: goalContinueMaxWallClockMinutes,\n\t\t});\n\t\ttry {\n\t\t\tawait this.continueExclusive({\n\t\t\t\tmaxTurns: scaled.maxTurns,\n\t\t\t\tmaxStallTurns,\n\t\t\t\tmaxWallClockMinutes: scaled.maxWallClockMinutes,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.deps.emit({ type: \"warning\", message: `Goal auto-continuation failed: ${message}` });\n\t\t}\n\t}\n\n\tprivate skippedResult(\n\t\toptions: GoalContinuationLoopOptions,\n\t\tstopReason: \"already_continuing\" | \"session_disposed\" | \"goal_tool_unavailable\",\n\t): GoalContinuationLoopResult {\n\t\treturn {\n\t\t\tturnsSubmitted: 0,\n\t\t\tstopReason,\n\t\t\tfinalSnapshot: this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns }),\n\t\t};\n\t}\n}\n"]}
@@ -0,0 +1,93 @@
1
+ import { scaleContinuationBudgetsForCapability } from "../model-capability.js";
2
+ /** Owns the single-flight goal continuation loop and its foreground-idle timer. */
3
+ export class GoalAutoContinueController {
4
+ _timer;
5
+ _isContinuing = false;
6
+ deps;
7
+ constructor(deps) {
8
+ this.deps = deps;
9
+ }
10
+ clearTimer() {
11
+ if (this._timer !== undefined) {
12
+ clearTimeout(this._timer);
13
+ this._timer = undefined;
14
+ }
15
+ }
16
+ scheduleFromIdle(options) {
17
+ if (options?.autoContinueGoal === false || this._isContinuing || this.deps.isDisposed())
18
+ return;
19
+ if (!this.deps.getModelCapabilityProfile().backgroundLanesEnabled)
20
+ return;
21
+ const { maxStallTurns, goalAutoContinue, goalAutoContinueDelayMs } = this.deps
22
+ .getSettingsManager()
23
+ .getAutonomySettings();
24
+ if (!goalAutoContinue)
25
+ return;
26
+ const snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });
27
+ if (snapshot.continuation.action !== "continue")
28
+ return;
29
+ const activeGoalId = snapshot.goalState?.goalId;
30
+ if (activeGoalId !== undefined && this.deps.hasInFlightLaneForGoal(activeGoalId))
31
+ return;
32
+ this.clearTimer();
33
+ this._timer = setTimeout(() => {
34
+ this._timer = undefined;
35
+ void this.runScheduled();
36
+ }, goalAutoContinueDelayMs);
37
+ const timer = this._timer;
38
+ if (typeof timer === "object" && timer && "unref" in timer) {
39
+ const { unref } = timer;
40
+ unref?.call(timer);
41
+ }
42
+ }
43
+ async continueExclusive(options) {
44
+ if (this._isContinuing)
45
+ return this.skippedResult(options, "already_continuing");
46
+ if (this.deps.isDisposed())
47
+ return this.skippedResult(options, "session_disposed");
48
+ if (!this.deps.isGoalToolActive())
49
+ return this.skippedResult(options, "goal_tool_unavailable");
50
+ this._isContinuing = true;
51
+ try {
52
+ return await this.deps.continueGoalLoop(options);
53
+ }
54
+ finally {
55
+ this._isContinuing = false;
56
+ }
57
+ }
58
+ async runScheduled() {
59
+ if (this._isContinuing || this.deps.isDisposed())
60
+ return;
61
+ const { maxStallTurns, goalContinueTurns, goalContinueMaxWallClockMinutes, goalAutoContinue } = this.deps
62
+ .getSettingsManager()
63
+ .getAutonomySettings();
64
+ if (!goalAutoContinue)
65
+ return;
66
+ const snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });
67
+ if (snapshot.continuation.action !== "continue")
68
+ return;
69
+ const scaled = scaleContinuationBudgetsForCapability(this.deps.getModelCapabilityProfile(), {
70
+ maxTurns: goalContinueTurns,
71
+ maxWallClockMinutes: goalContinueMaxWallClockMinutes,
72
+ });
73
+ try {
74
+ await this.continueExclusive({
75
+ maxTurns: scaled.maxTurns,
76
+ maxStallTurns,
77
+ maxWallClockMinutes: scaled.maxWallClockMinutes,
78
+ });
79
+ }
80
+ catch (error) {
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ this.deps.emit({ type: "warning", message: `Goal auto-continuation failed: ${message}` });
83
+ }
84
+ }
85
+ skippedResult(options, stopReason) {
86
+ return {
87
+ turnsSubmitted: 0,
88
+ stopReason,
89
+ finalSnapshot: this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns }),
90
+ };
91
+ }
92
+ }
93
+ //# sourceMappingURL=goal-auto-continue-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-auto-continue-controller.js","sourceRoot":"","sources":["../../../src/core/goals/goal-auto-continue-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tAgentSessionEvent,\n\tGoalContinuationLoopOptions,\n\tGoalContinuationLoopResult,\n\tPromptOptions,\n} from \"../agent-session.ts\";\nimport { type ModelCapabilityProfile, scaleContinuationBudgetsForCapability } from \"../model-capability.ts\";\nimport type { SettingsManager } from \"../settings-manager.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goal-runtime-snapshot.ts\";\n\nexport interface GoalAutoContinueControllerDeps {\n\tisDisposed(): boolean;\n\tisGoalToolActive(): boolean;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\tgetGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;\n\thasInFlightLaneForGoal(goalId: string): boolean;\n\tcontinueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;\n\temit(event: AgentSessionEvent): void;\n}\n\n/** Owns the single-flight goal continuation loop and its foreground-idle timer. */\nexport class GoalAutoContinueController {\n\tprivate _timer: ReturnType<typeof setTimeout> | undefined;\n\tprivate _isContinuing = false;\n\tprivate readonly deps: GoalAutoContinueControllerDeps;\n\n\tconstructor(deps: GoalAutoContinueControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tclearTimer(): void {\n\t\tif (this._timer !== undefined) {\n\t\t\tclearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\tscheduleFromIdle(options?: PromptOptions): void {\n\t\tif (options?.autoContinueGoal === false || this._isContinuing || this.deps.isDisposed()) return;\n\t\tif (!this.deps.getModelCapabilityProfile().backgroundLanesEnabled) return;\n\n\t\tconst { maxStallTurns, goalAutoContinue, goalAutoContinueDelayMs } = this.deps\n\t\t\t.getSettingsManager()\n\t\t\t.getAutonomySettings();\n\t\tif (!goalAutoContinue) return;\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });\n\t\tif (snapshot.continuation.action !== \"continue\") return;\n\t\tconst activeGoalId = snapshot.goalState?.goalId;\n\t\tif (activeGoalId !== undefined && this.deps.hasInFlightLaneForGoal(activeGoalId)) return;\n\n\t\tthis.clearTimer();\n\t\tthis._timer = setTimeout(() => {\n\t\t\tthis._timer = undefined;\n\t\t\tvoid this.runScheduled();\n\t\t}, goalAutoContinueDelayMs);\n\t\tconst timer = this._timer;\n\t\tif (typeof timer === \"object\" && timer && \"unref\" in timer) {\n\t\t\tconst { unref } = timer as { unref?: () => void };\n\t\t\tunref?.call(timer);\n\t\t}\n\t}\n\n\tasync continueExclusive(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult> {\n\t\tif (this._isContinuing) return this.skippedResult(options, \"already_continuing\");\n\t\tif (this.deps.isDisposed()) return this.skippedResult(options, \"session_disposed\");\n\t\tif (!this.deps.isGoalToolActive()) return this.skippedResult(options, \"goal_tool_unavailable\");\n\t\tthis._isContinuing = true;\n\t\ttry {\n\t\t\treturn await this.deps.continueGoalLoop(options);\n\t\t} finally {\n\t\t\tthis._isContinuing = false;\n\t\t}\n\t}\n\n\tprivate async runScheduled(): Promise<void> {\n\t\tif (this._isContinuing || this.deps.isDisposed()) return;\n\t\tconst { maxStallTurns, goalContinueTurns, goalContinueMaxWallClockMinutes, goalAutoContinue } = this.deps\n\t\t\t.getSettingsManager()\n\t\t\t.getAutonomySettings();\n\t\tif (!goalAutoContinue) return;\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });\n\t\tif (snapshot.continuation.action !== \"continue\") return;\n\t\tconst scaled = scaleContinuationBudgetsForCapability(this.deps.getModelCapabilityProfile(), {\n\t\t\tmaxTurns: goalContinueTurns,\n\t\t\tmaxWallClockMinutes: goalContinueMaxWallClockMinutes,\n\t\t});\n\t\ttry {\n\t\t\tawait this.continueExclusive({\n\t\t\t\tmaxTurns: scaled.maxTurns,\n\t\t\t\tmaxStallTurns,\n\t\t\t\tmaxWallClockMinutes: scaled.maxWallClockMinutes,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.deps.emit({ type: \"warning\", message: `Goal auto-continuation failed: ${message}` });\n\t\t}\n\t}\n\n\tprivate skippedResult(\n\t\toptions: GoalContinuationLoopOptions,\n\t\tstopReason: \"already_continuing\" | \"session_disposed\" | \"goal_tool_unavailable\",\n\t): GoalContinuationLoopResult {\n\t\treturn {\n\t\t\tturnsSubmitted: 0,\n\t\t\tstopReason,\n\t\t\tfinalSnapshot: this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns }),\n\t\t};\n\t}\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { GoalState } from "./goal-state.ts";
2
2
  export type GoalContinuationAction = "continue" | "ask-user" | "finalize" | "stop" | "waiting";
3
- export type GoalContinuationReasonCode = "goal_active" | "goal_completed" | "goal_blocked" | "goal_cancelled" | "stall_limit_reached" | "no_open_requirements" | "blocked_requirements_present" | "missing_goal_state" | "worker_in_flight" | "worker_wait_timeout" | "lane_sync_conflict" | "lane_sync_required";
3
+ export type GoalContinuationReasonCode = "goal_active" | "goal_completed" | "goal_blocked" | "goal_cancelled" | "stall_limit_reached" | "no_open_requirements" | "acceptance_evidence_required" | "blocked_requirements_present" | "missing_goal_state" | "worker_in_flight" | "worker_wait_timeout" | "lane_sync_conflict" | "lane_sync_required";
4
4
  export interface GoalContinuationDecision {
5
5
  action: GoalContinuationAction;
6
6
  reasonCode: GoalContinuationReasonCode;
@@ -1 +1 @@
1
- {"version":3,"file":"goal-continuation-controller.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-continuation-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;AAC/F,MAAM,MAAM,0BAA0B,GACnC,aAAa,GACb,gBAAgB,GAChB,cAAc,GACd,gBAAgB,GAChB,qBAAqB,GACrB,sBAAsB,GACtB,8BAA8B,GAC9B,oBAAoB,GACpB,kBAAkB,GAClB,qBAAqB,GACrB,oBAAoB,GACpB,oBAAoB,CAAC;AAExB,MAAM,WAAW,wBAAwB;IACxC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,UAAU,EAAE,0BAA0B,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,uBAAuB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,wBAAwB;IACxC,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,wBAAwB,CAAC;IACnC;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,wBAAwB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/C;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC3C,GAAG,wBAAwB,CAwL3B","sourcesContent":["import type { GoalState } from \"./goal-state.ts\";\n\nexport type GoalContinuationAction = \"continue\" | \"ask-user\" | \"finalize\" | \"stop\" | \"waiting\";\nexport type GoalContinuationReasonCode =\n\t| \"goal_active\"\n\t| \"goal_completed\"\n\t| \"goal_blocked\"\n\t| \"goal_cancelled\"\n\t| \"stall_limit_reached\"\n\t| \"no_open_requirements\"\n\t| \"blocked_requirements_present\"\n\t| \"missing_goal_state\"\n\t| \"worker_in_flight\"\n\t| \"worker_wait_timeout\"\n\t| \"lane_sync_conflict\"\n\t| \"lane_sync_required\";\n\nexport interface GoalContinuationDecision {\n\taction: GoalContinuationAction;\n\treasonCode: GoalContinuationReasonCode;\n\tmessage: string;\n\tgoalId?: string;\n\tstallTurns?: number;\n\tmaxStallTurns?: number;\n\topenRequirementIds: readonly string[];\n\tblockedRequirementIds: readonly string[];\n\tsatisfiedRequirementIds: readonly string[];\n}\n\nexport interface GoalContinuationSettings {\n\tmaxStallTurns: number;\n}\n\nexport function evaluateGoalContinuation(args: {\n\tstate?: GoalState;\n\tsettings: GoalContinuationSettings;\n\t/**\n\t * LaneIds currently queued/running AND tagged with THIS goal's id (see `LaneRecord.goalId`).\n\t * When an open requirement is bound (`Requirement.boundLaneId`) to one of these lanes, the\n\t * goal is WAITING on that worker rather than stalled or ready for another pass: the loop must not\n\t * submit a hollow continuation prompt (which would then misreport as `goal_state_not_advanced`),\n\t * and the idle scheduler must not race a re-dispatch against the same open requirement. Optional\n\t * so every pre-existing (in-flight-unaware) caller keeps compiling and behaving unchanged.\n\t */\n\tinFlightGoalLaneIds?: ReadonlySet<string>;\n\t/**\n\t * Current time as an ISO string, paired with `maxWorkerWaitMs` to detect a bound in-flight\n\t * requirement that has hung past its deadline (see below). Optional so every pre-existing\n\t * caller that omits it keeps behaving byte-identically -- the goal waits indefinitely, exactly\n\t * as before this field existed.\n\t */\n\tnow?: string;\n\t/**\n\t * Maximum milliseconds a bound in-flight requirement (`Requirement.boundAt`) may wait before\n\t * this escalates to `action:\"ask-user\"`/`reasonCode:\"worker_wait_timeout\"` instead of\n\t * `\"waiting\"` -- a worker that is alive-but-hung past its deadline must not wait forever. Only\n\t * takes effect when BOTH `now` and this are supplied; escalation fires only once EVERY\n\t * bound-in-flight open requirement has individually passed `boundAt + maxWorkerWaitMs`, so a\n\t * goal with a mix of fresh and stale bindings keeps waiting on the fresh one.\n\t */\n\tmaxWorkerWaitMs?: number;\n\t/**\n\t * Bound lanes (`Requirement.boundLaneId` -- SAME id-space as `inFlightGoalLaneIds`, not the raw\n\t * worktree-sync `laneKey`) whose worktree-sync lane has a rebase stopped on conflicts\n\t * (`LaneFacts.rebaseInProgress`). The caller (`goal-runtime-snapshot.ts`) is responsible for\n\t * translating live per-worktree-lane status into this id-space by matching each status entry's\n\t * own `boundLaneId` against the requirement -- this function stays pure and never resolves a\n\t * worktree laneKey itself, exactly like `inFlightGoalLaneIds` never resolves a tmux job id.\n\t * Checked BEFORE the waiting branch (see `lane_sync_conflict` below): a stalled-but-conflicted\n\t * worker must get the resolve directive, not silently wait forever. Optional so every\n\t * pre-existing caller keeps compiling and behaving byte-identically when omitted or empty.\n\t */\n\tlaneSyncConflictLaneKeys?: ReadonlySet<string>;\n\t/**\n\t * Bound lanes (`Requirement.boundLaneId`, same id-space note as {@link laneSyncConflictLaneKeys})\n\t * whose worktree-sync lane is stale and must sync with current main before further work\n\t * (`sync_required`, i.e. NOT already covered by `laneSyncConflictLaneKeys` -- a lane with a\n\t * rebase in progress is reported as a conflict, never double-counted here). Checked BEFORE the\n\t * waiting branch, same precedence rationale as above. Optional so every pre-existing caller keeps\n\t * compiling and behaving byte-identically when omitted or empty.\n\t */\n\tsyncRequiredLaneKeys?: ReadonlySet<string>;\n}): GoalContinuationDecision {\n\tif (!args.state) {\n\t\treturn {\n\t\t\taction: \"ask-user\",\n\t\t\treasonCode: \"missing_goal_state\",\n\t\t\tmessage: \"No goal state is present.\",\n\t\t\topenRequirementIds: [],\n\t\t\tblockedRequirementIds: [],\n\t\t\tsatisfiedRequirementIds: [],\n\t\t};\n\t}\n\n\tconst state = args.state;\n\tconst openRequirementIds: string[] = [];\n\tconst blockedRequirementIds: string[] = [];\n\tconst satisfiedRequirementIds: string[] = [];\n\n\tfor (const req of state.requirements) {\n\t\tif (req.status === \"open\") openRequirementIds.push(req.id);\n\t\telse if (req.status === \"blocked\") blockedRequirementIds.push(req.id);\n\t\telse if (req.status === \"satisfied\") satisfiedRequirementIds.push(req.id);\n\t}\n\n\tconst baseDecision = {\n\t\tgoalId: state.goalId,\n\t\tstallTurns: state.stallTurns,\n\t\tmaxStallTurns: args.settings.maxStallTurns,\n\t\topenRequirementIds,\n\t\tblockedRequirementIds,\n\t\tsatisfiedRequirementIds,\n\t};\n\n\tif (state.status === \"completed\") {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"finalize\",\n\t\t\treasonCode: \"goal_completed\",\n\t\t\tmessage: \"The goal is marked as completed.\",\n\t\t};\n\t}\n\n\tif (state.status === \"blocked\") {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"ask-user\",\n\t\t\treasonCode: \"goal_blocked\",\n\t\t\tmessage: \"The goal is explicitly blocked.\",\n\t\t};\n\t}\n\n\tif (state.status === \"cancelled\") {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"stop\",\n\t\t\treasonCode: \"goal_cancelled\",\n\t\t\tmessage: \"The goal has been cancelled.\",\n\t\t};\n\t}\n\n\t// Status is active\n\tif (blockedRequirementIds.length > 0) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"ask-user\",\n\t\t\treasonCode: \"blocked_requirements_present\",\n\t\t\tmessage: \"One or more requirements are blocked.\",\n\t\t};\n\t}\n\n\tif (openRequirementIds.length === 0) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"finalize\",\n\t\t\treasonCode: \"no_open_requirements\",\n\t\t\tmessage: \"There are no open requirements left to satisfy.\",\n\t\t};\n\t}\n\n\t// Worktree-sync directives take precedence over the \"waiting\" branch below: a worker whose bound\n\t// lane is conflicted or stale is NOT merely in-flight-and-quiet -- it needs an explicit directive\n\t// (resolve conflicts, or sync) delivered through the continuation prompt, and a stalled-but-stale\n\t// worker must never be left waiting indefinitely for a sync it was never told to run. Conflict is\n\t// checked first: a lane with a rebase already stopped on conflicts cannot usefully be told to\n\t// \"sync\" again (that lane's `sync_required` flag is never set while `rebaseInProgress` is true --\n\t// see `goal-runtime-snapshot.ts` -- so the two sets below are disjoint by construction, not by a\n\t// priority check here).\n\tconst laneSyncConflictLaneKeys = args.laneSyncConflictLaneKeys;\n\tconst conflictedRequirements = laneSyncConflictLaneKeys\n\t\t? state.requirements.filter(\n\t\t\t\t(requirement) =>\n\t\t\t\t\trequirement.status === \"open\" &&\n\t\t\t\t\trequirement.boundLaneId !== undefined &&\n\t\t\t\t\tlaneSyncConflictLaneKeys.has(requirement.boundLaneId),\n\t\t\t)\n\t\t: [];\n\tif (conflictedRequirements.length > 0) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"continue\",\n\t\t\treasonCode: \"lane_sync_conflict\",\n\t\t\tmessage: `Requirement(s) ${conflictedRequirements.map((requirement) => requirement.id).join(\", \")} are bound to a worktree-sync lane with a rebase stopped on conflicts; resolve via worktree_sync action:\"continue\" (or \"abort_sync\") before this goal can progress further.`,\n\t\t};\n\t}\n\n\tconst syncRequiredLaneKeys = args.syncRequiredLaneKeys;\n\tconst syncRequiredRequirements = syncRequiredLaneKeys\n\t\t? state.requirements.filter(\n\t\t\t\t(requirement) =>\n\t\t\t\t\trequirement.status === \"open\" &&\n\t\t\t\t\trequirement.boundLaneId !== undefined &&\n\t\t\t\t\tsyncRequiredLaneKeys.has(requirement.boundLaneId),\n\t\t\t)\n\t\t: [];\n\tif (syncRequiredRequirements.length > 0) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"continue\",\n\t\t\treasonCode: \"lane_sync_required\",\n\t\t\tmessage: `Requirement(s) ${syncRequiredRequirements.map((requirement) => requirement.id).join(\", \")} are bound to a worktree-sync lane that must rebase current main before further work; deliver worktree_sync action:\"sync\" to the bound worker, or run it directly for an idle lane.`,\n\t\t};\n\t}\n\n\t// A worker is dispatched (queued/running) against an open requirement this goal owns — wait\n\t// for it rather than submit a hollow pass or let the stall counter judge the goal unproductive.\n\t// Checked BEFORE the stall check so an in-flight worker always wins over an accumulated stall\n\t// count: the goal isn't stalled, it's actively being worked by something other than this loop.\n\tconst inFlightGoalLaneIds = args.inFlightGoalLaneIds;\n\tconst boundInFlightRequirements = inFlightGoalLaneIds\n\t\t? state.requirements.filter(\n\t\t\t\t(requirement) =>\n\t\t\t\t\trequirement.status === \"open\" &&\n\t\t\t\t\trequirement.boundLaneId !== undefined &&\n\t\t\t\t\tinFlightGoalLaneIds.has(requirement.boundLaneId),\n\t\t\t)\n\t\t: [];\n\n\tif (boundInFlightRequirements.length > 0) {\n\t\t// Never-hang backstop: a worker alive-but-hung past its deadline must escalate to the owner\n\t\t// instead of waiting forever. Only evaluated when the caller supplies BOTH a clock reading and\n\t\t// a deadline; escalates only once EVERY bound-in-flight requirement has individually timed out,\n\t\t// so one fresh binding keeps the goal legitimately waiting.\n\t\tif (args.now !== undefined && args.maxWorkerWaitMs !== undefined) {\n\t\t\tconst nowMs = Date.parse(args.now);\n\t\t\tconst maxWorkerWaitMs = args.maxWorkerWaitMs;\n\t\t\tconst allTimedOut =\n\t\t\t\tNumber.isFinite(nowMs) &&\n\t\t\t\tboundInFlightRequirements.every((requirement) => {\n\t\t\t\t\tif (requirement.boundAt === undefined) return false;\n\t\t\t\t\tconst boundAtMs = Date.parse(requirement.boundAt);\n\t\t\t\t\treturn Number.isFinite(boundAtMs) && boundAtMs + maxWorkerWaitMs <= nowMs;\n\t\t\t\t});\n\t\t\tif (allTimedOut) {\n\t\t\t\treturn {\n\t\t\t\t\t...baseDecision,\n\t\t\t\t\taction: \"ask-user\",\n\t\t\t\t\treasonCode: \"worker_wait_timeout\",\n\t\t\t\t\tmessage: `A dispatched worker has not completed within the maximum wait of ${maxWorkerWaitMs}ms; escalating to the owner instead of waiting indefinitely.`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"waiting\",\n\t\t\treasonCode: \"worker_in_flight\",\n\t\t\tmessage: \"A worker is dispatched against an open requirement; waiting for it to finish before continuing.\",\n\t\t};\n\t}\n\n\tif (args.settings.maxStallTurns > 0 && state.stallTurns >= args.settings.maxStallTurns) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"ask-user\",\n\t\t\treasonCode: \"stall_limit_reached\",\n\t\t\tmessage: `The goal has reached the maximum stall limit of ${args.settings.maxStallTurns} turns.`,\n\t\t};\n\t}\n\n\treturn {\n\t\t...baseDecision,\n\t\taction: \"continue\",\n\t\treasonCode: \"goal_active\",\n\t\tmessage: \"The goal is active and making progress.\",\n\t};\n}\n"]}
1
+ {"version":3,"file":"goal-continuation-controller.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-continuation-controller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;AAC/F,MAAM,MAAM,0BAA0B,GACnC,aAAa,GACb,gBAAgB,GAChB,cAAc,GACd,gBAAgB,GAChB,qBAAqB,GACrB,sBAAsB,GACtB,8BAA8B,GAC9B,8BAA8B,GAC9B,oBAAoB,GACpB,kBAAkB,GAClB,qBAAqB,GACrB,oBAAoB,GACpB,oBAAoB,CAAC;AAExB,MAAM,WAAW,wBAAwB;IACxC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,UAAU,EAAE,0BAA0B,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,uBAAuB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,wBAAwB;IACxC,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,wBAAwB,CAAC;IACnC;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,wBAAwB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/C;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC3C,GAAG,wBAAwB,CAiM3B","sourcesContent":["import { getUnprovenGoalRequirementIds } from \"./goal-acceptance.ts\";\nimport type { GoalState } from \"./goal-state.ts\";\n\nexport type GoalContinuationAction = \"continue\" | \"ask-user\" | \"finalize\" | \"stop\" | \"waiting\";\nexport type GoalContinuationReasonCode =\n\t| \"goal_active\"\n\t| \"goal_completed\"\n\t| \"goal_blocked\"\n\t| \"goal_cancelled\"\n\t| \"stall_limit_reached\"\n\t| \"no_open_requirements\"\n\t| \"acceptance_evidence_required\"\n\t| \"blocked_requirements_present\"\n\t| \"missing_goal_state\"\n\t| \"worker_in_flight\"\n\t| \"worker_wait_timeout\"\n\t| \"lane_sync_conflict\"\n\t| \"lane_sync_required\";\n\nexport interface GoalContinuationDecision {\n\taction: GoalContinuationAction;\n\treasonCode: GoalContinuationReasonCode;\n\tmessage: string;\n\tgoalId?: string;\n\tstallTurns?: number;\n\tmaxStallTurns?: number;\n\topenRequirementIds: readonly string[];\n\tblockedRequirementIds: readonly string[];\n\tsatisfiedRequirementIds: readonly string[];\n}\n\nexport interface GoalContinuationSettings {\n\tmaxStallTurns: number;\n}\n\nexport function evaluateGoalContinuation(args: {\n\tstate?: GoalState;\n\tsettings: GoalContinuationSettings;\n\t/**\n\t * LaneIds currently queued/running AND tagged with THIS goal's id (see `LaneRecord.goalId`).\n\t * When an open requirement is bound (`Requirement.boundLaneId`) to one of these lanes, the\n\t * goal is WAITING on that worker rather than stalled or ready for another pass: the loop must not\n\t * submit a hollow continuation prompt (which would then misreport as `goal_state_not_advanced`),\n\t * and the idle scheduler must not race a re-dispatch against the same open requirement. Optional\n\t * so every pre-existing (in-flight-unaware) caller keeps compiling and behaving unchanged.\n\t */\n\tinFlightGoalLaneIds?: ReadonlySet<string>;\n\t/**\n\t * Current time as an ISO string, paired with `maxWorkerWaitMs` to detect a bound in-flight\n\t * requirement that has hung past its deadline (see below). Optional so every pre-existing\n\t * caller that omits it keeps behaving byte-identically -- the goal waits indefinitely, exactly\n\t * as before this field existed.\n\t */\n\tnow?: string;\n\t/**\n\t * Maximum milliseconds a bound in-flight requirement (`Requirement.boundAt`) may wait before\n\t * this escalates to `action:\"ask-user\"`/`reasonCode:\"worker_wait_timeout\"` instead of\n\t * `\"waiting\"` -- a worker that is alive-but-hung past its deadline must not wait forever. Only\n\t * takes effect when BOTH `now` and this are supplied; escalation fires only once EVERY\n\t * bound-in-flight open requirement has individually passed `boundAt + maxWorkerWaitMs`, so a\n\t * goal with a mix of fresh and stale bindings keeps waiting on the fresh one.\n\t */\n\tmaxWorkerWaitMs?: number;\n\t/**\n\t * Bound lanes (`Requirement.boundLaneId` -- SAME id-space as `inFlightGoalLaneIds`, not the raw\n\t * worktree-sync `laneKey`) whose worktree-sync lane has a rebase stopped on conflicts\n\t * (`LaneFacts.rebaseInProgress`). The caller (`goal-runtime-snapshot.ts`) is responsible for\n\t * translating live per-worktree-lane status into this id-space by matching each status entry's\n\t * own `boundLaneId` against the requirement -- this function stays pure and never resolves a\n\t * worktree laneKey itself, exactly like `inFlightGoalLaneIds` never resolves a tmux job id.\n\t * Checked BEFORE the waiting branch (see `lane_sync_conflict` below): a stalled-but-conflicted\n\t * worker must get the resolve directive, not silently wait forever. Optional so every\n\t * pre-existing caller keeps compiling and behaving byte-identically when omitted or empty.\n\t */\n\tlaneSyncConflictLaneKeys?: ReadonlySet<string>;\n\t/**\n\t * Bound lanes (`Requirement.boundLaneId`, same id-space note as {@link laneSyncConflictLaneKeys})\n\t * whose worktree-sync lane is stale and must sync with current main before further work\n\t * (`sync_required`, i.e. NOT already covered by `laneSyncConflictLaneKeys` -- a lane with a\n\t * rebase in progress is reported as a conflict, never double-counted here). Checked BEFORE the\n\t * waiting branch, same precedence rationale as above. Optional so every pre-existing caller keeps\n\t * compiling and behaving byte-identically when omitted or empty.\n\t */\n\tsyncRequiredLaneKeys?: ReadonlySet<string>;\n}): GoalContinuationDecision {\n\tif (!args.state) {\n\t\treturn {\n\t\t\taction: \"ask-user\",\n\t\t\treasonCode: \"missing_goal_state\",\n\t\t\tmessage: \"No goal state is present.\",\n\t\t\topenRequirementIds: [],\n\t\t\tblockedRequirementIds: [],\n\t\t\tsatisfiedRequirementIds: [],\n\t\t};\n\t}\n\n\tconst state = args.state;\n\tconst openRequirementIds: string[] = [];\n\tconst blockedRequirementIds: string[] = [];\n\tconst satisfiedRequirementIds: string[] = [];\n\n\tfor (const req of state.requirements) {\n\t\tif (req.status === \"open\") openRequirementIds.push(req.id);\n\t\telse if (req.status === \"blocked\") blockedRequirementIds.push(req.id);\n\t\telse if (req.status === \"satisfied\") satisfiedRequirementIds.push(req.id);\n\t}\n\n\tconst baseDecision = {\n\t\tgoalId: state.goalId,\n\t\tstallTurns: state.stallTurns,\n\t\tmaxStallTurns: args.settings.maxStallTurns,\n\t\topenRequirementIds,\n\t\tblockedRequirementIds,\n\t\tsatisfiedRequirementIds,\n\t};\n\n\tif (state.status === \"completed\") {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"finalize\",\n\t\t\treasonCode: \"goal_completed\",\n\t\t\tmessage: \"The goal is marked as completed.\",\n\t\t};\n\t}\n\n\tif (state.status === \"blocked\") {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"ask-user\",\n\t\t\treasonCode: \"goal_blocked\",\n\t\t\tmessage: \"The goal is explicitly blocked.\",\n\t\t};\n\t}\n\n\tif (state.status === \"cancelled\") {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"stop\",\n\t\t\treasonCode: \"goal_cancelled\",\n\t\t\tmessage: \"The goal has been cancelled.\",\n\t\t};\n\t}\n\n\t// Status is active\n\tif (blockedRequirementIds.length > 0) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"ask-user\",\n\t\t\treasonCode: \"blocked_requirements_present\",\n\t\t\tmessage: \"One or more requirements are blocked.\",\n\t\t};\n\t}\n\n\tif (openRequirementIds.length === 0) {\n\t\tconst unprovenRequirementIds = getUnprovenGoalRequirementIds(state);\n\t\tif (unprovenRequirementIds.length > 0) {\n\t\t\treturn {\n\t\t\t\t...baseDecision,\n\t\t\t\taction: \"continue\",\n\t\t\t\treasonCode: \"acceptance_evidence_required\",\n\t\t\t\tmessage: `Requirement(s) ${unprovenRequirementIds.join(\", \")} still need trusted acceptance evidence.`,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"finalize\",\n\t\t\treasonCode: \"no_open_requirements\",\n\t\t\tmessage: \"There are no open requirements left to satisfy.\",\n\t\t};\n\t}\n\n\t// Worktree-sync directives take precedence over the \"waiting\" branch below: a worker whose bound\n\t// lane is conflicted or stale is NOT merely in-flight-and-quiet -- it needs an explicit directive\n\t// (resolve conflicts, or sync) delivered through the continuation prompt, and a stalled-but-stale\n\t// worker must never be left waiting indefinitely for a sync it was never told to run. Conflict is\n\t// checked first: a lane with a rebase already stopped on conflicts cannot usefully be told to\n\t// \"sync\" again (that lane's `sync_required` flag is never set while `rebaseInProgress` is true --\n\t// see `goal-runtime-snapshot.ts` -- so the two sets below are disjoint by construction, not by a\n\t// priority check here).\n\tconst laneSyncConflictLaneKeys = args.laneSyncConflictLaneKeys;\n\tconst conflictedRequirements = laneSyncConflictLaneKeys\n\t\t? state.requirements.filter(\n\t\t\t\t(requirement) =>\n\t\t\t\t\trequirement.status === \"open\" &&\n\t\t\t\t\trequirement.boundLaneId !== undefined &&\n\t\t\t\t\tlaneSyncConflictLaneKeys.has(requirement.boundLaneId),\n\t\t\t)\n\t\t: [];\n\tif (conflictedRequirements.length > 0) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"continue\",\n\t\t\treasonCode: \"lane_sync_conflict\",\n\t\t\tmessage: `Requirement(s) ${conflictedRequirements.map((requirement) => requirement.id).join(\", \")} are bound to a worktree-sync lane with a rebase stopped on conflicts; resolve via worktree_sync action:\"continue\" (or \"abort_sync\") before this goal can progress further.`,\n\t\t};\n\t}\n\n\tconst syncRequiredLaneKeys = args.syncRequiredLaneKeys;\n\tconst syncRequiredRequirements = syncRequiredLaneKeys\n\t\t? state.requirements.filter(\n\t\t\t\t(requirement) =>\n\t\t\t\t\trequirement.status === \"open\" &&\n\t\t\t\t\trequirement.boundLaneId !== undefined &&\n\t\t\t\t\tsyncRequiredLaneKeys.has(requirement.boundLaneId),\n\t\t\t)\n\t\t: [];\n\tif (syncRequiredRequirements.length > 0) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"continue\",\n\t\t\treasonCode: \"lane_sync_required\",\n\t\t\tmessage: `Requirement(s) ${syncRequiredRequirements.map((requirement) => requirement.id).join(\", \")} are bound to a worktree-sync lane that must rebase current main before further work; deliver worktree_sync action:\"sync\" to the bound worker, or run it directly for an idle lane.`,\n\t\t};\n\t}\n\n\t// A worker is dispatched (queued/running) against an open requirement this goal owns — wait\n\t// for it rather than submit a hollow pass or let the stall counter judge the goal unproductive.\n\t// Checked BEFORE the stall check so an in-flight worker always wins over an accumulated stall\n\t// count: the goal isn't stalled, it's actively being worked by something other than this loop.\n\tconst inFlightGoalLaneIds = args.inFlightGoalLaneIds;\n\tconst boundInFlightRequirements = inFlightGoalLaneIds\n\t\t? state.requirements.filter(\n\t\t\t\t(requirement) =>\n\t\t\t\t\trequirement.status === \"open\" &&\n\t\t\t\t\trequirement.boundLaneId !== undefined &&\n\t\t\t\t\tinFlightGoalLaneIds.has(requirement.boundLaneId),\n\t\t\t)\n\t\t: [];\n\n\tif (boundInFlightRequirements.length > 0) {\n\t\t// Never-hang backstop: a worker alive-but-hung past its deadline must escalate to the owner\n\t\t// instead of waiting forever. Only evaluated when the caller supplies BOTH a clock reading and\n\t\t// a deadline; escalates only once EVERY bound-in-flight requirement has individually timed out,\n\t\t// so one fresh binding keeps the goal legitimately waiting.\n\t\tif (args.now !== undefined && args.maxWorkerWaitMs !== undefined) {\n\t\t\tconst nowMs = Date.parse(args.now);\n\t\t\tconst maxWorkerWaitMs = args.maxWorkerWaitMs;\n\t\t\tconst allTimedOut =\n\t\t\t\tNumber.isFinite(nowMs) &&\n\t\t\t\tboundInFlightRequirements.every((requirement) => {\n\t\t\t\t\tif (requirement.boundAt === undefined) return false;\n\t\t\t\t\tconst boundAtMs = Date.parse(requirement.boundAt);\n\t\t\t\t\treturn Number.isFinite(boundAtMs) && boundAtMs + maxWorkerWaitMs <= nowMs;\n\t\t\t\t});\n\t\t\tif (allTimedOut) {\n\t\t\t\treturn {\n\t\t\t\t\t...baseDecision,\n\t\t\t\t\taction: \"ask-user\",\n\t\t\t\t\treasonCode: \"worker_wait_timeout\",\n\t\t\t\t\tmessage: `A dispatched worker has not completed within the maximum wait of ${maxWorkerWaitMs}ms; escalating to the owner instead of waiting indefinitely.`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"waiting\",\n\t\t\treasonCode: \"worker_in_flight\",\n\t\t\tmessage: \"A worker is dispatched against an open requirement; waiting for it to finish before continuing.\",\n\t\t};\n\t}\n\n\tif (args.settings.maxStallTurns > 0 && state.stallTurns >= args.settings.maxStallTurns) {\n\t\treturn {\n\t\t\t...baseDecision,\n\t\t\taction: \"ask-user\",\n\t\t\treasonCode: \"stall_limit_reached\",\n\t\t\tmessage: `The goal has reached the maximum stall limit of ${args.settings.maxStallTurns} turns.`,\n\t\t};\n\t}\n\n\treturn {\n\t\t...baseDecision,\n\t\taction: \"continue\",\n\t\treasonCode: \"goal_active\",\n\t\tmessage: \"The goal is active and making progress.\",\n\t};\n}\n"]}