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