@overdeck/desktop 0.45.22 → 0.45.24

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 (429) hide show
  1. package/drizzle/overdeck/0000_overdeck_init.sql +2 -0
  2. package/package.json +1 -1
  3. package/server/{acceptance-criteria-BH7ac_1G.js → acceptance-criteria-DDCnXF58.js} +5 -5
  4. package/server/{advancing-selfheal-cSvXNZLZ.js → advancing-selfheal-BVwW4qRn.js} +6 -6
  5. package/server/{agent-gc-Dj2gncak.js → agent-gc-CHcnvonI.js} +4 -4
  6. package/server/agent-state-Bc3tzUC8.js +3 -0
  7. package/server/{agent-state-service-DistflNn.js → agent-state-service-BsgWxZuB.js} +5 -5
  8. package/server/{agent-state-service-CsQ07Pxe.js → agent-state-service-D5UGKIJL.js} +1 -1
  9. package/server/{agents-DXdHmRTC.js → agents-EbI0zCJl.js} +4 -4
  10. package/server/agents-wi9NWzs2.js +3 -0
  11. package/server/{archive-planning-Cp_DC1j-.js → archive-planning-BZDgzoEj.js} +1 -1
  12. package/server/{archive-planning-BvDuWNMo.js → archive-planning-Du25Jtst.js} +30 -6
  13. package/server/{artifact-index-jobs-CDBFL4OU.js → artifact-index-jobs-CbEDfCDu.js} +2 -2
  14. package/server/assemble-J2ZZ2C15.js +339 -0
  15. package/server/{backlog-auto-trigger-DmA1IbYf.js → backlog-auto-trigger-2R2jog7n.js} +3 -3
  16. package/server/{behavior-CGj16clS.js → behavior-B3exJ42O.js} +2 -2
  17. package/server/{briefing-freshness-C12WgkaB.js → briefing-freshness-D1wzUIWv.js} +2 -2
  18. package/server/{build-info-BRpqQvNd.js → build-info-CZk_TITh.js} +1 -1
  19. package/server/{build-info-NU3KbXYw.js → build-info-Ie_D0_BT.js} +3 -3
  20. package/server/{checkpoint-manager-CTPjAmtH.js → checkpoint-manager-BI1Wxoku.js} +2 -2
  21. package/server/{checkpoint-manager-rOgT98WY.js → checkpoint-manager-DwbQoqrC.js} +1 -1
  22. package/server/checkpoint-worker.js +1 -1
  23. package/server/{checkpoints-B5jFNi2G.js → checkpoints-BJIs5QIA.js} +1 -1
  24. package/server/{checkpoints-BJum14fs.js → checkpoints-CQB1h765.js} +2 -2
  25. package/server/{claude-hooks-provision-BhwWK-TQ.js → claude-hooks-provision-8Ngo8TpT.js} +1 -1
  26. package/server/{claude-hooks-provision-D7p5WcKZ.js → claude-hooks-provision-D8GXF3_s.js} +2 -2
  27. package/server/{claude-permissions-nSFKH78M.js → claude-permissions-DNWEVV6z.js} +2 -2
  28. package/server/{claude-settings-overlay-BI_UIke0.js → claude-settings-overlay-CEdaqpWI.js} +2 -2
  29. package/server/{claude-settings-overlay-DHBwjS1X.js → claude-settings-overlay-DWy-Bb4Y.js} +1 -1
  30. package/server/cli-B86rejVV.js +3 -0
  31. package/server/{cli-CbKNkFE5.js → cli-RoOx5DUf.js} +9 -9
  32. package/server/{cliproxy-Brf_5MuG.js → cliproxy-C8a1vtTU.js} +2 -2
  33. package/server/{cliproxy-bONZDBbC.js → cliproxy-fHfIeiGd.js} +1 -1
  34. package/server/{close-issue-CxReRxIc.js → close-issue-DoOifFIK.js} +5 -5
  35. package/server/{closed-issue-reaper-CQ3kqha7.js → closed-issue-reaper-Bk3SU4RC.js} +9 -9
  36. package/server/{closed-issue-reaper-DXP2Xyv3.js → closed-issue-reaper-CktPXo8m.js} +1 -1
  37. package/server/{codex-CbmztVcc.js → codex-BZXGhUVB.js} +27 -8
  38. package/server/codex-GcHuUF-k.js +3 -0
  39. package/server/{codex-parser-jl8AeAmB.js → codex-parser-CFZ1_1Jl.js} +2 -2
  40. package/server/{config-q4Gvd8dq.js → config-BZ0sHTju.js} +2 -2
  41. package/server/{config-DeTsoZdR.js → config-CKxYiCmX.js} +2 -2
  42. package/server/{config-BJKocXKd.js → config-DzV30TOm.js} +1 -1
  43. package/server/{config-yaml-ig8Nwtiw.js → config-yaml-DySPNmKN.js} +77 -194
  44. package/server/{conflict-gate-CFqPVxEg.js → conflict-gate-BaCVrH-N.js} +3 -3
  45. package/server/{conflict-gate-CHU9K1vU.js → conflict-gate-BhAY7TUd.js} +1 -1
  46. package/server/context-layers-BrPmE__h.js +4 -0
  47. package/server/context-layers-igZ4FI4_.js +37 -0
  48. package/server/{control-settings-D_odMIaC.js → control-settings-BQp7_4uD.js} +4 -4
  49. package/server/{conversation-compaction-B6YSRLda.js → conversation-compaction-CgrR8YYp.js} +8 -8
  50. package/server/{conversation-compaction-CN4-5YYt.js → conversation-compaction-eQB5CkOe.js} +1 -1
  51. package/server/{conversation-service-Dq33SYEj.js → conversation-service-BLd3MYsG.js} +1 -1
  52. package/server/{conversation-service-C3avRVEr.js → conversation-service-DPJ71F33.js} +329 -19
  53. package/server/{conversations-bYpM9-kZ.js → conversations-6AfUfbwc.js} +5 -5
  54. package/server/{conversations-H9if0bCQ.js → conversations-CAsJF9kh.js} +1 -1
  55. package/server/{cost-Codk1pc2.js → cost-B2ddP6wq.js} +18 -2
  56. package/server/{cost-wH46-WsJ.js → cost-R-m6Yi-W.js} +3 -3
  57. package/server/{cost-sync-D_cre6WM.js → cost-sync-BsN6ER0n.js} +3 -3
  58. package/server/{dag-CJpgf9A3.js → dag-B1ucnjDU.js} +9 -9
  59. package/server/{dangerous-git-ops-D6AF-Hg_.js → dangerous-git-ops-BXqeWVSf.js} +1 -1
  60. package/server/dashboard-db-worker.js +7 -7
  61. package/server/{deacon-CkT_kznS.js → deacon-CU_cAYSR.js} +406 -607
  62. package/server/{deacon-B8HbQdqU.js → deacon-kiQrb4ZT.js} +3 -3
  63. package/server/{deacon-nudge-log-D6K9K-rE.js → deacon-nudge-log-6wVqAaqz.js} +3 -3
  64. package/server/{deacon-swarm-BryRr6AO.js → deacon-swarm-BXZZ1Z2-.js} +17 -17
  65. package/server/deacon-swarm-C-GwfjJy.js +2 -0
  66. package/server/deacon.js +8 -8
  67. package/server/{deploy-patrol-1IrZxicY.js → deploy-patrol-AiFoP8dw.js} +136 -19
  68. package/server/{deploy-window-wDf4UgHt.js → deploy-window-C3F-tihF.js} +77 -10
  69. package/server/{deploy-window-7ZYHh7Zp.js → deploy-window-DkZraDyj.js} +1 -1
  70. package/server/{dev-supervisor-C7p9kEpc.js → dev-supervisor-bcOWqdQE.js} +2 -2
  71. package/server/{devcontainer-renderer-CNmQy0pL.js → devcontainer-renderer-_d6XhyND.js} +3 -3
  72. package/server/dist-Bzy0y69j.js +3 -0
  73. package/server/{dist-D-Ed5yVw.js → dist-Cb_0uvpS.js} +1 -1
  74. package/server/{docker-BCZ89M9X.js → docker-DcMTFTUC.js} +2 -2
  75. package/server/{dod-DbJLYcHv.js → dod-C6n9RV3G.js} +1 -1
  76. package/server/{ensure-devcontainer-Cs6W9Yzj.js → ensure-devcontainer-BZgOhRo7.js} +1 -1
  77. package/server/{ensure-devcontainer-V5JWaAva.js → ensure-devcontainer-DNi-ono3.js} +4 -4
  78. package/server/{esm-shims-dTeNdq7X.js → esm-shims-OSHAYaS1.js} +1 -1
  79. package/server/{event-store-TnWdydfB.js → event-store-CQL6NeqB.js} +3 -3
  80. package/server/{event-store-CpBGsO6_.js → event-store-EV2cMiXj.js} +1 -1
  81. package/server/{events-Dsuv6qyB.js → events-BTdrdEq8.js} +1 -1
  82. package/server/{events-DSUdR-04.js → events-DTfAe4My.js} +3 -3
  83. package/server/{factory-BMrK4TrE.js → factory-CFibJuaw.js} +50 -13
  84. package/server/{feature-registry-population-B-CI9hgE.js → feature-registry-population-DKZFMbFt.js} +4 -4
  85. package/server/{feature-registry-storage-B972ph_A.js → feature-registry-storage-BdPuLcBX.js} +2 -2
  86. package/server/{features-Bc-Bmiye.js → features-ez2271oH.js} +2 -2
  87. package/server/{feedback-target-DZ-iAnAa.js → feedback-target-7mqqwrX6.js} +7 -7
  88. package/server/{feedback-target-DS6NUuSs.js → feedback-target-CoFDHiM8.js} +1 -1
  89. package/server/{feedback-writer-BXP6g75N.js → feedback-writer-CY5L1lB7.js} +6 -5
  90. package/server/{feedback-writer-Bx5z11mF.js → feedback-writer-D3a22RCd.js} +1 -1
  91. package/server/flywheel-DHrgCphM.js +1084 -0
  92. package/server/flywheel-DzGtWnJ1.js +477 -0
  93. package/server/flywheel-SVF02Jpq.js +2 -0
  94. package/server/{flywheel-merge-order-CTgHeg-W.js → flywheel-merge-order-UU0tiUld.js} +10 -9
  95. package/server/{flywheel-merge-order-CNKAY489.js → flywheel-merge-order-UUcsCDb6.js} +1 -1
  96. package/server/{forge-B1S_KZv4.js → forge-CNFnyqps.js} +2 -2
  97. package/server/forge-DXnS_YFH.js +2 -0
  98. package/server/{from-DpwQ-sc3.js → from-kNxT0bZC.js} +1 -1
  99. package/server/{fts-db-DmAcuCkV.js → fts-db-CE46vDkZ.js} +2 -2
  100. package/server/{fts-operations-7d_RkmWp.js → fts-operations-CqY6zl-i.js} +2 -2
  101. package/server/{git-utils-Cc3FHDKd.js → git-utils-CRw2zLPC.js} +68 -3
  102. package/server/git-utils-D0_yhCgF.js +3 -0
  103. package/server/{github-app-CDPtGF7J.js → github-app-C3QrAUd0.js} +27 -8
  104. package/server/{github-app-CU14oYzP.js → github-app-ClZ2lk8D.js} +1 -1
  105. package/server/{github-client-XjTJbBm7.js → github-client-BnIsSrEN.js} +2 -2
  106. package/server/{github-client-CwBZZ3t7.js → github-client-DfQYVUaw.js} +1 -1
  107. package/server/{github-pr-lookup-CFGeVhqv.js → github-pr-lookup-D16NGsSO.js} +2 -2
  108. package/server/{harness-binary-B2CTcD84.js → harness-binary-DPUi-Pv8.js} +32 -6
  109. package/server/{harness-resolve-D5FVQTmy.js → harness-resolve-3lNZkyT4.js} +18 -9
  110. package/server/{health-events-DREeevle.js → health-events-BBcTYQKU.js} +2 -2
  111. package/server/{health-events-C1whmd58.js → health-events-BDOHby5Q.js} +1 -1
  112. package/server/{hooks-yxRsjk-z.js → hooks-CKZgXlJb.js} +2 -2
  113. package/server/{hooks-B-HItBiS.js → hooks-DPVX1rwQ.js} +1 -1
  114. package/server/{hume-Dz_OymXE.js → hume-Cz8igP9E.js} +2 -2
  115. package/server/hume-ccTf4wTX.js +3 -0
  116. package/server/{idle-stack-reaper-DrCv0OAG.js → idle-stack-reaper-CuLF0j2H.js} +1 -1
  117. package/server/{idle-stack-reaper-Yf34xXxY.js → idle-stack-reaper-DDIRwxUY.js} +2 -2
  118. package/server/{in-flight-guard-BoWm66Kh.js → in-flight-guard-CAAkqPLu.js} +2 -2
  119. package/server/{infra-D4iNWMA5.js → infra-BEqFxlJc.js} +14 -4
  120. package/server/{injection-D7T75Pye.js → injection-C8aiVJrF.js} +191 -183
  121. package/server/{injection-BVNa4MQW.js → injection-CPUIqNhI.js} +1 -1
  122. package/server/{inspect-agent-BXCgNeZ4.js → inspect-agent-BLuJ1AkT.js} +137 -81
  123. package/server/inspect-agent-Brtz0URj.js +2 -0
  124. package/server/{internal-token-C9DdEOKY.js → internal-token-BrcU6qfe.js} +1 -1
  125. package/server/{internal-token-CXsfxslp.js → internal-token-s0-oKybJ.js} +2 -2
  126. package/server/{io-DQR9PMgS.js → io-C9kF1wtJ.js} +160 -184
  127. package/server/{issue-closed-DtmJM0D0.js → issue-closed-CDJk4pIp.js} +7 -7
  128. package/server/{issue-lifecycle-8PSS9Anq.js → issue-lifecycle-B8sZHVHh.js} +1 -1
  129. package/server/{issue-lifecycle-CeyW_HoD.js → issue-lifecycle-ojZAzDBP.js} +22 -22
  130. package/server/{issue-service-singleton-DBZabEl7.js → issue-service-singleton-BvJ09SaR.js} +1 -1
  131. package/server/{issue-service-singleton-D_RVYBuL.js → issue-service-singleton-Yx5DdmVJ.js} +8 -8
  132. package/server/{issues-pDW9VUNz.js → issues-BMmP_77e.js} +2 -2
  133. package/server/{jsonl-parser-C3HVNR5d.js → jsonl-parser-BXSlHwEh.js} +3 -3
  134. package/server/{label-cleanup-DPwiZlWo.js → label-cleanup-B0UJwnv5.js} +3 -3
  135. package/server/{label-reconciler-Df1ZduPH.js → label-reconciler-B4iOAjlC.js} +4 -4
  136. package/server/{launcher-generator-COgR2By1.js → launcher-generator-BBWG_SiS.js} +105 -10
  137. package/server/{lifecycle-io-taIb0PoH.js → lifecycle-io-BREGpnLk.js} +37 -37
  138. package/server/lifecycle-io-DPo7x11I.js +3 -0
  139. package/server/{lifecycle-CtPDcZ9u.js → lifecycle-xmBfK2GJ.js} +6 -6
  140. package/server/lifecycle-yPd___5E.js +121 -0
  141. package/server/{linear-client-DsFkIxee.js → linear-client-BsuUvkdA.js} +2 -2
  142. package/server/{linear-client-YwZMDIwQ.js → linear-client-CTskEeT_.js} +1 -1
  143. package/server/memory-fts-worker.js +1 -1
  144. package/server/{merge-BwsyNRJV.js → merge-B1DiziGU.js} +1 -1
  145. package/server/{merge-C1t-bspg.js → merge-CefQNAnq.js} +3 -3
  146. package/server/{merge-agent-DDiIjzk-.js → merge-agent-4WOWlSYA.js} +28 -28
  147. package/server/{merge-agent-B-SnXOoc.js → merge-agent-CKkgIBPB.js} +1 -1
  148. package/server/{merge-batch-Byt-QnEl.js → merge-batch-ClFn2T-y.js} +1 -1
  149. package/server/merge-ops-CR44oU9-.js +2 -0
  150. package/server/{merge-rebase-Cm6fXaZN.js → merge-rebase-BfaTyXsw.js} +2 -2
  151. package/server/merge-set-DJ8-UErs.js +83 -0
  152. package/server/{merge-set-6LEglm6o.js → merge-set-bUqbm3yc.js} +1 -1
  153. package/server/{merge-sync-B95_4in_.js → merge-sync-BJYeOODr.js} +1 -1
  154. package/server/{merge-sync-Do8_q32h.js → merge-sync-VPV28g0g.js} +2 -2
  155. package/server/{merge-train-CKana8EH.js → merge-train-CHopycP0.js} +3 -3
  156. package/server/{merge-train-deps-BgmqwIfA.js → merge-train-deps-D1cOZzy2.js} +4 -4
  157. package/server/{merge-verification-CqnUzGEK.js → merge-verification-BkaOqPsw.js} +4 -4
  158. package/server/{multipart-parser-tdOQDJ3o.js → multipart-parser-duPAfSBw.js} +2 -2
  159. package/server/{no-resume-mode-BWOSZjWd.js → no-resume-mode-Nw29vaFb.js} +11 -8
  160. package/server/node_modules/.package-lock.json +8 -8
  161. package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/package.json +3 -3
  162. package/server/node_modules/undici/README.md +5 -0
  163. package/server/node_modules/undici/docs/docs/api/Client.md +4 -0
  164. package/server/node_modules/undici/docs/docs/api/Dispatcher.md +6 -0
  165. package/server/node_modules/undici/docs/docs/api/Interceptors.md +59 -0
  166. package/server/node_modules/undici/docs/docs/api/MockAgent.md +27 -0
  167. package/server/node_modules/undici/docs/docs/api/MockPool.md +28 -1
  168. package/server/node_modules/undici/docs/docs/index.md +5 -0
  169. package/server/node_modules/undici/lib/core/request.js +1 -1
  170. package/server/node_modules/undici/lib/core/util.js +6 -1
  171. package/server/node_modules/undici/lib/dispatcher/agent.js +3 -3
  172. package/server/node_modules/undici/lib/dispatcher/client-h1.js +31 -6
  173. package/server/node_modules/undici/lib/dispatcher/client-h2.js +34 -43
  174. package/server/node_modules/undici/lib/handler/cache-handler.js +93 -5
  175. package/server/node_modules/undici/lib/handler/cache-revalidation-handler.js +10 -0
  176. package/server/node_modules/undici/lib/handler/redirect-handler.js +5 -2
  177. package/server/node_modules/undici/lib/handler/retry-handler.js +6 -4
  178. package/server/node_modules/undici/lib/interceptor/cache.js +21 -4
  179. package/server/node_modules/undici/lib/interceptor/decompress.js +4 -0
  180. package/server/node_modules/undici/lib/mock/mock-interceptor.js +25 -7
  181. package/server/node_modules/undici/lib/mock/mock-utils.js +39 -10
  182. package/server/node_modules/undici/lib/util/cache.js +49 -7
  183. package/server/node_modules/undici/lib/web/fetch/body.js +2 -1
  184. package/server/node_modules/undici/lib/web/fetch/response.js +1 -3
  185. package/server/node_modules/undici/package.json +2 -2
  186. package/server/node_modules/undici/types/client.d.ts +2 -2
  187. package/server/node_modules/undici/types/dispatcher.d.ts +3 -3
  188. package/server/node_modules/undici/types/mock-interceptor.d.ts +5 -1
  189. package/server/{orphan-proposed-reconciler-xklgvNA7.js → orphan-proposed-reconciler-BvKkWMJQ.js} +15 -13
  190. package/server/{orphan-proposed-reconciler-DSvsJ5M7.js → orphan-proposed-reconciler-CTSGcK8p.js} +1 -1
  191. package/server/package-lock.json +9 -9
  192. package/server/package.json +1 -1
  193. package/server/{pan-dir-DOcf6ug3.js → pan-dir-B0c3w0Ya.js} +78 -58
  194. package/server/{paths-DooHpt9p.js → paths-D-xWGHdh.js} +2 -2
  195. package/server/{pickup-CaskQ7j6.js → pickup-DUCLWrJY.js} +17 -16
  196. package/server/{pipeline-membership-1LGRjo-k.js → pipeline-membership-mY3GprlK.js} +186 -67
  197. package/server/{pipeline-notifier-CZsrU_nj.js → pipeline-notifier-BJr8gQX6.js} +1 -1
  198. package/server/{pipeline-notifier-B6FVsYF9.js → pipeline-notifier-BXkJzDC4.js} +2 -2
  199. package/server/{project-pipelines-CtNG_rvs.js → project-pipelines-C0MWUeCU.js} +2 -2
  200. package/server/project-repos-B6GycDh9.js +94 -0
  201. package/server/project-repos-DXKQsc_m.js +3 -0
  202. package/server/projects-vjjcmiN8.js +3 -0
  203. package/server/{prompts-EvhMeeHG.js → prompts-DxzFXuLl.js} +2 -2
  204. package/server/{providers-BDDPULlp.js → providers-qQAwGQDW.js} +7 -7
  205. package/server/{providers-DNSQqy2V.js → providers-u4Ga0jOr.js} +1 -1
  206. package/server/public/assets/{chunk-BO2N2NFS-sym_ufXe.js → chunk-BO2N2NFS-BamJdinX.js} +4 -4
  207. package/server/public/assets/{diagram-OG6HWLK6-CQJPBNBu.js → diagram-OG6HWLK6-DfOB8-w4.js} +1 -1
  208. package/server/public/assets/{dist-DukGS2YZ.js → dist-ByIuzMQW.js} +3 -3
  209. package/server/public/assets/{dist-Bio7THQU.js → dist-D7zYSEA6.js} +1 -1
  210. package/server/public/assets/{ganttDiagram-6RSMTGT7-D4lWwte3.js → ganttDiagram-6RSMTGT7-EdmRyXL3.js} +1 -1
  211. package/server/public/assets/{highlighted-body-OFNGDK62-CaLboCt5.js → highlighted-body-OFNGDK62-MerOfTX8.js} +1 -1
  212. package/server/public/assets/{index-Bwejq-5s.js → index-CWeMerGO.js} +147 -144
  213. package/server/public/assets/index-CenuJ3Cw.css +1 -0
  214. package/server/public/assets/{linear-qUWmMZ-c.js → linear-BBbArIcK.js} +1 -1
  215. package/server/public/assets/mermaid-GHXKKRXX-CpPWAFwE.js +1 -0
  216. package/server/public/assets/{pieDiagram-4H26LBE5-C4zYulEm.js → pieDiagram-4H26LBE5-BKIddiJN.js} +1 -1
  217. package/server/public/assets/{prod-C8VtLuWC.js → prod-B8vYiu52.js} +4 -4
  218. package/server/public/assets/{quadrantDiagram-W4KKPZXB-qP04zyZC.js → quadrantDiagram-W4KKPZXB-D6RtoULk.js} +1 -1
  219. package/server/public/assets/{sankeyDiagram-5OEKKPKP-eMPTX_cG.js → sankeyDiagram-5OEKKPKP-CLrCedZX.js} +1 -1
  220. package/server/public/assets/{xychartDiagram-2RQKCTM6-Dme1PIt6.js → xychartDiagram-2RQKCTM6-hznR8DYl.js} +1 -1
  221. package/server/public/index.html +2 -2
  222. package/server/{pull-requests-By_BteaL.js → pull-requests-7N8WKseP.js} +1 -1
  223. package/server/{pull-requests-8z2N1UOV.js → pull-requests-_lHueOHW.js} +13 -11
  224. package/server/{queries-ZABd5aXP.js → queries-CjNIf-Ub.js} +1200 -201
  225. package/server/{rally-i_goYmiA.js → rally-CyWT_lNO.js} +3 -3
  226. package/server/{rally-api-DqKqIMNR.js → rally-api-C8e_1JRi.js} +2 -2
  227. package/server/{rally-api-xHazRk0d.js → rally-api-ClOSd-Tt.js} +1 -1
  228. package/server/{rally-client-COYz1vYR.js → rally-client-lc-nokqr.js} +4 -4
  229. package/server/{rally-client-CGWEHaPd.js → rally-client-sgtVqzNU.js} +1 -1
  230. package/server/rally-zhhtXuDP.js +3 -0
  231. package/server/{reap-terminal-sessions-CaMkFOIH.js → reap-terminal-sessions-7KeHv1R1.js} +1 -1
  232. package/server/{reap-terminal-sessions-DG9eiMVz.js → reap-terminal-sessions-BtHPo6Xc.js} +1 -1
  233. package/server/rebuild-stack-CFvcBHsS.js +4 -0
  234. package/server/{rebuild-stack-JUm0jqYP.js → rebuild-stack-lwtsW0GO.js} +26 -35
  235. package/server/{reconstruct-cache-wU79aCee.js → reconstruct-cache-C3Ui7ojV.js} +6 -6
  236. package/server/record-CN0pj4_5.js +3 -0
  237. package/server/{record-update-D3N6WMFR.js → record-update-bLJxAXH4.js} +1 -1
  238. package/server/record-update-dE1Aw83j.js +310 -0
  239. package/server/{recovery-trip-DkzJSej9.js → recovery-trip-BvRWfUXM.js} +2 -2
  240. package/server/{release-engine-DMINVN8S.js → release-engine-Cx48W7z8.js} +5 -5
  241. package/server/{release-set-DeUSOEHy.js → release-set-CQfuN9-v.js} +3 -3
  242. package/server/{remote-b-c5bOce.js → remote-C_2hasl7.js} +3 -3
  243. package/server/{remote-Dr5kGYSZ.js → remote-ChgimyYa.js} +3 -3
  244. package/server/{remote-agents-Ct_WOyke.js → remote-agents-By1ige5O.js} +6 -6
  245. package/server/{remote-agents-D6O_zETW.js → remote-agents-CA3Ihlxj.js} +1 -1
  246. package/server/{remote-completion-Dca3hAaa.js → remote-completion-fVe9e_3u.js} +12 -12
  247. package/server/resume-BFv1PGpA.js +3 -0
  248. package/server/{review-agent-CThvgK2r.js → review-agent-CIGOQYBD.js} +29 -25
  249. package/server/{review-agent-CCum8sGe.js → review-agent-DNboMyW4.js} +1 -1
  250. package/server/{review-artifacts-CXywD0F2.js → review-artifacts-nRDPuMxd.js} +5 -5
  251. package/server/{review-context-OYfKUprb.js → review-context-2uj-YQ8J.js} +70 -26
  252. package/server/review-status-btEYlaxA.js +3 -0
  253. package/server/{review-status-enrichment-crNAtIpL.js → review-status-enrichment-Dci-ZwEt.js} +3 -3
  254. package/server/{review-status-DFRjDFef.js → review-status-kZj_KyCk.js} +77 -28
  255. package/server/{review-status-sync-DpAMD_WO.js → review-status-sync-BTOpxtTC.js} +10 -5
  256. package/server/{review-status-sync-D60PJWNL.js → review-status-sync-DSDK3OMy.js} +1 -1
  257. package/server/{review-verdict-feedback-CpUZl5aE.js → review-verdict-feedback-CCjVA7Uk.js} +6 -6
  258. package/server/{review-verdict-feedback-JImjut0z.js → review-verdict-feedback-CTd4QMBu.js} +1 -1
  259. package/server/{rollup-CZ02qgxk.js → rollup-BKQC7jp4.js} +1 -1
  260. package/server/{rollup-SyuF09e4.js → rollup-FGsKki9R.js} +8 -8
  261. package/server/{scanner-Br0Z5S6R.js → scanner-CnqEZeDy.js} +45 -14
  262. package/server/{sequence-io-fxU0DlwW.js → sequence-io-D909zgeH.js} +4 -4
  263. package/server/{sequencer-agent-B5YC5XVP.js → sequencer-agent-CzkcK6P5.js} +5 -5
  264. package/server/server.js +2961 -919
  265. package/server/{service-Bagb90KZ.js → service-CLeAoU6G.js} +55 -54
  266. package/server/{session-fork-BqQxd-V6.js → session-fork-CHI1XvL9.js} +2 -2
  267. package/server/{session-history-XdS0JKp3.js → session-history-BT3IvqhC.js} +4 -4
  268. package/server/settings-66A6uvIA.js +2 -0
  269. package/server/{settings-BNbocWOg.js → settings-BTMvpNFU.js} +3 -3
  270. package/server/{settings-uLc6DEYI.js → settings-C0zgxOGf.js} +2 -2
  271. package/server/{shadow-engineering-D4X_s3fA.js → shadow-engineering-BJfgm-hX.js} +2 -2
  272. package/server/{shadow-state-C_NkP-sa.js → shadow-state-7a-524T8.js} +2 -2
  273. package/server/{shadow-state-CbvsN3qG.js → shadow-state-DnDyjujH.js} +1 -1
  274. package/server/{shadow-utils-CYMRPeRT.js → shadow-utils-Db3oroFG.js} +2 -2
  275. package/server/{ship-log-__QtAFXG.js → ship-log-D9MlVzYU.js} +1 -1
  276. package/server/{smart-compaction-jdMkG1vo.js → smart-compaction-CV2ukKND.js} +1 -1
  277. package/server/{smart-compaction-CLZOWin1.js → smart-compaction-KCYScAU1.js} +7 -7
  278. package/server/{spawn-planning-session-D5OXo8pT.js → spawn-planning-session-DKpdFyOC.js} +35 -27
  279. package/server/{spawn-planning-session-8D6mE033.js → spawn-planning-session-qUC2VWu3.js} +1 -1
  280. package/server/{specialist-completion-CqmduO92.js → specialist-completion-Ky9wCDpb.js} +1 -1
  281. package/server/{specialist-context-CJ-QjFR-.js → specialist-context-Bn3dCGS2.js} +5 -5
  282. package/server/{specialist-handoff-logger-Dnjlwt7g.js → specialist-handoff-logger-CMEwYxa2.js} +2 -2
  283. package/server/{specialist-logs-CQnKzuZs.js → specialist-logs-C5p8G7tx.js} +3 -3
  284. package/server/{specialists-Bg9v4z3F.js → specialists-CC4r-c2s.js} +1015 -460
  285. package/server/{specialists-D0gGXMZx.js → specialists-CShuVGWT.js} +1 -1
  286. package/server/specialists-DM6bWjsi.js +3 -0
  287. package/server/{specialists-CwZHb5cb.js → specialists-jiuuyVkp.js} +66 -74
  288. package/server/specs-C8Wsn2XO.js +3 -0
  289. package/server/{src-BMMoA_ba.js → src-CXmtj0im.js} +6 -6
  290. package/server/{src-Bqdtd6po.js → src-CdmnTM2m.js} +5 -5
  291. package/server/{src-k039WVYY.js → src-DtcUOZCq.js} +112 -18
  292. package/server/stack-health-Bn3o8FBX.js +3 -0
  293. package/server/{stack-health-C-ifC7b0.js → stack-health-DxSdG9cW.js} +63 -9
  294. package/server/{stale-task-claims-L7pJkQFL.js → stale-task-claims-KJKxxiiQ.js} +4 -4
  295. package/server/{staleness-_Tr-GwdA.js → staleness-CT34nwr9.js} +1 -1
  296. package/server/{staleness-BID1BKdr.js → staleness-DPm7ClwA.js} +15 -3
  297. package/server/{state-home-CONQvZR0.js → state-home-Cn_Qu8IX.js} +96 -44
  298. package/server/{state-migrate-BeBW0KXA.js → state-migrate-BGg2ZaOt.js} +12 -3
  299. package/server/{stuck-remediation-CKnIryGT.js → stuck-remediation-DYuPRYYw.js} +307 -26
  300. package/server/{stuck-remediation-CsGD5Ksb.js → stuck-remediation-s9_2qfi6.js} +1 -1
  301. package/server/{substrate-bug-weights-service-CpIZILj9.js → substrate-bug-weights-service-B-FwCs5V.js} +3 -3
  302. package/server/{summary-fork-bX6ax6X7.js → summary-fork-D1RAk9rJ.js} +78 -12
  303. package/server/{summary-fork-agEMsPSo.js → summary-fork-DpcxN9tf.js} +2 -2
  304. package/server/supervisor/{injection-budget-C1iPirFS.js → injection-budget-Csw5tqfJ.js} +2 -41
  305. package/server/supervisor/pty-supervisor.js +2 -1
  306. package/server/supervisor/pty-token-x-ea28Us.js +43 -0
  307. package/server/supervisor-FTJAIsAK.js +2 -0
  308. package/server/{supervisor-Ctt4eRYz.js → supervisor-wM4ooB5O.js} +3 -3
  309. package/server/{swarm-readiness-DdmJVX7y.js → swarm-readiness-DlP_UjHq.js} +4 -4
  310. package/server/{system-prerequisites-DSXBvffY.js → system-prerequisites-DUDj1YHf.js} +56 -29
  311. package/server/{teardown-workspace-DDCf5n-a.js → teardown-workspace-DeTTImn-.js} +15 -15
  312. package/server/{test-agent-queue-BJZ9Nfcc.js → test-agent-queue-MbxX5vqq.js} +7 -7
  313. package/server/{test-verdict-Cp5tljlJ.js → test-verdict-bWYBtqBg.js} +1 -1
  314. package/server/{test-verdict-D_-sXIz_.js → test-verdict-gscuksPv.js} +1 -1
  315. package/server/{tldr-daemon-B8_oT5gx.js → tldr-daemon-BYFkts95.js} +1 -1
  316. package/server/{tldr-daemon-BQFmmFp0.js → tldr-daemon-C-69c3OI.js} +2 -2
  317. package/server/tmux-B6z7h_vZ.js +3 -0
  318. package/server/{tmux-L5LgvGLp.js → tmux-R7N7y-Mh.js} +76 -44
  319. package/server/{token-BkQLy4yS.js → token-BK6t8BIT.js} +2 -2
  320. package/server/token-util-DuPuNriz.js +3 -0
  321. package/server/{token-util-DI8sGlmF.js → token-util-DxXu-vd1.js} +1 -1
  322. package/server/{tracker-config-DdT-nUAM.js → tracker-config-G6latr8C.js} +2 -2
  323. package/server/{tracker-utils-DV3h1FYG.js → tracker-utils-B4ylXZQK.js} +1 -1
  324. package/server/{tracker-utils-CawsPSS2.js → tracker-utils-DHv5pSDT.js} +2 -2
  325. package/server/{traefik-connect-C6mJckNs.js → traefik-connect-Df2uXPsQ.js} +1 -1
  326. package/server/{tunnel-IdK1iTHx.js → tunnel-CDVKJpRv.js} +3 -3
  327. package/server/{tunnel-BIFl98rc.js → tunnel-DGdUkGoZ.js} +1 -1
  328. package/server/{types-7-GnJn0t.js → types-5M0lupud.js} +1 -1
  329. package/server/{uat-train-0Ujk8GCv.js → uat-train-B05BWkp-.js} +1 -1
  330. package/server/{uat-train-j0cPeMSj.js → uat-train-DDSrSoV1.js} +28 -28
  331. package/server/validate-IfDGkaVR.js +177 -0
  332. package/server/{validation-DcAgXTQp.js → validation-CC9OPqJZ.js} +1 -1
  333. package/server/{validation-CabEISJT.js → validation-Dw1pzDzr.js} +259 -9
  334. package/server/{verification-artifact-Ck1tefmz.js → verification-artifact-B3itKTxu.js} +1 -1
  335. package/server/{verification-artifact-BuTA1QbL.js → verification-artifact-TVFSz_q1.js} +1 -1
  336. package/server/{verification-runner-DlJezzef.js → verification-runner-BW4HOKAz.js} +1 -1
  337. package/server/{verification-runner-FiuAZ-H2.js → verification-runner-CkgX6EtI.js} +174 -124
  338. package/server/{vtt-parser-BnRsYzcz.js → vtt-parser-DYPbKcIJ.js} +1 -1
  339. package/server/{work-agent-prompt-DLthEPAq.js → work-agent-prompt-Du8Vh2aJ.js} +13 -13
  340. package/server/workflows-BQu4RFQ-.js +2 -0
  341. package/server/{workflows-BZNNYkGC.js → workflows-mbQzh6mQ.js} +39 -77
  342. package/server/{workspace-3IC-UkAi.js → workspace--8tnhDI0.js} +1 -1
  343. package/server/{workspace-D9ppycdg.js → workspace-BPUws1-_.js} +3 -3
  344. package/server/{workspace-config-Bigl_-Lk.js → workspace-config-CIVWZOQr.js} +1 -1
  345. package/server/{workspace-manager-ClkCIWTe.js → workspace-manager-CBhNdzE4.js} +76 -18
  346. package/server/workspace-manager-CWT55qeI.js +3 -0
  347. package/server/{workspace-metadata-BE0efUv_.js → workspace-metadata-BzHnwwX4.js} +1 -1
  348. package/server/{workspace-metadata-tiSRIWJP.js → workspace-metadata-_WygnyoX.js} +2 -2
  349. package/server/{workspaces-CFPcL0vl.js → workspaces-Do_9J0LQ.js} +309 -124
  350. package/sync-sources/agents/pan-inspect-agent.md +1 -1
  351. package/sync-sources/agents/pan-merge-agent.md +1 -1
  352. package/sync-sources/agents/pan-planning-agent.md +4 -4
  353. package/sync-sources/agents/pan-work-agent.md +1 -1
  354. package/sync-sources/dev-skills/pan-commit/SKILL.md +1 -1
  355. package/sync-sources/dev-skills/test-specialist-workflow/SKILL.md +1 -1
  356. package/sync-sources/hooks/record-cost-event.js +369 -75
  357. package/sync-sources/hooks/session-start-hook +7 -0
  358. package/sync-sources/hooks/work-agent-stop-hook +1 -1
  359. package/sync-sources/rules/per-bead-durability.md +1 -1
  360. package/sync-sources/rules/prd-authoring.md +1 -1
  361. package/sync-sources/skills/feature-work/SKILL.md +1 -1
  362. package/sync-sources/skills/okf/README.md +11 -3
  363. package/sync-sources/skills/okf/SKILL.md +12 -1
  364. package/sync-sources/skills/okf/docs/USAGE.md +15 -0
  365. package/sync-sources/skills/okf/references/overdeck.md +28 -0
  366. package/sync-sources/skills/pan/SKILL.md +2 -2
  367. package/sync-sources/skills/pan-admin-config/SKILL.md +1 -0
  368. package/sync-sources/skills/pan-approve/SKILL.md +1 -1
  369. package/sync-sources/skills/pan-close/SKILL.md +1 -1
  370. package/sync-sources/skills/pan-doctor/SKILL.md +1 -1
  371. package/sync-sources/skills/pan-flywheel/SKILL.md +18 -1
  372. package/sync-sources/skills/pan-knowledge/SKILL.md +10 -0
  373. package/sync-sources/skills/pan-new-project/SKILL.md +9 -11
  374. package/sync-sources/skills/pan-orders/SKILL.md +33 -0
  375. package/sync-sources/skills/pan-oversee/SKILL.md +5 -5
  376. package/sync-sources/skills/pan-plan/SKILL.md +6 -6
  377. package/sync-sources/skills/pan-quickstart/SKILL.md +1 -1
  378. package/sync-sources/skills/pan-recap/SKILL.md +2 -2
  379. package/sync-sources/skills/pan-review/SKILL.md +1 -1
  380. package/sync-sources/skills/pan-start/SKILL.md +7 -3
  381. package/sync-sources/skills/pan-swarm/SKILL.md +1 -1
  382. package/sync-sources/skills/pan-sync/SKILL.md +1 -1
  383. package/sync-sources/skills/pan-task/SKILL.md +2 -2
  384. package/sync-sources/skills/pan-wipe/SKILL.md +2 -2
  385. package/sync-sources/skills/pipeline-list/SKILL.md +1 -1
  386. package/sync-sources/skills/pipeline-status/SKILL.md +2 -2
  387. package/sync-sources/skills/plan/SKILL.md +11 -11
  388. package/sync-sources/skills/write-vbrief/SKILL.md +7 -282
  389. package/sync-sources/skills/write-xbrief/SKILL.md +295 -0
  390. package/sync-sources/templates/claude-md/sections/warnings.md +1 -1
  391. package/sync-sources/templates/traefik/dynamic/overdeck.yml.template +10 -0
  392. package/server/agent-state-Co5Sy2Fw.js +0 -3
  393. package/server/agents-DyHKlq1I.js +0 -3
  394. package/server/cli-BRORS9qY.js +0 -3
  395. package/server/codex-DNyiQDKW.js +0 -3
  396. package/server/context-layers-15gDOk20.js +0 -165
  397. package/server/context-layers-DlQKkHH2.js +0 -4
  398. package/server/deacon-swarm-CV54eNmU.js +0 -2
  399. package/server/dist-j64RzSrQ.js +0 -3
  400. package/server/flywheel-6WHU4O7A.js +0 -207
  401. package/server/flywheel-COR0mbRI.js +0 -522
  402. package/server/flywheel-Ctr5uuvo.js +0 -2
  403. package/server/forge-B0_opi-V.js +0 -2
  404. package/server/git-utils-CsKGnzFA.js +0 -3
  405. package/server/hume-BmhvfVbV.js +0 -3
  406. package/server/inspect-agent-Dq0z1k3H.js +0 -2
  407. package/server/layers-Cp1To4L8.js +0 -93
  408. package/server/lifecycle-io-DmY2qwCn.js +0 -3
  409. package/server/merge-blockers-BYjeXrVN.js +0 -41
  410. package/server/merge-ops-DwWCkSgo.js +0 -2
  411. package/server/merge-set-DiISXcC-.js +0 -139
  412. package/server/projects-BKkwpH6q.js +0 -3
  413. package/server/public/assets/index-Bdc4V9ba.css +0 -1
  414. package/server/public/assets/mermaid-GHXKKRXX-B5v_r7cI.js +0 -1
  415. package/server/rally-OSJXdiHK.js +0 -3
  416. package/server/rebuild-stack-CKjJAI8L.js +0 -3
  417. package/server/record-rwK-nKv_.js +0 -3
  418. package/server/record-update-Crz74r-6.js +0 -168
  419. package/server/resume-wZZsWtzz.js +0 -3
  420. package/server/review-status-Bnh-rdkY.js +0 -3
  421. package/server/settings-BlUsVaUc.js +0 -2
  422. package/server/specialists-Dqe6_HE5.js +0 -3
  423. package/server/specs-BhOKBzqk.js +0 -3
  424. package/server/stack-health-Bd06frFn.js +0 -3
  425. package/server/supervisor-COVHrBTg.js +0 -2
  426. package/server/tmux-BAvtac_u.js +0 -3
  427. package/server/token-util-BArZLN6w.js +0 -3
  428. package/server/workflows-C4mR0Reb.js +0 -2
  429. package/server/workspace-manager-B1WOfKvB.js +0 -3
@@ -1,43 +1,45 @@
1
1
  import { a as __toCommonJS } from "./rolldown-runtime-DNCzzKnI.js";
2
- import { A as canUseHarnessSync, D as resolveTieredExecutionEnabled, H as init_providers, Hn as init_projects, I as UnknownModelError, In as getProjectSync, Kn as listProjectsSync, M as init_harness_policy, O as resolveTieredExecutionEnabledForIssue, T as init_tier_table, U as piProviderForModel, V as getProviderForModelSync, Xr as encodeClaudeProjectDir, c as isClaudeCodeChannelsEnabled, dr as extractPrefixSync, f as loadConfigSync, fr as init_issue_id, ii as init_paths, jr as init_errors, mr as parseIssueIdSync, nr as resolveProjectFromIssueSync, oi as packageRoot, ri as getOverdeckHome, si as resolveOhmypiExtensionPath, t as init_config_yaml, tt as resolveModel, xn as shouldCommitLegacyWorkspaceArtifacts, xr as FsError, yn as init_state_read_home } from "./config-yaml-ig8Nwtiw.js";
2
+ import { A as canUseHarnessSync, Br as getOverdeckHome, D as resolveTieredExecutionEnabled, H as init_providers, I as UnknownModelError, Jn as extractPrefixSync, M as init_harness_policy, Nr as encodeClaudeProjectDir, O as resolveTieredExecutionEnabledForIssue, Qt as parsePorcelainStatusPaths, T as init_tier_table, Tn as init_projects, U as piProviderForModel, Ur as packageRoot, V as getProviderForModelSync, Vr as init_paths, Wr as resolveOhmypiExtensionPath, Yn as init_issue_id, Yt as init_state_plane, Zn as parseIssueIdSync, c as isClaudeCodeChannelsEnabled, f as loadConfigSync, fn as findProjectByPathSync, in as shouldCommitLegacyWorkspaceArtifacts, ir as FsError, kn as listProjectsSync, mr as init_errors, nn as init_state_read_home, rn as resolveStateReadHomeSync, t as init_config_yaml, tt as resolveModel, yn as getProjectSync, zn as resolveProjectFromIssueSync } from "./config-yaml-DySPNmKN.js";
3
3
  import { a as init_activity_logger, n as emitActivityEntrySync } from "./activity-logger-DDlOIAML.js";
4
- import { d as getOverdeckDatabaseSync, f as init_infra } from "./infra-D4iNWMA5.js";
5
- import { B as sendRawKeystroke, D as killSession, H as sessionExistsSync, J as BLANKED_PROVIDER_ENV, M as listSessions, T as isHarnessProcessAlive, U as setOption, V as sessionExists, X as buildChildEnvWithoutTmuxSync, Z as init_child_env, g as findManagedServerPidSync, i as capturePane, j as listSessionNames, m as exactPaneTarget, s as createSession, w as init_tmux, z as sendKeysAsync } from "./tmux-L5LgvGLp.js";
6
- import { a as init_event_store, n as getEventStore } from "./event-store-TnWdydfB.js";
7
- import { k as normalizeChannelReplyPayload, o as getHarnessBehavior, t as init_src, x as DomainEvent } from "./src-k039WVYY.js";
8
- import { C as AGENT_COLUMNS_FOR_DB, O as stateToOverdeckParamsForDb, T as init_agent_state_sync, c as init_review_status, s as getReviewStatusSync, u as loadReviewStatuses, y as setReviewStatusSync } from "./review-status-DFRjDFef.js";
9
- import { a as EventStoreService, c as ReadModelService, d as init_read_model, i as init_issue_lifecycle, s as init_domain_services, t as IssueLifecycle, u as getClosedIssueIdsForReadSource } from "./issue-lifecycle-CeyW_HoD.js";
10
- import { d as isTerminalIssueStage, o as init_agents, u as getIssueStageSync } from "./agents-DXdHmRTC.js";
11
- import { n as ensureSessionContextBriefingFile, r as init_briefing_freshness } from "./briefing-freshness-C12WgkaB.js";
12
- import { o as getPricingSync, s as init_cost, t as calculateCostSync } from "./cost-Codk1pc2.js";
13
- import { $ as stopAgent, $n as init_pty_token, $t as init_ohmypi_fifo, Cn as clearAgentTroubled, Dn as getAgentDir, Et as writeBridgeTokenSync, Gn as saveAgentStateSync, Gt as getProviderAuthMode, H as getLatestSessionId, Ht as getAgentRuntimeBaseCommand, K as saveSessionId, Kn as setAgentPaused, L as messageAgent, Lt as resolveStaffing, M as resumeAgent, Mt as transitionIssueToInProgress, O as recoverAgent, Pt as init_staffing, Qt as piFifoPaths, R as init_operator_interventions, Rn as markAgentStoppedState, S as init_tier_escalation, Tt as init_bridge_token, U as getLatestSessionIdSync, V as getActivity, W as getSessionId, Wn as saveAgentState, Xn as writeAgentStateJsonSync, Yn as wipeAgentStateDirs, Zt as init_pi_fifo, _n as writeChannelsBridgeMcpConfig, _r as init_codex_auth, _t as deliverAgentPermissionDecision, an as determineModel, ar as normalizeAgentId, bt as setAgentDeliveryMethod, cr as waitForReadySignal, en as ohmypiFifoPaths, er as writePtyToken, fr as saveAgentRuntimeState, gn as dismissDevChannelsDialog, gr as checkCodexAuthStatus, gt as deliverAgentMessage, i as listRunningAgents, jn as getAgentStateSync, k as restartAgent, kn as getAgentState, ln as init_provider_health, lr as getAgentRuntimeState, nr as resolvePtySupervisorScriptPath, on as getProviderEnvForModel, r as listAgentStates, rr as clearReadySignal, s as init_agents$1, sn as getProviderExportsForModel, tr as init_pty_supervisor_locate, un as validateProviderHealth, ur as getAgentRuntimeStateSync, wt as BRIDGE_TOKEN_HEADER, x as decideEscalation, xn as clearAgentPaused, z as operatorInterventionEvent } from "./queries-ZABd5aXP.js";
14
- import { a as performHandoff, i as checkAllTriggers, s as getAgentHealth, t as getCloisterService } from "./service-Bagb90KZ.js";
15
- import { o as init_persistent_logger, s as logAgentLifecycleSync } from "./session-history-XdS0JKp3.js";
16
- import { t as init_behavior } from "./behavior-CGj16clS.js";
17
- import { a as init_tracker_config, t as getGitHubConfig } from "./tracker-config-DdT-nUAM.js";
18
- import { n as init_issue_service_singleton, r as issue_service_singleton_exports } from "./issue-service-singleton-D_RVYBuL.js";
19
- import { o as getInternalTokenSync, s as init_internal_token } from "./internal-token-CXsfxslp.js";
4
+ import { d as getOverdeckDatabaseSync, f as init_infra } from "./infra-BEqFxlJc.js";
5
+ import { $ as init_child_env, B as sendKeysAsync, D as killSession, H as sessionExists, M as listSessionNames, N as listSessions, Q as buildChildEnvWithoutTmuxSync, T as isHarnessProcessAlive, U as sessionExistsSync, V as sendRawKeystroke, W as setOption, X as BLANKED_PROVIDER_ENV, g as findManagedServerPidSync, i as capturePane, m as exactPaneTarget, s as createSession, w as init_tmux } from "./tmux-R7N7y-Mh.js";
6
+ import { a as init_event_store, n as getEventStore } from "./event-store-CQL6NeqB.js";
7
+ import { M as normalizeChannelReplyPayload, o as getHarnessBehavior, t as init_src, w as DomainEvent } from "./src-DtcUOZCq.js";
8
+ import { E as init_agent_state_sync, b as setReviewStatusSync, c as getReviewStatusSync, d as loadReviewStatuses, i as clearWorkspaceStuck, k as stateToOverdeckParamsForDb, l as init_review_status, w as AGENT_COLUMNS_FOR_DB } from "./review-status-kZj_KyCk.js";
9
+ import { a as EventStoreService, c as ReadModelService, d as init_read_model, i as init_issue_lifecycle, s as init_domain_services, t as IssueLifecycle, u as getClosedIssueIdsForReadSource } from "./issue-lifecycle-ojZAzDBP.js";
10
+ import { d as isTerminalIssueStage, o as init_agents, u as getIssueStageSync } from "./agents-EbI0zCJl.js";
11
+ import { n as ensureSessionContextBriefingFile, r as init_briefing_freshness } from "./briefing-freshness-D1wzUIWv.js";
12
+ import { o as getPricingSync, s as init_cost, t as calculateCostSync } from "./cost-B2ddP6wq.js";
13
+ import { Ar as saveAgentRuntimeState, At as deliverAgentPermissionDecision, Bt as assertWorkspaceStackHealthyForSpawn, Cr as normalizeAgentId, Dn as validateProviderHealth, Dr as getAgentRuntimeState, En as init_provider_health, Er as waitForReadySignal, F as init_tier_escalation, Fn as writeChannelsBridgeMcpConfig, Fr as init_codex_auth, G as resumeAgent, Gn as getAgentDir, Gt as transitionIssueToInProgress, H as restartAgent, Lt as BRIDGE_TOKEN_HEADER, Nt as setAgentDeliveryMethod, Or as getAgentRuntimeStateSync, P as decideEscalation, Pn as dismissDevChannelsDialog, Pr as checkCodexAuthStatus, Q as operatorInterventionEvent, Rt as init_bridge_token, Sn as determineModel, Ut as init_spawn_prep, V as recoverAgent, Vn as clearAgentTroubled, X as messageAgent, Xt as resolveStaffing, Yn as getAgentStateSync, Z as init_operator_interventions, _r as init_pty_token, an as getProviderAuthMode, at as saveSessionId, b as init_agents$1, br as resolvePtySupervisorScriptPath, cr as saveAgentState, et as getActivity, fn as init_pi_fifo, ft as stopAgent, hn as ohmypiFifoPaths, i as listRunningAgents, kt as deliverAgentMessage, lr as saveAgentStateSync, mn as init_ohmypi_fifo, mr as writeAgentStateJsonSync, nn as getAgentRuntimeBaseCommand, nr as markAgentStoppedState, nt as getLatestSessionIdSync, pn as piFifoPaths, pr as wipeAgentStateDirs, qn as getAgentState, qt as init_staffing, r as listAgentStates, rt as getSessionId, sn as init_runtime_command, tn as getAcpLauncherFields, tt as getLatestSessionId, un as waitForAcpHostReady, ur as setAgentPaused, vr as writePtyToken, wn as getProviderExportsForModel, xr as clearReadySignal, yr as init_pty_supervisor_locate, zn as clearAgentPaused, zt as writeBridgeTokenSync } from "./queries-CjNIf-Ub.js";
14
+ import { a as performHandoff, i as checkAllTriggers, s as getAgentHealth, t as getCloisterService } from "./service-CLeAoU6G.js";
15
+ import { o as init_persistent_logger, s as logAgentLifecycleSync } from "./session-history-BT3IvqhC.js";
16
+ import { t as init_behavior } from "./behavior-B3exJ42O.js";
17
+ import { a as init_tracker_config, t as getGitHubConfig } from "./tracker-config-G6latr8C.js";
18
+ import { n as init_issue_service_singleton, r as issue_service_singleton_exports } from "./issue-service-singleton-Yx5DdmVJ.js";
19
+ import { o as getInternalTokenSync, s as init_internal_token } from "./internal-token-s0-oKybJ.js";
20
20
  import { a as PAN_DIRNAME, i as PAN_CONTINUE_FILENAME, p as init_types } from "./types-hraoaQIp.js";
21
- import { F as generateVBriefFilename, I as init_lifecycle, f as readTierOverrides, g as serializeVBriefDocument, h as recordTierPromotion, m as readWorkspacePlanSync, o as init_io, r as findPlan, u as readPlan } from "./io-DQR9PMgS.js";
22
- import { B as removeFavorite, E as hasOtherActiveConversationOnTmuxSession, F as markConversationEnded, G as setConversationHarness, K as setConversationModel, M as listConversations, P as markConversationActive, Q as updateConversationDeliveryMethod, S as getConversationByName, W as setConversationEffort, g as backfillConversationModel, h as archiveConversation, k as init_conversations, nt as updateLastAttached, rt as updateSpawnError, x as getConversationById, y as createConversation } from "./conversations-bYpM9-kZ.js";
23
- import { n as recordFeatureRegistryLifecycle, t as init_feature_registry_population } from "./feature-registry-population-B-CI9hgE.js";
24
- import { a as requireModelOverrideSync, i as normalizeModelOverrideSync, n as init_launcher_generator, r as init_model_validation, t as generateLauncherScriptSync } from "./launcher-generator-COgR2By1.js";
25
- import { a as init_claude_permissions, o as resolvePermissionModeSync, r as getClaudePermissionFlagsStringSync, t as BYPASS_PERMISSION_MODE } from "./claude-permissions-nSFKH78M.js";
26
- import { h as workspaceContextFile, l as piGlobalContextFile, o as init_layers } from "./layers-Cp1To4L8.js";
27
- import { n as init_harness_binary, r as prepareHarnessLaunch } from "./harness-binary-B2CTcD84.js";
28
- import { a as init_stack_health, i as getWorkspaceStackHealth } from "./stack-health-C-ifC7b0.js";
29
- import { l as init_dag, o as getDispatchableItems } from "./dag-CJpgf9A3.js";
30
- import { n as resolveHarness, t as init_harness_resolve } from "./harness-resolve-D5FVQTmy.js";
31
- import { a as loadCloisterConfigSync, r as init_config } from "./config-DeTsoZdR.js";
32
- import { c as isStateMigrated, o as init_state_home } from "./state-home-CONQvZR0.js";
33
- import { a as init_lifecycle_io, c as updatePlanStatus, s as transitionVBriefOnMain } from "./lifecycle-io-taIb0PoH.js";
34
- import { _ as init_codex_conversation_parser, c as parseEntireConversation, d as init_pi_conversation_parser, g as parseOhmypiConversationMessages, m as init_ohmypi_conversation_parser, p as parsePiConversationMessages, t as init_conversation_service, v as parseCodexConversationMessages } from "./conversation-service-C3avRVEr.js";
35
- import { _ as parseCodexApprovalPrompt, d as getAgentPendingQuestions$1, f as getAgentWorkspace$1, g as normalizeAwaitingInputPrompt, m as detectAwaitingInputForAgent, o as getRuntimeForAgent, p as scanPendingInputsPromise, s as computeAgentEnrichment, u as getAgentJsonlPath$1 } from "./stuck-remediation-CKnIryGT.js";
36
- import { Mt as getResourceConfig, Pt as getSystemHealthSnapshot, Tt as classifyMemoryPressure } from "./deacon-CkT_kznS.js";
37
- import { o as stopWorkspaceDocker, r as init_workspace_manager } from "./workspace-manager-ClkCIWTe.js";
38
- import { a as init_jsonl_parser, o as normalizeModelName } from "./jsonl-parser-C3HVNR5d.js";
39
- import { r as init_in_flight_guard, t as createInFlightGuard } from "./in-flight-guard-BoWm66Kh.js";
40
- import { a as loadWorkspaceMetadataSync } from "./workspace-metadata-tiSRIWJP.js";
21
+ import { a as generateXBriefFilename, o as init_lifecycle } from "./lifecycle-yPd___5E.js";
22
+ import { f as readTierOverrides, g as serializeXBriefDocument, h as recordTierPromotion, m as readWorkspacePlanSync, o as init_io, r as findPlan, u as readPlan } from "./io-C9kF1wtJ.js";
23
+ import { B as removeFavorite, E as hasOtherActiveConversationOnTmuxSession, F as markConversationEnded, G as setConversationHarness, K as setConversationModel, M as listConversations, P as markConversationActive, Q as updateConversationDeliveryMethod, S as getConversationByName, W as setConversationEffort, g as backfillConversationModel, h as archiveConversation, k as init_conversations, nt as updateLastAttached, rt as updateSpawnError, x as getConversationById, y as createConversation } from "./conversations-6AfUfbwc.js";
24
+ import { n as recordFeatureRegistryLifecycle, t as init_feature_registry_population } from "./feature-registry-population-DKZFMbFt.js";
25
+ import { a as normalizeModelOverrideSync, i as init_model_validation, n as init_launcher_generator, o as requireModelOverrideSync, r as MODEL_ID_PATTERN, t as generateLauncherScriptSync } from "./launcher-generator-BBWG_SiS.js";
26
+ import { a as init_claude_permissions, o as resolvePermissionModeSync, r as getClaudePermissionFlagsStringSync, t as BYPASS_PERMISSION_MODE } from "./claude-permissions-DNWEVV6z.js";
27
+ import { E as init_layers, I as workspaceContextFile, k as piGlobalContextFile } from "./assemble-J2ZZ2C15.js";
28
+ import { i as prepareHarnessLaunch, r as init_harness_binary } from "./harness-binary-DPUi-Pv8.js";
29
+ import { a as getWorkspaceStackHealth, s as init_stack_health } from "./stack-health-DxSdG9cW.js";
30
+ import { l as init_dag, o as getDispatchableItems } from "./dag-B1ucnjDU.js";
31
+ import { n as resolveHarness, t as init_harness_resolve } from "./harness-resolve-3lNZkyT4.js";
32
+ import { a as loadCloisterConfigSync, r as init_config } from "./config-CKxYiCmX.js";
33
+ import { c as isStateMigrated, o as init_state_home } from "./state-home-Cn_Qu8IX.js";
34
+ import { a as init_lifecycle_io, c as updatePlanStatus, s as transitionXBriefOnMain } from "./lifecycle-io-BREGpnLk.js";
35
+ import { _ as init_codex_conversation_parser, b as parseAcpConversationMessages, c as parseEntireConversation, d as init_pi_conversation_parser, g as parseOhmypiConversationMessages, m as init_ohmypi_conversation_parser, p as parsePiConversationMessages, t as init_conversation_service, v as parseCodexConversationMessages, y as init_acp_conversation_parser } from "./conversation-service-DPJ71F33.js";
36
+ import { _ as parseCodexApprovalPrompt, d as getAgentPendingQuestions$1, f as getAgentWorkspace$1, g as normalizeAwaitingInputPrompt, m as detectAwaitingInputForAgent, o as getRuntimeForAgent, p as scanPendingInputsPromise, s as computeAgentEnrichment, u as getAgentJsonlPath$1 } from "./stuck-remediation-DYuPRYYw.js";
37
+ import { B as getBook, H as liveOrderIssueLookup, T as resolveLiveFlywheelRunId, w as readFlywheelLaunchMetadata, y as getFlywheelRunDir, z as computeBookProgress } from "./flywheel-DHrgCphM.js";
38
+ import { Dt as classifyMemoryPressure, It as getSystemHealthSnapshot, Pt as getResourceConfig } from "./deacon-CU_cAYSR.js";
39
+ import { o as stopWorkspaceDocker, r as init_workspace_manager } from "./workspace-manager-CBhNdzE4.js";
40
+ import { a as init_jsonl_parser, o as normalizeModelName } from "./jsonl-parser-BXSlHwEh.js";
41
+ import { r as init_in_flight_guard, t as createInFlightGuard } from "./in-flight-guard-CAAkqPLu.js";
42
+ import { a as loadWorkspaceMetadataSync } from "./workspace-metadata-_WygnyoX.js";
41
43
  import { basename, delimiter, dirname, join, resolve } from "node:path";
42
44
  import { Cause, Data, Effect, Layer, Option, Schema, Stream } from "effect";
43
45
  import { join as join$1 } from "path";
@@ -99,9 +101,8 @@ init_cost();
99
101
  init_behavior();
100
102
  init_persistent_logger();
101
103
  init_tracker_config();
102
- init_types();
103
- init_review_status();
104
104
  init_lifecycle();
105
+ init_review_status();
105
106
  init_io();
106
107
  /**
107
108
  * Resolve the default cwd used when no project is selected — the "devroot"
@@ -200,10 +201,12 @@ init_layers();
200
201
  init_harness_binary();
201
202
  init_ohmypi_fifo();
202
203
  init_pi_fifo();
204
+ init_runtime_command();
203
205
  init_stack_health();
204
206
  init_launcher_generator();
205
207
  init_dag();
206
208
  init_staffing();
209
+ init_spawn_prep();
207
210
  init_harness_resolve();
208
211
  init_bridge_token();
209
212
  init_operator_interventions();
@@ -364,7 +367,215 @@ async function waitForSessionRespawn(sessionName, timeoutMs) {
364
367
  return Effect.runPromise(sessionExists(sessionName));
365
368
  }
366
369
  //#endregion
370
+ //#region routes/dashboard-auth.ts
371
+ init_state_read_home();
372
+ init_state_plane();
373
+ init_types();
374
+ init_issue_lifecycle();
375
+ init_codex_auth();
376
+ init_config();
377
+ init_state_home();
378
+ init_internal_token();
379
+ const DASHBOARD_SESSION_COOKIE = "overdeck_session";
380
+ const DASHBOARD_CSRF_HEADER = "x-overdeck-csrf-token";
381
+ const DASHBOARD_SESSION_MAX_AGE_SECONDS = 3600 * 24 * 30;
382
+ let browserSessionToken;
383
+ let browserCsrfToken;
384
+ function getDashboardSessionToken() {
385
+ if (browserSessionToken) return browserSessionToken;
386
+ const override = process.env["OVERDECK_DASHBOARD_SESSION_TOKEN"];
387
+ if (override) {
388
+ browserSessionToken = override;
389
+ return browserSessionToken;
390
+ }
391
+ const internal = getInternalTokenSync();
392
+ browserSessionToken = internal ? createHmac("sha256", internal).update("overdeck-dashboard-session-v1").digest("base64url") : randomBytes(32).toString("base64url");
393
+ return browserSessionToken;
394
+ }
395
+ function dashboardCsrfToken() {
396
+ if (browserCsrfToken) return browserCsrfToken;
397
+ const override = process.env["OVERDECK_DASHBOARD_CSRF_TOKEN"];
398
+ if (override) {
399
+ browserCsrfToken = override;
400
+ return browserCsrfToken;
401
+ }
402
+ const internal = getInternalTokenSync();
403
+ browserCsrfToken = internal ? createHmac("sha256", internal).update("overdeck-dashboard-csrf-v1").digest("base64url") : randomBytes(32).toString("base64url");
404
+ return browserCsrfToken;
405
+ }
406
+ function constantTimeTokenEqual$1(provided, expected) {
407
+ if (!provided) return false;
408
+ return timingSafeEqual(createHash$1("sha256").update(provided).digest(), createHash$1("sha256").update(expected).digest());
409
+ }
410
+ function cookieValue(cookieHeader, name) {
411
+ if (!cookieHeader) return void 0;
412
+ for (const part of cookieHeader.split(";")) {
413
+ const [rawKey, ...rawValue] = part.trim().split("=");
414
+ if (rawKey !== name) continue;
415
+ try {
416
+ return decodeURIComponent(rawValue.join("="));
417
+ } catch {
418
+ return;
419
+ }
420
+ }
421
+ }
422
+ function dashboardSessionCookieHeader(options = {}) {
423
+ const secure = options.secure ? "; Secure" : "";
424
+ return `${DASHBOARD_SESSION_COOKIE}=${encodeURIComponent(getDashboardSessionToken())}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${DASHBOARD_SESSION_MAX_AGE_SECONDS}${secure}`;
425
+ }
426
+ function hasDashboardInternalTokenHeaders(headers) {
427
+ const expected = getInternalTokenSync();
428
+ if (!expected) return false;
429
+ if (constantTimeTokenEqual$1(getHeaderFromMap(headers, "x-overdeck-internal-token"), expected)) return true;
430
+ const authorization = getHeaderFromMap(headers, "authorization");
431
+ if (authorization) {
432
+ const [scheme, token] = authorization.split(/\s+/);
433
+ if (scheme?.toLowerCase() === "bearer" && constantTimeTokenEqual$1(token, expected)) return true;
434
+ }
435
+ return false;
436
+ }
437
+ function hasDashboardInternalToken(request) {
438
+ return hasDashboardInternalTokenHeaders(request.headers);
439
+ }
440
+ function hasDashboardAuthHeaders(headers) {
441
+ return hasDashboardInternalTokenHeaders(headers) || constantTimeTokenEqual$1(cookieValue(getHeaderFromMap(headers, "cookie"), "overdeck_session"), getDashboardSessionToken());
442
+ }
443
+ function hasDashboardAuth(request) {
444
+ return hasDashboardAuthHeaders(request.headers);
445
+ }
446
+ function isJsonContentType(headers) {
447
+ const contentType = getHeaderFromMap(headers, "content-type");
448
+ if (!contentType) return false;
449
+ const [mime] = contentType.toLowerCase().split(";");
450
+ return mime.trim() === "application/json";
451
+ }
452
+ function hasTrustedExactOrigin(headers) {
453
+ const origin = getHeaderFromMap(headers, "origin");
454
+ if (!origin) return false;
455
+ const normalized = normalizeOrigin(origin);
456
+ return !!normalized && getTrustedOrigins().includes(normalized);
457
+ }
458
+ function hasValidCsrfToken(headers) {
459
+ return constantTimeTokenEqual$1(getHeaderFromMap(headers, DASHBOARD_CSRF_HEADER), dashboardCsrfToken());
460
+ }
461
+ function rejectUnsafeDashboardMutationRequest(request) {
462
+ const authError = rejectUnauthorizedDashboardRequest(request);
463
+ if (authError) return authError;
464
+ const headers = request.headers;
465
+ if (!isJsonContentType(headers)) return jsonResponse({ error: "Content-Type must be application/json" }, { status: 400 });
466
+ if (hasDashboardInternalToken(request)) return null;
467
+ if (getHeaderFromMap(headers, "origin") && !hasTrustedExactOrigin(headers)) return jsonResponse({ error: "Invalid origin" }, { status: 403 });
468
+ if (!hasValidCsrfToken(headers)) return jsonResponse({ error: "Invalid CSRF token" }, { status: 403 });
469
+ return null;
470
+ }
471
+ function ipv4ToInt(value) {
472
+ const parts = value.split(".");
473
+ if (parts.length !== 4) return null;
474
+ let result = 0;
475
+ for (const part of parts) {
476
+ const octet = Number(part);
477
+ if (!Number.isInteger(octet) || octet < 0 || octet > 255) return null;
478
+ result = (result << 8 | octet) >>> 0;
479
+ }
480
+ return result;
481
+ }
482
+ /** RFC1918 private ranges — Docker bridges always fall inside one of these. */
483
+ function isRfc1918(ip) {
484
+ return (ip & 4278190080) >>> 0 === 167772160 || (ip & 4293918720) >>> 0 === 2886729728 || (ip & 4294901760) >>> 0 === 3232235520;
485
+ }
486
+ /**
487
+ * True when `addr` is the IP of a container on one of THIS host's Docker bridge
488
+ * interfaces (docker0 / br-*). Such a peer can only be a process running on this
489
+ * machine — our own host-local Traefik fronting overdeck.localhost, or another
490
+ * Overdeck container — never a LAN client (a direct LAN hit to the 0.0.0.0
491
+ * API port arrives with a LAN peer that is not inside any Docker bridge subnet).
492
+ *
493
+ * Pure for testability: the interface map is injected. We only ever trust
494
+ * RFC1918 bridge subnets, so a misconfigured public-range br-* cannot widen
495
+ * trust beyond the local Docker fabric.
496
+ */
497
+ function peerIsHostLocalDockerBridge(addr, interfaces) {
498
+ const peer = ipv4ToInt(addr);
499
+ if (peer === null) return false;
500
+ for (const [name, addrs] of Object.entries(interfaces)) {
501
+ if (name !== "docker0" && !name.startsWith("br-")) continue;
502
+ for (const info of addrs ?? []) {
503
+ if (info.family !== "IPv4") continue;
504
+ const ifaceIp = ipv4ToInt(info.address);
505
+ const mask = ipv4ToInt(info.netmask);
506
+ if (ifaceIp === null || mask === null) continue;
507
+ const network = (ifaceIp & mask) >>> 0;
508
+ if (!isRfc1918(network)) continue;
509
+ if ((peer & mask) >>> 0 === network) return true;
510
+ }
511
+ }
512
+ return false;
513
+ }
514
+ /**
515
+ * True when `addr` shares an RFC1918 subnet with one of THIS process's own
516
+ * network interfaces. This is the in-container counterpart of
517
+ * peerIsHostLocalDockerBridge: inside a container the Docker bridge is
518
+ * invisible — the container sees only its veth endpoints (eth0/eth1), so the
519
+ * docker0/br-* name filter never matches and the host-local Traefik fronting
520
+ * the stack 401'd every session mint (first hit live on the PAN-1737 UAT
521
+ * stack). A peer inside one of the container's attached Docker-network
522
+ * subnets can only be another container on those networks — the server
523
+ * publishes no host port (Traefik labels only), so no LAN client can ever
524
+ * present such an address. Only consulted when running inside a container.
525
+ */
526
+ function peerIsLocalContainerNetwork(addr, interfaces) {
527
+ const peer = ipv4ToInt(addr);
528
+ if (peer === null) return false;
529
+ for (const addrs of Object.values(interfaces)) for (const info of addrs ?? []) {
530
+ if (info.family !== "IPv4" || info.internal) continue;
531
+ const ifaceIp = ipv4ToInt(info.address);
532
+ const mask = ipv4ToInt(info.netmask);
533
+ if (ifaceIp === null || mask === null) continue;
534
+ const network = (ifaceIp & mask) >>> 0;
535
+ if (!isRfc1918(network)) continue;
536
+ if ((peer & mask) >>> 0 === network) return true;
537
+ }
538
+ return false;
539
+ }
540
+ /** Docker creates /.dockerenv in every container — stable in-container signal. */
541
+ const runningInContainer = existsSync("/.dockerenv");
542
+ /**
543
+ * A request we trust to mint a session because it originated from this machine —
544
+ * the local browser hitting the dashboard directly (127.0.0.1), or the
545
+ * host-local Traefik that fronts overdeck.localhost. In Docker, that Traefik reaches
546
+ * the host server from a Docker-bridge IP (e.g. 172.18.0.2), NOT 127.0.0.1, so
547
+ * a literal-loopback check alone silently breaks the zero-step bootstrap for
548
+ * every overdeck.localhost user — their session mint 401s and no cookie is ever set.
549
+ *
550
+ * This is the auto-bootstrap that removes the manual one-time #overdeck_token
551
+ * step: any browser on overdeck.localhost gets a session with no user action. It is
552
+ * NOT a security downgrade for the LAN — the raw API ports bind 0.0.0.0, but a
553
+ * direct LAN hit has a non-loopback, non-Docker-bridge peer and is rejected. We
554
+ * read ONLY the real TCP peer (request.remoteAddress), never X-Forwarded-For,
555
+ * which a caller could spoof.
556
+ */
557
+ function isLoopbackPeer(request) {
558
+ const remoteAddress = request.remoteAddress;
559
+ const raw = remoteAddress && typeof remoteAddress === "object" && "_tag" in remoteAddress ? Option.getOrElse(remoteAddress, () => "") : typeof remoteAddress === "string" ? remoteAddress : "";
560
+ if (!raw) return false;
561
+ const addr = raw.startsWith("::ffff:") ? raw.slice(7) : raw;
562
+ if (addr === "127.0.0.1" || addr === "::1") return true;
563
+ if (peerIsHostLocalDockerBridge(addr, networkInterfaces())) return true;
564
+ return runningInContainer && peerIsLocalContainerNetwork(addr, networkInterfaces());
565
+ }
566
+ function rejectUnauthorizedDashboardSessionMintRequest(request) {
567
+ if (!getInternalTokenSync()) return jsonResponse({ error: "dashboard session token not configured" }, { status: 503 });
568
+ if (!hasDashboardInternalToken(request) && !isLoopbackPeer(request)) return jsonResponse({ error: "unauthorized" }, { status: 401 });
569
+ return null;
570
+ }
571
+ function rejectUnauthorizedDashboardRequest(request) {
572
+ if (!getInternalTokenSync()) return jsonResponse({ error: "dashboard session token not configured" }, { status: 503 });
573
+ if (!hasDashboardAuth(request)) return jsonResponse({ error: "unauthorized" }, { status: 401 });
574
+ return null;
575
+ }
576
+ //#endregion
367
577
  //#region routes/jsonl-resolver.ts
578
+ init_acp_conversation_parser();
368
579
  init_codex_conversation_parser();
369
580
  init_ohmypi_conversation_parser();
370
581
  init_pi_conversation_parser();
@@ -390,6 +601,8 @@ init_conversation_service();
390
601
  * order no longer reflects time-recency. We disambiguate
391
602
  * across all listed IDs by JSONL mtime.
392
603
  * 3. runtime state — in-process mirror, populated by hooks
604
+ * 4. agents registry — the DB row's session_id recorded at spawn; survives
605
+ * janitor removal of the agent directory
393
606
  *
394
607
  * Async-only (fs/promises) because this code path runs inside the dashboard
395
608
  * server's event loop.
@@ -475,6 +688,10 @@ async function resolveClaudeSessionId(agentId, opts = {}) {
475
688
  const runtimeState = await (opts.getRuntimeStateAsync ?? ((id) => Effect.runPromise(getAgentRuntimeState(id))))(agentId);
476
689
  if (runtimeState?.claudeSessionId) return runtimeState.claudeSessionId;
477
690
  } catch {}
691
+ if (!opts.agentsDirOverride) {
692
+ const registrySessionId = getAgentStateSync(agentId)?.sessionId?.trim();
693
+ if (registrySessionId) return registrySessionId;
694
+ }
478
695
  return null;
479
696
  }
480
697
  /**
@@ -532,7 +749,7 @@ async function readRecordedState(agentId, opts) {
532
749
  * Resolve the harness for transcript routing. Reads state.json, then
533
750
  * self-corrects when the recorded value is stale.
534
751
  *
535
- * Trust an explicit non-default harness (codex / pi / ohmypi) — it was chosen
752
+ * Trust an explicit non-default harness (codex / pi / ohmypi / acp) — it was chosen
536
753
  * by resolveHarness at spawn time and is not the generic fallback. The
537
754
  * 'claude-code' default, however, goes stale in one observed case: a
538
755
  * wipe-and-respawn that changed the provider-default harness left state.json
@@ -550,8 +767,9 @@ async function readRecordedState(agentId, opts) {
550
767
  */
551
768
  async function resolveAgentHarness(agentId, opts = {}) {
552
769
  const recorded = (await readRecordedState(agentId, opts)).harness;
553
- if (recorded === "codex" || recorded === "pi" || recorded === "ohmypi") return recorded;
770
+ if (recorded === "codex" || recorded === "pi" || recorded === "ohmypi" || recorded === "acp") return recorded;
554
771
  if (await agentHasClaudeTranscript(agentId, opts)) return recorded ?? "claude-code";
772
+ if (await resolveAcpTranscriptPath(agentId, opts)) return "acp";
555
773
  if (await resolveCodexRolloutPath(agentId, opts)) return "codex";
556
774
  if (await resolvePiSessionPath(agentId, opts)) return "ohmypi";
557
775
  return recorded;
@@ -581,7 +799,7 @@ async function resolveCodexRolloutPath(agentId, opts = {}) {
581
799
  const agentDir = join(opts.agentsDirOverride ?? join(getOverdeckHome(), "agents"), agentId);
582
800
  const codexHome = join(agentDir, "codex-home");
583
801
  if (!await pathExists$1(codexHome)) return null;
584
- const { findRolloutPath, findLatestRollout } = await import("./codex-DNyiQDKW.js");
802
+ const { findRolloutPath, findLatestRollout } = await import("./codex-GcHuUF-k.js");
585
803
  const threadId = (await readOptional(join(agentDir, "codex-thread-id")))?.trim();
586
804
  if (threadId) {
587
805
  const rollout = findRolloutPath(codexHome, threadId);
@@ -589,6 +807,11 @@ async function resolveCodexRolloutPath(agentId, opts = {}) {
589
807
  }
590
808
  return findLatestRollout(codexHome);
591
809
  }
810
+ /** Resolve the normalized transcript written by the persistent ACP host. */
811
+ async function resolveAcpTranscriptPath(agentId, opts = {}) {
812
+ const transcriptPath = join(opts.agentsDirOverride ?? join(getOverdeckHome(), "agents"), agentId, "acp-session.jsonl");
813
+ return await pathExists$1(transcriptPath) ? transcriptPath : null;
814
+ }
592
815
  /**
593
816
  * Resolve the pi/kimi session JSONL (PAN-1908). Pi writes its session transcript
594
817
  * as `<iso-ts>_<session-id>.jsonl` either in the agent dir's `sessions/` subdir
@@ -599,6 +822,7 @@ async function resolveCodexRolloutPath(agentId, opts = {}) {
599
822
  async function resolvePiSessionPath(agentId, opts = {}) {
600
823
  const agentDir = join(opts.agentsDirOverride ?? join(getOverdeckHome(), "agents"), agentId);
601
824
  const NON_TRANSCRIPT = new Set([
825
+ "acp-session.jsonl",
602
826
  "cost-events.jsonl",
603
827
  "activity.jsonl",
604
828
  "pending-events.jsonl"
@@ -637,17 +861,19 @@ async function resolveJsonlPath(agentId, workspacePath, opts = {}) {
637
861
  const behavior = behaviorForHarness(harness);
638
862
  if (behavior.transcriptKind === "codex-rollout-jsonl") return resolveCodexRolloutPath(agentId, opts);
639
863
  if (behavior.transcriptKind === "ohmypi-jsonl") return resolvePiSessionPath(agentId, opts);
864
+ if (behavior.transcriptKind === "acp-jsonl") return resolveAcpTranscriptPath(agentId, opts);
640
865
  const claudeSessionId = await resolveClaudeSessionId(agentId, opts);
641
866
  if (!claudeSessionId) {
642
867
  logTranscriptResolution(agentId, `missing-session-id:${harness ?? "unknown"}`, `failed harness=${harness ?? "unknown"} reason=no-session-id checked=session.id,sessions.json,runtime-state`, opts);
643
868
  return null;
644
869
  }
645
- const jsonlPath = join(opts.claudeProjectsDirOverride ?? join(homedir(), ".claude", "projects"), encodeClaudeProjectDir(workspacePath), `${claudeSessionId}.jsonl`);
870
+ const effectiveWorkspacePath = (await readRecordedState(agentId, opts)).workspace ?? workspacePath;
871
+ const jsonlPath = join(opts.claudeProjectsDirOverride ?? join(homedir(), ".claude", "projects"), encodeClaudeProjectDir(effectiveWorkspacePath), `${claudeSessionId}.jsonl`);
646
872
  if (await pathExists$1(jsonlPath)) {
647
873
  logTranscriptResolution(agentId, `resolved:${jsonlPath}`, `resolved harness=${harness ?? "claude-code"} sessionId=${claudeSessionId} path=${jsonlPath}`, opts);
648
874
  return jsonlPath;
649
875
  }
650
- logTranscriptResolution(agentId, `missing-jsonl:${jsonlPath}`, `failed harness=${harness ?? "claude-code"} reason=jsonl-missing sessionId=${claudeSessionId} workspace=${workspacePath} expectedPath=${jsonlPath}`, opts);
876
+ logTranscriptResolution(agentId, `missing-jsonl:${jsonlPath}`, `failed harness=${harness ?? "claude-code"} reason=jsonl-missing sessionId=${claudeSessionId} workspace=${effectiveWorkspacePath} expectedPath=${jsonlPath}`, opts);
651
877
  return null;
652
878
  }
653
879
  //#endregion
@@ -807,7 +1033,7 @@ async function getWorkAgentLifecycleStateSnapshot(agentOrIssueId) {
807
1033
  runtimeState: runtime,
808
1034
  agentStatus,
809
1035
  canStartFresh: (!hasLiveTmuxSession || hasLiveTmuxSession && isStopped) && (!requiresSessionResetBeforeFreshStart || isOrphaned),
810
- canResumeSession: !isRunning && hasResumableBackingState && (isStopped || isCrashed),
1036
+ canResumeSession: !isRunning && hasSavedSession && hasResumableBackingState && (isStopped || isCrashed),
811
1037
  canRestartWithContext: hasAgentState && hasWorkspace,
812
1038
  canResetSession: hasSavedSession && hasResumableBackingState,
813
1039
  requiresSessionResetBeforeFreshStart,
@@ -834,15 +1060,14 @@ const getWorkAgentLifecycleState = (agentOrIssueId) => Effect.promise(() => getW
834
1060
  * expected feature branch. Reports drift and missing-workspace state
835
1061
  * because both are silent failure modes today.
836
1062
  *
837
- * Cache: in-memory, 30s TTL, keyed by absolute path. Cache entries are
838
- * invalidated when the mtime of <path>/.git/HEAD changes, so a checkout
839
- * under a live conversation is picked up on the next list call without
840
- * waiting for TTL.
1063
+ * Cache: in-memory, keyed by absolute path and invalidated when the mtime of
1064
+ * <path>/.git/HEAD changes. Every read still stats HEAD, so a checkout under a
1065
+ * live conversation is picked up immediately without periodically respawning
1066
+ * two git processes for every unchanged workspace.
841
1067
  *
842
1068
  * All I/O is async (execFile / fs.promises). No execSync, no readFileSync.
843
1069
  */
844
1070
  const execFileAsync$3 = promisify(execFile);
845
- const GIT_INFO_TTL_MS = 3e4;
846
1071
  const GIT_INFO_TIMEOUT_MS = 2e3;
847
1072
  const cache = /* @__PURE__ */ new Map();
848
1073
  /**
@@ -917,22 +1142,20 @@ async function readGitInfoFresh(absPath) {
917
1142
  };
918
1143
  }
919
1144
  /**
920
- * Resolve branch + worktree status for a conversation cwd. Cached for
921
- * GIT_INFO_TTL_MS, invalidated on HEAD mtime change.
1145
+ * Resolve branch + worktree status for a conversation cwd. Cached until the
1146
+ * HEAD mtime changes.
922
1147
  */
923
1148
  async function resolveConversationGitInfo(absPath) {
924
- const now = Date.now();
925
1149
  const headMtimeMs = await readHeadMtimeMs(absPath);
926
1150
  if (headMtimeMs === null) return {
927
1151
  branch: null,
928
1152
  isWorktree: false
929
1153
  };
930
1154
  const cached = cache.get(absPath);
931
- if (cached && cached.expiresAt > now && cached.headMtimeMs === headMtimeMs) return cached.value;
1155
+ if (cached && cached.headMtimeMs === headMtimeMs) return cached.value;
932
1156
  const value = await readGitInfoFresh(absPath);
933
1157
  cache.set(absPath, {
934
1158
  value,
935
- expiresAt: now + GIT_INFO_TTL_MS,
936
1159
  headMtimeMs
937
1160
  });
938
1161
  return value;
@@ -976,7 +1199,8 @@ function buildPanStartArgs(input) {
976
1199
  "--model",
977
1200
  input.model,
978
1201
  ...input.harness ? ["--harness", input.harness] : [],
979
- ...input.allowHost ? ["--host", "--yes"] : []
1202
+ ...input.allowHost ? ["--host", "--yes"] : [],
1203
+ ...input.offBook ? ["--off-book"] : []
980
1204
  ];
981
1205
  }
982
1206
  /**
@@ -1085,7 +1309,7 @@ function emitStartAgentPhase(issueId, phase, status, reason, details = {}) {
1085
1309
  });
1086
1310
  }
1087
1311
  const INTERNAL_TOKEN_HEADER = "x-overdeck-internal-token";
1088
- function constantTimeTokenEqual$1(provided, expected) {
1312
+ function constantTimeTokenEqual(provided, expected) {
1089
1313
  if (!provided) return false;
1090
1314
  const providedBuffer = Buffer.from(provided, "utf8");
1091
1315
  const expectedBuffer = Buffer.from(expected, "utf8");
@@ -1111,7 +1335,7 @@ async function validateAgentRuntimeEventAuth(request) {
1111
1335
  error: "internal token not configured"
1112
1336
  }, { status: 503 })
1113
1337
  };
1114
- if (constantTimeTokenEqual$1(getHeaderFromMap(request.headers, INTERNAL_TOKEN_HEADER), expected)) return { ok: true };
1338
+ if (constantTimeTokenEqual(getHeaderFromMap(request.headers, INTERNAL_TOKEN_HEADER), expected)) return { ok: true };
1115
1339
  return {
1116
1340
  ok: false,
1117
1341
  response: jsonResponse({
@@ -1727,7 +1951,7 @@ const getAgentOutputRoute = HttpRouter.add("GET", "/api/agents/:id/output", http
1727
1951
  } catch {}
1728
1952
  let stdout;
1729
1953
  if (isRemote && vmName) {
1730
- const { getRemoteAgentOutput } = await import("./remote-agents-D6O_zETW.js");
1954
+ const { getRemoteAgentOutput } = await import("./remote-agents-CA3Ihlxj.js");
1731
1955
  stdout = await getRemoteAgentOutput(id, vmName, parseInt(String(lines), 10) || 100);
1732
1956
  } else stdout = await Effect.runPromise(capturePane(id, parseInt(String(lines), 10) || 100));
1733
1957
  if (!stdout || stdout.trim() === "" || stdout.trim() === "Session not found") {
@@ -1792,6 +2016,20 @@ async function buildConversationResponse(id) {
1792
2016
  streaming: false
1793
2017
  };
1794
2018
  }
2019
+ if (harness === "acp") {
2020
+ const sessionFile = await resolveAcpTranscriptPath(id);
2021
+ if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;
2022
+ const result = await parseAcpConversationMessages(sessionFile);
2023
+ return {
2024
+ ...result,
2025
+ messages: result.messages.map((message) => message.role === "assistant" ? {
2026
+ ...message,
2027
+ completedAt: message.completedAt ?? message.createdAt,
2028
+ streaming: false
2029
+ } : message),
2030
+ streaming: false
2031
+ };
2032
+ }
1795
2033
  let jsonlPath = null;
1796
2034
  const pinnedSessionId = await readLauncherPinnedSessionId(id);
1797
2035
  if (pinnedSessionId) {
@@ -1907,7 +2145,7 @@ const postAgentTellRoute = postAgentMessageLikeRoute("/api/agents/:id/tell");
1907
2145
  const postAgentPokeRoute = HttpRouter.add("POST", "/api/agents/:id/poke", httpHandler(Effect.gen(function* () {
1908
2146
  const id = (yield* HttpRouter.params)["id"] ?? "";
1909
2147
  const { message } = yield* readJsonBody$1;
1910
- const pokeMsg = message || "You seem to have been inactive for a while. If you're stuck:\n1. Check your current task in continue.vbrief.json\n2. Try an alternative approach if blocked\n3. Ask for help if needed\n\nWhat's your current status?";
2148
+ const pokeMsg = message || "You seem to have been inactive for a while. If you're stuck:\n1. Check your current xBRIEF task with `pan task show <issue> <item-id>`\n2. Try an alternative approach if blocked\n3. Ask for help if needed\n\nWhat's your current status?";
1911
2149
  yield* Effect.promise(() => messageAgent(id, pokeMsg));
1912
2150
  return jsonResponse({
1913
2151
  success: true,
@@ -2148,7 +2386,7 @@ async function cleanupConversationAttachments(name) {
2148
2386
  * sessions that the lifecycle poller missed. */
2149
2387
  async function cleanupOrphanedConversationAttachments() {
2150
2388
  try {
2151
- const { listConversations } = await import("./conversations-H9if0bCQ.js");
2389
+ const { listConversations } = await import("./conversations-CAsJF9kh.js");
2152
2390
  const convNames = new Set(listConversations().map((c) => c.name));
2153
2391
  const root = getConversationAttachmentsRoot();
2154
2392
  await runInBatches((await readdir(root, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !convNames.has(entry.name)).map((entry) => join(root, entry.name)), 10, (path) => rm(path, {
@@ -2426,7 +2664,7 @@ async function stopConversationRuntime(conv, name) {
2426
2664
  function shellQuote(str) {
2427
2665
  return "'" + str.replace(/'/g, "'\"'\"'") + "'";
2428
2666
  }
2429
- const SAFE_MODEL_PATTERN = /^[a-zA-Z0-9_.:\/-]+$/;
2667
+ const SAFE_MODEL_PATTERN = MODEL_ID_PATTERN;
2430
2668
  const SAFE_EFFORT_PATTERN = /^(low|medium|high)$/;
2431
2669
  const SAFE_ISSUE_ID_PATTERN = /^[A-Z0-9]+-[0-9]+$/;
2432
2670
  const PI_CONVERSATION_SOURCE_CONTRACT = [
@@ -2438,7 +2676,7 @@ async function resolveAllowedHarness(requested, model) {
2438
2676
  if (!model) return "claude-code";
2439
2677
  return resolveHarness({
2440
2678
  model,
2441
- explicit: requested === "ohmypi" || requested === "claude-code" || requested === "codex" ? requested : void 0
2679
+ explicit: requested === "ohmypi" || requested === "claude-code" || requested === "codex" || requested === "acp" ? requested : void 0
2442
2680
  });
2443
2681
  }
2444
2682
  async function isInsideGitWorkTree(dir) {
@@ -2520,6 +2758,10 @@ function shouldUseSupervisorForConversation(harness, options = {}) {
2520
2758
  return getHarnessBehavior(harness).supportsPtySupervisor && process.env.OVERDECK_DOCKER_WORKSPACE !== "1" && process.env.PAN_DOCKER !== "1";
2521
2759
  }
2522
2760
  async function waitForConversationRuntimeReady(tmuxSession, harness, mode) {
2761
+ if (harness === "acp") {
2762
+ await waitForAcpHostReady(tmuxSession);
2763
+ return;
2764
+ }
2523
2765
  const transcriptKind = getHarnessBehavior(harness).transcriptKind;
2524
2766
  if (transcriptKind === "ohmypi-jsonl") {
2525
2767
  if (!await waitForPiTuiReady(tmuxSession)) throw new Error(`Pi (ohmypi) did not become interactive in ${tmuxSession} within the startup window — MCP server connections are likely still in progress. Retry once MCP servers finish connecting, or check ~/.claude/mcp.json for a slow/hanging server.`);
@@ -2535,7 +2777,7 @@ async function handleConversationSwitchModel(name, body) {
2535
2777
  const currentHarness = conv.harness ?? "claude-code";
2536
2778
  const requestedHarness = body["harness"];
2537
2779
  let harness = currentHarness;
2538
- if (requestedHarness === "ohmypi" || requestedHarness === "pi" || requestedHarness === "claude-code" || requestedHarness === "codex") {
2780
+ if (requestedHarness === "ohmypi" || requestedHarness === "pi" || requestedHarness === "claude-code" || requestedHarness === "codex" || requestedHarness === "acp") {
2539
2781
  const requestedRuntime = requestedHarness === "pi" ? "ohmypi" : requestedHarness;
2540
2782
  if (requestedRuntime !== currentHarness) {
2541
2783
  const policyModel = model ?? conv.model ?? "";
@@ -2688,14 +2930,25 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
2688
2930
  let providerExportsStr = "";
2689
2931
  let piFields;
2690
2932
  let codexFields;
2933
+ let acpFields;
2691
2934
  let codexTransport;
2692
- if (model) {
2935
+ if (behavior.launchCommandKind === "acp-host") {
2936
+ if (!model) throw new Error("ACP conversation requires a model");
2937
+ if (!SAFE_MODEL_PATTERN.test(model)) throw new Error("Invalid model name");
2938
+ const sessionIdPath = join(getOverdeckHome(), "agents", tmuxSession, "acp-session-id");
2939
+ const resumeSessionId = resume ? await readFile(sessionIdPath, "utf-8").then((value) => value.trim() || void 0).catch(() => void 0) : void 0;
2940
+ await rm(sessionIdPath, { force: true });
2941
+ acpFields = {
2942
+ ...getAcpLauncherFields(tmuxSession, model, cwd, harnessLaunch.binaryPath, "work"),
2943
+ resumeSessionId
2944
+ };
2945
+ runtimeCommand = "acp-host";
2946
+ } else if (model) {
2693
2947
  if (!SAFE_MODEL_PATTERN.test(model)) throw new Error("Invalid model name");
2694
2948
  runtimeCommand = await getAgentRuntimeBaseCommand(model, void 0, void 0, harness);
2695
2949
  const mode = resolvePermissionModeSync();
2696
2950
  if (!runtimeCommand.includes("--permission-mode")) runtimeCommand = `${runtimeCommand} --permission-mode ${mode === "auto" ? "auto" : BYPASS_PERMISSION_MODE}`;
2697
2951
  providerExportsStr = (await getProviderExportsForModel(model)).trim();
2698
- await getProviderEnvForModel(model);
2699
2952
  if (behavior.transcriptKind === "ohmypi-jsonl") {
2700
2953
  if (getProviderForModelSync(model).name === "openai") {
2701
2954
  const auth = await getOhmypiCodexAuthStatus({ refreshIfExpired: true });
@@ -2727,7 +2980,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
2727
2980
  const codexApprovalPolicy = codexPermMode === "full-access" ? "never" : "on-request";
2728
2981
  const codexSandboxMode = codexPermMode === "full-access" ? "danger-full-access" : codexPermMode === "read-only" ? "read-only" : "workspace-write";
2729
2982
  const codexApprovalsReviewer = codexPermMode === "auto-review" ? "auto_review" : void 0;
2730
- const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-DNyiQDKW.js");
2983
+ const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-GcHuUF-k.js");
2731
2984
  initCodexHome(codexHome, {
2732
2985
  trustedDir: cwd,
2733
2986
  approvalPolicy: codexApprovalPolicy,
@@ -2757,7 +3010,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
2757
3010
  await writePtyToken(tmuxSession);
2758
3011
  }
2759
3012
  let channelsBridgeMcpConfig;
2760
- if (!piFields && !codexFields && !plainFork && isClaudeCodeChannelsEnabled() && (!model || getProviderForModelSync(model).name === "anthropic") && process.env.CLAUDE_CODE_USE_BEDROCK !== "1" && process.env.CLAUDE_CODE_USE_VERTEX !== "1" && process.env.CLAUDE_CODE_USE_FOUNDRY !== "1" && process.env.OVERDECK_DOCKER_WORKSPACE !== "1" && process.env.PAN_DOCKER !== "1") {
3013
+ if (!piFields && !codexFields && !acpFields && !plainFork && isClaudeCodeChannelsEnabled() && (!model || getProviderForModelSync(model).name === "anthropic") && process.env.CLAUDE_CODE_USE_BEDROCK !== "1" && process.env.CLAUDE_CODE_USE_VERTEX !== "1" && process.env.CLAUDE_CODE_USE_FOUNDRY !== "1" && process.env.OVERDECK_DOCKER_WORKSPACE !== "1" && process.env.PAN_DOCKER !== "1") {
2761
3014
  channelsBridgeMcpConfig = join(stateDir, "agent-mcp.json");
2762
3015
  writeBridgeTokenSync(tmuxSession);
2763
3016
  await writeChannelsBridgeMcpConfig(channelsBridgeMcpConfig, tmuxSession);
@@ -2771,19 +3024,19 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
2771
3024
  unsetProviderEnv: true,
2772
3025
  overdeckEnv: {
2773
3026
  ...issueId ? { issueId } : {},
2774
- ...piFields || codexFields || useSupervisor ? { agentId: tmuxSession } : {}
3027
+ ...piFields || codexFields || acpFields || useSupervisor ? { agentId: tmuxSession } : {}
2775
3028
  },
2776
3029
  extraEnvExports: [harnessLaunch.pathExport, `export OVERDECK_DASHBOARD_URL="http://127.0.0.1:${process.env["API_PORT"] ?? process.env["PORT"] ?? "3011"}"`],
2777
3030
  providerExports: providerExportsStr || void 0,
2778
3031
  trapHup: true,
2779
3032
  baseCommand: runtimeCommand,
2780
- appendSystemPromptFiles: piFields ? await piConversationSystemPromptFiles(cwd) : codexFields ? [] : await claudeConversationSystemPromptFiles(cwd),
3033
+ appendSystemPromptFiles: piFields ? await piConversationSystemPromptFiles(cwd) : codexFields || acpFields ? [] : await claudeConversationSystemPromptFiles(cwd),
2781
3034
  model: launcherModel,
2782
- ...piFields ?? codexFields ?? {
3035
+ ...piFields ?? codexFields ?? acpFields ?? {
2783
3036
  resumeSessionId: resume ? claudeSessionId : void 0,
2784
3037
  sessionId: resume ? void 0 : claudeSessionId
2785
3038
  },
2786
- extraArgs: !piFields && effort ? `--effort "${effort}"` : void 0,
3039
+ extraArgs: !piFields && !acpFields && effort ? `--effort "${effort}"` : void 0,
2787
3040
  keepAlive: true,
2788
3041
  fileMode: 448,
2789
3042
  channelsBridgeMcpConfig,
@@ -2796,7 +3049,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
2796
3049
  } catch {}
2797
3050
  console.log(`[claude-invoke] purpose=conversation-session | model=${model || "default"} | source=conversations.ts:spawnConversationSession | session=${tmuxSession} | resume=${resume} | command="${runtimeCommand}"`);
2798
3051
  try {
2799
- const { preTrustDirectory } = await import("./workspace-manager-B1WOfKvB.js");
3052
+ const { preTrustDirectory } = await import("./workspace-manager-CWT55qeI.js");
2800
3053
  preTrustDirectory(cwd);
2801
3054
  } catch {}
2802
3055
  try {
@@ -2813,7 +3066,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
2813
3066
  const codexHomeDir = codexFields.codexHome;
2814
3067
  (async () => {
2815
3068
  try {
2816
- const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-DNyiQDKW.js");
3069
+ const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-GcHuUF-k.js");
2817
3070
  const rollout = await waitForCodexRollout(codexHomeDir, 12e4);
2818
3071
  if (rollout) {
2819
3072
  const threadId = extractThreadIdFromRollout(rollout);
@@ -2889,10 +3142,14 @@ async function handleConversationCreate(body, deps) {
2889
3142
  await spawnConversationSession(tmuxSession, cwd, claudeSessionId, model, effort, issueId, false, harness);
2890
3143
  console.log(`[conversations] tmux session ${tmuxSession} spawned, sessionId: ${claudeSessionId}`);
2891
3144
  await waitForConversationRuntimeReady(tmuxSession, harness, "spawn");
2892
- if (message) await deliverAgentMessage(tmuxSession, message, "conversation-message", resolveConversationDeliveryMethod(conv));
3145
+ if (message) {
3146
+ const delivery = await deliverAgentMessage(tmuxSession, message, "conversation-message", resolveConversationDeliveryMethod(conv));
3147
+ if (harness === "acp" && !delivery.ok) throw new Error(`ACP initial prompt did not land: ${delivery.failure ?? "unknown failure"}`);
3148
+ }
2893
3149
  } catch (spawnErr) {
2894
3150
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
2895
3151
  console.error(`[conversations] background spawn failed for ${tmuxSession}: ${msg}`);
3152
+ if (harness === "acp") await stopConversationRuntime(conv, name);
2896
3153
  updateSpawnError(name, msg);
2897
3154
  getEventStore().emitOnly({
2898
3155
  type: "conversation.created",
@@ -2978,6 +3235,9 @@ async function handleConversationResume(name, body, deps) {
2978
3235
  reattached: false,
2979
3236
  sessionAlive: true
2980
3237
  });
3238
+ } catch (error) {
3239
+ if (harness === "acp") await stopConversationRuntime(conv, name);
3240
+ throw error;
2981
3241
  } finally {
2982
3242
  respawn.done();
2983
3243
  }
@@ -3012,13 +3272,16 @@ async function handleConversationRestartAll(deps) {
3012
3272
  const results = [];
3013
3273
  for (const conv of convs) {
3014
3274
  const respawn = markRespawnPending(conv.tmuxSession);
3275
+ let attemptedHarness = conv.harness ?? "claude-code";
3015
3276
  try {
3016
3277
  await Effect.runPromise(killSession(conv.tmuxSession).pipe(Effect.catch(() => Effect.succeed(void 0))));
3017
3278
  const oldSessionId = conv.claudeSessionId;
3018
3279
  const sessionFileForResume = await deps.resolveSessionFile(conv);
3019
3280
  const canResume = !!oldSessionId && !!sessionFileForResume && existsSync(sessionFileForResume);
3020
3281
  const harness = await resolveAllowedHarness(conv.harness, conv.model);
3282
+ attemptedHarness = harness;
3021
3283
  await spawnConversationSession(conv.tmuxSession, conv.cwd, oldSessionId ?? randomUUID$1(), conv.model ?? void 0, conv.effort ?? void 0, conv.issueId ?? void 0, canResume, harness);
3284
+ if (harness === "acp") await waitForConversationRuntimeReady(conv.tmuxSession, harness, "respawn");
3022
3285
  setConversationHarness(conv.name, harness);
3023
3286
  markConversationActive(conv.name);
3024
3287
  results.push({
@@ -3029,6 +3292,7 @@ async function handleConversationRestartAll(deps) {
3029
3292
  } catch (err) {
3030
3293
  const msg = err instanceof Error ? err.message : String(err);
3031
3294
  console.error(`[conversations] Failed to restart ${conv.name}:`, msg);
3295
+ if (attemptedHarness === "acp") await stopConversationRuntime(conv, conv.name);
3032
3296
  results.push({
3033
3297
  name: conv.name,
3034
3298
  model: conv.model,
@@ -3232,6 +3496,7 @@ function pickDeliverAs(bodyDeliverAs) {
3232
3496
  }
3233
3497
  function resolveConversationDeliveryMethod(conv) {
3234
3498
  const harness = conv.harness ?? "claude-code";
3499
+ if (harness === "acp") return "auto";
3235
3500
  if (isPiControlChannelHarness(harness)) return "auto";
3236
3501
  if (harness === "codex" && loadConfigSync().config.codex?.transport !== "tui") return "auto";
3237
3502
  return conv.deliveryMethod ?? (getHarnessBehavior(harness).deliveryKind === "rpc-fifo" ? "tmux" : "auto");
@@ -3692,14 +3957,14 @@ const postInternalAgentPermissionRequestRoute = HttpRouter.add("POST", "/api/int
3692
3957
  error: "missing agent id"
3693
3958
  }, { status: 400 });
3694
3959
  const request = yield* HttpServerRequest.HttpServerRequest;
3695
- const { INTERNAL_TOKEN_HEADER, getInternalTokenSync } = yield* Effect.promise(() => import("./internal-token-C9DdEOKY.js"));
3960
+ const { INTERNAL_TOKEN_HEADER, getInternalTokenSync } = yield* Effect.promise(() => import("./internal-token-BrcU6qfe.js"));
3696
3961
  const expected = getInternalTokenSync();
3697
3962
  if (!expected) return jsonResponse({
3698
3963
  ok: false,
3699
3964
  error: "internal token not configured"
3700
3965
  }, { status: 503 });
3701
3966
  const rawHeader = request.headers[INTERNAL_TOKEN_HEADER];
3702
- if (!constantTimeTokenEqual$1(Array.isArray(rawHeader) ? rawHeader[0] : rawHeader, expected)) return jsonResponse({
3967
+ if (!constantTimeTokenEqual(Array.isArray(rawHeader) ? rawHeader[0] : rawHeader, expected)) return jsonResponse({
3703
3968
  ok: false,
3704
3969
  error: "forbidden"
3705
3970
  }, { status: 403 });
@@ -3959,7 +4224,7 @@ const getAgentHealthHistoryRoute = HttpRouter.add("GET", "/api/agents/:id/health
3959
4224
  const request = yield* HttpServerRequest.HttpServerRequest;
3960
4225
  const urlOpt = HttpServerRequest.toURL(request);
3961
4226
  const hours = Option.isSome(urlOpt) ? urlOpt.value.searchParams.get("hours") ?? "24" : "24";
3962
- const { getHealthHistory } = yield* Effect.promise(() => import("./health-events-C1whmd58.js"));
4227
+ const { getHealthHistory } = yield* Effect.promise(() => import("./health-events-BDOHby5Q.js"));
3963
4228
  const endTime = /* @__PURE__ */ new Date();
3964
4229
  const startTime = /* @__PURE__ */ new Date(endTime.getTime() - parseInt(hours) * 60 * 60 * 1e3);
3965
4230
  const events = getHealthHistory(id, startTime.toISOString(), endTime.toISOString());
@@ -4000,7 +4265,7 @@ function emitAgentRuntimeEvent(id, body, timestamp) {
4000
4265
  detail: String(decoded.failure)
4001
4266
  }, { status: 400 })
4002
4267
  };
4003
- const { AgentStateService } = yield* Effect.promise(() => import("./agent-state-service-CsQ07Pxe.js"));
4268
+ const { AgentStateService } = yield* Effect.promise(() => import("./agent-state-service-D5UGKIJL.js"));
4004
4269
  yield* (yield* AgentStateService).emit(decoded.success);
4005
4270
  return {
4006
4271
  ok: true,
@@ -4082,7 +4347,7 @@ const getAgentRuntimeRoute = HttpRouter.add("GET", "/api/agents/:id/runtime", ht
4082
4347
  success: false,
4083
4348
  error: "missing agent id"
4084
4349
  }, { status: 400 });
4085
- const { AgentStateService } = yield* Effect.promise(() => import("./agent-state-service-CsQ07Pxe.js"));
4350
+ const { AgentStateService } = yield* Effect.promise(() => import("./agent-state-service-D5UGKIJL.js"));
4086
4351
  const snapshot = yield* (yield* AgentStateService).get(id);
4087
4352
  if (!snapshot) return jsonResponse({
4088
4353
  success: false,
@@ -4146,57 +4411,129 @@ function saveAgentStateAndEmitEvent(state, event) {
4146
4411
  function saveAgentStateAndEmitEventProgram(state, event) {
4147
4412
  return Effect.sync(() => saveAgentStateAndEmitEvent(state, event));
4148
4413
  }
4149
- /**
4150
- * Dependency-injected variant for tests.
4151
- */
4152
- function saveAgentStateAndEmitEventWithDeps(db, eventStore, state, event) {
4153
- prepareAgentStateForSave(state);
4154
- writeAgentStateJsonSync(state);
4414
+ function writeProjectionRows(db, state, event) {
4155
4415
  const record = event;
4156
4416
  const timestamp = record["timestamp"] ?? (/* @__PURE__ */ new Date()).toISOString();
4157
4417
  const timestampMs = new Date(timestamp).getTime();
4158
4418
  const payload = JSON.stringify(record["payload"] ?? {});
4159
4419
  const updatedAt = Date.now();
4420
+ db.prepare(`INSERT OR IGNORE INTO issues (id, stage, updated_at) VALUES (?, 'working', ?)`).run(state.issueId, updatedAt);
4421
+ db.prepare(`INSERT OR REPLACE INTO agents (${AGENT_COLUMNS_FOR_DB.join(", ")}) VALUES (${AGENT_COLUMNS_FOR_DB.map(() => "?").join(", ")})`).run(...stateToOverdeckParamsForDb(state, updatedAt));
4422
+ db.prepare(`INSERT INTO events (type, timestamp, payload) VALUES (?, ?, ?)`).run(event.type, timestampMs, payload);
4423
+ const sequence = db.prepare(`SELECT last_insert_rowid() AS sequence`).get()?.sequence ?? 0;
4424
+ return {
4425
+ sequence,
4426
+ stored: buildStoredEvent(event, sequence)
4427
+ };
4428
+ }
4429
+ function emitCommittedProjection(eventStore, state, event, rows) {
4430
+ eventStore.emitStored(rows.stored);
4431
+ logAgentLifecycleSync(state.id, `projected ${event.type} (seq=${rows.sequence}) for ${state.id}`);
4432
+ return { sequence: rows.sequence };
4433
+ }
4434
+ function rollbackTransaction(db) {
4435
+ try {
4436
+ db.exec("ROLLBACK");
4437
+ } catch {}
4438
+ }
4439
+ function saveAgentStateAndEmitEventWithDeps(db, eventStore, state, event) {
4440
+ prepareAgentStateForSave(state);
4441
+ writeAgentStateJsonSync(state);
4160
4442
  db.exec("BEGIN IMMEDIATE");
4161
4443
  try {
4162
- db.prepare(`INSERT OR IGNORE INTO issues (id, stage, updated_at) VALUES (?, 'working', ?)`).run(state.issueId, updatedAt);
4163
- db.prepare(`INSERT OR REPLACE INTO agents (${AGENT_COLUMNS_FOR_DB.join(", ")}) VALUES (${AGENT_COLUMNS_FOR_DB.map(() => "?").join(", ")})`).run(...stateToOverdeckParamsForDb(state, updatedAt));
4164
- db.prepare(`INSERT INTO events (type, timestamp, payload) VALUES (?, ?, ?)`).run(event.type, timestampMs, payload);
4165
- const sequence = db.prepare(`SELECT last_insert_rowid() AS sequence`).get()?.sequence ?? 0;
4444
+ const rows = writeProjectionRows(db, state, event);
4166
4445
  db.exec("COMMIT");
4167
- const stored = buildStoredEvent(event, sequence);
4168
- eventStore.emitStored(stored);
4169
- logAgentLifecycleSync(state.id, `projected ${event.type} (seq=${sequence}) for ${state.id}`);
4170
- return { sequence };
4446
+ return emitCommittedProjection(eventStore, state, event, rows);
4171
4447
  } catch (err) {
4172
- try {
4173
- db.exec("ROLLBACK");
4174
- } catch {}
4448
+ rollbackTransaction(db);
4175
4449
  throw err;
4176
4450
  }
4177
4451
  }
4178
- //#endregion
4179
- //#region routes/agents/lifecycle-stop.ts
4180
- init_agents$1();
4181
- init_activity_logger();
4182
- init_tmux();
4183
- init_domain_services();
4184
- function createAgentStopHandler(lifecycleEvent) {
4185
- return httpHandler(Effect.gen(function* () {
4186
- const originCheck = validateOrigin(yield* HttpServerRequest.HttpServerRequest);
4187
- if (!originCheck.ok) return jsonResponse({
4188
- ok: false,
4189
- error: originCheck.error
4190
- }, { status: 403 });
4191
- const id = (yield* HttpRouter.params)["id"] ?? "";
4452
+ /**
4453
+ * Claim the single in-flight work-spawn slot and project its starting state.
4454
+ * The SQLite write lock makes the status check and placeholder insert one
4455
+ * operation, so concurrent dashboard requests cannot both launch `pan start`.
4456
+ */
4457
+ function claimAgentStartPlaceholderWithDeps(db, eventStore, state, event, hasLiveTmuxSession) {
4458
+ if (hasLiveTmuxSession) return {
4459
+ claimed: false,
4460
+ reason: "live-session"
4461
+ };
4462
+ prepareAgentStateForSave(state);
4463
+ db.exec("BEGIN IMMEDIATE");
4464
+ try {
4465
+ const current = db.prepare(`SELECT status, started_at AS startedAt FROM agents WHERE id = ?`).get(state.id);
4466
+ const startingWithinGrace = current?.status === "starting" && current.startedAt !== null && Date.now() - current.startedAt < 12e4;
4467
+ if (current?.status === "running" || startingWithinGrace) {
4468
+ rollbackTransaction(db);
4469
+ return {
4470
+ claimed: false,
4471
+ reason: "active-state"
4472
+ };
4473
+ }
4474
+ writeAgentStateJsonSync(state);
4475
+ const rows = writeProjectionRows(db, state, event);
4476
+ db.exec("COMMIT");
4477
+ emitCommittedProjection(eventStore, state, event, rows);
4478
+ return { claimed: true };
4479
+ } catch (err) {
4480
+ rollbackTransaction(db);
4481
+ throw err;
4482
+ }
4483
+ }
4484
+ function claimAgentStartPlaceholderProgram(state, event, hasLiveTmuxSession) {
4485
+ return Effect.sync(() => claimAgentStartPlaceholderWithDeps(getOverdeckDatabaseSync(), getEventStore(), state, event, hasLiveTmuxSession));
4486
+ }
4487
+ /**
4488
+ * Roll back only the placeholder owned by this failed spawn attempt. If the
4489
+ * agent has already advanced to a real running state, the compare fails and
4490
+ * the successful concurrent launch is left untouched.
4491
+ */
4492
+ function rollbackAgentStartPlaceholderWithDeps(db, eventStore, placeholder, fallback, event) {
4493
+ prepareAgentStateForSave(fallback);
4494
+ db.exec("BEGIN IMMEDIATE");
4495
+ try {
4496
+ const current = db.prepare(`SELECT status, model, started_at AS startedAt FROM agents WHERE id = ?`).get(placeholder.id);
4497
+ const placeholderStartedAt = new Date(placeholder.startedAt).getTime();
4498
+ if (!(current?.status === "starting" && current.model === "pending-work-spawn" && current.startedAt === placeholderStartedAt)) {
4499
+ rollbackTransaction(db);
4500
+ return false;
4501
+ }
4502
+ writeAgentStateJsonSync(fallback);
4503
+ const rows = writeProjectionRows(db, fallback, event);
4504
+ db.exec("COMMIT");
4505
+ emitCommittedProjection(eventStore, fallback, event, rows);
4506
+ return true;
4507
+ } catch (err) {
4508
+ rollbackTransaction(db);
4509
+ throw err;
4510
+ }
4511
+ }
4512
+ function rollbackAgentStartPlaceholderProgram(placeholder, fallback, event) {
4513
+ return Effect.sync(() => rollbackAgentStartPlaceholderWithDeps(getOverdeckDatabaseSync(), getEventStore(), placeholder, fallback, event));
4514
+ }
4515
+ //#endregion
4516
+ //#region routes/agents/lifecycle-stop.ts
4517
+ init_agents$1();
4518
+ init_activity_logger();
4519
+ init_tmux();
4520
+ init_domain_services();
4521
+ function createAgentStopHandler(lifecycleEvent) {
4522
+ return httpHandler(Effect.gen(function* () {
4523
+ const originCheck = validateOrigin(yield* HttpServerRequest.HttpServerRequest);
4524
+ if (!originCheck.ok) return jsonResponse({
4525
+ ok: false,
4526
+ error: originCheck.error
4527
+ }, { status: 403 });
4528
+ const id = (yield* HttpRouter.params)["id"] ?? "";
4192
4529
  yield* EventStoreService;
4193
4530
  const stateBeforeStop = yield* getAgentState(id);
4194
4531
  yield* Effect.promise(() => appendAgentLifecycleLog(id, lifecycleEvent));
4195
4532
  yield* stopAgent(id);
4196
4533
  if (stateBeforeStop?.issueId) yield* Effect.promise(async () => {
4197
4534
  try {
4198
- const { resolveProjectFromIssueSync } = await import("./projects-BKkwpH6q.js");
4199
- const { findWorkspacePath } = await import("./archive-planning-Cp_DC1j-.js");
4535
+ const { resolveProjectFromIssueSync } = await import("./projects-vjjcmiN8.js");
4536
+ const { findWorkspacePath } = await import("./archive-planning-BZDgzoEj.js");
4200
4537
  const issueLower = stateBeforeStop.issueId.toLowerCase();
4201
4538
  const workspacePath = findWorkspacePath(resolveProjectFromIssueSync(stateBeforeStop.issueId)?.projectPath ?? process.cwd(), issueLower);
4202
4539
  if (workspacePath) {
@@ -4332,9 +4669,18 @@ const postAgentUnpauseRoute = HttpRouter.add("POST", "/api/agents/:id/unpause",
4332
4669
  payload: buildAgentControlEventPayload(updatedState, toAgentStatusPayload(stateBeforeUnpause.status), hasLiveTmuxSession)
4333
4670
  });
4334
4671
  invalidateAgentsCache();
4672
+ let resumeTriggered = false;
4673
+ if ((yield* getWorkAgentLifecycleState(id)).canResumeSession && updatedState.troubled !== true) {
4674
+ resumeTriggered = true;
4675
+ yield* Effect.promise(() => appendAgentLifecycleLog(id, "agent.unpause_immediate_resume_triggered"));
4676
+ import("./resume-BFv1PGpA.js").then(({ resumeAgent }) => resumeAgent(id)).then((result) => {
4677
+ if (!result.success) console.warn(`[agents] immediate resume after unpause failed for ${id}: ${result.error}`);
4678
+ }).catch((err) => console.warn(`[agents] immediate resume after unpause errored for ${id}:`, err));
4679
+ }
4335
4680
  return jsonResponse({
4336
4681
  success: true,
4337
- agent: updatedState
4682
+ agent: updatedState,
4683
+ resumeTriggered
4338
4684
  });
4339
4685
  })));
4340
4686
  const postAgentUntroubledRoute = HttpRouter.add("POST", "/api/agents/:id/untroubled", httpHandler(Effect.gen(function* () {
@@ -4372,6 +4718,7 @@ const postAgentUntroubledRoute = HttpRouter.add("POST", "/api/agents/:id/untroub
4372
4718
  init_config_yaml();
4373
4719
  init_agents$1();
4374
4720
  init_harness_policy();
4721
+ init_model_validation();
4375
4722
  init_operator_interventions();
4376
4723
  init_projects();
4377
4724
  init_tmux();
@@ -5020,7 +5367,6 @@ const postAgentsRestartWithConfigRoute = HttpRouter.add("POST", "/api/agents/res
5020
5367
  })));
5021
5368
  //#endregion
5022
5369
  //#region routes/agents/control.ts
5023
- init_config();
5024
5370
  init_jsonl_parser();
5025
5371
  init_model_validation();
5026
5372
  init_paths();
@@ -5174,10 +5520,328 @@ const postAgentSwitchModelRoute = HttpRouter.add("POST", "/api/agents/:id/switch
5174
5520
  return jsonResponse({ error: `Agent ${(yield* HttpRouter.params)["id"] ?? ""} model is locked once the agent is spawned` }, { status: 409 });
5175
5521
  })));
5176
5522
  //#endregion
5177
- //#region ../../lib/vbrief/auto-synthesize.ts
5178
- init_state_read_home();
5179
- init_codex_auth();
5180
- init_state_home();
5523
+ //#region ../../lib/orders/eligibility.ts
5524
+ function evaluateOrderDispatchEligibility(input) {
5525
+ const issueId = input.issueId.toUpperCase();
5526
+ const item = input.book.items.find((candidate) => candidate.issue.toUpperCase() === issueId);
5527
+ if (!item) {
5528
+ const overrideUsed = input.offBook === true;
5529
+ return {
5530
+ eligible: overrideUsed,
5531
+ overrideUsed,
5532
+ ...overrideUsed ? {} : {
5533
+ code: "off-book",
5534
+ message: `Issue ${issueId} is not in active order book ${input.book.id}; work-agent dispatch is blocked. Retry with --off-book only for an intentional exception.`
5535
+ },
5536
+ conditions: [{
5537
+ key: "book-membership",
5538
+ met: overrideUsed,
5539
+ detail: overrideUsed ? `Issue ${issueId} is outside the book and the operator supplied --off-book.` : `Issue ${issueId} is not a member of ${input.book.id}.`
5540
+ }]
5541
+ };
5542
+ }
5543
+ const terminalByIssue = new Map(input.progress.items.map((progress) => [progress.issue.toUpperCase(), progress.terminal]));
5544
+ for (const [issue, terminal] of input.prerequisiteTerminal ?? []) if (!terminalByIssue.has(issue.toUpperCase())) terminalByIssue.set(issue.toUpperCase(), terminal);
5545
+ const unmetPrereq = item.prereqs.find((prereq) => terminalByIssue.get(prereq.toUpperCase()) !== true);
5546
+ const prereqsMet = unmetPrereq === void 0;
5547
+ const otherInFlight = input.book.items.filter((candidate) => candidate.issue.toUpperCase() !== issueId && input.inFlightIssues.has(candidate.issue.toUpperCase()));
5548
+ const laneBBlocker = item.lane === "B" ? otherInFlight.find((candidate) => candidate.lane === "B") : void 0;
5549
+ const laneAInFlight = otherInFlight.filter((candidate) => candidate.lane === "A").length;
5550
+ const laneSlotFree = item.lane === "B" ? laneBBlocker === void 0 : laneAInFlight < input.book.settings.laneAConcurrency;
5551
+ const postureOpen = input.book.settings.posture === "open";
5552
+ const prdReverified = !item.reVerify;
5553
+ const conditions = [
5554
+ {
5555
+ key: "book-membership",
5556
+ met: true,
5557
+ detail: `${issueId} is a member of ${input.book.id}.`
5558
+ },
5559
+ {
5560
+ key: "pickup-posture",
5561
+ met: postureOpen,
5562
+ detail: postureOpen ? "Order-book pickup posture is OPEN." : "Order-book pickup posture is DRAIN."
5563
+ },
5564
+ {
5565
+ key: "lane-slot",
5566
+ met: laneSlotFree,
5567
+ detail: item.lane === "B" ? laneBBlocker ? `Lane B is occupied by ${laneBBlocker.issue}.` : "Lane B has no issue in flight." : `${laneAInFlight}/${input.book.settings.laneAConcurrency} Lane A slots are in flight.`
5568
+ },
5569
+ {
5570
+ key: "prerequisites",
5571
+ met: prereqsMet,
5572
+ detail: unmetPrereq ? `Prerequisite ${unmetPrereq.toUpperCase()} is not terminal.` : "Every prerequisite is landed or parked."
5573
+ },
5574
+ {
5575
+ key: "prd-reverified",
5576
+ met: prdReverified,
5577
+ detail: prdReverified ? "No PRD re-verification is pending." : `${issueId} requires PRD re-verification before dispatch.`
5578
+ }
5579
+ ];
5580
+ if (!postureOpen) return {
5581
+ eligible: false,
5582
+ overrideUsed: false,
5583
+ code: "book-draining",
5584
+ message: `Issue ${issueId} cannot start because order book ${input.book.id} is in DRAIN posture; no new book items may dispatch.`,
5585
+ conditions
5586
+ };
5587
+ if (unmetPrereq) return {
5588
+ eligible: false,
5589
+ overrideUsed: false,
5590
+ code: "prerequisite-unmet",
5591
+ message: `Issue ${issueId} cannot start because prerequisite ${unmetPrereq.toUpperCase()} has not landed or been parked.`,
5592
+ conditions
5593
+ };
5594
+ if (!prdReverified) return {
5595
+ eligible: false,
5596
+ overrideUsed: false,
5597
+ code: "reverify-required",
5598
+ message: `Issue ${issueId} cannot start until its PRD has been re-verified against current main.`,
5599
+ conditions
5600
+ };
5601
+ if (laneBBlocker) return {
5602
+ eligible: false,
5603
+ overrideUsed: false,
5604
+ code: "lane-b-busy",
5605
+ message: `Issue ${issueId} cannot start in Lane B because ${laneBBlocker.issue} is still in flight; Lane B allows one active issue at a time.`,
5606
+ conditions
5607
+ };
5608
+ if (item.lane === "A" && !laneSlotFree) return {
5609
+ eligible: false,
5610
+ overrideUsed: false,
5611
+ code: "lane-a-full",
5612
+ message: `Issue ${issueId} cannot start in Lane A because ${laneAInFlight} issues are already in flight and laneAConcurrency is ${input.book.settings.laneAConcurrency}.`,
5613
+ conditions
5614
+ };
5615
+ return {
5616
+ eligible: true,
5617
+ overrideUsed: false,
5618
+ conditions
5619
+ };
5620
+ }
5621
+ //#endregion
5622
+ //#region ../../lib/orders/dispatch-gate.ts
5623
+ init_review_status();
5624
+ init_projects();
5625
+ function stateRootFor(projectRoot) {
5626
+ const project = findProjectByPathSync(projectRoot);
5627
+ if (!project) throw new Error(`No configured project contains ${projectRoot}`);
5628
+ return resolveStateReadHomeSync(project).root;
5629
+ }
5630
+ function defaultInFlightIssues(_bookId, issueIds) {
5631
+ const inFlight = /* @__PURE__ */ new Set();
5632
+ for (const issueId of issueIds) {
5633
+ const lifecycle = getWorkAgentLifecycleStateSync(issueId);
5634
+ const review = getReviewStatusSync(issueId);
5635
+ if (lifecycle.isRunning || review !== null && review.reviewStatus !== "pending") inFlight.add(issueId.toUpperCase());
5636
+ }
5637
+ return inFlight;
5638
+ }
5639
+ const booklessDecision = {
5640
+ eligible: true,
5641
+ overrideUsed: false,
5642
+ conditions: []
5643
+ };
5644
+ async function checkActiveOrderDispatch(projectRoot, issueId, options = {}, deps = {}) {
5645
+ const runId = await (deps.resolveRunId ?? resolveLiveFlywheelRunId)();
5646
+ if (!runId) return {
5647
+ ordersBound: false,
5648
+ decision: booklessDecision
5649
+ };
5650
+ const launch = await (deps.readLaunch ?? readFlywheelLaunchMetadata)(runId);
5651
+ const bookId = launch?.orders?.bookId;
5652
+ if (!bookId) return {
5653
+ ordersBound: false,
5654
+ runId,
5655
+ decision: booklessDecision
5656
+ };
5657
+ const stateRoot = (deps.stateRoot ?? stateRootFor)(launch?.workspace ?? projectRoot);
5658
+ const book = (deps.getOrderBook ?? getBook)(stateRoot, bookId);
5659
+ if (!book) throw new Error(`Active Flywheel run ${runId} references missing order book ${bookId}`);
5660
+ const progress = (deps.computeProgress ?? computeBookProgress)(book);
5661
+ const prerequisiteIds = [...new Set(book.items.flatMap((item) => item.prereqs.map((prereq) => prereq.toUpperCase())))];
5662
+ const prerequisiteState = (deps.issueLookup ?? liveOrderIssueLookup)(prerequisiteIds);
5663
+ const prerequisiteTerminal = new Map(prerequisiteIds.map((prereq) => {
5664
+ const state = prerequisiteState.get(prereq);
5665
+ return [prereq, state ? !state.open || state.parked : false];
5666
+ }));
5667
+ const inFlightIssues = new Set((deps.inFlightIssues ?? defaultInFlightIssues)(book.id, book.items.filter((item) => !progress.items.find((entry) => entry.issue === item.issue)?.terminal).map((item) => item.issue)));
5668
+ for (const reserved of deps.reservedIssues ?? []) inFlightIssues.add(reserved.toUpperCase());
5669
+ return {
5670
+ ordersBound: true,
5671
+ runId,
5672
+ bookId,
5673
+ decision: evaluateOrderDispatchEligibility({
5674
+ book,
5675
+ progress,
5676
+ issueId,
5677
+ inFlightIssues,
5678
+ prerequisiteTerminal,
5679
+ offBook: options.offBook
5680
+ })
5681
+ };
5682
+ }
5683
+ async function recordOffBookOverride(runId, bookId, issueId, actor = process.env["OVERDECK_AGENT_ID"] ?? "operator") {
5684
+ const runDir = getFlywheelRunDir(runId);
5685
+ const path = join(runDir, "orders-overrides.jsonl");
5686
+ await mkdir(runDir, { recursive: true });
5687
+ await appendFile(path, `${JSON.stringify({
5688
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5689
+ runId,
5690
+ bookId,
5691
+ issueId: issueId.toUpperCase(),
5692
+ actor,
5693
+ override: "off-book"
5694
+ })}\n`, "utf8");
5695
+ return path;
5696
+ }
5697
+ //#endregion
5698
+ //#region ../../lib/orders/dispatch-reservation.ts
5699
+ var OrderDispatchReservationError = class extends Error {
5700
+ constructor(check) {
5701
+ super(check.decision.message ?? "Order-book dispatch is blocked.");
5702
+ this.check = check;
5703
+ this.name = "OrderDispatchReservationError";
5704
+ }
5705
+ };
5706
+ const DEFAULT_STALE_MS = 900 * 1e3;
5707
+ const processLocks = /* @__PURE__ */ new Map();
5708
+ let reservationSequence = 0;
5709
+ async function withProcessLock(key, action) {
5710
+ const previous = processLocks.get(key) ?? Promise.resolve();
5711
+ let release;
5712
+ const current = new Promise((resolve) => {
5713
+ release = resolve;
5714
+ });
5715
+ const tail = previous.then(() => current);
5716
+ processLocks.set(key, tail);
5717
+ await previous;
5718
+ try {
5719
+ return await action();
5720
+ } finally {
5721
+ release();
5722
+ if (processLocks.get(key) === tail) processLocks.delete(key);
5723
+ }
5724
+ }
5725
+ function paths(root) {
5726
+ return {
5727
+ state: join(root, "orders-dispatch-reservations.json"),
5728
+ temp: join(root, `orders-dispatch-reservations.${process.pid}.tmp`),
5729
+ lock: join(root, "orders-dispatch-reservations.lock")
5730
+ };
5731
+ }
5732
+ async function acquireFileLock(path, staleMs, now) {
5733
+ try {
5734
+ await mkdir(path);
5735
+ return;
5736
+ } catch (error) {
5737
+ if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) !== "EEXIST") throw error;
5738
+ }
5739
+ if (await stat$1(path).then((value) => now.getTime() - value.mtimeMs).catch(() => 0) <= staleMs) throw new Error("Another order-book dispatch admission is in progress; retry after it finishes.");
5740
+ await rm(path, {
5741
+ recursive: true,
5742
+ force: true
5743
+ });
5744
+ await mkdir(path);
5745
+ }
5746
+ async function readReservations(path) {
5747
+ try {
5748
+ const parsed = JSON.parse(await readFile(path, "utf8"));
5749
+ return Array.isArray(parsed) ? parsed : [];
5750
+ } catch (error) {
5751
+ if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) === "ENOENT") return [];
5752
+ throw error;
5753
+ }
5754
+ }
5755
+ async function writeReservations(path, tempPath, reservations) {
5756
+ await writeFile(tempPath, `${JSON.stringify(reservations, null, 2)}\n`, {
5757
+ encoding: "utf8",
5758
+ mode: 384
5759
+ });
5760
+ await rename(tempPath, path);
5761
+ }
5762
+ function freshReservations(reservations, now, staleMs) {
5763
+ return reservations.filter((reservation) => {
5764
+ const reservedAt = Date.parse(reservation.reservedAt);
5765
+ return Number.isFinite(reservedAt) && now.getTime() - reservedAt <= staleMs;
5766
+ });
5767
+ }
5768
+ async function reserveActiveOrderDispatch(projectRoot, issueId, options = {}, deps = {}) {
5769
+ const initial = await checkActiveOrderDispatch(projectRoot, issueId, options, deps);
5770
+ if (!initial.ordersBound || !initial.runId || !initial.bookId || !initial.decision.eligible) return {
5771
+ check: initial,
5772
+ release: async () => {}
5773
+ };
5774
+ const root = (deps.reservationRoot ?? getFlywheelRunDir)(initial.runId);
5775
+ await mkdir(root, { recursive: true });
5776
+ const reservationPaths = paths(root);
5777
+ const staleMs = deps.staleMs ?? DEFAULT_STALE_MS;
5778
+ const now = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
5779
+ const reservationId = `${process.pid}-${now.getTime()}-${reservationSequence++}`;
5780
+ let check = initial;
5781
+ await withProcessLock(reservationPaths.lock, async () => {
5782
+ await acquireFileLock(reservationPaths.lock, staleMs, now);
5783
+ try {
5784
+ const current = freshReservations(await readReservations(reservationPaths.state), now, staleMs);
5785
+ check = await checkActiveOrderDispatch(projectRoot, issueId, options, {
5786
+ ...deps,
5787
+ reservedIssues: new Set(current.filter((reservation) => reservation.bookId === initial.bookId).map((reservation) => reservation.issueId))
5788
+ });
5789
+ if (!check.decision.eligible) return;
5790
+ current.push({
5791
+ id: reservationId,
5792
+ bookId: initial.bookId,
5793
+ issueId: issueId.toUpperCase(),
5794
+ reservedAt: now.toISOString()
5795
+ });
5796
+ await writeReservations(reservationPaths.state, reservationPaths.temp, current);
5797
+ } finally {
5798
+ await rm(reservationPaths.lock, {
5799
+ recursive: true,
5800
+ force: true
5801
+ });
5802
+ }
5803
+ });
5804
+ if (!check.decision.eligible) return {
5805
+ check,
5806
+ release: async () => {}
5807
+ };
5808
+ if (options.recordOverride && check.decision.overrideUsed) await (deps.recordOverride ?? recordOffBookOverride)(initial.runId, initial.bookId, issueId);
5809
+ let released = false;
5810
+ return {
5811
+ check,
5812
+ release: async () => {
5813
+ if (released) return;
5814
+ released = true;
5815
+ const releasedAt = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
5816
+ await withProcessLock(reservationPaths.lock, async () => {
5817
+ await acquireFileLock(reservationPaths.lock, staleMs, releasedAt);
5818
+ try {
5819
+ const current = freshReservations(await readReservations(reservationPaths.state), releasedAt, staleMs).filter((reservation) => reservation.id !== reservationId);
5820
+ await writeReservations(reservationPaths.state, reservationPaths.temp, current);
5821
+ } finally {
5822
+ await rm(reservationPaths.lock, {
5823
+ recursive: true,
5824
+ force: true
5825
+ });
5826
+ }
5827
+ });
5828
+ }
5829
+ };
5830
+ }
5831
+ async function withActiveOrderDispatchReservation(projectRoot, issueId, options, action, deps = {}) {
5832
+ const reservation = await reserveActiveOrderDispatch(projectRoot, issueId, options, deps);
5833
+ if (!reservation.check.decision.eligible) return { check: reservation.check };
5834
+ try {
5835
+ return {
5836
+ check: reservation.check,
5837
+ result: await action()
5838
+ };
5839
+ } finally {
5840
+ await reservation.release();
5841
+ }
5842
+ }
5843
+ //#endregion
5844
+ //#region ../../lib/xbrief/auto-synthesize.ts
5181
5845
  init_io();
5182
5846
  function cleanMarkdownLine(line) {
5183
5847
  return line.replace(/^[-*+]\s+\[[ xX]\]\s+/, "").replace(/^[-*+]\s+/, "").replace(/^#+\s+/, "").replace(/`([^`]+)`/g, "$1").trim();
@@ -5198,7 +5862,7 @@ function extractAcceptanceCriteriaFromIssue(title, body) {
5198
5862
  }
5199
5863
  return [`Issue's stated fix implemented with tests`];
5200
5864
  }
5201
- function synthesizeMinimalVBrief(issue) {
5865
+ function synthesizeMinimalXBrief(issue) {
5202
5866
  const issueId = issue.issueId.toUpperCase();
5203
5867
  const issueLabel = issueId.toLowerCase();
5204
5868
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -5209,9 +5873,9 @@ function synthesizeMinimalVBrief(issue) {
5209
5873
  created: now,
5210
5874
  metadata: { kind: "acceptance_criterion" }
5211
5875
  }));
5212
- const canonicalFilename = generateVBriefFilename(issueId, issue.title, now);
5876
+ const canonicalFilename = generateXBriefFilename(issueId, issue.title, now);
5213
5877
  return {
5214
- vBRIEFInfo: {
5878
+ xBRIEFInfo: {
5215
5879
  version: "0.8",
5216
5880
  created: now,
5217
5881
  updated: now,
@@ -5258,8 +5922,8 @@ function synthesizeMinimalVBrief(issue) {
5258
5922
  }
5259
5923
  };
5260
5924
  }
5261
- async function writeAutoStartVBriefPromise(projectRoot, workspacePath, issue) {
5262
- const document = synthesizeMinimalVBrief(issue);
5925
+ async function writeAutoStartXBriefPromise(projectRoot, workspacePath, issue) {
5926
+ const document = synthesizeMinimalXBrief(issue);
5263
5927
  const canonicalFilename = document.plan.metadata?.canonicalFilename;
5264
5928
  const projectSpecsDir = join$1(projectRoot, ".pan", "specs");
5265
5929
  const projectSpecPath = join$1(projectSpecsDir, canonicalFilename);
@@ -5271,7 +5935,7 @@ async function writeAutoStartVBriefPromise(projectRoot, workspacePath, issue) {
5271
5935
  }
5272
5936
  };
5273
5937
  await mkdir$1(projectSpecsDir, { recursive: true });
5274
- await writeFile$1(projectSpecPath, serializeVBriefDocument(projectDocument), "utf-8");
5938
+ await writeFile$1(projectSpecPath, serializeXBriefDocument(projectDocument), "utf-8");
5275
5939
  return {
5276
5940
  document,
5277
5941
  workspaceSpecPath: projectSpecPath,
@@ -5280,235 +5944,34 @@ async function writeAutoStartVBriefPromise(projectRoot, workspacePath, issue) {
5280
5944
  };
5281
5945
  }
5282
5946
  /**
5283
- * Effect variant of writeAutoStartVBrief. Wraps mkdir + writeFile in a typed
5947
+ * Effect variant of writeAutoStartXBrief. Wraps mkdir + writeFile in a typed
5284
5948
  * FsError channel so callers in Effect-native code can compose with this
5285
5949
  * synthesis step without losing failure typing.
5286
5950
  */
5287
- const writeAutoStartVBrief = (projectRoot, workspacePath, issue) => Effect.tryPromise({
5288
- try: () => writeAutoStartVBriefPromise(projectRoot, workspacePath, issue),
5951
+ const writeAutoStartXBrief = (projectRoot, workspacePath, issue) => Effect.tryPromise({
5952
+ try: () => writeAutoStartXBriefPromise(projectRoot, workspacePath, issue),
5289
5953
  catch: (cause) => new FsError({
5290
5954
  path: projectRoot,
5291
- operation: "writeAutoStartVBrief",
5955
+ operation: "writeAutoStartXBrief",
5292
5956
  cause
5293
5957
  })
5294
5958
  });
5295
5959
  //#endregion
5296
- //#region routes/dashboard-auth.ts
5297
- init_issue_lifecycle();
5298
- init_internal_token();
5299
- const DASHBOARD_SESSION_COOKIE = "overdeck_session";
5300
- const DASHBOARD_CSRF_HEADER = "x-overdeck-csrf-token";
5301
- const DASHBOARD_SESSION_MAX_AGE_SECONDS = 3600 * 24 * 30;
5302
- let browserSessionToken;
5303
- let browserCsrfToken;
5304
- function getDashboardSessionToken() {
5305
- if (browserSessionToken) return browserSessionToken;
5306
- const override = process.env["OVERDECK_DASHBOARD_SESSION_TOKEN"];
5307
- if (override) {
5308
- browserSessionToken = override;
5309
- return browserSessionToken;
5310
- }
5311
- const internal = getInternalTokenSync();
5312
- browserSessionToken = internal ? createHmac("sha256", internal).update("overdeck-dashboard-session-v1").digest("base64url") : randomBytes(32).toString("base64url");
5313
- return browserSessionToken;
5314
- }
5315
- function dashboardCsrfToken() {
5316
- if (browserCsrfToken) return browserCsrfToken;
5317
- const override = process.env["OVERDECK_DASHBOARD_CSRF_TOKEN"];
5318
- if (override) {
5319
- browserCsrfToken = override;
5320
- return browserCsrfToken;
5321
- }
5322
- const internal = getInternalTokenSync();
5323
- browserCsrfToken = internal ? createHmac("sha256", internal).update("overdeck-dashboard-csrf-v1").digest("base64url") : randomBytes(32).toString("base64url");
5324
- return browserCsrfToken;
5325
- }
5326
- function constantTimeTokenEqual(provided, expected) {
5327
- if (!provided) return false;
5328
- return timingSafeEqual(createHash$1("sha256").update(provided).digest(), createHash$1("sha256").update(expected).digest());
5329
- }
5330
- function cookieValue(cookieHeader, name) {
5331
- if (!cookieHeader) return void 0;
5332
- for (const part of cookieHeader.split(";")) {
5333
- const [rawKey, ...rawValue] = part.trim().split("=");
5334
- if (rawKey !== name) continue;
5335
- try {
5336
- return decodeURIComponent(rawValue.join("="));
5337
- } catch {
5338
- return;
5339
- }
5340
- }
5341
- }
5342
- function dashboardSessionCookieHeader(options = {}) {
5343
- const secure = options.secure ? "; Secure" : "";
5344
- return `${DASHBOARD_SESSION_COOKIE}=${encodeURIComponent(getDashboardSessionToken())}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${DASHBOARD_SESSION_MAX_AGE_SECONDS}${secure}`;
5345
- }
5346
- function hasDashboardInternalTokenHeaders(headers) {
5347
- const expected = getInternalTokenSync();
5348
- if (!expected) return false;
5349
- if (constantTimeTokenEqual(getHeaderFromMap(headers, "x-overdeck-internal-token"), expected)) return true;
5350
- const authorization = getHeaderFromMap(headers, "authorization");
5351
- if (authorization) {
5352
- const [scheme, token] = authorization.split(/\s+/);
5353
- if (scheme?.toLowerCase() === "bearer" && constantTimeTokenEqual(token, expected)) return true;
5354
- }
5355
- return false;
5356
- }
5357
- function hasDashboardInternalToken(request) {
5358
- return hasDashboardInternalTokenHeaders(request.headers);
5359
- }
5360
- function hasDashboardAuthHeaders(headers) {
5361
- return hasDashboardInternalTokenHeaders(headers) || constantTimeTokenEqual(cookieValue(getHeaderFromMap(headers, "cookie"), "overdeck_session"), getDashboardSessionToken());
5362
- }
5363
- function hasDashboardAuth(request) {
5364
- return hasDashboardAuthHeaders(request.headers);
5365
- }
5366
- function isJsonContentType(headers) {
5367
- const contentType = getHeaderFromMap(headers, "content-type");
5368
- if (!contentType) return false;
5369
- const [mime] = contentType.toLowerCase().split(";");
5370
- return mime.trim() === "application/json";
5371
- }
5372
- function hasTrustedExactOrigin(headers) {
5373
- const origin = getHeaderFromMap(headers, "origin");
5374
- if (!origin) return false;
5375
- const normalized = normalizeOrigin(origin);
5376
- return !!normalized && getTrustedOrigins().includes(normalized);
5377
- }
5378
- function hasValidCsrfToken(headers) {
5379
- return constantTimeTokenEqual(getHeaderFromMap(headers, DASHBOARD_CSRF_HEADER), dashboardCsrfToken());
5380
- }
5381
- function rejectUnsafeDashboardMutationRequest(request) {
5382
- const authError = rejectUnauthorizedDashboardRequest(request);
5383
- if (authError) return authError;
5384
- const headers = request.headers;
5385
- if (!isJsonContentType(headers)) return jsonResponse({ error: "Content-Type must be application/json" }, { status: 400 });
5386
- if (hasDashboardInternalToken(request)) return null;
5387
- if (getHeaderFromMap(headers, "origin") && !hasTrustedExactOrigin(headers)) return jsonResponse({ error: "Invalid origin" }, { status: 403 });
5388
- if (!hasValidCsrfToken(headers)) return jsonResponse({ error: "Invalid CSRF token" }, { status: 403 });
5389
- return null;
5390
- }
5391
- function ipv4ToInt(value) {
5392
- const parts = value.split(".");
5393
- if (parts.length !== 4) return null;
5394
- let result = 0;
5395
- for (const part of parts) {
5396
- const octet = Number(part);
5397
- if (!Number.isInteger(octet) || octet < 0 || octet > 255) return null;
5398
- result = (result << 8 | octet) >>> 0;
5399
- }
5400
- return result;
5401
- }
5402
- /** RFC1918 private ranges — Docker bridges always fall inside one of these. */
5403
- function isRfc1918(ip) {
5404
- return (ip & 4278190080) >>> 0 === 167772160 || (ip & 4293918720) >>> 0 === 2886729728 || (ip & 4294901760) >>> 0 === 3232235520;
5405
- }
5406
- /**
5407
- * True when `addr` is the IP of a container on one of THIS host's Docker bridge
5408
- * interfaces (docker0 / br-*). Such a peer can only be a process running on this
5409
- * machine — our own host-local Traefik fronting overdeck.localhost, or another
5410
- * Overdeck container — never a LAN client (a direct LAN hit to the 0.0.0.0
5411
- * API port arrives with a LAN peer that is not inside any Docker bridge subnet).
5412
- *
5413
- * Pure for testability: the interface map is injected. We only ever trust
5414
- * RFC1918 bridge subnets, so a misconfigured public-range br-* cannot widen
5415
- * trust beyond the local Docker fabric.
5416
- */
5417
- function peerIsHostLocalDockerBridge(addr, interfaces) {
5418
- const peer = ipv4ToInt(addr);
5419
- if (peer === null) return false;
5420
- for (const [name, addrs] of Object.entries(interfaces)) {
5421
- if (name !== "docker0" && !name.startsWith("br-")) continue;
5422
- for (const info of addrs ?? []) {
5423
- if (info.family !== "IPv4") continue;
5424
- const ifaceIp = ipv4ToInt(info.address);
5425
- const mask = ipv4ToInt(info.netmask);
5426
- if (ifaceIp === null || mask === null) continue;
5427
- const network = (ifaceIp & mask) >>> 0;
5428
- if (!isRfc1918(network)) continue;
5429
- if ((peer & mask) >>> 0 === network) return true;
5430
- }
5431
- }
5432
- return false;
5433
- }
5434
- /**
5435
- * True when `addr` shares an RFC1918 subnet with one of THIS process's own
5436
- * network interfaces. This is the in-container counterpart of
5437
- * peerIsHostLocalDockerBridge: inside a container the Docker bridge is
5438
- * invisible — the container sees only its veth endpoints (eth0/eth1), so the
5439
- * docker0/br-* name filter never matches and the host-local Traefik fronting
5440
- * the stack 401'd every session mint (first hit live on the PAN-1737 UAT
5441
- * stack). A peer inside one of the container's attached Docker-network
5442
- * subnets can only be another container on those networks — the server
5443
- * publishes no host port (Traefik labels only), so no LAN client can ever
5444
- * present such an address. Only consulted when running inside a container.
5445
- */
5446
- function peerIsLocalContainerNetwork(addr, interfaces) {
5447
- const peer = ipv4ToInt(addr);
5448
- if (peer === null) return false;
5449
- for (const addrs of Object.values(interfaces)) for (const info of addrs ?? []) {
5450
- if (info.family !== "IPv4" || info.internal) continue;
5451
- const ifaceIp = ipv4ToInt(info.address);
5452
- const mask = ipv4ToInt(info.netmask);
5453
- if (ifaceIp === null || mask === null) continue;
5454
- const network = (ifaceIp & mask) >>> 0;
5455
- if (!isRfc1918(network)) continue;
5456
- if ((peer & mask) >>> 0 === network) return true;
5457
- }
5458
- return false;
5459
- }
5460
- /** Docker creates /.dockerenv in every container — stable in-container signal. */
5461
- const runningInContainer = existsSync("/.dockerenv");
5462
- /**
5463
- * A request we trust to mint a session because it originated from this machine —
5464
- * the local browser hitting the dashboard directly (127.0.0.1), or the
5465
- * host-local Traefik that fronts overdeck.localhost. In Docker, that Traefik reaches
5466
- * the host server from a Docker-bridge IP (e.g. 172.18.0.2), NOT 127.0.0.1, so
5467
- * a literal-loopback check alone silently breaks the zero-step bootstrap for
5468
- * every overdeck.localhost user — their session mint 401s and no cookie is ever set.
5469
- *
5470
- * This is the auto-bootstrap that removes the manual one-time #overdeck_token
5471
- * step: any browser on overdeck.localhost gets a session with no user action. It is
5472
- * NOT a security downgrade for the LAN — the raw API ports bind 0.0.0.0, but a
5473
- * direct LAN hit has a non-loopback, non-Docker-bridge peer and is rejected. We
5474
- * read ONLY the real TCP peer (request.remoteAddress), never X-Forwarded-For,
5475
- * which a caller could spoof.
5476
- */
5477
- function isLoopbackPeer(request) {
5478
- const remoteAddress = request.remoteAddress;
5479
- const raw = remoteAddress && typeof remoteAddress === "object" && "_tag" in remoteAddress ? Option.getOrElse(remoteAddress, () => "") : typeof remoteAddress === "string" ? remoteAddress : "";
5480
- if (!raw) return false;
5481
- const addr = raw.startsWith("::ffff:") ? raw.slice(7) : raw;
5482
- if (addr === "127.0.0.1" || addr === "::1") return true;
5483
- if (peerIsHostLocalDockerBridge(addr, networkInterfaces())) return true;
5484
- return runningInContainer && peerIsLocalContainerNetwork(addr, networkInterfaces());
5485
- }
5486
- function rejectUnauthorizedDashboardSessionMintRequest(request) {
5487
- if (!getInternalTokenSync()) return jsonResponse({ error: "dashboard session token not configured" }, { status: 503 });
5488
- if (!hasDashboardInternalToken(request) && !isLoopbackPeer(request)) return jsonResponse({ error: "unauthorized" }, { status: 401 });
5489
- return null;
5490
- }
5491
- function rejectUnauthorizedDashboardRequest(request) {
5492
- if (!getInternalTokenSync()) return jsonResponse({ error: "dashboard session token not configured" }, { status: 503 });
5493
- if (!hasDashboardAuth(request)) return jsonResponse({ error: "unauthorized" }, { status: 401 });
5494
- return null;
5495
- }
5496
- //#endregion
5497
5960
  //#region routes/agents/spawn-helpers.ts
5498
5961
  init_child_env();
5499
5962
  init_agents$1();
5500
5963
  function handleRemoteAgentSpawn(input) {
5501
5964
  return Effect.gen(function* () {
5502
5965
  const { issueId, workspacePath, workspaceMetadata, spawnModel, projectPath, spawnGuardrails, lifecycle } = input;
5503
- const { spawnRemoteAgent, checkRemoteSpendCap } = yield* Effect.promise(() => import("./remote-agents-D6O_zETW.js"));
5504
- const { createFlyProviderFromConfig } = yield* Effect.promise(() => import("./remote-Dr5kGYSZ.js"));
5505
- const { loadConfigSync: loadPanConfig } = yield* Effect.promise(() => import("./config-BJKocXKd.js"));
5966
+ const { spawnRemoteAgent, checkRemoteSpendCap } = yield* Effect.promise(() => import("./remote-agents-CA3Ihlxj.js"));
5967
+ const { createFlyProviderFromConfig } = yield* Effect.promise(() => import("./remote-ChgimyYa.js"));
5968
+ const { loadConfigSync: loadPanConfig } = yield* Effect.promise(() => import("./config-DzV30TOm.js"));
5506
5969
  const panConfig = loadPanConfig();
5507
5970
  const spendCap = checkRemoteSpendCap(panConfig);
5508
5971
  if (!spendCap.allowed) return jsonResponse({ error: spendCap.message }, { status: 429 });
5509
5972
  const fly = createFlyProviderFromConfig(panConfig.remote);
5510
5973
  yield* Effect.promise(() => fly.syncAllCredentials(workspaceMetadata.vmName));
5511
- const { buildWorkAgentPrompt, getTrackerContext } = yield* Effect.promise(() => import("./work-agent-prompt-DLthEPAq.js"));
5974
+ const { buildWorkAgentPrompt, getTrackerContext } = yield* Effect.promise(() => import("./work-agent-prompt-Du8Vh2aJ.js"));
5512
5975
  const trackerContext = yield* Effect.promise(() => getTrackerContext(issueId, workspacePath));
5513
5976
  const agentPrompt = yield* Effect.promise(() => buildWorkAgentPrompt({
5514
5977
  issueId,
@@ -5829,10 +6292,23 @@ init_child_env();
5829
6292
  init_harness_policy();
5830
6293
  init_activity_logger();
5831
6294
  init_projects();
6295
+ init_review_status();
6296
+ init_state_read_home();
5832
6297
  init_io();
5833
6298
  init_read_model();
5834
6299
  init_domain_services();
5835
6300
  init_tmux();
6301
+ function orderDispatchConflict(decision) {
6302
+ if (decision.eligible) return null;
6303
+ return {
6304
+ status: 409,
6305
+ body: {
6306
+ error: decision.message ?? "Order-book dispatch is blocked.",
6307
+ code: decision.code ?? "order-dispatch-blocked",
6308
+ conditions: decision.conditions
6309
+ }
6310
+ };
6311
+ }
5836
6312
  /**
5837
6313
  * PAN-2386: emit a dashboard activity event when start-agent refuses to spawn
5838
6314
  * because the workspace is dirty. Kept as a pure function so the refusal path
@@ -5852,6 +6328,25 @@ function emitDirtyWorkspaceRefusalActivity(issueId, porcelain) {
5852
6328
  });
5853
6329
  } catch {}
5854
6330
  }
6331
+ const LEGACY_WORKSPACE_RUNTIME_FILES = new Set([
6332
+ ".pan/continue.json",
6333
+ ".pan/spec.vbrief.json",
6334
+ ".pan/kickoff.md",
6335
+ ".pan/sessions.jsonl",
6336
+ ".pan/pipeline-verdict.json",
6337
+ ".pan/verification-latest.json",
6338
+ ".pan/agent-mcp.json"
6339
+ ]);
6340
+ function isOverdeckWorkspaceRuntimePath(path) {
6341
+ if (path === ".overdeck" || path.startsWith(".overdeck/")) return true;
6342
+ if (path === ".pan" || path === ".pan/") return true;
6343
+ if (LEGACY_WORKSPACE_RUNTIME_FILES.has(path)) return true;
6344
+ return path.startsWith(".pan/feedback/") || path.startsWith(".pan/review/") || path.startsWith(".pan/test/") || path.startsWith(".pan/handoff-");
6345
+ }
6346
+ /** True when git porcelain contains only Overdeck-owned workspace runtime files. */
6347
+ function isOnlyOverdeckRuntimeWorkspaceChanges(porcelain) {
6348
+ return parsePorcelainStatusPaths(porcelain).every(isOverdeckWorkspaceRuntimePath);
6349
+ }
5855
6350
  function spawnGuardrailResourcesHint(hint) {
5856
6351
  const resourcesHint = "Open /resources to inspect Machine Room pressure before retrying.";
5857
6352
  return hint ? `${hint} ${resourcesHint}` : resourcesHint;
@@ -5882,6 +6377,7 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
5882
6377
  const { issueId, projectId } = body;
5883
6378
  const autoStart = body.auto === true;
5884
6379
  const guardrailAcknowledged = body.guardrailAcknowledged === true;
6380
+ const offBook = body.offBook === true;
5885
6381
  const requestedHostOverride = body.host === true || body.allowHost === true;
5886
6382
  if (!issueId) return jsonResponse({ error: "issueId required" }, { status: 400 });
5887
6383
  const legacyFields = [
@@ -5937,6 +6433,8 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
5937
6433
  const resolvedProject = resolveProjectFromIssueSync(String(issueId));
5938
6434
  const projectConfig = resolvedProject ? getProjectSync(resolvedProject.projectKey) : null;
5939
6435
  const projectPath = projectConfig?.path ?? getProjectPath(projectId, issuePrefix);
6436
+ const orderConflict = orderDispatchConflict((yield* Effect.promise(() => checkActiveOrderDispatch(projectPath, issueId, { offBook }))).decision);
6437
+ if (orderConflict) return jsonResponse(orderConflict.body, { status: orderConflict.status });
5940
6438
  const workspacePath = join(projectPath, "workspaces", `feature-${issueLower}`);
5941
6439
  if (!existsSync(workspacePath)) try {
5942
6440
  const nodeDir = dirname(process.execPath);
@@ -5956,7 +6454,7 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
5956
6454
  const workspacePanContinuePath = join(workspacePanDir, PAN_CONTINUE_FILENAME);
5957
6455
  let planPath = yield* findPlan(workspacePath);
5958
6456
  if (autoStart && !planPath) {
5959
- yield* writeAutoStartVBrief(projectPath, workspacePath, {
6457
+ yield* writeAutoStartXBrief(projectPath, workspacePath, {
5960
6458
  issueId,
5961
6459
  title: cachedIssue?.title || issueId,
5962
6460
  body: cachedIssue?.description || "",
@@ -5965,7 +6463,7 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
5965
6463
  planPath = yield* findPlan(workspacePath);
5966
6464
  }
5967
6465
  if (!planPath) return jsonResponse({
5968
- error: `No workspace vBRIEF found for ${issueId}. Work agents require a finalized plan.`,
6466
+ error: `No workspace xBRIEF found for ${issueId}. Work agents require a finalized plan.`,
5969
6467
  hint: "Run planning first, or use auto-start to synthesize a plan before starting the work agent.",
5970
6468
  issueId
5971
6469
  }, { status: 422 });
@@ -5982,8 +6480,8 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
5982
6480
  if (planReadResult._tag === "failure") {
5983
6481
  const { planErr } = planReadResult;
5984
6482
  return jsonResponse({
5985
- error: `Could not read workspace vBRIEF for ${issueId}: ${planErr instanceof Error ? planErr.message : String(planErr)}`,
5986
- hint: "Re-run planning to produce a readable vBRIEF before starting the work agent.",
6483
+ error: `Could not read workspace xBRIEF for ${issueId}: ${planErr instanceof Error ? planErr.message : String(planErr)}`,
6484
+ hint: "Re-run planning to produce a readable xBRIEF before starting the work agent.",
5987
6485
  issueId
5988
6486
  }, { status: 422 });
5989
6487
  }
@@ -6081,10 +6579,18 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6081
6579
  }
6082
6580
  if (!isRemote) {
6083
6581
  emitStartAgentPhase(issueId, "stackHealthGate", "start", "checking workspace docker stack health", { workspacePath });
6084
- const stackHealth = yield* getWorkspaceStackHealth(issueId, {
6582
+ let stackHealth = yield* getWorkspaceStackHealth(issueId, {
6085
6583
  projectConfig,
6086
6584
  workspacePath
6087
6585
  });
6586
+ if (!stackHealth.healthy && !allowHost) {
6587
+ emitStartAgentPhase(issueId, "stackHealthGate", "start", "stack unhealthy — attempting workspace stack rebuild", { workspacePath });
6588
+ yield* Effect.promise(() => assertWorkspaceStackHealthyForSpawn(issueId, "work", false, workspacePath).catch(() => void 0));
6589
+ stackHealth = yield* getWorkspaceStackHealth(issueId, {
6590
+ projectConfig,
6591
+ workspacePath
6592
+ });
6593
+ }
6088
6594
  if (!stackHealth.healthy) {
6089
6595
  yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, "agent.start_blocked_stack_unhealthy", {
6090
6596
  issueId,
@@ -6146,33 +6652,6 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6146
6652
  }).unref();
6147
6653
  }
6148
6654
  }).pipe(Effect.catch(() => Effect.void));
6149
- yield* transitionVBriefOnMain(projectPath, issueId, "active", "approved", `scope: approve ${issueId.toUpperCase()} vBRIEF`).pipe(Effect.match({
6150
- onSuccess: (result) => {
6151
- if (result.moved) console.log(`[start-agent] vBRIEF moved ${result.fromDir} → active for ${issueId}`);
6152
- if (result.committed) console.log(`[start-agent] Committed approval transition on main for ${issueId}`);
6153
- },
6154
- onFailure: (err) => {
6155
- console.warn(`[start-agent] vBRIEF approval transition failed (non-fatal): ${err?.message ?? err}`);
6156
- }
6157
- }));
6158
- if (existsSync(planPath)) try {
6159
- updatePlanStatus(planPath, "running");
6160
- console.log(`[start-agent] Set plan.status=running for ${issueId}`);
6161
- } catch (planStatusErr) {
6162
- console.warn(`[start-agent] Failed to set plan.status=running (non-fatal): ${planStatusErr?.message ?? planStatusErr}`);
6163
- }
6164
- try {
6165
- const { appendSessionEntry, getProjectConfigFromWorkspacePath, resolveProjectForIssue } = yield* Effect.promise(() => import("./record-rwK-nKv_.js"));
6166
- const recordProject = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(workspacePath);
6167
- yield* Effect.promise(() => appendSessionEntry(recordProject, issueId, {
6168
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6169
- reason: "start",
6170
- agentModel: spawnModel
6171
- }));
6172
- console.log(`[start-agent] Wrote start session entry to record for ${issueId}`);
6173
- } catch (continueErr) {
6174
- console.warn(`[start-agent] Failed to write start entry to record (non-fatal): ${continueErr?.message ?? continueErr}`);
6175
- }
6176
6655
  let gatesCommitted = false;
6177
6656
  const commitClearedGates = async () => {
6178
6657
  if (!startGateBlock || gatesCommitted) return;
@@ -6193,8 +6672,43 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6193
6672
  troubled: startGateBlock.troubled
6194
6673
  });
6195
6674
  };
6675
+ let workStartAccepted = false;
6676
+ const markWorkStartAccepted = async () => {
6677
+ if (workStartAccepted) return;
6678
+ workStartAccepted = true;
6679
+ await Effect.runPromise(transitionXBriefOnMain(projectPath, issueId, "active", "approved", `scope: approve ${issueId.toUpperCase()} xBRIEF`).pipe(Effect.match({
6680
+ onSuccess: (result) => {
6681
+ if (result.moved) console.log(`[start-agent] xBRIEF moved ${result.fromDir} → active for ${issueId}`);
6682
+ if (result.committed) console.log(`[start-agent] Committed approval transition for ${issueId}`);
6683
+ },
6684
+ onFailure: (err) => {
6685
+ console.warn(`[start-agent] xBRIEF approval transition failed (non-fatal): ${err?.message ?? err}`);
6686
+ }
6687
+ })));
6688
+ if (existsSync(planPath)) try {
6689
+ updatePlanStatus(planPath, "running");
6690
+ console.log(`[start-agent] Set plan.status=running for ${issueId}`);
6691
+ } catch (planStatusErr) {
6692
+ console.warn(`[start-agent] Failed to set plan.status=running (non-fatal): ${planStatusErr?.message ?? planStatusErr}`);
6693
+ }
6694
+ try {
6695
+ const { appendSessionEntry, getProjectConfigFromWorkspacePath, resolveProjectForIssue } = await import("./record-CN0pj4_5.js");
6696
+ await appendSessionEntry(resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(workspacePath), issueId, {
6697
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6698
+ reason: "start",
6699
+ agentModel: spawnModel
6700
+ });
6701
+ console.log(`[start-agent] Wrote start session entry to record for ${issueId}`);
6702
+ } catch (continueErr) {
6703
+ console.warn(`[start-agent] Failed to write start entry to record (non-fatal): ${continueErr?.message ?? continueErr}`);
6704
+ }
6705
+ if (getReviewStatusSync(issueId)?.stuckReason === "planning_auto_handoff_failed") clearWorkspaceStuck(issueId);
6706
+ };
6196
6707
  if (isRemote && workspaceMetadata) {
6197
- const response = yield* Effect.promise(() => spawnAfterClearingStartGates({
6708
+ const admitted = yield* Effect.promise(() => withActiveOrderDispatchReservation(projectPath, issueId, {
6709
+ offBook,
6710
+ recordOverride: true
6711
+ }, () => spawnAfterClearingStartGates({
6198
6712
  agentSessionName,
6199
6713
  gate: startGateBlock,
6200
6714
  initialState: initialAgentState,
@@ -6208,9 +6722,13 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6208
6722
  lifecycle
6209
6723
  })),
6210
6724
  isSuccessful: (remoteResponse) => remoteResponse.status >= 200 && remoteResponse.status < 300
6211
- }));
6725
+ })));
6726
+ const admittedConflict = orderDispatchConflict(admitted.check.decision);
6727
+ if (admittedConflict) return jsonResponse(admittedConflict.body, { status: admittedConflict.status });
6728
+ const response = admitted.result;
6212
6729
  if (response.status < 200 || response.status >= 300) return response;
6213
6730
  yield* Effect.promise(commitClearedGates);
6731
+ yield* Effect.promise(markWorkStartAccepted);
6214
6732
  return response;
6215
6733
  }
6216
6734
  const devScript = join(workspacePath, "dev");
@@ -6242,11 +6760,11 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6242
6760
  console.log(`[start-agent] Killed stale tmux session ${agentSessionName}`);
6243
6761
  }).pipe(Effect.catch(() => Effect.void));
6244
6762
  if (!(body.acknowledgeDirtyWorkspace === true)) try {
6245
- const { stdout: statusOut } = yield* Effect.promise(() => execAsync$2("git status --porcelain", {
6763
+ const { stdout: statusOut } = yield* Effect.promise(() => execAsync$2("git status --porcelain --untracked-files=all", {
6246
6764
  cwd: workspacePath,
6247
6765
  encoding: "utf-8"
6248
6766
  }));
6249
- if (statusOut.trim()) {
6767
+ if (statusOut.trim() && !isOnlyOverdeckRuntimeWorkspaceChanges(statusOut)) {
6250
6768
  const { stdout: diffOut } = yield* Effect.promise(() => execAsync$2("git diff HEAD --stat", {
6251
6769
  cwd: workspacePath,
6252
6770
  encoding: "utf-8"
@@ -6275,11 +6793,14 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6275
6793
  console.warn(`[start-agent] Failed to check workspace status for ${issueId}: ${message}`);
6276
6794
  }
6277
6795
  const bodyHarness = body.harness;
6278
- const userPickedHarness = bodyHarness === "ohmypi" || bodyHarness === "claude-code" || bodyHarness === "codex" ? bodyHarness : null;
6796
+ const userPickedHarness = bodyHarness === "ohmypi" || bodyHarness === "claude-code" || bodyHarness === "codex" || bodyHarness === "acp" ? bodyHarness : null;
6279
6797
  let effectiveHarness = null;
6280
6798
  if (userPickedHarness !== null) effectiveHarness = (yield* Effect.promise(async () => canUseHarnessSync(userPickedHarness, spawnModel, await getProviderAuthMode(spawnModel)))).allowed ? userPickedHarness : "claude-code";
6281
6799
  const spawnPanCommand = async (args, cwd) => {
6282
- const activityId = await spawnAfterClearingStartGates({
6800
+ const admitted = await withActiveOrderDispatchReservation(projectPath, issueId, {
6801
+ offBook,
6802
+ recordOverride: false
6803
+ }, () => spawnAfterClearingStartGates({
6283
6804
  agentSessionName,
6284
6805
  gate: gatesCommitted ? null : startGateBlock,
6285
6806
  initialState: initialAgentState,
@@ -6291,9 +6812,10 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6291
6812
  args,
6292
6813
  cwd
6293
6814
  })
6294
- });
6815
+ }));
6816
+ if (!admitted.check.decision.eligible || !admitted.result) throw new OrderDispatchReservationError(admitted.check);
6295
6817
  await commitClearedGates();
6296
- return activityId;
6818
+ return admitted.result;
6297
6819
  };
6298
6820
  const updateIssueStatus = async () => {
6299
6821
  await Effect.runPromise(lifecycle.transitionTo(issueId, "in_progress").pipe(Effect.catch(() => Effect.void)));
@@ -6313,7 +6835,58 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6313
6835
  spawnPanCommand,
6314
6836
  updateIssueStatus
6315
6837
  });
6316
- if (containerResponse) return containerResponse;
6838
+ if (containerResponse) {
6839
+ if (containerResponse.status >= 200 && containerResponse.status < 300) yield* Effect.promise(markWorkStartAccepted);
6840
+ return containerResponse;
6841
+ }
6842
+ const placeholderStartedAt = (/* @__PURE__ */ new Date()).toISOString();
6843
+ const placeholderState = {
6844
+ id: agentSessionName,
6845
+ issueId,
6846
+ workspace: workspacePath,
6847
+ role,
6848
+ ...effectiveHarness ? { harness: effectiveHarness } : {},
6849
+ model: "pending-work-spawn",
6850
+ status: "starting",
6851
+ startedAt: placeholderStartedAt,
6852
+ hostOverride: allowHost || void 0
6853
+ };
6854
+ const hasLiveTmuxSession = yield* sessionExists(agentSessionName).pipe(Effect.catch(() => Effect.succeed(true)));
6855
+ const placeholderClaim = yield* claimAgentStartPlaceholderProgram(placeholderState, {
6856
+ type: "agent.started",
6857
+ timestamp: placeholderStartedAt,
6858
+ payload: {
6859
+ agentId: agentSessionName,
6860
+ issueId,
6861
+ agent: {
6862
+ id: agentSessionName,
6863
+ issueId,
6864
+ workspace: workspacePath,
6865
+ status: "starting",
6866
+ startedAt: placeholderStartedAt,
6867
+ role,
6868
+ ...effectiveHarness ? { runtime: effectiveHarness } : {}
6869
+ }
6870
+ }
6871
+ }, hasLiveTmuxSession);
6872
+ if (!placeholderClaim.claimed) {
6873
+ yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, "agent.start_placeholder_blocked", {
6874
+ issueId,
6875
+ role,
6876
+ workspacePath,
6877
+ reason: placeholderClaim.reason
6878
+ }));
6879
+ return jsonResponse({
6880
+ error: `Agent ${agentSessionName} is already starting or running.`,
6881
+ code: "AGENT_START_IN_FLIGHT"
6882
+ }, { status: 409 });
6883
+ }
6884
+ yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, "agent.start_placeholder_created", {
6885
+ issueId,
6886
+ role,
6887
+ workspacePath,
6888
+ startedAt: placeholderStartedAt
6889
+ }));
6317
6890
  yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, "agent.work_spawn_requested", {
6318
6891
  issueId,
6319
6892
  role,
@@ -6326,13 +6899,42 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6326
6899
  issueId,
6327
6900
  model: spawnModel,
6328
6901
  harness: effectiveHarness,
6329
- allowHost
6902
+ allowHost,
6903
+ offBook
6330
6904
  }), workspacePath));
6905
+ yield* Effect.promise(markWorkStartAccepted);
6331
6906
  emitStartAgentPhase(issueId, "spawn", "success", "local work agent spawn requested", {
6332
6907
  workspacePath,
6333
6908
  activityId
6334
6909
  });
6335
6910
  } catch (error) {
6911
+ const initialStateIsPlaceholder = initialAgentState?.model.startsWith("pending-") === true;
6912
+ const fallbackState = initialAgentState && !initialStateIsPlaceholder ? { ...initialAgentState } : {
6913
+ ...placeholderState,
6914
+ model: spawnModel,
6915
+ status: "stopped",
6916
+ stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
6917
+ };
6918
+ const rolledBack = yield* rollbackAgentStartPlaceholderProgram(placeholderState, fallbackState, {
6919
+ type: "agent.status_changed",
6920
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6921
+ payload: {
6922
+ agentId: agentSessionName,
6923
+ status: fallbackState.status,
6924
+ previousStatus: "starting",
6925
+ hasLiveTmuxSession: false
6926
+ }
6927
+ });
6928
+ yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, "agent.start_placeholder_rollback", {
6929
+ issueId,
6930
+ rolledBack,
6931
+ fallbackStatus: fallbackState.status
6932
+ }));
6933
+ invalidateAgentsCache();
6934
+ if (error instanceof OrderDispatchReservationError) {
6935
+ const conflict = orderDispatchConflict(error.check.decision);
6936
+ return jsonResponse(conflict.body, { status: conflict.status });
6937
+ }
6336
6938
  const output = String(error?.output ?? error?.message ?? "");
6337
6939
  if (output.includes(`Workspace docker stack for ${issueId}`) && output.includes("is not healthy")) {
6338
6940
  const failedStackHealth = yield* getWorkspaceStackHealth(issueId, {
@@ -6376,55 +6978,8 @@ const postAgentsRoute = HttpRouter.add("POST", "/api/agents", httpHandler(Effect
6376
6978
  activityId: error?.activityId
6377
6979
  }, { status: 500 });
6378
6980
  }
6379
- const earlyAgentId = agentSessionName;
6380
- const earlyStateDir = join(homedir(), ".overdeck", "agents", earlyAgentId);
6381
- yield* Effect.promise(() => mkdir(earlyStateDir, { recursive: true }));
6382
- saveAgentStateSync({
6383
- id: earlyAgentId,
6384
- issueId,
6385
- ...effectiveHarness ? { harness: effectiveHarness } : {},
6386
- model: "pending-work-spawn",
6387
- status: "starting",
6388
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
6389
- workspace: workspacePath,
6390
- role,
6391
- hostOverride: allowHost || void 0
6392
- });
6393
- updateRegistryForAgentStart(issueId, workspacePath, earlyAgentId);
6394
- yield* Effect.promise(() => appendAgentLifecycleLog(earlyAgentId, "agent.start_placeholder_created", {
6395
- issueId,
6396
- role,
6397
- workspacePath,
6398
- activityId
6399
- }));
6981
+ updateRegistryForAgentStart(issueId, workspacePath, agentSessionName);
6400
6982
  yield* Effect.promise(() => updateIssueStatus());
6401
- yield* saveAgentStateAndEmitEventProgram({
6402
- id: earlyAgentId,
6403
- issueId,
6404
- workspace: workspacePath,
6405
- role,
6406
- ...effectiveHarness ? { harness: effectiveHarness } : {},
6407
- model: "pending-work-spawn",
6408
- status: "starting",
6409
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
6410
- hostOverride: allowHost || void 0
6411
- }, {
6412
- type: "agent.started",
6413
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6414
- payload: {
6415
- agentId: earlyAgentId,
6416
- issueId,
6417
- agent: {
6418
- id: earlyAgentId,
6419
- issueId,
6420
- workspace: workspacePath,
6421
- status: "starting",
6422
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
6423
- role,
6424
- ...effectiveHarness ? { runtime: effectiveHarness } : {}
6425
- }
6426
- }
6427
- });
6428
6983
  try {
6429
6984
  getIssueDataService().patchIssue(issueId, {
6430
6985
  status: "In Progress",
@@ -6589,10 +7144,10 @@ const _serverManagedMerges = /* @__PURE__ */ new Set();
6589
7144
  function firePostMergeLifecycle(issueId, options) {
6590
7145
  const started = postMergeGuard.run(issueId, async () => {
6591
7146
  const projectPath = getProjectPathForIssue(extractPrefixSync(issueId) ?? issueId.split("-")[0]);
6592
- const { postMergeLifecycle } = await import("./merge-agent-B-SnXOoc.js");
7147
+ const { postMergeLifecycle } = await import("./merge-agent-CKkgIBPB.js");
6593
7148
  await postMergeLifecycle(issueId, projectPath, options?.sourceBranch, options?.verifiedMergedRef ? { verifiedMergedRef: options.verifiedMergedRef } : void 0);
6594
7149
  console.log(`[merge] post-merge lifecycle completed for ${issueId}`);
6595
- const { runMergeTrainReconcile } = await import("./merge-train-CKana8EH.js");
7150
+ const { runMergeTrainReconcile } = await import("./merge-train-CHopycP0.js");
6596
7151
  const outcomes = await runMergeTrainReconcile(issueId);
6597
7152
  if (outcomes.length > 0) console.log(`[merge-train] reconciled ${outcomes.length} sibling(s) after ${issueId}: ` + outcomes.map((o) => `${o.issueId}=${o.result}`).join(", "));
6598
7153
  }, (err) => console.error(`[merge] post-merge lifecycle failed for ${issueId}:`, err));
@@ -6613,15 +7168,15 @@ init_io();
6613
7168
  init_domain_services();
6614
7169
  init_tmux();
6615
7170
  const getSpecialistsRoute = HttpRouter.add("GET", "/api/specialists", httpHandler(Effect.gen(function* () {
6616
- const { getAllSpecialistStatus, getAllProjectSpecialistStatuses } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7171
+ const { getAllSpecialistStatus, getAllProjectSpecialistStatuses } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
6617
7172
  return jsonResponse({
6618
7173
  specialists: yield* Effect.promise(() => getAllSpecialistStatus()),
6619
7174
  projects: yield* Effect.promise(() => getAllProjectSpecialistStatuses())
6620
7175
  });
6621
7176
  })));
6622
7177
  const postSpecialistsResetAllRoute = HttpRouter.add("POST", "/api/specialists/reset-all", httpHandler(Effect.gen(function* () {
6623
- const { getAllSpecialists, isRunning, getTmuxSessionName } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
6624
- const { clearHookSync } = yield* Effect.promise(() => import("./hooks-B-HItBiS.js"));
7178
+ const { getAllSpecialists, isRunning, getTmuxSessionName } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
7179
+ const { clearHookSync } = yield* Effect.promise(() => import("./hooks-DPVX1rwQ.js"));
6625
7180
  const specialists = getAllSpecialists();
6626
7181
  const results = [];
6627
7182
  for (const specialist of specialists) {
@@ -6673,11 +7228,11 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6673
7228
  if (!itemId) return jsonResponse({ error: "itemId is required for a passed inspect verdict" }, { status: 400 });
6674
7229
  const project = resolveProjectFromIssueSync(normalizedIssueId);
6675
7230
  const workspacePath = project && join(project.projectPath, "workspaces", `feature-${normalizedIssueId.toLowerCase()}`);
6676
- if (!(workspacePath ? readWorkspacePlanSync(workspacePath) : void 0)?.plan.items.some((item) => item.id === itemId)) return jsonResponse({ error: `Item "${itemId}" does not exist in the vBRIEF for ${normalizedIssueId}` }, { status: 400 });
7231
+ if (!(workspacePath ? readWorkspacePlanSync(workspacePath) : void 0)?.plan.items.some((item) => item.id === itemId)) return jsonResponse({ error: `Item "${itemId}" does not exist in the xBRIEF for ${normalizedIssueId}` }, { status: 400 });
6677
7232
  }
6678
7233
  console.log(`[specialists/done] ${specialist} signaling ${status} for ${normalizedIssueId}`);
6679
7234
  if (specialist === "merge") {
6680
- const { reportSpecialistCompletion } = yield* Effect.promise(() => import("./specialist-completion-CqmduO92.js"));
7235
+ const { reportSpecialistCompletion } = yield* Effect.promise(() => import("./specialist-completion-Ky9wCDpb.js"));
6681
7236
  if (yield* reportSpecialistCompletion(normalizedIssueId, {
6682
7237
  status,
6683
7238
  notes
@@ -6724,7 +7279,7 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6724
7279
  if (specialist === "inspect" && status === "failed") yield* Effect.promise(() => reportTieredInspectFailureEscalation(normalizedIssueId, notes));
6725
7280
  yield* Effect.promise(async () => {
6726
7281
  try {
6727
- const { getTmuxSessionName, updateRunMetadata, makeSpecialistRegistryKey } = await import("./specialists-Dqe6_HE5.js");
7282
+ const { getTmuxSessionName, updateRunMetadata, makeSpecialistRegistryKey } = await import("./specialists-DM6bWjsi.js");
6728
7283
  const projectKey = resolveProjectFromIssueSync(normalizedIssueId)?.projectKey;
6729
7284
  const tmuxSession = projectKey ? getTmuxSessionName(`${specialist}-agent`, projectKey, normalizedIssueId) : getTmuxSessionName(`${specialist}-agent`);
6730
7285
  saveAgentRuntimeState(tmuxSession, {
@@ -6743,7 +7298,7 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6743
7298
  });
6744
7299
  console.log(`[specialists/done] Cleared registry lock for ${registryKey} (${projectKey})`);
6745
7300
  }
6746
- const { updateSpecialistHandoffStatus } = await import("./specialist-handoff-logger-Dnjlwt7g.js");
7301
+ const { updateSpecialistHandoffStatus } = await import("./specialist-handoff-logger-CMEwYxa2.js");
6747
7302
  if (await Effect.runPromise(updateSpecialistHandoffStatus(normalizedIssueId, `${specialist}-agent`, status === "passed" ? "completed" : "failed", status === "passed" ? "success" : "failure"))) console.log(`[specialists/done] Updated handoff log: ${specialist}-agent ${normalizedIssueId} → ${status}`);
6748
7303
  } catch (err) {
6749
7304
  console.error(`[specialists/done] Error managing specialist state:`, err);
@@ -6755,7 +7310,7 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6755
7310
  if (project) {
6756
7311
  const workspacePath = join(project.projectPath, "workspaces", `feature-${normalizedIssueId.toLowerCase()}`);
6757
7312
  if (existsSync(workspacePath)) {
6758
- const { getWorkspaceGitInfo } = await import("./git-utils-CsKGnzFA.js");
7313
+ const { getWorkspaceGitInfo } = await import("./git-utils-D0_yhCgF.js");
6759
7314
  const { HEAD } = await Effect.runPromise(getWorkspaceGitInfo(workspacePath));
6760
7315
  setReviewStatusSync(normalizedIssueId, { reviewedAtCommit: HEAD });
6761
7316
  console.log(`[specialists/done] Snapshotted reviewedAtCommit=${HEAD.substring(0, 8)} for ${normalizedIssueId}`);
@@ -6767,7 +7322,7 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6767
7322
  });
6768
7323
  if (specialist === "inspect" && status === "passed") yield* Effect.promise(async () => {
6769
7324
  try {
6770
- const { onInspectComplete } = await import("./inspect-agent-Dq0z1k3H.js");
7325
+ const { onInspectComplete } = await import("./inspect-agent-Brtz0URj.js");
6771
7326
  const project = resolveProjectFromIssueSync(normalizedIssueId);
6772
7327
  if (project) {
6773
7328
  const workspacePath = join(project.projectPath, "workspaces", `feature-${normalizedIssueId.toLowerCase()}`);
@@ -6783,7 +7338,7 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6783
7338
  if (project) {
6784
7339
  if (existsSync(join(project.projectPath, "workspaces", `feature-${normalizedIssueId.toLowerCase()}`))) {
6785
7340
  setReviewStatusSync(normalizedIssueId, { testStatus: "passed" });
6786
- const { initEventStore } = await import("./event-store-CpBGsO6_.js");
7341
+ const { initEventStore } = await import("./event-store-EV2cMiXj.js");
6787
7342
  await (await initEventStore()).appendAsync({
6788
7343
  type: "test.passed",
6789
7344
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -6836,8 +7391,8 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6836
7391
  if (notes?.toLowerCase().includes("conflict")) yield* Effect.promise(async () => {
6837
7392
  try {
6838
7393
  const workAgentId = `agent-${normalizedIssueId.toLowerCase()}`;
6839
- const { sessionExists } = await import("./tmux-BAvtac_u.js");
6840
- const { messageAgent, spawnAgent, getAgentStateSync } = await import("./agents-DyHKlq1I.js");
7394
+ const { sessionExists } = await import("./tmux-B6z7h_vZ.js");
7395
+ const { messageAgent, spawnAgent, getAgentStateSync } = await import("./agents-wi9NWzs2.js");
6841
7396
  if (await Effect.runPromise(sessionExists(workAgentId))) {
6842
7397
  await messageAgent(workAgentId, `MERGE CONFLICT: The merge-agent could not rebase your branch onto main due to conflicts. Please fix this now:\n\n1. git fetch origin main\n2. git rebase origin/main\n3. Resolve any conflicts (git add <file> && git rebase --continue)\n4. git push --force-with-lease\n5. Resubmit: curl -s -X POST http://localhost:3011/api/review/${normalizedIssueId}/request -H "Content-Type: application/json" -d "{}"\n\nConflict details: ${notes}`);
6843
7398
  console.log(`[specialists/done] Sent rebase instructions to ${workAgentId}`);
@@ -6851,7 +7406,7 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6851
7406
  try {
6852
7407
  const project = resolveProjectFromIssueSync(normalizedIssueId);
6853
7408
  const workspacePath = project ? join(project.projectPath, "workspaces", `feature-${normalizedIssueId.toLowerCase()}`) : void 0;
6854
- const { deliverReviewVerdictFeedback } = await import("./review-verdict-feedback-JImjut0z.js");
7409
+ const { deliverReviewVerdictFeedback } = await import("./review-verdict-feedback-CTd4QMBu.js");
6855
7410
  const result = await Effect.runPromise(deliverReviewVerdictFeedback({
6856
7411
  issueId: normalizedIssueId,
6857
7412
  verdict: status === "failed" ? "failed" : "blocked",
@@ -6892,7 +7447,7 @@ const postSpecialistsDoneRoute = HttpRouter.add("POST", "/api/specialists/done",
6892
7447
  });
6893
7448
  })));
6894
7449
  const postSpecialistsLogsCleanupAllRoute = HttpRouter.add("POST", "/api/specialists/logs/cleanup-all", httpHandler(Effect.gen(function* () {
6895
- const { cleanupAllLogsSync } = yield* Effect.promise(() => import("./specialist-logs-CQnKzuZs.js"));
7450
+ const { cleanupAllLogsSync } = yield* Effect.promise(() => import("./specialist-logs-C5p8G7tx.js"));
6896
7451
  const results = cleanupAllLogsSync();
6897
7452
  return jsonResponse({
6898
7453
  success: true,
@@ -6902,7 +7457,7 @@ const postSpecialistsLogsCleanupAllRoute = HttpRouter.add("POST", "/api/speciali
6902
7457
  });
6903
7458
  })));
6904
7459
  const getSpecialistsProjectsRoute = HttpRouter.add("GET", "/api/specialists/projects", httpHandler(Effect.gen(function* () {
6905
- const { getAllProjectSpecialistStatuses } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7460
+ const { getAllProjectSpecialistStatuses } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
6906
7461
  return jsonResponse(yield* Effect.promise(() => getAllProjectSpecialistStatuses()));
6907
7462
  })));
6908
7463
  const postSpecialistWakeRoute = HttpRouter.add("POST", "/api/specialists/:name/wake", httpHandler(Effect.gen(function* () {
@@ -6946,7 +7501,7 @@ const postSpecialistReportStatusRoute = HttpRouter.add("POST", "/api/specialists
6946
7501
  "blocked",
6947
7502
  "failed"
6948
7503
  ].includes(status)) {
6949
- const { getTmuxSessionName } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7504
+ const { getTmuxSessionName } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
6950
7505
  saveAgentRuntimeState(getTmuxSessionName(name), {
6951
7506
  state: "idle",
6952
7507
  lastActivity: (/* @__PURE__ */ new Date()).toISOString()
@@ -7067,7 +7622,7 @@ const postProjectSpecialistKillRoute = HttpRouter.add("POST", "/api/specialists/
7067
7622
  const issueId = params["issueId"];
7068
7623
  const type = params["type"];
7069
7624
  if (!validateSpecialistAgentName(type)) return jsonResponse({ error: "Invalid specialist type. Must be review-agent, test-agent, or merge-agent" }, { status: 400 });
7070
- const { getTmuxSessionName, makeSpecialistRegistryKey, getRunMetadata } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7625
+ const { getTmuxSessionName, makeSpecialistRegistryKey, getRunMetadata } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
7071
7626
  const tmuxSession = getRunMetadata(project, makeSpecialistRegistryKey(type, issueId)).tmuxSession ?? getTmuxSessionName(type, project, issueId);
7072
7627
  yield* Effect.promise(() => Effect.runPromise(killSession(tmuxSession)).catch(() => {}));
7073
7628
  saveAgentRuntimeState(tmuxSession, {
@@ -7094,7 +7649,7 @@ const getProjectSpecialistRunsRoute = HttpRouter.add("GET", "/api/specialists/:p
7094
7649
  if (limitParam) limit = parseInt(limitParam, 10);
7095
7650
  if (offsetParam) offset = parseInt(offsetParam, 10);
7096
7651
  }
7097
- const { listRunLogsSync } = yield* Effect.promise(() => import("./specialist-logs-CQnKzuZs.js"));
7652
+ const { listRunLogsSync } = yield* Effect.promise(() => import("./specialist-logs-C5p8G7tx.js"));
7098
7653
  return jsonResponse(listRunLogsSync(project, type, {
7099
7654
  limit,
7100
7655
  offset
@@ -7105,7 +7660,7 @@ const getProjectSpecialistRunStreamRoute = HttpRouter.add("GET", "/api/specialis
7105
7660
  const project = params["project"];
7106
7661
  const type = params["type"];
7107
7662
  const runId = params["runId"];
7108
- const { getRunLogPath, isRunLogActive } = yield* Effect.promise(() => import("./specialist-logs-CQnKzuZs.js"));
7663
+ const { getRunLogPath, isRunLogActive } = yield* Effect.promise(() => import("./specialist-logs-C5p8G7tx.js"));
7109
7664
  const logPath = getRunLogPath(project, type, runId);
7110
7665
  if (!existsSync(logPath)) return jsonResponse({ error: "Run log not found" }, { status: 404 });
7111
7666
  const encoder = new TextEncoder();
@@ -7164,7 +7719,7 @@ const getProjectSpecialistRunRoute = HttpRouter.add("GET", "/api/specialists/:pr
7164
7719
  const project = params["project"];
7165
7720
  const type = params["type"];
7166
7721
  const runId = params["runId"];
7167
- const { getRunLogSync, parseLogMetadata } = yield* Effect.promise(() => import("./specialist-logs-CQnKzuZs.js"));
7722
+ const { getRunLogSync, parseLogMetadata } = yield* Effect.promise(() => import("./specialist-logs-C5p8G7tx.js"));
7168
7723
  const content = getRunLogSync(project, type, runId);
7169
7724
  if (!content) return jsonResponse({ error: "Run log not found" }, { status: 404 });
7170
7725
  return jsonResponse({
@@ -7178,7 +7733,7 @@ const postProjectSpecialistRunTerminateRoute = HttpRouter.add("POST", "/api/spec
7178
7733
  const project = params["project"];
7179
7734
  const type = params["type"];
7180
7735
  if (!validateSpecialistAgentName(type)) return jsonResponse({ error: "Invalid specialist type. Must be review-agent, test-agent, or merge-agent" }, { status: 400 });
7181
- const { terminateSpecialist } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7736
+ const { terminateSpecialist } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
7182
7737
  yield* Effect.promise(() => terminateSpecialist(project, type));
7183
7738
  return jsonResponse({
7184
7739
  success: true,
@@ -7190,7 +7745,7 @@ const postProjectSpecialistGracePauseRoute = HttpRouter.add("POST", "/api/specia
7190
7745
  const project = params["project"];
7191
7746
  const type = params["type"];
7192
7747
  if (!validateSpecialistAgentName(type)) return jsonResponse({ error: "Invalid specialist type. Must be review-agent, test-agent, or merge-agent" }, { status: 400 });
7193
- const { pauseGracePeriod } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7748
+ const { pauseGracePeriod } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
7194
7749
  if (pauseGracePeriod(project, type)) return jsonResponse({
7195
7750
  success: true,
7196
7751
  message: "Grace period paused"
@@ -7202,7 +7757,7 @@ const postProjectSpecialistGraceResumeRoute = HttpRouter.add("POST", "/api/speci
7202
7757
  const project = params["project"];
7203
7758
  const type = params["type"];
7204
7759
  if (!validateSpecialistAgentName(type)) return jsonResponse({ error: "Invalid specialist type. Must be review-agent, test-agent, or merge-agent" }, { status: 400 });
7205
- const { resumeGracePeriod } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7760
+ const { resumeGracePeriod } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
7206
7761
  if (resumeGracePeriod(project, type)) return jsonResponse({
7207
7762
  success: true,
7208
7763
  message: "Grace period resumed"
@@ -7214,7 +7769,7 @@ const postProjectSpecialistGraceExitRoute = HttpRouter.add("POST", "/api/special
7214
7769
  const project = params["project"];
7215
7770
  const type = params["type"];
7216
7771
  if (!validateSpecialistAgentName(type)) return jsonResponse({ error: "Invalid specialist type. Must be review-agent, test-agent, or merge-agent" }, { status: 400 });
7217
- const { exitGracePeriod } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7772
+ const { exitGracePeriod } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
7218
7773
  exitGracePeriod(project, type);
7219
7774
  return jsonResponse({
7220
7775
  success: true,
@@ -7226,7 +7781,7 @@ const getProjectSpecialistGraceRoute = HttpRouter.add("GET", "/api/specialists/:
7226
7781
  const project = params["project"];
7227
7782
  const type = params["type"];
7228
7783
  if (!validateSpecialistAgentName(type)) return jsonResponse({ error: "Invalid specialist type. Must be review-agent, test-agent, or merge-agent" }, { status: 400 });
7229
- const { getGracePeriodState } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7784
+ const { getGracePeriodState } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
7230
7785
  const state = getGracePeriodState(project, type);
7231
7786
  if (state) return jsonResponse(state);
7232
7787
  else return jsonResponse({ error: "No active grace period" }, { status: 404 });
@@ -7235,7 +7790,7 @@ const getProjectSpecialistContextRoute = HttpRouter.add("GET", "/api/specialists
7235
7790
  const params = yield* HttpRouter.params;
7236
7791
  const project = params["project"];
7237
7792
  const type = params["type"];
7238
- const { loadContextDigest } = yield* Effect.promise(() => import("./specialist-context-CJ-QjFR-.js"));
7793
+ const { loadContextDigest } = yield* Effect.promise(() => import("./specialist-context-Bn3dCGS2.js"));
7239
7794
  const digest = loadContextDigest(project, type);
7240
7795
  if (digest) return jsonResponse({ digest });
7241
7796
  else return jsonResponse({ error: "No context digest found" }, { status: 404 });
@@ -7244,7 +7799,7 @@ const postProjectSpecialistContextRegenerateRoute = HttpRouter.add("POST", "/api
7244
7799
  const params = yield* HttpRouter.params;
7245
7800
  const project = params["project"];
7246
7801
  const type = params["type"];
7247
- const { regenerateContextDigest } = yield* Effect.promise(() => import("./specialist-context-CJ-QjFR-.js"));
7802
+ const { regenerateContextDigest } = yield* Effect.promise(() => import("./specialist-context-Bn3dCGS2.js"));
7248
7803
  const digest = yield* Effect.promise(() => regenerateContextDigest(project, type));
7249
7804
  if (digest) return jsonResponse({
7250
7805
  digest,
@@ -7263,7 +7818,7 @@ const postProjectSpecialistCompleteRoute = HttpRouter.add("POST", "/api/speciali
7263
7818
  "blocked"
7264
7819
  ].includes(status)) return jsonResponse({ error: "Valid status (passed/failed/blocked) is required" }, { status: 400 });
7265
7820
  if (!validateSpecialistAgentName(type)) return jsonResponse({ error: "Invalid specialist type. Must be review-agent, test-agent, or merge-agent" }, { status: 400 });
7266
- const { signalSpecialistCompletion } = yield* Effect.promise(() => import("./specialists-Dqe6_HE5.js"));
7821
+ const { signalSpecialistCompletion } = yield* Effect.promise(() => import("./specialists-DM6bWjsi.js"));
7267
7822
  signalSpecialistCompletion(project, type, {
7268
7823
  status,
7269
7824
  notes
@@ -7297,8 +7852,8 @@ const postProjectSpecialistLogsCleanupRoute = HttpRouter.add("POST", "/api/speci
7297
7852
  const params = yield* HttpRouter.params;
7298
7853
  const project = params["project"];
7299
7854
  const type = params["type"];
7300
- const { cleanupOldLogsSync } = yield* Effect.promise(() => import("./specialist-logs-CQnKzuZs.js"));
7301
- const { getSpecialistRetention } = yield* Effect.promise(() => import("./projects-BKkwpH6q.js"));
7855
+ const { cleanupOldLogsSync } = yield* Effect.promise(() => import("./specialist-logs-C5p8G7tx.js"));
7856
+ const { getSpecialistRetention } = yield* Effect.promise(() => import("./projects-vjjcmiN8.js"));
7302
7857
  const retention = getSpecialistRetention(project);
7303
7858
  const deleted = cleanupOldLogsSync(project, type, {
7304
7859
  maxDays: retention.max_days,
@@ -7319,7 +7874,7 @@ const postProjectReviewRestartRoute = HttpRouter.add("POST", "/api/specialists/:
7319
7874
  const project = params["project"];
7320
7875
  const issueId = params["issueId"];
7321
7876
  const { model, harness } = yield* readJsonBody;
7322
- const { killAllReviewerSessions } = yield* Effect.promise(() => import("./review-agent-CCum8sGe.js"));
7877
+ const { killAllReviewerSessions } = yield* Effect.promise(() => import("./review-agent-DNboMyW4.js"));
7323
7878
  const killResult = yield* killAllReviewerSessions(project, issueId);
7324
7879
  const projectConfig = resolveProjectFromIssueSync(issueId);
7325
7880
  if (!projectConfig) return jsonResponse({ error: `Cannot resolve project for ${issueId}` }, { status: 404 });
@@ -7333,7 +7888,7 @@ const postProjectReviewRestartRoute = HttpRouter.add("POST", "/api/specialists/:
7333
7888
  }));
7334
7889
  branch = stdout.trim() || "unknown";
7335
7890
  } catch {}
7336
- const { spawnReviewRoleForIssue } = yield* Effect.promise(() => import("./review-agent-CCum8sGe.js"));
7891
+ const { spawnReviewRoleForIssue } = yield* Effect.promise(() => import("./review-agent-DNboMyW4.js"));
7337
7892
  const prUrl = getReviewStatusSync(issueId)?.prUrl;
7338
7893
  const result = yield* spawnReviewRoleForIssue({
7339
7894
  issueId,
@@ -7482,6 +8037,6 @@ const specialistsProjectRouteLayer = Layer.mergeAll(getProjectSpecialistStatusRo
7482
8037
  */
7483
8038
  const specialistsRouteLayer = Layer.mergeAll(specialistsLegacyRouteLayer, specialistsProjectRouteLayer);
7484
8039
  //#endregion
7485
- export { runInBatches as $, handleConversationDelete as A, stopConversationRuntime as B, isPiControlChannelHarness as C, conversationNeedsRunningRepair as D, validateConversationControlAckOrigin as E, isInsideGitWorkTree as F, cleanupOrphanedConversationAttachments as G, waitForPiTuiReady as H, resolveAllowedHarness as I, extractConversationAttachmentPaths as J, cleanupUnreferencedConversationAttachments as K, shouldReportUnresolvedLiveSession as L, handleConversationResume as M, handleConversationStop as N, conversationSessionAliveFromState as O, handleConversationSwitchModel as P, removeConversationAttachment as Q, spawnConversationSession as R, handleConversationThinkingLevel as S, resolveConversationDeliveryMethod as T, waitForTmuxSession as U, tmuxSessionExists as V, cleanupConversationAttachments as W, hasConversationAttachment as X, getConversationAttachmentsRoot as Y, isManagedConversationAttachmentPath as Z, handleConversationCodexApproval as _, validateOrigin as _t, agentsRouteLayer as a, panCliInvocation as at, handleConversationDeliveryMethod as b, getDefaultCwd as bt, hasDashboardInternalToken as c, resolveAgentHarness as ct, rejectUnsafeDashboardMutationRequest as d, resolveJsonlPath as dt, evaluateSpawnGuardrails as et, saveAgentStateAndEmitEvent as f, resolvePiSessionPath as ft, handleConversationAbort as g, isTrustedOriginForHost as gt, deliverConversationViaControlChannel as h, getHeaderFromMap as ht, validateSpecialistAutoCompleteMetadata as i, getWorkAgentLifecycleStateSync as it, handleConversationRestartAll as j, handleConversationCreate as k, rejectUnauthorizedDashboardRequest as l, resolveClaudeSessionId as lt, codexConversationPendingInput as m, waitForSessionRespawn as mt, _serverManagedMerges as n, resolveAgentGitInfo as nt, dashboardCsrfToken as o, spawnPanCli as ot, saveAgentStateAndEmitEventProgram as p, isRespawnPending as pt, ensureConversationAttachmentDir as q, firePostMergeLifecycle as r, resolveConversationGitInfo as rt, dashboardSessionCookieHeader as s, readLauncherPinnedSessionId as st, specialistsRouteLayer as t, invalidateAgentsCache as tt, rejectUnauthorizedDashboardSessionMintRequest as u, resolveCodexRolloutPath as ut, handleConversationCompact as v, validateOriginHeaders as vt, pickDeliverAs as w, handleConversationPlanAction as x, jsonResponse as xt, handleConversationControlAck as y, httpHandler as yt, startConversationModelBackfill as z };
8040
+ export { panCliInvocation as $, resolveAllowedHarness as A, cleanupUnreferencedConversationAttachments as B, handleConversationCreate as C, jsonStringResponse as Ct, handleConversationStop as D, handleConversationResume as E, tmuxSessionExists as F, isManagedConversationAttachmentPath as G, extractConversationAttachmentPaths as H, waitForPiTuiReady as I, evaluateSpawnGuardrails as J, removeConversationAttachment as K, waitForTmuxSession as L, spawnConversationSession as M, startConversationModelBackfill as N, handleConversationSwitchModel as O, stopConversationRuntime as P, getWorkAgentLifecycleStateSync as Q, cleanupConversationAttachments as R, conversationSessionAliveFromState as S, jsonResponse as St, handleConversationRestartAll as T, getConversationAttachmentsRoot as U, ensureConversationAttachmentDir as V, hasConversationAttachment as W, resolveAgentGitInfo as X, invalidateAgentsCache as Y, resolveConversationGitInfo as Z, isPiControlChannelHarness as _, isTrustedOriginForHost as _t, agentsRouteLayer as a, resolveCodexRolloutPath as at, validateConversationControlAckOrigin as b, httpHandler as bt, codexConversationPendingInput as c, dashboardCsrfToken as ct, handleConversationCodexApproval as d, rejectUnauthorizedDashboardRequest as dt, spawnPanCli as et, handleConversationCompact as f, rejectUnauthorizedDashboardSessionMintRequest as ft, handleConversationThinkingLevel as g, getHeaderFromMap as gt, handleConversationPlanAction as h, waitForSessionRespawn as ht, validateSpecialistAutoCompleteMetadata as i, resolveClaudeSessionId as it, shouldReportUnresolvedLiveSession as j, isInsideGitWorkTree as k, deliverConversationViaControlChannel as l, dashboardSessionCookieHeader as lt, handleConversationDeliveryMethod as m, isRespawnPending as mt, _serverManagedMerges as n, resolveAcpTranscriptPath as nt, saveAgentStateAndEmitEvent as o, resolveJsonlPath as ot, handleConversationControlAck as p, rejectUnsafeDashboardMutationRequest as pt, runInBatches as q, firePostMergeLifecycle as r, resolveAgentHarness as rt, saveAgentStateAndEmitEventProgram as s, resolvePiSessionPath as st, specialistsRouteLayer as t, readLauncherPinnedSessionId as tt, handleConversationAbort as u, hasDashboardInternalToken as ut, pickDeliverAs as v, validateOrigin as vt, handleConversationDelete as w, conversationNeedsRunningRepair as x, getDefaultCwd as xt, resolveConversationDeliveryMethod as y, validateOriginHeaders as yt, cleanupOrphanedConversationAttachments as z };
7486
8041
 
7487
- //# sourceMappingURL=specialists-Bg9v4z3F.js.map
8042
+ //# sourceMappingURL=specialists-CC4r-c2s.js.map