agentic-orchestrator 0.1.2 → 0.1.4

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 (300) hide show
  1. package/.claude/settings.local.json +15 -0
  2. package/CLAUDE.md +126 -0
  3. package/README.md +166 -25
  4. package/agentic/orchestrator/adapters.yaml +3 -0
  5. package/agentic/orchestrator/gates.yaml +47 -0
  6. package/agentic/orchestrator/policy.yaml +89 -0
  7. package/agentic/orchestrator/schemas/adapters.schema.json +12 -0
  8. package/agentic/orchestrator/schemas/gates.schema.json +6 -1
  9. package/agentic/orchestrator/schemas/index.schema.json +14 -0
  10. package/agentic/orchestrator/schemas/multi-project.schema.json +41 -0
  11. package/agentic/orchestrator/schemas/policy.schema.json +449 -52
  12. package/agentic/orchestrator/schemas/state.schema.json +16 -0
  13. package/agentic/orchestrator/tools/catalog.json +68 -0
  14. package/agentic/orchestrator/tools/schemas/input/cost.get.input.schema.json +10 -0
  15. package/agentic/orchestrator/tools/schemas/input/cost.record.input.schema.json +13 -0
  16. package/agentic/orchestrator/tools/schemas/input/feature.send_message.input.schema.json +11 -0
  17. package/agentic/orchestrator/tools/schemas/input/performance.get_analytics.input.schema.json +10 -0
  18. package/agentic/orchestrator/tools/schemas/input/performance.record_outcome.input.schema.json +18 -0
  19. package/agentic/orchestrator/tools/schemas/output/cost.get.output.schema.json +13 -0
  20. package/agentic/orchestrator/tools/schemas/output/cost.record.output.schema.json +13 -0
  21. package/agentic/orchestrator/tools/schemas/output/feature.ready_to_merge.output.schema.json +7 -0
  22. package/agentic/orchestrator/tools/schemas/output/feature.send_message.output.schema.json +23 -0
  23. package/agentic/orchestrator/tools/schemas/output/performance.get_analytics.output.schema.json +46 -0
  24. package/agentic/orchestrator/tools/schemas/output/performance.record_outcome.output.schema.json +10 -0
  25. package/agentic/orchestrator/tools.md +5 -0
  26. package/apps/control-plane/scripts/validate-architecture-rules.mjs +28 -2
  27. package/apps/control-plane/scripts/validate-docker-mcp-contract.mjs +12 -0
  28. package/apps/control-plane/scripts/validate-mcp-contracts.ts +92 -0
  29. package/apps/control-plane/src/application/adapters/adapter-registry.ts +169 -0
  30. package/apps/control-plane/src/application/multi-project-loader.ts +119 -0
  31. package/apps/control-plane/src/application/services/activity-monitor-service.ts +199 -0
  32. package/apps/control-plane/src/application/services/cost-tracking-service.ts +82 -0
  33. package/apps/control-plane/src/application/services/dependency-scheduler-service.ts +86 -0
  34. package/apps/control-plane/src/application/services/feature-deletion-service.ts +8 -7
  35. package/apps/control-plane/src/application/services/gate-interpolation-service.ts +15 -0
  36. package/apps/control-plane/src/application/services/gate-service.ts +38 -2
  37. package/apps/control-plane/src/application/services/instance-isolation-service.ts +18 -0
  38. package/apps/control-plane/src/application/services/issue-tracker-service.ts +469 -0
  39. package/apps/control-plane/src/application/services/merge-service.ts +67 -3
  40. package/apps/control-plane/src/application/services/notifier-service.ts +295 -0
  41. package/apps/control-plane/src/application/services/performance-analytics-service.ts +122 -0
  42. package/apps/control-plane/src/application/services/plan-service.ts +51 -0
  43. package/apps/control-plane/src/application/services/pr-monitor-service.ts +262 -0
  44. package/apps/control-plane/src/application/services/reactions-service.ts +175 -0
  45. package/apps/control-plane/src/application/services/reporting-service.ts +17 -2
  46. package/apps/control-plane/src/application/services/run-lease-service.ts +16 -38
  47. package/apps/control-plane/src/application/tools/tool-metadata.ts +4 -1
  48. package/apps/control-plane/src/cli/aop.ts +1 -1
  49. package/apps/control-plane/src/cli/attach-command-handler.ts +120 -0
  50. package/apps/control-plane/src/cli/cleanup-command-handler.ts +190 -0
  51. package/apps/control-plane/src/cli/cli-argument-parser.ts +69 -3
  52. package/apps/control-plane/src/cli/dashboard-command-handler.ts +57 -0
  53. package/apps/control-plane/src/cli/help-command-handler.ts +163 -0
  54. package/apps/control-plane/src/cli/init-command-handler.ts +609 -0
  55. package/apps/control-plane/src/cli/resume-command-handler.ts +1 -0
  56. package/apps/control-plane/src/cli/retry-command-handler.ts +138 -0
  57. package/apps/control-plane/src/cli/run-command-handler.ts +115 -3
  58. package/apps/control-plane/src/cli/send-command-handler.ts +65 -0
  59. package/apps/control-plane/src/cli/status-command-handler.ts +102 -2
  60. package/apps/control-plane/src/cli/types.ts +26 -1
  61. package/apps/control-plane/src/core/constants.ts +8 -2
  62. package/apps/control-plane/src/core/error-codes.ts +3 -1
  63. package/apps/control-plane/src/core/gates.ts +170 -50
  64. package/apps/control-plane/src/core/kernel.ts +280 -5
  65. package/apps/control-plane/src/core/path-layout.ts +12 -0
  66. package/apps/control-plane/src/core/tool-caller.ts +36 -0
  67. package/apps/control-plane/src/core/workspace-hooks.ts +87 -0
  68. package/apps/control-plane/src/interfaces/cli/bootstrap.ts +258 -9
  69. package/apps/control-plane/src/providers/providers.ts +235 -14
  70. package/apps/control-plane/src/supervisor/build-wave-executor.ts +129 -8
  71. package/apps/control-plane/src/supervisor/qa-wave-executor.ts +123 -5
  72. package/apps/control-plane/src/supervisor/run-coordinator.ts +143 -6
  73. package/apps/control-plane/src/supervisor/runtime.ts +135 -6
  74. package/apps/control-plane/src/supervisor/types.ts +12 -21
  75. package/apps/control-plane/src/supervisor/worker-decision-loop.ts +8 -0
  76. package/apps/control-plane/test/activity-monitor.spec.ts +294 -0
  77. package/apps/control-plane/test/adapter-registry.spec.ts +132 -0
  78. package/apps/control-plane/test/batch-operations.spec.ts +112 -0
  79. package/apps/control-plane/test/bootstrap-attach.spec.ts +102 -0
  80. package/apps/control-plane/test/bootstrap-edge-cases.spec.ts +252 -0
  81. package/apps/control-plane/test/bootstrap.spec.ts +560 -0
  82. package/apps/control-plane/test/cleanup-command.spec.ts +301 -0
  83. package/apps/control-plane/test/cli-helpers.spec.ts +404 -1
  84. package/apps/control-plane/test/cli.unit.spec.ts +182 -1
  85. package/apps/control-plane/test/collision-queue.spec.ts +104 -1
  86. package/apps/control-plane/test/core-utils.spec.ts +175 -2
  87. package/apps/control-plane/test/cost-tracking.spec.ts +143 -0
  88. package/apps/control-plane/test/dashboard-api.integration.spec.ts +247 -0
  89. package/apps/control-plane/test/dashboard-client.spec.ts +116 -0
  90. package/apps/control-plane/test/dashboard-command.spec.ts +103 -0
  91. package/apps/control-plane/test/dependency-scheduler.spec.ts +189 -0
  92. package/apps/control-plane/test/epoch-tracking.spec.ts +4 -4
  93. package/apps/control-plane/test/feature-deletion-service.spec.ts +422 -0
  94. package/apps/control-plane/test/feature-lifecycle.spec.ts +202 -0
  95. package/apps/control-plane/test/git-spawn-error.spec.ts +24 -0
  96. package/apps/control-plane/test/incremental-gates.spec.ts +137 -0
  97. package/apps/control-plane/test/init-wizard.spec.ts +506 -0
  98. package/apps/control-plane/test/instance-isolation.spec.ts +83 -0
  99. package/apps/control-plane/test/issue-tracker.spec.ts +890 -0
  100. package/apps/control-plane/test/kernel.coverage.spec.ts +3 -5
  101. package/apps/control-plane/test/kernel.coverage2.spec.ts +871 -0
  102. package/apps/control-plane/test/kernel.spec.ts +13 -11
  103. package/apps/control-plane/test/lock-service.spec.ts +508 -0
  104. package/apps/control-plane/test/mcp-helpers.spec.ts +176 -0
  105. package/apps/control-plane/test/mcp.spec.ts +50 -15
  106. package/apps/control-plane/test/merge-service.spec.ts +67 -4
  107. package/apps/control-plane/test/multi-project.spec.ts +372 -0
  108. package/apps/control-plane/test/notifier-service.spec.ts +388 -0
  109. package/apps/control-plane/test/parallel-gates.spec.ts +312 -0
  110. package/apps/control-plane/test/patch-service.spec.ts +253 -0
  111. package/apps/control-plane/test/performance-analytics.spec.ts +338 -0
  112. package/apps/control-plane/test/planning-wave-executor.spec.ts +168 -0
  113. package/apps/control-plane/test/pr-monitor.spec.ts +385 -0
  114. package/apps/control-plane/test/providers.spec.ts +344 -1
  115. package/apps/control-plane/test/reactions.spec.ts +392 -0
  116. package/apps/control-plane/test/resume-command.spec.ts +390 -0
  117. package/apps/control-plane/test/run-coordinator.spec.ts +481 -2
  118. package/apps/control-plane/test/schema-date-time.spec.ts +46 -0
  119. package/apps/control-plane/test/service-retry-paths.spec.ts +30 -0
  120. package/apps/control-plane/test/services.spec.ts +95 -2
  121. package/apps/control-plane/test/session-management.spec.ts +450 -0
  122. package/apps/control-plane/test/spec-ingestion.spec.ts +190 -0
  123. package/apps/control-plane/test/supervisor-collaborators.spec.ts +699 -2
  124. package/apps/control-plane/test/supervisor.spec.ts +36 -30
  125. package/apps/control-plane/test/supervisor.unit.spec.ts +405 -0
  126. package/apps/control-plane/test/worker-decision-loop.spec.ts +57 -0
  127. package/apps/control-plane/test/workspace-hooks.spec.ts +177 -0
  128. package/apps/control-plane/vitest.config.ts +21 -5
  129. package/dist/apps/control-plane/application/adapters/adapter-registry.d.ts +44 -0
  130. package/dist/apps/control-plane/application/adapters/adapter-registry.js +76 -0
  131. package/dist/apps/control-plane/application/adapters/adapter-registry.js.map +1 -0
  132. package/dist/apps/control-plane/application/multi-project-loader.d.ts +31 -0
  133. package/dist/apps/control-plane/application/multi-project-loader.js +82 -0
  134. package/dist/apps/control-plane/application/multi-project-loader.js.map +1 -0
  135. package/dist/apps/control-plane/application/services/activity-monitor-service.d.ts +43 -0
  136. package/dist/apps/control-plane/application/services/activity-monitor-service.js +132 -0
  137. package/dist/apps/control-plane/application/services/activity-monitor-service.js.map +1 -0
  138. package/dist/apps/control-plane/application/services/cost-tracking-service.d.ts +28 -0
  139. package/dist/apps/control-plane/application/services/cost-tracking-service.js +48 -0
  140. package/dist/apps/control-plane/application/services/cost-tracking-service.js.map +1 -0
  141. package/dist/apps/control-plane/application/services/dependency-scheduler-service.d.ts +26 -0
  142. package/dist/apps/control-plane/application/services/dependency-scheduler-service.js +75 -0
  143. package/dist/apps/control-plane/application/services/dependency-scheduler-service.js.map +1 -0
  144. package/dist/apps/control-plane/application/services/feature-deletion-service.d.ts +2 -0
  145. package/dist/apps/control-plane/application/services/feature-deletion-service.js +6 -7
  146. package/dist/apps/control-plane/application/services/feature-deletion-service.js.map +1 -1
  147. package/dist/apps/control-plane/application/services/gate-interpolation-service.d.ts +7 -0
  148. package/dist/apps/control-plane/application/services/gate-interpolation-service.js +7 -0
  149. package/dist/apps/control-plane/application/services/gate-interpolation-service.js.map +1 -0
  150. package/dist/apps/control-plane/application/services/gate-service.js +32 -2
  151. package/dist/apps/control-plane/application/services/gate-service.js.map +1 -1
  152. package/dist/apps/control-plane/application/services/instance-isolation-service.d.ts +11 -0
  153. package/dist/apps/control-plane/application/services/instance-isolation-service.js +17 -0
  154. package/dist/apps/control-plane/application/services/instance-isolation-service.js.map +1 -0
  155. package/dist/apps/control-plane/application/services/issue-tracker-service.d.ts +65 -0
  156. package/dist/apps/control-plane/application/services/issue-tracker-service.js +358 -0
  157. package/dist/apps/control-plane/application/services/issue-tracker-service.js.map +1 -0
  158. package/dist/apps/control-plane/application/services/merge-service.d.ts +4 -0
  159. package/dist/apps/control-plane/application/services/merge-service.js +44 -2
  160. package/dist/apps/control-plane/application/services/merge-service.js.map +1 -1
  161. package/dist/apps/control-plane/application/services/notifier-service.d.ts +74 -0
  162. package/dist/apps/control-plane/application/services/notifier-service.js +212 -0
  163. package/dist/apps/control-plane/application/services/notifier-service.js.map +1 -0
  164. package/dist/apps/control-plane/application/services/performance-analytics-service.d.ts +39 -0
  165. package/dist/apps/control-plane/application/services/performance-analytics-service.js +75 -0
  166. package/dist/apps/control-plane/application/services/performance-analytics-service.js.map +1 -0
  167. package/dist/apps/control-plane/application/services/plan-service.d.ts +1 -0
  168. package/dist/apps/control-plane/application/services/plan-service.js +53 -0
  169. package/dist/apps/control-plane/application/services/plan-service.js.map +1 -1
  170. package/dist/apps/control-plane/application/services/pr-monitor-service.d.ts +44 -0
  171. package/dist/apps/control-plane/application/services/pr-monitor-service.js +192 -0
  172. package/dist/apps/control-plane/application/services/pr-monitor-service.js.map +1 -0
  173. package/dist/apps/control-plane/application/services/reactions-service.d.ts +67 -0
  174. package/dist/apps/control-plane/application/services/reactions-service.js +114 -0
  175. package/dist/apps/control-plane/application/services/reactions-service.js.map +1 -0
  176. package/dist/apps/control-plane/application/services/reporting-service.d.ts +1 -0
  177. package/dist/apps/control-plane/application/services/reporting-service.js +13 -2
  178. package/dist/apps/control-plane/application/services/reporting-service.js.map +1 -1
  179. package/dist/apps/control-plane/application/services/run-lease-service.d.ts +2 -0
  180. package/dist/apps/control-plane/application/services/run-lease-service.js +14 -38
  181. package/dist/apps/control-plane/application/services/run-lease-service.js.map +1 -1
  182. package/dist/apps/control-plane/application/tools/tool-metadata.js +3 -1
  183. package/dist/apps/control-plane/application/tools/tool-metadata.js.map +1 -1
  184. package/dist/apps/control-plane/cli/aop.d.ts +1 -1
  185. package/dist/apps/control-plane/cli/aop.js +1 -1
  186. package/dist/apps/control-plane/cli/attach-command-handler.d.ts +12 -0
  187. package/dist/apps/control-plane/cli/attach-command-handler.js +98 -0
  188. package/dist/apps/control-plane/cli/attach-command-handler.js.map +1 -0
  189. package/dist/apps/control-plane/cli/cleanup-command-handler.d.ts +12 -0
  190. package/dist/apps/control-plane/cli/cleanup-command-handler.js +162 -0
  191. package/dist/apps/control-plane/cli/cleanup-command-handler.js.map +1 -0
  192. package/dist/apps/control-plane/cli/cli-argument-parser.js +73 -3
  193. package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -1
  194. package/dist/apps/control-plane/cli/dashboard-command-handler.d.ts +7 -0
  195. package/dist/apps/control-plane/cli/dashboard-command-handler.js +45 -0
  196. package/dist/apps/control-plane/cli/dashboard-command-handler.js.map +1 -0
  197. package/dist/apps/control-plane/cli/help-command-handler.d.ts +8 -0
  198. package/dist/apps/control-plane/cli/help-command-handler.js +146 -0
  199. package/dist/apps/control-plane/cli/help-command-handler.js.map +1 -0
  200. package/dist/apps/control-plane/cli/init-command-handler.d.ts +26 -0
  201. package/dist/apps/control-plane/cli/init-command-handler.js +517 -0
  202. package/dist/apps/control-plane/cli/init-command-handler.js.map +1 -0
  203. package/dist/apps/control-plane/cli/resume-command-handler.js +1 -1
  204. package/dist/apps/control-plane/cli/resume-command-handler.js.map +1 -1
  205. package/dist/apps/control-plane/cli/retry-command-handler.d.ts +8 -0
  206. package/dist/apps/control-plane/cli/retry-command-handler.js +111 -0
  207. package/dist/apps/control-plane/cli/retry-command-handler.js.map +1 -0
  208. package/dist/apps/control-plane/cli/run-command-handler.d.ts +5 -0
  209. package/dist/apps/control-plane/cli/run-command-handler.js +82 -3
  210. package/dist/apps/control-plane/cli/run-command-handler.js.map +1 -1
  211. package/dist/apps/control-plane/cli/send-command-handler.d.ts +8 -0
  212. package/dist/apps/control-plane/cli/send-command-handler.js +55 -0
  213. package/dist/apps/control-plane/cli/send-command-handler.js.map +1 -0
  214. package/dist/apps/control-plane/cli/status-command-handler.d.ts +12 -1
  215. package/dist/apps/control-plane/cli/status-command-handler.js +55 -2
  216. package/dist/apps/control-plane/cli/status-command-handler.js.map +1 -1
  217. package/dist/apps/control-plane/cli/types.d.ts +26 -1
  218. package/dist/apps/control-plane/cli/types.js +15 -1
  219. package/dist/apps/control-plane/cli/types.js.map +1 -1
  220. package/dist/apps/control-plane/core/constants.d.ts +6 -0
  221. package/dist/apps/control-plane/core/constants.js +8 -2
  222. package/dist/apps/control-plane/core/constants.js.map +1 -1
  223. package/dist/apps/control-plane/core/error-codes.d.ts +2 -0
  224. package/dist/apps/control-plane/core/error-codes.js +3 -1
  225. package/dist/apps/control-plane/core/error-codes.js.map +1 -1
  226. package/dist/apps/control-plane/core/gates.d.ts +4 -0
  227. package/dist/apps/control-plane/core/gates.js +140 -43
  228. package/dist/apps/control-plane/core/gates.js.map +1 -1
  229. package/dist/apps/control-plane/core/kernel.d.ts +50 -1
  230. package/dist/apps/control-plane/core/kernel.js +220 -7
  231. package/dist/apps/control-plane/core/kernel.js.map +1 -1
  232. package/dist/apps/control-plane/core/path-layout.d.ts +3 -0
  233. package/dist/apps/control-plane/core/path-layout.js +9 -0
  234. package/dist/apps/control-plane/core/path-layout.js.map +1 -1
  235. package/dist/apps/control-plane/core/tool-caller.d.ts +32 -0
  236. package/dist/apps/control-plane/core/tool-caller.js +2 -0
  237. package/dist/apps/control-plane/core/tool-caller.js.map +1 -0
  238. package/dist/apps/control-plane/core/workspace-hooks.d.ts +20 -0
  239. package/dist/apps/control-plane/core/workspace-hooks.js +69 -0
  240. package/dist/apps/control-plane/core/workspace-hooks.js.map +1 -0
  241. package/dist/apps/control-plane/interfaces/cli/bootstrap.js +245 -9
  242. package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -1
  243. package/dist/apps/control-plane/providers/providers.d.ts +42 -3
  244. package/dist/apps/control-plane/providers/providers.js +216 -5
  245. package/dist/apps/control-plane/providers/providers.js.map +1 -1
  246. package/dist/apps/control-plane/supervisor/build-wave-executor.d.ts +3 -0
  247. package/dist/apps/control-plane/supervisor/build-wave-executor.js +115 -6
  248. package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
  249. package/dist/apps/control-plane/supervisor/qa-wave-executor.d.ts +3 -0
  250. package/dist/apps/control-plane/supervisor/qa-wave-executor.js +109 -5
  251. package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
  252. package/dist/apps/control-plane/supervisor/run-coordinator.d.ts +15 -0
  253. package/dist/apps/control-plane/supervisor/run-coordinator.js +132 -6
  254. package/dist/apps/control-plane/supervisor/run-coordinator.js.map +1 -1
  255. package/dist/apps/control-plane/supervisor/runtime.d.ts +3 -0
  256. package/dist/apps/control-plane/supervisor/runtime.js +110 -6
  257. package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
  258. package/dist/apps/control-plane/supervisor/types.d.ts +9 -16
  259. package/dist/apps/control-plane/supervisor/types.js.map +1 -1
  260. package/dist/apps/control-plane/supervisor/worker-decision-loop.d.ts +3 -0
  261. package/dist/apps/control-plane/supervisor/worker-decision-loop.js +5 -0
  262. package/dist/apps/control-plane/supervisor/worker-decision-loop.js.map +1 -1
  263. package/eslint.config.mjs +2 -1
  264. package/package.json +12 -2
  265. package/packages/web-dashboard/next-env.d.ts +5 -0
  266. package/packages/web-dashboard/next.config.js +7 -0
  267. package/packages/web-dashboard/package.json +26 -0
  268. package/packages/web-dashboard/src/app/api/actions/route.ts +64 -0
  269. package/packages/web-dashboard/src/app/api/events/route.ts +51 -0
  270. package/packages/web-dashboard/src/app/api/features/[id]/checkout/route.ts +256 -0
  271. package/packages/web-dashboard/src/app/api/features/[id]/diff/route.ts +10 -0
  272. package/packages/web-dashboard/src/app/api/features/[id]/evidence/[artifact]/route.ts +25 -0
  273. package/packages/web-dashboard/src/app/api/features/[id]/review/route.ts +63 -0
  274. package/packages/web-dashboard/src/app/api/features/[id]/route.ts +16 -0
  275. package/packages/web-dashboard/src/app/api/projects/route.ts +31 -0
  276. package/packages/web-dashboard/src/app/api/status/route.ts +15 -0
  277. package/packages/web-dashboard/src/app/globals.css +2 -0
  278. package/packages/web-dashboard/src/app/layout.tsx +15 -0
  279. package/packages/web-dashboard/src/app/page.tsx +393 -0
  280. package/packages/web-dashboard/src/lib/aop-client.ts +244 -0
  281. package/packages/web-dashboard/src/lib/multi-project-config.ts +116 -0
  282. package/packages/web-dashboard/src/lib/orchestrator-tools.ts +284 -0
  283. package/packages/web-dashboard/src/lib/types.ts +58 -0
  284. package/packages/web-dashboard/tsconfig.json +40 -0
  285. package/packages/web-dashboard/vitest.config.ts +6 -0
  286. package/spec-files/completed/agentic_orchestrator_feature_gaps_closure_spec.md +1764 -0
  287. package/spec-files/outstanding/agentic_orchestrator_enterprise_governance_dashboard_spec.md +348 -0
  288. package/spec-files/outstanding/agentic_orchestrator_knowledge_canary_spec.md +344 -0
  289. package/spec-files/outstanding/agentic_orchestrator_observability_integrity_diagnostics_spec.md +374 -0
  290. package/spec-files/outstanding/agentic_orchestrator_performance_improvements_spec.md +1059 -0
  291. package/spec-files/outstanding/agentic_orchestrator_planning_review_quality_spec.md +466 -0
  292. package/spec-files/outstanding/agentic_orchestrator_quality_adoption_execution_spec.md +198 -0
  293. package/spec-files/outstanding/agentic_orchestrator_validator_hardening_spec.md +365 -0
  294. package/spec-files/progress.md +481 -52
  295. /package/spec-files/{agentic_orchestrator_cli_delete_command_spec.md → completed/agentic_orchestrator_cli_delete_command_spec.md} +0 -0
  296. /package/spec-files/{agentic_orchestrator_dot_aop_generated_artifacts_spec.md → completed/agentic_orchestrator_dot_aop_generated_artifacts_spec.md} +0 -0
  297. /package/spec-files/{agentic_orchestrator_mcp_formalization_spec.md → completed/agentic_orchestrator_mcp_formalization_spec.md} +0 -0
  298. /package/spec-files/{agentic_orchestrator_oop_refactor_spec.md → completed/agentic_orchestrator_oop_refactor_spec.md} +0 -0
  299. /package/spec-files/{agentic_orchestrator_single_global_orchestrator_spec.md → completed/agentic_orchestrator_single_global_orchestrator_spec.md} +0 -0
  300. /package/spec-files/{agentic_orchestrator_spec.md → completed/agentic_orchestrator_spec.md} +0 -0
@@ -0,0 +1,262 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { TOOLS } from '../../core/constants.js';
4
+ import type { FeatureStatePayload, ToolCaller as SupervisorToolCaller } from '../../core/tool-caller.js';
5
+ import type { NotifierService } from './notifier-service.js';
6
+
7
+ const execFileAsync = promisify(execFile);
8
+
9
+ export type CiStatus = 'passing' | 'failing' | 'pending' | 'none';
10
+ export type ReviewDecision = 'approved' | 'changes_requested' | 'pending' | 'none';
11
+
12
+ export interface PrInfo {
13
+ number: number;
14
+ url: string;
15
+ ci_status: CiStatus;
16
+ review_decision: ReviewDecision;
17
+ merge_ready: boolean;
18
+ pending_review_threads: number;
19
+ has_conflicts: boolean;
20
+ merge_score: number;
21
+ }
22
+
23
+ export interface ChangesRequestedPolicy {
24
+ enabled: boolean;
25
+ action: string;
26
+ escalate_after: number;
27
+ }
28
+
29
+ export type GhRunner = (args: string[]) => Promise<{ stdout: string; exitCode: number }>;
30
+
31
+ export function createGhRunner(fn?: GhRunner): GhRunner {
32
+ if (fn !== undefined) {
33
+ return fn;
34
+ }
35
+ return async (args: string[]) => {
36
+ try {
37
+ const { stdout } = await execFileAsync('gh', args, { timeout: 15_000 });
38
+ return { stdout, exitCode: 0 };
39
+ } catch (err: unknown) {
40
+ const e = err as Record<string, unknown>;
41
+ if (e['code'] === 'ENOENT' || e['code'] === 127) {
42
+ return { stdout: '', exitCode: 127 };
43
+ }
44
+ const exitCode = typeof e['code'] === 'number' ? e['code'] : 1;
45
+ return { stdout: '', exitCode };
46
+ }
47
+ };
48
+ }
49
+
50
+ export interface PrMonitorServiceDependencies {
51
+ toolCaller: SupervisorToolCaller;
52
+ notifier?: NotifierService;
53
+ reactionsPolicy?: { changes_requested?: ChangesRequestedPolicy };
54
+ ghRunner?: GhRunner;
55
+ }
56
+
57
+ interface GhPrViewResult {
58
+ number: number;
59
+ url: string;
60
+ statusCheckRollup: Array<{ state: string }> | null;
61
+ reviewDecision: string | null;
62
+ mergeable: string;
63
+ reviewThreads: Array<{ isResolved: boolean }> | null;
64
+ }
65
+
66
+ interface GhReviewsResult {
67
+ reviews: Array<{ body: string; state: string }>;
68
+ }
69
+
70
+ function mapCiStatus(checks: Array<{ state: string }> | null | undefined): CiStatus {
71
+ if (!checks || checks.length === 0) {
72
+ return 'none';
73
+ }
74
+ const upper = checks.map((c) => c.state.toUpperCase());
75
+ if (upper.some((s) => s === 'FAILURE' || s === 'ERROR')) {
76
+ return 'failing';
77
+ }
78
+ if (upper.some((s) => s === 'PENDING' || s === 'IN_PROGRESS' || s === 'EXPECTED' || s === 'WAITING')) {
79
+ return 'pending';
80
+ }
81
+ return 'passing';
82
+ }
83
+
84
+ function mapReviewDecision(decision: string | null | undefined): ReviewDecision {
85
+ if (!decision) {
86
+ return 'none';
87
+ }
88
+ switch (decision.toUpperCase()) {
89
+ case 'APPROVED':
90
+ return 'approved';
91
+ case 'CHANGES_REQUESTED':
92
+ return 'changes_requested';
93
+ case 'REVIEW_REQUIRED':
94
+ return 'pending';
95
+ default:
96
+ return 'none';
97
+ }
98
+ }
99
+
100
+ export class PrMonitorService {
101
+ private readonly toolCaller: SupervisorToolCaller;
102
+ private readonly notifier: NotifierService | undefined;
103
+ private readonly reactionsPolicy: { changes_requested?: ChangesRequestedPolicy } | undefined;
104
+ private readonly ghRunner: GhRunner;
105
+
106
+ constructor(dependencies: PrMonitorServiceDependencies) {
107
+ this.toolCaller = dependencies.toolCaller;
108
+ this.notifier = dependencies.notifier;
109
+ this.reactionsPolicy = dependencies.reactionsPolicy;
110
+ this.ghRunner = dependencies.ghRunner ?? createGhRunner();
111
+ }
112
+
113
+ async detectPr(featureId: string, branch: string): Promise<PrInfo | null> {
114
+ const candidates = [...new Set([branch, featureId, `feature/${featureId}`].map((value) => value.trim()).filter(Boolean))];
115
+ let parsed: GhPrViewResult | null = null;
116
+ for (const candidate of candidates) {
117
+ const result = await this.ghRunner([
118
+ 'pr', 'view', '--head', candidate,
119
+ '--json', 'number,url,statusCheckRollup,reviewDecision,mergeable,reviewThreads'
120
+ ]);
121
+
122
+ if (result.exitCode !== 0 || !result.stdout.trim()) {
123
+ continue;
124
+ }
125
+
126
+ try {
127
+ const maybeParsed = JSON.parse(result.stdout) as GhPrViewResult;
128
+ if (maybeParsed.number) {
129
+ parsed = maybeParsed;
130
+ break;
131
+ }
132
+ } catch {
133
+ // Try the next candidate branch.
134
+ }
135
+ }
136
+
137
+ if (!parsed?.number) {
138
+ return null;
139
+ }
140
+
141
+ const ci_status = mapCiStatus(parsed.statusCheckRollup);
142
+ const review_decision = mapReviewDecision(parsed.reviewDecision);
143
+ const has_conflicts = parsed.mergeable === 'CONFLICTING';
144
+ const merge_ready =
145
+ parsed.mergeable === 'MERGEABLE' && ci_status === 'passing' && review_decision === 'approved';
146
+ const pending_review_threads = parsed.reviewThreads?.filter((t) => !t.isResolved).length ?? 0;
147
+
148
+ const partial: Omit<PrInfo, 'merge_score'> = {
149
+ number: parsed.number,
150
+ url: parsed.url,
151
+ ci_status,
152
+ review_decision,
153
+ merge_ready,
154
+ pending_review_threads,
155
+ has_conflicts
156
+ };
157
+ const merge_score = this.computeMergeScore({ ...partial, merge_score: 0 });
158
+ return { ...partial, merge_score };
159
+ }
160
+
161
+ async storePrState(featureId: string, prInfo: PrInfo): Promise<void> {
162
+ const current = await this.toolCaller.callTool<FeatureStatePayload>(
163
+ 'orchestrator',
164
+ TOOLS.FEATURE_STATE_GET,
165
+ { feature_id: featureId }
166
+ );
167
+
168
+ await this.toolCaller.callTool('orchestrator', TOOLS.FEATURE_STATE_PATCH, {
169
+ feature_id: featureId,
170
+ expected_version: current.data.front_matter.version,
171
+ patch: {
172
+ front_matter: {
173
+ pr: {
174
+ number: prInfo.number,
175
+ url: prInfo.url,
176
+ ci_status: prInfo.ci_status,
177
+ review_decision: prInfo.review_decision,
178
+ merge_ready: prInfo.merge_ready,
179
+ pending_review_threads: prInfo.pending_review_threads,
180
+ has_conflicts: prInfo.has_conflicts,
181
+ merge_score: prInfo.merge_score
182
+ }
183
+ }
184
+ }
185
+ });
186
+ }
187
+
188
+ computeMergeScore(prInfo: PrInfo): number {
189
+ let score = 0;
190
+ if (prInfo.ci_status === 'passing') {
191
+ score += 40;
192
+ }
193
+ if (prInfo.review_decision === 'approved') {
194
+ score += 40;
195
+ }
196
+ if (!prInfo.has_conflicts) {
197
+ score += 15;
198
+ }
199
+ if (prInfo.pending_review_threads === 0) {
200
+ score += 5;
201
+ }
202
+ return score;
203
+ }
204
+
205
+ async handleChangesRequested(featureId: string, prInfo: PrInfo): Promise<void> {
206
+ const reaction = this.reactionsPolicy?.changes_requested;
207
+ if (!reaction?.enabled) {
208
+ return;
209
+ }
210
+ if (reaction.action !== 'send_review_context_to_agent') {
211
+ return;
212
+ }
213
+
214
+ const reviewResult = await this.ghRunner(['pr', 'view', String(prInfo.number), '--json', 'reviews']);
215
+
216
+ let reviewBody = '';
217
+ if (reviewResult.exitCode === 0 && reviewResult.stdout.trim()) {
218
+ try {
219
+ const parsed = JSON.parse(reviewResult.stdout) as GhReviewsResult;
220
+ const lastReview = parsed.reviews?.[parsed.reviews.length - 1];
221
+ reviewBody = lastReview?.body ?? '';
222
+ } catch {
223
+ // ignore parse errors
224
+ }
225
+ }
226
+
227
+ await this.toolCaller.callTool('orchestrator', TOOLS.FEATURE_LOG_APPEND, {
228
+ feature_id: featureId,
229
+ note: `PR #${prInfo.number} changes requested. Review context:\n${reviewBody}`
230
+ });
231
+
232
+ await this.toolCaller
233
+ .callTool('orchestrator', TOOLS.FEATURE_SEND_MESSAGE, {
234
+ feature_id: featureId,
235
+ message: `PR review requested changes:\n${reviewBody || '(no review body provided)'}`,
236
+ })
237
+ .catch(() => undefined);
238
+
239
+ if (this.notifier) {
240
+ await this.notifier.notify('changes_requested', {
241
+ feature_id: featureId,
242
+ message: `PR #${prInfo.number} has changes requested for feature '${featureId}'.`,
243
+ details: { pr_number: prInfo.number, pr_url: prInfo.url }
244
+ });
245
+ }
246
+ }
247
+
248
+ async checkAndUpdate(featureId: string, branch: string): Promise<PrInfo | null> {
249
+ const prInfo = await this.detectPr(featureId, branch);
250
+ if (!prInfo) {
251
+ return null;
252
+ }
253
+
254
+ await this.storePrState(featureId, prInfo);
255
+
256
+ if (prInfo.review_decision === 'changes_requested') {
257
+ await this.handleChangesRequested(featureId, prInfo);
258
+ }
259
+
260
+ return prInfo;
261
+ }
262
+ }
@@ -0,0 +1,175 @@
1
+ import { TOOLS } from '../../core/constants.js';
2
+ import type { FeatureStatePayload, ToolCaller as SupervisorToolCaller } from '../../core/tool-caller.js';
3
+ import type { NotifierService } from './notifier-service.js';
4
+
5
+ export interface GateFailedReaction {
6
+ enabled: boolean;
7
+ max_retries: number;
8
+ action: 'retry_with_agent_repair' | 'notify_only';
9
+ escalate_after: number;
10
+ retry_delay_ms: number;
11
+ }
12
+
13
+ export interface ReactionsPolicy {
14
+ gate_failed?: GateFailedReaction;
15
+ agent_stuck?: { enabled: boolean; action: string; idle_threshold_ms: number; escalate_after: number };
16
+ collision_detected?: { enabled: boolean; action: string };
17
+ ready_to_merge?: { enabled: boolean; action: string };
18
+ changes_requested?: { enabled: boolean; action: string; escalate_after: number };
19
+ }
20
+
21
+ export interface GateRepairContext {
22
+ featureId: string;
23
+ gateName: string;
24
+ exitCode: number;
25
+ logs: string;
26
+ evidenceSummary: string;
27
+ retryCount: number;
28
+ failureHistory?: Array<{
29
+ attempt: number;
30
+ gate_name: string;
31
+ exit_code: number;
32
+ evidence_summary: string;
33
+ logs_excerpt: string;
34
+ failed_at: string;
35
+ retry_delay_ms?: number;
36
+ }>;
37
+ }
38
+
39
+ export interface ReactionsServiceDependencies {
40
+ toolCaller: SupervisorToolCaller;
41
+ notifier?: NotifierService;
42
+ policy?: ReactionsPolicy;
43
+ }
44
+
45
+ const DEFAULT_GATE_FAILED_REACTION: GateFailedReaction = {
46
+ enabled: false,
47
+ max_retries: 2,
48
+ action: 'notify_only',
49
+ escalate_after: 2,
50
+ retry_delay_ms: 30000
51
+ };
52
+
53
+ export class ReactionsService {
54
+ private readonly toolCaller: SupervisorToolCaller;
55
+ private readonly notifier: NotifierService | undefined;
56
+ private readonly policy: ReactionsPolicy;
57
+
58
+ constructor(dependencies: ReactionsServiceDependencies) {
59
+ this.toolCaller = dependencies.toolCaller;
60
+ this.notifier = dependencies.notifier;
61
+ this.policy = dependencies.policy ?? {};
62
+ }
63
+
64
+ shouldRetry(_featureId: string, retryCount: number): boolean {
65
+ const reaction = this.policy.gate_failed ?? DEFAULT_GATE_FAILED_REACTION;
66
+ if (!reaction.enabled) {
67
+ return false;
68
+ }
69
+ if (reaction.action !== 'retry_with_agent_repair') {
70
+ return false;
71
+ }
72
+ return retryCount < reaction.max_retries;
73
+ }
74
+
75
+ retryDelayMs(): number {
76
+ const reaction = this.policy.gate_failed ?? DEFAULT_GATE_FAILED_REACTION;
77
+ if (typeof reaction.retry_delay_ms === 'number' && Number.isFinite(reaction.retry_delay_ms) && reaction.retry_delay_ms >= 0) {
78
+ return Math.floor(reaction.retry_delay_ms);
79
+ }
80
+ return DEFAULT_GATE_FAILED_REACTION.retry_delay_ms;
81
+ }
82
+
83
+ maxRetries(): number {
84
+ const reaction = this.policy.gate_failed ?? DEFAULT_GATE_FAILED_REACTION;
85
+ if (typeof reaction.max_retries === 'number' && Number.isFinite(reaction.max_retries) && reaction.max_retries >= 0) {
86
+ return Math.floor(reaction.max_retries);
87
+ }
88
+ return DEFAULT_GATE_FAILED_REACTION.max_retries;
89
+ }
90
+
91
+ async waitBeforeRetry(): Promise<void> {
92
+ const delayMs = this.retryDelayMs();
93
+ if (delayMs <= 0) {
94
+ return;
95
+ }
96
+ await new Promise<void>((resolve) => setTimeout(resolve, delayMs));
97
+ }
98
+
99
+ buildRepairPrompt(ctx: GateRepairContext): string {
100
+ const maxRetries = this.maxRetries();
101
+ const delayMs = this.retryDelayMs();
102
+ const failureSummary =
103
+ Array.isArray(ctx.failureHistory) && ctx.failureHistory.length > 0
104
+ ? ctx.failureHistory
105
+ .slice(-3)
106
+ .map(
107
+ (item) =>
108
+ `- attempt=${item.attempt}; gate=${item.gate_name}; exit=${item.exit_code}; at=${item.failed_at}; evidence=${item.evidence_summary || 'n/a'}`
109
+ )
110
+ .join('\n')
111
+ : '(none)';
112
+ return [
113
+ 'Gate execution failed. Review the error logs below and apply fixes.',
114
+ '',
115
+ `Gate: ${ctx.gateName}`,
116
+ `Exit Code: ${ctx.exitCode}`,
117
+ '',
118
+ 'Logs:',
119
+ ctx.logs,
120
+ '',
121
+ 'Evidence:',
122
+ ctx.evidenceSummary,
123
+ '',
124
+ `Retry: ${ctx.retryCount + 1} of ${maxRetries}`
125
+ ,
126
+ `Retry delay (ms): ${delayMs}`,
127
+ '',
128
+ 'Failure history (most recent first):',
129
+ failureSummary
130
+ ].join('\n');
131
+ }
132
+
133
+ async recordRetry(featureId: string, retryCount: number): Promise<void> {
134
+ const current = await this.toolCaller.callTool<FeatureStatePayload>(
135
+ 'orchestrator',
136
+ TOOLS.FEATURE_STATE_GET,
137
+ { feature_id: featureId }
138
+ );
139
+ await this.toolCaller.callTool('orchestrator', TOOLS.FEATURE_STATE_PATCH, {
140
+ feature_id: featureId,
141
+ expected_version: current.data.front_matter.version,
142
+ patch: {
143
+ front_matter: {
144
+ gate_retry_count: retryCount,
145
+ last_retry_at: new Date().toISOString()
146
+ }
147
+ }
148
+ });
149
+ }
150
+
151
+ shouldEscalate(retryCount: number): boolean {
152
+ const reaction = this.policy.gate_failed ?? DEFAULT_GATE_FAILED_REACTION;
153
+ return retryCount >= reaction.escalate_after;
154
+ }
155
+
156
+ async notifyEscalation(featureId: string, ctx: GateRepairContext): Promise<void> {
157
+ if (!this.notifier) {
158
+ return;
159
+ }
160
+ await this.notifier.notify('gate_failed', {
161
+ feature_id: featureId,
162
+ message: `Gate '${ctx.gateName}' failed after ${ctx.retryCount} retries on feature '${featureId}'.`,
163
+ details: {
164
+ gate_name: ctx.gateName,
165
+ exit_code: ctx.exitCode,
166
+ retry_count: ctx.retryCount,
167
+ retry_delay_ms: this.retryDelayMs(),
168
+ max_retries: this.maxRetries(),
169
+ escalate_after: (this.policy.gate_failed ?? DEFAULT_GATE_FAILED_REACTION).escalate_after,
170
+ last_failure: ctx.failureHistory?.at(-1) ?? null,
171
+ failure_history: ctx.failureHistory ?? []
172
+ }
173
+ });
174
+ }
175
+ }
@@ -1,5 +1,5 @@
1
1
  import { detectPlanCollisions } from '../../core/collisions.js';
2
- import { pathExists, stableHash } from '../../core/fs.js';
2
+ import { pathExists, readJson, stableHash } from '../../core/fs.js';
3
3
 
4
4
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
5
  type AnyRecord = Record<string, any>;
@@ -23,6 +23,7 @@ export interface ReportingServicePort {
23
23
  getPolicySnapshot(): AnyRecord;
24
24
  readIndex(): Promise<AnyRecord>;
25
25
  statePath(featureId: string): string;
26
+ featureCostPath(featureId: string): string;
26
27
  readState(featureId: string): Promise<{ frontMatter: AnyRecord }>;
27
28
  featureStateGet(featureId: string): Promise<{ data: { front_matter: AnyRecord } }>;
28
29
  repoDiffBundle(featureId: string): Promise<{ data: AnyRecord }>;
@@ -83,13 +84,27 @@ export class ReportingService {
83
84
  continue;
84
85
  }
85
86
  const state = await this.port.readState(featureId);
87
+ const costData = await readJson<{ estimated_cost_usd: number; tokens_used: number }>(
88
+ this.port.featureCostPath(featureId), null
89
+ );
86
90
  features.push({
87
91
  feature_id: featureId,
88
92
  status: state.frontMatter.status,
93
+ branch:
94
+ typeof state.frontMatter.worktree_branch === 'string'
95
+ ? state.frontMatter.worktree_branch
96
+ : typeof state.frontMatter.branch === 'string'
97
+ ? state.frontMatter.branch
98
+ : null,
89
99
  locks: readHeldLocks(state.frontMatter),
90
100
  gate_profile: state.frontMatter.gate_profile,
91
101
  gates: state.frontMatter.gates,
92
- last_updated: state.frontMatter.last_updated
102
+ pr: state.frontMatter.pr ?? null,
103
+ last_updated: state.frontMatter.last_updated,
104
+ activity_state: state.frontMatter.activity_state,
105
+ activity_last_event_at: state.frontMatter.activity_last_event_at,
106
+ activity_detected_via: state.frontMatter.activity_detected_via,
107
+ cost: costData ? { estimated_cost_usd: costData.estimated_cost_usd, tokens_used: costData.tokens_used } : null
93
108
  });
94
109
  }
95
110
 
@@ -36,6 +36,8 @@ export interface RunLeaseServicePort {
36
36
  withIndexLock<T>(operation: () => Promise<T>): Promise<T>;
37
37
  readIndex(): Promise<IndexSnapshot>;
38
38
  writeIndex(index: IndexSnapshot): Promise<void>;
39
+ readRunLease(): Promise<RuntimeSessionsSnapshot>;
40
+ writeRunLease(data: RuntimeSessionsSnapshot): Promise<void>;
39
41
  normalizeRuntimeSessions(value: unknown, at?: string): RuntimeSessionsSnapshot;
40
42
  isRunLeaseFresh(runtimeSessions: RuntimeSessionsSnapshot): boolean;
41
43
  emptyRuntimeSessions(at?: string): RuntimeSessionsSnapshot;
@@ -71,8 +73,7 @@ export class RunLeaseService {
71
73
  : stableHash('none');
72
74
 
73
75
  return await this.port.withIndexLock(async () => {
74
- const index = await this.port.readIndex();
75
- const current = this.port.normalizeRuntimeSessions(index.runtime_sessions);
76
+ const current = this.port.normalizeRuntimeSessions(await this.port.readRunLease());
76
77
  const hasLease = current.run_id !== 'none' && current.owner_instance_id !== 'none';
77
78
  const fresh = hasLease && this.port.isRunLeaseFresh(current);
78
79
  const sameOwner = current.run_id === input.run_id && current.owner_instance_id === input.owner_instance_id;
@@ -126,10 +127,7 @@ export class RunLeaseService {
126
127
  claimed.feature_sessions = {};
127
128
  }
128
129
 
129
- index.runtime_sessions = claimed;
130
- index.version += 1;
131
- index.updated_at = nowIso();
132
- await this.port.writeIndex(index);
130
+ await this.port.writeRunLease(claimed);
133
131
 
134
132
  return {
135
133
  data: {
@@ -153,8 +151,7 @@ export class RunLeaseService {
153
151
 
154
152
  const ttlMs = this.port.runLeaseTtlSeconds() * 1000;
155
153
  return await this.port.withIndexLock(async () => {
156
- const index = await this.port.readIndex();
157
- const current = this.port.normalizeRuntimeSessions(index.runtime_sessions);
154
+ const current = this.port.normalizeRuntimeSessions(await this.port.readRunLease());
158
155
  if (current.run_id !== runId || current.owner_instance_id !== ownerInstanceId) {
159
156
  throw {
160
157
  normalizedResponse: fail(ERROR_CODES.RUN_LEASE_NOT_OWNED, 'Cannot renew a run lease not owned by this instance', {
@@ -173,10 +170,7 @@ export class RunLeaseService {
173
170
  last_heartbeat_at: nowIso(),
174
171
  lease_expires_at: new Date(Date.now() + ttlMs).toISOString()
175
172
  };
176
- index.runtime_sessions = updated;
177
- index.version += 1;
178
- index.updated_at = nowIso();
179
- await this.port.writeIndex(index);
173
+ await this.port.writeRunLease(updated);
180
174
 
181
175
  return {
182
176
  data: {
@@ -188,16 +182,12 @@ export class RunLeaseService {
188
182
 
189
183
  async releaseRunLease(runId: string, ownerInstanceId: string): Promise<{ data: { released: boolean } }> {
190
184
  return await this.port.withIndexLock(async () => {
191
- const index = await this.port.readIndex();
192
- const current = this.port.normalizeRuntimeSessions(index.runtime_sessions);
185
+ const current = this.port.normalizeRuntimeSessions(await this.port.readRunLease());
193
186
  if (current.run_id !== runId || current.owner_instance_id !== ownerInstanceId) {
194
187
  return { data: { released: false } };
195
188
  }
196
189
 
197
- index.runtime_sessions = this.port.emptyRuntimeSessions(nowIso());
198
- index.version += 1;
199
- index.updated_at = nowIso();
200
- await this.port.writeIndex(index);
190
+ await this.port.writeRunLease(this.port.emptyRuntimeSessions(nowIso()));
201
191
  return { data: { released: true } };
202
192
  });
203
193
  }
@@ -219,8 +209,7 @@ export class RunLeaseService {
219
209
  }
220
210
 
221
211
  return await this.port.withIndexLock(async () => {
222
- const index = await this.port.readIndex();
223
- const current = this.port.normalizeRuntimeSessions(index.runtime_sessions);
212
+ const current = this.port.normalizeRuntimeSessions(await this.port.readRunLease());
224
213
  if (current.run_id !== run_id || current.owner_instance_id !== owner_instance_id) {
225
214
  throw {
226
215
  normalizedResponse: fail(ERROR_CODES.RUN_LEASE_NOT_OWNED, 'Cannot update orchestrator session without run ownership', {
@@ -242,10 +231,7 @@ export class RunLeaseService {
242
231
  orchestrator_session_id,
243
232
  orchestrator_epoch: nextEpoch
244
233
  };
245
- index.runtime_sessions = updated;
246
- index.version += 1;
247
- index.updated_at = nowIso();
248
- await this.port.writeIndex(index);
234
+ await this.port.writeRunLease(updated);
249
235
 
250
236
  return {
251
237
  data: {
@@ -259,8 +245,7 @@ export class RunLeaseService {
259
245
  const { run_id, owner_instance_id, feature_id, planner_session_id, builder_session_id, qa_session_id } = params;
260
246
 
261
247
  return await this.port.withIndexLock(async () => {
262
- const index = await this.port.readIndex();
263
- const current = this.port.normalizeRuntimeSessions(index.runtime_sessions);
248
+ const current = this.port.normalizeRuntimeSessions(await this.port.readRunLease());
264
249
  if (current.run_id !== run_id || current.owner_instance_id !== owner_instance_id) {
265
250
  throw {
266
251
  normalizedResponse: fail(ERROR_CODES.RUN_LEASE_NOT_OWNED, 'Cannot update feature sessions without run ownership', {
@@ -283,13 +268,10 @@ export class RunLeaseService {
283
268
  }
284
269
  };
285
270
 
286
- index.runtime_sessions = {
271
+ await this.port.writeRunLease({
287
272
  ...current,
288
273
  feature_sessions: nextFeatureSessions
289
- };
290
- index.version += 1;
291
- index.updated_at = nowIso();
292
- await this.port.writeIndex(index);
274
+ });
293
275
  return { data: { updated: true } };
294
276
  });
295
277
  }
@@ -297,8 +279,7 @@ export class RunLeaseService {
297
279
  async pruneFeatureSessionAssignments(params: PruneFeatureSessionParams): Promise<{ data: { removed: string[] } }> {
298
280
  const active = new Set(params.active_feature_ids);
299
281
  return await this.port.withIndexLock(async () => {
300
- const index = await this.port.readIndex();
301
- const current = this.port.normalizeRuntimeSessions(index.runtime_sessions);
282
+ const current = this.port.normalizeRuntimeSessions(await this.port.readRunLease());
302
283
  if (current.run_id !== params.run_id || current.owner_instance_id !== params.owner_instance_id) {
303
284
  throw {
304
285
  normalizedResponse: fail(ERROR_CODES.RUN_LEASE_NOT_OWNED, 'Cannot prune feature sessions without run ownership', {
@@ -324,13 +305,10 @@ export class RunLeaseService {
324
305
  }
325
306
 
326
307
  if (removed.length > 0) {
327
- index.runtime_sessions = {
308
+ await this.port.writeRunLease({
328
309
  ...current,
329
310
  feature_sessions: kept
330
- };
331
- index.version += 1;
332
- index.updated_at = nowIso();
333
- await this.port.writeIndex(index);
311
+ });
334
312
  }
335
313
 
336
314
  removed.sort((a, b) => a.localeCompare(b));
@@ -18,7 +18,10 @@ const MUTATING_TOOLS = [
18
18
  TOOLS.LOCKS_ACQUIRE,
19
19
  TOOLS.LOCKS_RELEASE,
20
20
  TOOLS.FEATURE_READY_TO_MERGE,
21
- TOOLS.FEATURE_DELETE
21
+ TOOLS.FEATURE_DELETE,
22
+ TOOLS.FEATURE_SEND_MESSAGE,
23
+ TOOLS.COST_RECORD,
24
+ TOOLS.PERFORMANCE_RECORD_OUTCOME
22
25
  ] as const;
23
26
 
24
27
  export const TOOL_BEHAVIOR_METADATA: Readonly<Record<string, ToolBehaviorMetadata>> = Object.freeze(
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S tsx
1
+ #!/usr/bin/env node
2
2
  import { runCli } from '../interfaces/cli/bootstrap.js';
3
3
  import type { RuntimeContext } from './types.js';
4
4