@overdeck/desktop 0.45.24 → 0.46.0

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 (511) hide show
  1. package/drizzle/overdeck/0000_overdeck_init.sql +32 -0
  2. package/package.json +1 -1
  3. package/server/{acceptance-criteria-DDCnXF58.js → acceptance-criteria-CCwFGICj.js} +2 -2
  4. package/server/activity-logger-CMthG6Vd.js +3 -0
  5. package/server/{activity-logger-DDlOIAML.js → activity-logger-CyteR4JO.js} +24 -13
  6. package/server/{advancing-selfheal-BVwW4qRn.js → advancing-selfheal-De1L-Sb4.js} +6 -6
  7. package/server/{agent-gc-CHcnvonI.js → agent-gc-CBULruzm.js} +5 -5
  8. package/server/agent-state-VQk8fY9F.js +3 -0
  9. package/server/{agent-state-service-BsgWxZuB.js → agent-state-service-BlnSkRFA.js} +5 -5
  10. package/server/{agent-state-service-D5UGKIJL.js → agent-state-service-hZpxIw1E.js} +1 -1
  11. package/server/agents-BKKkxKgK.js +3 -0
  12. package/server/{agents-EbI0zCJl.js → agents-C5rWYhb6.js} +4 -4
  13. package/server/archive-planning-0FJ6Xd3b.js +2 -0
  14. package/server/{archive-planning-Du25Jtst.js → archive-planning-C0lAbNB_.js} +16 -24
  15. package/server/{artifact-index-jobs-CbEDfCDu.js → artifact-index-jobs-ug6ZxIP5.js} +2 -2
  16. package/server/{assemble-J2ZZ2C15.js → assemble-DnffNpOo.js} +2 -2
  17. package/server/{backlog-auto-trigger-2R2jog7n.js → backlog-auto-trigger-Dh-i9PQf.js} +3 -3
  18. package/server/{behavior-B3exJ42O.js → behavior-CfIg6pKp.js} +2 -2
  19. package/server/bridge-pool-patrol-CUFmjVDN.js +136 -0
  20. package/server/{briefing-freshness-D1wzUIWv.js → briefing-freshness-DHmCSLar.js} +2 -2
  21. package/server/{build-info-CZk_TITh.js → build-info-BP160gU7.js} +1 -1
  22. package/server/{build-info-Ie_D0_BT.js → build-info-CuFfxZxp.js} +5 -3
  23. package/server/{checkpoint-manager-BI1Wxoku.js → checkpoint-manager-DEWD82qK.js} +2 -2
  24. package/server/{checkpoint-manager-DwbQoqrC.js → checkpoint-manager-Dxh0FYsv.js} +1 -1
  25. package/server/checkpoint-worker.js +1 -1
  26. package/server/{checkpoints-CQB1h765.js → checkpoints-CZvbtLnr.js} +2 -2
  27. package/server/{checkpoints-BJIs5QIA.js → checkpoints-DMTgXuLG.js} +1 -1
  28. package/server/{claude-hooks-provision-8Ngo8TpT.js → claude-hooks-provision-CN-xo-do.js} +1 -1
  29. package/server/{claude-hooks-provision-D8GXF3_s.js → claude-hooks-provision-fvy4RD2b.js} +13 -2
  30. package/server/{claude-permissions-DNWEVV6z.js → claude-permissions-CN0cSCPp.js} +2 -2
  31. package/server/{claude-settings-overlay-CEdaqpWI.js → claude-settings-overlay-CJn35FHQ.js} +2 -2
  32. package/server/{claude-settings-overlay-DWy-Bb4Y.js → claude-settings-overlay-Dabi9slj.js} +1 -1
  33. package/server/cli-DmXETsw-.js +3 -0
  34. package/server/{cli-RoOx5DUf.js → cli-oCWYA1pE.js} +9 -9
  35. package/server/{cliproxy-C8a1vtTU.js → cliproxy-C8mB3KuB.js} +2 -2
  36. package/server/{cliproxy-fHfIeiGd.js → cliproxy-PGwTX_o8.js} +1 -1
  37. package/server/{close-issue-DoOifFIK.js → close-issue-D5MF5GqF.js} +5 -5
  38. package/server/{closed-issue-reaper-CktPXo8m.js → closed-issue-reaper-CJ24u0Zt.js} +1 -1
  39. package/server/{closed-issue-reaper-Bk3SU4RC.js → closed-issue-reaper-Cltx86x5.js} +37 -16
  40. package/server/{codex-GcHuUF-k.js → codex-BdEsUFWh.js} +1 -1
  41. package/server/{codex-BZXGhUVB.js → codex-Drlf5Xhf.js} +36 -7
  42. package/server/{codex-parser-CFZ1_1Jl.js → codex-parser-DpB89Vqa.js} +2 -2
  43. package/server/compaction-continuation-K2AxQX4H.js +200 -0
  44. package/server/{config-CKxYiCmX.js → config-B2IMcBxx.js} +4 -3
  45. package/server/{config-DzV30TOm.js → config-DV-grfQc.js} +1 -1
  46. package/server/{config-BZ0sHTju.js → config-Dr3pW6w6.js} +2 -2
  47. package/server/{config-yaml-DySPNmKN.js → config-yaml-CmOPzmBb.js} +268 -14
  48. package/server/{conflict-gate-BaCVrH-N.js → conflict-gate-B9cN3SBi.js} +4 -4
  49. package/server/{conflict-gate-BhAY7TUd.js → conflict-gate-Dh754uyA.js} +1 -1
  50. package/server/{context-layers-BrPmE__h.js → context-layers-D6LZo3l1.js} +2 -2
  51. package/server/{context-layers-igZ4FI4_.js → context-layers-dR5YxHYB.js} +3 -3
  52. package/server/{control-settings-BQp7_4uD.js → control-settings-C7DUehE1.js} +49 -19
  53. package/server/{conversation-compaction-CgrR8YYp.js → conversation-compaction-BoqdDXWF.js} +8 -8
  54. package/server/{conversation-compaction-eQB5CkOe.js → conversation-compaction-Dj4FAKoZ.js} +1 -1
  55. package/server/{conversation-service-DPJ71F33.js → conversation-service-5aCnq1zR.js} +8 -7
  56. package/server/conversation-service-BCxl4f8Z.js +3 -0
  57. package/server/{conversations-6AfUfbwc.js → conversations-BUpYsOZg.js} +4 -4
  58. package/server/{conversations-CAsJF9kh.js → conversations-C34dyIb9.js} +1 -1
  59. package/server/{cost-B2ddP6wq.js → cost-Bgcub5kW.js} +12 -2
  60. package/server/{cost-R-m6Yi-W.js → cost-CvxRrucd.js} +3 -3
  61. package/server/{cost-sync-BsN6ER0n.js → cost-sync-Dv3LUaNW.js} +3 -3
  62. package/server/{dag-B1ucnjDU.js → dag-ombYYt4s.js} +2 -2
  63. package/server/{dangerous-git-ops-BXqeWVSf.js → dangerous-git-ops-BcJPC5Rr.js} +1 -1
  64. package/server/dashboard-db-worker.js +7 -7
  65. package/server/deacon-DrXqT9LQ.js +4 -0
  66. package/server/{deacon-CU_cAYSR.js → deacon-hi9KLPLT.js} +2933 -2704
  67. package/server/{deacon-nudge-log-6wVqAaqz.js → deacon-nudge-log-BNBebzeL.js} +3 -3
  68. package/server/deacon-pending-lifecycle-Cpk0k7Hi.js +33 -0
  69. package/server/{deacon-swarm-BXZZ1Z2-.js → deacon-swarm-DBxUaO-A.js} +13 -13
  70. package/server/deacon-swarm-I2JfX5Tg.js +2 -0
  71. package/server/deacon.js +14 -19
  72. package/server/dead-end-trip-CQcP70ND.js +13 -0
  73. package/server/delivery-Da0zEMY4.js +3 -0
  74. package/server/{deploy-patrol-AiFoP8dw.js → deploy-patrol-Df-Tmaq0.js} +69 -18
  75. package/server/deploy-queue-9SrhZR63.js +3 -0
  76. package/server/deploy-queue-UjKGYsDe.js +169 -0
  77. package/server/deploy-window-jOiPRHL0.js +3 -0
  78. package/server/{deploy-window-C3F-tihF.js → deploy-window-rpqpWqHA.js} +40 -16
  79. package/server/{dev-supervisor-bcOWqdQE.js → dev-supervisor-CC46Ig8q.js} +2 -2
  80. package/server/{devcontainer-renderer-_d6XhyND.js → devcontainer-renderer-68CSCRnu.js} +3 -3
  81. package/server/{dist-Cb_0uvpS.js → dist-CJy72uEg.js} +1 -1
  82. package/server/dist-DSlxIU1H.js +3 -0
  83. package/server/{dist-src-BSoS7mkd.js → dist-src-Bik83hFi.js} +1 -1
  84. package/server/docker-BMVTSFL5.js +3 -0
  85. package/server/{docker-DcMTFTUC.js → docker-Dam7mbIB.js} +2 -2
  86. package/server/{dod-C6n9RV3G.js → dod-Dar-yheg.js} +8 -4
  87. package/server/durable-review-pipeline-CGZ5QBxh.js +17 -0
  88. package/server/durable-review-pipeline-jpgCJZFp.js +3 -0
  89. package/server/{ensure-devcontainer-DNi-ono3.js → ensure-devcontainer-B057YA81.js} +4 -4
  90. package/server/{ensure-devcontainer-BZgOhRo7.js → ensure-devcontainer-C98V8yW-.js} +1 -1
  91. package/server/{esm-shims-OSHAYaS1.js → esm-shims-Br9BPdkR.js} +1 -1
  92. package/server/{event-store-EV2cMiXj.js → event-store-ELqkVOn0.js} +1 -1
  93. package/server/{event-store-CQL6NeqB.js → event-store-zY5MJHgF.js} +23 -4
  94. package/server/{events-BTdrdEq8.js → events-D6pyqhzM.js} +1 -1
  95. package/server/{events-DTfAe4My.js → events-D9PR1IC0.js} +3 -3
  96. package/server/{factory-CFibJuaw.js → factory-CRoOpGsF.js} +5 -5
  97. package/server/{feature-registry-population-DKZFMbFt.js → feature-registry-population--YUXU999.js} +4 -4
  98. package/server/{feature-registry-storage-BdPuLcBX.js → feature-registry-storage-BORBTo0y.js} +2 -2
  99. package/server/{features-ez2271oH.js → features--nFkJY8v.js} +2 -2
  100. package/server/{feedback-target-CoFDHiM8.js → feedback-target-C0ZzyHvU.js} +1 -1
  101. package/server/{feedback-target-7mqqwrX6.js → feedback-target-Dt9BsapX.js} +8 -8
  102. package/server/{feedback-writer-CY5L1lB7.js → feedback-writer-BG0RUZiu.js} +5 -5
  103. package/server/{feedback-writer-D3a22RCd.js → feedback-writer-D1fm-5Z8.js} +1 -1
  104. package/server/{flywheel-SVF02Jpq.js → flywheel-BekukH4i.js} +1 -1
  105. package/server/{flywheel-DHrgCphM.js → flywheel-BiT3odRb.js} +23 -15
  106. package/server/{flywheel-DzGtWnJ1.js → flywheel-CLVB55Be.js} +16 -15
  107. package/server/flywheel-merge-order-BJv1DZz1.js +462 -0
  108. package/server/flywheel-merge-order-BKw9o6ku.js +2 -0
  109. package/server/forge-CZbSg6Ds.js +383 -0
  110. package/server/forge-DK3blQb_.js +3 -0
  111. package/server/{from-kNxT0bZC.js → from-D_4_dk7i.js} +1 -1
  112. package/server/{fts-db-CE46vDkZ.js → fts-db-BeOomFa-.js} +2 -2
  113. package/server/{fts-operations-CqY6zl-i.js → fts-operations-BdewJ_g9.js} +2 -2
  114. package/server/git-utils-CI6cwyyC.js +3 -0
  115. package/server/{git-utils-CRw2zLPC.js → git-utils-ameCgDdy.js} +30 -5
  116. package/server/{github-app-C3QrAUd0.js → github-app-CQ1Lo6LA.js} +3 -3
  117. package/server/{github-app-ClZ2lk8D.js → github-app-DtAsvwzK.js} +1 -1
  118. package/server/{github-client-BnIsSrEN.js → github-client-BfyDxuRN.js} +2 -2
  119. package/server/{github-client-DfQYVUaw.js → github-client-Cj0yl_07.js} +1 -1
  120. package/server/github-pr-lookup-BDq6Wznz.js +58 -0
  121. package/server/{harness-binary-DPUi-Pv8.js → harness-binary-DqkftwTb.js} +2 -2
  122. package/server/{harness-resolve-3lNZkyT4.js → harness-resolve-CqMqM1Mb.js} +4 -4
  123. package/server/{health-events-BBcTYQKU.js → health-events-CazRxJUS.js} +2 -2
  124. package/server/{health-events-BDOHby5Q.js → health-events-_xfTaOUs.js} +1 -1
  125. package/server/{hooks-CKZgXlJb.js → hooks-BO3QT1sn.js} +2 -2
  126. package/server/{hooks-DPVX1rwQ.js → hooks-BnJsxlSq.js} +1 -1
  127. package/server/{hume-Cz8igP9E.js → hume-CDYJxqx-.js} +2 -2
  128. package/server/hume-Dq--7rOQ.js +3 -0
  129. package/server/{idle-stack-reaper-CuLF0j2H.js → idle-stack-reaper-BYqERdkE.js} +1 -1
  130. package/server/{idle-stack-reaper-DDIRwxUY.js → idle-stack-reaper-CnGx_PO_.js} +99 -6
  131. package/server/in-flight-guard-Bq3epG58.js +45 -0
  132. package/server/{infra-BEqFxlJc.js → infra-BjuxvjdS.js} +70 -5
  133. package/server/{injection-CPUIqNhI.js → injection-DZxlV2al.js} +1 -1
  134. package/server/{injection-C8aiVJrF.js → injection-UCf8EsjX.js} +7 -7
  135. package/server/{inspect-agent-BLuJ1AkT.js → inspect-agent-6kylZnrm.js} +26 -25
  136. package/server/inspect-agent-BVwDrVrz.js +2 -0
  137. package/server/{internal-token-s0-oKybJ.js → internal-token-DvMTQSOS.js} +2 -2
  138. package/server/{internal-token-BrcU6qfe.js → internal-token-M9omeXM1.js} +1 -1
  139. package/server/{io-C9kF1wtJ.js → io-VHvc0Ssp.js} +43 -26
  140. package/server/{issue-closed-CDJk4pIp.js → issue-closed-B6KJkmHZ.js} +7 -7
  141. package/server/{issue-lifecycle-B8sZHVHh.js → issue-lifecycle-9gk3fnXN.js} +1 -1
  142. package/server/{issue-lifecycle-ojZAzDBP.js → issue-lifecycle-C4Zj0WmE.js} +34 -28
  143. package/server/{issue-service-singleton-Yx5DdmVJ.js → issue-service-singleton-DmCh7EHj.js} +10 -10
  144. package/server/{issue-service-singleton-BvJ09SaR.js → issue-service-singleton-PMx8F1jr.js} +1 -1
  145. package/server/{issues-BMmP_77e.js → issues-CgnQ1OGQ.js} +2 -2
  146. package/server/{jsonl-parser-BXSlHwEh.js → jsonl-parser-DJOH_O-w.js} +5 -4
  147. package/server/{label-cleanup-B0UJwnv5.js → label-cleanup-Dv7AV_R1.js} +3 -3
  148. package/server/{label-reconciler-B4iOAjlC.js → label-reconciler--F85rufp.js} +4 -4
  149. package/server/{lifecycle-yPd___5E.js → lifecycle-CTdOWYMi.js} +2 -2
  150. package/server/{lifecycle-xmBfK2GJ.js → lifecycle-DDN8ApVY.js} +6 -7
  151. package/server/{lifecycle-io-BREGpnLk.js → lifecycle-io-D-lGdzK3.js} +5 -5
  152. package/server/{lifecycle-io-DPo7x11I.js → lifecycle-io-D_zTLvP1.js} +1 -1
  153. package/server/{linear-client-BsuUvkdA.js → linear-client-BQU36QqE.js} +2 -2
  154. package/server/{linear-client-CTskEeT_.js → linear-client-CY4O_Tu3.js} +1 -1
  155. package/server/linear-mcp-auth-BnU4TYfY.js +280 -0
  156. package/server/linear-mcp-auth-NYBq_oHZ.js +2 -0
  157. package/server/memory-fts-worker.js +1 -1
  158. package/server/{merge-CefQNAnq.js → merge-Bpx4jjht.js} +13 -11
  159. package/server/{merge-agent-4WOWlSYA.js → merge-agent-DdMVSV2Y.js} +233 -83
  160. package/server/{merge-agent-CKkgIBPB.js → merge-agent-DrXa1CpW.js} +1 -1
  161. package/server/{merge-batch-ClFn2T-y.js → merge-batch-C5q1c1GX.js} +1 -1
  162. package/server/merge-completeness-BMm8tMyZ.js +277 -0
  163. package/server/merge-completeness-D0ljXbk3.js +3 -0
  164. package/server/{merge-B1DiziGU.js → merge-idOG--29.js} +1 -1
  165. package/server/merge-ops-DdpTiEMt.js +2 -0
  166. package/server/merge-rebase-DGQpYJU3.js +2 -0
  167. package/server/{merge-rebase-BfaTyXsw.js → merge-rebase-xLnMlw5h.js} +3 -3
  168. package/server/{merge-set-DJ8-UErs.js → merge-set-DH7ot3yz.js} +11 -5
  169. package/server/merge-set-DzsOTBg5.js +3 -0
  170. package/server/{merge-sync-VPV28g0g.js → merge-sync-CQ-LgZ1_.js} +406 -153
  171. package/server/merge-sync-Ca7F5JnQ.js +3 -0
  172. package/server/{merge-train-CHopycP0.js → merge-train-DTEMyIu4.js} +18 -8
  173. package/server/{merge-train-deps-D1cOZzy2.js → merge-train-deps-CNPJzepw.js} +4 -4
  174. package/server/merge-verification-x3JO8Dtz.js +220 -0
  175. package/server/merged-docker-cleanup-worker-CTpC27Bs.js +107 -0
  176. package/server/merged-docker-cleanup-worker-Cdi9apm-.js +3 -0
  177. package/server/{multipart-parser-duPAfSBw.js → multipart-parser-IMSBfePV.js} +2 -2
  178. package/server/{no-resume-mode-Nw29vaFb.js → no-resume-mode-C5ULAXwO.js} +1 -1
  179. package/server/node_modules/.package-lock.json +27 -27
  180. package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/package.json +3 -3
  181. package/server/node_modules/@google-cloud/common/package.json +2 -2
  182. package/server/node_modules/gaxios/build/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  183. package/server/node_modules/gaxios/build/esm/tsconfig.tsbuildinfo +1 -1
  184. package/server/node_modules/gaxios/package.json +1 -1
  185. package/server/node_modules/google-auth-library/build/src/auth/googleauth.js +24 -19
  186. package/server/node_modules/google-auth-library/package.json +1 -1
  187. package/server/node_modules/google-gax/build/protos/google/api/auth.proto +2 -1
  188. package/server/node_modules/google-gax/build/protos/google/api/backend.proto +12 -1
  189. package/server/node_modules/google-gax/build/protos/google/api/billing.proto +1 -1
  190. package/server/node_modules/google-gax/build/protos/google/api/client.proto +165 -53
  191. package/server/node_modules/google-gax/build/protos/google/api/cloudquotas/v1/resources.proto +1 -1
  192. package/server/node_modules/google-gax/build/protos/google/api/cloudquotas/v1beta/quota_adjuster_settings.proto +7 -3
  193. package/server/node_modules/google-gax/build/protos/google/api/cloudquotas/v1beta/resources.proto +1 -1
  194. package/server/node_modules/google-gax/build/protos/google/api/config_change.proto +1 -1
  195. package/server/node_modules/google-gax/build/protos/google/api/context.proto +1 -1
  196. package/server/node_modules/google-gax/build/protos/google/api/control.proto +3 -3
  197. package/server/node_modules/google-gax/build/protos/google/api/distribution.proto +1 -1
  198. package/server/node_modules/google-gax/build/protos/google/api/documentation.proto +1 -1
  199. package/server/node_modules/google-gax/build/protos/google/api/endpoint.proto +1 -1
  200. package/server/node_modules/google-gax/build/protos/google/api/error_reason.proto +186 -1
  201. package/server/node_modules/google-gax/build/protos/google/api/expr/v1alpha1/checked.proto +31 -3
  202. package/server/node_modules/google-gax/build/protos/google/api/expr/v1alpha1/eval.proto +8 -9
  203. package/server/node_modules/google-gax/build/protos/google/api/expr/v1alpha1/explain.proto +1 -2
  204. package/server/node_modules/google-gax/build/protos/google/api/expr/v1alpha1/syntax.proto +20 -22
  205. package/server/node_modules/google-gax/build/protos/google/api/expr/v1alpha1/value.proto +2 -3
  206. package/server/node_modules/google-gax/build/protos/google/api/field_behavior.proto +1 -1
  207. package/server/node_modules/google-gax/build/protos/google/api/field_info.proto +1 -1
  208. package/server/node_modules/google-gax/build/protos/google/api/http.proto +1 -1
  209. package/server/node_modules/google-gax/build/protos/google/api/httpbody.proto +1 -1
  210. package/server/node_modules/google-gax/build/protos/google/api/label.proto +1 -1
  211. package/server/node_modules/google-gax/build/protos/google/api/launch_stage.proto +1 -1
  212. package/server/node_modules/google-gax/build/protos/google/api/log.proto +1 -1
  213. package/server/node_modules/google-gax/build/protos/google/api/logging.proto +1 -1
  214. package/server/node_modules/google-gax/build/protos/google/api/metric.proto +1 -1
  215. package/server/node_modules/google-gax/build/protos/google/api/monitored_resource.proto +1 -1
  216. package/server/node_modules/google-gax/build/protos/google/api/monitoring.proto +1 -1
  217. package/server/node_modules/google-gax/build/protos/google/api/policy.proto +1 -1
  218. package/server/node_modules/google-gax/build/protos/google/api/quota.proto +1 -1
  219. package/server/node_modules/google-gax/build/protos/google/api/resource.proto +1 -1
  220. package/server/node_modules/google-gax/build/protos/google/api/routing.proto +8 -4
  221. package/server/node_modules/google-gax/build/protos/google/api/service.proto +2 -3
  222. package/server/node_modules/google-gax/build/protos/google/api/source_info.proto +1 -1
  223. package/server/node_modules/google-gax/build/protos/google/api/system_parameter.proto +1 -1
  224. package/server/node_modules/google-gax/build/protos/google/api/usage.proto +7 -27
  225. package/server/node_modules/google-gax/build/protos/google/api/visibility.proto +1 -1
  226. package/server/node_modules/google-gax/build/protos/google/longrunning/operations.proto +20 -1
  227. package/server/node_modules/google-gax/build/protos/google/protobuf/any.proto +52 -108
  228. package/server/node_modules/google-gax/build/protos/google/protobuf/compiler/ruby/ruby_generated_code.proto +2 -0
  229. package/server/node_modules/google-gax/build/protos/google/protobuf/cpp_features.proto +21 -1
  230. package/server/node_modules/google-gax/build/protos/google/protobuf/cpp_file_options.proto +24 -0
  231. package/server/node_modules/google-gax/build/protos/google/protobuf/cpp_options.proto +15 -0
  232. package/server/node_modules/google-gax/build/protos/google/protobuf/descriptor.proto +118 -30
  233. package/server/node_modules/google-gax/build/protos/google/protobuf/field_mask.proto +16 -18
  234. package/server/node_modules/google-gax/build/protos/google/protobuf/internal_options.proto +8 -1
  235. package/server/node_modules/google-gax/build/protos/google/protobuf/json/json_enumval_custom_string.proto +26 -0
  236. package/server/node_modules/google-gax/build/protos/google/protobuf/json_enumvalue_options.proto +29 -0
  237. package/server/node_modules/google-gax/build/protos/google/protobuf/json_options.proto +14 -0
  238. package/server/node_modules/google-gax/build/protos/google/protobuf/struct.proto +38 -22
  239. package/server/node_modules/google-gax/build/protos/google/protobuf/test_protos/forced_micro_string.proto +3 -0
  240. package/server/node_modules/google-gax/build/protos/google/protobuf/test_protos/repeated_field_proxy_import_message.proto +14 -0
  241. package/server/node_modules/google-gax/build/protos/google/protobuf/timestamp.proto +9 -8
  242. package/server/node_modules/google-gax/build/protos/google/protobuf/util/json_format_proto3.proto +18 -0
  243. package/server/node_modules/google-gax/build/protos/google/rpc/code.proto +1 -1
  244. package/server/node_modules/google-gax/build/protos/google/rpc/context/attribute_context.proto +7 -2
  245. package/server/node_modules/google-gax/build/protos/google/rpc/context/audit_context.proto +1 -1
  246. package/server/node_modules/google-gax/build/protos/google/rpc/error_details.proto +5 -5
  247. package/server/node_modules/google-gax/build/protos/google/rpc/http.proto +1 -1
  248. package/server/node_modules/google-gax/build/protos/google/rpc/status.proto +1 -2
  249. package/server/node_modules/google-gax/build/protos/google/type/calendar_period.proto +1 -1
  250. package/server/node_modules/google-gax/build/protos/google/type/color.proto +9 -10
  251. package/server/node_modules/google-gax/build/protos/google/type/date.proto +11 -9
  252. package/server/node_modules/google-gax/build/protos/google/type/datetime.proto +17 -17
  253. package/server/node_modules/google-gax/build/protos/google/type/dayofweek.proto +1 -1
  254. package/server/node_modules/google-gax/build/protos/google/type/decimal.proto +11 -13
  255. package/server/node_modules/google-gax/build/protos/google/type/expr.proto +1 -1
  256. package/server/node_modules/google-gax/build/protos/google/type/fraction.proto +1 -1
  257. package/server/node_modules/google-gax/build/protos/google/type/interval.proto +1 -2
  258. package/server/node_modules/google-gax/build/protos/google/type/latlng.proto +4 -5
  259. package/server/node_modules/google-gax/build/protos/google/type/localized_text.proto +3 -3
  260. package/server/node_modules/google-gax/build/protos/google/type/money.proto +1 -2
  261. package/server/node_modules/google-gax/build/protos/google/type/month.proto +1 -1
  262. package/server/node_modules/google-gax/build/protos/google/type/phone_number.proto +20 -15
  263. package/server/node_modules/google-gax/build/protos/google/type/postal_address.proto +39 -39
  264. package/server/node_modules/google-gax/build/protos/google/type/quaternion.proto +4 -4
  265. package/server/node_modules/google-gax/build/protos/google/type/timeofday.proto +11 -8
  266. package/server/node_modules/google-gax/package.json +5 -5
  267. package/server/node_modules/retry-request/package.json +4 -4
  268. package/server/node_modules/teeny-request/package.json +5 -5
  269. package/server/node_modules/undici/lib/cache/memory-cache-store.js +54 -9
  270. package/server/node_modules/undici/lib/cache/sqlite-cache-store.js +7 -1
  271. package/server/node_modules/undici/lib/core/request.js +12 -1
  272. package/server/node_modules/undici/lib/dispatcher/client-h1.js +11 -2
  273. package/server/node_modules/undici/lib/global.js +36 -16
  274. package/server/node_modules/undici/lib/handler/cache-handler.js +236 -116
  275. package/server/node_modules/undici/lib/handler/cache-revalidation-handler.js +3 -3
  276. package/server/node_modules/undici/lib/handler/retry-handler.js +24 -0
  277. package/server/node_modules/undici/lib/interceptor/cache.js +152 -62
  278. package/server/node_modules/undici/lib/interceptor/decompress.js +5 -2
  279. package/server/node_modules/undici/lib/util/cache.js +328 -66
  280. package/server/node_modules/undici/lib/util/date.js +23 -6
  281. package/server/node_modules/undici/lib/web/cookies/util.js +78 -8
  282. package/server/node_modules/undici/package.json +1 -1
  283. package/server/{orphan-proposed-reconciler-CTSGcK8p.js → orphan-proposed-reconciler-CWmOdM9-.js} +1 -1
  284. package/server/{orphan-proposed-reconciler-BvKkWMJQ.js → orphan-proposed-reconciler-Dg6U4ADZ.js} +24 -15
  285. package/server/package-lock.json +28 -28
  286. package/server/package.json +1 -1
  287. package/server/{pan-dir-B0c3w0Ya.js → pan-dir-ubraCiUg.js} +24 -10
  288. package/server/{paths-D-xWGHdh.js → paths-cXmOwSbh.js} +2 -2
  289. package/server/pending-lifecycle-claim-DEteYdS9.js +110 -0
  290. package/server/{pickup-DUCLWrJY.js → pickup-D8xtG1J2.js} +1 -1
  291. package/server/pipeline-membership-BGSvkT2M.js +3 -0
  292. package/server/pipeline-membership-CrAPLYtf.js +57 -0
  293. package/server/{pipeline-membership-mY3GprlK.js → pipeline-membership-gather-CET40frr.js} +177 -109
  294. package/server/pipeline-membership-gather-DTfcgxLl.js +3 -0
  295. package/server/{pipeline-notifier-BXkJzDC4.js → pipeline-notifier-BuQ0e4hF.js} +2 -2
  296. package/server/{pipeline-notifier-BJr8gQX6.js → pipeline-notifier-ImNXknf0.js} +1 -1
  297. package/server/post-merge-lifecycle-worker-DbO10pum.js +50 -0
  298. package/server/{project-pipelines-C0MWUeCU.js → project-pipelines-Bim6bThI.js} +2 -2
  299. package/server/{project-repos-B6GycDh9.js → project-repos-C0nuoBGv.js} +7 -3
  300. package/server/project-repos-CRE6Ykow.js +3 -0
  301. package/server/projects-B03Wr-xc.js +3 -0
  302. package/server/{prompts-DxzFXuLl.js → prompts-BEx08AIu.js} +2 -2
  303. package/server/{providers-u4Ga0jOr.js → providers-D7IluJe0.js} +1 -1
  304. package/server/{providers-qQAwGQDW.js → providers-evXhiQeg.js} +7 -7
  305. package/server/public/assets/{chunk-BO2N2NFS-BamJdinX.js → chunk-BO2N2NFS-D1s4-Sqp.js} +4 -4
  306. package/server/public/assets/{diagram-OG6HWLK6-DfOB8-w4.js → diagram-OG6HWLK6-1gdMoSF7.js} +1 -1
  307. package/server/public/assets/{dist-D7zYSEA6.js → dist-CxAOHbil.js} +1 -1
  308. package/server/public/assets/{dist-ByIuzMQW.js → dist-DqDRYW6P.js} +3 -3
  309. package/server/public/assets/{ganttDiagram-6RSMTGT7-EdmRyXL3.js → ganttDiagram-6RSMTGT7-BYex-Ec7.js} +1 -1
  310. package/server/public/assets/{highlighted-body-OFNGDK62-MerOfTX8.js → highlighted-body-OFNGDK62-DlVkiG0S.js} +1 -1
  311. package/server/public/assets/index-Cc7cnFC4.css +1 -0
  312. package/server/public/assets/index-K0hAYCgV.js +807 -0
  313. package/server/public/assets/{linear-BBbArIcK.js → linear-B4cuEFmO.js} +1 -1
  314. package/server/public/assets/mermaid-GHXKKRXX-DyDHFXHV.js +1 -0
  315. package/server/public/assets/{pieDiagram-4H26LBE5-BKIddiJN.js → pieDiagram-4H26LBE5-CBHNBIJQ.js} +1 -1
  316. package/server/public/assets/{prod-B8vYiu52.js → prod-CN2nHBvX.js} +4 -4
  317. package/server/public/assets/{quadrantDiagram-W4KKPZXB-D6RtoULk.js → quadrantDiagram-W4KKPZXB-Bom87VOQ.js} +1 -1
  318. package/server/public/assets/{sankeyDiagram-5OEKKPKP-CLrCedZX.js → sankeyDiagram-5OEKKPKP-UrBeyLxk.js} +1 -1
  319. package/server/public/assets/{xychartDiagram-2RQKCTM6-hznR8DYl.js → xychartDiagram-2RQKCTM6-DhkbrIxR.js} +1 -1
  320. package/server/public/index.html +2 -2
  321. package/server/{pull-requests-7N8WKseP.js → pull-requests-Cv7LxzUN.js} +1 -1
  322. package/server/{pull-requests-_lHueOHW.js → pull-requests-DHWiKxo7.js} +19 -10
  323. package/server/{queries-CjNIf-Ub.js → queries-DHJm7bTd.js} +631 -110
  324. package/server/{rally-CyWT_lNO.js → rally-B71M9f4o.js} +3 -3
  325. package/server/rally-YDjU513q.js +3 -0
  326. package/server/{rally-api-ClOSd-Tt.js → rally-api-D5uXN08-.js} +1 -1
  327. package/server/{rally-api-C8e_1JRi.js → rally-api-Dkqmok65.js} +2 -2
  328. package/server/{rally-client-lc-nokqr.js → rally-client-Bfqm6XI4.js} +4 -4
  329. package/server/{rally-client-sgtVqzNU.js → rally-client-CxsA6ExK.js} +1 -1
  330. package/server/{reap-terminal-sessions-7KeHv1R1.js → reap-terminal-sessions-BQ23nriz.js} +2 -2
  331. package/server/{reap-terminal-sessions-BtHPo6Xc.js → reap-terminal-sessions-CvmwaqKv.js} +2 -2
  332. package/server/{rebuild-stack-lwtsW0GO.js → rebuild-stack-0EEYReVZ.js} +16 -7
  333. package/server/{rebuild-stack-CFvcBHsS.js → rebuild-stack-qeRqHZxH.js} +2 -2
  334. package/server/{reconstruct-cache-C3Ui7ojV.js → reconstruct-cache-BbYFsqLa.js} +7 -6
  335. package/server/record-CtroO946.js +3 -0
  336. package/server/record-update-DH6dLJID.js +3 -0
  337. package/server/{record-update-dE1Aw83j.js → record-update-Z0QCxmMp.js} +124 -34
  338. package/server/{recovery-trip-BvRWfUXM.js → recovery-trip-eJe26h-f.js} +2 -2
  339. package/server/{release-engine-Cx48W7z8.js → release-engine-lPUp-MTd.js} +5 -5
  340. package/server/{release-set-CQfuN9-v.js → release-set-BwGeyZKX.js} +3 -3
  341. package/server/{remote-C_2hasl7.js → remote-BCd02Es1.js} +3 -3
  342. package/server/{remote-ChgimyYa.js → remote-CVGUhXMV.js} +3 -3
  343. package/server/{remote-agents-CA3Ihlxj.js → remote-agents-C1bssi41.js} +2 -2
  344. package/server/{remote-agents-By1ige5O.js → remote-agents-C_VxssLU.js} +300 -46
  345. package/server/{remote-completion-fVe9e_3u.js → remote-completion-DAB0P706.js} +12 -12
  346. package/server/resume-u9byntrq.js +3 -0
  347. package/server/{review-agent-DNboMyW4.js → review-agent-CD4RCI0Y.js} +1 -1
  348. package/server/{review-agent-CIGOQYBD.js → review-agent-CHhI66UP.js} +60 -47
  349. package/server/{review-artifacts-nRDPuMxd.js → review-artifacts-DSMRIfuJ.js} +7 -6
  350. package/server/{review-context-2uj-YQ8J.js → review-context-oFuBbiEE.js} +8 -8
  351. package/server/{in-flight-guard-CAAkqPLu.js → review-convoy-liveness-CpzrcJ5F.js} +3 -44
  352. package/server/review-status-BZH8h-6W.js +3 -0
  353. package/server/review-status-emit-CUExtHd5.js +50 -0
  354. package/server/{review-status-enrichment-Dci-ZwEt.js → review-status-enrichment-QV_hQkgS.js} +4 -4
  355. package/server/{review-status-source-CxuEjLGg.js → review-status-source-CpvJnvnQ.js} +24 -3
  356. package/server/review-status-source-ylw5QZUR.js +3 -0
  357. package/server/{review-status-sync-BTOpxtTC.js → review-status-sync-483sujRo.js} +2 -2
  358. package/server/{review-status-sync-DSDK3OMy.js → review-status-sync-NaWvI9Xf.js} +1 -1
  359. package/server/review-status-yljtTd-q.js +7521 -0
  360. package/server/{review-verdict-feedback-CTd4QMBu.js → review-verdict-feedback-BPDn4li_.js} +1 -1
  361. package/server/{review-verdict-feedback-CCjVA7Uk.js → review-verdict-feedback-CELeUinh.js} +48 -8
  362. package/server/{rollup-FGsKki9R.js → rollup-DTNvelz9.js} +8 -8
  363. package/server/{rollup-BKQC7jp4.js → rollup-DYZfq1c4.js} +1 -1
  364. package/server/{scanner-CnqEZeDy.js → scanner-CV35oSn4.js} +8 -7
  365. package/server/{sequence-io-D909zgeH.js → sequence-io-DF_8iuhV.js} +4 -4
  366. package/server/{sequencer-agent-CzkcK6P5.js → sequencer-agent-B2WeCtGi.js} +5 -5
  367. package/server/server.js +1952 -578
  368. package/server/{service-CLeAoU6G.js → service-Br9rpBNA.js} +81 -64
  369. package/server/{session-fork-CHI1XvL9.js → session-fork-D0LY152N.js} +2 -2
  370. package/server/{session-history-BT3IvqhC.js → session-history-BUyK29eq.js} +4 -4
  371. package/server/{settings-BTMvpNFU.js → settings-Bse1Pdcj.js} +3 -3
  372. package/server/settings-CdvIF84R.js +2 -0
  373. package/server/{settings-C0zgxOGf.js → settings-CvVGqNEs.js} +3 -2
  374. package/server/{shadow-engineering-BJfgm-hX.js → shadow-engineering-BWLa8ODz.js} +2 -2
  375. package/server/{shadow-state-7a-524T8.js → shadow-state-B4fpKxOO.js} +2 -2
  376. package/server/{shadow-state-DnDyjujH.js → shadow-state-FV6PHfEW.js} +1 -1
  377. package/server/{shadow-utils-Db3oroFG.js → shadow-utils-D7asqxOZ.js} +2 -2
  378. package/server/{ship-log-D9MlVzYU.js → ship-log-xplJ8t75.js} +1 -1
  379. package/server/{smart-compaction-CV2ukKND.js → smart-compaction-CPp2Ykf6.js} +1 -1
  380. package/server/{smart-compaction-KCYScAU1.js → smart-compaction-D3U_79f-.js} +8 -7
  381. package/server/{spawn-planning-session-qUC2VWu3.js → spawn-planning-session-1nFi3gfk.js} +1 -1
  382. package/server/{spawn-planning-session-DKpdFyOC.js → spawn-planning-session-DGfZ5B8D.js} +17 -17
  383. package/server/{specialist-completion-Ky9wCDpb.js → specialist-completion-Br48eiDP.js} +1 -1
  384. package/server/{specialist-context-Bn3dCGS2.js → specialist-context-cVbGnTa3.js} +5 -5
  385. package/server/{specialist-handoff-logger-CMEwYxa2.js → specialist-handoff-logger-DK-GTz4s.js} +2 -2
  386. package/server/{specialist-logs-C5p8G7tx.js → specialist-logs-Cl5-hqT8.js} +3 -3
  387. package/server/{specialists-CC4r-c2s.js → specialists-CNfYO3G2.js} +975 -227
  388. package/server/{specialists-jiuuyVkp.js → specialists-Cicieuzu.js} +18 -18
  389. package/server/{specialists-CShuVGWT.js → specialists-JHsdU3pA.js} +1 -1
  390. package/server/{specialists-DM6bWjsi.js → specialists-Tdezg_6N.js} +1 -1
  391. package/server/specs-Bt7WJDoC.js +3 -0
  392. package/server/src-CRmCHlma.js +11408 -0
  393. package/server/{src-CXmtj0im.js → src-Ck028hjO.js} +6 -6
  394. package/server/{src-CdmnTM2m.js → src-UWcGe0Ix.js} +3 -3
  395. package/server/{stack-health-Bn3o8FBX.js → stack-health-CJLMrLBl.js} +1 -1
  396. package/server/{stack-health-DxSdG9cW.js → stack-health-DqSqx0zv.js} +7 -5
  397. package/server/{stale-task-claims-KJKxxiiQ.js → stale-task-claims-CYFOUNlT.js} +4 -4
  398. package/server/{staleness-CT34nwr9.js → staleness-BQ-0gMYK.js} +1 -1
  399. package/server/{staleness-DPm7ClwA.js → staleness-D47CUJok.js} +1 -1
  400. package/server/{state-home-Cn_Qu8IX.js → state-home-BYV7qZTW.js} +2 -2
  401. package/server/{state-mapping-BL_-3Yt8.js → state-mapping-BVldLhBf.js} +1 -1
  402. package/server/{state-migrate-BGg2ZaOt.js → state-migrate-CvvBtS0Y.js} +4 -4
  403. package/server/{stuck-remediation-s9_2qfi6.js → stuck-remediation-Wt0UVvu1.js} +1 -1
  404. package/server/{stuck-remediation-DYuPRYYw.js → stuck-remediation-mkLC6G_t.js} +79 -22
  405. package/server/{substrate-bug-weights-service-B-FwCs5V.js → substrate-bug-weights-service-XfLAKnmK.js} +3 -3
  406. package/server/{summary-fork-DpcxN9tf.js → summary-fork-CD7GOts0.js} +2 -2
  407. package/server/{summary-fork-D1RAk9rJ.js → summary-fork-xiXBnWG9.js} +8 -8
  408. package/server/supervisor/{injection-budget-Csw5tqfJ.js → injection-budget-B5SbvDx2.js} +1 -1
  409. package/server/supervisor/{paths-DehGY2Fj.js → paths-K3noE4N_.js} +1 -1
  410. package/server/supervisor/pty-supervisor.js +49 -3
  411. package/server/supervisor/{pty-token-x-ea28Us.js → pty-token-B7lp0ElT.js} +2 -2
  412. package/server/{supervisor-wM4ooB5O.js → supervisor-DL788vos.js} +3 -3
  413. package/server/supervisor-UWiQNK-y.js +2 -0
  414. package/server/{swarm-readiness-DlP_UjHq.js → swarm-readiness-BV14DF5N.js} +3 -3
  415. package/server/{system-prerequisites-DUDj1YHf.js → system-prerequisites-pdWG6ZQs.js} +2 -2
  416. package/server/{teardown-workspace-DeTTImn-.js → teardown-workspace-l2F4_zim.js} +15 -16
  417. package/server/{test-agent-queue-MbxX5vqq.js → test-agent-queue-qakFIIGz.js} +6 -6
  418. package/server/{test-verdict-bWYBtqBg.js → test-verdict-BJYDggMm.js} +1 -1
  419. package/server/{test-verdict-gscuksPv.js → test-verdict-Gw-btEfb.js} +1 -1
  420. package/server/{tldr-daemon-BYFkts95.js → tldr-daemon-BKOYB616.js} +1 -1
  421. package/server/{tldr-daemon-C-69c3OI.js → tldr-daemon-CBaWkV9k.js} +2 -2
  422. package/server/{tmux-R7N7y-Mh.js → tmux-B1Wbxz5o.js} +3 -3
  423. package/server/{tmux-B6z7h_vZ.js → tmux-DQ6jB76i.js} +1 -1
  424. package/server/{launcher-generator-BBWG_SiS.js → tmux-dedup-DbN6MGzM.js} +428 -9
  425. package/server/{token-BK6t8BIT.js → token-Cxbk023d.js} +2 -2
  426. package/server/{token-util-DxXu-vd1.js → token-util-CLq2MmQt.js} +1 -1
  427. package/server/token-util-zlz9U0ki.js +3 -0
  428. package/server/{tracker-config-G6latr8C.js → tracker-config-DZ8Zn2nQ.js} +2 -2
  429. package/server/{tracker-utils-DHv5pSDT.js → tracker-utils-L0FKtjq2.js} +3 -3
  430. package/server/{traefik-connect-Df2uXPsQ.js → traefik-connect-bNX6jKkb.js} +1 -1
  431. package/server/{tunnel-CDVKJpRv.js → tunnel-BAm1XgjG.js} +3 -3
  432. package/server/{tunnel-DGdUkGoZ.js → tunnel-CjSnHdTc.js} +1 -1
  433. package/server/{types-5M0lupud.js → types-q67TYYg5.js} +1 -1
  434. package/server/uat-promote-verification-CKB9ZrKC.js +92 -0
  435. package/server/uat-train-Cu-4nvbf.js +2 -0
  436. package/server/uat-train-D5UjcyAa.js +0 -0
  437. package/server/{validate-IfDGkaVR.js → validate-Cxsx5Uz1.js} +8 -6
  438. package/server/{validation-CC9OPqJZ.js → validation-DEaTZafj.js} +1 -1
  439. package/server/{validation-Dw1pzDzr.js → validation-VZgJxmqc.js} +4 -4
  440. package/server/{verification-artifact-B3itKTxu.js → verification-artifact-C85cRTc_.js} +1 -1
  441. package/server/{verification-artifact-TVFSz_q1.js → verification-artifact-g3yuxQDL.js} +1 -1
  442. package/server/{verification-runner-BW4HOKAz.js → verification-runner-DlZI8yhw.js} +1 -1
  443. package/server/{verification-runner-CkgX6EtI.js → verification-runner-KDZIsBj4.js} +40 -18
  444. package/server/{vtt-parser-DYPbKcIJ.js → vtt-parser-Jr94BF3Y.js} +1 -1
  445. package/server/{work-agent-prompt-Du8Vh2aJ.js → work-agent-prompt-C-vuP3_J.js} +20 -9
  446. package/server/workflows-CHrx_hcW.js +2 -0
  447. package/server/{workflows-mbQzh6mQ.js → workflows-D0oip4eV.js} +161 -78
  448. package/server/{workspace--8tnhDI0.js → workspace-BqFg__wH.js} +1 -1
  449. package/server/{workspace-BPUws1-_.js → workspace-C0fH3aek.js} +3 -3
  450. package/server/workspace-anchor-drift-B4gMRHl4.js +3 -0
  451. package/server/workspace-anchor-drift-CZKofakh.js +224 -0
  452. package/server/{workspace-config-CIVWZOQr.js → workspace-config-B123aZl-.js} +1 -1
  453. package/server/workspace-manager-CDTKjjdU.js +3 -0
  454. package/server/{workspace-manager-CBhNdzE4.js → workspace-manager-CwO-Hek7.js} +156 -117
  455. package/server/{workspace-metadata-_WygnyoX.js → workspace-metadata-CQv00QwK.js} +2 -2
  456. package/server/{workspace-metadata-BzHnwwX4.js → workspace-metadata-D4M8BM4W.js} +1 -1
  457. package/server/{workspaces-Do_9J0LQ.js → workspaces-j1e_6aAT.js} +719 -292
  458. package/sync-sources/dev-skills/pan-dashboard-restart/SKILL.md +34 -10
  459. package/sync-sources/hooks/linear-mcp-auth-hook +97 -0
  460. package/sync-sources/rules/ask-user-question-self-contained.md +9 -0
  461. package/sync-sources/skills/pan-close/SKILL.md +5 -3
  462. package/sync-sources/skills/pan-peer-review/SKILL.md +195 -0
  463. package/sync-sources/skills/pan-reload/SKILL.md +3 -3
  464. package/sync-sources/skills/pan-restart/SKILL.md +5 -4
  465. package/sync-sources/skills/pan-review/SKILL.md +7 -1
  466. package/sync-sources/skills/pan-sync-main/SKILL.md +9 -8
  467. package/sync-sources/skills/pipeline-status/SKILL.md +10 -1
  468. package/sync-sources/skills/workspace-add-repo/SKILL.md +16 -1
  469. package/server/activity-logger-OEUBdfZ8.js +0 -3
  470. package/server/agent-state-Bc3tzUC8.js +0 -3
  471. package/server/agents-wi9NWzs2.js +0 -3
  472. package/server/archive-planning-BZDgzoEj.js +0 -3
  473. package/server/cli-B86rejVV.js +0 -3
  474. package/server/conversation-service-BLd3MYsG.js +0 -3
  475. package/server/deacon-kiQrb4ZT.js +0 -4
  476. package/server/deacon-swarm-C-GwfjJy.js +0 -2
  477. package/server/deploy-window-DkZraDyj.js +0 -3
  478. package/server/dist-Bzy0y69j.js +0 -3
  479. package/server/flywheel-merge-order-UU0tiUld.js +0 -245
  480. package/server/flywheel-merge-order-UUcsCDb6.js +0 -2
  481. package/server/forge-CNFnyqps.js +0 -270
  482. package/server/forge-DXnS_YFH.js +0 -2
  483. package/server/git-utils-D0_yhCgF.js +0 -3
  484. package/server/github-pr-lookup-D16NGsSO.js +0 -44
  485. package/server/hume-ccTf4wTX.js +0 -3
  486. package/server/inspect-agent-Brtz0URj.js +0 -2
  487. package/server/merge-ops-CR44oU9-.js +0 -2
  488. package/server/merge-set-bUqbm3yc.js +0 -3
  489. package/server/merge-sync-BJYeOODr.js +0 -3
  490. package/server/merge-verification-BkaOqPsw.js +0 -124
  491. package/server/project-repos-DXKQsc_m.js +0 -3
  492. package/server/projects-vjjcmiN8.js +0 -3
  493. package/server/public/assets/index-CWeMerGO.js +0 -804
  494. package/server/public/assets/index-CenuJ3Cw.css +0 -1
  495. package/server/public/assets/mermaid-GHXKKRXX-CpPWAFwE.js +0 -1
  496. package/server/rally-zhhtXuDP.js +0 -3
  497. package/server/record-CN0pj4_5.js +0 -3
  498. package/server/record-update-bLJxAXH4.js +0 -3
  499. package/server/resume-BFv1PGpA.js +0 -3
  500. package/server/review-status-btEYlaxA.js +0 -3
  501. package/server/review-status-kZj_KyCk.js +0 -1470
  502. package/server/settings-66A6uvIA.js +0 -2
  503. package/server/specs-C8Wsn2XO.js +0 -3
  504. package/server/src-DtcUOZCq.js +0 -4826
  505. package/server/supervisor-FTJAIsAK.js +0 -2
  506. package/server/token-util-DuPuNriz.js +0 -3
  507. package/server/tracker-utils-B4ylXZQK.js +0 -3
  508. package/server/uat-train-B05BWkp-.js +0 -2
  509. package/server/uat-train-DDSrSoV1.js +0 -2152
  510. package/server/workflows-BQu4RFQ-.js +0 -2
  511. package/server/workspace-manager-CWT55qeI.js +0 -3
package/server/server.js CHANGED
@@ -1,110 +1,115 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM, r as __exportAll, t as __commonJSMin } from "./rolldown-runtime-DNCzzKnI.js";
2
- import { A as canUseHarnessSync, Br as getOverdeckHome, Cr as LOGS_DIR, D as resolveTieredExecutionEnabled, E as resolveTieredExecutionBlock, Et as getIssueWorkspacePath, Fn as renameProject, Fr as getDocsDisableStatePath, Gr as sessionFilePath, H as init_providers, Hr as isDevMode, Ht as flushAutoCommits, I as UnknownModelError, Ir as getDocsIndexPath, J as defaultBackgroundAiFeatures, Jn as extractPrefixSync, Ln as resolveInfraRepo, Lr as getDocsTelemetryPath, M as init_harness_policy, Nr as encodeClaudeProjectDir, Nt as readIssueRecordSync, On as listProjects, Ot as init_record, Pn as registerProjectSync, Pr as getDocsBudgetStatePath, Q as computeModelOrigin, R as getBuiltInDefaultHarness, Rn as resolveProjectFromIssue, T as init_tier_table, Tn as init_projects, Un as setProjectAutoMergeDefaultSync, Ur as packageRoot, Ut as init_auto_commit, V as getProviderForModelSync, Vr as init_paths, Vt as flushAllPendingAutoCommits, Wn as setProjectSwarmPolicySync, X as DEFAULT_ROLES, Y as init_registry, Yn as init_issue_id, Z as DEFAULT_WORKHORSES, Zn as parseIssueIdSync, _ as getDefaultDocsConfig, at as getModelEffortLevelsSync, br as CLAUDE_DIR, cn as ProjectRenameError, ct as modelSupportsImagesSync, d as loadConfigNoMigration, er as require_dist$1, f as loadConfigSync, fn as findProjectByPathSync, fr as TrackerError, g as mergeConfigs, gn as getIssuePrefix, h as jsYaml, hr as AGENTS_DIR, ht as ROLE_EFFORTS, i as getConversationsConfig, ir as FsError, jn as loadProjectsConfigSync, jr as SYNC_SOURCES, k as validateTieredExecutionConfig, kn as listProjectsSync, lt as resolveModelIdSync, m as init_js_yaml, mn as findProjectByTeamSync, mr as init_errors, mt as PARENT_MODEL_REF, n as clearConfigCache, nn as init_state_read_home, nt as MODEL_CAPABILITIES, o as getGlobalConfigPath, or as GitHubApiError, ot as hasModelCapabilitySync, p as updateConversationsConfig, pt as COMPLIANCE_MODES, r as getConversationSearchConfigSync, rn as resolveStateReadHomeSync, rt as MODEL_DEPRECATIONS, s as init_load, st as init_model_capabilities, t as init_config_yaml, tt as resolveModel, u as isTldrEnabledSync, un as extractTeamPrefix, ur as ProcessSpawnError, v as getDefaultTtsDaemonConfig, w as TieredExecutionConfigError, wr as OVERDECK_HOME, y as mergeTtsDaemonConfigs, yn as getProjectSync, z as getKimiAnthropicBaseUrl, zn as resolveProjectFromIssueSync, zr as getLegacyHome } from "./config-yaml-DySPNmKN.js";
3
- import { a as shouldRefuseHostDashboardPort, i as readHostDashboardApiPort, n as getDashboardIdentity, r as isNonPrimaryCheckoutRoot } from "./deacon-nudge-log-6wVqAaqz.js";
4
- import { $ as panCliInvocation, A as resolveAllowedHarness, B as cleanupUnreferencedConversationAttachments, C as handleConversationCreate, Ct as jsonStringResponse, D as handleConversationStop, E as handleConversationResume, F as tmuxSessionExists, G as isManagedConversationAttachmentPath, H as extractConversationAttachmentPaths, I as waitForPiTuiReady, J as evaluateSpawnGuardrails, K as removeConversationAttachment, L as waitForTmuxSession$1, M as spawnConversationSession, N as startConversationModelBackfill, O as handleConversationSwitchModel, P as stopConversationRuntime, R as cleanupConversationAttachments, S as conversationSessionAliveFromState, St as jsonResponse, T as handleConversationRestartAll, U as getConversationAttachmentsRoot, V as ensureConversationAttachmentDir, W as hasConversationAttachment, X as resolveAgentGitInfo, Y as invalidateAgentsCache, Z as resolveConversationGitInfo, _ as isPiControlChannelHarness, _t as isTrustedOriginForHost, a as agentsRouteLayer, at as resolveCodexRolloutPath, b as validateConversationControlAckOrigin, bt as httpHandler, c as codexConversationPendingInput, ct as dashboardCsrfToken, d as handleConversationCodexApproval, dt as rejectUnauthorizedDashboardRequest, f as handleConversationCompact, ft as rejectUnauthorizedDashboardSessionMintRequest, g as handleConversationThinkingLevel, gt as getHeaderFromMap, h as handleConversationPlanAction, ht as waitForSessionRespawn, it as resolveClaudeSessionId, j as shouldReportUnresolvedLiveSession, k as isInsideGitWorkTree, l as deliverConversationViaControlChannel, lt as dashboardSessionCookieHeader, m as handleConversationDeliveryMethod, mt as isRespawnPending, nt as resolveAcpTranscriptPath, o as saveAgentStateAndEmitEvent, ot as resolveJsonlPath, p as handleConversationControlAck, pt as rejectUnsafeDashboardMutationRequest, q as runInBatches, rt as resolveAgentHarness, s as saveAgentStateAndEmitEventProgram, st as resolvePiSessionPath, t as specialistsRouteLayer, tt as readLauncherPinnedSessionId, u as handleConversationAbort, ut as hasDashboardInternalToken, v as pickDeliverAs, vt as validateOrigin$1, w as handleConversationDelete, x as conversationNeedsRunningRepair, xt as getDefaultCwd, y as resolveConversationDeliveryMethod, yt as validateOriginHeaders, z as cleanupOrphanedConversationAttachments } from "./specialists-CC4r-c2s.js";
5
- import { a as init_activity_logger, i as emitDashboardLifecycleSync, n as emitActivityEntrySync, r as emitActivityTtsSync } from "./activity-logger-DDlOIAML.js";
6
- import { A as init_paths$1, O as OVERDECK_MIGRATION_PATH, f as init_infra, i as DbLive, k as getOverdeckDatabasePath, u as TmuxLive } from "./infra-BEqFxlJc.js";
2
+ import { A as canUseHarnessSync, An as extractTeamPrefix, Cn as resolveStateReadHomeSync, D as resolveTieredExecutionEnabled, Dr as GitHubApiError, E as resolveTieredExecutionBlock, Fr as init_errors, Gn as init_projects, Gr as OVERDECK_HOME, H as init_providers, I as UnknownModelError, In as getIssuePrefix, Ir as AGENTS_DIR, J as defaultBackgroundAiFeatures, Jn as listProjects, M as init_harness_policy, Mn as findProjectByPathSync, Mt as getIssueWorkspacePath, Nr as TrackerError, On as ProjectRenameError, Pn as findProjectByTeamSync, Pt as init_record, Q as computeModelOrigin, Qr as SYNC_SOURCES, R as getBuiltInDefaultHarness, Sn as init_state_read_home, T as init_tier_table, Tr as FsError, V as getProviderForModelSync, Vr as CLAUDE_DIR, Wr as LOGS_DIR, X as DEFAULT_ROLES, Y as init_registry, Yn as listProjectsSync, Z as DEFAULT_WORKHORSES, Zn as loadProjectsConfigSync, _ as getDefaultDocsConfig, ar as resolveProjectFromIssueSync, at as getModelEffortLevelsSync, ci as init_paths, cn as flushAutoCommits, ct as modelSupportsImagesSync, d as loadConfigNoMigration, dr as setProjectSwarmPolicySync, ei as encodeClaudeProjectDir, er as registerProjectSync, f as loadConfigSync, fi as sessionFilePath, g as mergeConfigs, gr as init_issue_id, gt as ROLE_EFFORTS, h as jsYaml, hr as extractPrefixSync, ht as PARENT_MODEL_REF, i as getConversationsConfig, ii as getDocsTelemetryPath, ir as resolveProjectFromIssue, jr as ProcessSpawnError, k as validateTieredExecutionConfig, li as isDevMode, ln as init_auto_commit, lr as setProjectAutoMergeDefaultSync, lt as resolveModelIdSync, m as init_js_yaml, mt as COMPLIANCE_MODES, n as clearConfigCache, ni as getDocsDisableStatePath, nt as MODEL_CAPABILITIES, o as getGlobalConfigPath, oi as getLegacyHome, ot as hasModelCapabilitySync, p as updateConversationsConfig, r as getConversationSearchConfigSync, ri as getDocsIndexPath, rr as resolveInfraRepo, rt as MODEL_DEPRECATIONS, s as init_load, si as getOverdeckHome, sn as flushAllPendingAutoCommits, st as init_model_capabilities, t as init_config_yaml, ti as getDocsBudgetStatePath, tr as renameProject, tt as resolveModel, u as isTldrEnabledSync, ui as packageRoot, ur as setProjectMergeTrainSync, v as getDefaultTtsDaemonConfig, vr as parseIssueIdSync, w as TieredExecutionConfigError, xr as require_dist$1, y as mergeTtsDaemonConfigs, z as getKimiAnthropicBaseUrl, zn as getProjectSync, zt as readIssueRecordSync } from "./config-yaml-CmOPzmBb.js";
3
+ import { a as shouldRefuseHostDashboardPort, i as readHostDashboardApiPort, n as getDashboardIdentity, r as isNonPrimaryCheckoutRoot } from "./deacon-nudge-log-BNBebzeL.js";
4
+ import { $ as composerCommandConfirmationFromBody, A as resolveAllowedHarness, B as cleanupUnreferencedConversationAttachments, C as handleConversationCreate, Ct as isTrustedOriginForHost, D as handleConversationStop, Dt as getDefaultCwd, E as handleConversationResume, Et as httpHandler, F as tmuxSessionExists, G as isManagedConversationAttachmentPath, H as extractConversationAttachmentPaths, I as waitForPiTuiReady, J as composerCommandResultHttpStatus, K as removeConversationAttachment, L as waitForTmuxSession$1, M as spawnConversationSession, N as startConversationModelBackfill, O as handleConversationSwitchModel, Ot as jsonResponse, P as stopConversationRuntime, Q as ComposerCommandConfirmationError, R as cleanupConversationAttachments, S as conversationSessionAliveFromState, St as getHeaderFromMap, T as handleConversationRestartAll, Tt as validateOriginHeaders, U as getConversationAttachmentsRoot, V as ensureConversationAttachmentDir, W as hasConversationAttachment, X as resolvePolicy, Y as handleComposerCommandMessage, Z as ComposerCommandParseError, _ as isPiControlChannelHarness, _t as rejectUnauthorizedDashboardRequest, a as agentsRouteLayer, at as panCliInvocation, b as validateConversationControlAckOrigin, bt as isRespawnPending, c as codexConversationPendingInput, ct as resolveAcpTranscriptPath, d as handleConversationCodexApproval, dt as resolveCodexRolloutPath, et as evaluateSpawnGuardrails, f as handleConversationCompact, ft as resolveJsonlPath, g as handleConversationThinkingLevel, gt as hasDashboardInternalToken, h as handleConversationPlanAction, ht as dashboardSessionCookieHeader, j as shouldReportUnresolvedLiveSession, k as isInsideGitWorkTree, kt as jsonStringResponse, l as deliverConversationViaControlChannel, lt as resolveAgentHarness, m as handleConversationDeliveryMethod, mt as dashboardCsrfToken, nt as resolveAgentGitInfo, o as saveAgentStateAndEmitEvent, p as handleConversationControlAck, pt as resolvePiSessionPath, q as runInBatches, rt as resolveConversationGitInfo, s as saveAgentStateAndEmitEventProgram, st as readLauncherPinnedSessionId, t as specialistsRouteLayer, tt as invalidateAgentsCache, u as handleConversationAbort, ut as resolveClaudeSessionId, v as pickDeliverAs, vt as rejectUnauthorizedDashboardSessionMintRequest, w as handleConversationDelete, wt as validateOrigin$1, x as conversationNeedsRunningRepair, xt as waitForSessionRespawn, y as resolveConversationDeliveryMethod, yt as rejectUnsafeDashboardMutationRequest, z as cleanupOrphanedConversationAttachments } from "./specialists-CNfYO3G2.js";
5
+ import { a as emitDashboardLifecycleSync, i as emitActivityTtsSync, o as init_activity_logger, r as emitActivityEntrySync } from "./activity-logger-CyteR4JO.js";
6
+ import { A as init_paths$1, O as OVERDECK_MIGRATION_PATH, f as init_infra, i as DbLive, k as getOverdeckDatabasePath, u as TmuxLive } from "./infra-BjuxvjdS.js";
7
7
  import { n as openDatabase, t as init_driver } from "./driver-CKVGi51s.js";
8
- import { n as updateIssueRecord, t as init_record_update } from "./record-update-dE1Aw83j.js";
9
- import { $ as init_child_env, B as sendKeysAsync, C as getWindowDimensions, D as killSession, H as sessionExists, J as init_ui_theme, L as resizeWindow, M as listSessionNames, Q as buildChildEnvWithoutTmuxSync, T as isHarnessProcessAlive, Y as setUiTheme, Z as buildChildEnvSync, d as ensureManagedTmuxContextOnce, i as capturePane, l as deliveryVerifyLine, n as buildTmuxArgs, o as capturePaneText, s as createSession, w as init_tmux, z as sendKeys } from "./tmux-R7N7y-Mh.js";
10
- import { a as init_event_store, i as initEventStore, n as getEventStore } from "./event-store-CQL6NeqB.js";
11
- import { A as AgentSnapshot, C as EDITORS, D as MemoryObservation, S as FlywheelStatus, T as SystemHealthSnapshot, b as WS_METHODS, c as ContextLayerSaveRequest, f as FlywheelStats, j as ReviewStatusSnapshot, k as AgentRuntimeSnapshot, l as ContextPreviewRequest, n as compareIssueIds, o as getHarnessBehavior, t as init_src, v as PanRpcError, w as DomainEvent, x as FlywheelRunId, y as PanRpcGroup } from "./src-DtcUOZCq.js";
12
- import { a as fixStuckCommentedReviews, b as setReviewStatusSync, c as getReviewStatusSync, d as loadReviewStatuses, l as init_review_status, n as clearReviewStatus, o as fixStuckReadyForMerge, p as markWorkspaceStuck, r as clearStuckMergeStatuses, s as getReviewStatus, u as loadReadyForMergeFlags, y as setReviewStatus } from "./review-status-kZj_KyCk.js";
13
- import { a as EventStoreService, c as ReadModelService, d as init_read_model, f as toAgentResolution, i as init_issue_lifecycle, l as ReadModelServiceLive, m as toRole, n as IssueLifecycleLive, o as EventStoreServiceLive, p as toAgentStatus, s as init_domain_services, t as IssueLifecycle } from "./issue-lifecycle-ojZAzDBP.js";
14
- import { c as listAllAgentsSync, n as AgentsResolverLive, o as init_agents } from "./agents-EbI0zCJl.js";
15
- import { i as init_paths$2, l as resolveObservationsFile, r as ensureParentDir, s as resolveIssueMemoryRoot, t as assertMemorySafeSegment } from "./paths-D-xWGHdh.js";
16
- import { i as recordBriefingSessionStart, n as ensureSessionContextBriefingFile, r as init_briefing_freshness, t as appendFreshBriefingUpdate } from "./briefing-freshness-D1wzUIWv.js";
17
- import { n as listFeatureRegistryEntries, t as init_feature_registry_storage } from "./feature-registry-storage-BdPuLcBX.js";
18
- import { a as diffAgainstMain, c as diffCheckpoints, d as diffPatchSinceCommit, f as findCommitAtTime, h as listCheckpoints, l as diffFilesAgainstHead, m as init_checkpoint_manager, o as diffAgainstMainFiles, s as diffCheckpointFiles, t as captureCheckpoint, u as diffPatchFilesAgainstHead } from "./checkpoint-manager-BI1Wxoku.js";
19
- import { Cn as getProviderEnvForModel, Cr as normalizeAgentId, D as init_tier_supervisor, Dr as getAgentRuntimeState, E as deliverCommitForReview, Er as waitForReadySignal, Fr as init_codex_auth, Ft as init_transcript_landing, Gn as getAgentDir, It as probeTranscriptSince, On as getOpenAIAuthStatus, Or as getAgentRuntimeStateSync, Pr as checkCodexAuthStatus, Pt as captureTranscriptUserRecordSnapshot, Q as operatorInterventionEvent, X as messageAgent, Yn as getAgentStateSync, Yt as resolveImplicitStaffing, Z as init_operator_interventions, _t as emergencyBrake, a as listRunningAgentsSync, an as getProviderAuthMode, b as init_agents$1, c as getRuntimeCensus, cn as injectPiConversationMemory, d as panePidsForSession, f as refreshRuntimeCensus, gr as readAgentHarnessModelRecordSync, hr as init_agent_record_sync, i as listRunningAgents, j as supervisorAgentId, kn as init_openai_auth, kt as deliverAgentMessage, l as getRuntimeCensusSnapshot, lr as saveAgentStateSync, n as init_queries, p as runtimeCensusHasHarnessProcess, qn as getAgentState, qt as init_staffing, r as listAgentStates, u as init_runtime_census, vn as getClaudeAuthStatus, yn as init_claude_auth, yt as init_concurrency } from "./queries-CjNIf-Ub.js";
20
- import { a as getCostForIssueAggregateSync, c as getDailyTrendsSync, d as init_cost_sync, l as getModelRollupSync, n as getBackgroundCostBySourceSync, p as queryCostEventsSync, r as getCavemanExperimentDataSync, s as getCostsByIssueSync, u as getTodayCostSync } from "./cost-sync-BsN6ER0n.js";
21
- import { c as getAgentsNeedingAttention, d as CostResolverLive, f as CostWriter, l as CostDoorLive, n as readCloisterStateFile, o as generateHealthSummary, s as getAgentHealth, t as getCloisterService, u as CostResolver } from "./service-CLeAoU6G.js";
22
- import { u as init_agent_runtime_mirror } from "./session-history-BT3IvqhC.js";
23
- import { t as init_behavior } from "./behavior-B3exJ42O.js";
24
- import { n as AgentStateServiceLive } from "./agent-state-service-BsgWxZuB.js";
25
- import { a as init_tracker_config, i as initTrackerConfigCache, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-G6latr8C.js";
8
+ import { i as updateIssueRecord, n as clearRecordPipelineClosedOutSync, r as init_record_update } from "./record-update-Z0QCxmMp.js";
9
+ import { $ as buildChildEnvSync, B as sendKeysAsync, C as getWindowDimensions, D as killSession, E as isPaneDead, H as sessionExists, L as resizeWindow, M as listSessionNames, T as isHarnessProcessAlive, V as sendRawKeystroke, X as init_ui_theme, Z as setUiTheme, d as ensureManagedTmuxContextOnce, et as buildChildEnvWithoutTmuxSync, i as capturePane, l as deliveryVerifyLine, n as buildTmuxArgs, o as capturePaneText, s as createSession, tt as init_child_env, w as init_tmux, z as sendKeys } from "./tmux-B1Wbxz5o.js";
10
+ import { a as init_event_store, i as initEventStore, n as getEventStore } from "./event-store-zY5MJHgF.js";
11
+ import { A as AgentRuntimeSnapshot, C as FlywheelStatus, E as SystemHealthSnapshot, M as ReviewStatusSnapshot, O as MemoryObservation, S as FlywheelRunId, T as DomainEvent, b as PanRpcGroup, j as AgentSnapshot, l as ContextLayerSaveRequest, n as COMPOSER_COMMAND_MANIFEST, p as FlywheelStats, r as compareIssueIds, s as getHarnessBehavior, t as init_src, u as ContextPreviewRequest, w as EDITORS, x as WS_METHODS, y as PanRpcError } from "./src-CRmCHlma.js";
12
+ import { A as getOrCreateInstallId, D as getAnalyticsService, M as init_config, N as resolveTelemetryEnabled, O as init_service, P as telemetryEnvironmentForcesOff, S as setReviewStatusSync, a as clearStuckMergeStatuses, c as fixStuckReadyForMerge, f as init_review_status, g as markWorkspaceStuck, i as clearReviewStatus, j as init_install_id, k as synchronizeAnalyticsServices, l as getReviewStatus, m as loadReviewStatuses, p as loadReadyForMergeFlags, s as fixStuckCommentedReviews, u as getReviewStatusSync, x as setReviewStatus } from "./review-status-yljtTd-q.js";
13
+ import { a as EventStoreService, c as ReadModelService, f as init_read_model, h as toRole, i as init_issue_lifecycle, l as ReadModelServiceLive, m as toAgentStatus, n as IssueLifecycleLive, o as EventStoreServiceLive, p as toAgentResolution, s as init_domain_services, t as IssueLifecycle, u as activityEntriesFromStoredEvents } from "./issue-lifecycle-C4Zj0WmE.js";
14
+ import { c as listAllAgentsSync, n as AgentsResolverLive, o as init_agents } from "./agents-C5rWYhb6.js";
15
+ import { i as init_paths$2, l as resolveObservationsFile, r as ensureParentDir, s as resolveIssueMemoryRoot, t as assertMemorySafeSegment } from "./paths-cXmOwSbh.js";
16
+ import { i as recordBriefingSessionStart, n as ensureSessionContextBriefingFile, r as init_briefing_freshness, t as appendFreshBriefingUpdate } from "./briefing-freshness-DHmCSLar.js";
17
+ import { n as listFeatureRegistryEntries, t as init_feature_registry_storage } from "./feature-registry-storage-BORBTo0y.js";
18
+ import { a as diffAgainstMain, c as diffCheckpoints, d as diffPatchSinceCommit, f as findCommitAtTime, h as listCheckpoints, l as diffFilesAgainstHead, m as init_checkpoint_manager, o as diffAgainstMainFiles, s as diffCheckpointFiles, t as captureCheckpoint, u as diffPatchFilesAgainstHead } from "./checkpoint-manager-DEWD82qK.js";
19
+ import { $ as operatorInterventionEvent, $n as getAgentDir, Bt as init_transcript_landing, Cr as init_agent_record_sync, D as init_tier_supervisor, E as deliverCommitForReview, Fn as init_openai_auth, Fr as getAgentRuntimeState, Hr as checkCodexAuthStatus, Ir as getAgentRuntimeStateSync, Mt as deliverAgentMessage, Pn as getOpenAIAuthStatus, Pr as waitForReadySignal, Q as init_operator_interventions, Qt as init_staffing, Tn as init_claude_auth, Ur as init_codex_auth, Vt as probeTranscriptSince, X as messageAgent, Y as init_messaging, _r as saveAgentStateSync, a as listRunningAgentsSync, b as init_agents$1, bt as init_concurrency, c as getRuntimeCensus, d as panePidsForSession, dn as getProviderAuthMode, en as resolveImplicitStaffing, f as refreshRuntimeCensus, i as listRunningAgents, j as supervisorAgentId, jr as normalizeAgentId, kn as getProviderEnvForModel, l as getRuntimeCensusSnapshot, mn as injectPiConversationMemory, n as init_queries, p as runtimeCensusHasHarnessProcess, r as listAgentStates, rr as getAgentStateSync, tr as getAgentState, u as init_runtime_census, vt as emergencyBrake, wn as getClaudeAuthStatus, wr as readAgentHarnessModelRecordSync, zt as captureTranscriptUserRecordSnapshot } from "./queries-DHJm7bTd.js";
20
+ import { a as getCostForIssueAggregateSync, c as getDailyTrendsSync, d as init_cost_sync, l as getModelRollupSync, n as getBackgroundCostBySourceSync, p as queryCostEventsSync, r as getCavemanExperimentDataSync, s as getCostsByIssueSync, u as getTodayCostSync } from "./cost-sync-Dv3LUaNW.js";
21
+ import { c as getAgentsNeedingAttention, d as CostResolverLive, f as CostWriter, l as CostDoorLive, n as readCloisterStateFile, o as generateHealthSummary, s as getAgentHealth, t as getCloisterService, u as CostResolver } from "./service-Br9rpBNA.js";
22
+ import { u as init_agent_runtime_mirror } from "./session-history-BUyK29eq.js";
23
+ import { t as init_behavior } from "./behavior-CfIg6pKp.js";
24
+ import { n as AgentStateServiceLive } from "./agent-state-service-BlnSkRFA.js";
25
+ import { a as init_tracker_config, i as initTrackerConfigCache, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-DZ8Zn2nQ.js";
26
26
  import { d as init_typed_errors, i as PlanEmpty, l as WorkspaceCreateError, n as AgentStartError, s as TrackerApiError, t as AgentAlreadyRunning, u as WorkspaceNotFound } from "./typed-errors-2BTMxkZq.js";
27
- import { i as init_linear_client, r as LinearClientOptionalLive, t as LinearClient } from "./linear-client-BsuUvkdA.js";
28
- import { i as init_github_client, r as GitHubClientOptionalLive, t as GitHubClient } from "./github-client-BnIsSrEN.js";
29
- import { i as init_rally_client, r as RallyClientOptionalLive, t as RallyClient } from "./rally-client-lc-nokqr.js";
30
- import { a as resolveTrackerTypeSync, i as resolveGitHubIssueSync, n as init_tracker_utils } from "./tracker-utils-DHv5pSDT.js";
31
- import { a as CacheService, i as startSharedIssueService, n as init_issue_service_singleton, o as init_cache_service, r as issue_service_singleton_exports, s as parseIntegerHeader, t as getSharedIssueService } from "./issue-service-singleton-Yx5DdmVJ.js";
32
- import { i as ensureInternalTokenSync, o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-s0-oKybJ.js";
33
- import { a as setPipelineHandlerSync, t as init_pipeline_notifier } from "./pipeline-notifier-BXkJzDC4.js";
34
- import { c as init_review_status_sync, i as getMergeBlockerReconcileCandidates } from "./review-status-sync-BTOpxtTC.js";
35
- import { n as init_review_status_source, t as classifyAdvancingSessionLifecycle } from "./review-status-source-CxuEjLGg.js";
27
+ import { i as init_linear_client, r as LinearClientOptionalLive, t as LinearClient } from "./linear-client-BQU36QqE.js";
28
+ import { i as init_github_client, r as GitHubClientOptionalLive, t as GitHubClient } from "./github-client-BfyDxuRN.js";
29
+ import { i as init_rally_client, r as RallyClientOptionalLive, t as RallyClient } from "./rally-client-Bfqm6XI4.js";
30
+ import { n as resolveGitHubIssueSync, r as resolveTrackerTypeSync, t as init_tracker_utils } from "./tracker-utils-L0FKtjq2.js";
31
+ import { a as CacheService, i as startSharedIssueService, n as init_issue_service_singleton, o as init_cache_service, r as issue_service_singleton_exports, s as parseIntegerHeader, t as getSharedIssueService } from "./issue-service-singleton-DmCh7EHj.js";
32
+ import { i as ensureInternalTokenSync, o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-DvMTQSOS.js";
33
+ import { a as setPipelineHandlerSync, t as init_pipeline_notifier } from "./pipeline-notifier-BuQ0e4hF.js";
34
+ import { c as init_review_status_sync, i as getMergeBlockerReconcileCandidates } from "./review-status-sync-483sujRo.js";
35
+ import { n as init_review_status_source, t as classifyAdvancingSessionLifecycle } from "./review-status-source-CpvJnvnQ.js";
36
36
  import { a as PAN_DIRNAME, d as WORKSPACE_RUNTIME_DIRNAME, f as asPanSpecDocument, i as PAN_CONTINUE_FILENAME, p as init_types } from "./types-hraoaQIp.js";
37
- import { C as transitionToMerging, _ as markFailed, c as isFlywheelGloballyPaused, d as listDuePendingAutoMerges, f as listProblemAutoMerges, g as markBlocked, n as getActionableAutoMerge, o as init_merge_sync, t as cancelPending, u as listActiveAutoMerges, v as markMerged, x as scheduleAutoMergeWithResult, y as requeueToPending } from "./merge-sync-VPV28g0g.js";
38
- import { C as init_specs, D as writeSpecDocument, O as writeSpecForIssue, a as findWorkspaceDraftPlan, m as readWorkspacePlanSync, o as init_io, p as readWorkspacePlan, r as findPlan, s as isPlanningComplete, u as readPlan, v as init_types$1, x as findSpecByIssue, y as subItemsOf } from "./io-C9kF1wtJ.js";
39
- import { C as restartCliproxy, S as readPidFile, T as stopCliproxy, b as isCliproxyInstalled, d as getCliproxyAuthDir, o as bridgeCodexAuthToCliproxy, v as init_cliproxy, w as startCliproxy, x as isCliproxyRunning, y as installCliproxy } from "./cliproxy-C8a1vtTU.js";
40
- import { $ as updateConversationForkFallbackReason, A as isAgentConversationName, B as removeFavorite, C as getConversationByTmuxSession, D as importLegacyConversation, F as markConversationEnded, H as setClearedToConvId, I as markConversationRunning, J as setForkRequest, M as listConversations, N as listFavoritedIds, O as incrementForkRetryCount, P as markConversationActive, S as getConversationByName, T as getStuckForks, U as setConversationClaudeSessionId, X as unarchiveConversation, Y as setImportedConversationLinks, Z as updateConversationCost, _ as canRefineTitle, b as getConversationByClaudeSessionId, et as updateConversationTitle, gt as getDiscoveredSessionBySessionId, h as archiveConversation, j as listArchivedConversationsWithEnrichment, k as init_conversations, q as setFavorite, tt as updateForkStatus, v as canReplaceTitle, w as getConversationLedgerCosts, x as getConversationById, y as createConversation, yt as init_discovered_sessions, z as recordConversationHandoff } from "./conversations-6AfUfbwc.js";
41
- import { n as isBackgroundFeatureEnabled, t as init_features } from "./features-ez2271oH.js";
42
- import { i as init_settings, r as getMemoryWorkerConcurrency, t as areMemoryObservationsEnabled } from "./settings-BTMvpNFU.js";
43
- import { i as updateMemoryHealth, n as extractWithProviderPolicy, r as init_health, t as init_providers$1 } from "./providers-qQAwGQDW.js";
44
- 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";
45
- import { a as init_claude_permissions, r as getClaudePermissionFlagsStringSync } from "./claude-permissions-DNWEVV6z.js";
46
- import { C as ensureProjectLayer, E as init_layers, I as workspaceContextFile, N as resolveProjectContextFile, P as resolveWorkspaceContextFile, S as codexGlobalContextFile, T as globalContextFile$1, b as validateTemplate, c as init_render, d as userContentOutsideRegion, i as workspaceContextWithoutProjectLayer, r as init_assemble, s as hasManagedRegion, v as init_harness, y as renderForHarness } from "./assemble-J2ZZ2C15.js";
47
- import { l as init_codex, s as findRolloutPath } from "./codex-BZXGhUVB.js";
48
- import { i as prepareHarnessLaunch, r as init_harness_binary } from "./harness-binary-DPUi-Pv8.js";
49
- import { f as init_config, m as loadConfigSync$1, s as getDashboardApiUrlSync } from "./config-BZ0sHTju.js";
50
- import { C as setMergeTrainEnabled, S as setFlywheelRequireUatBeforeMerge, b as setFlywheelAutoPickupBacklog, c as isCloisterSpawnsPausedSync, d as isFlywheelAutoPickupBacklog, f as isFlywheelGloballyPaused$1, g as setCloisterSpawnsPausedSync, h as setBootReconciliationDecision, m as isMergeTrainEnabled, o as getSetting, p as isFlywheelRequireUatBeforeMerge, r as getBootReconciliationState, s as init_control_settings, u as isDeaconGloballyPausedSync, v as setDeaconGloballyPausedSync, w as setSetting } from "./control-settings-BQp7_4uD.js";
51
- import { l as init_dag, r as compileGlob } from "./dag-B1ucnjDU.js";
52
- import { a as loadCloisterConfigSync, o as saveCloisterConfigSync, r as init_config$1, s as shouldAutoStart } from "./config-CKxYiCmX.js";
53
- import { c as isStateMigrated, i as ensureStateWorktree, o as init_state_home, r as clearStateMigrationCache, s as inspectStateMigration } from "./state-home-Cn_Qu8IX.js";
54
- import { i as checkPrdGateSync, s as promoteWorkspacePrdDraft, t as init_pan_dir } from "./pan-dir-B0c3w0Ya.js";
55
- import { a as init_lifecycle_io, t as appendContinueSessionEntryForIssue } from "./lifecycle-io-BREGpnLk.js";
56
- import { r as init_swarm_readiness, t as analyzeSwarmReadiness } from "./swarm-readiness-DlP_UjHq.js";
57
- import { a as buildMatchQuery, c as init_query_expansion, i as resolveKnowledgeBundleRoot, n as init_injection, o as init_search, r as injectPromptTimeMemory, s as clearQueryExpansionCache } from "./injection-C8aiVJrF.js";
58
- import { i as runMemoryFtsTransaction, n as init_fts_db, r as runMemoryFtsStatement, t as closeMemoryFtsDatabases } from "./fts-db-CE46vDkZ.js";
59
- import { n as require_auth_errors, r as require_token_error, t as require_token_util } from "./token-util-DxXu-vd1.js";
60
- import { A as createSettledTtlPromiseCache, D as init_advisory_checks, E as verifyAppCanMerge, M as withConcurrencyLimit, O as isAdvisoryCheckName, d as getPullRequestState, f as init_github_app, j as init_concurrency$1, k as createPromiseConcurrencyLimiter, l as getMergeBackendStatus, p as isGitHubAppConfigured } from "./github-app-C3QrAUd0.js";
61
- import { C as getWorkspacePathForIssue, E as stopAgentOutputService, S as resolveIssueHeadlineCost, T as startAgentOutputService, _ as getCostsForIssueSync, b as processPendingFeedbackDeliveries, d as resumeQueuedMerges, f as syncWalFromAllProjects, g as migrateAllSessionsSync, h as reclassifyUnknownCostEventsSync, l as internalEventsRouteLayer, m as reconcilePiTranscripts, p as reconcile$1, t as workspacesRouteLayer, u as eventsRouteLayer, v as rebuildCacheSync, w as retainAgentOutputInterest, x as getCachedRunningAgents, y as syncCacheSync } from "./workspaces-Do_9J0LQ.js";
62
- import { _ as init_codex_conversation_parser, a as contextUsageFromParseResult, b as parseAcpConversationMessages, c as parseEntireConversation, d as init_pi_conversation_parser, f as isPiSessionFile, g as parseOhmypiConversationMessages, h as isOhmypiSessionFile, i as computeContextUsage, l as parseFromLastCompactBoundary, m as init_ohmypi_conversation_parser, n as gateSnapshotEmission, o as summarizeConversationActivity, p as parsePiConversationMessages, r as watchConversation, s as parseConversationMessages, t as init_conversation_service, v as parseCodexConversationMessages, y as init_acp_conversation_parser } from "./conversation-service-DPJ71F33.js";
63
- import { c as countPendingAskUserQuestionsForAgent, h as detectAwaitingInputFromPaneSync, l as getAgentJsonlMtime, m as detectAwaitingInputForAgent, o as getRuntimeForAgent, p as scanPendingInputsPromise, s as computeAgentEnrichment } from "./stuck-remediation-DYuPRYYw.js";
64
- import { i as CostThresholdError, l as embed, s as parseRelativeTime } from "./scanner-CnqEZeDy.js";
65
- import { c as tailEventsSync, n as deduplicateEventsSync, o as readEventsSync } from "./events-DTfAe4My.js";
66
- import { n as recordBackgroundAiCost, t as init_cost } from "./cost-R-m6Yi-W.js";
67
- import { a as getByIssueId, i as parseFlywheelStatsWindow, n as derivePipelineRunStatsInputs, o as markFixed, r as computeFlywheelStats, s as upsert } from "./substrate-bug-weights-service-B-FwCs5V.js";
68
- import { c as startUatTrainReconciler, d as openFlywheelRunReportForDashboard, f as pauseFlywheelRunForDashboard, g as runDashboardDbJob, h as startFlywheelRunForDashboard, l as stopUatTrainReconciler, m as resumeFlywheelRunForDashboard, p as readCurrentFlywheelStatusForDashboard, u as abortFlywheelRunForDashboard } from "./uat-train-DDSrSoV1.js";
69
- import { n as parseSequenceMd, r as writeSequenceMd, t as init_sequence_io } from "./sequence-io-D909zgeH.js";
70
- import { _ as selectNeedsPlanning, c as classifyIssue, d as computeLanes, f as computeStats, g as normalizeGate, l as computeCohort, m as init_pickup, p as computeWaves, u as computeEpicGroups } from "./pickup-DUCLWrJY.js";
71
- import { B as getBook, C as readCurrentLatestFlywheelStatus, E as subscribeLatestFlywheelStatus, F as setItemRequirements, H as liveOrderIssueLookup, I as setSettings, L as setStatus, M as moveItem, N as removeItem, O as writeLatestFlywheelStatus, P as renameBook, R as backlogCandidates, T as resolveLiveFlywheelRunId, V as listBooks, b as isFlywheelRunId, j as createBook, k as addItems, p as buildClassifyLookups, v as getFlywheelRunDetail, x as listFlywheelRuns, z as computeBookProgress } from "./flywheel-DHrgCphM.js";
72
- import { At as getContainerHistory, Ct as paneShowsClaudeBootBlockingScreen, F as setAgentStatusChangedNotifier, Ft as getSystemHealthEvidenceSnapshot, I as setAgentStoppedNotifier, J as startBootReconciliation, K as isBootReconciliationCandidate, L as setMergeReadyNotifier, Lt as isSmeeConfiguredSync, Mt as getAcceptedSystemHealthSnapshot, Nt as getDeployStaleness, O as loadConfig, Ot as getResourceStacks, Rt as startSmeeProcessSync, jt as getCurrentDockerStats, k as loadState, kt as formatUptime, n as assessDeaconPatrolFreshness, q as listBootReconciliationCandidates, t as applyBootReconciliationDecision, x as getDeaconLogs, zt as classifyAgentHealth } from "./deacon-CU_cAYSR.js";
73
- import { d as init_platform, u as detectPlatform } from "./workspace-manager-CBhNdzE4.js";
74
- import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-CY5L1lB7.js";
75
- import { r as init_in_flight_guard, t as createInFlightGuard } from "./in-flight-guard-CAAkqPLu.js";
76
- import { V as getReviewerSessionName, t as init_specialists } from "./specialists-jiuuyVkp.js";
77
- import { c as findPrdAnywhereSync, l as init_prd_locations } from "./archive-planning-Du25Jtst.js";
78
- import { a as getShadowState, s as init_shadow_state } from "./shadow-state-7a-524T8.js";
79
- import { n as init_issue_closed, t as clearIssueClosedCache } from "./issue-closed-CDJk4pIp.js";
80
- import { b as resolveSwarmPolicy } from "./deacon-swarm-BXZZ1Z2-.js";
81
- import { a as init_boot_gates, i as formatBootGateState, o as resolveBootGates } from "./no-resume-mode-Nw29vaFb.js";
82
- import { a as applyIssueObjectionLabel, c as applyIssueReleasedLabel, d as removeIssueBlocksMainLabel, f as removeIssueObjectionLabel, g as removeIssueVetoedLabel, h as removeIssueReleasedLabel, i as applyIssueBlocksMainLabel, l as applyIssueVetoedLabel, m as removeIssueReadyLabel, o as applyIssueParkedLabel, p as removeIssueParkedLabel, s as applyIssueReadyLabel, u as init_label_ops } from "./label-reconciler-B4iOAjlC.js";
83
- import { c as init_resource_utils, i as gatherProjectLensSignals, l as parseIssueIdFromTextSync, n as init_pipeline_membership, o as init_pipeline_membership_gather, r as resolvePipelineMembership, s as listOpenPullRequestsSnapshot } from "./pipeline-membership-mY3GprlK.js";
84
- import { a as loadWorkspaceMetadataSync } from "./workspace-metadata-_WygnyoX.js";
85
- import { i as resolveAutoSpawnOnFinalize, o as spawnPlanningSession } from "./spawn-planning-session-DKpdFyOC.js";
86
- import { d as killRemoteAgent, m as loadRemoteAgentState, o as getRemoteAgentOutput, r as checkRemoteSpendCap, s as init_remote_agents, v as sendToRemoteAgent, y as spawnRemoteAgent } from "./remote-agents-By1ige5O.js";
87
- import { r as spawnInspectAgent } from "./inspect-agent-BLuJ1AkT.js";
88
- import { n as init_github_pr_lookup, r as lookupPullRequestNumberForBranch, t as githubPrLookupSource } from "./github-pr-lookup-D16NGsSO.js";
89
- import { a as fetchIssuePullRequestDiff, c as getCachedIssuePrTabResponse, d as setCachedIssuePrTabResponse, i as fetchIssuePullRequestDetails, l as getIssuePrTabCacheGeneration, n as fetchIssueCheckRuns, o as init_pull_requests, r as fetchIssuePullRequest, s as bumpIssuePrTabCacheGeneration, u as init_pr_tab_cache } from "./pull-requests-_lHueOHW.js";
90
- import { t as DOD_ROWS } from "./dod-C6n9RV3G.js";
91
- import { t as enrichReviewStatus } from "./review-status-enrichment-Dci-ZwEt.js";
92
- import { c as init_pending, l as maybeTriggerStatusRollup, o as readRecentObservations, r as init_rollup, u as writePendingTurn } from "./rollup-FGsKki9R.js";
93
- import { n as getTldrDaemonServiceSync, r as init_tldr_daemon } from "./tldr-daemon-C-69c3OI.js";
94
- import { T as listGitOperationsSync, w as init_git_activity } from "./merge-agent-4WOWlSYA.js";
95
- import { t as readVerificationArtifact } from "./verification-artifact-TVFSz_q1.js";
96
- import { t as getAgentCommandSync } from "./settings-C0zgxOGf.js";
97
- import { n as init_review_agent, r as isExtendedReviewEnabled } from "./review-agent-CIGOQYBD.js";
98
- import { r as isRemoteAvailable, t as createFlyProviderFromConfig } from "./remote-C_2hasl7.js";
99
- import { n as init_claude_settings_overlay, t as detectProviderEnvConflicts } from "./claude-settings-overlay-CEdaqpWI.js";
100
- import { _ as init_transcript_adapter, a as generateFallbackSummary, c as handoffPreconditionFallbackReason, d as prependFallbackFocus, f as requestHandoffFromAgent, g as getTranscriptAdapter, i as copySessionFromCompactBoundary, l as init_summary_fork, o as generateSummaryForFork, p as reserveSummaryForkSession, r as authorHandoffExternal, s as handoffFailureReason, u as logHandoffFallback } from "./summary-fork-D1RAk9rJ.js";
101
- import { a as init_conversation_compaction, c as shouldInterceptManualCompact, n as compactConversationNative, o as isCompacting } from "./conversation-compaction-CgrR8YYp.js";
102
- import { t as init_context_layers } from "./context-layers-igZ4FI4_.js";
103
- import { r as parseArtifactRef } from "./forge-CNFnyqps.js";
104
- import { i as getMergeBlockersPayload, n as hasOrderIssuePrd, r as validateBookForStart, t as createOrderPrdLookup } from "./validate-IfDGkaVR.js";
105
- import { a as spawnSequencerAgent, i as init_sequencer_agent, n as clearFinishedSequencerRun, r as getSequencerRunStatus, t as SEQUENCER_AGENT_ID } from "./sequencer-agent-CzkcK6P5.js";
106
- import "./supervisor-wM4ooB5O.js";
107
- import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-CQB1h765.js";
37
+ import { C as transitionToMerging, S as scheduleAutoMergeWithResult, _ as markMergingBlocked, b as requeueToPending, c as isFlywheelGloballyPaused, d as listActiveAutoMerges, f as listDuePendingAutoMerges, g as markMerged, h as markFailed, m as markBlocked, p as listProblemAutoMerges, r as getActionableAutoMerge, s as init_merge_sync, t as cancelPending } from "./merge-sync-CQ-LgZ1_.js";
38
+ import { D as writeSpecForIssue, E as writeSpecDocument, S as init_specs, a as findWorkspaceDraftPlan, m as readWorkspacePlanSync, o as init_io, p as readWorkspacePlan, r as findPlan, s as isPlanningComplete, u as readPlan, v as init_types$1, x as findSpecByIssue, y as subItemsOf } from "./io-VHvc0Ssp.js";
39
+ import { C as restartCliproxy, S as readPidFile, T as stopCliproxy, b as isCliproxyInstalled, d as getCliproxyAuthDir, o as bridgeCodexAuthToCliproxy, v as init_cliproxy, w as startCliproxy, x as isCliproxyRunning, y as installCliproxy } from "./cliproxy-C8mB3KuB.js";
40
+ import { $ as updateConversationForkFallbackReason, A as isAgentConversationName, B as removeFavorite, C as getConversationByTmuxSession, D as importLegacyConversation, F as markConversationEnded, H as setClearedToConvId, I as markConversationRunning, J as setForkRequest, M as listConversations, N as listFavoritedIds, O as incrementForkRetryCount, P as markConversationActive, S as getConversationByName, T as getStuckForks, U as setConversationClaudeSessionId, X as unarchiveConversation, Y as setImportedConversationLinks, Z as updateConversationCost, _ as canRefineTitle, _t as getDiscoveredStats, at as aggregateDiscoveredSessionCost, b as getConversationByClaudeSessionId, ct as countDiscoveredSessions, et as updateConversationTitle, ft as findDiscoveredSessions, gt as getDiscoveredSessionBySessionId, h as archiveConversation, j as listArchivedConversationsWithEnrichment, k as init_conversations, mt as getDiscoveredSessionById, ot as aggregateDiscoveredSessionCostBy, q as setFavorite, tt as updateForkStatus, v as canReplaceTitle, w as getConversationLedgerCosts, x as getConversationById, y as createConversation, yt as init_discovered_sessions, z as recordConversationHandoff } from "./conversations-BUpYsOZg.js";
41
+ import { n as isBackgroundFeatureEnabled, t as init_features } from "./features--nFkJY8v.js";
42
+ import { i as init_settings, r as getMemoryWorkerConcurrency, t as areMemoryObservationsEnabled } from "./settings-Bse1Pdcj.js";
43
+ import { i as updateMemoryHealth, n as extractWithProviderPolicy, r as init_health, t as init_providers$1 } from "./providers-evXhiQeg.js";
44
+ import { d as generateLauncherScriptSync, f as init_launcher_generator, g as requireModelOverrideSync, h as normalizeModelOverrideSync, m as init_model_validation, p as MODEL_ID_PATTERN } from "./tmux-dedup-DbN6MGzM.js";
45
+ import { a as init_claude_permissions, r as getClaudePermissionFlagsStringSync } from "./claude-permissions-CN0cSCPp.js";
46
+ import { C as ensureProjectLayer, E as init_layers, I as workspaceContextFile, N as resolveProjectContextFile, P as resolveWorkspaceContextFile, S as codexGlobalContextFile, T as globalContextFile$1, b as validateTemplate, c as init_render, d as userContentOutsideRegion, i as workspaceContextWithoutProjectLayer, r as init_assemble, s as hasManagedRegion, v as init_harness, y as renderForHarness } from "./assemble-DnffNpOo.js";
47
+ import { l as init_codex, s as findRolloutPath } from "./codex-Drlf5Xhf.js";
48
+ import { i as prepareHarnessLaunch, r as init_harness_binary } from "./harness-binary-DqkftwTb.js";
49
+ import { f as init_config$1, m as loadConfigSync$1, s as getDashboardApiUrlSync } from "./config-Dr3pW6w6.js";
50
+ import { C as setFlywheelRequireUatBeforeMerge, T as setSetting, _ as setCloisterSpawnsPausedSync, c as isCloisterSpawnsPausedSync, d as isFlywheelAutoPickupBacklog, f as isFlywheelGloballyPaused$1, h as setBootReconciliationDecision, m as isMergeTrainEnabled, n as FLYWHEEL_GLOBAL_PAUSE_KEY, o as getSetting, p as isFlywheelRequireUatBeforeMerge, r as getBootReconciliationState, s as init_control_settings, t as FLYWHEEL_ACTIVE_RUN_ID_KEY, u as isDeaconGloballyPausedSync, w as setMergeTrainEnabled, x as setFlywheelAutoPickupBacklog, y as setDeaconGloballyPausedSync } from "./control-settings-C7DUehE1.js";
51
+ import { n as resolveHarness, t as init_harness_resolve } from "./harness-resolve-CqMqM1Mb.js";
52
+ import { a as loadCloisterConfigSync, o as saveCloisterConfigSync, r as init_config$2, s as shouldAutoStart } from "./config-B2IMcBxx.js";
53
+ import { c as isStateMigrated, i as ensureStateWorktree, o as init_state_home, r as clearStateMigrationCache, s as inspectStateMigration } from "./state-home-BYV7qZTW.js";
54
+ import { i as checkPrdGateSync, s as promoteWorkspacePrdDraft, t as init_pan_dir } from "./pan-dir-ubraCiUg.js";
55
+ import { a as init_lifecycle_io, t as appendContinueSessionEntryForIssue } from "./lifecycle-io-D-lGdzK3.js";
56
+ import { r as init_swarm_readiness, t as analyzeSwarmReadiness } from "./swarm-readiness-BV14DF5N.js";
57
+ import { a as buildMatchQuery, c as init_query_expansion, i as resolveKnowledgeBundleRoot, n as init_injection, o as init_search, r as injectPromptTimeMemory, s as clearQueryExpansionCache } from "./injection-UCf8EsjX.js";
58
+ import { i as runMemoryFtsTransaction, n as init_fts_db, r as runMemoryFtsStatement, t as closeMemoryFtsDatabases } from "./fts-db-BeOomFa-.js";
59
+ import { n as require_auth_errors, r as require_token_error, t as require_token_util } from "./token-util-CLq2MmQt.js";
60
+ import { A as createSettledTtlPromiseCache, D as init_advisory_checks, E as verifyAppCanMerge, M as withConcurrencyLimit, O as isAdvisoryCheckName, d as getPullRequestState, f as init_github_app, j as init_concurrency$1, k as createPromiseConcurrencyLimiter, l as getMergeBackendStatus, p as isGitHubAppConfigured } from "./github-app-CQ1Lo6LA.js";
61
+ import { C as getWorkspacePathForIssue, E as stopAgentOutputService, S as resolveIssueHeadlineCost, T as startAgentOutputService, _ as getCostsForIssueSync, b as processPendingFeedbackDeliveries, d as resumeQueuedMerges, f as syncWalFromAllProjects, g as migrateAllSessionsSync, h as reclassifyUnknownCostEventsSync, l as internalEventsRouteLayer, m as reconcilePiTranscripts, p as reconcile$1, t as workspacesRouteLayer, u as eventsRouteLayer, v as rebuildCacheSync, w as retainAgentOutputInterest, x as getCachedRunningAgents, y as syncCacheSync } from "./workspaces-j1e_6aAT.js";
62
+ import { _ as init_codex_conversation_parser, a as contextUsageFromParseResult, b as parseAcpConversationMessages, c as parseEntireConversation, d as init_pi_conversation_parser, f as isPiSessionFile, g as parseOhmypiConversationMessages, h as isOhmypiSessionFile, i as computeContextUsage, l as parseFromLastCompactBoundary, m as init_ohmypi_conversation_parser, n as gateSnapshotEmission, o as summarizeConversationActivity, p as parsePiConversationMessages, r as watchConversation, s as parseConversationMessages, t as init_conversation_service, v as parseCodexConversationMessages, y as init_acp_conversation_parser } from "./conversation-service-5aCnq1zR.js";
63
+ import { c as countPendingAskUserQuestionsForAgent, g as detectAwaitingInputFromPaneSync, h as detectAwaitingInputForAgent, l as getAgentJsonlMtime, m as scanPendingInputsPromise, o as getRuntimeForAgent, s as computeAgentEnrichment } from "./stuck-remediation-mkLC6G_t.js";
64
+ import { a as enrichSessions, c as searchSessions, i as CostThresholdError, l as embed, n as getSessionsFeedFacets, o as embedSessions, r as listSessionsFeed, s as parseRelativeTime, t as scan } from "./scanner-CV35oSn4.js";
65
+ import { c as tailEventsSync, n as deduplicateEventsSync, o as readEventsSync } from "./events-D9PR1IC0.js";
66
+ import { n as recordBackgroundAiCost, t as init_cost } from "./cost-CvxRrucd.js";
67
+ import { a as getByIssueId, i as parseFlywheelStatsWindow, n as derivePipelineRunStatsInputs, o as markFixed, r as computeFlywheelStats, s as upsert, t as listSubstrateBugWeights } from "./substrate-bug-weights-service-XfLAKnmK.js";
68
+ import { n as parseSequenceMd, r as writeSequenceMd, t as init_sequence_io } from "./sequence-io-DF_8iuhV.js";
69
+ import { _ as selectNeedsPlanning, c as classifyIssue, d as computeLanes, f as computeStats, g as normalizeGate, l as computeCohort, m as init_pickup, p as computeWaves, u as computeEpicGroups } from "./pickup-D8xtG1J2.js";
70
+ import { B as getBook, C as readCurrentLatestFlywheelStatus, D as writeFlywheelLaunchMetadata, E as subscribeLatestFlywheelStatus, F as setItemRequirements, H as liveOrderIssueLookup, I as setSettings, L as setStatus, M as moveItem, N as removeItem, O as writeLatestFlywheelStatus, P as renameBook, R as backlogCandidates, S as nextFlywheelRunId, T as resolveLiveFlywheelRunId, V as listBooks, a as isFlywheelDevcontainerRuntime, b as isFlywheelRunId, f as spawnFlywheelAgent, g as abortFlywheelRun, j as createBook, k as addItems, p as buildClassifyLookups, s as loadResumeSessionId, t as FLYWHEEL_ORCHESTRATOR_AGENT_ID, u as saveResumeSessionId, v as getFlywheelRunDetail, w as readFlywheelLaunchMetadata, x as listFlywheelRuns, z as computeBookProgress } from "./flywheel-BiT3odRb.js";
71
+ import { At as formatUptime, Bt as classifyAgentHealth, D as loadConfig, F as setAgentStoppedNotifier, G as isBootReconciliationCandidate, I as setMergeReadyNotifier, It as getSystemHealthEvidenceSnapshot, K as listBootReconciliationCandidates, Mt as getCurrentDockerStats, Nt as getAcceptedSystemHealthSnapshot, O as loadState, P as setAgentStatusChangedNotifier, Pt as getDeployStaleness, Rt as isSmeeConfiguredSync, W as extendBootReconciliationGrace, b as getDeaconLogs, jt as getContainerHistory, kt as getResourceStacks, n as assessDeaconPatrolFreshness, q as startBootReconciliation, t as applyBootReconciliationDecision, wt as paneShowsClaudeBootBlockingScreen, zt as startSmeeProcessSync } from "./deacon-hi9KLPLT.js";
72
+ import { c as installGitHooksInDir, f as detectPlatform, p as init_platform, s as init_git_hooks } from "./workspace-manager-CwO-Hek7.js";
73
+ import { a as getShadowState, s as init_shadow_state } from "./shadow-state-B4fpKxOO.js";
74
+ import { n as init_issue_closed, t as clearIssueClosedCache } from "./issue-closed-B6KJkmHZ.js";
75
+ import { i as parseArtifactRef, r as init_forge } from "./forge-CZbSg6Ds.js";
76
+ import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-BG0RUZiu.js";
77
+ import { V as getReviewerSessionName, t as init_specialists } from "./specialists-Cicieuzu.js";
78
+ import { s as findPrdAnywhereSync } from "./archive-planning-C0lAbNB_.js";
79
+ import { b as resolveSwarmPolicy } from "./deacon-swarm-DBxUaO-A.js";
80
+ import { a as init_boot_gates, i as formatBootGateState, o as resolveBootGates } from "./no-resume-mode-C5ULAXwO.js";
81
+ import { r as init_in_flight_guard, t as createInFlightGuard } from "./in-flight-guard-Bq3epG58.js";
82
+ import { a as applyIssueObjectionLabel, c as applyIssueReleasedLabel, d as removeIssueBlocksMainLabel, f as removeIssueObjectionLabel, g as removeIssueVetoedLabel, h as removeIssueReleasedLabel, i as applyIssueBlocksMainLabel, l as applyIssueVetoedLabel, m as removeIssueReadyLabel, o as applyIssueParkedLabel, p as removeIssueParkedLabel, s as applyIssueReadyLabel, u as init_label_ops } from "./label-reconciler--F85rufp.js";
83
+ import { r as computeMergeQueueFromCandidates, s as listEligibleCandidatesByProject } from "./flywheel-merge-order-BJv1DZz1.js";
84
+ import { a as gatherProjectLensSignals, g as parseIssueIdFromTextSync, h as init_resource_utils, n as PipelineMembershipUnavailableError, s as init_pipeline_membership_gather, u as listOpenPullRequestsSnapshot } from "./pipeline-membership-gather-CET40frr.js";
85
+ import { n as init_pipeline_membership, r as resolvePipelineMembership } from "./pipeline-membership-CrAPLYtf.js";
86
+ import { a as loadWorkspaceMetadataSync } from "./workspace-metadata-CQv00QwK.js";
87
+ import { i as resolveAutoSpawnOnFinalize, o as spawnPlanningSession } from "./spawn-planning-session-DGfZ5B8D.js";
88
+ import { d as killRemoteAgent, m as loadRemoteAgentState, o as getRemoteAgentOutput, r as checkRemoteSpendCap, s as init_remote_agents, v as sendToRemoteAgent, y as spawnRemoteAgent } from "./remote-agents-C_VxssLU.js";
89
+ import { r as spawnInspectAgent } from "./inspect-agent-6kylZnrm.js";
90
+ import { i as lookupPullRequestNumberForBranch, n as init_github_pr_lookup, t as githubPrLookupSource } from "./github-pr-lookup-BDq6Wznz.js";
91
+ import { a as fetchIssuePullRequestDiff, c as getCachedIssuePrTabResponse, d as setCachedIssuePrTabResponse, i as fetchIssuePullRequestDetails, l as getIssuePrTabCacheGeneration, n as fetchIssueCheckRuns, o as init_pull_requests, r as fetchIssuePullRequest, s as bumpIssuePrTabCacheGeneration, u as init_pr_tab_cache } from "./pull-requests-DHWiKxo7.js";
92
+ import { n as DOD_ROWS } from "./dod-Dar-yheg.js";
93
+ import { c as init_pending, l as maybeTriggerStatusRollup, o as readRecentObservations, r as init_rollup, u as writePendingTurn } from "./rollup-DTNvelz9.js";
94
+ import { n as getTldrDaemonServiceSync, r as init_tldr_daemon } from "./tldr-daemon-CBaWkV9k.js";
95
+ import { T as listGitOperationsSync, w as init_git_activity } from "./merge-agent-DdMVSV2Y.js";
96
+ import { t as readVerificationArtifact } from "./verification-artifact-g3yuxQDL.js";
97
+ import { i as readPendingDeploy, n as init_deploy_queue } from "./deploy-queue-UjKGYsDe.js";
98
+ import { t as getAgentCommandSync } from "./settings-CvVGqNEs.js";
99
+ import { n as init_review_agent, r as isExtendedReviewEnabled } from "./review-agent-CHhI66UP.js";
100
+ import { r as isRemoteAvailable, t as createFlyProviderFromConfig } from "./remote-BCd02Es1.js";
101
+ import { n as init_claude_settings_overlay, t as detectProviderEnvConflicts } from "./claude-settings-overlay-CJn35FHQ.js";
102
+ import { _ as init_transcript_adapter, a as generateFallbackSummary, c as handoffPreconditionFallbackReason, d as prependFallbackFocus, f as requestHandoffFromAgent, g as getTranscriptAdapter, i as copySessionFromCompactBoundary, l as init_summary_fork, o as generateSummaryForFork, p as reserveSummaryForkSession, r as authorHandoffExternal, s as handoffFailureReason, u as logHandoffFallback } from "./summary-fork-xiXBnWG9.js";
103
+ import { a as init_conversation_compaction, c as shouldInterceptManualCompact, n as compactConversationNative, o as isCompacting } from "./conversation-compaction-BoqdDXWF.js";
104
+ import { t as init_context_layers } from "./context-layers-dR5YxHYB.js";
105
+ import { a as appendLinearMcpAuthHealthyEvent, r as appendLinearMcpAuthCallbackRelayedEvent, u as resolveLinearMcpAuthIntervention } from "./linear-mcp-auth-BnU4TYfY.js";
106
+ import { i as getMergeBlockersPayload, n as hasOrderIssuePrd, r as validateBookForStart, t as createOrderPrdLookup } from "./validate-Cxsx5Uz1.js";
107
+ import { a as spawnSequencerAgent, i as init_sequencer_agent, n as clearFinishedSequencerRun, r as getSequencerRunStatus, t as SEQUENCER_AGENT_ID } from "./sequencer-agent-B2WeCtGi.js";
108
+ import { n as sameCanonicalReviewStatus, t as emitReviewStatusChanged } from "./review-status-emit-CUExtHd5.js";
109
+ import "./supervisor-DL788vos.js";
110
+ import { l as startUatTrainReconciler, u as stopUatTrainReconciler } from "./uat-train-D5UjcyAa.js";
111
+ import { n as settlePendingLifecycleClaim, t as claimPendingLifecycleFile } from "./pending-lifecycle-claim-DEteYdS9.js";
112
+ import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-CZvbtLnr.js";
108
113
  import { createRequire } from "node:module";
109
114
  import * as sp from "node:path";
110
115
  import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
@@ -112,8 +117,8 @@ import { Cause, Context, Data, Duration, Effect, Exit, Fiber, FileSystem, Layer,
112
117
  import { createReadStream, createWriteStream, existsSync, fstatSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
113
118
  import { basename as basename$1, dirname as dirname$1, join as join$1, resolve as resolve$1 } from "path";
114
119
  import { homedir } from "os";
115
- import { homedir as homedir$1, type } from "node:os";
116
- import { constants, createReadStream as createReadStream$1, existsSync as existsSync$1, mkdirSync as mkdirSync$1, promises as promises$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, readlinkSync, renameSync as renameSync$1, rmSync as rmSync$1, stat, statSync as statSync$1, symlinkSync as symlinkSync$1, unwatchFile, watch, watchFile } from "node:fs";
120
+ import { freemem as freemem$1, homedir as homedir$1, totalmem as totalmem$1, type } from "node:os";
121
+ import { constants, createReadStream as createReadStream$1, existsSync as existsSync$1, mkdirSync as mkdirSync$1, promises as promises$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, rmSync as rmSync$1, stat, statSync as statSync$1, unwatchFile, watch, watchFile } from "node:fs";
117
122
  import { FetchHttpClient, HttpRouter, HttpServer, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
118
123
  import { EventEmitter } from "node:events";
119
124
  import { access, appendFile, chmod, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat as stat$1, unlink, writeFile } from "node:fs/promises";
@@ -329,9 +334,9 @@ const ServerConfigLayer = Layer.effect(ServerConfig, Effect.sync(() => {
329
334
  const identity = getDashboardIdentity();
330
335
  const hostDashboardApiPort = readHostDashboardApiPort();
331
336
  const overrideRequested = process.env["OVERDECK_WORKSPACE_DASHBOARD_ALLOW_PRIMARY"] === "1";
332
- const agentId = process.env["OVERDECK_AGENT_ID"];
337
+ const agentId = process.env["OVERDECK_DASHBOARD_SPAWNED_BY"];
333
338
  if (overrideRequested && agentId !== void 0 && !agentId.startsWith("conv-")) {
334
- const msg = `Refusing host dashboard port override for pipeline-role identity OVERDECK_AGENT_ID=${agentId}. Work, planning, review, and flywheel agents must never bind the host dashboard port; use the workspace container endpoint instead. Only an operator-supervised conversation (conv-*) or a process with no agent identity may use this emergency override.`;
339
+ const msg = `Refusing host dashboard port override for pipeline-role identity OVERDECK_DASHBOARD_SPAWNED_BY=${agentId}. Work, planning, review, and flywheel agents must never bind the host dashboard port; use the workspace container endpoint instead. Only an operator-supervised conversation (conv-*) or a process with no agent identity may use this emergency override.`;
335
340
  console.error(`[overdeck] ${msg}`);
336
341
  throw new ServerConfigError("API_PORT", msg);
337
342
  }
@@ -713,7 +718,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
713
718
  if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
714
719
  const plan = readWorkspacePlanSync(workspacePath);
715
720
  if (!plan || plan.plan.items.length === 0) throw new PlanEmpty({ id: issueId });
716
- const { getAgentState, spawnAgent, normalizeAgentId } = await import("./agents-wi9NWzs2.js");
721
+ const { getAgentState, spawnAgent, normalizeAgentId } = await import("./agents-BKKkxKgK.js");
717
722
  const normalizedId = normalizeAgentId(issueId);
718
723
  if ((await Effect.runPromise(getAgentState(normalizedId)))?.status === "running") throw new AgentAlreadyRunning({ id: issueId });
719
724
  return {
@@ -744,7 +749,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
744
749
  const { workspacePath, projectPath, issue } = opts;
745
750
  if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
746
751
  const sessionName = opts.sessionName ?? `planning-${issueId.toLowerCase()}`;
747
- const { spawnPlanningSession } = await import("./spawn-planning-session-qUC2VWu3.js");
752
+ const { spawnPlanningSession } = await import("./spawn-planning-session-1nFi3gfk.js");
748
753
  const result = await spawnPlanningSession({
749
754
  issue: {
750
755
  id: issue.id,
@@ -777,14 +782,14 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
777
782
  }),
778
783
  kill: (agentId) => Effect.tryPromise({
779
784
  try: async () => {
780
- const { stopAgent } = await import("./agents-wi9NWzs2.js");
785
+ const { stopAgent } = await import("./agents-BKKkxKgK.js");
781
786
  await Effect.runPromise(stopAgent(agentId));
782
787
  },
783
788
  catch: () => void 0
784
789
  }).pipe(Effect.ignore),
785
790
  message: (agentId, msg) => Effect.tryPromise({
786
791
  try: async () => {
787
- const { messageAgent } = await import("./agents-wi9NWzs2.js");
792
+ const { messageAgent } = await import("./agents-BKKkxKgK.js");
788
793
  await messageAgent(agentId, msg);
789
794
  },
790
795
  catch: (err) => new AgentStartError({
@@ -795,8 +800,8 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
795
800
  }),
796
801
  deepWipe: (issueId, opts) => Effect.tryPromise({
797
802
  try: async () => {
798
- const { deepWipe } = await import("./workflows-BQu4RFQ-.js");
799
- const { resolveProjectFromIssueSync } = await import("./projects-vjjcmiN8.js");
803
+ const { deepWipe } = await import("./workflows-CHrx_hcW.js");
804
+ const { resolveProjectFromIssueSync } = await import("./projects-B03Wr-xc.js");
800
805
  const project = resolveProjectFromIssueSync(issueId);
801
806
  const projectPath = project?.projectPath ?? process.cwd();
802
807
  await Effect.runPromise(deepWipe({
@@ -828,7 +833,7 @@ init_types$1();
828
833
  * Route handlers and AgentSpawner use this instead of calling workspace-manager directly.
829
834
  */
830
835
  init_typed_errors();
831
- const execAsync$10 = promisify(exec);
836
+ const execAsync$11 = promisify(exec);
832
837
  var WorkspaceService = class extends Context.Service()("overdeck/dashboard/WorkspaceService") {};
833
838
  const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
834
839
  function getWorkspacePath(issueId) {
@@ -855,8 +860,8 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
855
860
  const { workspacePath, branch } = getWorkspacePath(issueId);
856
861
  const issueLower = issueId.toLowerCase();
857
862
  if (existsSync$1(workspacePath)) return workspacePath;
858
- const { createWorkspace } = await import("./workspace-manager-CWT55qeI.js");
859
- const { loadProjectsConfigSync } = await import("./projects-vjjcmiN8.js");
863
+ const { createWorkspace } = await import("./workspace-manager-CDTKjjdU.js");
864
+ const { loadProjectsConfigSync } = await import("./projects-B03Wr-xc.js");
860
865
  const { projects } = loadProjectsConfigSync();
861
866
  const project = resolveProjectFromIssueSync(issueId);
862
867
  if (!project) throw new WorkspaceCreateError({
@@ -891,7 +896,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
891
896
  const { workspacePath } = getWorkspacePath(issueId);
892
897
  const issueLower = issueId.toLowerCase();
893
898
  if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
894
- const { removeWorkspace } = await import("./workspace-manager-CWT55qeI.js");
899
+ const { removeWorkspace } = await import("./workspace-manager-CDTKjjdU.js");
895
900
  const project = resolveProjectFromIssueSync(issueId);
896
901
  if (!project) throw new WorkspaceCreateError({
897
902
  id: issueId,
@@ -922,7 +927,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
922
927
  try: async () => {
923
928
  const { workspacePath } = getWorkspacePath(issueId);
924
929
  const issueLower = issueId.toLowerCase();
925
- const { stopWorkspaceDocker } = await import("./workspace-manager-CWT55qeI.js");
930
+ const { stopWorkspaceDocker } = await import("./workspace-manager-CDTKjjdU.js");
926
931
  await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));
927
932
  },
928
933
  catch: () => void 0
@@ -991,7 +996,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
991
996
  join(workspacePath, ".devcontainer", "docker-compose.devcontainer.yml")
992
997
  ];
993
998
  if (!composePaths.find((p) => existsSync$1(p))) {
994
- const { ensureDevcontainerSync } = await import("./ensure-devcontainer-BZgOhRo7.js");
999
+ const { ensureDevcontainerSync } = await import("./ensure-devcontainer-C98V8yW-.js");
995
1000
  const ensure = ensureDevcontainerSync({
996
1001
  workspacePath,
997
1002
  issueId
@@ -1002,7 +1007,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
1002
1007
  });
1003
1008
  }
1004
1009
  const finalComposePath = composePaths.find((p) => existsSync$1(p));
1005
- if (finalComposePath) await execAsync$10(`docker compose -f "${finalComposePath}" up -d --build`, {
1010
+ if (finalComposePath) await execAsync$11(`docker compose -f "${finalComposePath}" up -d --build`, {
1006
1011
  cwd: dirname(finalComposePath),
1007
1012
  timeout: 3e5
1008
1013
  });
@@ -1148,7 +1153,7 @@ const OpenRouterServiceLive = Layer.effect(OpenRouterService, Effect.sync(() =>
1148
1153
  //#endregion
1149
1154
  //#region services/open.ts
1150
1155
  init_src();
1151
- const execAsync$9 = promisify(exec);
1156
+ const execAsync$10 = promisify(exec);
1152
1157
  function getFileManagerCommand() {
1153
1158
  switch (process.platform) {
1154
1159
  case "linux": return "xdg-open";
@@ -1181,7 +1186,7 @@ function launchDetached(launch) {
1181
1186
  }
1182
1187
  async function isCommandAvailable(command) {
1183
1188
  try {
1184
- await execAsync$9(`${process.platform === "win32" ? "where" : "which"} ${command}`);
1189
+ await execAsync$10(`${process.platform === "win32" ? "where" : "which"} ${command}`);
1185
1190
  return true;
1186
1191
  } catch {
1187
1192
  return false;
@@ -4858,14 +4863,14 @@ init_codex_auth();
4858
4863
  init_queries();
4859
4864
  init_cliproxy();
4860
4865
  init_tmux();
4861
- init_config();
4866
+ init_config$1();
4862
4867
  const reauthSessions = /* @__PURE__ */ new Map();
4863
4868
  const SESSION_MAX_AGE_MS = 3600 * 1e3;
4864
- const readJsonBody$19 = Effect.gen(function* () {
4869
+ const readJsonBody$20 = Effect.gen(function* () {
4865
4870
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
4866
4871
  return text ? JSON.parse(text) : {};
4867
4872
  });
4868
- function rejectInvalidOrigin(request) {
4873
+ function rejectInvalidOrigin$1(request) {
4869
4874
  const originCheck = validateOrigin$1(request);
4870
4875
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
4871
4876
  return null;
@@ -4940,7 +4945,7 @@ async function getExistingLiveReauthSession() {
4940
4945
  return null;
4941
4946
  }
4942
4947
  const postCodexReauthRoute = HttpRouter.add("POST", "/api/settings/codex-reauth", httpHandler(Effect.gen(function* () {
4943
- const originError = rejectInvalidOrigin(yield* HttpServerRequest.HttpServerRequest);
4948
+ const originError = rejectInvalidOrigin$1(yield* HttpServerRequest.HttpServerRequest);
4944
4949
  if (originError) return originError;
4945
4950
  const existing = yield* Effect.promise(() => getExistingLiveReauthSession());
4946
4951
  if (existing) {
@@ -4964,9 +4969,9 @@ const postCodexReauthRoute = HttpRouter.add("POST", "/api/settings/codex-reauth"
4964
4969
  }, { headers: { "Set-Cookie": buildTerminalCookie(sessionName, terminalToken) } });
4965
4970
  })));
4966
4971
  const postCodexReauthStatusRoute = HttpRouter.add("POST", "/api/settings/codex-reauth/status", httpHandler(Effect.gen(function* () {
4967
- const originError = rejectInvalidOrigin(yield* HttpServerRequest.HttpServerRequest);
4972
+ const originError = rejectInvalidOrigin$1(yield* HttpServerRequest.HttpServerRequest);
4968
4973
  if (originError) return originError;
4969
- const body = yield* readJsonBody$19;
4974
+ const body = yield* readJsonBody$20;
4970
4975
  const sessionName = typeof body.session === "string" ? body.session : "";
4971
4976
  const token = typeof body.token === "string" ? body.token : "";
4972
4977
  const session = getLiveReauthSession(sessionName);
@@ -5491,6 +5496,7 @@ init_codex();
5491
5496
  init_launcher_generator();
5492
5497
  init_transcript_landing();
5493
5498
  init_operator_interventions();
5499
+ init_messaging();
5494
5500
  init_agents$1();
5495
5501
  init_runtime_census();
5496
5502
  const MINIMUM_NODE_MAJOR = 24;
@@ -17255,7 +17261,7 @@ var require_get_vercel_oidc_token = /* @__PURE__ */ __commonJSMin(((exports, mod
17255
17261
  err = error;
17256
17262
  }
17257
17263
  try {
17258
- const [{ getTokenPayload, isExpired }, { refreshToken }] = await Promise.all([await import("./token-util-DuPuNriz.js").then((m) => /* @__PURE__ */ __toESM(m.default)), await import("./token-BK6t8BIT.js").then((m) => /* @__PURE__ */ __toESM(m.default))]);
17264
+ const [{ getTokenPayload, isExpired }, { refreshToken }] = await Promise.all([await import("./token-util-zlz9U0ki.js").then((m) => /* @__PURE__ */ __toESM(m.default)), await import("./token-Cxbk023d.js").then((m) => /* @__PURE__ */ __toESM(m.default))]);
17259
17265
  if (!token || isExpired(getTokenPayload(token), options?.expirationBufferMs)) {
17260
17266
  await refreshToken(options);
17261
17267
  token = getVercelOidcTokenSync();
@@ -29572,7 +29578,7 @@ var GoogleCloudTranscription = class {
29572
29578
  }
29573
29579
  async start(apiKey, model) {
29574
29580
  try {
29575
- const { SpeechClient } = await import("./src-CXmtj0im.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1)).then((module) => module.v1p1beta1 ?? module.v1);
29581
+ const { SpeechClient } = await import("./src-Ck028hjO.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1)).then((module) => module.v1p1beta1 ?? module.v1);
29576
29582
  if (this.closed) return;
29577
29583
  const stream = new SpeechClient({ apiKey }).streamingRecognize({
29578
29584
  config: {
@@ -29836,7 +29842,7 @@ const DEFAULT_VOICE_SETTINGS = {
29836
29842
  model: "gpt-4.1-mini"
29837
29843
  }
29838
29844
  };
29839
- const readJsonBody$18 = Effect.gen(function* () {
29845
+ const readJsonBody$19 = Effect.gen(function* () {
29840
29846
  const request = yield* HttpServerRequest.HttpServerRequest;
29841
29847
  const contentLength = Number(request.headers["content-length"] ?? 0);
29842
29848
  if (Number.isFinite(contentLength) && contentLength > MAX_VOICE_SETTINGS_BODY_BYTES) return {
@@ -29959,7 +29965,7 @@ const getVoiceSettingsRoute = HttpRouter.add("GET", "/api/voice/settings", httpH
29959
29965
  const putVoiceSettingsRoute = HttpRouter.add("PUT", "/api/voice/settings", httpHandler(Effect.gen(function* () {
29960
29966
  const originError = requireTrustedOrigin$2(yield* HttpServerRequest.HttpServerRequest);
29961
29967
  if (originError) return originError;
29962
- const parsed = yield* readJsonBody$18;
29968
+ const parsed = yield* readJsonBody$19;
29963
29969
  if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 413 });
29964
29970
  const body = parsed.body;
29965
29971
  if (!isVoiceSettings(body)) return jsonResponse({ error: "Invalid voice settings payload" }, { status: 400 });
@@ -30252,12 +30258,219 @@ function streamAgentOutput(eventStore, agentId) {
30252
30258
  })))));
30253
30259
  }
30254
30260
  //#endregion
30255
- //#region services/read-workspace-file.ts
30261
+ //#region services/dashboard-db-task.ts
30256
30262
  init_acp_conversation_parser();
30257
30263
  init_codex_conversation_parser();
30258
30264
  init_ohmypi_conversation_parser();
30259
30265
  init_pi_conversation_parser();
30260
30266
  init_conversation_service();
30267
+ init_control_settings();
30268
+ const MAX_PENDING_JOBS = 32;
30269
+ const SEMANTIC_SEARCH_TIMEOUT_MS = Number.parseInt(process.env["OVERDECK_SEMANTIC_SEARCH_TIMEOUT_MS"] ?? "15000", 10);
30270
+ const COALESCED_OPERATIONS = new Set([
30271
+ "scanConversations",
30272
+ "enrichSessions",
30273
+ "embedSessions",
30274
+ "searchSessionsSemantic",
30275
+ "listSubstrateBugWeights"
30276
+ ]);
30277
+ const workers = {
30278
+ read: null,
30279
+ long: null,
30280
+ semantic: null
30281
+ };
30282
+ const pending = /* @__PURE__ */ new Map();
30283
+ const sharedJobs = /* @__PURE__ */ new Map();
30284
+ let latestSemanticJobId = null;
30285
+ function workerScriptUrl() {
30286
+ return import.meta.url.endsWith(".ts") ? new URL("./dashboard-db-worker.ts", import.meta.url) : new URL("./dashboard-db-worker.js", import.meta.url);
30287
+ }
30288
+ function failPendingForLane(lane, err) {
30289
+ for (const [id, job] of pending.entries()) {
30290
+ if (job.lane !== lane) continue;
30291
+ if (job.timeout) clearTimeout(job.timeout);
30292
+ job.reject(err);
30293
+ pending.delete(id);
30294
+ }
30295
+ for (const [key, job] of sharedJobs.entries()) if (job.lane === lane) sharedJobs.delete(key);
30296
+ if (lane === "semantic") latestSemanticJobId = null;
30297
+ }
30298
+ function stableStringify(value) {
30299
+ if (value == null || typeof value !== "object") return JSON.stringify(value);
30300
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
30301
+ return `{${Object.entries(value).filter(([, v]) => typeof v !== "function" && v !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
30302
+ }
30303
+ function coalescingKey(operation, payload) {
30304
+ if (!COALESCED_OPERATIONS.has(operation)) return null;
30305
+ return `${operation}:${stableStringify(payload)}`;
30306
+ }
30307
+ function workerLane(operation) {
30308
+ if (operation === "searchSessionsSemantic") return "semantic";
30309
+ return COALESCED_OPERATIONS.has(operation) ? "long" : "read";
30310
+ }
30311
+ function cancelOlderSemanticSearches() {
30312
+ if (!latestSemanticJobId && !workers.semantic) return;
30313
+ failPendingForLane("semantic", /* @__PURE__ */ new Error("Superseded by a newer semantic search"));
30314
+ workers.semantic?.terminate();
30315
+ workers.semantic = null;
30316
+ }
30317
+ function aggregateDiscoveredSessionCostByPayload(payload) {
30318
+ if (typeof payload === "string") return aggregateDiscoveredSessionCostBy(payload);
30319
+ const input = payload;
30320
+ return aggregateDiscoveredSessionCostBy(input?.groupBy ?? "workspace", input?.filter ?? {});
30321
+ }
30322
+ function getWorker(lane) {
30323
+ const existing = workers[lane];
30324
+ if (existing) return existing;
30325
+ const worker = new Worker(workerScriptUrl(), { execArgv: process.execArgv.filter((arg) => !arg.startsWith("--inspect")) });
30326
+ workers[lane] = worker;
30327
+ worker.on("message", (message) => {
30328
+ const job = pending.get(message.id);
30329
+ if (!job) return;
30330
+ if (message.progress !== void 0) {
30331
+ job.progressChain = job.progressChain.then(async () => {
30332
+ for (const listener of job.progressListeners) await listener(message.progress);
30333
+ });
30334
+ return;
30335
+ }
30336
+ pending.delete(message.id);
30337
+ if (job.timeout) clearTimeout(job.timeout);
30338
+ if (job.lane === "semantic" && latestSemanticJobId === message.id) latestSemanticJobId = null;
30339
+ if (message.ok) {
30340
+ job.progressChain.then(() => job.resolve(message.result), job.reject);
30341
+ return;
30342
+ }
30343
+ const err = message.error?.name === "CostThresholdError" ? new CostThresholdError(message.error.estimatedCost ?? 0, message.error.threshold ?? 0, message.error.sessionCount ?? 0) : new Error(message.error?.message ?? "Dashboard database worker failed");
30344
+ err.name = message.error?.name ?? "DashboardDatabaseWorkerError";
30345
+ err.stack = message.error?.stack;
30346
+ job.progressChain.then(() => job.reject(err), job.reject);
30347
+ });
30348
+ worker.on("error", (err) => {
30349
+ failPendingForLane(lane, err);
30350
+ if (workers[lane] === worker) workers[lane] = null;
30351
+ });
30352
+ worker.on("exit", (code) => {
30353
+ if (code !== 0) failPendingForLane(lane, /* @__PURE__ */ new Error(`Dashboard database ${lane} worker exited with code ${code}`));
30354
+ if (workers[lane] === worker) workers[lane] = null;
30355
+ });
30356
+ return worker;
30357
+ }
30358
+ async function runInline$1(operation, payload, onProgress) {
30359
+ switch (operation) {
30360
+ case "getDiscoveredStats": return getDiscoveredStats();
30361
+ case "listDiscoveredSessions": {
30362
+ const filter = payload;
30363
+ return {
30364
+ sessions: findDiscoveredSessions(filter),
30365
+ total: countDiscoveredSessions({
30366
+ ...filter,
30367
+ limit: void 0,
30368
+ offset: void 0
30369
+ })
30370
+ };
30371
+ }
30372
+ case "listSessionsFeed": return listSessionsFeed(payload);
30373
+ case "getSessionsFeedFacets": return getSessionsFeedFacets(payload);
30374
+ case "getDiscoveredSessionById": return getDiscoveredSessionById(payload);
30375
+ case "aggregateDiscoveredSessionCost": return aggregateDiscoveredSessionCost(payload);
30376
+ case "aggregateDiscoveredSessionCostBy": return aggregateDiscoveredSessionCostByPayload(payload);
30377
+ case "searchSessions":
30378
+ case "searchSessionsSemantic": return searchSessions(payload);
30379
+ case "scanConversations": return scan({
30380
+ ...payload,
30381
+ onProgress
30382
+ });
30383
+ case "enrichSessions": return enrichSessions({
30384
+ ...payload,
30385
+ onProgress
30386
+ });
30387
+ case "embedSessions": return embedSessions({
30388
+ ...payload,
30389
+ autoInstall: true,
30390
+ onProgress
30391
+ });
30392
+ case "getConversationByName": return getConversationByName(payload);
30393
+ case "getSetting": return getSetting(payload);
30394
+ case "setSetting": {
30395
+ const input = payload;
30396
+ setSetting(input.key, input.value);
30397
+ return null;
30398
+ }
30399
+ case "listSubstrateBugWeights": {
30400
+ const input = payload;
30401
+ return listSubstrateBugWeights(input.window, {
30402
+ limit: input.limit,
30403
+ offset: input.offset
30404
+ });
30405
+ }
30406
+ case "getArtifactBySlug": {
30407
+ const { getArtifactBySlugJob } = await import("./artifact-index-jobs-ug6ZxIP5.js");
30408
+ return getArtifactBySlugJob(payload);
30409
+ }
30410
+ case "listArtifactsForWorkspaceOrIssue": {
30411
+ const { listArtifactsForWorkspaceOrIssueJob } = await import("./artifact-index-jobs-ug6ZxIP5.js");
30412
+ return listArtifactsForWorkspaceOrIssueJob(payload);
30413
+ }
30414
+ case "unshareArtifactBySlug": {
30415
+ const { unshareArtifactBySlugJob } = await import("./artifact-index-jobs-ug6ZxIP5.js");
30416
+ return unshareArtifactBySlugJob(payload);
30417
+ }
30418
+ }
30419
+ }
30420
+ function runDashboardDbJob(operation, payload, onProgress) {
30421
+ if (import.meta.url.endsWith(".ts") && process.env["VITEST"]) return runInline$1(operation, payload, onProgress);
30422
+ const key = coalescingKey(operation, payload);
30423
+ const existing = key ? sharedJobs.get(key) : void 0;
30424
+ if (existing) {
30425
+ if (onProgress) existing.progressListeners.add(onProgress);
30426
+ return existing.promise;
30427
+ }
30428
+ if (pending.size >= MAX_PENDING_JOBS) return Promise.reject(/* @__PURE__ */ new Error("Dashboard database worker queue is full"));
30429
+ const id = randomUUID$1();
30430
+ const lane = workerLane(operation);
30431
+ if (operation === "searchSessionsSemantic") {
30432
+ cancelOlderSemanticSearches();
30433
+ latestSemanticJobId = id;
30434
+ }
30435
+ const progressListeners = /* @__PURE__ */ new Set();
30436
+ if (onProgress) progressListeners.add(onProgress);
30437
+ const promise = new Promise((resolve, reject) => {
30438
+ const timeout = operation === "searchSessionsSemantic" ? setTimeout(() => {
30439
+ if (!pending.get(id)) return;
30440
+ pending.delete(id);
30441
+ for (const [sharedKey, sharedJob] of sharedJobs.entries()) if (sharedJob.promise === promise) sharedJobs.delete(sharedKey);
30442
+ if (latestSemanticJobId === id) latestSemanticJobId = null;
30443
+ reject(/* @__PURE__ */ new Error("Semantic search timed out"));
30444
+ workers.semantic?.terminate();
30445
+ workers.semantic = null;
30446
+ }, Number.isFinite(SEMANTIC_SEARCH_TIMEOUT_MS) ? SEMANTIC_SEARCH_TIMEOUT_MS : 15e3) : null;
30447
+ pending.set(id, {
30448
+ lane,
30449
+ operation,
30450
+ resolve,
30451
+ reject,
30452
+ progressListeners,
30453
+ progressChain: Promise.resolve(),
30454
+ timeout
30455
+ });
30456
+ getWorker(lane).postMessage({
30457
+ id,
30458
+ operation,
30459
+ payload
30460
+ });
30461
+ });
30462
+ if (key) {
30463
+ sharedJobs.set(key, {
30464
+ lane,
30465
+ promise,
30466
+ progressListeners
30467
+ });
30468
+ promise.then(() => sharedJobs.delete(key), () => sharedJobs.delete(key));
30469
+ }
30470
+ return promise;
30471
+ }
30472
+ //#endregion
30473
+ //#region services/read-workspace-file.ts
30261
30474
  init_src();
30262
30475
  init_projects();
30263
30476
  const MAX_WORKSPACE_FILE_BYTES = 256 * 1024;
@@ -30599,6 +30812,7 @@ function normalizeSessionsFeedFilter(input) {
30599
30812
  * are implemented via TerminalService (dual-runtime PTY, B20).
30600
30813
  */
30601
30814
  init_src();
30815
+ init_conversations();
30602
30816
  init_paths();
30603
30817
  init_projects();
30604
30818
  function storedToDomainEvent(stored) {
@@ -31372,7 +31586,6 @@ init_state_home();
31372
31586
  init_pan_dir();
31373
31587
  init_in_flight_guard();
31374
31588
  init_remote_agents();
31375
- init_dag();
31376
31589
  init_swarm_readiness();
31377
31590
  var PlanQualityLintError = class extends Error {
31378
31591
  constructor(issues) {
@@ -31487,9 +31700,6 @@ const PARALLEL_SAFE_REASON_FIELDS = [
31487
31700
  "readinessReason",
31488
31701
  "readiness_reason"
31489
31702
  ];
31490
- const FILE_SIZE_BASELINE_PATH = "scripts/file-size-baseline.txt";
31491
- const FILE_SIZE_RECONCILIATION_PATTERN = /\b(?:drop(?:ped|ping)?|lower(?:ed|ing)?|reconcil(?:e|ed|es|ing|iation)|remov(?:e|ed|es|ing)|updat(?:e|ed|es|ing))\b/i;
31492
- const FILE_SIZE_LINT_COMMAND_PATTERN = /(?:\b(?:bun|npm|pnpm|yarn)\s+(?:run\s+)?lint(?::file-size)?(?=$|[\s;&|])|scripts\/lint-file-size\.sh)/i;
31493
31703
  function issue(itemId, rule, message) {
31494
31704
  return {
31495
31705
  itemId,
@@ -31630,44 +31840,6 @@ function lintDocsCoverage(doc) {
31630
31840
  if (doc.plan.items.filter((item) => item.status !== "cancelled").some(documentsSomething)) return [];
31631
31841
  return [issue(null, "docs-item-missing", "Plan has no documentation work item: no active item sets metadata.kind \"docs\" or names a documentation file (.md/.mdx, or a docs/ path) in metadata.files_scope. Add the item naming the exact doc files it touches, or set plan.metadata.docsJustification to state why the change alters no documented surface.")];
31632
31842
  }
31633
- function itemSearchText(item) {
31634
- return [
31635
- item.title,
31636
- item.narrative?.Action,
31637
- ...item.metadata?.expected_outputs ?? [],
31638
- ...acceptanceCriteria(item).map((ac) => ac.title)
31639
- ].filter((value) => typeof value === "string").join("\n");
31640
- }
31641
- function scopeIncludesPath(item, filePath) {
31642
- return (item.metadata?.files_scope ?? []).some((scope) => compileGlob(scope).regex.test(filePath));
31643
- }
31644
- function requiresFileSizeLint(item) {
31645
- if ((item.metadata?.verify_commands ?? []).some((command) => typeof command === "string" && FILE_SIZE_LINT_COMMAND_PATTERN.test(command))) return true;
31646
- return acceptanceCriteria(item).some((ac) => FILE_SIZE_LINT_COMMAND_PATTERN.test(ac.title));
31647
- }
31648
- function lintDeferredFileSizeRatchet(doc) {
31649
- const activeItems = doc.plan.items.filter((item) => item.status !== "cancelled");
31650
- const readiness = analyzeSwarmReadiness(doc);
31651
- const issues = [];
31652
- const seen = /* @__PURE__ */ new Set();
31653
- for (const reconciliation of activeItems) {
31654
- if (!scopeIncludesPath(reconciliation, FILE_SIZE_BASELINE_PATH)) continue;
31655
- const reconciliationText = itemSearchText(reconciliation);
31656
- if (!reconciliationText.includes(FILE_SIZE_BASELINE_PATH) || !FILE_SIZE_RECONCILIATION_PATTERN.test(reconciliationText)) continue;
31657
- const reconciledSourceScopes = (reconciliation.metadata?.files_scope ?? []).filter((scope) => scope.startsWith("src/") && reconciliationText.includes(scope));
31658
- if (reconciledSourceScopes.length === 0) continue;
31659
- for (const candidate of activeItems) {
31660
- if (candidate.id === reconciliation.id || scopeIncludesPath(candidate, FILE_SIZE_BASELINE_PATH) || !requiresFileSizeLint(candidate)) continue;
31661
- const deferredFile = (readiness.overlapMatrix[candidate.id]?.[reconciliation.id] ?? []).find((filePath) => reconciledSourceScopes.some((scope) => compileGlob(scope).regex.test(filePath)));
31662
- if (!deferredFile) continue;
31663
- const key = `${candidate.id}\0${deferredFile}`;
31664
- if (seen.has(key)) continue;
31665
- seen.add(key);
31666
- issues.push(issue(candidate.id, "deferred-file-size-ratchet", `Item ${candidate.id} requires file-size-backed lint after changing baselined file ${deferredFile}, but defers ${FILE_SIZE_BASELINE_PATH} reconciliation to item ${reconciliation.id}. Include the baseline in ${candidate.id} metadata.files_scope and reconcile it in the same commit, or use an intermediate verification command that excludes the file-size ratchet. For an active plan, return the issue to planning, preserve stable item IDs, apply one of these repairs, and re-finalize before rerunning inspection.`));
31667
- }
31668
- }
31669
- return issues;
31670
- }
31671
31843
  function orderedPairKey(left, right) {
31672
31844
  return [left, right].sort().join("\0");
31673
31845
  }
@@ -31729,7 +31901,6 @@ function lintPlanQuality(doc, options = {}) {
31729
31901
  ...lintDocumentReferences(doc),
31730
31902
  ...lintDocsCoverage(doc),
31731
31903
  ...lintTraceCoverage(doc, options.prdText),
31732
- ...lintDeferredFileSizeRatchet(doc),
31733
31904
  ...lintOverlapAudit(doc, options)
31734
31905
  ];
31735
31906
  }
@@ -31746,7 +31917,7 @@ const execFileAsync$18 = promisify(execFile);
31746
31917
  async function loadTrackerIssues() {
31747
31918
  const map = /* @__PURE__ */ new Map();
31748
31919
  try {
31749
- const { getSharedIssueService } = await import("./issue-service-singleton-BvJ09SaR.js");
31920
+ const { getSharedIssueService } = await import("./issue-service-singleton-PMx8F1jr.js");
31750
31921
  const issues = (await getSharedIssueService()).getIssues();
31751
31922
  for (const issue of issues) if (issue.identifier) map.set(issue.identifier.toUpperCase(), issue);
31752
31923
  } catch {}
@@ -31769,7 +31940,7 @@ async function loadDockerContainers() {
31769
31940
  }
31770
31941
  async function loadActiveRemoteAgentStates() {
31771
31942
  try {
31772
- const { listActiveRemoteAgentStates } = await import("./remote-agents-CA3Ihlxj.js");
31943
+ const { listActiveRemoteAgentStates } = await import("./remote-agents-C1bssi41.js");
31773
31944
  return listActiveRemoteAgentStates();
31774
31945
  } catch {
31775
31946
  return [];
@@ -31824,9 +31995,11 @@ function refreshMembershipSnapshot(project, snapshot, getMembership, now) {
31824
31995
  snapshot.value = value;
31825
31996
  snapshot.refreshedAt = now();
31826
31997
  snapshot.lastError = void 0;
31998
+ snapshot.lastErrorReason = void 0;
31827
31999
  snapshot.lastErrorAt = void 0;
31828
32000
  }).catch((error) => {
31829
32001
  snapshot.lastError = error instanceof Error ? error.message : String(error);
32002
+ snapshot.lastErrorReason = error instanceof PipelineMembershipUnavailableError ? error.reason : "gather_failed";
31830
32003
  snapshot.lastErrorAt = now();
31831
32004
  console.warn(`[pipeline-membership] refresh failed for ${project.name ?? project.path}; keeping last-good snapshot:`, snapshot.lastError);
31832
32005
  }).finally(() => {
@@ -31844,7 +32017,8 @@ function readPipelineMembershipSnapshotsForProjects(projects) {
31844
32017
  };
31845
32018
  return {
31846
32019
  project,
31847
- error: /* @__PURE__ */ new Error(snapshot?.lastError ? `Pipeline membership refresh failed: ${snapshot.lastError}` : "Pipeline membership snapshot is loading")
32020
+ error: /* @__PURE__ */ new Error(snapshot?.lastError ? `Pipeline membership refresh failed: ${snapshot.lastError}` : "Pipeline membership snapshot is loading"),
32021
+ unavailableReason: snapshot?.lastErrorReason
31848
32022
  };
31849
32023
  });
31850
32024
  }
@@ -33096,8 +33270,10 @@ async function completePlanningForIssue(options) {
33096
33270
  workspacePath,
33097
33271
  issueId: id
33098
33272
  }));
33099
- if (draftPromotion.promoted) emitCompletePlanningPhase(id, "prdPromote", "success", `promoted ${draftPromotion.source} -> ${draftPromotion.path}`);
33100
- else emitCompletePlanningPhase(id, "prdPromote", "skipped", draftPromotion.reason);
33273
+ if (draftPromotion.promoted) {
33274
+ const removalNote = draftPromotion.sourceRemoved ? "" : " (workspace copy left in place)";
33275
+ emitCompletePlanningPhase(id, "prdPromote", "success", `promoted ${draftPromotion.source} -> ${draftPromotion.path}${removalNote}`);
33276
+ } else emitCompletePlanningPhase(id, "prdPromote", "skipped", draftPromotion.reason);
33101
33277
  } catch (error) {
33102
33278
  const reason = error instanceof Error ? error.message : String(error);
33103
33279
  emitCompletePlanningPhase(id, "prdPromote", "failure", reason);
@@ -33374,6 +33550,10 @@ async function reopenWorkspaceStatePromise(issueId, workspacePath, options = {})
33374
33550
  });
33375
33551
  result.specialistStatesReset = true;
33376
33552
  const resolved = resolveProjectFromIssueSync(issueId);
33553
+ if (resolved) {
33554
+ const project = getProjectSync(resolved.projectKey);
33555
+ if (project) clearRecordPipelineClosedOutSync(project, issueId.toUpperCase());
33556
+ }
33377
33557
  if (resolved) try {
33378
33558
  const noteParts = [`Reopened on ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`];
33379
33559
  if (options.reason) noteParts.push(`reason: ${options.reason}`);
@@ -33408,7 +33588,7 @@ init_projects();
33408
33588
  init_tracker_utils();
33409
33589
  init_agents$1();
33410
33590
  init_issue_id();
33411
- const execAsync$8 = promisify(exec);
33591
+ const execAsync$9 = promisify(exec);
33412
33592
  const execFileAsync$15 = promisify(execFile);
33413
33593
  function isGitHubIssue$4(issueId) {
33414
33594
  const resolved = resolveGitHubIssueSync(issueId);
@@ -33480,7 +33660,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
33480
33660
  timeout: 15e3
33481
33661
  });
33482
33662
  try {
33483
- const { setReviewStatusSync } = await import("./review-status-btEYlaxA.js");
33663
+ const { setReviewStatusSync } = await import("./review-status-BZH8h-6W.js");
33484
33664
  setReviewStatusSync(issueId.toUpperCase(), { prUrl: void 0 });
33485
33665
  } catch {}
33486
33666
  return [`Closed PR #${prNumber} on ${githubCheck.owner}/${githubCheck.repo}`];
@@ -33534,7 +33714,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
33534
33714
  const { ctx, projectConfig } = buildLifecycleContext(id, getIssueDataService$8().getIssueSource(id) ?? void 0);
33535
33715
  const deleteWorkspace = opts.deleteWorkspace ?? true;
33536
33716
  cleanupLog.push(...await closeIssuePullRequest(id, mode === "cancel" ? "Canceled via Overdeck" : "Reset to Todo via Overdeck"));
33537
- const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-xmBfK2GJ.js");
33717
+ const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-DDN8ApVY.js");
33538
33718
  const workflow = mode === "cancel" ? cancelIssueWorkflow : resetToTodo;
33539
33719
  const result = await Effect.runPromise(workflow(ctx, {
33540
33720
  deleteWorkspace,
@@ -33549,7 +33729,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
33549
33729
  }));
33550
33730
  cleanupLog.push(...result.steps.flatMap((step) => step.details || [step.error].filter(Boolean)));
33551
33731
  if (mode === "cancel") try {
33552
- const { transitionXBriefOnMain } = await import("./lifecycle-io-DPo7x11I.js");
33732
+ const { transitionXBriefOnMain } = await import("./lifecycle-io-D_zTLvP1.js");
33553
33733
  const tx = await Effect.runPromise(transitionXBriefOnMain(ctx.projectPath, id, "cancelled", "cancelled", `scope: cancel ${id.toUpperCase()} xBRIEF`));
33554
33734
  if (tx.moved) cleanupLog.push(`xBRIEF moved ${tx.fromDir} → cancelled`);
33555
33735
  if (tx.committed) cleanupLog.push(`Committed xBRIEF cancellation on main`);
@@ -33557,8 +33737,8 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
33557
33737
  cleanupLog.push(`xBRIEF cancel transition failed (non-fatal): ${err?.message ?? err}`);
33558
33738
  }
33559
33739
  try {
33560
- const { killAllReviewerSessions } = await import("./review-agent-DNboMyW4.js");
33561
- const { resolveProjectFromIssueSync } = await import("./projects-vjjcmiN8.js");
33740
+ const { killAllReviewerSessions } = await import("./review-agent-CD4RCI0Y.js");
33741
+ const { resolveProjectFromIssueSync } = await import("./projects-B03Wr-xc.js");
33562
33742
  const projectKey = resolveProjectFromIssueSync(id)?.projectKey;
33563
33743
  if (projectKey) {
33564
33744
  const { killed } = await Effect.runPromise(killAllReviewerSessions(projectKey, id.toUpperCase()));
@@ -33572,7 +33752,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
33572
33752
  cleanupLog.push("Cleared review status");
33573
33753
  } catch {}
33574
33754
  try {
33575
- const { resetPostMergeState } = await import("./merge-agent-CKkgIBPB.js");
33755
+ const { resetPostMergeState } = await import("./merge-agent-DrXa1CpW.js");
33576
33756
  resetPostMergeState(id);
33577
33757
  resetPostMergeState(id.toUpperCase());
33578
33758
  cleanupLog.push("Cleared merge state");
@@ -33592,7 +33772,7 @@ function closeIssueTransition(options) {
33592
33772
  const { issueId, body, eventStore } = options;
33593
33773
  const { reason } = body;
33594
33774
  const projectPath = getProjectPath$3(void 0, extractPrefixSync(issueId) ?? issueId.split("-")[0]);
33595
- const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-xmBfK2GJ.js"));
33775
+ const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-DDN8ApVY.js"));
33596
33776
  const githubCheck = isGitHubIssue$4(issueId);
33597
33777
  const issueDataService = getIssueDataService$8();
33598
33778
  const issueSource = issueDataService.getIssueSource(issueId);
@@ -33615,7 +33795,7 @@ function closeIssueTransition(options) {
33615
33795
  };
33616
33796
  }
33617
33797
  const result = yield* closeWorkflow(ctx, { reason });
33618
- if (githubCheck.isGitHub) execAsync$8("pan sync", {
33798
+ if (githubCheck.isGitHub) execAsync$9("pan sync", {
33619
33799
  encoding: "utf-8",
33620
33800
  timeout: 3e4
33621
33801
  }).catch(() => {});
@@ -33826,13 +34006,13 @@ function reopenIssueTransition(options) {
33826
34006
  }
33827
34007
  yield* Effect.promise(async () => {
33828
34008
  try {
33829
- const { stdout } = await execAsync$8(`gh pr list --head ${`feature/${id.toLowerCase()}`} --state closed --json number,mergedAt --limit 1`, {
34009
+ const { stdout } = await execAsync$9(`gh pr list --head ${`feature/${id.toLowerCase()}`} --state closed --json number,mergedAt --limit 1`, {
33830
34010
  encoding: "utf-8",
33831
34011
  timeout: 15e3
33832
34012
  });
33833
34013
  const prs = JSON.parse(stdout.trim() || "[]");
33834
34014
  if (prs.length > 0 && !prs[0].mergedAt) {
33835
- await execAsync$8(`gh pr reopen ${prs[0].number}`, {
34015
+ await execAsync$9(`gh pr reopen ${prs[0].number}`, {
33836
34016
  encoding: "utf-8",
33837
34017
  timeout: 15e3
33838
34018
  });
@@ -33859,7 +34039,7 @@ function reopenIssueTransition(options) {
33859
34039
  else clearReviewStatus(id.toUpperCase());
33860
34040
  } catch {}
33861
34041
  try {
33862
- const { resetPostMergeState } = await import("./merge-agent-CKkgIBPB.js");
34042
+ const { resetPostMergeState } = await import("./merge-agent-DrXa1CpW.js");
33863
34043
  resetPostMergeState(id);
33864
34044
  resetPostMergeState(id.toUpperCase());
33865
34045
  } catch {}
@@ -33923,7 +34103,7 @@ function moveIssueStatus(options) {
33923
34103
  "done"
33924
34104
  ];
33925
34105
  if (!targetStatus || !validStatuses.includes(targetStatus)) return jsonResponse({ error: `Invalid targetStatus. Must be one of: ${validStatuses.join(", ")}` }, { status: 400 });
33926
- const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-DnDyjujH.js"));
34106
+ const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-FV6PHfEW.js"));
33927
34107
  const issueState = {
33928
34108
  backlog: "open",
33929
34109
  todo: "open",
@@ -33986,7 +34166,7 @@ function moveIssueStatus(options) {
33986
34166
  init_issue_service_singleton();
33987
34167
  init_issue_id();
33988
34168
  init_agents$1();
33989
- const execAsync$7 = promisify(exec);
34169
+ const execAsync$8 = promisify(exec);
33990
34170
  function getIssueDataService$7() {
33991
34171
  return getSharedIssueService();
33992
34172
  }
@@ -34011,28 +34191,28 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
34011
34191
  if (projectRoot) {
34012
34192
  const workspacePath = join(projectRoot, "workspaces", `feature-${issueLower}`);
34013
34193
  try {
34014
- if ((await execAsync$7("git worktree list --porcelain", {
34194
+ if ((await execAsync$8("git worktree list --porcelain", {
34015
34195
  cwd: projectRoot,
34016
34196
  encoding: "utf-8"
34017
34197
  })).stdout.includes(workspacePath)) {
34018
- await execAsync$7(`git worktree remove "${workspacePath}" --force`, {
34198
+ await execAsync$8(`git worktree remove "${workspacePath}" --force`, {
34019
34199
  cwd: projectRoot,
34020
34200
  encoding: "utf-8"
34021
34201
  });
34022
34202
  cleanupLog.push(`Removed git worktree: ${workspacePath}`);
34023
34203
  } else if (existsSync$1(workspacePath)) {
34024
- await execAsync$7(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
34204
+ await execAsync$8(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
34025
34205
  cleanupLog.push(`Removed directory: ${workspacePath}`);
34026
34206
  }
34027
34207
  } catch {
34028
34208
  if (existsSync$1(workspacePath)) {
34029
- await execAsync$7(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
34209
+ await execAsync$8(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
34030
34210
  cleanupLog.push(`Removed directory: ${workspacePath}`);
34031
34211
  }
34032
34212
  }
34033
34213
  const branchName = `feature/${issueLower}`;
34034
34214
  try {
34035
- await execAsync$7(`git branch -D "${branchName}" 2>/dev/null || true`, {
34215
+ await execAsync$8(`git branch -D "${branchName}" 2>/dev/null || true`, {
34036
34216
  cwd: projectRoot,
34037
34217
  encoding: "utf-8"
34038
34218
  });
@@ -34041,7 +34221,7 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
34041
34221
  }
34042
34222
  const agentDir = join(homedir$1(), ".overdeck", "agents", `agent-${issueLower}`);
34043
34223
  if (existsSync$1(agentDir)) {
34044
- await execAsync$7(`rm -rf "${agentDir}"`, { encoding: "utf-8" });
34224
+ await execAsync$8(`rm -rf "${agentDir}"`, { encoding: "utf-8" });
34045
34225
  cleanupLog.push(`Removed agent state: ${agentDir}`);
34046
34226
  }
34047
34227
  await Effect.runPromise(eventStore.append({
@@ -34150,7 +34330,7 @@ async function copySettingsToWorkspace(id) {
34150
34330
  success: false,
34151
34331
  error: "Workspace not found"
34152
34332
  }, { status: 404 });
34153
- const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-CWT55qeI.js");
34333
+ const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-CDTKjjdU.js");
34154
34334
  const result = copyOverdeckSettingsToWorkspaceSync(workspacePath);
34155
34335
  return jsonResponse({
34156
34336
  success: result.errors.length === 0 || result.copied.length > 0,
@@ -34200,10 +34380,11 @@ init_lifecycle_io();
34200
34380
  init_io();
34201
34381
  init_issue_id();
34202
34382
  init_projects();
34383
+ init_record_update();
34203
34384
  init_tracker_utils();
34204
34385
  init_tmux();
34205
34386
  init_typed_errors();
34206
- const execAsync$6 = promisify(exec);
34387
+ const execAsync$7 = promisify(exec);
34207
34388
  const execFileAsync$14 = promisify(execFile);
34208
34389
  function getIssueDataService$6() {
34209
34390
  return getSharedIssueService();
@@ -34631,10 +34812,10 @@ function restartFromPlan(options) {
34631
34812
  });
34632
34813
  const resetResult = yield* Effect.promise(async () => {
34633
34814
  try {
34634
- const { runGitResetHard } = await import("./dangerous-git-ops-BXqeWVSf.js");
34815
+ const { runGitResetHard } = await import("./dangerous-git-ops-BcJPC5Rr.js");
34635
34816
  async function findPlanningCommit(grep, label) {
34636
34817
  try {
34637
- const { stdout } = await execAsync$6(`git log --grep="${grep.replace(/"/g, "\\\"")}" --format=%H -1`, {
34818
+ const { stdout } = await execAsync$7(`git log --grep="${grep.replace(/"/g, "\\\"")}" --format=%H -1`, {
34638
34819
  cwd: workspacePath,
34639
34820
  encoding: "utf-8",
34640
34821
  timeout: 1e4
@@ -34650,7 +34831,7 @@ function restartFromPlan(options) {
34650
34831
  }
34651
34832
  const found = await findPlanningCommit(`Complete planning for ${id}`, "complete-planning message") ?? await findPlanningCommit(`chore: planning artifacts for ${id}`, "agent-start message") ?? await (async () => {
34652
34833
  try {
34653
- const { stdout } = await execAsync$6(`git log --diff-filter=A --format=%H -1 -- .pan/spec.vbrief.json`, {
34834
+ const { stdout } = await execAsync$7(`git log --diff-filter=A --format=%H -1 -- .pan/spec.vbrief.json`, {
34654
34835
  cwd: workspacePath,
34655
34836
  encoding: "utf-8",
34656
34837
  timeout: 1e4
@@ -34783,7 +34964,7 @@ init_github_pr_lookup();
34783
34964
  init_tracker_utils();
34784
34965
  init_tracker_config();
34785
34966
  init_pr_tab_cache();
34786
- const execAsync$5 = promisify(exec);
34967
+ const execAsync$6 = promisify(exec);
34787
34968
  function isGitHubIssue$2(issueId) {
34788
34969
  const resolved = resolveGitHubIssueSync(issueId);
34789
34970
  if (resolved.isGitHub) return {
@@ -34831,7 +35012,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
34831
35012
  if (githubCheck.isGitHub && githubCheck.owner && githubCheck.repo && githubCheck.number) {
34832
35013
  const { owner, repo, number } = githubCheck;
34833
35014
  try {
34834
- const { stdout } = await execAsync$5(`gh api "repos/${owner}/${repo}/issues/${number}/comments?per_page=100"`, {
35015
+ const { stdout } = await execAsync$6(`gh api "repos/${owner}/${repo}/issues/${number}/comments?per_page=100"`, {
34835
35016
  encoding: "utf-8",
34836
35017
  timeout: 15e3,
34837
35018
  maxBuffer: 8 * 1024 * 1024
@@ -34894,7 +35075,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
34894
35075
  const collectedItems = [];
34895
35076
  const prConversation = (async () => {
34896
35077
  try {
34897
- const { stdout } = await execAsync$5(`gh api "repos/${prOwner}/${prRepo}/issues/${prNumber}/comments?per_page=100"`, {
35078
+ const { stdout } = await execAsync$6(`gh api "repos/${prOwner}/${prRepo}/issues/${prNumber}/comments?per_page=100"`, {
34898
35079
  encoding: "utf-8",
34899
35080
  timeout: 15e3,
34900
35081
  maxBuffer: 8 * 1024 * 1024
@@ -34915,7 +35096,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
34915
35096
  })();
34916
35097
  const prReviews = (async () => {
34917
35098
  try {
34918
- const { stdout } = await execAsync$5(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/reviews?per_page=100"`, {
35099
+ const { stdout } = await execAsync$6(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/reviews?per_page=100"`, {
34919
35100
  encoding: "utf-8",
34920
35101
  timeout: 15e3,
34921
35102
  maxBuffer: 8 * 1024 * 1024
@@ -34940,7 +35121,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
34940
35121
  })();
34941
35122
  const prInlineComments = (async () => {
34942
35123
  try {
34943
- const { stdout } = await execAsync$5(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/comments?per_page=100"`, {
35124
+ const { stdout } = await execAsync$6(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/comments?per_page=100"`, {
34944
35125
  encoding: "utf-8",
34945
35126
  timeout: 15e3,
34946
35127
  maxBuffer: 8 * 1024 * 1024
@@ -35083,7 +35264,7 @@ function closeOutIssue(id, opts = {}) {
35083
35264
  }
35084
35265
  const closeOutResult = yield* Effect.promise(async () => {
35085
35266
  try {
35086
- const { closeOut } = await import("./lifecycle-xmBfK2GJ.js");
35267
+ const { closeOut } = await import("./lifecycle-DDN8ApVY.js");
35087
35268
  return {
35088
35269
  ok: true,
35089
35270
  result: await Effect.runPromise(closeOut(ctx, {
@@ -35172,7 +35353,7 @@ function bulkCloseOut(body) {
35172
35353
  const invalidIds = issueIds.filter((id) => !isValidIssueId(id));
35173
35354
  if (invalidIds.length > 0) return jsonResponse({ error: `Invalid issue ID format: ${invalidIds.join(", ")}` }, { status: 400 });
35174
35355
  const eventStore = yield* EventStoreService;
35175
- const { closeOut } = yield* Effect.promise(() => import("./lifecycle-xmBfK2GJ.js"));
35356
+ const { closeOut } = yield* Effect.promise(() => import("./lifecycle-DDN8ApVY.js"));
35176
35357
  const issueDataService = getIssueDataService$5();
35177
35358
  const tasks = [];
35178
35359
  const agentChecks = yield* withConcurrencyLimit(issueIds.map((id) => Effect.promise(async () => {
@@ -35340,7 +35521,7 @@ const execFileAsync$13 = promisify(execFile);
35340
35521
  function getIssueDataService$4() {
35341
35522
  return getSharedIssueService();
35342
35523
  }
35343
- const readJsonBody$17 = Effect.gen(function* () {
35524
+ const readJsonBody$18 = Effect.gen(function* () {
35344
35525
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
35345
35526
  try {
35346
35527
  return text ? JSON.parse(text) : {};
@@ -35398,8 +35579,8 @@ const getIssueShipLogRoute = HttpRouter.add("GET", "/api/issues/:id/ship-log", h
35398
35579
  const id = (yield* HttpRouter.params)["id"] ?? "";
35399
35580
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35400
35581
  return yield* Effect.promise(async () => {
35401
- const { getShipLog } = await import("./ship-log-D9MlVzYU.js");
35402
- const { getReviewStatusSync } = await import("./review-status-btEYlaxA.js");
35582
+ const { getShipLog } = await import("./ship-log-xplJ8t75.js");
35583
+ const { getReviewStatusSync } = await import("./review-status-BZH8h-6W.js");
35403
35584
  const log = getShipLog(id);
35404
35585
  const rs = getReviewStatusSync(id);
35405
35586
  return jsonResponse({
@@ -35414,8 +35595,8 @@ const getIssueVerificationRoute = HttpRouter.add("GET", "/api/issues/:id/verific
35414
35595
  const id = (yield* HttpRouter.params)["id"] ?? "";
35415
35596
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35416
35597
  return yield* Effect.promise(async () => {
35417
- const { readVerificationArtifact } = await import("./verification-artifact-B3itKTxu.js");
35418
- const { getReviewStatusSync } = await import("./review-status-btEYlaxA.js");
35598
+ const { readVerificationArtifact } = await import("./verification-artifact-C85cRTc_.js");
35599
+ const { getReviewStatusSync } = await import("./review-status-BZH8h-6W.js");
35419
35600
  const { join } = await import("node:path");
35420
35601
  const resolved = resolveProjectFromIssueSync(id);
35421
35602
  const workspacePath = resolved ? join(resolved.projectPath, "workspaces", `feature-${id.toLowerCase()}`) : null;
@@ -35433,7 +35614,7 @@ const postIssueCloseRoute = HttpRouter.add("POST", "/api/issues/:issueId/close",
35433
35614
  if (!parseIssueIdSync(issueId)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35434
35615
  return yield* closeIssueTransition({
35435
35616
  issueId,
35436
- body: yield* readJsonBody$17,
35617
+ body: yield* readJsonBody$18,
35437
35618
  eventStore: yield* EventStoreService
35438
35619
  });
35439
35620
  })));
@@ -35442,7 +35623,7 @@ const postIssueStartPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/star
35442
35623
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35443
35624
  return yield* startPlanningForIssue({
35444
35625
  id,
35445
- body: yield* readJsonBody$17,
35626
+ body: yield* readJsonBody$18,
35446
35627
  eventStore: yield* EventStoreService,
35447
35628
  linear: yield* LinearClient,
35448
35629
  github: yield* GitHubClient,
@@ -35455,7 +35636,7 @@ const postIssueAbortPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/abor
35455
35636
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35456
35637
  return yield* abortPlanningForIssue({
35457
35638
  id,
35458
- body: yield* readJsonBody$17,
35639
+ body: yield* readJsonBody$18,
35459
35640
  lifecycle: yield* IssueLifecycle,
35460
35641
  linear: yield* LinearClient,
35461
35642
  eventStore: yield* EventStoreService
@@ -35465,7 +35646,7 @@ const postIssueCompletePlanningRoute = HttpRouter.add("POST", "/api/issues/:id/c
35465
35646
  const request = yield* HttpServerRequest.HttpServerRequest;
35466
35647
  const id = (yield* HttpRouter.params)["id"] ?? "";
35467
35648
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35468
- const body = yield* readJsonBody$17;
35649
+ const body = yield* readJsonBody$18;
35469
35650
  const eventStore = yield* EventStoreService;
35470
35651
  const linear = yield* LinearClient;
35471
35652
  const lifecycle = yield* IssueLifecycle;
@@ -35491,7 +35672,7 @@ const postIssueResetRoute = HttpRouter.add("POST", "/api/issues/:id/reset", http
35491
35672
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35492
35673
  return yield* resetIssueTransition({
35493
35674
  id,
35494
- body: yield* readJsonBody$17,
35675
+ body: yield* readJsonBody$18,
35495
35676
  eventStore: yield* EventStoreService
35496
35677
  });
35497
35678
  })));
@@ -35515,7 +35696,7 @@ const postIssueCancelRoute = HttpRouter.add("POST", "/api/issues/:id/cancel", ht
35515
35696
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35516
35697
  return yield* cancelIssueTransition({
35517
35698
  id,
35518
- body: yield* readJsonBody$17,
35699
+ body: yield* readJsonBody$18,
35519
35700
  eventStore: yield* EventStoreService
35520
35701
  });
35521
35702
  })));
@@ -35526,7 +35707,7 @@ const postIssueReopenRoute = HttpRouter.add("POST", "/api/issues/:id/reopen", ht
35526
35707
  if (authError) return authError;
35527
35708
  return yield* reopenIssueTransition({
35528
35709
  id,
35529
- body: yield* readJsonBody$17,
35710
+ body: yield* readJsonBody$18,
35530
35711
  lifecycle: yield* IssueLifecycle,
35531
35712
  linear: yield* LinearClient,
35532
35713
  eventStore: yield* EventStoreService
@@ -35546,7 +35727,7 @@ const postIssueMoveStatusRoute = HttpRouter.add("POST", "/api/issues/:id/move-st
35546
35727
  if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
35547
35728
  return yield* moveIssueStatus({
35548
35729
  id,
35549
- body: yield* readJsonBody$17,
35730
+ body: yield* readJsonBody$18,
35550
35731
  eventStore: yield* EventStoreService,
35551
35732
  lifecycle: yield* IssueLifecycle
35552
35733
  });
@@ -35558,7 +35739,7 @@ const postIssueCleanupWorkspaceRoute = HttpRouter.add("POST", "/api/issues/:id/c
35558
35739
  })));
35559
35740
  const postIssueDeepWipeRoute = HttpRouter.add("POST", "/api/issues/:id/deep-wipe", httpHandler(Effect.gen(function* () {
35560
35741
  const id = (yield* HttpRouter.params)["id"] ?? "";
35561
- const body = yield* readJsonBody$17;
35742
+ const body = yield* readJsonBody$18;
35562
35743
  const eventStore = yield* EventStoreService;
35563
35744
  return yield* Effect.promise(() => deepWipeIssue(id, body, eventStore));
35564
35745
  })));
@@ -35614,7 +35795,7 @@ const postIssueTaskInspectRoute = HttpRouter.add("POST", "/api/issues/:id/tasks/
35614
35795
  return yield* inspectIssueTask({
35615
35796
  id: (params["id"] ?? "").toUpperCase(),
35616
35797
  itemId: params["itemId"] ?? "",
35617
- body: yield* readJsonBody$17
35798
+ body: yield* readJsonBody$18
35618
35799
  });
35619
35800
  })));
35620
35801
  const getIssuePlanningStateRoute = HttpRouter.add("GET", "/api/issues/:id/planning-state", httpHandler(Effect.gen(function* () {
@@ -35756,7 +35937,13 @@ const getPipelineMembershipRoute = HttpRouter.add("GET", "/api/pipeline/membersh
35756
35937
  if (!project) return jsonResponse({ error: `Project not found: ${projectKey}` }, { status: 404 });
35757
35938
  const snapshot = readPipelineMembershipSnapshotsForProjects([project])[0];
35758
35939
  if (snapshot?.memberships) return jsonResponse(snapshot.memberships);
35759
- return jsonResponse({ error: snapshot?.error instanceof Error ? snapshot.error.message : "Pipeline membership snapshot is loading" }, { status: 503 });
35940
+ if (snapshot?.error && snapshot.unavailableReason) return jsonResponse({
35941
+ status: "unavailable",
35942
+ reason: snapshot.unavailableReason ?? "gather_failed",
35943
+ message: snapshot.error instanceof Error ? snapshot.error.message : String(snapshot.error),
35944
+ projectKey
35945
+ });
35946
+ return jsonResponse({ error: "Pipeline membership snapshot is loading" }, { status: 503 });
35760
35947
  })));
35761
35948
  const postPipelineMembershipRefreshRoute = HttpRouter.add("POST", "/api/pipeline/membership/refresh", httpHandler(Effect.gen(function* () {
35762
35949
  const request = yield* HttpServerRequest.HttpServerRequest;
@@ -35769,7 +35956,12 @@ const postPipelineMembershipRefreshRoute = HttpRouter.add("POST", "/api/pipeline
35769
35956
  yield* Effect.promise(() => refreshMembershipSnapshotsForProjects([project]));
35770
35957
  const snapshot = readPipelineMembershipSnapshotsForProjects([project])[0];
35771
35958
  if (snapshot?.memberships) return jsonResponse(snapshot.memberships);
35772
- return jsonResponse({ error: snapshot?.error instanceof Error ? snapshot.error.message : "Pipeline membership refresh failed" }, { status: 502 });
35959
+ return jsonResponse({
35960
+ status: "unavailable",
35961
+ reason: snapshot?.unavailableReason ?? "gather_failed",
35962
+ message: snapshot?.error instanceof Error ? snapshot.error.message : "Pipeline membership refresh failed",
35963
+ projectKey
35964
+ });
35773
35965
  })));
35774
35966
  const pipelineMembershipRouteLayer = Layer.mergeAll(getPipelineMembershipRoute, postPipelineMembershipRefreshRoute);
35775
35967
  //#endregion
@@ -36050,8 +36242,7 @@ const getCostsBackgroundRoute = HttpRouter.add("GET", "/api/costs/background", h
36050
36242
  const costsRouteLayer = Layer.mergeAll(getCostsSummaryRoute, getCostsByIssueRoute, getCostsBackgroundRoute, postCostsRebuildRoute, postCostsDeduplicateRoute, getCostsStreamRoute, getCostsTrendsRoute, getCostsByModelRoute, getCostsIssueRoute, getCostsByAgentRoute, postCostsSyncWalRoute, postCostsReconcileRoute, getCostsExperimentsRoute);
36051
36243
  //#endregion
36052
36244
  //#region services/deacon-supervisor.ts
36053
- init_control_settings();
36054
- init_config$1();
36245
+ init_config$2();
36055
36246
  init_concurrency();
36056
36247
  init_activity_logger();
36057
36248
  const DEFAULT_RESTART_WINDOW_MS = 6e4;
@@ -36222,6 +36413,7 @@ const isChildRunning = defaultSupervisor.isChildRunning;
36222
36413
  //#endregion
36223
36414
  //#region services/cloister-control-surface.ts
36224
36415
  init_agents$1();
36416
+ init_control_settings();
36225
36417
  init_paths();
36226
36418
  function readDurableCloisterStatus(deps = {}) {
36227
36419
  const cloisterState = (deps.readCloisterStateFile ?? readCloisterStateFile)();
@@ -36306,9 +36498,9 @@ function reloadDurableCloisterConfig(deps = {}) {
36306
36498
  * PUT /api/cloister/config
36307
36499
  * GET /api/cloister/agents/health
36308
36500
  */
36309
- init_config$1();
36501
+ init_config$2();
36310
36502
  init_agents$1();
36311
- const readJsonBody$16 = Effect.gen(function* () {
36503
+ const readJsonBody$17 = Effect.gen(function* () {
36312
36504
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
36313
36505
  try {
36314
36506
  return text ? JSON.parse(text) : {};
@@ -36411,7 +36603,7 @@ const getCloisterConfigRoute = HttpRouter.add("GET", "/api/cloister/config", htt
36411
36603
  catch: (err) => new Error(err instanceof Error ? err.message : String(err))
36412
36604
  })));
36413
36605
  const putCloisterConfigRoute = HttpRouter.add("PUT", "/api/cloister/config", httpHandler(Effect.gen(function* () {
36414
- const updates = yield* readJsonBody$16;
36606
+ const updates = yield* readJsonBody$17;
36415
36607
  yield* Effect.try({
36416
36608
  try: () => {
36417
36609
  saveCloisterConfigSync(updates);
@@ -37784,7 +37976,7 @@ const getStackTeardownEstimateRoute = HttpRouter.add("GET", "/api/resources/stac
37784
37976
  })));
37785
37977
  const postStackTeardownRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/teardown", httpHandler(Effect.gen(function* () {
37786
37978
  const params = yield* HttpRouter.params;
37787
- const body = yield* readJsonBody$15;
37979
+ const body = yield* readJsonBody$16;
37788
37980
  return yield* postStackTeardownEffect(params["issueId"] ?? "", body);
37789
37981
  })));
37790
37982
  function getStackTeardownEstimateEffect(issue) {
@@ -37958,7 +38150,7 @@ function lines(value) {
37958
38150
  function isDockerIdentifier(value) {
37959
38151
  return /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(value);
37960
38152
  }
37961
- const readJsonBody$15 = Effect.gen(function* () {
38153
+ const readJsonBody$16 = Effect.gen(function* () {
37962
38154
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
37963
38155
  try {
37964
38156
  return text ? JSON.parse(text) : {};
@@ -38067,7 +38259,6 @@ async function deriveSessionPresence(agentId, rtState, tmuxSessionNames) {
38067
38259
  }
38068
38260
  //#endregion
38069
38261
  //#region ../../lib/settings-api-tiered-execution.ts
38070
- init_prd_locations();
38071
38262
  function tieredExecutionConfigForSave(config, providerAuth) {
38072
38263
  if (config === void 0) return void 0;
38073
38264
  const validated = validateTieredExecutionConfig(config, { providerAuth });
@@ -38093,6 +38284,17 @@ function validateTieredExecutionSettings(config, providerAuth) {
38093
38284
  }
38094
38285
  }
38095
38286
  //#endregion
38287
+ //#region ../../lib/settings-api-telemetry.ts
38288
+ function validateApiTelemetryConfig(value) {
38289
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return ["telemetry must be an object"];
38290
+ const telemetry = value;
38291
+ const errors = [];
38292
+ if (typeof telemetry.enabled !== "boolean") errors.push("telemetry.enabled must be a boolean");
38293
+ if (telemetry.effectiveEnabled !== void 0 && typeof telemetry.effectiveEnabled !== "boolean") errors.push("telemetry.effectiveEnabled must be a boolean");
38294
+ if (telemetry.installId !== void 0 && typeof telemetry.installId !== "string") errors.push("telemetry.installId must be a string");
38295
+ return errors;
38296
+ }
38297
+ //#endregion
38096
38298
  //#region ../../lib/settings-api.ts
38097
38299
  /**
38098
38300
  * Settings API Adapter
@@ -38101,6 +38303,9 @@ function validateTieredExecutionSettings(config, providerAuth) {
38101
38303
  * Converts between YAML config format and frontend API format.
38102
38304
  */
38103
38305
  init_config_yaml();
38306
+ init_config();
38307
+ init_install_id();
38308
+ init_service();
38104
38309
  const API_TTS_KEYS = [
38105
38310
  "enabled",
38106
38311
  "voice",
@@ -38480,6 +38685,11 @@ function loadSettingsApi() {
38480
38685
  rtk: { enabled: config.rtk?.enabled ?? false },
38481
38686
  tldr: { enabled: config.tldr?.enabled ?? true }
38482
38687
  },
38688
+ telemetry: {
38689
+ enabled: config.telemetry?.enabled ?? true,
38690
+ effectiveEnabled: resolveTelemetryEnabled(),
38691
+ installId: getOrCreateInstallId()
38692
+ },
38483
38693
  tts: toApiTtsConfig(config.tts),
38484
38694
  tts_summarizer: {
38485
38695
  model: config.ttsSummarizer?.model,
@@ -38561,6 +38771,7 @@ async function writeYamlConfigPreservingComments(yamlConfig) {
38561
38771
  ["memory", config.memory],
38562
38772
  ["background_ai", config.background_ai],
38563
38773
  ["tracker_keys", config.tracker_keys],
38774
+ ["telemetry", config.telemetry],
38564
38775
  ["experimental", config.experimental],
38565
38776
  ["claude", config.claude],
38566
38777
  ["codex", config.codex],
@@ -38660,6 +38871,7 @@ async function saveSettingsApiPromise(settings) {
38660
38871
  features: settings.background_ai.features
38661
38872
  } : void 0,
38662
38873
  tracker_keys: settings.tracker_keys,
38874
+ telemetry: settings.telemetry ? { enabled: settings.telemetry.enabled } : void 0,
38663
38875
  experimental: settings.experimental ? {
38664
38876
  experimentalFeatures: settings.experimental.experimentalFeatures,
38665
38877
  claudeCodeChannels: settings.experimental.claudeCodeChannels,
@@ -38673,6 +38885,7 @@ async function saveSettingsApiPromise(settings) {
38673
38885
  tiered_execution: tieredExecutionConfigForSave(settings.tiered_execution, currentConfig.providerAuth)
38674
38886
  });
38675
38887
  clearConfigCache();
38888
+ await synchronizeAnalyticsServices();
38676
38889
  }
38677
38890
  async function updateSettingsApiPromise(updates) {
38678
38891
  const current = loadSettingsApi();
@@ -38750,6 +38963,11 @@ async function updateSettingsApiPromise(updates) {
38750
38963
  ...current.tracker_keys,
38751
38964
  ...updates.tracker_keys
38752
38965
  },
38966
+ telemetry: {
38967
+ enabled: updates.telemetry?.enabled ?? current.telemetry?.enabled ?? true,
38968
+ effectiveEnabled: !telemetryEnvironmentForcesOff() && (updates.telemetry?.enabled ?? current.telemetry?.enabled ?? true),
38969
+ installId: current.telemetry?.installId
38970
+ },
38753
38971
  experimental: {
38754
38972
  ...current.experimental,
38755
38973
  ...updates.experimental
@@ -38821,6 +39039,7 @@ function validateSettingsApi(settings) {
38821
39039
  if (settings.memory.sidebar_refresh_interval_ms !== void 0 && (!Number.isInteger(settings.memory.sidebar_refresh_interval_ms) || settings.memory.sidebar_refresh_interval_ms < 1)) errors.push("memory.sidebar_refresh_interval_ms must be a positive integer");
38822
39040
  if (settings.memory.worker_concurrency !== void 0 && (!Number.isInteger(settings.memory.worker_concurrency) || settings.memory.worker_concurrency < 1)) errors.push("memory.worker_concurrency must be a positive integer");
38823
39041
  }
39042
+ if (settings.telemetry !== void 0) errors.push(...validateApiTelemetryConfig(settings.telemetry));
38824
39043
  if (settings.experimental !== void 0) if (typeof settings.experimental !== "object" || settings.experimental === null) errors.push("experimental must be an object");
38825
39044
  else {
38826
39045
  const experimental = settings.experimental;
@@ -39288,7 +39507,7 @@ init_linear_client();
39288
39507
  init_config_yaml();
39289
39508
  init_issue_service_singleton();
39290
39509
  init_io();
39291
- const execAsync$4 = promisify(exec);
39510
+ const execAsync$5 = promisify(exec);
39292
39511
  const execFileAsync$8 = promisify(execFile);
39293
39512
  function getIssueDataService$3() {
39294
39513
  return getSharedIssueService();
@@ -39342,7 +39561,7 @@ function getProjectPath$1(issuePrefix) {
39342
39561
  }
39343
39562
  return setProjectPathCache(issuePrefix, join(homedir$1(), "Projects"));
39344
39563
  }
39345
- const readJsonBody$14 = Effect.gen(function* () {
39564
+ const readJsonBody$15 = Effect.gen(function* () {
39346
39565
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
39347
39566
  try {
39348
39567
  return text ? JSON.parse(text) : {};
@@ -39613,16 +39832,18 @@ async function fetchActivityDataWithContext(issueId, context = {}) {
39613
39832
  const isShipStage = ss.type === "merge";
39614
39833
  const nodeType = isShipStage ? "ship" : "test";
39615
39834
  const specialistSessionId = isShipStage ? `agent-${issueLower}-ship` : `agent-${issueLower}-test`;
39835
+ const specialistIsLive = tmuxSessionNames.has(specialistSessionId);
39836
+ const specialistState = getAgentStateSync(specialistSessionId);
39837
+ if (isShipStage && !specialistIsLive && !specialistState) continue;
39616
39838
  if (includeTranscripts && ss.status === "running") try {
39617
39839
  const output = (await Effect.runPromise(capturePane(specialistSessionId, 100))).trim();
39618
39840
  if (output && (output.includes(issueId.toUpperCase()) || output.includes(issueId) || output.includes(issueLower))) transcriptParts.push(`\n--- Live Output ---\n${output}`);
39619
39841
  else if (output) transcriptParts.push(`\n--- Waiting ---\nSpecialist is processing another issue. Will update when it reaches ${issueId}.`);
39620
39842
  } catch {}
39621
- const specialistIsLive = tmuxSessionNames.has(specialistSessionId);
39622
39843
  const specialistIsZombie = specialistIsLive && (ss.status === "completed" || ss.status === "failed");
39623
39844
  const specialistPresence = specialistIsLive && !specialistIsZombie ? ss.status === "running" ? "active" : "idle" : specialistIsZombie ? "idle" : "ended";
39624
39845
  const specialistJsonlPath = await resolveJsonlPath(specialistSessionId, workspacePath);
39625
- const specialistState = getAgentStateSync(specialistSessionId);
39846
+ if (isShipStage && !specialistIsLive && !specialistJsonlPath) continue;
39626
39847
  sections.push({
39627
39848
  type: nodeType,
39628
39849
  sessionId: specialistSessionId,
@@ -39841,7 +40062,7 @@ async function fetchPlanningData(issueId, options = {}) {
39841
40062
  const cached = stashCountCache.get(cacheKey);
39842
40063
  if (cached && cached.timestamp > Date.now() - STASH_COUNT_CACHE_TTL_MS) result.stashCount = cached.count;
39843
40064
  else {
39844
- const { stdout: stashList } = await execAsync$4("git stash list", {
40065
+ const { stdout: stashList } = await execAsync$5("git stash list", {
39845
40066
  cwd: workspacePath,
39846
40067
  encoding: "utf-8"
39847
40068
  });
@@ -39929,7 +40150,7 @@ async function generateStatusReview(issueId) {
39929
40150
  };
39930
40151
  const execSafe = async (cmd, opts) => {
39931
40152
  try {
39932
- const { stdout } = await execAsync$4(cmd, {
40153
+ const { stdout } = await execAsync$5(cmd, {
39933
40154
  encoding: "utf-8",
39934
40155
  timeout: 1e4,
39935
40156
  ...opts
@@ -40031,7 +40252,7 @@ Be specific: reference actual file names, function names, requirement text, disc
40031
40252
  const { command: cliCmd, args: cliArgs } = getAgentCommandSync(statusModelId);
40032
40253
  const modelFlag = cliArgs.length > 0 ? ` ${cliArgs.join(" ")}` : "";
40033
40254
  const promptFile = join(planningDir, ".status-review-prompt.tmp");
40034
- const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-u4Ga0jOr.js");
40255
+ const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-D7IluJe0.js");
40035
40256
  let providerEnvStr = "";
40036
40257
  let providerEnv = {};
40037
40258
  const statusProvider = getProviderForModelSync(statusModelId);
@@ -40048,7 +40269,7 @@ Be specific: reference actual file names, function names, requirement text, disc
40048
40269
  try {
40049
40270
  const env = buildChildEnvSync(process.env, providerEnv);
40050
40271
  const promptContent = await readFile(promptFile, "utf-8");
40051
- const { stdout: aiReview } = await execAsync$4(`${cliCmd} -p${modelFlag} --no-session-persistence`, {
40272
+ const { stdout: aiReview } = await execAsync$5(`${cliCmd} -p${modelFlag} --no-session-persistence`, {
40052
40273
  encoding: "utf-8",
40053
40274
  timeout: 12e4,
40054
40275
  maxBuffer: 1024 * 1024,
@@ -40106,7 +40327,7 @@ ${issueContext ? `## Issue Tracker Data\n${issueContext}\n` : ""}---
40106
40327
  }
40107
40328
  const postMissionControlUploadRoute = HttpRouter.add("POST", "/api/command-deck/planning/:issueId/upload", httpHandler(Effect.gen(function* () {
40108
40329
  const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
40109
- const { type, filename, content } = yield* readJsonBody$14;
40330
+ const { type, filename, content } = yield* readJsonBody$15;
40110
40331
  const issueLower = issueId.toLowerCase();
40111
40332
  const issuePrefix = extractPrefixSync(issueId) ?? issueId.split("-")[0];
40112
40333
  if (!type || !filename || !content) return jsonResponse({ error: "type, filename, and content are required" }, { status: 400 });
@@ -40114,7 +40335,7 @@ const postMissionControlUploadRoute = HttpRouter.add("POST", "/api/command-deck/
40114
40335
  let safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "-");
40115
40336
  let processedContent = content;
40116
40337
  if (safeName.endsWith(".vtt")) {
40117
- const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-DYPbKcIJ.js"));
40338
+ const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-Jr94BF3Y.js"));
40118
40339
  processedContent = vttToMarkdown(content);
40119
40340
  safeName = safeName.replace(/\.vtt$/, ".md");
40120
40341
  }
@@ -40135,7 +40356,7 @@ const postMissionControlUploadRoute = HttpRouter.add("POST", "/api/command-deck/
40135
40356
  const postMissionControlSyncDiscussionsRoute = HttpRouter.add("POST", "/api/command-deck/planning/:issueId/sync-discussions", httpHandler(Effect.gen(function* () {
40136
40357
  const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
40137
40358
  if (!parseIssueIdSync(issueId)) return jsonResponse({ error: "Invalid issue id: " + issueId }, { status: 400 });
40138
- const body = yield* readJsonBody$14;
40359
+ const body = yield* readJsonBody$15;
40139
40360
  const linear = yield* LinearClient;
40140
40361
  const { tracker } = body;
40141
40362
  const issueLower = issueId.toLowerCase();
@@ -40281,7 +40502,7 @@ const postMissionControlSyncDiscussionsRoute = HttpRouter.add("POST", "/api/comm
40281
40502
  })));
40282
40503
  const postMissionControlPlanningInitRoute = HttpRouter.add("POST", "/api/command-deck/planning/:issueId/init", httpHandler(Effect.gen(function* () {
40283
40504
  const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
40284
- const body = yield* readJsonBody$14;
40505
+ const body = yield* readJsonBody$15;
40285
40506
  const eventStore = yield* EventStoreService;
40286
40507
  const { shadow } = body;
40287
40508
  const issueLower = issueId.toLowerCase();
@@ -40340,7 +40561,7 @@ const commandDeckRouteLayer = Layer.mergeAll(getMissionControlActivityRoute, get
40340
40561
  * GET /api/remote/workspaces/:issueId/agent/output
40341
40562
  * POST /api/remote/workspaces/:issueId/agent/tell
40342
40563
  */
40343
- init_config();
40564
+ init_config$1();
40344
40565
  init_domain_services();
40345
40566
  async function loadRemoteWorkspaceMetadata(issueId) {
40346
40567
  const normalizedId = issueId.toLowerCase().replace(/[^a-z0-9-]/g, "-");
@@ -40366,7 +40587,7 @@ async function listRemoteWorkspaceMetadata() {
40366
40587
  }
40367
40588
  return workspaces;
40368
40589
  }
40369
- const readJsonBody$13 = Effect.gen(function* () {
40590
+ const readJsonBody$14 = Effect.gen(function* () {
40370
40591
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
40371
40592
  try {
40372
40593
  return text ? JSON.parse(text) : {};
@@ -40481,7 +40702,7 @@ const stopRemoteWorkspaceRoute = HttpRouter.add("POST", "/api/remote/workspaces/
40481
40702
  })));
40482
40703
  const startRemoteAgentRoute = HttpRouter.add("POST", "/api/remote/workspaces/:issueId/agent/start", httpHandler(Effect.gen(function* () {
40483
40704
  const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
40484
- const body = yield* readJsonBody$13;
40705
+ const body = yield* readJsonBody$14;
40485
40706
  const eventStore = yield* EventStoreService;
40486
40707
  const { prompt, model } = body;
40487
40708
  const metadata = yield* Effect.promise(() => loadRemoteWorkspaceMetadata(issueId));
@@ -40545,7 +40766,7 @@ const getRemoteAgentOutputRoute = HttpRouter.add("GET", "/api/remote/workspaces/
40545
40766
  })));
40546
40767
  const tellRemoteAgentRoute = HttpRouter.add("POST", "/api/remote/workspaces/:issueId/agent/tell", httpHandler(Effect.gen(function* () {
40547
40768
  const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
40548
- const { message } = yield* readJsonBody$13;
40769
+ const { message } = yield* readJsonBody$14;
40549
40770
  if (!message) return jsonResponse({ error: "Message is required" }, { status: 400 });
40550
40771
  const metadata = yield* Effect.promise(() => loadRemoteWorkspaceMetadata(issueId));
40551
40772
  if (!metadata) return jsonResponse({ error: "Remote workspace not found" }, { status: 404 });
@@ -40562,6 +40783,24 @@ const remoteRouteLayer = Layer.mergeAll(getRemoteStatusRoute, listRemoteWorkspac
40562
40783
  init_providers();
40563
40784
  init_harness_policy();
40564
40785
  /**
40786
+ * Model ids legal in the `?models=` query of GET /api/settings/harness-policy.
40787
+ *
40788
+ * Square brackets are significant: the long-context variants are named
40789
+ * `k3[1m]` and `claude-opus-5[1m]`. The pickers batch every known model id
40790
+ * into one request, so rejecting a single bracketed id 400s the whole batch
40791
+ * and leaves every model without a policy decision.
40792
+ */
40793
+ const SAFE_MODEL_PATTERN$2 = /^[a-zA-Z0-9_.:\/[\]-]+$/;
40794
+ const MAX_HARNESS_POLICY_MODELS = 250;
40795
+ const MAX_HARNESS_POLICY_MODEL_LENGTH = 200;
40796
+ /** Parse and validate the `models` query value; `null` means reject with 400. */
40797
+ function parseHarnessPolicyModels(rawModels) {
40798
+ const models = (rawModels ?? "").split(",").map((model) => model.trim()).filter(Boolean);
40799
+ if (models.length === 0 || models.length > MAX_HARNESS_POLICY_MODELS) return null;
40800
+ if (models.some((model) => model.length > MAX_HARNESS_POLICY_MODEL_LENGTH || !SAFE_MODEL_PATTERN$2.test(model))) return null;
40801
+ return models;
40802
+ }
40803
+ /**
40565
40804
  * Resolve a per-model harness-policy decision map for `models`.
40566
40805
  *
40567
40806
  * Caches the auth-mode resolver per provider so multiple models under one
@@ -44686,7 +44925,7 @@ function consumeReindexConfirmationNonce(nonce, estimatedUsd) {
44686
44925
  reindexConfirmationNonces.delete(nonce);
44687
44926
  return !!entry && entry.expiresAt > Date.now() && Math.abs(entry.estimatedUsd - estimatedUsd) < 1e-6;
44688
44927
  }
44689
- const readJsonBody$12 = Effect.gen(function* () {
44928
+ const readJsonBody$13 = Effect.gen(function* () {
44690
44929
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
44691
44930
  try {
44692
44931
  return text ? JSON.parse(text) : {};
@@ -44759,7 +44998,7 @@ const getOpenAIAuthRoute = HttpRouter.add("GET", "/api/settings/openai-auth", ht
44759
44998
  return jsonResponse(yield* getOpenAIAuthStatus());
44760
44999
  })));
44761
45000
  const postTestApiKeyRoute = HttpRouter.add("POST", "/api/settings/test-api-key", httpHandler(Effect.gen(function* () {
44762
- const { provider, apiKey, model } = yield* readJsonBody$12;
45001
+ const { provider, apiKey, model } = yield* readJsonBody$13;
44763
45002
  if (!provider || !apiKey) return jsonResponse({ error: "Provider and apiKey are required" }, { status: 400 });
44764
45003
  return yield* Effect.promise(async () => {
44765
45004
  let success = false;
@@ -45069,7 +45308,7 @@ const postTestApiKeyRoute = HttpRouter.add("POST", "/api/settings/test-api-key",
45069
45308
  });
45070
45309
  })));
45071
45310
  const postValidateApiKeyRoute = HttpRouter.add("POST", "/api/settings/validate-api-key", httpHandler(Effect.gen(function* () {
45072
- const { provider, apiKey } = yield* readJsonBody$12;
45311
+ const { provider, apiKey } = yield* readJsonBody$13;
45073
45312
  if (!provider || !apiKey) return jsonResponse({ error: "Provider and apiKey are required" }, { status: 400 });
45074
45313
  if (![
45075
45314
  "openai",
@@ -45304,7 +45543,7 @@ const getConversationSearchReindexEstimateRoute = HttpRouter.add("GET", "/api/se
45304
45543
  const postConversationSearchReindexRoute = HttpRouter.add("POST", "/api/settings/conversation-search/reindex", httpHandler(Effect.gen(function* () {
45305
45544
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
45306
45545
  if (authError) return authError;
45307
- const body = yield* readJsonBody$12;
45546
+ const body = yield* readJsonBody$13;
45308
45547
  return yield* Effect.promise(async () => {
45309
45548
  if (activeConversationSearchReindex) return jsonResponse({ error: "Conversation search reindex already in progress" }, { status: 409 });
45310
45549
  const job = (async () => {
@@ -45354,7 +45593,7 @@ const getConversationSearchReindexProgressRoute = HttpRouter.add("GET", "/api/se
45354
45593
  return jsonResponse(conversationReindexProgress);
45355
45594
  })));
45356
45595
  const putSettingsRoute = HttpRouter.add("PUT", "/api/settings", httpHandler(Effect.gen(function* () {
45357
- const body = yield* readJsonBody$12;
45596
+ const body = yield* readJsonBody$13;
45358
45597
  return yield* Effect.promise(async () => {
45359
45598
  try {
45360
45599
  const newSettings = body;
@@ -45375,7 +45614,7 @@ const putSettingsRoute = HttpRouter.add("PUT", "/api/settings", httpHandler(Effe
45375
45614
  });
45376
45615
  })));
45377
45616
  const putUiThemeRoute = HttpRouter.add("PUT", "/api/settings/ui-theme", httpHandler(Effect.gen(function* () {
45378
- const body = yield* readJsonBody$12;
45617
+ const body = yield* readJsonBody$13;
45379
45618
  return yield* Effect.promise(async () => {
45380
45619
  const theme = body?.theme;
45381
45620
  if (theme !== "light" && theme !== "dark") return jsonResponse({ error: "theme must be 'light' or 'dark'" }, { status: 400 });
@@ -45390,7 +45629,7 @@ const getOpenRouterModelsRoute = HttpRouter.add("GET", "/api/settings/openrouter
45390
45629
  });
45391
45630
  })));
45392
45631
  const putOpenRouterFavoritesRoute = HttpRouter.add("PUT", "/api/settings/openrouter/favorites", httpHandler(Effect.gen(function* () {
45393
- const { favorites } = yield* readJsonBody$12;
45632
+ const { favorites } = yield* readJsonBody$13;
45394
45633
  if (!Array.isArray(favorites)) return jsonResponse({ error: "favorites must be an array of model IDs" }, { status: 400 });
45395
45634
  const modelIds = favorites.filter((f) => typeof f === "string");
45396
45635
  return yield* Effect.promise(async () => {
@@ -45406,7 +45645,7 @@ const putOpenRouterFavoritesRoute = HttpRouter.add("PUT", "/api/settings/openrou
45406
45645
  });
45407
45646
  })));
45408
45647
  const putOpenRouterApiKeyRoute = HttpRouter.add("PUT", "/api/settings/openrouter/api-key", httpHandler(Effect.gen(function* () {
45409
- const { apiKey } = yield* readJsonBody$12;
45648
+ const { apiKey } = yield* readJsonBody$13;
45410
45649
  if (apiKey !== void 0 && typeof apiKey !== "string") return jsonResponse({ error: "apiKey must be a string when provided" }, { status: 400 });
45411
45650
  return yield* Effect.promise(async () => {
45412
45651
  try {
@@ -45421,18 +45660,16 @@ const putOpenRouterApiKeyRoute = HttpRouter.add("PUT", "/api/settings/openrouter
45421
45660
  });
45422
45661
  })));
45423
45662
  const postOpenRouterTestKeyRoute = HttpRouter.add("POST", "/api/settings/openrouter/test-key", httpHandler(Effect.gen(function* () {
45424
- const { apiKey } = yield* readJsonBody$12;
45663
+ const { apiKey } = yield* readJsonBody$13;
45425
45664
  if (!apiKey) return jsonResponse({ error: "apiKey is required" }, { status: 400 });
45426
45665
  return jsonResponse(yield* (yield* OpenRouterService).validateApiKey(apiKey));
45427
45666
  })));
45428
- const SAFE_MODEL_PATTERN$1 = /^[a-zA-Z0-9_.:\/-]+$/;
45429
- const MAX_HARNESS_POLICY_MODELS = 250;
45430
- const MAX_HARNESS_POLICY_MODEL_LENGTH = 200;
45667
+ const SAFE_MODEL_PATTERN$1 = /^[a-zA-Z0-9_.:\/[\]-]+$/;
45431
45668
  const getHarnessPolicyRoute = HttpRouter.add("GET", "/api/settings/harness-policy", httpHandler(Effect.gen(function* () {
45432
45669
  const request = yield* HttpServerRequest.HttpServerRequest;
45433
45670
  return yield* Effect.promise(async () => {
45434
- const models = (new URL(request.url, "http://localhost").searchParams.get("models") ?? "").split(",").map((model) => model.trim()).filter(Boolean);
45435
- if (models.length === 0 || models.length > MAX_HARNESS_POLICY_MODELS || models.some((model) => model.length > MAX_HARNESS_POLICY_MODEL_LENGTH || !SAFE_MODEL_PATTERN$1.test(model))) return jsonResponse({ error: "Valid models parameter is required" }, { status: 400 });
45671
+ const models = parseHarnessPolicyModels(new URL(request.url, "http://localhost").searchParams.get("models"));
45672
+ if (!models) return jsonResponse({ error: "Valid models parameter is required" }, { status: 400 });
45436
45673
  return jsonResponse({ decisions: await buildHarnessPolicyDecisions(models, getProviderAuthMode) });
45437
45674
  });
45438
45675
  })));
@@ -45474,7 +45711,7 @@ const getLegacyImportPreviewRoute = HttpRouter.add("GET", "/api/settings/legacy-
45474
45711
  const postLegacyImportRoute = HttpRouter.add("POST", "/api/settings/legacy-import/conversations", httpHandler(Effect.gen(function* () {
45475
45712
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
45476
45713
  if (authError) return authError;
45477
- const body = yield* readJsonBody$12;
45714
+ const body = yield* readJsonBody$13;
45478
45715
  return yield* Effect.try({
45479
45716
  try: () => {
45480
45717
  const { path, names } = body;
@@ -45486,7 +45723,7 @@ const postLegacyImportRoute = HttpRouter.add("POST", "/api/settings/legacy-impor
45486
45723
  const settingsRouteLayer = Layer.mergeAll(getSettingsRoute, getAvailableModelsRoute, getOptimalDefaultsRoute, getMiniMaxDefaultsRoute, getClaudeAuthRoute, getOpenAIAuthRoute, postTestApiKeyRoute, postValidateApiKeyRoute, getConversationSearchStatusRoute, getConversationSearchReindexEstimateRoute, postConversationSearchReindexRoute, getConversationSearchReindexProgressRoute, putSettingsRoute, putUiThemeRoute, getOpenRouterModelsRoute, putOpenRouterFavoritesRoute, putOpenRouterApiKeyRoute, postOpenRouterTestKeyRoute, getHarnessPolicyRoute, getProviderEnvConflictsRoute, getLegacyImportPreviewRoute, postLegacyImportRoute);
45487
45724
  //#endregion
45488
45725
  //#region routes/autopreso.ts
45489
- const readJsonBody$11 = Effect.gen(function* () {
45726
+ const readJsonBody$12 = Effect.gen(function* () {
45490
45727
  const request = yield* HttpServerRequest.HttpServerRequest;
45491
45728
  const contentLength = Number(request.headers["content-length"] ?? 0);
45492
45729
  if (Number.isFinite(contentLength) && contentLength > 256e3) return {
@@ -45517,7 +45754,7 @@ function requireTrustedOrigin$1(request) {
45517
45754
  const startRoute = HttpRouter.add("POST", "/api/autopreso/start", httpHandler(Effect.gen(function* () {
45518
45755
  const originError = requireTrustedOrigin$1(yield* HttpServerRequest.HttpServerRequest);
45519
45756
  if (originError) return originError;
45520
- const parsed = yield* readJsonBody$11;
45757
+ const parsed = yield* readJsonBody$12;
45521
45758
  if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 413 });
45522
45759
  const elements = readElementLikes(parsed.body);
45523
45760
  const validation = validateAutoPresoCanvasElements(elements);
@@ -45550,6 +45787,7 @@ const autopresoRouteLayer = Layer.mergeAll(startRoute, backToStagingRoute, reset
45550
45787
  * GET /api/activity/:id
45551
45788
  */
45552
45789
  init_domain_services();
45790
+ init_read_model();
45553
45791
  init_agents$1();
45554
45792
  init_review_status();
45555
45793
  init_cost_sync();
@@ -45630,16 +45868,7 @@ const getMetricsStuckRoute = HttpRouter.add("GET", "/api/metrics/stuck", httpHan
45630
45868
  });
45631
45869
  })));
45632
45870
  const getActivityRoute = HttpRouter.add("GET", "/api/activity", httpHandler(Effect.gen(function* () {
45633
- return jsonResponse((yield* (yield* EventStoreService).queryByType("activity.entry", 100)).map((e) => ({
45634
- id: e.payload["id"],
45635
- timestamp: e.timestamp,
45636
- source: e.payload["source"],
45637
- level: e.payload["level"],
45638
- message: e.payload["message"],
45639
- details: e.payload["details"],
45640
- issueId: e.payload["issueId"],
45641
- link: e.payload["link"]
45642
- })));
45871
+ return jsonResponse(activityEntriesFromStoredEvents(yield* (yield* EventStoreService).queryByType("activity.entry", 300), 100));
45643
45872
  })));
45644
45873
  const getActivityDetailedRoute = HttpRouter.add("GET", "/api/activity/detailed", httpHandler(Effect.gen(function* () {
45645
45874
  return jsonResponse((yield* (yield* EventStoreService).queryByType("activity.detailed", 200)).map((e) => ({
@@ -45666,16 +45895,19 @@ const getActivityByIdRoute = HttpRouter.add("GET", "/api/activity/:id", httpHand
45666
45895
  const params = yield* HttpRouter.params;
45667
45896
  const eventStore = yield* EventStoreService;
45668
45897
  const id = params["id"] ?? "";
45669
- const activity = (yield* eventStore.queryByType("activity.entry", 1e3)).find((e) => e.payload["id"] === id);
45898
+ const activity = activityEntriesFromStoredEvents(yield* eventStore.queryByType("activity.entry", 1e3), 1e3).find((entry) => entry["id"] === id);
45670
45899
  if (!activity) return jsonResponse({ error: "Activity not found" }, { status: 404 });
45671
45900
  return jsonResponse({
45672
- id: activity.payload["id"],
45673
- timestamp: activity.timestamp,
45674
- source: activity.payload["source"],
45675
- level: activity.payload["level"],
45676
- message: activity.payload["message"],
45677
- details: activity.payload["details"],
45678
- issueId: activity.payload["issueId"]
45901
+ id: activity["id"],
45902
+ timestamp: activity["timestamp"],
45903
+ source: activity["source"],
45904
+ level: activity["level"],
45905
+ status: activity["status"],
45906
+ command: activity["command"],
45907
+ message: activity["message"],
45908
+ details: activity["details"],
45909
+ output: activity["output"],
45910
+ issueId: activity["issueId"]
45679
45911
  });
45680
45912
  })));
45681
45913
  /** Parse and validate query params for GET /api/git-activity. Exported for unit testing. */
@@ -45723,7 +45955,7 @@ const metricsRouteLayer = Layer.mergeAll(getMetricsSummaryRoute, getMetricsCosts
45723
45955
  //#region routes/boot-reconciliation.ts
45724
45956
  init_tmux();
45725
45957
  init_control_settings();
45726
- const readJsonBody$10 = Effect.gen(function* () {
45958
+ const readJsonBody$11 = Effect.gen(function* () {
45727
45959
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
45728
45960
  try {
45729
45961
  return text ? JSON.parse(text) : {};
@@ -45752,7 +45984,7 @@ function bootReconciliationReadOnly(agent) {
45752
45984
  const getBootReconciliationRoute = HttpRouter.add("GET", "/api/boot-reconciliation", Effect.sync(() => {
45753
45985
  const state = getBootReconciliationState();
45754
45986
  const candidateIds = new Set(listBootReconciliationCandidates().map((agent) => agent.id));
45755
- const agents = listAllAgentsSync().filter((agent) => agent.role === "work" && (candidateIds.has(agent.id) || agent.paused === true || agent.troubled === true || Boolean(agent.hostOverride))).map((agent) => ({
45987
+ const toRow = (agent) => ({
45756
45988
  id: agent.id,
45757
45989
  issueId: agent.issueId,
45758
45990
  role: agent.role,
@@ -45763,12 +45995,26 @@ const getBootReconciliationRoute = HttpRouter.add("GET", "/api/boot-reconciliati
45763
45995
  cost: agent.costSoFar ?? null,
45764
45996
  remote: Boolean(agent.hostOverride),
45765
45997
  readOnly: bootReconciliationReadOnly(agent)
45766
- }));
45998
+ });
45999
+ const workAgents = listAllAgentsSync().filter((agent) => agent.role === "work");
46000
+ const set = workAgents.filter((agent) => candidateIds.has(agent.id)).map(toRow);
46001
+ const context = workAgents.filter((agent) => !candidateIds.has(agent.id) && (agent.paused === true || agent.troubled === true || Boolean(agent.hostOverride))).map(toRow);
45767
46002
  return jsonResponse({
45768
46003
  ...state,
45769
- set: agents
46004
+ maxGraceExtensions: 3,
46005
+ set,
46006
+ context
45770
46007
  });
45771
46008
  }));
46009
+ /**
46010
+ * Push the grace deadline out while the operator is answering a different
46011
+ * blocking dialog. Bounded server-side by MAX_BOOT_RECONCILIATION_GRACE_EXTENSIONS;
46012
+ * the client may call this freely and the cap decides.
46013
+ */
46014
+ const postBootReconciliationExtendRoute = HttpRouter.add("POST", "/api/boot-reconciliation/extend", httpHandler(Effect.sync(() => jsonResponse({
46015
+ ok: true,
46016
+ ...extendBootReconciliationGrace()
46017
+ }))));
45772
46018
  function isBootReconciliationDecision(value) {
45773
46019
  return value === "resume_all" || value === "hold_all" || value === "per_agent";
45774
46020
  }
@@ -45779,7 +46025,7 @@ function parsePerAgentMap(value) {
45779
46025
  return parsed;
45780
46026
  }
45781
46027
  const postBootReconciliationDecisionRoute = HttpRouter.add("POST", "/api/boot-reconciliation/decision", httpHandler(Effect.gen(function* () {
45782
- const body = yield* readJsonBody$10;
46028
+ const body = yield* readJsonBody$11;
45783
46029
  if (!isBootReconciliationDecision(body.decision)) return jsonResponse({
45784
46030
  ok: false,
45785
46031
  error: "Body must include decision resume_all, hold_all, or per_agent"
@@ -45799,7 +46045,7 @@ const postBootReconciliationDecisionRoute = HttpRouter.add("POST", "/api/boot-re
45799
46045
  count: resumed.length
45800
46046
  });
45801
46047
  })));
45802
- const bootReconciliationRouteLayer = Layer.mergeAll(getBootReconciliationRoute, postBootReconciliationDecisionRoute);
46048
+ const bootReconciliationRouteLayer = Layer.mergeAll(getBootReconciliationRoute, postBootReconciliationDecisionRoute, postBootReconciliationExtendRoute);
45803
46049
  //#endregion
45804
46050
  //#region routes/misc/shared.ts
45805
46051
  init_projects();
@@ -45876,7 +46122,7 @@ function getGitHubLocalPaths() {
45876
46122
  }
45877
46123
  return out;
45878
46124
  }
45879
- const readJsonBody$9 = Effect.gen(function* () {
46125
+ const readJsonBody$10 = Effect.gen(function* () {
45880
46126
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
45881
46127
  try {
45882
46128
  return text ? JSON.parse(text) : {};
@@ -45888,7 +46134,7 @@ const readJsonBody$9 = Effect.gen(function* () {
45888
46134
  //#region routes/misc/project-mappings.ts
45889
46135
  const getProjectMappingsRoute = HttpRouter.add("GET", "/api/project-mappings", httpHandler(Effect.promise(() => getProjectMappings().then((m) => jsonResponse(m)))));
45890
46136
  const putProjectMappingsRoute = HttpRouter.add("PUT", "/api/project-mappings", Effect.gen(function* () {
45891
- const mappings = yield* readJsonBody$9;
46137
+ const mappings = yield* readJsonBody$10;
45892
46138
  if (!Array.isArray(mappings)) return jsonResponse({ error: "Expected array of mappings" }, { status: 400 });
45893
46139
  yield* Effect.promise(() => saveProjectMappings(mappings));
45894
46140
  return jsonResponse({
@@ -45897,7 +46143,7 @@ const putProjectMappingsRoute = HttpRouter.add("PUT", "/api/project-mappings", E
45897
46143
  });
45898
46144
  }));
45899
46145
  const postProjectMappingsRoute = HttpRouter.add("POST", "/api/project-mappings", Effect.gen(function* () {
45900
- const { linearProjectId, linearProjectName, linearPrefix, localPath } = yield* readJsonBody$9;
46146
+ const { linearProjectId, linearProjectName, linearPrefix, localPath } = yield* readJsonBody$10;
45901
46147
  if (!linearProjectId || !localPath) return jsonResponse({ error: "linearProjectId and localPath required" }, { status: 400 });
45902
46148
  return yield* Effect.promise(async () => {
45903
46149
  try {
@@ -45924,7 +46170,7 @@ const postProjectMappingsRoute = HttpRouter.add("POST", "/api/project-mappings",
45924
46170
  const projectMappingsRouteLayer = Layer.mergeAll(getProjectMappingsRoute, putProjectMappingsRoute, postProjectMappingsRoute);
45925
46171
  //#endregion
45926
46172
  //#region routes/misc/trackers.ts
45927
- init_config();
46173
+ init_config$1();
45928
46174
  init_config_yaml();
45929
46175
  init_projects();
45930
46176
  init_tracker_config();
@@ -46004,14 +46250,14 @@ const getTrackerStatusRoute = HttpRouter.add("GET", "/api/tracker-status", Effec
46004
46250
  }
46005
46251
  }));
46006
46252
  const postRallyValidateRoute = HttpRouter.add("POST", "/api/rally/validate", Effect.gen(function* () {
46007
- const { apiKey, server, workspace, project } = yield* readJsonBody$9;
46253
+ const { apiKey, server, workspace, project } = yield* readJsonBody$10;
46008
46254
  if (!apiKey) return jsonResponse({
46009
46255
  valid: false,
46010
46256
  error: "API key is required"
46011
46257
  }, { status: 400 });
46012
46258
  return yield* Effect.promise(async () => {
46013
46259
  try {
46014
- const { RallyRestApi } = await import("./rally-api-ClOSd-Tt.js");
46260
+ const { RallyRestApi } = await import("./rally-api-D5uXN08-.js");
46015
46261
  const api = new RallyRestApi({
46016
46262
  apiKey,
46017
46263
  server: server || "https://rally1.rallydev.com"
@@ -46353,7 +46599,7 @@ const getDeaconPauseRoute = HttpRouter.add("GET", "/api/deacon/pause", Effect.tr
46353
46599
  * Persists to `app_settings` so the flag survives dashboard restarts.
46354
46600
  */
46355
46601
  const postDeaconPauseRoute = HttpRouter.add("POST", "/api/deacon/pause", httpHandler(Effect.gen(function* () {
46356
- const body = yield* readJsonBody$9;
46602
+ const body = yield* readJsonBody$10;
46357
46603
  if (typeof body.paused !== "boolean") return jsonResponse({ error: "Body must include { paused: boolean }" }, { status: 400 });
46358
46604
  setDeaconGloballyPausedSync(body.paused);
46359
46605
  console.log(`[deacon] Global pause flag set to ${body.paused}`);
@@ -46365,6 +46611,7 @@ const deaconRouteLayer = Layer.mergeAll(getNoResumeModeRoute, postResumeAllRoute
46365
46611
  init_config_yaml();
46366
46612
  init_issue_id();
46367
46613
  init_types();
46614
+ init_paths();
46368
46615
  init_projects();
46369
46616
  init_remote_agents();
46370
46617
  init_tmux();
@@ -46396,7 +46643,7 @@ const getPlanningStatusRoute = HttpRouter.add("GET", "/api/planning/:issueId/sta
46396
46643
  const remoteState = loadRemoteAgentState(sessionName);
46397
46644
  const isRemote = !!remoteState;
46398
46645
  const vmName = remoteState?.vmName ?? "";
46399
- const { getAgentStateSync } = await import("./agents-wi9NWzs2.js");
46646
+ const { getAgentStateSync } = await import("./agents-BKKkxKgK.js");
46400
46647
  const agentStarting = getAgentStateSync(sessionName)?.status === "starting";
46401
46648
  let tmuxSessionAlive = false;
46402
46649
  if (!isRemote) try {
@@ -46448,7 +46695,7 @@ const postPlanningMessageRoute = HttpRouter.add("POST", "/api/planning/:issueId/
46448
46695
  const issueId = new URL(request.url, "http://localhost").pathname.split("/")[3] || "";
46449
46696
  const sessionName = `planning-${issueId.toLowerCase()}`;
46450
46697
  const issueLower = issueId.toLowerCase();
46451
- const { message } = yield* readJsonBody$9;
46698
+ const { message } = yield* readJsonBody$10;
46452
46699
  const eventStore = yield* EventStoreService;
46453
46700
  if (!message) return jsonResponse({ error: "Message required" }, { status: 400 });
46454
46701
  return yield* Effect.promise(async () => {
@@ -46540,7 +46787,7 @@ Continue the PLANNING session. Do NOT implement anything.
46540
46787
  const agentCwd = workspacePath;
46541
46788
  const harnessLaunch = await prepareHarnessLaunch("claude-code");
46542
46789
  if (existsSync$1(outputFile)) await rename(outputFile, join(planningDir, `output-${Date.now()}.jsonl`));
46543
- const { getAgentCommandSync } = await import("./settings-66A6uvIA.js");
46790
+ const { getAgentCommandSync } = await import("./settings-CdvIF84R.js");
46544
46791
  let msgPlanningModel = "claude-sonnet-5";
46545
46792
  try {
46546
46793
  msgPlanningModel = resolveModel("plan", void 0, loadConfigSync().config);
@@ -46548,6 +46795,7 @@ Continue the PLANNING session. Do NOT implement anything.
46548
46795
  const msgAgentCmd = getAgentCommandSync(msgPlanningModel);
46549
46796
  const msgPermissionFlags = getClaudePermissionFlagsStringSync();
46550
46797
  const msgCmdWithArgs = msgAgentCmd.args.length > 0 ? `${msgAgentCmd.command} ${msgAgentCmd.args.join(" ")} ${msgPermissionFlags}` : `${msgAgentCmd.command} ${msgPermissionFlags}`;
46798
+ const agentStateDir = join(getOverdeckHome(), "agents", sessionName);
46551
46799
  const launcherScript = join(agentStateDir, "continuation-launcher.sh");
46552
46800
  await mkdir(agentStateDir, { recursive: true });
46553
46801
  await writeFile(launcherScript, generateLauncherScriptSync({
@@ -46662,7 +46910,7 @@ async function getIndexStats(rootPath, isMain) {
46662
46910
  }
46663
46911
  const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Effect.promise(async () => {
46664
46912
  try {
46665
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-BYFkts95.js");
46913
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-BKOYB616.js");
46666
46914
  const projectRoot = process.cwd();
46667
46915
  const venvPath = join(projectRoot, ".venv");
46668
46916
  const results = [];
@@ -46707,7 +46955,7 @@ const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Ef
46707
46955
  }));
46708
46956
  const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Effect.promise(async () => {
46709
46957
  try {
46710
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-BYFkts95.js");
46958
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-BKOYB616.js");
46711
46959
  const projectRoot = process.cwd();
46712
46960
  const venvPath = join(projectRoot, ".venv");
46713
46961
  if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
@@ -46724,7 +46972,7 @@ const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Ef
46724
46972
  }));
46725
46973
  const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effect.promise(async () => {
46726
46974
  try {
46727
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-BYFkts95.js");
46975
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-BKOYB616.js");
46728
46976
  const projectRoot = process.cwd();
46729
46977
  const venvPath = join(projectRoot, ".venv");
46730
46978
  if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
@@ -46741,7 +46989,7 @@ const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effe
46741
46989
  }));
46742
46990
  const postTldrReloadRoute = HttpRouter.add("POST", "/api/services/tldr/reload", Effect.promise(async () => {
46743
46991
  try {
46744
- const { getTldrDaemonServiceSync } = await import("./tldr-daemon-BYFkts95.js");
46992
+ const { getTldrDaemonServiceSync } = await import("./tldr-daemon-BKOYB616.js");
46745
46993
  const enabled = isTldrEnabledSync();
46746
46994
  const projectRoot = process.cwd();
46747
46995
  const targets = [];
@@ -46904,7 +47152,7 @@ init_tracker_config();
46904
47152
  init_issue_id();
46905
47153
  init_projects();
46906
47154
  init_tmux();
46907
- const execAsync$3 = promisify(exec);
47155
+ const execAsync$4 = promisify(exec);
46908
47156
  const execFileAsync$6 = promisify(execFile);
46909
47157
  function getIssueDataService$1() {
46910
47158
  const { getSharedIssueService } = (init_issue_service_singleton(), __toCommonJS(issue_service_singleton_exports));
@@ -46929,7 +47177,7 @@ const getVersionRoute = HttpRouter.add("GET", "/api/version", Effect.promise(asy
46929
47177
  const version = await getOverdeckVersion();
46930
47178
  let supervisorUrl = null;
46931
47179
  try {
46932
- const { getSupervisorUrlSync } = await import("./supervisor-FTJAIsAK.js");
47180
+ const { getSupervisorUrlSync } = await import("./supervisor-UWiQNK-y.js");
46933
47181
  supervisorUrl = getSupervisorUrlSync();
46934
47182
  } catch {}
46935
47183
  return jsonResponse({
@@ -46939,11 +47187,11 @@ const getVersionRoute = HttpRouter.add("GET", "/api/version", Effect.promise(asy
46939
47187
  });
46940
47188
  }));
46941
47189
  const getPrerequisitesRoute = HttpRouter.add("GET", "/api/prerequisites", Effect.promise(async () => {
46942
- const { checkSystemPrerequisites } = await import("./system-prerequisites-DUDj1YHf.js");
47190
+ const { checkSystemPrerequisites } = await import("./system-prerequisites-pdWG6ZQs.js");
46943
47191
  return jsonResponse(await checkSystemPrerequisites());
46944
47192
  }));
46945
47193
  const getSetupDiagnosticsRoute = HttpRouter.add("GET", "/api/diagnostics/setup", Effect.promise(async () => {
46946
- const { collectSetupDiagnostics } = await import("./system-prerequisites-DUDj1YHf.js");
47194
+ const { collectSetupDiagnostics } = await import("./system-prerequisites-pdWG6ZQs.js");
46947
47195
  return jsonResponse(await collectSetupDiagnostics(await getOverdeckVersion()));
46948
47196
  }));
46949
47197
  const getSyncStatusRoute = HttpRouter.add("GET", "/api/sync-status", Effect.sync(() => {
@@ -46975,6 +47223,7 @@ const getRegisteredProjectsRoute = HttpRouter.add("GET", "/api/registered-projec
46975
47223
  name: p.config.name,
46976
47224
  path: p.config.path,
46977
47225
  linearTeam: getIssuePrefix(p.config) || null,
47226
+ membershipQueryable: Boolean(getIssuePrefix(p.config)),
46978
47227
  githubRepo: p.config.github_repo || null,
46979
47228
  linearProject: p.config.linear_project || null
46980
47229
  })));
@@ -46987,7 +47236,7 @@ const getConfirmationsRoute = HttpRouter.add("GET", "/api/confirmations", Effect
46987
47236
  const postConfirmationRespondRoute = HttpRouter.add("POST", "/api/confirmations/:id/respond", Effect.gen(function* () {
46988
47237
  const request = yield* HttpServerRequest.HttpServerRequest;
46989
47238
  const id = new URL(request.url, "http://localhost").pathname.split("/")[3] || "";
46990
- const { confirmed } = yield* readJsonBody$9;
47239
+ const { confirmed } = yield* readJsonBody$10;
46991
47240
  const confirmationRequest = pendingConfirmations.get(id);
46992
47241
  if (!confirmationRequest) return jsonResponse({ error: "Confirmation request not found" }, { status: 404 });
46993
47242
  return yield* Effect.promise(async () => {
@@ -47123,7 +47372,7 @@ const postShadowMonitorRoute = HttpRouter.add("POST", "/api/shadow/:issueId/moni
47123
47372
  const projectPath = await getProjectPath(issuePrefix);
47124
47373
  const workspacePath = join(projectPath, "workspaces", `feature-${issueLower}`);
47125
47374
  if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
47126
- const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-BJfgm-hX.js");
47375
+ const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-BWLa8ODz.js");
47127
47376
  const config = {
47128
47377
  issueId,
47129
47378
  workspacePath,
@@ -47145,7 +47394,7 @@ const postShadowObserveRoute = HttpRouter.add("POST", "/api/shadow/:issueId/obse
47145
47394
  const issueId = new URL(request.url, "http://localhost").pathname.split("/")[3] || "";
47146
47395
  const issueLower = issueId.toLowerCase();
47147
47396
  const issuePrefix = extractPrefixSync(issueId) ?? issueId.split("-")[0];
47148
- const { mode } = yield* readJsonBody$9;
47397
+ const { mode } = yield* readJsonBody$10;
47149
47398
  return yield* Effect.promise(async () => {
47150
47399
  try {
47151
47400
  const projectPath = await getProjectPath(issuePrefix);
@@ -47153,7 +47402,7 @@ const postShadowObserveRoute = HttpRouter.add("POST", "/api/shadow/:issueId/obse
47153
47402
  if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
47154
47403
  const ghConfig = getGitHubConfig();
47155
47404
  if (!ghConfig) return jsonResponse({ error: "GitHub not configured - Observer requires GitHub" }, { status: 400 });
47156
- const { runObserverCycle } = await import("./shadow-engineering-BJfgm-hX.js");
47405
+ const { runObserverCycle } = await import("./shadow-engineering-BWLa8ODz.js");
47157
47406
  const firstRepo = ghConfig.repos[0];
47158
47407
  const config = {
47159
47408
  issueId,
@@ -47185,7 +47434,7 @@ const postDevRebuildRoute = HttpRouter.add("POST", "/api/dev/rebuild", Effect.ge
47185
47434
  if (!overdeckDevMode || !overdeckProjectRoot) return jsonResponse({ error: "Rebuild only available in dev mode" }, { status: 403 });
47186
47435
  return yield* Effect.promise(async () => {
47187
47436
  try {
47188
- const { stdout, stderr } = await execAsync$3("npm run build", {
47437
+ const { stdout, stderr } = await execAsync$4("npm run build", {
47189
47438
  cwd: overdeckProjectRoot,
47190
47439
  timeout: 12e4
47191
47440
  });
@@ -48538,6 +48787,7 @@ async function getConversationDiffTurn(name, turnId, fileFilter, deps) {
48538
48787
  }
48539
48788
  //#endregion
48540
48789
  //#region ../../lib/conversations/discovered-session-file.ts
48790
+ init_discovered_sessions();
48541
48791
  async function resolveDiscoveredSessionFile(locator) {
48542
48792
  if (!locator) return null;
48543
48793
  const discovered = getDiscoveredSessionBySessionId(locator);
@@ -48668,8 +48918,12 @@ function titleTranscriptWindow(transcript) {
48668
48918
  * production code into the live checkout (main saw `fs-lock.ts` appear from a
48669
48919
  * dead session). Three independent gates:
48670
48920
  *
48671
- * - `--bare` skips hooks, settings allowlists, MCP, and CLAUDE.md from the
48672
- * cwd and `~/.claude` — a repo's permissive settings can never re-arm it;
48921
+ * - `--safe-mode` + `--setting-sources ''` skip hooks, settings allowlists,
48922
+ * MCP, and CLAUDE.md from the cwd and `~/.claude` — a repo's permissive
48923
+ * settings can never re-arm it. (Formerly `--bare`, but as of Claude Code
48924
+ * 2.1.209 `--bare` also skips credential reads, so every spawn failed with
48925
+ * "Not logged in" under subscription OAuth; `--safe-mode` explicitly keeps
48926
+ * auth working.)
48673
48927
  * - `--tools ''` + `--disallowedTools mcp__*` strip every built-in and MCP
48674
48928
  * tool so the model never sees a tool definition;
48675
48929
  * - `--permission-mode dontAsk` auto-denies (never prompts, never hangs) if
@@ -48681,7 +48935,9 @@ function titleTranscriptWindow(transcript) {
48681
48935
  function buildStructuredClaudeArgs(schema, model) {
48682
48936
  return [
48683
48937
  "-p",
48684
- "--bare",
48938
+ "--safe-mode",
48939
+ "--setting-sources",
48940
+ "",
48685
48941
  "--tools",
48686
48942
  "",
48687
48943
  "--disallowedTools",
@@ -48896,6 +49152,340 @@ function isNonEmptyJsonlLine(buffer) {
48896
49152
  return buffer.subarray(0, end).some((byte) => byte !== 32 && byte !== 9);
48897
49153
  }
48898
49154
  //#endregion
49155
+ //#region ../../lib/pane-choice-menu.ts
49156
+ init_transcript_adapter();
49157
+ init_summary_fork();
49158
+ init_conversation_compaction();
49159
+ /**
49160
+ * PAN-3113 — parse blocking numbered-choice menus from a captured tmux pane.
49161
+ *
49162
+ * Claude Code (and sibling harnesses) render modal choices as a cursor-driven
49163
+ * numbered menu — the session-resume gate is the driving case:
49164
+ *
49165
+ * This session is 4h 5m old and 146.9k tokens.
49166
+ *
49167
+ * Resuming the full session will consume a substantial portion of your
49168
+ * usage limits. We recommend resuming from a summary.
49169
+ *
49170
+ * ❯ 1. Resume from summary (recommended)
49171
+ * 2. Resume full session as-is
49172
+ * 3. Don't ask me again
49173
+ *
49174
+ * Enter to confirm · Esc to cancel
49175
+ *
49176
+ * These menus never appear as transcript tool calls, so neither the AUQ
49177
+ * pipeline nor the JSONL pending-input scan can see them. This module turns
49178
+ * the pane text into a structured menu the dashboard can render as an
49179
+ * actionable card (PAN-3113 mockup A) and answer with literal keystrokes
49180
+ * (Down × N + Enter).
49181
+ *
49182
+ * The parser is deliberately conservative — a false positive parks a phantom
49183
+ * decision card in a live conversation, which is worse than a miss:
49184
+ *
49185
+ * - Options must be numbered sequentially from 1 (kills prose lists such as
49186
+ * "3. FCM push is dead…" and "1. First step / 2. Second step" plans).
49187
+ * - High confidence requires BOTH a ❯ cursor row and a harness footer hint
49188
+ * ("Enter to confirm · Esc to cancel"). Exactly one of the two is low
49189
+ * confidence (render the verbatim mirror, not the card). Neither → null.
49190
+ * - The menu must be the current bottom-of-pane surface: after the menu and
49191
+ * its footer, only blank lines, box separators, or further footer hints
49192
+ * may follow. Real output below the menu means it was answered/scrolled.
49193
+ * - Claude permission menus ("1. Yes / 2. Yes, and allow / 3. No") are
49194
+ * excluded — the existing tool_permission detection path owns them.
49195
+ * - Multi-select menus ("space to select") are excluded — answering needs
49196
+ * toggle semantics the card does not model.
49197
+ */
49198
+ const ANSI_PATTERN = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
49199
+ /** "❯ 1. Resume from summary (recommended)" / " 2. Resume full session as-is" */
49200
+ const OPTION_LINE = /^\s*(❯|›)?\s*(\d{1,2})[.)]\s+(\S.*)$/;
49201
+ /** Box-drawing separator lines (Claude draws ─── rules around its input box). */
49202
+ const SEPARATOR_LINE = /^\s*[─━═]{6,}\s*$/;
49203
+ /** Bare input prompt with nothing typed — the menu is gone when this follows. */
49204
+ const BARE_PROMPT_LINE = /^\s*(❯|›)\s*$/;
49205
+ const MAX_TRAILING_LINES = 6;
49206
+ const MAX_CONTEXT_LINES = 8;
49207
+ const MAX_OPTIONS = 9;
49208
+ const MAX_LABEL_CHARS = 100;
49209
+ const RECOMMENDED_SUFFIX = /\s*\((recommended)\)\s*$/i;
49210
+ function cleanLine(line) {
49211
+ return line.replace(ANSI_PATTERN, "").replace(/[\x00-\x1f\x7f]/g, "").replace(/\s+$/g, "");
49212
+ }
49213
+ /**
49214
+ * PAN-3068 shape — key/action pairs separated by "·". Every segment must open
49215
+ * with a key name, so real post-answer output never qualifies.
49216
+ */
49217
+ function looksLikeHarnessFooterHint(line) {
49218
+ const segments = line.split("·").map((s) => s.trim()).filter(Boolean);
49219
+ if (segments.length === 0) return false;
49220
+ return segments.every((segment) => /^(?:esc(?:ape)?|tab|enter|return|space|backspace|shift\+\S+|ctrl\+\S+|alt\+\S+|\?)\b/i.test(segment) || /^press\s+\S+/i.test(segment));
49221
+ }
49222
+ /** "1. Yes / 2. Yes, and allow… / 3. No" — owned by the permission-menu path. */
49223
+ function looksLikePermissionOptions(options) {
49224
+ if (options.length !== 3) return false;
49225
+ return /^yes$/i.test(options[0].label) && /^yes,?\s*(?:and\s*)?(?:allow|don't ask|always)/i.test(options[1].label) && /^no\b/i.test(options[2].label);
49226
+ }
49227
+ function parseOptionLine(line) {
49228
+ const match = OPTION_LINE.exec(line);
49229
+ if (!match) return null;
49230
+ const number = Number(match[2]);
49231
+ if (!Number.isInteger(number) || number < 1 || number > MAX_OPTIONS) return null;
49232
+ const rawLabel = match[3].trim();
49233
+ if (rawLabel.length === 0 || rawLabel.length > MAX_LABEL_CHARS) return null;
49234
+ return {
49235
+ cursor: Boolean(match[1]),
49236
+ number,
49237
+ rawLabel
49238
+ };
49239
+ }
49240
+ /**
49241
+ * Parse the last numbered-choice menu in the pane, or null when the pane is
49242
+ * not currently blocked on one. See the module header for the conservative
49243
+ * acceptance rules.
49244
+ */
49245
+ function parsePaneChoiceMenu(paneText) {
49246
+ if (!paneText) return null;
49247
+ const lines = paneText.split("\n").map(cleanLine);
49248
+ let end = lines.length - 1;
49249
+ while (end >= 0 && lines[end].trim() === "") end -= 1;
49250
+ if (end < 0) return null;
49251
+ let lastOption = -1;
49252
+ for (let i = end; i >= 0; i -= 1) {
49253
+ if (parseOptionLine(lines[i])) {
49254
+ lastOption = i;
49255
+ break;
49256
+ }
49257
+ if (end - i > MAX_TRAILING_LINES + MAX_OPTIONS) break;
49258
+ }
49259
+ if (lastOption < 0) return null;
49260
+ const optionLines = [];
49261
+ let firstOption = lastOption;
49262
+ for (let i = lastOption; i >= 0; i -= 1) {
49263
+ const parsed = parseOptionLine(lines[i]);
49264
+ if (!parsed) break;
49265
+ optionLines.unshift(parsed);
49266
+ firstOption = i;
49267
+ }
49268
+ if (optionLines.length < 2 || optionLines.length > MAX_OPTIONS) return null;
49269
+ for (let i = 0; i < optionLines.length; i += 1) if (optionLines[i].number !== i + 1) return null;
49270
+ const cursorRows = optionLines.map((opt, index) => opt.cursor ? index : -1).filter((index) => index >= 0);
49271
+ if (cursorRows.length > 1) return null;
49272
+ const trailing = lines.slice(lastOption + 1).filter((line) => line.trim() !== "");
49273
+ if (trailing.length > MAX_TRAILING_LINES) return null;
49274
+ let footerHint = null;
49275
+ for (const line of trailing) {
49276
+ if (BARE_PROMPT_LINE.test(line)) return null;
49277
+ if (SEPARATOR_LINE.test(line)) continue;
49278
+ if (looksLikeHarnessFooterHint(line.trim())) {
49279
+ if (footerHint === null) footerHint = line.trim();
49280
+ continue;
49281
+ }
49282
+ return null;
49283
+ }
49284
+ const options = optionLines.map((opt) => {
49285
+ const recommended = RECOMMENDED_SUFFIX.test(opt.rawLabel);
49286
+ return {
49287
+ number: opt.number,
49288
+ label: recommended ? opt.rawLabel.replace(RECOMMENDED_SUFFIX, "") : opt.rawLabel,
49289
+ recommended
49290
+ };
49291
+ });
49292
+ if (looksLikePermissionOptions(options)) return null;
49293
+ if (footerHint && /space\s+to\s+(?:select|toggle)/i.test(footerHint)) return null;
49294
+ const contextLines = [];
49295
+ let paragraphs = 0;
49296
+ let i = firstOption - 1;
49297
+ while (i >= 0 && lines[i].trim() === "") i -= 1;
49298
+ for (; i >= 0 && contextLines.length < MAX_CONTEXT_LINES; i -= 1) {
49299
+ const line = lines[i];
49300
+ if (SEPARATOR_LINE.test(line) || BARE_PROMPT_LINE.test(line)) break;
49301
+ if (parseOptionLine(line)) break;
49302
+ if (line.trim() === "") {
49303
+ if (paragraphs >= 2) break;
49304
+ let j = i - 1;
49305
+ while (j >= 0 && lines[j].trim() === "") j -= 1;
49306
+ if (j < 0 || SEPARATOR_LINE.test(lines[j]) || BARE_PROMPT_LINE.test(lines[j]) || parseOptionLine(lines[j])) break;
49307
+ i = j + 1;
49308
+ continue;
49309
+ }
49310
+ if (lines[i + 1] === void 0 || lines[i + 1].trim() === "") paragraphs += 1;
49311
+ contextLines.unshift(line.trim());
49312
+ }
49313
+ const hasCursor = cursorRows.length === 1;
49314
+ if (!hasCursor && footerHint === null) return null;
49315
+ const confidence = hasCursor && footerHint !== null ? "high" : "low";
49316
+ return {
49317
+ title: contextLines[0] ?? options[0].label,
49318
+ contextLines,
49319
+ options,
49320
+ selectedIndex: hasCursor ? cursorRows[0] : 0,
49321
+ footerHint,
49322
+ confidence
49323
+ };
49324
+ }
49325
+ /** Stable identity for patrol dedupe — same menu re-parsed must not re-surface. */
49326
+ function paneChoiceMenuSignature(menu) {
49327
+ return `${menu.title}::${menu.options.map((o) => `${o.number}:${o.label}`).join("|")}`;
49328
+ }
49329
+ /**
49330
+ * Literal tmux keystrokes answering the menu with `selectedIndex`: arrow the
49331
+ * cursor from its current row to the target, then confirm. Claude Code menus
49332
+ * move the ❯ cursor with ↑/↓ and confirm with Enter; number keys are not
49333
+ * guaranteed across harness versions, so arrows are the universal path.
49334
+ */
49335
+ function buildChoiceKeystrokes(menu, selectedIndex) {
49336
+ const clamped = Math.max(0, Math.min(selectedIndex, menu.options.length - 1));
49337
+ const from = menu.selectedIndex;
49338
+ const keys = [];
49339
+ for (let i = from; i < clamped; i += 1) keys.push("Down");
49340
+ for (let i = from; i > clamped; i -= 1) keys.push("Up");
49341
+ keys.push("Enter");
49342
+ return keys;
49343
+ }
49344
+ //#endregion
49345
+ //#region ../../lib/overdeck/conversation-pane-choice.ts
49346
+ /**
49347
+ * PAN-3113 — surface blocking numbered-choice menus in claude-code
49348
+ * conversation panes (the session-resume gate is the driving case) as
49349
+ * actionable pending input, and answer them from the dashboard.
49350
+ *
49351
+ * Detection is request-driven, not a patrol: the pending-input feed already
49352
+ * polls every few seconds, so detection rides that cycle — when the menu
49353
+ * disappears (answered from the dashboard OR directly in the terminal) the
49354
+ * next poll simply stops reporting it. No server-side state machine, no
49355
+ * stale records.
49356
+ *
49357
+ * Answering re-captures uncached, verifies the menu the operator saw is still
49358
+ * the menu on screen (signature match), drives the selection with literal
49359
+ * keystrokes (Up/Down + Enter via sendRawKeystroke), then re-captures to
49360
+ * confirm the menu dismissed — the same contract as the Codex TUI approval
49361
+ * flow (`handleConversationCodexApproval`).
49362
+ */
49363
+ init_conversations();
49364
+ init_behavior();
49365
+ init_tmux();
49366
+ const PANE_CAPTURE_LINES = 90;
49367
+ const KEYSTROKE_GAP_MS = 60;
49368
+ const DELIVERY_CONFIRM_WAIT_MS = 700;
49369
+ function isClaudeCodeConversation(conv) {
49370
+ return getHarnessBehavior(conv.harness).transcriptKind === "claude-jsonl";
49371
+ }
49372
+ /**
49373
+ * Parse the conversation's current pane for a blocking numbered-choice menu.
49374
+ * Returns null when the conversation is not claude-code, the capture fails,
49375
+ * or no menu is on screen. Detection-only callers (the pending-input feed)
49376
+ * use the shared cached capture; the answer path captures uncached.
49377
+ */
49378
+ async function claudeConversationPaneChoice(conv, options = {}) {
49379
+ if (!isClaudeCodeConversation(conv)) return null;
49380
+ const capture = options.capture ?? ((sessionName, lines) => capturePaneText(sessionName, lines));
49381
+ let pane;
49382
+ try {
49383
+ pane = await capture(conv.tmuxSession, PANE_CAPTURE_LINES);
49384
+ } catch {
49385
+ return null;
49386
+ }
49387
+ const menu = parsePaneChoiceMenu(pane);
49388
+ if (!menu) return null;
49389
+ return {
49390
+ signature: paneChoiceMenuSignature(menu),
49391
+ title: menu.title,
49392
+ contextLines: menu.contextLines,
49393
+ options: menu.options.map((o) => ({
49394
+ number: o.number,
49395
+ label: o.label,
49396
+ recommended: o.recommended
49397
+ })),
49398
+ selectedIndex: menu.selectedIndex,
49399
+ footerHint: menu.footerHint,
49400
+ confidence: menu.confidence
49401
+ };
49402
+ }
49403
+ /**
49404
+ * POST /api/conversations/:id/pane-choice — answer the live pane menu.
49405
+ *
49406
+ * Body: `{ selectedIndex: number (0-based), signature: string }`. The
49407
+ * signature must match the menu currently on screen, or the operator answered
49408
+ * a stale card and we refuse (409) rather than send keystrokes into a
49409
+ * different prompt. Returns a plain `{ body, status }` for the route to
49410
+ * serialize — the same shape as the conversation-read handlers, so the
49411
+ * contract is unit-testable without an HTTP layer.
49412
+ */
49413
+ async function handleConversationPaneChoiceAnswer(rawId, body, deps = {}) {
49414
+ try {
49415
+ const selectedIndex = Number(body["selectedIndex"]);
49416
+ const signature = typeof body["signature"] === "string" ? body["signature"] : "";
49417
+ if (!Number.isInteger(selectedIndex) || selectedIndex < 0 || selectedIndex > 8) return {
49418
+ body: { error: "selectedIndex must be an integer 0-8" },
49419
+ status: 400
49420
+ };
49421
+ if (!signature) return {
49422
+ body: { error: "signature is required" },
49423
+ status: 400
49424
+ };
49425
+ const numericId = Number(rawId);
49426
+ const conv = !Number.isNaN(numericId) && /^\d+$/.test(rawId) ? getConversationById(numericId) : getConversationByName(rawId);
49427
+ if (!conv) return {
49428
+ body: { error: "Conversation not found" },
49429
+ status: 404
49430
+ };
49431
+ if (!isClaudeCodeConversation(conv)) return {
49432
+ body: { error: "Not a Claude Code conversation" },
49433
+ status: 400
49434
+ };
49435
+ if (!await (deps.sessionExists ?? tmuxSessionExists)(conv.tmuxSession)) return {
49436
+ body: { error: "Conversation session is not running" },
49437
+ status: 409
49438
+ };
49439
+ const capture = deps.capture ?? ((sessionName, lines) => capturePaneText(sessionName, lines));
49440
+ const sendKey = deps.sendKey ?? ((sessionName, key) => Effect.runPromise(sendRawKeystroke(sessionName, key, "pane-choice")));
49441
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
49442
+ const menu = parsePaneChoiceMenu(await capture(conv.tmuxSession, PANE_CAPTURE_LINES));
49443
+ if (!menu) return {
49444
+ body: {
49445
+ error: "The choice menu is no longer on screen",
49446
+ code: "menu-gone"
49447
+ },
49448
+ status: 409
49449
+ };
49450
+ if (paneChoiceMenuSignature(menu) !== signature) return {
49451
+ body: {
49452
+ error: "The choice menu changed since the card was rendered — refresh and re-answer",
49453
+ code: "menu-changed"
49454
+ },
49455
+ status: 409
49456
+ };
49457
+ if (selectedIndex >= menu.options.length) return {
49458
+ body: { error: `selectedIndex out of range (0-${menu.options.length - 1})` },
49459
+ status: 400
49460
+ };
49461
+ const keys = buildChoiceKeystrokes(menu, selectedIndex);
49462
+ for (const key of keys) {
49463
+ await sendKey(conv.tmuxSession, key);
49464
+ await sleep(KEYSTROKE_GAP_MS);
49465
+ }
49466
+ await sleep(DELIVERY_CONFIRM_WAIT_MS);
49467
+ const after = parsePaneChoiceMenu(await capture(conv.tmuxSession, PANE_CAPTURE_LINES));
49468
+ if (after && paneChoiceMenuSignature(after) === signature) return {
49469
+ body: {
49470
+ error: "Keystrokes were sent but the menu is still on screen — answer it from the terminal",
49471
+ code: "delivery-unconfirmed"
49472
+ },
49473
+ status: 409
49474
+ };
49475
+ return { body: {
49476
+ ok: true,
49477
+ answeredLabel: menu.options[selectedIndex].label
49478
+ } };
49479
+ } catch (error) {
49480
+ const msg = error instanceof Error ? error.message : String(error);
49481
+ console.error("[conversations] pane-choice answer failed:", msg);
49482
+ return {
49483
+ body: { error: "Internal server error" },
49484
+ status: 500
49485
+ };
49486
+ }
49487
+ }
49488
+ //#endregion
48899
49489
  //#region ../../lib/overdeck/conversation-reads.ts
48900
49490
  init_concurrency$1();
48901
49491
  init_behavior();
@@ -48907,7 +49497,6 @@ init_conversation_service();
48907
49497
  init_pi_conversation_parser();
48908
49498
  init_ohmypi_conversation_parser();
48909
49499
  init_codex_conversation_parser();
48910
- init_conversation_compaction();
48911
49500
  function result(body, status) {
48912
49501
  return status === void 0 ? { body } : {
48913
49502
  body,
@@ -49130,13 +49719,16 @@ async function getConversationsPendingInputFeed(deps) {
49130
49719
  const codex = await codexConversationPendingInput(conv, true, lastActivityAt ?? (/* @__PURE__ */ new Date()).toISOString());
49131
49720
  if (codex.approval) pending = codex.approval;
49132
49721
  }
49133
- if (!pending && !pendingPlan) return null;
49722
+ let paneChoice = null;
49723
+ if (!pending && !pendingPlan) paneChoice = await claudeConversationPaneChoice(conv);
49724
+ if (!pending && !pendingPlan && !paneChoice) return null;
49134
49725
  return {
49135
49726
  name: conv.name,
49136
49727
  title: conv.title ?? null,
49137
49728
  issueId: conv.issueId ?? null,
49138
49729
  ...pending ? { pendingAskUserQuestion: pending } : {},
49139
- ...pendingPlan ? { pendingProposedPlan: pendingPlan } : {}
49730
+ ...pendingPlan ? { pendingProposedPlan: pendingPlan } : {},
49731
+ ...paneChoice ? { pendingPaneChoice: paneChoice } : {}
49140
49732
  };
49141
49733
  })), 8))).filter((row) => row !== null));
49142
49734
  } catch (error) {
@@ -49182,6 +49774,14 @@ async function getConversationRead(rawId, deps) {
49182
49774
  if (codex.approval) pendingAskUserQuestion = codex.approval;
49183
49775
  }
49184
49776
  }
49777
+ let pendingPaneChoice = null;
49778
+ if (sessionAlive && pendingInputCount === 0) {
49779
+ pendingPaneChoice = await claudeConversationPaneChoice(conv);
49780
+ if (pendingPaneChoice) {
49781
+ pendingInputKinds = [...pendingInputKinds, "paneChoice"];
49782
+ pendingInputCount = pendingInputKinds.length;
49783
+ }
49784
+ }
49185
49785
  return result({
49186
49786
  ...conv,
49187
49787
  sessionAlive,
@@ -49191,6 +49791,7 @@ async function getConversationRead(rawId, deps) {
49191
49791
  pendingInputCount,
49192
49792
  pendingInputKinds,
49193
49793
  pendingAskUserQuestion,
49794
+ ...pendingPaneChoice ? { pendingPaneChoice } : {},
49194
49795
  transcriptMissing: conversationTranscriptMissing(conv, sessionAlive, convSf),
49195
49796
  needsTerminal: await conversationNeedsTerminal(conv, sessionAlive, convSf)
49196
49797
  });
@@ -49483,8 +50084,6 @@ async function getConversationAbout(name, forceRefresh, deps) {
49483
50084
  }
49484
50085
  //#endregion
49485
50086
  //#region ../../lib/overdeck/conversation-list.ts
49486
- init_transcript_adapter();
49487
- init_summary_fork();
49488
50087
  init_agents$1();
49489
50088
  init_concurrency$1();
49490
50089
  init_behavior();
@@ -49912,7 +50511,35 @@ async function handleConversationMessage(name, body, deps = {
49912
50511
  const message = typeof body["message"] === "string" ? body["message"].trim() : "";
49913
50512
  if (!message) return jsonResponse({ error: "Message is required" }, { status: 400 });
49914
50513
  if (message.length > MAX_MESSAGE_LENGTH) return jsonResponse({ error: `Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters` }, { status: 400 });
49915
- if (getHarnessBehavior(conv.harness).transcriptKind === "claude-jsonl" && shouldInterceptManualCompact(message)) {
50514
+ try {
50515
+ const result = await handleComposerCommandMessage({
50516
+ message,
50517
+ confirmation: composerCommandConfirmationFromBody(body),
50518
+ target: {
50519
+ kind: "conversation",
50520
+ id: conv.name,
50521
+ harness: conv.harness ?? "claude-code",
50522
+ cwd: conv.cwd,
50523
+ issueId: conv.issueId ?? void 0
50524
+ }
50525
+ });
50526
+ if (result !== null) return jsonResponse(result, { status: composerCommandResultHttpStatus(result) });
50527
+ } catch (error) {
50528
+ if (error instanceof ComposerCommandConfirmationError) return jsonResponse({
50529
+ error: error.message,
50530
+ code: error.code
50531
+ }, { status: 422 });
50532
+ if (!(error instanceof ComposerCommandParseError)) throw error;
50533
+ const status = error.code === "unknown-command" ? 404 : error.code === "unknown-flag" ? 422 : 400;
50534
+ return jsonResponse({
50535
+ error: error.message,
50536
+ code: error.code,
50537
+ token: error.token,
50538
+ expected: error.expected
50539
+ }, { status });
50540
+ }
50541
+ const interceptManualCompact = deps.shouldInterceptManualCompact ?? shouldInterceptManualCompact;
50542
+ if (getHarnessBehavior(conv.harness).transcriptKind === "claude-jsonl" && interceptManualCompact(message)) {
49916
50543
  const compactSessionFile = await deps.resolveSessionFile(conv);
49917
50544
  if (!compactSessionFile || !existsSync$1(compactSessionFile)) return jsonResponse({ error: `No session file found for conversation ${conv.name}` }, { status: 400 });
49918
50545
  const compactResult = await compactConversationNative(compactSessionFile, conv.name);
@@ -49951,7 +50578,7 @@ async function handleConversationMessage(name, body, deps = {
49951
50578
  const effectiveAttachmentPaths = partition.effectiveAttachmentPaths;
49952
50579
  const droppedImageCount = partition.droppedImageCount;
49953
50580
  if (droppedImageCount > 0 && !outboundMessage && effectiveAttachmentPaths.length === 0) return jsonResponse({ error: `${conv.model ?? "This model"} can't read images. Switch to a vision-capable model (e.g. mimo-v2.5) to send images.` }, { status: 422 });
49954
- let deliveredMessage = transformMessageForHarness(outboundMessage, harness, effectiveAttachmentPaths);
50581
+ let deliveredMessage = (deps.transformMessageForHarness ?? transformMessageForHarness)(outboundMessage, harness, effectiveAttachmentPaths);
49955
50582
  if (behavior.injectsPromptTimeMemory) deliveredMessage = await injectPiConversationMemory({
49956
50583
  cwd: conv.cwd,
49957
50584
  issueId: conv.issueId,
@@ -50352,7 +50979,7 @@ async function refreshMergeStateFromGitHub(issueId, repo, prNumber) {
50352
50979
  let mergeState;
50353
50980
  let isDraft;
50354
50981
  let checksFailed;
50355
- const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-ClZ2lk8D.js");
50982
+ const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-DtAsvwzK.js");
50356
50983
  if (isGitHubAppConfigured()) {
50357
50984
  const prState = await Effect.runPromise(getPullRequestState(owner, repoName, prNumber));
50358
50985
  mergeable = prState.mergeable === null ? "UNKNOWN" : prState.mergeable ? "MERGEABLE" : "CONFLICTING";
@@ -50503,8 +51130,8 @@ async function handlePullRequestPromise(payload) {
50503
51130
  bumpIssuePrTabCacheGeneration(issueId);
50504
51131
  const repo = payload.repository.full_name;
50505
51132
  if (payload.action === "closed" && pr.merged === true) try {
50506
- const { postMergeLifecycle } = await import("./merge-agent-CKkgIBPB.js");
50507
- const { resolveProjectFromIssueSync } = await import("./projects-vjjcmiN8.js");
51133
+ const { postMergeLifecycle } = await import("./merge-agent-DrXa1CpW.js");
51134
+ const { resolveProjectFromIssueSync } = await import("./projects-B03Wr-xc.js");
50508
51135
  const project = resolveProjectFromIssueSync(issueId);
50509
51136
  if (project) {
50510
51137
  const branchName = pr.head.ref;
@@ -51351,7 +51978,7 @@ startConversationModelBackfill(resolveSessionFile);
51351
51978
  function conversationReadJson(response) {
51352
51979
  return jsonResponse(response.body, response.status === void 0 ? void 0 : { status: response.status });
51353
51980
  }
51354
- const readJsonBody$8 = Effect.gen(function* () {
51981
+ const readJsonBody$9 = Effect.gen(function* () {
51355
51982
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
51356
51983
  try {
51357
51984
  return text ? JSON.parse(text) : {};
@@ -51411,7 +52038,7 @@ const postConversationRoute = HttpRouter.add("POST", "/api/conversations", Effec
51411
52038
  const request = yield* HttpServerRequest.HttpServerRequest;
51412
52039
  const originCheck = validateOrigin$1(request);
51413
52040
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51414
- const body = yield* readJsonBody$8;
52041
+ const body = yield* readJsonBody$9;
51415
52042
  const reqOrigin = getHeader(request, "origin") ?? "none";
51416
52043
  const reqReferer = getHeader(request, "referer") ?? "none";
51417
52044
  const reqUserAgent = getHeader(request, "user-agent") ?? "none";
@@ -51430,14 +52057,14 @@ const postConversationResumeRoute = HttpRouter.add("POST", "/api/conversations/:
51430
52057
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51431
52058
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51432
52059
  const name = (yield* HttpRouter.params)["name"] ?? "";
51433
- const body = yield* readJsonBody$8;
52060
+ const body = yield* readJsonBody$9;
51434
52061
  return yield* Effect.promise(async () => handleConversationResume(name, body, { resolveSessionFile }));
51435
52062
  }));
51436
52063
  const postConversationSwitchModelRoute = HttpRouter.add("POST", "/api/conversations/:name/switch-model", Effect.gen(function* () {
51437
52064
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51438
52065
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51439
52066
  const name = (yield* HttpRouter.params)["name"] ?? "";
51440
- const body = yield* readJsonBody$8;
52067
+ const body = yield* readJsonBody$9;
51441
52068
  return yield* Effect.promise(async () => {
51442
52069
  try {
51443
52070
  return await handleConversationSwitchModel(name, body);
@@ -51452,7 +52079,7 @@ const postConversationThinkingLevelRoute = HttpRouter.add("POST", "/api/conversa
51452
52079
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51453
52080
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51454
52081
  const name = (yield* HttpRouter.params)["name"] ?? "";
51455
- const body = yield* readJsonBody$8;
52082
+ const body = yield* readJsonBody$9;
51456
52083
  return yield* Effect.promise(async () => {
51457
52084
  try {
51458
52085
  return await handleConversationThinkingLevel(name, body);
@@ -51548,7 +52175,7 @@ const postConversationDeleteImageRoute = HttpRouter.add("POST", "/api/conversati
51548
52175
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51549
52176
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51550
52177
  const name = (yield* HttpRouter.params)["name"] ?? "";
51551
- const body = yield* readJsonBody$8;
52178
+ const body = yield* readJsonBody$9;
51552
52179
  return yield* Effect.promise(async () => {
51553
52180
  try {
51554
52181
  return await handleConversationImageDelete(name, body);
@@ -51563,7 +52190,7 @@ const postConversationMessageRoute = HttpRouter.add("POST", "/api/conversations/
51563
52190
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51564
52191
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51565
52192
  const name = (yield* HttpRouter.params)["name"] ?? "";
51566
- const body = yield* readJsonBody$8;
52193
+ const body = yield* readJsonBody$9;
51567
52194
  return yield* Effect.promise(async () => {
51568
52195
  try {
51569
52196
  return await handleConversationMessage(name, body, conversationMessageDependencies);
@@ -51583,14 +52210,24 @@ const postConversationCodexApprovalRoute = HttpRouter.add("POST", "/api/conversa
51583
52210
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51584
52211
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51585
52212
  const rawId = (yield* HttpRouter.params)["id"] ?? "";
51586
- const body = yield* readJsonBody$8;
52213
+ const body = yield* readJsonBody$9;
51587
52214
  return yield* Effect.promise(() => handleConversationCodexApproval(rawId, body));
51588
52215
  }));
52216
+ const postConversationPaneChoiceRoute = HttpRouter.add("POST", "/api/conversations/:id/pane-choice", Effect.gen(function* () {
52217
+ const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
52218
+ if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
52219
+ const rawId = (yield* HttpRouter.params)["id"] ?? "";
52220
+ const body = yield* readJsonBody$9;
52221
+ return yield* Effect.promise(async () => {
52222
+ const result = await handleConversationPaneChoiceAnswer(rawId, body);
52223
+ return jsonResponse(result.body, { status: result.status });
52224
+ });
52225
+ }));
51589
52226
  const postConversationDeliveryMethodRoute = HttpRouter.add("POST", "/api/conversations/:name/delivery-method", Effect.gen(function* () {
51590
52227
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51591
52228
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51592
52229
  const name = (yield* HttpRouter.params)["name"] ?? "";
51593
- const body = yield* readJsonBody$8;
52230
+ const body = yield* readJsonBody$9;
51594
52231
  return yield* Effect.promise(async () => {
51595
52232
  try {
51596
52233
  return await handleConversationDeliveryMethod(name, body);
@@ -51605,7 +52242,7 @@ const postConversationControlAckRoute = HttpRouter.add("POST", "/api/conversatio
51605
52242
  const request = yield* HttpServerRequest.HttpServerRequest;
51606
52243
  const originCheck = validateConversationControlAckOrigin(request.headers, request.method);
51607
52244
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51608
- const body = yield* readJsonBody$8;
52245
+ const body = yield* readJsonBody$9;
51609
52246
  return yield* Effect.promise(async () => {
51610
52247
  try {
51611
52248
  const result = handleConversationControlAck(body);
@@ -51621,7 +52258,7 @@ const patchConversationRoute = HttpRouter.add("PATCH", "/api/conversations/:name
51621
52258
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51622
52259
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51623
52260
  const name = (yield* HttpRouter.params)["name"] ?? "";
51624
- const body = yield* readJsonBody$8;
52261
+ const body = yield* readJsonBody$9;
51625
52262
  return yield* Effect.promise(async () => {
51626
52263
  try {
51627
52264
  const result = patchConversationTitle(name, body);
@@ -51708,14 +52345,14 @@ const postConversationSummaryForkRoute = HttpRouter.add("POST", "/api/conversati
51708
52345
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51709
52346
  const params = yield* HttpRouter.params;
51710
52347
  const name = decodeURIComponent(params["name"] ?? "");
51711
- const body = yield* readJsonBody$8;
52348
+ const body = yield* readJsonBody$9;
51712
52349
  return yield* Effect.promise(() => handleConversationSummaryFork(name, body));
51713
52350
  }));
51714
52351
  const postConversationPlanActionRoute = HttpRouter.add("POST", "/api/conversations/:name/plan-action", Effect.gen(function* () {
51715
52352
  const originCheck = validateOrigin$1(yield* HttpServerRequest.HttpServerRequest);
51716
52353
  if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
51717
52354
  const name = (yield* HttpRouter.params)["name"] ?? "";
51718
- const body = yield* readJsonBody$8;
52355
+ const body = yield* readJsonBody$9;
51719
52356
  return yield* Effect.promise(() => handleConversationPlanAction(name, body));
51720
52357
  }));
51721
52358
  const conversationDiffDependencies = {
@@ -51770,7 +52407,7 @@ const getConversationAboutRoute = HttpRouter.add("GET", "/api/conversations/:nam
51770
52407
  return conversationReadJson(await getConversationAbout(name, new URL(request.url, "http://localhost").searchParams.get("refresh") === "1", conversationReadDependencies));
51771
52408
  });
51772
52409
  }));
51773
- const conversationsRouteLayer = Layer.mergeAll(getConversationsRoute, getConversationsPendingInputRoute, getArchivedConversationsRoute, getConversationRoute, getConversationHandoffDocRoute, postConversationRoute, patchConversationRoute, deleteConversationRoute, postConversationStopRoute, postConversationResumeRoute, postConversationSwitchModelRoute, postConversationThinkingLevelRoute, postConversationCompactRoute, postConversationAbortRoute, postConversationRestartAllRoute, postConversationArchiveRoute, postConversationUnarchiveRoute, getConversationMessagesRoute, getConversationMessageLocatorRoute, postConversationUploadImageRoute, postConversationDeleteImageRoute, postConversationMessageRoute, postConversationCodexApprovalRoute, postConversationDeliveryMethodRoute, postConversationControlAckRoute, postConversationFavoriteRoute, deleteConversationFavoriteRoute, postConversationSummaryForkRoute, postConversationPlanActionRoute, getConversationDiffsRoute, getConversationDiffFullRoute, getConversationDiffTurnRoute, postConversationRetitleRoute, getConversationAboutRoute);
52410
+ const conversationsRouteLayer = Layer.mergeAll(getConversationsRoute, getConversationsPendingInputRoute, getArchivedConversationsRoute, getConversationRoute, getConversationHandoffDocRoute, postConversationRoute, patchConversationRoute, deleteConversationRoute, postConversationStopRoute, postConversationResumeRoute, postConversationSwitchModelRoute, postConversationThinkingLevelRoute, postConversationCompactRoute, postConversationAbortRoute, postConversationRestartAllRoute, postConversationArchiveRoute, postConversationUnarchiveRoute, getConversationMessagesRoute, getConversationMessageLocatorRoute, postConversationUploadImageRoute, postConversationDeleteImageRoute, postConversationMessageRoute, postConversationCodexApprovalRoute, postConversationPaneChoiceRoute, postConversationDeliveryMethodRoute, postConversationControlAckRoute, postConversationFavoriteRoute, deleteConversationFavoriteRoute, postConversationSummaryForkRoute, postConversationPlanActionRoute, getConversationDiffsRoute, getConversationDiffFullRoute, getConversationDiffTurnRoute, postConversationRetitleRoute, getConversationAboutRoute);
51774
52411
  //#endregion
51775
52412
  //#region routes/show.ts
51776
52413
  /**
@@ -51817,7 +52454,7 @@ const showRouteLayer = Layer.mergeAll(getShowRoute, getShowShadowRoute, getShowH
51817
52454
  //#endregion
51818
52455
  //#region routes/project-rename.ts
51819
52456
  init_projects();
51820
- const readJsonBody$7 = Effect.gen(function* () {
52457
+ const readJsonBody$8 = Effect.gen(function* () {
51821
52458
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
51822
52459
  try {
51823
52460
  return text ? JSON.parse(text) : {};
@@ -51829,7 +52466,7 @@ const postProjectRenameRoute = HttpRouter.add("POST", "/api/projects/:projectKey
51829
52466
  const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
51830
52467
  if (authError) return authError;
51831
52468
  const projectIdentifier = (yield* HttpRouter.params)["projectKey"] ?? "";
51832
- const body = yield* readJsonBody$7;
52469
+ const body = yield* readJsonBody$8;
51833
52470
  if (typeof body.name !== "string") return jsonResponse({ error: "Project name must be a string" }, { status: 400 });
51834
52471
  const result = yield* Effect.result(renameProject(projectIdentifier, body.name));
51835
52472
  if (Result$1.isFailure(result)) {
@@ -51848,9 +52485,8 @@ const postProjectRenameRoute = HttpRouter.add("POST", "/api/projects/:projectKey
51848
52485
  * No chalk, no console, no execSync. Import and use from API routes or CLI.
51849
52486
  */
51850
52487
  init_projects();
51851
- init_paths();
51852
52488
  init_context_layers();
51853
- const BUNDLED_HOOKS_DIR = SYNC_SOURCES.gitHooks;
52489
+ init_git_hooks();
51854
52490
  var DuplicateProjectError = class extends Error {
51855
52491
  constructor(key, existingPath) {
51856
52492
  super(`Project already registered with key: ${key} (path: ${existingPath})`);
@@ -51859,31 +52495,6 @@ var DuplicateProjectError = class extends Error {
51859
52495
  this.name = "DuplicateProjectError";
51860
52496
  }
51861
52497
  };
51862
- function installGitHooksInDir(gitDir) {
51863
- const hooksTarget = join(gitDir, "hooks");
51864
- let installed = 0;
51865
- if (!existsSync$1(hooksTarget)) mkdirSync$1(hooksTarget, { recursive: true });
51866
- if (!existsSync$1(BUNDLED_HOOKS_DIR)) return 0;
51867
- try {
51868
- const hooks = readdirSync$1(BUNDLED_HOOKS_DIR).filter((f) => {
51869
- const p = join(BUNDLED_HOOKS_DIR, f);
51870
- return existsSync$1(p) && statSync$1(p).isFile();
51871
- });
51872
- for (const hook of hooks) {
51873
- const source = join(BUNDLED_HOOKS_DIR, hook);
51874
- const target = join(hooksTarget, hook);
51875
- if (existsSync$1(target)) {
51876
- try {
51877
- if (readlinkSync(target) === source) continue;
51878
- } catch {}
51879
- renameSync$1(target, `${target}.backup`);
51880
- }
51881
- symlinkSync$1(source, target);
51882
- installed++;
51883
- }
51884
- } catch {}
51885
- return installed;
51886
- }
51887
52498
  /**
51888
52499
  * Register a project from a filesystem path.
51889
52500
  *
@@ -51904,7 +52515,7 @@ async function registerProjectFromPath(opts) {
51904
52515
  registerProjectSync(key, projectConfig);
51905
52516
  const seededContextLayer = ensureProjectLayer(fullPath);
51906
52517
  try {
51907
- const { preTrustDirectorySync } = await import("./workspace-manager-CWT55qeI.js");
52518
+ const { preTrustDirectorySync } = await import("./workspace-manager-CDTKjjdU.js");
51908
52519
  preTrustDirectorySync(fullPath);
51909
52520
  } catch {}
51910
52521
  let hooksInstalled = 0;
@@ -51943,9 +52554,9 @@ init_pan_dir();
51943
52554
  init_io();
51944
52555
  init_specs();
51945
52556
  init_paths();
51946
- const execAsync$2 = promisify(exec);
52557
+ const execAsync$3 = promisify(exec);
51947
52558
  async function getIssueDataService() {
51948
- const { getSharedIssueService } = await import("./issue-service-singleton-BvJ09SaR.js");
52559
+ const { getSharedIssueService } = await import("./issue-service-singleton-PMx8F1jr.js");
51949
52560
  return getSharedIssueService();
51950
52561
  }
51951
52562
  /** Returns true if the path exists (any type). */
@@ -52237,8 +52848,9 @@ async function collectSessionTreeNodes(issueId, workspacePath, projectPath, cont
52237
52848
  if (latestMerge) {
52238
52849
  const shipSessionName = `agent-${issueLower}-ship`;
52239
52850
  const shipIsLive = context.tmuxSessionNames.has(shipSessionName);
52240
- const shipJsonlPath = await resolveJsonlPath(shipSessionName, workspacePath);
52241
- sections.push({
52851
+ const shipState = getAgentStateSync(shipSessionName);
52852
+ const shipJsonlPath = shipIsLive || shipState ? await resolveJsonlPath(shipSessionName, workspacePath) : null;
52853
+ if (shipIsLive || shipJsonlPath) sections.push({
52242
52854
  type: "ship",
52243
52855
  sessionId: shipSessionName,
52244
52856
  model: "specialist",
@@ -52321,11 +52933,22 @@ async function fetchProjectSessionTree(projectKey, sharedContext) {
52321
52933
  const features = [];
52322
52934
  const issueTitles = sharedContext?.issueTitles ?? await buildIssueTitleMap();
52323
52935
  if (await pathExists$1(workspacesDir)) {
52324
- const featureCandidates = (await readdir(workspacesDir, { withFileTypes: true }).catch(() => [])).filter((e) => e.isDirectory() && e.name.startsWith("feature-")).map((e) => ({
52325
- name: e.name,
52326
- issueLower: e.name.replace("feature-", ""),
52327
- issueId: e.name.replace("feature-", "").toUpperCase()
52328
- })).filter((c) => /^[a-z]+-\d+$/.test(c.issueLower));
52936
+ const entries = await readdir(workspacesDir, { withFileTypes: true }).catch(() => []);
52937
+ const candidateIssueLowers = /* @__PURE__ */ new Set();
52938
+ for (const entry of entries) {
52939
+ if (!entry.isDirectory()) continue;
52940
+ const match = /^feature-([a-z]+-\d+)(?:-strike|-slot-\d+)?$/.exec(entry.name);
52941
+ if (match) candidateIssueLowers.add(match[1]);
52942
+ }
52943
+ for (const issueLower of liveTmuxIssueIds) {
52944
+ if (candidateIssueLowers.has(issueLower)) continue;
52945
+ if (resolveProjectFromIssueSync(issueLower.toUpperCase())?.projectKey === project.key) candidateIssueLowers.add(issueLower);
52946
+ }
52947
+ const featureCandidates = [...candidateIssueLowers].map((issueLower) => ({
52948
+ name: `feature-${issueLower}`,
52949
+ issueLower,
52950
+ issueId: issueLower.toUpperCase()
52951
+ }));
52329
52952
  const results = await Effect.runPromise(withConcurrencyLimit(featureCandidates.map((c) => Effect.promise(async () => {
52330
52953
  const agentDir = join(getOverdeckHome(), "agents", `agent-${c.issueLower}`);
52331
52954
  const planningAgentDir = join(getOverdeckHome(), "agents", `planning-${c.issueLower}`);
@@ -52403,7 +53026,7 @@ const getProjectReleaseStatusRoute = HttpRouter.add("GET", "/api/projects/:proje
52403
53026
  const projectKey = (yield* HttpRouter.params)["projectKey"] ?? "";
52404
53027
  return jsonResponse(yield* Effect.tryPromise({
52405
53028
  try: async () => {
52406
- const { fetchProjectReleaseStatus } = await import("./project-pipelines-C0MWUeCU.js");
53029
+ const { fetchProjectReleaseStatus } = await import("./project-pipelines-Bim6bThI.js");
52407
53030
  return fetchProjectReleaseStatus(projectKey);
52408
53031
  },
52409
53032
  catch: (err) => new Error(err instanceof Error ? err.message : String(err))
@@ -52613,7 +53236,7 @@ const postProjectsRoute = HttpRouter.add("POST", "/api/projects", httpHandler(Ef
52613
53236
  if ((await readdir(target).catch(() => [])).length > 0) return jsonResponse({ error: `target directory already exists and is non-empty: ${target}` }, { status: 409 });
52614
53237
  }
52615
53238
  await mkdir(target, { recursive: true });
52616
- await execAsync$2("git init", { cwd: target });
53239
+ await execAsync$3("git init", { cwd: target });
52617
53240
  try {
52618
53241
  const result = await registerProjectFromPath({
52619
53242
  path: target,
@@ -52638,6 +53261,271 @@ const postProjectsRoute = HttpRouter.add("POST", "/api/projects", httpHandler(Ef
52638
53261
  })));
52639
53262
  const projectsRouteLayer = Layer.mergeAll(getProjectSessionTreeRoute, getAllSessionTreesRoute, getProjectReleaseStatusRoute, getProjectAutoMergeDefaultRoute, postProjectAutoMergeDefaultRoute, postProjectRenameRoute, getProjectSwarmPolicyRoute, postProjectSwarmPolicyRoute, getIssueSwarmPolicyRoute, postIssueSwarmPolicyRoute, getIssueStaffingRoute, postIssueStaffingRoute, postProjectsRoute);
52640
53263
  //#endregion
53264
+ //#region routes/projects-merge-train.ts
53265
+ /** PAN-1696: Per-project merge-train override endpoints (GET/POST) */
53266
+ init_projects();
53267
+ const getProjectMergeTrainRoute = HttpRouter.add("GET", "/api/projects/:projectKey/merge-train", httpHandler(Effect.gen(function* () {
53268
+ const config = getProjectSync((yield* HttpRouter.params)["projectKey"] ?? "");
53269
+ if (!config) return jsonResponse({ error: "Project not found" }, { status: 404 });
53270
+ const { isMergeTrainEnabledForProject } = yield* Effect.promise(() => import("./merge-sync-Ca7F5JnQ.js"));
53271
+ return jsonResponse({
53272
+ value: config.merge_train ?? null,
53273
+ effective: isMergeTrainEnabledForProject(config)
53274
+ });
53275
+ })));
53276
+ const postProjectMergeTrainRoute = HttpRouter.add("POST", "/api/projects/:projectKey/merge-train", httpHandler(Effect.gen(function* () {
53277
+ const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
53278
+ if (authError) return authError;
53279
+ const key = (yield* HttpRouter.params)["projectKey"] ?? "";
53280
+ if (!getProjectSync(key)) return jsonResponse({ error: "Project not found" }, { status: 404 });
53281
+ const v = (yield* readProjectJsonBody).value;
53282
+ if (v !== "enabled" && v !== "disabled" && v !== null) return jsonResponse({ error: "value must be 'enabled', 'disabled', or null" }, { status: 400 });
53283
+ const { isMergeTrainEnabledForProject } = yield* Effect.promise(() => import("./merge-sync-Ca7F5JnQ.js"));
53284
+ setProjectMergeTrainSync(key, v);
53285
+ const updated = getProjectSync(key);
53286
+ return jsonResponse({
53287
+ value: v,
53288
+ effective: updated ? isMergeTrainEnabledForProject(updated) : false
53289
+ });
53290
+ })));
53291
+ const projectsMergeTrainRouteLayer = Layer.mergeAll(getProjectMergeTrainRoute, postProjectMergeTrainRoute);
53292
+ //#endregion
53293
+ //#region routes/merge-train.ts
53294
+ /**
53295
+ * PAN-1696 merge-train-routes: the aggregate `/api/merge-train/*` namespace.
53296
+ *
53297
+ * The merge-train (conflict-aware merge order + UAT batch trains) is a
53298
+ * per-project pipeline concern, but its only HTTP surface used to live under
53299
+ * `/api/flywheel/*` and answered for the Overdeck repo alone. These routes
53300
+ * answer for EVERY tracked project and require no active flywheel run: the
53301
+ * ready set comes from the review-status records via
53302
+ * `listEligibleCandidatesByProject`, not from a run's `activePipeline`.
53303
+ *
53304
+ * The legacy `/api/flywheel/*` merge-train routes stay in place until the
53305
+ * frontend migrates — removing them is the remove-legacy-routes item.
53306
+ */
53307
+ init_projects();
53308
+ const readUnknownJsonBody$1 = Effect.gen(function* () {
53309
+ const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
53310
+ try {
53311
+ return {
53312
+ ok: true,
53313
+ body: text ? JSON.parse(text) : {}
53314
+ };
53315
+ } catch {
53316
+ return {
53317
+ ok: false,
53318
+ error: "Request body must be valid JSON"
53319
+ };
53320
+ }
53321
+ });
53322
+ /** A plain JSON object — not null, not an array, not a primitive. */
53323
+ function isJsonObject(value) {
53324
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53325
+ }
53326
+ /**
53327
+ * A disabled project reports an empty queue rather than being omitted, so the
53328
+ * multi-project view can render it as an explicitly-off row instead of making
53329
+ * "off" and "no ready work" look identical.
53330
+ */
53331
+ async function queueEntryForProject(key, config, enabled) {
53332
+ const base = {
53333
+ projectKey: key,
53334
+ projectName: config.name,
53335
+ enabled
53336
+ };
53337
+ if (!enabled) return {
53338
+ ...base,
53339
+ queue: []
53340
+ };
53341
+ const projectPath = resolve(config.path);
53342
+ const candidates = listEligibleCandidatesByProject(projectPath);
53343
+ if (candidates.length === 0) return {
53344
+ ...base,
53345
+ queue: []
53346
+ };
53347
+ const queue = await Effect.runPromise(computeMergeQueueFromCandidates(candidates, projectPath).pipe(Effect.provide(layer)));
53348
+ return {
53349
+ ...base,
53350
+ queue
53351
+ };
53352
+ }
53353
+ /**
53354
+ * PAN-1696 AC1: one entry per tracked project, with that project's effective
53355
+ * flag and queue, with no flywheel run active. One project's git failure must
53356
+ * not blank the whole response, so a failed project reports an empty queue.
53357
+ */
53358
+ async function getMergeTrainQueuesPayload() {
53359
+ const { isMergeTrainEnabledForProject } = await import("./merge-sync-Ca7F5JnQ.js");
53360
+ const projects = listProjectsSync();
53361
+ return (await Promise.allSettled(projects.map(({ key, config }) => queueEntryForProject(key, config, isMergeTrainEnabledForProject(config))))).flatMap((outcome, i) => {
53362
+ if (outcome.status === "fulfilled") return [outcome.value];
53363
+ const entry = projects[i];
53364
+ if (!entry) return [];
53365
+ const reason = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);
53366
+ console.warn(`[merge-train] queue for project ${entry.key} failed: ${reason}`);
53367
+ return [{
53368
+ projectKey: entry.key,
53369
+ projectName: entry.config.name,
53370
+ enabled: true,
53371
+ queue: []
53372
+ }];
53373
+ });
53374
+ }
53375
+ /** PAN-1696 AC2: per-project generation chains across every tracked project. */
53376
+ async function getMergeTrainGenerationsPayload() {
53377
+ const { isMergeTrainEnabledForProject } = await import("./merge-sync-Ca7F5JnQ.js");
53378
+ const { getUatGenerationsPayload } = await import("./uat-train-Cu-4nvbf.js");
53379
+ return (await Promise.allSettled(listProjectsSync().map(async ({ key, config }) => ({
53380
+ projectKey: key,
53381
+ projectName: config.name,
53382
+ enabled: isMergeTrainEnabledForProject(config),
53383
+ generations: await getUatGenerationsPayload(resolve(config.path))
53384
+ })))).flatMap((outcome) => outcome.status === "fulfilled" ? [outcome.value] : []);
53385
+ }
53386
+ async function defaultOrderedIssueIdsForProject(projectPath) {
53387
+ const candidates = listEligibleCandidatesByProject(projectPath);
53388
+ if (candidates.length === 0) return [];
53389
+ return (await Effect.runPromise(computeMergeQueueFromCandidates(candidates, projectPath).pipe(Effect.provide(layer)))).map((item) => item.issueId);
53390
+ }
53391
+ async function defaultMergeOne(issueId) {
53392
+ const { triggerMerge } = await import("./merge-ops-DdpTiEMt.js");
53393
+ const r = await triggerMerge(issueId);
53394
+ return r.success ? { ok: true } : {
53395
+ ok: false,
53396
+ reason: r.error ?? r.message ?? "merge failed"
53397
+ };
53398
+ }
53399
+ async function postMergeTrainMergeNextPayload(payload, deps = {}) {
53400
+ if (!isJsonObject(payload)) return {
53401
+ status: 400,
53402
+ body: { error: "body must be a JSON object: { n, project }" }
53403
+ };
53404
+ const body = payload;
53405
+ const n = typeof body.n === "number" && Number.isFinite(body.n) ? Math.floor(body.n) : 0;
53406
+ if (n <= 0) return {
53407
+ status: 400,
53408
+ body: { error: "n must be a positive integer" }
53409
+ };
53410
+ if (typeof body.project !== "string" || body.project.trim() === "") return {
53411
+ status: 400,
53412
+ body: { error: "project must be a non-empty string" }
53413
+ };
53414
+ const projectKey = body.project.trim();
53415
+ const config = getProjectSync(projectKey);
53416
+ if (!config) return {
53417
+ status: 404,
53418
+ body: { error: `Unknown project key: ${projectKey}` }
53419
+ };
53420
+ const { isMergeTrainEnabledForProject } = await import("./merge-sync-Ca7F5JnQ.js");
53421
+ if (!isMergeTrainEnabledForProject(config)) return {
53422
+ status: 409,
53423
+ body: { error: `merge-train is disabled for project ${projectKey}` }
53424
+ };
53425
+ const issueIds = (await (deps.getOrderedIssueIds ?? defaultOrderedIssueIdsForProject)(resolve(config.path))).slice(0, n);
53426
+ const { shipMergeBatch } = await import("./merge-batch-C5q1c1GX.js");
53427
+ return {
53428
+ status: 200,
53429
+ body: {
53430
+ projectKey,
53431
+ outcomes: await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne })
53432
+ }
53433
+ };
53434
+ }
53435
+ /**
53436
+ * PAN-1696: forced reconcile. With `{ project }` it rebuilds that one project's
53437
+ * generation; with no body it reconciles every merge-train-enabled project.
53438
+ */
53439
+ async function postMergeTrainAssemblePayload(payload) {
53440
+ if (!isJsonObject(payload)) return {
53441
+ status: 400,
53442
+ body: { error: "body must be a JSON object: {} for all projects, or { project }" }
53443
+ };
53444
+ if ("project" in payload) {
53445
+ const raw = payload["project"];
53446
+ if (typeof raw !== "string" || raw.trim() === "") return {
53447
+ status: 400,
53448
+ body: { error: "project must be a non-empty string; omit the field entirely to reconcile every project" }
53449
+ };
53450
+ const projectKey = raw.trim();
53451
+ const config = getProjectSync(projectKey);
53452
+ if (!config) return {
53453
+ status: 404,
53454
+ body: { error: `Unknown project key: ${projectKey}` }
53455
+ };
53456
+ const { runUatTrainReconcile } = await import("./uat-train-Cu-4nvbf.js");
53457
+ return {
53458
+ status: 200,
53459
+ body: { projects: [{
53460
+ projectKey,
53461
+ result: await runUatTrainReconcile({
53462
+ force: true,
53463
+ projectRoot: resolve(config.path)
53464
+ })
53465
+ }] }
53466
+ };
53467
+ }
53468
+ const { runUatTrainReconcileAllProjects } = await import("./uat-train-Cu-4nvbf.js");
53469
+ return {
53470
+ status: 200,
53471
+ body: { projects: await runUatTrainReconcileAllProjects({ force: true }) }
53472
+ };
53473
+ }
53474
+ /**
53475
+ * PAN-1737 generation names contain a slash (`uat/pan-otter-0610`); URL params
53476
+ * carry the name WITHOUT the `uat/` prefix and handlers reconstitute it. Same
53477
+ * reconstruction as the legacy flywheel routes so links stay interchangeable.
53478
+ */
53479
+ function uatGenerationNameFromParam(param) {
53480
+ const decoded = decodeURIComponent(param);
53481
+ return decoded.startsWith("uat/") ? decoded : `uat/${decoded}`;
53482
+ }
53483
+ const getMergeTrainQueuesRoute = HttpRouter.add("GET", "/api/merge-train/queues", httpHandler(Effect.gen(function* () {
53484
+ return jsonResponse(yield* Effect.promise(() => getMergeTrainQueuesPayload()));
53485
+ })));
53486
+ const getMergeTrainGenerationsRoute = HttpRouter.add("GET", "/api/merge-train/generations", httpHandler(Effect.gen(function* () {
53487
+ return jsonResponse(yield* Effect.promise(() => getMergeTrainGenerationsPayload()));
53488
+ })));
53489
+ const postMergeTrainGenerationStackRoute = HttpRouter.add("POST", "/api/merge-train/generations/:name/stack", httpHandler(Effect.gen(function* () {
53490
+ const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
53491
+ if (authError) return authError;
53492
+ const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
53493
+ const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-Cu-4nvbf.js"));
53494
+ const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));
53495
+ if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });
53496
+ return jsonResponse({
53497
+ frontendUrl: result.frontendUrl,
53498
+ evicted: result.evicted
53499
+ });
53500
+ })));
53501
+ const postMergeTrainGenerationPromoteRoute = HttpRouter.add("POST", "/api/merge-train/generations/:name/promote", httpHandler(Effect.gen(function* () {
53502
+ const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
53503
+ if (authError) return authError;
53504
+ const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
53505
+ const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-Cu-4nvbf.js"));
53506
+ const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-JHsdU3pA.js"));
53507
+ const result = yield* Effect.promise(() => postUatGenerationPromotePayload(name, firePostMergeLifecycle));
53508
+ if (!result.success) return jsonResponse(result, { status: result.reason === "not-found" ? 404 : result.reason === "merge-failed" ? 500 : 409 });
53509
+ return jsonResponse(result);
53510
+ })));
53511
+ const postMergeTrainAssembleRoute = HttpRouter.add("POST", "/api/merge-train/assemble", httpHandler(Effect.gen(function* () {
53512
+ const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
53513
+ if (authError) return authError;
53514
+ const parsed = yield* readUnknownJsonBody$1;
53515
+ if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });
53516
+ const result = yield* Effect.promise(() => postMergeTrainAssemblePayload(parsed.body));
53517
+ return jsonResponse(result.body, { status: result.status });
53518
+ })));
53519
+ const postMergeTrainMergeNextRoute = HttpRouter.add("POST", "/api/merge-train/merge-next", httpHandler(Effect.gen(function* () {
53520
+ const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
53521
+ if (authError) return authError;
53522
+ const parsed = yield* readUnknownJsonBody$1;
53523
+ if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });
53524
+ const result = yield* Effect.promise(() => postMergeTrainMergeNextPayload(parsed.body));
53525
+ return jsonResponse(result.body, { status: result.status });
53526
+ })));
53527
+ const mergeTrainRouteLayer = Layer.mergeAll(getMergeTrainQueuesRoute, getMergeTrainGenerationsRoute, postMergeTrainGenerationStackRoute, postMergeTrainGenerationPromoteRoute, postMergeTrainAssembleRoute, postMergeTrainMergeNextRoute);
53528
+ //#endregion
52641
53529
  //#region routes/context.ts
52642
53530
  init_src();
52643
53531
  init_layers();
@@ -53037,7 +53925,7 @@ function issueIdFromWorkspacePath(workspacePath) {
53037
53925
  const match = /(?:^|[^a-z0-9])([a-z]+-\d+)(?:[^a-z0-9]|$)/i.exec(workspacePath);
53038
53926
  return match ? match[1].toUpperCase() : null;
53039
53927
  }
53040
- function readJsonBody$6() {
53928
+ function readJsonBody$7() {
53041
53929
  return Effect.gen(function* () {
53042
53930
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
53043
53931
  return text ? JSON.parse(text) : {};
@@ -53051,7 +53939,7 @@ const getContextLayersRoute = HttpRouter.add("GET", "/api/context/layers", httpH
53051
53939
  return jsonResponse(yield* Effect.promise(() => loadContextLayers(projects)));
53052
53940
  })));
53053
53941
  const postContextPreviewRoute = HttpRouter.add("POST", "/api/context/preview", httpHandler(Effect.gen(function* () {
53054
- const body = yield* readJsonBody$6();
53942
+ const body = yield* readJsonBody$7();
53055
53943
  const parsed = yield* Effect.try({
53056
53944
  try: () => decodePreviewRequest(body),
53057
53945
  catch: (error) => new Error(error instanceof Error ? error.message : String(error))
@@ -53060,7 +53948,7 @@ const postContextPreviewRoute = HttpRouter.add("POST", "/api/context/preview", h
53060
53948
  return jsonResponse(yield* Effect.promise(() => previewContextLayers(projects, parsed)));
53061
53949
  })));
53062
53950
  const putContextLayerRoute = HttpRouter.add("PUT", "/api/context/layers", httpHandler(Effect.gen(function* () {
53063
- const body = yield* readJsonBody$6();
53951
+ const body = yield* readJsonBody$7();
53064
53952
  const parsed = yield* Effect.try({
53065
53953
  try: () => decodeSaveRequest(body),
53066
53954
  catch: (error) => new Error(error instanceof Error ? error.message : String(error))
@@ -53084,6 +53972,56 @@ const postContextSyncRoute = HttpRouter.add("POST", "/api/context/sync", httpHan
53084
53972
  })));
53085
53973
  const contextRouteLayer = Layer.mergeAll(getContextLayersRoute, postContextPreviewRoute, putContextLayerRoute, postContextSyncRoute);
53086
53974
  //#endregion
53975
+ //#region routes/commands.ts
53976
+ init_src();
53977
+ const COMMANDS_API_HARNESSES = [
53978
+ "claude-code",
53979
+ "codex",
53980
+ "ohmypi",
53981
+ "acp"
53982
+ ];
53983
+ const OVERDECK_COMMANDS = COMPOSER_COMMAND_MANIFEST.map((entry) => {
53984
+ const policy = resolvePolicy(entry.path);
53985
+ return {
53986
+ ...entry,
53987
+ mode: policy.mode,
53988
+ safety: policy.safety
53989
+ };
53990
+ });
53991
+ const COMMAND_PAYLOADS = Object.fromEntries(COMMANDS_API_HARNESSES.map((harness) => [harness, {
53992
+ harness,
53993
+ overdeck: OVERDECK_COMMANDS,
53994
+ native: getHarnessBehavior(harness).nativeCommands ?? []
53995
+ }]));
53996
+ function getCommandsPayload(harness) {
53997
+ return COMMAND_PAYLOADS[harness];
53998
+ }
53999
+ function isCommandsApiHarness(value) {
54000
+ return COMMANDS_API_HARNESSES.includes(value);
54001
+ }
54002
+ function resolveCommandsRequest(harness) {
54003
+ const requestedHarness = harness ?? "claude-code";
54004
+ if (!isCommandsApiHarness(requestedHarness)) return {
54005
+ ok: false,
54006
+ error: `Unknown harness "${requestedHarness}". Expected one of: ${COMMANDS_API_HARNESSES.join(", ")}.`,
54007
+ accepted: COMMANDS_API_HARNESSES
54008
+ };
54009
+ return {
54010
+ ok: true,
54011
+ payload: getCommandsPayload(requestedHarness)
54012
+ };
54013
+ }
54014
+ const getCommandsRoute = HttpRouter.add("GET", "/api/commands", httpHandler(Effect.gen(function* () {
54015
+ const request = yield* HttpServerRequest.HttpServerRequest;
54016
+ const result = resolveCommandsRequest(new URL(request.url, "http://localhost").searchParams.get("harness"));
54017
+ if (!result.ok) return jsonResponse({
54018
+ error: result.error,
54019
+ accepted: result.accepted
54020
+ }, { status: 400 });
54021
+ return jsonResponse(result.payload);
54022
+ })));
54023
+ const commandsRouteLayer = Layer.mergeAll(getCommandsRoute);
54024
+ //#endregion
53087
54025
  //#region routes/admin.ts
53088
54026
  init_tldr_daemon();
53089
54027
  init_projects();
@@ -53193,7 +54131,7 @@ const adminRouteLayer = Layer.mergeAll(getAdminTldrRoute, postAdminBackfillTitle
53193
54131
  * requiring a heavy `pan install` step before first use.
53194
54132
  */
53195
54133
  init_platform();
53196
- const execAsync$1 = promisify(exec);
54134
+ const execAsync$2 = promisify(exec);
53197
54135
  const execFileAsync$2 = promisify(execFile);
53198
54136
  const PREREQ_REGISTRY = {
53199
54137
  addGitHubProject: ["gh"],
@@ -53252,7 +54190,7 @@ function getLinuxManualInstallHint(tool, packageManager) {
53252
54190
  async function installLinuxPackage(tool) {
53253
54191
  const packageManager = await detectLinuxPackageManager();
53254
54192
  if (!packageManager) throw new Error(`No supported Linux package manager found. Install ${getLinuxManualInstallHint(tool, null)}`);
53255
- await execAsync$1(getLinuxInstallCommand(tool, packageManager), { timeout: 12e4 });
54193
+ await execAsync$2(getLinuxInstallCommand(tool, packageManager), { timeout: 12e4 });
53256
54194
  }
53257
54195
  async function isToolInstalled(tool) {
53258
54196
  if (tool === "docker") return checkCommand("docker", ["info"]);
@@ -53295,7 +54233,7 @@ async function installTool(tool) {
53295
54233
  }
53296
54234
  }
53297
54235
  async function installTmux() {
53298
- if (await Effect.runPromise(detectPlatform()) === "darwin") await execAsync$1("brew install tmux", { timeout: 12e4 });
54236
+ if (await Effect.runPromise(detectPlatform()) === "darwin") await execAsync$2("brew install tmux", { timeout: 12e4 });
53299
54237
  else await installLinuxPackage("tmux");
53300
54238
  return {
53301
54239
  tool: "tmux",
@@ -53309,14 +54247,14 @@ async function installTtyd() {
53309
54247
  mkdirSync$1(binDir, { recursive: true });
53310
54248
  const ttydPath = join(binDir, "ttyd");
53311
54249
  if (plat === "darwin") try {
53312
- await execAsync$1("brew install ttyd", { timeout: 12e4 });
54250
+ await execAsync$2("brew install ttyd", { timeout: 12e4 });
53313
54251
  return {
53314
54252
  tool: "ttyd",
53315
54253
  success: true,
53316
54254
  message: "ttyd installed via Homebrew"
53317
54255
  };
53318
54256
  } catch {}
53319
- await execAsync$1(`curl -sL "https://github.com/tsl0922/ttyd/releases/latest/download/ttyd.x86_64" -o "${ttydPath}" && chmod +x "${ttydPath}"`, { timeout: 6e4 });
54257
+ await execAsync$2(`curl -sL "https://github.com/tsl0922/ttyd/releases/latest/download/ttyd.x86_64" -o "${ttydPath}" && chmod +x "${ttydPath}"`, { timeout: 6e4 });
53320
54258
  return {
53321
54259
  tool: "ttyd",
53322
54260
  success: true,
@@ -53325,8 +54263,8 @@ async function installTtyd() {
53325
54263
  }
53326
54264
  async function installMkcert() {
53327
54265
  if (await Effect.runPromise(detectPlatform()) === "darwin") {
53328
- await execAsync$1("brew install mkcert", { timeout: 12e4 });
53329
- await execAsync$1("mkcert -install", { timeout: 3e4 });
54266
+ await execAsync$2("brew install mkcert", { timeout: 12e4 });
54267
+ await execAsync$2("mkcert -install", { timeout: 3e4 });
53330
54268
  return {
53331
54269
  tool: "mkcert",
53332
54270
  success: true,
@@ -53336,8 +54274,8 @@ async function installMkcert() {
53336
54274
  const binDir = join(homedir$1(), ".local", "bin");
53337
54275
  mkdirSync$1(binDir, { recursive: true });
53338
54276
  const mkcertPath = join(binDir, "mkcert");
53339
- await execAsync$1(`curl -sL "https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-${process.arch === "x64" ? "amd64" : process.arch}" -o "${mkcertPath}" && chmod +x "${mkcertPath}"`, { timeout: 6e4 });
53340
- await execAsync$1("mkcert -install", { timeout: 3e4 });
54277
+ await execAsync$2(`curl -sL "https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-${process.arch === "x64" ? "amd64" : process.arch}" -o "${mkcertPath}" && chmod +x "${mkcertPath}"`, { timeout: 6e4 });
54278
+ await execAsync$2("mkcert -install", { timeout: 3e4 });
53341
54279
  return {
53342
54280
  tool: "mkcert",
53343
54281
  success: true,
@@ -53345,7 +54283,7 @@ async function installMkcert() {
53345
54283
  };
53346
54284
  }
53347
54285
  async function installJq() {
53348
- if (await Effect.runPromise(detectPlatform()) === "darwin") await execAsync$1("brew install jq", { timeout: 12e4 });
54286
+ if (await Effect.runPromise(detectPlatform()) === "darwin") await execAsync$2("brew install jq", { timeout: 12e4 });
53349
54287
  else await installLinuxPackage("jq");
53350
54288
  return {
53351
54289
  tool: "jq",
@@ -53360,7 +54298,7 @@ async function installOx() {
53360
54298
  const binDir = join(homedir$1(), ".local", "bin");
53361
54299
  const oxPath = join(binDir, "ox");
53362
54300
  mkdirSync$1(binDir, { recursive: true });
53363
- await execAsync$1(`curl -sL "https://github.com/eltmon/ox/releases/download/latest/ox-${platform}-${arch}" -o "${oxPath}" && chmod +x "${oxPath}"`, { timeout: 6e4 });
54301
+ await execAsync$2(`curl -sL "https://github.com/eltmon/ox/releases/download/latest/ox-${platform}-${arch}" -o "${oxPath}" && chmod +x "${oxPath}"`, { timeout: 6e4 });
53364
54302
  return {
53365
54303
  tool: "ox",
53366
54304
  success: true,
@@ -53377,7 +54315,7 @@ async function installOx() {
53377
54315
  * GET /api/prereqs/:feature — check which tools are missing for a feature
53378
54316
  * POST /api/prereqs/install — install a specific tool by name
53379
54317
  */
53380
- const readJsonBody$5 = Effect.gen(function* () {
54318
+ const readJsonBody$6 = Effect.gen(function* () {
53381
54319
  const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
53382
54320
  try {
53383
54321
  return text ? JSON.parse(text) : {};
@@ -53414,7 +54352,7 @@ const checkFeatureRoute = HttpRouter.add("GET", "/api/prereqs/:feature", httpHan
53414
54352
  });
53415
54353
  })));
53416
54354
  const installToolRoute = HttpRouter.add("POST", "/api/prereqs/install", httpHandler(Effect.gen(function* () {
53417
- const tool = (yield* readJsonBody$5).tool;
54355
+ const tool = (yield* readJsonBody$6).tool;
53418
54356
  if (!tool) return jsonResponse({ error: "Missing 'tool' in request body" }, { status: 400 });
53419
54357
  if (!INSTALLABLE_TOOLS.some((t) => t === tool)) return jsonResponse({ error: `Tool '${tool}' is not auto-installable` }, { status: 400 });
53420
54358
  const result = yield* Effect.promise(() => installTool(tool));
@@ -54075,7 +55013,7 @@ function getCheckpointWorker() {
54075
55013
  return worker;
54076
55014
  }
54077
55015
  async function runInline(operation, payload) {
54078
- const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-BJIs5QIA.js");
55016
+ const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-DMTgXuLG.js");
54079
55017
  switch (operation) {
54080
55018
  case "claimTranscriptRange": return claimTranscriptRange(payload);
54081
55019
  case "commitTranscriptRange": return commitTranscriptRange(payload);
@@ -55229,7 +56167,7 @@ function buildPendingTurn(input, fromOffset, toOffset, compressed) {
55229
56167
  };
55230
56168
  }
55231
56169
  async function emitMemoryObservationCreated(observation, timestamp) {
55232
- const { initEventStore } = await import("./event-store-EV2cMiXj.js");
56170
+ const { initEventStore } = await import("./event-store-ELqkVOn0.js");
55233
56171
  await (await initEventStore()).appendAsync({
55234
56172
  type: "memory.observation_created",
55235
56173
  timestamp,
@@ -56360,7 +57298,16 @@ async function handleMemorySessionStartBody(body, options = {}) {
56360
57298
  statusCode: 422,
56361
57299
  error: "memory identity could not be resolved"
56362
57300
  };
56363
- const fileStat = await (options.statTranscript ?? getTranscriptStat$1)(trustedTranscriptPath);
57301
+ let fileStat;
57302
+ try {
57303
+ fileStat = await (options.statTranscript ?? getTranscriptStat$1)(trustedTranscriptPath);
57304
+ } catch (error) {
57305
+ if (error.code === "ENOENT") return {
57306
+ status: "transcript-missing",
57307
+ sessionId
57308
+ };
57309
+ throw error;
57310
+ }
56364
57311
  (options.registerTranscript ?? registerTranscriptForPolling)({
56365
57312
  agentId: stringField(body.agentId) ?? stringField(body.agent_id) ?? identity.runId,
56366
57313
  sessionId,
@@ -56491,7 +57438,7 @@ const postMemorySessionStartRoute = HttpRouter.add("POST", "/api/memory/session/
56491
57438
  }
56492
57439
  const readModel = yield* ReadModelService;
56493
57440
  const result = yield* Effect.promise(() => handleMemorySessionStartBody(body, { resolveAgentIdBySessionId: async (sessionId) => Effect.runPromise(readModel.getAgentIdBySessionId(sessionId)) }));
56494
- if (result.status === "subagent" || result.status === "disabled") return HttpServerResponse.text("", { status: 204 });
57441
+ if (result.status === "subagent" || result.status === "disabled" || result.status === "transcript-missing") return HttpServerResponse.text("", { status: 204 });
56495
57442
  if (result.status === "error") return jsonResponse({
56496
57443
  ok: false,
56497
57444
  error: result.error
@@ -56895,6 +57842,93 @@ const postTestCheckpointRoute = HttpRouter.add("POST", "/api/agents/:agentId/dif
56895
57842
  }));
56896
57843
  const diffsRouteLayer = Layer.mergeAll(getDiffsRoute, getTurnDiffRoute, getFullDiffRoute, getVsMainDiffRoute, postTestCheckpointRoute);
56897
57844
  //#endregion
57845
+ //#region routes/linear-mcp-auth.ts
57846
+ init_issue_service_singleton();
57847
+ const LINEAR_MCP_AUTH_CALLBACK_COPY_PREFIX = "The operator completed the Linear OAuth authorization in their browser. Finish the flow now: call mcp__linear__complete_authentication with callback_url set to exactly this URL:";
57848
+ const readJsonBody$5 = Effect.gen(function* () {
57849
+ const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
57850
+ return text ? JSON.parse(text) : {};
57851
+ });
57852
+ function rejectInvalidOrigin(request) {
57853
+ const originCheck = validateOrigin$1(request);
57854
+ if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });
57855
+ return null;
57856
+ }
57857
+ function isValidLinearMcpCallbackUrl(value) {
57858
+ try {
57859
+ const url = new URL(value);
57860
+ const localHost = url.hostname === "localhost" || url.hostname === "127.0.0.1";
57861
+ return url.protocol === "http:" && localHost && !!url.searchParams.get("code") && !!url.searchParams.get("state");
57862
+ } catch {
57863
+ return false;
57864
+ }
57865
+ }
57866
+ function callbackCopy(callbackUrl) {
57867
+ return `${LINEAR_MCP_AUTH_CALLBACK_COPY_PREFIX} ${callbackUrl} — then re-check Linear access and resume your canonical task.`;
57868
+ }
57869
+ /**
57870
+ * Attach each blocked agent's canonical tracker URL (Linear web URL, GitHub
57871
+ * html_url) from the issues read door so the banner can link every blocked
57872
+ * agent's issue — including Linear-tracked ones the frontend cannot derive a
57873
+ * URL for. Best-effort: an unresolvable issue falls back to null and the
57874
+ * banner renders its own fallback.
57875
+ */
57876
+ function withIssueUrls(intervention) {
57877
+ let urlByIdentifier;
57878
+ try {
57879
+ urlByIdentifier = /* @__PURE__ */ new Map();
57880
+ const issues = getSharedIssueService().getIssues({ includeCompleted: true });
57881
+ for (const issue of issues) if (typeof issue.identifier === "string" && typeof issue.url === "string" && issue.url !== "") urlByIdentifier.set(issue.identifier.toLowerCase(), issue.url);
57882
+ } catch {
57883
+ return intervention;
57884
+ }
57885
+ return {
57886
+ ...intervention,
57887
+ blockedAgents: intervention.blockedAgents.map((agent) => ({
57888
+ ...agent,
57889
+ issueUrl: agent.issueId ? urlByIdentifier.get(agent.issueId.toLowerCase()) ?? null : null
57890
+ }))
57891
+ };
57892
+ }
57893
+ const getLinearMcpAuthRoute = HttpRouter.add("GET", "/api/linear-mcp-auth", httpHandler(Effect.gen(function* () {
57894
+ return jsonResponse(withIssueUrls(yield* Effect.promise(() => resolveLinearMcpAuthIntervention())));
57895
+ })));
57896
+ const postLinearMcpAuthCallbackRoute = HttpRouter.add("POST", "/api/linear-mcp-auth/callback", httpHandler(Effect.gen(function* () {
57897
+ const originError = rejectInvalidOrigin(yield* HttpServerRequest.HttpServerRequest);
57898
+ if (originError) return originError;
57899
+ const body = yield* readJsonBody$5;
57900
+ const callbackUrl = typeof body["callbackUrl"] === "string" ? body["callbackUrl"] : "";
57901
+ if (!isValidLinearMcpCallbackUrl(callbackUrl)) return jsonResponse({
57902
+ success: false,
57903
+ error: "callbackUrl must be a localhost OAuth callback URL with code and state parameters"
57904
+ }, { status: 400 });
57905
+ const intervention = yield* Effect.promise(() => resolveLinearMcpAuthIntervention());
57906
+ if (intervention.authUrlAgentId === null) return jsonResponse({
57907
+ success: false,
57908
+ error: "No blocked agent owns the active Linear authorization URL"
57909
+ }, { status: 409 });
57910
+ yield* Effect.promise(() => messageAgent(intervention.authUrlAgentId, callbackCopy(callbackUrl), "linear-mcp-auth-callback"));
57911
+ yield* Effect.promise(() => appendLinearMcpAuthCallbackRelayedEvent({
57912
+ agentId: intervention.authUrlAgentId,
57913
+ issueId: intervention.blockedAgents.find((agent) => agent.agentId === intervention.authUrlAgentId)?.issueId ?? null
57914
+ }));
57915
+ return jsonResponse({
57916
+ success: true,
57917
+ relayedTo: intervention.authUrlAgentId
57918
+ });
57919
+ })));
57920
+ const postLinearMcpAuthCompleteRoute = HttpRouter.add("POST", "/api/linear-mcp-auth/complete", httpHandler(Effect.gen(function* () {
57921
+ const originError = rejectInvalidOrigin(yield* HttpServerRequest.HttpServerRequest);
57922
+ if (originError) return originError;
57923
+ yield* Effect.promise(() => appendLinearMcpAuthHealthyEvent({
57924
+ agentId: "operator",
57925
+ issueId: null,
57926
+ source: "operator"
57927
+ }));
57928
+ return jsonResponse({ success: true });
57929
+ })));
57930
+ const linearMcpAuthRouteLayer = Layer.mergeAll(getLinearMcpAuthRoute, postLinearMcpAuthCallbackRoute, postLinearMcpAuthCompleteRoute);
57931
+ //#endregion
56898
57932
  //#region routes/terminals.ts
56899
57933
  /**
56900
57934
  * Terminal routes (PAN-1545 / PAN-1561) — ad-hoc tmux sessions for the
@@ -57644,6 +58678,287 @@ const getSubstrateBugWeightsRoute = HttpRouter.add("GET", "/api/flywheel/substra
57644
58678
  })));
57645
58679
  })));
57646
58680
  //#endregion
58681
+ //#region services/flywheel-actions.ts
58682
+ init_config_yaml();
58683
+ init_harness_resolve();
58684
+ init_control_settings();
58685
+ init_tmux();
58686
+ const DEFAULT_BRIEF_PATH$1 = "docs/flywheel-brief.md";
58687
+ const execAsync$1 = promisify(exec);
58688
+ function mb(bytes) {
58689
+ return Math.round(bytes / 1024 / 1024);
58690
+ }
58691
+ function isInsideRoot$1(projectRoot, candidate) {
58692
+ const relativePath = relative(projectRoot, candidate);
58693
+ return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
58694
+ }
58695
+ function resolveFlywheelStartBriefPath(cwd, requestedPath) {
58696
+ const rawPath = requestedPath?.trim() || DEFAULT_BRIEF_PATH$1;
58697
+ if (rawPath.includes("\0")) throw new Error("Brief path is invalid");
58698
+ const root = resolve(cwd);
58699
+ const absolutePath = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
58700
+ if (!isInsideRoot$1(root, absolutePath)) throw new Error("Brief path must stay inside the project root");
58701
+ const normalizedRoot = root.endsWith(sep) ? root : `${root}${sep}`;
58702
+ const displayPath = absolutePath === root ? "." : relative(root, absolutePath);
58703
+ return {
58704
+ absolutePath,
58705
+ displayPath: absolutePath.startsWith(normalizedRoot) ? displayPath : absolutePath
58706
+ };
58707
+ }
58708
+ async function assertExistingPathInsideRoot$1(projectRoot, candidate) {
58709
+ const [realRoot, realCandidate] = await Promise.all([realpath(projectRoot), realpath(candidate)]);
58710
+ if (!isInsideRoot$1(realRoot, realCandidate)) throw new Error("Brief path must stay inside the project root");
58711
+ }
58712
+ async function requireFlywheelBrief(cwd, requestedPath) {
58713
+ const resolved = resolveFlywheelStartBriefPath(cwd, requestedPath);
58714
+ try {
58715
+ await assertExistingPathInsideRoot$1(cwd, resolved.absolutePath);
58716
+ await readFile(resolved.absolutePath, "utf8");
58717
+ return resolved;
58718
+ } catch (error) {
58719
+ if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) === "ENOENT") throw new Error(`Flywheel brief not found: ${resolved.displayPath}`);
58720
+ throw error;
58721
+ }
58722
+ }
58723
+ async function dashboardSetting(key) {
58724
+ return runDashboardDbJob("getSetting", key);
58725
+ }
58726
+ async function setDashboardSetting(key, value) {
58727
+ await runDashboardDbJob("setSetting", {
58728
+ key,
58729
+ value
58730
+ });
58731
+ }
58732
+ async function getActiveRunId() {
58733
+ const value = await dashboardSetting(FLYWHEEL_ACTIVE_RUN_ID_KEY);
58734
+ return value && value.trim() ? value : null;
58735
+ }
58736
+ async function setActiveRunId(runId) {
58737
+ await setDashboardSetting(FLYWHEEL_ACTIVE_RUN_ID_KEY, runId ?? "");
58738
+ }
58739
+ async function isPaused() {
58740
+ return await dashboardSetting(FLYWHEEL_GLOBAL_PAUSE_KEY) === "true";
58741
+ }
58742
+ async function setPaused(paused) {
58743
+ await setDashboardSetting(FLYWHEEL_GLOBAL_PAUSE_KEY, paused ? "true" : "false");
58744
+ }
58745
+ async function readGateSnapshot() {
58746
+ const [paused, activeRunId] = await Promise.all([isPaused(), getActiveRunId()]);
58747
+ return {
58748
+ paused,
58749
+ activeRunId
58750
+ };
58751
+ }
58752
+ async function resolveFlywheelRoleConfig() {
58753
+ const { config } = await Effect.runPromise(loadConfigNoMigration());
58754
+ const flywheel = config.roles?.flywheel;
58755
+ const model = resolveModel("flywheel", void 0, config);
58756
+ return {
58757
+ harness: await resolveHarness({ model }),
58758
+ model,
58759
+ effort: flywheel?.effort ?? "high",
58760
+ minAgents: flywheel?.minAgents ?? 20,
58761
+ maxAgents: flywheel?.maxAgents ?? 30,
58762
+ scope: flywheel?.scope ?? "pan-only"
58763
+ };
58764
+ }
58765
+ async function gitOutput(command, cwd) {
58766
+ const { stdout } = await execAsync$1(command, {
58767
+ cwd,
58768
+ encoding: "utf8"
58769
+ });
58770
+ return stdout.trim();
58771
+ }
58772
+ async function createInitialFlywheelStatus(runId, startedAt, cwd, agentModel, agentHarness, roleConfig) {
58773
+ const ramTotalMb = mb(totalmem$1());
58774
+ return {
58775
+ runId,
58776
+ startedAt,
58777
+ elapsedMs: 0,
58778
+ orchestrator: {
58779
+ harness: agentHarness ?? roleConfig.harness,
58780
+ model: agentModel ?? roleConfig.model,
58781
+ effort: roleConfig.effort,
58782
+ ctxPercent: 0
58783
+ },
58784
+ headline: {
58785
+ bugsFixed: 0,
58786
+ swarmItemsMerged: 0,
58787
+ swarmItemsTotal: 0,
58788
+ prsMerged: 0,
58789
+ awaitingUat: 0
58790
+ },
58791
+ scope: roleConfig.scope,
58792
+ activePipeline: [],
58793
+ substrateBugs: [],
58794
+ agents: [{
58795
+ id: FLYWHEEL_ORCHESTRATOR_AGENT_ID,
58796
+ label: "flywheel-orchestrator",
58797
+ status: "running",
58798
+ role: "flywheel",
58799
+ model: agentModel
58800
+ }],
58801
+ parked: [],
58802
+ suggestions: [],
58803
+ system: {
58804
+ mainHead: await gitOutput("git rev-parse --short HEAD", cwd).catch(() => "unknown"),
58805
+ ramUsedMb: Math.max(0, ramTotalMb - mb(freemem$1())),
58806
+ ramTotalMb,
58807
+ swapUsedMb: 0,
58808
+ swapTotalMb: 0,
58809
+ agentsActive: 1,
58810
+ agentsCap: roleConfig.maxAgents
58811
+ },
58812
+ openQuestions: [],
58813
+ ticks: 0,
58814
+ lastTickAt: startedAt
58815
+ };
58816
+ }
58817
+ async function startFlywheelRunForDashboard(options = {}) {
58818
+ const cwd = options.cwd ?? process.cwd();
58819
+ if (isFlywheelDevcontainerRuntime()) throw new Error("Refusing to spawn flywheel-orchestrator inside a workspace devcontainer");
58820
+ const activeRunId = await resolveLiveFlywheelRunId();
58821
+ if (activeRunId) if (!await Effect.runPromise(sessionExists("flywheel-orchestrator")) || await Effect.runPromise(isPaneDead("flywheel-orchestrator"))) {
58822
+ console.log(`[flywheel] ${activeRunId} gate stuck — orchestrator pane is dead; auto-aborting`);
58823
+ await Effect.runPromise(killSession(FLYWHEEL_ORCHESTRATOR_AGENT_ID)).catch(() => {});
58824
+ await abortFlywheelRun(activeRunId);
58825
+ } else throw new Error(`Flywheel run ${activeRunId} is already active; pause, resume, or report it before starting another run`);
58826
+ const brief = await requireFlywheelBrief(cwd, options.brief ?? DEFAULT_BRIEF_PATH$1);
58827
+ const runId = await nextFlywheelRunId();
58828
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
58829
+ const roleConfig = await resolveFlywheelRoleConfig();
58830
+ await writeFlywheelLaunchMetadata({
58831
+ version: 1,
58832
+ runId,
58833
+ workspace: cwd,
58834
+ briefPath: brief.absolutePath,
58835
+ briefDisplayPath: brief.displayPath,
58836
+ scope: roleConfig.scope
58837
+ });
58838
+ const agent = await spawnFlywheelAgent(runId, {
58839
+ briefPath: brief.absolutePath,
58840
+ workspace: cwd,
58841
+ model: roleConfig.model,
58842
+ harness: roleConfig.harness,
58843
+ effort: roleConfig.effort,
58844
+ minAgents: roleConfig.minAgents,
58845
+ maxAgents: roleConfig.maxAgents,
58846
+ scope: roleConfig.scope
58847
+ });
58848
+ await setActiveRunId(runId);
58849
+ await setPaused(false);
58850
+ await writeLatestFlywheelStatus(await createInitialFlywheelStatus(runId, startedAt, cwd, agent.model, agent.harness, roleConfig));
58851
+ return {
58852
+ runId,
58853
+ briefDisplayPath: resolveFlywheelStartBriefPath(cwd, options.brief ?? DEFAULT_BRIEF_PATH$1).displayPath,
58854
+ agentModel: agent.model
58855
+ };
58856
+ }
58857
+ async function pauseFlywheelRunForDashboard() {
58858
+ const before = await readGateSnapshot();
58859
+ if (before.paused) return {
58860
+ before,
58861
+ after: before,
58862
+ changed: false
58863
+ };
58864
+ if (before.activeRunId) try {
58865
+ const { readFile } = await import("node:fs/promises");
58866
+ const { join } = await import("node:path");
58867
+ const { getAgentDir } = await import("./agents-BKKkxKgK.js");
58868
+ const sessionId = (await readFile(join(getAgentDir(FLYWHEEL_ORCHESTRATOR_AGENT_ID), "session.id"), "utf-8")).trim();
58869
+ if (sessionId) await saveResumeSessionId(before.activeRunId, sessionId);
58870
+ } catch {}
58871
+ await setPaused(true);
58872
+ await import("./agents-BKKkxKgK.js").then(({ stopAgent }) => Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID)));
58873
+ return {
58874
+ before,
58875
+ after: await readGateSnapshot(),
58876
+ changed: true
58877
+ };
58878
+ }
58879
+ async function abortFlywheelRunForDashboard() {
58880
+ const candidate = await getActiveRunId();
58881
+ if (!candidate) return { aborted: await resolveLiveFlywheelRunId() ?? null };
58882
+ const { stopAgent } = await import("./agents-BKKkxKgK.js");
58883
+ await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID));
58884
+ await abortFlywheelRun(candidate);
58885
+ return { aborted: candidate };
58886
+ }
58887
+ async function resumeFlywheelRunForDashboard() {
58888
+ const before = await readGateSnapshot();
58889
+ if (!before.paused && await Effect.runPromise(sessionExists("flywheel-orchestrator"))) return {
58890
+ before,
58891
+ after: before,
58892
+ changed: false
58893
+ };
58894
+ if (!before.activeRunId) throw new Error("No active flywheel run to resume");
58895
+ const launch = await readFlywheelLaunchMetadata(before.activeRunId);
58896
+ if (!launch) throw new Error(`Flywheel run ${before.activeRunId} is missing launch metadata; cannot resume safely`);
58897
+ const brief = await requireFlywheelBrief(launch.workspace, launch.briefPath);
58898
+ const roleConfig = await resolveFlywheelRoleConfig();
58899
+ if (launch.scope !== roleConfig.scope) await writeFlywheelLaunchMetadata({
58900
+ ...launch,
58901
+ version: 1,
58902
+ scope: roleConfig.scope
58903
+ });
58904
+ const resumeSessionId = await loadResumeSessionId(before.activeRunId) ?? void 0;
58905
+ await spawnFlywheelAgent(before.activeRunId, {
58906
+ workspace: launch.workspace,
58907
+ briefPath: brief.absolutePath,
58908
+ model: roleConfig.model,
58909
+ harness: roleConfig.harness,
58910
+ effort: roleConfig.effort,
58911
+ minAgents: roleConfig.minAgents,
58912
+ maxAgents: roleConfig.maxAgents,
58913
+ scope: roleConfig.scope,
58914
+ resumeSessionId
58915
+ });
58916
+ await setPaused(false);
58917
+ return {
58918
+ before,
58919
+ after: await readGateSnapshot(),
58920
+ changed: true
58921
+ };
58922
+ }
58923
+ function getPlatformOpenCommand() {
58924
+ switch (process.platform) {
58925
+ case "linux": return "xdg-open";
58926
+ case "darwin": return "open";
58927
+ case "win32": return "explorer";
58928
+ default: throw new Error(`Opening files is not supported on ${process.platform}`);
58929
+ }
58930
+ }
58931
+ function openPathDetached(path) {
58932
+ spawn(getPlatformOpenCommand(), [path], {
58933
+ detached: true,
58934
+ stdio: "ignore"
58935
+ }).unref();
58936
+ }
58937
+ async function resolveReportOpenRunId(runId) {
58938
+ if (runId) return runId;
58939
+ const runs = await listFlywheelRuns();
58940
+ const run = runs.find((candidate) => candidate.status === "complete") ?? runs[0];
58941
+ if (!run) throw new Error("no flywheel run report to open");
58942
+ return run.id;
58943
+ }
58944
+ async function openFlywheelRunReportForDashboard(options = {}) {
58945
+ const runId = await resolveReportOpenRunId(options.runId);
58946
+ const detail = await getFlywheelRunDetail(runId);
58947
+ if (!detail) throw new Error(`Flywheel run not found: ${runId}`);
58948
+ if (!detail.paths.report) throw new Error(`No report exists for ${runId}`);
58949
+ await (options.opener ?? openPathDetached)(detail.paths.report);
58950
+ return {
58951
+ runId,
58952
+ path: detail.paths.report
58953
+ };
58954
+ }
58955
+ async function readCurrentFlywheelStatusForDashboard() {
58956
+ const activeRunId = await resolveLiveFlywheelRunId();
58957
+ if (!activeRunId) return null;
58958
+ const activeRun = await getFlywheelRunDetail(activeRunId);
58959
+ return activeRun?.status === "running" ? activeRun.latest : null;
58960
+ }
58961
+ //#endregion
57647
58962
  //#region services/flywheel-state.ts
57648
58963
  const FLYWHEEL_STATE_RELATIVE_PATH = "docs/FLYWHEEL-STATE.md";
57649
58964
  function resolveStatePath(projectRoot) {
@@ -57679,6 +58994,7 @@ async function readFlywheelState(options = {}) {
57679
58994
  const AUTO_MERGE_COOLDOWN_MS = 3e5;
57680
58995
  //#endregion
57681
58996
  //#region ../../lib/cloister/auto-merge-eligibility.ts
58997
+ init_forge();
57682
58998
  init_review_status();
57683
58999
  init_tracker_utils();
57684
59000
  const execFileAsync$1 = promisify(execFile);
@@ -57938,6 +59254,7 @@ init_src();
57938
59254
  init_activity_logger();
57939
59255
  init_tmux();
57940
59256
  init_control_settings();
59257
+ init_forge();
57941
59258
  init_review_status();
57942
59259
  init_projects();
57943
59260
  const DEFAULT_BRIEF_PATH = "docs/flywheel-brief.md";
@@ -58242,7 +59559,7 @@ async function getFlywheelConversationPayload() {
58242
59559
  };
58243
59560
  }
58244
59561
  async function postFlywheelReportPayload() {
58245
- const { flywheelReportCommand } = await import("./flywheel-DzGtWnJ1.js");
59562
+ const { flywheelReportCommand } = await import("./flywheel-CLVB55Be.js");
58246
59563
  await flywheelReportCommand({ cwd: process.cwd() });
58247
59564
  return {
58248
59565
  status: 200,
@@ -58397,39 +59714,6 @@ const getAutoMergeProblemsRoute = HttpRouter.add("GET", "/api/flywheel/auto-merg
58397
59714
  const getMergeBlockersRoute = HttpRouter.add("GET", "/api/flywheel/merge-blockers", httpHandler(Effect.gen(function* () {
58398
59715
  return jsonResponse(getMergeBlockersPayload());
58399
59716
  })));
58400
- async function defaultGetOrderedIssueIds() {
58401
- const status = await readCurrentFlywheelStatusForDashboard();
58402
- if (!status) return [];
58403
- const { computeMergeQueue } = await import("./flywheel-merge-order-UUcsCDb6.js");
58404
- return (await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd()).pipe(Effect.provide(layer)))).map((i) => i.issueId);
58405
- }
58406
- async function defaultMergeOne(issueId) {
58407
- const { triggerMerge } = await import("./merge-ops-CR44oU9-.js");
58408
- const r = await triggerMerge(issueId);
58409
- return r.success ? { ok: true } : {
58410
- ok: false,
58411
- reason: r.error ?? r.message ?? "merge failed"
58412
- };
58413
- }
58414
- /**
58415
- * PAN-1691 "merge next N" / "ship the UAT candidate": merge the first N issues
58416
- * of the conflict-aware merge order, one at a time, stopping at the first
58417
- * failure (the rest would need re-rebasing). Pure-ish + DI for testing.
58418
- */
58419
- async function postFlywheelMergeNextPayload(payload, deps = {}) {
58420
- const body = payload ?? {};
58421
- const n = typeof body.n === "number" && Number.isFinite(body.n) ? Math.floor(body.n) : 0;
58422
- if (n <= 0) return {
58423
- status: 400,
58424
- body: { error: "n must be a positive integer" }
58425
- };
58426
- const issueIds = (await (deps.getOrderedIssueIds ?? defaultGetOrderedIssueIds)()).slice(0, n);
58427
- const { shipMergeBatch } = await import("./merge-batch-ClFn2T-y.js");
58428
- return {
58429
- status: 200,
58430
- body: { outcomes: await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne }) }
58431
- };
58432
- }
58433
59717
  const postAutoMergeScheduleRoute = HttpRouter.add("POST", "/api/flywheel/auto-merge/schedule", httpHandler(Effect.gen(function* () {
58434
59718
  const originError = requireTrustedOrigin(yield* HttpServerRequest.HttpServerRequest);
58435
59719
  if (originError) return originError;
@@ -58438,14 +59722,6 @@ const postAutoMergeScheduleRoute = HttpRouter.add("POST", "/api/flywheel/auto-me
58438
59722
  const result = yield* Effect.promise(() => postAutoMergeSchedulePayload(parsed.body));
58439
59723
  return jsonResponse(result.body, { status: result.status });
58440
59724
  })));
58441
- const postFlywheelMergeNextRoute = HttpRouter.add("POST", "/api/flywheel/merge-next", httpHandler(Effect.gen(function* () {
58442
- const originError = requireTrustedOrigin(yield* HttpServerRequest.HttpServerRequest);
58443
- if (originError) return originError;
58444
- const parsed = yield* readUnknownJsonBody;
58445
- if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });
58446
- const result = yield* Effect.promise(() => postFlywheelMergeNextPayload(parsed.body));
58447
- return jsonResponse(result.body, { status: result.status });
58448
- })));
58449
59725
  const deleteAutoMergeRoute = HttpRouter.add("DELETE", "/api/flywheel/auto-merge/:id", httpHandler(Effect.gen(function* () {
58450
59726
  const originError = requireTrustedOrigin(yield* HttpServerRequest.HttpServerRequest);
58451
59727
  if (originError) return originError;
@@ -58577,68 +59853,14 @@ const postFlywheelBriefRoute = HttpRouter.add("POST", "/api/flywheel/brief", htt
58577
59853
  });
58578
59854
  });
58579
59855
  })));
58580
- const getFlywheelMergeQueueRoute = HttpRouter.add("GET", "/api/flywheel/merge-queue", httpHandler(Effect.gen(function* () {
58581
- return yield* Effect.promise(async () => {
58582
- const status = await readCurrentFlywheelStatusForDashboard();
58583
- if (!status) return jsonResponse([]);
58584
- const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-UUcsCDb6.js");
58585
- return jsonResponse(await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd(), { getPrUrl: resolveMergeQueuePrUrl }).pipe(Effect.provide(layer))));
58586
- });
58587
- })));
58588
- /**
58589
- * PAN-1737 UAT batch trains. Generation names contain a slash
58590
- * (`uat/pan-otter-0610`); URL params carry the name WITHOUT the `uat/`
58591
- * prefix and handlers reconstitute it.
58592
- */
58593
- function uatGenerationNameFromParam(param) {
58594
- const decoded = decodeURIComponent(param);
58595
- return decoded.startsWith("uat/") ? decoded : `uat/${decoded}`;
58596
- }
58597
- const getUatGenerationsRoute = HttpRouter.add("GET", "/api/flywheel/uat-generations", httpHandler(Effect.gen(function* () {
58598
- const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-B05BWkp-.js"));
58599
- return jsonResponse(yield* Effect.promise(() => getUatGenerationsPayload()));
58600
- })));
58601
59856
  const getUatCandidateRoute = HttpRouter.add("GET", "/api/flywheel/uat-candidate", httpHandler(Effect.gen(function* () {
58602
- const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-B05BWkp-.js"));
59857
+ const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-Cu-4nvbf.js"));
58603
59858
  return jsonResponse(yield* Effect.promise(() => getUatCandidatePayload()));
58604
59859
  })));
58605
- const postUatGenerationStackRoute = HttpRouter.add("POST", "/api/flywheel/uat-generations/:name/stack", httpHandler(Effect.gen(function* () {
58606
- const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
58607
- if (authError) return authError;
58608
- const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
58609
- const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-B05BWkp-.js"));
58610
- const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));
58611
- if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });
58612
- return jsonResponse({
58613
- frontendUrl: result.frontendUrl,
58614
- evicted: result.evicted
58615
- });
58616
- })));
58617
- const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-generations/:name/promote", httpHandler(Effect.gen(function* () {
58618
- const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
58619
- if (authError) return authError;
58620
- const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
58621
- const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-B05BWkp-.js"));
58622
- const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-CShuVGWT.js"));
58623
- const result = yield* Effect.promise(() => postUatGenerationPromotePayload(name, firePostMergeLifecycle));
58624
- if (!result.success) return jsonResponse(result, { status: result.reason === "not-found" ? 404 : result.reason === "merge-failed" ? 500 : 409 });
58625
- return jsonResponse(result);
58626
- })));
58627
- /**
58628
- * PAN-1737: repurposed from the PAN-1691 one-shot candidate assembly. Now a
58629
- * FORCED reconcile — rebuild the current generation even when the chain
58630
- * already answers the ready set (operator suspects staleness).
58631
- */
58632
- const postFlywheelAssembleUatRoute = HttpRouter.add("POST", "/api/flywheel/assemble-uat", httpHandler(Effect.gen(function* () {
58633
- const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
58634
- if (authError) return authError;
58635
- const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-B05BWkp-.js"));
58636
- return jsonResponse(yield* Effect.promise(() => runUatTrainReconcile({ force: true })));
58637
- })));
58638
59860
  const getFlywheelStateRoute = HttpRouter.add("GET", "/api/flywheel/state", httpHandler(Effect.gen(function* () {
58639
59861
  return yield* Effect.promise(async () => jsonResponse(await readFlywheelState()));
58640
59862
  })));
58641
- const flywheelRouteLayer = Layer.mergeAll(getFlywheelRunsRoute, getFlywheelRunRoute, getFlywheelConversationRoute, getFlywheelCurrentRoute, getFlywheelStatsRoute, getSubstrateBugWeightsRoute, getFlywheelConfigRoute, postFlywheelConfigRoute, getPendingAutoMergeRoute, getAutoMergeProblemsRoute, getMergeBlockersRoute, getMergeBackendRoute, postAutoMergeScheduleRoute, postFlywheelMergeNextRoute, deleteAutoMergeRoute, getFlywheelMergeQueueRoute, getUatGenerationsRoute, getUatCandidateRoute, postUatGenerationStackRoute, postUatGenerationPromoteRoute, postFlywheelAssembleUatRoute, getFlywheelStateRoute, postFlywheelStatusRoute, postFlywheelStartRoute, postFlywheelPauseRoute, postFlywheelResumeRoute, postFlywheelAbortRoute, postFlywheelReportRoute, postFlywheelReportOpenRoute, getFlywheelBriefRoute, postFlywheelBriefRoute);
59863
+ const flywheelRouteLayer = Layer.mergeAll(getFlywheelRunsRoute, getFlywheelRunRoute, getFlywheelConversationRoute, getFlywheelCurrentRoute, getFlywheelStatsRoute, getSubstrateBugWeightsRoute, getFlywheelConfigRoute, postFlywheelConfigRoute, getPendingAutoMergeRoute, getAutoMergeProblemsRoute, getMergeBlockersRoute, getMergeBackendRoute, postAutoMergeScheduleRoute, deleteAutoMergeRoute, getUatCandidateRoute, getFlywheelStateRoute, postFlywheelStatusRoute, postFlywheelStartRoute, postFlywheelPauseRoute, postFlywheelResumeRoute, postFlywheelAbortRoute, postFlywheelReportRoute, postFlywheelReportOpenRoute, getFlywheelBriefRoute, postFlywheelBriefRoute);
58642
59864
  //#endregion
58643
59865
  //#region routes/orders.ts
58644
59866
  init_projects();
@@ -58827,7 +60049,7 @@ function previewBrief(book) {
58827
60049
  return `${lines.join("\n")}\n`;
58828
60050
  }
58829
60051
  async function defaultStartOrderBook(book) {
58830
- const { startFlywheelRun } = await import("./flywheel-DzGtWnJ1.js");
60052
+ const { startFlywheelRun } = await import("./flywheel-CLVB55Be.js");
58831
60053
  return startFlywheelRun({
58832
60054
  cwd: process.cwd(),
58833
60055
  orders: book.id
@@ -61985,7 +63207,8 @@ const staticRouteLayer = HttpRouter.add("GET", "*", Effect.gen(function* () {
61985
63207
  const projectRoot = selfPath.includes("/dist/") ? pathService.resolve(selfPath, "..", "..") : pathService.resolve(selfPath, "..", "..", "..");
61986
63208
  const staticDir = process.env["OVERDECK_FRONTEND_DIR"] ?? pathService.resolve(projectRoot, "dist", "dashboard", "public");
61987
63209
  const staticRoot = pathService.resolve(staticDir);
61988
- const rawRelative = (url.value.pathname === "/" ? "/index.html" : url.value.pathname).replace(/^[/\\]+/, "");
63210
+ const urlPath = url.value.pathname === "/" ? "/index.html" : url.value.pathname;
63211
+ const rawRelative = urlPath.replace(/^[/\\]+/, "");
61989
63212
  const normalized = pathService.normalize(rawRelative).replace(/^[/\\]+/, "");
61990
63213
  if (normalized.length === 0 || normalized.startsWith("..") || normalized.includes("\0")) return HttpServerResponse.text("Invalid path", { status: 400 });
61991
63214
  const sep = pathService.sep;
@@ -61998,6 +63221,7 @@ const staticRouteLayer = HttpRouter.add("GET", "*", Effect.gen(function* () {
61998
63221
  }
61999
63222
  const fileInfo = yield* fileSystem.stat(filePath).pipe(Effect.catch(() => Effect.succeed(null)));
62000
63223
  if (!fileInfo || fileInfo.type !== "File") {
63224
+ if (pathService.extname(urlPath)) return HttpServerResponse.text("Not Found", { status: 404 });
62001
63225
  const indexPath = pathService.resolve(staticRoot, "index.html");
62002
63226
  const indexInfo = yield* fileSystem.stat(indexPath).pipe(Effect.catch(() => Effect.succeed(null)));
62003
63227
  if (!indexInfo || indexInfo.type !== "File") return HttpServerResponse.text("Not Found", { status: 404 });
@@ -62007,7 +63231,7 @@ const staticRouteLayer = HttpRouter.add("GET", "*", Effect.gen(function* () {
62007
63231
  if (filePath.endsWith("index.html")) return HttpServerResponse.setHeader(res, "Cache-Control", "no-cache, no-store, must-revalidate");
62008
63232
  return res;
62009
63233
  }));
62010
- const makeRoutesLayer = Layer.mergeAll(healthRouteLayer, dashboardSessionPreflightRouteLayer, dashboardSessionRouteLayer, websocketRpcRouteLayer, issuesRouteLayer, pipelineMembershipRouteLayer, agentsRouteLayer, workspacesRouteLayer, specialistsRouteLayer, costsRouteLayer, cloisterRouteLayer, resourcesRouteLayer, commandDeckRouteLayer, remoteRouteLayer, settingsRouteLayer, voiceRouteLayer, autopresoRouteLayer, metricsRouteLayer, miscRouteLayer, paletteRouteLayer, conversationsRouteLayer, eventsRouteLayer, showRouteLayer, projectsRouteLayer, contextRouteLayer, adminRouteLayer, prereqsRouteLayer, cliproxyRouteLayer, ttsRouteLayer, webhooksRouteLayer, hooksRouteLayer, diffsRouteLayer, codexAuthRouteLayer, terminalsRouteLayer, discoveredSessionsRouteLayer, flywheelRouteLayer, ordersRouteLayer, artifactsRouteLayer, featureRegistryRouteLayer, fsRouteLayer, tieredCalloutsRouteLayer, backlogRouteLayer, internalEventsRouteLayer, knowledgeViewerRouteLayer, staticRouteLayer);
63234
+ const makeRoutesLayer = Layer.mergeAll(healthRouteLayer, dashboardSessionPreflightRouteLayer, dashboardSessionRouteLayer, websocketRpcRouteLayer, issuesRouteLayer, pipelineMembershipRouteLayer, agentsRouteLayer, workspacesRouteLayer, specialistsRouteLayer, costsRouteLayer, cloisterRouteLayer, resourcesRouteLayer, commandDeckRouteLayer, remoteRouteLayer, settingsRouteLayer, voiceRouteLayer, autopresoRouteLayer, metricsRouteLayer, miscRouteLayer, paletteRouteLayer, conversationsRouteLayer, eventsRouteLayer, showRouteLayer, projectsRouteLayer, projectsMergeTrainRouteLayer, mergeTrainRouteLayer, contextRouteLayer, commandsRouteLayer, adminRouteLayer, prereqsRouteLayer, cliproxyRouteLayer, ttsRouteLayer, webhooksRouteLayer, hooksRouteLayer, diffsRouteLayer, codexAuthRouteLayer, linearMcpAuthRouteLayer, terminalsRouteLayer, discoveredSessionsRouteLayer, flywheelRouteLayer, ordersRouteLayer, artifactsRouteLayer, featureRegistryRouteLayer, fsRouteLayer, tieredCalloutsRouteLayer, backlogRouteLayer, internalEventsRouteLayer, knowledgeViewerRouteLayer, staticRouteLayer);
62011
63235
  const OverdeckAgentsResolverLive = AgentsResolverLive.pipe(Layer.provide(Layer.mergeAll(DbLive, TmuxLive)));
62012
63236
  const OverdeckCostResolverLive = CostResolverLive.pipe(Layer.provide(DbLive));
62013
63237
  const ReadModelWithOverdeckLive = ReadModelServiceLive.pipe(Layer.provide(OverdeckAgentsResolverLive));
@@ -62060,6 +63284,7 @@ const runServer = Layer.launch(makeServerLayer);
62060
63284
  init_agents$1();
62061
63285
  init_review_status();
62062
63286
  init_concurrency$1();
63287
+ init_runtime_census();
62063
63288
  init_event_store();
62064
63289
  init_activity_logger();
62065
63290
  init_read_model();
@@ -62078,7 +63303,43 @@ function buildAwaitingInputActivityMessage(agentId, issueId, kinds) {
62078
63303
  const kindList = kinds.length > 0 ? kinds.join(", ") : "input";
62079
63304
  return issueId ? `${agentId} on ${issueId} is waiting for ${kindList}` : `${agentId} is waiting for ${kindList}`;
62080
63305
  }
63306
+ function shouldSkipEnrichmentCycle(census) {
63307
+ return !census.tmuxAvailable;
63308
+ }
63309
+ function hasReapablePendingInput(enrichment) {
63310
+ return enrichment.pendingInputCount > 0 || enrichment.pendingAskUserQuestion != null || enrichment.pendingProposedPlan != null;
63311
+ }
63312
+ function buildPendingReapEvent(agentId, previous) {
63313
+ return {
63314
+ type: "agent.enrichment_changed",
63315
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
63316
+ payload: {
63317
+ agentId,
63318
+ role: previous.role,
63319
+ hasPendingQuestion: false,
63320
+ pendingQuestionCount: 0,
63321
+ pendingQuestionPrompt: void 0,
63322
+ pendingQuestionReason: void 0,
63323
+ pendingInputCount: 0,
63324
+ pendingInputKinds: [],
63325
+ pendingAskUserQuestion: void 0,
63326
+ pendingProposedPlan: void 0,
63327
+ resolution: previous.resolution,
63328
+ resolutionCount: previous.resolutionCount
63329
+ }
63330
+ };
63331
+ }
63332
+ function buildExpiredQuestionActivityMessage(agentId, issueId) {
63333
+ return `${issueId ? `${agentId} on ${issueId}` : agentId} stopped with its question unanswered — the question has expired and is no longer actionable`;
63334
+ }
62081
63335
  async function pollOnce(state) {
63336
+ let census;
63337
+ try {
63338
+ census = await getRuntimeCensus();
63339
+ } catch {
63340
+ return;
63341
+ }
63342
+ if (shouldSkipEnrichmentCycle(census)) return;
62082
63343
  let runningAgents;
62083
63344
  try {
62084
63345
  runningAgents = await Effect.runPromise(listRunningAgents());
@@ -62200,7 +63461,20 @@ async function pollOnce(state) {
62200
63461
  } catch {}
62201
63462
  })), 4));
62202
63463
  const activeIds = new Set(activeAgents.map((a) => a.id));
62203
- for (const id of state.lastEnrichment.keys()) if (!activeIds.has(id)) {
63464
+ for (const [id, previousEnrichment] of state.lastEnrichment) if (!activeIds.has(id)) {
63465
+ if (hasReapablePendingInput(previousEnrichment)) {
63466
+ try {
63467
+ await eventStore.appendAsync(buildPendingReapEvent(id, previousEnrichment));
63468
+ } catch {}
63469
+ const agentRecord = runningAgents.find((agent) => agent.id === id);
63470
+ const issueId = agentRecord?.issueId;
63471
+ emitActivityEntrySync({
63472
+ source: toRole(agentRecord?.role) ?? "work",
63473
+ level: "info",
63474
+ message: buildExpiredQuestionActivityMessage(id, issueId),
63475
+ issueId
63476
+ });
63477
+ }
62204
63478
  state.lastEnrichment.delete(id);
62205
63479
  state.lastScan.delete(id);
62206
63480
  }
@@ -62208,7 +63482,7 @@ async function pollOnce(state) {
62208
63482
  /** Poll interval: 10 seconds (was 3s). The enrichment data changes slowly —
62209
63483
  * pending questions, resolution state, phase — none of these need sub-second
62210
63484
  * latency. 10s eliminates ~70% of poller I/O without any user-visible lag. */
62211
- const POLL_INTERVAL_MS$3 = 1e4;
63485
+ const POLL_INTERVAL_MS$4 = 1e4;
62212
63486
  const serviceState$2 = {
62213
63487
  timer: null,
62214
63488
  lastEnrichment: /* @__PURE__ */ new Map(),
@@ -62220,7 +63494,7 @@ function startAgentEnrichmentService() {
62220
63494
  if (serviceState$2.timer !== null) return;
62221
63495
  serviceState$2.timer = setInterval(() => {
62222
63496
  pollOnce(serviceState$2).catch(() => {});
62223
- }, POLL_INTERVAL_MS$3);
63497
+ }, POLL_INTERVAL_MS$4);
62224
63498
  }
62225
63499
  function stopAgentEnrichmentService() {
62226
63500
  if (serviceState$2.timer !== null) {
@@ -62248,7 +63522,7 @@ function stopAgentEnrichmentService() {
62248
63522
  * Bounded + cheap: each issue has a throttle. Ready/no-blocker rows use the PAN-1620
62249
63523
  * 3-minute cadence; already-blocked mergeability rows use a 10-minute cadence.
62250
63524
  */
62251
- const POLL_INTERVAL_MS$2 = 6e4;
63525
+ const POLL_INTERVAL_MS$3 = 6e4;
62252
63526
  /** Re-check each ready-to-merge PR at most this often, to bound `gh` calls. */
62253
63527
  const RECHECK_INTERVAL_MS = 18e4;
62254
63528
  /** Re-check already-flagged mergeability blockers less often; they are not mergeable yet. */
@@ -62280,7 +63554,7 @@ function parsePrUrl$1(url) {
62280
63554
  function hasMergeabilityBlocker(blockers) {
62281
63555
  return blockers?.some((blocker) => MERGEABILITY_BLOCKERS.has(blocker.type)) ?? false;
62282
63556
  }
62283
- async function reconcileOnce(state) {
63557
+ async function reconcileOnce$1(state) {
62284
63558
  const candidates = await Effect.runPromise(getMergeBlockerReconcileCandidates());
62285
63559
  const now = Date.now();
62286
63560
  for (const candidate of candidates) {
@@ -62316,8 +63590,8 @@ async function reconcileOnce(state) {
62316
63590
  function startMergeBlockerReconcileService() {
62317
63591
  if (serviceState$1.timer !== null) return;
62318
63592
  serviceState$1.timer = setInterval(() => {
62319
- reconcileOnce(serviceState$1).catch(() => {});
62320
- }, POLL_INTERVAL_MS$2);
63593
+ reconcileOnce$1(serviceState$1).catch(() => {});
63594
+ }, POLL_INTERVAL_MS$3);
62321
63595
  serviceState$1.timer.unref?.();
62322
63596
  }
62323
63597
  function enqueueRefresh(state, task) {
@@ -62341,6 +63615,51 @@ function drainRefreshQueue(state) {
62341
63615
  }
62342
63616
  }
62343
63617
  //#endregion
63618
+ //#region services/review-status-reconcile-service.ts
63619
+ init_review_status();
63620
+ init_event_store();
63621
+ const POLL_INTERVAL_MS$2 = 6e4;
63622
+ let reconcileTimer = null;
63623
+ function reviewStatusPayload(event) {
63624
+ if (!event.payload || typeof event.payload !== "object") return null;
63625
+ const payload = event.payload;
63626
+ if (typeof payload.issueId !== "string" || !payload.status || typeof payload.status !== "object") return null;
63627
+ return payload;
63628
+ }
63629
+ async function reconcileOnce() {
63630
+ const cached = loadReviewStatuses();
63631
+ const canonicalByIssue = /* @__PURE__ */ new Map();
63632
+ for (const issueId of Object.keys(cached)) {
63633
+ const canonical = getReviewStatusSync(issueId);
63634
+ if (!canonical) continue;
63635
+ canonicalByIssue.set(issueId.toUpperCase(), canonical);
63636
+ }
63637
+ const store = getEventStore();
63638
+ const latestByIssue = /* @__PURE__ */ new Map();
63639
+ for (const event of store.queryLatestPerIssue("review.status_changed")) {
63640
+ const payload = reviewStatusPayload(event);
63641
+ if (payload) latestByIssue.set(payload.issueId.toUpperCase(), event);
63642
+ }
63643
+ for (const [issueId, canonical] of canonicalByIssue) {
63644
+ const latest = latestByIssue.get(issueId);
63645
+ const latestPayload = latest ? reviewStatusPayload(latest) : null;
63646
+ const lastUpdatedAt = latestPayload?.status.updatedAt ?? "";
63647
+ if (lastUpdatedAt > canonical.updatedAt) continue;
63648
+ if (lastUpdatedAt === canonical.updatedAt && latestPayload && sameCanonicalReviewStatus(latestPayload.status, canonical)) continue;
63649
+ console.log(`[review-status-reconcile] re-emitting status for ${issueId} (canonical differs from last event — healing lost status_changed)`);
63650
+ emitReviewStatusChanged((event) => store.append(event), issueId, canonical);
63651
+ }
63652
+ }
63653
+ function startReviewStatusReconcileService() {
63654
+ if (getDashboardIdentity().mode !== "primary") return false;
63655
+ if (reconcileTimer !== null) return true;
63656
+ reconcileTimer = setInterval(() => {
63657
+ reconcileOnce().catch(() => {});
63658
+ }, POLL_INTERVAL_MS$2);
63659
+ reconcileTimer.unref?.();
63660
+ return true;
63661
+ }
63662
+ //#endregion
62344
63663
  //#region services/project-resource-refresh-queue.ts
62345
63664
  init_projects();
62346
63665
  const PROJECT_RESOURCE_CONVERGENCE_INTERVAL_MS = 5 * 6e4;
@@ -64028,14 +65347,42 @@ const RESTART_MARKER = join$1(homedir(), ".overdeck", "dashboard-restarting.json
64028
65347
  * loads new content-hashed chunk filenames (no ERR_MODULE_NOT_FOUND after rebuild).
64029
65348
  */
64030
65349
  async function defaultLifecycleRunner(pending) {
64031
- const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-CKkgIBPB.js");
65350
+ const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-DrXa1CpW.js");
64032
65351
  await postMergeLifecycle(pending.issueId, pending.projectPath, pending.sourceBranch, { skipDeploy: true });
64033
65352
  if (pending.sourceBranch) await notifyTldrDaemon(pending.projectPath, pending.sourceBranch);
64034
65353
  }
65354
+ async function runClaimedPendingLifecycle(claim, pending, runner, lifecycleDelayMs, startTime) {
65355
+ let succeeded = false;
65356
+ try {
65357
+ if (lifecycleDelayMs > 0) await new Promise((resolve) => setTimeout(resolve, lifecycleDelayMs));
65358
+ await runner(pending);
65359
+ succeeded = true;
65360
+ emitDashboardLifecycleSync("completed", {
65361
+ reason: pending.reason ?? "post-merge",
65362
+ issueId: pending.issueId,
65363
+ durationMs: Date.now() - startTime
65364
+ });
65365
+ } catch (error) {
65366
+ const message = error instanceof Error ? error.message : String(error);
65367
+ console.error(`[overdeck] Post-merge lifecycle failed for ${pending.issueId}: ${message}`);
65368
+ emitDashboardLifecycleSync("failed", {
65369
+ reason: pending.reason ?? "post-merge",
65370
+ issueId: pending.issueId,
65371
+ error: message
65372
+ });
65373
+ } finally {
65374
+ try {
65375
+ await settlePendingLifecycleClaim(claim, pending.issueId, succeeded);
65376
+ } catch (error) {
65377
+ const message = error instanceof Error ? error.message : String(error);
65378
+ console.warn(`[overdeck] Failed to settle pending lifecycle claim for ${pending.issueId}: ${message}`);
65379
+ }
65380
+ }
65381
+ }
64035
65382
  /**
64036
65383
  * Check for and process a pending post-merge lifecycle file.
64037
- * Reads, validates, deletes the file, then schedules lifecycle execution.
64038
- * Safe to call unconditionally on server startup — no-op if file absent.
65384
+ * Atomically claims the file before execution so dashboard and Deacon cannot
65385
+ * run the same handoff. The durable mergeStep marker owns retries after failure.
64039
65386
  *
64040
65387
  * Also checks for a RESTART_MARKER file (written by deploy script before killing
64041
65388
  * the old server) and emits dashboard.lifecycle_started if found.
@@ -64061,35 +65408,25 @@ async function processPendingLifecycle(options) {
64061
65408
  } catch (err) {
64062
65409
  console.warn(`[overdeck] Failed to process restart marker: ${err.message}`);
64063
65410
  }
64064
- if (!existsSync(pendingFile)) return;
64065
65411
  const startTime = Date.now();
64066
65412
  try {
64067
- const raw = await readFile$1(pendingFile, "utf-8");
64068
- await unlink$1(pendingFile);
64069
- const pending = JSON.parse(raw);
65413
+ const claim = await claimPendingLifecycleFile(pendingFile);
65414
+ if (!claim) return;
65415
+ let pending;
65416
+ try {
65417
+ pending = JSON.parse(claim.raw);
65418
+ } catch (error) {
65419
+ await claim.discard();
65420
+ throw error;
65421
+ }
64070
65422
  const age = (options?.now ?? Date.now()) - (pending.timestamp ?? 0);
64071
65423
  if (age > staleThresholdMs) {
65424
+ await claim.discard();
64072
65425
  console.warn(`[overdeck] Ignoring stale pending-post-merge.json (age: ${Math.round(age / 6e4)}min) for ${pending.issueId}`);
64073
65426
  return;
64074
65427
  }
64075
- console.log(`[overdeck] Found pending post-merge lifecycle for ${pending.issueId} — scheduling in ${lifecycleDelayMs}ms`);
64076
- setTimeout(async () => {
64077
- try {
64078
- await runner(pending);
64079
- emitDashboardLifecycleSync("completed", {
64080
- reason: pending.reason ?? "post-merge",
64081
- issueId: pending.issueId,
64082
- durationMs: Date.now() - startTime
64083
- });
64084
- } catch (err) {
64085
- console.error(`[overdeck] Post-merge lifecycle failed for ${pending.issueId}: ${err.message}`);
64086
- emitDashboardLifecycleSync("failed", {
64087
- reason: pending.reason ?? "post-merge",
64088
- issueId: pending.issueId,
64089
- error: err.message
64090
- });
64091
- }
64092
- }, lifecycleDelayMs);
65428
+ console.log(`[overdeck] Found pending post-merge lifecycle for ${pending.issueId} — running in ${lifecycleDelayMs}ms`);
65429
+ runClaimedPendingLifecycle(claim, pending, runner, lifecycleDelayMs, startTime);
64093
65430
  } catch (err) {
64094
65431
  console.warn(`[overdeck] Failed to process pending-post-merge.json: ${err.message}`);
64095
65432
  }
@@ -64337,6 +65674,8 @@ const cleanupClosedIssueAgentDirectories = (options) => Effect.tryPromise({
64337
65674
  //#region services/auto-merge-executor.ts
64338
65675
  init_activity_logger();
64339
65676
  init_merge_sync();
65677
+ init_deploy_queue();
65678
+ init_review_status();
64340
65679
  const AUTO_MERGE_EXECUTOR_INTERVAL_MS = 3e4;
64341
65680
  const REQUEUE_BACKOFF_MS = 6e4;
64342
65681
  let timer$1 = null;
@@ -64349,7 +65688,7 @@ function failureReason(result) {
64349
65688
  return result.error ?? result.message ?? `merge returned status ${result.statusCode ?? "unknown"}`;
64350
65689
  }
64351
65690
  async function defaultMergeIssue(issueId) {
64352
- const { triggerMerge } = await import("./merge-ops-CR44oU9-.js");
65691
+ const { triggerMerge } = await import("./merge-ops-DdpTiEMt.js");
64353
65692
  return triggerMerge(issueId);
64354
65693
  }
64355
65694
  function defaultAnnounceFailure(issueId, reason) {
@@ -64371,6 +65710,10 @@ async function tickAutoMergeExecutor(deps = {}) {
64371
65710
  log("[auto-merge] flywheel paused, skipping tick");
64372
65711
  return;
64373
65712
  }
65713
+ if (await (deps.hasPendingDeploy ?? (async () => await readPendingDeploy() !== null))()) {
65714
+ log(`[auto-merge] dashboard deploy queued, deferring ${entries.length} merge(s) before preparation`);
65715
+ return;
65716
+ }
64374
65717
  for (const entry of entries) {
64375
65718
  if (isPaused()) {
64376
65719
  log("[auto-merge] flywheel paused, skipping tick");
@@ -64397,6 +65740,25 @@ async function tickAutoMergeExecutor(deps = {}) {
64397
65740
  continue;
64398
65741
  }
64399
65742
  const reason = failureReason(result);
65743
+ if (result.deferred) {
65744
+ const retryAt = new Date(nowDate.getTime() + REQUEUE_BACKOFF_MS).toISOString();
65745
+ log((deps.requeueToPending ?? requeueToPending)(entry.id, retryAt) ? `[auto-merge] merge deferred for ${entry.issueId}; requeued without consuming retry budget for ${retryAt}` : `[auto-merge] failed to requeue deferred merge for ${entry.issueId} (#${entry.id})`);
65746
+ continue;
65747
+ }
65748
+ if (result.retryable) {
65749
+ const retryCount = (deps.getMergeRetryCount ?? ((issueId) => getReviewStatusSync(issueId)?.mergeRetryCount ?? 0))(entry.issueId);
65750
+ if (retryCount >= 3) {
65751
+ const blockedReason = `Auto-merge for ${entry.issueId} blocked: ${reason} (retried ${retryCount} times — fix the underlying cause and re-schedule)`;
65752
+ if ((deps.markMergingBlocked ?? markMergingBlocked)(entry.id, blockedReason)) (deps.announceFailure ?? defaultAnnounceFailure)(entry.issueId, blockedReason);
65753
+ else log(`[auto-merge] lost circuit-breaker block race for ${entry.issueId} (#${entry.id}), skipping announcement`);
65754
+ } else {
65755
+ const nextRetryCount = retryCount + 1;
65756
+ (deps.setMergeRetryCount ?? ((issueId, count) => setReviewStatusSync(issueId, { mergeRetryCount: count })))(entry.issueId, nextRetryCount);
65757
+ const retryAt = new Date(nowDate.getTime() + REQUEUE_BACKOFF_MS).toISOString();
65758
+ log((deps.requeueToPending ?? requeueToPending)(entry.id, retryAt) ? `[auto-merge] retryable merge failure for ${entry.issueId}; requeued attempt ${nextRetryCount}/3 for ${retryAt}` : `[auto-merge] failed to requeue retryable merge for ${entry.issueId} (#${entry.id})`);
65759
+ }
65760
+ continue;
65761
+ }
64400
65762
  (deps.markFailed ?? markFailed)(entry.id, reason);
64401
65763
  (deps.announceFailure ?? defaultAnnounceFailure)(entry.issueId, reason);
64402
65764
  } catch (error) {
@@ -64561,17 +65923,46 @@ function createOverdeckDatabase(options = {}) {
64561
65923
  try {
64562
65924
  applyMigration(db, migrationPath);
64563
65925
  const tables = userTableNames(db);
64564
- if (tables.length !== 32) throw new Error(`Expected 32 overdeck tables, found ${tables.length}: ${tables.join(", ")}`);
65926
+ if (tables.length !== 34) throw new Error(`Expected 34 overdeck tables, found ${tables.length}: ${tables.join(", ")}`);
64565
65927
  assertEmptyTables(db, tables);
64566
65928
  } finally {
64567
65929
  db.close();
64568
65930
  }
64569
65931
  return {
64570
65932
  dbPath,
64571
- tableCount: 32
65933
+ tableCount: 34
64572
65934
  };
64573
65935
  }
64574
65936
  //#endregion
65937
+ //#region telemetry.ts
65938
+ init_agents$1();
65939
+ init_projects();
65940
+ init_service();
65941
+ const defaultDeps = {
65942
+ analytics: getAnalyticsService("server"),
65943
+ listProjects: listProjectsSync,
65944
+ listAgents: () => Effect.runPromise(listRunningAgents())
65945
+ };
65946
+ function bucketServerCount(value) {
65947
+ if (value <= 0) return "0";
65948
+ if (value <= 2) return "1-2";
65949
+ if (value <= 5) return "3-5";
65950
+ if (value <= 10) return "6-10";
65951
+ return "11+";
65952
+ }
65953
+ async function captureServerBootTelemetry(deps = defaultDeps) {
65954
+ try {
65955
+ const [projects, agents] = await Promise.all([Promise.resolve(deps.listProjects()), deps.listAgents()]);
65956
+ deps.analytics.capture("server_boot", {
65957
+ project_count: bucketServerCount(projects.length),
65958
+ active_agent_count: bucketServerCount(agents.filter((agent) => agent.tmuxActive).length)
65959
+ });
65960
+ } catch {}
65961
+ }
65962
+ function startServerBootTelemetry(deps = defaultDeps) {
65963
+ captureServerBootTelemetry(deps);
65964
+ }
65965
+ //#endregion
64575
65966
  //#region ../../lib/state-auto-migrate.ts
64576
65967
  init_projects();
64577
65968
  init_state_home();
@@ -64596,7 +65987,7 @@ const defaultDependencies = {
64596
65987
  inspect: inspectStateMigration,
64597
65988
  ensureWorktree: ensureStateWorktree,
64598
65989
  migrate: async (projectKey, project, signal) => {
64599
- const { migrateProjectState } = await import("./state-migrate-BGg2ZaOt.js");
65990
+ const { migrateProjectState } = await import("./state-migrate-CvvBtS0Y.js");
64600
65991
  await migrateProjectState(projectKey, { signal }, project);
64601
65992
  },
64602
65993
  clearCache: clearStateMigrationCache,
@@ -64728,7 +66119,7 @@ init_internal_token();
64728
66119
  init_review_status();
64729
66120
  init_event_store();
64730
66121
  init_activity_logger();
64731
- init_config$1();
66122
+ init_config$2();
64732
66123
  init_agents$1();
64733
66124
  init_paths();
64734
66125
  init_tmux();
@@ -64763,6 +66154,7 @@ if (typeof Bun !== "undefined") {
64763
66154
  runMain(main);
64764
66155
  }
64765
66156
  await whenDashboardListening();
66157
+ startServerBootTelemetry();
64766
66158
  startEventLoopMonitor();
64767
66159
  console.log("[overdeck] Event loop delay monitor started");
64768
66160
  warnIfAutonomousMergeBackendUnavailable();
@@ -64798,13 +66190,15 @@ startAgentOutputService();
64798
66190
  console.log("[overdeck] AgentOutputService started");
64799
66191
  startMergeBlockerReconcileService();
64800
66192
  console.log("[overdeck] MergeBlockerReconcileService started");
66193
+ if (startReviewStatusReconcileService()) console.log("[overdeck] ReviewStatusReconcileService started");
66194
+ else console.log("[overdeck] ReviewStatusReconcileService skipped — non-primary dashboard process");
64801
66195
  if (shouldStartStaleCheckRetriggerService()) {
64802
66196
  startStaleCheckRetriggerService();
64803
66197
  console.log("[overdeck] StaleCheckRetriggerService started");
64804
66198
  }
64805
66199
  if (process.env.OVERDECK_MODE === "desktop") (async () => {
64806
66200
  try {
64807
- const { provisionClaudeHooks } = await import("./claude-hooks-provision-8Ngo8TpT.js");
66201
+ const { provisionClaudeHooks } = await import("./claude-hooks-provision-CN-xo-do.js");
64808
66202
  const result = await provisionClaudeHooks();
64809
66203
  if (!result.ok) console.warn(`[overdeck] Claude hook provisioning skipped: ${result.reason}`);
64810
66204
  else if (result.changed) console.log(`[overdeck] Claude hooks provisioned: ${result.binariesSynced} scripts, registered ${result.registered.length} hook(s)${result.pruned.length ? `, pruned ${result.pruned.length} stale` : ""}`);
@@ -64816,21 +66210,11 @@ if (process.env.OVERDECK_MODE === "desktop") (async () => {
64816
66210
  setPipelineHandlerSync((event) => {
64817
66211
  switch (event.type) {
64818
66212
  case "status_changed":
64819
- (async () => {
64820
- try {
64821
- const enriched = await Effect.runPromise(enrichReviewStatus(event.issueId, event.status));
64822
- getEventStore().append({
64823
- type: "review.status_changed",
64824
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
64825
- payload: {
64826
- issueId: event.issueId,
64827
- status: enriched
64828
- }
64829
- });
64830
- } catch (err) {
64831
- console.error("[pipeline] Failed to append status_changed event:", err);
64832
- }
64833
- })();
66213
+ try {
66214
+ emitReviewStatusChanged((domainEvent) => getEventStore().append(domainEvent), event.issueId, event.status);
66215
+ } catch (err) {
66216
+ console.error("[pipeline] Failed to append status_changed event:", err);
66217
+ }
64834
66218
  return;
64835
66219
  case "review.approved":
64836
66220
  case "test.passed":
@@ -65012,21 +66396,11 @@ console.log("[overdeck] Agent stopped/status notifiers → domain events wired")
65012
66396
  setMergeReadyNotifier((issueId) => {
65013
66397
  const status = getReviewStatusSync(issueId);
65014
66398
  if (!status) return;
65015
- (async () => {
65016
- try {
65017
- const enriched = await Effect.runPromise(enrichReviewStatus(issueId, status));
65018
- getEventStore().append({
65019
- type: "review.status_changed",
65020
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
65021
- payload: {
65022
- issueId,
65023
- status: enriched
65024
- }
65025
- });
65026
- } catch (err) {
65027
- console.error("[pipeline] Failed to append merge-ready event:", err);
65028
- }
65029
- })();
66399
+ try {
66400
+ emitReviewStatusChanged((domainEvent) => getEventStore().append(domainEvent), issueId, status);
66401
+ } catch (err) {
66402
+ console.error("[pipeline] Failed to append merge-ready event:", err);
66403
+ }
65030
66404
  });
65031
66405
  console.log("[overdeck] Merge-ready notifier → domain events wired");
65032
66406
  startConversationLifecycleService();
@@ -65148,7 +66522,7 @@ const handleShutdownSignal = async (signal) => {
65148
66522
  await stopAllKnowledgeViewers().catch((err) => console.warn("[knowledge-viewer] shutdown failed:", err?.message ?? err));
65149
66523
  await stopDeaconChild().catch((err) => console.warn("[deacon-supervisor] child shutdown failed:", err?.message ?? err));
65150
66524
  try {
65151
- const { killAllGateProcessGroups } = await import("./validation-CC9OPqJZ.js");
66525
+ const { killAllGateProcessGroups } = await import("./validation-DEaTZafj.js");
65152
66526
  const reaped = killAllGateProcessGroups();
65153
66527
  if (reaped > 0) console.log(`[overdeck] reaped ${reaped} in-flight gate process group(s)`);
65154
66528
  } catch (err) {
@@ -65203,7 +66577,7 @@ reconcileStaleGitHubBlockers().then((n) => {
65203
66577
  }
65204
66578
  }).catch((err) => console.warn(`[overdeck] Startup blocker reconciliation failed: ${err.message}`));
65205
66579
  try {
65206
- const { resetProcessingToQueued } = await import("./merge-sync-BJYeOODr.js");
66580
+ const { resetProcessingToQueued } = await import("./merge-sync-Ca7F5JnQ.js");
65207
66581
  const resetCount = resetProcessingToQueued();
65208
66582
  if (resetCount > 0) {
65209
66583
  console.log(`[overdeck] Reset ${resetCount} stuck merge queue entries to queued`);
@@ -65225,7 +66599,7 @@ else {
65225
66599
  console.log("[overdeck] Auto-merge executor started");
65226
66600
  }
65227
66601
  try {
65228
- const { reconcileInterruptedVerifications } = await import("./verification-runner-BW4HOKAz.js");
66602
+ const { reconcileInterruptedVerifications } = await import("./verification-runner-DlZI8yhw.js");
65229
66603
  const interrupted = reconcileInterruptedVerifications();
65230
66604
  if (interrupted > 0) console.log(`[overdeck] recovered ${interrupted} orphaned verification worker(s)`);
65231
66605
  } catch (err) {