@overdeck/core 0.50.3 → 0.50.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents-DNMRPC8n.js.map +1 -1
- package/dist/{build-info-BJuEVWOh.js → build-info-ioRdOmqT.js} +3 -3
- package/dist/{build-info-BJuEVWOh.js.map → build-info-ioRdOmqT.js.map} +1 -1
- package/dist/build-info-kMMitDOP.js +2 -0
- package/dist/cli/index.js +4 -4
- package/dist/conversation-compaction-GFfO5UZL.js.map +1 -1
- package/dist/dashboard/{advancing-selfheal-Bq8V0QUa.js → advancing-selfheal-Dp5_Gai3.js} +3 -3
- package/dist/dashboard/{advancing-selfheal-Bq8V0QUa.js.map → advancing-selfheal-Dp5_Gai3.js.map} +1 -1
- package/dist/dashboard/{agent-gc-Coz8zQt_.js → agent-gc-JzoSs5Pi.js} +3 -3
- package/dist/dashboard/{agent-gc-Coz8zQt_.js.map → agent-gc-JzoSs5Pi.js.map} +1 -1
- package/dist/dashboard/{agent-idle-CAwB-K6M.js → agent-idle-D-Qy5BN_.js} +2 -2
- package/dist/dashboard/{agent-idle-CAwB-K6M.js.map → agent-idle-D-Qy5BN_.js.map} +1 -1
- package/dist/dashboard/{agent-state-Ci6PhjOE.js → agent-state-B196fj1h.js} +38 -16
- package/dist/dashboard/agent-state-B196fj1h.js.map +1 -0
- package/dist/dashboard/{agent-state-CbUEwUEQ.js → agent-state-IpWnw8lv.js} +1 -1
- package/dist/dashboard/{agent-state-service-D6IVrX3B.js → agent-state-service-CCo5-9DR.js} +3 -3
- package/dist/dashboard/{agent-state-service-D6IVrX3B.js.map → agent-state-service-CCo5-9DR.js.map} +1 -1
- package/dist/dashboard/{agent-state-service-DcN1j6eK.js → agent-state-service-D_F3VZ23.js} +1 -1
- package/dist/dashboard/{agents-BVvL4o_4.js → agents-CkGhnAmu.js} +1 -1
- package/dist/dashboard/{backlog-auto-trigger-BuSBqG2V.js → backlog-auto-trigger-B5Z2zBtC.js} +2 -2
- package/dist/dashboard/{backlog-auto-trigger-BuSBqG2V.js.map → backlog-auto-trigger-B5Z2zBtC.js.map} +1 -1
- package/dist/dashboard/build-info-BTjOB0Xr.js +2 -0
- package/dist/dashboard/{build-info-Cj3Y0K87.js → build-info-D0EJNj6C.js} +3 -3
- package/dist/dashboard/{build-info-Cj3Y0K87.js.map → build-info-D0EJNj6C.js.map} +1 -1
- package/dist/dashboard/cli-CIznFrcE.js +2 -0
- package/dist/dashboard/{cli-DIhQieif.js → cli-DCbVrSsg.js} +2 -2
- package/dist/dashboard/{cli-DIhQieif.js.map → cli-DCbVrSsg.js.map} +1 -1
- package/dist/dashboard/{close-issue-CRlmrE5g.js → close-issue-Bmu9aOIx.js} +2 -2
- package/dist/dashboard/{close-issue-CRlmrE5g.js.map → close-issue-Bmu9aOIx.js.map} +1 -1
- package/dist/dashboard/{closed-issue-reaper-DrHyYgJR.js → closed-issue-reaper-COxfTtLz.js} +5 -5
- package/dist/dashboard/{closed-issue-reaper-DrHyYgJR.js.map → closed-issue-reaper-COxfTtLz.js.map} +1 -1
- package/dist/dashboard/{closed-issue-reaper-DWlw2EtL.js → closed-issue-reaper-DwMwG_Pz.js} +1 -1
- package/dist/dashboard/{compaction-continuation-DhYgsXmF.js → compaction-continuation-Bqc212Zm.js} +2 -2
- package/dist/dashboard/{compaction-continuation-DhYgsXmF.js.map → compaction-continuation-Bqc212Zm.js.map} +1 -1
- package/dist/dashboard/{conflict-gate-vczgdVTa.js → conflict-gate-9mPVg7-5.js} +1 -1
- package/dist/dashboard/{conflict-gate-Bt-lLgYf.js → conflict-gate-CGQTcR0I.js} +3 -3
- package/dist/dashboard/{conflict-gate-Bt-lLgYf.js.map → conflict-gate-CGQTcR0I.js.map} +1 -1
- package/dist/dashboard/{conversation-compaction-CsdCbcQB.js → conversation-compaction-C7P6V8Iw.js} +5 -5
- package/dist/dashboard/{conversation-compaction-CsdCbcQB.js.map → conversation-compaction-C7P6V8Iw.js.map} +1 -1
- package/dist/dashboard/{conversation-compaction-D0zgWM1f.js → conversation-compaction-Cck5oi-V.js} +1 -1
- package/dist/dashboard/{conversation-service-6LaymUV-.js → conversation-service-CsOpwI9Y.js} +37 -5
- package/dist/dashboard/{conversation-service-6LaymUV-.js.map → conversation-service-CsOpwI9Y.js.map} +1 -1
- package/dist/dashboard/conversation-service-plOFkoMo.js +3 -0
- package/dist/dashboard/dashboard-db-worker.js +1 -1
- package/dist/dashboard/{deacon-CU47dU7U.js → deacon-B8oMMIgN.js} +62 -62
- package/dist/dashboard/{deacon-CU47dU7U.js.map → deacon-B8oMMIgN.js.map} +1 -1
- package/dist/dashboard/{deacon-DYsOa_ss.js → deacon-b7J57Svt.js} +2 -2
- package/dist/dashboard/{deacon-pending-lifecycle-ijGX8wLb.js → deacon-pending-lifecycle-3NIJ7ew5.js} +2 -2
- package/dist/dashboard/{deacon-pending-lifecycle-ijGX8wLb.js.map → deacon-pending-lifecycle-3NIJ7ew5.js.map} +1 -1
- package/dist/dashboard/deacon.js +5 -5
- package/dist/dashboard/{delivery-DMYxIE0J.js → delivery-lnI-gQKo.js} +1 -1
- package/dist/dashboard/{deploy-patrol-C4eXco1S.js → deploy-patrol-u3v2krg9.js} +2 -2
- package/dist/dashboard/{deploy-patrol-C4eXco1S.js.map → deploy-patrol-u3v2krg9.js.map} +1 -1
- package/dist/dashboard/{feedback-stuck-retirement-Dd5XNrMr.js → feedback-stuck-retirement-CoFNqjCc.js} +2 -2
- package/dist/dashboard/{feedback-stuck-retirement-Dd5XNrMr.js.map → feedback-stuck-retirement-CoFNqjCc.js.map} +1 -1
- package/dist/dashboard/{feedback-target-BzdJ2wIR.js → feedback-target-BBCh3RRq.js} +1 -1
- package/dist/dashboard/{feedback-target-BcsA_GRV.js → feedback-target-CeCq6EhW.js} +5 -5
- package/dist/dashboard/{feedback-target-BcsA_GRV.js.map → feedback-target-CeCq6EhW.js.map} +1 -1
- package/dist/dashboard/{flywheel-Is3w2lcT.js → flywheel-DKldI63J.js} +7 -7
- package/dist/dashboard/{flywheel-Is3w2lcT.js.map → flywheel-DKldI63J.js.map} +1 -1
- package/dist/dashboard/{flywheel-D0guNqkh.js → flywheel-DpRPLErh.js} +7 -7
- package/dist/dashboard/{flywheel-D0guNqkh.js.map → flywheel-DpRPLErh.js.map} +1 -1
- package/dist/dashboard/{flywheel-B34cdfbd.js → flywheel-jeZFYvm-.js} +1 -1
- package/dist/dashboard/{flywheel-merge-order-6HiPR2WG.js → flywheel-merge-order-0OnoosaO.js} +1 -1
- package/dist/dashboard/{flywheel-merge-order-BsNPMu0S.js → flywheel-merge-order-PG5op4j7.js} +2 -2
- package/dist/dashboard/{flywheel-merge-order-BsNPMu0S.js.map → flywheel-merge-order-PG5op4j7.js.map} +1 -1
- package/dist/dashboard/{harness-resolve-CzTFVZ5l.js → harness-resolve-C0HWj3H9.js} +2 -2
- package/dist/dashboard/{harness-resolve-CzTFVZ5l.js.map → harness-resolve-C0HWj3H9.js.map} +1 -1
- package/dist/dashboard/{identity-Dc7rQLln.js → identity-Cot1Hg4G.js} +2 -2
- package/dist/dashboard/{identity-Dc7rQLln.js.map → identity-Cot1Hg4G.js.map} +1 -1
- package/dist/dashboard/{inspect-agent-CP7Bu47O.js → inspect-agent-BDmLWbGx.js} +3 -3
- package/dist/dashboard/{inspect-agent-CP7Bu47O.js.map → inspect-agent-BDmLWbGx.js.map} +1 -1
- package/dist/dashboard/inspect-agent-BcOkA-hC.js +2 -0
- package/dist/dashboard/{issue-lifecycle-BPbczKyy.js → issue-lifecycle-1B_v8Rf5.js} +6 -6
- package/dist/dashboard/{issue-lifecycle-BPbczKyy.js.map → issue-lifecycle-1B_v8Rf5.js.map} +1 -1
- package/dist/dashboard/{issue-lifecycle-FaugTi39.js → issue-lifecycle-CIrfbIbj.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-Dsad9NTt.js → issue-service-singleton-BVwPzjwF.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-BrZQJLRI.js → issue-service-singleton-KcRrOiFD.js} +2 -2
- package/dist/dashboard/{issue-service-singleton-BrZQJLRI.js.map → issue-service-singleton-KcRrOiFD.js.map} +1 -1
- package/dist/dashboard/{jsonl-resolver-DQEe0DHM.js → jsonl-resolver-jxbif7MD.js} +3 -3
- package/dist/dashboard/{jsonl-resolver-DQEe0DHM.js.map → jsonl-resolver-jxbif7MD.js.map} +1 -1
- package/dist/dashboard/{kimi-code-Cx5YUJpS.js → kimi-code-BZh3NgcN.js} +1 -1
- package/dist/dashboard/{lifecycle-DZFHmpGI.js → lifecycle-CvcZ4_fA.js} +4 -4
- package/dist/dashboard/{lifecycle-DZFHmpGI.js.map → lifecycle-CvcZ4_fA.js.map} +1 -1
- package/dist/dashboard/{linear-mcp-auth-DylPJABZ.js → linear-mcp-auth-CZoj_-d7.js} +1 -1
- package/dist/dashboard/{linear-mcp-auth-BFlpMvWh.js → linear-mcp-auth-Dx74yCAY.js} +2 -2
- package/dist/dashboard/{linear-mcp-auth-BFlpMvWh.js.map → linear-mcp-auth-Dx74yCAY.js.map} +1 -1
- package/dist/dashboard/{memory-governor-B7uDWRo9.js → memory-governor-Bv3LAqk3.js} +5 -5
- package/dist/dashboard/{memory-governor-B7uDWRo9.js.map → memory-governor-Bv3LAqk3.js.map} +1 -1
- package/dist/dashboard/{merge-agent-ddEcZDZD.js → merge-agent-1zVLl6k_.js} +1 -1
- package/dist/dashboard/{merge-agent-ttRY053V.js → merge-agent-8zrQu48t.js} +8 -8
- package/dist/dashboard/{merge-agent-ttRY053V.js.map → merge-agent-8zrQu48t.js.map} +1 -1
- package/dist/dashboard/merge-ops-CFJaMPLu.js +2 -0
- package/dist/dashboard/{merge-train-f_un38UJ.js → merge-train-BYH_cF7G.js} +2 -2
- package/dist/dashboard/{merge-train-f_un38UJ.js.map → merge-train-BYH_cF7G.js.map} +1 -1
- package/dist/dashboard/{merge-train-deps-GPlHOjK6.js → merge-train-deps-BTfXM5AR.js} +2 -2
- package/dist/dashboard/{merge-train-deps-GPlHOjK6.js.map → merge-train-deps-BTfXM5AR.js.map} +1 -1
- package/dist/dashboard/{messaging-BasbftDu.js → messaging-Bk6vGsMw.js} +1 -1
- package/dist/dashboard/{orphan-proposed-reconciler-DFHdQSEg.js → orphan-proposed-reconciler-B898fk26.js} +6 -6
- package/dist/dashboard/{orphan-proposed-reconciler-DFHdQSEg.js.map → orphan-proposed-reconciler-B898fk26.js.map} +1 -1
- package/dist/dashboard/{orphan-proposed-reconciler-DzAxrM99.js → orphan-proposed-reconciler-Bx_aWS4B.js} +1 -1
- package/dist/dashboard/{parked-residue-DMtbxJml.js → parked-residue-q2kzrYSm.js} +9 -9
- package/dist/dashboard/{parked-residue-DMtbxJml.js.map → parked-residue-q2kzrYSm.js.map} +1 -1
- package/dist/dashboard/{post-merge-lifecycle-worker-jzN2g_EY.js → post-merge-lifecycle-worker-Cl7-uf4g.js} +2 -2
- package/dist/dashboard/{post-merge-lifecycle-worker-jzN2g_EY.js.map → post-merge-lifecycle-worker-Cl7-uf4g.js.map} +1 -1
- package/dist/dashboard/{project-resource-refresh-queue-cqOeJ0fB.js → project-resource-refresh-queue-O_dRfPHn.js} +1 -1
- package/dist/dashboard/{project-resource-refresh-queue-DebwjPPA.js → project-resource-refresh-queue-se3T9IYP.js} +4 -4
- package/dist/dashboard/{project-resource-refresh-queue-DebwjPPA.js.map → project-resource-refresh-queue-se3T9IYP.js.map} +1 -1
- package/dist/dashboard/{reconstruct-cache-Bb2Jg2DZ.js → reconstruct-cache-XpWC9L0F.js} +2 -2
- package/dist/dashboard/{reconstruct-cache-Bb2Jg2DZ.js.map → reconstruct-cache-XpWC9L0F.js.map} +1 -1
- package/dist/dashboard/{release-engine-78iCajIc.js → release-engine-BuzxOamf.js} +2 -2
- package/dist/dashboard/{release-engine-78iCajIc.js.map → release-engine-BuzxOamf.js.map} +1 -1
- package/dist/dashboard/{remote-completion-D5wzZCte.js → remote-completion-BCZgeKPS.js} +3 -3
- package/dist/dashboard/{remote-completion-D5wzZCte.js.map → remote-completion-BCZgeKPS.js.map} +1 -1
- package/dist/dashboard/{resolver-BGZh3lXA.js → resolver-BeNrqNou.js} +5 -5
- package/dist/dashboard/{resolver-BGZh3lXA.js.map → resolver-BeNrqNou.js.map} +1 -1
- package/dist/dashboard/{resource-pressure-patrol-Bg23rPV-.js → resource-pressure-patrol-Ccg_aCgO.js} +3 -3
- package/dist/dashboard/{resource-pressure-patrol-Bg23rPV-.js.map → resource-pressure-patrol-Ccg_aCgO.js.map} +1 -1
- package/dist/dashboard/{resume-BKyB4hA1.js → resume-KGCrn0DV.js} +1 -1
- package/dist/dashboard/{review-agent-ZdP-HmwY.js → review-agent-BmM6YMZW.js} +1 -1
- package/dist/dashboard/{review-agent-DHs-JngN.js → review-agent-DRWVU8fP.js} +13 -13
- package/dist/dashboard/{review-agent-DHs-JngN.js.map → review-agent-DRWVU8fP.js.map} +1 -1
- package/dist/dashboard/{review-convoy-liveness-BTMvU4CA.js → review-convoy-liveness-EvCDv8Q_.js} +3 -3
- package/dist/dashboard/{review-convoy-liveness-BTMvU4CA.js.map → review-convoy-liveness-EvCDv8Q_.js.map} +1 -1
- package/dist/dashboard/{review-run-recovery-BYalQzbh.js → review-run-recovery-Ctm48Snt.js} +1 -1
- package/dist/dashboard/{review-run-recovery-BvVG3oUr.js → review-run-recovery-DNDI58cM.js} +2 -2
- package/dist/dashboard/{review-run-recovery-BvVG3oUr.js.map → review-run-recovery-DNDI58cM.js.map} +1 -1
- package/dist/dashboard/{review-status-DXTFzCmR.js → review-status-FnlvQuPI.js} +1 -1
- package/dist/dashboard/{review-status-emit-C9YW6SQL.js → review-status-emit-CeMS31DX.js} +2 -2
- package/dist/dashboard/{review-status-emit-C9YW6SQL.js.map → review-status-emit-CeMS31DX.js.map} +1 -1
- package/dist/dashboard/{review-status-BegAr86G.js → review-status-yY5qt7Rl.js} +5 -5
- package/dist/dashboard/{review-status-BegAr86G.js.map → review-status-yY5qt7Rl.js.map} +1 -1
- package/dist/dashboard/{review-verdict-feedback-qlVBi19-.js → review-verdict-feedback-BNgRj1e3.js} +4 -4
- package/dist/dashboard/{review-verdict-feedback-qlVBi19-.js.map → review-verdict-feedback-BNgRj1e3.js.map} +1 -1
- package/dist/dashboard/{review-verdict-feedback-Dp139Lmv.js → review-verdict-feedback-D39cuSV1.js} +1 -1
- package/dist/dashboard/{review-verdict-writer-CJr_Ob0Y.js → review-verdict-writer-DvFfKAn-.js} +2 -2
- package/dist/dashboard/{review-verdict-writer-CJr_Ob0Y.js.map → review-verdict-writer-DvFfKAn-.js.map} +1 -1
- package/dist/dashboard/{review-verdict-writer-BVco3WoI.js → review-verdict-writer-PcGBkvJ4.js} +1 -1
- package/dist/dashboard/{sequence-io-iljWlslY.js → sequence-io-DZyschsf.js} +2 -2
- package/dist/dashboard/{sequence-io-iljWlslY.js.map → sequence-io-DZyschsf.js.map} +1 -1
- package/dist/dashboard/{sequencer-agent-BGGVKxtw.js → sequencer-agent-BXDw5cNx.js} +5 -5
- package/dist/dashboard/{sequencer-agent-BGGVKxtw.js.map → sequencer-agent-BXDw5cNx.js.map} +1 -1
- package/dist/dashboard/server.js +79 -72
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/{service-kzhN0DLg.js → service-DP50Omvn.js} +33 -33
- package/dist/dashboard/{service-kzhN0DLg.js.map → service-DP50Omvn.js.map} +1 -1
- package/dist/dashboard/{smart-compaction-owKHJDbL.js → smart-compaction-944GL8PN.js} +1 -1
- package/dist/dashboard/{smart-compaction-C96F2aFQ.js → smart-compaction-BzDfF6yb.js} +2 -2
- package/dist/dashboard/{smart-compaction-C96F2aFQ.js.map → smart-compaction-BzDfF6yb.js.map} +1 -1
- package/dist/dashboard/{spawn-planning-session-CiJdOHTD.js → spawn-planning-session-BcVnHJjm.js} +1 -1
- package/dist/dashboard/{spawn-planning-session-FmJa6PCS.js → spawn-planning-session-CH8IYli7.js} +3 -3
- package/dist/dashboard/{spawn-planning-session-FmJa6PCS.js.map → spawn-planning-session-CH8IYli7.js.map} +1 -1
- package/dist/dashboard/{specialist-context-_ootmzn2.js → specialist-context-DhDTXVht.js} +3 -3
- package/dist/dashboard/{specialist-context-_ootmzn2.js.map → specialist-context-DhDTXVht.js.map} +1 -1
- package/dist/dashboard/{specialist-logs-BbAX221-.js → specialist-logs-C1bcnZ-G.js} +2 -2
- package/dist/dashboard/{specialist-logs-BbAX221-.js.map → specialist-logs-C1bcnZ-G.js.map} +1 -1
- package/dist/dashboard/{specialists-COZoYaiz.js → specialists-BKmReEWT.js} +53 -53
- package/dist/dashboard/{specialists-COZoYaiz.js.map → specialists-BKmReEWT.js.map} +1 -1
- package/dist/dashboard/{specialists-RPLJnYc1.js → specialists-Bvrvt0br.js} +12 -12
- package/dist/dashboard/{specialists-RPLJnYc1.js.map → specialists-Bvrvt0br.js.map} +1 -1
- package/dist/dashboard/{specialists-DTAOVZpU.js → specialists-CD6GEKXk.js} +1 -1
- package/dist/dashboard/{specialists-D8sYZnPb.js → specialists-eFWpJRBc.js} +1 -1
- package/dist/dashboard/{stall-sweeper-9bRBZhrQ.js → stall-sweeper-D3fZvg7O.js} +3 -3
- package/dist/dashboard/{stall-sweeper-9bRBZhrQ.js.map → stall-sweeper-D3fZvg7O.js.map} +1 -1
- package/dist/dashboard/{state-migrate-J3tFJnbd.js → state-migrate-KZ2goscQ.js} +2 -2
- package/dist/dashboard/{state-migrate-J3tFJnbd.js.map → state-migrate-KZ2goscQ.js.map} +1 -1
- package/dist/dashboard/{stuck-remediation-BvTK_NYN.js → stuck-remediation-Cb2V1roj.js} +1 -1
- package/dist/dashboard/{stuck-remediation-4Rf_xMEb.js → stuck-remediation-CnoV0kcv.js} +5 -5
- package/dist/dashboard/{stuck-remediation-4Rf_xMEb.js.map → stuck-remediation-CnoV0kcv.js.map} +1 -1
- package/dist/dashboard/{summary-fork-PLcckTRE.js → summary-fork-CxuNBM0F.js} +2 -2
- package/dist/dashboard/{summary-fork-D-cylknJ.js → summary-fork-lC2vMXly.js} +4 -4
- package/dist/dashboard/{summary-fork-D-cylknJ.js.map → summary-fork-lC2vMXly.js.map} +1 -1
- package/dist/dashboard/{teardown-workspace-CTL16rnG.js → teardown-workspace-C-9murBw.js} +4 -4
- package/dist/dashboard/{teardown-workspace-CTL16rnG.js.map → teardown-workspace-C-9murBw.js.map} +1 -1
- package/dist/dashboard/{test-agent-queue-CMgYZiR_.js → test-agent-queue-YCBRzaAB.js} +3 -3
- package/dist/dashboard/{test-agent-queue-CMgYZiR_.js.map → test-agent-queue-YCBRzaAB.js.map} +1 -1
- package/dist/dashboard/{uat-failure-feedback-DS6W9X3I.js → uat-failure-feedback-B-1mL8Zp.js} +3 -3
- package/dist/dashboard/{uat-failure-feedback-DS6W9X3I.js.map → uat-failure-feedback-B-1mL8Zp.js.map} +1 -1
- package/dist/dashboard/{uat-promote-verification-CRsIHE_q.js → uat-promote-verification-BEBPcpPg.js} +2 -2
- package/dist/dashboard/{uat-promote-verification-CRsIHE_q.js.map → uat-promote-verification-BEBPcpPg.js.map} +1 -1
- package/dist/dashboard/{uat-train-BoA9r6_I.js → uat-train-BFo3OQUB.js} +1 -1
- package/dist/dashboard/{uat-train-Cd8CJeNe.js → uat-train-COgoJomz.js} +8 -8
- package/dist/dashboard/{uat-train-Cd8CJeNe.js.map → uat-train-COgoJomz.js.map} +1 -1
- package/dist/dashboard/{validate-BdmvTIym.js → validate-069_N1e2.js} +2 -2
- package/dist/dashboard/{validate-BdmvTIym.js.map → validate-069_N1e2.js.map} +1 -1
- package/dist/dashboard/{verification-runner-BO0K3xNA.js → verification-runner-CQnZyC5A.js} +1 -1
- package/dist/dashboard/{verification-runner-fEY1Qj-l.js → verification-runner-fy3wNrWn.js} +4 -4
- package/dist/dashboard/{verification-runner-fEY1Qj-l.js.map → verification-runner-fy3wNrWn.js.map} +1 -1
- package/dist/dashboard/workflows-Bk6Kt0ZQ.js +2 -0
- package/dist/dashboard/{workflows-CxVnygYU.js → workflows-N4QMkDzN.js} +10 -10
- package/dist/dashboard/{workflows-CxVnygYU.js.map → workflows-N4QMkDzN.js.map} +1 -1
- package/dist/dashboard/{workspaces-Cpz0xJWQ.js → workspaces-DmoyfHoU.js} +33 -33
- package/dist/dashboard/{workspaces-Cpz0xJWQ.js.map → workspaces-DmoyfHoU.js.map} +1 -1
- package/dist/{lifecycle-C6k0HWEO.js → lifecycle-DCFcjg7y.js} +1 -1
- package/dist/{lifecycle-D5oRpvvB.js → lifecycle-GkaHQJNK.js} +2 -2
- package/dist/{lifecycle-D5oRpvvB.js.map → lifecycle-GkaHQJNK.js.map} +1 -1
- package/dist/{merge-agent-Drufc7tU.js → merge-agent-CogNFasY.js} +2 -2
- package/dist/{merge-agent-Drufc7tU.js.map → merge-agent-CogNFasY.js.map} +1 -1
- package/dist/{merge-agent-CBOVY_eC.js → merge-agent-eteKQ9Rb.js} +1 -1
- package/package.json +1 -1
- package/dist/build-info-BgrwLUy6.js +0 -2
- package/dist/dashboard/agent-state-Ci6PhjOE.js.map +0 -1
- package/dist/dashboard/build-info-CYlxvZSQ.js +0 -2
- package/dist/dashboard/cli-ZAS502uC.js +0 -2
- package/dist/dashboard/conversation-service-Dlks_hSS.js +0 -3
- package/dist/dashboard/inspect-agent-DTuSg95i.js +0 -2
- package/dist/dashboard/merge-ops-Bgc-EScO.js +0 -2
- package/dist/dashboard/workflows-CgGNyl1z.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-state-B196fj1h.js","names":["existsSync","readFileSync","readFileSync","openSync","execFileAsync","existsSync","readFileSync","existsSync","createHash","readdirSync","readFileSync","join","readFile","unlink","dirname","join","readFile","loadYamlConfig","loadYamlConfig","NodeServicesLayer","platform","ChildProcessSpawner","join","SECTION_SEPARATOR","join","join","existsSync","readFileSync","randomUUID","createHash","existsSync","execAsync","shellQuote","openSync","fsConstants","execAsync","homedir","existsSync","resolve","writeFile","renameAsync","execAsync","mkdir","join","loadYamlConfig","httpRequest","basename","dirname","promisify","exec","loadYamlConfig","basename","loadYamlConfig","loadConfigSync","join","join","join","decodeJwtPayload","readFile","stat","open","existsSync","readFileSync","readFileSync","createHash","randomUUID","createReadStream","existsSync","join","httpRequest","dirname","computeCpuPercent","percent","defaultAdapters","execFileAsync","loadavg","totalmem","defaultAdapters","execFileAsync","getPlatform","execFileAsync","join","join","execAsync","promisify","exec","mkdirAsync","writeFileAsync","rm","join","existsSync","execAsync","createHash","existsSync","readdirSync","statSync","statAsync","readdirAsync","homedir","SPAWN_READY_TIMEOUT_MS","readFileAsync","mkdirAsync","writeFileAsync","renameAsync","rmAsync","shellQuote","pollUntilSessionGone","execAsync","readFileSync","join","writeFileAsync","mkdir","writeFile","execAsync","resolve","escapeRegExp","promisify","exec","existsSync","readFileSync","readdirSync","statSync","execAsync","randomUUID","join","join","join","existsSync","readFileSync","hasAgentRuntimeInSession","join","join","join","readdir","stat","execAsync","basename","readFile","promisify","exec","sessionExists","sessionExistsEffect","detectAwaitingInputForAgent","detectAwaitingInputForAgentEffect","countPendingAskUserQuestionsForCurrentAgentSession","countPendingAskUserQuestionsForCurrentAgentSessionEffect","join","readdirAsync","runGitShow","execFileAsync","execFileAsync","defaultSupervisorAgentId","join","readFile","mkdir","writeFile","fetchAgentRuntimeSnapshot","join","join","readdir","mkdirAsync","writeFileAsync"],"sources":["../../src/lib/agent-runtime-mirror.ts","../../src/lib/cloister/auto-resume-config.ts","../../src/lib/overdeck/agent-rollback-state.ts","../../src/lib/overdeck/agent-record-sync.ts","../../src/lib/pan-dir/agents.ts","../../src/lib/agent-runtime.ts","../../src/lib/bundle-imports.ts","../../src/lib/channels/pty-supervisor-locate.ts","../../src/lib/pty-token.ts","../../src/lib/resume-contract.ts","../../src/lib/agents/supervisor-channels.ts","../../src/lib/openai-auth.ts","../../src/lib/provider-health.ts","../../src/lib/agents/provider-env.ts","../../src/lib/claude-auth.ts","../../src/lib/context-layers/assemble.ts","../../src/lib/context-layers/harness.ts","../../src/lib/context-layers/layers.ts","../../src/lib/context-layers/rules.ts","../../src/lib/context-layers/render.ts","../../src/lib/context-layers/materialize.ts","../../src/lib/acp/context.ts","../../src/lib/runtimes/ohmypi-fifo.ts","../../src/lib/runtimes/pi-fifo.ts","../../src/lib/context-layers/launch-sources.ts","../../src/lib/agents/runtime-command.ts","../../src/lib/runtimes/muse-context.ts","../../src/lib/agents/reopen-guard.ts","../../src/lib/agents/resolve-tier.ts","../../src/lib/agents/staffing.ts","../../src/lib/agents/spawn-prep.ts","../../src/lib/cv.ts","../../src/lib/cloister/verdict-preservation.ts","../../src/lib/codex-auth.ts","../../src/lib/bridge-token.ts","../../src/lib/runtimes/kimi-context-envelope.ts","../../src/lib/channels/injection-budget.ts","../../src/lib/transcript-landing.ts","../../src/lib/agents/delivery.ts","../../src/lib/cloister/memory-verdict-cache.ts","../../src/lib/cloister/swarm-slot-lifecycle.ts","../../src/lib/cloister/concurrency.ts","../../src/lib/system-health/types.ts","../../src/lib/system-health/darwin.ts","../../src/lib/system-health/inotify.ts","../../src/lib/system-health/linux.ts","../../src/lib/system-health/collector.ts","../../src/lib/runtime-census.ts","../../src/lib/agents/state-dir-removal.ts","../../src/lib/agents/queries.ts","../../src/lib/agents/termination.ts","../../src/lib/session-history.ts","../../src/lib/agents/hook-readiness.ts","../../src/lib/agents/registered-slot-spawn.ts","../../src/lib/runtimes/kimi-code.ts","../../src/lib/agents/managed-kimi-delivery.ts","../../src/lib/agents/spawn.ts","../../src/lib/runtimes/ohmypi.ts","../../src/lib/overdeck/event-reads.ts","../../src/lib/agents/activity.ts","../../src/lib/operator-interventions.ts","../../src/lib/session-rotation.ts","../../src/lib/agents/eaten-message-watcher.ts","../../src/lib/agents/monitor-transport.ts","../../src/lib/agents/messaging.ts","../../src/lib/context-overflow.ts","../../src/lib/agents/session-pointers.ts","../../src/lib/agents/resume-spawn-plan.ts","../../src/lib/session-pane-choice.ts","../../src/lib/agents/resume-pane-choice.ts","../../src/lib/review-lifecycle-guard.ts","../../src/lib/agents/resume.ts","../../src/lib/graceful-restart.ts","../../src/lib/agent-input-detection.ts","../../src/lib/agent-enrichment.ts","../../src/lib/agents/pending-decision-gate.ts","../../src/lib/agents/recovery.ts","../../src/lib/agents/slot-reconcile.ts","../../src/lib/agents/standing-tiers.ts","../../src/lib/agents/tier-escalation.ts","../../src/lib/agents/tier-feed.ts","../../src/lib/agents/tier-supervisor.ts","../../src/lib/agents/tier-replay.ts","../../src/lib/agents.ts","../../src/lib/agents/runtime-state.ts","../../src/lib/agents/identity.ts","../../src/lib/agents/role.ts","../../src/lib/agents/agent-state.ts"],"sourcesContent":["/**\n * PAN-800 — in-process mirror of AgentStateService's canonical ref.\n *\n * The dashboard server writes into this mirror on every event fold so lib code\n * running in the same process can read the latest AgentRuntimeSnapshot.\n * CLI/out-of-process callers should prefer the async HTTP path in agent-runtime.ts.\n *\n * All exports are Effect-native (PAN-1249 wave-1 migration).\n */\n\nimport { Effect } from 'effect';\nimport type { AgentRuntimeSnapshot } from '@overdeck/contracts';\n\nlet mirror: Record<string, AgentRuntimeSnapshot> = {};\n\n/**\n * Set to true by AgentStateServiceLive's layer construction. When true, the\n * async HTTP adapter in agent-runtime.ts short-circuits to the sync mirror\n * instead of making a localhost HTTP call — we're running IN the dashboard,\n * so fetching our own HTTP server would deadlock if it's still bootstrapping.\n */\nlet inProcessService = false;\n\nexport function markAgentStateServiceInProcess(): Effect.Effect<void, never> {\n return Effect.sync(() => {\n inProcessService = true;\n });\n}\n\nexport function isAgentStateServiceInProcess(): Effect.Effect<boolean, never> {\n return Effect.sync(() => inProcessService);\n}\n\nexport function setAgentRuntimeMirror(next: Record<string, AgentRuntimeSnapshot>): Effect.Effect<void, never> {\n return Effect.sync(() => {\n mirror = next;\n });\n}\n\nexport function getRuntimeSnapshot(agentId: string): Effect.Effect<AgentRuntimeSnapshot | null, never> {\n return Effect.sync(() => mirror[agentId] ?? null);\n}\n","import { getProjectSync, resolveProjectFromIssueSync } from '../projects.js';\n\nexport interface AutoResumeConfig {\n maxConsecutiveFailures: number;\n troubledWindowMs: number;\n failureBackoffSchedule: number[];\n}\n\nexport const DEFAULT_AUTO_RESUME_CONFIG: AutoResumeConfig = {\n maxConsecutiveFailures: 3,\n troubledWindowMs: 10 * 60 * 1000,\n failureBackoffSchedule: [5, 30, 120],\n};\n\nfunction positiveNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0;\n}\n\nfunction validBackoffSchedule(value: unknown): value is number[] {\n return Array.isArray(value)\n && value.length > 0\n && value.every((entry) => typeof entry === 'number' && Number.isFinite(entry) && entry >= 0);\n}\n\nexport function normalizeAutoResumeConfig(\n override: Partial<AutoResumeConfig> | undefined,\n source: string,\n): AutoResumeConfig {\n if (override === undefined) return DEFAULT_AUTO_RESUME_CONFIG;\n\n const config: AutoResumeConfig = { ...DEFAULT_AUTO_RESUME_CONFIG };\n if (override.maxConsecutiveFailures !== undefined) {\n if (positiveNumber(override.maxConsecutiveFailures)) {\n config.maxConsecutiveFailures = override.maxConsecutiveFailures;\n } else {\n console.warn(`[auto-resume] Invalid ${source}.maxConsecutiveFailures; using default ${DEFAULT_AUTO_RESUME_CONFIG.maxConsecutiveFailures}`);\n }\n }\n\n if (override.troubledWindowMs !== undefined) {\n if (positiveNumber(override.troubledWindowMs)) {\n config.troubledWindowMs = override.troubledWindowMs;\n } else {\n console.warn(`[auto-resume] Invalid ${source}.troubledWindowMs; using default ${DEFAULT_AUTO_RESUME_CONFIG.troubledWindowMs}`);\n }\n }\n\n if (override.failureBackoffSchedule !== undefined) {\n if (validBackoffSchedule(override.failureBackoffSchedule)) {\n config.failureBackoffSchedule = override.failureBackoffSchedule;\n } else {\n console.warn(`[auto-resume] Invalid ${source}.failureBackoffSchedule; using default ${DEFAULT_AUTO_RESUME_CONFIG.failureBackoffSchedule.join(',')}`);\n }\n }\n\n return config;\n}\n\nexport function resolveAutoResumeConfigForIssue(issueId: string | undefined): AutoResumeConfig {\n if (!issueId) return DEFAULT_AUTO_RESUME_CONFIG;\n\n const resolved = resolveProjectFromIssueSync(issueId);\n if (!resolved) return DEFAULT_AUTO_RESUME_CONFIG;\n\n const project = getProjectSync(resolved.projectKey);\n return normalizeAutoResumeConfig(project?.autoResume, `projects.${resolved.projectKey}.autoResume`);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { getOverdeckHome } from '../paths.js';\nimport type { AgentState } from '../agents.js';\n\nconst AGENT_STATE_ROLLBACK_FILE = 'state' + '.json';\n\nexport function getRollbackAgentStatePath(agentId: string): string {\n return join(getOverdeckHome(), 'agents', agentId, AGENT_STATE_ROLLBACK_FILE);\n}\n\nexport function readRollbackAgentStateSync<T>(\n agentId: string,\n parse: (content: string, normalizedId: string) => T | null,\n): T | null {\n const stateFile = getRollbackAgentStatePath(agentId);\n if (!existsSync(stateFile)) return null;\n return parse(readFileSync(stateFile, 'utf8'), agentId);\n}\n\nexport function writeRollbackAgentStateSync(\n state: AgentState,\n serialize: (state: AgentState) => string,\n): void {\n const stateFile = getRollbackAgentStatePath(state.id);\n mkdirSync(join(getOverdeckHome(), 'agents', state.id), { recursive: true });\n writeFileSync(stateFile, serialize(state));\n}\n\nexport async function readRollbackAgentHarnessFromDir(\n agentsDir: string,\n agentId: string,\n): Promise<string | null> {\n try {\n const raw = await readFile(join(agentsDir, agentId, AGENT_STATE_ROLLBACK_FILE), 'utf8');\n const state = JSON.parse(raw) as { harness?: unknown };\n return typeof state.harness === 'string' ? state.harness : null;\n } catch {\n return null;\n }\n}\n","import type { RuntimeName } from '../runtimes/types.js';\nimport {\n readIssueRecordSync,\n resolveProjectForIssue,\n writeAgentHarnessModelSync,\n} from '../pan-dir/record.js';\n\nexport function readAgentHarnessModelRecordSync(issueId: string): {\n harness?: RuntimeName;\n model?: string;\n} | null {\n const project = resolveProjectForIssue(issueId);\n if (!project) return null;\n const record = readIssueRecordSync(project, issueId);\n return {\n harness: record?.harness,\n model: record?.model,\n };\n}\n\nexport function writeAgentHarnessModelRecordSync(\n issueId: string,\n harness: RuntimeName,\n model: string,\n): void {\n const project = resolveProjectForIssue(issueId);\n if (!project) return;\n writeAgentHarnessModelSync(project, issueId, harness, model);\n}\n\n","/** Canonical read/write door for durable per-agent runtime metadata. */\nimport {\n closeSync,\n existsSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from 'node:fs';\nimport { execFile } from 'node:child_process';\nimport { hostname } from 'node:os';\nimport { dirname, join, relative } from 'node:path';\nimport { promisify } from 'node:util';\nimport { Effect } from 'effect';\n\nimport type { Role } from '../agents/role.js';\n\n/**\n * Structural view of the agent runtime state this door needs. Declared here\n * (not imported from agents/agent-state.ts) so this module stays a leaf in the\n * module graph — agent-state.ts calls back into this door, and an import edge\n * in both directions is a lint:circular violation. Structural typing accepts\n * AgentState at call sites with no casts; if the canonical Role union grows,\n * assignability fails loudly here.\n */\nexport interface AgentPlaneAgentState {\n id: string;\n issueId: string;\n role: Role;\n workspace: string;\n harness?: RuntimeName;\n model?: string;\n branch?: string;\n startedAt?: string;\n sessionId?: string;\n}\nimport { getOverdeckHome } from '../paths.js';\nimport {\n getProjectSync,\n resolveProjectFromIssueSync,\n type ProjectConfig,\n} from '../projects.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { resolveStateReadHomeSync } from '../state-read-home.js';\nimport {\n flushAutoCommits,\n pushPendingStateCommits,\n queueAutoCommit,\n reconcileStatePlaneDrift,\n type FlushResult,\n} from './auto-commit.js';\nimport { withRecordFsLock } from './fs-lock.js';\nimport { withStateGitLock } from './state-git-lock.js';\n\nexport const AGENT_PLANE_DIRNAME = 'agents';\nexport const AGENT_PLANE_VERSION = 1 as const;\nconst execFileAsync = promisify(execFile);\nconst MAX_AGENT_PUSH_RECONCILIATIONS = 3;\n\nexport type AgentPlaneSessionReason = 'spawn' | 'rotation' | 'recovered';\nexport type AgentPlaneLifecycleEvent = 'spawned' | 'stopped' | 'tombstoned';\n\nexport interface AgentPlaneSessionEntry {\n id: string;\n startedAt: string;\n reason: AgentPlaneSessionReason;\n}\n\nexport interface AgentPlaneTombstonePredicate {\n closedOutFlag: boolean;\n trackerState: string;\n liveTmux: boolean | null;\n openChangeRequest: boolean | null;\n inFlightReviewOrTest: boolean | null;\n}\n\nexport interface AgentPlaneLifecycleEntry {\n at: string;\n event: AgentPlaneLifecycleEvent;\n predicate?: AgentPlaneTombstonePredicate;\n filesRemoved?: string[];\n}\n\nexport interface AgentPlaneRecord {\n version: typeof AGENT_PLANE_VERSION;\n agentId: string;\n issueId: string;\n projectKey: string;\n role: Role;\n origin: {\n machineId: string;\n overdeckHome: string;\n };\n launch: {\n harness: RuntimeName | null;\n model: string | null;\n workspace: string;\n branch: string | null;\n };\n sessions: AgentPlaneSessionEntry[];\n lifecycle: AgentPlaneLifecycleEntry[];\n archiveRef: string | null;\n recovered: boolean;\n}\n\nexport interface AgentPlaneSeed {\n id: string;\n issueId: string;\n role: Role;\n workspace: string;\n harness?: RuntimeName;\n model?: string;\n branch?: string;\n startedAt?: string;\n}\n\nexport class AgentPlaneOwnershipError extends Error {\n constructor(agentId: string, ownerMachine: string, writerMachine: string) {\n super(\n `Refusing to write durable agent ${agentId}: origin machine is ${ownerMachine}, `\n + `current machine is ${writerMachine}.`,\n );\n this.name = 'AgentPlaneOwnershipError';\n }\n}\n\ninterface AgentPlaneContext {\n projectKey: string;\n project: ProjectConfig;\n root: string;\n path: string;\n}\n\nfunction safeAgentId(agentId: string): string {\n const normalized = agentId.trim();\n if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(normalized)) {\n throw new Error(`Invalid agent id for durable agent plane: ${agentId}`);\n }\n return normalized;\n}\n\nfunction resolveAgentPlaneContext(\n issueId: string,\n agentId: string,\n operation?: string,\n): AgentPlaneContext | null {\n const resolved = resolveProjectFromIssueSync(issueId);\n if (!resolved) {\n if (operation) {\n console.warn(`[pan-dir/agents] skipping ${operation} for ${agentId}: no project resolves ${issueId}`);\n }\n return null;\n }\n\n const project = getProjectSync(resolved.projectKey);\n if (!project) {\n if (operation) {\n console.warn(\n `[pan-dir/agents] skipping ${operation} for ${agentId}: project ${resolved.projectKey} is not configured`,\n );\n }\n return null;\n }\n\n const stateHome = resolveStateReadHomeSync(project);\n if (!stateHome.migrated) {\n if (operation) {\n console.warn(\n `[pan-dir/agents] skipping ${operation} for ${agentId}: project ${resolved.projectKey} `\n + 'has not migrated to overdeck-state',\n );\n }\n return null;\n }\n\n const normalizedAgentId = safeAgentId(agentId);\n return {\n projectKey: resolved.projectKey,\n project,\n root: stateHome.root,\n path: join(stateHome.root, AGENT_PLANE_DIRNAME, `${normalizedAgentId}.json`),\n };\n}\n\nfunction isNodeError(error: unknown, code: string): boolean {\n return error instanceof Error && (error as NodeJS.ErrnoException).code === code;\n}\n\nfunction parseAgentPlaneRecord(raw: string, path: string): AgentPlaneRecord {\n const parsed = JSON.parse(raw) as Partial<AgentPlaneRecord>;\n if (\n parsed.version !== AGENT_PLANE_VERSION\n || typeof parsed.agentId !== 'string'\n || typeof parsed.issueId !== 'string'\n || typeof parsed.projectKey !== 'string'\n || !parsed.origin\n || typeof parsed.origin.machineId !== 'string'\n || !parsed.launch\n || !Array.isArray(parsed.sessions)\n || !Array.isArray(parsed.lifecycle)\n ) {\n throw new Error(`Invalid durable agent record at ${path}`);\n }\n return parsed as AgentPlaneRecord;\n}\n\nfunction readAgentPlaneRecordAtPath(path: string): AgentPlaneRecord | null {\n try {\n return parseAgentPlaneRecord(readFileSync(path, 'utf-8'), path);\n } catch (error) {\n if (isNodeError(error, 'ENOENT')) return null;\n throw error;\n }\n}\n\nexport function readAgentPlaneRecordSync(\n issueId: string,\n agentId: string,\n): AgentPlaneRecord | null {\n const context = resolveAgentPlaneContext(issueId, agentId);\n return context ? readAgentPlaneRecordAtPath(context.path) : null;\n}\n\nfunction writeAgentPlaneRecordAtomicSync(path: string, record: AgentPlaneRecord): void {\n mkdirSync(dirname(path), { recursive: true });\n const temporaryPath = `${path}.${process.pid}.${Date.now()}.tmp`;\n writeFileSync(temporaryPath, `${JSON.stringify(record, null, 2)}\\n`, 'utf-8');\n const temporaryFd = openSync(temporaryPath, 'r');\n try {\n fsyncSync(temporaryFd);\n } finally {\n closeSync(temporaryFd);\n }\n renameSync(temporaryPath, path);\n const directoryFd = openSync(dirname(path), 'r');\n try {\n fsyncSync(directoryFd);\n } finally {\n closeSync(directoryFd);\n }\n parseAgentPlaneRecord(readFileSync(path, 'utf-8'), path);\n}\n\nfunction gitFailureMessage(error: unknown): string {\n if (error && typeof error === 'object') {\n const failure = error as Error & { stderr?: string; stdout?: string };\n return failure.stderr?.trim() || failure.stdout?.trim() || failure.message;\n }\n return String(error);\n}\n\nfunction isRemoteRefRaceError(message: string): boolean {\n return /non-fast-forward|fetch first|stale info|cannot lock ref.*expected|failed to update ref|failed to push some refs/i.test(message);\n}\n\nasync function git(root: string, args: string[]): Promise<string> {\n const result = await execFileAsync('git', args, {\n cwd: root,\n encoding: 'utf8',\n timeout: 30_000,\n maxBuffer: 16 * 1024 * 1024,\n env: { ...process.env, HUSKY: '0' },\n });\n return result.stdout.trim();\n}\n\nfunction appendMissing<T>(remote: T[], local: T[]): T[] {\n const known = new Set(remote.map((entry) => JSON.stringify(entry)));\n return [...remote, ...local.filter((entry) => !known.has(JSON.stringify(entry)))];\n}\n\nfunction mergeAgentPlaneRecords(\n remote: AgentPlaneRecord | null,\n local: AgentPlaneRecord,\n): AgentPlaneRecord {\n if (!remote) return local;\n if (\n remote.agentId !== local.agentId\n || remote.issueId !== local.issueId\n || remote.projectKey !== local.projectKey\n || remote.origin.machineId !== local.origin.machineId\n ) {\n throw new Error(`Refusing to reconcile incompatible durable agent record ${local.agentId}`);\n }\n return {\n ...remote,\n sessions: appendMissing(remote.sessions, local.sessions),\n lifecycle: appendMissing(remote.lifecycle, local.lifecycle),\n archiveRef: local.archiveRef ?? remote.archiveRef,\n recovered: remote.recovered || local.recovered,\n };\n}\n\nasync function abortAgentPlaneMerge(root: string): Promise<void> {\n await git(root, ['merge', '--abort']).catch(() => undefined);\n}\n\nasync function reconcileAgentPlanePush(\n context: AgentPlaneContext,\n desired: AgentPlaneRecord,\n): Promise<FlushResult> {\n const relativePath = relative(context.root, context.path).replace(/\\\\/g, '/');\n try {\n for (let attempt = 0; attempt < MAX_AGENT_PUSH_RECONCILIATIONS; attempt += 1) {\n const status = await git(context.root, ['status', '--porcelain']);\n if (status) return { committed: true, pushed: false, reason: 'agent-plane reconciliation blocked by dirty state worktree' };\n\n await git(context.root, ['fetch', 'origin', 'overdeck-state']);\n try {\n await git(context.root, ['merge', '--no-edit', 'origin/overdeck-state']);\n } catch (mergeError) {\n const conflicts = (await git(context.root, ['diff', '--name-only', '--diff-filter=U']))\n .split('\\n').map((path) => path.trim()).filter(Boolean);\n if (conflicts.length !== 1 || conflicts[0] !== relativePath) {\n await abortAgentPlaneMerge(context.root);\n return {\n committed: true,\n pushed: false,\n reason: `agent-plane reconciliation conflicted outside ${relativePath}: ${conflicts.join(', ') || gitFailureMessage(mergeError)}`,\n };\n }\n\n await git(context.root, ['checkout', '--theirs', '--', relativePath]);\n const remote = readAgentPlaneRecordAtPath(context.path);\n writeAgentPlaneRecordAtomicSync(context.path, mergeAgentPlaneRecords(remote, desired));\n await git(context.root, ['add', '--', relativePath]);\n await git(context.root, ['-c', 'core.editor=true', 'commit', '--no-edit']);\n }\n\n try {\n await git(context.root, ['push', 'origin', 'overdeck-state']);\n return { committed: true, pushed: true };\n } catch (pushError) {\n const message = gitFailureMessage(pushError);\n if (!isRemoteRefRaceError(message)) {\n return { committed: true, pushed: false, reason: `agent-plane reconciled push failed: ${message}` };\n }\n }\n }\n return {\n committed: true,\n pushed: false,\n reason: `agent-plane push lost ${MAX_AGENT_PUSH_RECONCILIATIONS} consecutive remote ref races`,\n };\n } catch (error) {\n await abortAgentPlaneMerge(context.root);\n return { committed: true, pushed: false, reason: `agent-plane reconciliation failed: ${gitFailureMessage(error)}` };\n }\n}\n\nfunction makeAgentPlaneRecord(state: AgentPlaneSeed, projectKey: string): AgentPlaneRecord {\n return {\n version: AGENT_PLANE_VERSION,\n agentId: safeAgentId(state.id),\n issueId: state.issueId.toUpperCase(),\n projectKey,\n role: state.role,\n origin: {\n machineId: hostname(),\n overdeckHome: getOverdeckHome(),\n },\n launch: {\n harness: state.harness ?? null,\n model: state.model ?? null,\n workspace: state.workspace,\n branch: state.branch ?? null,\n },\n sessions: [],\n lifecycle: [],\n archiveRef: null,\n recovered: false,\n };\n}\n\nfunction assertAgentPlaneOwnership(record: AgentPlaneRecord): void {\n const writerMachine = hostname();\n if (record.origin.machineId !== writerMachine) {\n throw new AgentPlaneOwnershipError(record.agentId, record.origin.machineId, writerMachine);\n }\n}\n\nasync function updateAgentPlaneRecord(\n state: AgentPlaneSeed,\n operation: string,\n mutate: (record: AgentPlaneRecord) => AgentPlaneRecord | null,\n deferCommit = false,\n): Promise<boolean> {\n const context = resolveAgentPlaneContext(state.issueId, state.id, operation);\n if (!context) return false;\n\n return withRecordFsLock(\n context.project,\n state.issueId,\n {\n writerId: `agent-plane:${safeAgentId(state.id)}`,\n recordPath: context.path,\n // Patient retries (PAN-3513 append gap, 2026-08-04): the default ~1s\n // window loses to any competing state-door writer that holds the\n // per-issue lock through its git commit+push (several seconds). These\n // plane writes are durability-critical fire-and-forget appends with no\n // latency requirement — a dropped write is a permanently lost session\n // pointer (observed: 12 real sessions, 1 on the plane; 12 dropped\n // appends in one log window). Wait out the writer instead.\n retryDelaysMs: [250, 500, 1000, 2000, 4000, 8000, 8000],\n },\n async () => {\n const existing = readAgentPlaneRecordAtPath(context.path);\n if (existing) {\n assertAgentPlaneOwnership(existing);\n if (existing.projectKey !== context.projectKey) {\n throw new Error(\n `Refusing to write durable agent ${state.id}: record belongs to project `\n + `${existing.projectKey}, resolved project is ${context.projectKey}.`,\n );\n }\n }\n\n const next = mutate(existing ?? makeAgentPlaneRecord(state, context.projectKey));\n if (!next) return false;\n writeAgentPlaneRecordAtomicSync(context.path, next);\n queueAutoCommit({\n projectRoot: context.project.path,\n repoRoot: context.root,\n paths: [context.path],\n subject: `chore(agents): update ${safeAgentId(state.id)} runtime record`,\n defer: deferCommit,\n });\n return true;\n },\n );\n}\n\nfunction appendSession(\n sessions: AgentPlaneSessionEntry[],\n entry: AgentPlaneSessionEntry | undefined,\n): AgentPlaneSessionEntry[] {\n if (!entry || sessions.some((session) => session.id === entry.id)) return sessions;\n return [...sessions, entry];\n}\n\nexport function recordAgentPlaneSpawn(\n state: AgentPlaneAgentState,\n sessionId = state.sessionId,\n): Promise<boolean> {\n const at = state.startedAt || new Date().toISOString();\n const session = sessionId?.trim()\n ? { id: sessionId.trim(), startedAt: at, reason: 'spawn' as const }\n : undefined;\n return updateAgentPlaneRecord(state, 'spawn record', (current) => ({\n ...current,\n issueId: state.issueId.toUpperCase(),\n role: state.role,\n launch: {\n harness: state.harness ?? null,\n model: state.model ?? null,\n workspace: state.workspace,\n branch: state.branch ?? null,\n },\n sessions: appendSession(current.sessions, session),\n lifecycle: [...current.lifecycle, { at, event: 'spawned' }],\n }));\n}\n\nexport function appendAgentPlaneSession(\n state: AgentPlaneAgentState,\n entry: AgentPlaneSessionEntry,\n): Promise<boolean> {\n const normalizedId = entry.id.trim();\n if (!normalizedId) return Promise.resolve(false);\n return updateAgentPlaneRecord(state, 'session append', (current) => {\n if (current.sessions.some((session) => session.id === normalizedId)) return null;\n return {\n ...current,\n sessions: [...current.sessions, { ...entry, id: normalizedId }],\n };\n });\n}\n\nexport function appendAgentPlaneLifecycle(\n state: AgentPlaneAgentState,\n entry: AgentPlaneLifecycleEntry,\n options: { deferCommit?: boolean } = {},\n): Promise<boolean> {\n return updateAgentPlaneRecord(state, `${entry.event} lifecycle append`, (current) => ({\n ...current,\n lifecycle: [...current.lifecycle, entry],\n }), options.deferCommit);\n}\n\nexport function backfillAgentPlaneRecord(\n state: AgentPlaneSeed,\n sessions: AgentPlaneSessionEntry[],\n recovered: boolean,\n options: { deferCommit?: boolean } = {},\n): Promise<boolean> {\n return updateAgentPlaneRecord(state, 'backfill', (current) => {\n const knownSessionIds = new Set(current.sessions.map((session) => session.id));\n const missingSessions = sessions.filter((entry) => {\n const id = entry.id.trim();\n if (!id || knownSessionIds.has(id)) return false;\n knownSessionIds.add(id);\n return true;\n });\n const needsSpawnedLifecycle = !current.lifecycle.some((entry) => entry.event === 'spawned');\n const nextRecovered = current.recovered || recovered;\n if (missingSessions.length === 0 && !needsSpawnedLifecycle && nextRecovered === current.recovered) {\n return null;\n }\n const spawnedAt = state.startedAt\n ?? missingSessions[0]?.startedAt\n ?? new Date().toISOString();\n return {\n ...current,\n sessions: [\n ...current.sessions,\n ...missingSessions.map((entry) => ({ ...entry, id: entry.id.trim() })),\n ],\n lifecycle: needsSpawnedLifecycle\n ? [{ at: spawnedAt, event: 'spawned' }, ...current.lifecycle]\n : current.lifecycle,\n recovered: nextRecovered,\n };\n }, options.deferCommit);\n}\n\nexport async function flushAgentPlaneWrites(issueId: string, agentId: string): Promise<FlushResult | null> {\n const context = resolveAgentPlaneContext(issueId, agentId, 'flush');\n if (!context || !existsSync(context.root)) return null;\n const desired = readAgentPlaneRecordAtPath(context.path);\n if (!desired) return null;\n\n return withStateGitLock(context.root, `agent-plane-flush:${safeAgentId(agentId)}`, context.path, async () => {\n const flushed = await Effect.runPromise(flushAutoCommits(context.project.path));\n if (flushed.errored) return flushed;\n if (flushed.committed && flushed.pushed !== true) {\n if (!isRemoteRefRaceError(flushed.reason ?? '')) return flushed;\n return reconcileAgentPlanePush(context, desired);\n }\n\n const reconciled = await Effect.runPromise(reconcileStatePlaneDrift(context.project.path));\n if (reconciled.errored || (reconciled.committed && reconciled.pushed !== true)) {\n return reconciled;\n }\n if (flushed.committed || reconciled.committed) {\n return { committed: true, pushed: true };\n }\n\n const push = await Effect.runPromise(pushPendingStateCommits(context.project.path));\n if (push?.pushed === false && isRemoteRefRaceError(push.reason ?? '')) {\n return reconcileAgentPlanePush(context, desired);\n }\n return {\n committed: false,\n pushed: push?.pushed,\n reason: push?.reason ?? reconciled.reason ?? flushed.reason,\n };\n });\n}\n","/**\n * PAN-800 — out-of-process client for the canonical AgentRuntimeSnapshot.\n *\n * Every caller that previously read/wrote ~/.overdeck/agents/<id>/runtime.json\n * now goes through this module. There is no file fallback: the dashboard's\n * AgentStateService SubscriptionRef is the single source of truth for runtime\n * snapshots. CLI and lib modules that need runtime state hit the HTTP API.\n * Server-reachable code inside the dashboard process yields AgentStateService\n * directly (zero-roundtrip) — this module is for everything else.\n */\n\nimport { Data, Effect } from 'effect'\nimport type {\n AgentRuntimeSnapshot,\n Activity,\n AgentResolution,\n ChannelReplyArtifactRef,\n ChannelReplyKind,\n WaitingReason,\n} from '@overdeck/contracts'\nimport { ensureInternalTokenSync, INTERNAL_TOKEN_HEADER } from './internal-token.js'\n\n// Use 127.0.0.1 explicitly: when /etc/hosts resolves `localhost` to ::1\n// (IPv6 first), Node's undici-based fetch() connects to [::1]:3011 and\n// fails because the dashboard listens on the IPv4 wildcard 0.0.0.0.\n// curl falls back to IPv4; Node's fetch in this version does not.\nconst DASHBOARD_URL = process.env['OVERDECK_DASHBOARD_URL'] || 'http://127.0.0.1:3011'\nconst DEFAULT_TIMEOUT_MS = 1500\n\nfunction abortSignal(ms: number): AbortSignal {\n const controller = new AbortController()\n setTimeout(() => controller.abort(), ms).unref?.()\n return controller.signal\n}\n\nclass AgentRuntimeFetchError extends Data.TaggedError('AgentRuntimeFetchError')<{\n readonly url: string\n readonly cause?: unknown\n}> {}\n\nexport const getAgentRuntimeSnapshot = (\n agentId: string,\n): Effect.Effect<AgentRuntimeSnapshot | null> => {\n if (!agentId) return Effect.succeed(null)\n const url = `${DASHBOARD_URL}/api/agents/${encodeURIComponent(agentId)}/runtime`\n return Effect.gen(function* () {\n const res = yield* Effect.tryPromise({\n try: () => fetch(url, { signal: abortSignal(DEFAULT_TIMEOUT_MS) }),\n catch: (cause) => new AgentRuntimeFetchError({ url, cause }),\n })\n if (!res.ok) return null\n const body = yield* Effect.tryPromise({\n try: () => res.json() as Promise<{ success: boolean; snapshot?: AgentRuntimeSnapshot }>,\n catch: (cause) => new AgentRuntimeFetchError({ url, cause }),\n })\n return body.success && body.snapshot ? body.snapshot : null\n }).pipe(Effect.orElseSucceed(() => null))\n}\n\ntype HeartbeatBody =\n | { kind: 'activity'; activity: Activity; tool?: string }\n | { kind: 'hook_fired'; hookName: string; tool?: string }\n | { kind: 'thinking_start'; lastToolAt: string }\n | { kind: 'thinking_stop'; resolvedBy: 'tool' | 'waiting' | 'idle' | 'stopped' }\n | { kind: 'waiting_start'; reason: WaitingReason; message?: string }\n | { kind: 'waiting_clear'; clearedBy: 'user_response' | 'timeout' | 'stopped' | 'tool_resumed' }\n | { kind: 'message_received'; direction: 'to_agent' | 'from_agent'; source: 'user' | 'cloister' | 'specialist' | 'automated' }\n | { kind: 'channel_reply'; reply: { kind: ChannelReplyKind; summary: string; artifactRefs?: ChannelReplyArtifactRef[] } }\n | { kind: 'model_set'; model: string; claudeSessionId?: string | null; sessionModel?: string; sessionHarness?: string }\n | { kind: 'resolution_set'; resolution: AgentResolution; resolutionCount: number }\n | { kind: 'current_issue_set'; currentIssue?: string }\n | { kind: 'context_saturation_changed'; contextSaturatedAt?: string }\n\n/**\n * Emit a runtime event. Returns true if the dashboard accepted it, false on\n * any failure (timeout, network error, non-2xx). Callers get fire-and-forget\n * semantics — no retry, no buffering. The bash hooks have their own\n * pending-events.jsonl fallback; in-process callers just log and move on.\n */\nexport const emitAgentEvent = (\n agentId: string,\n body: HeartbeatBody,\n): Effect.Effect<boolean> => {\n if (!agentId) return Effect.succeed(false)\n const url = `${DASHBOARD_URL}/api/agents/${encodeURIComponent(agentId)}/heartbeat`\n return Effect.gen(function* () {\n // The heartbeat route requires the internal token\n // (validateAgentRuntimeEventAuth). Without it every event POSTed from a\n // CLI process was silently 403'd — a codex slot agent's `pan done`\n // resolution never reached AgentStateService, so the swarm coordinator\n // classified finished slots as still running, forever. The bash hooks\n // attach this same header; the lib emitter must too.\n const token = yield* Effect.try({\n try: (): string => ensureInternalTokenSync(),\n catch: (cause) => new AgentRuntimeFetchError({ url, cause }),\n }).pipe(Effect.orElseSucceed((): string | null => null))\n if (!token) return false\n const res = yield* Effect.tryPromise({\n try: () =>\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', [INTERNAL_TOKEN_HEADER]: token },\n body: JSON.stringify({ ...body, timestamp: new Date().toISOString() }),\n signal: abortSignal(DEFAULT_TIMEOUT_MS),\n }),\n catch: (cause) => new AgentRuntimeFetchError({ url, cause }),\n })\n return res.ok\n }).pipe(Effect.orElseSucceed(() => false))\n}\n\n// ─── Convenience wrappers mirroring the event taxonomy ────────────────────────\n\nexport const emitActivity = (\n agentId: string,\n activity: Activity,\n tool?: string,\n): Effect.Effect<boolean> => emitAgentEvent(agentId, { kind: 'activity', activity, tool })\n\nexport const emitWaitingStart = (\n agentId: string,\n reason: WaitingReason,\n message?: string,\n): Effect.Effect<boolean> => emitAgentEvent(agentId, { kind: 'waiting_start', reason, message })\n\nexport const emitWaitingClear = (\n agentId: string,\n clearedBy: 'user_response' | 'timeout' | 'stopped' | 'tool_resumed' = 'user_response',\n): Effect.Effect<boolean> => emitAgentEvent(agentId, { kind: 'waiting_clear', clearedBy })\n\nexport const emitModelSet = (\n agentId: string,\n model: string,\n claudeSessionId?: string,\n sessionOrigin?: { sessionModel?: string; sessionHarness?: string },\n): Effect.Effect<boolean> => emitAgentEvent(agentId, { kind: 'model_set', model, claudeSessionId, ...sessionOrigin })\n\nexport const emitMessageReceived = (\n agentId: string,\n direction: 'to_agent' | 'from_agent',\n source: 'user' | 'cloister' | 'specialist' | 'automated',\n): Effect.Effect<boolean> => emitAgentEvent(agentId, { kind: 'message_received', direction, source })\n\nexport const emitChannelReply = (\n agentId: string,\n reply: { kind: ChannelReplyKind; summary: string; artifactRefs?: ChannelReplyArtifactRef[] },\n): Effect.Effect<boolean> => emitAgentEvent(agentId, { kind: 'channel_reply', reply })\n\nexport const emitResolution = (\n agentId: string,\n resolution: AgentResolution,\n resolutionCount: number,\n): Effect.Effect<boolean> => emitAgentEvent(agentId, { kind: 'resolution_set', resolution, resolutionCount })\n\nexport const emitContextSaturationChanged = (\n agentId: string,\n contextSaturatedAt?: string,\n): Effect.Effect<boolean> => emitAgentEvent(agentId, { kind: 'context_saturation_changed', contextSaturatedAt })\n","/**\n * Resolve a built bundle's bare imports the way Node will at boot.\n *\n * Existence is not bootability. A bundle file can sit on disk in perfect shape\n * while the node_modules it will be loaded against is incomplete, and the only\n * symptom is `ERR_MODULE_NOT_FOUND` after the process has already replaced the\n * healthy one (PAN-3172 for the PTY supervisor, PAN-3264 for the dashboard\n * server). Probing from the bundle's own location turns that into a check a\n * caller can make *before* it launches or switches over.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\n\n/** `from \"x\"`, `import \"x\"`, and `import(\"x\")` in bundled ESM output. */\nconst IMPORT_SOURCE_PATTERN = /\\b(?:from|import)\\s*\\(?\\s*([\"'])([^\"'\\n]+)\\1/g;\n/** Bare package specifiers — relative, absolute, and `node:` are always fine. */\nconst PACKAGE_SPECIFIER_PATTERN = /^(?:@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*(?:\\/[^\\s]*)?$/;\n\n/**\n * Bare package specifiers the bundle imports but cannot resolve from its own\n * location. Empty means the artifact can start where it sits.\n *\n * Only a genuine module-not-found counts. A package that is present but whose\n * exports refuse the `require` condition still resolves under `import`, so it\n * must not be reported as missing.\n */\nexport function unresolvedBundleImports(scriptPath: string): string[] {\n const source = readFileSync(scriptPath, 'utf8');\n const requireFrom = createRequire(scriptPath);\n const missing = new Set<string>();\n\n for (const match of source.matchAll(IMPORT_SOURCE_PATTERN)) {\n const specifier = match[2];\n if (!specifier || !PACKAGE_SPECIFIER_PATTERN.test(specifier)) continue;\n try {\n requireFrom.resolve(specifier);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND') missing.add(specifier);\n }\n }\n\n return [...missing];\n}\n","/**\n * Locate (and if necessary materialize) the PTY supervisor artifact.\n *\n * The supervisor (dist/pty-supervisor.js, built by the root tsdown config) is\n * spawned inside the agent/conversation tmux session under the HOST `node`,\n * and the session must outlive the process that spawned it. That rules out\n * running it from inside a packaged desktop app: the Linux AppImage is a\n * transient FUSE mount that disappears when the app quits, and the staged\n * dashboard node_modules carry an Electron-ABI node-pty that host node cannot\n * load. So the desktop packaging stages a host-runnable copy — the supervisor\n * chunk closure plus the platform-specific Node-API node-pty prebuild — next to the\n * server bundle (server/supervisor/, see\n * apps/desktop/scripts/prepare-server-resources.mjs), and this module copies\n * it onto real disk under ${OVERDECK_HOME}/runtime/ before first use\n * (PAN-2592).\n *\n * Resolution order:\n * 1. <packageRoot>/dist/pty-supervisor.js — dev checkouts, `pan up`, and\n * npm-global @overdeck/core installs (node-pty resolves from the package\n * root's own node_modules).\n * 2. <bundleDir>/supervisor/pty-supervisor.js — desktop layouts; the staged\n * tree is materialized to ${OVERDECK_HOME}/runtime/pty-supervisor/<hash>/\n * (content-hash keyed over the whole staged tree, so an app upgrade\n * refreshes it even when only non-entry files changed) and the vendored\n * packages land under node_modules/ there for normal Node resolution.\n * 3. <activeBundle.repoRoot>/dist/pty-supervisor.js — the primary checkout a\n * `pan reload` deployment was built from (PAN-3172).\n *\n * A candidate is only accepted when its own bare imports actually resolve from\n * where it sits. Existence alone is not enough: `pan reload` runs the dashboard\n * out of ~/.overdeck/deployments/dashboard/.pan-reload-generation-{a,b}, and a\n * generation whose node_modules cannot resolve @lydell/node-pty produces a\n * supervisor that dies on ERR_MODULE_NOT_FOUND before it ever binds its socket\n * — every conversation spawned afterwards then died on a socket timeout with no\n * hint of the real cause (PAN-3172). Falling through to the primary checkout,\n * whose node_modules is complete, keeps spawns alive; that copy is the same\n * build, since activateDashboardDeployment() copies the generation's dist/ into\n * the primary checkout.\n *\n * Missing (or unresolvable) everywhere is a build/packaging defect and throws —\n * never silently degrade a spawn that the caller expects to be supervised.\n */\n\nimport { createHash } from 'node:crypto';\nimport { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { unresolvedBundleImports } from '../bundle-imports.js';\nimport { readActiveDashboardBundleSync } from '../deploy/active-dashboard-bundle.js';\nimport { getOverdeckHome, packageRoot } from '../paths.js';\n\nconst moduleDir = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Bare package specifiers the built supervisor imports but cannot resolve from\n * its own location. Empty means the artifact can start where it sits.\n */\nexport const unresolvedSupervisorImports = unresolvedBundleImports;\n\n/**\n * Why a freshly built deployment cannot run the supervisor, or null if it can.\n *\n * `pan reload`'s health check only exercises the dashboard's HTTP surface, which\n * stays green even when the generation's node_modules is incomplete — so the\n * reload reported \"healthy\" while every new conversation died on a supervisor\n * socket timeout (PAN-3172). Callers use this to fail the deploy instead.\n */\nexport function supervisorDeploymentFailure(deployRoot: string): string | null {\n const supervisorPath = join(deployRoot, 'dist', 'pty-supervisor.js');\n if (!existsSync(supervisorPath)) return `Build did not create ${supervisorPath}`;\n\n const missing = unresolvedSupervisorImports(supervisorPath);\n if (missing.length === 0) return null;\n return `Deployment cannot resolve ${missing.join(', ')} from ${supervisorPath} — `\n + 'the PTY supervisor would die on startup and every new conversation would time out. '\n + 'Run `bun install` in the deployment root, then reload again';\n}\n\nexport function resolvePtySupervisorScriptPath(): string {\n const activeBundle = readActiveDashboardBundleSync();\n const candidates = [\n join(packageRoot, 'dist', 'pty-supervisor.js'),\n // Desktop layouts: all server chunks (this module included) sit in one\n // bundle directory with the staged supervisor tree beside them.\n join(moduleDir, 'supervisor', 'pty-supervisor.js'),\n ...(activeBundle ? [join(activeBundle.repoRoot, 'dist', 'pty-supervisor.js')] : []),\n ];\n\n const rejected: string[] = [];\n for (const candidate of candidates) {\n if (!existsSync(candidate)) continue;\n const staged = dirname(candidate) === join(moduleDir, 'supervisor');\n const entry = staged ? materializePtySupervisorRuntime(dirname(candidate)) : candidate;\n const missing = unresolvedSupervisorImports(entry);\n if (missing.length === 0) return entry;\n rejected.push(`${entry} cannot resolve ${missing.join(', ')}`);\n }\n\n if (rejected.length > 0) {\n throw new Error(\n `pty-supervisor cannot start — its dependencies are missing from every built copy: ${rejected.join('; ')}. `\n + 'Reinstall dependencies where the dashboard is running from (`bun install`), then rebuild.',\n );\n }\n throw new Error('pty-supervisor build artifact missing — run `npm run build`.');\n}\n\n/**\n * Hash the contents and relative paths of every file under the staged tree.\n * This detects changes to any copied file, not just the entry point.\n */\nfunction hashStagedTree(stagedDir: string): string {\n const hash = createHash('sha256');\n const walk = (dir: string) => {\n const entries = readdirSync(dir, { withFileTypes: true });\n entries.sort((a, b) => a.name.localeCompare(b.name));\n for (const entry of entries) {\n const full = join(dir, entry.name);\n const rel = relative(stagedDir, full);\n if (entry.isDirectory()) {\n hash.update(`${rel}/\\n`);\n walk(full);\n } else if (entry.isFile()) {\n hash.update(`${rel}\\n`);\n hash.update(readFileSync(full));\n }\n }\n };\n walk(stagedDir);\n return hash.digest('hex').slice(0, 16);\n}\n\n/**\n * Copy the staged supervisor tree to durable disk and return the entry path.\n * Idempotent and race-safe: the copy lands in a temp dir and is renamed into\n * place; the target is keyed by a content hash of the entire staged tree.\n */\nexport function materializePtySupervisorRuntime(\n stagedDir: string,\n overdeckHome: string = getOverdeckHome(),\n): string {\n const key = hashStagedTree(stagedDir);\n const targetDir = join(overdeckHome, 'runtime', 'pty-supervisor', key);\n const entry = join(targetDir, 'pty-supervisor.js');\n if (existsSync(entry)) return entry;\n\n const tmpDir = `${targetDir}.tmp-${process.pid}`;\n rmSync(tmpDir, { recursive: true, force: true });\n mkdirSync(tmpDir, { recursive: true });\n for (const name of readdirSync(stagedDir)) {\n if (name === 'vendor') continue;\n cpSync(join(stagedDir, name), join(tmpDir, name), { recursive: true });\n }\n // Vendored packages are staged as vendor/<name> (a nested node_modules would\n // be stripped from the npx flavor by npm pack); restore the resolvable name.\n const vendorDir = join(stagedDir, 'vendor');\n if (existsSync(vendorDir)) {\n cpSync(vendorDir, join(tmpDir, 'node_modules'), { recursive: true });\n }\n try {\n renameSync(tmpDir, targetDir);\n } catch (err) {\n rmSync(tmpDir, { recursive: true, force: true });\n // A concurrent spawn won the rename race — its copy is complete.\n if (!existsSync(entry)) throw err;\n }\n return entry;\n}\n","import { randomBytes } from 'node:crypto';\nimport { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getOverdeckHome } from './paths.js';\n\nexport const PTY_TOKEN_HEADER = 'x-overdeck-pty-token';\n\nfunction ptyTokenDir(agentId: string): string {\n return join(getOverdeckHome(), 'agents', agentId);\n}\n\nexport function getPtyTokenPath(agentId: string): string {\n return join(ptyTokenDir(agentId), 'pty-token');\n}\n\nexport function readPtyTokenSync(agentId: string): string | null {\n try {\n const value = readFileSync(getPtyTokenPath(agentId), 'utf8').trim();\n return value.length > 0 ? value : null;\n } catch {\n return null;\n }\n}\n\nexport async function readPtyToken(agentId: string): Promise<string | null> {\n try {\n const value = (await readFile(getPtyTokenPath(agentId), 'utf8')).trim();\n return value.length > 0 ? value : null;\n } catch {\n return null;\n }\n}\n\nexport function writePtyTokenSync(agentId: string): string {\n const dir = ptyTokenDir(agentId);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const token = randomBytes(32).toString('hex');\n const path = getPtyTokenPath(agentId);\n writeFileSync(path, `${token}\\n`, { mode: 0o600 });\n try {\n chmodSync(path, 0o600);\n } catch {\n // best effort\n }\n return token;\n}\n\nexport async function writePtyToken(agentId: string): Promise<string> {\n const dir = ptyTokenDir(agentId);\n await mkdir(dir, { recursive: true, mode: 0o700 });\n const token = randomBytes(32).toString('hex');\n const path = getPtyTokenPath(agentId);\n await writeFile(path, `${token}\\n`, { mode: 0o600 });\n try {\n await chmod(path, 0o600);\n } catch {\n // best effort\n }\n return token;\n}\n","export type ResumeCause = 'operator' | 'system' | 'message';\n\nexport function buildResumeContract(cause: ResumeCause): string {\n const reason = cause === 'operator'\n ? 'The operator resumed this session after stopping or pausing it.'\n : cause === 'message'\n ? 'This session was resumed because a new message arrived while it was stopped.'\n : 'This session was restored after its prior process ended unexpectedly, such as during a crash or reboot.';\n\n return `${reason} The conversation transcript was restored, but process-local machinery was not. ` +\n 'Re-establish any timers or wakeups, monitors, background processes, loops, or cron work you still need before continuing. ' +\n 'If this session runs a recurring tick loop, explicitly arm its next wakeup now.';\n}\n","import { readFile, unlink } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { homedir } from 'os';\nimport { dirname, join } from 'path';\nimport { Effect } from 'effect';\nimport { emitActivityEntrySync } from '../activity-logger.js';\nimport { isClaudeCodeChannelsMcpEnabled } from '../config-yaml.js';\nimport type { ModelId } from '../settings.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport { resolvePtySupervisorScriptPath } from '../channels/pty-supervisor-locate.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { getProviderForModelSync } from '../providers.js';\nimport { writePtyToken } from '../pty-token.js';\nimport { buildResumeContract, type ResumeCause } from '../resume-contract.js';\nimport { capturePane, sendRawKeystroke } from '../tmux.js';\nimport {\n getAgentDir,\n getAgentState,\n recordAgentFailure,\n saveAgentState,\n saveAgentStateSync,\n type AgentState,\n type Role,\n} from './agent-state.js';\n\ninterface SupervisorChannelsSpawnOptions {\n issueId: string;\n workspace: string;\n role?: Role;\n model?: string;\n harness?: RuntimeName;\n allowHost?: boolean;\n}\n\nexport function buildDefaultResumeContinueMessage(issueId: string): string {\n return `You are resuming work on ${issueId}. Read .pan/continue.json for context and pick up where you left off — do not wait for further instructions.`;\n}\n\n/** Completed marker check: the agent dir's own markers, or the workspace's. */\nexport function hasCompletionMarkerForAgent(state: AgentState): boolean {\n const agentDir = getAgentDir(state.id);\n if (existsSync(join(agentDir, 'completed')) || existsSync(join(agentDir, 'completed.processed'))) return true;\n const workspace = state.workspace;\n if (workspace && (existsSync(join(workspace, '.pan', 'completed')) || existsSync(join(workspace, '.pan', 'completed.processed')))) return true;\n return false;\n}\n\n/**\n * PAN-2974 (root cause B): the resume kickoff is phase-aware. Telling an agent\n * whose work was already handed off (completed marker) to \"pick up where you\n * left off — do not wait for further instructions\" makes it re-drive pipeline\n * stages on its own authority — 2026-07-21 MIN-882: a post-reboot resume\n * re-ran verification, restored config, and fired `pan review request` /\n * `pan review restart` while the operator was mid-UAT on finished work.\n * A handed-off agent gets a PASSIVE restore instead; only agents that crashed\n * mid-implementation get the aggressive kickoff.\n */\nexport function buildResumeContinueMessage(state: AgentState): string {\n const issueId = state.issueId;\n if (hasCompletionMarkerForAgent(state)) {\n return [\n `Your session for ${issueId} was restored after a restart.`,\n `The work was already handed off — a completed marker exists. Do NOT re-drive pipeline stages (review, test, verify, merge) on your own authority.`,\n `Stay available for the operator and act only on new instructions.`,\n ].join(' ');\n }\n return buildDefaultResumeContinueMessage(issueId);\n}\n\nexport async function buildResumeMessageForAgent(\n state: AgentState,\n fallbackMessage: string,\n callerMessage?: string,\n cause: ResumeCause = state.stoppedByUser === true ? 'operator' : callerMessage ? 'message' : 'system',\n): Promise<{ message?: string; redeliveringKickoff: boolean; error?: string }> {\n const contract = buildResumeContract(cause);\n if (state.role !== 'work' || state.kickoffDelivered !== false) {\n return { message: `${callerMessage ?? fallbackMessage}\\n\\n${contract}`, redeliveringKickoff: false };\n }\n\n const promptPath = join(getAgentDir(state.id), 'initial-prompt.md');\n try {\n const kickoffPrompt = await readFile(promptPath, 'utf-8');\n const suffix = callerMessage\n ? `\\n\\n---\\n\\nAdditional message delivered during resume:\\n\\n${callerMessage}`\n : '';\n return { message: `${kickoffPrompt}${suffix}\\n\\n${contract}`, redeliveringKickoff: true };\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n return {\n redeliveringKickoff: true,\n error: `kickoff prompt missing at ${promptPath}: ${reason}`,\n };\n }\n}\n\nexport function markKickoffRedelivered(state: AgentState): void {\n state.kickoffDelivered = true;\n saveAgentStateSync(state);\n}\n\nexport async function recordKickoffDeliveryFailure(state: AgentState, issueId: string, source: Role | 'work-agent'): Promise<void> {\n await Effect.runPromise(recordAgentFailure(state.id, 'kickoff delivery failed'));\n const failedState = await Effect.runPromise(getAgentState(state.id));\n if (failedState) {\n failedState.status = 'running';\n failedState.kickoffDelivered = false;\n await Effect.runPromise(saveAgentState(failedState));\n }\n state.status = 'running';\n state.kickoffDelivered = false;\n emitActivityEntrySync({\n source,\n level: 'error',\n message: `${state.id}: kickoff delivery failed`,\n issueId,\n });\n}\n\ninterface ChannelsDecision {\n eligible: boolean;\n reason?: string;\n}\n\ninterface SupervisorDecision {\n eligible: boolean;\n reason?: string;\n}\n\nexport function decideSupervisorForWorkAgent(\n agentId: string,\n options: SupervisorChannelsSpawnOptions,\n state: AgentState,\n): SupervisorDecision {\n void options;\n const log = (eligible: boolean, reason?: string): void => {\n const tag = eligible ? 'supervisor:eligible' : `supervisor:ineligible:${reason ?? 'unknown'}`;\n console.log(`[${agentId}] ${tag}`);\n };\n\n if (state.role !== 'work' && state.role !== 'strike') {\n log(false, 'not-a-work-or-strike-agent');\n return { eligible: false, reason: 'not-a-work-or-strike-agent' };\n }\n\n if (process.env.OVERDECK_DOCKER_WORKSPACE === '1' || process.env.PAN_DOCKER === '1') {\n log(false, 'docker-not-supported-yet');\n return { eligible: false, reason: 'docker-not-supported-yet' };\n }\n\n const behavior = state.harness ? getHarnessBehavior(state.harness) : null;\n if (!behavior?.supportsPtySupervisor) {\n const reason = `harness-${state.harness ?? 'unknown'}`;\n log(false, reason);\n return { eligible: false, reason };\n }\n\n log(true);\n return { eligible: true };\n}\n\nexport async function prepareSupervisorForFreshLaunch(\n agentId: string,\n options: SupervisorChannelsSpawnOptions,\n state: AgentState,\n): Promise<{ useSupervisor: boolean; supervisorScriptPath?: string }> {\n const supervisorDecision = decideSupervisorForWorkAgent(agentId, options, state);\n if (!supervisorDecision.eligible) {\n delete state.supervisorEnabled;\n return { useSupervisor: false };\n }\n\n const supervisorScriptPath = resolvePtySupervisorScriptPath();\n await writePtyToken(agentId);\n state.supervisorEnabled = true;\n return { useSupervisor: true, supervisorScriptPath };\n}\n\nexport async function prepareSupervisorForRelaunch(\n agentId: string,\n state: AgentState,\n model: string,\n harness: RuntimeName,\n): Promise<{ useSupervisor: boolean; supervisorScriptPath?: string }> {\n // PAN-3257: decide eligibility fresh, exactly like a new spawn — never gate\n // on the persisted supervisorEnabled flag. A state write that lost the flag\n // (observed after the PAN-1837 crash-resume) would otherwise permanently\n // downgrade the agent to tmux-only delivery while its stale supervisor\n // socket kept refusing every connection. Eligibility itself is cheap and\n // deterministic (role, harness, docker), so re-deriving it cannot drift.\n const relaunchState: AgentState = { ...state, model, harness };\n const supervisorDecision = decideSupervisorForWorkAgent(agentId, {\n issueId: state.issueId || agentId.replace(/^agent-/, '').toUpperCase(),\n workspace: state.workspace,\n role: state.role,\n model,\n harness,\n allowHost: state.hostOverride,\n }, relaunchState);\n if (!supervisorDecision.eligible) {\n delete state.supervisorEnabled;\n // A supervisor-less relaunch must not leave a dead socket behind: the\n // delivery tiers probe the socket path first, and a stale file turns\n // every probe into ECONNREFUSED instead of a clean socket-missing\n // fall-through to tmux.\n await removeStaleSupervisorSocket(agentId);\n return { useSupervisor: false };\n }\n\n const supervisorScriptPath = resolvePtySupervisorScriptPath();\n await writePtyToken(agentId);\n state.supervisorEnabled = true;\n return { useSupervisor: true, supervisorScriptPath };\n}\n\n/** Remove a dead supervisor socket so delivery probes fail fast to the next tier (PAN-3257). */\nasync function removeStaleSupervisorSocket(agentId: string): Promise<void> {\n const socketPath = join(getOverdeckHome(), 'sockets', `pty-${agentId}.sock`);\n try {\n await unlink(socketPath);\n console.log(`[${agentId}] removed stale supervisor socket at ${socketPath}`);\n } catch { /* absent — nothing to clean */ }\n}\n\n/**\n * Decide whether to enable Claude Code Channels for a work-agent launch.\n *\n * Eligibility (all required):\n * - experimental.claudeCodeChannelsMcp is true in the merged config\n * - the agent is a work agent (specialists/conversations stay off MCP)\n * - the harness is Claude Code (not Pi or another runtime harness)\n * - auth provider is Anthropic-direct (excludes Bedrock/Vertex/Foundry)\n * - the workspace is not running inside a Docker container\n *\n * Logs the decision exactly once with a category prefix so users can see why\n * the bridge did or did not engage. The function is otherwise side-effect\n * free; the caller is responsible for writing the .mcp.json and mutating\n * state.channelsEnabled when eligible is true. This legacy MCP transport is now\n * opt-in for new spawns; the PTY supervisor is the default delivery transport.\n */\nexport function decideChannelsForWorkAgent(\n agentId: string,\n _options: SupervisorChannelsSpawnOptions,\n state: AgentState,\n): ChannelsDecision {\n const log = (eligible: boolean, reason?: string): void => {\n const tag = eligible ? 'channels:eligible' : `channels:ineligible:${reason ?? 'unknown'}`;\n console.log(`[${agentId}] ${tag}`);\n };\n\n if (!isClaudeCodeChannelsMcpEnabled()) {\n return { eligible: false, reason: 'mcp-default-off' };\n }\n\n if (state.role !== 'work') {\n log(false, 'not-a-work-agent');\n return { eligible: false, reason: 'not-a-work-agent' };\n }\n\n if (!state.harness || !getHarnessBehavior(state.harness).supportsChannelsBridge) {\n log(false, `harness-${state.harness ?? 'unknown'}`);\n return { eligible: false, reason: `harness-${state.harness ?? 'unknown'}` };\n }\n\n // Auth gate. The Channels capability is gated by Anthropic auth in the\n // compiled Claude Code binary; we only attempt the bridge when the model\n // routes to the anthropic provider.\n const provider = getProviderForModelSync(state.model as ModelId);\n if (provider.name !== 'anthropic') {\n log(false, `provider-${provider.name}`);\n return { eligible: false, reason: `provider-${provider.name}` };\n }\n\n if (\n process.env.CLAUDE_CODE_USE_BEDROCK === '1' ||\n process.env.CLAUDE_CODE_USE_VERTEX === '1' ||\n process.env.CLAUDE_CODE_USE_FOUNDRY === '1'\n ) {\n log(false, 'auth-bedrock-vertex-foundry');\n return { eligible: false, reason: 'auth-bedrock-vertex-foundry' };\n }\n\n // Docker workspace gate. We do not yet share a socket dir between host and\n // container; deferred to a follow-up issue (see hazards H10).\n if (\n process.env.OVERDECK_DOCKER_WORKSPACE === '1' ||\n process.env.PAN_DOCKER === '1'\n ) {\n log(false, 'docker-not-supported-yet');\n return { eligible: false, reason: 'docker-not-supported-yet' };\n }\n\n log(true);\n return { eligible: true };\n}\n\n/**\n * Write the per-agent MCP config that points claude at the overdeck-bridge\n * stdio server. The path is the workspace-local <workspace>/.pan/agent-mcp.json\n * — one config per agent, never shared, never reused.\n */\nexport async function writeChannelsBridgeMcpConfig(\n configPath: string,\n agentId: string,\n): Promise<void> {\n const fsp = await import('fs/promises');\n await fsp.mkdir(dirname(configPath), { recursive: true });\n // Resolve the bridge entrypoint from the project root. The source file\n // lives in src/lib/channels/ and is executed directly via `bun run`\n // (Bun runs TypeScript without pre-compilation). We must point at the\n // source, not a dist copy, because the build does not copy the bridge\n // script into the bundle output.\n const here = dirname(import.meta.url.replace('file://', ''));\n const projectRoot = join(here, '..', '..');\n const repoBridgePath = join(projectRoot, 'src', 'lib', 'channels', 'overdeck-bridge.ts');\n const mcpConfig = {\n mcpServers: {\n 'overdeck-bridge': {\n command: 'bun',\n args: ['run', repoBridgePath],\n env: {\n OVERDECK_AGENT_ID: agentId,\n OVERDECK_HOME: process.env.OVERDECK_HOME ?? join(homedir(), '.overdeck'),\n },\n },\n },\n };\n await fsp.writeFile(configPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');\n}\n\n/**\n * Dismiss the dev-channels confirmation TUI dialog rendered by\n * `claude --dangerously-load-development-channels`. The dialog text\n * 'WARNING: Loading development channels' must be on screen before any prompt\n * is delivered, otherwise the channel listener never registers and every\n * early channel push silently falls back to tmux.\n *\n * Polling budget is 20s because cold-start claude with TLDR + Playwright MCP\n * servers attached commonly takes 8–15s to render the first frame; a tighter\n * budget false-negatives. If the dialog is not detected within the timeout,\n * we proceed — the dialog is suppressed in some auth states (e.g. when the\n * binary takes a non-interactive code path), and the launch must not block\n * forever.\n *\n * Uses sendRawKeystrokeAsync intentionally: sendKeysAsync's load-buffer +\n * paste-buffer machinery is for typing message bodies, not for a single\n * Enter on a TUI prompt where mistimed paste can fire before the dialog\n * accepts input.\n *\n * Once the dialog is detected we send Enter and KEEP checking — a single\n * keystroke can be dropped if the TUI is still mid-render, which left the\n * dialog on screen with the helper already returned. We re-send Enter every\n * RESEND_INTERVAL_MS until the needle is gone (bounded by DISMISS_BUDGET_MS).\n */\nexport async function dismissDevChannelsDialog(agentId: string): Promise<void> {\n const TIMEOUT_MS = 20_000;\n const POLL_INTERVAL_MS = 200;\n const RESEND_INTERVAL_MS = 150;\n const DISMISS_BUDGET_MS = 5_000;\n const NEEDLE = 'WARNING: Loading development channels';\n const start = Date.now();\n while (Date.now() - start < TIMEOUT_MS) {\n try {\n const pane = await Effect.runPromise(capturePane(agentId, 50));\n if (pane.includes(NEEDLE)) {\n // Dialog is up. Send Enter, then keep re-sending until the needle\n // clears — the first keystroke can land before the TUI is ready to\n // accept it, leaving the dialog stuck on screen.\n const dismissStart = Date.now();\n while (Date.now() - dismissStart < DISMISS_BUDGET_MS) {\n await Effect.runPromise(sendRawKeystroke(agentId, 'C-m', 'channels:dismiss-dev-dialog'));\n await new Promise((r) => setTimeout(r, RESEND_INTERVAL_MS));\n const after = await Effect.runPromise(\n capturePane(agentId, 50).pipe(Effect.catch(() => Effect.succeed(''))),\n );\n if (!after.includes(NEEDLE)) return;\n }\n console.log(`[${agentId}] channels:dismiss:dialog-still-present-after-budget`);\n return;\n }\n } catch {\n // Capture failures are transient (tmux session not yet visible to\n // the new pane); keep polling within the budget.\n }\n await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));\n }\n console.log(`[${agentId}] channels:dismiss:dialog-not-detected`);\n}\n","/**\n * openai-auth.ts\n *\n * Reads Codex/ChatGPT local auth state and keeps the CLIProxy subscription\n * credential store in sync. OpenAI models are supported only through\n * Codex/ChatGPT subscription auth routed via CLIProxyAPI; API-key fallback is\n * intentionally not used because api.openai.com does not expose an\n * Anthropic-compatible /v1/messages endpoint.\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { readFile } from 'fs/promises';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport { FsError } from './errors.js';\nimport { bridgeCodexAuthToCliproxySync } from './cliproxy.js';\n\nexport interface OpenAIAuthStatus {\n /** True if Codex auth storage exists locally. */\n installed: boolean;\n /** True if Codex/ChatGPT OAuth refresh credentials are available. */\n loggedIn: boolean;\n /** True if the access token is expired, even if refresh may still succeed. */\n expired: boolean;\n /** Auth mode persisted by Codex (usually \"chatgpt\" for subscription auth). */\n authMode: string | null;\n /** Codex account identifier, if present. */\n accountId: string | null;\n /** When Codex last refreshed auth state, if recorded. */\n lastRefresh: string | null;\n /** Access-token expiry timestamp in ms, if it can be derived from the JWT. */\n accessTokenExpiresAt: number | null;\n /** True when an OpenAI API key is present in auth.json or env. */\n hasOpenAIApiKey: boolean;\n /** True when Overdeck bridged ~/.codex/auth.json into CLIProxy config. */\n bridgedFromCodex: boolean;\n}\n\ninterface RawAuthFile {\n auth_mode?: unknown;\n last_refresh?: unknown;\n OPENAI_API_KEY?: unknown;\n tokens?: {\n id_token?: unknown;\n access_token?: unknown;\n refresh_token?: unknown;\n account_id?: unknown;\n };\n}\n\nfunction getCodexAuthPath(): string {\n return join(homedir(), '.codex', 'auth.json');\n}\n\nfunction decodeBase64Url(value: string): string | null {\n try {\n const normalized = value.replace(/-/g, '+').replace(/_/g, '/');\n const padding = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));\n return Buffer.from(normalized + padding, 'base64').toString('utf8');\n } catch {\n return null;\n }\n}\n\nfunction decodeJwtPayload(token: string | null): Record<string, unknown> | null {\n if (!token) return null;\n const parts = token.split('.');\n if (parts.length < 2) return null;\n\n const decoded = decodeBase64Url(parts[1]);\n if (!decoded) return null;\n\n try {\n return JSON.parse(decoded) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction getJwtExpiry(token: string | null): number | null {\n const payload = decodeJwtPayload(token);\n return typeof payload?.exp === 'number' ? payload.exp * 1000 : null;\n}\n\nasync function readCodexAuthAsync(authPath: string): Promise<RawAuthFile | null> {\n if (!existsSync(authPath)) return null;\n try {\n return JSON.parse(await readFile(authPath, 'utf8')) as RawAuthFile;\n } catch {\n return null;\n }\n}\n\nfunction readCodexAuthSync(authPath: string): RawAuthFile | null {\n if (!existsSync(authPath)) return null;\n try {\n return JSON.parse(readFileSync(authPath, 'utf8')) as RawAuthFile;\n } catch {\n return null;\n }\n}\n\nfunction hasApiKey(raw: RawAuthFile | null): boolean {\n return !!process.env.OPENAI_API_KEY\n || (typeof raw?.OPENAI_API_KEY === 'string' && raw.OPENAI_API_KEY.trim().length > 0);\n}\n\nfunction buildStatus(raw: RawAuthFile | null, installed: boolean, bridgedFromCodex: boolean): OpenAIAuthStatus {\n const authMode = typeof raw?.auth_mode === 'string' ? raw.auth_mode : null;\n const lastRefresh = typeof raw?.last_refresh === 'string' ? raw.last_refresh : null;\n const codexAccessToken = typeof raw?.tokens?.access_token === 'string' ? raw.tokens.access_token : null;\n const codexRefreshToken = typeof raw?.tokens?.refresh_token === 'string' ? raw.tokens.refresh_token : null;\n const codexIdToken = typeof raw?.tokens?.id_token === 'string' ? raw.tokens.id_token : null;\n const codexAccountId = typeof raw?.tokens?.account_id === 'string' ? raw.tokens.account_id : null;\n const accessTokenExpiresAt = getJwtExpiry(codexAccessToken) ?? getJwtExpiry(codexIdToken);\n\n return {\n installed,\n loggedIn: !!codexRefreshToken,\n expired: !!(accessTokenExpiresAt && accessTokenExpiresAt < Date.now()),\n authMode,\n accountId: codexAccountId,\n lastRefresh,\n accessTokenExpiresAt,\n hasOpenAIApiKey: hasApiKey(raw),\n bridgedFromCodex,\n };\n}async function getOpenAIAuthStatusPromise(): Promise<OpenAIAuthStatus> {\n const codexDir = join(homedir(), '.codex');\n const authPath = getCodexAuthPath();\n const installed = existsSync(codexDir) || existsSync(authPath);\n const raw = await readCodexAuthAsync(authPath);\n\n let bridgedFromCodex = false;\n try {\n if (bridgeCodexAuthToCliproxySync()) {\n bridgedFromCodex = true;\n }\n } catch { /* non-fatal — cliproxy bridge is best-effort */ }\n\n return buildStatus(raw, installed, bridgedFromCodex);\n}\n\n/** Effect variant of {@link getOpenAIAuthStatus}. */\nexport const getOpenAIAuthStatus = (): Effect.Effect<OpenAIAuthStatus, FsError> =>\n Effect.tryPromise({\n try: () => getOpenAIAuthStatusPromise(),\n catch: (cause) => new FsError({ path: getCodexAuthPath(), operation: 'getOpenAIAuthStatus', cause }),\n });\n\n/** Synchronous variant for CLI-side code. Dashboard server routes should use {@link getOpenAIAuthStatus}. */\nexport function getOpenAIAuthStatusSync(): OpenAIAuthStatus {\n const codexDir = join(homedir(), '.codex');\n const authPath = getCodexAuthPath();\n const installed = existsSync(codexDir) || existsSync(authPath);\n const raw = readCodexAuthSync(authPath);\n\n let bridgedFromCodex = false;\n try {\n if (bridgeCodexAuthToCliproxySync()) {\n bridgedFromCodex = true;\n }\n } catch { /* non-fatal */ }\n\n return buildStatus(raw, installed, bridgedFromCodex);\n}\n","/**\n * Provider Health Probing\n *\n * Pre-flight check for non-Anthropic direct providers before spawning agents.\n * Sends a minimal request to detect quota exhaustion, auth failures, and\n * network issues BEFORE the agent enters Claude Code's opaque retry loop.\n */\n\nimport { Effect } from 'effect';\nimport { getProviderEnvSync, getProviderForModelSync, type ProviderConfig } from './providers.js';\nimport { loadConfigSync as loadYamlConfig } from './config-yaml.js';\nimport { ensureOpenAICompatibleProxyRunning } from './openai-compatible-proxy.js';\nimport type { ModelId } from './settings.js';\n\nexport type ProbeResultKind = 'quota' | 'auth' | 'timeout' | 'network' | 'server' | 'unknown';\n\nexport type ProbeResult =\n | { ok: true }\n | { ok: false; kind: ProbeResultKind; status?: number; message: string };\n\ninterface CacheEntry {\n result: ProbeResult;\n expires: number;\n}\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes — for durable results (ok, auth, quota)\nconst TRANSIENT_CACHE_TTL_MS = 15 * 1000; // timeout/network/server/unknown are often a one-off blip\nconst PROBE_TIMEOUT_MS = 8000;\nconst cache = new Map<string, CacheEntry>();\n\nfunction cacheKey(provider: string, apiKey: string): string {\n // Hash the key to avoid storing raw secrets in memory map keys\n const keyHash = apiKey.slice(0, 8) + '...' + apiKey.slice(-4);\n return `${provider}:${keyHash}`;\n}\n\nfunction classifyError(status: number, body: string): ProbeResult {\n const lower = body.toLowerCase();\n\n if (status === 429) {\n if (lower.includes('quota') || lower.includes('exhausted') || lower.includes('limit')) {\n return { ok: false, kind: 'quota', status, message: parseErrorMessage(body) || 'Quota exhausted' };\n }\n return { ok: false, kind: 'quota', status, message: parseErrorMessage(body) || 'Rate limited (429)' };\n }\n\n if (status === 401 || status === 403) {\n return { ok: false, kind: 'auth', status, message: parseErrorMessage(body) || 'Invalid API key' };\n }\n\n if (status >= 500) {\n return { ok: false, kind: 'server', status, message: parseErrorMessage(body) || `Server error (${status})` };\n }\n\n return { ok: false, kind: 'unknown', status, message: parseErrorMessage(body) || `Unexpected status ${status}` };\n}\n\nfunction parseErrorMessage(body: string): string {\n try {\n const parsed = JSON.parse(body);\n return parsed?.error?.message || parsed?.message || '';\n } catch {\n return body.slice(0, 200);\n }\n}\n\nexport function buildAnthropicMessagesUrl(baseUrl: string): string {\n const normalized = baseUrl.replace(/\\/+$/, '');\n return normalized.endsWith('/v1')\n ? `${normalized}/messages`\n : `${normalized}/v1/messages`;\n}async function probeProviderPromise(\n provider: ProviderConfig,\n apiKey: string,\n model: string,\n): Promise<ProbeResult> {\n const key = cacheKey(provider.name, apiKey);\n const cached = cache.get(key);\n if (cached && Date.now() < cached.expires) {\n return cached.result;\n }\n\n const result = await doProbe(provider, apiKey, model);\n\n // Auth/quota failures are durable (a bad key or exhausted quota won't\n // resolve itself in seconds) and are worth caching for the full TTL.\n // timeout/network/server/unknown are frequently a one-off blip — caching\n // those for 5 minutes would keep telling the user a healthy provider is\n // down for the full window, blocking every retry with a stale verdict.\n const isTransientFailure = !result.ok && (result.kind === 'timeout' || result.kind === 'network' || result.kind === 'server' || result.kind === 'unknown');\n const ttl = isTransientFailure ? TRANSIENT_CACHE_TTL_MS : CACHE_TTL_MS;\n cache.set(key, { result, expires: Date.now() + ttl });\n return result;\n}\n\n/**\n * Clear cached probe result for a provider (e.g. after key change).\n */\nexport function invalidateProbeCacheSync(provider?: string): void {\n if (provider) {\n for (const k of cache.keys()) {\n if (k.startsWith(`${provider}:`)) cache.delete(k);\n }\n } else {\n cache.clear();\n }\n}\n\nasync function doProbe(\n provider: ProviderConfig,\n apiKey: string,\n model: string,\n): Promise<ProbeResult> {\n // Nous Portal and DashScope route through the local OpenAI-compatible proxy\n // sidecar at 127.0.0.1:12436. Without this, the probe runs before the proxy\n // is booted on the first spawn after every dashboard restart and surfaces a\n // spurious \"Cannot reach <provider>: fetch failed\" toast.\n if (provider.name === 'nous' || provider.name === 'dashscope') {\n await Effect.runPromise(ensureOpenAICompatibleProxyRunning());\n // Use GET /v1/models instead of POST /v1/messages. These providers serve\n // reasoning models (qwen/qwen3.6-plus; qwen3.8-max has always-on thinking)\n // that ignore max_tokens for their reasoning phase and routinely take >8s\n // to answer a one-character probe, blowing the timeout. /v1/models\n // verifies network + auth without burning reasoning tokens.\n return probeModelsEndpoint(provider, apiKey);\n }\n\n const providerEnv = getProviderEnvSync(provider, apiKey);\n const baseUrl = providerEnv.ANTHROPIC_BASE_URL ?? provider.baseUrl;\n if (!baseUrl) return { ok: true };\n\n const url = buildAnthropicMessagesUrl(baseUrl);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n signal: controller.signal,\n headers: {\n 'content-type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model,\n max_tokens: 1,\n messages: [{ role: 'user', content: '.' }],\n }),\n });\n\n clearTimeout(timer);\n const body = await response.text();\n\n // 2xx = provider is working (even if it returned empty/short content)\n if (response.ok) {\n return { ok: true };\n }\n\n return classifyError(response.status, body);\n } catch (err: unknown) {\n clearTimeout(timer);\n return classifyFetchError(err, provider);\n }\n}\n\nasync function probeModelsEndpoint(\n provider: ProviderConfig,\n apiKey: string,\n): Promise<ProbeResult> {\n const baseUrl = provider.baseUrl;\n if (!baseUrl) return { ok: true };\n\n const normalized = baseUrl.replace(/\\/+$/, '');\n const url = normalized.endsWith('/v1') ? `${normalized}/models` : `${normalized}/v1/models`;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n signal: controller.signal,\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n accept: 'application/json',\n },\n });\n\n clearTimeout(timer);\n if (response.ok) return { ok: true };\n return classifyError(response.status, await response.text());\n } catch (err: unknown) {\n clearTimeout(timer);\n return classifyFetchError(err, provider);\n }\n}\n\nfunction classifyFetchError(err: unknown, provider: ProviderConfig): ProbeResult {\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return { ok: false, kind: 'timeout', message: `Provider did not respond within ${PROBE_TIMEOUT_MS / 1000}s` };\n }\n if (err.message.includes('ECONNREFUSED') || err.message.includes('ENOTFOUND') || err.message.includes('fetch failed')) {\n return { ok: false, kind: 'network', message: `Cannot reach ${provider.displayName}: ${err.message}` };\n }\n }\n return { ok: false, kind: 'unknown', message: `Probe failed: ${(err as Error).message ?? err}` };\n}\n\n/**\n * User-facing error message for a failed probe, suitable for dashboard display.\n */\nexport function formatProbeError(provider: ProviderConfig, model: string, result: ProbeResult & { ok: false }): string {\n const prefix = `${provider.displayName} (${model})`;\n\n switch (result.kind) {\n case 'quota':\n return `${prefix}: quota exhausted — top up credits or switch to a different model`;\n case 'auth':\n return `${prefix}: authentication failed — check your API key in Settings`;\n case 'timeout':\n return `${prefix}: endpoint unreachable (timeout) — the provider may be down`;\n case 'network':\n return `${prefix}: network error — ${result.message}`;\n case 'server':\n return `${prefix}: provider returned ${result.status} server error — try again later or switch models`;\n default:\n return `${prefix}: pre-flight check failed — ${result.message}`;\n }\n}async function validateProviderHealthPromise(\n model: string,\n apiKey?: string,\n): Promise<void> {\n const provider = getProviderForModelSync(model as ModelId);\n\n // Skip: Anthropic native and OpenAI subscription routing have their own checks.\n if (provider.name === 'anthropic' || provider.name === 'openai') {\n return;\n }\n\n const key = apiKey ?? resolveApiKey(provider);\n if (!key) return; // No key configured — let downstream handle the \"no key\" error\n\n const result = await Effect.runPromise(probeProvider(provider, key, model));\n if (!result.ok) {\n throw new ProviderHealthError(provider, model, result);\n }\n}\n\nfunction resolveApiKey(provider: ProviderConfig): string | undefined {\n const { config } = loadYamlConfig();\n return config.apiKeys[provider.name as keyof typeof config.apiKeys];\n}\n\nexport class ProviderHealthError extends Error {\n public readonly provider: ProviderConfig;\n public readonly model: string;\n public readonly probeResult: ProbeResult & { ok: false };\n\n constructor(provider: ProviderConfig, model: string, result: ProbeResult & { ok: false }) {\n super(formatProbeError(provider, model, result));\n this.name = 'ProviderHealthError';\n this.provider = provider;\n this.model = model;\n this.probeResult = result;\n }\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect variant of {@link probeProvider}. Never fails — probe results\n * (including error classifications) are carried in the success channel as\n * `ProbeResult`.\n */\nexport const probeProvider = (\n provider: ProviderConfig,\n apiKey: string,\n model: string,\n): Effect.Effect<ProbeResult, never> =>\n Effect.promise(() => probeProviderPromise(provider, apiKey, model));\n\n/** Effect variant of {@link validateProviderHealth}. */\nexport const validateProviderHealth = (\n model: string,\n apiKey?: string,\n): Effect.Effect<void, ProviderHealthError> =>\n Effect.tryPromise({\n try: () => validateProviderHealthPromise(model, apiKey),\n catch: (cause) => {\n if (cause instanceof ProviderHealthError) return cause;\n // Should never happen — validateProviderHealth only throws ProviderHealthError.\n // Re-wrap defensively so the typed error channel stays narrow.\n const provider = getProviderForModelSync(model as ModelId);\n return new ProviderHealthError(provider, model, {\n ok: false,\n kind: 'unknown',\n message: cause instanceof Error ? cause.message : String(cause),\n });\n },\n });\n\n/** Effect variant of {@link invalidateProbeCacheSync}. Pure cache mutation; cannot fail. */\nexport const invalidateProbeCache = (provider?: string): Effect.Effect<void, never> =>\n Effect.sync(() => invalidateProbeCacheSync(provider));\n","import { Effect } from 'effect';\nimport type { NormalizedCavemanConfig } from '../config-yaml.js';\nimport { loadConfigSync as loadYamlConfig, resolveModel } from '../config-yaml.js';\nimport { readCavemanVariant } from '../caveman/workspace.js';\nimport { bridgeGeminiAuthToCliproxy, getCliproxyClientEnv } from '../cliproxy.js';\nimport { normalizeModelOverrideSync, requireModelOverrideSync } from '../model-validation.js';\nimport { getOpenAIAuthStatus } from '../openai-auth.js';\nimport { ensureOpenAICompatibleProxyRunning } from '../openai-compatible-proxy.js';\nimport { validateProviderHealth } from '../provider-health.js';\nimport { getProviderEnvSync, getProviderForModelSync } from '../providers.js';\nimport { CLIPROXY_GPT56_CONTEXT_WINDOW, CLIPROXY_GPT56_LONG_CONTEXT_WINDOW, GPT56_LONG_CONTEXT_VARIANTS, OPENROUTER_MODEL_CONTEXT_WINDOWS, hasModelCapabilitySync, getModelCapabilitySync, resolveModelIdSync } from '../model-capabilities.js';\nimport type { Role } from './agent-state.js';\nimport type { RuntimeName } from '../runtimes/types.js';\n\n/** Map abstract/future model names to CLIProxy-supported names.\n * The CLIProxy registry has gpt-5.4 but not gpt-5.4-pro. */\nexport const CLI_PROXY_MODEL_ALIASES: Record<string, string> = {\n 'gpt-5.5-pro': 'gpt-5.5',\n 'gpt-5.4-pro': 'gpt-5.4',\n};\n\n/**\n * Get provider-specific env vars (BASE_URL, AUTH_TOKEN) for a model.\n * Reads the current API key from settings so resumed/recovered agents\n * always use the latest key.\n */\nexport async function getProviderEnvForModel(model: string, harness?: RuntimeName): Promise<Record<string, string>> {\n const provider = getProviderForModelSync(model);\n if (provider.name === 'anthropic') return {};\n // Muse owns login/API credentials; keep them out of Claude's environment.\n if (provider.name === 'meta' && harness === 'muse') return {};\n\n // PAN-1837 review fix: native kimi-code auth is host-owned via `kimi login`\n // (~/.kimi-code/config.toml) — it does not need config.apiKeys.kimi at all.\n // Without this short-circuit, a correctly logged-in operator with no\n // apiKeys.kimi configured hit \"No API key configured for Kimi\" (line ~81\n // below) before ever reaching getProviderEnvSync's own isKimiCode gate,\n // which only skips the Anthropic-compat env — it never bypassed this\n // upstream API-key requirement.\n if (provider.name === 'kimi' && harness === 'kimi-code') {\n return getProviderEnvSync(provider, '', harness);\n }\n\n const { config } = loadYamlConfig();\n\n // OpenRouter API key is stored in config.yaml under providers.openrouter.api_key\n if (provider.name === 'openrouter') {\n const apiKey = config.apiKeys.openrouter;\n if (apiKey) {\n return getProviderEnvSync(provider, apiKey, harness);\n }\n throw new Error(`OpenRouter API key not configured. Add your key in Settings → OpenRouter before using model \"${model}\".`);\n }\n\n const apiKey = config.apiKeys[provider.name as keyof typeof config.apiKeys];\n\n if (provider.name === 'google') {\n if (!apiKey) {\n throw new Error(`Google API key not configured. Add GOOGLE_API_KEY in Settings → Google or ~/.overdeck.env before using model \"${model}\".`);\n }\n\n if (!await Effect.runPromise(bridgeGeminiAuthToCliproxy(apiKey))) {\n throw new Error(`Failed to bridge Google API key into CLIProxy before using model \"${model}\".`);\n }\n\n return getCliproxyClientEnv();\n }\n\n if (provider.name === 'openai') {\n const authStatus = await Effect.runPromise(getOpenAIAuthStatus());\n if (authStatus.loggedIn) {\n // Route through the local CLIProxyAPI sidecar using the user's\n // ChatGPT subscription OAuth tokens. Claude Code sees a normal\n // Anthropic-compatible endpoint and never needs an API key.\n return getCliproxyClientEnv();\n }\n\n const configuredKey = apiKey || authStatus.hasOpenAIApiKey;\n throw new Error(\n configuredKey\n ? `OpenAI API-key routing is no longer supported for model \"${model}\" because api.openai.com does not expose an Anthropic-compatible /v1/messages endpoint. Sign in with a Codex/ChatGPT subscription via \\`pan admin specialists codex login\\` or Dashboard Settings → Codex Login.`\n : `Codex/ChatGPT subscription login required for OpenAI model \"${model}\". Sign in via \\`pan admin specialists codex login\\` or Dashboard Settings → Codex Login.`,\n );\n }\n\n if (apiKey) {\n if (provider.name === 'nous') {\n await Effect.runPromise(ensureOpenAICompatibleProxyRunning());\n }\n await Effect.runPromise(validateProviderHealth(model, apiKey));\n return getProviderEnvSync(provider, apiKey, harness);\n }\n\n throw new Error(`No API key configured for ${provider.displayName}. Configure it in Settings before using model \"${model}\".`);\n}\n\n/**\n * Get bash export lines for provider env vars (for use in launcher scripts).\n * Returns empty string for Anthropic models.\n */\nconst PROVIDER_ENV_KEYS = [\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_BASE_URL',\n 'ANTHROPIC_AUTH_TOKEN',\n 'ANTHROPIC_DEFAULT_HAIKU_MODEL',\n 'ANTHROPIC_DEFAULT_OPUS_MODEL',\n 'ANTHROPIC_DEFAULT_SONNET_MODEL',\n 'ANTHROPIC_SMALL_FAST_MODEL',\n 'CLAUDE_CODE_SUBAGENT_MODEL',\n 'CLAUDE_CODE_AUTO_COMPACT_WINDOW',\n 'CLAUDE_CODE_MAX_CONTEXT_TOKENS',\n 'OPENAI_API_KEY',\n 'GEMINI_API_KEY',\n 'API_TIMEOUT_MS',\n 'CLAUDE_CODE_API_KEY_HELPER_TTL_MS',\n // Pi-native provider env vars (bridged from Overdeck settings so Pi can auth)\n 'KIMI_API_KEY',\n 'MINIMAX_API_KEY',\n 'ZAI_API_KEY',\n 'MIMO_API_KEY',\n 'OPENROUTER_API_KEY',\n 'NOUS_API_KEY',\n 'DASHSCOPE_API_KEY',\n 'QUANTUMLLAMA_API_KEY',\n] as const;\n\n// PAN-3057: the harness pin and the capability table are ONE number now. They\n// used to disagree (372K here, 150K in model-capabilities), so the dashboard\n// meter and the Deacon's proactive compaction scored GPT-5.6 agents against a\n// window 2.5x smaller than the one the harness was actually given.\n// PAN-3388: bare ids pin to the 272K billing tier; [372k] variants opt into\n// the long window at 2x-input/1.5x-output quota burn past 272K.\n// gpt-6-astra shares the 272K billing tier and the 872K raw ceiling with the\n// GPT-5.6 family (Codex catalog, 2026-09-07), so it takes the same pin. The\n// set name is kept for continuity with PAN-3057/PAN-3388.\nconst GPT_56_MODELS = new Set(['gpt-6-astra', 'gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna']);\nconst GPT_56_LONG_MODELS = new Set(Object.keys(GPT56_LONG_CONTEXT_VARIANTS));\n\ninterface ClaudeCodeContextPolicy {\n autoCompactWindow?: number;\n maxContextTokens?: number;\n}\n\nexport function getClaudeCodeContextPolicyForModel(model: string): ClaudeCodeContextPolicy {\n const provider = getProviderForModelSync(model);\n if (provider.name === 'anthropic') {\n return hasModelCapabilitySync(model)\n ? { autoCompactWindow: getModelCapabilitySync(resolveModelIdSync(model)).contextWindow }\n : {};\n }\n\n\n const resolvedModel = resolveModelIdSync(model);\n // OpenRouter models are unknown to Claude Code, which assumes a 200K window\n // for unrecognized ids. Pin both vars (K3 precedent — only\n // CLAUDE_CODE_MAX_CONTEXT_TOKENS is verified to lift the 200K assumption;\n // CLAUDE_CODE_AUTO_COMPACT_WINDOW keeps the PAN-2441 compaction pin aligned).\n if (provider.name === 'openrouter') {\n const openrouterWindow = OPENROUTER_MODEL_CONTEXT_WINDOWS[resolvedModel];\n if (openrouterWindow !== undefined) {\n return {\n autoCompactWindow: openrouterWindow,\n maxContextTokens: openrouterWindow,\n };\n }\n return {};\n }\n if (GPT_56_LONG_MODELS.has(resolvedModel)) {\n return {\n autoCompactWindow: CLIPROXY_GPT56_LONG_CONTEXT_WINDOW,\n maxContextTokens: CLIPROXY_GPT56_LONG_CONTEXT_WINDOW,\n };\n }\n if (GPT_56_MODELS.has(resolvedModel)) {\n return {\n autoCompactWindow: CLIPROXY_GPT56_CONTEXT_WINDOW,\n maxContextTokens: CLIPROXY_GPT56_CONTEXT_WINDOW,\n };\n }\n if (!hasModelCapabilitySync(resolvedModel)) return {};\n\n const contextWindow = getModelCapabilitySync(resolvedModel).contextWindow;\n // Unknown-to-Claude model IDs otherwise keep its smaller native budget.\n // Both ceilings must describe the same context shown in Overdeck's picker.\n return { autoCompactWindow: contextWindow, maxContextTokens: contextWindow };\n}\n\nexport async function getProviderExportsForModel(model: string, harness?: RuntimeName): Promise<string> {\n const envVars = await getProviderEnvForModel(model, harness);\n const unsetLines = PROVIDER_ENV_KEYS.map(key => `unset ${key}`);\n const contextPolicy = getClaudeCodeContextPolicyForModel(model);\n const exportLines = Object.entries(envVars)\n .map(([k, v]) => `export ${k}=\"${v.replace(/\"/g, '\\\\\"')}\"`);\n if (contextPolicy.maxContextTokens !== undefined) {\n exportLines.push(`export CLAUDE_CODE_MAX_CONTEXT_TOKENS=\"${contextPolicy.maxContextTokens}\"`);\n }\n if (contextPolicy.autoCompactWindow !== undefined) {\n exportLines.push(`export CLAUDE_CODE_AUTO_COMPACT_WINDOW=\"${contextPolicy.autoCompactWindow}\"`);\n }\n\n return [...unsetLines, ...exportLines].join('\\n') + '\\n';\n}\n\n/**\n * Build a sanitized env for programmatically spawning `claude`.\n *\n * The dashboard parent process may inherit provider env vars (e.g.\n * ANTHROPIC_BASE_URL pointing at the CLIProxy sidecar) that would mis-route\n * a child process targeting an Anthropic model. Launcher *scripts* strip\n * these via `unset` lines; programmatic spawns must do the same.\n *\n * Returns a copy of `baseEnv` (default: process.env) with all PROVIDER_ENV_KEYS\n * deleted, then overlaid with the correct provider env for `model`.\n */\nexport async function buildSpawnEnvForModel(\n model: string,\n baseEnv: NodeJS.ProcessEnv = process.env,\n): Promise<Record<string, string>> {\n const sanitized: Record<string, string> = {};\n for (const [k, v] of Object.entries(baseEnv)) {\n if (v === undefined) continue;\n if ((PROVIDER_ENV_KEYS as readonly string[]).includes(k)) continue;\n sanitized[k] = v;\n }\n const providerEnv = await getProviderEnvForModel(model);\n const contextPolicy = getClaudeCodeContextPolicyForModel(model);\n return {\n ...sanitized,\n ...providerEnv,\n ...(contextPolicy.maxContextTokens !== undefined ? { CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(contextPolicy.maxContextTokens) } : {}),\n ...(contextPolicy.autoCompactWindow !== undefined ? { CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(contextPolicy.autoCompactWindow) } : {}),\n };\n}\n\n/**\n * Get tmux -e flags for provider env vars (for use in tmux new-session).\n * Returns empty string for Anthropic models.\n */\nexport async function getProviderTmuxFlags(model: string): Promise<string> {\n const envVars = await getProviderEnvForModel(model);\n let flags = '';\n for (const [key, value] of Object.entries(envVars)) {\n flags += ` -e ${key}=\"${value.replace(/\"/g, '\\\\\"')}\"`;\n }\n return flags;\n}\n\n/**\n * Build shell export lines to inject into a work agent's launcher.sh.\n *\n * Sets CAVEMAN_DEFAULT_MODE and OVERDECK_CAVEMAN_VARIANT so the caveman\n * SessionStart hook activates at the right intensity level and cost events\n * carry the A/B test variant.\n *\n * @param workspacePath Absolute workspace path (to read stored variant)\n * @param config Normalized caveman config from YamlConfig\n * @param isPlanning True for planning agents — caveman always disabled there\n * @returns Shell export lines to prepend to the launcher script\n */\nexport async function buildCavemanExports(\n workspacePath: string,\n config: NormalizedCavemanConfig,\n isPlanning: boolean\n): Promise<string> {\n // Planning agents: never compress — output is user-facing\n if (isPlanning || !config.enabled) return '';\n\n const variant = await Effect.runPromise(readCavemanVariant(workspacePath));\n\n // If this workspace's A/B variant is 'disabled', set variant for tracking but no mode\n if (variant === 'off') return '';\n if (variant === 'disabled') {\n return `export OVERDECK_CAVEMAN_VARIANT=\"${variant}\"\\n`;\n }\n\n // Work agents use the 'work' intensity mode\n const mode = config.modes.work;\n if (mode === 'off' || mode === 'disabled') return '';\n\n return `export CAVEMAN_DEFAULT_MODE=\"${mode}\"\\nexport OVERDECK_CAVEMAN_VARIANT=\"${variant}\"\\n`;\n}\n\n/**\n * Determine which model to use for a role-based work agent.\n *\n * Priority:\n * 1. Explicitly provided model (options.model)\n * 2. Role routing via config.yaml roles/workhorses (defaults to work)\n *\n * Resolution failures propagate as spawn-time errors. Per PAN-1048 PRD:\n * invalid workhorse references and unresolved role configs must fail loudly\n * at config-load/spawn time, not silently fall back to a hidden default\n * model. Defaults are seeded into the config when entries are absent\n * (DEFAULT_WORKHORSES / DEFAULT_ROLES) — anything that still raises here\n * is a real configuration bug the user must see.\n */\n/**\n * Models that are known-broken for autonomous *work* agents and must never be\n * used to spawn one, even if a project pins them in config. The gate fails\n * loudly for the work role when the model wasn't an explicit per-spawn override.\n *\n * Empty as of PAN-1584: gpt-5.5 used to wedge at launch with CLIProxy \"System\n * messages are not allowed\", which was a stale CLIProxyAPI binary (6.9.45)\n * mis-translating Claude Code's request to the Codex backend. Upgrading the\n * pinned CLIProxyAPI to v7.1.39 (+ a version-aware installer) fixed it; gpt-5.5\n * work agents now launch clean under the claude-code harness. The mechanism is\n * retained for any future known-broken model. (Pi-harness gpt-5.5 was not\n * re-verified in this pass — re-add 'gpt-5.5' here if a Pi init hang resurfaces.)\n */\nconst WORK_AGENT_BROKEN_MODELS = new Set<string>([]);\n\nexport function determineModel(options: { model?: string; role?: Role; spawnKey?: string } = {}): string {\n const modelOverride = normalizeModelOverrideSync(options.model);\n const resolved = modelOverride\n ? modelOverride\n : requireModelOverrideSync(resolveModel(options.role ?? 'work', undefined, loadYamlConfig().config, options.spawnKey));\n\n // Work-agent safety net: a config pin (or smart-selection) must not spawn a\n // work agent on a model that is known to wedge for the work role. Fail loudly\n // rather than launch a dead agent or silently substitute another model. Only\n // applies to the work role and only when the model wasn't an explicit,\n // deliberate per-spawn override.\n const role = options.role ?? 'work';\n if (role === 'work' && !modelOverride && WORK_AGENT_BROKEN_MODELS.has(resolved)) {\n throw new Error(\n `Resolved work model \"${resolved}\" is known-broken for work agents. ` +\n 'Set roles.work.model to a working model in config.yaml, or pass an explicit --model override.',\n );\n }\n\n return resolved;\n}\n","/**\n * claude-auth.ts\n *\n * Reads Claude Code's credentials to determine whether the user is logged in\n * with a subscription (MAX / Pro / Team) or not.\n *\n * Credentials are checked in order:\n * 1. ~/.claude/.credentials.json (Linux, older Claude Code versions)\n * 2. macOS Keychain: \"Claude Code-credentials\" (macOS with newer Claude Code)\n *\n * The credential payload contains:\n * claudeAiOauth.subscriptionType — \"max\" | \"pro\" | \"team\" | null\n * claudeAiOauth.rateLimitTier — e.g. \"default_claude_max_20x\"\n * claudeAiOauth.accessToken — OAuth bearer token\n * claudeAiOauth.expiresAt — Unix timestamp (ms)\n */\n\nimport { existsSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { platform } from 'process';\nimport { Duration, Effect, FileSystem } from 'effect';\nimport { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';\nimport { ChildProcess } from 'effect/unstable/process';\nimport { layer as NodeServicesLayer } from '@effect/platform-node/NodeServices';\nimport { ClaudeCredentialParseError } from './errors.js';\n\nexport interface ClaudeAuthStatus {\n /** True if the ~/.claude directory exists (i.e. Claude Code has been installed). */\n installed: boolean;\n /** True if a non-expired OAuth token is present. */\n loggedIn: boolean;\n /** True if the token exists but has already expired. */\n expired: boolean;\n /** \"max\" | \"pro\" | \"team\" | null — from claudeAiOauth.subscriptionType */\n subscriptionType: string | null;\n /** e.g. \"default_claude_max_20x\" */\n rateLimitTier: string | null;\n /** Token expiry timestamp in ms, or null if unknown. */\n expiresAt: number | null;\n /** True when ANTHROPIC_API_KEY is set in the server process environment.\n * When present, Claude Code prefers this over subscription auth. */\n hasAnthropicApiKey: boolean;\n}\n\nexport function parseOAuthPayload(raw: string): Effect.Effect<\n {\n loggedIn: boolean;\n expired: boolean;\n subscriptionType: string | null;\n rateLimitTier: string | null;\n expiresAt: number | null;\n },\n ClaudeCredentialParseError\n> {\n return Effect.try({\n try: () => {\n const creds = JSON.parse(raw) as Record<string, unknown>;\n const oauth = (creds.claudeAiOauth ?? {}) as Record<string, unknown>;\n\n if (!oauth.accessToken) {\n return { loggedIn: false, expired: false, subscriptionType: null, rateLimitTier: null, expiresAt: null };\n }\n\n const expiresAt = typeof oauth.expiresAt === 'number' ? oauth.expiresAt : null;\n const expired = !!(expiresAt && expiresAt < Date.now());\n\n // Treat as logged in even if expired — Claude Code auto-refreshes tokens\n // transparently. Only mark as not-logged-in if there's no token at all.\n return {\n loggedIn: true,\n expired,\n subscriptionType: typeof oauth.subscriptionType === 'string' ? oauth.subscriptionType : null,\n rateLimitTier: typeof oauth.rateLimitTier === 'string' ? oauth.rateLimitTier : null,\n expiresAt,\n };\n },\n catch: (cause) => new ClaudeCredentialParseError({ message: 'Failed to parse credentials JSON', cause }),\n });\n}\n\n/**\n * Read credentials JSON from macOS Keychain.\n * Claude Code ≥2.x stores credentials under service \"Claude Code-credentials\".\n */\nconst readKeychainCredentials: Effect.Effect<string | null, never, ChildProcessSpawner> =\n Effect.gen(function* () {\n if (platform !== 'darwin') return null;\n const spawner = yield* ChildProcessSpawner;\n const cmd = ChildProcess.make('security', [\n 'find-generic-password', '-s', 'Claude Code-credentials', '-w',\n ]);\n return yield* spawner.string(cmd).pipe(\n Effect.timeout(Duration.seconds(3)),\n Effect.map((s) => s.trim() || null),\n Effect.orElseSucceed(() => null),\n );\n });\n\nconst getClaudeAuthStatusImpl: Effect.Effect<\n ClaudeAuthStatus,\n never,\n FileSystem.FileSystem | ChildProcessSpawner\n> = Effect.gen(function* () {\n const claudeDir = join(homedir(), '.claude');\n const credPath = join(claudeDir, '.credentials.json');\n\n const installed = existsSync(claudeDir);\n const hasAnthropicApiKey = !!process.env.ANTHROPIC_API_KEY;\n\n let loggedIn = false;\n let expired = false;\n let subscriptionType: string | null = null;\n let rateLimitTier: string | null = null;\n let expiresAt: number | null = null;\n\n // 1. Try flat credentials file (Linux, older Claude Code)\n if (existsSync(credPath)) {\n const fs = yield* FileSystem.FileSystem;\n const result = yield* fs.readFileString(credPath, 'utf-8').pipe(\n Effect.flatMap(parseOAuthPayload),\n Effect.orElseSucceed(() => null),\n );\n if (result) {\n loggedIn = result.loggedIn;\n expired = result.expired;\n subscriptionType = result.subscriptionType;\n rateLimitTier = result.rateLimitTier;\n expiresAt = result.expiresAt;\n }\n }\n\n // 2. Fall back to macOS Keychain (Claude Code ≥2.x on macOS)\n if (!loggedIn) {\n const keychainRaw = yield* readKeychainCredentials;\n if (keychainRaw) {\n const result = yield* parseOAuthPayload(keychainRaw).pipe(\n Effect.orElseSucceed(() => null),\n );\n if (result) {\n loggedIn = result.loggedIn;\n expired = result.expired;\n subscriptionType = result.subscriptionType;\n rateLimitTier = result.rateLimitTier;\n expiresAt = result.expiresAt;\n }\n }\n }\n\n return { installed, loggedIn, expired, subscriptionType, rateLimitTier, expiresAt, hasAnthropicApiKey };\n});\n\nexport function getClaudeAuthStatus(): Effect.Effect<ClaudeAuthStatus, never> {\n return getClaudeAuthStatusImpl.pipe(\n Effect.scoped,\n Effect.provide(NodeServicesLayer),\n );\n}\n","/**\n * Workspace context assembly (PAN-1201).\n *\n * The persisted workspace layer is harness-neutral. Overdeck assembles it at\n * workspace creation under `<workspace>/.overdeck/context/workspace.md`, then\n * launch-time composition renders global and project sources for the active\n * harness and combines them with this workspace-only content.\n */\n\nconst SECTION_SEPARATOR = '\\n\\n---\\n\\n';\nexport const PROJECT_LAYER_START = '<!-- overdeck:project-layer:start';\nexport const PROJECT_LAYER_END = '<!-- overdeck:project-layer:end -->';\nexport const WORKSPACE_CONTEXT_V2 = '<!-- overdeck:workspace-context:v2 harness-neutral -->';\n\nfunction removeMarkedProjectLayer(content: string): string | null {\n const start = content.indexOf(PROJECT_LAYER_START);\n if (start < 0) return null;\n const markerEnd = content.indexOf('-->', start + PROJECT_LAYER_START.length);\n if (markerEnd < 0) return null;\n const marker = content.slice(start, markerEnd + 3);\n const lengthMatch = /\\schars=(\\d+)\\s-->$/.exec(marker);\n if (!lengthMatch) return null;\n const projectStart = markerEnd + 4;\n const projectLength = Number(lengthMatch[1]);\n const projectEnd = projectStart + projectLength;\n if (content[markerEnd + 3] !== '\\n') return null;\n if (content.slice(projectEnd, projectEnd + 1 + PROJECT_LAYER_END.length) !== `\\n${PROJECT_LAYER_END}`) return null;\n const end = projectEnd + 1;\n\n let before = content.slice(0, start);\n let after = content.slice(end + PROJECT_LAYER_END.length);\n if (before.endsWith(SECTION_SEPARATOR) && after.startsWith(SECTION_SEPARATOR)) {\n before = before.slice(0, -SECTION_SEPARATOR.length);\n } else if (before.endsWith(SECTION_SEPARATOR)) {\n before = before.slice(0, -SECTION_SEPARATOR.length);\n } else if (after.startsWith(SECTION_SEPARATOR)) {\n after = after.slice(SECTION_SEPARATOR.length);\n }\n return `${before}${after}`.trim();\n}\n\n/**\n * Remove a project layer embedded by pre-PAN-3779 workspace assembly.\n *\n * New bundles contain no project layer. Legacy marked bundles can be stripped\n * exactly. Ambiguous legacy bundles must be reviewed before launch; silently\n * truncating them would lose memory or status alongside stale project text.\n */\nexport function workspaceContextWithoutProjectLayer(content: string): string {\n if (content.includes(WORKSPACE_CONTEXT_V2)) return content.trim();\n const marked = removeMarkedProjectLayer(content);\n if (marked !== null) return marked;\n\n if (content.includes(PROJECT_LAYER_START) || content.includes(SECTION_SEPARATOR)) {\n throw new Error('Legacy workspace context has no valid project boundary. Review it with pan context edit --layer workspace and preserve its memory/status before rebuilding the workspace context.');\n }\n return content.trim();\n}\n\n/** Inputs for {@link assembleWorkspaceContext}. */\nexport interface WorkspaceContextInput {\n /** Issue identifier, e.g. \"PAN-1201\". */\n issueId: string;\n /** Absolute path to the workspace worktree. */\n workspacePath: string;\n /** Issue title, when known. */\n issueTitle?: string;\n /** Feature branch name, when known. */\n branch?: string;\n /** Short xBRIEF summary, when known. */\n xbriefSummary?: string;\n /** Current pipeline phase, when known. */\n phase?: string;\n /** Pre-formatted PAN-1052 memory block, when available. */\n memoryContext?: string;\n /** Pre-formatted live workspace status summary, when available. */\n statusSummary?: string;\n}\n\n/** Assemble a harness-neutral workspace layer. */\nexport function assembleWorkspaceContext(input: WorkspaceContextInput): string {\n const sections: string[] = [WORKSPACE_CONTEXT_V2];\n\n const header = [`# Workspace: ${input.issueId}`, ''];\n if (input.issueTitle) header.push(`**Issue:** ${input.issueId} — ${input.issueTitle}`);\n else header.push(`**Issue:** ${input.issueId}`);\n if (input.branch) header.push(`**Branch:** ${input.branch}`);\n header.push(`**Path:** ${input.workspacePath}`);\n if (input.phase) header.push(`**Phase:** ${input.phase}`);\n if (input.xbriefSummary) {\n header.push('', '## Plan Summary', '', input.xbriefSummary.trim());\n }\n sections.push(header.join('\\n'));\n\n if (input.memoryContext && input.memoryContext.trim()) {\n sections.push(input.memoryContext.trim());\n }\n\n if (input.statusSummary && input.statusSummary.trim()) {\n sections.push(['## Workspace Status', '', input.statusSummary.trim()].join('\\n'));\n }\n\n return sections.join(SECTION_SEPARATOR).replace(/\\n{3,}/g, '\\n\\n').trimEnd() + '\\n';\n}\n","/**\n * Harness templating for context layers (PAN-1201).\n *\n * A single canonical markdown source per context layer renders to N\n * harness-specific outputs. Harness-specific divergence is expressed with\n * Mustache-style block markers:\n *\n * {{#harness:claude}} … {{/harness:claude}}\n * {{#harness:pi}} … {{/harness:pi}}\n *\n * Blocks may be stacked to mark one span for several harnesses (union):\n *\n * {{#harness:claude}}{{#harness:pi}}\n * Shared: never commit secrets.\n * {{/harness:claude}}{{/harness:pi}}\n *\n * Rendering for harness H keeps a span when it is covered by no harness\n * marker (always-on) or by a marker naming H; every marker is stripped from\n * the output. Markers for an unrecognised harness name are still parsed —\n * `pan context validate` only warns about them — so a layer can be authored\n * for a harness Overdeck does not ship an adapter for yet.\n */\n\nimport type { Harness } from '@overdeck/contracts';\n\n/** Short marker name used in `{{#harness:<name>}}` blocks. */\nexport type HarnessMarker = 'claude' | 'pi' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | 'opencode' | 'muse';\n\n/** Maps a Overdeck {@link Harness} to its templating marker name. */\nexport const HARNESS_MARKERS: Record<Harness, HarnessMarker> = {\n 'claude-code': 'claude',\n ohmypi: 'ohmypi',\n codex: 'codex',\n acp: 'acp',\n opencode: 'opencode',\n 'kimi-code': 'kimi-code',\n muse: 'muse',\n};\n\n/** Marker names Overdeck ships an adapter for in v1. */\nexport const KNOWN_HARNESS_MARKERS: ReadonlySet<string> = new Set<HarnessMarker>(['claude', 'pi', 'ohmypi', 'codex', 'acp', 'kimi-code', 'opencode', 'muse']);\n\n/** Matches an open `{{#harness:x}}` or close `{{/harness:x}}` marker. */\nconst MARKER_RE = /\\{\\{([#/])harness:([a-zA-Z0-9_-]+)\\}\\}/g;\n\n/**\n * Render a canonical layer body for one harness, or a union of harnesses\n * sharing one physical target file (e.g. AGENTS.md, read natively by both\n * ohmypi and kimi-code — PAN-1837 review fix: rendering AGENTS.md for\n * 'ohmypi' alone stripped every span authored only under\n * {{#harness:kimi-code}}, so Kimi-specific context never survived `pan\n * sync`). A span is kept when it is covered by no harness marker (always-on)\n * or by a marker naming any of the target harnesses; every marker is\n * stripped from the output. Runs of 3+ blank lines left by removed spans\n * collapse to a single blank line so output stays tidy.\n */\nexport function renderForHarness(content: string, harness: Harness | readonly Harness[]): string {\n const harnessList: readonly Harness[] = Array.isArray(harness) ? harness : [harness as Harness];\n const targets = new Set<string>(harnessList.map((h) => HARNESS_MARKERS[h]));\n const openCounts = new Map<string, number>();\n let out = '';\n let lastIndex = 0;\n\n const emit = (text: string): void => {\n // The \"covering set\" is every harness name with a currently-open marker.\n // Empty → always-on. Counters (not a stack) make overlapping, non-nested\n // stacked blocks like {{#claude}}{{#pi}}…{{/claude}}{{/pi}} resolve right.\n let covered = false;\n let anyOpen = false;\n for (const [name, count] of openCounts) {\n if (count > 0) {\n anyOpen = true;\n if (targets.has(name)) covered = true;\n }\n }\n if (!anyOpen || covered) out += text;\n };\n\n MARKER_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = MARKER_RE.exec(content)) !== null) {\n emit(content.slice(lastIndex, m.index));\n const [, kind, name] = m;\n const delta = kind === '#' ? 1 : -1;\n openCounts.set(name, (openCounts.get(name) ?? 0) + delta);\n lastIndex = m.index + m[0].length;\n }\n emit(content.slice(lastIndex));\n\n return out.replace(/\\n{3,}/g, '\\n\\n');\n}\n\n/** A problem found by {@link validateTemplate}. */\nexport interface TemplateIssue {\n severity: 'error' | 'warning';\n message: string;\n}\n\n/** Result of linting a context-layer template. */\nexport interface TemplateValidation {\n ok: boolean;\n issues: TemplateIssue[];\n}\n\n/**\n * Lint a context-layer template for malformed harness blocks.\n *\n * Errors: an unclosed `{{#harness:x}}` or a stray `{{/harness:x}}` with no\n * matching open. Warnings: a marker naming a harness Overdeck has no\n * adapter for (allowed for forward compatibility, surfaced so typos —\n * `{{#harness:clade}}` — do not pass silently).\n */\nexport function validateTemplate(content: string): TemplateValidation {\n const issues: TemplateIssue[] = [];\n const openCounts = new Map<string, number>();\n const seen = new Set<string>();\n\n MARKER_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = MARKER_RE.exec(content)) !== null) {\n const [, kind, name] = m;\n seen.add(name);\n if (kind === '#') {\n openCounts.set(name, (openCounts.get(name) ?? 0) + 1);\n } else {\n const next = (openCounts.get(name) ?? 0) - 1;\n openCounts.set(name, next);\n if (next < 0) {\n issues.push({\n severity: 'error',\n message: `stray closing marker {{/harness:${name}}} with no matching {{#harness:${name}}}`,\n });\n }\n }\n }\n\n for (const [name, count] of openCounts) {\n if (count > 0) {\n issues.push({\n severity: 'error',\n message: `unclosed block: {{#harness:${name}}} opened ${count} time(s) without a matching {{/harness:${name}}}`,\n });\n }\n }\n\n for (const name of seen) {\n if (!KNOWN_HARNESS_MARKERS.has(name)) {\n issues.push({\n severity: 'warning',\n message: `unknown harness \"${name}\" — Overdeck ships adapters for: ${[...KNOWN_HARNESS_MARKERS].join(', ')}`,\n });\n }\n }\n\n return { ok: !issues.some((i) => i.severity === 'error'), issues };\n}\n","/**\n * Context layer model (PAN-1201).\n *\n * Three layers of context compose at sync time:\n *\n * global — ~/.overdeck/context/global.md (+ global/{skills,agents}/)\n * Applies to every harness invocation, everywhere.\n * project — <projectRoot>/.overdeck/context/project.md\n * Applies when CWD is under a registered project. Committed.\n * workspace — <workspace>/.overdeck/context/workspace.md\n * Auto-assembled by Overdeck at spawn time. Gitignored.\n *\n * The global layer lives under ~/.overdeck (per-machine). Project and\n * Project and workspace context live outside the permanent state plane under\n * `.overdeck/`. Reads retain a `.pan/context/` compatibility fallback.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { getOverdeckHome } from '../paths.js';\n\n/** The three context layers, outermost to innermost. */\nexport type ContextLayerKind = 'global' | 'project' | 'workspace';\n\nexport const CONTEXT_LAYER_KINDS: readonly ContextLayerKind[] = ['global', 'project', 'workspace'];\n\n/** A context layer's canonical markdown file and its on-disk presence. */\nexport interface ContextLayer {\n kind: ContextLayerKind;\n /** Absolute path to the layer's canonical markdown file. */\n file: string;\n /** Absolute path to the layer's directory (holds the .md + skills/agents). */\n dir: string;\n exists: boolean;\n}\n\n// ─── Path resolution ──────────────────────────────────────────────────────\n\n/** `~/.overdeck/context` — the global layer directory. */\nexport function globalContextDir(): string {\n return join(getOverdeckHome(), 'context');\n}\n\n/** `~/.overdeck/context/global.md` — the global layer's canonical source. */\nexport function globalContextFile(): string {\n return join(globalContextDir(), 'global.md');\n}\n\n/** `~/.overdeck/context/global/skills` — user skills in the global layer. */\nexport function globalSkillsDir(): string {\n return join(globalContextDir(), 'global', 'skills');\n}\n\n/** `~/.overdeck/context/global/agents` — user agents in the global layer. */\nexport function globalAgentsDir(): string {\n return join(globalContextDir(), 'global', 'agents');\n}\n\n/** `<projectRoot>/.overdeck/context` — a registered project's layer directory. */\nexport function projectContextDir(projectRoot: string): string {\n return join(projectRoot, '.overdeck', 'context');\n}\n\n/** `<projectRoot>/.overdeck/context/project.md` — a project's canonical source. */\nexport function projectContextFile(projectRoot: string): string {\n return join(projectContextDir(projectRoot), 'project.md');\n}\n\nexport function legacyProjectContextFile(projectRoot: string): string {\n return join(projectRoot, '.pan', 'context', 'project.md');\n}\n\nexport function resolveProjectContextFile(projectRoot: string): string {\n const canonical = projectContextFile(projectRoot);\n return existsSync(canonical) ? canonical : legacyProjectContextFile(projectRoot);\n}\n\n/** `<workspace>/.overdeck/context` — a workspace's layer directory. */\nexport function workspaceContextDir(workspacePath: string): string {\n return join(workspacePath, '.overdeck', 'context');\n}\n\n/** `<workspace>/.pan/context/workspace.md` — the auto-assembled bundle. */\nexport function workspaceContextFile(workspacePath: string): string {\n return join(workspaceContextDir(workspacePath), 'workspace.md');\n}\n\nexport function legacyWorkspaceContextFile(workspacePath: string): string {\n return join(workspacePath, '.pan', 'context', 'workspace.md');\n}\n\nexport function resolveWorkspaceContextFile(workspacePath: string): string {\n const canonical = workspaceContextFile(workspacePath);\n return existsSync(canonical) ? canonical : legacyWorkspaceContextFile(workspacePath);\n}\n\n/** `~/.overdeck/context/pi-global.md` — the rendered global layer for Pi. */\nexport function piGlobalContextFile(): string {\n return join(getOverdeckHome(), 'context', 'pi-global.md');\n}\n\n/** `~/.overdeck/context/claude-global.md` — launch-time global context for managed Claude sessions. */\nexport function claudeGlobalContextFile(): string {\n return join(getOverdeckHome(), 'context', 'claude-global.md');\n}\n\n/** `~/.overdeck/context/codex-global.md` — the rendered global layer for Codex. */\nexport function codexGlobalContextFile(): string {\n return join(getOverdeckHome(), 'context', 'codex-global.md');\n}\n\n// ─── Starter templates ────────────────────────────────────────────────────\n\n/** Seeded into `global.md` by `pan install` / first `pan sync`. */\nexport const GLOBAL_STARTER = `# Per-Machine Context (this machine only)\n\nThis file is **per-machine** — it lives at \\`~/.overdeck/context/global.md\\`\non the disk in front of you. Nothing syncs it to your other machines.\n\nFor content you want on **every** machine — engineering rules, coding\nstandards, behavioral guidelines — add a **bundled rule**:\n\\`sync-sources/rules/<name>.md\\` in the overdeck repo with\n\\`scope: universal\\` (or \\`scope: dev\\` for Overdeck-developer-only rules).\nIt will ship with every \\`pan install\\` / \\`pan sync\\`. \"Bundled rule\",\n\"global layer\" (this file), \"project layer\", and \"workspace layer\" are the\ncanonical placement terms — see the context-nomenclature bundled rule.\n\nThis file should be empty unless you have **genuinely machine-specific**\nquirks to record (e.g. \"this laptop's GPU is an RTX 3090\", \"/data is\nmounted at /mnt/foo here\"). Most machines should leave it untouched.\n\n## Harness-specific blocks\n\nIf you do add content, you can target a single harness with Mustache-style\nblocks — an opening \\`harness:claude\\` (or \\`harness:pi\\`) tag and a matching\nclosing tag, each wrapped in double curly braces. Anything outside a block\napplies to every harness. See \\`docs/CONTEXT-LAYERS.md\\` for the exact syntax\nand examples (not shown inline here — the renderer would strip live tags from\nthis very file).\n`;\n\n/** Seeded into a project's `project.md` when it is registered. */\nexport const PROJECT_STARTER = `# Project Context\n\nContext for this project specifically. \\`pan sync\\` renders it into the\nproject's CLAUDE.md (and equivalents) when CWD is under this project.\n\nThis file is committed to the repo; \\`workspace.md\\` is not.\n`;\n\n// ─── Layer introspection ──────────────────────────────────────────────────\n\n/** Describe the global layer's on-disk state. */\nexport function globalLayer(): ContextLayer {\n const file = globalContextFile();\n return { kind: 'global', file, dir: globalContextDir(), exists: existsSync(file) };\n}\n\n/** Describe a project layer's on-disk state. */\nexport function projectLayer(projectRoot: string): ContextLayer {\n const file = resolveProjectContextFile(projectRoot);\n return { kind: 'project', file, dir: dirname(file), exists: existsSync(file) };\n}\n\n/** Describe a workspace layer's on-disk state. */\nexport function workspaceLayer(workspacePath: string): ContextLayer {\n const file = resolveWorkspaceContextFile(workspacePath);\n return {\n kind: 'workspace',\n file,\n dir: dirname(file),\n exists: existsSync(file),\n };\n}\n\n/**\n * Ensure the global layer exists, seeding `global.md` with a starter when\n * absent. Idempotent — never overwrites existing content. Returns true when\n * a file was created.\n */\nexport function ensureGlobalLayer(): boolean {\n const file = globalContextFile();\n if (existsSync(file)) return false;\n mkdirSync(globalContextDir(), { recursive: true });\n writeFileSync(file, GLOBAL_STARTER, 'utf-8');\n return true;\n}\n\n/**\n * Ensure a project's layer dir exists with a stub `project.md`. Idempotent.\n * Returns true when a file was created. Called by `pan projects add`.\n */\nexport function ensureProjectLayer(projectRoot: string): boolean {\n const file = projectContextFile(projectRoot);\n if (existsSync(file)) return false;\n mkdirSync(projectContextDir(projectRoot), { recursive: true });\n writeFileSync(file, PROJECT_STARTER, 'utf-8');\n return true;\n}\n\n/** Read a layer's canonical markdown, or '' when the file does not exist. */\nexport function readLayerContent(file: string): string {\n return existsSync(file) ? readFileSync(file, 'utf-8') : '';\n}\n","/**\n * Bundled engineering rules (PAN-1201).\n *\n * Overdeck ships a small set of engineering rules under sync-sources/rules/.\n * Each rule carries a `scope:` frontmatter key:\n *\n * universal — distributed everywhere (relevant to anyone running agents)\n * dev — distributed only on a overdeck checkout (rules about\n * developing Overdeck itself)\n *\n * `pan sync` folds the applicable rules into the managed launch context.\n * Their `paths:` frontmatter (Claude Code path-scoping) is dropped on fold —\n * a folded rule is always-on.\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport type { Harness } from '@overdeck/contracts';\nimport type { ContextConfig } from '../config-yaml.js';\nimport { SYNC_SOURCES } from '../paths.js';\nimport { renderForHarness } from './harness.js';\n\n/** Where a bundled rule is distributed. */\nexport type RuleScope = 'universal' | 'dev';\n\n/** A parsed bundled rule. */\nexport interface BundledRule {\n /** File basename without the `.md` extension. */\n name: string;\n scope: RuleScope;\n /** Markdown body with frontmatter stripped. */\n body: string;\n}\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\n\n/**\n * Parse a rule file into `{ scope, body }`. Rules with no frontmatter, or\n * frontmatter without a recognised `scope:`, default to `universal`.\n */\nexport function parseRule(name: string, raw: string): BundledRule {\n const m = FRONTMATTER_RE.exec(raw);\n let scope: RuleScope = 'universal';\n let body = raw;\n if (m) {\n body = raw.slice(m[0].length);\n const scopeLine = m[1].split(/\\r?\\n/).find((l) => /^\\s*scope\\s*:/.test(l));\n const value = scopeLine?.split(':')[1]?.trim().replace(/['\"]/g, '');\n if (value === 'dev' || value === 'universal') scope = value;\n }\n return { name, scope, body: body.trim() };\n}\n\n/** Read every bundled rule from sync-sources/rules/, sorted by name. */\nexport function readBundledRules(): BundledRule[] {\n const dir = SYNC_SOURCES.rules;\n if (!existsSync(dir)) return [];\n return readdirSync(dir)\n .filter((f) => f.endsWith('.md'))\n .sort()\n .map((f) => parseRule(f.replace(/\\.md$/, ''), readFileSync(join(dir, f), 'utf-8')));\n}\n\n/**\n * Rule names switched off via `context.rules: { <name>: false }` in\n * config.yaml. Absent keys and `true` mean the rule stays on, so a bundled\n * rule is default-on and the map only ever lists opt-outs.\n */\nexport function disabledRuleNames(context: ContextConfig | undefined): Set<string> {\n return new Set(\n Object.entries(context?.rules ?? {})\n .filter(([, enabled]) => enabled === false)\n .map(([name]) => name),\n );\n}\n\n/**\n * Render the applicable bundled rules into one managed instruction section.\n *\n * `includeDev` admits `scope: dev` rules — set it from `isDevMode()` so they\n * only fold in on a overdeck checkout. `disabled` names rules switched off in\n * config (see {@link disabledRuleNames}); callers that render for real\n * distribution or preview must pass it so both surfaces agree. Each rule body\n * is rendered for the target harness so any `{{#harness:*}}` blocks resolve.\n * Returns '' when no rules apply.\n */\nexport function renderBundledRules(\n harness: Harness,\n includeDev: boolean,\n disabled: ReadonlySet<string> = new Set(),\n): string {\n const rules = readBundledRules().filter(\n (r) => (includeDev || r.scope === 'universal') && !disabled.has(r.name),\n );\n const sections = rules\n .map((r) => {\n const body = renderForHarness(r.body, harness).trim();\n return body ? `Source: ${join(SYNC_SOURCES.rules, `${r.name}.md`)} (scope: ${r.scope})\\n\\n${body}` : '';\n })\n .filter((s) => s.length > 0);\n if (sections.length === 0) return '';\n return `## Overdeck Engineering Rules\\n\\n${sections.join('\\n\\n')}`;\n}\n","/** Harness-specific rendering of canonical Overdeck context layers. */\n\nimport type { Harness } from '@overdeck/contracts';\nimport { loadConfigSync } from '../config-yaml.js';\nimport { renderForHarness } from './harness.js';\nimport { disabledRuleNames, renderBundledRules } from './rules.js';\nimport { globalContextFile, readLayerContent, resolveProjectContextFile } from './layers.js';\n\n/** Render machine context plus bundled rules for a managed harness launch. */\nexport function renderGlobalLayer(harness: Harness, includeDevRules: boolean): string {\n const source = globalContextFile();\n const body = renderForHarness(readLayerContent(source), harness).trim();\n const layer = body ? `## Machine context\\n\\nSource: ${source}\\n\\n${body}` : '';\n const rules = renderBundledRules(harness, includeDevRules, configDisabledRuleNames());\n const sections = [layer, rules].filter((section) => section.length > 0);\n return sections.length > 0\n ? `# Overdeck managed instructions\\n\\nThis is a generated composition. Each section identifies its own source; the machine-context file is only one input.\\n\\n${sections.join('\\n\\n---\\n\\n')}`\n : '';\n}\n\n/** Resolve config-disabled bundled rules; a broken config disables nothing. */\nexport function configDisabledRuleNames(): Set<string> {\n try {\n return disabledRuleNames(loadConfigSync().config.context);\n } catch {\n return new Set();\n }\n}\n\n/** Render one project's canonical source for a managed harness launch. */\nexport function renderProjectLayer(projectRoot: string, harness: Harness | readonly Harness[]): string {\n const source = resolveProjectContextFile(projectRoot);\n const raw = readLayerContent(source);\n if (raw.trim().length === 0) return '';\n const body = renderForHarness(raw, harness).trim();\n return body ? `## Project context\\n\\nSource: ${source}\\n\\n${body}` : '';\n}\n","/** Build one harness-correct, Overdeck-owned context artifact for a launch. */\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\n\nimport type { Harness } from '@overdeck/contracts';\n\nimport { getOverdeckHome, isDevMode } from '../paths.js';\nimport { findProjectByPathSync } from '../projects.js';\nimport { workspaceContextWithoutProjectLayer } from './assemble.js';\nimport { renderForHarness } from './harness.js';\nimport { resolveWorkspaceContextFile } from './layers.js';\nimport { renderGlobalLayer, renderProjectLayer } from './render.js';\n\nconst SECTION_SEPARATOR = '\\n\\n---\\n\\n';\n\n/**\n * Compose global rules, the current project's canonical layer, and the\n * workspace-only portion of a saved workspace bundle for the launch harness.\n *\n * Workspace bundles created before PAN-3779 may contain a project section\n * rendered for whichever harness created the worktree (historically Claude).\n * Strip that structural section and render the canonical project source for\n * the current harness instead. This prevents stale Claude-filtered context\n * from leaking into Codex, OMP, ACP, or Kimi launches and avoids delivering\n * the project layer twice.\n */\nexport function renderManagedLaunchContext(workspace: string, harness: Harness): string {\n const sections: string[] = [renderGlobalLayer(harness, isDevMode())];\n const project = findProjectByPathSync(workspace);\n if (project) sections.push(renderProjectLayer(project.path, harness));\n\n const workspaceFile = resolveWorkspaceContextFile(workspace);\n if (existsSync(workspaceFile)) {\n const saved = readFileSync(workspaceFile, 'utf8');\n const workspaceOnly = workspaceContextWithoutProjectLayer(saved);\n const body = renderForHarness(workspaceOnly, harness).trim();\n if (body) sections.push(`## Workspace context\\n\\nSource: ${workspaceFile}\\n\\n${body}`);\n }\n\n return sections\n .map((section) => section.trim())\n .filter((section) => section.length > 0)\n .join(SECTION_SEPARATOR)\n .trim();\n}\n\n/** Atomically materialize launch context at an explicitly Overdeck-owned path. */\nexport function materializeManagedLaunchContext(\n targetFile: string,\n workspace: string,\n harness: Harness,\n): string {\n const content = renderManagedLaunchContext(workspace, harness);\n mkdirSync(dirname(targetFile), { recursive: true });\n const tmp = `${targetFile}.${randomUUID()}.tmp`;\n writeFileSync(tmp, content.length > 0 ? `${content}\\n` : '', { mode: 0o600 });\n renameSync(tmp, targetFile);\n return targetFile;\n}\n\n/**\n * Stable private artifact used by ordinary non-ACP launchers. The workspace\n * hash keeps paths short and avoids exposing project names in receipts.\n */\nexport function materializeSharedManagedLaunchContext(\n workspace: string,\n harness: Harness,\n): string {\n const workspaceHash = createHash('sha256').update(resolve(workspace)).digest('hex').slice(0, 16);\n return materializeManagedLaunchContext(\n join(getOverdeckHome(), 'context', 'launch', `${harness}-${workspaceHash}.md`),\n workspace,\n harness,\n );\n}\n","import { join } from 'node:path';\n\nimport { materializeManagedLaunchContext } from '../context-layers/materialize.js';\n\nexport function materializeAcpContextFile(\n agentDir: string,\n workspace: string,\n harness: 'acp' | 'opencode' = 'acp',\n): string {\n const contextPath = join(agentDir, 'acp-context.md');\n return materializeManagedLaunchContext(contextPath, workspace, harness);\n}\n","/**\n * oh-my-pi (omp) RPC named-pipe lifecycle (PAN-1989).\n *\n * omp runs in `--mode rpc` and reads JSONL commands from stdin. We feed omp by\n * redirecting its stdin from a per-agent fifo at:\n *\n * $OVERDECK_HOME/agents/<agentId>/rpc.in\n *\n * The launcher script creates the fifo, then `exec omp --mode rpc ... < <fifo>`.\n * The runtime adapter (OhmypiRuntime.sendMessage) writes JSONL lines to the fifo.\n *\n * Why a fifo and not stdin pipe + tmux paste-buffer:\n * - tmux paste-buffer is unreliable for JSONL (Enter timing, terminal\n * echo, line buffering). The fifo path bypasses the TTY entirely.\n * - omp remains visible inside its tmux pane for crash isolation and\n * visual debugging — only its stdin is redirected.\n *\n * Ordering invariant:\n * - The launcher creates the fifo BEFORE spawning omp.\n * - omp's first stdout event is `session_start` which the extension turns\n * into ready.json under $OVERDECK_HOME/agents/<agentId>/.\n * - Adapter MUST wait for ready.json before opening the writer side of\n * the fifo.\n *\n * IMPORTANT: this module performs blocking I/O (mkfifo + file open) inside\n * async helpers. NEVER call it from a dashboard server route handler — only\n * from spawnAgent and killAgent paths that run on dedicated workers.\n */\n\nimport { existsSync, mkdirSync, openSync, writeSync, closeSync, unlinkSync, constants as fsConstants } from 'node:fs'\nimport { join } from 'node:path'\nimport { exec } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { Data, Effect } from 'effect'\nimport { getOverdeckHome } from '../paths.js'\n\nconst execAsync = promisify(exec)\n\nexport interface OhmypiFifoPaths {\n agentDir: string\n readyPath: string\n fifoPath: string\n}\n\nexport class OhmypiNotReady extends Error {\n readonly code = 'OHMYPI_NOT_READY' as const\n constructor(message: string) {\n super(message)\n this.name = 'OhmypiNotReady'\n }\n}\n\nexport function ohmypiFifoPaths(agentId: string, home?: string): OhmypiFifoPaths {\n const overdeckHome = home === undefined ? getOverdeckHome() : join(home, '.overdeck')\n const agentDir = join(overdeckHome, 'agents', agentId)\n return {\n agentDir,\n readyPath: join(agentDir, 'ready.json'),\n fifoPath: join(agentDir, 'rpc.in'),\n }\n}\n\nasync function createOhmypiFifoPromise(agentId: string, home?: string): Promise<string> {\n const paths = ohmypiFifoPaths(agentId, home)\n mkdirSync(paths.agentDir, { recursive: true, mode: 0o700 })\n if (existsSync(paths.fifoPath)) {\n unlinkSync(paths.fifoPath)\n }\n await execAsync(`mkfifo -m 600 ${shellQuote(paths.fifoPath)}`)\n return paths.fifoPath\n}\n\n/**\n * Unlink the fifo. Safe to call when the fifo does not exist.\n */\nexport function destroyOhmypiFifoSync(agentId: string, home?: string): void {\n const paths = ohmypiFifoPaths(agentId, home)\n try {\n unlinkSync(paths.fifoPath)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err\n }\n}\n\n/**\n * Write a single JSONL command to the agent's fifo.\n *\n * Throws OhmypiNotReady (without blocking) when ready.json is not yet present.\n * Throws OhmypiNotReady when the fifo exists but omp has no read fd open (ENXIO).\n */\nexport function writeOhmypiCommandSync(agentId: string, command: unknown, home?: string): void {\n const paths = ohmypiFifoPaths(agentId, home)\n if (!existsSync(paths.readyPath)) {\n throw new OhmypiNotReady(\n `omp agent ${agentId}: ready.json not present yet (waiting for session_start)`,\n )\n }\n if (!existsSync(paths.fifoPath)) {\n throw new OhmypiNotReady(`omp agent ${agentId}: rpc.in fifo missing`)\n }\n\n let fd: number\n try {\n fd = openSync(paths.fifoPath, fsConstants.O_WRONLY | fsConstants.O_NONBLOCK)\n } catch (err) {\n const errno = (err as NodeJS.ErrnoException).code\n if (errno === 'ENXIO') {\n throw new OhmypiNotReady(`omp agent ${agentId}: no reader on rpc.in fifo (omp exited?)`)\n }\n throw err\n }\n try {\n const line = `${JSON.stringify(command)}\\n`\n writeSync(fd, line)\n } finally {\n closeSync(fd)\n }\n}\n\n// Tiny shell-arg quoter. Acceptable inputs are absolute paths under HOME.\nfunction shellQuote(s: string): string {\n if (/^[A-Za-z0-9._/-]+$/.test(s)) return s\n return `'${s.replace(/'/g, `'\\\\''`)}'`\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/** Tagged error for ohmypi-fifo Effect variants. */\nexport class OhmypiFifoError extends Data.TaggedError('OhmypiFifoError')<{\n readonly agentId: string\n readonly stage: 'create' | 'write' | 'destroy'\n readonly message: string\n readonly cause?: unknown\n}> {}\n\n/** Effect variant of `createOhmypiFifo`. */\nexport const createOhmypiFifo = (\n agentId: string,\n home?: string,\n): Effect.Effect<string, OhmypiFifoError> =>\n Effect.tryPromise({\n try: () => createOhmypiFifoPromise(agentId, home),\n catch: (cause) =>\n new OhmypiFifoError({\n agentId,\n stage: 'create',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n })\n\n/** Effect variant of `writeOhmypiCommand`. */\nexport const writeOhmypiCommand = (\n agentId: string,\n command: unknown,\n home?: string,\n): Effect.Effect<void, OhmypiFifoError> =>\n Effect.try({\n try: () => writeOhmypiCommandSync(agentId, command, home),\n catch: (cause) =>\n new OhmypiFifoError({\n agentId,\n stage: 'write',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n })\n\n/** Effect variant of `destroyOhmypiFifo`. */\nexport const destroyOhmypiFifo = (\n agentId: string,\n home?: string,\n): Effect.Effect<void, OhmypiFifoError> =>\n Effect.try({\n try: () => destroyOhmypiFifoSync(agentId, home),\n catch: (cause) =>\n new OhmypiFifoError({\n agentId,\n stage: 'destroy',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n })\n","/**\n * Pi RPC named-pipe lifecycle (PAN-636).\n *\n * Pi runs in `--mode rpc` and reads JSONL commands from stdin. We feed Pi by\n * redirecting its stdin from a per-agent fifo at:\n *\n * $OVERDECK_HOME/agents/<agentId>/rpc.in\n *\n * The launcher script creates the fifo, then `exec pi --mode rpc ... < <fifo>`.\n * The runtime adapter (PiRuntime.sendMessage) writes JSONL lines to the fifo.\n *\n * Why a fifo and not stdin pipe + tmux paste-buffer:\n * - tmux paste-buffer is unreliable for JSONL (Enter timing, terminal\n * echo, line buffering). The fifo path bypasses the TTY entirely.\n * - Pi remains visible inside its tmux pane for crash isolation and\n * visual debugging — only its stdin is redirected.\n *\n * Ordering invariant (hazard H1):\n * - The launcher creates the fifo BEFORE spawning Pi.\n * - Pi's first stdout event is `session_start` which the extension turns\n * into ready.json under $OVERDECK_HOME/agents/<agentId>/.\n * - Adapter MUST wait for ready.json before opening the writer side of\n * the fifo. Opening for write before a reader exists would block\n * indefinitely (or, with O_NONBLOCK, fail fast with ENXIO). We use\n * readiness-by-marker as the explicit synchronization point.\n *\n * IMPORTANT: this module performs blocking I/O (mkfifo + file open) inside\n * async helpers. NEVER call it from a dashboard server route handler — only\n * from spawnAgent and killAgent paths that run on dedicated workers.\n */\n\nimport { existsSync, mkdirSync, openSync, writeSync, closeSync, unlinkSync, constants as fsConstants } from 'node:fs'\nimport { join } from 'node:path'\nimport { exec } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { Data, Effect } from 'effect'\nimport { getOverdeckHome } from '../paths.js'\n\nconst execAsync = promisify(exec)\n\nexport interface PiFifoPaths {\n agentDir: string\n readyPath: string\n fifoPath: string\n}\n\nexport class PiNotReady extends Error {\n readonly code = 'PI_NOT_READY' as const\n constructor(message: string) {\n super(message)\n this.name = 'PiNotReady'\n }\n}\n\nexport function piFifoPaths(agentId: string, home?: string): PiFifoPaths {\n const overdeckHome = home === undefined ? getOverdeckHome() : join(home, '.overdeck')\n const agentDir = join(overdeckHome, 'agents', agentId)\n return {\n agentDir,\n readyPath: join(agentDir, 'ready.json'),\n fifoPath: join(agentDir, 'rpc.in'),\n }\n}async function createPiFifoPromise(agentId: string, home?: string): Promise<string> {\n const paths = piFifoPaths(agentId, home)\n mkdirSync(paths.agentDir, { recursive: true, mode: 0o700 })\n if (existsSync(paths.fifoPath)) {\n unlinkSync(paths.fifoPath)\n }\n // Node has no native mkfifo. Shell out to /usr/bin/mkfifo. -m 600 sets the\n // mode atomically. We single-quote the path to defang any unexpected shell\n // metacharacters even though agent ids are alphanumeric + dash by contract.\n await execAsync(`mkfifo -m 600 ${shellQuote(paths.fifoPath)}`)\n return paths.fifoPath\n}\n\n/**\n * Unlink the fifo. Safe to call when the fifo does not exist.\n */\nexport function destroyPiFifoSync(agentId: string, home?: string): void {\n const paths = piFifoPaths(agentId, home)\n try {\n unlinkSync(paths.fifoPath)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err\n }\n}\n\n/**\n * Write a single JSONL command to the agent's fifo.\n *\n * Throws PiNotReady (without blocking) when ready.json is not yet present —\n * the adapter is expected to wait for the Pi extension to write ready.json\n * on session_start before issuing RPC writes (hazard H1).\n *\n * Throws PiNotReady (also without blocking) when the fifo exists but Pi has\n * no read fd open — opening O_WRONLY|O_NONBLOCK on a reader-less fifo fails\n * with ENXIO. This is the typed signal that Pi died and the adapter should\n * recycle the agent rather than retry.\n *\n * The command is JSON-stringified and a single trailing newline is added so\n * Pi's JSONL parser sees one record per line.\n */\nexport function writePiCommandSync(agentId: string, command: unknown, home?: string): void {\n const paths = piFifoPaths(agentId, home)\n if (!existsSync(paths.readyPath)) {\n throw new PiNotReady(\n `Pi agent ${agentId}: ready.json not present yet (waiting for session_start)`,\n )\n }\n if (!existsSync(paths.fifoPath)) {\n throw new PiNotReady(`Pi agent ${agentId}: rpc.in fifo missing`)\n }\n\n let fd: number\n try {\n fd = openSync(paths.fifoPath, fsConstants.O_WRONLY | fsConstants.O_NONBLOCK)\n } catch (err) {\n const errno = (err as NodeJS.ErrnoException).code\n if (errno === 'ENXIO') {\n throw new PiNotReady(`Pi agent ${agentId}: no reader on rpc.in fifo (Pi exited?)`)\n }\n throw err\n }\n try {\n const line = `${JSON.stringify(command)}\\n`\n writeSync(fd, line)\n } finally {\n closeSync(fd)\n }\n}\n\n// Tiny shell-arg quoter. Acceptable inputs are absolute paths under HOME.\nfunction shellQuote(s: string): string {\n if (/^[A-Za-z0-9._/-]+$/.test(s)) return s\n return `'${s.replace(/'/g, `'\\\\''`)}'`\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n//\n// Additive Effect-channel variants of the fifo helpers above. Sync/promise\n// variants are preserved so the existing PiRuntime adapter keeps working.\n\n/** Tagged error for pi-fifo Effect variants. */\nexport class PiFifoError extends Data.TaggedError('PiFifoError')<{\n readonly agentId: string\n readonly stage: 'create' | 'write' | 'destroy'\n readonly message: string\n readonly cause?: unknown\n}> {}\n\n/** Effect variant of `createPiFifo`. */\nexport const createPiFifo = (\n agentId: string,\n home?: string,\n): Effect.Effect<string, PiFifoError> =>\n Effect.tryPromise({\n try: () => createPiFifoPromise(agentId, home),\n catch: (cause) =>\n new PiFifoError({\n agentId,\n stage: 'create',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n })\n\n/**\n * Effect variant of `writePiCommand`. Lifts the sync FD ops via `Effect.try`.\n * The PiNotReady signal is preserved in the cause field so callers can branch.\n */\nexport const writePiCommand = (\n agentId: string,\n command: unknown,\n home?: string,\n): Effect.Effect<void, PiFifoError> =>\n Effect.try({\n try: () => writePiCommandSync(agentId, command, home),\n catch: (cause) =>\n new PiFifoError({\n agentId,\n stage: 'write',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n })\n\n/** Effect variant of `destroyPiFifo`. */\nexport const destroyPiFifo = (\n agentId: string,\n home?: string,\n): Effect.Effect<void, PiFifoError> =>\n Effect.try({\n try: () => destroyPiFifoSync(agentId, home),\n catch: (cause) =>\n new PiFifoError({\n agentId,\n stage: 'destroy',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n })\n","/** Resolve explicit context inputs without depending on agent lifecycle code. */\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { ensureSessionContextBriefingFile } from '../briefing-freshness.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { materializeSharedManagedLaunchContext } from './materialize.js';\n\nexport async function claudeSystemPromptFiles(workspace: string, harness: RuntimeName | undefined): Promise<string[]> {\n const effectiveHarness = harness ?? 'claude-code';\n const behavior = getHarnessBehavior(effectiveHarness);\n if (behavior.contextLayerKind === 'acp' || behavior.contextLayerKind === 'opencode' || behavior.contextLayerKind === 'muse') {\n return [];\n }\n\n const files: string[] = [];\n if (behavior.contextLayerKind === 'codex') {\n // A private CODEX_HOME moves native ~/.codex/AGENTS.md out of discovery.\n // Preserve that user-authored layer through the explicit developer channel.\n const nativeOverride = join(homedir(), '.codex', 'AGENTS.override.md');\n const nativeGlobal = existsSync(nativeOverride) ? nativeOverride : join(homedir(), '.codex', 'AGENTS.md');\n if (existsSync(nativeGlobal)) files.push(nativeGlobal);\n }\n\n // Every managed harness receives one current-harness render of Overdeck's\n // global, project, and workspace layers. Native Kimi consumes this file via\n // its once-per-session first-user-message envelope rather than a launch flag.\n files.push(materializeSharedManagedLaunchContext(workspace, effectiveHarness));\n files.push(await ensureSessionContextBriefingFile());\n\n return files;\n}\n","import { existsSync, mkdirSync, readFileSync, statfsSync, statSync, writeFileSync } from 'fs';\nimport { mkdir, rename as renameAsync, stat as statAsync, writeFile } from 'fs/promises';\nimport { exec } from 'child_process';\nimport { request as httpRequest } from 'node:http';\nimport { randomUUID } from 'crypto';\nimport { homedir } from 'os';\nimport { basename, dirname, join, resolve } from 'path';\nimport { promisify } from 'util';\nimport { parse as parseYaml } from 'yaml';\nimport { Effect } from 'effect';\nimport type { MemoryIdentity } from '@overdeck/contracts';\nimport { getClaudePermissionFlagsStringSync } from '../claude-permissions.js';\nimport { loadConfigSync as loadYamlConfig } from '../config-yaml.js';\nimport type { RoleEffort } from '../config-yaml.js';\nimport { getClaudeAuthStatus } from '../claude-auth.js';\nimport { materializeAcpContextFile } from '../acp/context.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport { initCodexHome } from '../runtimes/codex.js';\nimport { createOhmypiFifo, ohmypiFifoPaths, OhmypiNotReady, writeOhmypiCommandSync } from '../runtimes/ohmypi-fifo.js';\nimport { createPiFifo, piFifoPaths, PiNotReady, writePiCommandSync } from '../runtimes/pi-fifo.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { requireModelOverrideSync, shellQuoteModelIdSync } from '../model-validation.js';\nimport { getOpenAIAuthStatus } from '../openai-auth.js';\nimport { getOverdeckHome, packageRoot, resolveOhmypiExtensionPath, resolvePiExtensionPath } from '../paths.js';\nimport { getProviderForModelSync, resolveKimiCodeModelAlias } from '../providers.js';\nimport type { AuthMode } from '../subscription-types.js';\nimport { capturePane, sessionExists } from '../tmux.js';\nimport { getAgentDir, getAgentStateSync, type Role } from './agent-state.js';\nimport { waitForReadySignal } from './identity.js';\nimport { CLI_PROXY_MODEL_ALIASES } from './provider-env.js';\nimport { getClaudeCodeLaunchModelSync } from '../kimi-claude-routing.js';\n\nconst execAsync = promisify(exec);\nconst missingRoleDefinitionWarnings = new Set<string>();\n\nexport interface RoleMcpServerDef {\n type?: string;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n}\n\n/** Parse and flatten a role definition's mcpServers frontmatter. */\nexport function parseRoleMcpServersSync(definitionPath: string): Record<string, RoleMcpServerDef> {\n const abs = resolve(definitionPath);\n if (!existsSync(abs)) return {};\n\n const raw = readFileSync(abs, 'utf8');\n const fm = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/);\n if (!fm) return {};\n\n try {\n const frontmatter = parseYaml(fm[1]);\n if (!frontmatter || typeof frontmatter !== 'object') return {};\n const entries = (frontmatter as Record<string, unknown>).mcpServers;\n if (!Array.isArray(entries)) return {};\n\n const servers: Record<string, RoleMcpServerDef> = {};\n for (const entry of entries) {\n if (!entry || typeof entry !== 'object') continue;\n for (const [name, definition] of Object.entries(entry as Record<string, unknown>)) {\n if (definition && typeof definition === 'object') {\n servers[name] = definition as RoleMcpServerDef;\n }\n }\n }\n return servers;\n } catch {\n return {};\n }\n}\n\n/**\n * Write an agent launcher script atomically. Every agent shares a fixed\n * `launcher.sh` path inside its agent dir, and spawn/resume/restart paths can\n * overlap (e.g. a Deacon recovery racing a manual restart). Writing in place\n * lets one path read a half-written script; write to a unique temp file then\n * rename (atomic on the same filesystem).\n */\nexport async function writeLauncherScriptAtomic(launcherScript: string, content: string): Promise<void> {\n const tmp = `${launcherScript}.${randomUUID()}.tmp`;\n await writeFile(tmp, content, { mode: 0o755 });\n await renameAsync(tmp, launcherScript);\n}\n\nexport { claudeSystemPromptFiles } from '../context-layers/launch-sources.js';\n\nfunction isNodeNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';\n}\n\n/**\n * BFS-walk a process subtree rooted at `rootPid` looking for the active agent\n * runtime. Returns true if any process in the tree matches the expected harness,\n * false if the tree exists but no match, false on any error.\n *\n * Used by sendAgentMessage zombie detection. pane_pid is the tmux pane's root\n * process, which is bash for work-agent launchers (`bash launcher.sh`) but can\n * be the runtime directly for specialists (`exec claude ...` / `exec pi ...`).\n */\nexport async function hasAgentRuntimeInSubtree(rootPid: string, harness: RuntimeName = 'claude-code'): Promise<boolean> {\n const expectedProcessNames = new Set(getHarnessBehavior(harness).processNames);\n const queue: string[] = [rootPid];\n const seen = new Set<string>();\n while (queue.length > 0) {\n const pid = queue.shift()!;\n if (seen.has(pid) || !/^\\d+$/.test(pid)) continue;\n seen.add(pid);\n\n try {\n const { stdout: comm } = await execAsync(`ps -p ${pid} -o comm=`);\n const name = comm.trim();\n if (expectedProcessNames.has(name) || (harness === 'muse' && name.startsWith('muse-bin-'))) return true;\n } catch {\n continue;\n }\n\n try {\n const { stdout: kids } = await execAsync(`pgrep -P ${pid}`);\n for (const kid of kids.trim().split('\\n').filter(Boolean)) {\n queue.push(kid);\n }\n } catch {\n // pgrep exits non-zero when there are no children — not an error.\n }\n }\n return false;\n}\n\nexport async function getPiLauncherFields(agentId: string, model: string): Promise<{\n harness: 'ohmypi';\n piExtensionPath: string;\n piFifoPath: string;\n piSessionDir: string;\n model: string;\n}> {\n const paths = piFifoPaths(agentId);\n await mkdir(paths.agentDir, { recursive: true, mode: 0o700 });\n const piExtensionPath = resolvePiExtensionPath();\n if (!piExtensionPath) {\n throw new Error(\n `Pi extension not built. Run: npm run build\\n(looked for dist/extensions/pi.js and packages/pi-extension/dist/index.js under ${packageRoot})`\n );\n }\n // The launcher rebuilds the command, so it must receive the selected model explicitly.\n return {\n harness: 'ohmypi',\n piExtensionPath,\n piFifoPath: await Effect.runPromise(createPiFifo(agentId)),\n piSessionDir: paths.agentDir,\n model,\n };\n}\n\nexport async function getOhmypiLauncherFields(agentId: string, model: string, effort?: string): Promise<{\n harness: 'ohmypi';\n piEffort: string;\n piExtensionPath: string;\n piFifoPath: string;\n piSessionDir: string;\n model: string;\n}> {\n const paths = ohmypiFifoPaths(agentId);\n await mkdir(paths.agentDir, { recursive: true, mode: 0o700 });\n const ohmypiExtensionPath = resolveOhmypiExtensionPath();\n if (!ohmypiExtensionPath) {\n throw new Error(\n `ohmypi extension not built. Run: npm run build\\n(looked for dist/extensions/ohmypi.js and packages/ohmypi-extension/dist/index.js under ${packageRoot})`\n );\n }\n return {\n harness: 'ohmypi',\n piEffort: effort ?? 'high',\n piExtensionPath: ohmypiExtensionPath,\n piFifoPath: await Effect.runPromise(createOhmypiFifo(agentId)),\n piSessionDir: paths.agentDir,\n model,\n };\n}\n\nexport function getAcpLauncherFields(\n agentId: string,\n model: string,\n workspace: string,\n binaryPath: string,\n _role?: Role,\n effort?: string,\n): {\n harness: 'acp' | 'opencode';\n acpAgentId: string;\n acpProvider: string;\n acpWorkspace: string;\n acpBinaryPath: string;\n acpContextFile: string;\n acpEffort?: string;\n model: string;\n unsetProviderEnv: true;\n} {\n return {\n harness: model.startsWith('opencode/') || model.startsWith('opencode-go/') ? 'opencode' : 'acp',\n acpAgentId: agentId,\n acpProvider: getProviderForModelSync(model).name,\n acpWorkspace: workspace,\n acpBinaryPath: binaryPath,\n acpContextFile: materializeAcpContextFile(getAgentDir(agentId), workspace, model.startsWith('opencode/') || model.startsWith('opencode-go/') ? 'opencode' : 'acp'),\n ...(effort ? { acpEffort: effort } : {}),\n model,\n unsetProviderEnv: true,\n };\n}\n\nexport function getKimiCodeLauncherFields(model: string, effort?: string): {\n harness: 'kimi-code';\n kimiCodeModel: string;\n kimiCodeYolo: true;\n kimiCodeEffort?: string;\n model: string;\n unsetProviderEnv: true;\n kimiContextDelivery: 'initial-message';\n} {\n const kimiCodeModel = resolveKimiCodeModelAlias(model);\n return {\n harness: 'kimi-code',\n kimiCodeModel,\n kimiCodeYolo: true,\n ...(effort ? { kimiCodeEffort: effort } : {}),\n model,\n unsetProviderEnv: true,\n kimiContextDelivery: 'initial-message',\n };\n}\n\nexport function getCodexLauncherFields(agentId: string, model: string, workspacePath?: string, role?: Role, effort?: string): {\n harness: 'codex';\n codexMode: 'app-server' | 'work-tui';\n codexEffort: string;\n codexHome: string;\n codexSessionDir: string;\n model: string;\n} {\n const codexHome = join(homedir(), '.overdeck', 'agents', agentId, 'codex-home-v2');\n const codexConfig = loadYamlConfig().config.codex;\n // Match conversation permissions and pre-trust the workspace to avoid onboarding prompts.\n const codexPermMode = codexConfig?.permissionMode ?? 'workspace';\n const approvalPolicy = codexPermMode === 'full-access' ? 'never' : 'on-request';\n const sandboxMode =\n codexPermMode === 'full-access' ? 'danger-full-access'\n : codexPermMode === 'read-only' ? 'read-only'\n : 'workspace-write';\n const approvalsReviewer = codexPermMode === 'auto-review' ? 'auto_review' : undefined;\n initCodexHome(codexHome, {\n trustedDir: workspacePath,\n model,\n effort,\n approvalPolicy,\n sandboxMode,\n approvalsReviewer,\n mcpServers: role ? parseRoleMcpServersSync(roleAgentDefinitionPath(role)) : undefined,\n });\n return {\n harness: 'codex',\n codexMode: codexConfig?.transport === 'tui' ? 'work-tui' : 'app-server',\n codexEffort: effort ?? 'high',\n codexHome,\n codexSessionDir: join(codexHome, 'sessions'),\n model,\n };\n}\n\n/**\n * Wait for the Pi work-agent ready marker (`ready.json`) to appear.\n * Pi does not produce the Claude SessionStart hook signal, so resume/restart\n * paths must use this instead of `waitForReadySignal()`.\n */\nasync function waitForPiAgentReady(agentId: string, timeoutSec = 30): Promise<boolean> {\n const { readyPath } = piFifoPaths(agentId);\n const deadline = Date.now() + timeoutSec * 1000;\n while (Date.now() < deadline) {\n if (existsSync(readyPath)) return true;\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n\nexport const OHMYPI_AGENT_READY_TIMEOUT_SECONDS = 120;\n\nasync function waitForOhmypiAgentReady(agentId: string, timeoutSec = OHMYPI_AGENT_READY_TIMEOUT_SECONDS): Promise<boolean> {\n const { readyPath } = ohmypiFifoPaths(agentId);\n const deadline = Date.now() + timeoutSec * 1000;\n while (Date.now() < deadline) {\n if (existsSync(readyPath)) return true;\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n\n/**\n * PAN-2100: build a diagnostic suffix for an ohmypi readiness failure.\n *\n * An ENOSPC (disk-full) omp crash surfaces as an opaque \"did not become ready\"\n * timeout — omp cannot write `ready.json` when the disk is full. That exact\n * condition crashed flywheel-orchestrator (RUN-20) and the only record of it sat\n * unread in output.log for hours, sending the flywheel down a wrong \"kimi→ohmypi\n * misroute\" diagnosis. Surface the real signals at throw time: free disk on the\n * Overdeck-home filesystem and the tail of the agent's output.log. Best-effort:\n * every probe is independently guarded so diagnostics never mask the real error.\n */\nexport function describeOhmypiSpawnFailure(agentId: string): string {\n const parts: string[] = [];\n try {\n const { bavail, bsize } = statfsSync(getOverdeckHome());\n const freeMb = Math.round((bavail * bsize) / (1024 * 1024));\n parts.push(`freeDisk=${freeMb}MiB`);\n if (freeMb < 512) parts.push('(DISK NEARLY FULL — likely ENOSPC crash)');\n } catch { /* statfs best-effort */ }\n try {\n const logPath = join(getAgentDir(agentId), 'output.log');\n if (existsSync(logPath)) {\n const tail = readFileSync(logPath, 'utf8').slice(-1500).trim().split('\\n').slice(-8).join('\\n');\n if (tail) parts.push(`output.log tail:\\n${tail}`);\n }\n } catch { /* best-effort */ }\n return parts.length ? ` [${parts.join(' ')}]` : '';\n}\n\n/**\n * PAN-1837: readinessKind 'kimi-session-signal' has no consumer of its own —\n * without this branch, waitForPromptReady's default case waits for Claude\n * Code's ready-signal file, which the native kimi binary never writes, so\n * every kimi-code spawn times out after 30s (confirmed live: wi14 e2e run).\n * Ready is defined the same way as the codex TUI branch above: the pane must\n * show both the input box (a `>` inside its box-drawn border) and the bottom\n * status line (verified live against installed kimi 0.29.2 — the status line\n * always renders `context: N% (…)` regardless of --yolo/model).\n */\nasync function waitForKimiCodeTuiReady(agentId: string, timeoutSec = 30): Promise<boolean> {\n const deadline = Date.now() + timeoutSec * 1000;\n while (Date.now() < deadline) {\n try {\n if (!(await Effect.runPromise(sessionExists(agentId)))) return false;\n const pane = await Effect.runPromise(capturePane(agentId, 80));\n const hasInputPrompt = /[│|]\\s*>\\s*[│|]?/.test(pane);\n const hasStatusLine = /context:\\s*\\d+%/.test(pane);\n if (hasInputPrompt && hasStatusLine) {\n return true;\n }\n } catch {\n // The pane may not exist yet immediately after tmux session creation.\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n\nasync function waitForCodexTuiReady(agentId: string, timeoutSec = 30): Promise<boolean> {\n const deadline = Date.now() + timeoutSec * 1000;\n while (Date.now() < deadline) {\n try {\n if (!(await Effect.runPromise(sessionExists(agentId)))) return false;\n const pane = await Effect.runPromise(capturePane(agentId, 80));\n // The codex TUI is ready when its input prompt (a line starting with the\n // `›` glyph) AND its status line (`<model> ... · <cwd>`) are both on\n // screen. PAN-1803: the previous check keyed off the first-run\n // trust-wizard markers (\"press enter to continue\") — but pre-trusting the\n // workspace (correctly) skips that wizard, so those markers never appear\n // and the kickoff never fired. Detect the actual ready prompt instead.\n const hasInputPrompt = /^\\s*[›>]\\s/m.test(pane);\n const hasStatusLine = /·\\s+[~/]/.test(pane);\n if (hasInputPrompt && hasStatusLine) {\n return true;\n }\n // Fallback: if pre-trust ever fails and the wizard does appear, treat its\n // markers as ready (the kickoff paste will dismiss + drive it).\n if (/press enter to continue/i.test(pane) || /ctrl[+-][cj]/i.test(pane)) {\n return true;\n }\n } catch {\n // The pane may not exist yet immediately after tmux session creation.\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n\nexport interface CodexAppServerStatus {\n state?: string;\n}\n\ninterface CodexAppServerReadyDeps {\n sessionExists?: (agentId: string) => Promise<boolean>;\n readStatus?: (agentId: string) => Promise<CodexAppServerStatus>;\n sleep?: (ms: number) => Promise<void>;\n now?: () => number;\n}\n\nfunction readAppServerToken(agentId: string): string | null {\n const tokenPath = join(getAgentDir(agentId), 'appserver-token');\n if (!existsSync(tokenPath)) return null;\n try {\n return readFileSync(tokenPath, 'utf-8').trim() || null;\n } catch {\n return null;\n }\n}\n\nexport async function getCodexAppServerStatus(agentId: string): Promise<CodexAppServerStatus> {\n const socketPath = join(getOverdeckHome(), 'sockets', `appserver-${agentId}.sock`);\n const token = readAppServerToken(agentId);\n if (!existsSync(socketPath)) throw new Error(`app-server socket missing for ${agentId}`);\n if (!token) throw new Error(`app-server token missing for ${agentId}`);\n const payload = JSON.stringify({ op: 'status' });\n\n return new Promise((resolveStatus, reject) => {\n let settled = false;\n const timeout = setTimeout(() => {\n if (settled) return;\n settled = true;\n req.destroy(new Error(`app-server status op timed out after 2000ms`));\n reject(new Error(`app-server status op timed out after 2000ms`));\n }, 2_000);\n const finishErr = (error: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n reject(error);\n };\n const finishOk = (status: CodexAppServerStatus) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolveStatus(status);\n };\n const req = httpRequest(\n {\n socketPath,\n path: '/',\n method: 'POST',\n agent: false,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(payload),\n 'X-Overdeck-Bridge-Token': token,\n },\n },\n (res) => {\n let body = '';\n res.setEncoding('utf-8');\n res.on('data', (chunk) => { body += chunk; });\n res.on('end', () => {\n const status = res.statusCode ?? 0;\n if (status < 200 || status >= 300) {\n finishErr(new Error(`app-server status op returned HTTP ${status}`));\n return;\n }\n try {\n finishOk(JSON.parse(body) as CodexAppServerStatus);\n } catch (error) {\n finishErr(new Error(`app-server status op returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`));\n }\n });\n },\n );\n req.on('error', finishErr);\n req.write(payload);\n req.end();\n });\n}\n\nexport async function waitForCodexAppServerReady(\n agentId: string,\n timeoutSec = 30,\n deps: CodexAppServerReadyDeps = {},\n): Promise<void> {\n const now = deps.now ?? (() => Date.now());\n const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((resolveSleep) => setTimeout(resolveSleep, ms)));\n const sessionExistsForAgent = deps.sessionExists ?? (async (id: string) => Effect.runPromise(sessionExists(id)));\n const readStatus = deps.readStatus ?? getCodexAppServerStatus;\n const deadline = now() + timeoutSec * 1000;\n let lastState = 'unknown';\n let lastError = '';\n\n while (now() < deadline) {\n if (!(await sessionExistsForAgent(agentId))) {\n throw new Error(`Codex app-server session ${agentId} exited before readiness.`);\n }\n try {\n const status = await readStatus(agentId);\n lastState = status.state ?? 'unknown';\n if (lastState === 'ready') return;\n } catch (error) {\n lastError = error instanceof Error ? error.message : String(error);\n }\n await sleep(500);\n }\n\n const detail = lastError ? ` Last status error: ${lastError}` : ` Last state: ${lastState}.`;\n throw new Error(`Timed out waiting for Codex app-server readiness for ${agentId}.${detail}`);\n}\n\nexport interface AcpHostReadyDeps {\n now?: () => number;\n sleep?: (ms: number) => Promise<void>;\n sessionExists?: (agentId: string) => Promise<boolean>;\n readText?: (path: string) => string;\n pathExists?: (path: string) => boolean;\n}\n\nexport async function waitForAcpHostReady(\n agentId: string,\n timeoutSec = 30,\n deps: AcpHostReadyDeps = {},\n): Promise<void> {\n const now = deps.now ?? (() => Date.now());\n const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((resolveSleep) => setTimeout(resolveSleep, ms)));\n const sessionExistsForAgent = deps.sessionExists ?? (async (id: string) => Effect.runPromise(sessionExists(id)));\n const readText = deps.readText ?? ((path: string) => readFileSync(path, 'utf8'));\n const pathExists = deps.pathExists ?? existsSync;\n const agentDir = getAgentDir(agentId);\n const sessionIdPath = join(agentDir, 'acp-session-id');\n const errorPath = join(agentDir, 'acp-launch-error');\n const tokenPath = join(agentDir, 'acp-token');\n const socketPath = join(getOverdeckHome(), 'sockets', `acp-${agentId}.sock`);\n const deadline = now() + timeoutSec * 1000;\n\n while (now() < deadline) {\n if (pathExists(errorPath)) {\n const launchError = readText(errorPath).trim();\n if (launchError) throw new Error(`ACP host ${agentId} failed to start: ${launchError}`);\n }\n if (!(await sessionExistsForAgent(agentId))) {\n throw new Error(`ACP host session ${agentId} exited before readiness.`);\n }\n\n try {\n const sessionId = readText(sessionIdPath).trim();\n const token = readText(tokenPath).trim();\n if (sessionId && token && pathExists(socketPath)) return;\n } catch (error) {\n if (!isNodeNotFound(error)) throw error;\n }\n\n await sleep(500);\n }\n\n throw new Error(`Timed out waiting for ACP host readiness for ${agentId}.`);\n}\n\nexport async function waitForPromptReady(agentId: string, harness: RuntimeName | undefined, timeoutSec = 30): Promise<boolean> {\n const readinessKind =\n harness === 'codex' && loadYamlConfig().config.codex?.transport !== 'tui'\n ? 'codex-app-server-ready'\n : getHarnessBehavior(harness).readinessKind;\n if (readinessKind === 'codex-app-server-ready') {\n await waitForCodexAppServerReady(agentId, timeoutSec);\n return true;\n }\n if (readinessKind === 'acp-host-ready') {\n await waitForAcpHostReady(agentId, timeoutSec);\n return true;\n }\n if (readinessKind === 'codex-tui-prompt') return waitForCodexTuiReady(agentId, timeoutSec);\n if (readinessKind === 'muse-tui-prompt') return waitForMuseTuiReady(agentId, timeoutSec);\n if (readinessKind === 'kimi-session-signal') return waitForKimiCodeTuiReady(agentId, timeoutSec);\n return waitForReadySignal(agentId, timeoutSec);\n}\n\nexport function inferMemoryProjectId(workspacePath: string): string {\n const workspaceName = basename(workspacePath);\n if (workspaceName.startsWith('feature-')) return basename(dirname(dirname(workspacePath)));\n return workspaceName;\n}\n\n/**\n * Inject prompt-time memory context into a Pi prompt (PAN-1546).\n * Mirrors Claude Code's UserPromptSubmit hook behaviour: every follow-up\n * prompt gets relevant memory surfaced via RAG.\n */\nasync function injectPiPromptTimeMemory(agentId: string, prompt: string): Promise<string> {\n if (!prompt.trim()) return prompt;\n\n const agentState = getAgentStateSync(agentId);\n if (!agentState || !agentState.workspace || !agentState.issueId) {\n return prompt;\n }\n\n try {\n const identity: MemoryIdentity = {\n projectId: inferMemoryProjectId(agentState.workspace),\n workspaceId: basename(agentState.workspace),\n issueId: agentState.issueId,\n runId: agentId,\n sessionId: agentId,\n agentRole: agentState.role ?? 'work',\n agentHarness: agentState.harness ?? 'claude-code',\n };\n const { injectPromptTimeMemory } = await import('../memory/injection.js');\n const result = await injectPromptTimeMemory({ prompt, identity, surface: 'user-prompt' });\n if (result.context) {\n return `${result.context}\\n\\n---\\n\\n${prompt}`;\n }\n } catch (error) {\n console.warn(`[agents] Prompt-time memory injection failed for ${agentId}:`, error instanceof Error ? error.message : String(error));\n }\n return prompt;\n}\n\n/**\n * Prompt-time memory injection for a Pi CONVERSATION follow-up (PAN-1546).\n *\n * The rpc/work-agent path (injectPiPromptTimeMemory) reads agent-state.json,\n * which conversations do not have — they deliver follow-ups via tmux\n * paste-buffer, not the FIFO. This builds the memory identity from the\n * conversation's cwd + issueId instead. Only issue-linked conversations have\n * memory to surface; everything else (and any failure) passes through\n * unchanged so a memory hiccup can never block a message.\n */\nexport async function injectPiConversationMemory(\n opts: { cwd: string; issueId?: string | null; conversationName: string },\n prompt: string,\n): Promise<string> {\n if (!prompt.trim() || !opts.issueId || !opts.cwd) return prompt;\n try {\n const identity: MemoryIdentity = {\n projectId: inferMemoryProjectId(opts.cwd),\n workspaceId: basename(opts.cwd),\n issueId: opts.issueId,\n runId: opts.conversationName,\n sessionId: opts.conversationName,\n agentRole: 'work',\n agentHarness: 'ohmypi',\n };\n const { injectPromptTimeMemory } = await import('../memory/injection.js');\n const result = await injectPromptTimeMemory({ prompt, identity, surface: 'user-prompt' });\n if (result.context) {\n return `${result.context}\\n\\n---\\n\\n${prompt}`;\n }\n } catch (error) {\n console.warn(`[agents] Conversation memory injection failed for ${opts.conversationName}:`, error instanceof Error ? error.message : String(error));\n }\n return prompt;\n}\n\n/**\n * Deliver a prompt to a Pi work agent through the FIFO JSONL command protocol.\n * Pi never reads tmux input — pasting prompts there is a no-op as far as the\n * model is concerned. Throws if Pi never reached readiness within the timeout.\n */\nexport async function writePiAgentPrompt(agentId: string, prompt: string, timeoutSec = 30): Promise<void> {\n const augmentedPrompt = await injectPiPromptTimeMemory(agentId, prompt);\n const ready = await waitForPiAgentReady(agentId, timeoutSec);\n if (!ready) {\n throw new Error(`Pi agent ${agentId} did not become ready within ${timeoutSec}s`);\n }\n try {\n // steer: Pi delivers immediately when idle and queues mid-turn; a bare\n // prompt is rejected with AgentBusyError while a run is active.\n writePiCommandSync(agentId, { id: randomUUID(), type: 'prompt', message: augmentedPrompt, streamingBehavior: 'steer' });\n } catch (err) {\n if (err instanceof PiNotReady) {\n throw new Error(`Pi agent ${agentId} reader gone before prompt could be delivered: ${err.message}`);\n }\n throw err;\n }\n}\n\nexport async function writeOhmypiAgentPrompt(agentId: string, prompt: string, timeoutSec = OHMYPI_AGENT_READY_TIMEOUT_SECONDS): Promise<void> {\n const augmentedPrompt = await injectPiPromptTimeMemory(agentId, prompt);\n const ready = await waitForOhmypiAgentReady(agentId, timeoutSec);\n if (!ready) {\n throw new Error(`ohmypi agent ${agentId} did not become ready within ${timeoutSec}s${describeOhmypiSpawnFailure(agentId)}`);\n }\n try {\n // steer: omp delivers immediately when idle and queues mid-turn; a bare\n // prompt is rejected with AgentBusyError while a run is active.\n writeOhmypiCommandSync(agentId, { id: randomUUID(), type: 'prompt', message: augmentedPrompt, streamingBehavior: 'steer' });\n } catch (err) {\n if (err instanceof OhmypiNotReady) {\n throw new Error(`ohmypi agent ${agentId} reader gone before prompt could be delivered: ${err.message}`);\n }\n throw err;\n }\n}\n\nexport async function getProviderAuthMode(model: string): Promise<AuthMode | undefined> {\n const provider = getProviderForModelSync(model);\n if (provider.name === 'anthropic') {\n const authStatus = await Effect.runPromise(getClaudeAuthStatus());\n if (authStatus.hasAnthropicApiKey) return 'api-key';\n return authStatus.loggedIn ? 'subscription' : undefined;\n }\n\n if (provider.name === 'openai') {\n const { config } = loadYamlConfig();\n const authStatus = await Effect.runPromise(getOpenAIAuthStatus());\n return authStatus.loggedIn\n ? 'subscription'\n : (config.providerAuth?.openai ?? 'api-key');\n }\n\n if (provider.name === 'google') {\n const { config } = loadYamlConfig();\n return config.providerAuth?.google;\n }\n\n return undefined;\n}\n\n/**\n * Build the base command that the launcher will exec for an agent.\n *\n * The `harness` parameter (PAN-636) selects between Claude Code (default)\n * and ohmypi/Pi. When the harness uses the ohmypi RPC command, the function\n * returns `omp --mode rpc --model <model>`; the launcher generator then layers\n * --session-dir, --extension, --no-context-files, and the stdin-from-fifo\n * redirect on top via generateLauncherScript. The `agentName` (PAN-982:\n * --name) and `agentDefinition` (PAN-982: --agent) parameters only apply to the\n * Claude Code path — ohmypi has no agent-definition system.\n */\nexport async function getAgentRuntimeBaseCommand(\n model: string,\n agentName?: string,\n agentDefinition?: string,\n harness: RuntimeName = 'claude-code',\n effort?: RoleEffort,\n): Promise<string> {\n const validatedModel = requireModelOverrideSync(model);\n const quotedModel = shellQuoteModelIdSync(harness === 'claude-code' ? getClaudeCodeLaunchModelSync(validatedModel) : validatedModel);\n const behavior = getHarnessBehavior(harness);\n if (behavior.launchCommandKind === 'ohmypi-rpc') {\n return `omp --mode rpc --model ${quotedModel}`;\n }\n if (behavior.launchCommandKind === 'codex-work-tui') {\n // buildCodexCommand in launcher-generator builds the full Codex command;\n // return a stub base command so the launcher generator can short-circuit.\n return `codex`;\n }\n if (behavior.launchCommandKind === 'acp-host') {\n return 'acp-host';\n }\n if (behavior.launchCommandKind === 'muse-tui') return 'muse';\n if (behavior.launchCommandKind === 'kimi-code-tui') {\n // buildKimiCodeCommand in launcher-generator builds the full `kimi -m ... --yolo`\n // command; return a stub base command so the launcher generator can short-circuit.\n return 'kimi-code';\n }\n\n // Integration tests can inject a harmless harness command so a leaked or\n // intentionally-real tmux session never runs the production `claude` binary.\n if (process.env.OVERDECK_TEST_HARNESS_COMMAND) {\n return process.env.OVERDECK_TEST_HARNESS_COMMAND;\n }\n\n const provider = getProviderForModelSync(validatedModel);\n // PAN-982: --name <agentId> creates a human-readable Claude session name discoverable via\n // `claude --resume`.\n const nameFlag = agentName ? ` --name ${agentName}` : '';\n\n // Classify agentDefinition. A registered agent NAME (e.g. pan-review-agent,\n // resolved from ~/.claude/agents/*.md) still launches via `--agent <name>` —\n // Claude Code 2.1.195 resolves names natively, and its frontmatter owns\n // permissionMode/tools/effort. A role definition FILE (roles/<role>.md) can no\n // longer be passed to --agent (PAN-2087: 2.1.195 dropped --agent file support),\n // so its frontmatter-stripped body is injected as an appended system prompt.\n const defIsRoleFile = !!agentDefinition\n && existsSync(resolve(agentDefinition))\n && statSync(resolve(agentDefinition)).isFile();\n\n if (agentDefinition && !defIsRoleFile) {\n // Registered agent NAME — unchanged. --model still wins over any frontmatter\n // default model; permissionMode/effort come from the named agent's frontmatter.\n const agentFlag = ` --agent ${agentDefinition}`;\n const effortFlag = effort ? ` --effort ${effort}` : '';\n if (provider.name === 'openai' && (await getProviderAuthMode(validatedModel)) === 'subscription') {\n const resolvedModel = CLI_PROXY_MODEL_ALIASES[validatedModel] ?? validatedModel;\n return `claude${agentFlag} --model ${shellQuoteModelIdSync(resolvedModel)}${effortFlag}${nameFlag}`;\n }\n return `claude${agentFlag} --model ${quotedModel}${effortFlag}${nameFlag}`;\n }\n\n // PAN-2087: role FILE (inject body + effort) or no definition. permissionMode\n // comes from the global permission flags; the role's hooks fire globally via\n // ~/.claude/settings.json. roleInject folds in effort when a role file is\n // present; otherwise --effort is passed directly.\n const permissionFlags = getClaudePermissionFlagsStringSync();\n const roleInject = defIsRoleFile ? roleSystemPromptInjectionSync(agentDefinition as string, effort) : '';\n const effortFlag = (!defIsRoleFile && effort) ? ` --effort ${effort}` : '';\n\n // OpenAI subscription → local CLIProxyAPI sidecar exposes an\n // Anthropic-compatible /v1/messages endpoint, so Claude Code can drive\n // gpt-* models directly via ANTHROPIC_BASE_URL (no wrapper process).\n // The provider env vars are injected separately by getProviderEnvForModel.\n if (provider.name === 'openai' && (await getProviderAuthMode(validatedModel)) === 'subscription') {\n // CLIProxy supports gpt-5.x but not the -pro variant; map aliases to real names.\n const resolvedModel = CLI_PROXY_MODEL_ALIASES[validatedModel] ?? validatedModel;\n return `claude ${permissionFlags}${roleInject} --model ${shellQuoteModelIdSync(resolvedModel)}${effortFlag}${nameFlag}`;\n }\n\n return `claude ${permissionFlags}${roleInject} --model ${quotedModel}${effortFlag}${nameFlag}`;\n}\n\n/**\n * Resolve the role's Claude-harness agent-definition path.\n *\n * Returns the file Claude Code's `--agent` flag should load to seed the run\n * with the role's frontmatter (permissions, tools, hooks, default model).\n * Returns `null` when the role does not have a Claude agent definition — for\n * example, the review convoy sub-roles, whose prompts are harness-agnostic\n * templates the orchestrator inlines into the spawn message (see\n * `buildConvoyPrompt` in `src/lib/cloister/review-agent.ts`). Sub-role\n * templates live in `roles/review-<subRole>.md`; they are deliberately not\n * loaded via `--agent` so the same content drives Claude Code, Pi, and\n * future harnesses uniformly and never auto-discovers as an ambient Claude\n * subagent inside a work agent's session.\n *\n * Without a sub-role the return is always the top-level role file; callers\n * can rely on the overload to avoid null-handling on that path.\n */\nexport function roleAgentDefinitionPath(role: Role): string;\nexport function roleAgentDefinitionPath(role: Role, subRole: string | undefined): string | null;\nexport function roleAgentDefinitionPath(role: Role, subRole?: string): string | null {\n if (role === 'review' && subRole) {\n return null;\n }\n return join(packageRoot, 'roles', `${role}.md`);\n}\n\n/**\n * PAN-2087: Claude Code 2.1.195 removed support for passing an agent DEFINITION\n * FILE to `--agent` — it now resolves only registered agent NAMES (from\n * `~/.claude/agents/*.md`). Role definitions live as files (`roles/<role>.md`),\n * so `claude --agent roles/<role>.md` started failing with \"agent not found\" and\n * the agent exited on launch. Instead of `--agent`, inject the role's BODY as an\n * appended system prompt and translate its `effort:` frontmatter to `--effort`.\n *\n * Everything the role frontmatter used to supply via `--agent` is reconstituted\n * as flags:\n * - body → `--append-system-prompt-file` (frontmatter stripped)\n * - `effort:` → `--effort`\n * - `mcpServers:` → a generated `--mcp-config` JSON (PAN-2090)\n * - `tools:` → `--allowedTools` (PAN-2090), with each declared MCP server\n * appended as `mcp__<name>` so the role can still call its own MCP tools —\n * otherwise the strict allow-list would block e.g. playwright for `test`.\n *\n * The role frontmatter's hooks are deliberately NOT reconstituted: they are\n * installed globally in `~/.claude/settings.json` and fire for every claude\n * session, and per the PAN-1402 note Claude Code never honored path-form\n * `--agent` frontmatter hooks anyway. permissionMode is covered by the\n * launcher's permission flags. Generated artifacts are cached under\n * `~/.overdeck/role-prompts/` and referenced by absolute path, so they do not\n * depend on `roles/` existing in the agent's cwd.\n *\n * Returns the flags (with a leading space) to splice in place of the old\n * `--agent <file>` flag, or '' when the definition file is missing.\n */\nexport function roleSystemPromptInjectionSync(definitionPath: string, explicitEffort?: RoleEffort): string {\n const abs = resolve(definitionPath);\n if (!existsSync(abs)) return '';\n const raw = readFileSync(abs, 'utf8');\n let body = raw;\n let frontmatter: Record<string, unknown> = {};\n const fm = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/);\n if (fm) {\n body = raw.slice(fm[0].length);\n try {\n const parsed = parseYaml(fm[1]);\n if (parsed && typeof parsed === 'object') frontmatter = parsed as Record<string, unknown>;\n } catch {\n // Malformed frontmatter — fall back to body-only injection.\n }\n }\n\n const dir = join(getOverdeckHome(), 'role-prompts');\n mkdirSync(dir, { recursive: true });\n const stem = basename(definitionPath).replace(/\\.md$/, '');\n const outPath = join(dir, `${stem}.md`);\n writeFileSync(outPath, `Source: ${abs}\\n\\n${body}`);\n\n const flags: string[] = [` --append-system-prompt-file '${outPath}'`];\n\n const effort = explicitEffort ?? (typeof frontmatter.effort === 'string' ? (frontmatter.effort as RoleEffort) : undefined);\n if (effort) flags.push(` --effort ${effort}`);\n\n // mcpServers: a YAML list of single-key maps ({ playwright: { … } }). Flatten\n // into one { mcpServers: { name: def } } config loaded via --mcp-config. It is\n // additive (the launcher's channels --mcp-config still applies; no\n // --strict-mcp-config), so the role keeps any project/global MCP servers too.\n const servers = parseRoleMcpServersSync(definitionPath);\n const mcpNames = Object.keys(servers);\n if (mcpNames.length > 0) {\n const mcpPath = join(dir, `${stem}.mcp.json`);\n writeFileSync(mcpPath, JSON.stringify({ mcpServers: servers }, null, 2));\n flags.push(` --mcp-config '${mcpPath}'`);\n }\n\n // tools: allow-list → --allowedTools (comma-joined single arg so the variadic\n // never swallows later flags). Append `mcp__<server>` for each declared MCP\n // server so its tools survive the strict allow-list.\n if (Array.isArray(frontmatter.tools)) {\n const toolNames = frontmatter.tools.filter((t): t is string => typeof t === 'string');\n if (toolNames.length > 0) {\n const allowed = [...toolNames, ...mcpNames.map((n) => `mcp__${n}`)];\n flags.push(` --allowedTools '${allowed.join(',')}'`);\n }\n }\n\n return flags.join('');\n}\n\n/** Build a Claude/ohmypi base command for role-based runs. */\nexport async function getRoleRuntimeBaseCommand(\n model: string,\n agentName: string,\n role: Role,\n harness: RuntimeName = 'claude-code',\n subRole?: string,\n effort?: RoleEffort,\n): Promise<string> {\n const validatedModel = requireModelOverrideSync(model);\n const quotedModel = shellQuoteModelIdSync(harness === 'claude-code' ? getClaudeCodeLaunchModelSync(validatedModel) : validatedModel);\n const behavior = getHarnessBehavior(harness);\n if (behavior.launchCommandKind === 'ohmypi-rpc') {\n const mcpNames = Object.keys(parseRoleMcpServersSync(roleAgentDefinitionPath(role)));\n if (mcpNames.length > 0) {\n console.warn(`[spawn] role '${role}' declares MCP servers (${mcpNames.join(', ')}) but harness 'ohmypi' does not provision MCP — those tools will be unavailable`);\n }\n return `omp --mode rpc --model ${quotedModel}`;\n }\n if (behavior.launchCommandKind === 'codex-work-tui') {\n return `codex`;\n }\n if (behavior.launchCommandKind === 'acp-host') {\n return 'acp-host';\n }\n if (behavior.launchCommandKind === 'muse-tui') return 'muse';\n if (behavior.launchCommandKind === 'kimi-code-tui') {\n // buildKimiCodeCommand in launcher-generator builds the full `kimi -m ... --yolo`\n // command; return a stub base command so the launcher generator can short-circuit.\n return 'kimi-code';\n }\n\n // Integration tests can inject a harmless harness command so a leaked or\n // intentionally-real tmux session never runs the production `claude` binary.\n if (process.env.OVERDECK_TEST_HARNESS_COMMAND) {\n return process.env.OVERDECK_TEST_HARNESS_COMMAND;\n }\n\n const provider = getProviderForModelSync(validatedModel);\n const requestedDefinitionPath = roleAgentDefinitionPath(role, subRole);\n const definitionPath = requestedDefinitionPath && existsSync(resolve(requestedDefinitionPath))\n ? requestedDefinitionPath\n : null;\n if (requestedDefinitionPath && !definitionPath && !missingRoleDefinitionWarnings.has(requestedDefinitionPath)) {\n missingRoleDefinitionWarnings.add(requestedDefinitionPath);\n console.warn(\n `[agents] Role definition ${resolve(requestedDefinitionPath)} does not exist; launching ${role} without its role system prompt`,\n );\n }\n // PAN-2087: inject the role body (+ effort frontmatter) as an appended system\n // prompt instead of `--agent <file>` (Claude Code dropped --agent file support).\n const roleInject = definitionPath ? roleSystemPromptInjectionSync(definitionPath, effort) : '';\n const nameFlag = ` --name ${agentName}`;\n // PAN-3077: never omit --effort on definition-less runs (review sub-roles,\n // standing supervisor). Omission hands the choice to the harness default —\n // xhigh on Opus 5 — violating the effort-defaults-to-high policy. An\n // explicitly passed effort still wins; role-file runs get effort from\n // frontmatter via roleSystemPromptInjectionSync.\n const effortFlag = !definitionPath ? ` --effort ${effort ?? 'high'}` : '';\n // permissionMode now comes from the global permission flags for EVERY role\n // (the old --agent path relied on role frontmatter, which Claude Code no longer\n // applies). This honors the user's bypass/auto setting uniformly.\n const permissionFlags = ` ${getClaudePermissionFlagsStringSync()}`;\n\n // PAN-1557: convoy sub-reviewers now run as interactive, attachable sessions\n // (prompt delivered via tmux, completion signalled by the Stop-hook) instead\n // of headless `claude --print`. No role uses --print anymore.\n const printFlag = '';\n\n if (provider.name === 'openai' && (await getProviderAuthMode(validatedModel)) === 'subscription') {\n const resolvedModel = CLI_PROXY_MODEL_ALIASES[validatedModel] ?? validatedModel;\n return `claude${printFlag}${roleInject}${permissionFlags} --model ${shellQuoteModelIdSync(resolvedModel)}${effortFlag}${nameFlag}`;\n }\n\n return `claude${printFlag}${roleInject}${permissionFlags} --model ${quotedModel}${effortFlag}${nameFlag}`;\n}\n\nasync function waitForMuseTuiReady(agentId: string, timeoutSec: number): Promise<boolean> {\n const deadline = Date.now() + timeoutSec * 1000;\n while (Date.now() < deadline) {\n if (!(await Effect.runPromise(sessionExists(agentId)))) return false;\n const pane = await Effect.runPromise(capturePane(agentId, 80));\n if (/^\\s*⟩\\s/m.test(pane) && /(?:muse-spark|Muse Code)/.test(pane)) return true;\n await new Promise(resolve => setTimeout(resolve, 250));\n }\n return false;\n}\n","/** Materialize Overdeck-owned context for Muse's developer-message file input. */\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getOverdeckHome, isDevMode } from '../paths.js';\nimport { findProjectByPathSync } from '../projects.js';\nimport { renderGlobalLayer, renderProjectLayer } from '../context-layers/render.js';\nimport { resolveWorkspaceContextFile } from '../context-layers/layers.js';\nimport { renderForHarness } from '../context-layers/harness.js';\nimport { workspaceContextWithoutProjectLayer } from '../context-layers/assemble.js';\nimport { museDataHome } from './muse-session.js';\n\nexport async function materializeMuseContext(agentId: string, workspace: string, roleFile?: string): Promise<string> {\n museDataHome(agentId); // Validate identity before forming artifact paths.\n const sections = [renderGlobalLayer('muse', isDevMode())];\n const project = findProjectByPathSync(workspace);\n if (project) sections.push(renderProjectLayer(project.path, 'muse'));\n const workspaceLayer = await readFile(resolveWorkspaceContextFile(workspace), 'utf8').catch(error => {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return '';\n throw error;\n });\n sections.push(renderForHarness(project ? workspaceContextWithoutProjectLayer(workspaceLayer) : workspaceLayer, 'muse'));\n if (roleFile) sections.push(await readFile(roleFile, 'utf8'));\n const dir = join(getOverdeckHome(), 'agents', agentId);\n await mkdir(dir, { recursive: true });\n const path = join(dir, 'muse-context.md');\n await writeFile(path, sections.filter(section => section.trim()).join('\\n\\n---\\n\\n'), { mode: 0o600 });\n return path;\n}\n","import { clearRecordPipelineClosedOut } from '../pan-dir/record-update.js';\nimport { readIssueRecordSync } from '../pan-dir/record.js';\nimport {\n getProjectSync,\n resolveProjectFromIssueSync,\n type ProjectConfig,\n type ResolvedProject,\n} from '../projects.js';\n\nexport interface AgentSpawnReopenGuardDeps {\n resolveProject: (issueId: string) => ResolvedProject | null;\n getProject: (projectKey: string) => ProjectConfig | null;\n hasClosedOutRecord: (project: ProjectConfig, issueId: string) => boolean;\n clearClosedOut: (project: ProjectConfig, issueId: string) => Promise<boolean>;\n log: (message: string) => void;\n}\n\nfunction defaultDeps(): AgentSpawnReopenGuardDeps {\n return {\n resolveProject: resolveProjectFromIssueSync,\n getProject: getProjectSync,\n hasClosedOutRecord: (project, issueId) => {\n const record = readIssueRecordSync(project, issueId);\n return record?.pipeline.closedOut === true || Boolean(record?.pipeline.closedOutAt);\n },\n clearClosedOut: (project, issueId) => clearRecordPipelineClosedOut(project, issueId),\n log: (message) => console.log(message),\n };\n}\n\n/** Clear stale terminal record state immediately before a new agent process starts. */\nexport async function clearStaleClosedOutBeforeSpawn(\n issueId: string,\n deps: AgentSpawnReopenGuardDeps = defaultDeps(),\n): Promise<boolean> {\n const normalizedIssueId = issueId.toUpperCase();\n const resolved = deps.resolveProject(normalizedIssueId);\n if (!resolved) {\n deps.log(`[spawn] ${normalizedIssueId} has no configured project; no close-out record was checked`);\n return false;\n }\n const project = deps.getProject(resolved.projectKey);\n if (!project) {\n deps.log(`[spawn] ${normalizedIssueId} resolved missing project ${resolved.projectKey}; no close-out record was checked`);\n return false;\n }\n if (!deps.hasClosedOutRecord(project, normalizedIssueId)) return false;\n\n const changed = await deps.clearClosedOut(project, normalizedIssueId);\n if (changed) {\n deps.log(`[spawn] Cleared stale closedOut state for ${normalizedIssueId} before agent launch`);\n }\n return changed;\n}\n","import type { RuntimeName } from '../runtimes/types.js';\nimport type { XBriefDifficulty, XBriefItem, XBriefItemKind } from '../xbrief/types.js';\nimport type { TierDefinition } from './tier-table.js';\n\n/**\n * Model + harness resolution chain for tiered execution (PAN-1791, folding in\n * PAN-1196). Resolution order for each bead:\n *\n * 1. per-bead `metadata.model` override — wins for the model; the harness\n * (and dispatch lane) still comes from the rest of the chain\n * 2. `byKind[item.metadata.kind]` — subject-matter routing to a tier name\n * 3. `difficultyToTier[item.metadata.difficulty]` — the tier table\n * 4. `roleDefault` — the configured role default\n *\n * When nothing resolves and no default is configured, resolveTier throws a\n * named ResolveTierError. It NEVER falls back to a hardcoded model literal\n * (no-hardcoded-model-fallbacks rule).\n *\n * Pure function over already-validated config — no spawning, no I/O here.\n */\n\n/**\n * Reserved tierName values naming the resolution source when the result did\n * not come from a configured tier. Configured tier names must not use these.\n */\nexport const OVERRIDE_TIER_NAME = 'override';\nexport const ROLE_DEFAULT_TIER_NAME = 'role-default';\n\nexport interface ResolvedTier {\n tierName: string;\n model: string;\n harness: RuntimeName;\n}\n\nexport interface RoleDefaultTier {\n model: string;\n harness: RuntimeName;\n}\n\n/**\n * Structural subset of ValidatedTieredExecutionConfig (tiers +\n * difficultyToTier) plus the chain-specific inputs. Callers can spread a\n * validated tier-table config and attach byKind/roleDefault.\n */\nexport interface ResolveTierConfig {\n tiers: Record<string, TierDefinition>;\n difficultyToTier: Partial<Record<XBriefDifficulty, string>>;\n /** Subject-matter routing: item kind -> configured tier name. */\n byKind?: Partial<Record<XBriefItemKind, string>>;\n roleDefault?: RoleDefaultTier;\n}\n\nexport class ResolveTierError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ResolveTierError';\n }\n}\n\nfunction itemLabel(item: Pick<XBriefItem, 'id' | 'title'>): string {\n return item.id || item.title || '<unknown item>';\n}\n\nfunction lookupTier(\n config: ResolveTierConfig,\n tierName: string,\n source: string,\n item: Pick<XBriefItem, 'id' | 'title'>,\n): ResolvedTier {\n const tier = config.tiers[tierName];\n if (!tier) {\n throw new ResolveTierError(\n `${source} routes item '${itemLabel(item)}' to tier '${tierName}', but no such tier is configured`,\n );\n }\n return { tierName, model: tier.model, harness: tier.harness };\n}\n\n/** The chain below the per-bead override: byKind -> byDifficulty -> role default. */\nfunction resolveBaseTier(\n item: Pick<XBriefItem, 'id' | 'title' | 'metadata'>,\n config: ResolveTierConfig,\n): ResolvedTier | undefined {\n const kind = item.metadata?.kind;\n const kindTierName = kind ? config.byKind?.[kind] : undefined;\n if (kindTierName) {\n return lookupTier(config, kindTierName, `byKind['${kind}']`, item);\n }\n\n const difficulty = item.metadata?.difficulty;\n const difficultyTierName = difficulty ? config.difficultyToTier[difficulty] : undefined;\n if (difficultyTierName) {\n return lookupTier(config, difficultyTierName, `difficultyToTier['${difficulty}']`, item);\n }\n\n if (config.roleDefault) {\n return {\n tierName: ROLE_DEFAULT_TIER_NAME,\n model: config.roleDefault.model,\n harness: config.roleDefault.harness,\n };\n }\n\n return undefined;\n}\n\nexport function resolveTier(\n item: Pick<XBriefItem, 'id' | 'title' | 'metadata'>,\n config: ResolveTierConfig,\n): ResolvedTier {\n const base = resolveBaseTier(item, config);\n\n const override = item.metadata?.model;\n if (typeof override === 'string' && override.length > 0) {\n if (!base) {\n throw new ResolveTierError(\n `item '${itemLabel(item)}' sets metadata.model '${override}' but no tier resolves its harness: `\n + 'no byKind match, no byDifficulty tier, and no role default configured',\n );\n }\n return { tierName: OVERRIDE_TIER_NAME, model: override, harness: base.harness };\n }\n\n if (!base) {\n throw new ResolveTierError(\n `no tier/model configured for item '${itemLabel(item)}': no metadata.model override, `\n + 'no byKind match, no byDifficulty tier, and no role default configured',\n );\n }\n\n return base;\n}\n","/**\n * PAN-2397 W1 — Always Tiered: the single staffing resolver.\n *\n * Every bead dispatch resolves WHO runs it through this one function. When an\n * explicit tier table is enabled (globally or via the plan's per-issue\n * override), staffing comes from the tier resolution chain. Otherwise staffing\n * comes from the IMPLICIT tier table: a degenerate one-tier world whose\n * `default` tier is exactly today's roles.work resolution (same resolveModel\n * call, same spawnKey, so Role-Models work distributions keep their\n * deterministic per-spawn behavior). \"Non-tiered execution\" is therefore not\n * a separate code path — it is this degenerate configuration.\n *\n * Fallthrough rule (D-explicit-gap): when the explicit table cannot place a\n * bead (no difficulty, no by_kind match, no per-bead override — the\n * ResolveTierError case), staffing falls through to the implicit tier. That\n * reproduces the historical behavior where such beads ran on the role-default\n * model, without callers special-casing it.\n */\n\nimport type { RuntimeName } from '../runtimes/types.js';\nimport type { XBriefItem } from '../xbrief/types.js';\nimport { loadConfigSync as loadYamlConfig } from '../config-yaml.js';\nimport type { NormalizedConfig } from '../config-yaml/schema.js';\nimport { resolveModel } from '../config-yaml/roles.js';\nimport { requireModelOverrideSync } from '../model-validation.js';\nimport { readIssueRecordSync } from '../pan-dir/record.js';\nimport { getBuiltInDefaultHarness, getProviderForModelSync } from '../providers.js';\nimport { getProjectSync, resolveProjectFromIssueSync } from '../projects.js';\nimport { fmix32, fnv1a32 } from '../config-yaml/percent.js';\nimport { resolveTier } from './resolve-tier.js';\nimport { resolveTieredExecutionEnabled, type TierDistributionEntry } from './tier-table.js';\n\nexport const IMPLICIT_TIER_NAME = 'default';\n\nexport interface Staffing {\n tierName: string;\n model: string;\n harness: RuntimeName;\n /** true when staffing came from the implicit roles.work-derived tier. */\n implicit: boolean;\n}\n\nexport interface ResolveStaffingOptions {\n /** plan.metadata — carries the per-issue tiered_execution override. */\n planMetadata?: { [key: string]: unknown };\n /**\n * Deterministic key for Role-Models percent distributions in the implicit\n * tier (spawn.ts uses `${role}:${issueId}`). Required for byte-identical\n * behavior with the historical determineModel path.\n */\n spawnKey?: string;\n /** Injectable config for tests; defaults to the loaded config. */\n config?: Pick<NormalizedConfig, 'roles' | 'workhorses' | 'tieredExecution' | 'providerHarnesses'>;\n /** Issue whose durable work-model override should be applied. */\n issueId?: string;\n}\n\n/** Per-issue work-model override from the issue record, or undefined. */\nexport function resolveIssueWorkModel(issueId: string): string | undefined {\n const resolved = resolveProjectFromIssueSync(issueId);\n if (!resolved) return undefined;\n const project = getProjectSync(resolved.projectKey);\n if (!project) return undefined;\n return readIssueRecordSync(project, issueId)?.workModel || undefined;\n}\n\n/** Provider-default harness for a model (PAN-1984: harness is derived from the\n * model's provider — per-provider setting else built-in default). */\nexport function providerDefaultHarnessSync(\n model: string,\n config: Pick<NormalizedConfig, 'providerHarnesses'>,\n): RuntimeName {\n const provider = getProviderForModelSync(model).name;\n return config.providerHarnesses?.[provider] ?? getBuiltInDefaultHarness(provider);\n}\n\n/** The implicit tier: roles.work resolution as a Staffing. Fails loudly when\n * the work role is unresolvable — never a hardcoded fallback. */\nexport function resolveImplicitStaffing(\n config: Pick<NormalizedConfig, 'roles' | 'workhorses' | 'providerHarnesses'>,\n spawnKey?: string,\n): Staffing {\n const model = requireModelOverrideSync(resolveModel('work', undefined, config, spawnKey));\n return {\n tierName: IMPLICIT_TIER_NAME,\n model,\n harness: providerDefaultHarnessSync(model, config),\n implicit: true,\n };\n}\n\n/**\n * THE staffing resolver (FR-1). Explicit tier table when enabled for the\n * issue; implicit roles.work tier otherwise — and as the fallthrough when the\n * explicit table cannot place the bead.\n */\nexport function resolveStaffing(\n item: Pick<XBriefItem, 'id' | 'title' | 'metadata'>,\n options: ResolveStaffingOptions = {},\n): Staffing {\n const config = options.config ?? loadYamlConfig().config;\n const issueModel = options.issueId ? resolveIssueWorkModel(options.issueId) : undefined;\n if (issueModel) {\n const model = requireModelOverrideSync(issueModel);\n return {\n tierName: 'issue-override',\n model,\n harness: providerDefaultHarnessSync(model, config),\n implicit: false,\n };\n }\n const tiered = config.tieredExecution;\n\n if (tiered && resolveTieredExecutionEnabled(tiered, options.planMetadata)) {\n try {\n const tier = resolveTier(item, tiered);\n // PAN-2391: a distribution tier spreads beads across weighted\n // model+harness entries. Selection is deterministic per bead so\n // replay/re-resolution always lands on the same entry. A per-bead\n // metadata.model override never reaches here with the tier's name\n // (resolveTier returns the override pseudo-tier), so overrides win.\n const distribution = tiered.tiers?.[tier.tierName]?.distribution;\n if (distribution && distribution.length > 0) {\n const entry = pickDistributionEntry(distribution, `${options.spawnKey ?? ''}:${item.id}`);\n return { tierName: tier.tierName, model: entry.model, harness: entry.harness, implicit: false };\n }\n return { tierName: tier.tierName, model: tier.model, harness: tier.harness, implicit: false };\n } catch {\n // Explicit table cannot place this bead — fall through to the implicit\n // tier (historical role-default behavior, now uniform).\n }\n }\n\n return resolveImplicitStaffing(config, options.spawnKey);\n}\n\n/** Deterministic weighted pick (D6): FNV-1a of the selection key → bucket in\n * [0, 100). Same key always selects the same entry. */\nexport function pickDistributionEntry(\n entries: readonly TierDistributionEntry[],\n selectionKey: string,\n): TierDistributionEntry {\n const total = entries.reduce((sum, entry) => sum + entry.weight, 0);\n const bucket = fmix32(fnv1a32(selectionKey)) % total;\n let cursor = 0;\n for (const entry of entries) {\n cursor += entry.weight;\n if (bucket < cursor) return entry;\n }\n return entries[entries.length - 1]!;\n}\n","import { materializeMuseContext } from '../runtimes/muse-context.js';\nimport { resolveMuseSessionPath, museSessionId } from '../runtimes/muse-session.js';\nimport { existsSync } from 'fs';\nimport { basename, join } from 'path';\nimport { Effect } from 'effect';\nimport { emitActivityEntrySync } from '../activity-logger.js';\nimport { getClaudePermissionFlagsStringSync } from '../claude-permissions.js';\nimport { loadConfigSync } from '../config.js';\nimport { loadConfigSync as loadYamlConfig } from '../config-yaml.js';\nimport type { RoleEffort } from '../config-yaml.js';\nimport { getFlywheelActiveRunIdSync } from '../overdeck/control-settings.js';\nimport { createTrackerFromConfig, createTracker } from '../tracker/factory.js';\nimport type { IssueState } from '../tracker/interface.js';\nimport { findProjectByPathSync, getIssuePrefix, resolveProjectFromIssueSync } from '../projects.js';\nimport { getWorkspaceStackHealth } from '../workspace/stack-health.js';\nimport { resolveCanonicalReviewStatus } from '../cloister/review-status-source.js';\nimport { generateLauncherScriptSync } from '../launcher-generator.js';\nimport { getProviderForModelSync, setupCredentialFileAuthSync, clearCredentialFileAuthSync } from '../providers.js';\nimport type { ModelId, ComplexityLevel } from '../settings.js';\nimport { requireModelOverrideSync } from '../model-validation.js';\nimport type { MemoryIdentity } from '@overdeck/contracts';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { readWorkspacePlanSync } from '../xbrief/io.js';\nimport { getDispatchableItems } from '../xbrief/dag.js';\nimport { type Role } from './agent-state.js';\nimport type { TierAssignment } from './dispatch-tier.js';\nimport { normalizeFlywheelRunId } from './provenance.js';\nimport { clearStaleClosedOutBeforeSpawn } from './reopen-guard.js';\nimport { resolveStaffing } from './staffing.js';\nimport { resolveTieredExecutionEnabled, resolveTieredExecutionEnabledForIssue } from './tier-table.js';\nimport {\n buildCavemanExports,\n getProviderEnvForModel,\n getProviderExportsForModel,\n} from './provider-env.js';\nimport {\n claudeSystemPromptFiles,\n getAcpLauncherFields,\n getAgentRuntimeBaseCommand,\n getCodexLauncherFields,\n getKimiCodeLauncherFields,\n getOhmypiLauncherFields,\n inferMemoryProjectId,\n roleAgentDefinitionPath,\n roleSystemPromptInjectionSync,\n} from './runtime-command.js';\n\nexport type FlywheelSpawnEnv = {\n OVERDECK_FLYWHEEL_RUN_ID?: string;\n OVERDECK_FLYWHEEL_AGENT_ROLE?: Role;\n};\n\nexport function resolveFlywheelSpawnEnv(role: Role, runIdOverride?: string | null): FlywheelSpawnEnv {\n const runId = normalizeFlywheelRunId(runIdOverride ?? getFlywheelActiveRunIdSync());\n return runId\n ? { OVERDECK_FLYWHEEL_RUN_ID: runId, OVERDECK_FLYWHEEL_AGENT_ROLE: role }\n : {};\n}\n\nexport function resolveAgentStartedBy(\n explicit: string | undefined,\n flywheelRunId: string | undefined,\n environmentToken = process.env['OVERDECK_AGENT_STARTED_BY'],\n): string {\n const resolved = explicit?.trim() || environmentToken?.trim() || (flywheelRunId ? `flywheel:${flywheelRunId}` : '');\n if (!resolved) throw new Error('Agent spawn provenance is required: pass startedBy at the launch entry point.');\n return resolved;\n}\n\nexport function flywheelEnvExports(env: FlywheelSpawnEnv): string[] {\n return [\n env.OVERDECK_FLYWHEEL_RUN_ID ? `export OVERDECK_FLYWHEEL_RUN_ID=${env.OVERDECK_FLYWHEEL_RUN_ID}` : undefined,\n env.OVERDECK_FLYWHEEL_AGENT_ROLE ? `export OVERDECK_FLYWHEEL_AGENT_ROLE=${env.OVERDECK_FLYWHEEL_AGENT_ROLE}` : undefined,\n ].filter((value): value is string => value !== undefined);\n}\n\nexport interface SpawnOptions {\n issueId: string;\n workspace: string;\n /** Coding-agent harness (PAN-636). Defaults to 'claude-code' when omitted. */\n harness?: RuntimeName;\n model?: string;\n prompt?: string;\n /**\n * Spawn role. Defaults to 'work'. The 'strike' role is the bypass path that\n * skips plan/review/test/ship and lands directly on main — see roles/strike.md.\n * Strike sessions are named `strike-<issue-id>` instead of `agent-<issue-id>`.\n */\n role?: 'work' | 'strike' | 'knowledge';\n difficulty?: ComplexityLevel;\n agentType?: 'review-agent' | 'test-agent' | 'merge-agent' | 'work-agent';\n\n // Work type system (PAN-118)\n phase?: 'exploration' | 'implementation' | 'testing' | 'documentation' | 'review-response' | 'planning' | 'synthesis';\n workType?: string; // Explicit work type ID (overrides phase-based detection)\n\n /**\n * Optional registered slot agent id. Omitted for the default one-work-agent\n * path; set only by spawnRun when launching a per-item slot.\n */\n agentId?: string;\n /** Registered swarm slot index for per-item work-agent spawning. */\n slotIndex?: number;\n /** xBRIEF item id assigned to the registered swarm slot. */\n slotItemId?: string;\n /** Marks the bare parent work agent as the swarm foreman. */\n foreman?: boolean;\n allowHost?: boolean;\n flywheelRunId?: string;\n startedBy: string;\n /** True only when planning consent was the release authority for this autonomous work launch. */\n autoSpawnConsentRequired?: boolean;\n /** Claude Code `--effort` level for the spawned session (work/strike). */\n effort?: RoleEffort;\n}\n\nexport interface SpawnRunOptions {\n workspace?: string;\n harness?: RuntimeName;\n model?: string;\n prompt?: string;\n agentId?: string;\n /**\n * Sub-role within the review convoy (PAN-1059).\n * When set alongside role='review', each convoy reviewer gets its own\n * isolated tmux session using the code-review-<subRole> agent definition.\n * Values: 'security' | 'correctness' | 'performance' | 'requirements'\n */\n subRole?: string;\n /**\n * Review convoy wiring (PAN-977). When spawning a review sub-role, the\n * synthesis agent id and the reviewer's output path are passed in up front\n * so the generated launcher can own the REVIEWER_READY/FAILED/TIMEOUT signal\n * deterministically on process exit. Persisted onto AgentState too.\n */\n reviewSynthesisAgentId?: string;\n reviewOutputPath?: string;\n /** Run-scoped reviewer metadata persisted before the tmux session launches. */\n reviewRunId?: string;\n reviewDeadlineAt?: string;\n allowHost?: boolean;\n registerConversation?: boolean;\n effort?: RoleEffort;\n extraEnvExports?: string[];\n resumeSessionId?: string;\n flywheelRunId?: string;\n startedBy: string;\n /** True only when planning consent was the release authority for this autonomous work launch. */\n autoSpawnConsentRequired?: boolean;\n /** 1-based registered slot index for per-item work-agent spawning. */\n slotIndex?: number;\n /** xBRIEF item id assigned to this registered slot. Required with slotIndex. */\n slotItemId?: string;\n /** Marks the bare parent work agent as the swarm foreman. */\n foreman?: boolean;\n /** Attempt-specific branch used when a static slot id is safely reused. */\n slotBranch?: string;\n /** Optional per-spawn cap for registered work-agent slots. Defaults to the work-agent governor cap. */\n maxRegisteredSlots?: number;\n}\n\nexport interface RegisteredSlotSpawn {\n agentId: string;\n branch: string;\n workspace: string;\n slotIndex: number;\n slotItemId: string;\n}\n\n/**\n * Thread a tiered-execution tier assignment into spawn options (PAN-1791).\n * When the assignment resolved a tier, its model+harness replace the parent\n * defaults so the dispatched bead runs on the tier its difficulty selected.\n * With no assignment (tiering disabled), the options pass through unchanged.\n */\nexport function applyTierAssignment<T extends Pick<SpawnRunOptions, 'model' | 'harness'>>(\n options: T,\n assignment?: TierAssignment,\n): T {\n if (!assignment?.model) return options;\n return {\n ...options,\n model: assignment.model,\n harness: assignment.harness ?? options.harness,\n };\n}\n\nexport function resolveRegisteredSlotSpawn(\n issueId: string,\n baseWorkspace: string,\n options: Pick<SpawnRunOptions, 'slotIndex' | 'slotItemId' | 'slotBranch'>,\n): RegisteredSlotSpawn | null {\n const { slotIndex, slotItemId: slotItemIdRaw } = options;\n if (slotIndex === undefined && slotItemIdRaw === undefined) return null;\n if (slotIndex === undefined || slotItemIdRaw === undefined) {\n throw new Error('Registered slot spawn requires both slotIndex and slotItemId.');\n }\n if (!Number.isInteger(slotIndex) || slotIndex < 1) {\n throw new Error(`Registered slot index must be a positive integer; got ${slotIndex}.`);\n }\n const slotItemId = slotItemIdRaw.trim();\n if (!slotItemId) {\n throw new Error('Registered slot spawn requires a non-empty slotItemId.');\n }\n\n const issueLower = issueId.toLowerCase();\n return {\n agentId: `agent-${issueLower}-slot-${slotIndex}`,\n branch: options.slotBranch ?? `feature/${issueLower}-slot-${slotIndex}`,\n workspace: `${baseWorkspace}-slot-${slotIndex}`,\n slotIndex,\n slotItemId,\n };\n}\n\nexport interface SlotTierSpawnParams {\n model?: string;\n harness?: RuntimeName;\n tierName?: string;\n /** PAN-2397: true when staffing came from the implicit roles.work tier.\n * Implicit staffing intentionally omits `harness` so the spawn keeps its\n * historical harness handling (provider-default derived from the model). */\n implicit?: boolean;\n}\n\n/**\n * Tiered-execution model resolution for a registered slot spawn (PAN-1791,\n * fixing PAN-1196's \"difficulty captured and ignored\"). When tiered execution\n * is enabled for the plan, resolve the slot item's tier through\n * assignDispatchTier and return its (model, harness) as spawn params so the\n * dispatched bead runs on the tier its difficulty selected.\n *\n * Returns {} — leaving the existing model resolution untouched — when:\n * - tiered execution is disabled (globally and per-plan), or\n * - an explicit per-spawn model override was passed (same precedence as\n * determineModel: an explicit override outranks routing), or\n * - the item carries no difficulty and no per-bead model override, in which\n * case the chain's role-default step IS the existing configured\n * role-default resolution in determineModel (nothing hardcoded here).\n */\nexport function resolveSlotTierSpawnParams(\n baseWorkspace: string,\n slotItemId: string,\n explicitModel?: string,\n spawnKey?: string,\n): SlotTierSpawnParams {\n // An explicit per-spawn model override outranks all staffing (same\n // precedence as determineModel).\n if (explicitModel) return {};\n const doc = readWorkspacePlanSync(baseWorkspace);\n if (!doc) return {};\n const planMetadata = doc?.plan?.metadata;\n\n // Extract issueId from workspace path: feature-<issueId>\n const workspaceName = basename(baseWorkspace);\n const issueIdMatch = workspaceName.match(/^feature-(.+)$/i);\n const issueId = issueIdMatch ? issueIdMatch[1].toUpperCase() : null;\n\n const config = loadYamlConfig().config;\n const tiered = config.tieredExecution;\n // Use issue-aware resolver to honor record override (PAN-2383)\n const explicitTableActive = Boolean(\n tiered && (issueId ? resolveTieredExecutionEnabledForIssue(tiered, issueId, planMetadata) : resolveTieredExecutionEnabled(tiered, planMetadata))\n );\n\n const item = doc.plan.items.find((candidate) => candidate.id === slotItemId);\n if (!item) {\n // With an explicit table active a missing item is a data-integrity signal;\n // otherwise fall back to the historical role-default spawn path.\n if (explicitTableActive) {\n throw new Error(\n `Tiered execution is enabled but item '${slotItemId}' was not found in the plan for ${baseWorkspace}.`,\n );\n }\n return {};\n }\n\n // PAN-2397 (Always Tiered): staffing ALWAYS resolves — explicit tier table\n // when enabled, else the implicit roles.work tier (same resolveModel +\n // spawnKey as determineModel, so distributions stay deterministic).\n const staffing = resolveStaffing(item, { planMetadata, spawnKey, config, issueId: issueId ?? undefined });\n return {\n model: staffing.model,\n harness: staffing.implicit ? undefined : staffing.harness,\n tierName: staffing.tierName,\n implicit: staffing.implicit,\n };\n}\n\n/**\n * Tiered-execution model resolution for the single work-agent path.\n * Auto-start and non-swarm issues still run one foreground work agent, but\n * the first dispatchable xBRIEF item is the same scheduling unit the agent is\n * about to execute. If tiered execution is enabled for the issue, route that\n * agent through the item's resolved tier; otherwise leave role-default model\n * resolution untouched.\n */\nexport function resolveSingleWorkTierSpawnParams(\n workspace: string,\n explicitModel?: string,\n spawnKey?: string,\n): SlotTierSpawnParams {\n if (explicitModel) return {};\n\n const doc = readWorkspacePlanSync(workspace);\n if (!doc) return {};\n const planMetadata = doc?.plan?.metadata;\n\n const item = getDispatchableItems(doc, new Set())[0];\n if (!item) return {};\n\n // Extract issueId from workspace path: feature-<issueId>\n const workspaceName = basename(workspace);\n const issueIdMatch = workspaceName.match(/^feature-(.+)$/i);\n const issueId = issueIdMatch ? issueIdMatch[1].toUpperCase() : null;\n\n // PAN-2397 (Always Tiered): the single-work path staffs through the same\n // resolver as slots — explicit table when enabled, implicit roles.work\n // tier otherwise. Use issue-aware resolver to honor record override (PAN-2383).\n const config = loadYamlConfig().config;\n const tiered = config.tieredExecution;\n const effectiveTieredEnabled = issueId\n ? resolveTieredExecutionEnabledForIssue(tiered, issueId, planMetadata)\n : resolveTieredExecutionEnabled(tiered, planMetadata);\n\n const staffing = resolveStaffing(item, {\n planMetadata,\n spawnKey,\n issueId: issueId ?? undefined,\n config: { ...config, tieredExecution: { ...tiered, enabled: effectiveTieredEnabled } },\n });\n return {\n model: staffing.model,\n harness: staffing.implicit ? undefined : staffing.harness,\n tierName: staffing.tierName,\n implicit: staffing.implicit,\n };\n}\n\n/**\n * Shared tracker resolution logic for issue state transitions.\n *\n * Resolution order (by project tracker type):\n * 1. github_repo → GitHub Issues (takes priority over issue_prefix, since projects\n * like overdeck use GitHub Issues with a prefix, not Linear)\n * 2. rally_project → Rally\n * 3. issue_prefix (no github_repo) → Linear (covers gitlab+linear and pure-linear projects)\n * 4. gitlab_repo only → warn and skip (GitLab doesn't support label-based state transitions)\n *\n * Precedence rationale: issue_prefix was renamed from linear_team but is now also set on\n * GitHub-hosted projects (e.g. issue_prefix: PAN for overdeck GitHub Issues).\n * github_repo must be checked first so GitHub projects don't misroute to Linear.\n */\nasync function transitionIssueState(issueId: string, state: IssueState, workspacePath?: string): Promise<void> {\n // Guard: bare numeric IDs (no alphabetic prefix, e.g. \"484\") must never reach\n // any tracker API. Linear's searchIssues(\"484\") would match MIN-484 in the wrong\n // team. Log a warning and skip — the workspace's project must use prefixed IDs.\n if (/^\\d+$/.test(issueId)) {\n console.warn(\n `[agents] Skipping ${state} transition for bare numeric ID \"${issueId}\" — ` +\n `issue IDs must include a project prefix (e.g. PAN-${issueId}). ` +\n `This workspace was likely created before the pan- prefix convention.`\n );\n return;\n }\n\n // Resolve the project from workspacePath — its configured tracker is authoritative.\n // Every issue MUST belong to a registered project with a tracker configured.\n const projectConfig = workspacePath ? findProjectByPathSync(workspacePath) : null;\n if (!projectConfig) {\n throw new Error(`Cannot transition ${issueId}: no project config found for workspace ${workspacePath || '(none)'}. Register the project in projects.yaml.`);\n }\n\n // Project has a GitHub repo — use GitHub Issues tracker.\n // Checked BEFORE issue_prefix because github_repo projects (e.g. overdeck)\n // set issue_prefix for their GitHub Issue prefix (PAN-), not for Linear.\n if (projectConfig.github_repo) {\n const [owner, repo] = projectConfig.github_repo.split('/');\n const tracker = createTracker({ type: 'github', owner, repo });\n await Effect.runPromise(tracker.transitionIssue(issueId, state));\n console.log(`[agents] Transitioned ${issueId} to ${state} via GitHub (${projectConfig.github_repo})`);\n return;\n }\n\n // Project has a Rally project — use Rally tracker\n if (projectConfig.rally_project) {\n const config = loadConfigSync();\n const trackersConfig = config.trackers;\n if (!trackersConfig?.rally) {\n throw new Error(`Project ${projectConfig.name} uses Rally (project: ${projectConfig.rally_project}) but no Rally tracker is configured in config.yaml`);\n }\n const tracker = createTrackerFromConfig(trackersConfig, 'rally');\n await Effect.runPromise(tracker.transitionIssue(issueId, state));\n console.log(`[agents] Transitioned ${issueId} to ${state} via Rally (project: ${projectConfig.rally_project})`);\n return;\n }\n\n // Project has a Linear team prefix (and no github_repo) — use Linear tracker.\n // This covers: pure-Linear projects and gitlab+Linear projects (e.g. mind-your-now).\n if (getIssuePrefix(projectConfig)) {\n const config = loadConfigSync();\n const trackersConfig = config.trackers;\n if (!trackersConfig?.linear) {\n throw new Error(`Project ${projectConfig.name} uses Linear (team: ${getIssuePrefix(projectConfig)}) but no Linear tracker is configured in config.yaml`);\n }\n const tracker = createTrackerFromConfig(trackersConfig, 'linear');\n await Effect.runPromise(tracker.transitionIssue(issueId, state));\n console.log(`[agents] Transitioned ${issueId} to ${state} via Linear (team: ${getIssuePrefix(projectConfig)})`);\n return;\n }\n\n if (projectConfig.gitlab_repo) {\n console.warn(`[agents] GitLab project detected (${projectConfig.gitlab_repo}) but GitLab does not support ${state} label transitions`);\n return;\n }\n\n throw new Error(`Project ${projectConfig.name} has no tracker configured (need issue_prefix, github_repo, or rally_project in projects.yaml)`);\n}\n\nexport async function transitionIssueToInProgress(issueId: string, workspacePath?: string): Promise<void> {\n return transitionIssueState(issueId, 'in_progress', workspacePath);\n}\n\n/**\n * Transitions an issue to \"in_review\" state in the configured issue tracker.\n * Fire-and-forget — logs warnings on failure but never blocks the pipeline.\n */\nexport async function transitionIssueToInReview(issueId: string, workspacePath?: string): Promise<void> {\n return transitionIssueState(issueId, 'in_review', workspacePath);\n}\n\nexport interface AgentLaunchConfig {\n launcherContent: string;\n providerEnv: Record<string, string>;\n}\n\nexport async function buildAgentLaunchConfig(opts: {\n agentId: string;\n model: string;\n workspace: string;\n role: Role;\n spawnMode?: 'resume';\n resumeSessionId?: string;\n isPlanning?: boolean;\n /** Per-agent .mcp.json path for the experimental Channels bridge. */\n channelsBridgeMcpConfig?: string;\n /** MCP server name to load as a Channel; defaults to 'overdeck-bridge'. */\n channelsBridgeServerName?: string;\n useSupervisor?: boolean;\n supervisorScriptPath?: string;\n /** Claude Code session id for fresh launches that need a known id before boot. */\n sessionId?: string;\n /**\n * Coding-agent harness (PAN-636). Defaults to 'claude-code' when omitted —\n * preserves bit-for-bit pre-PAN-636 behavior. When 'pi', the launcher is\n * built via the Pi command-line generator instead of the claude path; opts\n * like agentId-as-name and agent-frontmatter are ignored because Pi has\n * no agent-definition system.\n */\n harness?: RuntimeName;\n /** Exact harness executable validated by the shared launch preflight. */\n harnessBinaryPath?: string;\n extraEnvExports?: string[];\n /** Claude Code `--effort` level threaded into the launcher command. */\n effort?: RoleEffort;\n /** Inline prompt to embed in launch commands that still support prompt arguments. */\n promptInline?: string;\n}): Promise<AgentLaunchConfig> {\n const model = requireModelOverrideSync(opts.model);\n\n // Substrate guard: inject permission deny rules for Overdeck infrastructure\n // paths (.claude/agents/, .claude/hooks/, ~/.overdeck/, JSONL session dirs)\n // into the workspace's .claude/settings.local.json. Idempotent. Without this\n // an xBRIEF action like \"delete the legacy pan-*-agent.md files\" can convince\n // an agent to brick its own runtime. PAN-1048 X1 incident, 2026-05-09.\n try {\n const { injectOverdeckInfraDeny } = await import('../claude-settings-overlay.js');\n await Effect.runPromise(injectOverdeckInfraDeny(opts.workspace));\n } catch (err) {\n console.warn(`[agents] injectOverdeckInfraDeny failed for ${opts.agentId} (non-fatal): ${err instanceof Error ? err.message : err}`);\n }\n\n const behavior = getHarnessBehavior(opts.harness);\n const isAcp = behavior.launchCommandKind === 'acp-host';\n const providerEnv = isAcp ? {} : await getProviderEnvForModel(model, opts.harness);\n\n if (!isAcp) {\n const provider = getProviderForModelSync(model as ModelId);\n if (provider.authType === 'credential-file') {\n setupCredentialFileAuthSync(provider, opts.workspace);\n } else {\n clearCredentialFileAuthSync(opts.workspace);\n }\n }\n\n const providerExports = isAcp ? undefined : await getProviderExportsForModel(model, opts.harness);\n\n // PAN-1048: resume/restart launchers must respect the agent's role.\n // A resumed review/test/ship run loads the wrong frontmatter (and wrong\n // tool permissions) if it always points at roles/work.md.\n const launchRole: Role = opts.isPlanning ? 'plan' : opts.role;\n\n // PAN-1055: ohmypi harness needs --session-dir + fifo redirect threaded into\n // the launcher; getOhmypiLauncherFields() resolves them from the agent state\n // and they're spread into generateLauncherScript() below.\n // PAN-1574: codex harness needs its per-agent CODEX_HOME path.\n const piLauncherFields = behavior.usesRpcFifo\n ? await getOhmypiLauncherFields(opts.agentId, model, opts.effort)\n : {};\n const codexLauncherFields = behavior.usesCodexHome\n ? getCodexLauncherFields(opts.agentId, model, opts.workspace, launchRole, opts.effort)\n : {};\n const museSavedSession = opts.harness === 'muse' && opts.spawnMode === 'resume'\n ? await resolveMuseSessionPath(opts.agentId) : null;\n const museLauncherFields = opts.harness === 'muse' ? {\n harness: 'muse' as const,\n museModel: model,\n museEffort: opts.effort,\n museContextFile: await materializeMuseContext(opts.agentId, opts.workspace, roleAgentDefinitionPath(launchRole)),\n museResumeSessionId: museSavedSession ? museSessionId(museSavedSession) : undefined,\n } : {};\n const kimiCodeLauncherFields = behavior.launchCommandKind === 'kimi-code-tui'\n ? getKimiCodeLauncherFields(model, opts.effort)\n : {};\n if (isAcp && !opts.harnessBinaryPath) {\n throw new Error('ACP launch requires the executable path resolved by preflight');\n }\n const acpLauncherFields = isAcp\n ? getAcpLauncherFields(\n opts.agentId,\n model,\n opts.workspace,\n opts.harnessBinaryPath!,\n launchRole,\n opts.effort,\n )\n : {};\n\n if (opts.spawnMode === 'resume' && opts.resumeSessionId) {\n // Resume sessions adopt the role definition via --agent.\n // Permissions/model/tools/hooks come from roles/<role>.md frontmatter.\n // --name <agentId> gives the resumed Claude session a human-readable handle.\n //\n // The frontmatter's permissionMode: bypassPermissions only bypasses prompts\n // INSIDE cwd. Tools that touch siblings of cwd (e.g. bd reading\n // .beads/issues.jsonl through git subprocesses, pan reading\n // ~/.overdeck/...) still hit \"Do you want to proceed?\" without DSP.\n // Mid-Bash dialog dismissals (deacon nudge, paste-buffer write, sibling\n // hook output) cancel the in-flight tool call and surface as\n // `Interrupted · What should Claude do instead?` (PAN-1024 reproduced\n // this loop on every fresh resume of PAN-1044/PAN-934).\n //\n // Match the fresh-spawn path: when permissionMode resolves to 'bypass'\n // (PAN_YOLO=true OR claude.permissionMode=bypass in config), prepend\n // --dangerously-skip-permissions on resume too.\n // Use the shared helper so the only string literal for DSP lives in\n // claude-permissions.ts (see scripts/lint-permissions.sh allowlist).\n const launcherContent = generateLauncherScriptSync({\n role: launchRole,\n spawnMode: 'resume',\n workingDir: opts.workspace,\n changeDir: false,\n setTerminalEnv: true,\n providerExports,\n // PAN-2087: claude-code resumes inject the role body (+ effort) as an\n // appended system prompt instead of `--agent <file>` (Claude Code 2.1.195\n // dropped --agent file support); permission flags come from the global\n // resolver. ohmypi/codex resumes route through getAgentRuntimeBaseCommand\n // which short-circuits to the omp/codex form.\n baseCommand: behavior.launchCommandKind !== 'claude-code'\n ? await getAgentRuntimeBaseCommand(model, opts.agentId, launchRole, opts.harness)\n : `claude ${getClaudePermissionFlagsStringSync()}${roleSystemPromptInjectionSync(roleAgentDefinitionPath(launchRole))}`,\n resumeSessionId: opts.resumeSessionId,\n // PAN-3189: the git guard is emitted for launchers that carry an agent\n // id. Work/strike/review agents — the ones the stash/rebase rules exist\n // for — were the only launchers omitting it, so they ran unguarded and\n // inherited whichever guard their spawner happened to have on PATH.\n overdeckEnv: { agentId: opts.agentId },\n model: behavior.launchCommandKind !== 'claude-code' || providerExports?.includes('ANTHROPIC_BASE_URL') ? model : undefined,\n extraArgs: behavior.launchCommandKind !== 'claude-code' ? undefined : `--name ${opts.agentId}`,\n appendSystemPromptFiles: await claudeSystemPromptFiles(opts.workspace, opts.harness),\n extraEnvExports: opts.extraEnvExports,\n useSupervisor: opts.useSupervisor,\n supervisorScriptPath: opts.supervisorScriptPath,\n promptInline: opts.promptInline,\n ...piLauncherFields,\n ...codexLauncherFields,\n ...acpLauncherFields,\n ...kimiCodeLauncherFields,\n ...museLauncherFields,\n });\n return { launcherContent, providerEnv };\n }\n\n const yamlConfig = loadYamlConfig();\n const cavemanExports = await buildCavemanExports(\n opts.workspace,\n yamlConfig.config.caveman,\n opts.isPlanning ?? false,\n );\n\n // PAN-982: pass the role definition path + agentId through getAgentRuntimeBaseCommand so it\n // emits 'claude --agent roles/<role>.md --name <agentId>'.\n // PAN-636: when the harness uses the ohmypi RPC command, the helper\n // short-circuits to an omp --mode rpc line and the\n // agentName/agentDefinition arguments are ignored (Pi has no agent\n // definitions). The launcher generator's Pi branch then layers --session-dir\n // and the fifo redirect on top.\n const agentDefinition = roleAgentDefinitionPath(launchRole);\n const launcherContent = generateLauncherScriptSync({\n role: launchRole,\n workingDir: opts.workspace,\n changeDir: false,\n setTerminalEnv: true,\n providerExports,\n cavemanExports,\n baseCommand: await getAgentRuntimeBaseCommand(model, opts.agentId, agentDefinition, opts.harness ?? 'claude-code', opts.effort),\n // PAN-3189: see the resume branch above — same reason, fresh-spawn path.\n overdeckEnv: { agentId: opts.agentId },\n sessionId: behavior.sessionIdSource === 'launcher-session-id' ? opts.sessionId : undefined,\n appendSystemPromptFiles: await claudeSystemPromptFiles(opts.workspace, opts.harness),\n extraEnvExports: opts.extraEnvExports,\n useSupervisor: opts.useSupervisor,\n supervisorScriptPath: opts.supervisorScriptPath,\n promptInline: opts.promptInline,\n ...piLauncherFields,\n ...codexLauncherFields,\n ...acpLauncherFields,\n ...kimiCodeLauncherFields,\n ...museLauncherFields,\n ...(opts.channelsBridgeMcpConfig\n ? {\n channelsBridgeMcpConfig: opts.channelsBridgeMcpConfig,\n channelsBridgeServerName: opts.channelsBridgeServerName ?? 'overdeck-bridge',\n }\n : {}),\n });\n\n return { launcherContent, providerEnv };\n}\n\nexport function defaultRunWorkspace(issueId: string): string {\n const project = resolveProjectFromIssueSync(issueId);\n if (!project) {\n throw new Error(`Cannot spawn role run for ${issueId}: no project is configured for this issue prefix`);\n }\n return join(project.projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`);\n}\n\nexport async function retrieveSpawnTimeMemoryContext(input: {\n prompt: string;\n issueId: string;\n workspace: string;\n agentId: string;\n role: Role;\n harness: RuntimeName;\n}): Promise<string> {\n if (!input.prompt.trim()) return '';\n\n try {\n const identity: MemoryIdentity = {\n projectId: inferMemoryProjectId(input.workspace),\n workspaceId: basename(input.workspace),\n issueId: input.issueId,\n runId: input.agentId,\n sessionId: input.agentId,\n agentRole: input.role,\n agentHarness: input.harness,\n };\n const { injectPromptTimeMemory } = await import('../memory/injection.js');\n return (await injectPromptTimeMemory({ prompt: input.prompt, identity, surface: 'spawn' })).context;\n } catch (error) {\n console.warn(`[agents] Spawn-time memory context unavailable for ${input.agentId}:`, error instanceof Error ? error.message : String(error));\n return '';\n }\n}\n\nexport async function withSpawnTimeMemoryContext(input: {\n prompt: string;\n issueId: string;\n workspace: string;\n agentId: string;\n role: Role;\n harness: RuntimeName;\n}): Promise<string> {\n const context = await retrieveSpawnTimeMemoryContext(input);\n return context ? `${context}\\n\\n---\\n\\n${input.prompt}` : input.prompt;\n}\n\nexport function runAgentId(issueId: string, role: Role, subRole?: string): string {\n const base = role === 'work'\n ? `agent-${issueId.toLowerCase()}`\n : `agent-${issueId.toLowerCase()}-${role}`;\n return subRole ? `${base}-${subRole}` : base;\n}\n\n/**\n * Spawn-time stack-rebuild self-heal state. PAN-1618: the work-spawn gate\n * (`assertWorkspaceStackHealthyForSpawn`) used to fail hard when the workspace\n * docker stack was down, with only manual recoveries (`pan workspace rebuild`\n * or interactive `--host`). Under autonomous operation a fully-planned\n * `proposed` item whose stack happened to be down could never auto-start its\n * work agent — it sat at the gate forever. This mirrors the PAN-1247\n * orphan-test self-heal one role earlier: rebuild the stack before failing,\n * bounded by a cooldown + attempt cap so a stack that genuinely cannot be\n * rebuilt escalates to a human instead of looping `docker compose` forever.\n */\nconst spawnStackRebuildState: Map<string, { lastAttempt: number; attempts: number; escalated: boolean; hostFallbackNoticed?: boolean }> =\n new Map();\nconst SPAWN_STACK_REBUILD_COOLDOWN_MS = 15 * 60 * 1000;\nconst SPAWN_STACK_REBUILD_MAX_ATTEMPTS = 3;\n\n/**\n * Spawn a role-based Overdeck run. Work delegates to the existing work-agent\n * path; review/test/ship use the role definition files under roles/.\n */\nexport async function prepareWorkspaceForAgentSpawn(\n issueId: string,\n role: Role,\n allowHost = false,\n workspacePath?: string,\n): Promise<void> {\n await clearStaleClosedOutBeforeSpawn(issueId);\n await assertWorkspaceStackHealthyForSpawn(issueId, role, allowHost, workspacePath);\n}\n\nexport async function assertWorkspaceStackHealthyForSpawn(\n issueId: string,\n role: Role,\n allowHost = false,\n workspacePath?: string,\n): Promise<void> {\n if (role === 'plan' || role === 'knowledge') return;\n\n // PAN-1872: guard against an undefined issueId so workspace health checks do\n // not crash with `Cannot read properties of undefined (reading 'toUpperCase')`\n // while pan start is recovering from a sync-main conflict.\n const normalizedIssue = (issueId ?? '').toUpperCase();\n\n // PAN-1746: absence of a workspace must be a HARDER failure than an unhealthy\n // one. The host-fallback path below lets advancing roles (review/test/ship)\n // proceed when the docker stack is merely unhealthy — but a workspace\n // directory that does not exist at all means the launcher would fall back to\n // its cwd ($HOME) and wedge Claude at the folder-trust prompt while it holds\n // an advancing slot against the PAN-1665 governor. Refuse the spawn outright\n // instead of degrading to host. (`work`'s resume path already guards this in\n // restartAgent; this closes the same gap on the role-run spawn path.)\n if (workspacePath && !existsSync(workspacePath)) {\n throw new Error(\n `Workspace for ${normalizedIssue} does not exist at ${workspacePath} — refusing to spawn ${role}. `\n + `A missing workspace would land the agent in $HOME at the folder-trust prompt. `\n + `Recreate the workspace ('pan workspace rebuild ${normalizedIssue}') before retrying.`,\n );\n }\n\n const health = await Effect.runPromise(getWorkspaceStackHealth(issueId, { workspacePath }));\n if (health.healthy) {\n spawnStackRebuildState.delete(normalizedIssue);\n return;\n }\n\n const details = health.reasons.join('; ');\n const message = `Workspace docker stack for ${normalizedIssue} is not healthy: ${details}. Run 'pan workspace rebuild ${normalizedIssue}' or retry with --host --yes to override.`;\n\n if (allowHost) {\n // PAN-1556: host-override is a per-spawn detail, not user-facing activity —\n // it fired once per convoy member and buried real feed items (conversations).\n // Keep the console.warn for debugging; do not emit to the session feed.\n console.warn(`[agents] ${message}`);\n return;\n }\n\n // PAN-1645 + PAN-1618: an unhealthy stack must NEVER *block* the advancing\n // roles. review/test/ship all operate on the HOST workspace — ship\n // rebases/pushes against the host .git, review reads the committed diff, and\n // test runs the project's quality gates (host-run unless a gate explicitly\n // opts into a container) — so they do not need the workspace's docker\n // containers at all. The long-standing manual `--host` workaround (PAN-1645)\n // burned enormous effort just rediscovering that ship-on-broken-docker is a\n // false gate. For these roles we still attempt one bounded autonomous rebuild\n // (so a project whose test gates DO run in containers gets a healthy stack\n // when recoverable), but if it can't be made healthy we AUTO-FALL-BACK TO\n // HOST and proceed instead of throwing.\n //\n // `work` is different: a work agent may rely on the dev container's services,\n // so silently running it on the host could build/test against a missing\n // environment. work keeps the hard gate (rebuild → escalate to a human).\n const hostFallbackEligible = role !== 'work';\n\n const record = spawnStackRebuildState.get(normalizedIssue)\n ?? { lastAttempt: 0, attempts: 0, escalated: false };\n const now = Date.now();\n\n const fallbackToHost = (reason: string): void => {\n console.warn(`[agents] ${message} — auto-falling back to host for ${role} (${reason})`);\n // Emit the host-fallback notice once per issue. Use a SEPARATE latch from\n // the work-escalation latch (`escalated`): if review/test/ship trip the\n // host fallback first, a later `work` spawn for the same broken-stack issue\n // must still be able to emit its own (error-level) dead-end marker — the\n // operator's only signal that a work agent is blocked on docker.\n if (!record.hostFallbackNoticed) {\n record.hostFallbackNoticed = true;\n spawnStackRebuildState.set(normalizedIssue, record);\n emitActivityEntrySync({\n source: role,\n level: 'warn',\n issueId: normalizedIssue,\n message: `agent-spawn-host-fallback: ${normalizedIssue}`,\n details: `Workspace docker stack unhealthy (${details}); ${role} spawn proceeds on the host without containers. ${reason}`,\n });\n }\n };\n\n // A blocked or failed review means the work agent is explicitly being\n // restarted to repair known-broken code. Rebuilding its container stack would\n // compile the same broken branch and can only delay that repair, so work from\n // the host instead.\n const reviewStatus = role === 'work' ? resolveCanonicalReviewStatus(normalizedIssue).status?.reviewStatus : undefined;\n if (reviewStatus === 'blocked' || reviewStatus === 'failed') {\n fallbackToHost(`review is ${reviewStatus}; rework must repair the branch before its stack can build`);\n return;\n }\n\n const blockWork = (markerMessage: string, errDetails: string): never => {\n if (!record.escalated) {\n record.escalated = true;\n spawnStackRebuildState.set(normalizedIssue, record);\n emitActivityEntrySync({\n source: role,\n level: 'error',\n issueId: normalizedIssue,\n message: markerMessage,\n details: errDetails,\n });\n }\n throw new Error(message);\n };\n\n if (record.attempts >= SPAWN_STACK_REBUILD_MAX_ATTEMPTS) {\n if (hostFallbackEligible) {\n fallbackToHost(`rebuild exhausted after ${record.attempts} attempts`);\n return;\n }\n blockWork(\n `agent-spawn-stack-rebuild-exhausted: ${normalizedIssue}`,\n `Workspace docker stack still unhealthy after ${record.attempts} rebuild attempts: ${details}. Manual 'pan workspace rebuild ${normalizedIssue}' or retry with --host --yes needed.`,\n );\n }\n\n if (now - record.lastAttempt < SPAWN_STACK_REBUILD_COOLDOWN_MS) {\n // A rebuild was attempted recently and the stack is still unhealthy —\n // don't hammer `docker compose` every spawn.\n if (hostFallbackEligible) {\n fallbackToHost('rebuild on cooldown');\n return;\n }\n blockWork(`agent-spawn-blocked-stack-unhealthy: ${normalizedIssue}`, details);\n }\n\n record.lastAttempt = now;\n record.attempts += 1;\n spawnStackRebuildState.set(normalizedIssue, record);\n console.log(\n `[agents] Workspace stack for ${normalizedIssue} unhealthy (${details}) — rebuilding ` +\n `before spawn (attempt ${record.attempts}/${SPAWN_STACK_REBUILD_MAX_ATTEMPTS})`,\n );\n\n const { rebuildWorkspaceStack } = await import('../workspace/rebuild-stack.js');\n const result = await Effect.runPromise(\n rebuildWorkspaceStack(issueId, {\n onProgress: (m) => console.log(`[agents] ${normalizedIssue} stack rebuild: ${m}`),\n }),\n ).catch((err: unknown) => ({ success: false as const, error: err instanceof Error ? err.message : String(err) }));\n\n if (result.success) {\n spawnStackRebuildState.delete(normalizedIssue);\n console.log(`[agents] Workspace stack for ${normalizedIssue} rebuilt — proceeding with spawn`);\n return;\n }\n\n console.warn(`[agents] Workspace stack rebuild failed for ${normalizedIssue}: ${result.error}`);\n if (hostFallbackEligible) {\n fallbackToHost(`rebuild failed: ${result.error ?? 'unknown'}`);\n return;\n }\n blockWork(`agent-spawn-stack-rebuild-failed: ${normalizedIssue}`, result.error ?? details);\n}\n","/**\n * Agent CV (Work History) System\n *\n * Tracks agent performance over time to enable capability-based routing.\n */\n\nimport { Effect } from 'effect';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { AGENTS_DIR } from './paths.js';\nimport { FsError } from './errors.js';\n\nexport interface WorkEntry {\n issueId: string;\n startedAt: string;\n completedAt?: string;\n outcome: 'success' | 'failed' | 'abandoned' | 'in_progress';\n duration?: number; // minutes\n skills?: string[];\n failureReason?: string;\n commits?: number;\n linesChanged?: number;\n}\n\nexport interface AgentCV {\n agentId: string;\n createdAt: string;\n lastActive: string;\n runtime: string;\n model: string;\n stats: {\n totalIssues: number;\n successCount: number;\n failureCount: number;\n abandonedCount: number;\n avgDuration: number; // minutes\n successRate: number; // 0-1\n };\n skillsUsed: string[];\n recentWork: WorkEntry[];\n}\n\nfunction getCVFile(agentId: string): string {\n return join(AGENTS_DIR, agentId, 'cv.json');\n}\n\n/**\n * Read an agent's CV without creating one.\n */\nexport function readAgentCVSync(agentId: string): AgentCV | null {\n const cvFile = getCVFile(agentId);\n\n if (!existsSync(cvFile)) return null;\n\n try {\n return JSON.parse(readFileSync(cvFile, 'utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Get or create an agent's CV\n */\nexport function getAgentCVSync(agentId: string): AgentCV {\n const existing = readAgentCVSync(agentId);\n if (existing) return existing;\n\n // Create new CV\n const cv: AgentCV = {\n agentId,\n createdAt: new Date().toISOString(),\n lastActive: new Date().toISOString(),\n runtime: 'claude',\n model: 'sonnet',\n stats: {\n totalIssues: 0,\n successCount: 0,\n failureCount: 0,\n abandonedCount: 0,\n avgDuration: 0,\n successRate: 0,\n },\n skillsUsed: [],\n recentWork: [],\n };\n\n saveAgentCVSync(cv);\n return cv;\n}\n\n/**\n * Save an agent's CV\n */\nexport function saveAgentCVSync(cv: AgentCV): void {\n const dir = join(AGENTS_DIR, cv.agentId);\n mkdirSync(dir, { recursive: true });\n writeFileSync(getCVFile(cv.agentId), JSON.stringify(cv, null, 2));\n}\n\n/**\n * Start tracking work for an agent\n */\nexport function startWorkSync(agentId: string, issueId: string, skills?: string[]): void {\n const cv = getAgentCVSync(agentId);\n\n const entry: WorkEntry = {\n issueId,\n startedAt: new Date().toISOString(),\n outcome: 'in_progress',\n skills,\n };\n\n cv.recentWork.unshift(entry);\n cv.stats.totalIssues++;\n cv.lastActive = new Date().toISOString();\n\n // Track skills\n if (skills) {\n for (const skill of skills) {\n if (!cv.skillsUsed.includes(skill)) {\n cv.skillsUsed.push(skill);\n }\n }\n }\n\n // Keep only last 50 entries\n if (cv.recentWork.length > 50) {\n cv.recentWork = cv.recentWork.slice(0, 50);\n }\n\n saveAgentCVSync(cv);\n}\n\n/**\n * Complete work for an agent\n */\nexport function completeWorkSync(\n agentId: string,\n issueId: string,\n outcome: 'success' | 'failed' | 'abandoned',\n details?: { commits?: number; linesChanged?: number; failureReason?: string }\n): void {\n const cv = getAgentCVSync(agentId);\n\n // Find the work entry\n const entry = cv.recentWork.find(\n (w) => w.issueId === issueId && w.outcome === 'in_progress'\n );\n\n if (entry) {\n entry.outcome = outcome;\n entry.completedAt = new Date().toISOString();\n entry.duration = Math.round(\n (new Date().getTime() - new Date(entry.startedAt).getTime()) / (1000 * 60)\n );\n if (details?.commits) entry.commits = details.commits;\n if (details?.linesChanged) entry.linesChanged = details.linesChanged;\n if (details?.failureReason) entry.failureReason = details.failureReason;\n }\n\n // Update stats\n if (outcome === 'success') {\n cv.stats.successCount++;\n } else if (outcome === 'failed') {\n cv.stats.failureCount++;\n } else if (outcome === 'abandoned') {\n cv.stats.abandonedCount++;\n }\n\n // Calculate success rate\n const completed = cv.stats.successCount + cv.stats.failureCount + cv.stats.abandonedCount;\n cv.stats.successRate = completed > 0 ? cv.stats.successCount / completed : 0;\n\n // Calculate average duration (only from completed work)\n const completedEntries = cv.recentWork.filter(\n (w) => w.duration !== undefined && w.outcome !== 'in_progress'\n );\n if (completedEntries.length > 0) {\n const totalDuration = completedEntries.reduce((sum, w) => sum + (w.duration || 0), 0);\n cv.stats.avgDuration = Math.round(totalDuration / completedEntries.length);\n }\n\n cv.lastActive = new Date().toISOString();\n saveAgentCVSync(cv);\n}\n\n/**\n * Get agent rankings by success rate\n */\nexport function getAgentRankingsSync(): Array<{\n agentId: string;\n successRate: number;\n totalIssues: number;\n avgDuration: number;\n}> {\n const rankings: Array<{\n agentId: string;\n successRate: number;\n totalIssues: number;\n avgDuration: number;\n }> = [];\n\n if (!existsSync(AGENTS_DIR)) return rankings;\n\n const dirs = readdirSync(AGENTS_DIR, { withFileTypes: true }).filter(\n (d) => d.isDirectory()\n );\n\n for (const dir of dirs) {\n const cv = getAgentCVSync(dir.name);\n if (cv.stats.totalIssues > 0) {\n rankings.push({\n agentId: dir.name,\n successRate: cv.stats.successRate,\n totalIssues: cv.stats.totalIssues,\n avgDuration: cv.stats.avgDuration,\n });\n }\n }\n\n // Sort by success rate, then by total issues\n rankings.sort((a, b) => {\n if (b.successRate !== a.successRate) {\n return b.successRate - a.successRate;\n }\n return b.totalIssues - a.totalIssues;\n });\n\n return rankings;\n}\n\n/**\n * Format CV for display\n */\nexport function formatCVSync(cv: AgentCV): string {\n const lines: string[] = [\n `# Agent CV: ${cv.agentId}`,\n '',\n `Runtime: ${cv.runtime} (${cv.model})`,\n `Created: ${cv.createdAt}`,\n `Last Active: ${cv.lastActive}`,\n '',\n '## Statistics',\n '',\n `- Total Issues: ${cv.stats.totalIssues}`,\n `- Success Rate: ${(cv.stats.successRate * 100).toFixed(1)}%`,\n `- Successes: ${cv.stats.successCount}`,\n `- Failures: ${cv.stats.failureCount}`,\n `- Abandoned: ${cv.stats.abandonedCount}`,\n `- Avg Duration: ${cv.stats.avgDuration} minutes`,\n '',\n ];\n\n if (cv.skillsUsed.length > 0) {\n lines.push('## Skills Used');\n lines.push('');\n lines.push(cv.skillsUsed.join(', '));\n lines.push('');\n }\n\n if (cv.recentWork.length > 0) {\n lines.push('## Recent Work');\n lines.push('');\n\n for (const work of cv.recentWork.slice(0, 10)) {\n const statusIcon = {\n success: '✓',\n failed: '✗',\n abandoned: '⊘',\n in_progress: '●',\n }[work.outcome];\n\n const duration = work.duration ? ` (${work.duration}m)` : '';\n lines.push(`${statusIcon} ${work.issueId}${duration}`);\n\n if (work.failureReason) {\n lines.push(` Reason: ${work.failureReason}`);\n }\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n// CV file IO is sync by design. Read paths return Effect.sync; write paths\n// surface FsError.\n\n/** Load (or create) the CV for an agent. Pure-ish. */\nexport const getAgentCV = (agentId: string): Effect.Effect<AgentCV> =>\n Effect.sync(() => getAgentCVSync(agentId));\n\n/** Persist a CV to disk. */\nexport const saveAgentCV = (cv: AgentCV): Effect.Effect<void, FsError> =>\n Effect.try({\n try: () => saveAgentCVSync(cv),\n catch: (cause) =>\n new FsError({ path: cv.agentId, operation: 'save-agent-cv', cause }),\n });\n\n/** Mark the start of a work item on an agent's CV. */\nexport const startWork = (\n agentId: string,\n issueId: string,\n skills?: string[],\n): Effect.Effect<void, FsError> =>\n Effect.try({\n try: () => startWorkSync(agentId, issueId, skills),\n catch: (cause) =>\n new FsError({ path: agentId, operation: 'cv-start-work', cause }),\n });\n\n/** Mark completion (success / failure / abandoned). */\nexport const completeWork = (\n ...args: Parameters<typeof completeWorkSync>\n): Effect.Effect<void, FsError> =>\n Effect.try({\n try: () => completeWorkSync(...args),\n catch: (cause) =>\n new FsError({ path: args[0], operation: 'cv-complete-work', cause }),\n });\n\n/** Aggregate rankings across all agents. Pure-ish. */\nexport const getAgentRankings = (): Effect.Effect<\n ReturnType<typeof getAgentRankingsSync>\n> => Effect.sync(() => getAgentRankingsSync());\n\n/** Render a CV as text. Pure. */\nexport const formatCV = (cv: AgentCV): Effect.Effect<string> =>\n Effect.sync(() => formatCVSync(cv));\n","import type { HeadAnchor } from '../git-utils.js';\nimport { evaluateWorkspaceAnchorDrift } from '../workspace-anchor-drift.js';\nimport { readVerdictPreservationStatus } from './work-start-verdicts.js';\n\nexport type PipelineVerdictPreservationDecision =\n | { preserve: true; reason: string; refreshedAnchor?: HeadAnchor }\n | { preserve: false; reason: string };\n\n/** Decide whether a fresh work-agent session may keep verdicts earned by the current code. */\nexport async function shouldPreservePipelineVerdicts(\n issueId: string,\n workspacePath: string,\n): Promise<PipelineVerdictPreservationDecision> {\n try {\n const status = readVerdictPreservationStatus(issueId);\n if (!status) return { preserve: false, reason: 'no pipeline verdicts are recorded' };\n if (!status.reviewedAtCommit) {\n return { preserve: false, reason: 'the review verdict has no commit anchor' };\n }\n if (status.reviewStatus !== 'passed' && status.reviewStatus !== 'skipped') {\n return { preserve: false, reason: `review is ${status.reviewStatus}` };\n }\n if (status.testStatus !== 'passed') {\n return { preserve: false, reason: `tests are ${status.testStatus}` };\n }\n if (status.verificationStatus !== 'passed') {\n return { preserve: false, reason: `verification is ${status.verificationStatus ?? 'missing'}` };\n }\n\n const verdict = await evaluateWorkspaceAnchorDrift(\n issueId,\n workspacePath,\n status.reviewedAtCommit,\n );\n switch (verdict.kind) {\n case 'current':\n return { preserve: true, reason: 'workspace HEAD matches the reviewed commit anchor' };\n case 'benign':\n return {\n preserve: true,\n reason: 'workspace HEAD moved without changing the reviewed code',\n refreshedAnchor: verdict.currentAnchor,\n };\n case 'drifted':\n return { preserve: false, reason: 'workspace code changed after review' };\n case 'unreadable':\n return { preserve: false, reason: 'the current workspace commit anchor is unreadable' };\n }\n } catch (error) {\n return {\n preserve: false,\n reason: `verdict preservation check failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n","import { open, readFile, stat } from 'fs/promises';\nimport { readFileSync, statSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { Effect, Data } from 'effect';\nimport { decodeJwtPayload, getCliproxyAuthDir, getCliproxyLogPath } from './cliproxy.js';\nimport { getProviderForModelSync } from './providers.js';\n\n/**\n * Which store a codex auth status came from (PAN-2285). 'native' = the codex\n * CLI's own ~/.codex/auth.json (used by codex-harness agents); 'cliproxy' =\n * CLIProxy's bridged codex-primary.json (used by gpt-5.x under claude-code).\n */\nexport type CodexAuthSource = 'native' | 'cliproxy';\n\n/** Wrapper error for Codex auth probing — preserves the underlying cause. */\nexport class CodexAuthCheckError extends Data.TaggedError('CodexAuthCheckError')<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\nexport interface CodexAuthValid {\n status: 'valid';\n email: string;\n expiresAt: string;\n source?: CodexAuthSource;\n}\n\nexport interface CodexAuthExpired {\n status: 'expired';\n email: string;\n expiresAt: string;\n source?: CodexAuthSource;\n}\n\nexport interface CodexAuthBurned {\n status: 'burned';\n email: string;\n expiresAt: string;\n source?: CodexAuthSource;\n /** Agent ids whose live pane showed the revoked-token burn markers (PAN-2285). */\n affectedAgents?: string[];\n}\n\nexport interface CodexAuthMissing {\n status: 'missing';\n source?: CodexAuthSource;\n}\n\nexport interface CodexAuthUnknown {\n status: 'unknown';\n message?: string;\n source?: CodexAuthSource;\n}\n\nexport type CodexAuthStatus = CodexAuthValid | CodexAuthExpired | CodexAuthBurned | CodexAuthMissing | CodexAuthUnknown;\n\ninterface CheckCodexAuthOptions {\n ignoreBurnBefore?: number;\n agentStates?: ReadonlyArray<CodexAuthBurnFlagState & { id: string }>;\n}\n\ninterface CliproxyCodexCredentials {\n access_token?: string;\n email?: string;\n type?: string;\n}// ─── Native ~/.codex/auth.json store (PAN-2285) ────────────────────────────────\n\n/** Absolute path to the codex CLI's own global credential file. */\nexport function getNativeCodexAuthPath(): string {\n return join(homedir(), '.codex', 'auth.json');\n}\n\ninterface NativeCodexAuthFile {\n OPENAI_API_KEY?: unknown;\n last_refresh?: unknown;\n tokens?: {\n access_token?: unknown;\n id_token?: unknown;\n };\n}\n\nexport interface NativeCodexAuthResult {\n status: 'valid' | 'expired' | 'missing' | 'unknown';\n email?: string;\n expiresAt?: string;\n lastRefresh?: string;\n}\n\n/**\n * Pure classifier for the native store's raw JSON (PAN-2285). Decodes the\n * access_token JWT `exp` to decide valid/expired; reports missing when the file\n * is absent (raw === null) and unknown when it is malformed. API-key mode\n * (OPENAI_API_KEY set, no OAuth tokens) is a valid auth state. Exported for\n * testing with fabricated JWTs.\n */\nexport function classifyNativeCodexAuth(raw: string | null, now: number = Date.now()): NativeCodexAuthResult {\n if (raw === null) return { status: 'missing' };\n\n let parsed: NativeCodexAuthFile;\n try {\n parsed = JSON.parse(raw) as NativeCodexAuthFile;\n } catch {\n return { status: 'unknown' };\n }\n\n const lastRefresh = typeof parsed.last_refresh === 'string' ? parsed.last_refresh : undefined;\n const accessToken = typeof parsed.tokens?.access_token === 'string' ? parsed.tokens.access_token : null;\n\n if (!accessToken) {\n // No OAuth token; codex may be in API-key mode (a valid, non-expiring state).\n if (typeof parsed.OPENAI_API_KEY === 'string' && parsed.OPENAI_API_KEY) {\n return { status: 'valid', lastRefresh };\n }\n return { status: 'unknown', lastRefresh };\n }\n\n const idToken = typeof parsed.tokens?.id_token === 'string' ? parsed.tokens.id_token : null;\n const idClaims = idToken ? decodeJwtPayload(idToken) : null;\n const email = idClaims && typeof idClaims.email === 'string' ? idClaims.email : undefined;\n\n const accessClaims = decodeJwtPayload(accessToken);\n const expSec = accessClaims && typeof accessClaims.exp === 'number' ? accessClaims.exp : null;\n if (expSec === null) return { status: 'unknown', email, lastRefresh };\n\n const expiresAt = new Date(expSec * 1000).toISOString();\n if (expSec * 1000 <= now) return { status: 'expired', email, expiresAt, lastRefresh };\n return { status: 'valid', email, expiresAt, lastRefresh };\n}\n\n/** Async native-store probe used by the dashboard status endpoint. */\nasync function probeNativeCodexAuth(now: number = Date.now()): Promise<NativeCodexAuthResult & { mtimeMs: number }> {\n const path = getNativeCodexAuthPath();\n let raw: string | null;\n let mtimeMs = 0;\n try {\n raw = await readFile(path, 'utf8');\n mtimeMs = (await stat(path)).mtimeMs;\n } catch {\n raw = null;\n }\n return { ...classifyNativeCodexAuth(raw, now), mtimeMs };\n}\n\n/** Sync native-store probe used by the spawn gate. */\nexport function probeNativeCodexAuthSync(now: number = Date.now()): NativeCodexAuthResult {\n let raw: string | null;\n try {\n raw = readFileSync(getNativeCodexAuthPath(), 'utf8');\n } catch {\n raw = null;\n }\n return classifyNativeCodexAuth(raw, now);\n}\n\nfunction nativeCodexAuthMtimeSync(): number {\n try {\n return statSync(getNativeCodexAuthPath()).mtimeMs;\n } catch {\n return 0;\n }\n}\n\n// ─── Pane-burn flag, persisted in agent state (PAN-2285) ───────────────────────\n//\n// A revoked refresh token is invisible statically: the codex CLI keeps a valid\n// (unexpired) access-token JWT while its refresh token is dead, and the failure\n// never lands in the rollout JSONL — only in the live TUI pane. The deacon pane\n// patrols grep for the burn markers and flag the agent; the status endpoint and\n// spawn gate consult the flags so the CodexAuthBanner fires and new codex spawns\n// are refused.\n//\n// CRITICAL: the writers (deacon patrols) run in the deacon CHILD process while\n// the readers (the /api/settings/codex-auth endpoint and the spawn gate) run in\n// the dashboard server process — module memory does NOT cross that boundary. The\n// flag is therefore persisted in the agent's own state (troubled +\n// lastFailureReason prefixed `codex-auth-burned[<ISO flag time>]`), written\n// through saveAgentStateSync — which mirrors into the shared SQLite agents\n// table — and read back through the agents-table door\n// (listOverdeckAgentStatesSync). No new store, no in-memory registry.\n\n/**\n * The revoked-token markers that appear in a burned codex agent's pane.\n *\n * The first three are the NATIVE codex CLI's own error text. PAN-3528: a\n * gpt-5.x agent on the claude-code harness never emits them — CLIProxy absorbs\n * the upstream 401 and re-surfaces it as a 503 that reads like a transient\n * server hiccup (\"usually temporary — try again in a moment\"), so the agent\n * retries against a permanently dead credential while the pane detector sees\n * nothing. The last two are that CLIProxy signature; `no auth available\n * (providers=codex` means CLIProxy has disabled the codex provider outright,\n * which is the same dead-credential condition as a native revoke.\n */\nconst CODEX_AUTH_BURN_MARKERS = [\n 'could not be refreshed because your refresh token was revoked',\n 'token_invalidated',\n 'token_revoked',\n 'auth_unavailable',\n 'no auth available (providers=codex',\n];\n\n/**\n * Pure classifier over a codex agent's pane tail (PAN-2285). True when the pane\n * shows the revoked/invalidated refresh-token error. Exported so it is testable\n * without tmux.\n */\nexport function paneShowsCodexAuthBurn(paneText: string): boolean {\n return CODEX_AUTH_BURN_MARKERS.some((marker) => paneText.includes(marker));\n}\n\n/**\n * Is this agent's traffic authenticated by the codex credential family (PAN-3528)?\n *\n * Two routes share one credential: the native `codex` harness reads\n * ~/.codex/auth.json directly, and an openai-provider model under any other\n * harness reaches OpenAI through CLIProxy, which serves the SAME token bridged\n * into codex-primary.json. A revoked refresh token kills both. The pane patrols\n * used to check burn markers only for `harness === 'codex'`, so the CLIProxy\n * route — the standing routing for GPT models on machines that set\n * `openai.harness: claude-code` — was never scanned at all.\n *\n * An unset or unregistered model resolves to \"not codex-routed\" rather than\n * throwing: this runs inside deacon patrols that sweep every tmux session, and\n * one stale agent row must not kill the sweep.\n */\nexport function isCodexAuthRouted(harness: string | undefined, model: string | undefined): boolean {\n if (harness === 'codex') return true;\n if (!model) return false;\n try {\n return getProviderForModelSync(model).name === 'openai';\n } catch {\n return false;\n }\n}\n\n/** lastFailureReason prefix that marks an agent as codex-auth-burned. */\nexport const CODEX_AUTH_BURNED_REASON_PREFIX = 'codex-auth-burned';\n\n/**\n * Minimal structural slice of AgentState the burn flag lives in. Declared\n * structurally (rather than importing AgentState) so the flag helpers stay pure\n * and this module adds no import edge toward the agents barrel (which exports\n * spawn.ts — a module that imports this one).\n */\nexport interface CodexAuthBurnFlagState {\n troubled?: boolean;\n troubledAt?: string;\n lastFailureReason?: string;\n lastFailureAt?: string;\n}\n\n/**\n * Mark an agent state as codex-auth-burned (PAN-2285). Pure mutation — the\n * caller persists via the agent-state write door (saveAgentStateSync), which\n * mirrors into the shared agents table so the flag crosses the deacon/server\n * process boundary. Returns true only when the state was newly flagged\n * (idempotent: an already-flagged state is left untouched), so callers can emit\n * a single operator notice instead of one per patrol tick. The flag time is\n * embedded in the reason (`codex-auth-burned[<ISO>]`) because the agents table\n * has no dedicated column for it and `troubledAt` may predate the burn when the\n * agent was already troubled for another reason.\n */\nexport function applyCodexAuthBurnFlag(state: CodexAuthBurnFlagState, nowMs: number = Date.now()): boolean {\n if (state.troubled && state.lastFailureReason?.startsWith(CODEX_AUTH_BURNED_REASON_PREFIX)) {\n return false;\n }\n const nowIso = new Date(nowMs).toISOString();\n state.troubled = true;\n if (!state.troubledAt) state.troubledAt = nowIso;\n state.lastFailureReason =\n `${CODEX_AUTH_BURNED_REASON_PREFIX}[${nowIso}]: Codex refresh token was revoked — ` +\n 're-authenticate (dashboard Codex-auth banner has a Re-authenticate button, or run `codex login`)';\n state.lastFailureAt = nowIso;\n return true;\n}\n\n/**\n * When (epoch ms) a state was flagged codex-auth-burned, or null if it is not\n * currently flagged. Requires the troubled gate to still be set — `pan\n * untroubled` / resume clears troubled and the failure fields, which retires the\n * flag. Parses the ISO stamp embedded in the reason; falls back to `troubledAt`\n * for flags written by builds that predate the embedded stamp.\n */\nexport function codexAuthBurnFlaggedAtMs(state: CodexAuthBurnFlagState): number | null {\n if (!state.troubled) return null;\n const reason = state.lastFailureReason;\n if (!reason?.startsWith(CODEX_AUTH_BURNED_REASON_PREFIX)) return null;\n const embedded = reason.match(/^codex-auth-burned\\[([^\\]]+)\\]/)?.[1];\n const fromReason = embedded ? Date.parse(embedded) : Number.NaN;\n if (Number.isFinite(fromReason)) return fromReason;\n const fromTroubledAt = state.troubledAt ? Date.parse(state.troubledAt) : Number.NaN;\n return Number.isFinite(fromTroubledAt) ? fromTroubledAt : 0;\n}\n\n/**\n * Pure filter: which of these agent states are burned AND still current? Flags\n * recorded strictly before the native store's last write are stale: a global\n * `codex login` rewrites ~/.codex/auth.json (bumping its mtime) and heals every\n * agent through the shared symlink, so those burns no longer apply (the flag\n * itself is retired when the agent is untroubled/resumed).\n */\nexport function filterCodexAuthBurnedAgentIds(\n states: ReadonlyArray<CodexAuthBurnFlagState & { id: string }>,\n nativeMtimeMs: number,\n): string[] {\n const active: string[] = [];\n for (const state of states) {\n const flaggedAt = codexAuthBurnFlaggedAtMs(state);\n if (flaggedAt !== null && flaggedAt >= nativeMtimeMs) active.push(state.id);\n }\n return active;\n}\n\n/**\n * Agents currently flagged codex-auth-burned. Callers supply states read through\n * the agent resolver so this pure auth module does not create a spawn-time cycle.\n */\nexport function listCodexAuthBurnedAgentsSync(\n nativeMtimeMs: number,\n states: ReadonlyArray<CodexAuthBurnFlagState & { id: string }>,\n): string[] {\n return filterCodexAuthBurnedAgentIds(states, nativeMtimeMs);\n}\n\n/** Sync convenience for the spawn gate: are any agents still burned right now? */\nexport function hasActiveBurnedCodexAgentsSync(\n states: ReadonlyArray<CodexAuthBurnFlagState & { id: string }>,\n): boolean {\n return listCodexAuthBurnedAgentsSync(nativeCodexAuthMtimeSync(), states).length > 0;\n}\n\n// ─── Combined status (native ⊕ cliproxy ⊕ pane-burn) ───────────────────────────\n\nconst CODEX_AUTH_SEVERITY: Record<CodexAuthStatus['status'], number> = {\n burned: 4,\n expired: 3,\n valid: 2,\n missing: 1,\n unknown: 0,\n};\n\nfunction nativeToStatus(native: NativeCodexAuthResult): CodexAuthStatus {\n switch (native.status) {\n case 'valid':\n return { status: 'valid', email: native.email ?? '', expiresAt: native.expiresAt ?? '', source: 'native' };\n case 'expired':\n return { status: 'expired', email: native.email ?? '', expiresAt: native.expiresAt ?? '', source: 'native' };\n case 'missing':\n return { status: 'missing', source: 'native' };\n case 'unknown':\n return { status: 'unknown', source: 'native' };\n }\n}\n\nfunction withSource(status: CodexAuthStatus, source: CodexAuthSource): CodexAuthStatus {\n return { ...status, source };\n}\n\n/**\n * Merge the native and cliproxy statuses into the single worst state the banner\n * should react to (burned > expired > valid > missing > unknown). Both stores\n * are healed by the same `codex login`, so surfacing the more severe one is\n * correct and its remedy is identical. Exported for testing.\n */\nexport function combineCodexAuthStatuses(\n native: CodexAuthStatus,\n cliproxy: CodexAuthStatus,\n): CodexAuthStatus {\n const nativeSev = CODEX_AUTH_SEVERITY[native.status];\n const cliproxySev = CODEX_AUTH_SEVERITY[cliproxy.status];\n // Tie → prefer native (PAN-2285 is about the native codex-harness store).\n return cliproxySev > nativeSev ? withSource(cliproxy, 'cliproxy') : native;\n}\n\nasync function checkCodexAuthStatusPromise(options: CheckCodexAuthOptions = {}): Promise<CodexAuthStatus> {\n const now = Date.now();\n const native = await probeNativeCodexAuth(now);\n const cliproxy = await checkCliproxyCodexAuthStatusPromise(options);\n\n // Live pane-burn is the strongest signal for the native store: the JWT can\n // still be unexpired while the refresh token is revoked, so it beats the\n // static probe. Flags are read from the shared agents table — the deacon\n // child process wrote them there via saveAgentStateSync.\n const activeBurned = listCodexAuthBurnedAgentsSync(native.mtimeMs, options.agentStates ?? []);\n if (activeBurned.length > 0) {\n return {\n status: 'burned',\n email: native.email ?? '',\n expiresAt: native.expiresAt ?? '',\n source: 'native',\n affectedAgents: activeBurned,\n };\n }\n\n return combineCodexAuthStatuses(nativeToStatus(native), cliproxy);\n}\n\n/**\n * Spawn gate (PAN-2285): refuse to launch a NEW codex-harness agent while the\n * native codex auth is unusable — missing/expired file or any agent currently\n * burned (revoked refresh token shared across the family). Resumes are exempt:\n * the symlink migration in initCodexHome heals a stale home on relaunch. Throws\n * a clear, remedy-naming error, mirroring the harness-policy ToS gate.\n */\nexport function assertCodexNativeAuthForSpawn(\n harness: string | undefined,\n agentStates: ReadonlyArray<CodexAuthBurnFlagState & { id: string }>,\n): void {\n if (harness !== 'codex') return;\n const native = probeNativeCodexAuthSync();\n const reason =\n native.status === 'missing'\n ? 'not signed in (~/.codex/auth.json is missing)'\n : native.status === 'expired'\n ? 'expired (~/.codex/auth.json access token has lapsed)'\n : hasActiveBurnedCodexAgentsSync(agentStates)\n ? 'revoked (a running codex agent hit a revoked refresh token — the shared token family is dead)'\n : null;\n if (reason === null) return;\n throw new Error(\n `Cannot spawn a Codex agent: native Codex authentication is ${reason}. ` +\n 'Run `codex login` on the host (or click Re-authenticate in the dashboard Codex-auth banner), then retry.',\n );\n}\n\nasync function checkCliproxyCodexAuthStatusPromise(options: CheckCodexAuthOptions = {}): Promise<CodexAuthStatus> {\n const credPath = join(getCliproxyAuthDir(), 'codex-primary.json');\n\n let raw: string;\n let credMtimeMs: number | null = null;\n try {\n raw = await readFile(credPath, 'utf8');\n credMtimeMs = (await stat(credPath)).mtimeMs;\n } catch {\n return { status: 'missing' };\n }\n\n let creds: CliproxyCodexCredentials;\n try {\n creds = JSON.parse(raw) as CliproxyCodexCredentials;\n } catch {\n return { status: 'unknown', message: 'Malformed credential file' };\n }\n\n const accessToken = typeof creds.access_token === 'string' ? creds.access_token : null;\n if (!accessToken) {\n return { status: 'unknown', message: 'Missing access_token in credential file' };\n }\n\n const claims = decodeJwtPayload(accessToken);\n if (!claims) {\n return { status: 'unknown', message: 'Unable to decode access_token' };\n }\n\n const expSec = typeof claims.exp === 'number' ? claims.exp : null;\n if (expSec === null) {\n return { status: 'unknown', message: 'Missing exp claim in access_token' };\n }\n\n const email = typeof creds.email === 'string' ? creds.email : '';\n const expiresAt = new Date(expSec * 1000).toISOString();\n\n if (expSec * 1000 <= Date.now()) {\n return { status: 'expired', email, expiresAt };\n }\n\n const jwtStatus: CodexAuthStatus = { status: 'valid', email, expiresAt };\n // The credential file's own write time is the authoritative \"you last logged\n // in at\" reference: any auth failure logged AFTER it means the CURRENT token\n // is dead. An explicit caller option (the re-auth flow's session.createdAt)\n // still wins. Without this, callers that pass no option fell back to the crude\n // 1h staleness window and reported a burned token as \"valid\" once the burn\n // line aged out — exactly the gap that let gpt-5.5 agents spawn into 503s.\n const effectiveOptions: CheckCodexAuthOptions = {\n ignoreBurnBefore: options.ignoreBurnBefore ?? credMtimeMs ?? undefined,\n };\n return await applyBurnedTokenOverride(jwtStatus, email, expiresAt, effectiveOptions);\n}\n\n/** Read the trailing window of the cliproxy log for burn/failure analysis. */\nasync function readLogTail(path: string): Promise<string> {\n const TAIL_BYTES = 128 * 1024;\n const file = await open(path, 'r');\n try {\n const stat = await file.stat();\n const length = Math.min(stat.size, TAIL_BYTES);\n const buffer = Buffer.alloc(length);\n await file.read(buffer, 0, length, stat.size - length);\n return buffer.toString('utf8');\n } finally {\n await file.close();\n }\n}\n\nasync function applyBurnedTokenOverride(\n baseStatus: CodexAuthStatus,\n email: string,\n expiresAt: string,\n options: CheckCodexAuthOptions,\n): Promise<CodexAuthStatus> {\n let logRaw: string;\n try {\n logRaw = await readLogTail(getCliproxyLogPath());\n } catch {\n return baseStatus;\n }\n return evaluateBurnedFromLog(logRaw, baseStatus, email, expiresAt, options);\n}\n\nconst BURN_STALENESS_MS = 60 * 60 * 1000;\n\n/**\n * Pure decision: given the cliproxy log tail, decide whether a JWT-valid token\n * is actually burned. Exported for regression testing (PAN-1584).\n *\n * \"Auth failure\" evidence = either a refresh-token burn line\n * (`refresh token has already been used`, `refresh_token_reused`, or a\n * non-retryable `token refresh failed with status 401`) OR a `503` on `/v1/messages` /\n * `/v1/chat/completions` (the live symptom the agent hits once cliproxy disables\n * the provider — and which keeps appearing after the burn line stops, since a\n * disabled provider no longer attempts refreshes). \"Success\" = a `200` on those\n * same paths (proves the auth path works right now).\n *\n * Decision:\n * - No failure evidence → trust base status.\n * - Success AFTER the last failure → trust base status (recovered).\n * - Failure AFTER the credential's last write (ignoreBurnBefore) → BURNED,\n * regardless of age. The credential write time is when you last logged in;\n * a failure after it means the current token is dead. This is authoritative\n * and is the fix for the bug where a >1h-stale burn line was dismissed as\n * \"valid\" during a quiet period even though the token was dead (PAN-1584).\n * - Failure BEFORE that cutoff → trust base status (re-authed since).\n * - No cutoff available → fall back to the staleness backstop:\n * burned only if the last failure is within BURN_STALENESS_MS.\n *\n * We intentionally don't probe cliproxy with HTTP — `GET /v1/models` always\n * 401s (it needs real OAuth, not the local key) and would generate spurious log\n * lines on every dashboard load.\n */\nexport function evaluateBurnedFromLog(\n logRaw: string,\n baseStatus: CodexAuthStatus,\n email: string,\n expiresAt: string,\n options: CheckCodexAuthOptions & { now?: number } = {},\n): CodexAuthStatus {\n const now = options.now ?? Date.now();\n // Scan a wider window than the original 50 lines so a quiet recovery period\n // can still surface a later success.\n const lines = logRaw.split('\\n').slice(-500);\n\n // PAN-913 matched `refresh token has already been used`; PAN-1455 adds\n // `refresh_token_reused`. PAN-3528 adds the openai_auth.go line those two sit\n // under: a non-retryable 401 on refresh is the definitive \"this credential is\n // dead\" signal, it covers revocations whose JSON body says neither of the\n // above, and — unlike the continuation lines — it carries its own timestamp.\n const isBurnLine = (l: string) =>\n l.includes('refresh token has already been used')\n || l.includes('refresh_token_reused')\n || l.includes('token refresh failed with status 401');\n const isAuthFailure503 = (l: string) =>\n /\\b503 \\|/.test(l) && /POST\\s+\"\\/v1\\/(messages|chat\\/completions)/.test(l);\n const isSuccess = (l: string) =>\n /\\b200 \\|/.test(l) && /POST\\s+\"\\/v1\\/(messages|chat\\/completions)/.test(l);\n\n // The bracketed timestamp lives on the gin_logger/openai_auth line; burn\n // messages sit on a JSON continuation line with no prefix, so scan back a few\n // lines for the nearest one.\n //\n // PAN-3528: cliproxy stamps these in LOCAL time, not UTC. Parsing them with a\n // trailing `Z` shifted every failure by the host's UTC offset — on a UTC-4\n // host that made a burn look 4 hours OLDER than it was, so it fell before the\n // credential-write cutoff below and the endpoint reported a dead token as\n // \"valid\" while every gpt-5.6 agent 503'd. Omitting the offset makes\n // Date.parse interpret the stamp in local time, which is what it is.\n const timestampAt = (idx: number): number | null => {\n for (let j = idx; j >= Math.max(0, idx - 10); j--) {\n const m = lines[j]?.match(/^\\[(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2})\\]/);\n if (m) {\n const t = Date.parse(`${m[1]}T${m[2]}`);\n if (Number.isFinite(t)) return t;\n }\n }\n return null;\n };\n\n let lastFailureIdx = -1;\n let lastFailureTimestamp: number | null = null;\n let lastSuccessIdx = -1;\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i] ?? '';\n if (lastFailureIdx < 0 && (isBurnLine(line) || isAuthFailure503(line))) {\n lastFailureIdx = i;\n lastFailureTimestamp = timestampAt(i);\n }\n if (lastSuccessIdx < 0 && isSuccess(line)) {\n lastSuccessIdx = i;\n }\n if (lastFailureIdx >= 0 && lastSuccessIdx >= 0) break;\n }\n\n // No failure evidence at all → trust the JWT-based status.\n if (lastFailureIdx < 0) return baseStatus;\n\n // A successful LLM call came AFTER the failure → auth path works again.\n if (lastSuccessIdx > lastFailureIdx) return baseStatus;\n\n // Authoritative path: we know when the credential was last written. A failure\n // after that write means the CURRENT token is dead — flag burned no matter how\n // old the line is. (Pad by 1000ms: burn lines carry second-precision stamps, so\n // a credential written in the same second as the failure shouldn't look newer.)\n if (options.ignoreBurnBefore !== undefined) {\n if (lastFailureTimestamp !== null && lastFailureTimestamp + 1000 <= options.ignoreBurnBefore) {\n return baseStatus;\n }\n return { status: 'burned', email, expiresAt };\n }\n\n // No credential-write reference → staleness backstop only.\n if (lastFailureTimestamp !== null && now - lastFailureTimestamp > BURN_STALENESS_MS) {\n return baseStatus;\n }\n\n return { status: 'burned', email, expiresAt };\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect-native checkCodexAuthStatus. The Promise version is designed to\n * swallow all I/O errors and report the auth state through the typed status\n * union. The Effect variant wraps that to make it composable; it only fails\n * with CodexAuthCheckError if the underlying call itself throws unexpectedly\n * (i.e., not from the documented \"missing/unknown\" branches).\n */\nexport const checkCodexAuthStatus = (\n options: CheckCodexAuthOptions = {},\n): Effect.Effect<CodexAuthStatus, CodexAuthCheckError> =>\n Effect.tryPromise({\n try: () => checkCodexAuthStatusPromise(options),\n catch: (cause) =>\n new CodexAuthCheckError({\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n });\n","import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { randomBytes } from 'node:crypto';\nimport { join } from 'node:path';\nimport { Effect } from 'effect';\nimport { FsError } from './errors.js';\n\nimport { getOverdeckHome } from './paths.js';\n\nexport const BRIDGE_TOKEN_HEADER = 'x-overdeck-bridge-token';\n\nfunction bridgeTokensDir(): string {\n return join(getOverdeckHome(), 'bridge-tokens');\n}\n\nexport function getBridgeTokenPath(agentId: string): string {\n return join(bridgeTokensDir(), `${agentId}.token`);\n}\n\nexport function readBridgeTokenSync(agentId: string): string | null {\n const path = getBridgeTokenPath(agentId);\n if (!existsSync(path)) return null;\n try {\n const value = readFileSync(path, 'utf8').trim();\n return value.length > 0 ? value : null;\n } catch {\n return null;\n }\n}\n\nexport function writeBridgeTokenSync(agentId: string): string {\n const dir = bridgeTokensDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n const token = randomBytes(32).toString('hex');\n const path = getBridgeTokenPath(agentId);\n writeFileSync(path, `${token}\\n`, { mode: 0o600 });\n try {\n chmodSync(path, 0o600);\n } catch {\n // best effort\n }\n return token;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Read the bridge token for an agent. Returns `null` if no token exists.\n * Effect-native variant — never fails (errors are swallowed to null like the Promise version).\n */\nexport const readBridgeToken = (agentId: string): Effect.Effect<string | null> =>\n Effect.sync(() => readBridgeTokenSync(agentId));\n\n/**\n * Generate and persist a new bridge token. Returns the new token.\n * Effect-native variant — fails with FsError if the write cannot be persisted.\n */\nexport const writeBridgeToken = (agentId: string): Effect.Effect<string, FsError> =>\n Effect.try({\n try: () => writeBridgeTokenSync(agentId),\n catch: (cause) =>\n new FsError({\n path: getBridgeTokenPath(agentId),\n operation: 'writeBridgeToken',\n cause,\n }),\n });\n","/** Explicit first-user-message context transport for the native Kimi CLI. */\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport { claudeSystemPromptFiles } from '../context-layers/launch-sources.js';\nimport { getOverdeckHome } from '../paths.js';\n\nexport const KIMI_CONTEXT_START = '<overdeck-managed-context version=\"1\">';\nexport const KIMI_CONTEXT_END = '</overdeck-managed-context>';\nexport const KIMI_TASK_START = '<overdeck-user-task>';\nexport const KIMI_TASK_END = '</overdeck-user-task>';\n\ninterface KimiContextReceipt {\n version: 1;\n sessions: Record<string, { deliveredAt: string; sha256: string }>;\n}\n\nexport interface PreparedKimiMessage {\n message: string;\n sessionId: string;\n contextIncluded: boolean;\n contextSha256?: string;\n overdeckHome: string;\n}\n\nfunction receiptPath(agentId: string, overdeckHome = getOverdeckHome()): string {\n return join(overdeckHome, 'agents', agentId, 'kimi-context-delivery.json');\n}\n\nfunction readReceipt(agentId: string, overdeckHome = getOverdeckHome()): KimiContextReceipt {\n try {\n const parsed = JSON.parse(readFileSync(receiptPath(agentId, overdeckHome), 'utf8')) as KimiContextReceipt;\n if (parsed.version === 1 && parsed.sessions && typeof parsed.sessions === 'object') return parsed;\n } catch { /* no valid receipt yet */ }\n return { version: 1, sessions: {} };\n}\n\nexport function readManagedKimiSessionId(agentId: string, overdeckHome = getOverdeckHome()): string | null {\n try {\n return readFileSync(join(overdeckHome, 'agents', agentId, 'kimi-session-id'), 'utf8').trim() || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Wait for the spawn path to persist the captured kimi-session-id.\n *\n * A conversation's tmux session exists (and the composer enables) before\n * `spawnConversationSession` finishes diffing Kimi's session bucket, so a\n * message sent in that window used to fail with \"captured kimi-session-id is\n * missing\". Poll the pointer file instead of failing on the first miss. Returns\n * null when the id has still not appeared at the deadline — the caller decides\n * how to report that (the spawn may have failed, or Kimi may still be starting).\n */\nexport async function waitForManagedKimiSessionId(\n agentId: string,\n options: { timeoutMs?: number; pollMs?: number; overdeckHome?: string } = {},\n): Promise<string | null> {\n const timeoutMs = options.timeoutMs ?? 15_000;\n const pollMs = options.pollMs ?? 250;\n const overdeckHome = options.overdeckHome ?? getOverdeckHome();\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const sessionId = readManagedKimiSessionId(agentId, overdeckHome);\n if (sessionId) return sessionId;\n if (Date.now() >= deadline) return null;\n await new Promise<void>((resolve) => setTimeout(resolve, Math.min(pollMs, Math.max(0, deadline - Date.now()))));\n }\n}\n\n/** Pure formatter: context first, original task afterward, with hard delimiters. */\nexport function buildKimiContextEnvelope(contextFiles: readonly string[], taskMessage: string): {\n message: string;\n sha256: string;\n} {\n const sections = contextFiles.flatMap((file) => {\n const content = readFileSync(file, 'utf8').trim();\n return content ? [`## Context source: ${file}\\n\\n${content}`] : [];\n });\n if (sections.length === 0) {\n throw new Error('Managed Kimi launch blocked: required launch context was empty or unreadable.');\n }\n const context = sections.join('\\n\\n---\\n\\n');\n const sha256 = createHash('sha256').update(context).digest('hex');\n const envelope = [\n KIMI_CONTEXT_START,\n 'Treat this delimited block as managed launch context for this session. Apply it throughout the session.',\n '',\n context,\n KIMI_CONTEXT_END,\n ];\n if (taskMessage.length > 0) {\n envelope.push('', KIMI_TASK_START, taskMessage, KIMI_TASK_END);\n }\n return { message: envelope.join('\\n'), sha256 };\n}\n\n/**\n * Add context only if this exact native Kimi session has not received it.\n * A resume/recovery of the same session therefore keeps task messages clean;\n * a fresh session receives the envelope on its first visible user message.\n */\nexport async function prepareKimiMessage(\n agentId: string,\n workspace: string,\n taskMessage: string,\n options: { sessionId?: string; contextFiles?: string[]; overdeckHome?: string } = {},\n): Promise<PreparedKimiMessage> {\n const overdeckHome = options.overdeckHome ?? getOverdeckHome();\n const sessionId = options.sessionId ?? readManagedKimiSessionId(agentId, overdeckHome);\n if (!sessionId) {\n throw new Error(`Managed Kimi message blocked for ${agentId}: captured kimi-session-id is missing.`);\n }\n if (readReceipt(agentId, overdeckHome).sessions[sessionId]) {\n return { message: taskMessage, sessionId, contextIncluded: false, overdeckHome };\n }\n const contextFiles = options.contextFiles ?? await claudeSystemPromptFiles(workspace, 'kimi-code');\n const envelope = buildKimiContextEnvelope(contextFiles, taskMessage);\n return {\n message: envelope.message,\n sessionId,\n contextIncluded: true,\n contextSha256: envelope.sha256,\n overdeckHome,\n };\n}\n\n/** Persist only after the envelope delivery returned success. */\nexport function markKimiContextDelivered(agentId: string, prepared: PreparedKimiMessage): void {\n if (!prepared.contextIncluded || !prepared.contextSha256) return;\n const path = receiptPath(agentId, prepared.overdeckHome);\n const receipt = readReceipt(agentId, prepared.overdeckHome);\n if (receipt.sessions[prepared.sessionId]?.sha256 === prepared.contextSha256) return;\n receipt.sessions[prepared.sessionId] = {\n deliveredAt: new Date().toISOString(),\n sha256: prepared.contextSha256,\n };\n mkdirSync(dirname(path), { recursive: true });\n const tmp = `${path}.${randomUUID()}.tmp`;\n writeFileSync(tmp, `${JSON.stringify(receipt, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, path);\n}\n","/**\n * Payload-size-aware injection budget shared between the PTY supervisor and\n * its delivery client. This is the single timing source for both sides of the\n * protocol; keeping their waits in one leaf module makes\n * mid-injection abandonment structurally impossible: the client always waits\n * strictly longer than the supervisor's worst-case internal path.\n *\n * This module must remain a leaf (it imports nothing from src/lib) so the\n * dashboard build does not gain a new circular-ESM edge.\n */\n\nexport const INPUT_ECHO_CONFIRM_INTERVAL_MS = 50;\nexport const INPUT_ECHO_CONFIRM_ATTEMPTS = 2;\nexport const INPUT_ECHO_CONFIRM_PREFIX_CHARS = 40;\nexport const INPUT_SUBMIT_CONFIRM_ATTEMPTS = 2;\nexport const INPUT_SUBMIT_CONFIRM_INTERVAL_MS = 500;\nexport const INPUT_PURGE_MAX_CHARS = 262_144;\nexport const SUPERVISOR_CLIENT_MARGIN_MS = 2_000;\nconst INJECTION_OVERHEAD_MS = 3_000; // PTY writes, log appends, scheduler slack\n\n/** Echo window for one attempt: 2.5s floor + 100ms per KB, capped at 15s. */\nexport function echoConfirmTimeoutMs(contentLength: number): number {\n return Math.min(2_500 + Math.ceil(contentLength / 1_024) * 100, 15_000);\n}\n\n/** Pause between confirmed echo and the standalone Enter: 300ms floor + 50ms/KB, capped at 2s. */\nexport function inputSettleMs(contentLength: number): number {\n return Math.min(300 + Math.ceil(contentLength / 1_024) * 50, 2_000);\n}\n\n/** Settle after a purge: 150ms floor + 25ms/KB of erased content, capped at 1s. */\nexport function purgeSettleMs(erasedChars: number): number {\n return Math.min(150 + Math.ceil(erasedChars / 1_024) * 25, 1_000);\n}\n\n/** Worst-case wall time injectPtyMessage can spend before responding. */\nexport function supervisorInjectionBudgetMs(contentLength: number): number {\n const clamped = Math.min(contentLength, INPUT_PURGE_MAX_CHARS);\n return (\n INPUT_ECHO_CONFIRM_ATTEMPTS * echoConfirmTimeoutMs(contentLength) +\n INPUT_ECHO_CONFIRM_ATTEMPTS * purgeSettleMs(clamped) +\n inputSettleMs(contentLength) +\n INPUT_SUBMIT_CONFIRM_ATTEMPTS * INPUT_SUBMIT_CONFIRM_INTERVAL_MS +\n purgeSettleMs(clamped) +\n INJECTION_OVERHEAD_MS\n );\n}\n","import { createReadStream, existsSync } from 'node:fs';\nimport { stat } from 'node:fs/promises';\nimport { sessionFilePath } from './paths.js';\n\nconst DEFAULT_TAIL_BYTES = 512 * 1024;\n\nexport interface TranscriptUserRecordSnapshot {\n readonly sessionFile: string;\n readonly userRecordCount: number;\n readonly fileSize?: number;\n /** Byte offset from which this snapshot was parsed. */\n readonly rangeStartByte?: number;\n /** Byte offset after the last complete JSONL line observed by this snapshot. */\n readonly readOffset?: number;\n readonly lastUserRecord?: {\n readonly lineNumber: number;\n readonly timestamp?: string;\n readonly uuid?: string;\n };\n}\n\nasync function readFileRange(filePath: string, start: number, endExclusive: number): Promise<string> {\n if (endExclusive <= start) return '';\n\n return await new Promise<string>((resolve, reject) => {\n const stream = createReadStream(filePath, { start, end: endExclusive - 1, encoding: 'utf8' });\n let data = '';\n stream.on('data', chunk => { data += chunk; });\n stream.on('end', () => resolve(data));\n stream.on('error', reject);\n });\n}\n\nfunction dropLeadingPartialLine(content: string): string {\n const firstNewline = content.indexOf('\\n');\n if (firstNewline === -1) return '';\n return content.slice(firstNewline + 1);\n}\n\nfunction nextCompleteLineOffset(rangeStartByte: number, rawContent: string, fileSize: number): number {\n if (!rawContent) return fileSize;\n if (rawContent.endsWith('\\n')) return fileSize;\n\n const lastNewline = rawContent.lastIndexOf('\\n');\n if (lastNewline === -1) return rangeStartByte;\n return rangeStartByte + Buffer.byteLength(rawContent.slice(0, lastNewline + 1), 'utf8');\n}\n\nfunction isLandedUserRecord(entry: unknown): boolean {\n if (!entry || typeof entry !== 'object') return false;\n const record = entry as {\n type?: unknown;\n message?: { role?: unknown; content?: unknown };\n };\n if (record.type !== 'user' || record.message?.role !== 'user') return false;\n\n const content = record.message.content;\n if (typeof content === 'string') return content.trim().length > 0;\n if (!Array.isArray(content)) return false;\n\n return content.some((item: unknown) => {\n if (!item || typeof item !== 'object') return false;\n return (item as { type?: unknown }).type !== 'tool_result';\n });\n}\n\nfunction parseLandedUserRecords(content: string): Pick<TranscriptUserRecordSnapshot, 'userRecordCount' | 'lastUserRecord'> {\n let userRecordCount = 0;\n let lastUserRecord: TranscriptUserRecordSnapshot['lastUserRecord'];\n\n const lines = content.split('\\n');\n for (const [index, line] of lines.entries()) {\n if (!line.trim()) continue;\n try {\n const entry = JSON.parse(line) as { timestamp?: unknown; uuid?: unknown };\n if (!isLandedUserRecord(entry)) continue;\n userRecordCount += 1;\n lastUserRecord = {\n lineNumber: index + 1,\n ...(typeof entry.timestamp === 'string' ? { timestamp: entry.timestamp } : {}),\n ...(typeof entry.uuid === 'string' ? { uuid: entry.uuid } : {}),\n };\n } catch {\n // Claude may be appending the JSONL while we read it. Ignore malformed\n // partial lines; the next poll sees the completed record.\n }\n }\n\n return { userRecordCount, ...(lastUserRecord ? { lastUserRecord } : {}) };\n}\n\nexport async function captureTranscriptUserRecordSnapshot(\n workspace: string,\n sessionId: string,\n options: { tailBytes?: number; fromByteOffset?: number } = {},\n): Promise<TranscriptUserRecordSnapshot> {\n const sessionFile = sessionFilePath(workspace, sessionId);\n if (!existsSync(sessionFile)) {\n return { sessionFile, userRecordCount: 0, fileSize: 0, rangeStartByte: 0, readOffset: 0 };\n }\n\n try {\n const fileStat = await stat(sessionFile);\n const fileSize = fileStat.size;\n const rangeStartByte = options.fromByteOffset === undefined\n ? Math.max(0, fileSize - (options.tailBytes ?? DEFAULT_TAIL_BYTES))\n : Math.min(Math.max(0, options.fromByteOffset), fileSize);\n const rawContent = await readFileRange(sessionFile, rangeStartByte, fileSize);\n const content = options.fromByteOffset === undefined && rangeStartByte > 0\n ? dropLeadingPartialLine(rawContent)\n : rawContent;\n const parsed = parseLandedUserRecords(content);\n\n return {\n sessionFile,\n ...parsed,\n fileSize,\n rangeStartByte,\n readOffset: nextCompleteLineOffset(rangeStartByte, rawContent, fileSize),\n };\n } catch {\n return { sessionFile, userRecordCount: 0, fileSize: 0, rangeStartByte: 0, readOffset: 0 };\n }\n}\n\nexport function hasNewTranscriptUserRecord(\n before: TranscriptUserRecordSnapshot,\n after: TranscriptUserRecordSnapshot,\n): boolean {\n if (before.sessionFile !== after.sessionFile) return after.userRecordCount > 0;\n\n if (\n before.readOffset !== undefined &&\n after.rangeStartByte !== undefined &&\n after.rangeStartByte === before.readOffset\n ) {\n return after.userRecordCount > 0;\n }\n\n return after.userRecordCount > before.userRecordCount;\n}\n\nexport interface TranscriptWatchProbe {\n /** A landed user record whose content contains the watched message text. */\n matchedUserRecord: boolean;\n /** Real model turns observed at/after the probe's start offset. */\n realAssistantTurnCount?: number;\n /** compact_boundary records observed at/after the probe's start offset. */\n compactBoundaryCount: number;\n}\n\nconst MATCH_PREFIX_CHARS = 120;\n\nfunction normalizeForContentMatch(value: string): string {\n return value.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * Harness-meta user records the matcher must never treat as the user's own\n * message landing. Compaction itself writes user-type records (the\n * continuation summary, `<command-name>`/`<local-command-*>` entries), and a\n * continuation summary can even quote the watched message — counting any of\n * these as a landing would mask exactly the eaten-by-compaction case the\n * probe exists to detect (PAN-1635 / PAN-1769).\n */\nconst META_USER_CONTENT_PREFIXES = [\n 'This session is being continued',\n '<command-name>',\n '<local-command-',\n 'Caveat: The messages below',\n];\n\nfunction userRecordText(entry: unknown): string | null {\n if (!entry || typeof entry !== 'object') return null;\n const record = entry as { type?: unknown; message?: { role?: unknown; content?: unknown } };\n if (record.type !== 'user' || record.message?.role !== 'user') return null;\n const content = record.message.content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return null;\n return content\n .filter((item): item is { type?: unknown; text?: unknown } => !!item && typeof item === 'object')\n .filter((item) => item.type === 'text' && typeof item.text === 'string')\n .map((item) => item.text as string)\n .join('\\n');\n}\n\nfunction isCompactBoundaryRecord(entry: unknown): boolean {\n if (!entry || typeof entry !== 'object') return false;\n const record = entry as { type?: unknown; subtype?: unknown };\n return record.type === 'system' && record.subtype === 'compact_boundary';\n}\n\nfunction isRealAssistantTurn(entry: unknown): boolean {\n if (!entry || typeof entry !== 'object') return false;\n const record = entry as { type?: unknown; message?: { role?: unknown; model?: unknown } };\n return record.type === 'assistant'\n && record.message?.role === 'assistant'\n && record.message.model !== '<synthetic>';\n}\n\n/**\n * Scan the transcript from `fromByteOffset` for (a) a user record carrying\n * `messageText` and (b) compact boundaries. Backs the eaten-by-compaction\n * watcher: a boundary appearing without the message means Claude Code's\n * submit-time compaction dropped the just-delivered prompt.\n */\nexport async function probeTranscriptSince(\n workspace: string,\n sessionId: string,\n fromByteOffset: number,\n messageText: string,\n): Promise<TranscriptWatchProbe> {\n const needle = normalizeForContentMatch(messageText).slice(0, MATCH_PREFIX_CHARS);\n const sessionFile = sessionFilePath(workspace, sessionId);\n if (!needle || !existsSync(sessionFile)) {\n return { matchedUserRecord: false, realAssistantTurnCount: 0, compactBoundaryCount: 0 };\n }\n\n try {\n const fileStat = await stat(sessionFile);\n const start = Math.min(Math.max(0, fromByteOffset), fileStat.size);\n const content = await readFileRange(sessionFile, start, fileStat.size);\n let matchedUserRecord = false;\n let realAssistantTurnCount = 0;\n let compactBoundaryCount = 0;\n for (const line of content.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const entry = JSON.parse(line) as unknown;\n if (isCompactBoundaryRecord(entry)) {\n compactBoundaryCount += 1;\n continue;\n }\n if (isRealAssistantTurn(entry)) {\n realAssistantTurnCount += 1;\n continue;\n }\n const text = userRecordText(entry);\n if (text === null) continue;\n if (META_USER_CONTENT_PREFIXES.some((prefix) => text.startsWith(prefix))) continue;\n if (normalizeForContentMatch(text).includes(needle)) matchedUserRecord = true;\n } catch {\n // Partial trailing line mid-append; the next probe sees it complete.\n }\n }\n return { matchedUserRecord, realAssistantTurnCount, compactBoundaryCount };\n } catch {\n return { matchedUserRecord: false, realAssistantTurnCount: 0, compactBoundaryCount: 0 };\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { request as httpRequest } from 'node:http';\nimport { join, dirname } from 'path';\nimport { homedir } from 'os';\nimport { Effect } from 'effect';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport { markKimiContextDelivered, prepareKimiMessage, type PreparedKimiMessage } from '../runtimes/kimi-context-envelope.js';\nimport type { AgentState } from '../agents.js';\nimport {\n normalizeAgentId,\n getAgentState,\n saveAgentState,\n getAgentDir,\n waitForPromptReady,\n SESSION_EXITED_BEFORE_KICKOFF,\n} from '../agents.js';\nimport { getAgentRuntimeState } from './runtime-state.js';\nimport { isPaneDead, sendKeys, sessionExists } from '../tmux.js';\nimport { completeKeyedSubmit, sendKeysDedup } from '../tmux-dedup.js';\nimport { BRIDGE_TOKEN_HEADER, readBridgeTokenSync } from '../bridge-token.js';\nimport { PTY_TOKEN_HEADER, readPtyToken } from '../pty-token.js';\nimport {\n SUPERVISOR_CLIENT_MARGIN_MS,\n supervisorInjectionBudgetMs,\n} from '../channels/injection-budget.js';\nimport {\n captureTranscriptUserRecordSnapshot,\n probeTranscriptSince,\n type TranscriptUserRecordSnapshot,\n} from '../transcript-landing.js';\n\nexport type DeliveryResult = {\n ok: boolean;\n path: 'app-server' | 'acp' | 'supervisor' | 'channels' | 'tmux' | 'pi' | 'codex';\n failure?: string;\n /** True when the delivery was suppressed by the keyed dedup record — the\n * side effect already happened on an earlier call with the same key. */\n deduplicated?: boolean;\n};\n\nexport interface DeliverAgentMessageOptions {\n /** Conversation sessions have no AgentState; identify their Kimi context explicitly. */\n kimiContext?: { workspace: string; sessionId?: string };\n /** Runtime-owned precomposition seam; prevents the shared Kimi guard from nesting envelopes. */\n preparedKimiContext?: PreparedKimiMessage;\n /**\n * Idempotency key (PAN-2997). Keyed deliveries are deduplicated by the\n * crash-independent delivery component, not by dashboard-side state: the\n * PTY supervisor reserves the key synchronously before injecting and\n * completes it only after the content-plus-Enter injection succeeds, and\n * the tmux fallback keeps a two-phase (pending/terminal) per-session marker\n * owned by the tmux server across the whole paste-settle-submit\n * transaction. A dashboard crash anywhere in those flows can therefore\n * neither lose nor duplicate a keyed message.\n *\n * Keyed payloads are ONLY routed through those two tiers. The\n * app-server/ACP/Channels tiers acknowledge receipt without enforcing the\n * key, so they are skipped in auto mode and rejected when requested\n * explicitly — a crash after their visible side effect but before the\n * caller's acknowledgment would otherwise replay the wake.\n */\n dedupKey?: string;\n}\n\n/**\n * PAN-1988: resume / feedback / continue delivery must be RESILIENT. When an agent is pinned to\n * the strict 'supervisor' transport — which throws with NO fallback when its echo-confirmation\n * fails (the recurring \"input echo confirmation failed\" that left review feedback undelivered to\n * the work agent every round) — deliver via 'auto' instead, so a supervisor failure falls back to\n * the proven tmux paste-buffer and the message still lands. Other explicit methods\n * ('tmux'/'channels'/'auto') are preserved. The strict 'supervisor' contract itself (PAN-1769) is\n * intentionally left intact in deliverAgentMessage for callers that opt into it directly.\n */\nexport function resilientDeliveryMethod(\n method: 'auto' | 'supervisor' | 'channels' | 'tmux' | undefined,\n): 'auto' | 'supervisor' | 'channels' | 'tmux' | undefined {\n return method === 'supervisor' ? 'auto' : method;\n}\n\n/**\n * Resolve OVERDECK_HOME — same fallback semantics as overdeck-bridge.\n */\nfunction overdeckHomeForSockets(): string {\n return process.env.OVERDECK_HOME ?? join(homedir(), '.overdeck');\n}\n\nfunction overdeckHomeForChannels(): string {\n return overdeckHomeForSockets();\n}\n\n/**\n * Append a delivery-event log line to the per-agent bridge log. Best-effort.\n */\nasync function appendChannelDeliveryLog(\n agentId: string,\n entry: {\n path: 'app-server' | 'acp' | 'supervisor' | 'channel' | 'tmux';\n reason?: string;\n caller?: string;\n appServer?: string;\n acp?: string;\n 'pty-supervisor'?: string;\n channels?: string;\n },\n): Promise<void> {\n try {\n const home = overdeckHomeForSockets();\n const dir = join(home, 'logs');\n await (await import('fs/promises')).mkdir(dir, { recursive: true });\n const line = JSON.stringify({\n ts: new Date().toISOString(),\n agentId,\n ...entry,\n });\n await (await import('fs/promises')).appendFile(\n join(dir, `bridge-${agentId}.log`),\n `${line}\\n`,\n 'utf-8',\n );\n } catch {\n // Non-critical\n }\n}\n\nfunction readSocketTokenSync(agentId: string, filename: string): string | null {\n try {\n const tokenPath = join(overdeckHomeForSockets(), 'agents', agentId, filename);\n if (!existsSync(tokenPath)) return null;\n const token = readFileSync(tokenPath, 'utf-8').trim();\n return token || null;\n } catch {\n return null;\n }\n}\n\nfunction readAppServerTokenSync(agentId: string): string | null {\n return readSocketTokenSync(agentId, 'appserver-token');\n}\n\nfunction readAcpTokenSync(agentId: string): string | null {\n return readSocketTokenSync(agentId, 'acp-token');\n}\n\n/**\n * A non-2xx RESPONSE from a protocol host. The host answered, so the outcome\n * is definitive: for a keyed supervisor request a 502 means the injection\n * failed and was purged and the key was never recorded — unlike a lost\n * response, this is safe to fall back from.\n */\nexport class SocketPostStatusError extends Error {\n readonly status: number;\n constructor(status: number, message: string) {\n super(message);\n this.name = 'SocketPostStatusError';\n this.status = status;\n }\n}\n\n/**\n * Thrown when a keyed delivery's outcome is UNKNOWN: the keyed supervisor\n * request was sent but no response came back (timeout, reset, dropped\n * connection), so the injection may have completed. Crossing to the tmux\n * tier with the same key would risk a second visible delivery, because the\n * supervisor and tmux keep INDEPENDENT dedup stores. The caller's recovery\n * must retry the same key at the same tier — the supervisor's in-flight\n * reservation and delivered set deduplicate the retry (PAN-2997 cycle 8).\n */\nexport class AmbiguousKeyedDeliveryError extends Error {\n constructor(agentId: string, caller: string, reason: string) {\n super(\n `MessageDeliveryFailed: ambiguous keyed delivery for ${agentId} (${caller}): ${reason} — ` +\n 'the supervisor may have completed the injection; NOT crossing to the tmux tier with the same key',\n );\n this.name = 'AmbiguousKeyedDeliveryError';\n }\n}\n\n/**\n * Pure, exported for tests (PAN-1837): classify a keyed supervisor POST\n * failure. Connect-phase errors (refused / socket path gone / permission)\n * fire before any bytes reach the supervisor, so the injection definitively\n * did NOT happen — a stale socket file left by a crashed supervisor refuses\n * every connection forever, and treating that as ambiguous strands keyed\n * deliveries permanently. A received non-2xx means the supervisor answered\n * and recorded no key. Everything else (timeout, reset mid-response) is\n * genuinely ambiguous.\n */\nexport function keyedSupervisorFailureKind(err: unknown): 'connect-failed' | 'status' | 'ambiguous' {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ECONNREFUSED' || code === 'ENOENT' || code === 'EACCES') return 'connect-failed';\n if (err instanceof SocketPostStatusError) return 'status';\n return 'ambiguous';\n}\n\n/**\n * POST a JSON body to a Unix-domain socket using node:net + a hand-rolled\n * minimal HTTP/1.1 request. Resolves on a 200-class response, rejects on any\n * error including socket-not-found, connection refused, an optional client\n * timeout, or non-2xx status. Kept tiny on purpose because this is a hot path\n * and these local protocol hosts need only a narrow HTTP client.\n */\nasync function postUnixSocketJson(\n socketPath: string,\n body: unknown,\n timeoutMs: number | undefined,\n token: string,\n tokenHeader: string = BRIDGE_TOKEN_HEADER,\n): Promise<{ status: number; body: string }> {\n const payload = JSON.stringify(body);\n\n return new Promise((resolveCall, reject) => {\n // Settle exactly once. Without this guard a late idle-timeout or\n // post-response socket error could reject after the response already\n // resolved the promise.\n let settled = false;\n let timeout: ReturnType<typeof setTimeout> | undefined;\n const clearClientTimeout = () => {\n if (timeout) clearTimeout(timeout);\n timeout = undefined;\n };\n const finishOk = (value: { status: number; body: string }) => {\n if (settled) return;\n settled = true;\n clearClientTimeout();\n resolveCall(value);\n };\n const finishErr = (err: Error) => {\n if (settled) return;\n settled = true;\n clearClientTimeout();\n reject(err);\n };\n\n const req = httpRequest(\n {\n socketPath,\n path: '/',\n method: 'POST',\n agent: false,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(payload),\n [tokenHeader]: token,\n },\n },\n (res) => {\n let responseBody = '';\n res.setEncoding('utf8');\n res.on('data', (chunk) => {\n responseBody += chunk;\n });\n res.on('end', () => {\n const status = res.statusCode ?? 0;\n if (status >= 200 && status < 300) {\n finishOk({ status, body: responseBody });\n return;\n }\n finishErr(new SocketPostStatusError(status, `socket POST: status ${status}: ${responseBody}`));\n });\n },\n );\n\n if (timeoutMs !== undefined) {\n timeout = setTimeout(() => {\n req.destroy(new Error('socket POST timeout'));\n }, timeoutMs);\n timeout.unref?.();\n }\n req.on('error', (err: Error) => {\n finishErr(err);\n });\n req.write(payload);\n req.end();\n });\n}\n\n/**\n * Single delivery primitive for orchestrator-to-work-agent messages. Auto mode\n * tries persistent protocol hosts, the PTY supervisor socket, legacy Channels\n * MCP, then tmux. Explicit socket methods and ACP targets are strict: ACP prompts\n * cannot fall back to terminal injection because only session/prompt reaches the agent.\n */\nexport async function deliverAgentMessage(\n agentId: string,\n message: string,\n caller: string = 'unknown',\n deliveryMethod?: 'auto' | 'supervisor' | 'channels' | 'tmux',\n opts: DeliverAgentMessageOptions = {},\n): Promise<DeliveryResult> {\n const normalizedId = normalizeAgentId(agentId);\n const dedupKey = opts.dedupKey;\n\n let channelsEnabled = false;\n let resolvedMethod = deliveryMethod;\n let state: AgentState | null = null;\n try {\n state = await Effect.runPromise(getAgentState(normalizedId));\n channelsEnabled = Boolean(state?.channelsEnabled);\n resolvedMethod ??= state?.deliveryMethod ?? 'auto';\n } catch {\n resolvedMethod ??= 'auto';\n }\n\n const isAcpTarget = state?.harness === 'acp' || state?.harness === 'opencode';\n if (isAcpTarget && resolvedMethod !== 'auto') {\n throw new Error(\n `MessageDeliveryFailed: ACP delivery failed for ${normalizedId} (${caller}): ACP requires authenticated host RPC delivery`,\n );\n }\n\n let preparedKimiMessage = opts.preparedKimiContext;\n if (preparedKimiMessage && preparedKimiMessage.message !== message) {\n throw new Error(`Managed Kimi message blocked for ${normalizedId}: prepared envelope does not match the delivered message.`);\n }\n const kimiContext = state?.harness === 'kimi-code' && state.workspace\n ? { workspace: state.workspace }\n : opts.kimiContext;\n if (kimiContext && !preparedKimiMessage) {\n preparedKimiMessage = await prepareKimiMessage(normalizedId, kimiContext.workspace, message, {\n sessionId: kimiContext.sessionId,\n });\n message = preparedKimiMessage.message;\n }\n const completeDelivery = (result: DeliveryResult): DeliveryResult => {\n if (result.ok && !result.deduplicated && preparedKimiMessage) {\n markKimiContextDelivered(normalizedId, preparedKimiMessage);\n }\n return result;\n };\n\n // Keyed deliveries take a dedicated, narrower cascade: only the tiers whose\n // crash-independent component enforces the key across the complete side\n // effect. Everything below this branch is the unkeyed cascade.\n if (dedupKey !== undefined) {\n return completeDelivery(await deliverKeyedAgentMessage(normalizedId, message, caller, resolvedMethod ?? 'auto', isAcpTarget, dedupKey));\n }\n\n if (resolvedMethod === 'tmux') {\n await assertTmuxTargetCanReceive(normalizedId, caller);\n await Effect.runPromise(sendKeys(normalizedId, message));\n return completeDelivery({ ok: true, path: 'tmux' });\n }\n\n let appServerFailure: string | undefined;\n if (resolvedMethod === 'auto' && !isAcpTarget) {\n const appServerSocketPath = join(overdeckHomeForSockets(), 'sockets', `appserver-${normalizedId}.sock`);\n if (existsSync(appServerSocketPath)) {\n const appServerToken = readAppServerTokenSync(normalizedId);\n if (!appServerToken) {\n appServerFailure = 'appserver-token-missing';\n } else {\n try {\n const appServerBody: Record<string, unknown> = { op: 'message', content: message, meta: { caller } };\n if (state?.model) appServerBody.model = state.model;\n await postUnixSocketJson(\n appServerSocketPath,\n appServerBody,\n 8_000,\n appServerToken,\n );\n await appendChannelDeliveryLog(normalizedId, { path: 'app-server', caller });\n return completeDelivery({ ok: true, path: 'app-server' });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n appServerFailure = `socket-post-failed: ${reason}`;\n }\n }\n }\n }\n\n let acpFailure: string | undefined;\n if (resolvedMethod === 'auto') {\n const acpSocketPath = join(overdeckHomeForSockets(), 'sockets', `acp-${normalizedId}.sock`);\n const acpSocketExists = existsSync(acpSocketPath);\n if (isAcpTarget || acpSocketExists) {\n const acpToken = readAcpTokenSync(normalizedId);\n if (!acpSocketExists) {\n acpFailure = 'socket-missing';\n } else if (!acpToken) {\n acpFailure = 'acp-token-missing';\n } else {\n try {\n // The ACP host acknowledges queue acceptance rather than model-turn\n // completion, so this bounds a wedged local host without constraining\n // how long the provider may take to finish the queued turn.\n await postUnixSocketJson(\n acpSocketPath,\n { op: 'message', content: message, meta: { caller } },\n 8_000,\n acpToken,\n );\n await appendChannelDeliveryLog(normalizedId, { path: 'acp', caller });\n return completeDelivery({ ok: true, path: 'acp' });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n acpFailure = `socket-post-failed: ${reason}`;\n }\n }\n\n // ACP prompts only enter the agent through session/prompt on the host RPC\n // socket. Terminal fallbacks can accept pasted text while bypassing the ACP\n // session entirely, so an ACP transport failure must remain a loud failure.\n await appendChannelDeliveryLog(normalizedId, {\n path: 'acp',\n reason: acpFailure,\n caller,\n });\n throw new Error(\n `MessageDeliveryFailed: ACP delivery failed for ${normalizedId} (${caller}): ${acpFailure}`,\n );\n }\n }\n\n let supervisorFailure: string | undefined;\n if (resolvedMethod === 'auto' || resolvedMethod === 'supervisor') {\n const supervisorSocketPath = join(overdeckHomeForSockets(), 'sockets', `pty-${normalizedId}.sock`);\n const ptyToken = await readPtyToken(normalizedId);\n if (!existsSync(supervisorSocketPath)) {\n supervisorFailure = 'socket-missing';\n } else if (!ptyToken) {\n supervisorFailure = 'pty-token-missing';\n } else {\n try {\n // The client deadline must remain above the supervisor's payload-aware\n // worst-case injection budget. Abandoning the POST mid-injection would\n // fire the tmux fallback while the supervisor is still writing and\n // re-create the duplicate-writer race PAN-1769 fixed.\n const supervisorResponse = await postUnixSocketJson(\n supervisorSocketPath,\n { content: message, meta: { caller } },\n supervisorInjectionBudgetMs(message.length) + SUPERVISOR_CLIENT_MARGIN_MS,\n ptyToken,\n PTY_TOKEN_HEADER,\n );\n await appendChannelDeliveryLog(normalizedId, { path: 'supervisor', caller });\n return completeDelivery({ ok: true, path: 'supervisor' });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n supervisorFailure = `socket-post-failed: ${reason}`;\n }\n }\n\n if (resolvedMethod === 'supervisor') {\n throw new Error(`MessageDeliveryFailed: PTY supervisor delivery failed for ${normalizedId} (${caller}): ${supervisorFailure}`);\n }\n }\n\n if (resolvedMethod === 'auto' || resolvedMethod === 'channels') {\n let channelFailure: string | undefined;\n const socketPath = join(overdeckHomeForSockets(), 'sockets', `agent-${normalizedId}.sock`);\n if (!channelsEnabled) {\n channelFailure = 'channels-disabled';\n } else if (!existsSync(socketPath)) {\n channelFailure = 'socket-missing';\n } else {\n const bridgeToken = readBridgeTokenSync(normalizedId);\n if (!bridgeToken) {\n channelFailure = 'bridge-token-missing';\n } else {\n try {\n await postUnixSocketJson(\n socketPath,\n { content: message, meta: { caller } },\n 2000,\n bridgeToken,\n );\n await appendChannelDeliveryLog(normalizedId, {\n path: 'channel',\n caller,\n ...(supervisorFailure ? { 'pty-supervisor': supervisorFailure } : {}),\n });\n return completeDelivery({ ok: true, path: 'channels' });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n channelFailure = `socket-post-failed: ${reason}`;\n }\n }\n }\n\n if (resolvedMethod === 'channels') {\n throw new Error(`MessageDeliveryFailed: Channels delivery failed for ${normalizedId} (${caller}): ${channelFailure}`);\n }\n\n await appendChannelDeliveryLog(normalizedId, {\n path: 'tmux',\n reason: channelFailure,\n caller,\n ...(appServerFailure ? { appServer: appServerFailure } : {}),\n ...(acpFailure ? { acp: acpFailure } : {}),\n ...(supervisorFailure ? { 'pty-supervisor': supervisorFailure } : {}),\n ...(channelFailure ? { channels: channelFailure } : {}),\n });\n await assertTmuxTargetCanReceive(normalizedId, caller);\n await Effect.runPromise(sendKeys(normalizedId, message));\n return completeDelivery({ ok: true, path: 'tmux', failure: channelFailure ?? supervisorFailure });\n }\n\n await assertTmuxTargetCanReceive(normalizedId, caller);\n await Effect.runPromise(sendKeys(normalizedId, message));\n return completeDelivery({ ok: true, path: 'tmux' });\n}\n\n/**\n * Keyed delivery cascade (PAN-2997). Only two tiers may carry a keyed\n * message, because only their crash-independent components enforce the key\n * across the COMPLETE visible side effect:\n *\n * - the PTY supervisor reserves the key synchronously (closing the concurrent\n * same-key race) and completes it only after its single-request\n * content-plus-Enter injection succeeds; and\n * - the tmux fallback keeps two-phase pending/terminal session markers owned\n * by the tmux server across paste, settle, and submit.\n *\n * App-server, ACP, and Channels acknowledge receipt without enforcing the\n * key, so they are never used for keyed payloads: a dashboard crash after\n * their side effect but before the caller's acknowledgment would replay the\n * wake. Explicitly requesting one of them with a key is a loud error, not a\n * silent downgrade.\n */\nasync function deliverKeyedAgentMessage(\n normalizedId: string,\n message: string,\n caller: string,\n resolvedMethod: 'auto' | 'supervisor' | 'channels' | 'tmux',\n isAcpTarget: boolean,\n dedupKey: string,\n): Promise<DeliveryResult> {\n if (isAcpTarget) {\n throw new Error(\n `MessageDeliveryFailed: keyed delivery failed for ${normalizedId} (${caller}): the ACP tier cannot enforce a dedup key`,\n );\n }\n if (resolvedMethod === 'channels') {\n throw new Error(\n `MessageDeliveryFailed: keyed delivery failed for ${normalizedId} (${caller}): the Channels tier cannot enforce a dedup key`,\n );\n }\n if (resolvedMethod === 'tmux') {\n return deliverKeyedViaTmux(normalizedId, message, caller, dedupKey);\n }\n\n let supervisorFailure: string | undefined;\n const supervisorSocketPath = join(overdeckHomeForSockets(), 'sockets', `pty-${normalizedId}.sock`);\n const ptyToken = await readPtyToken(normalizedId);\n if (!existsSync(supervisorSocketPath)) {\n supervisorFailure = 'socket-missing';\n } else if (!ptyToken) {\n supervisorFailure = 'pty-token-missing';\n } else {\n try {\n // The client deadline must remain above the supervisor's payload-aware\n // worst-case injection budget. Abandoning the POST mid-injection would\n // fire the tmux fallback while the supervisor is still writing and\n // re-create the duplicate-writer race PAN-1769 fixed.\n const supervisorResponse = await postUnixSocketJson(\n supervisorSocketPath,\n { content: message, meta: { caller }, dedupKey },\n supervisorInjectionBudgetMs(message.length) + SUPERVISOR_CLIENT_MARGIN_MS,\n ptyToken,\n PTY_TOKEN_HEADER,\n );\n await appendChannelDeliveryLog(normalizedId, { path: 'supervisor', caller });\n let deduplicated = false;\n try {\n deduplicated = (JSON.parse(supervisorResponse.body) as { deduplicated?: boolean }).deduplicated === true;\n } catch {\n // Legacy supervisor build without dedup support — treat as delivered.\n }\n return { ok: true, path: 'supervisor', deduplicated };\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n const kind = keyedSupervisorFailureKind(err);\n if (kind === 'ambiguous') {\n // AMBIGUOUS (cycle 8): the request was sent but no response came back,\n // so the supervisor may have completed the injection. The tmux tier\n // keeps an INDEPENDENT key store, so crossing to it with the same key\n // could submit a second visible wake. Stay with the same delivery\n // owner: the caller's recovery retries the same key at the supervisor,\n // whose in-flight reservation/delivered set deduplicates the retry.\n throw new AmbiguousKeyedDeliveryError(normalizedId, caller, reason);\n }\n supervisorFailure = `${kind === 'connect-failed' ? 'socket-connect-failed' : 'socket-post-failed'}: ${reason}`;\n }\n }\n\n if (resolvedMethod === 'supervisor') {\n throw new Error(`MessageDeliveryFailed: PTY supervisor delivery failed for ${normalizedId} (${caller}): ${supervisorFailure}`);\n }\n\n await appendChannelDeliveryLog(normalizedId, { path: 'tmux', reason: supervisorFailure, caller });\n const result = await deliverKeyedViaTmux(normalizedId, message, caller, dedupKey);\n return { ...result, failure: supervisorFailure };\n}\n\n/**\n * Keyed tmux delivery: paste under the two-phase marker protocol, then\n * complete the submit. Pending claims carry a strictly-read payload state:\n * unverified requires active-composer proof, while enter-attempted preserves\n * no-repaste rollback recovery. Unset legacy/unknown states fail closed. A\n * terminal marker suppresses the replay entirely.\n */\nasync function deliverKeyedViaTmux(\n normalizedId: string,\n message: string,\n caller: string,\n dedupKey: string,\n): Promise<DeliveryResult> {\n await assertTmuxTargetCanReceive(normalizedId, caller);\n const phase = await sendKeysDedup(normalizedId, message, dedupKey, caller);\n if (phase !== 'deduplicated') {\n await completeKeyedSubmit(normalizedId, dedupKey);\n }\n return { ok: true, path: 'tmux', deduplicated: phase === 'deduplicated' };\n}\n\n/**\n * PAN-2228: loud failure semantics for the tmux tier. `tmux send-keys` into a\n * session whose pane process has EXITED (a remain-on-exit corpse) succeeds at the\n * tmux level while the message lands nowhere — the zombie-kickoff failure mode\n * (PAN-2179): callers saw ok:true, treated feedback as delivered, and the issue\n * silently stalled. Verify a live pane BEFORE pasting, and throw the same\n * MessageDeliveryFailed shape the strict socket methods use so every caller's\n * existing failure path fires instead of a false success. (A fully MISSING\n * session needs no check here — tmux send-keys itself errors loudly on it.)\n */\nasync function assertTmuxTargetCanReceive(normalizedId: string, caller: string): Promise<void> {\n const alive = await Effect.runPromise(sessionExists(normalizedId));\n if (!alive) return; // send-keys will fail loudly on its own for a missing session\n const paneDead = await Effect.runPromise(isPaneDead(normalizedId));\n if (paneDead) {\n throw new Error(`MessageDeliveryFailed: tmux delivery failed for ${normalizedId} (${caller}): pane-dead (session alive but the harness process has exited)`);\n }\n}\n\n// 3s was too tight on hosts where SessionStart hooks (memory RAG injection,\n// Subspace + Overdeck both) delay the first transcript flush past the window —\n// kickoff confirmation then killed healthy agents whose user record landed\n// seconds later (observed 2026-07-29, macOS). The wait returns as soon as the\n// record appears, so a generous ceiling adds no latency on the happy path.\nconst RESUME_TRANSCRIPT_CONFIRM_TIMEOUT_MS = 30_000;\nconst RESUME_TRANSCRIPT_CONFIRM_INTERVAL_MS = 100;\n\nasync function waitForTranscriptMessageLanding(\n workspace: string,\n sessionId: string,\n before: TranscriptUserRecordSnapshot,\n message: string,\n probe: typeof probeTranscriptSince,\n timeoutMs = RESUME_TRANSCRIPT_CONFIRM_TIMEOUT_MS,\n intervalMs = RESUME_TRANSCRIPT_CONFIRM_INTERVAL_MS,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n const fromByteOffset = before.readOffset ?? before.fileSize ?? 0;\n do {\n const result = await probe(workspace, sessionId, fromByteOffset, message);\n if (result.matchedUserRecord || (result.realAssistantTurnCount ?? 0) > 0) return true;\n await new Promise(resolve => setTimeout(resolve, intervalMs));\n } while (Date.now() < deadline);\n\n const result = await probe(workspace, sessionId, fromByteOffset, message);\n return result.matchedUserRecord || (result.realAssistantTurnCount ?? 0) > 0;\n}\n\nexport async function deliverResumeMessageWithTranscriptConfirmation(args: {\n agentId: string;\n workspace: string;\n sessionId: string;\n message: string;\n caller: string;\n deliveryMethod?: 'auto' | 'supervisor' | 'channels' | 'tmux';\n timeoutMs?: number;\n intervalMs?: number;\n deliver?: typeof deliverAgentMessage;\n snapshot?: typeof captureTranscriptUserRecordSnapshot;\n probe?: typeof probeTranscriptSince;\n}): Promise<{ delivered: boolean; attempts: number; lastDelivery?: DeliveryResult }> {\n const snapshot = args.snapshot ?? captureTranscriptUserRecordSnapshot;\n const probe = args.probe ?? probeTranscriptSince;\n const deliver = args.deliver ?? deliverAgentMessage;\n const before = await snapshot(args.workspace, args.sessionId);\n let lastDelivery: DeliveryResult | undefined;\n\n for (let attempt = 1; attempt <= 2; attempt += 1) {\n lastDelivery = await deliver(args.agentId, args.message, args.caller, args.deliveryMethod);\n if (lastDelivery.ok && await waitForTranscriptMessageLanding(\n args.workspace,\n args.sessionId,\n before,\n args.message,\n probe,\n args.timeoutMs,\n args.intervalMs,\n )) {\n return { delivered: true, attempts: attempt, lastDelivery };\n }\n if (attempt < 2) {\n console.warn(`[resumeAgent] Auto-continue prompt did not land in ${args.sessionId}; redelivering once.`);\n }\n }\n\n return { delivered: false, attempts: 2, ...(lastDelivery ? { lastDelivery } : {}) };\n}\n\nexport async function deliverInitialPromptWithRetry(\n agentId: string,\n prompt: string,\n caller: string,\n deliveryMethod?: 'auto' | 'supervisor' | 'channels' | 'tmux',\n options: {\n timeoutMs?: number;\n intervalMs?: number;\n settleDelayMs?: number;\n deliver?: typeof deliverAgentMessage;\n snapshot?: typeof captureTranscriptUserRecordSnapshot;\n probe?: typeof probeTranscriptSince;\n getState?: (agentId: string) => Promise<AgentState | null>;\n waitForReady?: typeof waitForPromptReady;\n sessionExists?: (agentId: string) => Promise<boolean>;\n } = {},\n): Promise<DeliveryResult> {\n const normalizedId = normalizeAgentId(agentId);\n const deliver = options.deliver ?? deliverAgentMessage;\n const snapshot = options.snapshot ?? captureTranscriptUserRecordSnapshot;\n const probe = options.probe ?? probeTranscriptSince;\n const getState = options.getState ?? (async (id: string) => {\n try {\n return await Effect.runPromise(getAgentState(normalizeAgentId(id)));\n } catch {\n return null;\n }\n });\n const waitForReady = options.waitForReady ?? waitForPromptReady;\n const sessionExistsForAgent = options.sessionExists ?? (async (id: string) => (\n Effect.runPromise(sessionExists(normalizeAgentId(id)))\n ));\n\n function promptReadyTimeoutSeconds(): number {\n const raw = process.env.OVERDECK_PROMPT_READY_TIMEOUT_SECONDS;\n if (!raw) return 30;\n const parsed = Number(raw);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 30;\n }\n\n async function resolveTranscriptConfirmationTarget(\n state: AgentState | null,\n ): Promise<{ workspace: string; sessionId: string; before: TranscriptUserRecordSnapshot } | null> {\n if (!state?.workspace || getHarnessBehavior(state.harness).transcriptKind !== 'claude-jsonl') {\n return null;\n }\n\n let sessionId = state.sessionId;\n if (!sessionId) {\n try {\n sessionId = (await Effect.runPromise(getAgentRuntimeState(normalizedId)))?.claudeSessionId;\n } catch {\n sessionId = undefined;\n }\n }\n if (!sessionId) return null;\n\n return {\n workspace: state.workspace,\n sessionId,\n before: await snapshot(state.workspace, sessionId),\n };\n }\n\n // PAN-1803: the codex TUI mangles a large pasted kickoff prompt — a multi-\n // thousand-character paste garbles its input and trips its \"Create a plan?\"\n // mode hint, so the agent never executes. Write the full brief to a file and\n // deliver a SHORT pointer instead (robust regardless of transport — the same\n // pattern that makes file-backed handoffs reliable). Only codex needs this;\n // claude-code/pi line-based input handle the full prompt fine.\n //\n // PAN-2330: keep the workspace-level .pan/kickoff.md owned by the work/strike\n // agent. Review, test, planning, flywheel, and other role prompts can share\n // the same workspace; writing those prompts to .pan/kickoff.md overwrites the\n // work agent's brief and a later resume can make it read the wrong task.\n let deliveredPrompt = prompt;\n let state = await getState(agentId);\n try {\n if (state?.harness && getHarnessBehavior(state.harness).usesCodexHome) {\n const ownsWorkspaceKickoff = state.workspace && (state.role === 'work' || state.role === 'strike');\n const kickoffPath = ownsWorkspaceKickoff\n ? join(state.workspace, '.pan', 'kickoff.md')\n : join(getAgentDir(normalizedId), 'kickoff.md');\n const displayPath = ownsWorkspaceKickoff ? '`.pan/kickoff.md`' : `\\`${kickoffPath}\\``;\n mkdirSync(dirname(kickoffPath), { recursive: true });\n writeFileSync(kickoffPath, prompt, 'utf-8');\n deliveredPrompt =\n `Your complete task brief has been written to ${displayPath}. `\n + 'Read that file in full now and execute it exactly — it is your full set of work '\n + 'instructions. Begin immediately and keep working autonomously until done; do not '\n + 'wait for further input.';\n }\n } catch {\n // Non-fatal: fall back to delivering the full prompt inline.\n }\n\n let lastFailure = 'not-attempted';\n for (let attempt = 1; attempt <= 2; attempt += 1) {\n let harness: RuntimeName | undefined;\n try {\n state = await getState(agentId);\n harness = state?.harness;\n } catch {\n harness = undefined;\n }\n const readyTimeoutSeconds = promptReadyTimeoutSeconds();\n const ready = await waitForReady(agentId, harness, readyTimeoutSeconds);\n if (!ready) {\n const alive = await sessionExistsForAgent(agentId);\n lastFailure = alive ? 'ready-signal-timeout' : SESSION_EXITED_BEFORE_KICKOFF;\n const displayName = getHarnessBehavior(harness).displayName;\n console.error(`[${agentId}] ${displayName} did not become ready within ${readyTimeoutSeconds}s (kickoff attempt ${attempt}/2)`);\n if (!alive) break;\n continue;\n }\n\n const settleDelayMs = options.settleDelayMs ?? 500;\n if (settleDelayMs > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, settleDelayMs));\n }\n try {\n const confirmationTarget = await resolveTranscriptConfirmationTarget(state);\n // Codex app-server agents do not have a PTY supervisor socket. Work-agent\n // state can still project supervisorEnabled=true into the legacy strict\n // `supervisor` method. Resolve the persisted method here before forcing\n // resilient routing; passing undefined would let deliverAgentMessage\n // re-read that strict method and abort the cascade. Initial kickoff and\n // Deacon redelivery use app-server first, then the PTY path used by Codex\n // TUI. Claude Code keeps its strict supervisor contract.\n const kickoffDeliveryMethod = state?.harness === 'codex'\n ? resilientDeliveryMethod(deliveryMethod ?? state.deliveryMethod)\n : deliveryMethod;\n const result = await deliver(agentId, deliveredPrompt, caller, kickoffDeliveryMethod);\n if (result.ok) {\n if (!confirmationTarget) return result;\n const confirmed = await waitForTranscriptMessageLanding(\n confirmationTarget.workspace,\n confirmationTarget.sessionId,\n confirmationTarget.before,\n deliveredPrompt,\n probe,\n options.timeoutMs,\n options.intervalMs,\n );\n if (confirmed) return result;\n lastFailure = 'kickoff-not-confirmed';\n } else {\n lastFailure = result.failure ?? `delivery returned ok=false via ${result.path}`;\n }\n } catch (err) {\n lastFailure = err instanceof Error ? err.message : String(err);\n }\n console.error(`[${agentId}] Kickoff delivery attempt ${attempt}/2 failed: ${lastFailure}`);\n }\n\n return { ok: false, path: 'tmux', failure: lastFailure };\n}\n\nexport async function deliverAgentPermissionDecision(\n agentId: string,\n requestId: string,\n behavior: 'allow' | 'deny',\n): Promise<void> {\n const normalizedId = normalizeAgentId(agentId);\n\n let state: AgentState | null = null;\n try {\n state = await Effect.runPromise(getAgentState(normalizedId));\n } catch {\n state = null;\n }\n\n if (!state?.channelsEnabled) {\n throw new Error(`agent ${normalizedId} is not using Claude channels`);\n }\n\n const socketPath = join(overdeckHomeForChannels(), 'sockets', `agent-${normalizedId}.sock`);\n if (!existsSync(socketPath)) {\n throw new Error(`bridge socket missing for ${normalizedId}`);\n }\n\n const bridgeToken = readBridgeTokenSync(normalizedId);\n if (!bridgeToken) {\n throw new Error(`bridge token missing for ${normalizedId}`);\n }\n\n await postUnixSocketJson(\n socketPath,\n {\n type: 'permission_response',\n requestId,\n behavior,\n },\n 2000,\n bridgeToken,\n );\n\n await appendChannelDeliveryLog(normalizedId, {\n path: 'channel',\n caller: `permission-response:${requestId}:${behavior}`,\n });\n}\n\n/** Update just the delivery method on an agent's state file. */\nexport async function setAgentDeliveryMethod(\n agentId: string,\n deliveryMethod: 'auto' | 'supervisor' | 'channels' | 'tmux',\n): Promise<void> {\n const state = await Effect.runPromise(getAgentState(agentId));\n if (!state) return;\n state.deliveryMethod = deliveryMethod;\n await Effect.runPromise(saveAgentState(state));\n}\n","/**\n * PAN-2500: the cached MemoryVerdict, split out from memory-governor.ts into\n * its own zero-dependency module. concurrency.ts (canDispatchAdvancing /\n * tryReserveAdvancingSlot) needs to read this synchronously without importing\n * memory-governor.ts directly — memory-governor.ts pulls in\n * dashboard/server/routes/resources/stacks.ts, which transitively reaches\n * concurrency.ts itself (stacks.ts -> review-status.ts -> agents.ts ->\n * agents/spawn.ts -> concurrency.ts), so a direct concurrency.ts ->\n * memory-governor.ts import closes a real cycle (caught by\n * scripts/lint-circular-deps.sh). This module has no imports of its own, so\n * it cannot be part of any cycle.\n */\n\nexport type MemoryPressureBand = 'ok' | 'soft' | 'hard';\n\nexport interface MemoryPressureThresholds {\n warningBytes: number;\n criticalBytes: number;\n}\n\nexport type GovernorTriggerKind = 'soft-dip' | 'hard' | 'swap-psi' | 'psi-unavailable';\n\nexport interface GovernorTrigger {\n kind: GovernorTriggerKind;\n readingBytes: number;\n thresholdBytes: number;\n at: number;\n}\n\nexport interface MemoryVerdict {\n band: MemoryPressureBand;\n availableBytes: number;\n thresholds: MemoryPressureThresholds;\n swapTotalBytes?: number;\n swapFreeBytes?: number;\n psiSomeAvg10?: number | null;\n psiFullAvg10?: number | null;\n loadPerCore?: number | null;\n trigger?: GovernorTrigger | null;\n}\n\nlet cachedVerdict: MemoryVerdict | null = null;\n\n/**\n * The verdict from the most recent assessMemoryPressure() call, or null before\n * any patrol has run one. Synchronous — no I/O.\n */\nexport function getCachedMemoryVerdict(): MemoryVerdict | null {\n return cachedVerdict;\n}\n\nexport function setCachedMemoryVerdict(verdict: MemoryVerdict | null): void {\n cachedVerdict = verdict;\n}\n\n/** Test-only. */\nexport function setCachedMemoryVerdictForTests(verdict: MemoryVerdict | null): void {\n cachedVerdict = verdict;\n}\n","import type { AgentState } from '../agents/agent-state.js';\nimport { readIssueRecordForWorkspaceSync, type PanIssueRecord } from '../pan-dir/record.js';\nimport { readWorkspacePlanSync } from '../xbrief/io.js';\nimport type { XBriefDocument } from '../xbrief/types.js';\n\ntype SwarmSlotAgent = Pick<AgentState, 'id' | 'issueId' | 'role' | 'workspace' | 'slotIndex' | 'slotItemId'>;\ntype PlanReader = (workspacePath: string) => XBriefDocument | null;\ntype RecordReader = (workspacePath: string, issueId: string) => PanIssueRecord | null;\n\n/** A registered slot must not run after its assigned item reaches a terminal state. */\nexport function isTerminalSwarmSlotAgent(\n agent: SwarmSlotAgent,\n readPlan: PlanReader = readWorkspacePlanSync,\n readRecord: RecordReader = readIssueRecordForWorkspaceSync,\n): boolean {\n if (agent.role !== 'work' || !agent.workspace) return false;\n const idMatch = /-slot-(\\d+)$/.exec(agent.id);\n const slotIndex = agent.slotIndex ?? (idMatch ? Number(idMatch[1]) : undefined);\n if (!slotIndex) return false;\n\n const baseWorkspace = agent.workspace.replace(/-slot-\\d+$/, '');\n const record = readRecord(baseWorkspace, agent.issueId);\n const assignment = record?.swarm?.slotAssignments\n ?.find(candidate => candidate.slotIndex === slotIndex);\n const itemId = agent.slotItemId ?? assignment?.itemId;\n if (!itemId) return false;\n\n // Swarm task transitions are persisted through the issue record's write\n // door. The immutable workspace plan can still say `running` after the\n // member-repo branch has merged, so recovery must honor the canonical\n // override before it considers reviving stale slot ownership.\n const canonicalStatus = record?.statusOverrides?.[itemId];\n if (canonicalStatus === 'completed' || canonicalStatus === 'cancelled') return true;\n\n const plan = readPlan(baseWorkspace) ?? readPlan(agent.workspace);\n const item = plan?.plan.items.find(candidate => candidate.id === itemId);\n return item?.status === 'completed' || item?.status === 'cancelled';\n}\n","/**\n * Concurrency governor for deacon auto-resume / auto-dispatch (PAN-1665).\n *\n * The deacon's patrol re-spawns work from several sources (resume stopped work\n * agents, re-dispatch orphaned review/test specialists, …). With no ceiling, an\n * unfreeze marches the box toward dozens of concurrent heavy `claude` processes.\n *\n * This module is the single budget every patrol spawn path consults. Two rules:\n *\n * 1. It is a **gate on starting new work** — it NEVER kills a running agent to\n * get back under a limit. If the system is already over (a forced `pan start`,\n * a backlog at unfreeze), the deacon simply resumes/dispatches nothing until\n * natural attrition drains the count. Forcibly trimming an over-limit system\n * is the operator's explicit emergency brake, never automatic.\n * 2. Advancing roles (review/test/ship) get reserved headroom above the work cap\n * so the pipeline can always drain instead of deadlocking with work agents.\n */\n\nimport { loadCloisterConfigSync } from './config.js';\nimport {\n listRunningAgentsSync,\n stopAgentSync,\n getAgentRuntimeStateSync,\n} from '../agents.js';\nimport { countAgentsByStatus } from '../overdeck/agents.js';\nimport { getCachedMemoryVerdict } from './memory-verdict-cache.js';\nimport { isRoleTerminal, type AdvancingRole } from './reap-terminal-sessions.js';\nimport { readReviewStatusMap } from './review-status-source.js';\nimport { isTerminalSwarmSlotAgent } from './swarm-slot-lifecycle.js';\n\nconst DEFAULT_MAX_WORK_AGENTS = 6;\nconst DEFAULT_RESERVED_ADVANCING_SLOTS = 3;\nconst DEFAULT_RESERVED_SWARM_SLOTS = 3;\n\n/** Roles that advance work through the pipeline and must keep reserved headroom. */\nconst ADVANCING_ROLES = new Set(['review', 'test', 'ship']);\n\n/**\n * Swarm slots (PAN-2212) are work-role sessions with an `agent-<issue>-slot-N`\n * id. They draw from a dedicated swarm reserve and are counted APART from `work`,\n * so a busy pipeline never starves the swarm — and running swarm slots never\n * starve review/test in reverse.\n */\nconst SWARM_SLOT_ID = /-slot-\\d+$/;\n\nexport interface ConcurrencyLimits {\n maxWorkAgents: number;\n reservedAdvancingSlots: number;\n /** Dedicated swarm-slot reserve, isolated from the work/advancing ceiling (PAN-2212). */\n reservedSwarmSlots: number;\n /** Overall ceiling for any auto-dispatch: work cap + reserved advancing slots. */\n totalCeiling: number;\n /** Whether operator-started agents are exempt from governor reaping (PAN-1812). */\n exemptOperatorStarted: boolean;\n}\n\nfunction normalizeCount(value: unknown, fallback: number, min: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;\n const floored = Math.floor(value);\n return floored < min ? min : floored;\n}\n\nexport function getConcurrencyLimits(): ConcurrencyLimits {\n const c = loadCloisterConfigSync().concurrency;\n const maxWorkAgents = normalizeCount(c?.max_work_agents, DEFAULT_MAX_WORK_AGENTS, 1);\n const reservedAdvancingSlots = normalizeCount(c?.reserved_advancing_slots, DEFAULT_RESERVED_ADVANCING_SLOTS, 0);\n const reservedSwarmSlots = normalizeCount(c?.reserved_swarm_slots, DEFAULT_RESERVED_SWARM_SLOTS, 0);\n return {\n maxWorkAgents,\n reservedAdvancingSlots,\n reservedSwarmSlots,\n totalCeiling: maxWorkAgents + reservedAdvancingSlots,\n exemptOperatorStarted: c?.exempt_operator_started ?? true,\n };\n}\n\nexport interface RunningCounts {\n /** Regular work agents, EXCLUDING swarm slots (PAN-2212). */\n work: number;\n advancing: number;\n /** Swarm-slot work agents, counted apart from `work` (PAN-2212). */\n swarm: number;\n /** work + advancing (EXCLUDES swarm) — the ceiling every non-swarm dispatch consults. */\n total: number;\n}\n\n/**\n * Human-readable breakdown of what currently counts against the ceiling, for\n * deferral diagnostics (PAN-1716). Lists the offending session ids so a\n * starvation is diagnosable from the patrol log alone — the original livelock\n * (completed review/test sessions that never get reaped) is invisible from a\n * bare \"ceiling reached\" line.\n */\nexport function describeRunningAgents(): string {\n const alive = listRunningAgentsSync().filter(a => a.tmuxActive);\n const swarm = alive.filter(a => a.role === 'work' && SWARM_SLOT_ID.test(a.id) && !isTerminalSwarmSlotAgent(a)).map(a => a.id);\n const work = alive.filter(a => a.role === 'work' && !SWARM_SLOT_ID.test(a.id)).map(a => a.id);\n const advancing = alive.filter(a => a.role && ADVANCING_ROLES.has(a.role)).map(a => a.id);\n const warmIdle = countWarmIdleAdvancingAgents(alive);\n const { totalCeiling } = getConcurrencyLimits();\n return `counts: work=${work.length} advancing=${advancing.length} (warm-idle=${warmIdle}, excluded) swarm=${swarm.length} total=${work.length + Math.max(0, advancing.length - warmIdle)}/${totalCeiling}`\n + ` | advancing=[${advancing.join(', ')}] work=[${work.join(', ')}] swarm=[${swarm.join(', ')}]`;\n}\n\n/** Count currently-running agents by role class.\n *\n * PAN-1908: the agents table is the authoritative runtime registry. Counts are\n * derived from status='running' rows grouped by role; the deacon's event-driven\n * updates keep status in sync with tmux liveness.\n */\n/** Count tmux-alive swarm-slot work agents (agent-<issue>-slot-N) — PAN-2212. */\nfunction countRunningSwarmSlots(): { total: number; active: number } {\n const slots = listRunningAgentsSync().filter(\n a => a.tmuxActive && a.role === 'work' && SWARM_SLOT_ID.test(a.id),\n );\n return {\n total: slots.length,\n active: slots.filter(agent => !isTerminalSwarmSlotAgent(agent)).length,\n };\n}\n\n/**\n * Count tmux-ALIVE swarm-slot work agents for one issue (agent-<issue>-slot-N).\n * Stale agents-table rows whose tmux session is dead do NOT count — counting\n * them blocked all dispatch at zero live slots after a reset (PAN-2214).\n */\nexport function countRunningSwarmSlotsForIssue(\n issueId: string,\n agents: ReturnType<typeof listRunningAgentsSync> = listRunningAgentsSync(),\n isTerminalSlot: (agent: ReturnType<typeof listRunningAgentsSync>[number]) => boolean = isTerminalSwarmSlotAgent,\n): number {\n const prefix = `agent-${issueId.toLowerCase()}-slot-`;\n return agents.filter(\n a => a.tmuxActive\n && a.role === 'work'\n && SWARM_SLOT_ID.test(a.id)\n && a.id.startsWith(prefix)\n && !isTerminalSlot(a),\n ).length;\n}\n\n/**\n * PAN-2579: count running advancing-role agents whose issue's phase verdict is\n * already terminal — \"warm-idle\" sessions kept alive for fast re-review under\n * the warm-by-default lifecycle. They are free capacity, not load: excluding\n * them from the ceiling is what lets warm sessions persist without recreating\n * the PAN-1716 livelock (completed reviewers starving every new dispatch).\n * Best-effort: if the review-status source is unregistered or unreadable,\n * count nothing as warm-idle (the ceiling stays conservative). The status map\n * is read through the cycle-free review-status-source leaf, registered by\n * review-status.ts at module load.\n */\nexport function countWarmIdleAdvancingAgents(\n agents: ReturnType<typeof listRunningAgentsSync> = listRunningAgentsSync(),\n): number {\n const advancingRows = agents.filter(a => a.role && ADVANCING_ROLES.has(a.role) && a.issueId);\n if (advancingRows.length === 0) return 0;\n const statuses = readReviewStatusMap();\n if (!statuses) return 0;\n let warmIdle = 0;\n for (const row of advancingRows) {\n const status = statuses[row.issueId!.toUpperCase()];\n if (status && isRoleTerminal(row.role as AdvancingRole, status)) warmIdle++;\n }\n return warmIdle;\n}\n\nexport function countRunningAgents(): RunningCounts {\n const counts = countAgentsByStatus('running');\n const workTotal = counts['work'] ?? 0;\n let advancingTotal = 0;\n for (const role of ADVANCING_ROLES) {\n advancingTotal += counts[role] ?? 0;\n }\n // Swarm slots are work-role sessions but draw from the dedicated swarm reserve,\n // so subtract them from `work` (PAN-2212): the swarm neither starves nor is\n // starved by the work/advancing ceiling.\n const swarmSlots = countRunningSwarmSlots();\n const swarm = swarmSlots.active;\n const work = Math.max(0, workTotal - swarmSlots.total);\n // PAN-2579: warm-idle advancing sessions (verdict terminal, kept alive for the\n // next cycle) do not occupy the ceiling.\n const advancing = Math.max(0, advancingTotal - countWarmIdleAdvancingAgents());\n return { work, advancing, swarm, total: work + advancing };\n}\n\nconst GIB = 1024 ** 3;\n\n/**\n * PAN-2504: additional work-agent admissions permitted by the *memory* budget,\n * rather than the fixed `max_work_agents` count. This is what makes the box fill\n * toward its RAM instead of idling at a hardcoded 6.\n *\n * additional = floor((availableBytes - softReserve) / work_footprint)\n * capped by (memory_driven_max_work_agents - runningWork)\n *\n * The SOFT reserve is read from the cached memory verdict's\n * `thresholds.warningBytes` — the governor already publishes it there, so this\n * needs no import of memory-governor.ts (which would close a real dependency\n * cycle; see memory-verdict-cache.ts).\n *\n * Returns null — meaning \"use the count-based cap\" — when memory-driven mode is\n * off OR the governor has not published a verdict yet (fail safe to the fixed\n * cap rather than admitting blind). availableBytes already reflects the RSS of\n * currently-running agents, so `additional` is genuinely how many *more* fit;\n * the per-iteration `assessMemoryPressure()` brake in the deacon loops still\n * catches an under-estimated footprint mid-fill.\n */\nexport function memoryDrivenWorkSlots(runningWork: number): number | null {\n const c = loadCloisterConfigSync().concurrency;\n if (c?.memory_driven !== true) return null;\n const verdict = getCachedMemoryVerdict();\n if (!verdict) return null;\n const perAgentBytes = Math.max(0.25, c?.work_footprint_gb ?? 2) * GIB;\n const budgetBytes = verdict.availableBytes - verdict.thresholds.warningBytes;\n const fits = budgetBytes > 0 ? Math.floor(budgetBytes / perAgentBytes) : 0;\n const ceiling = normalizeCount(c?.memory_driven_max_work_agents, 24, 1);\n return Math.max(0, Math.min(fits, ceiling - runningWork));\n}\n\n/**\n * How many work agents the deacon may resume/spawn this patrol. Zero when already\n * at or over the cap — the deacon then resumes nothing and lets attrition drain.\n *\n * PAN-2504: when `concurrency.memory_driven` is on and the governor has a cached\n * verdict, the ceiling is the memory budget (`memoryDrivenWorkSlots`) instead of\n * the fixed count cap.\n */\nexport function workResumeSlotsAvailable(\n counts: RunningCounts = countRunningAgents(),\n limits: ConcurrencyLimits = getConcurrencyLimits(),\n): number {\n const memSlots = memoryDrivenWorkSlots(counts.work);\n if (memSlots !== null) return memSlots;\n return Math.max(0, limits.maxWorkAgents - counts.work);\n}\n\n/**\n * Whether an advancing-role (review/test/ship) dispatch is allowed. Gated on the\n * overall ceiling so review/test/ship can always claim their reserved headroom —\n * and, per PAN-2500 specialist-budget, on the memory governor: don't reserve GB\n * for a specialist under memory pressure. Count-slot semantics are UNCHANGED\n * when the cached band is 'ok' (or no patrol has assessed memory yet).\n */\nexport function canDispatchAdvancing(\n counts: RunningCounts = countRunningAgents(),\n limits: ConcurrencyLimits = getConcurrencyLimits(),\n): boolean {\n const verdict = getCachedMemoryVerdict();\n if (verdict && verdict.band !== 'ok') return false;\n return counts.total < limits.totalCeiling;\n}\n\n// ---------------------------------------------------------------------------\n// Per-patrol advancing-dispatch reservation\n//\n// countRunningAgents() only sees tmux-alive sessions. Agents dispatched earlier\n// in the SAME patrol haven't registered a session yet, so several dispatch\n// functions (checkOrphanedReviewStatuses, checkMissingReviewStatuses,\n// checkPendingTestDispatch, checkPostReviewCommits) running back-to-back would\n// each see the stale low count and blow past the ceiling. runPatrol() resets\n// this counter at the top of every cycle; each dispatch site reserves a slot.\n// ---------------------------------------------------------------------------\nlet advancingReservedThisPatrol = 0;\n/** Dedicated per-patrol swarm-dispatch budget, isolated from advancing (PAN-2212). */\nlet swarmReservedThisPatrol = 0;\n\n/** Reset the per-patrol dispatch budgets. Called once at patrol start. */\nexport function resetPatrolDispatchBudget(): void {\n advancingReservedThisPatrol = 0;\n swarmReservedThisPatrol = 0;\n}\n\n/**\n * Claim one advancing-role (review/test/ship) dispatch slot for this patrol.\n * Returns false when the total ceiling is reached, or (PAN-2500 specialist-budget)\n * when the memory governor's cached band is not 'ok' — the caller must DEFER\n * (leave status untouched so a later patrol retries), never fail. Counts both\n * tmux-alive agents and advancing dispatches already reserved this patrol.\n */\nexport function tryReserveAdvancingSlot(\n counts: RunningCounts = countRunningAgents(),\n limits: ConcurrencyLimits = getConcurrencyLimits(),\n): boolean {\n const verdict = getCachedMemoryVerdict();\n if (verdict && verdict.band !== 'ok') return false;\n if (counts.total + advancingReservedThisPatrol >= limits.totalCeiling) return false;\n advancingReservedThisPatrol++;\n return true;\n}\n\n/** Release a same-patrol advancing reservation when dispatch was calmly gated. */\nexport function releaseAdvancingSlot(): void {\n advancingReservedThisPatrol = Math.max(0, advancingReservedThisPatrol - 1);\n}\n\n/**\n * Claim one swarm-slot dispatch for this patrol (PAN-2212). Gated ONLY on the\n * dedicated swarm reserve — never the work/advancing ceiling — so a busy pipeline\n * never starves the swarm. Returns false when the reserve is full; the caller\n * DEFERS (leave the item unclaimed so a later patrol retries), never fails.\n */\nexport function tryReserveSwarmSlot(\n counts: RunningCounts = countRunningAgents(),\n limits: ConcurrencyLimits = getConcurrencyLimits(),\n): boolean {\n if (counts.swarm + swarmReservedThisPatrol >= limits.reservedSwarmSlots) return false;\n swarmReservedThisPatrol++;\n return true;\n}\n\n/** Release a same-patrol swarm reservation when dispatch was calmly gated. */\nexport function releaseSwarmSlot(): void {\n swarmReservedThisPatrol = Math.max(0, swarmReservedThisPatrol - 1);\n}\n\n// ---------------------------------------------------------------------------\n// Emergency brake\n//\n// The governor never auto-kills to satisfy a limit, so an over-cap system\n// (forced `pan start`s, an unfreeze backlog) stays over until attrition drains.\n// The emergency brake is the *explicit operator action* that forcibly trims the\n// excess. It is deliberately separate from the nuclear `emergencyStop` (which\n// kills ALL agents): the brake stops only work agents above the cap, idle ones\n// first, and clears the user-stop flag so the deacon re-admits them as slots free\n// (drain-at-cap, not retirement). Never called automatically.\n// ---------------------------------------------------------------------------\nexport interface BrakeResult {\n /** Running work agents before the brake. */\n before: number;\n /** The configured work-agent cap. */\n cap: number;\n /** Agent ids stopped by the brake. */\n stopped: string[];\n /** Running work agents after the brake. */\n remaining: number;\n}\n\nexport function emergencyBrake(): BrakeResult {\n const { maxWorkAgents, exemptOperatorStarted } = getConcurrencyLimits();\n const runningWork = listRunningAgentsSync().filter(a => a.tmuxActive && a.role === 'work');\n const excess = runningWork.length - maxWorkAgents;\n if (excess <= 0) {\n return { before: runningWork.length, cap: maxWorkAgents, stopped: [], remaining: runningWork.length };\n }\n\n // PAN-1812: operator-started work agents (no flywheelRunId) are exempt from\n // automatic governor reaping when the config flag is enabled.\n const candidates = exemptOperatorStarted\n ? runningWork.filter(a => a.flywheelRunId !== undefined && a.flywheelRunId !== null && a.flywheelRunId !== '')\n : runningWork;\n\n // Stop the least-productive first: idle agents ahead of active ones, and among\n // equals the stalest (oldest lastActivity) first.\n const ordered = [...candidates].sort((a, b) => {\n const aIdle = getAgentRuntimeStateSync(a.id)?.state === 'idle' ? 0 : 1;\n const bIdle = getAgentRuntimeStateSync(b.id)?.state === 'idle' ? 0 : 1;\n if (aIdle !== bIdle) return aIdle - bIdle;\n return (Date.parse(a.lastActivity ?? '') || 0) - (Date.parse(b.lastActivity ?? '') || 0);\n });\n\n const stopped: string[] = [];\n for (const agent of ordered.slice(0, excess)) {\n try {\n // 'system' cause (the default) leaves stoppedByUser unset so the deacon\n // re-admits this agent when a slot frees — the brake trims to the cap, it\n // does not retire the work.\n stopAgentSync(agent.id, 'system');\n stopped.push(agent.id);\n } catch {\n // best effort — skip agents that fail to stop cleanly\n }\n }\n\n return {\n before: runningWork.length,\n cap: maxWorkAgents,\n stopped,\n remaining: runningWork.length - stopped.length,\n };\n}\n","export type HostPlatform = 'linux' | 'darwin' | 'unsupported';\n\nexport type HostMetricSignal<T> =\n | { status: 'available'; value: T }\n | { status: 'unavailable'; reason: string };\n\nexport interface CpuCounters {\n idle: number;\n total: number;\n}\n\nexport interface SwapCounters {\n pagesIn: number;\n pagesOut: number;\n}\n\nexport interface HostCollectorCounters {\n cpu: CpuCounters | null;\n swap: SwapCounters | null;\n}\n\nexport interface HostMetricSample {\n platform: HostPlatform;\n sampledAtMs: number;\n cpuPercent: HostMetricSignal<number>;\n loadAverage1m: HostMetricSignal<number>;\n loadPerCore1m: HostMetricSignal<number>;\n totalMemoryBytes: HostMetricSignal<number>;\n usedMemoryBytes: HostMetricSignal<number>;\n availableMemoryBytes: HostMetricSignal<number>;\n memoryUsedPercent: HostMetricSignal<number>;\n memoryPressureSomeAvg10: HostMetricSignal<number>;\n memoryPressureFullAvg10: HostMetricSignal<number>;\n memoryPressureFreePercent: HostMetricSignal<number>;\n swapTotalBytes: HostMetricSignal<number>;\n swapUsedBytes: HostMetricSignal<number>;\n swapUsedPercent: HostMetricSignal<number>;\n swapActivityBytesPerMinute: HostMetricSignal<number>;\n committedMemoryBytes: HostMetricSignal<number>;\n commitLimitBytes: HostMetricSignal<number>;\n virtualCommitmentPercent: HostMetricSignal<number>;\n inotifyWatchesUsed: HostMetricSignal<number>;\n inotifyWatchesMax: HostMetricSignal<number>;\n inotifyWatchesUsedPercent: HostMetricSignal<number>;\n counters: HostCollectorCounters;\n}\n\nexport interface HostHealthCollector {\n readonly platform: HostPlatform;\n sample(previous?: HostMetricSample): Promise<HostMetricSample>;\n}\n\nexport function available<T>(value: T): HostMetricSignal<T> {\n return { status: 'available', value };\n}\n\nexport function unavailable<T = number>(reason: string): HostMetricSignal<T> {\n return { status: 'unavailable', reason };\n}\n","import { execFile } from 'node:child_process';\nimport { cpus, loadavg, totalmem, type CpuInfo } from 'node:os';\nimport { promisify } from 'node:util';\n\nimport {\n available,\n unavailable,\n type CpuCounters,\n type HostHealthCollector,\n type HostMetricSample,\n type HostMetricSignal,\n type SwapCounters,\n} from './types.js';\n\nconst execFileAsync = promisify(execFile);\nconst COMMAND_TIMEOUT_MS = 5_000;\nconst MIB = 1024 ** 2;\n\nexport const DARWIN_MEMORY_PRESSURE_DEFAULTS = Object.freeze({\n warningFreePercent: 10,\n criticalFreePercent: 5,\n swapActivityWarningBytesPerMinute: 64 * MIB,\n swapActivityCriticalBytesPerMinute: 256 * MIB,\n});\n\nexport interface DarwinCollectorAdapters {\n execFile(command: string, args: readonly string[], timeoutMs: number): Promise<string>;\n cpus(): CpuInfo[];\n loadAverage1m(): number;\n totalMemoryBytes(): number;\n now(): number;\n}\n\nexport interface ParsedDarwinVmStat {\n pageSizeBytes: number;\n availableMemoryBytes?: number;\n swap?: SwapCounters;\n}\n\nexport interface ParsedDarwinSwapUsage {\n totalBytes: number;\n usedBytes: number;\n freeBytes: number;\n}\n\nexport function parseDarwinMemoryPressure(content: string): number | null {\n const patterns = [\n /System-wide memory free percentage:\\s*([\\d.]+)\\s*%/i,\n /System-wide memory free percentage\\s*=\\s*([\\d.]+)\\s*%?/i,\n ];\n\n for (const pattern of patterns) {\n const match = content.match(pattern);\n if (!match) continue;\n const value = Number(match[1]);\n if (Number.isFinite(value) && value >= 0 && value <= 100) return value;\n }\n return null;\n}\n\nexport function parseDarwinVmStat(content: string): ParsedDarwinVmStat | null {\n const pageSizeMatch = content.match(/page size of\\s+(\\d+)\\s+bytes/i);\n if (!pageSizeMatch) return null;\n const pageSizeBytes = Number(pageSizeMatch[1]);\n if (!Number.isFinite(pageSizeBytes) || pageSizeBytes <= 0) return null;\n\n const pages = new Map<string, number>();\n for (const line of content.split('\\n')) {\n const match = line.match(/^([^:]+):\\s+(\\d+)\\.?$/);\n if (!match) continue;\n const value = Number(match[2]);\n if (Number.isFinite(value)) pages.set(match[1]!.trim(), value);\n }\n\n const availablePageNames = ['Pages free', 'Pages inactive', 'Pages speculative'];\n const availablePages = availablePageNames.every((name) => pages.has(name))\n ? availablePageNames.reduce((sum, name) => sum + pages.get(name)!, 0)\n : undefined;\n const swapIn = pages.get('Swapins');\n const swapOut = pages.get('Swapouts');\n\n return {\n pageSizeBytes,\n availableMemoryBytes: availablePages == null ? undefined : availablePages * pageSizeBytes,\n swap: swapIn == null || swapOut == null ? undefined : { pagesIn: swapIn, pagesOut: swapOut },\n };\n}\n\nfunction unitBytes(value: string, unit: string): number | null {\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < 0) return null;\n const multiplier = unit.toUpperCase() === 'G'\n ? 1024 ** 3\n : unit.toUpperCase() === 'K'\n ? 1024\n : MIB;\n return Math.round(parsed * multiplier);\n}\n\nexport function parseDarwinSwapUsage(content: string): ParsedDarwinSwapUsage | null {\n const total = content.match(/total\\s*=\\s*([\\d.]+)([KMG])/i);\n const used = content.match(/used\\s*=\\s*([\\d.]+)([KMG])/i);\n const free = content.match(/free\\s*=\\s*([\\d.]+)([KMG])/i);\n if (!total || !used || !free) return null;\n\n const totalBytes = unitBytes(total[1]!, total[2]!);\n const usedBytes = unitBytes(used[1]!, used[2]!);\n const freeBytes = unitBytes(free[1]!, free[2]!);\n return totalBytes == null || usedBytes == null || freeBytes == null\n ? null\n : { totalBytes, usedBytes, freeBytes };\n}\n\nexport function aggregateDarwinCpuCounters(cpuInfo: readonly CpuInfo[]): CpuCounters | null {\n if (cpuInfo.length === 0) return null;\n let idle = 0;\n let total = 0;\n for (const cpu of cpuInfo) {\n const values = Object.values(cpu.times);\n if (values.some((value) => !Number.isFinite(value) || value < 0)) return null;\n idle += cpu.times.idle;\n total += values.reduce((sum, value) => sum + value, 0);\n }\n return { idle, total };\n}\n\nfunction computeCpuPercent(\n current: CpuCounters,\n previous: CpuCounters | null | undefined,\n): HostMetricSignal<number> {\n if (!previous) return unavailable('A previous CPU time sample is required.');\n const totalDelta = current.total - previous.total;\n const idleDelta = current.idle - previous.idle;\n if (totalDelta <= 0 || idleDelta < 0 || idleDelta > totalDelta) {\n return unavailable('CPU times did not advance monotonically.');\n }\n return available(Math.round(((totalDelta - idleDelta) / totalDelta) * 1000) / 10);\n}\n\nfunction computeSwapActivity(\n current: SwapCounters,\n previous: SwapCounters | null | undefined,\n elapsedMs: number,\n pageSizeBytes: number,\n): HostMetricSignal<number> {\n if (!previous) return unavailable('A previous swap counter sample is required.');\n const pages = current.pagesIn - previous.pagesIn + current.pagesOut - previous.pagesOut;\n if (pages < 0 || elapsedMs <= 0) return unavailable('Swap counters or sample timing were invalid.');\n return available(Math.round(pages * pageSizeBytes * 60_000 / elapsedMs));\n}\n\nfunction percent(part: number, total: number): number {\n if (total <= 0) return 0;\n return Math.round((part / total) * 1000) / 10;\n}\n\nconst defaultAdapters: DarwinCollectorAdapters = {\n execFile: async (command, args, timeoutMs) => {\n const { stdout } = await execFileAsync(command, [...args], {\n encoding: 'utf-8',\n timeout: timeoutMs,\n });\n return stdout;\n },\n cpus,\n loadAverage1m: () => loadavg()[0] ?? 0,\n totalMemoryBytes: totalmem,\n now: () => Date.now(),\n};\n\nasync function command(\n adapters: DarwinCollectorAdapters,\n executable: string,\n args: readonly string[],\n): Promise<string | null> {\n try {\n return await adapters.execFile(executable, args, COMMAND_TIMEOUT_MS);\n } catch {\n return null;\n }\n}\n\nexport function createDarwinHostHealthCollector(\n adapters: DarwinCollectorAdapters = defaultAdapters,\n): HostHealthCollector {\n return {\n platform: 'darwin',\n async sample(previous?: HostMetricSample): Promise<HostMetricSample> {\n const sampledAtMs = adapters.now();\n const [pressureOutput, vmStatOutput, swapOutput] = await Promise.all([\n command(adapters, 'memory_pressure', ['-Q']),\n command(adapters, 'vm_stat', []),\n command(adapters, 'sysctl', ['-n', 'vm.swapusage']),\n ]);\n const pressureFreePercent = pressureOutput == null\n ? null\n : parseDarwinMemoryPressure(pressureOutput);\n const vmStat = vmStatOutput == null ? null : parseDarwinVmStat(vmStatOutput);\n const swapUsage = swapOutput == null ? null : parseDarwinSwapUsage(swapOutput);\n const cpuInfo = adapters.cpus();\n const cpuCounters = aggregateDarwinCpuCounters(cpuInfo);\n const totalMemoryBytes = adapters.totalMemoryBytes();\n const availableMemoryBytes = pressureFreePercent != null\n ? Math.round(totalMemoryBytes * pressureFreePercent / 100)\n : vmStat?.availableMemoryBytes ?? null;\n const usedMemoryBytes = Number.isFinite(totalMemoryBytes) && availableMemoryBytes != null\n ? Math.max(totalMemoryBytes - availableMemoryBytes, 0)\n : null;\n const loadAverage1m = adapters.loadAverage1m();\n const elapsedMs = previous ? sampledAtMs - previous.sampledAtMs : 0;\n\n return {\n platform: 'darwin',\n sampledAtMs,\n cpuPercent: cpuCounters\n ? computeCpuPercent(cpuCounters, previous?.counters.cpu)\n : unavailable('macOS CPU times are unavailable.'),\n loadAverage1m: Number.isFinite(loadAverage1m) && loadAverage1m >= 0\n ? available(loadAverage1m)\n : unavailable('The one-minute load average is unavailable.'),\n loadPerCore1m: Number.isFinite(loadAverage1m) && loadAverage1m >= 0\n && cpuInfo.length > 0\n ? available(Math.round((loadAverage1m / cpuInfo.length) * 100) / 100)\n : unavailable('Load average or CPU core count is unavailable.'),\n totalMemoryBytes: Number.isFinite(totalMemoryBytes) && totalMemoryBytes > 0\n ? available(totalMemoryBytes)\n : unavailable('Total memory is unavailable.'),\n usedMemoryBytes: usedMemoryBytes == null\n ? unavailable('Used memory cannot be derived.')\n : available(usedMemoryBytes),\n availableMemoryBytes: availableMemoryBytes == null\n ? unavailable('Available memory is unavailable.')\n : available(availableMemoryBytes),\n memoryUsedPercent: usedMemoryBytes == null\n ? unavailable('Memory utilization cannot be derived.')\n : available(percent(usedMemoryBytes, totalMemoryBytes)),\n memoryPressureSomeAvg10: unavailable('macOS does not expose Linux memory PSI.'),\n memoryPressureFullAvg10: unavailable('macOS does not expose Linux memory PSI.'),\n memoryPressureFreePercent: pressureFreePercent == null\n ? unavailable('memory_pressure did not report a recognized free percentage.')\n : available(pressureFreePercent),\n swapTotalBytes: swapUsage\n ? available(swapUsage.totalBytes)\n : unavailable('vm.swapusage is unavailable.'),\n swapUsedBytes: swapUsage\n ? available(swapUsage.usedBytes)\n : unavailable('vm.swapusage is unavailable.'),\n swapUsedPercent: swapUsage\n ? available(percent(swapUsage.usedBytes, swapUsage.totalBytes))\n : unavailable('vm.swapusage is unavailable.'),\n swapActivityBytesPerMinute: vmStat?.swap\n ? computeSwapActivity(\n vmStat.swap,\n previous?.counters.swap,\n elapsedMs,\n vmStat.pageSizeBytes,\n )\n : unavailable('vm_stat swap activity counters are unavailable.'),\n committedMemoryBytes: unavailable('macOS does not expose Linux Committed_AS.'),\n commitLimitBytes: unavailable('macOS does not expose Linux CommitLimit.'),\n virtualCommitmentPercent: unavailable('macOS virtual commitment is unavailable.'),\n inotifyWatchesUsed: unavailable('inotify is Linux-only.'),\n inotifyWatchesMax: unavailable('inotify is Linux-only.'),\n inotifyWatchesUsedPercent: unavailable('inotify is Linux-only.'),\n counters: {\n cpu: cpuCounters,\n swap: vmStat?.swap ?? null,\n },\n };\n },\n };\n}\n","/**\n * inotify watch/instance accounting for the local user.\n *\n * inotify watches are a per-UID kernel budget (fs.inotify.max_user_watches)\n * shared by every process the user runs — host processes and Docker\n * containers alike, since containers share the kernel and (in Overdeck's\n * setups) the same uid. When the budget is exhausted, any program that\n * tries to add a watch fails with ENOSPC; the classic symptom is a Vite\n * dev server dying at startup while everything else looks healthy\n * (PAN-3063, the 2026-07-25 MIN-898 incident).\n *\n * The kernel exposes no aggregate usage counter, so the only way to measure\n * usage is to walk /proc/<pid>/fd for anon_inode:inotify descriptors and sum\n * the `inotify wd:` lines in the matching fdinfo files. Unprivileged scans\n * can only read same-uid processes, which conveniently matches the per-UID\n * scope of the budget itself.\n */\nimport { readdir, readFile, readlink } from 'node:fs/promises';\n\nexport interface InotifyConsumer {\n pid: number;\n watches: number;\n command: string;\n}\n\nexport interface InotifySample {\n watchesUsed: number;\n watchesMax: number | null;\n instancesUsed: number;\n instancesMax: number | null;\n topConsumers: InotifyConsumer[];\n}\n\nexport interface InotifyScanAdapters {\n readdir(path: string): Promise<string[]>;\n readlink(path: string): Promise<string>;\n readFile(path: string): Promise<string>;\n}\n\nexport const INOTIFY_TOP_CONSUMER_COUNT = 5;\n\n/** Pids scanned concurrently; keeps the /proc walk from monopolizing the fd table. */\nconst PID_SCAN_CONCURRENCY = 32;\n\nconst defaultAdapters: InotifyScanAdapters = {\n readdir: (path) => readdir(path),\n readlink: (path) => readlink(path),\n readFile: (path) => readFile(path, 'utf-8'),\n};\n\n/** Counts `inotify wd:` entries in a /proc/<pid>/fdinfo/<fd> payload. */\nexport function countInotifyWatches(fdinfoContent: string): number {\n let count = 0;\n for (const line of fdinfoContent.split('\\n')) {\n if (line.startsWith('inotify wd:')) count += 1;\n }\n return count;\n}\n\nexport function parseProcLimit(content: string): number | null {\n const value = Number(content.trim());\n return Number.isFinite(value) && value > 0 ? value : null;\n}\n\nasync function readLimit(adapters: InotifyScanAdapters, path: string): Promise<number | null> {\n try {\n return parseProcLimit(await adapters.readFile(path));\n } catch {\n return null;\n }\n}\n\ninterface PidScan {\n pid: number;\n watches: number;\n instances: number;\n}\n\nasync function scanPid(adapters: InotifyScanAdapters, pid: number): Promise<PidScan | null> {\n let fds: string[];\n try {\n fds = await adapters.readdir(`/proc/${pid}/fd`);\n } catch {\n return null; // other-uid process, or it exited mid-scan\n }\n\n let watches = 0;\n let instances = 0;\n for (const fd of fds) {\n let target: string;\n try {\n target = await adapters.readlink(`/proc/${pid}/fd/${fd}`);\n } catch {\n continue;\n }\n if (!target.includes('inotify')) continue;\n instances += 1;\n try {\n watches += countInotifyWatches(await adapters.readFile(`/proc/${pid}/fdinfo/${fd}`));\n } catch {\n // fd closed between readdir and readFile\n }\n }\n if (instances === 0) return null;\n return { pid, watches, instances };\n}\n\nasync function readCommand(adapters: InotifyScanAdapters, pid: number): Promise<string> {\n try {\n const cmdline = await adapters.readFile(`/proc/${pid}/cmdline`);\n const command = cmdline.replaceAll('\\0', ' ').trim();\n return command.length > 120 ? `${command.slice(0, 117)}…` : command;\n } catch {\n return `pid ${pid}`;\n }\n}\n\n/**\n * Walks /proc and totals inotify watches and instances held by processes\n * this user can read. Returns null when /proc is unavailable (non-Linux).\n */\nexport async function sampleInotify(\n adapters: InotifyScanAdapters = defaultAdapters,\n): Promise<InotifySample | null> {\n let procEntries: string[];\n try {\n procEntries = await adapters.readdir('/proc');\n } catch {\n return null;\n }\n const pids = procEntries.filter((entry) => /^\\d+$/.test(entry)).map(Number);\n\n const scans: PidScan[] = [];\n for (let i = 0; i < pids.length; i += PID_SCAN_CONCURRENCY) {\n const chunk = pids.slice(i, i + PID_SCAN_CONCURRENCY);\n const results = await Promise.all(chunk.map((pid) => scanPid(adapters, pid)));\n for (const result of results) {\n if (result) scans.push(result);\n }\n }\n\n const [watchesMax, instancesMax] = await Promise.all([\n readLimit(adapters, '/proc/sys/fs/inotify/max_user_watches'),\n readLimit(adapters, '/proc/sys/fs/inotify/max_user_instances'),\n ]);\n\n const top = [...scans]\n .sort((a, b) => b.watches - a.watches)\n .slice(0, INOTIFY_TOP_CONSUMER_COUNT);\n const topConsumers = await Promise.all(top.map(async (scan) => ({\n pid: scan.pid,\n watches: scan.watches,\n command: await readCommand(adapters, scan.pid),\n })));\n\n return {\n watchesUsed: scans.reduce((sum, scan) => sum + scan.watches, 0),\n watchesMax,\n instancesUsed: scans.reduce((sum, scan) => sum + scan.instances, 0),\n instancesMax,\n topConsumers,\n };\n}\n","import { execFile } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { promisify } from 'node:util';\n\nimport { sampleInotify, type InotifySample } from './inotify.js';\nimport {\n available,\n unavailable,\n type CpuCounters,\n type HostHealthCollector,\n type HostMetricSample,\n type HostMetricSignal,\n type SwapCounters,\n} from './types.js';\n\nconst execFileAsync = promisify(execFile);\nconst KB = 1024;\n\nexport const LINUX_MEMORY_PRESSURE_DEFAULTS = Object.freeze({\n someWarningAvg10: 5,\n fullCriticalAvg10: 1,\n swapActivityWarningBytesPerMinute: 64 * 1024 ** 2,\n swapActivityCriticalBytesPerMinute: 256 * 1024 ** 2,\n});\n\nexport interface LinuxCollectorAdapters {\n readFile(path: string): Promise<string>;\n readPageSizeBytes(): Promise<number>;\n now(): number;\n sampleInotify(): Promise<InotifySample | null>;\n}\n\n/**\n * The inotify scan walks every readable /proc/<pid>/fd entry, which is far\n * heavier than the single-file reads above — so it refreshes on its own\n * slower cadence and the collector serves the cached value in between.\n */\nexport const INOTIFY_REFRESH_MS = 60_000;\n\nexport interface ParsedMemInfo {\n memTotalBytes?: number;\n memAvailableBytes?: number;\n swapTotalBytes?: number;\n swapFreeBytes?: number;\n committedMemoryBytes?: number;\n commitLimitBytes?: number;\n}\n\nexport interface ParsedProcStat {\n cpu: CpuCounters;\n coreCount: number;\n}\n\nexport interface ParsedMemoryPressure {\n someAvg10?: number;\n fullAvg10?: number;\n}\n\nexport function parseMemInfo(content: string): ParsedMemInfo {\n const values = new Map<string, number>();\n for (const line of content.split('\\n')) {\n const match = line.match(/^(\\w+):\\s+(\\d+)\\s+kB$/);\n if (!match) continue;\n const value = Number(match[2]);\n if (Number.isFinite(value)) values.set(match[1]!, value * KB);\n }\n\n return {\n memTotalBytes: values.get('MemTotal'),\n memAvailableBytes: values.get('MemAvailable'),\n swapTotalBytes: values.get('SwapTotal'),\n swapFreeBytes: values.get('SwapFree'),\n committedMemoryBytes: values.get('Committed_AS'),\n commitLimitBytes: values.get('CommitLimit'),\n };\n}\n\nexport function parseProcStat(content: string): ParsedProcStat | null {\n const lines = content.split('\\n');\n const cpuLine = lines.find((line) => line.startsWith('cpu '));\n if (!cpuLine) return null;\n\n const values = cpuLine.trim().split(/\\s+/).slice(1).map(Number);\n if (values.length < 4 || values.some((value) => !Number.isFinite(value))) return null;\n\n const idle = values[3]! + (values[4] ?? 0);\n const total = values.reduce((sum, value) => sum + value, 0);\n const coreCount = lines.filter((line) => /^cpu\\d+\\s/.test(line)).length;\n if (total < 0 || idle < 0) return null;\n\n return {\n cpu: { idle, total },\n coreCount: Math.max(coreCount, 1),\n };\n}\n\nexport function parseMemoryPressure(content: string): ParsedMemoryPressure {\n const result: ParsedMemoryPressure = {};\n for (const line of content.split('\\n')) {\n const match = line.match(/^(some|full)\\s+.*\\bavg10=([^\\s]+)/);\n if (!match) continue;\n const value = Number(match[2]);\n if (!Number.isFinite(value) || value < 0) continue;\n if (match[1] === 'some') result.someAvg10 = value;\n if (match[1] === 'full') result.fullAvg10 = value;\n }\n return result;\n}\n\nexport function parseVmstat(content: string): SwapCounters | null {\n let pagesIn: number | null = null;\n let pagesOut: number | null = null;\n\n for (const line of content.split('\\n')) {\n const match = line.match(/^(pswpin|pswpout)\\s+(\\d+)$/);\n if (!match) continue;\n const value = Number(match[2]);\n if (!Number.isFinite(value)) continue;\n if (match[1] === 'pswpin') pagesIn = value;\n if (match[1] === 'pswpout') pagesOut = value;\n }\n\n return pagesIn == null || pagesOut == null ? null : { pagesIn, pagesOut };\n}\n\nexport function parseLoadAverage(content: string): number | null {\n const value = Number(content.trim().split(/\\s+/)[0]);\n return Number.isFinite(value) && value >= 0 ? value : null;\n}\n\nexport function computeCpuPercent(\n current: CpuCounters,\n previous: CpuCounters | null | undefined,\n): HostMetricSignal<number> {\n if (!previous) return unavailable('A previous CPU counter sample is required.');\n const totalDelta = current.total - previous.total;\n const idleDelta = current.idle - previous.idle;\n if (totalDelta <= 0 || idleDelta < 0 || idleDelta > totalDelta) {\n return unavailable('CPU counters did not advance monotonically.');\n }\n return available(Math.round(((totalDelta - idleDelta) / totalDelta) * 1000) / 10);\n}\n\nexport function computeSwapActivityBytesPerMinute(\n current: SwapCounters,\n previous: SwapCounters | null | undefined,\n elapsedMs: number,\n pageSizeBytes: number,\n): HostMetricSignal<number> {\n if (!previous) return unavailable('A previous swap counter sample is required.');\n const pageDelta = current.pagesIn - previous.pagesIn + current.pagesOut - previous.pagesOut;\n if (pageDelta < 0 || elapsedMs <= 0 || !Number.isFinite(pageSizeBytes) || pageSizeBytes <= 0) {\n return unavailable('Swap counters or sample timing were invalid.');\n }\n return available(Math.round(pageDelta * pageSizeBytes * 60_000 / elapsedMs));\n}\n\nconst defaultAdapters: LinuxCollectorAdapters = {\n readFile: (path) => readFile(path, 'utf-8'),\n readPageSizeBytes: async () => {\n const { stdout } = await execFileAsync('getconf', ['PAGESIZE'], {\n encoding: 'utf-8',\n timeout: 5_000,\n });\n const value = Number(stdout.trim());\n if (!Number.isFinite(value) || value <= 0) throw new Error('getconf returned an invalid page size');\n return value;\n },\n now: () => Date.now(),\n sampleInotify: () => sampleInotify(),\n};\n\nasync function readParsed<T>(\n adapters: LinuxCollectorAdapters,\n path: string,\n parse: (content: string) => T,\n): Promise<T | null> {\n try {\n return parse(await adapters.readFile(path));\n } catch {\n return null;\n }\n}\n\nfunction percent(part: number, total: number): number {\n if (total <= 0) return 0;\n return Math.round((part / total) * 1000) / 10;\n}\n\nexport function createLinuxHostHealthCollector(\n adapters: LinuxCollectorAdapters = defaultAdapters,\n): HostHealthCollector {\n let inotifyCache: { atMs: number; sample: InotifySample | null } | null = null;\n\n return {\n platform: 'linux',\n async sample(previous?: HostMetricSample): Promise<HostMetricSample> {\n const sampledAtMs = adapters.now();\n if (!inotifyCache || sampledAtMs - inotifyCache.atMs >= INOTIFY_REFRESH_MS) {\n inotifyCache = {\n atMs: sampledAtMs,\n sample: await adapters.sampleInotify().catch(() => null),\n };\n }\n const inotify = inotifyCache.sample;\n const [memInfo, procStat, pressure, vmstat, loadAverage, pageSizeBytes] = await Promise.all([\n readParsed(adapters, '/proc/meminfo', parseMemInfo),\n readParsed(adapters, '/proc/stat', parseProcStat),\n readParsed(adapters, '/proc/pressure/memory', parseMemoryPressure),\n readParsed(adapters, '/proc/vmstat', parseVmstat),\n readParsed(adapters, '/proc/loadavg', parseLoadAverage),\n adapters.readPageSizeBytes().catch(() => null),\n ]);\n\n const totalMemoryBytes = memInfo?.memTotalBytes;\n const availableMemoryBytes = memInfo?.memAvailableBytes;\n const usedMemoryBytes = totalMemoryBytes != null && availableMemoryBytes != null\n ? Math.max(totalMemoryBytes - availableMemoryBytes, 0)\n : null;\n const swapTotalBytes = memInfo?.swapTotalBytes;\n const swapFreeBytes = memInfo?.swapFreeBytes;\n const swapUsedBytes = swapTotalBytes != null && swapFreeBytes != null\n ? Math.max(swapTotalBytes - swapFreeBytes, 0)\n : null;\n const committedMemoryBytes = memInfo?.committedMemoryBytes;\n const commitLimitBytes = memInfo?.commitLimitBytes;\n const cpuCounters = procStat?.cpu ?? null;\n const swapCounters = vmstat ?? null;\n const elapsedMs = previous ? sampledAtMs - previous.sampledAtMs : 0;\n\n return {\n platform: 'linux',\n sampledAtMs,\n cpuPercent: cpuCounters\n ? computeCpuPercent(cpuCounters, previous?.counters.cpu)\n : unavailable('The aggregate CPU counters are unavailable.'),\n loadAverage1m: loadAverage == null\n ? unavailable('The one-minute load average is unavailable.')\n : available(loadAverage),\n loadPerCore1m: loadAverage == null || !procStat\n ? unavailable('Load average or CPU core count is unavailable.')\n : available(Math.round((loadAverage / procStat.coreCount) * 100) / 100),\n totalMemoryBytes: totalMemoryBytes == null\n ? unavailable('MemTotal is unavailable.')\n : available(totalMemoryBytes),\n usedMemoryBytes: usedMemoryBytes == null\n ? unavailable('Used memory cannot be derived without MemTotal and MemAvailable.')\n : available(usedMemoryBytes),\n availableMemoryBytes: availableMemoryBytes == null\n ? unavailable('MemAvailable is unavailable.')\n : available(availableMemoryBytes),\n memoryUsedPercent: usedMemoryBytes == null || totalMemoryBytes == null\n ? unavailable('Memory utilization cannot be derived.')\n : available(percent(usedMemoryBytes, totalMemoryBytes)),\n memoryPressureSomeAvg10: pressure?.someAvg10 == null\n ? unavailable('Linux memory PSI some.avg10 is unavailable.')\n : available(pressure.someAvg10),\n memoryPressureFullAvg10: pressure?.fullAvg10 == null\n ? unavailable('Linux memory PSI full.avg10 is unavailable.')\n : available(pressure.fullAvg10),\n memoryPressureFreePercent: unavailable('Linux reports memory pressure through PSI.'),\n swapTotalBytes: swapTotalBytes == null\n ? unavailable('SwapTotal is unavailable.')\n : available(swapTotalBytes),\n swapUsedBytes: swapUsedBytes == null\n ? unavailable('Swap usage cannot be derived without SwapTotal and SwapFree.')\n : available(swapUsedBytes),\n swapUsedPercent: swapUsedBytes == null || swapTotalBytes == null\n ? unavailable('Swap utilization cannot be derived.')\n : available(percent(swapUsedBytes, swapTotalBytes)),\n swapActivityBytesPerMinute: swapCounters && pageSizeBytes != null\n ? computeSwapActivityBytesPerMinute(\n swapCounters,\n previous?.counters.swap,\n elapsedMs,\n pageSizeBytes,\n )\n : unavailable('Linux swap activity counters are unavailable.'),\n committedMemoryBytes: committedMemoryBytes == null\n ? unavailable('Committed_AS is unavailable.')\n : available(committedMemoryBytes),\n commitLimitBytes: commitLimitBytes == null\n ? unavailable('CommitLimit is unavailable.')\n : available(commitLimitBytes),\n virtualCommitmentPercent: committedMemoryBytes == null || commitLimitBytes == null\n ? unavailable('Virtual commitment cannot be derived.')\n : available(percent(committedMemoryBytes, commitLimitBytes)),\n inotifyWatchesUsed: inotify == null\n ? unavailable('inotify usage could not be scanned from /proc.')\n : available(inotify.watchesUsed),\n inotifyWatchesMax: inotify?.watchesMax == null\n ? unavailable('fs.inotify.max_user_watches is unavailable.')\n : available(inotify.watchesMax),\n inotifyWatchesUsedPercent: inotify == null || inotify.watchesMax == null\n ? unavailable('inotify watch utilization cannot be derived.')\n : available(percent(inotify.watchesUsed, inotify.watchesMax)),\n counters: {\n cpu: cpuCounters,\n swap: swapCounters,\n },\n };\n },\n };\n}\n","/**\n * Selects the platform host-health collector. Linux and macOS expose native\n * current-pressure signals; unsupported platforms return unavailable signals\n * so callers never mistake missing measurements for measured zeroes.\n */\nimport { platform as getPlatform } from 'node:os';\n\nimport { createDarwinHostHealthCollector } from './darwin.js';\nimport { createLinuxHostHealthCollector } from './linux.js';\nimport {\n unavailable,\n type HostHealthCollector,\n type HostMetricSample,\n type HostPlatform,\n} from './types.js';\n\nexport interface ProcessTreeRow {\n pid: number;\n ppid: number;\n}\n\nexport interface ProcessTreeIndex {\n childrenByParent: ReadonlyMap<number, readonly number[]>;\n}\n\nexport function buildProcessTreeIndex(rows: Iterable<ProcessTreeRow>): ProcessTreeIndex {\n const childrenByParent = new Map<number, number[]>();\n for (const row of rows) {\n const children = childrenByParent.get(row.ppid);\n if (children) children.push(row.pid);\n else childrenByParent.set(row.ppid, [row.pid]);\n }\n return { childrenByParent };\n}\n\nexport function collectDescendantPids(rootPid: number, index: ProcessTreeIndex): Set<number> {\n const descendants = new Set<number>();\n const queue = [rootPid];\n\n while (queue.length > 0) {\n const pid = queue.shift();\n if (pid == null || descendants.has(pid)) continue;\n descendants.add(pid);\n queue.push(...(index.childrenByParent.get(pid) ?? []));\n }\n\n return descendants;\n}\n\nfunction createUnsupportedCollector(platform: HostPlatform = 'unsupported'): HostHealthCollector {\n const signal = () => unavailable('Host health collection is unsupported on this platform.');\n return {\n platform,\n async sample(): Promise<HostMetricSample> {\n return {\n platform,\n sampledAtMs: Date.now(),\n cpuPercent: signal(),\n loadAverage1m: signal(),\n loadPerCore1m: signal(),\n totalMemoryBytes: signal(),\n usedMemoryBytes: signal(),\n availableMemoryBytes: signal(),\n memoryUsedPercent: signal(),\n memoryPressureSomeAvg10: signal(),\n memoryPressureFullAvg10: signal(),\n memoryPressureFreePercent: signal(),\n swapTotalBytes: signal(),\n swapUsedBytes: signal(),\n swapUsedPercent: signal(),\n swapActivityBytesPerMinute: signal(),\n committedMemoryBytes: signal(),\n commitLimitBytes: signal(),\n virtualCommitmentPercent: signal(),\n inotifyWatchesUsed: signal(),\n inotifyWatchesMax: signal(),\n inotifyWatchesUsedPercent: signal(),\n counters: { cpu: null, swap: null },\n };\n },\n };\n}\n\nexport interface HostCollectorSelection {\n platform?: NodeJS.Platform;\n linux?: HostHealthCollector;\n darwin?: HostHealthCollector;\n}\n\nexport function createHostHealthCollector(\n selection: HostCollectorSelection = {},\n): HostHealthCollector {\n const platform = selection.platform ?? getPlatform();\n if (platform === 'linux') return selection.linux ?? createLinuxHostHealthCollector();\n if (platform === 'darwin') return selection.darwin ?? createDarwinHostHealthCollector();\n return createUnsupportedCollector();\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { Effect } from 'effect';\n\nimport { buildProcessTreeIndex, collectDescendantPids, type ProcessTreeIndex } from './system-health/collector.js';\nimport type { TmuxPaneRecord } from './tmux.js';\n\nconst execFileAsync = promisify(execFile);\nexport const RUNTIME_CENSUS_TTL_MS = 3_000;\n\nconst KEEPALIVE_FOREGROUND_COMMANDS = new Set(['sleep', 'bash', 'sh', 'dash', 'zsh', 'ash']);\n\nexport interface RuntimeProcessRecord {\n pid: number;\n ppid: number;\n cpuPercent: number;\n rssBytes: number;\n comm: string;\n command: string;\n}\n\nexport interface RuntimeCensus {\n sampledAt: number;\n available: boolean;\n tmuxAvailable: boolean;\n processAvailable: boolean;\n stale: boolean;\n error: string | null;\n panes: readonly TmuxPaneRecord[];\n sessionNames: ReadonlySet<string>;\n panesBySession: ReadonlyMap<string, readonly TmuxPaneRecord[]>;\n processesByPid: ReadonlyMap<number, RuntimeProcessRecord>;\n processTree: ProcessTreeIndex;\n}\n\nexport interface RuntimeCensusServiceDeps {\n listPanes(): Promise<readonly TmuxPaneRecord[]>;\n readProcesses(): Promise<readonly RuntimeProcessRecord[]>;\n now?: () => number;\n ttlMs?: number;\n}\n\nexport interface RuntimeCensusService {\n get(options?: { fresh?: boolean }): Promise<RuntimeCensus>;\n peek(): RuntimeCensus | null;\n reset(): void;\n}\n\nexport function parseRuntimeProcessTable(output: string): RuntimeProcessRecord[] {\n return output.split('\\n').flatMap((line) => {\n const match = line.trim().match(/^(\\d+)\\s+(\\d+)\\s+([0-9.]+)\\s+(\\d+)\\s+(\\S+)(?:\\s+(.*))?$/);\n if (!match) return [];\n const pid = Number(match[1]);\n const ppid = Number(match[2]);\n const cpuPercent = Number(match[3]);\n const rssKb = Number(match[4]);\n if (![pid, ppid, cpuPercent, rssKb].every(Number.isFinite)) return [];\n return [{\n pid,\n ppid,\n cpuPercent,\n rssBytes: rssKb * 1024,\n comm: match[5] ?? '',\n command: match[6] ?? match[5] ?? '',\n }];\n });\n}\n\nasync function readRuntimeProcessTable(): Promise<RuntimeProcessRecord[]> {\n const { stdout } = await execFileAsync(\n 'ps',\n ['-eo', 'pid=,ppid=,pcpu=,rss=,comm=,args='],\n { encoding: 'utf8', timeout: 10_000 },\n );\n return parseRuntimeProcessTable(String(stdout));\n}\n\nfunction buildRuntimeCensus(\n panes: readonly TmuxPaneRecord[],\n processes: readonly RuntimeProcessRecord[],\n sampledAt: number,\n options: {\n tmuxAvailable: boolean;\n processAvailable: boolean;\n stale?: boolean;\n error?: string | null;\n },\n): RuntimeCensus {\n const panesBySession = new Map<string, TmuxPaneRecord[]>();\n for (const pane of panes) {\n const sessionPanes = panesBySession.get(pane.sessionName);\n if (sessionPanes) sessionPanes.push(pane);\n else panesBySession.set(pane.sessionName, [pane]);\n }\n const processesByPid = new Map(processes.map((process) => [process.pid, process]));\n return {\n sampledAt,\n available: options.tmuxAvailable,\n tmuxAvailable: options.tmuxAvailable,\n processAvailable: options.processAvailable,\n stale: options.stale ?? false,\n error: options.error ?? null,\n panes,\n sessionNames: new Set(panesBySession.keys()),\n panesBySession,\n processesByPid,\n processTree: buildProcessTreeIndex(processes),\n };\n}\n\nexport function createRuntimeCensusService(deps: RuntimeCensusServiceDeps): RuntimeCensusService {\n const now = deps.now ?? Date.now;\n const ttlMs = deps.ttlMs ?? RUNTIME_CENSUS_TTL_MS;\n let snapshot: RuntimeCensus | null = null;\n let inFlight: Promise<RuntimeCensus> | null = null;\n\n const refresh = (): Promise<RuntimeCensus> => {\n if (inFlight) return inFlight;\n inFlight = Promise.allSettled([deps.listPanes(), deps.readProcesses()])\n .then(([panesResult, processesResult]) => {\n const errors: string[] = [];\n if (panesResult.status === 'rejected') {\n errors.push(panesResult.reason instanceof Error ? panesResult.reason.message : String(panesResult.reason));\n }\n if (processesResult.status === 'rejected') {\n errors.push(processesResult.reason instanceof Error ? processesResult.reason.message : String(processesResult.reason));\n }\n const panes = panesResult.status === 'fulfilled'\n ? panesResult.value\n : snapshot?.panes ?? [];\n const processes = processesResult.status === 'fulfilled'\n ? processesResult.value\n : [...(snapshot?.processesByPid.values() ?? [])];\n snapshot = buildRuntimeCensus(panes, processes, now(), {\n tmuxAvailable: panesResult.status === 'fulfilled',\n processAvailable: processesResult.status === 'fulfilled',\n stale: errors.length > 0,\n error: errors.length > 0 ? errors.join('; ') : null,\n });\n return snapshot;\n })\n .finally(() => {\n inFlight = null;\n });\n return inFlight;\n };\n\n return {\n get(options) {\n if (!options?.fresh && snapshot && now() - snapshot.sampledAt < ttlMs) {\n return Promise.resolve(snapshot);\n }\n return refresh();\n },\n peek() {\n return snapshot;\n },\n reset() {\n snapshot = null;\n inFlight = null;\n },\n };\n}\n\nconst runtimeCensusService = createRuntimeCensusService({\n listPanes: async () => {\n const { listAllPaneRecords } = await import('./tmux.js');\n return Effect.runPromise(listAllPaneRecords());\n },\n readProcesses: readRuntimeProcessTable,\n});\n\nexport function getRuntimeCensus(options?: { fresh?: boolean }): Promise<RuntimeCensus> {\n return runtimeCensusService.get(options);\n}\n\nexport function refreshRuntimeCensus(): Promise<RuntimeCensus> {\n return runtimeCensusService.get({ fresh: true });\n}\n\nexport function getRuntimeCensusSnapshot(): RuntimeCensus | null {\n return runtimeCensusService.peek();\n}\n\nexport function resetRuntimeCensusForTests(): void {\n runtimeCensusService.reset();\n}\n\nexport function panePidsForSession(census: RuntimeCensus, sessionName: string): number[] {\n return (census.panesBySession.get(sessionName) ?? []).map((pane) => pane.panePid);\n}\n\nexport function descendantPidsForSession(census: RuntimeCensus, sessionName: string): Set<number> {\n const descendants = new Set<number>();\n for (const panePid of panePidsForSession(census, sessionName)) {\n for (const pid of collectDescendantPids(panePid, census.processTree)) descendants.add(pid);\n }\n return descendants;\n}\n\nexport function runtimeCensusHasHarnessProcess(census: RuntimeCensus, sessionName: string): boolean {\n if (!census.tmuxAvailable || !census.processAvailable) return true;\n const panePids = panePidsForSession(census, sessionName);\n if (panePids.length === 0) return false;\n for (const panePid of panePids) {\n for (const pid of collectDescendantPids(panePid, census.processTree)) {\n const comm = census.processesByPid.get(pid)?.comm;\n if (comm && !KEEPALIVE_FOREGROUND_COMMANDS.has(comm)) return true;\n }\n }\n return false;\n}\n","import { lstat, readdir, realpath, rmdir, unlink, writeFile } from 'node:fs/promises';\nimport { isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nimport { AGENTS_DIR } from '../paths.js';\n\nexport interface RemoveAgentStateDirResult {\n removedFiles: number;\n preservedTranscripts: number;\n /** true when the dir was fully removed (no transcripts existed) */\n removedDir: boolean;\n}\n\nexport const RETAINED_TRANSCRIPTS_MARKER = '.retained-transcripts';\n\nfunction hasErrorCode(error: unknown, code: string): boolean {\n return (error as NodeJS.ErrnoException).code === code;\n}\n\nfunction isContained(root: string, candidate: string): boolean {\n const pathFromRoot = relative(root, candidate);\n return pathFromRoot !== '' &&\n pathFromRoot !== '..' &&\n !pathFromRoot.startsWith(`..${sep}`) &&\n !isAbsolute(pathFromRoot);\n}\n\nfunction assertContained(root: string, candidate: string): void {\n if (!isContained(root, candidate)) {\n throw new Error(`removeAgentStateDir: path escapes AGENTS_DIR: ${candidate}`);\n }\n}\n\nasync function cleanDirectory(\n dirPath: string,\n canonicalRoot: string,\n isRoot: boolean,\n): Promise<RemoveAgentStateDirResult> {\n let pathStat;\n try {\n pathStat = await lstat(dirPath);\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) {\n return { removedFiles: 0, preservedTranscripts: 0, removedDir: true };\n }\n throw error;\n }\n\n if (pathStat.isSymbolicLink()) {\n if (isRoot) {\n throw new Error(`removeAgentStateDir: refusing symbolic-link root: ${dirPath}`);\n }\n await unlink(dirPath);\n return { removedFiles: 1, preservedTranscripts: 0, removedDir: true };\n }\n if (!pathStat.isDirectory()) {\n throw new Error(`removeAgentStateDir: expected directory: ${dirPath}`);\n }\n\n const canonicalDir = await realpath(dirPath);\n assertContained(canonicalRoot, canonicalDir);\n const entries = await readdir(dirPath, { withFileTypes: true });\n let removedFiles = 0;\n let preservedTranscripts = 0;\n\n for (const entry of entries) {\n const entryPath = join(dirPath, entry.name);\n if (entry.isSymbolicLink()) {\n await unlink(entryPath);\n removedFiles += 1;\n } else if (entry.isDirectory()) {\n const result = await cleanDirectory(entryPath, canonicalRoot, false);\n removedFiles += result.removedFiles;\n preservedTranscripts += result.preservedTranscripts;\n } else if (entry.name.endsWith('.jsonl')) {\n preservedTranscripts += 1;\n } else {\n await unlink(entryPath);\n removedFiles += 1;\n }\n }\n\n try {\n await rmdir(dirPath);\n return { removedFiles, preservedTranscripts, removedDir: true };\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) {\n return { removedFiles, preservedTranscripts, removedDir: true };\n }\n if (hasErrorCode(error, 'ENOTEMPTY') || hasErrorCode(error, 'EEXIST')) {\n return { removedFiles, preservedTranscripts, removedDir: false };\n }\n throw error;\n }\n}\n\nexport async function hasRetainedTranscriptsMarker(dirPath: string): Promise<boolean> {\n try {\n return (await lstat(join(dirPath, RETAINED_TRANSCRIPTS_MARKER))).isFile();\n } catch {\n return false;\n }\n}\n\nexport async function markRetainedTranscripts(dirPath: string): Promise<void> {\n try {\n await writeFile(join(dirPath, RETAINED_TRANSCRIPTS_MARKER), '', { flag: 'wx' });\n } catch (error) {\n if (!hasErrorCode(error, 'EEXIST') && !hasErrorCode(error, 'ENOENT')) throw error;\n }\n}\n\nasync function collectAgentStateFilesForRemoval(\n dirPath: string,\n canonicalRoot: string,\n candidateRoot: string,\n): Promise<string[]> {\n const canonicalDir = await realpath(dirPath);\n assertContained(canonicalRoot, canonicalDir);\n const entries = await readdir(dirPath, { withFileTypes: true });\n const files: string[] = [];\n for (const entry of entries) {\n const entryPath = join(dirPath, entry.name);\n if (entry.isSymbolicLink()) {\n files.push(relative(candidateRoot, entryPath));\n } else if (entry.isDirectory()) {\n files.push(...await collectAgentStateFilesForRemoval(\n entryPath,\n canonicalRoot,\n candidateRoot,\n ));\n } else if (!entry.name.endsWith('.jsonl')) {\n files.push(relative(candidateRoot, entryPath));\n }\n }\n return files;\n}\n\n/** List the runtime files the sanctioned cleanup door would remove. */\nexport async function listAgentStateFilesForRemoval(\n dirPath: string,\n agentsRootPath: string = AGENTS_DIR,\n): Promise<string[]> {\n const agentsRoot = resolve(agentsRootPath);\n const candidate = resolve(dirPath);\n assertContained(agentsRoot, candidate);\n if (relative(agentsRoot, candidate).includes(sep)) {\n throw new Error(`listAgentStateFilesForRemoval: expected direct child of AGENTS_DIR: ${candidate}`);\n }\n\n try {\n const candidateStat = await lstat(candidate);\n if (candidateStat.isSymbolicLink()) {\n throw new Error(`listAgentStateFilesForRemoval: refusing symbolic-link root: ${candidate}`);\n }\n if (!candidateStat.isDirectory()) {\n throw new Error(`listAgentStateFilesForRemoval: expected directory: ${candidate}`);\n }\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return [];\n throw error;\n }\n\n const [canonicalRoot, canonicalCandidate] = await Promise.all([\n realpath(agentsRoot),\n realpath(candidate),\n ]);\n assertContained(canonicalRoot, canonicalCandidate);\n if (relative(canonicalRoot, canonicalCandidate).includes(sep)) {\n throw new Error(`listAgentStateFilesForRemoval: canonical path is not a direct child of AGENTS_DIR: ${canonicalCandidate}`);\n }\n return (await collectAgentStateFilesForRemoval(\n candidate,\n canonicalRoot,\n candidate,\n )).sort();\n}\n\n/**\n * The ONLY sanctioned way to delete or clean an agent state directory\n * (PAN-3357). Deletes runtime residue; preserves every transcript artifact\n * (any regular file matching **\\/*.jsonl) in place, pruning empty\n * subdirectories that held only residue. Symbolic links are never traversed.\n */\nexport async function removeAgentStateDir(\n dirPath: string,\n agentsRootPath: string = AGENTS_DIR,\n): Promise<RemoveAgentStateDirResult> {\n const agentsRoot = resolve(agentsRootPath);\n const candidate = resolve(dirPath);\n assertContained(agentsRoot, candidate);\n if (relative(agentsRoot, candidate).includes(sep)) {\n throw new Error(`removeAgentStateDir: expected direct child of AGENTS_DIR: ${candidate}`);\n }\n\n let candidateStat;\n try {\n candidateStat = await lstat(candidate);\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) {\n return { removedFiles: 0, preservedTranscripts: 0, removedDir: true };\n }\n throw error;\n }\n if (candidateStat.isSymbolicLink()) {\n throw new Error(`removeAgentStateDir: refusing symbolic-link root: ${candidate}`);\n }\n if (!candidateStat.isDirectory()) {\n throw new Error(`removeAgentStateDir: expected directory: ${candidate}`);\n }\n\n const [canonicalRoot, canonicalCandidate] = await Promise.all([\n realpath(agentsRoot),\n realpath(candidate),\n ]);\n assertContained(canonicalRoot, canonicalCandidate);\n if (relative(canonicalRoot, canonicalCandidate).includes(sep)) {\n throw new Error(`removeAgentStateDir: canonical path is not a direct child of AGENTS_DIR: ${canonicalCandidate}`);\n }\n\n return cleanDirectory(candidate, canonicalRoot, true);\n}\n","import { existsSync } from 'fs';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport { FsError, TmuxError } from '../errors.js';\nimport type { AgentStatus } from '@overdeck/contracts';\nimport type { AgentState, Role } from '../agents.js';\nimport { getAgentState, isRole, normalizeAgentId } from '../agents.js';\nimport { killSession, listSessionsSync } from '../tmux.js';\nimport { getRuntimeCensus, getRuntimeCensusSnapshot } from '../runtime-census.js';\nimport { AGENTS_DIR } from '../paths.js';\nimport { getRollbackAgentStatePath } from '../overdeck/agent-rollback-state.js';\nimport { listOverdeckAgentStatesSync } from '../overdeck/agent-state-sync.js';\nimport { removeAgentStateDir } from './state-dir-removal.js';\n\nexport function listRunningAgentsSync(): (AgentState & { tmuxActive: boolean })[] {\n // Match liveness against ALL overdeck-socket sessions, not just `agent-*`.\n // Agent state dirs are named by role prefix (planning-/agent-/conv-/strike-);\n // getAgentSessions only returns `agent-*`, so planning/conv/strike sessions\n // would always read tmuxActive:false and get dropped by the enrichment poller.\n const census = getRuntimeCensusSnapshot();\n const tmuxNames = census\n ? census.sessionNames\n : new Set(listSessionsSync().map((session) => session.name));\n const tmuxUnavailable = census?.tmuxAvailable === false;\n\n return listOverdeckAgentStatesSync().map((state) => {\n const normalizedId = normalizeAgentId(state.id);\n return {\n ...state,\n id: normalizedId,\n tmuxActive: tmuxUnavailable || tmuxNames.has(normalizedId),\n };\n });\n}\n\n/**\n * PAN-1908: list all agents in the SQLite registry with optional filtering.\n * This is the replacement for enumerating ~/.overdeck/agents/ directories.\n */\nexport function listAgentStates(options?: { status?: AgentStatus; role?: Role }): AgentState[] {\n return listOverdeckAgentStatesSync()\n .filter((state) => {\n if (options?.status && state.status !== options.status) return false;\n if (options?.role && state.role !== options.role) return false;\n return true;\n });\n}\n\n\nexport const listRunningAgents = (): Effect.Effect<(AgentState & { tmuxActive: boolean })[], FsError | TmuxError> =>\n Effect.gen(function* () {\n // PAN-1908: authoritative registry is the SQLite agents table; no directory scan.\n //\n // TRAP — `tmuxActive` reflects whether THIS process can see the agent's tmux\n // session on the `overdeck` socket. A one-off `tsx -e`/CLI process may not;\n // unavailable census evidence deliberately fails open rather than declaring\n // every agent dead. Trust the dashboard server's view (it owns the socket) or\n // check the tmux session directly with `tmux -L overdeck list-sessions`.\n //\n // Use the UNFILTERED session list (not getAgentSessions, which is `agent-*`\n // only): agent state dirs carry role prefixes (planning-/agent-/conv-/strike-),\n // and planning/conv/strike sessions must read tmuxActive:true so the\n // enrichment poller scans them for AskUserQuestion / pending input (PAN-1395).\n const runtimeCensus = yield* Effect.promise(() => getRuntimeCensus());\n const tmuxNames = runtimeCensus.sessionNames;\n\n return listOverdeckAgentStatesSync().map((state) => {\n const normalizedId = normalizeAgentId(state.id);\n return {\n ...state,\n id: normalizedId,\n tmuxActive: !runtimeCensus.tmuxAvailable || tmuxNames.has(normalizedId),\n };\n });\n });\n\n/**\n * PAN-1048 P2: async startup migration.\n *\n * The previous synchronous version used readdirSync, readFileSync,\n * killSession (sync tmux subprocess), and rmSync — all on the Node\n * event loop. Called from warnOnBareNumericIssueIds() during dashboard\n * read-model bootstrap, this blocked all HTTP/WebSocket/PTY traffic on\n * server startup while it scanned every agent dir, killed stale tmux\n * sessions, and recursively cleaned directories.\n *\n * This async variant does the same work using fs/promises and the\n * already-async killSessionAsync() so the bootstrap path no longer\n * stalls the event loop.\n */\nexport async function dropLegacyAgentStatesMissingRoleAsync(): Promise<number> {\n if (!existsSync(AGENTS_DIR)) return 0;\n\n const fsp = await import('fs/promises');\n let entries: string[];\n try {\n entries = await fsp.readdir(AGENTS_DIR);\n } catch {\n return 0;\n }\n\n let dropped = 0;\n await Promise.all(\n entries.map(async (entry) => {\n const dirPath = join(AGENTS_DIR, entry);\n let stat;\n try {\n stat = await fsp.stat(dirPath);\n } catch {\n return;\n }\n if (!stat.isDirectory()) return;\n\n const agentId = normalizeAgentId(entry);\n const stateFile = getRollbackAgentStatePath(agentId);\n let raw: { role?: unknown };\n try {\n const contents = await fsp.readFile(stateFile, 'utf8');\n raw = JSON.parse(contents) as { role?: unknown };\n } catch {\n return;\n }\n if (isRole(raw.role)) return;\n\n try { await Effect.runPromise(killSession(agentId)); } catch { /* best effort */ }\n try {\n await removeAgentStateDir(dirPath);\n dropped++;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[agents] Failed to drop legacy agent state ${agentId}: ${msg}`);\n }\n }),\n );\n\n return dropped;\n}\n\n/**\n * Scan ~/.overdeck/agents/ for state files with bare numeric issueIds\n * (e.g. \"484\" instead of \"PAN-484\") and log warnings to stderr.\n *\n * These workspaces were created before the pan- prefix convention and may\n * cause cross-tracker pollution if their in_review transition is triggered.\n * Called once at server startup to surface legacy state files.\n */\n/**\n * PAN-1048 P2: bootstrap-path migration is async.\n *\n * Sweeps legacy state files missing a `role` field and warns on bare\n * numeric issueIds. Both passes used to be synchronous (readdirSync,\n * readFileSync, killSession, rmSync), which blocked the dashboard\n * server's event loop on startup. The async version scans the same\n * directory once per concern and uses fs/promises throughout.\n */\nexport async function warnOnBareNumericIssueIds(): Promise<void> {\n const droppedLegacyAgents = await dropLegacyAgentStatesMissingRoleAsync();\n if (droppedLegacyAgents > 0) {\n console.warn(`[agents] Dropped ${droppedLegacyAgents} legacy agent state file(s) missing role`);\n }\n\n if (!existsSync(AGENTS_DIR)) return;\n\n const fsp = await import('fs/promises');\n let entries: string[];\n try {\n entries = await fsp.readdir(AGENTS_DIR);\n } catch {\n return;\n }\n\n const legacy: string[] = [];\n await Promise.all(\n entries.map(async (entry) => {\n const dirPath = join(AGENTS_DIR, entry);\n try {\n const stat = await fsp.stat(dirPath);\n if (!stat.isDirectory()) return;\n } catch {\n return;\n }\n const state = await Effect.runPromise(getAgentState(entry));\n if (state?.issueId && /^\\d+$/.test(state.issueId)) {\n legacy.push(`${entry} (issueId: \"${state.issueId}\")`);\n }\n }),\n );\n\n if (legacy.length > 0) {\n console.warn(\n `[agents] WARNING: ${legacy.length} agent state file(s) have bare numeric issueIds ` +\n `(created before the pan- prefix convention). These agents will not be able to ` +\n `transition tracker state. Consider removing or updating them:\\n` +\n legacy.map(l => ` ~/.overdeck/agents/${l}`).join('\\n')\n );\n }\n}\n","import { readFileSync, mkdirSync, writeFileSync, rmSync } from 'fs';\nimport { mkdir as mkdirAsync, rm, writeFile as writeFileAsync } from 'fs/promises';\nimport { exec, execSync } from 'child_process';\nimport { promisify } from 'util';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport {\n getAgentDir,\n getAgentStateSync,\n getAgentState,\n saveAgentStateSync,\n saveAgentState,\n markAgentStoppedState,\n saveAgentRuntimeState,\n normalizeAgentId,\n type AgentStopCause,\n} from '../agents.js';\nimport { emitAgentEvent } from '../agent-runtime.js';\nimport { AGENTS_DIR } from '../paths.js';\nimport {\n sessionExistsSync,\n capturePaneSync,\n killSessionSync,\n sessionExists,\n capturePane,\n killSession,\n} from '../tmux.js';\nimport { FsError, TmuxError } from '../errors.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * True when the PID is a tmux process (client or server). Used to keep the\n * per-agent launcher kill sweep from ever signalling the shared tmux server\n * (PAN-1798). Reads /proc/<pid>/comm; on non-Linux or read failure returns\n * false (fail-open matches pre-fix behavior for non-tmux processes).\n */\nfunction isTmuxProcessSync(pid: number): boolean {\n try {\n return readFileSync(`/proc/${pid}/comm`, 'utf-8').trim() === 'tmux';\n } catch {\n return false;\n }\n}\n\n/**\n * Find and kill any running `launcher.sh` process for the given agent.\n *\n * PAN-1527: `tmux kill-session` only signals tmux-managed children. Planning\n * agents (and any agent whose launcher escapes its tmux session) leave\n * orphan launcher.sh processes alive — state.json says stopped, but bash is\n * still burning CPU and tokens hours later. This locates them by command\n * line and walks SIGTERM → grace → SIGKILL.\n *\n * Sync version: callable from CLI (`pan kill`) and from the existing\n * `stopAgentSync`. Uses execSync only via `pgrep`, which is fast and\n * non-blocking in practice. Acceptable per CLAUDE.md because this path is\n * sync-by-nature already and is only called from CLI contexts and existing\n * sync internals.\n */\nfunction killLauncherProcessSync(agentId: string): void {\n const launcherPath = join(AGENTS_DIR, agentId, 'launcher.sh');\n let pidsOut: string;\n try {\n pidsOut = execSync(\n `pgrep -f ${JSON.stringify(launcherPath)}`,\n { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] },\n ).trim();\n } catch {\n return; // pgrep exits 1 when there are no matches — nothing to kill\n }\n\n const pids = pidsOut\n .split('\\n')\n .map(s => Number.parseInt(s, 10))\n .filter(n => Number.isFinite(n) && n > 0 && n !== process.pid)\n // PAN-1798: when this agent's spawn FOUNDED the shared tmux server, the\n // server's cmdline embeds this launcher path (`tmux ... new-session ...\n // bash .../launcher.sh`), so pgrep -f matches the server itself. Killing\n // it destroys every session on the socket — agents, reviews, and all\n // conversations. Never signal a tmux process from the per-agent sweep.\n .filter(pid => !isTmuxProcessSync(pid));\n if (pids.length === 0) return;\n\n for (const pid of pids) {\n try { process.kill(pid, 'SIGTERM'); } catch { /* already gone */ }\n }\n\n // ~500ms grace period for orderly shutdown. Sync spawn of `sleep` is\n // acceptable in CLI context; this function is never reached from the\n // dashboard server (which uses the async `stopAgent` Effect below).\n try {\n execSync('sleep 0.5', { stdio: 'ignore' });\n } catch { /* ignore */ }\n\n const survivors: number[] = [];\n for (const pid of pids) {\n try {\n process.kill(pid, 0);\n survivors.push(pid);\n } catch {\n /* already dead */\n }\n }\n for (const pid of survivors) {\n try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ }\n }\n}\n\nasync function killLauncherProcessAsync(agentId: string): Promise<void> {\n const launcherPath = join(AGENTS_DIR, agentId, 'launcher.sh');\n let pidsOut: string;\n try {\n const { stdout } = await execAsync(`pgrep -f ${JSON.stringify(launcherPath)}`);\n pidsOut = stdout.trim();\n } catch {\n return; // pgrep exits 1 when there are no matches\n }\n\n const pids = pidsOut\n .split('\\n')\n .map(s => Number.parseInt(s, 10))\n .filter(n => Number.isFinite(n) && n > 0 && n !== process.pid)\n // PAN-1798: see killLauncherProcessSync — the founding tmux server's\n // cmdline embeds this launcher path; never signal tmux from this sweep.\n .filter(pid => !isTmuxProcessSync(pid));\n if (pids.length === 0) return;\n\n for (const pid of pids) {\n try { process.kill(pid, 'SIGTERM'); } catch { /* already gone */ }\n }\n\n await new Promise<void>(resolve => setTimeout(resolve, 500));\n\n for (const pid of pids) {\n try {\n process.kill(pid, 0);\n try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ }\n } catch {\n /* already dead */\n }\n }\n}\n\n/**\n * Stop an agent. `cause` records whether an operator asked for the stop; it\n * defaults to `'system'` so machinery-initiated stops never latch the\n * operator-stop gate (PAN-3324). See `AgentStopCause`.\n */\nexport function stopAgentSync(agentId: string, cause: AgentStopCause = 'system'): void {\n const normalizedId = normalizeAgentId(agentId);\n\n if (sessionExistsSync(normalizedId)) {\n // Capture tmux output before killing so logs remain viewable after stop\n try {\n const output = capturePaneSync(normalizedId, 5000);\n if (output) {\n const agentDir = getAgentDir(normalizedId);\n mkdirSync(agentDir, { recursive: true });\n writeFileSync(join(agentDir, 'output.log'), output);\n }\n } catch {\n // Non-fatal — best effort log capture\n }\n\n killSessionSync(normalizedId);\n }\n\n // PAN-1527: kill orphan launcher.sh processes that escape tmux (planning\n // agents, dashboard-spawned launchers, anything that survived tmux\n // kill-session). Runs even when no tmux session existed in the first place.\n killLauncherProcessSync(normalizedId);\n\n try {\n rmSync(join(AGENTS_DIR, normalizedId, 'git-guard'), { recursive: true, force: true }); // PAN-3357: not a dir removal\n } catch {\n // Non-fatal — stopping the agent must succeed even if guard cleanup fails.\n }\n\n const state = getAgentStateSync(normalizedId);\n if (state) {\n // Ensure id is set — runtime state files may lack it (PAN-150)\n if (!state.id) state.id = normalizedId;\n\n markAgentStoppedState(state, cause);\n saveAgentStateSync(state);\n }\n\n // Also mark runtime.json as stopped so Cloister/Deacon won't auto-restart.\n // state.json and runtime.json are separate files — both must agree the agent\n // was intentionally stopped to prevent race conditions with health check polls.\n console.log(`[agents] Stopping ${normalizedId}: tmux=${sessionExistsSync(normalizedId)} stateStatus=${state?.status ?? 'none'}`);\n saveAgentRuntimeState(normalizedId, {\n state: 'stopped',\n lastActivity: new Date().toISOString(),\n });\n}\n\n/** Async twin of `stopAgentSync`. Same `cause` semantics — see `AgentStopCause`. */\nexport const stopAgent = (\n agentId: string,\n cause: AgentStopCause = 'system',\n): Effect.Effect<void, FsError | TmuxError> => {\n const normalizedId = normalizeAgentId(agentId);\n\n return Effect.gen(function* () {\n if (yield* sessionExists(normalizedId)) {\n yield* Effect.gen(function* () {\n const output = yield* capturePane(normalizedId, 5000);\n if (!output) return;\n\n const agentDir = getAgentDir(normalizedId);\n const outputFile = join(agentDir, 'output.log');\n yield* Effect.tryPromise({\n try: () => mkdirAsync(agentDir, { recursive: true }),\n catch: (cause) => new FsError({ operation: 'mkdir', path: agentDir, cause }),\n });\n yield* Effect.tryPromise({\n try: () => writeFileAsync(outputFile, output),\n catch: (cause) => new FsError({ operation: 'write', path: outputFile, cause }),\n });\n }).pipe(Effect.catch(() => Effect.void));\n\n yield* killSession(normalizedId);\n }\n\n // PAN-1527: same orphan-launcher kill as stopAgentSync. Runs after\n // killSession so tmux gets the first chance to take everything down\n // cleanly; falls through and kills any survivor by command-line match.\n yield* Effect.tryPromise({\n try: () => killLauncherProcessAsync(normalizedId),\n catch: (cause): never => { throw cause; },\n }).pipe(Effect.catch(() => Effect.void));\n\n yield* Effect.promise(async () => {\n try {\n await rm(join(AGENTS_DIR, normalizedId, 'git-guard'), { recursive: true, force: true }); // PAN-3357: not a dir removal\n } catch {\n // Non-fatal — stopping the agent must succeed even if guard cleanup fails.\n }\n });\n\n const state = yield* getAgentState(normalizedId);\n if (state) {\n if (!state.id) state.id = normalizedId;\n\n markAgentStoppedState(state, cause);\n yield* saveAgentState(state);\n }\n\n const tmuxActive = yield* sessionExists(normalizedId);\n console.log(`[agents] Stopping ${normalizedId} (async): tmux=${tmuxActive} stateStatus=${state?.status ?? 'none'}`);\n yield* Effect.forkDetach(emitAgentEvent(normalizedId, {\n kind: 'activity',\n activity: 'stopped',\n }));\n });\n};\n","import { randomUUID } from 'crypto';\nimport { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport { getOverdeckHome } from './paths.js';\nimport { getHarnessBehavior } from './runtimes/behavior.js';\nimport type { RuntimeName } from './runtimes/types.js';\nimport { logAgentLifecycleSync } from './persistent-logger.js';\n\nexport const SESSION_RESET_MARKER = 'session-reset';\n\nexport function isSessionResetMarker(agentId: string): boolean {\n return existsSync(join(getOverdeckHome(), 'agents', agentId, SESSION_RESET_MARKER));\n}\n\nexport function clearSessionResetMarker(agentId: string): void {\n try {\n unlinkSync(join(getOverdeckHome(), 'agents', agentId, SESSION_RESET_MARKER));\n } catch {\n // The marker is absent for normal launches.\n }\n}\n\n/**\n * PAN-1989: durably record a Claude session id in the agent's append-only\n * sessions.json the moment the system learns it.\n *\n * Background: a work agent's resumable session id was only durable if the\n * PostToolUse heartbeat hook had written sessions.json (first tool call) or\n * auto-suspend had written session.id. A session that boots but is stopped\n * before its first tool — kickoff never delivered, or a reboot mid-run — left\n * the id only in the EPHEMERAL in-memory runtime snapshot, which a dashboard\n * restart or reboot rebuilds from sources (state.json + tmux) that don't carry\n * the session id. The agent then resolves to \"No saved session ID found\" and\n * goes troubled even though its JSONL transcript is intact on disk.\n *\n * Calling this from the AgentStateService event sink (the single convergence\n * point for every model_set event, hook-emitted or server-emitted) makes the\n * pointer durable from the moment it is learned. The append-only list may\n * accumulate aborted/empty ids; the resolver picks the freshest id with a real\n * transcript, so empties never shadow the truth. De-dupes; never throws.\n *\n * Lives in its own module (not agents.ts) so the dashboard event sink can import\n * it without pulling the whole agents.ts graph — avoids an ESM import cycle.\n */\nexport function readSessionIdHistorySync(agentId: string): string[] {\n try {\n const file = join(getOverdeckHome(), 'agents', agentId, 'sessions.json');\n if (!existsSync(file)) return [];\n const parsed: unknown = JSON.parse(readFileSync(file, 'utf8'));\n return Array.isArray(parsed)\n ? parsed.filter((value): value is string => typeof value === 'string' && Boolean(value.trim()))\n : [];\n } catch {\n return [];\n }\n}\n\nexport function appendSessionIdToHistory(agentId: string, sessionId: string): void {\n if (!sessionId || !sessionId.trim()) return;\n try {\n const dir = join(getOverdeckHome(), 'agents', agentId);\n mkdirSync(dir, { recursive: true });\n const file = join(dir, 'sessions.json');\n let list: string[] = [];\n if (existsSync(file)) {\n const parsed: unknown = JSON.parse(readFileSync(file, 'utf8'));\n if (Array.isArray(parsed)) list = parsed.filter((value): value is string => typeof value === 'string');\n }\n if (list.includes(sessionId)) return;\n list.push(sessionId);\n writeFileSync(file, JSON.stringify(list));\n } catch {\n /* non-fatal — bookkeeping only */\n }\n}\n\n/**\n * Persist the current Claude session pointer before launch.\n *\n * Fresh work agents know their Claude UUID before the process starts. Writing\n * it here makes transcript discovery and resume independent of lifecycle hooks\n * (and therefore independent of jq being installed on the host).\n */\nexport function persistCurrentSessionId(agentId: string, sessionId: string): void {\n if (!sessionId || !sessionId.trim()) return;\n try {\n const dir = join(getOverdeckHome(), 'agents', agentId);\n mkdirSync(dir, { recursive: true });\n writeFileSync(join(dir, 'session.id'), sessionId);\n } catch {\n /* non-fatal — the caller records diagnostics and transcript resolution will explain the miss */\n }\n}\n\n/**\n * Allocate and durably record a fresh Claude work-agent UUID before launch.\n * This makes transcript discovery independent of lifecycle hooks and jq.\n */\nexport function createFreshSessionIdentity(agentId: string, harness: RuntimeName): string | undefined {\n if (getHarnessBehavior(harness).sessionIdSource !== 'launcher-session-id') return undefined;\n const sessionId = randomUUID();\n persistCurrentSessionId(agentId, sessionId);\n appendSessionIdToHistory(agentId, sessionId);\n const dir = join(getOverdeckHome(), 'agents', agentId);\n logAgentLifecycleSync(\n agentId,\n `session identity allocated: harness=${harness} sessionId=${sessionId} `\n + `pointerPersisted=${existsSync(join(dir, 'session.id'))} `\n + `historyPersisted=${existsSync(join(dir, 'sessions.json'))}`,\n );\n return sessionId;\n}\n\nexport function logLauncherSessionPinned(agentId: string, sessionId: string, launcher: string): void {\n logAgentLifecycleSync(\n agentId,\n `launcher session pinned: sessionId=${sessionId} flag=--session-id launcher=${launcher}`,\n );\n}\n","import { provisionClaudeHooks } from '../claude-hooks-provision.js';\nimport { logAgentLifecycleSync } from '../persistent-logger.js';\nimport type { RuntimeName } from '../runtimes/types.js';\n\n/**\n * Ensure Claude's global lifecycle hooks are registered before the process can\n * emit SessionStart. This must be awaited: provisioning after launch races the\n * one event that writes ready.json and acknowledges kickoff delivery.\n */\nexport async function ensureLifecycleHooksBeforeLaunch(\n agentId: string,\n harness: RuntimeName,\n): Promise<void> {\n if (harness !== 'claude-code') return;\n\n const result = await provisionClaudeHooks();\n if (!result.ok) {\n const reason = result.reason ?? 'unknown provisioning failure';\n logAgentLifecycleSync(agentId, `hook provisioning: failed harness=${harness} reason=${reason}`);\n throw new Error(\n `Claude Code lifecycle hooks are unavailable: ${reason}. `\n + 'Run `pan up` to install host prerequisites, then `pan sync` and retry.',\n );\n }\n\n logAgentLifecycleSync(\n agentId,\n `hook provisioning: ready harness=${harness} changed=${result.changed} `\n + `binariesSynced=${result.binariesSynced} registered=${result.registered.length} pruned=${result.pruned.length}`,\n );\n}\n","import { exec } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { promisify } from 'node:util';\n\nimport { createActiveSlice } from '../xbrief/dag.js';\nimport { readWorkspacePlanSync } from '../xbrief/io.js';\nimport { resolveWorkspaceRepoRootsSync } from '../project-repos.js';\nimport type { RegisteredSlotSpawn } from './spawn-prep.js';\n\nconst execAsync = promisify(exec);\n\nexport function buildRegisteredSlotPrompt(\n issueId: string,\n baseWorkspace: string,\n slot: RegisteredSlotSpawn,\n extraPrompt?: string,\n): string {\n const doc = readWorkspacePlanSync(baseWorkspace);\n if (!doc) {\n throw new Error(\n `Registered slot spawn for ${issueId} requires a readable xBRIEF plan in ${baseWorkspace}.`,\n );\n }\n\n const slice = createActiveSlice(doc, {\n issueId: issueId.toUpperCase(),\n itemId: slot.slotItemId,\n currentItemIds: [slot.slotItemId],\n });\n const lines = [\n `# Registered Slot Assignment: ${slot.slotItemId}`,\n '',\n `Issue: ${issueId}`,\n `Slot: ${slot.slotIndex}`,\n `Agent: ${slot.agentId}`,\n `Branch: ${slot.branch}`,\n `Workspace: ${slot.workspace}`,\n '',\n 'You are a registered slot work agent. Implement only the target xBRIEF item below, keep changes scoped to that item, and do not merge this slot branch yourself.',\n '',\n slice.prompt,\n ];\n const trimmedExtra = extraPrompt?.trim();\n if (trimmedExtra) lines.push('', '## Additional Foreman Instructions', trimmedExtra);\n return lines.join('\\n');\n}\n\nexport async function ensureRegisteredSlotWorktree(issueId: string, baseWorkspace: string, slot: RegisteredSlotSpawn): Promise<void> {\n const repoRoots = resolveWorkspaceRepoRootsSync(issueId, baseWorkspace);\n const polyrepoRoots = repoRoots.filter(root => root.isPolyrepo);\n if (polyrepoRoots.length > 0) {\n const expected = polyrepoRoots.map(root => ({\n ...root,\n target: `${slot.workspace}/${root.dir.slice(baseWorkspace.length + 1).split('/')[0]}`,\n }));\n if (existsSync(slot.workspace)) {\n const missing = expected.filter(root => !existsSync(`${root.target}/.git`));\n if (missing.length === 0) {\n const branches = await Promise.all(expected.map(async root => {\n try {\n return (await execAsync('git branch --show-current', { cwd: root.target })).stdout.trim();\n } catch {\n return '';\n }\n }));\n if (branches.every(branch => branch === slot.branch)) return;\n }\n throw new Error(`Registered polyrepo slot workspace is incomplete at ${slot.workspace}; run pan swarm reset before dispatching it.`);\n }\n await mkdir(slot.workspace, { recursive: true });\n try {\n for (const root of expected) {\n const branchExists = await gitBranchExists(root.dir, slot.branch);\n const command = branchExists\n ? `git worktree add ${JSON.stringify(root.target)} ${JSON.stringify(slot.branch)}`\n : `git worktree add -b ${JSON.stringify(slot.branch)} ${JSON.stringify(root.target)} ${JSON.stringify(root.sourceBranch)}`;\n await execAsync(command, { cwd: root.dir });\n }\n } catch (error) {\n throw new Error(`Could not create isolated polyrepo slot workspace ${slot.workspace}: ${error instanceof Error ? error.message : String(error)}`);\n }\n return;\n }\n\n if (existsSync(slot.workspace)) {\n let branch = '';\n try {\n branch = (await execAsync('git branch --show-current', { cwd: slot.workspace })).stdout.trim();\n } catch { /* handled by the stale-workspace error below */ }\n if (branch === slot.branch) return;\n throw new Error(`Registered slot workspace is incomplete at ${slot.workspace}; run pan swarm reset before dispatching it.`);\n }\n await mkdir(dirname(slot.workspace), { recursive: true });\n const branchExists = await gitBranchExists(baseWorkspace, slot.branch);\n if (branchExists && !(await gitRefsEqual(baseWorkspace, slot.branch, repoRoots[0].sourceBranch))) {\n throw new Error(`Registered slot branch ${slot.branch} is not at ${repoRoots[0].sourceBranch}; refusing to reuse stale work.`);\n }\n const target = JSON.stringify(slot.workspace);\n const branch = JSON.stringify(slot.branch);\n const command = branchExists\n ? `git worktree add ${target} ${branch}`\n : `git worktree add -b ${branch} ${target} ${JSON.stringify(repoRoots[0].sourceBranch)}`;\n await execAsync(command, { cwd: baseWorkspace });\n}\n\nasync function gitBranchExists(workspace: string, branch: string): Promise<boolean> {\n try {\n await execAsync(`git show-ref --verify --quiet ${JSON.stringify(`refs/heads/${branch}`)}`, { cwd: workspace });\n return true;\n } catch {\n return false;\n }\n}\n\nasync function gitRefsEqual(workspace: string, left: string, right: string): Promise<boolean> {\n try {\n const [leftResult, rightResult] = await Promise.all([\n execAsync(`git rev-parse ${JSON.stringify(left)}`, { cwd: workspace }),\n execAsync(`git rev-parse ${JSON.stringify(right)}`, { cwd: workspace }),\n ]);\n return leftResult.stdout.trim() === rightResult.stdout.trim();\n } catch {\n return false;\n }\n}\n","/**\n * Kimi Code CLI runtime adapter (PAN-1837).\n *\n * Native TUI — not ACP. Kimi writes its own session tree at\n * `<kimiHome>/sessions/<workDirKey>/<sessionId>/agents/main/wire.jsonl`,\n * where `workDirKey = wd_<basename(workDir)>_<sha256(workDir).hex[:12]>`\n * (verified empirically against installed kimi 0.29.2, wi-fixture/D2 — Kimi\n * does not expose this derivation directly). Kimi generates its own session\n * id; it cannot be preset via a launch flag (D2/erratum E1: no --session, no\n * --work-dir). The id is captured post-launch as the session directory that\n * newly appears under the workspace's bucket, then persisted at\n * `~/.overdeck/agents/<id>/kimi-session-id` (mirrors codex's thread-id file)\n * so later introspection calls avoid re-walking the bucket.\n *\n * Delivery: PTY supervisor is primary (KIMI_CODE_BEHAVIOR.supportsPtySupervisor\n * is true, same as claude-code). sendMessage delegates to the existing generic\n * deliverAgentMessage() cascade (supervisor -> channels -> async tmux paste)\n * rather than re-implementing that tiering here — deliverAgentMessage already\n * dispatches on socket existence, not a hardcoded harness list, so kimi-code\n * flows through it for free once its PTY supervisor socket exists.\n *\n * getTokenUsage/getSessionCost delegate to wi8b's parseKimiSessionSync,\n * which sums every usage.record in the wire.jsonl — Kimi's own server-side\n * context cache accounting.\n */\n\nimport { exec } from 'node:child_process';\nimport { getManagedTmuxSocketName } from '../tmux.js';\nimport { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';\nimport { mkdir as mkdirAsync, readdir as readdirAsync, readFile as readFileAsync, rename as renameAsync, rm as rmAsync, stat as statAsync, writeFile as writeFileAsync } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\n\nimport type { AgentState } from '../agents/agent-state.js';\nimport { getAgentStateSync, saveAgentStateSync } from '../agents/agent-state.js';\nimport { listAgentStates } from '../agents/queries.js';\nimport { deliverAgentMessage } from '../agents/delivery.js';\nimport { resolvePtySupervisorScriptPath } from '../channels/pty-supervisor-locate.js';\nimport { writePtyToken } from '../pty-token.js';\nimport { generateLauncherScriptSync } from '../launcher-generator.js';\nimport { prepareHarnessLaunch } from '../harness-binary.js';\nimport { claudeSystemPromptFiles } from '../agents/runtime-command.js';\nimport { markKimiContextDelivered, prepareKimiMessage } from './kimi-context-envelope.js';\nimport { parseKimiSessionSync } from '../cost-parsers/kimi-parser.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { isPidDead } from '../pan-dir/fs-lock.js';\nimport { getRuntimeBehavior } from './behavior.js';\nimport { tmuxCreateSession, tmuxKillSession, tmuxSessionExists } from './tmux-cli.js';\nimport type {\n Agent,\n AgentRuntimeSync,\n CostBreakdown,\n HarnessBehavior,\n Heartbeat,\n Session,\n SpawnConfig,\n TokenUsage,\n} from './types.js';\n\nconst execAsync = promisify(exec);\nconst SPAWN_READY_TIMEOUT_MS = 60_000; // matches KIMI_CODE_BEHAVIOR.readyTimeoutSeconds\nconst POLL_INTERVAL_MS = 250;\n\nexport class KimiCodeSpawnTimeout extends Error {\n readonly code = 'KIMI_CODE_SPAWN_TIMEOUT' as const;\n constructor(agentId: string) {\n super(`Kimi Code agent ${agentId} did not write a new session under its workDirKey bucket within ${SPAWN_READY_TIMEOUT_MS}ms`);\n this.name = 'KimiCodeSpawnTimeout';\n }\n}\n\n/**\n * Compute Kimi's on-disk workDirKey bucket name for a working directory.\n * Verified against `wd_kimi-fixture-scratch_ef33f89ad7cf` (workDir\n * `/tmp/kimi-fixture-scratch`) and several pre-existing real sessions on the\n * machine that produced the wi-fixture capture (e.g.\n * `wd_overdeck_b289e7acb782`, `wd_feature-pan-2858_1dc66dc5021d`).\n */\nexport function kimiWorkDirKey(workDir: string): string {\n const hash = createHash('sha256').update(workDir).digest('hex').slice(0, 12);\n return `wd_${basename(workDir)}_${hash}`;\n}\n\n/** Absolute path to the session bucket Kimi writes for a working directory. */\nexport function kimiSessionsRoot(kimiHome: string, workDir: string): string {\n return join(kimiHome, 'sessions', kimiWorkDirKey(workDir));\n}\n\n/**\n * Locate wire.jsonl for a specific captured session id under the workspace's\n * bucket. Falls back to the newest session directory when the captured id's\n * wire.jsonl is missing (e.g. never captured, or the id file is stale).\n */\nexport function findKimiWirePath(kimiHome: string, workspace: string, sessionId: string | null): string | null {\n if (sessionId) {\n const candidate = join(kimiSessionsRoot(kimiHome, workspace), sessionId, 'agents', 'main', 'wire.jsonl');\n if (existsSync(candidate)) return candidate;\n }\n return findLatestKimiSession(kimiHome, workspace);\n}\n\n/** Fallback: the newest session directory by wire.jsonl mtime under the workspace's bucket. */\nexport function findLatestKimiSession(kimiHome: string, workspace: string): string | null {\n const bucketDir = kimiSessionsRoot(kimiHome, workspace);\n let entries: string[];\n try {\n entries = readdirSync(bucketDir);\n } catch {\n return null;\n }\n let newest: { path: string; mtimeMs: number } | null = null;\n for (const entry of entries) {\n const wirePath = join(bucketDir, entry, 'agents', 'main', 'wire.jsonl');\n let mtimeMs: number;\n try {\n mtimeMs = statSync(wirePath).mtimeMs;\n } catch {\n continue;\n }\n if (!newest || mtimeMs > newest.mtimeMs) newest = { path: wirePath, mtimeMs };\n }\n return newest?.path ?? null;\n}\n\n/**\n * Async twin of {@link findKimiWirePath} (PAN-1837 review fix, P2). The\n * dashboard's transcript resolver runs on the event loop and Command Deck\n * polling re-resolves the same session repeatedly, so the sync\n * readdirSync/statSync walk here would block the loop on every poll as a\n * session's history grows. Runtime-side sync callers (kill/spawn lifecycle)\n * keep using the sync versions above — this pair exists only for dashboard\n * routes, per the runtime's own documented sync contract.\n */\nexport async function findKimiWirePathAsync(kimiHome: string, workspace: string, sessionId: string | null): Promise<string | null> {\n if (sessionId) {\n const candidate = join(kimiSessionsRoot(kimiHome, workspace), sessionId, 'agents', 'main', 'wire.jsonl');\n try {\n await statAsync(candidate);\n return candidate;\n } catch { /* fall through to newest-session fallback */ }\n }\n return findLatestKimiSessionAsync(kimiHome, workspace);\n}\n\n/** Async twin of {@link findLatestKimiSession} — see {@link findKimiWirePathAsync}. */\nexport async function findLatestKimiSessionAsync(kimiHome: string, workspace: string): Promise<string | null> {\n const bucketDir = kimiSessionsRoot(kimiHome, workspace);\n let entries: string[];\n try {\n entries = await readdirAsync(bucketDir);\n } catch {\n return null;\n }\n let newest: { path: string; mtimeMs: number } | null = null;\n for (const entry of entries) {\n const wirePath = join(bucketDir, entry, 'agents', 'main', 'wire.jsonl');\n let mtimeMs: number;\n try {\n mtimeMs = (await statAsync(wirePath)).mtimeMs;\n } catch {\n continue;\n }\n if (!newest || mtimeMs > newest.mtimeMs) newest = { path: wirePath, mtimeMs };\n }\n return newest?.path ?? null;\n}\n\nfunction kimiHomeDefault(): string {\n return join(homedir(), '.kimi-code');\n}\n\n/**\n * Poll a workspace's Kimi session bucket for a directory that did not exist\n * in `existingBefore`, returning the newest such directory's name (the\n * session id) once one appears. Standalone so both KimiCodeRuntimeSync.spawnAgent\n * and the production `pan start` spawn path (spawn-prep.ts, which builds its\n * own launcher/tmux session rather than delegating to this class) can capture\n * the session id the same way — mirroring codex's standalone\n * waitForCodexRollout/extractThreadIdFromRollout pair.\n */\nexport async function waitForNewKimiSession(\n kimiHome: string,\n workspace: string,\n existingBefore: Set<string>,\n timeoutMs: number = SPAWN_READY_TIMEOUT_MS,\n): Promise<string | null> {\n const bucketDir = kimiSessionsRoot(kimiHome, workspace);\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n let entries: string[] = [];\n try {\n entries = readdirSync(bucketDir);\n } catch {\n entries = [];\n }\n const fresh = entries.filter((entry) => !existingBefore.has(entry));\n if (fresh.length > 0) {\n let newest: { name: string; mtimeMs: number } | null = null;\n for (const name of fresh) {\n let mtimeMs: number;\n try {\n mtimeMs = statSync(join(bucketDir, name)).mtimeMs;\n } catch {\n continue;\n }\n if (!newest || mtimeMs > newest.mtimeMs) newest = { name, mtimeMs };\n }\n if (newest) return newest.name;\n }\n await delay(POLL_INTERVAL_MS);\n }\n return null;\n}\n\n/**\n * Async twin of {@link waitForNewKimiSession} (PAN-1837 review fix, P2). The\n * conversation-spawn capture task runs on the dashboard event loop, so its\n * 250ms poll loop must not block on readdirSync/statSync — runtime-side sync\n * callers (kill/spawn lifecycle) keep the sync version above.\n */\nexport async function waitForNewKimiSessionAsync(\n kimiHome: string,\n workspace: string,\n existingBefore: Set<string>,\n timeoutMs: number = SPAWN_READY_TIMEOUT_MS,\n): Promise<string | null> {\n const bucketDir = kimiSessionsRoot(kimiHome, workspace);\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n let entries: string[] = [];\n try {\n entries = await readdirAsync(bucketDir);\n } catch {\n entries = [];\n }\n const fresh = entries.filter((entry) => !existingBefore.has(entry));\n if (fresh.length > 0) {\n let newest: { name: string; mtimeMs: number } | null = null;\n for (const name of fresh) {\n let mtimeMs: number;\n try {\n mtimeMs = (await statAsync(join(bucketDir, name))).mtimeMs;\n } catch {\n continue;\n }\n if (!newest || mtimeMs > newest.mtimeMs) newest = { name, mtimeMs };\n }\n if (newest) return newest.name;\n }\n await delay(POLL_INTERVAL_MS);\n }\n return null;\n}\n\n/** Persist the captured session id to `<overdeckHome>/agents/<id>/kimi-session-id` (mirrors codex's thread-id file). */\nexport function writeKimiSessionId(agentId: string, sessionId: string, overdeckHome: string = getOverdeckHome()): void {\n writeFileSync(join(overdeckHome, 'agents', agentId, 'kimi-session-id'), sessionId, 'utf-8');\n}\n\n/**\n * Cross-process per-bucket mutex (PAN-1837 review fix, cycle 7). Two Kimi\n * launches sharing a cwd both snapshot the same \"existing sessions\" set,\n * then both independently see both new session directories as fresh and\n * pick the same newest one — silently persisting the SAME kimi-session-id\n * for two different Overdeck identities. Serializing the whole\n * snapshot -> launch -> capture sequence per workDirKey bucket makes each\n * launch's \"existing\" snapshot include every prior launch's\n * already-captured directory, so only the true newcomer is ever fresh.\n *\n * This MUST be a filesystem lock, not an in-memory Map: the four Kimi launch\n * owners run in separate Node processes that share no module state —\n * `pan start`/`pan strike` spawn in the short-lived CLI process, dashboard\n * conversations launch in the dashboard server process, and Deacon\n * recovery/restart can launch in its own child process. An in-memory lock\n * only serializes callers inside the ONE process that happens to hold it; it\n * is silently absent for every other process, which is exactly the\n * cross-process race this lock exists to close. Modeled on the cross-process\n * per-issue record lock in pan-dir/fs-lock.ts (mkdir-based atomic acquire +\n * PID-liveness stale-lock recovery), but with a much longer retry budget:\n * a Kimi capture can legitimately hold the lock for up to\n * SPAWN_READY_TIMEOUT_MS (60s) while it polls for the new session directory,\n * so the record-lock's ~1s retry budget would starve a second launch on the\n * same bucket almost immediately.\n */\nconst KIMI_CAPTURE_LOCK_POLL_MS = 250;\nconst KIMI_CAPTURE_LOCK_MAX_WAIT_MS = 90_000; // > SPAWN_READY_TIMEOUT_MS, so a single holder's worst case never starves a waiter\n// PAN-1837 review fix (cycle 8): bounded age fallback for an ownerless lock\n// (owner.json missing, corrupt, or never durably written — e.g. the holder\n// died between mkdir succeeding and the write/rename completing). isPidDead\n// deliberately returns false for an unreadable pid, so without this fallback\n// such a lock is NEVER reclaimed and every future launch for that bucket\n// waits out the full budget and fails, forever. 75s comfortably exceeds the\n// legitimate worst-case single hold (~60-65s: snapshot + createSession +\n// SPAWN_READY_TIMEOUT_MS capture poll + persist) while staying under\n// KIMI_CAPTURE_LOCK_MAX_WAIT_MS so a waiter's own budget doesn't expire\n// first. This age check applies ONLY when a live PID could not be\n// determined — a lock whose owner.json names a confirmed-alive PID is never\n// reclaimed by age, preserving the \"never delete a lock held by a live\n// process\" invariant.\nconst KIMI_CAPTURE_LOCK_STALE_AGE_MS = 75_000;\n\ninterface KimiCaptureLockOwner {\n pid: number;\n acquiredAt: string;\n}\n\nexport class KimiCaptureLockTimeoutError extends Error {\n constructor(lockPath: string, waitedMs: number) {\n super(`Timed out after ${waitedMs}ms waiting for the Kimi session-capture lock at ${lockPath} — another process is still holding it.`);\n this.name = 'KimiCaptureLockTimeoutError';\n }\n}\n\n/** Deterministic, filesystem-safe lock path for a Kimi session bucket. */\nexport function kimiCaptureLockPath(bucketKey: string, overdeckHome = getOverdeckHome()): string {\n const hash = createHash('sha256').update(bucketKey).digest('hex').slice(0, 24);\n return join(overdeckHome, 'locks', 'kimi-capture', `${hash}.lock`);\n}\n\nasync function readKimiCaptureLockOwner(lockPath: string): Promise<Partial<KimiCaptureLockOwner>> {\n try {\n const parsed = JSON.parse(await readFileAsync(join(lockPath, 'owner.json'), 'utf8')) as Partial<KimiCaptureLockOwner>;\n // A corrupt/partial write can still parse as valid JSON missing pid, or\n // with a pid of the wrong type — treat anything that isn't a genuine\n // positive integer pid as \"no usable owner\", same as a read failure.\n if (typeof parsed.pid !== 'number' || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return {};\n return parsed;\n } catch {\n return {};\n }\n}\n\n/**\n * True once the lock directory itself (not owner.json) is older than the\n * stale-age bound. mkdir sets this directory's mtime at creation, and it is\n * never touched again once owner.json (a fresh directory entry) — if ever\n * written — settles, so this is trustworthy even when owner.json itself\n * never made it to disk.\n */\nasync function isKimiCaptureLockDirStale(lockPath: string): Promise<boolean> {\n try {\n const dirStat = await statAsync(lockPath);\n return Date.now() - dirStat.mtimeMs > KIMI_CAPTURE_LOCK_STALE_AGE_MS;\n } catch {\n // The lock directory vanished between our failed mkdir and this stat —\n // the holder already released it; the next acquire attempt will succeed.\n return true;\n }\n}\n\nasync function acquireKimiCaptureLock(lockPath: string): Promise<void> {\n await mkdirAsync(dirname(lockPath), { recursive: true, mode: 0o700 });\n const deadline = Date.now() + KIMI_CAPTURE_LOCK_MAX_WAIT_MS;\n\n for (;;) {\n try {\n await mkdirAsync(lockPath, { mode: 0o700 });\n const owner: KimiCaptureLockOwner = { pid: process.pid, acquiredAt: new Date().toISOString() };\n // Atomic write: write to a per-process temp file, then rename into\n // place. rename() is atomic on POSIX, so owner.json is either fully\n // absent or fully valid — never a partial write left behind by a\n // process that died mid-write.\n const tmpPath = join(lockPath, `owner.${process.pid}.${Date.now()}.tmp`);\n try {\n await writeFileAsync(tmpPath, JSON.stringify(owner, null, 2), 'utf8');\n await renameAsync(tmpPath, join(lockPath, 'owner.json'));\n } catch (error) {\n try { await rmAsync(tmpPath, { force: true }); } catch { /* best effort */ }\n try { await rmAsync(lockPath, { recursive: true, force: true }); } catch { /* best effort */ }\n throw error;\n }\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n const owner = await readKimiCaptureLockOwner(lockPath);\n if (owner.pid !== undefined) {\n if (isPidDead(owner.pid)) {\n // The process that held this lock is gone (crashed CLI invocation,\n // killed Deacon child, …) — reclaim rather than wait out the full\n // budget for a holder that will never release it.\n await rmAsync(lockPath, { recursive: true, force: true });\n continue;\n }\n // A confirmed-alive pid holds this lock — keep waiting; never\n // reclaim by age when liveness is actually known.\n } else if (await isKimiCaptureLockDirStale(lockPath)) {\n // No usable owner record (missing/corrupt/never durably written) and\n // the lock directory itself is old enough that no legitimate holder\n // could still be mid-capture — reclaim.\n await rmAsync(lockPath, { recursive: true, force: true });\n continue;\n }\n }\n\n if (Date.now() >= deadline) {\n throw new KimiCaptureLockTimeoutError(lockPath, KIMI_CAPTURE_LOCK_MAX_WAIT_MS);\n }\n await delay(KIMI_CAPTURE_LOCK_POLL_MS);\n }\n}\n\nasync function releaseKimiCaptureLock(lockPath: string): Promise<void> {\n await rmAsync(lockPath, { recursive: true, force: true });\n}\n\nexport async function withKimiSessionCaptureLock<T>(\n kimiHome: string,\n workspace: string,\n fn: () => Promise<T>,\n overdeckHome?: string,\n): Promise<T> {\n const bucketKey = kimiSessionsRoot(kimiHome, workspace);\n const lockPath = kimiCaptureLockPath(bucketKey, overdeckHome);\n await acquireKimiCaptureLock(lockPath);\n try {\n return await fn();\n } finally {\n await releaseKimiCaptureLock(lockPath);\n }\n}\n\n/**\n * Launch one managed native Kimi session and persist the identity required by\n * the first-message context guard before any caller can deliver a prompt.\n * Fresh launches snapshot and capture a newly-created native session under the\n * per-workspace lock. Resumes pass `-S` at launcher construction time, reuse\n * that exact identity, and therefore do not wait for a new directory.\n */\nexport async function launchAndCaptureManagedKimiSession(options: {\n agentId: string;\n workspace: string;\n launch: () => Promise<void>;\n resumeSessionId?: string;\n kimiHome?: string;\n overdeckHome?: string;\n timeoutMs?: number;\n}): Promise<string> {\n const kimiHome = options.kimiHome ?? join(homedir(), '.kimi-code');\n return withKimiSessionCaptureLock(kimiHome, options.workspace, async () => {\n if (options.resumeSessionId) {\n await options.launch();\n writeKimiSessionId(options.agentId, options.resumeSessionId, options.overdeckHome);\n return options.resumeSessionId;\n }\n\n let existingBefore = new Set<string>();\n try {\n existingBefore = new Set(await readdirAsync(kimiSessionsRoot(kimiHome, options.workspace)));\n } catch { /* fresh workspace bucket */ }\n\n await options.launch();\n const sessionId = await waitForNewKimiSessionAsync(\n kimiHome,\n options.workspace,\n existingBefore,\n options.timeoutMs,\n );\n if (!sessionId) throw new KimiCodeSpawnTimeout(options.agentId);\n writeKimiSessionId(options.agentId, sessionId, options.overdeckHome);\n return sessionId;\n }, options.overdeckHome);\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nasync function pollUntilSessionGone(agentId: string, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!(await tmuxSessionExists(agentId))) return true;\n await delay(POLL_INTERVAL_MS);\n }\n return false;\n}\n\nexport interface KimiCodeRuntimeOptions {\n readonly overdeckHome?: string;\n readonly kimiHome?: string;\n readonly execCommand?: (command: string) => Promise<{ readonly stdout: string }>;\n readonly prepareLaunch?: () => Promise<{ readonly binaryPath: string; readonly pathExport: string }>;\n readonly listAgentStates?: () => AgentState[];\n readonly deliverMessage?: (\n agentId: string,\n message: string,\n preparedKimiContext?: import('./kimi-context-envelope.js').PreparedKimiMessage,\n ) => Promise<{ readonly ok: boolean; readonly failure?: string }>;\n /** Resolves the PTY supervisor script path. Defaults to resolvePtySupervisorScriptPath(). */\n readonly resolveSupervisorScriptPath?: () => string;\n /** Writes the PTY supervisor's auth token for an agent. Defaults to the real writePtyToken(). */\n readonly writePtyTokenFor?: (agentId: string) => Promise<string>;\n}\n\nexport class KimiCodeRuntimeSync implements AgentRuntimeSync {\n readonly name = 'kimi-code' as const;\n private readonly overdeckHomeOverride: string | undefined;\n private readonly kimiHomeOverride: string | undefined;\n private readonly execCommand: (command: string) => Promise<{ readonly stdout: string }>;\n private readonly prepareLaunch: () => Promise<{ readonly binaryPath: string; readonly pathExport: string }>;\n private readonly resolveAgentStates: () => AgentState[];\n private readonly deliverMessage: (\n agentId: string,\n message: string,\n preparedKimiContext?: import('./kimi-context-envelope.js').PreparedKimiMessage,\n ) => Promise<{ readonly ok: boolean; readonly failure?: string }>;\n private readonly resolveSupervisorScriptPath: () => string;\n private readonly writePtyTokenFor: (agentId: string) => Promise<string>;\n\n constructor(options: KimiCodeRuntimeOptions = {}) {\n this.overdeckHomeOverride = options.overdeckHome;\n this.kimiHomeOverride = options.kimiHome;\n this.execCommand = options.execCommand ?? (async (command) => execAsync(command));\n this.prepareLaunch = options.prepareLaunch ?? (() => prepareHarnessLaunch('kimi-code'));\n this.resolveAgentStates = options.listAgentStates ?? (() => listAgentStates());\n this.deliverMessage = options.deliverMessage ?? ((agentId, message, preparedKimiContext) => (\n deliverAgentMessage(agentId, message, 'runtime:kimi-code', undefined, { preparedKimiContext })\n ));\n this.resolveSupervisorScriptPath = options.resolveSupervisorScriptPath ?? resolvePtySupervisorScriptPath;\n this.writePtyTokenFor = options.writePtyTokenFor ?? writePtyToken;\n }\n\n getHarnessBehavior(): HarnessBehavior {\n return getRuntimeBehavior('kimi-code');\n }\n\n getSessionPath(agentId: string): string | null {\n const workspace = this.workspaceFor(agentId);\n if (!workspace) return null;\n return findKimiWirePath(this.kimiHome(), workspace, this.readSessionId(agentId));\n }\n\n getLastActivity(agentId: string): Date | null {\n const path = this.getSessionPath(agentId);\n if (!path) return null;\n try {\n return statSync(path).mtime;\n } catch {\n return null;\n }\n }\n\n getHeartbeat(agentId: string): Heartbeat | null {\n const lastActivity = this.getLastActivity(agentId);\n if (!lastActivity) return null;\n return {\n timestamp: lastActivity,\n agentId,\n source: 'jsonl',\n confidence: 'medium',\n };\n }\n\n getTokenUsage(agentId: string): TokenUsage | null {\n const path = this.getSessionPath(agentId);\n if (!path) return null;\n return parseKimiSessionSync(path)?.usage ?? null;\n }\n\n getSessionCost(agentId: string): CostBreakdown | null {\n const path = this.getSessionPath(agentId);\n if (!path) return null;\n const parsed = parseKimiSessionSync(path);\n if (!parsed) return null;\n return {\n inputCost: 0,\n outputCost: 0,\n cacheReadCost: 0,\n cacheWriteCost: 0,\n totalCost: parsed.cost_v2 ?? parsed.cost ?? 0,\n currency: 'USD',\n };\n }\n\n async sendMessage(agentId: string, message: string): Promise<void> {\n const result = await this.deliverMessage(agentId, message);\n if (!result.ok) {\n throw new Error(`Kimi Code agent ${agentId}: message delivery failed${result.failure ? ` (${result.failure})` : ''}`);\n }\n }\n\n async killAgent(agentId: string): Promise<void> {\n // Step 1: Ctrl-C the interactive TUI so it can wind down cleanly.\n try {\n await this.execCommand(`tmux -L ${shellQuote(getManagedTmuxSocketName())} send-keys -t ${shellQuote(agentId)} C-c 2>/dev/null || true`);\n } catch {\n // Best effort: the SIGTERM/SIGKILL ladder below still tears down the session.\n }\n if (await pollUntilSessionGone(agentId, 2_000)) return;\n\n // Step 2: SIGTERM the pane's process group.\n let panePid: string | null = null;\n try {\n const { stdout } = await this.execCommand(\n `tmux -L ${shellQuote(getManagedTmuxSocketName())} list-panes -t ${shellQuote(agentId)} -F '#{pane_pid}' 2>/dev/null`,\n );\n panePid = stdout.trim() || null;\n if (panePid) {\n await this.execCommand(`kill -TERM -- -${panePid} 2>/dev/null || kill -TERM ${panePid} 2>/dev/null || true`);\n }\n } catch {\n // Best effort: the remaining kill ladder still tears down the tmux session.\n }\n if (await pollUntilSessionGone(agentId, 5_000)) return;\n\n // Step 3: SIGKILL, then fall back to tmux kill-session outright.\n if (panePid) {\n try {\n await this.execCommand(`kill -KILL -- -${panePid} 2>/dev/null || kill -KILL ${panePid} 2>/dev/null || true`);\n } catch {\n // Best effort: tmuxKillSession below removes any remaining session.\n }\n }\n if (await tmuxSessionExists(agentId)) await tmuxKillSession(agentId);\n }\n\n async spawnAgent(config: SpawnConfig): Promise<Agent> {\n const { pathExport } = await this.prepareLaunch();\n const model = config.model ?? '';\n\n const bucketDir = kimiSessionsRoot(this.kimiHome(), config.workspace);\n let existingBefore = new Set<string>();\n try {\n existingBefore = new Set(readdirSync(bucketDir));\n } catch {\n // Bucket doesn't exist yet — every session dir that appears is new.\n }\n\n // The PTY supervisor tier of deliverAgentMessage requires BOTH the\n // pty-<id>.sock (created when the supervisor process binds it) AND a\n // readable pty-token it authenticates requests against — write the token\n // and resolve the real, package/desktop-safe script path (not a hardcoded\n // dist/ literal, which silently breaks under desktop packaging or a\n // mid-reload generation, PAN-3172) before the tmux session exists.\n mkdirSync(join(this.home(), 'agents', config.agentId), { recursive: true });\n const supervisorScriptPath = this.resolveSupervisorScriptPath();\n await this.writePtyTokenFor(config.agentId);\n\n const launcherContent = generateLauncherScriptSync({\n role: 'work',\n workingDir: config.workspace,\n harness: 'kimi-code',\n kimiCodeModel: model,\n kimiCodeEffort: config.effort,\n kimiCodeYolo: true,\n extraEnvExports: [pathExport],\n overdeckEnv: { agentId: config.agentId },\n setTerminalEnv: true,\n useSupervisor: true,\n supervisorScriptPath,\n unsetProviderEnv: true,\n kimiContextDelivery: 'initial-message',\n appendSystemPromptFiles: await claudeSystemPromptFiles(config.workspace, 'kimi-code'),\n });\n const launcherScript = this.agentPath(config.agentId, 'launcher.sh');\n writeFileSync(launcherScript, launcherContent, { mode: 0o755 });\n\n await tmuxCreateSession(config.agentId, config.workspace, `bash ${shellQuote(launcherScript)}`, config.env ?? {});\n\n try {\n const sessionId = await this.waitForNewSessionId(config.workspace, existingBefore);\n if (!sessionId) throw new KimiCodeSpawnTimeout(config.agentId);\n this.writeSessionId(config.agentId, sessionId);\n this.markSupervisorEnabled(config.agentId);\n\n const initialMessage = await prepareKimiMessage(\n config.agentId,\n config.workspace,\n config.prompt ?? '',\n { sessionId, overdeckHome: this.home() },\n );\n const delivery = await this.deliverMessage(config.agentId, initialMessage.message, initialMessage);\n if (!delivery.ok) {\n throw new Error(\n `Kimi Code agent ${config.agentId}: initial context delivery failed`\n + (delivery.failure ? ` (${delivery.failure})` : ''),\n );\n }\n markKimiContextDelivered(config.agentId, initialMessage);\n\n return {\n id: config.agentId,\n sessionId,\n runtime: 'kimi-code',\n model,\n workspace: config.workspace,\n startedAt: new Date(),\n };\n } catch (error) {\n if (await tmuxSessionExists(config.agentId)) await tmuxKillSession(config.agentId);\n throw error;\n }\n }\n\n listSessions(workspace?: string): Session[] {\n const sessions: Session[] = [];\n for (const state of this.resolveAgentStates()) {\n if (state.harness !== 'kimi-code') continue;\n if (workspace && state.workspace !== workspace) continue;\n\n const sessionId = this.readSessionId(state.id);\n if (!sessionId) continue;\n\n const sessionPath = findKimiWirePath(this.kimiHome(), state.workspace, sessionId);\n let lastActivity: Date;\n try {\n lastActivity = statSync(sessionPath ?? '').mtime;\n } catch {\n continue;\n }\n\n sessions.push({\n id: sessionId,\n agentId: state.id,\n workspace: state.workspace,\n model: state.model ?? '',\n startedAt: new Date(state.startedAt),\n lastActivity,\n tokenUsage: { inputTokens: 0, outputTokens: 0 },\n });\n }\n return sessions;\n }\n\n async isRunning(agentId: string): Promise<boolean> {\n return tmuxSessionExists(agentId);\n }\n\n private home(): string {\n return this.overdeckHomeOverride ?? getOverdeckHome();\n }\n\n private kimiHome(): string {\n return this.kimiHomeOverride ?? kimiHomeDefault();\n }\n\n private agentPath(agentId: string, file: string): string {\n return join(this.home(), 'agents', agentId, file);\n }\n\n private workspaceFor(agentId: string): string | null {\n return getAgentStateSync(agentId)?.workspace ?? null;\n }\n\n private readSessionId(agentId: string): string | null {\n try {\n return readFileSync(this.agentPath(agentId, 'kimi-session-id'), 'utf-8').trim() || null;\n } catch {\n return null;\n }\n }\n\n private writeSessionId(agentId: string, sessionId: string): void {\n writeKimiSessionId(agentId, sessionId, this.home());\n }\n\n /**\n * Best-effort: mark the persisted agent state as supervisor-enabled so\n * prepareSupervisorForRelaunch (supervisor-channels.ts) re-supervises this\n * agent on relaunch instead of falling back to useSupervisor:false. A\n * no-op when no state has been saved for this agent yet (some callers of\n * this adapter's spawnAgent create state afterward).\n */\n private markSupervisorEnabled(agentId: string): void {\n const state = getAgentStateSync(agentId);\n if (!state) return;\n state.supervisorEnabled = true;\n saveAgentStateSync(state);\n }\n\n private async waitForNewSessionId(workspace: string, existingBefore: Set<string>): Promise<string | null> {\n return waitForNewKimiSession(this.kimiHome(), workspace, existingBefore);\n }\n}\n\nexport function createKimiCodeRuntimeSync(options: KimiCodeRuntimeOptions = {}): KimiCodeRuntimeSync {\n return new KimiCodeRuntimeSync(options);\n}\n","import type { RuntimeName } from '../runtimes/types.js';\n\ninterface DeliveryOutcome {\n ok: boolean;\n path: string;\n failure?: string;\n}\n\n/** Enforce native Kimi's mandatory context contract independently of role bookkeeping. */\nexport async function requireManagedKimiDelivery(options: {\n agentId: string;\n role: string;\n harness: RuntimeName;\n delivery: DeliveryOutcome;\n onFailure: () => Promise<void>;\n}): Promise<void> {\n if (options.harness !== 'kimi-code' || options.delivery.ok) return;\n await options.onFailure();\n throw new Error(\n `Agent ${options.agentId} (${options.role}) managed Kimi context delivery failed: `\n + (options.delivery.failure ?? `delivery returned ok=false via ${options.delivery.path}`),\n );\n}\n","import { materializeMuseContext } from '../runtimes/muse-context.js';\nimport { resolveMuseSessionPath, museSessionId } from '../runtimes/muse-session.js';\nimport { existsSync, mkdirSync, writeFileSync } from 'fs';\nimport { mkdir, writeFile, writeFile as writeFileAsync } from 'fs/promises';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { randomUUID } from 'crypto';\nimport { homedir } from 'os';\nimport { join, resolve } from 'path';\nimport { Effect } from 'effect';\nimport { emitActivityEntrySync, emitActivityTtsSync } from '../activity-logger.js';\nimport { BLANKED_PROVIDER_ENV } from '../child-env.js';\nimport { isTldrEnabledSync, loadConfigSync } from '../config-yaml.js';\nimport { createConversation, getConversationByName, reactivateConversationForSpawn, setConversationClaudeSessionId } from '../overdeck/conversations.js';\nimport { startWorkSync } from '../cv.js';\nimport { generateFixedPointPromptSync, checkHookSync, initHookSync } from '../hooks.js';\nimport { generateLauncherScriptSync } from '../launcher-generator.js';\nimport { getProviderForModelSync, setupCredentialFileAuthSync, clearCredentialFileAuthSync } from '../providers.js';\nimport { refreshWorkStartReviewedAnchor, resetWorkStartPipelineVerdicts } from '../cloister/work-start-verdicts.js';\nimport { recordAgentPlaneSpawn } from '../pan-dir/agents.js';\nimport { shouldPreservePipelineVerdicts } from '../cloister/verdict-preservation.js';\nimport { resetPostMergeState } from '../cloister/post-merge-state.js';\nimport { isRoleTerminal, resolveCanonicalReviewStatus } from '../cloister/review-status-source.js';\nimport { resolveHarness } from '../harness-resolve.js';\nimport { prepareHarnessLaunch } from '../harness-binary.js';\nimport { assertCodexNativeAuthForSpawn } from '../codex-auth.js';\nimport type { ModelId } from '../settings.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport { writeBridgeTokenSync } from '../bridge-token.js';\nimport { createSession, exactPaneTarget, sessionExists, setOption } from '../tmux.js';\nimport { readWorkspacePlanSync } from '../xbrief/io.js';\nimport {\n getAgentDir,\n markAgentRunning,\n recordStartupSessionExit,\n saveAgentState,\n saveAgentStateSync,\n SESSION_EXITED_BEFORE_KICKOFF,\n type AgentState,\n type Role,\n} from './agent-state.js';\nimport { saveAgentRuntimeState } from './runtime-state.js';\nimport { clearReadySignal } from './identity.js';\nimport { deliverAgentMessage, deliverInitialPromptWithRetry } from './delivery.js';\nimport { determineModel, getProviderEnvForModel, getProviderExportsForModel } from './provider-env.js';\nimport {\n claudeSystemPromptFiles,\n getAcpLauncherFields,\n getCodexLauncherFields,\n getKimiCodeLauncherFields,\n getOhmypiLauncherFields,\n getProviderAuthMode,\n getRoleRuntimeBaseCommand,\n roleAgentDefinitionPath,\n waitForPromptReady,\n writeLauncherScriptAtomic,\n writeOhmypiAgentPrompt,\n} from './runtime-command.js';\nimport {\n buildAgentLaunchConfig,\n defaultRunWorkspace,\n flywheelEnvExports,\n resolveAgentStartedBy,\n resolveRegisteredSlotSpawn,\n resolveSlotTierSpawnParams,\n resolveSingleWorkTierSpawnParams,\n resolveFlywheelSpawnEnv,\n runAgentId,\n transitionIssueToInProgress,\n withSpawnTimeMemoryContext,\n prepareWorkspaceForAgentSpawn,\n type SpawnOptions,\n type SpawnRunOptions,\n} from './spawn-prep.js';\nimport { getConcurrencyLimits } from '../cloister/concurrency.js';\nimport { listAgentStates } from './queries.js';\nimport { findProjectByPathSync } from '../projects.js';\nimport { isStateMigrated } from '../state-home.js';\nimport { shouldCommitLegacyWorkspaceArtifacts } from '../state-read-home.js';\nimport {\n decideChannelsForWorkAgent,\n dismissDevChannelsDialog,\n prepareSupervisorForFreshLaunch,\n recordKickoffDeliveryFailure,\n writeChannelsBridgeMcpConfig,\n} from './supervisor-channels.js';\nimport { stopAgent } from './termination.js';\nimport { clearSessionResetMarker, createFreshSessionIdentity, logLauncherSessionPinned } from '../session-history.js';\nimport { ensureLifecycleHooksBeforeLaunch } from './hook-readiness.js';\nimport {\n withAutoSpawnConsentClaim,\n type AcceptAutoSpawnConsent,\n} from '../planning/auto-spawn-consent.js';\nimport { isOperatorStartedBy } from './provenance.js';\nimport { buildRegisteredSlotPrompt, ensureRegisteredSlotWorktree } from './registered-slot-spawn.js';\nimport { launchAndCaptureManagedKimiSession } from '../runtimes/kimi-code.js';\nimport { requireManagedKimiDelivery } from './managed-kimi-delivery.js';\nconst execAsync = promisify(exec);\nexport async function spawnRun(issueId: string, role: Role, options: SpawnRunOptions): Promise<AgentState> {\n if (role !== 'work') return spawnRunWithoutConsentClaim(issueId, role, options);\n\n const flywheelRunId = resolveFlywheelSpawnEnv(role, options.flywheelRunId).OVERDECK_FLYWHEEL_RUN_ID;\n const startedBy = resolveAgentStartedBy(options.startedBy, flywheelRunId);\n const resolvedOptions = { ...options, startedBy };\n if (isOperatorStartedBy(startedBy) || options.autoSpawnConsentRequired !== true) {\n return spawnRunWithoutConsentClaim(issueId, role, resolvedOptions);\n }\n\n return withAutoSpawnConsentClaim(\n issueId,\n (acceptConsent) => spawnRunWithoutConsentClaim(issueId, role, resolvedOptions, acceptConsent),\n { isAccepted: (state) => state.status === 'running' && state.kickoffDelivered !== false },\n );\n}\n\nasync function spawnRunWithoutConsentClaim(\n issueId: string,\n role: Role,\n options: SpawnRunOptions,\n acceptConsent?: AcceptAutoSpawnConsent,\n): Promise<AgentState> {\n const workspace = options.workspace ?? defaultRunWorkspace(issueId);\n const modelSpawnKey = `${role}:${issueId}`;\n const selectedModel = determineModel({ model: options.model, role, spawnKey: modelSpawnKey });\n\n if (role === 'work') {\n const slot = resolveRegisteredSlotSpawn(issueId, workspace, options);\n // Tiered execution (PAN-1791): when enabled, the slot item's difficulty\n // selects the worker — the resolved tier's model+harness replace the\n // parent default in the spawn params. Disabled → both stay as resolved\n // above, unchanged.\n let slotModel = selectedModel;\n let slotHarness = options.harness;\n if (slot) {\n assertRegisteredSlotCap(issueId, options.maxRegisteredSlots);\n const tierParams = resolveSlotTierSpawnParams(workspace, slot.slotItemId, options.model, modelSpawnKey);\n if (tierParams.model) {\n slotModel = determineModel({ model: tierParams.model, role, spawnKey: modelSpawnKey });\n // Implicit staffing (PAN-2397) omits harness — keep the parent's\n // historical harness handling in that case.\n slotHarness = tierParams.harness ?? options.harness;\n }\n await ensureRegisteredSlotWorktree(issueId, workspace, slot);\n }\n const prompt = slot\n ? buildRegisteredSlotPrompt(issueId, workspace, slot, options.prompt)\n : options.prompt;\n return spawnAgentWithoutConsentClaim({\n issueId,\n workspace: slot?.workspace ?? workspace,\n agentId: slot?.agentId,\n harness: slotHarness,\n model: slot ? slotModel : options.model,\n prompt,\n role: 'work',\n allowHost: options.allowHost,\n flywheelRunId: options.flywheelRunId,\n startedBy: options.startedBy,\n autoSpawnConsentRequired: options.autoSpawnConsentRequired,\n effort: options.effort,\n slotIndex: slot?.slotIndex,\n slotItemId: slot?.slotItemId, foreman: options.foreman,\n }, acceptConsent);\n }\n\n const flywheelEnv = resolveFlywheelSpawnEnv(role, options.flywheelRunId);\n const startedBy = resolveAgentStartedBy(options.startedBy, flywheelEnv.OVERDECK_FLYWHEEL_RUN_ID);\n const agentId = options.agentId ?? runAgentId(issueId, role, options.subRole);\n if (await Effect.runPromise(sessionExists(agentId))) {\n // PAN-2579 (warm-by-default lifecycle): advancing-role sessions are no longer\n // reaped at verdict time, so a session alive at dispatch time may be a\n // warm-idle leftover from the PREVIOUS cycle rather than an active run. Reap\n // it here — at the moment its slot is actually needed — when that is provable\n // (its phase verdict is terminal, or its pane process has exited). A live\n // session with a non-terminal verdict is genuinely active: keep throwing so\n // a concurrent duplicate dispatch cannot stomp it. (Review dispatch reuses\n // its warm session with context via spawnReviewRoleForIssue's resume path\n // before ever reaching this guard; test/ship runs start fresh by design.)\n let reapWarmIdle = false;\n const advancing = role === 'review' || role === 'test' || role === 'ship';\n try {\n const { isPaneDead } = await import('../tmux.js');\n if (await Effect.runPromise(isPaneDead(agentId))) {\n reapWarmIdle = true;\n } else if (advancing) {\n const { status } = resolveCanonicalReviewStatus(issueId);\n reapWarmIdle = !!status && isRoleTerminal(role as 'review' | 'test' | 'ship', status);\n }\n } catch { /* probe failure → conservative: treat as active */ }\n if (!reapWarmIdle) {\n throw new Error(`Role run ${agentId} already running. Use 'pan tell' to message it.`);\n }\n console.log(`[spawn] ${agentId} is warm-idle from the previous cycle — reaping it for the new ${role} dispatch (PAN-2579)`);\n const { killSession } = await import('../tmux.js');\n await Effect.runPromise(killSession(agentId)).catch(() => {});\n }\n await prepareWorkspaceForAgentSpawn(issueId, role, options.allowHost, workspace);\n initHookSync(agentId);\n\n const resolvedHarness: RuntimeName = await resolveHarness({\n explicit: options.harness,\n role,\n model: selectedModel,\n });\n const harnessBehavior = getHarnessBehavior(resolvedHarness);\n const isAcp = harnessBehavior.launchCommandKind === 'acp-host';\n const harnessLaunch = await prepareHarnessLaunch(resolvedHarness);\n // PAN-2285: reject fresh Codex launches when native auth would wedge in a 401 loop.\n assertCodexNativeAuthForSpawn(resolvedHarness, listAgentStates());\n await ensureLifecycleHooksBeforeLaunch(agentId, resolvedHarness);\n if (\n getProviderForModelSync(selectedModel).name === 'openai'\n && (await getProviderAuthMode(selectedModel)) === 'subscription'\n ) {\n const { isCliproxyRunning } = await import('../cliproxy.js');\n if (!(await Effect.runPromise(isCliproxyRunning()))) {\n throw new Error(\n 'CLIProxyAPI sidecar is not running. GPT subscription role runs route through '\n + 'a local cliproxy process managed by `pan up`. Run `pan up` (or restart the '\n + 'dashboard) before spawning a GPT role run.',\n );\n }\n }\n\n const state: AgentState = {\n id: agentId,\n issueId,\n workspace,\n harness: resolvedHarness,\n role, foreman: options.foreman || undefined,\n model: selectedModel,\n modelSpawnKey,\n status: 'starting',\n startedAt: new Date().toISOString(),\n ...(resolvedHarness === 'codex' ? {} : { costSoFar: 0 }),\n hostOverride: options.allowHost || undefined,\n slotIndex: options.slotIndex,\n slotItemId: options.slotItemId,\n flywheelRunId: flywheelEnv.OVERDECK_FLYWHEEL_RUN_ID,\n startedBy,\n ...(role === 'review' && options.subRole ? { reviewSubRole: options.subRole } : {}),\n reviewRunId: options.reviewRunId,\n reviewSynthesisAgentId: options.reviewSynthesisAgentId,\n reviewOutputPath: options.reviewOutputPath,\n reviewDeadlineAt: options.reviewDeadlineAt,\n };\n // PAN-1048 P1: spawnRun is on the dashboard hot path (Effect routes,\n // reactive Cloister scheduler). All disk I/O here uses async fs/promises\n // so we never block the Node event loop.\n await Effect.runPromise(saveAgentState(state));\n const isSpecialistRole = role === 'review' || role === 'test' || role === 'ship' || role === 'knowledge';\n const shouldRegisterConversation = isSpecialistRole || options.registerConversation === true;\n // PAN-1557: convoy sub-reviewers are now interactive specialists — deliver\n // their prompt via tmux after Claude boots (same as the orchestrator/test/\n // ship), not on stdin to a headless `claude --print`.\n const shouldDeliverPromptViaTmux = shouldRegisterConversation && resolvedHarness === 'claude-code';\n const shouldDeliverPromptViaPi = shouldRegisterConversation && resolvedHarness === 'ohmypi';\n const shouldDeliverPromptViaCodexTui = shouldRegisterConversation && resolvedHarness === 'codex';\n const shouldDeliverPromptViaKimiCode = resolvedHarness === 'muse' || resolvedHarness === 'kimi-code';\n const shouldDeliverPromptViaAcp = resolvedHarness === 'acp' || resolvedHarness === 'opencode';\n const prompt = options.prompt\n ? await withSpawnTimeMemoryContext({\n prompt: options.prompt,\n issueId,\n workspace,\n agentId,\n role,\n harness: resolvedHarness,\n })\n : '';\n\n let promptFile: string | undefined;\n const tracksKickoffDelivery = role === 'flywheel';\n if (prompt && !shouldDeliverPromptViaAcp && (tracksKickoffDelivery || (!shouldDeliverPromptViaTmux && !shouldDeliverPromptViaPi && !shouldDeliverPromptViaCodexTui && !shouldDeliverPromptViaKimiCode))) {\n promptFile = join(getAgentDir(agentId), 'initial-prompt.md');\n await writeFileAsync(promptFile, prompt);\n }\n if (prompt && tracksKickoffDelivery) {\n state.kickoffDelivered = false;\n await Effect.runPromise(saveAgentState(state));\n }\n\n if (!isAcp) {\n const provider = getProviderForModelSync(selectedModel as ModelId);\n if (provider.authType === 'credential-file') {\n setupCredentialFileAuthSync(provider, workspace);\n } else {\n clearCredentialFileAuthSync(workspace);\n }\n }\n\n const providerExports = isAcp ? undefined : await getProviderExportsForModel(selectedModel, resolvedHarness);\n const providerEnv = isAcp ? {} : await getProviderEnvForModel(selectedModel, resolvedHarness);\n // PAN-1048 review feedback 005 (S1): when the resolved harness is ohmypi, thread\n // the per-agent ohmypi launcher fields (--session-dir, --extension, FIFO\n // redirect) through generateLauncherScript so the role launcher emits the\n // correct `omp --mode rpc` command instead of a malformed Claude command.\n // Without this, a config'd `roles.review.harness: ohmypi` produced a launcher\n // that silently fell back to Claude shape.\n const piLauncherFields = resolvedHarness === 'ohmypi'\n ? await getOhmypiLauncherFields(agentId, selectedModel, options.effort)\n : {};\n const codexLauncherFields = resolvedHarness === 'codex'\n ? getCodexLauncherFields(agentId, selectedModel, workspace, role, options.effort)\n : {};\n const acpLauncherFields = isAcp\n ? getAcpLauncherFields(\n agentId,\n selectedModel,\n workspace,\n harnessLaunch.binaryPath,\n role,\n options.effort,\n )\n : {};\n const museSavedSession = resolvedHarness === 'muse' && options.resumeSessionId\n ? await resolveMuseSessionPath(agentId) : null;\n const museLauncherFields = resolvedHarness === 'muse' ? {\n harness: 'muse' as const,\n museModel: selectedModel,\n museEffort: options.effort,\n museContextFile: await materializeMuseContext(agentId, workspace, roleAgentDefinitionPath(role)),\n museResumeSessionId: museSavedSession ? museSessionId(museSavedSession) : undefined,\n } : {};\n // Kimi launchers require their model and effort fields even for specialist roles.\n const kimiCodeLauncherFields = resolvedHarness === 'kimi-code'\n ? getKimiCodeLauncherFields(selectedModel, options.effort)\n : {};\n\n // Create a conversation record for every specialist role — sub-role reviewers,\n // the review orchestrator/synthesizer, test, and ship. The row is the index\n // the dashboard reads to (a) locate the JSONL via claude_session_id, (b) carry\n // pre-JSONL state (spawn_error, fork_status), and (c) let the\n // conversation-lifecycle service compute sessionAlive from real tmux liveness\n // instead of from the agent state machine's status field, which can lag.\n // Excluding the orchestrator here previously forced AgentOutputPanel to\n // synthesize a Conversation whose sessionAlive came from `agent.status`, and\n // stale snapshots made active synthesizers render as \"Starting…\".\n let sessionId: string | undefined;\n let rawSessionId: string | undefined;\n if (shouldRegisterConversation) {\n // Claude-style harnesses own their session id at launcher construction time.\n // ACP creates its session during host startup and persists acp-session-id itself.\n rawSessionId = (isAcp || resolvedHarness === 'kimi-code')\n ? options.resumeSessionId\n : (options.resumeSessionId ?? randomUUID());\n\n if (!isAcp && resolvedHarness !== 'kimi-code' && rawSessionId) {\n // Persist the session ID to <agentDir>/session.id so resolveClaudeSessionId can locate the\n // JSONL after the specialist exits. Works for both fresh (--session-id) and resumed (--resume).\n try {\n const agentDir = getAgentDir(agentId);\n await mkdir(agentDir, { recursive: true });\n await writeFile(join(agentDir, 'session.id'), rawSessionId, 'utf-8');\n } catch (err) {\n console.warn(`[spawnRun] Failed to persist session.id for ${agentId}:`, err instanceof Error ? err.message : String(err));\n }\n }\n\n try {\n const conversation = {\n name: agentId,\n tmuxSession: agentId,\n cwd: workspace,\n issueId,\n claudeSessionId: rawSessionId,\n model: selectedModel,\n harness: resolvedHarness,\n };\n if (getConversationByName(agentId)) {\n reactivateConversationForSpawn(conversation);\n } else {\n createConversation(conversation);\n }\n } catch (err) {\n // Non-fatal: the specialist still runs, but without a conversation record\n console.warn(`[spawnRun] Failed to register conversation for ${agentId}:`, err instanceof Error ? err.message : String(err));\n }\n\n // Only set sessionId (→ --session-id flag) for fresh spawns.\n // Resumes pass resumeSessionId (→ --resume flag) to the launcher instead.\n if (!options.resumeSessionId) {\n sessionId = rawSessionId;\n }\n }\n await recordAgentPlaneSpawn(state, rawSessionId);\n // PAN-1557: interactive convoy wiring is already present in the initial\n // AgentState saved before launch, so the Stop-hook can always deliver\n // REVIEWER_READY even if a later running-state cache write is contended.\n const extraEnvExports = [harnessLaunch.pathExport, ...flywheelEnvExports(flywheelEnv), ...(options.extraEnvExports ?? [])];\n if (role === 'knowledge' && !extraEnvExports.includes('export PATH=\"$HOME/.overdeck/bin:$PATH\"')) {\n extraEnvExports.push('export PATH=\"$HOME/.overdeck/bin:$PATH\"');\n }\n\n const launcherContent = generateLauncherScriptSync({\n role,\n workingDir: workspace,\n changeDir: false,\n setTerminalEnv: true,\n providerExports,\n promptFile: shouldDeliverPromptViaTmux ? undefined : promptFile,\n promptFileMode: undefined,\n overdeckEnv: { agentId, issueId, sessionType: options.subRole ? `${role}.${options.subRole}` : role },\n extraEnvExports,\n baseCommand: await getRoleRuntimeBaseCommand(selectedModel, agentId, role, resolvedHarness, options.subRole, options.effort),\n appendSystemPromptFiles: await claudeSystemPromptFiles(workspace, resolvedHarness),\n sessionId,\n resumeSessionId: options.resumeSessionId,\n reviewSignal: undefined,\n trapHup: undefined,\n ...piLauncherFields,\n ...codexLauncherFields,\n ...acpLauncherFields,\n ...kimiCodeLauncherFields,\n ...museLauncherFields,\n });\n\n const launcherScript = join(getAgentDir(agentId), 'launcher.sh');\n await writeLauncherScriptAtomic(launcherScript, launcherContent);\n const claudeCmd = `bash ${launcherScript}`;\n console.log(`[claude-invoke] purpose=role-run | role=${role} | model=${state.model} | source=agents.ts:spawnRun | session=${agentId} | command=\"${claudeCmd}\"`);\n\n try {\n const { preTrustDirectory } = await import('../workspace-manager.js') as { preTrustDirectory: (dir: string) => void };\n preTrustDirectory(workspace);\n } catch { /* non-fatal */ }\n\n // PAN-1594: clear any stale ready.json before launch so waitForReadySignal()\n // only observes the session-start signal from THIS launch.\n clearReadySignal(agentId);\n\n const launchRoleSession = () => Effect.runPromise(createSession(agentId, workspace, claudeCmd, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n TERM: 'xterm-256color',\n OVERDECK_AGENT_ID: agentId,\n OVERDECK_ISSUE_ID: issueId,\n OVERDECK_SESSION_TYPE: role,\n OVERDECK_AGENT_STARTED_BY: startedBy,\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false',\n GIT_SEQUENCE_EDITOR: 'false',\n ...flywheelEnv,\n ...providerEnv,\n },\n }));\n if (resolvedHarness === 'kimi-code') {\n try {\n rawSessionId = await launchAndCaptureManagedKimiSession({\n agentId,\n workspace,\n launch: launchRoleSession,\n resumeSessionId: options.resumeSessionId,\n });\n if (shouldRegisterConversation) setConversationClaudeSessionId(agentId, rawSessionId);\n } catch (error) {\n const { killSession } = await import('../tmux.js');\n await Effect.runPromise(killSession(agentId)).catch(() => {});\n throw error;\n }\n } else {\n await launchRoleSession();\n }\n if (shouldRegisterConversation) {\n await saveAgentRuntimeState(agentId, {\n claudeSessionId: rawSessionId,\n ...(options.resumeSessionId ? {} : {\n sessionModel: selectedModel,\n sessionHarness: resolvedHarness,\n }),\n });\n }\n await Effect.runPromise(setOption(agentId, 'destroy-unattached', 'off'));\n await Effect.runPromise(setOption(exactPaneTarget(agentId), 'remain-on-exit', 'on'));\n\n if (prompt || resolvedHarness === 'kimi-code') {\n if (shouldDeliverPromptViaAcp) {\n try {\n await waitForPromptReady(agentId, resolvedHarness, 30);\n const delivery = await deliverAgentMessage(agentId, prompt, 'spawnRun:initial-prompt');\n if (!delivery.ok) {\n throw new Error(delivery.failure ?? `ACP delivery returned ok=false via ${delivery.path}`);\n }\n if (tracksKickoffDelivery) {\n state.kickoffDelivered = true;\n await Effect.runPromise(saveAgentState(state));\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[${agentId}] ACP prompt delivery failed:`, message);\n if (tracksKickoffDelivery) {\n await recordKickoffDeliveryFailure(state, issueId, role);\n }\n await Effect.runPromise(stopAgent(agentId));\n throw new Error(`Agent ${agentId} kickoff delivery failed: ${message}`);\n }\n } else if (shouldDeliverPromptViaPi) {\n try {\n await writeOhmypiAgentPrompt(agentId, prompt);\n if (tracksKickoffDelivery) {\n state.kickoffDelivered = true;\n await Effect.runPromise(saveAgentState(state));\n }\n } catch (err) {\n console.error(`[${agentId}] ohmypi prompt delivery failed:`, err instanceof Error ? err.message : String(err));\n }\n } else if (shouldDeliverPromptViaTmux || shouldDeliverPromptViaCodexTui || shouldDeliverPromptViaKimiCode) {\n if (tracksKickoffDelivery) {\n const delivery = await deliverInitialPromptWithRetry(agentId, prompt, 'spawnRun:initial-prompt');\n await requireManagedKimiDelivery({\n agentId,\n role,\n harness: resolvedHarness,\n delivery,\n onFailure: async () => {\n if (delivery.failure === SESSION_EXITED_BEFORE_KICKOFF) {\n await recordStartupSessionExit(state, issueId, role);\n }\n await recordKickoffDeliveryFailure(state, issueId, role);\n await Effect.runPromise(stopAgent(agentId)).catch(() => {});\n },\n });\n if (delivery.ok) {\n state.kickoffDelivered = true;\n await Effect.runPromise(saveAgentState(state));\n } else if (delivery.failure === SESSION_EXITED_BEFORE_KICKOFF) {\n await recordStartupSessionExit(state, issueId, role);\n return state;\n }\n } else {\n // PAN-1594: wait for the hook-written ready.json (session-start hook),\n // not a tmux pane-scrape. No dependency on permission-mode footer text.\n // Kimi Code's own readiness (readinessKind 'kimi-session-signal') is a\n // pane-scan, not a hook file — waitForPromptReady dispatches correctly.\n const timeout = getHarnessBehavior(resolvedHarness).readyTimeoutSeconds;\n const ready = await waitForPromptReady(agentId, resolvedHarness, timeout);\n if (ready) {\n await new Promise<void>((resolve) => setTimeout(resolve, 500));\n try {\n const delivery = await deliverAgentMessage(agentId, prompt, 'spawnRun:initial-prompt');\n if (resolvedHarness === 'kimi-code' && !delivery.ok) {\n throw new Error(delivery.failure ?? `delivery returned ok=false via ${delivery.path}`);\n }\n } catch (error) {\n if (resolvedHarness === 'kimi-code') await Effect.runPromise(stopAgent(agentId)).catch(() => {});\n throw error;\n }\n } else {\n if (resolvedHarness === 'kimi-code') await Effect.runPromise(stopAgent(agentId)).catch(() => {});\n throw new Error(`[${agentId}] ${getHarnessBehavior(resolvedHarness).displayName} did not become ready within ${timeout}s`);\n }\n }\n }\n }\n\n markAgentRunning(state);\n\n // Stamp the producer-issued workspace anchor this role run was launched\n // against. The reactive scheduler uses it to distinguish a still-relevant\n // run from a zombie session after any monorepo or polyrepo HEAD advances.\n // A non-fatal probe failure leaves the marker absent and preserves the\n // status-only fallback.\n try {\n const { snapshotWorkspaceHeadsPromise } = await import('../git-utils.js');\n const headAnchor = await snapshotWorkspaceHeadsPromise(issueId, workspace);\n if (headAnchor) state.roleRunHead = headAnchor;\n } catch { /* non-fatal — marker stays absent */ }\n\n await Effect.runPromise(saveAgentState(state));\n\n // PAN-1556: the review role emits a single dedicated \"Review role spawned\"\n // event from spawnReviewRoleForIssue. Suppress the generic per-spawn\n // \"role started\" for review so the orchestrator + 4 convoy sub-reviewers\n // don't each spam the session feed and bury conversations.\n if (role !== 'review') {\n emitActivityEntrySync({\n source: role,\n level: 'info',\n message: `${role} role started for ${issueId}`,\n issueId,\n });\n }\n\n return state;\n}\n\nexport async function spawnAgent(options: SpawnOptions): Promise<AgentState> {\n const role: 'work' | 'strike' | 'knowledge' = options.role ?? 'work';\n if (role !== 'work') return spawnAgentWithoutConsentClaim(options);\n\n const flywheelRunId = resolveFlywheelSpawnEnv(role, options.flywheelRunId).OVERDECK_FLYWHEEL_RUN_ID;\n const startedBy = resolveAgentStartedBy(options.startedBy, flywheelRunId);\n const resolvedOptions = { ...options, startedBy };\n if (isOperatorStartedBy(startedBy) || options.autoSpawnConsentRequired !== true) {\n return spawnAgentWithoutConsentClaim(resolvedOptions);\n }\n\n return withAutoSpawnConsentClaim(\n options.issueId,\n (acceptConsent) => spawnAgentWithoutConsentClaim(resolvedOptions, acceptConsent),\n { isAccepted: (state) => state.status === 'running' && state.kickoffDelivered !== false },\n );\n}\n\nasync function spawnAgentWithoutConsentClaim(\n options: SpawnOptions,\n acceptConsent?: AcceptAutoSpawnConsent,\n): Promise<AgentState> {\n const role: 'work' | 'strike' | 'knowledge' = options.role ?? 'work';\n const sessionPrefix = role === 'strike' ? 'strike' : 'agent';\n const agentId = options.agentId ?? `${sessionPrefix}-${options.issueId.toLowerCase()}`;\n\n // Check if already running (scoped to the exact session name, including slot suffix)\n if (await Effect.runPromise(sessionExists(agentId))) {\n throw new Error(`Agent ${agentId} already running. Use 'pan tell' to message it.`);\n }\n\n await prepareWorkspaceForAgentSpawn(options.issueId, role, options.allowHost, options.workspace);\n\n // Initialize hook for this agent (FPP support)\n initHookSync(agentId);\n\n if (role !== 'strike' && role !== 'knowledge' && options.slotItemId === undefined && !readWorkspacePlanSync(options.workspace)) {\n throw new Error(`The required xBRIEF checklist for ${options.issueId} is missing or unreadable. Run planning before spawning a work agent.`);\n }\n\n // Determine model based on role configuration\n const modelSpawnKey = `${role}:${options.issueId}`;\n const singleTierParams = role === 'work' && options.slotItemId === undefined && options.slotIndex === undefined\n ? resolveSingleWorkTierSpawnParams(options.workspace, options.model, modelSpawnKey)\n : {};\n const selectedModel = determineModel({ model: singleTierParams.model ?? options.model, role, spawnKey: modelSpawnKey });\n console.log(`[DEBUG] Selected model: ${selectedModel}`);\n\n // When routing a GPT agent through ChatGPT subscription auth, the local\n // CLIProxyAPI sidecar MUST already be running. We only check — never\n // install/start from here, because spawnAgent is reachable from dashboard\n // route handlers where blocking on curl/tar would freeze the event loop\n // (see PAN-70 / PAN-446 — no blocking I/O in server code).\n if (\n getProviderForModelSync(selectedModel).name === 'openai'\n && (await getProviderAuthMode(selectedModel)) === 'subscription'\n ) {\n const { isCliproxyRunning } = await import('../cliproxy.js');\n if (!(await Effect.runPromise(isCliproxyRunning()))) {\n throw new Error(\n 'CLIProxyAPI sidecar is not running. GPT subscription agents route through '\n + 'a local cliproxy process managed by `pan up`. Run `pan up` (or restart the '\n + 'dashboard) before spawning a GPT agent.',\n );\n }\n }\n\n const resolvedHarness: RuntimeName = await resolveHarness({\n explicit: options.harness ?? singleTierParams.harness,\n role,\n model: selectedModel,\n });\n const isAcp = getHarnessBehavior(resolvedHarness).launchCommandKind === 'acp-host';\n const harnessLaunch = await prepareHarnessLaunch(resolvedHarness);\n // PAN-2285: reject fresh Codex launches when native auth would wedge in a 401 loop.\n assertCodexNativeAuthForSpawn(resolvedHarness, listAgentStates());\n await ensureLifecycleHooksBeforeLaunch(agentId, resolvedHarness);\n const flywheelEnv = resolveFlywheelSpawnEnv(role, options.flywheelRunId);\n const startedBy = resolveAgentStartedBy(options.startedBy, flywheelEnv.OVERDECK_FLYWHEEL_RUN_ID);\n const state: AgentState = {\n id: agentId,\n issueId: options.issueId,\n workspace: options.workspace,\n harness: resolvedHarness,\n role, foreman: options.foreman || undefined,\n model: selectedModel,\n modelSpawnKey,\n status: 'starting',\n startedAt: new Date().toISOString(),\n ...(resolvedHarness === 'codex' ? {} : { costSoFar: 0 }),\n hostOverride: options.allowHost || undefined,\n sessionId: createFreshSessionIdentity(agentId, resolvedHarness),\n flywheelRunId: flywheelEnv.OVERDECK_FLYWHEEL_RUN_ID,\n startedBy,\n };\n const supervisorLaunch = await prepareSupervisorForFreshLaunch(agentId, options, state);\n\n saveAgentStateSync(state);\n clearSessionResetMarker(agentId);\n await recordAgentPlaneSpawn(state);\n // Transition issue tracker to \"in progress\" immediately so Linear reflects reality\n // while workspace setup continues. Best-effort, don't block agent spawn.\n // Only for work agents, not planning/specialist agents.\n if (role === 'work') {\n try {\n const preservation = await shouldPreservePipelineVerdicts(options.issueId, options.workspace);\n if (preservation.preserve) {\n if (preservation.refreshedAnchor) refreshWorkStartReviewedAnchor(options.issueId, preservation.refreshedAnchor);\n console.log(`[spawn] Preserved pipeline verdicts for ${options.issueId} — ${preservation.reason}`);\n } else {\n const resetStatus = resetWorkStartPipelineVerdicts(options.issueId);\n if (resetStatus) resetPostMergeState(options.issueId);\n }\n } catch (err) {\n console.warn(`[agents] Could not reset stale pipeline verdicts for ${options.issueId}: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n transitionIssueToInProgress(options.issueId, options.workspace).catch((err) => {\n console.warn(`[agents] Could not transition ${options.issueId} to in_progress: ${err.message}`);\n });\n }\n\n // For child stories: synthesize feature context from parent feature plan\n // before the agent starts so readFeatureContext has O(1) local access.\n if (role === 'work') {\n try {\n const { writeStoryFeatureContext } = await import('../cloister/work-agent-prompt.js');\n await writeStoryFeatureContext(options.workspace, options.issueId);\n } catch (ctxErr: any) {\n console.warn(`[agents] Could not write story feature context for ${options.issueId}: ${ctxErr.message}`);\n }\n }\n\n // PAN-1215: One-shot cleanup of tracked workspace-only .pan/ artifacts.\n // These files are gitignored but may still be tracked on older branches.\n // If tracked, checkpoint commits and rebases can drop them, breaking the\n // verification gate. Remove them from the index when the workspace is clean.\n if (role === 'work') {\n try {\n const workspace = options.workspace;\n const project = findProjectByPathSync(workspace);\n if (project && !shouldCommitLegacyWorkspaceArtifacts(await isStateMigrated(project))) {\n console.warn(`[agents] Deferred legacy .pan/ index cleanup for ${options.issueId} — migrated projects use gitignored .overdeck/ runtime files; historical entries retire with the branch`);\n } else {\n const { stdout: trackedFiles } = await execAsync(\n 'git ls-files .pan/continue.json .pan/spec.vbrief.json',\n { cwd: workspace },\n );\n if (trackedFiles.trim()) {\n const { stdout: porcelain } = await execAsync(\n 'git status --porcelain -- .pan/',\n { cwd: workspace },\n );\n if (!porcelain.trim()) {\n await execAsync(\n 'git rm --cached --ignore-unmatch .pan/continue.json .pan/spec.vbrief.json',\n { cwd: workspace },\n );\n await execAsync(\n 'git commit -m \"chore: untrack workspace .pan/ artifacts (PAN-1215)\"',\n { cwd: workspace },\n );\n console.log(`[agents] Untracked workspace .pan/ artifacts for ${options.issueId}`);\n } else {\n console.warn(`[agents] Skipping .pan/ untrack for ${options.issueId} — .pan/ paths have uncommitted changes`);\n }\n }\n }\n } catch (err: any) {\n console.warn(`[agents] .pan/ untrack cleanup failed for ${options.issueId}: ${err.message}`);\n }\n }\n\n // Build prompt with FPP work if available\n let prompt = options.prompt || '';\n\n // FPP: Check for pending work on hook\n const { hasWork } = checkHookSync(agentId);\n if (hasWork) {\n const fixedPointPrompt = generateFixedPointPromptSync(agentId);\n if (fixedPointPrompt) {\n prompt = fixedPointPrompt + '\\n\\n---\\n\\n' + prompt;\n }\n }\n\n if (prompt) {\n prompt = await withSpawnTimeMemoryContext({\n prompt,\n issueId: options.issueId,\n workspace: options.workspace,\n agentId,\n role,\n harness: resolvedHarness,\n });\n }\n\n // ACP receives the initial prompt only after its authenticated host socket is ready.\n const promptFile = join(getAgentDir(agentId), 'initial-prompt.md');\n const tracksKickoffDelivery = role === 'work' || role === 'strike';\n if (prompt && !isAcp) {\n await writeFileAsync(promptFile, prompt);\n }\n if (prompt && tracksKickoffDelivery) {\n state.kickoffDelivered = false;\n saveAgentStateSync(state);\n }\n\n // Ensure TLDR daemon is running for the workspace (non-blocking, non-fatal).\n // Gated by the operator TLDR toggle: when disabled, the daemon is not started\n // and the agent (whose prompt reports TLDR_AVAILABLE=false) degrades to direct\n // file reads.\n try {\n const venvPath = join(options.workspace, '.venv');\n if (isTldrEnabledSync() && existsSync(venvPath)) {\n const { getTldrDaemonServiceSync } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonServiceSync(options.workspace, venvPath);\n const status = await tldrService.getStatus();\n if (!status.running) {\n await tldrService.start(true);\n console.log(`[${agentId}] Started TLDR daemon for workspace`);\n }\n }\n } catch {\n // Non-fatal — agents degrade to direct file reads if TLDR unavailable\n }\n\n // Write initial task cache for heartbeat hook\n writeTaskCache(agentId, options.issueId);\n\n // Clear ready signal before spawning (clean slate for PAN-87 fix)\n clearReadySignal(agentId);\n\n // Channels MCP gate: only the explicit legacy override writes a per-agent\n // .mcp.json, bridge token, and channelsEnabled state for new spawns. The PTY\n // supervisor remains the default delivery transport.\n const channelsDecision = decideChannelsForWorkAgent(agentId, options, state);\n let channelsBridgeMcpConfig: string | undefined;\n if (channelsDecision.eligible) {\n channelsBridgeMcpConfig = join(options.workspace, '.pan', 'agent-mcp.json');\n writeBridgeTokenSync(agentId);\n await writeChannelsBridgeMcpConfig(channelsBridgeMcpConfig, agentId);\n state.channelsEnabled = true;\n saveAgentStateSync(state);\n }\n\n const { launcherContent, providerEnv } = await buildAgentLaunchConfig({\n agentId,\n model: selectedModel,\n workspace: options.workspace,\n role,\n isPlanning: false,\n channelsBridgeMcpConfig,\n useSupervisor: supervisorLaunch.useSupervisor,\n supervisorScriptPath: supervisorLaunch.supervisorScriptPath,\n harness: state.harness ?? 'claude-code',\n harnessBinaryPath: harnessLaunch.binaryPath,\n sessionId: state.sessionId,\n extraEnvExports: [harnessLaunch.pathExport, ...flywheelEnvExports(flywheelEnv)],\n effort: options.effort,\n });\n\n const launcherScript = join(getAgentDir(agentId), 'launcher.sh');\n await writeLauncherScriptAtomic(launcherScript, launcherContent);\n if (state.sessionId) logLauncherSessionPinned(agentId, state.sessionId, launcherScript);\n const claudeCmd = `bash ${launcherScript}`;\n console.log(`[claude-invoke] purpose=work-agent | model=${state.model} | source=agents.ts:spawnAgent | session=${agentId} | command=\"${claudeCmd}\"`);\n\n // Pre-trust workspace directory in Claude Code to avoid the trust prompt\n try {\n const { preTrustDirectory } = await import('../workspace-manager.js') as { preTrustDirectory: (dir: string) => void };\n preTrustDirectory(options.workspace);\n } catch { /* non-fatal */ }\n\n // Configure workspace for GitHub App bot identity (PAN-536)\n // Agents push as panopticon-agent[bot] with short-lived installation tokens\n try {\n const { isGitHubAppConfigured, generateInstallationToken, configureWorkspaceForBot } = await import('../github-app.js');\n if (isGitHubAppConfigured()) {\n const { findProjectByPathSync } = await import('../projects.js');\n const project = findProjectByPathSync(resolve(options.workspace, '..', '..'));\n const ghRepo = project?.github_repo;\n if (ghRepo) {\n const [owner, repo] = ghRepo.split('/');\n const { token } = await Effect.runPromise(generateInstallationToken());\n await configureWorkspaceForBot(options.workspace, owner, repo, token);\n console.log(`[${agentId}] Configured workspace for bot push (panopticon-agent[bot])`);\n }\n }\n } catch (err: any) {\n console.warn(`[${agentId}] GitHub App config failed (falling back to SSH): ${err.message}`);\n }\n\n clearReadySignal(agentId);\n\n const launchWorkSession = () => Effect.runPromise(createSession(agentId, options.workspace, claudeCmd, {\n env: {\n ...BLANKED_PROVIDER_ENV, // Blank stale provider vars inherited by tmux server\n TERM: 'xterm-256color',\n OVERDECK_AGENT_ID: agentId,\n OVERDECK_ISSUE_ID: options.issueId,\n OVERDECK_SESSION_TYPE: role,\n OVERDECK_AGENT_STARTED_BY: startedBy,\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false', // Disable suggested prompts for autonomous agents (PAN-251)\n GIT_SEQUENCE_EDITOR: 'false', // Block interactive rebase / squash (agents forbidden from rewriting history)\n ...flywheelEnv,\n ...providerEnv, // Set correct provider env vars (BASE_URL, AUTH_TOKEN, etc.)\n }\n }));\n\n if (resolvedHarness === 'kimi-code') {\n try {\n await launchAndCaptureManagedKimiSession({\n agentId,\n workspace: options.workspace,\n launch: launchWorkSession,\n });\n } catch (err) {\n await Effect.runPromise(stopAgent(agentId)).catch(() => undefined);\n throw err;\n }\n } else {\n await launchWorkSession();\n }\n await acceptConsent?.();\n await saveAgentRuntimeState(agentId, {\n claudeSessionId: state.sessionId,\n sessionModel: selectedModel,\n sessionHarness: resolvedHarness,\n });\n\n // Channels: start dismissing the dev-channels confirmation dialog as soon as\n // the tmux session exists, but only block on completion when we are about to\n // deliver an initial prompt. Spawn-only callers should not sit in a 20s poll\n // loop waiting for a dialog they may never need.\n const dismissChannelsDialogPromise = channelsBridgeMcpConfig\n ? dismissDevChannelsDialog(agentId).catch(() => undefined)\n : null;\n\n // Send the initial prompt after the harness-specific readiness signal.\n if (prompt && isAcp) {\n try {\n await waitForPromptReady(agentId, resolvedHarness, 30);\n const delivery = await deliverAgentMessage(agentId, prompt, 'spawnAgent:initial-prompt');\n if (!delivery.ok) {\n throw new Error(delivery.failure ?? `ACP delivery returned ok=false via ${delivery.path}`);\n }\n if (tracksKickoffDelivery) {\n state.kickoffDelivered = true;\n saveAgentStateSync(state);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[${agentId}] ACP prompt delivery failed:`, message);\n if (tracksKickoffDelivery) {\n await recordKickoffDeliveryFailure(state, options.issueId, role);\n }\n await Effect.runPromise(stopAgent(agentId));\n throw new Error(`Agent ${agentId} kickoff delivery failed: ${message}`);\n }\n } else if (prompt && resolvedHarness === 'ohmypi') {\n try {\n await writeOhmypiAgentPrompt(agentId, prompt);\n if (tracksKickoffDelivery) {\n state.kickoffDelivered = true;\n saveAgentStateSync(state);\n }\n } catch (err) {\n console.error(`[${agentId}] ohmypi prompt delivery failed:`, err instanceof Error ? err.message : String(err));\n if (tracksKickoffDelivery) {\n await recordKickoffDeliveryFailure(state, options.issueId, role);\n await Effect.runPromise(stopAgent(agentId));\n throw new Error(`Agent ${agentId} kickoff delivery failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } else if (prompt || resolvedHarness === 'kimi-code') {\n if (dismissChannelsDialogPromise) {\n await dismissChannelsDialogPromise;\n }\n const delivery = await deliverInitialPromptWithRetry(agentId, prompt, 'spawnAgent:initial-prompt', state.deliveryMethod);\n await requireManagedKimiDelivery({\n agentId,\n role,\n harness: resolvedHarness,\n delivery,\n onFailure: async () => {\n if (tracksKickoffDelivery) {\n if (delivery.failure === SESSION_EXITED_BEFORE_KICKOFF) {\n await recordStartupSessionExit(state, options.issueId, role);\n }\n await recordKickoffDeliveryFailure(state, options.issueId, role);\n }\n await Effect.runPromise(stopAgent(agentId)).catch(() => {});\n },\n });\n if (delivery.ok) {\n if (tracksKickoffDelivery) {\n state.kickoffDelivered = true;\n saveAgentStateSync(state);\n }\n } else if (tracksKickoffDelivery) {\n if (delivery.failure === SESSION_EXITED_BEFORE_KICKOFF) {\n await recordStartupSessionExit(state, options.issueId, role);\n }\n await recordKickoffDeliveryFailure(state, options.issueId, role);\n await Effect.runPromise(stopAgent(agentId));\n throw new Error(`Agent ${agentId} kickoff delivery failed: ${delivery.failure ?? 'unknown error'}`);\n }\n }\n\n // For codex work agents, poll for the first rollout JSONL in the background\n // and persist the thread-id so transcript/cost lookups hit the fast path\n // (PAN-1805). Non-blocking — codex writes its rollout only after the kickoff\n // prompt lands, so a blocking wait here would stall spawn. The latest-rollout\n // fallback covers sessions whose first turn lands after this window.\n if (\n resolvedHarness === 'codex'\n && loadConfigSync().config.codex?.transport === 'tui'\n && getHarnessBehavior(resolvedHarness).readinessKind === 'codex-tui-prompt'\n ) {\n const codexHomeForAgent = join(homedir(), '.overdeck', 'agents', agentId, 'codex-home-v2');\n void (async () => {\n try {\n const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } =\n await import('../runtimes/codex.js');\n const rollout = await waitForCodexRollout(codexHomeForAgent, 120_000);\n if (rollout) {\n const threadId = extractThreadIdFromRollout(rollout);\n if (threadId) writeThreadId(agentId, threadId);\n }\n } catch { /* non-fatal — the latest-rollout fallback still resolves the transcript */ }\n })();\n }\n\n // Update status\n markAgentRunning(state);\n saveAgentStateSync(state);\n\n // Track work in CV\n startWorkSync(agentId, options.issueId);\n\n // Emit activity + TTS so the user knows an agent has started\n emitActivityEntrySync({\n source: role,\n level: 'info',\n message: `Work agent started for ${options.issueId}`,\n issueId: options.issueId,\n });\n emitActivityTtsSync({\n utterance: `Work agent started for ${options.issueId}`,\n priority: 2,\n issueId: options.issueId,\n source: 'work-agent',\n eventType: 'workAgent.started',\n });\n\n return state;\n}\n\nfunction assertRegisteredSlotCap(issueId: string, configuredCap?: number): void {\n const cap = configuredCap ?? getConcurrencyLimits().maxWorkAgents;\n if (!Number.isInteger(cap) || cap < 1) {\n throw new Error(`Registered slot cap must be a positive integer; got ${cap}.`);\n }\n\n const issueLower = issueId.toLowerCase();\n const slotAgentPattern = new RegExp(`^agent-${escapeRegExp(issueLower)}-slot-\\\\d+$`);\n const activeSlots = listAgentStates({ role: 'work' }).filter(agent =>\n slotAgentPattern.test(agent.id)\n && (agent.status === 'starting' || agent.status === 'running')\n );\n if (activeSlots.length >= cap) {\n throw new Error(\n `Registered slot cap reached for ${issueId}: ${activeSlots.length}/${cap} active slot agents.`\n );\n }\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Write task cache for heartbeat hook to use\n */\nfunction writeTaskCache(agentId: string, issueId: string): void {\n const cacheDir = join(getAgentDir(agentId));\n mkdirSync(cacheDir, { recursive: true });\n\n const cacheFile = join(cacheDir, 'current-task.json');\n writeFileSync(\n cacheFile,\n JSON.stringify({\n id: issueId,\n title: `Working on ${issueId}`,\n updated_at: new Date().toISOString()\n }, null, 2)\n );\n}\n","/**\n * oh-my-pi (omp) coding agent runtime adapter (PAN-1989).\n *\n * Implements AgentRuntime for the `omp` binary running in --mode rpc. The\n * adapter wires together three pieces:\n *\n * - ohmypi-fifo.ts — per-agent rpc.in named pipe lifecycle\n * - launcher-generator — emits the `exec omp --mode rpc ... < <fifo>` script\n * - ohmypi-extension — the vendored extension that writes ready.json,\n * heartbeats, and /pan-done markers from inside omp\n *\n * Spawn flow:\n * 1. mkdir agent dir\n * 2. createOhmypiFifo\n * 3. write launcher.sh via generateLauncherScript({ harness: 'ohmypi', ... })\n * 4. tmux new-session running launcher.sh\n * 5. wait for ~/.overdeck/agents/<id>/ready.json (max 120s)\n *\n * Runtime divergences vs pi (omp contract AC-1b, AC-3):\n * - omp binary requires Bun >=1.3.14 (shebang #!/usr/bin/env bun).\n * - Resume flag: `--resume <id>` (NOT `--session <id>` which omp accepts but\n * silently ignores for resume — see AC-3 in docs/ohmypi-contract.md).\n * - `--no-context-files` is removed; drop it from the launcher command.\n */\n\nimport { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync, chmodSync, unlinkSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { promisify } from 'node:util'\nimport { exec } from 'node:child_process'\nimport { randomUUID } from 'node:crypto'\nimport { Effect } from 'effect'\nimport type {\n AgentRuntime,\n AgentRuntimeSync,\n AgentRuntimeError,\n HarnessBehavior,\n Heartbeat,\n TokenUsage,\n CostBreakdown,\n Session,\n SpawnConfig,\n Agent,\n} from './types.js'\nimport { OHMYPI_BEHAVIOR } from './behavior.js'\nimport { sessionExists, killSession, createSession, listSessionsSync, getManagedTmuxSocketName } from '../tmux.js'\nimport { parseOhmypiSessionSync } from '../cost-parsers/ohmypi-parser.js'\nimport { prepareHarnessLaunch } from '../harness-binary.js'\nimport { generateLauncherScriptSync } from '../launcher-generator.js'\nimport { createOhmypiFifo, destroyOhmypiFifoSync, writeOhmypiCommandSync, ohmypiFifoPaths, OhmypiNotReady } from './ohmypi-fifo.js'\nimport { ProcessSpawnError, ProcessTimeoutError, TmuxError } from '../errors.js'\nimport { getOverdeckHome } from '../paths.js'\n\nconst execAsync = promisify(exec)\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\nconst ACTIVE_HEARTBEAT_TTL_MS = 60_000\nconst SPAWN_READY_TIMEOUT_MS = 120_000\n\nfunction overdeckDir(): string {\n return getOverdeckHome()\n}\nfunction heartbeatsDir(): string {\n return join(overdeckDir(), 'heartbeats')\n}\nfunction agentsDir(): string {\n return join(overdeckDir(), 'agents')\n}\n\nexport class OhmypiSpawnTimeout extends Error {\n readonly code = 'OHMYPI_SPAWN_TIMEOUT' as const\n constructor(agentId: string) {\n super(`omp agent ${agentId} did not write ready.json within ${SPAWN_READY_TIMEOUT_MS}ms${describeSpawnFailure(agentId)}`)\n this.name = 'OhmypiSpawnTimeout'\n }\n}\n\nexport interface OhmypiSpawnConfig extends SpawnConfig {\n /** Absolute path to packages/ohmypi-extension/dist/index.js. */\n piExtensionPath: string\n /** Optional extra args appended to the `omp` command line. */\n extraPiArgs?: string\n}\n\nfunction agentDirFor(agentId: string): string {\n return join(agentsDir(), agentId)\n}\n\nfunction describeSpawnFailure(agentId: string): string {\n const outputPath = join(agentDirFor(agentId), 'output.log')\n if (!existsSync(outputPath)) return ''\n try {\n const tail = readFileSync(outputPath, 'utf8').slice(-1500).trim().split('\\n').slice(-8).join('\\n')\n return tail ? ` [output.log tail:\\n${tail}]` : ''\n } catch {\n return ''\n }\n}\n\nfunction ohmypiSessionDirFor(agentId: string): string {\n return join(agentDirFor(agentId), 'sessions')\n}\n\nfunction readyPathFor(agentId: string): string {\n return ohmypiFifoPaths(agentId).readyPath\n}\n\nfunction sessionIdPathFor(agentId: string): string {\n return join(agentDirFor(agentId), 'session.id')\n}\n\n/**\n * Read the persisted omp session id for resume.\n * Returns null when the file is absent or unreadable.\n */\nfunction readStoredSessionId(agentId: string): string | null {\n const path = sessionIdPathFor(agentId)\n if (!existsSync(path)) return null\n try {\n const raw = readFileSync(path, 'utf8').trim()\n return raw || null\n } catch {\n return null\n }\n}\n\n/**\n * Resolve the real omp session id from the freshest session JSONL (PAN-1988 parity).\n * Returns null when no prior session exists on disk.\n *\n * Exported (PAN-2098) so the generic recovery path in agents.ts\n * (`getLatestSessionIdSync`) can resume a crashed ohmypi agent — omp never\n * writes a `session.id` file, so without this fallback the deacon reports\n * \"no saved session id\" and can only respawn fresh, losing context.\n */\nexport function resolveLatestOhmypiSessionId(agentId: string): string | null {\n const root = ohmypiSessionDirFor(agentId)\n if (!existsSync(root)) return null\n const files: { path: string; mtime: number }[] = []\n walkJsonl(root, files)\n if (files.length === 0) return null\n files.sort((a, b) => b.mtime - a.mtime)\n const parsed = parseOhmypiSessionSync(files[0]!.path)\n return parsed?.sessionId ?? null\n}\n\nexport class OhmypiRuntimeSync implements AgentRuntimeSync {\n readonly name = 'ohmypi' as const\n\n getHarnessBehavior(): HarnessBehavior {\n return OHMYPI_BEHAVIOR\n }\n\n getSessionPath(agentId: string): string | null {\n const root = ohmypiSessionDirFor(agentId)\n if (!existsSync(root)) return null\n const files: { path: string; mtime: number }[] = []\n walkJsonl(root, files)\n if (files.length === 0) return null\n files.sort((a, b) => b.mtime - a.mtime)\n return files[0]!.path\n }\n\n getLastActivity(agentId: string): Date | null {\n const session = this.getSessionPath(agentId)\n if (!session) return null\n try {\n return statSync(session).mtime\n } catch {\n return null\n }\n }\n\n getHeartbeat(agentId: string): Heartbeat | null {\n // 1. Fresh active heartbeat (<60s old).\n const heartbeatPath = join(heartbeatsDir(), `${agentId}.json`)\n if (existsSync(heartbeatPath)) {\n try {\n const data = JSON.parse(readFileSync(heartbeatPath, 'utf-8')) as {\n timestamp?: string\n tool_name?: string\n last_action?: string\n pid?: number\n }\n if (data.timestamp) {\n const ts = new Date(data.timestamp)\n if (Date.now() - ts.getTime() < ACTIVE_HEARTBEAT_TTL_MS) {\n return {\n timestamp: ts,\n agentId,\n source: 'active-heartbeat',\n confidence: 'high',\n toolName: data.tool_name,\n lastAction: data.last_action,\n pid: data.pid,\n }\n }\n }\n } catch {\n // fall through\n }\n }\n\n // 2. JSONL mtime.\n const lastActivity = this.getLastActivity(agentId)\n if (lastActivity) {\n return {\n timestamp: lastActivity,\n agentId,\n source: 'jsonl',\n confidence: 'medium',\n }\n }\n\n // 3. tmux session creation time (best-effort).\n try {\n const sess = listSessionsSync().find(s => s.name === agentId)\n if (sess) {\n return {\n timestamp: sess.created,\n agentId,\n source: 'tmux',\n confidence: 'low',\n }\n }\n } catch {\n // ignore\n }\n return null\n }\n\n getTokenUsage(agentId: string): TokenUsage | null {\n const session = this.getSessionPath(agentId)\n if (!session) return null\n const parsed = parseOhmypiSessionSync(session)\n return parsed?.usage ?? null\n }\n\n getSessionCost(agentId: string): CostBreakdown | null {\n const session = this.getSessionPath(agentId)\n if (!session) return null\n const parsed = parseOhmypiSessionSync(session)\n if (!parsed) return null\n const totalCost = parsed.cost_v2 ?? parsed.cost ?? 0\n return {\n inputCost: 0,\n outputCost: 0,\n cacheReadCost: 0,\n cacheWriteCost: 0,\n totalCost,\n currency: 'USD',\n }\n }\n\n async sendMessage(agentId: string, message: string): Promise<void> {\n if (!existsSync(readyPathFor(agentId))) {\n throw new OhmypiNotReady(`omp agent ${agentId}: ready.json not present yet`)\n }\n // steer: omp delivers immediately when idle and queues mid-turn; a bare\n // prompt is rejected with AgentBusyError while a run is active.\n writeOhmypiCommandSync(agentId, { id: randomUUID(), type: 'prompt', message, streamingBehavior: 'steer' })\n }\n\n /**\n * Kill an omp agent via the documented escalation ladder.\n *\n * 1. Send {cmd:'abort'} via the RPC fifo (graceful).\n * 2. Wait up to 2s for the tmux session to disappear on its own.\n * 3. SIGTERM the omp process group via tmux send-signal.\n * 4. Wait up to 5s for the tmux session to disappear.\n * 5. tmux kill-session (effectively SIGKILL).\n * 6. Always unlink rpc.in, ready.json, heartbeat, and completed marker.\n * NEVER touch session JSONL files — per JSONL-is-sacred rule.\n */\n async killAgent(agentId: string): Promise<void> {\n // Step 1: graceful RPC abort.\n try {\n writeOhmypiCommandSync(agentId, { id: randomUUID(), type: 'abort' })\n } catch {\n // No reader / not ready — fall through to signal escalation.\n }\n\n // Step 2: poll for tmux exit up to 2s.\n if (await pollUntilSessionGone(agentId, 2_000)) {\n cleanupOhmypiTransientFiles(agentId)\n return\n }\n\n // Step 3: SIGTERM via tmux.\n try {\n await execAsync(`tmux -L ${shellQuote(getManagedTmuxSocketName())} send-keys -t ${agentId} C-c 2>/dev/null || true`)\n } catch {\n // ignore\n }\n try {\n const { stdout } = await execAsync(\n `tmux -L ${shellQuote(getManagedTmuxSocketName())} list-panes -t ${shellQuote(agentId)} -F '#{pane_pid}' 2>/dev/null`\n )\n const pid = stdout.trim()\n if (pid) {\n await execAsync(`kill -TERM -- -${pid} 2>/dev/null || kill -TERM ${pid} 2>/dev/null || true`)\n }\n } catch {\n // ignore\n }\n\n // Step 4: poll for tmux exit up to another 5s.\n if (await pollUntilSessionGone(agentId, 5_000)) {\n cleanupOhmypiTransientFiles(agentId)\n return\n }\n\n // Step 5: SIGKILL fallback via tmux kill-session.\n try {\n if (await Effect.runPromise(sessionExists(agentId))) {\n await Effect.runPromise(killSession(agentId))\n }\n } finally {\n cleanupOhmypiTransientFiles(agentId)\n }\n }\n\n async spawnAgent(config: SpawnConfig & { piExtensionPath?: string }): Promise<Agent> {\n const piExtensionPath = config.piExtensionPath\n if (!piExtensionPath) {\n throw new Error('OhmypiRuntime.spawnAgent requires piExtensionPath in config')\n }\n\n const harnessLaunch = await prepareHarnessLaunch('ohmypi')\n const agentId = config.agentId\n const dir = agentDirFor(agentId)\n const sessionDir = ohmypiSessionDirFor(agentId)\n const hadPriorSpawn = existsSync(sessionDir) &&\n readdirSync(sessionDir).some((f) => f.endsWith('.jsonl'))\n mkdirSync(dir, { recursive: true, mode: 0o700 })\n mkdirSync(sessionDir, { recursive: true, mode: 0o700 })\n\n const fifoPath = await Effect.runPromise(createOhmypiFifo(agentId))\n\n const promptFile = config.prompt ? writeAgentPromptFile(agentId, config.prompt) : undefined\n\n // Resume from the stored session.id when present, else recover from the\n // freshest session JSONL (PAN-1988 parity). omp uses --resume (not --session).\n const resumeSessionId = readStoredSessionId(agentId) ?? resolveLatestOhmypiSessionId(agentId) ?? undefined\n if (!resumeSessionId && hadPriorSpawn) {\n console.warn(\n `[ohmypi-runtime] ${agentId}: prior session.jsonl exists but no resumable session id found — spawning a fresh omp session`,\n )\n }\n\n const launcherScript = generateLauncherScriptSync({\n role: 'work',\n workingDir: config.workspace,\n harness: 'ohmypi',\n piExtensionPath,\n piFifoPath: fifoPath,\n piSessionDir: sessionDir,\n model: config.model,\n promptFile,\n resumeSessionId,\n overdeckEnv: { agentId },\n setTerminalEnv: true,\n extraEnvExports: [harnessLaunch.pathExport],\n trapHup: true,\n })\n\n const launcherPath = join(dir, 'ohmypi-launcher.sh')\n writeFileSync(launcherPath, launcherScript)\n chmodSync(launcherPath, 0o755)\n\n await Effect.runPromise(createSession(agentId, config.workspace, `bash ${launcherPath}`, {\n env: { OVERDECK_AGENT_ID: agentId },\n }))\n\n await waitForReady(agentId, SPAWN_READY_TIMEOUT_MS)\n\n const readyData = readReady(agentId)\n return {\n id: agentId,\n sessionId: readyData?.sessionId ?? 'unknown',\n runtime: 'ohmypi',\n model: config.model ?? 'unknown',\n workspace: config.workspace,\n startedAt: new Date(),\n }\n }\n\n listSessions(workspace?: string): Session[] {\n const sessions: Session[] = []\n const root = agentsDir()\n if (!existsSync(root)) return sessions\n for (const agentName of safeReaddir(root)) {\n const sessionRoot = ohmypiSessionDirFor(agentName)\n if (!existsSync(sessionRoot)) continue\n const files: { path: string; mtime: number }[] = []\n walkJsonl(sessionRoot, files)\n for (const file of files) {\n const parsed = parseOhmypiSessionSync(file.path)\n if (!parsed) continue\n sessions.push({\n id: parsed.sessionId,\n agentId: agentName,\n workspace: workspace ?? 'unknown',\n model: parsed.model,\n startedAt: new Date(parsed.startTime),\n lastActivity: new Date(file.mtime),\n tokenUsage: parsed.usage,\n })\n }\n }\n return sessions\n }\n\n async isRunning(agentId: string): Promise<boolean> {\n return await Effect.runPromise(sessionExists(agentId))\n }\n}\n\nexport function createOhmypiRuntimeSync(): OhmypiRuntimeSync {\n return new OhmypiRuntimeSync()\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\nexport class OhmypiRuntime implements AgentRuntime {\n readonly name = 'ohmypi' as const\n private readonly inner: OhmypiRuntimeSync\n\n constructor(inner: OhmypiRuntimeSync = new OhmypiRuntimeSync()) {\n this.inner = inner\n }\n\n getSessionPath(agentId: string): string | null {\n return this.inner.getSessionPath(agentId)\n }\n getHarnessBehavior(): HarnessBehavior {\n return this.inner.getHarnessBehavior()\n }\n getLastActivity(agentId: string): Date | null {\n return this.inner.getLastActivity(agentId)\n }\n getHeartbeat(agentId: string): Heartbeat | null {\n return this.inner.getHeartbeat(agentId)\n }\n getTokenUsage(agentId: string): TokenUsage | null {\n return this.inner.getTokenUsage(agentId)\n }\n getSessionCost(agentId: string): CostBreakdown | null {\n return this.inner.getSessionCost(agentId)\n }\n listSessions(workspace?: string): Session[] {\n return this.inner.listSessions(workspace)\n }\n\n sendMessage(agentId: string, message: string): Effect.Effect<void, AgentRuntimeError> {\n return Effect.tryPromise({\n try: () => Promise.resolve(this.inner.sendMessage(agentId, message)),\n catch: (cause) =>\n new TmuxError({\n command: 'rpc-write',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n })\n }\n\n killAgent(agentId: string): Effect.Effect<void, AgentRuntimeError> {\n return Effect.tryPromise({\n try: () => this.inner.killAgent(agentId),\n catch: (cause) =>\n new TmuxError({\n command: 'kill-session',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n }),\n })\n }\n\n spawnAgent(config: SpawnConfig): Effect.Effect<Agent, AgentRuntimeError> {\n return Effect.tryPromise({\n try: () => this.inner.spawnAgent(config),\n catch: (cause) => {\n if (cause instanceof OhmypiSpawnTimeout) {\n return new ProcessTimeoutError({\n command: 'omp',\n args: ['--mode', 'rpc'],\n timeoutMs: SPAWN_READY_TIMEOUT_MS,\n })\n }\n return new ProcessSpawnError({\n command: 'omp',\n args: ['--mode', 'rpc'],\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n })\n },\n })\n }\n\n isRunning(agentId: string): Effect.Effect<boolean> {\n return Effect.promise(() => this.inner.isRunning(agentId))\n }\n}\n\nexport function createOhmypiRuntime(): OhmypiRuntime {\n return new OhmypiRuntime()\n}\n\n// ────────────────────────────────────────────────────────────────────────\n// helpers\n// ────────────────────────────────────────────────────────────────────────\n\nasync function pollUntilSessionGone(agentId: string, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs\n while (Date.now() < deadline) {\n if (!(await Effect.runPromise(sessionExists(agentId)))) return true\n await new Promise(r => setTimeout(r, 100))\n }\n return false\n}\n\n/**\n * Remove omp's transient per-agent files: rpc.in fifo, ready.json,\n * heartbeats/<id>.json, completed marker. Never touches session JSONLs\n * under <agentDir>/sessions/ — those are sacred (CLAUDE.md).\n */\nfunction cleanupOhmypiTransientFiles(agentId: string): void {\n try { destroyOhmypiFifoSync(agentId) } catch { /* ignore */ }\n const dir = agentDirFor(agentId)\n for (const transient of ['ready.json', 'completed']) {\n const path = join(dir, transient)\n if (existsSync(path)) {\n try { unlinkSync(path) } catch { /* ignore */ }\n }\n }\n const heartbeatPath = join(heartbeatsDir(), `${agentId}.json`)\n if (existsSync(heartbeatPath)) {\n try { unlinkSync(heartbeatPath) } catch { /* ignore */ }\n }\n // Sacred: NEVER touch <dir>/sessions/*.jsonl — JSONLs are conversation history.\n}\n\nfunction readReady(agentId: string): { sessionId?: string; reason?: string } | null {\n const path = readyPathFor(agentId)\n if (!existsSync(path)) return null\n try {\n return JSON.parse(readFileSync(path, 'utf-8'))\n } catch {\n return null\n }\n}\n\nasync function waitForReady(agentId: string, timeoutMs: number): Promise<void> {\n const deadline = Date.now() + timeoutMs\n const path = readyPathFor(agentId)\n while (Date.now() < deadline) {\n if (existsSync(path)) return\n await new Promise(r => setTimeout(r, 100))\n }\n throw new OhmypiSpawnTimeout(agentId)\n}\n\nfunction writeAgentPromptFile(agentId: string, prompt: string): string {\n const promptPath = join(agentDirFor(agentId), 'prompt.txt')\n writeFileSync(promptPath, prompt)\n return promptPath\n}\n\nfunction safeReaddir(dir: string): string[] {\n try {\n return readdirSync(dir)\n } catch {\n return []\n }\n}\n\nfunction walkJsonl(dir: string, out: { path: string; mtime: number }[]): void {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return\n }\n for (const entry of entries) {\n const full = join(dir, entry)\n let stat\n try {\n stat = statSync(full)\n } catch {\n continue\n }\n if (stat.isDirectory()) {\n walkJsonl(full, out)\n } else if (stat.isFile() && entry.endsWith('.jsonl')) {\n out.push({ path: full, mtime: stat.mtime.getTime() })\n }\n }\n}\n\n// Keep imports referenced even when only the types are used.\nvoid execAsync\nvoid tmpdir\n","/** Canonical narrow read door for retained agent runtime events. */\nimport { getOverdeckDatabaseSync } from './infra.js';\n\ninterface EventPayloadRow {\n payload: string | Record<string, unknown> | null;\n}\n\ninterface AgentRuntimeEventRow extends EventPayloadRow {\n type: 'agent.created' | 'agent.model_set';\n timestamp: string | number;\n}\n\nexport interface AgentRuntimeEventSession {\n id: string;\n startedAt: string;\n}\n\nexport interface AgentRuntimeEventEvidence {\n agentId: string;\n issueId: string | null;\n role: string | null;\n workspace: string | null;\n model: string | null;\n branch: string | null;\n startedAt: string | null;\n sessions: AgentRuntimeEventSession[];\n}\n\nfunction parsePayload(payload: EventPayloadRow['payload']): Record<string, unknown> | null {\n if (payload && typeof payload === 'object') return payload;\n if (typeof payload !== 'string') return null;\n const parsed = JSON.parse(payload) as unknown;\n return parsed && typeof parsed === 'object' ? parsed as Record<string, unknown> : null;\n}\n\n/**\n * Return the latest retained session id learned for one agent. Missing\n * claudeSessionId fields are skipped; an explicit null/empty value is a clear\n * tombstone and prevents an older id from being resurrected.\n */\nexport function readLatestAgentClaudeSessionIdEventSync(agentId: string): string | null {\n const row = getOverdeckDatabaseSync().prepare(`\n SELECT payload\n FROM events\n WHERE type = 'agent.model_set'\n AND json_extract(payload, '$.agentId') = ?\n AND json_type(payload, '$.claudeSessionId') IS NOT NULL\n ORDER BY sequence DESC\n LIMIT 1\n `).get(agentId) as EventPayloadRow | undefined;\n const payload = parsePayload(row?.payload ?? null);\n if (!payload) return null;\n const sessionId = payload['claudeSessionId'];\n if (typeof sessionId !== 'string' || !sessionId.trim()) return null;\n return sessionId.trim();\n}\n\nfunction eventTimestamp(value: string | number): string {\n if (typeof value === 'string' && value.trim()) {\n const parsed = Date.parse(value);\n if (Number.isFinite(parsed)) return new Date(parsed).toISOString();\n }\n const numeric = Number(value);\n return new Date(Number.isFinite(numeric) ? numeric : 0).toISOString();\n}\n\n/** Read retained identity and session evidence for agent-plane backfill. */\nexport function listAgentRuntimeEventEvidenceSync(): AgentRuntimeEventEvidence[] {\n const rows = getOverdeckDatabaseSync().prepare(`\n SELECT type, timestamp, payload\n FROM events\n WHERE type IN ('agent.created', 'agent.model_set')\n ORDER BY sequence ASC\n `).all() as AgentRuntimeEventRow[];\n const evidence = new Map<string, AgentRuntimeEventEvidence>();\n\n for (const row of rows) {\n const payload = parsePayload(row.payload);\n const agentId = typeof payload?.['agentId'] === 'string'\n ? payload['agentId'].trim()\n : '';\n if (!payload || !agentId) continue;\n const current = evidence.get(agentId) ?? {\n agentId,\n issueId: null,\n role: null,\n workspace: null,\n model: null,\n branch: null,\n startedAt: null,\n sessions: [],\n };\n\n if (row.type === 'agent.created') {\n const agent = payload['agent'] && typeof payload['agent'] === 'object'\n ? payload['agent'] as Record<string, unknown>\n : {};\n const issueId = payload['issueId'] ?? agent['issueId'];\n if (typeof issueId === 'string' && issueId.trim()) current.issueId = issueId.trim();\n if (typeof agent['role'] === 'string' && agent['role'].trim()) current.role = agent['role'].trim();\n if (typeof agent['workspace'] === 'string' && agent['workspace'].trim()) current.workspace = agent['workspace'].trim();\n if (typeof agent['model'] === 'string' && agent['model'].trim()) current.model = agent['model'].trim();\n if (typeof agent['branch'] === 'string' && agent['branch'].trim()) current.branch = agent['branch'].trim();\n if (typeof agent['startedAt'] === 'string' && agent['startedAt'].trim()) current.startedAt = agent['startedAt'].trim();\n const createdSessionId = agent['sessionId'];\n if (typeof createdSessionId === 'string' && createdSessionId.trim()) {\n current.sessions.push({\n id: createdSessionId.trim(),\n startedAt: current.startedAt ?? eventTimestamp(row.timestamp),\n });\n }\n } else if (Object.prototype.hasOwnProperty.call(payload, 'claudeSessionId')) {\n const sessionId = payload['claudeSessionId'];\n if (typeof sessionId !== 'string' || !sessionId.trim()) {\n current.sessions = [];\n } else if (!current.sessions.some((session) => session.id === sessionId.trim())) {\n current.sessions.push({\n id: sessionId.trim(),\n startedAt: eventTimestamp(row.timestamp),\n });\n }\n }\n evidence.set(agentId, current);\n }\n\n return [...evidence.values()];\n}\n","import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, appendFileSync, statSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { Effect } from 'effect';\nimport {\n getAgentDir,\n getAgentStateSync,\n getAgentRuntimeStateSync,\n} from '../agents.js';\nimport { encodeClaudeProjectDir } from '../paths.js';\nimport { findLatestRollout, extractThreadIdFromRollout } from '../runtimes/codex.js';\nimport { resolveLatestOhmypiSessionId } from '../runtimes/ohmypi.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport { readLatestAgentClaudeSessionIdEventSync } from '../overdeck/event-reads.js';\nimport { appendAgentPlaneSession, readAgentPlaneRecordSync } from '../pan-dir/agents.js';\nimport { appendSessionIdToHistory, isSessionResetMarker, persistCurrentSessionId } from '../session-history.js';\n\n/** Activity log entry (still written by heartbeat-hook as a forensic artifact). */\nexport interface ActivityEntry {\n ts: string;\n tool: string;\n action?: string;\n state?: 'active' | 'idle';\n}\n\n/**\n * Append to activity log with automatic pruning to 100 entries\n */\nexport function appendActivity(agentId: string, entry: ActivityEntry): void {\n const dir = getAgentDir(agentId);\n mkdirSync(dir, { recursive: true });\n\n const activityFile = join(dir, 'activity.jsonl');\n\n // Append entry\n appendFileSync(activityFile, JSON.stringify(entry) + '\\n');\n\n // Prune to last 100 entries\n if (existsSync(activityFile)) {\n try {\n const lines = readFileSync(activityFile, 'utf8').trim().split('\\n');\n if (lines.length > 100) {\n const trimmed = lines.slice(-100);\n writeFileSync(activityFile, trimmed.join('\\n') + '\\n');\n }\n } catch (error) {\n // Ignore pruning errors - activity log is non-critical\n }\n }\n}\n\n/**\n * Read activity log (last N entries)\n */\nexport function getActivity(agentId: string, limit = 100): ActivityEntry[] {\n const activityFile = join(getAgentDir(agentId), 'activity.jsonl');\n\n if (!existsSync(activityFile)) {\n return [];\n }\n\n try {\n const lines = readFileSync(activityFile, 'utf8').trim().split('\\n');\n const entries = lines\n .filter(line => line.trim())\n .map(line => JSON.parse(line) as ActivityEntry)\n .slice(-limit);\n\n return entries;\n } catch {\n return [];\n }\n}\n\n/**\n * Save Claude session ID for later resume\n */\nexport function saveSessionId(\n agentId: string,\n sessionId: string,\n reason: 'rotation' | 'recovered' = 'rotation',\n): void {\n persistCurrentSessionId(agentId, sessionId);\n appendSessionIdToHistory(agentId, sessionId);\n const state = getAgentStateSync(agentId);\n if (!state) {\n console.warn(`[agents] Could not append durable session ${sessionId} for ${agentId}: agent state is missing`);\n return;\n }\n void appendAgentPlaneSession(state, {\n id: sessionId,\n startedAt: new Date().toISOString(),\n reason,\n }).catch((error) => {\n console.warn(\n `[agents] Could not append durable session ${sessionId} for ${agentId}: `\n + `${error instanceof Error ? error.message : String(error)}`,\n );\n });\n}\n\n/**\n * Get saved Claude session ID\n */\nexport function getSessionId(agentId: string): string | null {\n const sessionFile = join(getAgentDir(agentId), 'session.id');\n\n if (!existsSync(sessionFile)) {\n return null;\n }\n\n try {\n return readFileSync(sessionFile, 'utf8').trim();\n } catch {\n return null;\n }\n}\n\n/**\n * PAN-1988 — for a codex agent, resolve its REAL resumable thread id. codex writes a placeholder\n * UUID into `session.id` at spawn; the resumable id is the codex thread, recorded in the rollout.\n * Prefer the explicitly-captured `codex-thread-id`, then fall back to the freshest rollout on disk\n * (always current — codex writes a new rollout per resume, so this self-heals across resume cycles\n * without depending on the capture poll landing). Returns null for non-codex agents.\n */\nfunction resolveCodexThreadIdSync(agentId: string): string | null {\n const agentDir = getAgentDir(agentId);\n const codexHome = join(agentDir, 'codex-home');\n if (!existsSync(codexHome)) return null; // not a codex agent\n try {\n const threadIdPath = join(agentDir, 'codex-thread-id');\n if (existsSync(threadIdPath)) {\n const id = readFileSync(threadIdPath, 'utf-8').trim();\n if (id) return id;\n }\n } catch { /* non-fatal */ }\n try {\n const rollout = findLatestRollout(codexHome);\n if (rollout) {\n const id = extractThreadIdFromRollout(rollout);\n if (id) return id;\n }\n } catch { /* non-fatal */ }\n return null;\n}\n\n/**\n * Sync mirror of jsonl-resolver.ts's pickFreshestSessionId: from a list of\n * candidate session ids, return the one whose JSONL transcript has the most\n * recent mtime, skipping ids with no file on disk. Falls back to the last\n * appended id when none have a transcript (e.g. workspace moved). Returns null\n * only when there are no usable candidates.\n */\nfunction pickFreshestExistingSessionIdSync(agentId: string, candidates: unknown[]): string | null {\n const valid = candidates.filter((v): v is string => typeof v === 'string' && v.trim().length > 0);\n if (valid.length === 0) return null;\n const workspace = getAgentStateSync(agentId)?.workspace;\n if (workspace) {\n const projectDir = join(homedir(), '.claude', 'projects', encodeClaudeProjectDir(workspace));\n let best: { id: string; mtimeMs: number } | null = null;\n for (const id of valid) {\n try {\n const s = statSync(join(projectDir, `${id}.jsonl`));\n if (!best || s.mtimeMs > best.mtimeMs) best = { id, mtimeMs: s.mtimeMs };\n } catch { /* no JSONL for this id — skip */ }\n }\n if (best) return best.id;\n }\n return valid[valid.length - 1] ?? null;\n}\n\nexport interface SessionResolutionResult {\n sessionId: string | null;\n checked: string[];\n needsPointerRepair?: true;\n}\n\nexport interface ClaudeSessionRecoveryDeps {\n getAgentState?: typeof getAgentStateSync;\n isSessionReset?: (agentId: string) => boolean;\n readAgentPlaneRecord: typeof readAgentPlaneRecordSync;\n readEventSessionId: typeof readLatestAgentClaudeSessionIdEventSync;\n transcriptExists: (workspace: string, sessionId: string) => boolean;\n listTranscriptSessionIds: (workspace: string) => string[];\n log: (message: string) => void;\n}\n\nfunction claudeProjectDir(workspace: string): string {\n return join(homedir(), '.claude', 'projects', encodeClaudeProjectDir(workspace));\n}\n\nfunction defaultClaudeSessionRecoveryDeps(): ClaudeSessionRecoveryDeps {\n return {\n isSessionReset: isSessionResetMarker,\n readAgentPlaneRecord: readAgentPlaneRecordSync,\n readEventSessionId: readLatestAgentClaudeSessionIdEventSync,\n transcriptExists: (workspace, sessionId) => existsSync(join(claudeProjectDir(workspace), `${sessionId}.jsonl`)),\n listTranscriptSessionIds: (workspace) => {\n const projectDir = claudeProjectDir(workspace);\n try {\n return readdirSync(projectDir, { withFileTypes: true })\n .filter((entry) => entry.isFile() && entry.name.endsWith('.jsonl'))\n .map((entry) => entry.name.slice(0, -'.jsonl'.length));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.warn(\n `[agents] Transcript-directory scan failed for ${projectDir}: `\n + `${error instanceof Error ? error.message : String(error)}`,\n );\n }\n return [];\n }\n },\n log: (message) => console.warn(message),\n };\n}\n\nexport function resolveClaudeSessionRecoverySync(\n agentId: string,\n agentState: ReturnType<typeof getAgentStateSync>,\n deps: ClaudeSessionRecoveryDeps = defaultClaudeSessionRecoveryDeps(),\n): SessionResolutionResult {\n const checked: string[] = [];\n if (!agentState?.workspace || !agentState.issueId) {\n return { sessionId: null, checked: ['durable agents plane, event store, and transcript directory unavailable because agent metadata is missing'] };\n }\n\n try {\n checked.push('durable agents plane');\n const record = deps.readAgentPlaneRecord(agentState.issueId, agentId);\n const candidates = [...(record?.sessions ?? [])]\n .sort((left, right) => right.startedAt.localeCompare(left.startedAt));\n const candidate = candidates.find((entry) => deps.transcriptExists(agentState.workspace, entry.id));\n if (candidate) return { sessionId: candidate.id, checked, needsPointerRepair: true };\n } catch (error) {\n deps.log(\n `[agents] Durable agent-plane session lookup failed for ${agentId}: `\n + `${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n try {\n checked.push('agent.model_set event history');\n const eventSessionId = deps.readEventSessionId(agentId);\n if (eventSessionId && deps.transcriptExists(agentState.workspace, eventSessionId)) {\n return { sessionId: eventSessionId, checked, needsPointerRepair: true };\n }\n } catch (error) {\n deps.log(\n `[agents] Event-store session lookup failed for ${agentId}: `\n + `${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n checked.push('exactly-one transcript-directory scan');\n const transcriptIds = deps.listTranscriptSessionIds(agentState.workspace);\n return transcriptIds.length === 1\n ? { sessionId: transcriptIds[0] ?? null, checked, needsPointerRepair: true }\n : { sessionId: null, checked };\n}\n\nexport function resolveLatestSessionIdSync(\n agentId: string,\n recoveryDeps?: ClaudeSessionRecoveryDeps,\n): SessionResolutionResult {\n const isSessionReset = recoveryDeps?.isSessionReset ?? isSessionResetMarker;\n if (isSessionReset(agentId)) {\n return { sessionId: null, checked: ['session reset marker'] };\n }\n\n const checked: string[] = ['Codex rollout/thread id'];\n // 0. codex thread id FIRST — `session.id` below holds a placeholder UUID for codex agents, so\n // returning it would make resumeAgent target a non-existent thread and codex would drift into\n // a fresh rollout, losing conversation history (PAN-1988). The freshest rollout is the truth.\n const codexThreadId = resolveCodexThreadIdSync(agentId);\n if (codexThreadId) return { sessionId: codexThreadId, checked };\n\n // 1. ACP session id — the host writes the provider's durable session/load id.\n const agentState = recoveryDeps?.getAgentState?.(agentId) ?? getAgentStateSync(agentId);\n const sessionIdSource = agentState?.harness\n ? getHarnessBehavior(agentState.harness).sessionIdSource\n : undefined;\n if (sessionIdSource === 'acp-session-id') {\n checked.push('ACP session id');\n try {\n const acpSessionId = readFileSync(join(getAgentDir(agentId), 'acp-session-id'), 'utf-8').trim();\n if (acpSessionId) return { sessionId: acpSessionId, checked };\n } catch { /* non-fatal */ }\n }\n\n // 2. session.id (pinned before fresh launch and updated by suspend/resume) —\n // the real id for claude-code.\n checked.push('session.id');\n const fromSessionFile = getSessionId(agentId);\n if (fromSessionFile) return { sessionId: fromSessionFile, checked };\n\n // 2. sessions.json (append-only list of session ids the agent has used).\n // The array can hold aborted/empty ids (e.g. a fresh session that never\n // produced a transcript), so we can't trust \"last entry\" — pick the id\n // whose JSONL is freshest on disk, matching resolveClaudeSessionId\n // (jsonl-resolver.ts). Falls back to last-appended when none exist on disk.\n checked.push('sessions.json');\n const sessionsFile = join(getAgentDir(agentId), 'sessions.json');\n try {\n if (existsSync(sessionsFile)) {\n const sessions = JSON.parse(readFileSync(sessionsFile, 'utf8'));\n if (Array.isArray(sessions) && sessions.length > 0) {\n const picked = pickFreshestExistingSessionIdSync(agentId, sessions);\n if (picked) return { sessionId: picked, checked };\n }\n }\n } catch { /* non-fatal */ }\n\n // 3. runtime.json claudeSessionId\n checked.push('runtime.json');\n const runtimeState = getAgentRuntimeStateSync(agentId);\n if (runtimeState?.claudeSessionId) {\n return { sessionId: runtimeState.claudeSessionId, checked };\n }\n\n // 4. codex-thread-id (written after codex rollout appears; fallback so\n // resumeAgent can locate the Codex session even if session.id has a\n // stale random UUID from spawnRun's placeholder write).\n checked.push('codex-thread-id');\n const codexThreadIdPath = join(getAgentDir(agentId), 'codex-thread-id');\n try {\n if (existsSync(codexThreadIdPath)) {\n const threadId = readFileSync(codexThreadIdPath, 'utf-8').trim();\n if (threadId) return { sessionId: threadId, checked };\n }\n } catch { /* non-fatal */ }\n\n // 5. ohmypi (omp) — PAN-2098. omp never writes a `session.id` file, so none of\n // the claude-code/codex sources above can find it; the real id lives inside\n // the freshest session JSONL. Mirror the ohmypi runtime adapter's own resume\n // resolution so the deacon recovery path can resume a crashed ohmypi agent\n // instead of only respawning it fresh and losing context.\n if (sessionIdSource === 'transcript-jsonl') {\n checked.push('OhMyPi transcript session');\n const ohmypiSessionId = resolveLatestOhmypiSessionId(agentId);\n if (ohmypiSessionId) return { sessionId: ohmypiSessionId, checked };\n }\n\n // 6. kimi-code — the native Kimi Code CLI has no launcher-writable session.id\n // equivalent; the id is captured post-launch from its own wire.jsonl\n // session directory and persisted to `<agentDir>/kimi-session-id`\n // (writeKimiSessionId, mirrors codex's thread-id file above).\n if (sessionIdSource === 'kimi-session-newest') {\n checked.push('Kimi session pointer');\n try {\n const kimiSessionId = readFileSync(join(getAgentDir(agentId), 'kimi-session-id'), 'utf-8').trim();\n if (kimiSessionId) return { sessionId: kimiSessionId, checked };\n } catch { /* non-fatal */ }\n }\n\n if (agentState?.harness && sessionIdSource !== 'launcher-session-id') {\n return { sessionId: null, checked };\n }\n const recovered = resolveClaudeSessionRecoverySync(agentId, agentState, recoveryDeps);\n return {\n sessionId: recovered.sessionId,\n checked: [...checked, ...recovered.checked],\n ...(recovered.needsPointerRepair ? { needsPointerRepair: true as const } : {}),\n };\n}\n\nexport function getLatestSessionIdSync(agentId: string): string | null {\n return resolveLatestSessionIdSync(agentId).sessionId;\n}\n\nexport const getLatestSessionId = (\n agentId: string,\n recoveryDeps?: ClaudeSessionRecoveryDeps,\n): Effect.Effect<string | null> =>\n Effect.sync(() => resolveLatestSessionIdSync(agentId, recoveryDeps).sessionId);\n","import type { DomainEvent } from '@overdeck/contracts';\nimport { initEventStore } from '../dashboard/server/event-store.js';\nimport { getWorkspaceForIssue } from './workspaces/resolver.js';\n\nexport type OperatorInterventionKind = 'tell' | 'pause' | 'restart' | 'manual_edit' | 'deep_wipe' | 'unpause' | 'untroubled';\n\nexport interface OperatorInterventionInput {\n issueId: string;\n kind: OperatorInterventionKind;\n source: string;\n timestamp?: string;\n}\n\n/** PAN-1990 FR-15: additive workspaceId, populated when a workspace row exists for the issue. */\nexport function operatorInterventionEvent(input: OperatorInterventionInput): Omit<DomainEvent, 'sequence'> {\n return {\n type: 'operator.intervention',\n timestamp: input.timestamp ?? new Date().toISOString(),\n payload: {\n issueId: input.issueId,\n workspaceId: getWorkspaceForIssue(input.issueId)?.id,\n kind: input.kind,\n source: input.source,\n },\n } as Omit<DomainEvent, 'sequence'>;\n}\n\nexport async function appendOperatorInterventionEvent(input: OperatorInterventionInput): Promise<void> {\n const store = await initEventStore();\n await store.appendAsync(operatorInterventionEvent(input));\n}\n","/**\n * PAN-1980: session rotation on resume policy.\n *\n * Work agents used to rotate to a brand-new Claude session (a fresh JSONL\n * transcript) when a resume couldn't reuse the saved one — compact/overflow\n * recovery (PAN-1781), model/harness drift (PAN-1787/1797), or the messageAgent\n * resume-failure fresh-launch fallback. That rotation broke the conversation\n * panel (it pinned to a stale transcript while the terminal showed the live one)\n * and mostly papered over non-Anthropic CLIProxy overflow that native claude-code\n * handles in-place.\n *\n * Rotation is now DISABLED: a resume that would need a fresh session errors and\n * stops the agent for a human instead. Flip {@link ALLOW_SESSION_ROTATION_ON_RESUME}\n * to `true` to restore the old behavior; the intended replacement is in-place\n * `/compact` into the SAME session (the deacon already does this for\n * specialist/planning sessions).\n */\nexport const ALLOW_SESSION_ROTATION_ON_RESUME = false;\n\n/**\n * Whether a resume that would otherwise rotate to a fresh session must be\n * REFUSED. A resume needs a fresh session when it is a compact-recovery respawn\n * (`compactSeed` present) or the saved session drifted (model/harness). With\n * rotation disabled we refuse rather than rotate.\n */\nexport function sessionRotationRefused(opts: {\n compactSeed: boolean;\n driftReasons: readonly string[];\n allowExplicitRecovery?: boolean;\n}): boolean {\n const wouldRotate = opts.compactSeed || opts.driftReasons.length > 0;\n return wouldRotate && !ALLOW_SESSION_ROTATION_ON_RESUME && opts.allowExplicitRecovery !== true;\n}\n","/**\n * Eaten-by-compaction watcher for running Claude Code agents.\n *\n * Submit-time compaction can clear a delivered message from the composer\n * without writing its user record to the transcript. The transport has already\n * accepted the Enter at that point, so composer-only confirmation reports a\n * false success and pipeline feedback remains stranded. A compact boundary\n * without the message's own transcript record is positive evidence of that\n * loss; after a grace period, redeliver once without the original dedup key.\n */\n\nimport { probeTranscriptSince } from '../transcript-landing.js';\nimport { deliverAgentMessage } from './delivery.js';\n\nconst WATCH_TIMEOUT_MS = 5 * 60_000;\nconst WATCH_INTERVAL_MS = 3_000;\nconst REDELIVERY_GRACE_MS = 10_000;\n\nexport type EatenAgentMessageWatchOutcome = 'landed' | 'redelivered' | 'redelivery-failed' | 'unverified';\n\nexport interface EatenAgentMessageWatchArgs {\n agentId: string;\n workspace: string;\n sessionId: string;\n message: string;\n caller: string;\n deliveryMethod?: 'auto' | 'supervisor' | 'channels' | 'tmux';\n /** Transcript byte offset captured before the original delivery. */\n fromByteOffset: number;\n timeoutMs?: number;\n intervalMs?: number;\n graceMs?: number;\n deliver?: typeof deliverAgentMessage;\n probe?: typeof probeTranscriptSince;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function watchForEatenAgentMessage(\n args: EatenAgentMessageWatchArgs,\n): Promise<EatenAgentMessageWatchOutcome> {\n const deliver = args.deliver ?? deliverAgentMessage;\n const probe = args.probe ?? probeTranscriptSince;\n const timeoutMs = args.timeoutMs ?? WATCH_TIMEOUT_MS;\n const intervalMs = args.intervalMs ?? WATCH_INTERVAL_MS;\n const graceMs = args.graceMs ?? REDELIVERY_GRACE_MS;\n\n const deadline = Date.now() + timeoutMs;\n let boundarySeenAt: number | null = null;\n let redelivered = false;\n\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n const result = await probe(\n args.workspace,\n args.sessionId,\n args.fromByteOffset,\n args.message,\n );\n if (result.matchedUserRecord) {\n return redelivered ? 'redelivered' : 'landed';\n }\n if (redelivered || result.compactBoundaryCount === 0) continue;\n boundarySeenAt ??= Date.now();\n if (Date.now() - boundarySeenAt < graceMs) continue;\n\n console.warn(\n `[agent-eaten-message-watcher] ${args.agentId}: compact boundary landed without the ` +\n 'delivered message — submit-time compaction ate it; redelivering once.',\n );\n try {\n // A compact boundary plus an absent content-matched user record proves\n // the original side effect was lost. Redeliver without the completed\n // dedup key, which would otherwise suppress the repair attempt.\n await deliver(\n args.agentId,\n args.message,\n `${args.caller}:compaction-redelivery`,\n args.deliveryMethod,\n );\n redelivered = true;\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`[agent-eaten-message-watcher] ${args.agentId}: redelivery failed: ${message}`);\n return 'redelivery-failed';\n }\n }\n\n return 'unverified';\n}\n","/**\n * PAN-3015: pull-based monitor transport for Claude Code sessions.\n *\n * A `pan monitor` process runs as a background task INSIDE the agent's Claude\n * Code session. Claude Code surfaces background-command stdout to the model at\n * the next turn boundary (and wakes an idle session), so a message printed by\n * the monitor reaches the model without any keystroke injection — no composer\n * paste, no echo confirmation, no Enter timing.\n *\n * This module holds the shared pieces: the presence protocol senders use to\n * decide the monitor tier is live, the mail-file format, and the drain logic.\n * The long-running loop lives in `src/cli/commands/monitor.ts`; the delivery\n * decision lives in `messageAgent` (src/lib/agents/messaging.ts) — mid-session\n * tells only, never kickoff/resume (no monitor exists before the first turn).\n * See docs/MONITOR-TRANSPORT.md.\n */\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs';\nimport { readdir, readFile } from 'fs/promises';\nimport { join } from 'path';\nimport { getAgentDir } from './agent-state.js';\n\n/** Presence heartbeat cadence for a running monitor. */\nexport const MONITOR_HEARTBEAT_INTERVAL_MS = 15_000;\n/** A heartbeat older than this (3 missed beats) means the monitor is gone. */\nexport const MONITOR_PRESENCE_FRESHNESS_MS = 45_000;\n/** Monitor stdout blocks truncate here; `pan inbox` re-reads full bodies. */\nexport const MONITOR_BLOCK_MAX_BODY_CHARS = 4_000;\n\nexport interface MonitorPresence {\n pid: number;\n startedAt: string;\n heartbeatAt: string;\n}\n\nexport interface ParsedMailMessage {\n source?: string;\n date?: string;\n body: string;\n}\n\nexport function monitorPresencePath(agentId: string): string {\n return join(getAgentDir(agentId), 'monitor.json');\n}\n\nexport function agentMailDir(agentId: string): string {\n return join(getAgentDir(agentId), 'mail');\n}\n\nexport function agentMailReadDir(agentId: string): string {\n return join(agentMailDir(agentId), 'read');\n}\n\nexport function writeMonitorPresence(agentId: string, pid: number, startedAt: Date, now: Date): void {\n const presence: MonitorPresence = {\n pid,\n startedAt: startedAt.toISOString(),\n heartbeatAt: now.toISOString(),\n };\n mkdirSync(getAgentDir(agentId), { recursive: true });\n writeFileSync(monitorPresencePath(agentId), JSON.stringify(presence, null, 2));\n}\n\nexport function clearMonitorPresence(agentId: string): void {\n try {\n rmSync(monitorPresencePath(agentId), { force: true });\n } catch {\n // Best-effort; a stale file is caught by the freshness/pid checks.\n }\n}\n\nfunction isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * A monitor counts as live only with BOTH a fresh heartbeat and a live pid —\n * a dead monitor must fall through to keystroke transports, not strand the\n * message unread (it stays durable in mail/ either way).\n */\nexport function isMonitorLive(agentId: string, nowMs: number = Date.now()): boolean {\n try {\n const raw = readFileSync(monitorPresencePath(agentId), 'utf-8');\n const presence = JSON.parse(raw) as Partial<MonitorPresence>;\n if (typeof presence.pid !== 'number' || typeof presence.heartbeatAt !== 'string') return false;\n const beatMs = Date.parse(presence.heartbeatAt);\n if (!Number.isFinite(beatMs) || nowMs - beatMs > MONITOR_PRESENCE_FRESHNESS_MS) return false;\n return isPidAlive(presence.pid);\n } catch {\n return false;\n }\n}\n\n/**\n * Mail-file format (PAN-3015 adds the provenance header; the legacy format is\n * `# Message\\n\\n<body>` and must keep parsing):\n *\n * # Message\n *\n * source: pan-tell\n * date: 2026-07-24T00:00:00.000Z\n *\n * <body>\n */\nexport function formatMailFileContent(body: string, source: string, date: Date): string {\n return `# Message\\n\\nsource: ${source}\\ndate: ${date.toISOString()}\\n\\n${body}\\n`;\n}\n\nexport function parseMailFile(content: string): ParsedMailMessage {\n const withoutHeader = content.replace(/^# Message\\n\\n/, '');\n const sourceMatch = withoutHeader.match(/^source: (.+)\\ndate: (.+)\\n\\n/);\n if (!sourceMatch) {\n return { body: withoutHeader.replace(/\\n$/, '') };\n }\n return {\n source: sourceMatch[1],\n date: sourceMatch[2],\n body: withoutHeader.slice(sourceMatch[0].length).replace(/\\n$/, ''),\n };\n}\n\n/**\n * Render one message as the stdout block the model sees. stdout carries these\n * blocks ONLY; monitor diagnostics go to stderr.\n */\nexport function formatAgentMessageBlock(\n agentId: string,\n message: ParsedMailMessage,\n maxBodyChars: number = MONITOR_BLOCK_MAX_BODY_CHARS,\n): string {\n const meta = [\n message.source ? `source: ${message.source}` : null,\n message.date ? `at: ${message.date}` : null,\n ].filter(Boolean).join(' ');\n let body = message.body;\n if (body.length > maxBodyChars) {\n body = `${body.slice(0, maxBodyChars)}\\n[overdeck:agent-message] truncated — run \\`pan inbox ${agentId}\\` to read the full body`;\n }\n return `[overdeck:agent-message]${meta ? ` ${meta}` : ''}\\n${body}\\n[overdeck:agent-message] end`;\n}\n\n/**\n * Monitor territory: every `.md` mail file except `.pending.md`, which belongs\n * to the codex notify hook. `.delivered.md` post-delivery backups (PAN-3738)\n * are deliberately included — they were plain `<ts>.md` files before the suffix\n * existed and the monitor drained them, so keeping them in preserves today's\n * behavior exactly. The suffix is for humans reading `mail/`, not a filter.\n */\nfunction isPlainMailFile(name: string): boolean {\n return name.endsWith('.md') && !name.endsWith('.pending.md');\n}\n\n/**\n * Drain every unread plain mail file, oldest first. Each file is CLAIMED by\n * renaming it into mail/read/ before its block is emitted — a concurrent\n * drainer loses the rename and skips the file, so a message is never printed\n * twice (NFR-3). `.pending.md` (codex notify-hook territory) and `*.json`\n * (FPP mailbox) are never touched.\n *\n * Returns the number of messages emitted.\n */\nexport async function drainMailOnce(agentId: string, emit: (block: string) => void): Promise<number> {\n const mailDir = agentMailDir(agentId);\n const readDir = agentMailReadDir(agentId);\n let names: string[];\n try {\n names = (await readdir(mailDir)).filter(isPlainMailFile).sort();\n } catch {\n return 0;\n }\n if (names.length === 0) return 0;\n mkdirSync(readDir, { recursive: true });\n let emitted = 0;\n for (const name of names) {\n const claimed = join(readDir, name);\n try {\n renameSync(join(mailDir, name), claimed);\n } catch {\n continue; // Another drainer claimed it, or it vanished — never double-print.\n }\n const content = await readFile(claimed, 'utf-8');\n emit(formatAgentMessageBlock(agentId, parseMailFile(content)));\n emitted += 1;\n }\n return emitted;\n}\n\nexport interface InboxMessage extends ParsedMailMessage {\n file: string;\n read: boolean;\n}\n\n/**\n * Full-body re-read for `pan inbox` — unread mail plus the read archive,\n * oldest first, most recent `limit` entries. Moves nothing, truncates nothing.\n */\nexport function listInboxMessagesSync(agentId: string, limit: number): InboxMessage[] {\n const collect = (dir: string, read: boolean): InboxMessage[] => {\n if (!existsSync(dir)) return [];\n return readdirSync(dir)\n .filter(isPlainMailFile)\n .sort()\n .map((name) => ({\n ...parseMailFile(readFileSync(join(dir, name), 'utf-8')),\n file: name,\n read,\n }));\n };\n const all = [...collect(agentMailReadDir(agentId), true), ...collect(agentMailDir(agentId), false)]\n .sort((a, b) => a.file.localeCompare(b.file));\n return all.slice(-limit);\n}\n","import { createHash } from 'crypto';\nimport { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport { emitActivityEntrySync } from '../activity-logger.js';\nimport { BLANKED_PROVIDER_ENV } from '../child-env.js';\nimport { generateLauncherScriptSync } from '../launcher-generator.js';\nimport { prepareHarnessLaunch } from '../harness-binary.js';\nimport { appendOperatorInterventionEvent } from '../operator-interventions.js';\nimport { logAgentLifecycleSync } from '../persistent-logger.js';\nimport { getProviderForModelSync, setupCredentialFileAuthSync, clearCredentialFileAuthSync } from '../providers.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport { ALLOW_SESSION_ROTATION_ON_RESUME } from '../session-rotation.js';\nimport type { ModelId } from '../settings.js';\nimport { captureTranscriptUserRecordSnapshot } from '../transcript-landing.js';\nimport { createSession, killSession, listPaneValues, sessionExists } from '../tmux.js';\nimport {\n clearReadySignal,\n normalizeAgentId,\n waitForAgentIdle,\n} from './identity.js';\nimport {\n decideResumeGate,\n getAgentDir,\n getAgentResumeGateBlockReason,\n getAgentStateSync,\n markAgentRunning,\n saveAgentStateSync,\n type AgentState,\n type MessageAgentRedriveOptions,\n type Role,\n} from './agent-state.js';\nimport { getLatestSessionIdSync } from './activity.js';\nimport {\n deliverAgentMessage,\n deliverResumeMessageWithTranscriptConfirmation,\n resilientDeliveryMethod,\n} from './delivery.js';\nimport { watchForEatenAgentMessage } from './eaten-message-watcher.js';\nimport { formatMailFileContent, isMonitorLive } from './monitor-transport.js';\nimport { getAgentRuntimeStateSync } from './runtime-state.js';\nimport {\n claudeSystemPromptFiles,\n getCodexLauncherFields,\n getCodexAppServerStatus,\n getOhmypiLauncherFields,\n getRoleRuntimeBaseCommand,\n hasAgentRuntimeInSubtree,\n waitForPromptReady,\n} from './runtime-command.js';\nimport {\n buildResumeMessageForAgent,\n markKickoffRedelivered,\n prepareSupervisorForRelaunch,\n} from './supervisor-channels.js';\nimport {\n getProviderEnvForModel,\n getProviderExportsForModel,\n} from './provider-env.js';\n\nexport interface MessageDeliveryOutcome {\n delivered: boolean;\n queuedToMail: boolean;\n reason?: string;\n deduplicated?: boolean;\n}\n\nexport type MessageAgentOutcome = 'delivered' | 'queued';\n\n/** PAN-3736 — the one phrase for \"the message went to the mail file because the\n * agent was busy\". It states that the agent is alive, and names the file, so a\n * reader never mistakes a mid-turn agent for a dead one. Turn-end delivery is\n * real for `.pending.md` mail: the codex notify hook replays the oldest pending\n * file at every turn boundary. The phrase still stops short of promising it,\n * because the hook only runs for codex sessions. */\nfunction busyAgentQueuedReason(mailPath: string): string {\n return `agent is alive and mid-turn; message queued to its mail file (${mailPath})`;\n}\n\n/**\n * How a mail file is named — which is the same thing as saying who drains it\n * (PAN-3738):\n *\n * - `queued` → `<ts>.md` / `dedup-<hash>.md`. Plain mail: `pan monitor` drains\n * and prints it (`isPlainMailFile`), `pan inbox` re-reads it.\n * - `pending` → `<ts>.pending.md` / `dedup-<hash>.pending.md`. Busy-turn mail\n * the codex notify hook replays at the next turn end. The hook drains ONLY\n * `*.pending.md`, so keyed busy mail must carry the suffix too or it strands.\n * - `delivered` → `<ts>.delivered.md` / `dedup-<hash>.delivered.md`. A\n * post-delivery durable backup: the message already landed, and the suffix\n * stops a receipt from reading as queued mail to anyone inspecting `mail/`.\n * It stays plain mail for the monitor (`.delivered.md` ends with `.md` and\n * not `.pending.md`), so monitor and inbox behavior is unchanged.\n */\ntype MailKind = 'queued' | 'pending' | 'delivered';\n\nconst MAIL_FILENAME_SUFFIX: Record<MailKind, string> = {\n queued: '.md',\n pending: '.pending.md',\n delivered: '.delivered.md',\n};\n\n/** Writes the message to the agent's durable mail dir and returns the file path\n * it wrote, so callers can name it in operator-facing output (PAN-3736). */\nfunction queueAgentMail(\n agentId: string,\n message: string,\n kind: MailKind = 'queued',\n dedupKey?: string,\n source?: string,\n): string {\n const mailDir = join(getAgentDir(agentId), 'mail');\n mkdirSync(mailDir, { recursive: true });\n const now = new Date();\n const suffix = MAIL_FILENAME_SUFFIX[kind];\n // Keyed messages get a deterministic filename so a crash-replayed send\n // overwrites the same durable file instead of stacking a second copy.\n const filename = dedupKey !== undefined\n ? `dedup-${createHash('sha256').update(dedupKey).digest('hex').slice(0, 24)}${suffix}`\n : `${now.toISOString().replace(/[:.]/g, '-')}${suffix}`;\n // The provenance header is only written when a source is known (the monitor\n // tier, PAN-3015). Callers that replay mail verbatim (codex notify hook\n // pastes `.pending.md` content) keep the legacy `# Message\\n\\n<body>` shape.\n const content = source ? formatMailFileContent(message, source, now) : `# Message\\n\\n${message}\\n`;\n const mailPath = join(mailDir, filename);\n writeFileSync(mailPath, content\n );\n return mailPath;\n}\n\nconst USER_MESSAGE_INTERVENTION_SOURCES = new Set(['pan-tell', 'dashboard:user-message']);\n\nexport function resolveAgentDeliveryMethod(\n state: Pick<AgentState, 'harness' | 'deliveryMethod'> | null | undefined,\n): 'auto' | 'supervisor' | 'channels' | 'tmux' | undefined {\n if (state?.harness === 'acp' || state?.harness === 'opencode') return 'auto';\n return resilientDeliveryMethod(state?.deliveryMethod);\n}\n\nfunction claimCodexIdleTurn(agentId: string): boolean {\n try {\n unlinkSync(join(getAgentDir(agentId), 'turn-completed'));\n return true;\n } catch {\n return false;\n }\n}\n\nasync function appendTellInterventionForUserSource(normalizedId: string, caller: string): Promise<void> {\n if (!USER_MESSAGE_INTERVENTION_SOURCES.has(caller)) return;\n\n const agentState = getAgentStateSync(normalizedId);\n if (!agentState?.issueId) {\n console.debug(`[agents] Skipping tell intervention for ${normalizedId}; state.json has no issueId`);\n return;\n }\n\n await appendOperatorInterventionEvent({\n issueId: agentState.issueId,\n kind: 'tell',\n source: caller,\n });\n}\n\ntype DeliveryMethod = 'auto' | 'supervisor' | 'channels' | 'tmux' | undefined;\n\n/** Call the delivery door with the idempotency key only when one is set, so\n * unkeyed callers keep their exact pre-key call shape. */\nfunction deliverWithOptionalKey(\n normalizedId: string,\n message: string,\n caller: string,\n deliveryMethod: DeliveryMethod,\n dedupKey: string | undefined,\n) {\n return dedupKey !== undefined\n ? deliverAgentMessage(normalizedId, message, caller, deliveryMethod, { dedupKey })\n : deliverAgentMessage(normalizedId, message, caller, deliveryMethod);\n}\n\n/**\n * Keyed delivery to an agent that must be resumed first (PAN-2997 review\n * cycle 7). The keyed message NEVER rides the resume kickoff prompt: the\n * kickoff is delivered by components that cannot enforce the key, so a\n * dashboard crash after the kickoff but before any post-hoc key bookkeeping\n * would replay the wake. Instead the agent is resumed with its bare\n * auto-continue prompt, and the keyed message then goes through the keyed\n * delivery door, where the crash-independent component — the new session's\n * supervisor key set or the tmux session's two-phase markers — enforces\n * at-most-once across the whole replay window.\n */\nasync function resumeThenDeliverKeyed(\n normalizedId: string,\n message: string,\n caller: string,\n agentState: AgentState | null,\n dedupKey: string,\n): Promise<MessageDeliveryOutcome> {\n const { resumeAgent } = await import('../agents.js');\n const result = await resumeAgent(normalizedId);\n if (!result.success) {\n throw new Error(`Failed to auto-resume agent: ${result.error}`);\n }\n const delivery = await deliverAgentMessage(\n normalizedId,\n message,\n `messageAgent:${caller}`,\n resolveAgentDeliveryMethod(agentState),\n { dedupKey },\n );\n await appendTellInterventionForUserSource(normalizedId, caller);\n return {\n delivered: delivery.ok,\n queuedToMail: false,\n ...(delivery.deduplicated ? { deduplicated: true } : {}),\n };\n}\n\n\nexport async function messageAgent(\n agentId: string,\n message: string,\n caller = 'internal',\n opts: MessageAgentRedriveOptions = {},\n): Promise<MessageDeliveryOutcome> {\n const normalizedId = normalizeAgentId(agentId);\n const agentState = getAgentStateSync(normalizedId);\n\n // PAN-2668: pipeline feedback that owes rework (failed verification/review)\n // is a re-drive, not a casual message. Consult the intent policy so the\n // documented completed-handoff exception can clear stoppedByUser and deliver,\n // instead of silently mailing feedback to a queue nothing drains.\n const decideMessageGate = () => {\n const block = agentState ? getAgentResumeGateBlockReason(agentState) : undefined;\n const agentDir = getAgentDir(normalizedId);\n const hasCompletedHandoff = existsSync(join(agentDir, 'completed'))\n || existsSync(join(agentDir, 'completed.processed'));\n const decision = decideResumeGate(block, 'message-delivery', {\n hasCompletedHandoff,\n owesRework: opts.owesRework === true,\n });\n if (decision.decision === 'proceed' && decision.clearStoppedByUser && agentState) {\n console.log(`[agents] ${normalizedId} was operator-stopped but owes rework after a completed handoff — clearing stop gate to deliver feedback (PAN-2668)`);\n logAgentLifecycleSync(normalizedId, 'stoppedByUser cleared: completed handoff owes rework; delivering pipeline feedback (PAN-2668)');\n delete agentState.stoppedByUser;\n saveAgentStateSync(agentState);\n }\n return decision;\n };\n if (agentState?.paused === true) {\n const gateBlockReason = getAgentResumeGateBlockReason(agentState)?.reason ?? 'agent is paused';\n queueAgentMail(normalizedId, message, 'queued', opts.dedupKey);\n logAgentLifecycleSync(normalizedId, `messageAgent queued mail without resume: ${gateBlockReason}`);\n console.log(`[agents] Queued message for ${normalizedId}; ${gateBlockReason}`);\n return { delivered: false, queuedToMail: true, reason: gateBlockReason };\n }\n\n // Check if agent is suspended - auto-resume if so (PAN-80)\n const runtimeState = getAgentRuntimeStateSync(normalizedId);\n if (runtimeState?.state === 'suspended') {\n const suspendedGate = decideMessageGate();\n if (suspendedGate.decision !== 'proceed') {\n const gateBlockReason = suspendedGate.reason ?? 'agent is gated';\n queueAgentMail(normalizedId, message, 'queued', opts.dedupKey);\n logAgentLifecycleSync(normalizedId, `messageAgent queued mail without resume: ${gateBlockReason}`);\n console.log(`[agents] Queued message for ${normalizedId}; ${gateBlockReason}`);\n return { delivered: false, queuedToMail: true, reason: gateBlockReason };\n }\n console.log(`[agents] Auto-resuming suspended agent ${normalizedId} to deliver message`);\n if (opts.dedupKey !== undefined) {\n return resumeThenDeliverKeyed(normalizedId, message, caller, agentState, opts.dedupKey);\n }\n const { resumeAgent } = await import('../agents.js');\n const result = await resumeAgent(normalizedId, message);\n if (!result.success) {\n throw new Error(`Failed to auto-resume agent: ${result.error}`);\n }\n if (result.messageDelivered === false) {\n throw new Error(`Agent resumed but ready signal did not fire — message not delivered. Feedback is in the mail queue.`);\n }\n // Message already sent during resume\n await appendTellInterventionForUserSource(normalizedId, caller);\n return { delivered: true, queuedToMail: false };\n }\n\n // Check if agent is stopped — auto-resume to deliver feedback (PAN-367 / PAN-705)\n //\n // IMPORTANT: We delegate to resumeAgent() so we pick up the saved Claude session id\n // (`claude --resume <id>`) instead of fresh-launching with a new, empty session.\n // The previous implementation of this branch called `getAgentRuntimeBaseCommand(model)`\n // and passed an inline \"You are resuming work\" prompt as a positional argument,\n // which booted Claude Code in a fresh session (ctx 0%) with no memory of the\n // prior conversation, destroying agent continuity every time feedback arrived.\n //\n // We also restart when the tmux session still exists. Planning/work sessions use\n // `remain-on-exit on` so the shell persists after the agent process exits, and\n // sessionExists() returns true for that dead shell. resumeAgent() kills the zombie\n // session before re-creating it.\n if (agentState && agentState.status === 'stopped') {\n const stoppedGate = decideMessageGate();\n if (stoppedGate.decision !== 'proceed') {\n const gateBlockReason = stoppedGate.reason ?? 'agent is gated';\n queueAgentMail(normalizedId, message, 'queued', opts.dedupKey);\n logAgentLifecycleSync(normalizedId, `messageAgent queued mail without resume: ${gateBlockReason}`);\n console.log(`[agents] Queued message for ${normalizedId}; ${gateBlockReason}`);\n return { delivered: false, queuedToMail: true, reason: gateBlockReason };\n }\n console.log(`[agents] Auto-resuming stopped agent ${normalizedId} to deliver feedback (session exists: ${await Effect.runPromise(sessionExists(normalizedId))})`);\n\n if (opts.dedupKey !== undefined) {\n // Keyed deliveries resume bare and then enforce the key at the delivery\n // door — no kickoff-riding, no post-hoc key recording, and no mail\n // backup (a keyed mail file is a replay channel: a monitor started\n // later would drain and print it as a second copy).\n return resumeThenDeliverKeyed(normalizedId, message, caller, agentState, opts.dedupKey);\n }\n\n const { resumeAgent } = await import('../agents.js');\n const resumeResult = await resumeAgent(normalizedId, message);\n\n // Save to mail queue regardless so the agent can re-read feedback if needed\n queueAgentMail(normalizedId, message);\n\n if (resumeResult.success && resumeResult.messageDelivered !== false) {\n await appendTellInterventionForUserSource(normalizedId, caller);\n console.log(`[agents] Resumed ${normalizedId} and delivered feedback`);\n return { delivered: true, queuedToMail: true };\n }\n\n // Resume failed OR message was not delivered (ready signal timed out). Fall back to\n // a fresh launch so feedback is not silently dropped. This path intentionally mirrors\n // spawnAgent's launcher (provider exports + unset of leaked env vars) so the fallback\n // doesn't inherit stale ANTHROPIC_BASE_URL / OPENAI_API_KEY from the parent process.\n if (!resumeResult.success) {\n console.warn(`[agents] Resume failed for ${normalizedId}: ${resumeResult.error} — falling back to fresh launch`);\n } else {\n console.warn(`[agents] Resume succeeded for ${normalizedId} but message not delivered (ready signal timed out) — falling back to fresh launch`);\n }\n\n // PAN-1980: session rotation is disabled — do NOT fresh-launch a new session\n // as a fallback (that rotates the transcript and hides the resume failure).\n // Leave the agent stopped and surface it; the feedback was already queued in\n // the mail queue above, so it is not dropped.\n if (!ALLOW_SESSION_ROTATION_ON_RESUME) {\n const why = !resumeResult.success\n ? `resume failed (${resumeResult.error})`\n : 'resume succeeded but message delivery timed out';\n const stopMsg = `Not restarting ${normalizedId} with a fresh session — ${why}; session rotation is disabled (PAN-1980). Agent left stopped; feedback queued in mail.`;\n console.warn(`[agents] ${stopMsg}`);\n emitActivityEntrySync({ source: 'work-agent', level: 'error', message: `${normalizedId}: ${stopMsg}`, issueId: agentState.issueId });\n return { delivered: false, queuedToMail: true, reason: stopMsg };\n }\n\n const providerEnv = agentState.model ? await getProviderEnvForModel(agentState.model) : {};\n if (agentState.model) {\n const provider = getProviderForModelSync(agentState.model as ModelId);\n if (provider.authType === 'credential-file') {\n setupCredentialFileAuthSync(provider, agentState.workspace);\n } else {\n clearCredentialFileAuthSync(agentState.workspace);\n }\n }\n\n clearReadySignal(normalizedId);\n if (await Effect.runPromise(sessionExists(normalizedId))) {\n try { await Effect.runPromise(killSession(normalizedId)); } catch { /* ignore */ }\n }\n\n const providerExports = await getProviderExportsForModel(agentState.model || 'claude-sonnet-4-6');\n const fallbackLauncher = join(getAgentDir(normalizedId), 'launcher.sh');\n // PAN-1048 C4: resume must relaunch with the agent's actual role, not\n // hardcoded 'work'. A stopped review/test/ship run was previously\n // resurrected as a work agent because launcher generation ignored the\n // saved role. Use agentState.role and route through getRoleRuntimeBaseCommand\n // so the role-specific .claude/agents/* definition file is loaded.\n const resumeRole: Role = agentState.role ?? 'work';\n // PAN-1048 review feedback 006 (S1): Pi-backed resumes need the same\n // launcher fields the fresh-spawn path threads through generateLauncherScript.\n // buildPiCommand throws on missing piSessionDir, so the previous fallback\n // emitted a launcher that would crash on resume for any Pi role agent.\n const resumeModel = agentState.model || 'claude-sonnet-4-6';\n const fallbackHarness = agentState.harness ?? 'claude-code';\n const harnessLaunch = await prepareHarnessLaunch(fallbackHarness);\n const { assertWorkspaceStackHealthyForSpawn } = await import('../agents.js');\n await assertWorkspaceStackHealthyForSpawn(\n agentState.issueId || normalizedId.replace(/^agent-/, '').toUpperCase(),\n resumeRole,\n agentState.hostOverride === true,\n agentState.workspace,\n );\n const fallbackPiFields = fallbackHarness === 'ohmypi'\n ? await getOhmypiLauncherFields(normalizedId, resumeModel)\n : {};\n const fallbackCodexFields = fallbackHarness === 'codex'\n ? getCodexLauncherFields(normalizedId, resumeModel, agentState.workspace, resumeRole)\n : {};\n const fallbackSupervisorLaunch = await prepareSupervisorForRelaunch(normalizedId, agentState, resumeModel, fallbackHarness);\n const fallbackContent = generateLauncherScriptSync({\n role: resumeRole,\n workingDir: agentState.workspace,\n changeDir: false,\n setTerminalEnv: true,\n providerExports,\n extraEnvExports: [harnessLaunch.pathExport],\n baseCommand: await getRoleRuntimeBaseCommand(\n resumeModel,\n normalizedId,\n resumeRole,\n fallbackHarness,\n ),\n appendSystemPromptFiles: await claudeSystemPromptFiles(agentState.workspace, fallbackHarness),\n managedStateKey: normalizedId,\n overdeckEnv: {\n agentId: normalizedId,\n issueId: agentState.issueId,\n sessionType: resumeRole,\n },\n useSupervisor: fallbackSupervisorLaunch.useSupervisor,\n supervisorScriptPath: fallbackSupervisorLaunch.supervisorScriptPath,\n ...fallbackPiFields,\n ...fallbackCodexFields,\n });\n writeFileSync(fallbackLauncher, fallbackContent, { mode: 0o755 });\n await Effect.runPromise(createSession(normalizedId, agentState.workspace, `bash ${fallbackLauncher}`, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n OVERDECK_AGENT_ID: normalizedId,\n OVERDECK_ISSUE_ID: agentState.issueId || '',\n OVERDECK_SESSION_TYPE: agentState.role,\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false',\n ...providerEnv\n }\n }));\n\n markAgentRunning(agentState);\n saveAgentStateSync(agentState);\n\n const ready = await waitForPromptReady(normalizedId, fallbackHarness, 30);\n const fallbackResumePrompt = `You are resuming work on ${agentState.issueId}. Check .pan/feedback/ for specialist feedback that arrived while you were stopped, then continue working.\\n\\n${message}`;\n const resumeMessage = await buildResumeMessageForAgent(agentState, fallbackResumePrompt, message);\n let delivered = false;\n let reason: string | undefined;\n if (resumeMessage.error) {\n reason = resumeMessage.error;\n console.error(`[agents] Fallback-restarted ${normalizedId} but ${resumeMessage.error}`);\n emitActivityEntrySync({\n source: 'work-agent',\n level: 'error',\n message: `${normalizedId}: ${resumeMessage.error}`,\n issueId: agentState.issueId,\n });\n } else if (ready && resumeMessage.message) {\n if (fallbackHarness === 'claude-code') {\n const fallbackSessionId = getLatestSessionIdSync(normalizedId);\n if (fallbackSessionId) {\n const delivery = await deliverResumeMessageWithTranscriptConfirmation({\n agentId: normalizedId,\n workspace: agentState.workspace,\n sessionId: fallbackSessionId,\n message: resumeMessage.message,\n caller: 'resumeAgent:resume-prompt',\n deliveryMethod: resolveAgentDeliveryMethod(agentState),\n });\n delivered = delivery.delivered;\n if (!delivery.delivered) {\n reason = `resume prompt did not land after ${delivery.attempts} delivery attempts`;\n console.error(`[agents] Fallback resume prompt did not land after ${delivery.attempts} delivery attempts`);\n }\n } else {\n reason = 'no session id was recorded';\n console.error(`[agents] Fallback-restarted ${normalizedId} but no session id was recorded — feedback in mail queue`);\n }\n } else {\n const delivery = await deliverWithOptionalKey(normalizedId, resumeMessage.message, 'resumeAgent:resume-prompt', resolveAgentDeliveryMethod(agentState), opts.dedupKey);\n delivered = delivery.ok;\n if (!delivery.ok) reason = 'resume prompt delivery failed';\n }\n if (delivered) {\n if (resumeMessage.redeliveringKickoff) markKickoffRedelivered(agentState);\n await appendTellInterventionForUserSource(normalizedId, caller);\n console.log(`[agents] Fallback-restarted ${normalizedId} and delivered feedback`);\n }\n } else {\n reason = 'ready signal not detected';\n console.warn(`[agents] Fallback-restarted ${normalizedId} but ready signal not detected — feedback in mail queue`);\n }\n\n return { delivered, queuedToMail: true, ...(reason ? { reason } : {}) };\n }\n\n // Check if this is a remote agent\n const { loadRemoteAgentState, sendToRemoteAgent } = await import('../remote/remote-agents.js');\n const remoteState = loadRemoteAgentState(normalizedId);\n if (remoteState && remoteState.vmName) {\n console.log(`[agents] Sending message to remote agent ${normalizedId} on ${remoteState.vmName}`);\n if (opts.dedupKey !== undefined) {\n // Keyed: the REMOTE tmux server is the crash-independent component and\n // enforces the key across the complete paste-settle-submit transaction\n // via the same two-phase marker protocol as the local tmux tier — a\n // dashboard crash mid-send replays into a dedup, never a second wake.\n const { sendToRemoteAgentKeyed } = await import('../remote/remote-agents.js');\n const remoteOutcome = await sendToRemoteAgentKeyed(normalizedId, remoteState.vmName, message, opts.dedupKey);\n // Durable backup (idempotent keyed filename); the remote agent never\n // drains the local mail dir, so this cannot replay.\n queueAgentMail(normalizedId, message, 'delivered', opts.dedupKey);\n await appendTellInterventionForUserSource(normalizedId, caller);\n return {\n delivered: true,\n queuedToMail: true,\n ...(remoteOutcome === 'deduplicated'\n ? { reason: 'deduplicated', deduplicated: true }\n : {}),\n };\n }\n await sendToRemoteAgent(normalizedId, remoteState.vmName, message);\n\n // Also save a durable backup of the delivered message.\n queueAgentMail(normalizedId, message, 'delivered');\n await appendTellInterventionForUserSource(normalizedId, caller);\n return { delivered: true, queuedToMail: true };\n }\n\n const expectedHarness = agentState?.harness ?? 'claude-code';\n\n // PAN-3015 monitor tier: when the agent's Claude Code session runs a live\n // `pan monitor` background task, the durable mail file IS the delivery — the\n // monitor prints it to stdout and the harness surfaces it to the model,\n // waking an idle session. No keystroke transport runs, which sidesteps the\n // whole echo-confirm/paste/Enter failure class (PAN-1769, PAN-2228,\n // PAN-1988). Mid-session tells only: kickoff/resume never reach this path\n // (no monitor exists before the session's first turn), and a stale\n // heartbeat or dead pid falls through to the normal cascade.\n //\n // KEYED deliveries never take this tier (PAN-2997 review cycle 7): the\n // monitor claims a mail file by renaming it before emitting, so a monitor\n // exit between claim and emit loses the wake, and a dashboard crash after\n // the emit but before the outbox ack replays it — the mail spool cannot\n // enforce the key across the complete model-visible side effect. Keyed\n // messages fall through to the supervisor/tmux door, which can.\n if (expectedHarness === 'claude-code' && opts.dedupKey === undefined && isMonitorLive(normalizedId)) {\n queueAgentMail(normalizedId, message, 'queued', opts.dedupKey, caller);\n logAgentLifecycleSync(normalizedId, `messageAgent delivered via monitor mail (caller: ${caller})`);\n console.log(`[agents] Delivered message to ${normalizedId} via monitor inbox`);\n await appendTellInterventionForUserSource(normalizedId, caller);\n return { delivered: true, queuedToMail: true, reason: 'monitor' };\n }\n\n let appServerState: string | undefined;\n try {\n appServerState = (await getCodexAppServerStatus(normalizedId)).state;\n } catch {\n // A missing/unresponsive app-server means this target uses another\n // transport. Continue through the legacy tmux liveness path.\n }\n if (appServerState && appServerState !== 'closed' && appServerState !== 'error') {\n const promptReady = claimCodexIdleTurn(normalizedId);\n if (!promptReady) {\n // PAN-3736: a busy agent is a WORKING agent, not a dead one. Say that in\n // one phrase everywhere this outcome surfaces, and name the mail file so\n // a human or peer can read or hand-deliver it.\n //\n // PAN-3738: the file is `pending` mail whether or not the send is keyed.\n // The codex notify hook drains `*.pending.md` only, so a keyed message\n // named `dedup-<hash>.md` used to strand here forever — the keyed name\n // stays deterministic, it just carries the drainable suffix now.\n const mailPath = queueAgentMail(normalizedId, message, 'pending', opts.dedupKey);\n const busyReason = busyAgentQueuedReason(mailPath);\n logAgentLifecycleSync(normalizedId, `messageAgent: ${busyReason}`);\n console.log(`[agents] ${normalizedId}: ${busyReason}`);\n await appendTellInterventionForUserSource(normalizedId, caller);\n return { delivered: true, queuedToMail: true, reason: busyReason };\n }\n\n const deliveryMethod = resolveAgentDeliveryMethod(agentState);\n const delivery = await deliverWithOptionalKey(normalizedId, message, `messageAgent:${caller}`, deliveryMethod, opts.dedupKey);\n queueAgentMail(normalizedId, message, 'delivered', opts.dedupKey);\n await appendTellInterventionForUserSource(normalizedId, caller);\n return {\n delivered: delivery.ok,\n queuedToMail: true,\n ...(delivery.deduplicated ? { deduplicated: true } : {}),\n };\n }\n\n if (!(await Effect.runPromise(sessionExists(normalizedId)))) {\n throw new Error(`Agent ${normalizedId} not running`);\n }\n\n // Guard: if tmux session exists but Claude Code has exited, resume instead\n // of typing the message into a bare bash shell.\n //\n // Launchers differ: specialists `exec claude` so pane_pid IS claude, but\n // work-agent launchers run `bash launcher.sh` so pane_pid is bash and claude\n // runs as a descendant. Walk the pane's process subtree and treat the pane\n // as live if any descendant is the expected runtime for the saved harness.\n const panePids = await Effect.runPromise(listPaneValues(normalizedId, '#{pane_pid}'));\n if (panePids.length > 0 && !(await hasAgentRuntimeInSubtree(panePids[0], expectedHarness))) {\n console.warn(`[agents] ${normalizedId} tmux session is a zombie (no ${expectedHarness} runtime) — attempting resume`);\n if (opts.dedupKey !== undefined) {\n return resumeThenDeliverKeyed(normalizedId, message, caller, agentState, opts.dedupKey);\n }\n const { resumeAgent } = await import('../agents.js');\n const resumeResult = await resumeAgent(normalizedId, message);\n if (resumeResult.success) {\n const delivered = resumeResult.messageDelivered !== false;\n if (delivered) {\n await appendTellInterventionForUserSource(normalizedId, caller);\n }\n return { delivered, queuedToMail: false };\n }\n throw new Error(`Agent ${normalizedId} session is dead and resume failed: ${resumeResult.error}`);\n }\n\n // Codex's notify hook writes turn-completed at every idle boundary. Claiming\n // that marker makes the idle signal one-shot: the next message starts a turn,\n // and further messages queue until the hook reports the next completion.\n // Claude Code continues to use its hook-driven runtime mirror (PAN-1594).\n const promptReady = await waitForAgentIdle(normalizedId, 5000);\n if (!promptReady) {\n console.warn(`[agents] ${normalizedId} not at idle prompt after 5s — sending message anyway`);\n }\n\n const deliveryMethod = resolveAgentDeliveryMethod(agentState);\n const deliveryCaller = `messageAgent:${caller}`;\n const transcriptSessionId = getHarnessBehavior(expectedHarness).transcriptKind === 'claude-jsonl'\n ? agentState?.sessionId ?? getLatestSessionIdSync(normalizedId)\n : undefined;\n let transcriptWatch: { sessionId: string; fromByteOffset: number } | undefined;\n if (agentState?.workspace && transcriptSessionId) {\n const snapshot = await captureTranscriptUserRecordSnapshot(agentState.workspace, transcriptSessionId);\n transcriptWatch = {\n sessionId: transcriptSessionId,\n fromByteOffset: snapshot.readOffset ?? snapshot.fileSize ?? 0,\n };\n }\n\n const delivery = await deliverWithOptionalKey(\n normalizedId,\n message,\n deliveryCaller,\n deliveryMethod,\n opts.dedupKey,\n );\n\n // Save a durable backup. Unlike `.pending.md` busy-turn mail, the Codex hook\n // does not replay `.delivered.md` backups because they have already landed —\n // and the suffix says so to anyone reading `mail/` (PAN-3738).\n // Keyed deliveries skip the backup: the caller's outbox is the durable\n // receipt, and a keyed mail file is a replay channel — a monitor started\n // later would drain and print it as a second visible copy (cycle 7).\n if (opts.dedupKey === undefined) {\n queueAgentMail(normalizedId, message, 'delivered');\n }\n await appendTellInterventionForUserSource(normalizedId, caller);\n\n if (delivery.ok && transcriptWatch && agentState?.workspace) {\n void watchForEatenAgentMessage({\n agentId: normalizedId,\n workspace: agentState.workspace,\n sessionId: transcriptWatch.sessionId,\n message,\n caller: deliveryCaller,\n deliveryMethod,\n fromByteOffset: transcriptWatch.fromByteOffset,\n }).then((outcome) => {\n if (outcome === 'redelivered') {\n console.log(`[agents] ${normalizedId}: redelivered message eaten by submit-time compaction`);\n }\n }).catch((error: unknown) => {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[agents] eaten-message watcher failed for ${normalizedId}: ${errorMessage}`);\n });\n }\n\n return {\n delivered: delivery.ok,\n queuedToMail: opts.dedupKey === undefined,\n ...(delivery.deduplicated ? { deduplicated: true } : {}),\n };\n}\n\nexport async function messageAgentWithOutcome(\n agentId: string,\n message: string,\n caller = 'internal',\n opts: MessageAgentRedriveOptions = {},\n): Promise<MessageAgentOutcome> {\n const outcome = await messageAgent(agentId, message, caller, opts);\n return outcome.delivered ? 'delivered' : 'queued';\n}\n","/**\n * Context-window overflow is NOT a transient error. It surfaces as e.g.\n * \"API Error: 400 Your input exceeds the context window of this model.\" and\n * nudging \"continue\" only re-sends the same oversized context for the same 400.\n */\nexport const CONTEXT_OVERFLOW_PATTERNS = [\n 'input exceeds the context window',\n 'exceeds the context window of this model',\n 'exceeded model token limit',\n];\n\nexport const CONTEXT_OVERFLOW_TAIL_LINES = 40;\n\n/**\n * Recognize context-window overflow from any thrown error, including the\n * claude -p non-zero exit envelope that carries \"result\":\"Prompt is too long\"\n * and \"terminal_reason\":\"blocking_limit\".\n */\nexport function isContextOverflowError(err: unknown): boolean {\n const message = err instanceof Error ? err.message : String(err);\n const normalized = message.toLowerCase();\n const patterns = [\n ...CONTEXT_OVERFLOW_PATTERNS,\n 'prompt is too long',\n 'blocking_limit',\n ];\n return patterns.some(pattern => normalized.includes(pattern.toLowerCase()));\n}\n\nexport function isContextOverflowTail(output: string): boolean {\n const recentTail = output.split('\\n').slice(-CONTEXT_OVERFLOW_TAIL_LINES).join('\\n').toLowerCase();\n const patterns = [...CONTEXT_OVERFLOW_PATTERNS, 'prompt is too long'];\n return patterns.some(pattern => recentTail.includes(pattern.toLowerCase()));\n}\n\n/**\n * PAN-1781: Opening prompt for a fresh session that replaces a context-wedged\n * one. The old session is summarized out-of-band and the summary is embedded\n * here — the fresh session never resumes the old JSONL, so there is no stale\n * leaf for the harness to rewind to (the failure mode that made in-place\n * boundary injection a silent no-op ~half the time).\n *\n * When summarization fails entirely, `summary` is null and the seed degrades\n * to durable-artifact reconstruction only — the same end state the old\n * /clear + reseed tier produced, minus the keystroke fragility.\n */\nexport function buildCompactRecoverySeedMessage(issueId: string, summary: string | null): string {\n const lines = [\n `Your previous session for ${issueId} hit the model's context-window limit, so you are starting a fresh session. This freed tokens; it did not reset your filesystem work.`,\n 'Do NOT start over and do NOT expect the prior conversation to be available.',\n ];\n if (summary) {\n lines.push('', 'Summary of the archived session:', '', summary, '');\n } else {\n lines.push('');\n }\n lines.push(\n 'Reconstruct your exact work-in-progress from durable artifacts:',\n '1. Read .pan/continue.json for resumePoint, decisions, hazards, feedback, and sessionHistory.',\n '2. Run `pan task next` for the next open item and `pan task show <issue> <item>` for its current state.',\n '3. Inspect `git status` and `git diff` for uncommitted work already on disk.',\n 'Then continue from that reconstructed state and complete the next required bead — do not wait for further instructions.',\n );\n return lines.join('\\n');\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { emitAgentEvent } from '../agent-runtime.js';\nimport { SESSION_RESET_MARKER } from '../session-history.js';\nimport { getAgentDir, getAgentStateSync, saveAgentStateSync } from './agent-state.js';\n\nexport interface ClearedAgentSessionPointers {\n cleared: string[];\n}\n\nexport function isAgentSessionReset(agentId: string): boolean {\n return existsSync(join(getAgentDir(agentId), SESSION_RESET_MARKER));\n}\n\n/**\n * Clear every mutable pointer that can make a later launch resume an old\n * session. Claude JSONL transcripts live outside the agent directory and are\n * deliberately never touched.\n */\nexport async function clearAgentSessionPointers(\n agentId: string,\n): Promise<ClearedAgentSessionPointers> {\n const agentDir = getAgentDir(agentId);\n const cleared: string[] = [];\n\n for (const name of ['session.id', 'sessions.json', 'codex-thread-id', 'launcher.sh']) {\n const path = join(agentDir, name);\n if (!existsSync(path)) continue;\n unlinkSync(path);\n cleared.push(name);\n }\n\n // The agents plane retains session history for crash recovery. Keep an\n // explicit local intent marker so that recovery cannot recreate the pointer\n // the operator just cleared from that history or from a retained JSONL.\n mkdirSync(agentDir, { recursive: true });\n writeFileSync(join(agentDir, SESSION_RESET_MARKER), '');\n cleared.push(SESSION_RESET_MARKER);\n\n const runtimeFile = join(agentDir, 'runtime.json');\n if (existsSync(runtimeFile)) {\n try {\n const runtime = JSON.parse(readFileSync(runtimeFile, 'utf-8')) as Record<string, unknown>;\n if (Object.prototype.hasOwnProperty.call(runtime, 'claudeSessionId')) {\n delete runtime.claudeSessionId;\n writeFileSync(runtimeFile, JSON.stringify(runtime, null, 2));\n cleared.push('runtime.json:claudeSessionId');\n }\n } catch {\n // A malformed runtime cache must not prevent clearing the durable pointers.\n }\n }\n\n const state = getAgentStateSync(agentId);\n if (state?.sessionId) {\n delete state.sessionId;\n saveAgentStateSync(state);\n cleared.push('agents.session_id');\n }\n\n // Clear the live runtime projection too. In the dashboard process this\n // updates the in-memory read model; from the CLI it goes through the runtime\n // heartbeat endpoint. Either way a stale cached id cannot repopulate resume.\n await Effect.runPromise(emitAgentEvent(agentId, {\n kind: 'model_set',\n model: state?.model ?? 'unknown',\n claudeSessionId: null,\n }));\n cleared.push('runtime.claudeSessionId');\n\n return { cleared };\n}\n","import type { RuntimeName } from '../runtimes/types.js';\nimport { sessionRotationRefused } from '../session-rotation.js';\n\nexport interface ResumeSpawnPlanInput {\n harness: RuntimeName;\n compactSeed: boolean;\n driftReasons: readonly string[];\n piProcessWasAlive: boolean;\n transcriptExists: boolean;\n allowExplicitRecovery: boolean;\n}\n\nexport interface ResumeSpawnPlan {\n mode: 'resume-saved' | 'fresh';\n freshReason?: 'compact' | 'drift' | 'pi-dead-recovery' | 'claude-jsonl-missing';\n clearSessionPointers: boolean;\n rotationRefused: boolean;\n}\n\nexport function decideResumeSpawnPlan(input: ResumeSpawnPlanInput): ResumeSpawnPlan {\n const rotationRefused = sessionRotationRefused({\n compactSeed: input.compactSeed,\n driftReasons: input.driftReasons,\n allowExplicitRecovery: input.allowExplicitRecovery,\n });\n\n if (input.compactSeed) {\n return {\n mode: 'fresh',\n freshReason: 'compact',\n clearSessionPointers: false,\n rotationRefused,\n };\n }\n\n if (input.driftReasons.length > 0) {\n return {\n mode: 'fresh',\n freshReason: 'drift',\n clearSessionPointers: false,\n rotationRefused,\n };\n }\n\n if (input.harness === 'ohmypi' && !input.piProcessWasAlive) {\n return {\n mode: 'fresh',\n freshReason: 'pi-dead-recovery',\n clearSessionPointers: false,\n rotationRefused: false,\n };\n }\n\n if (input.harness === 'claude-code' && !input.transcriptExists) {\n return {\n mode: 'fresh',\n freshReason: 'claude-jsonl-missing',\n clearSessionPointers: true,\n rotationRefused: false,\n };\n }\n\n return {\n mode: 'resume-saved',\n clearSessionPointers: false,\n rotationRefused: false,\n };\n}\n","import { Effect } from 'effect'\n\nimport {\n buildChoiceKeystrokes,\n paneChoiceMenuSignature,\n parsePaneChoiceMenu,\n type PaneChoiceConfidence,\n} from './pane-choice-menu.js'\nimport { capturePaneText, sendRawKeystroke, sessionExists } from './tmux.js'\n\nconst PANE_CAPTURE_LINES = 90\nconst KEYSTROKE_GAP_MS = 60\nconst DELIVERY_CONFIRM_WAIT_MS = 700\n\nexport interface PendingPaneChoiceOption {\n number: number\n label: string\n recommended: boolean\n}\n\nexport interface PendingPaneChoice {\n /** Stable identity of the parsed menu — answers must match what is on screen. */\n signature: string\n title: string\n contextLines: string[]\n options: PendingPaneChoiceOption[]\n selectedIndex: number\n footerHint: string | null\n confidence: PaneChoiceConfidence\n}\n\nexport interface SessionPaneChoiceDeps {\n capture?: (sessionName: string, lines: number) => Promise<string>\n sendKey?: (sessionName: string, key: string) => Promise<void>\n sleep?: (ms: number) => Promise<void>\n sessionExists?: (sessionName: string) => Promise<boolean>\n}\n\nexport interface SessionPaneChoiceAnswer {\n selectedIndex: number\n signature: string\n}\n\nexport interface SessionPaneChoiceResult {\n body: Record<string, unknown>\n status?: number\n}\n\nexport async function captureSessionPaneChoice(\n sessionName: string,\n capture: (sessionName: string, lines: number) => Promise<string> = capturePaneText,\n): Promise<PendingPaneChoice | null> {\n let pane: string\n try {\n pane = await capture(sessionName, PANE_CAPTURE_LINES)\n } catch {\n return null\n }\n const menu = parsePaneChoiceMenu(pane)\n if (!menu) return null\n return {\n signature: paneChoiceMenuSignature(menu),\n title: menu.title,\n contextLines: menu.contextLines,\n options: menu.options.map((option) => ({\n number: option.number,\n label: option.label,\n recommended: option.recommended,\n })),\n selectedIndex: menu.selectedIndex,\n footerHint: menu.footerHint,\n confidence: menu.confidence,\n }\n}\n\nexport async function answerSessionPaneChoice(\n sessionName: string,\n body: Record<string, unknown> | SessionPaneChoiceAnswer,\n deps: SessionPaneChoiceDeps = {},\n): Promise<SessionPaneChoiceResult> {\n try {\n const selectedIndex = Number(body.selectedIndex)\n const signature = typeof body.signature === 'string' ? body.signature : ''\n if (!Number.isInteger(selectedIndex) || selectedIndex < 0 || selectedIndex > 8) {\n return { body: { error: 'selectedIndex must be an integer 0-8' }, status: 400 }\n }\n if (!signature) {\n return { body: { error: 'signature is required' }, status: 400 }\n }\n\n const isSessionRunning = deps.sessionExists\n ?? ((name: string) => Effect.runPromise(sessionExists(name)))\n if (!(await isSessionRunning(sessionName))) {\n return { body: { error: 'Conversation session is not running' }, status: 409 }\n }\n\n const capture = deps.capture ?? capturePaneText\n const sendKey = deps.sendKey\n ?? ((name: string, key: string) => Effect.runPromise(sendRawKeystroke(name, key, 'pane-choice')))\n const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)))\n\n const pane = await capture(sessionName, PANE_CAPTURE_LINES)\n const menu = parsePaneChoiceMenu(pane)\n if (!menu) {\n return { body: { error: 'The choice menu is no longer on screen', code: 'menu-gone' }, status: 409 }\n }\n if (paneChoiceMenuSignature(menu) !== signature) {\n return { body: { error: 'The choice menu changed since the card was rendered — refresh and re-answer', code: 'menu-changed' }, status: 409 }\n }\n if (selectedIndex >= menu.options.length) {\n return { body: { error: `selectedIndex out of range (0-${menu.options.length - 1})` }, status: 400 }\n }\n\n const keys = buildChoiceKeystrokes(menu, selectedIndex)\n for (const key of keys) {\n await sendKey(sessionName, key)\n await sleep(KEYSTROKE_GAP_MS)\n }\n\n await sleep(DELIVERY_CONFIRM_WAIT_MS)\n const after = parsePaneChoiceMenu(await capture(sessionName, PANE_CAPTURE_LINES))\n if (after && paneChoiceMenuSignature(after) === signature) {\n return { body: { error: 'Keystrokes were sent but the menu is still on screen — answer it from the terminal', code: 'delivery-unconfirmed' }, status: 409 }\n }\n return { body: { ok: true, answeredLabel: menu.options[selectedIndex]!.label } }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n console.error('[sessions] pane-choice answer failed:', message)\n return { body: { error: 'Internal server error' }, status: 500 }\n }\n}\n","import type { Role } from './role.js';\nimport {\n answerSessionPaneChoice,\n captureSessionPaneChoice,\n type SessionPaneChoiceDeps,\n} from '../session-pane-choice.js';\n\nconst AUTONOMOUS_RESUME_ROLES = new Set<Role>(['work', 'review', 'test', 'strike']);\nconst RESUME_SUMMARY_LABEL = 'Resume from summary';\nconst RESUME_GATE_LABELS = [\n RESUME_SUMMARY_LABEL,\n 'Resume full session as-is',\n \"Don't ask me again\",\n];\n\nexport type AgentResumePanePreparation =\n | { ready: true; action: 'clear' | 'resumed-from-summary' }\n | { ready: false; reason: string };\n\n/**\n * Cross only Claude Code's known resume-summary gate before autonomous agent\n * continuation delivery. Every other pane choice remains untouched.\n */\nexport async function prepareAutonomousAgentResumePane(\n agentId: string,\n role: Role,\n deps: SessionPaneChoiceDeps = {},\n): Promise<AgentResumePanePreparation> {\n const choice = deps.capture\n ? await captureSessionPaneChoice(agentId, deps.capture)\n : await captureSessionPaneChoice(agentId);\n if (!choice) return { ready: true, action: 'clear' };\n\n if (!AUTONOMOUS_RESUME_ROLES.has(role)) {\n return { ready: false, reason: `pane is blocked on a choice menu that role=${role} may not answer automatically` };\n }\n\n const labels = choice.options.map((option) => option.label);\n const summaryIndex = choice.options.findIndex((option) => (\n option.label === RESUME_SUMMARY_LABEL && option.recommended\n ));\n const isResumeSummaryGate = choice.options.length === RESUME_GATE_LABELS.length\n && RESUME_GATE_LABELS.every((label) => labels.includes(label))\n && summaryIndex >= 0;\n if (!isResumeSummaryGate) {\n return { ready: false, reason: 'pane is blocked on a choice menu other than the Claude resume-summary gate' };\n }\n\n const result = await answerSessionPaneChoice(agentId, {\n selectedIndex: summaryIndex,\n signature: choice.signature,\n }, deps);\n if (result.body.ok === true) {\n return { ready: true, action: 'resumed-from-summary' };\n }\n\n const error = typeof result.body.error === 'string' ? result.body.error : 'unknown pane-choice failure';\n return { ready: false, reason: `could not select Resume from summary: ${error}` };\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\nconst reviewLifecycleTails = new Map<string, Promise<void>>();\nconst heldReviewLifecycles = new AsyncLocalStorage<ReadonlySet<string>>();\n\n/** Return the issue owned by a review parent or convoy member agent id. */\nexport function reviewIssueIdForAgent(agentId: string): string | null {\n const match = /^agent-([a-z]+-\\d+)-review(?:-|$)/i.exec(agentId);\n return match?.[1]?.toUpperCase() ?? null;\n}\n\n/**\n * Serialize review-family lifecycle mutations for one issue.\n *\n * Dispatch owns this guard across stop, state wipe, and replacement spawn.\n * Direct review-family resumes enter through the same guard. Calls made by a\n * guarded dispatch are re-entrant because dispatch legitimately warm-resumes\n * the parent and convoy members while it owns the replacement sequence.\n */\nexport async function withReviewLifecycleGuard<T>(\n issueId: string,\n operation: () => T | Promise<T>,\n): Promise<T> {\n const key = issueId.toUpperCase();\n const held = heldReviewLifecycles.getStore();\n if (held?.has(key)) return operation();\n\n const previous = reviewLifecycleTails.get(key) ?? Promise.resolve();\n let release: () => void = () => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.catch(() => undefined).then(() => current);\n reviewLifecycleTails.set(key, tail);\n\n await previous.catch(() => undefined);\n try {\n const nextHeld = new Set(held);\n nextHeld.add(key);\n return await heldReviewLifecycles.run(nextHeld, operation);\n } finally {\n release();\n if (reviewLifecycleTails.get(key) === tail) reviewLifecycleTails.delete(key);\n }\n}\n\n/** Run review-family agents under their issue guard; leave other agents alone. */\nexport async function withReviewLifecycleGuardForAgent<T>(\n agentId: string,\n operation: () => T | Promise<T>,\n): Promise<T> {\n const issueId = reviewIssueIdForAgent(agentId);\n return issueId ? withReviewLifecycleGuard(issueId, operation) : operation();\n}\n","import { existsSync, unlinkSync } from 'fs';\nimport { randomUUID } from 'crypto';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport { emitActivityEntrySync } from '../activity-logger.js';\nimport { BLANKED_PROVIDER_ENV } from '../child-env.js';\nimport { buildCompactRecoverySeedMessage } from '../context-overflow.js';\nimport { resolveHarness } from '../harness-resolve.js';\nimport { prepareHarnessLaunch } from '../harness-binary.js';\nimport { normalizeModelOverrideSync, requireModelOverrideSync } from '../model-validation.js';\nimport { getOverdeckDatabaseSync } from '../overdeck/infra.js';\nimport { claudeSessionTranscriptExists, sessionFilePath } from '../paths.js';\nimport { logAgentLifecycleSync } from '../persistent-logger.js';\nimport { resolveProjectFromIssueSync } from '../projects.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { appendContinueSessionEntryForIssue } from '../xbrief/lifecycle-io.js';\nimport { createSession, isPaneDead, killSession, listPaneValues, sessionExists } from '../tmux.js';\nimport {\n decideResumeGate,\n getAgentDir,\n getAgentResumeGateBlockReason,\n getAgentStateSync,\n markAgentRunning,\n saveAgentStateSync,\n} from './agent-state.js';\nimport { resolveLatestSessionIdSync, saveSessionId } from './activity.js';\nimport { clearAgentSessionPointers } from './session-pointers.js';\nimport {\n deliverInitialPromptWithRetry,\n deliverResumeMessageWithTranscriptConfirmation,\n resilientDeliveryMethod,\n} from './delivery.js';\nimport { clearReadySignal, normalizeAgentId, waitForReadySignal } from './identity.js';\nimport {\n getAgentRuntimeStateSync,\n saveAgentRuntimeState,\n sessionResumeDriftReasons,\n} from './runtime-state.js';\nimport { decideResumeSpawnPlan } from './resume-spawn-plan.js';\nimport { prepareAutonomousAgentResumePane } from './resume-pane-choice.js';\nimport {\n hasAgentRuntimeInSubtree,\n writeLauncherScriptAtomic,\n writeOhmypiAgentPrompt,\n} from './runtime-command.js';\nimport {\n buildResumeContinueMessage,\n buildResumeMessageForAgent,\n markKickoffRedelivered,\n prepareSupervisorForRelaunch,\n} from './supervisor-channels.js';\nimport {\n assertWorkspaceStackHealthyForSpawn,\n buildAgentLaunchConfig,\n} from './spawn-prep.js';\nimport { buildResumeContract, type ResumeCause } from '../resume-contract.js';\nimport { withReviewLifecycleGuardForAgent } from '../review-lifecycle-guard.js';\n\n/**\n * Resume a suspended agent (PAN-80)\n *\n * Reads saved session ID and creates new tmux session with --resume flag.\n * Optionally sends a message after resuming.\n *\n * Auto-resume triggers:\n * - Specialists: When queued work arrives\n * - Work agents: When message is sent via /work-tell\n */\n/**\n * PAN-1781: Build the opening prompt for a compact-recovery respawn — an\n * out-of-band summary of the wedged session plus durable-artifact reseed\n * instructions. Replaces PAN-1675's in-place JSONL compaction\n * (compactAgentSession), which appended a compact_boundary the harness's\n * resume leaf selection bypassed ~half the time, silently rebuilding the full\n * pre-compact context. The old JSONL is read-only here — never mutated.\n *\n * Never throws and never returns an unusable seed: smart summary → fallback\n * heuristic summary → reseed-instructions-only, in that order. A missing\n * sessionId or workspace skips straight to the reseed-only seed.\n */\nexport async function buildCompactRecoverySeed(agentId: string): Promise<{ seed: string; summarized: boolean }> {\n const normalizedId = normalizeAgentId(agentId);\n const agentState = getAgentStateSync(normalizedId);\n const sessionId = resolveLatestSessionIdSync(normalizedId).sessionId;\n const issueId = agentState?.issueId || normalizedId.replace(/^agent-/, '').toUpperCase();\n\n let summary: string | null = null;\n if (agentState?.workspace && sessionId) {\n const sessionFile = sessionFilePath(agentState.workspace, sessionId);\n try {\n // Dynamic imports: keep conversation-compaction out of agents.ts's\n // top-level import graph (it pulls in dashboard server services).\n const [{ getConversationCompactionSettings }, { generateSmartSummary }] = await Promise.all([\n import('../../dashboard/server/services/conversation-compaction.js'),\n import('../conversations/smart-compaction.js'),\n ]);\n const settings = getConversationCompactionSettings();\n const result = await Effect.runPromise(generateSmartSummary({\n jsonlPath: sessionFile,\n model: settings.model,\n richMode: settings.richCompaction,\n mode: 'fork',\n }));\n summary = result.summary;\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n logAgentLifecycleSync(normalizedId, `compact-recovery smart summary failed (${error}); trying heuristic fallback`);\n try {\n const { generateFallbackSummary } = await import('../conversations/summary-fork.js');\n summary = await Effect.runPromise(generateFallbackSummary(sessionFile));\n } catch (fallbackErr) {\n const fallbackError = fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr);\n logAgentLifecycleSync(normalizedId, `compact-recovery fallback summary failed (${fallbackError}); seeding with reseed instructions only`);\n }\n }\n }\n\n return {\n seed: buildCompactRecoverySeedMessage(issueId, summary),\n summarized: summary !== null,\n };\n}\n\ntype ResumeAgentOptions = { model?: string; harness?: RuntimeName; allowHost?: boolean; compact?: boolean; recoverGated?: boolean; startedBy?: string; resumeCause?: ResumeCause };\ntype ResumeAgentResult = { success: boolean; messageDelivered?: boolean; error?: string };\n\nexport async function resumeAgent(agentId: string, message?: string, opts?: ResumeAgentOptions): Promise<ResumeAgentResult> {\n const normalizedId = normalizeAgentId(agentId);\n return withReviewLifecycleGuardForAgent(\n normalizedId,\n () => resumeAgentWithinLifecycle(normalizedId, message, opts),\n );\n}\n\nasync function resumeAgentWithinLifecycle(normalizedId: string, message?: string, opts?: ResumeAgentOptions): Promise<ResumeAgentResult> {\n const requestedModel = normalizeModelOverrideSync(opts?.model);\n logAgentLifecycleSync(normalizedId, `resumeAgent called (message=${message ? 'yes' : 'no'}, harness=${opts?.harness || 'unchanged'})`);\n\n // Check runtime state — allow both suspended (auto-suspend) and stopped/idle (manual stop, crash)\n const runtimeState = getAgentRuntimeStateSync(normalizedId);\n const agentState = getAgentStateSync(normalizedId);\n const gateBlock = agentState ? getAgentResumeGateBlockReason(agentState) : undefined;\n const gateDecision = decideResumeGate(gateBlock, 'operator-start');\n const bypassTroubledGate = opts?.compact === true && opts.recoverGated === true && agentState?.troubled === true && agentState.paused !== true;\n if (gateDecision.decision === 'block' && !bypassTroubledGate) {\n const reason = `Cannot resume ${normalizedId}: ${gateDecision.reason}. Clear the gate before resuming.`;\n logAgentLifecycleSync(normalizedId, `resumeAgent BLOCKED: ${reason}`);\n return { success: false, error: reason };\n }\n if (gateDecision.decision === 'proceed' && gateDecision.warning) {\n logAgentLifecycleSync(normalizedId, `resumeAgent: ${gateDecision.warning}`);\n }\n if (bypassTroubledGate) {\n logAgentLifecycleSync(normalizedId, `resumeAgent: bypassing troubled gate for explicit compact recovery (${gateBlock?.reason})`);\n }\n const hasWorkspace = !!agentState?.workspace && existsSync(agentState.workspace);\n // A `pending-` model (e.g. 'pending-work-spawn') is a mid-spawn placeholder\n // written before real model resolution; such an agent never produced a\n // resumable session, so it is a placeholder in ANY status — not only\n // 'starting'. Detecting it by status alone (the old gate) let a placeholder\n // that had transitioned to 'stopped' slip through to model resolution below,\n // where requireModelOverrideSync('pending-work-spawn') throws \"Unknown model\"\n // — which the deacon records as a resume failure and, after 3, trips the\n // troubled gate, stranding the agent forever (PAN-2377/PAN-2829). Reject on\n // the model prefix regardless of status so it hits the graceful \"start a\n // fresh agent\" path instead of crashing. A non-placeholder agent (real model)\n // is unaffected.\n const isPlaceholder = !!agentState && typeof agentState.model === 'string' && agentState.model.startsWith('pending-');\n const allowedRuntimeStates = ['suspended', 'idle'];\n const allowedAgentStatuses = ['stopped', 'completed'];\n\n // Also allow resuming a \"running\" OR \"starting\" agent with no live tmux session —\n // this happens after a system crash where tmux was killed but state.json was never\n // updated to 'stopped'. For 'starting' this is a spawn that got past model\n // resolution but whose tmux session died mid-launch (the deacon patrol would\n // normally heal starting→stopped after its grace window, but that requires the\n // deacon to be running / not in OVERDECK_NO_RESUME mode). A non-placeholder\n // 'starting' agent with a saved session is resumable exactly like a crashed\n // 'running' agent; placeholder 'starting' agents (model starts with 'pending-')\n // are still rejected below because they never produced a resumable session.\n // The lifecycle UI model already treats runtime=stopped as isStopped, so this\n // keeps the gate consistent with the Resume button that model enables.\n // PAN-2098: a crash leaves the agent at status='running'/'starting' but with no\n // live process. Two shapes: (a) tmux session gone entirely, or (b) the tmux\n // session is still up while the harness process inside its pane has exited — a\n // \"keep-alive corpse\" (remain-on-exit leaves a dead pane in a live session).\n // The old check used `!sessionExists` alone, so a corpse (session present, pane\n // dead) was misclassified as a healthy running agent and refused resume with a\n // reasonless \"Cannot resume … runtime=active, status=running\". Treat a dead pane\n // as crashed too, matching the start path (flywheel-actions.ts isPaneDead).\n const isRunningOrStarting = agentState?.status === 'running' || agentState?.status === 'starting';\n const sessionAlive = isRunningOrStarting ? await Effect.runPromise(sessionExists(normalizedId)) : false;\n const paneDead = isRunningOrStarting && (!sessionAlive || await Effect.runPromise(isPaneDead(normalizedId)));\n const isCrashed = isRunningOrStarting && paneDead;\n\n // PAN-1675 (keystone): a `compact` resume exists specifically to recover a\n // context-wedged agent, which is typically status='running' with a LIVE (but\n // stuck) tmux session sitting at an overflow/idle prompt. The normal canResume\n // gate rejects running+live-session agents — which would make\n // resumeAgent({compact:true}) (the deacon's overflow recovery tiers AND\n // `pan resume --compact`) a silent no-op for exactly the agents it targets.\n // So a compact-resume of a running agent is allowed: the flow below compacts\n // the JSONL out-of-band and then kills the wedged session before relaunch.\n // This is safe because the only callers of {compact:true} act on agents they\n // have already determined to be context-overflow-wedged.\n const isCompactRecovery = opts?.compact === true && agentState?.status === 'running';\n\n const canResume = (runtimeState && allowedRuntimeStates.includes(runtimeState.state))\n || (agentState && allowedAgentStatuses.includes(agentState.status))\n || isCrashed\n || isCompactRecovery;\n\n if (!canResume) {\n // PAN-2098: never refuse without a concrete reason. A running/starting agent\n // that reached here has a live session AND a live pane (a crash would have set\n // isCrashed above), so it is genuinely healthy and there is nothing to resume.\n const reason = isRunningOrStarting\n ? `Cannot resume ${normalizedId}: it appears healthy (tmux session up, harness process alive) — there is nothing to resume. Stop it first if you intend to restart it.`\n : `Cannot resume ${normalizedId}: runtime=${runtimeState?.state || 'unknown'}, status=${agentState?.status || 'unknown'} is not a resumable state.`;\n logAgentLifecycleSync(normalizedId, `resumeAgent BLOCKED: ${reason}`);\n return {\n success: false,\n error: reason\n };\n }\n\n // Get saved session ID from any available source\n const sessionResolution = resolveLatestSessionIdSync(normalizedId);\n const sessionId = sessionResolution.sessionId;\n if (!sessionId) {\n // PAN-2098: state the concrete reason and enumerate the sources actually\n // checked so the operator can distinguish missing local pointers from a\n // failed durable-plane/event-store reconstruction.\n const harnessLabel = agentState?.harness ?? 'unknown';\n const reason = `Cannot resume ${normalizedId} (harness=${harnessLabel}): no resumable session id found. Checked ${sessionResolution.checked.join(', ')}. Start a fresh agent instead.`;\n logAgentLifecycleSync(normalizedId, `resumeAgent BLOCKED: ${reason}`);\n return {\n success: false,\n error: reason\n };\n }\n if (sessionResolution.needsPointerRepair) {\n saveSessionId(normalizedId, sessionId, 'recovered');\n logAgentLifecycleSync(normalizedId, `resume pointer reconstructed from durable metadata: sessionId=${sessionId}`);\n }\n\n if (!agentState || !hasWorkspace || isPlaceholder) {\n const reason = 'Saved Claude session is orphaned because the backing workspace/agent state is missing or placeholder-only. Start a fresh agent instead.';\n logAgentLifecycleSync(normalizedId, `resumeAgent BLOCKED: ${reason}`);\n return {\n success: false,\n error: reason\n };\n }\n\n try {\n await assertWorkspaceStackHealthyForSpawn(\n agentState.issueId || normalizedId.replace(/^agent-/, '').toUpperCase(),\n agentState.role ?? 'work',\n opts?.allowHost === true || agentState.hostOverride === true,\n agentState.workspace,\n );\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n logAgentLifecycleSync(normalizedId, `resumeAgent BLOCKED: ${reason}`);\n return { success: false, error: reason };\n }\n\n // PAN-1781: compact recovery = summarize the wedged session out-of-band and\n // respawn a FRESH session seeded with that summary. The previous approach\n // (PAN-1675: append a compact_boundary to the JSONL and relaunch with\n // --resume) was silently bypassed by the harness's resume leaf selection\n // ~half the time in the field — the relaunched session rebuilt the full\n // pre-compact context, re-overflowed, and escalated to /clear. A fresh\n // seeded session has nothing stale to rewind to, so its starting context is\n // bounded by construction. The old JSONL stays untouched on disk.\n // buildCompactRecoverySeed never throws: it degrades smart summary →\n // heuristic summary → reseed-instructions-only.\n let compactSeed: string | null = null;\n if (opts?.compact) {\n const seedResult = await buildCompactRecoverySeed(normalizedId);\n compactSeed = seedResult.seed;\n logAgentLifecycleSync(normalizedId, `compact recovery: respawning fresh session (seed=${seedResult.summarized ? 'summary' : 'reseed-only'})`);\n }\n\n // PAN-2009: capture whether the ohmypi process is actually alive BEFORE we kill any\n // zombie session below. A DEAD omp process cannot be resumed by session id —\n // `omp --resume` against a cleaned-up session never writes ready.json (the \"did\n // not become ready within 30s\" hang) — and there is no live transcript to\n // protect, so it must be fresh-launched (recovery, not rotation). A live\n // (suspended) omp process stays on the normal resume path.\n const piProcessWasAlive = getHarnessBehavior(agentState.harness).usesRpcFifo\n ? await hasAgentRuntimeInSession(normalizedId, 'ohmypi')\n : false;\n\n // Kill any zombie tmux session (crashed agent left behind)\n if (await Effect.runPromise(sessionExists(normalizedId))) {\n try {\n await Effect.runPromise(killSession(normalizedId));\n } catch { /* non-fatal */ }\n }\n\n // Remove completed marker so the agent can work again\n const completedFile = join(getAgentDir(normalizedId), 'completed');\n if (existsSync(completedFile)) {\n try { unlinkSync(completedFile); } catch { /* non-fatal */ }\n }\n\n // Append 'resume' session entry to continue state (PAN-946: workspace-44p)\n try {\n if (agentState?.workspace) {\n const issueId = agentState.issueId || normalizedId.replace('agent-', '').toUpperCase();\n const resolved = resolveProjectFromIssueSync(issueId);\n if (resolved) {\n appendContinueSessionEntryForIssue(resolved.projectPath, issueId, {\n reason: 'resume',\n agentModel: agentState.model || undefined,\n });\n }\n }\n } catch (continueErr: any) {\n console.warn(`[resumeAgent] Failed to append resume entry to continue state (non-fatal): ${continueErr?.message ?? continueErr}`);\n }\n\n try {\n const resumeStartedAt = new Date().toISOString();\n const startedBy = opts?.startedBy?.trim() || agentState.startedBy?.trim() || 'resume-agent';\n agentState.startedBy = startedBy;\n // Clear ready signal before resuming (clean slate for PAN-87 fix)\n clearReadySignal(normalizedId);\n\n const model = requestedModel || requireModelOverrideSync(agentState.model || 'claude-sonnet-4-6');\n if (requestedModel && requestedModel !== agentState.model) {\n agentState.model = requestedModel;\n saveAgentStateSync(agentState);\n }\n // PAN-1797: agents predating session-origin metadata must not have their\n // stored harness treated as `explicit` on auto-resume — that pins a stale\n // pairing (e.g. gpt-5.5 on claude-code) over the provider default forever.\n // Re-resolve from the model for origin-less agents (only an operator-supplied\n // opts.harness counts as explicit); agents WITH origin metadata keep prior\n // behavior and are handled by sessionResumeDriftReasons below.\n const hasSessionOrigin = !!(runtimeState?.sessionModel && runtimeState?.sessionHarness);\n const priorHarness = agentState.harness;\n const effectiveHarness = await resolveHarness({\n explicit: hasSessionOrigin ? (opts?.harness ?? agentState.harness) : opts?.harness,\n role: agentState.role,\n model,\n });\n const harnessLaunch = await prepareHarnessLaunch(effectiveHarness);\n const legacyHarnessMigrated =\n !hasSessionOrigin && priorHarness !== undefined && priorHarness !== effectiveHarness;\n agentState.harness = effectiveHarness;\n const supervisorLaunch = await prepareSupervisorForRelaunch(normalizedId, agentState, model, effectiveHarness);\n saveAgentStateSync(agentState);\n const resumeDriftReasons = sessionResumeDriftReasons(runtimeState, model, effectiveHarness);\n if (legacyHarnessMigrated) {\n // PAN-1797: force a fresh session so the re-defaulted harness takes effect;\n // never reuse a session across a harness change.\n resumeDriftReasons.push(`legacy harness ${priorHarness}→${effectiveHarness} (PAN-1797 re-default)`);\n }\n const expectedClaudeJsonl = sessionFilePath(agentState.workspace, sessionId);\n const spawnPlan = decideResumeSpawnPlan({\n harness: effectiveHarness,\n compactSeed: Boolean(compactSeed),\n driftReasons: resumeDriftReasons,\n piProcessWasAlive,\n transcriptExists: effectiveHarness !== 'claude-code'\n || claudeSessionTranscriptExists(agentState.workspace, sessionId),\n allowExplicitRecovery: opts?.compact === true && opts.recoverGated === true,\n });\n const piDeadRecovery = spawnPlan.freshReason === 'pi-dead-recovery';\n if (piDeadRecovery) {\n logAgentLifecycleSync(normalizedId, 'resumeAgent: dead ohmypi process — fresh-launching for recovery instead of omp --resume (PAN-2009)');\n }\n const claudeJsonlMissingRecovery = spawnPlan.freshReason === 'claude-jsonl-missing';\n if (spawnPlan.clearSessionPointers) {\n logAgentLifecycleSync(\n normalizedId,\n `resumeAgent: saved Claude transcript is missing at ${expectedClaudeJsonl} — clearing stale pointers and fresh-launching (PAN-2895)`,\n );\n await clearAgentSessionPointers(normalizedId);\n }\n const shouldResumeSavedSession = spawnPlan.mode === 'resume-saved';\n // PAN-1980: refuse to rotate to a new session. A resume that would need a\n // fresh session — compact/overflow recovery or model/harness drift — now\n // errors and stops instead of starting a new transcript.\n if (spawnPlan.rotationRefused) {\n const reason = compactSeed\n ? 'context-overflow compaction would respawn a fresh session'\n : `session drift (${resumeDriftReasons.join(', ')})`;\n const errMsg = `Refusing to rotate ${normalizedId} to a new session — ${reason}; session rotation is disabled (PAN-1980). Agent left stopped.`;\n logAgentLifecycleSync(normalizedId, `resumeAgent: ${errMsg}`);\n emitActivityEntrySync({ source: 'work-agent', level: 'error', message: `${normalizedId}: ${errMsg}`, issueId: agentState.issueId });\n return { success: false, error: errMsg };\n }\n const freshSessionId = !shouldResumeSavedSession && effectiveHarness === 'claude-code'\n ? randomUUID()\n : undefined;\n if (resumeDriftReasons.length > 0) {\n logAgentLifecycleSync(normalizedId, `resumeAgent: starting fresh session instead of --resume because session origin drifted (${resumeDriftReasons.join(', ')})`);\n }\n if (freshSessionId) {\n saveSessionId(normalizedId, freshSessionId);\n agentState.sessionId = freshSessionId;\n } else if (!shouldResumeSavedSession) {\n try {\n unlinkSync(join(getAgentDir(normalizedId), 'session.id'));\n } catch { /* absent or already cleared */ }\n }\n\n // Compute the effective message before building the launcher so codex can\n // embed it as the inline prompt in `codex exec resume <threadId> <message>`.\n // PAN-1781: a compact recovery skips the kickoff-redelivery machinery — its\n // seed (summary + reseed instructions) IS the opening prompt of the fresh\n // session; a caller-supplied message rides along after it.\n const issueId = agentState.issueId || normalizedId.replace(/^agent-/, '').toUpperCase();\n const resumeCause = opts?.resumeCause ?? (agentState.stoppedByUser === true ? 'operator' : message ? 'message' : 'system');\n const defaultResumeMessage = buildResumeContinueMessage(agentState);\n const resumeMessage: { message?: string; redeliveringKickoff: boolean; error?: string } = compactSeed\n ? { message: `${message ? `${compactSeed}\\n\\n${message}` : compactSeed}\\n\\n${buildResumeContract(resumeCause)}`, redeliveringKickoff: false }\n : resumeDriftReasons.length > 0\n ? { message: `${message ?? defaultResumeMessage}\\n\\n${buildResumeContract(resumeCause)}`, redeliveringKickoff: false }\n : await buildResumeMessageForAgent(agentState, defaultResumeMessage, message, resumeCause);\n if (resumeMessage.error) {\n console.error(`[resumeAgent] ${resumeMessage.error}`);\n emitActivityEntrySync({\n source: 'work-agent',\n level: 'error',\n message: `${normalizedId}: ${resumeMessage.error}`,\n issueId,\n });\n return { success: false, error: resumeMessage.error };\n }\n const effectiveMessage = resumeMessage.message ?? defaultResumeMessage;\n\n const { launcherContent, providerEnv } = await buildAgentLaunchConfig({\n agentId: normalizedId,\n model,\n workspace: agentState.workspace,\n role: agentState.role,\n isPlanning: agentState.role === 'plan',\n // PAN-1781/PAN-1787: compact recovery and model/harness drift launch a\n // fresh session. Normal resumes keep re-attaching to the saved session.\n ...(shouldResumeSavedSession ? { spawnMode: 'resume' as const, resumeSessionId: sessionId } : {}),\n sessionId: freshSessionId,\n harness: effectiveHarness,\n harnessBinaryPath: harnessLaunch.binaryPath,\n useSupervisor: supervisorLaunch.useSupervisor,\n supervisorScriptPath: supervisorLaunch.supervisorScriptPath,\n extraEnvExports: [harnessLaunch.pathExport],\n });\n\n const launcherScript = join(getAgentDir(normalizedId), 'launcher.sh');\n await writeLauncherScriptAtomic(launcherScript, launcherContent);\n const claudeCmd = `bash ${launcherScript}`;\n\n await Effect.runPromise(createSession(normalizedId, agentState.workspace, claudeCmd, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n OVERDECK_AGENT_ID: normalizedId,\n OVERDECK_ISSUE_ID: agentState.issueId || '',\n OVERDECK_SESSION_TYPE: agentState.role,\n OVERDECK_AGENT_STARTED_BY: startedBy,\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false',\n ...providerEnv\n }\n }));\n if (freshSessionId) {\n await saveAgentRuntimeState(normalizedId, {\n claudeSessionId: freshSessionId,\n sessionModel: model,\n sessionHarness: effectiveHarness,\n });\n }\n\n // Always wake the resumed agent with a continue prompt — without it, the\n // re-attached session sits silently at its last state, and the user (or\n // deacon nudge loop) ends up sending one manually anyway. Default matches\n // restartAgent's wording so behaviour is consistent across both entry points.\n // Caller-supplied message wins.\n\n let messageDelivered = false;\n if (effectiveHarness === 'ohmypi') {\n // ohmypi does not fire the Claude SessionStart hook; wait for ready.json and\n // deliver the auto-continue prompt through the FIFO JSONL protocol.\n try {\n await writeOhmypiAgentPrompt(normalizedId, effectiveMessage);\n messageDelivered = true;\n if (resumeMessage.redeliveringKickoff) markKickoffRedelivered(agentState);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[resumeAgent] ohmypi prompt delivery failed: ${msg}`);\n }\n } else if (effectiveHarness === 'acp' || effectiveHarness === 'opencode') {\n const delivery = await deliverInitialPromptWithRetry(\n normalizedId,\n effectiveMessage,\n 'resumeAgent:acp-continue',\n );\n messageDelivered = delivery.ok;\n if (delivery.ok && resumeMessage.redeliveringKickoff) markKickoffRedelivered(agentState);\n if (!delivery.ok) {\n await Effect.runPromise(killSession(normalizedId));\n return {\n success: false,\n error: `ACP continue prompt did not land: ${delivery.failure ?? 'unknown failure'}`,\n };\n }\n } else if (effectiveHarness === 'codex') {\n const delivery = await deliverInitialPromptWithRetry(normalizedId, effectiveMessage, 'resumeAgent:codex-continue', resilientDeliveryMethod(agentState.deliveryMethod));\n messageDelivered = delivery.ok;\n if (delivery.ok && resumeMessage.redeliveringKickoff) markKickoffRedelivered(agentState);\n if (!delivery.ok) {\n console.error(`[resumeAgent] Codex continue prompt did not land: ${delivery.failure ?? 'unknown failure'}`);\n }\n } else if (effectiveHarness === 'kimi-code') {\n // PAN-1837 review fix: native Kimi never fires the Claude SessionStart\n // hook and never writes Claude JSONL, so the final Claude branch below\n // (waitForReadySignal + deliverResumeMessageWithTranscriptConfirmation)\n // always times out for it — the continue message never lands, but the\n // pre-fix code still fell through to reporting success. deliverInitialPromptWithRetry\n // waits for readiness through the native kimi-session-signal path\n // (waitForKimiCodeTuiReady) and delivers via the PTY supervisor, the\n // same shape as the acp/codex branches above; this covers both a true\n // -S resume and a fresh relaunch since the launcher config already\n // resolved which one applies.\n //\n // PAN-1837 review fix (cycle 6): mirror ACP's strict contract — a failed\n // delivery must not be reported as a successfully resumed agent. Kimi\n // has no fallback signal (no hook, no JSONL) to confirm the message\n // landed any other way, so a failure here means the operator's\n // continue/rework prompt is genuinely lost; killing the session and\n // failing the resume surfaces that instead of leaving Kimi sitting idle\n // at its prompt while the caller believes the resume succeeded.\n const delivery = await deliverInitialPromptWithRetry(normalizedId, effectiveMessage, 'resumeAgent:kimi-code-continue');\n messageDelivered = delivery.ok;\n if (delivery.ok && resumeMessage.redeliveringKickoff) markKickoffRedelivered(agentState);\n if (!delivery.ok) {\n await Effect.runPromise(killSession(normalizedId));\n return {\n success: false,\n error: `Kimi Code continue prompt did not land: ${delivery.failure ?? 'unknown failure'}`,\n };\n }\n } else if (!shouldResumeSavedSession) {\n // Fresh session fallback — deliver like a kickoff. Transcript\n // confirmation is impossible here: the new session's id is unknown until\n // its SessionStart hook fires, and the saved sessionId points at the\n // archived or mismatched session. deliverInitialPromptWithRetry waits for\n // the ready signal internally.\n const delivery = await deliverInitialPromptWithRetry(normalizedId, effectiveMessage, 'resumeAgent:compact-seed', resilientDeliveryMethod(agentState.deliveryMethod));\n messageDelivered = delivery.ok;\n if (!delivery.ok) {\n console.error(`[resumeAgent] Fresh-session continue prompt did not land: ${delivery.failure ?? 'unknown failure'}`);\n }\n } else {\n // Wait for SessionStart hook to signal ready (PAN-87: reliable message delivery)\n const ready = await waitForReadySignal(normalizedId, 30);\n if (ready) {\n // PAN-3636: long-lived Claude sessions may open on the harness's\n // resume-summary gate instead of a composer. Autonomous pipeline agents\n // have no operator attached to answer it, so cross only that exact menu\n // through the typed pane-choice door before injecting the continuation.\n const panePreparation = await prepareAutonomousAgentResumePane(normalizedId, agentState.role);\n if (!panePreparation.ready) {\n console.error(`[resumeAgent] Auto-continue prompt not sent: ${panePreparation.reason}`);\n } else {\n if (panePreparation.action === 'resumed-from-summary') {\n logAgentLifecycleSync(normalizedId, 'resumeAgent: selected Claude Resume from summary before continuation delivery');\n }\n try {\n const delivery = await deliverResumeMessageWithTranscriptConfirmation({\n agentId: normalizedId,\n workspace: agentState.workspace,\n sessionId,\n message: effectiveMessage,\n caller: 'resumeAgent:auto-continue',\n // A relaunched Claude work/strike agent owns a fresh supervisor socket;\n // require that verified PTY path instead of silently falling back to a\n // tmux paste whose compaction summary can masquerade as prompt landing.\n deliveryMethod: supervisorLaunch.useSupervisor\n ? 'supervisor'\n : resilientDeliveryMethod(agentState.deliveryMethod),\n });\n messageDelivered = delivery.delivered;\n if (delivery.delivered && resumeMessage.redeliveringKickoff) markKickoffRedelivered(agentState);\n if (!delivery.delivered) {\n console.error(`[resumeAgent] Auto-continue prompt did not land after ${delivery.attempts} delivery attempts`);\n }\n } catch (deliveryError) {\n // A thrown delivery abort (e.g. the pane is blocked on a choice menu —\n // the PAN-3212 guard) must reach the kill-on-failure path below, not\n // escape to the caller with the wedged session still alive: a live\n // wedge blocks every fresh-spawn fallback with \"already running\"\n // (observed as an unbreakable review-restart loop after the\n // 2026-08-04 OOM killed a reviewer mid-cycle).\n messageDelivered = false;\n console.error(`[resumeAgent] Auto-continue delivery threw: ${deliveryError instanceof Error ? deliveryError.message : String(deliveryError)}`);\n }\n }\n } else {\n console.error('Claude SessionStart hook did not fire during resume, continue prompt not sent');\n }\n }\n\n if (!messageDelivered) {\n await Effect.runPromise(killSession(normalizedId)).catch(() => undefined);\n const error = `Resume continue prompt did not become a confirmed turn for ${normalizedId}`;\n logAgentLifecycleSync(normalizedId, `resumeAgent FAILED: ${error}`);\n return { success: false, messageDelivered: false, error };\n }\n\n const resumedAt = new Date().toISOString();\n if (compactSeed) {\n console.log(`[agents] Respawned ${normalizedId} fresh with compact-recovery seed (archived session ${sessionId}${freshSessionId ? `, new session ${freshSessionId}` : ''})`);\n logAgentLifecycleSync(normalizedId, `resumeAgent SUCCESS: compact-recovery fresh respawn (archived sessionId=${sessionId}${freshSessionId ? `, newSessionId=${freshSessionId}` : ''}), messageDelivered=${messageDelivered}`);\n } else if (piDeadRecovery) {\n console.log(`[agents] Respawned ${normalizedId} fresh because the prior Pi process was dead (archived session ${sessionId})`);\n logAgentLifecycleSync(normalizedId, `resumeAgent SUCCESS: fresh respawn after dead Pi process (archived sessionId=${sessionId}), messageDelivered=${messageDelivered}`);\n } else if (claudeJsonlMissingRecovery) {\n console.log(`[agents] Respawned ${normalizedId} fresh because the saved Claude transcript was missing (stale session ${sessionId}${freshSessionId ? `, new session ${freshSessionId}` : ''})`);\n logAgentLifecycleSync(normalizedId, `resumeAgent SUCCESS: fresh respawn after missing Claude JSONL (stale sessionId=${sessionId}${freshSessionId ? `, newSessionId=${freshSessionId}` : ''}), messageDelivered=${messageDelivered}`);\n } else if (!shouldResumeSavedSession) {\n console.log(`[agents] Respawned ${normalizedId} fresh because session origin drifted (archived session ${sessionId}${freshSessionId ? `, new session ${freshSessionId}` : ''})`);\n logAgentLifecycleSync(normalizedId, `resumeAgent SUCCESS: fresh respawn after origin drift (archived sessionId=${sessionId}${freshSessionId ? `, newSessionId=${freshSessionId}` : ''}), messageDelivered=${messageDelivered}`);\n } else {\n console.log(`[agents] Resumed ${normalizedId} with Claude session ${sessionId}`);\n logAgentLifecycleSync(normalizedId, `resumeAgent SUCCESS: sessionId=${sessionId}, messageDelivered=${messageDelivered}`);\n }\n await saveAgentRuntimeState(normalizedId, {\n state: 'active',\n lastActivity: resumedAt,\n });\n\n // Update agent state\n if (agentState) {\n if (bypassTroubledGate) {\n delete agentState.troubled;\n delete agentState.troubledAt;\n delete agentState.consecutiveFailures;\n delete agentState.firstFailureInRunAt;\n delete agentState.lastFailureAt;\n delete agentState.lastFailureReason;\n delete agentState.lastFailureNextRetryAt;\n }\n agentState.lastResumeAt = resumeStartedAt;\n markAgentRunning(agentState, { preserveFailureTracking: true });\n saveAgentStateSync(agentState);\n }\n\n // PAN-1675: a successful compaction-resume genuinely recovers a\n // context-overflow-wedged agent — so clear a context_overflow `stuck` flag\n // here (set by markWorkspaceStuck once the old /compact+/clear ladder\n // exhausted). Without this the agent would stay flagged stuck forever and\n // the deacon's overflowBlocked gate would keep skipping its recovery, even\n // though the agent is now healthy. Only clear when the stuck reason is\n // context_overflow (don't clobber an unrelated stuck state).\n if (opts?.compact && agentState?.issueId) {\n try {\n const db = getOverdeckDatabaseSync();\n const row = db.prepare('SELECT stuck, stuck_reason FROM review_status WHERE issue_id = ?')\n .get(agentState.issueId.toUpperCase()) as { stuck?: number; stuck_reason?: string | null } | undefined;\n if (row?.stuck === 1 && row.stuck_reason === 'context_overflow') {\n db.prepare(`\n UPDATE review_status\n SET stuck = 0, stuck_reason = NULL, stuck_at = NULL, stuck_details = NULL, updated_at = ?\n WHERE issue_id = ?\n `).run(Date.now(), agentState.issueId.toUpperCase());\n logAgentLifecycleSync(normalizedId, `cleared context_overflow stuck flag after compaction-resume for ${agentState.issueId}`);\n }\n } catch (clearErr) {\n console.warn(`[agents] Could not clear stuck flag after compaction-resume for ${normalizedId}:`, clearErr);\n }\n }\n\n return { success: true, messageDelivered };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logAgentLifecycleSync(normalizedId, `resumeAgent FAILED: ${msg}`);\n return {\n success: false,\n error: `Failed to resume agent: ${msg}`\n };\n }\n}\n\n/**\n * Check whether a tmux session has an active agent runtime.\n * A session may exist with only a bare bash shell after Claude exits.\n */\nasync function hasAgentRuntimeInSession(sessionName: string, harness: RuntimeName): Promise<boolean> {\n try {\n const panePids = await Effect.runPromise(listPaneValues(sessionName, '#{pane_pid}'));\n if (panePids.length === 0) return false;\n return hasAgentRuntimeInSubtree(panePids[0]!, harness);\n } catch {\n return false;\n }\n}\n","import { existsSync, statSync } from 'fs';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport { sendEscapeKeyAsync, sendKeys } from './tmux.js';\nimport { getHarnessBehavior } from './runtimes/behavior.js';\nimport type { RuntimeName } from './runtimes/types.js';\n\nexport const GRACEFUL_RESTART_GRACE_MS = 60_000;\n\nexport async function sendGracefulRestartWarning(\n agentId: string,\n harness: RuntimeName | undefined,\n workspace: string,\n): Promise<void> {\n const warning = 'Restarting in 60s. Update .pan/continue.json now with all progress, decisions, hazards, and resume point.';\n try {\n if (harness && !getHarnessBehavior(harness).usesRpcFifo) {\n await sendEscapeKeyAsync(agentId, 2);\n await new Promise((r) => setTimeout(r, 1_000));\n }\n await Effect.runPromise(sendKeys(agentId, warning));\n } catch { /* non-fatal — session may already be dead */ }\n\n await new Promise(r => setTimeout(r, GRACEFUL_RESTART_GRACE_MS));\n\n const continueFile = join(workspace, '.pan', 'continue.json');\n if (existsSync(continueFile)) {\n const mtime = statSync(continueFile).mtimeMs;\n const ageMs = Date.now() - mtime;\n if (ageMs > 5 * 60 * 1000) {\n console.warn(`[restartAgent] continue.json is stale (${Math.round(ageMs / 1000)}s old) — proceeding anyway`);\n }\n }\n}\n","import { Effect } from 'effect'\nimport { capturePane } from './tmux.js'\nimport { TmuxError } from './errors.js'\n\nexport type AwaitingInputReason = 'tool_permission' | 'user_question' | 'disambiguation' | 'confirmation' | 'planning_done' | 'session_resume' | 'rate_limit' | 'other'\n\nexport interface AwaitingInputDetection {\n reason: AwaitingInputReason\n prompt: string\n}\n\nconst ANSI_PATTERN = /\\x1b\\[[0-9;?]*[ -/]*[@-~]/g\nconst MAX_PROMPT_CHARS = 2_000\nconst PANE_DETECTION_CACHE_TTL_MS = 10_000\nconst MAX_PANE_DETECTION_CACHE_ENTRIES = 256\nconst MAX_CONCURRENT_PANE_DETECTIONS = 4\nconst MAX_MENU_FOOTER_LINES = 4\n\ntype PaneDetectionCacheEntry = {\n expiresAt: number\n detection: AwaitingInputDetection | null\n}\n\nconst paneDetectionCache = new Map<string, PaneDetectionCacheEntry>()\nconst paneDetectionInFlight = new Map<string, Promise<AwaitingInputDetection | null>>()\nconst paneDetectionWaiters: Array<() => void> = []\nlet activePaneDetections = 0\n\nfunction getPaneDetectionCacheKey(agentId: string, options: { isPlanning?: boolean; lines?: number }): string {\n return `${agentId}:${options.isPlanning === true ? 'planning' : 'agent'}:${options.lines ?? 90}`\n}\n\nfunction sweepPaneDetectionCache(now = Date.now()): void {\n for (const [key, entry] of paneDetectionCache) {\n if (entry.expiresAt <= now) {\n paneDetectionCache.delete(key)\n }\n }\n\n while (paneDetectionCache.size > MAX_PANE_DETECTION_CACHE_ENTRIES) {\n const oldestKey = paneDetectionCache.keys().next().value\n if (!oldestKey) break\n paneDetectionCache.delete(oldestKey)\n }\n}\n\nasync function withPaneDetectionSlot<T>(fn: () => Promise<T>): Promise<T> {\n if (activePaneDetections >= MAX_CONCURRENT_PANE_DETECTIONS) {\n await new Promise<void>((resolve) => paneDetectionWaiters.push(resolve))\n }\n activePaneDetections += 1\n try {\n return await fn()\n } finally {\n activePaneDetections = Math.max(0, activePaneDetections - 1)\n paneDetectionWaiters.shift()?.()\n }\n}\n\nfunction cleanPaneLine(line: string): string {\n return line\n .replace(ANSI_PATTERN, '')\n .replace(/[\\x00-\\x1f\\x7f]/g, '')\n .replace(/\\s+$/g, '')\n}\n\nfunction compactPrompt(prompt: string): string {\n const compact = prompt\n .split('\\n')\n .map((line) => line.trim())\n .filter(Boolean)\n .join('\\n')\n .trim()\n\n if (compact.length <= MAX_PROMPT_CHARS) return compact\n return `${compact.slice(0, MAX_PROMPT_CHARS - 1)}…`\n}\n\nexport function normalizeAwaitingInputPrompt(prompt: string): string {\n return compactPrompt(prompt)\n}\n\nfunction snippetAround(lines: string[], index: number, before = 8, after = 4): string {\n const start = Math.max(0, index - before)\n const end = Math.min(lines.length, index + after + 1)\n return compactPrompt(lines.slice(start, end).join('\\n'))\n}\n\nfunction lastIndexMatching(lines: string[], predicate: (line: string, index: number) => boolean): number {\n for (let i = lines.length - 1; i >= 0; i -= 1) {\n if (predicate(lines[i]!, i)) return i\n }\n return -1\n}\n\nfunction isRecentPromptIndex(lines: string[], index: number): boolean {\n if (index < 0) return false\n return lines.length - index <= 25\n}\n\nfunction isCurrentPromptIndex(lines: string[], index: number, maxTrailingLines = 0): boolean {\n if (index < 0) return false\n return lines.length - index <= maxTrailingLines + 1\n}\n\n/**\n * PAN-3068 — a harness footer hint line: key/action pairs separated by \"·\",\n * e.g. \"Esc to cancel · Tab to amend · ctrl+e to explain\". Every segment must\n * open with a key name, so real post-answer output (\"● Bash(git status)\") never\n * qualifies.\n */\nfunction looksLikeHarnessFooterHint(line: string): boolean {\n const segments = line.split('·').map((segment) => segment.trim()).filter(Boolean)\n if (segments.length === 0) return false\n return segments.every((segment) =>\n /^(?:esc(?:ape)?|tab|enter|return|space|backspace|shift\\+\\S+|ctrl\\+\\S+|alt\\+\\S+|\\?)\\b/i.test(segment)\n || /^press\\s+\\S+/i.test(segment),\n )\n}\n\n/**\n * PAN-3068 — the permission menu is still on screen when nothing but harness\n * footer chrome follows its final option. Claude Code renders \"Esc to cancel ·\n * Tab to amend · ctrl+e to explain\" below \"3. No\", so the old rule (the option\n * must be the literal last pane line) meant Claude permission prompts were\n * *never* detected — agents froze on a permission gate while the dashboard\n * reported them healthy and working. Real output below the menu means the\n * prompt was answered and scrolled, so it still clears.\n */\nfunction isPermissionMenuStillOnScreen(lines: string[], menuEndIndex: number): boolean {\n if (menuEndIndex < 0) return false\n const trailing = lines.slice(menuEndIndex + 1)\n if (trailing.length > MAX_MENU_FOOTER_LINES) return false\n return trailing.every(looksLikeHarnessFooterHint)\n}\n\nfunction findPermissionMenuEndIndex(lines: string[], menuStartIndex: number): number {\n if (menuStartIndex < 0) return -1\n const end = Math.min(lines.length, menuStartIndex + 8)\n for (let i = menuStartIndex; i < end; i += 1) {\n if (/\\s*3\\.\\s*No\\b/i.test(lines[i]!)) return i\n }\n return menuStartIndex\n}\n\nfunction looksLikeClaudePermissionMenu(text: string): boolean {\n return /(?:^|\\n)\\s*❯?\\s*1\\.\\s*Yes\\b/i.test(text)\n && /(?:^|\\n)\\s*2\\.\\s*Yes,?\\s*(?:and\\s*)?(?:allow|don't ask|always)/i.test(text)\n && /(?:^|\\n)\\s*3\\.\\s*No\\b/i.test(text)\n}\n\nfunction looksLikePermissionText(text: string): boolean {\n return /permission|allow(?:\\s+this)?|tool use|bash command|mcp tool|do you want to proceed|do you want to continue/i.test(text)\n}\n\nfunction looksLikeGenericConfirmation(line: string): boolean {\n return /(?:\\?|:)\\s*(?:\\[[Yy]\\/[Nn]\\]|\\[[Nn]\\/[Yy]\\]|\\([Yy]\\/[Nn]\\)|\\([Nn]\\/[Yy]\\)|\\[y\\/N\\]|\\[Y\\/n\\])\\s*$/i.test(line)\n || /\\b(?:continue|proceed|confirm|approve|allow|run|execute|overwrite|delete|remove)\\b.*(?:\\[[Yy]\\/[Nn]\\]|\\([Yy]\\/[Nn]\\)|\\[y\\/N\\]|\\[Y\\/n\\])/i.test(line)\n}\n\nfunction looksLikePiPermission(text: string): boolean {\n return /(?:permission|approval|approve|allow).{0,80}(?:required|request|prompt|action|command)/i.test(text)\n || /(?:allow|approve|run|execute)\\s+(?:this\\s+)?(?:command|tool|action)/i.test(text)\n}\n\n/**\n * PAN-3766 — ohmypi `ask` modal footer. Pi renders a blocking multiple-choice\n * question in a box-drawn frame whose footer hint reads \"Enter select · n note\n * · ↑/↓ move · Esc cancel\". The modal sits at the bottom of the pane while open\n * and disappears when answered. The JSONL scan (scanPendingInputsPromise)\n * carries the question payload for conversations; this pane signal covers\n * sessions whose transcript is not wired into that scan (pi work agents).\n */\nfunction looksLikePiAskFooter(line: string): boolean {\n return /Enter\\s+select\\b/i.test(line) && /Esc\\s+cancel/i.test(line)\n}\n\n/** Box-drawing chrome lines that may follow the ask modal's footer (╰───╯). */\nfunction looksLikePiBoxChrome(line: string): boolean {\n return /^[\\s╰╯╭╮─│├┤┌┐└┘┬┴┼]*$/.test(line)\n}\n\n/** The modal's header line: plain (\"Ask 1 questions\") or boxed (\"╭─ Ask ─╮\"). */\nfunction looksLikePiAskHeader(line: string): boolean {\n return /^\\s*(?:╭\\s*─*\\s*)?Ask\\b/.test(line)\n}\n\nfunction looksLikePlanningDone(text: string): boolean {\n return /planning finalized\\s*[—-]\\s*click done/i.test(text)\n || /click Done in the dashboard/i.test(text)\n || /planning (?:is )?complete.{0,120}(?:click|press|select) Done/i.test(text)\n}\n\n/**\n * PAN-1520 (covers #1197) — Claude Code session-resume dialog. Fires when an\n * agent is resumed via `claude --resume <session>` and a prior session is\n * still considered active or interrupted. The dialog blocks tool execution\n * until acknowledged. Conservative patterns — we want false-negatives over\n * false-positives, so we only match strings that clearly indicate a resume\n * choice point.\n */\nfunction looksLikeSessionResumeDialog(text: string): boolean {\n return /this session (?:is|was)\\s+(?:still\\s+)?(?:active|interrupted|running)/i.test(text)\n || /resume (?:the\\s+)?previous session/i.test(text)\n || /(?:continue|resume) (?:this\\s+)?session\\??.{0,40}(?:\\[[Yy]\\/[Nn]\\]|\\([Yy]\\/[Nn]\\)|press\\s+enter)/i.test(text)\n || /press enter to (?:continue|resume)/i.test(text)\n}\n\n/**\n * PAN-1834 — Codex/gpt-5.5 rate-limit / model-switch modal. The harness shows a\n * choice between keeping the current model and switching to an alternative.\n * To avoid matching prose that merely mentions rate limits, require BOTH option\n * lines to be present near the bottom of the pane.\n */\nfunction looksLikeRateLimitModelSwitchModal(text: string): boolean {\n return /keep current model/i.test(text) && /switch to\\s+\\S+/i.test(text)\n}\n\n/**\n * PAN-1690 — Codex TUI approval prompt header. Codex asks for approval with a\n * distinctive \"Would you like to …?\" prompt box (command run, edits, network/\n * host grants) followed by a numbered Yes/…/No menu. Unlike Claude's menu,\n * Codex renders option descriptions and a footer hint *below* \"3. No\", so the\n * trailing-line heuristics the Claude path relies on never fire — we key on the\n * header instead. Verb list pulled from the codex binary; see PAN-1690.\n */\nfunction looksLikeCodexApprovalHeader(line: string): boolean {\n return /Would you like to (?:run the following command|grant these permissions?|make the following edits|allow|apply)\\b/i.test(line)\n}\n\nexport function detectAwaitingInputFromPaneSync(\n pane: string,\n options: { isPlanning?: boolean } = {},\n): AwaitingInputDetection | null {\n const lines = pane\n .split('\\n')\n .map(cleanPaneLine)\n .filter((line) => line.trim().length > 0)\n if (lines.length === 0) return null\n\n const recentLines = lines.slice(-45)\n const recentText = recentLines.join('\\n')\n\n if (looksLikeClaudePermissionMenu(recentText) && looksLikePermissionText(recentText)) {\n const menuIndex = lastIndexMatching(lines, (line) => /❯?\\s*1\\.\\s*Yes\\b/i.test(line))\n const menuEndIndex = findPermissionMenuEndIndex(lines, menuIndex)\n if (isRecentPromptIndex(lines, menuIndex) && isPermissionMenuStillOnScreen(lines, menuEndIndex)) {\n return {\n reason: 'tool_permission',\n prompt: snippetAround(lines, menuIndex, 10, 3),\n }\n }\n }\n\n // PAN-1690 — Codex TUI approval prompt. Key on the distinctive header plus a\n // still-visible numbered option list near the bottom (so an answered prompt\n // that scrolled into history doesn't re-fire). Tolerates the option\n // descriptions / footer hint Codex renders below \"3. No\".\n const codexHeaderIndex = lastIndexMatching(lines, looksLikeCodexApprovalHeader)\n if (codexHeaderIndex >= 0 && isRecentPromptIndex(lines, codexHeaderIndex)) {\n // Tolerate whatever selection-cursor glyph Codex renders (❯ › > ▶ •) — or\n // none — by consuming any leading non-word characters before \"1. Yes\".\n const optionIndex = lastIndexMatching(lines, (line) => /^[^\\w]*1\\.\\s*Yes\\b/i.test(line))\n if (optionIndex > codexHeaderIndex && lines.length - optionIndex <= 12) {\n return {\n reason: 'tool_permission',\n prompt: snippetAround(lines, codexHeaderIndex, 1, 14),\n }\n }\n }\n\n const piIndex = lastIndexMatching(lines, (line) => looksLikePiPermission(line) || looksLikeGenericConfirmation(line))\n if (isRecentPromptIndex(lines, piIndex) && isCurrentPromptIndex(lines, piIndex)) {\n const snippet = snippetAround(lines, piIndex, 6, 2)\n const reason = looksLikeGenericConfirmation(lines[piIndex]!) ? 'confirmation' : 'tool_permission'\n return { reason, prompt: snippet }\n }\n\n // PAN-3766 — ohmypi `ask` modal. Key on the footer; only box-drawing chrome\n // (the frame's bottom border) may follow it. Anchor the snippet at the\n // modal's \"Ask\" header when present.\n const piAskIndex = lastIndexMatching(lines, looksLikePiAskFooter)\n if (piAskIndex >= 0 && isRecentPromptIndex(lines, piAskIndex)) {\n const trailing = lines.slice(piAskIndex + 1)\n const stillOnScreen = trailing.length <= 3 && trailing.every(looksLikePiBoxChrome)\n if (stillOnScreen) {\n let headerIndex = -1\n for (let i = piAskIndex - 1; i >= 0 && i >= piAskIndex - 30; i--) {\n if (looksLikePiAskHeader(lines[i]!)) { headerIndex = i; break }\n }\n return {\n reason: 'user_question',\n prompt: snippetAround(lines, headerIndex >= 0 ? headerIndex : piAskIndex, 0, 12),\n }\n }\n }\n\n if (options.isPlanning && looksLikePlanningDone(recentText)) {\n const doneIndex = lastIndexMatching(lines, (line) => /done|planning/i.test(line))\n return {\n reason: 'planning_done',\n prompt: snippetAround(lines, doneIndex >= 0 ? doneIndex : lines.length - 1, 8, 2),\n }\n }\n\n // PAN-1520 (covers #1197) — Claude Code session-resume dialog.\n if (looksLikeSessionResumeDialog(recentText)) {\n const resumeIndex = lastIndexMatching(lines, (line) => looksLikeSessionResumeDialog(line))\n return {\n reason: 'session_resume',\n prompt: snippetAround(lines, resumeIndex >= 0 ? resumeIndex : lines.length - 1, 8, 2),\n }\n }\n\n // PAN-1834 — Codex/gpt-5.5 rate-limit / model-switch modal.\n if (looksLikeRateLimitModelSwitchModal(recentText)) {\n const rateLimitIndex = lastIndexMatching(lines, (line) => /keep current model|switch to\\s+\\S+/i.test(line))\n if (isRecentPromptIndex(lines, rateLimitIndex)) {\n return {\n reason: 'rate_limit',\n prompt: snippetAround(lines, rateLimitIndex >= 0 ? rateLimitIndex : lines.length - 1, 8, 2),\n }\n }\n }\n\n return null\n}async function detectAwaitingInputForAgentPromise(\n agentId: string,\n options: { isPlanning?: boolean; lines?: number; cache?: boolean } = {},\n): Promise<AwaitingInputDetection | null> {\n const cacheEnabled = options.cache !== false\n const cacheKey = getPaneDetectionCacheKey(agentId, options)\n const now = Date.now()\n\n if (cacheEnabled) {\n sweepPaneDetectionCache(now)\n\n const cached = paneDetectionCache.get(cacheKey)\n if (cached && cached.expiresAt > now) {\n paneDetectionCache.delete(cacheKey)\n paneDetectionCache.set(cacheKey, cached)\n return cached.detection\n }\n\n const inFlight = paneDetectionInFlight.get(cacheKey)\n if (inFlight) return inFlight\n }\n\n const detectionPromise = withPaneDetectionSlot(async () => {\n const pane = await Effect.runPromise(capturePane(agentId, options.lines ?? 90))\n const detection = detectAwaitingInputFromPaneSync(pane, options)\n if (cacheEnabled) {\n paneDetectionCache.set(cacheKey, {\n expiresAt: Date.now() + PANE_DETECTION_CACHE_TTL_MS,\n detection,\n })\n sweepPaneDetectionCache()\n }\n return detection\n })\n\n if (!cacheEnabled) return detectionPromise\n\n paneDetectionInFlight.set(cacheKey, detectionPromise)\n try {\n return await detectionPromise\n } finally {\n paneDetectionInFlight.delete(cacheKey)\n }\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect-native variant of detectAwaitingInputForAgent. Fails with TmuxError if\n * the pane capture fails outside the in-cache fast path.\n */\nexport const detectAwaitingInputForAgent = (\n agentId: string,\n options: { isPlanning?: boolean; lines?: number; cache?: boolean } = {},\n): Effect.Effect<AwaitingInputDetection | null, TmuxError> =>\n Effect.tryPromise({\n try: () => detectAwaitingInputForAgentPromise(agentId, options),\n catch: (cause) =>\n new TmuxError({\n command: 'capture-pane',\n message: `failed to detect awaiting input for ${agentId}`,\n cause,\n }),\n })\n\n/**\n * Capture the pane and return the detection synchronously. This is a pure\n * function over a captured string — exported for symmetry, useful when the\n * caller already has the pane text in hand.\n */\nexport const detectAwaitingInputFromPane = (\n pane: string,\n options: { isPlanning?: boolean } = {},\n): Effect.Effect<AwaitingInputDetection | null> =>\n Effect.sync(() => detectAwaitingInputFromPaneSync(pane, options))\n\nexport interface CodexApprovalPrompt {\n /** The \"Would you like to …?\" header line. */\n header: string\n /** Lines between the header and the first option (command, reason, etc.). */\n detail: string\n /** The numbered menu options, in order. */\n options: Array<{ number: number; label: string }>\n}\n\n/**\n * PAN-1690 — parse a Codex approval prompt (as captured by the codex-aware\n * branch of detectAwaitingInputFromPaneSync) into its header, detail, and\n * numbered options, so the dashboard can render the menu and answer it without\n * the terminal. Returns null when the text doesn't contain a usable menu\n * (fewer than two numbered options). Option lines tolerate a leading selection\n * cursor (❯ › > etc.); the footer hint and other non-numbered lines are\n * naturally excluded.\n */\nexport function parseCodexApprovalPrompt(text: string): CodexApprovalPrompt | null {\n const lines = text.split('\\n').map((l) => l.trim()).filter((l) => l.length > 0)\n if (lines.length === 0) return null\n\n const headerIndex = lines.findIndex((l) => looksLikeCodexApprovalHeader(l))\n if (headerIndex < 0) return null\n\n const optionRe = /^[^\\w]*(\\d+)\\.\\s*(.+?)\\s*$/\n const options: Array<{ number: number; label: string }> = []\n const detailLines: string[] = []\n let firstOptionSeen = false\n for (let i = headerIndex + 1; i < lines.length; i += 1) {\n const m = optionRe.exec(lines[i]!)\n if (m) {\n firstOptionSeen = true\n options.push({ number: Number(m[1]), label: m[2]!.trim() })\n } else if (!firstOptionSeen) {\n detailLines.push(lines[i]!)\n }\n // Lines after the first option that aren't options (footer hints, blanks)\n // are ignored.\n }\n\n if (options.length < 2) return null\n return {\n header: lines[headerIndex]!,\n detail: detailLines.join('\\n'),\n options,\n }\n}\n","/**\n * Agent enrichment utilities (PAN-440 / PAN-1048)\n *\n * Shared functions for computing enrichment fields:\n * role, hasPendingQuestion, pendingQuestionCount, resolution, resolutionCount\n *\n * Used by both the legacy REST /api/agents endpoint and the new\n * AgentEnrichmentService background poller.\n *\n * PAN-1048: replaced the legacy `agentPhase` string with the role primitive —\n * the dashboard derives label/status from `role` + lifecycle state.\n */\n\nimport { existsSync } from 'fs'\nimport { readdir, readFile, stat } from 'fs/promises'\nimport { homedir } from 'os'\nimport { basename, join } from 'path'\nimport { encodeClaudeProjectDir } from './paths.js'\nimport { promisify } from 'util'\nimport { exec } from 'child_process'\nimport { Effect } from 'effect'\nimport { FsError } from './errors.js'\nimport { getAgentDir, getAgentStateSync } from './agents/agent-state.js'\nimport { getAgentRuntimeState } from './agents/runtime-state.js'\nimport {\n detectAwaitingInputForAgent,\n normalizeAwaitingInputPrompt,\n type AwaitingInputDetection,\n} from './agent-input-detection.js'\nimport { resolveProjectFromIssueSync } from './projects.js'\nimport { getGitHubConfig } from '../dashboard/server/services/tracker-config.js'\nimport { extractPrefixSync } from './issue-id.js'\nimport { getLatestSessionIdSync } from './agents/activity.js'\n\nconst execAsync = promisify(exec)\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface QuestionOption { label: string; description: string }\nexport interface Question { question: string; header: string; options: QuestionOption[]; multiSelect: boolean }\nexport interface PendingQuestion { toolId: string; timestamp: string; questions: Question[] }\n\n/**\n * PAN-1520 — every \"agent is blocked waiting on operator\" surface we detect.\n * Used to drive the unified pending-input indicator and notifications.\n */\nexport type PendingInputKind =\n | 'askUserQuestion'\n | 'permissionRequest'\n | 'exitPlanMode'\n | 'enterPlanMode'\n | 'sessionResume'\n | 'rateLimit'\n // PAN-3113 — a blocking numbered-choice menu parsed from a claude-code\n // conversation's pane (session-resume gate et al.). Carries parsed options\n // and is answerable from the dashboard via /api/conversations/:id/pane-choice.\n | 'paneChoice'\n // An interactive session ended its turn — the operator's move. This is the\n // only kind for a question asked in prose, which carries no tool call and no\n // modal, and is therefore invisible to every other detector.\n | 'agentTurnEnded'\n // PAN-3233 — a question/confirmation/disambiguation prompt detected from the\n // pane or runtime state, with no structured payload. Answered in the\n // terminal, not through a dashboard control.\n | 'paneQuestion'\n\nexport interface PendingAskUserQuestionSnapshot {\n toolUseId: string\n askedAt: string\n questions: Array<{\n question: string\n header?: string\n multiSelect?: boolean\n options: Array<{ label: string; description?: string }>\n }>\n}\n\nexport interface AgentEnrichment {\n role: 'plan' | 'work' | 'review' | 'test' | 'ship' | 'flywheel' | undefined\n hasPendingQuestion: boolean\n pendingQuestionCount: number\n pendingQuestionPrompt?: string\n pendingQuestionReason?: string\n // PAN-1520 — unified pending-input surfaces.\n pendingInputCount: number\n pendingInputKinds: PendingInputKind[]\n pendingAskUserQuestion?: PendingAskUserQuestionSnapshot\n /**\n * Payload for an outstanding ExitPlanMode. The scan has always produced this\n * and the poller has always emitted `enrichment.pendingProposedPlan`, but the\n * field was never on this interface — so it read `undefined` forever and no\n * plan ever reached the store from the agent path. It compiled because the\n * root tsconfig excluded the dashboard from typecheck.\n */\n pendingProposedPlan?: { toolUseId: string; askedAt: string; plan: string }\n resolution: string\n resolutionCount: number\n /**\n * The JSONL scan this enrichment was derived from. The poller caches it per\n * agent and replays it while the file's mtime is unchanged, so a static\n * session costs no I/O without anyone having to fabricate an empty scan.\n */\n jsonlScan?: PendingInputsScan\n}\n\n// PAN-1520 / PAN-1834 — promote pane-detected blocking surfaces into the\n// unified pending-input set so the indicator / needs-you list fires.\nexport function appendPaneDetectionKind(detection: AwaitingInputDetection | null, kinds: PendingInputKind[]): void {\n if (detection?.reason === 'session_resume' && !kinds.includes('sessionResume')) {\n kinds.push('sessionResume')\n }\n if (detection?.reason === 'rate_limit' && !kinds.includes('rateLimit')) {\n kinds.push('rateLimit')\n }\n // PAN-3070 — the same promotion for a detected tool-permission prompt. The\n // Decisions surface already derives `permissionRequest` from this detection at\n // read time (PAN-3051), but nothing folded it in here, so every other consumer\n // of pendingInputKinds/pendingInputCount saw an empty set and described a\n // frozen agent as working. Folding it in once makes the surfaces agree.\n if (detection?.reason === 'tool_permission' && !kinds.includes('permissionRequest')) {\n kinds.push('permissionRequest')\n }\n // PAN-3233 — question-family detections previously yielded NO kind, making\n // the agent invisible to Decisions (kinds.length > 0 gate) and unreapable\n // (pendingInputCount > 0 gate). Guarded so a JSONL-backed AskUserQuestion is\n // not double-represented.\n if (\n (detection?.reason === 'user_question' || detection?.reason === 'confirmation'\n || detection?.reason === 'disambiguation' || detection?.reason === 'planning_done')\n && !kinds.includes('askUserQuestion') && !kinds.includes('paneQuestion')\n ) {\n kinds.push('paneQuestion')\n }\n}\n\n/**\n * PAN-3070 — the reasons that mean the agent is parked and cannot advance a\n * single tool call until a human answers. An agent in this state must never be\n * reported as `resolution: working` / `status: healthy`.\n *\n * `other` is deliberately excluded. PAN-1591 showed it is set by the generic\n * pane/runtime/fallback detections that carry no answerable prompt — trusting it\n * here would report agents as blocked while they are working normally.\n */\nconst BLOCKING_AWAITING_INPUT_REASONS: ReadonlySet<string> = new Set<AwaitingInputDetection['reason']>([\n 'tool_permission',\n 'user_question',\n 'disambiguation',\n 'confirmation',\n 'planning_done',\n 'session_resume',\n 'rate_limit',\n])\n\n/**\n * PAN-3070 — the single predicate every \"is this agent stuck?\" surface consults.\n * Takes the detection fields rather than a whole enrichment so the REST listing,\n * the enrichment itself, and tests all judge from the same two values.\n */\nexport function isBlockedOnPendingInput(agent: {\n hasPendingQuestion?: boolean\n pendingQuestionReason?: string\n}): boolean {\n return agent.hasPendingQuestion === true\n && BLOCKING_AWAITING_INPUT_REASONS.has(agent.pendingQuestionReason ?? '')\n}\n\n// PAN-1834 — an agent that IS the active specialist (review/test/ship) must\n// still surface its own pending-input. Only suppress the parked work/plan agent\n// when another specialist is active on the same issue.\nexport function isOwnActiveSpecialist(role: AgentEnrichment['role']): boolean {\n return role === 'review' || role === 'test' || role === 'ship'\n}\n\n/**\n * Interactive roles idle at a live prompt as their steady state — plan agents\n * after finalize (skipKill) and conversation sessions. For these,\n * stopped-but-session-alive is deliberate, never stale (PAN-3338).\n */\nexport function isInteractiveRoleAgent(agentId: string, role?: string): boolean {\n return role === 'plan' || agentId.startsWith('conv-')\n}\n\n// ─── JSONL path helpers ───────────────────────────────────────────────────────\n\nexport function getClaudeProjectDir(workspacePath: string): string {\n return join(homedir(), '.claude', 'projects', encodeClaudeProjectDir(workspacePath))\n}\n\nexport async function getActiveSessionPath(projectDir: string): Promise<string | null> {\n if (!existsSync(projectDir)) return null\n try {\n const entries = await readdir(projectDir)\n const jsonlFiles = entries.filter(f => f.endsWith('.jsonl'))\n if (jsonlFiles.length === 0) return null\n // Claude Code rotates/renames JSONL session files, so a file present at\n // readdir() can vanish before stat(). Stat each file independently and DROP\n // the ones that disappear — never let a single ENOENT reject the whole batch\n // and collapse the result to null. (PAN: the null path made the\n // complete-planning pending-AskUserQuestion guard scan nothing → it\n // completed planning while the operator's question was still open.)\n const withMtime = (\n await Promise.all(\n jsonlFiles.map(async f => {\n try {\n return { name: f, path: join(projectDir, f), mtime: (await stat(join(projectDir, f))).mtime.getTime() }\n } catch {\n return null\n }\n }),\n )\n ).filter((x): x is { name: string; path: string; mtime: number } => x !== null)\n if (withMtime.length === 0) return null\n withMtime.sort((a, b) => b.mtime - a.mtime)\n return withMtime[0].path\n } catch {\n return null\n }\n}\n\nfunction getProjectPathByPrefix(issuePrefix: string): string {\n const issueId = `${issuePrefix}-1`\n const resolved = resolveProjectFromIssueSync(issueId)\n if (resolved) return resolved.projectPath\n const config = getGitHubConfig()\n if (config) {\n for (const { owner, repo, prefix } of config.repos) {\n const repoPrefix = prefix || repo.toUpperCase().replace(/-CLI$/, '').replace(/-/g, '')\n if (repoPrefix.toUpperCase() === issuePrefix.toUpperCase()) {\n const possiblePaths = [\n join(homedir(), 'Projects', repo),\n join(homedir(), 'Projects', repo.replace(/-cli$/, '')),\n join(homedir(), 'Projects', owner, repo),\n ]\n for (const path of possiblePaths) {\n if (existsSync(path)) return path\n }\n }\n }\n }\n return join(homedir(), 'Projects')\n}async function getAgentWorkspacePromise(agentId: string): Promise<string | null> {\n const workspace = getAgentStateSync(agentId)?.workspace;\n if (workspace) return workspace;\n try {\n const { stdout: paneCwd } = await execAsync(\n `tmux display-message -t ${agentId} -p '#{pane_current_path}' 2>/dev/null`,\n { encoding: 'utf-8' }\n )\n const trimmed = paneCwd.trim()\n if (trimmed && existsSync(trimmed)) return trimmed\n } catch {}\n const isStrikeAgent = agentId.startsWith('strike-')\n const issueId = agentId.replace(/^(agent-|planning-|strike-)/, '').toUpperCase()\n const prefix = extractPrefixSync(issueId)\n if (!prefix) return null\n try {\n const projectPath = getProjectPathByPrefix(prefix)\n const workspaceNames = isStrikeAgent\n ? [`feature-${issueId.toLowerCase()}-strike`, `feature-${issueId.toLowerCase()}`]\n : [`feature-${issueId.toLowerCase()}`]\n for (const workspaceName of workspaceNames) {\n const workspacePath = join(projectPath, 'workspaces', workspaceName)\n if (existsSync(workspacePath)) return workspacePath\n }\n return projectPath\n } catch {\n return null\n }\n}/**\n * Resolve the transcript belonging to THIS agent.\n *\n * A Claude project dir is keyed on the cwd, so every session that ever ran in\n * the same cwd shares one directory. Agents whose cwd is the primary repo — the\n * flywheel orchestrator, conversations, any `--cwd <repo>` handoff — therefore\n * sit in a directory alongside each other's transcripts. Picking the freshest\n * file there attributes whichever session wrote last to whoever asks, so the\n * flywheel was observed reporting a conversation's open question as its own.\n *\n * The agent's own session id is recorded at spawn, so resolve that first and\n * only fall back to freshest-wins when the agent has no identifiable transcript\n * of its own (codex/omp keep their history elsewhere, and their thread ids are\n * not `.jsonl` files here).\n */\nasync function getAgentJsonlPathPromise(agentId: string): Promise<string | null> {\n const workspace = await Effect.runPromise(getAgentWorkspace(agentId))\n if (!workspace) return null\n const projectDir = getClaudeProjectDir(workspace)\n const sessionId = getLatestSessionIdSync(agentId)\n if (sessionId) {\n const ownPath = join(projectDir, `${sessionId}.jsonl`)\n if (existsSync(ownPath)) return ownPath\n }\n return await getActiveSessionPath(projectDir)\n}\n\n/**\n * Count outstanding AskUserQuestions across ALL of an agent's workspace JSONL\n * session files — not just the newest one. Claude Code rotates session files,\n * so an open AskUserQuestion can sit in a file that is not the most-recently-\n * modified. The complete-planning guard MUST be robust to that: scanning only\n * the active file let TIN-1 complete planning while the operator's question was\n * still open. Returns 0 only when no file has an unanswered AUQ.\n */\nasync function countPendingAskUserQuestionsForAgentPromise(agentId: string): Promise<number> {\n const workspace = await Effect.runPromise(getAgentWorkspace(agentId))\n if (!workspace) return 0\n const projectDir = getClaudeProjectDir(workspace)\n if (!existsSync(projectDir)) return 0\n let files: string[]\n try {\n files = (await readdir(projectDir)).filter(f => f.endsWith('.jsonl'))\n } catch {\n return 0\n }\n let total = 0\n for (const f of files) {\n try {\n const scan = await scanPendingInputsPromise(join(projectDir, f))\n total += scan.askUserQuestions.length\n } catch {\n // A file vanishing mid-scan (rotation) is not \"no question\" — but we\n // can't read it, so skip it; other files still contribute.\n }\n }\n return total\n}\n\n/** Effect-native: count pending AskUserQuestions across all of an agent's JSONL files. */\nexport const countPendingAskUserQuestionsForAgent = (\n agentId: string,\n): Effect.Effect<number> =>\n Effect.promise(() => countPendingAskUserQuestionsForAgentPromise(agentId))\n\n/**\n * Count pending AskUserQuestions only in the current agent session generation.\n * A forced fresh start preserves historical JSONLs while replacing the agent\n * state directory, so an unpinned fallback transcript older than startedAt\n * belongs to the discarded generation and must not recreate the decision gate.\n */\nasync function countPendingAskUserQuestionsForCurrentAgentSessionPromise(\n agentId: string,\n): Promise<number> {\n const jsonlPath = await getAgentJsonlPathPromise(agentId)\n if (!jsonlPath) return 0\n\n const currentSessionId = getLatestSessionIdSync(agentId)\n const isPinnedCurrentSession = currentSessionId !== null\n && basename(jsonlPath) === `${currentSessionId}.jsonl`\n if (!isPinnedCurrentSession) {\n const startedAtMs = Date.parse(getAgentStateSync(agentId)?.startedAt ?? '')\n if (Number.isFinite(startedAtMs)) {\n try {\n if ((await stat(jsonlPath)).mtimeMs < startedAtMs) return 0\n } catch {\n return 0\n }\n }\n }\n\n try {\n const scan = await scanPendingInputsPromise(jsonlPath)\n return scan.askUserQuestions.length\n } catch {\n return 0\n }\n}\n\n/** Effect-native: count pending AskUserQuestions in the current agent session generation. */\nexport const countPendingAskUserQuestionsForCurrentAgentSession = (\n agentId: string,\n): Effect.Effect<number> =>\n Effect.promise(() => countPendingAskUserQuestionsForCurrentAgentSessionPromise(agentId))\n\n// ─── JSONL scanning ───────────────────────────────────────────────────────────\n\n/**\n * Read the last `maxBytes` from a file. Efficient for large JSONL files where\n * only recent entries (at the end) are relevant.\n */\nasync function readFileTail(filePath: string, maxBytes: number): Promise<string> {\n try {\n const fileStat = await stat(filePath)\n const start = Math.max(0, fileStat.size - maxBytes)\n // For fs/promises readFile we can't specify start offset directly,\n // so use a stream approach for large files.\n if (start === 0) {\n return readFile(filePath, 'utf-8')\n }\n const { createReadStream } = await import('node:fs')\n return new Promise((resolve, reject) => {\n const stream = createReadStream(filePath, { start, encoding: 'utf-8' })\n let data = ''\n stream.on('data', chunk => { data += chunk })\n stream.on('end', () => resolve(data))\n stream.on('error', reject)\n })\n } catch {\n return ''\n }\n}/**\n * PAN-1520 — the hook (`sync-sources/hooks/ask-user-question-hook`) returns a\n * deny verdict with this reason string. When we see a tool_result whose content\n * matches this, treat it as a \"still pending\" — the operator has NOT actually\n * answered; the upstream tool was denied to force a plain-text restate.\n */\nconst ASK_USER_QUESTION_HOOK_DENY_MARKER = 'PAN-1520'\nconst ASK_USER_QUESTION_HOOK_DENY_PHRASE = 'surfaced to the operator'\n\nfunction askUserQuestionDenySignalPresent(content: unknown): boolean {\n const checkString = (text: string) =>\n text.includes(ASK_USER_QUESTION_HOOK_DENY_MARKER) ||\n text.includes(ASK_USER_QUESTION_HOOK_DENY_PHRASE)\n\n if (typeof content === 'string') return checkString(content)\n if (Array.isArray(content)) {\n return content.some((part: unknown) => {\n if (typeof part === 'string') return checkString(part)\n if (part && typeof part === 'object' && 'text' in part) {\n const text = (part as { text?: unknown }).text\n return typeof text === 'string' && checkString(text)\n }\n return false\n })\n }\n return false\n}\n\nfunction isAskUserQuestionHookDenyToolResult(item: { content?: unknown; is_error?: unknown }): boolean {\n if (item.is_error !== true) return false\n return askUserQuestionDenySignalPresent(item.content)\n}\n\nasync function getPendingQuestionsPromise(jsonlPath: string): Promise<PendingQuestion[]> {\n const detection = await scanPendingInputsPromise(jsonlPath)\n return detection.askUserQuestions\n}\n\nexport const EMPTY_PENDING_INPUTS_SCAN: PendingInputsScan = Object.freeze({\n askUserQuestions: [],\n enterPlanModeOpen: false,\n exitPlanModePending: false,\n})\n\nexport interface PendingInputsScan {\n readonly askUserQuestions: PendingQuestion[]\n /** Outstanding EnterPlanMode tool_use ids without a matching ExitPlanMode (plan being drafted). */\n readonly enterPlanModeOpen: boolean\n /** Outstanding ExitPlanMode tool_use without a matching tool_result (operator approval pending). */\n readonly exitPlanModePending: boolean\n /** Payload for the outstanding ExitPlanMode, when its plan text is available. */\n readonly pendingProposedPlan?: { toolUseId: string; askedAt: string; plan: string }\n}\n\n/**\n * PAN-1520 — does this user-message entry carry operator text (i.e. an answer)\n * rather than just being a wrapper for a tool_result? When the hook denies an\n * AskUserQuestion the agent restates the question as plain text; the operator's\n * next user message — whether typed in the terminal, sent from the conversation\n * composer, or delivered by the answer-question route — is the actual answer.\n * The presence of that user-text turn after the deny is what resolves the AUQ.\n */\nfunction isUserTextEntry(entry: unknown): boolean {\n if (!entry || typeof entry !== 'object') return false\n const e = entry as { type?: unknown; message?: { content?: unknown } }\n if (e.type !== 'user') return false\n const content = e.message?.content\n if (typeof content === 'string') return content.trim().length > 0\n if (Array.isArray(content)) {\n return content.some((item: unknown) => {\n if (!item || typeof item !== 'object') return false\n const t = (item as { type?: unknown }).type\n // Anything that's not a tool_result counts as operator text (text blocks,\n // image blocks pasted by the operator, etc.).\n return t !== 'tool_result'\n })\n }\n return false\n}\n\n// ─── ohmypi transcript shapes (PAN-3766) ─────────────────────────────────────\n//\n// Pi's `ask` tool is the AskUserQuestion analogue: the assistant turn carries a\n// `toolCall` content item (name 'ask') whose `arguments.questions` match\n// AskUserQuestion's input.questions shape, and the operator's answer — or pi's\n// own synthetic interrupt-skip when a queued message arrives — lands as a\n// `role: 'toolResult'` message carrying the matching toolCallId. Pi lines are\n// distinguished by the `{type: 'message', message: {role, ...}}` envelope, so\n// one scan pass handles claude and pi transcripts (and stale-harness files)\n// without threading the harness through every caller.\n\nfunction isPiMessageEntry(entry: unknown): boolean {\n if (!entry || typeof entry !== 'object') return false\n const e = entry as { type?: unknown; message?: { role?: unknown } }\n return e.type === 'message' && typeof e.message?.role === 'string'\n}\n\nfunction piAskQuestions(raw: unknown): Question[] {\n if (!Array.isArray(raw)) return []\n const questions: Question[] = []\n for (const q of raw) {\n if (!q || typeof q !== 'object') continue\n const r = q as Record<string, unknown>\n const options: QuestionOption[] = Array.isArray(r['options'])\n ? r['options']\n .filter((o): o is Record<string, unknown> => !!o && typeof o === 'object')\n .map((o) => ({\n label: typeof o['label'] === 'string' ? o['label'] : '',\n description: typeof o['description'] === 'string' ? o['description'] : '',\n }))\n : []\n questions.push({\n question: typeof r['question'] === 'string' ? r['question'] : '',\n header: typeof r['header'] === 'string' ? r['header'] : '',\n options,\n multiSelect: r['multiSelect'] === true,\n })\n }\n return questions\n}\n\ninterface AskScanState {\n askToolCalls: Map<string, PendingQuestion>\n askAnswered: Set<string>\n askDeniedAwaitingUser: Set<string>\n}\n\nfunction scanPiEntry(entry: unknown, state: AskScanState): void {\n const e = entry as {\n timestamp?: unknown\n message?: { role?: unknown; toolCallId?: unknown; content?: unknown }\n }\n const message = e.message\n if (!message) return\n const timestamp = typeof e.timestamp === 'string' ? e.timestamp : new Date().toISOString()\n\n if (message.role === 'assistant' && Array.isArray(message.content)) {\n for (const item of message.content) {\n if (!item || typeof item !== 'object') continue\n const c = item as { type?: unknown; name?: unknown; id?: unknown; arguments?: { questions?: unknown } }\n if (c.type === 'toolCall' && c.name === 'ask' && typeof c.id === 'string') {\n state.askToolCalls.set(c.id, {\n toolId: c.id,\n timestamp,\n questions: piAskQuestions(c.arguments?.questions),\n })\n }\n }\n return\n }\n\n // Any toolResult resolves the ask — including pi's synthetic interrupt-skip\n // (details.source 'interrupt_skipped'): either way the modal is gone from the\n // pane. If the agent still needs the answer it re-asks, creating a fresh\n // pending entry.\n if (message.role === 'toolResult' && typeof message.toolCallId === 'string') {\n if (state.askToolCalls.has(message.toolCallId)) {\n state.askAnswered.add(message.toolCallId)\n state.askDeniedAwaitingUser.delete(message.toolCallId)\n }\n }\n}\n\n/**\n * Scan a session transcript for inputs waiting on the operator: outstanding\n * AskUserQuestion tool calls (claude) or `ask` tool calls (ohmypi, PAN-3766),\n * and claude plan-mode state. Format is detected per line, so a transcript\n * resolves correctly even when the recorded harness is stale.\n */\nexport async function scanPendingInputsPromise(jsonlPath: string): Promise<PendingInputsScan> {\n if (!existsSync(jsonlPath)) {\n return { askUserQuestions: [], enterPlanModeOpen: false, exitPlanModePending: false }\n }\n try {\n // Only read the last 512KB — pending inputs are always recent.\n const content = await readFileTail(jsonlPath, 512_000)\n const lines = content.split('\\n').filter(line => line.trim())\n const askToolCalls = new Map<string, PendingQuestion>()\n const askAnswered = new Set<string>()\n // PAN-1520 — AUQs the hook denied but the operator hasn't yet answered as\n // plain text. The next user-text turn resolves all of them.\n const askDeniedAwaitingUser = new Set<string>()\n const exitPlanModeIds = new Set<string>()\n const exitPlanModeAnswered = new Set<string>()\n const exitPlanModeCalls = new Map<string, { timestamp: string; plan: string }>()\n let pendingExitPlanId: string | undefined\n const enterPlanModeIds = new Set<string>()\n const exitPlanModeFiredAfterEnter = new Set<string>() // tracks any ExitPlanMode (signals plan-mode session ended)\n\n for (const line of lines) {\n try {\n const entry = JSON.parse(line)\n\n // PAN-3766 — ohmypi transcript line; pi's `ask` tool feeds the same\n // pending-question bookkeeping as claude's AskUserQuestion.\n if (isPiMessageEntry(entry)) {\n scanPiEntry(entry, { askToolCalls, askAnswered, askDeniedAwaitingUser })\n continue\n }\n\n // PAN-1520 — a user-text turn resolves any AUQs still in the \"denied\n // but awaiting operator response\" state. Without this check, an AUQ\n // that was hook-denied and then answered via the dashboard modal (or\n // by the operator typing in the terminal) would appear pending forever.\n if (isUserTextEntry(entry)) {\n for (const id of askDeniedAwaitingUser) askAnswered.add(id)\n askDeniedAwaitingUser.clear()\n }\n\n const messageContent = entry.message?.content\n if (!Array.isArray(messageContent)) continue\n for (const item of messageContent) {\n if (item.type === 'tool_use') {\n if (item.name === 'AskUserQuestion' && typeof item.id === 'string') {\n askToolCalls.set(item.id, {\n toolId: item.id,\n timestamp: entry.timestamp || new Date().toISOString(),\n questions: item.input?.questions || [],\n })\n } else if (item.name === 'ExitPlanMode' && typeof item.id === 'string') {\n exitPlanModeIds.add(item.id)\n exitPlanModeFiredAfterEnter.add(item.id)\n const planText = typeof item.input?.plan === 'string' ? item.input.plan : ''\n exitPlanModeCalls.set(item.id, { timestamp: entry.timestamp || new Date().toISOString(), plan: planText })\n pendingExitPlanId = item.id\n } else if (item.name === 'EnterPlanMode' && typeof item.id === 'string') {\n enterPlanModeIds.add(item.id)\n }\n }\n if (item.type === 'tool_result' && typeof item.tool_use_id === 'string') {\n if (askToolCalls.has(item.tool_use_id)) {\n if (isAskUserQuestionHookDenyToolResult(item)) {\n // Hook deny — mark as \"waiting on operator text\" so the very\n // next user-text turn clears it.\n askDeniedAwaitingUser.add(item.tool_use_id)\n } else {\n askAnswered.add(item.tool_use_id)\n askDeniedAwaitingUser.delete(item.tool_use_id)\n }\n }\n if (exitPlanModeIds.has(item.tool_use_id)) {\n exitPlanModeAnswered.add(item.tool_use_id)\n }\n }\n }\n } catch { /* malformed JSONL line — skip */ }\n }\n\n const askUserQuestions = Array.from(askToolCalls.entries())\n .filter(([id]) => !askAnswered.has(id))\n .map(([, question]) => question)\n\n const exitPlanModePending = Array.from(exitPlanModeIds).some(id => !exitPlanModeAnswered.has(id))\n\n // FR-1 — carry the plan payload so the dashboard can promote it to an\n // approval modal (instead of the plan being visible only in the terminal).\n const pendingCall = pendingExitPlanId && !exitPlanModeAnswered.has(pendingExitPlanId)\n ? exitPlanModeCalls.get(pendingExitPlanId)\n : undefined\n const pendingProposedPlan = pendingCall\n ? { toolUseId: pendingExitPlanId!, askedAt: pendingCall.timestamp, plan: pendingCall.plan }\n : undefined\n\n // EnterPlanMode is \"open\" only if no ExitPlanMode has fired since the last\n // EnterPlanMode. Approximation: if there are EnterPlanMode ids AND no\n // ExitPlanMode has fired, we're still in plan mode.\n const enterPlanModeOpen = enterPlanModeIds.size > 0 && exitPlanModeFiredAfterEnter.size === 0\n\n return { askUserQuestions, enterPlanModeOpen, exitPlanModePending, ...(pendingProposedPlan ? { pendingProposedPlan } : {}) }\n } catch {\n return { askUserQuestions: [], enterPlanModeOpen: false, exitPlanModePending: false }\n }\n}async function getAgentPendingQuestionsPromise(agentId: string): Promise<PendingQuestion[]> {\n const jsonlPath = await Effect.runPromise(getAgentJsonlPath(agentId))\n if (!jsonlPath) return []\n return [...(await Effect.runPromise(getPendingQuestions(jsonlPath)))]\n}\n\nasync function getAgentJsonlMtimePromise(agentId: string): Promise<number | null> {\n const jsonlPath = await Effect.runPromise(getAgentJsonlPath(agentId))\n if (!jsonlPath || !existsSync(jsonlPath)) return null\n try {\n return (await stat(jsonlPath)).mtime.getTime()\n } catch {\n return null\n }\n}async function computeAgentEnrichmentPromise(\n agentId: string,\n startedAt?: string,\n hasActiveSpecialist?: boolean,\n cachedScan?: PendingInputsScan | null,\n): Promise<AgentEnrichment> {\n const isPlanning = agentId.startsWith('planning-')\n\n // Read persisted role for enrichment projection.\n const stateRole = getAgentStateSync(agentId)?.role\n\n const role: AgentEnrichment['role'] =\n (stateRole === 'plan' || stateRole === 'work' || stateRole === 'review' ||\n stateRole === 'test' || stateRole === 'ship' || stateRole === 'flywheel')\n ? stateRole\n : (isPlanning ? 'plan' : undefined)\n\n // Get runtime state for resolution + explicit waiting signals.\n const runtimeState = await Effect.runPromise(getAgentRuntimeState(agentId))\n\n // Get pending questions + other blocking surfaces, filtered by agent start time.\n // The caller (the enrichment poller) hands back the previous scan while the\n // JSONL's mtime is unchanged — an unchanged file cannot produce a different\n // scan, so replaying it costs no I/O and stays truthful. It must never be\n // replaced by an empty scan: an agent blocked on a question writes nothing,\n // so its mtime never moves again, and a fabricated \"nothing is pending\" would\n // latch permanently and strand the operator's dialog.\n let scan: PendingInputsScan = EMPTY_PENDING_INPUTS_SCAN\n if (cachedScan) {\n scan = cachedScan\n } else {\n const jsonlPath = await Effect.runPromise(getAgentJsonlPath(agentId))\n if (jsonlPath) scan = await scanPendingInputsPromise(jsonlPath)\n }\n\n let pendingQuestions: PendingQuestion[] = [...scan.askUserQuestions]\n const enterPlanModeOpen = scan.enterPlanModeOpen\n const exitPlanModePending = scan.exitPlanModePending\n if (pendingQuestions.length > 0 && startedAt) {\n const agentStartTime = new Date(startedAt).getTime()\n pendingQuestions = pendingQuestions.filter(q => {\n const qTime = new Date(q.timestamp).getTime()\n return !isNaN(qTime) && qTime >= agentStartTime\n })\n }\n\n const questionDetection: AwaitingInputDetection | null = pendingQuestions.length > 0\n ? {\n reason: 'user_question',\n prompt: normalizeAwaitingInputPrompt(\n pendingQuestions[0]?.questions\n .map(q => q.question)\n .filter(Boolean)\n .join('\\n') || 'Agent is waiting for a user answer',\n ),\n }\n : null\n\n const runtimeDetection: AwaitingInputDetection | null = runtimeState?.state === 'waiting-on-human'\n ? {\n reason: (runtimeState.waitingReason as AwaitingInputDetection['reason']) || 'other',\n prompt: normalizeAwaitingInputPrompt(\n runtimeState.waitingNotification || 'Agent is waiting for human input',\n ),\n }\n : null\n\n const paneDetection = !questionDetection && !runtimeDetection\n ? await Effect.runPromise(detectAwaitingInputForAgent(agentId, { isPlanning }))\n : null\n\n // Resolution is a stop-hook verdict and can outlive the stop that produced it.\n // Once tool activity resumes, the current activity is stronger evidence: do\n // not keep projecting the old needs_input verdict as an actionable decision.\n const fallbackDetection: AwaitingInputDetection | null =\n runtimeState?.resolution === 'needs_input' && runtimeState.state !== 'active'\n ? {\n reason: 'other',\n prompt: normalizeAwaitingInputPrompt('Agent stopped because it needs human input or hit a blocker'),\n }\n : null\n\n // An interactive session does not finish — it yields the turn. The Stop hook\n // reports that as `activity: idle`, which until now carried no operator\n // signal, so an agent that asked its question in prose sat silent: a prose\n // question writes no AskUserQuestion tool_use and puts no modal in the pane,\n // so questionDetection, paneDetection and runtimeDetection all miss it.\n //\n // Scoped to interactive roles deliberately. A work/review/test agent's idle\n // can mean between-items or complete; flagging those would flood the surface\n // and destroy the signal this exists to carry.\n const isInteractiveRole = isInteractiveRoleAgent(agentId, role)\n const turnEndedWaiting = isInteractiveRole && runtimeState?.state === 'idle'\n const turnEndedDetection: AwaitingInputDetection | null = turnEndedWaiting\n ? {\n reason: 'other',\n prompt: normalizeAwaitingInputPrompt('Agent finished its turn and is waiting for your reply'),\n }\n : null\n\n // turnEndedDetection is last: a real question or modal describes the wait far\n // better than the generic turn-end phrasing, so it only speaks when nothing\n // more specific did.\n const detection = questionDetection ?? runtimeDetection ?? paneDetection ?? fallbackDetection ?? turnEndedDetection\n // PAN-1834 — suppresses every surface derived from the parked agent's stale\n // JSONL/runtime/fallback/turn-end state while a specialist is active.\n const isSpecialistSuppressed = hasActiveSpecialist === true && !isOwnActiveSpecialist(role)\n // PAN-3233 — a live blocking pane modal (tool_permission foremost) is fresh\n // evidence the agent is frozen RIGHT NOW, unlike the stale surfaces above, so\n // it bypasses suppression. Review fix: this exemption is scoped to ONLY the\n // pane detection's own fingerprint/kind (below) — it must never also expose\n // unrelated stale JSONL state (a pending ExitPlanMode, an old AskUserQuestion)\n // that happens to be sitting in the same scan.\n const hasBlockingPaneDetection = paneDetection !== null && BLOCKING_AWAITING_INPUT_REASONS.has(paneDetection.reason)\n const shouldSuppressPendingInput = isSpecialistSuppressed && !hasBlockingPaneDetection\n const hasPendingQuestion = !shouldSuppressPendingInput && detection !== null\n\n // PAN-1520 — fold every blocking surface into a uniform set.\n // PermissionRequest is tracked server-side in channelPermissionRequestsById and\n // is contributed at the read-model layer (server merges it in). The enrichment\n // here owns the JSONL-derived kinds plus pane/runtime fallbacks.\n const pendingInputKinds: PendingInputKind[] = []\n let pendingAskUserQuestion: PendingAskUserQuestionSnapshot | undefined\n // PAN-3233 review fix — gated on isSpecialistSuppressed, NOT the pane-exempt\n // shouldSuppressPendingInput: these are all derived from the JSONL scan, which\n // describes the parked agent's stale state regardless of what the live pane\n // currently shows. The pane exemption must not leak them.\n if (!isSpecialistSuppressed) {\n if (pendingQuestions.length > 0) {\n pendingInputKinds.push('askUserQuestion')\n const first = pendingQuestions[0]\n pendingAskUserQuestion = {\n toolUseId: first.toolId,\n askedAt: first.timestamp,\n questions: first.questions.map(q => ({\n question: q.question,\n header: q.header,\n multiSelect: q.multiSelect,\n options: q.options.map(o => ({ label: o.label, description: o.description })),\n })),\n }\n }\n if (exitPlanModePending) pendingInputKinds.push('exitPlanMode')\n if (enterPlanModeOpen && !exitPlanModePending) pendingInputKinds.push('enterPlanMode')\n // Only when nothing more specific is open — an agent parked on a permission\n // prompt is idle too, and must read as the permission, not as a bare turn-end.\n if (turnEndedWaiting && pendingInputKinds.length === 0) pendingInputKinds.push('agentTurnEnded')\n }\n // PAN-1520 (covers #1197) — promote pane-detected session-resume dialogs\n // into the unified pending-input set so the indicator fires.\n // PAN-1834 — also promote pane-detected rate-limit / model-switch modals.\n // PAN-3233 — gated on the pane-exempt shouldSuppressPendingInput (not\n // isSpecialistSuppressed): this is the one surface the exemption exists for.\n if (!shouldSuppressPendingInput) {\n appendPaneDetectionKind(detection, pendingInputKinds)\n }\n\n return {\n role,\n hasPendingQuestion,\n // PAN-3233 — pane/runtime blocking detections carry no JSONL AskUserQuestion\n // entries, so this used to report count 0 even when hasPendingQuestion was\n // true. Fold them in: JSONL count when present, else 1 for a blocking\n // detection, else 0 (including when suppressed).\n pendingQuestionCount: shouldSuppressPendingInput\n ? 0\n : pendingQuestions.length > 0\n ? pendingQuestions.length\n : (isBlockedOnPendingInput({ hasPendingQuestion, pendingQuestionReason: detection?.reason }) ? 1 : 0),\n pendingQuestionPrompt: shouldSuppressPendingInput ? undefined : detection?.prompt,\n pendingQuestionReason: shouldSuppressPendingInput ? undefined : detection?.reason,\n pendingInputCount: pendingInputKinds.length,\n pendingInputKinds,\n pendingAskUserQuestion,\n // Suppressed alongside every other JSONL-derived surface: when a specialist\n // owns the issue the work agent is parked, not asking. PAN-3233 review fix —\n // gated on isSpecialistSuppressed, not the pane exemption: a live blocking\n // pane prompt is not evidence that a stale proposed plan is still relevant.\n pendingProposedPlan: isSpecialistSuppressed ? undefined : scan.pendingProposedPlan,\n // PAN-3070 — an agent holding an unanswered blocking prompt is not working.\n // The runtime resolution is written by the stop hook and stays at whatever\n // it last was, so a frozen agent kept reporting `working` for hours; the\n // detection we just computed is the fresher evidence and wins.\n resolution: isBlockedOnPendingInput({ hasPendingQuestion, pendingQuestionReason: detection?.reason })\n ? 'needs_input'\n : (runtimeState?.resolution || 'working'),\n resolutionCount: runtimeState?.resolutionCount || 0,\n jsonlScan: scan,\n }\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect-native variant of computeAgentEnrichment. Fails with FsError if any\n * underlying filesystem read fails outside the swallowed branches.\n */\nexport const computeAgentEnrichment = (\n agentId: string,\n startedAt?: string,\n hasActiveSpecialist?: boolean,\n cachedScan?: PendingInputsScan | null,\n): Effect.Effect<AgentEnrichment, FsError> =>\n Effect.tryPromise({\n try: () => computeAgentEnrichmentPromise(agentId, startedAt, hasActiveSpecialist, cachedScan),\n catch: (cause) =>\n new FsError({\n path: getAgentDir(agentId),\n operation: 'computeAgentEnrichment',\n cause,\n }),\n })\n\n/** Effect-native: resolve the workspace path for an agent (null on failure). */\nexport const getAgentWorkspace = (\n agentId: string,\n): Effect.Effect<string | null> =>\n Effect.promise(() => getAgentWorkspacePromise(agentId))\n\n/** Effect-native: resolve the active JSONL session path for an agent (null on failure). */\nexport const getAgentJsonlPath = (\n agentId: string,\n): Effect.Effect<string | null> =>\n Effect.promise(() => getAgentJsonlPathPromise(agentId))\n\n/** Effect-native: get the mtime of the agent's active JSONL session file. */\nexport const getAgentJsonlMtime = (\n agentId: string,\n): Effect.Effect<number | null> =>\n Effect.promise(() => getAgentJsonlMtimePromise(agentId))\n\n/** Effect-native: parse pending questions from a JSONL file. */\nexport const getPendingQuestions = (\n jsonlPath: string,\n): Effect.Effect<readonly PendingQuestion[]> =>\n Effect.promise(() => getPendingQuestionsPromise(jsonlPath))\n\n/** Effect-native: get pending questions for an agent by id. */\nexport const getAgentPendingQuestions = (\n agentId: string,\n): Effect.Effect<readonly PendingQuestion[]> =>\n Effect.promise(() => getAgentPendingQuestionsPromise(agentId))\n","import { Effect } from 'effect'\n\nimport { countPendingAskUserQuestionsForCurrentAgentSession as countPendingAskUserQuestionsForCurrentAgentSessionEffect } from '../agent-enrichment.js'\nimport {\n detectAwaitingInputForAgent as detectAwaitingInputForAgentEffect,\n type AwaitingInputDetection,\n type AwaitingInputReason,\n} from '../agent-input-detection.js'\nimport { TmuxError } from '../errors.js'\nimport { sessionExists as sessionExistsEffect } from '../tmux.js'\n\nexport interface PendingOperatorDecision {\n source: 'pane' | 'jsonl-auq'\n reason: AwaitingInputReason | 'ask_user_question'\n prompt?: string\n}\n\nexport const DESTRUCTIVE_RECOVERY_BLOCKING_REASONS: ReadonlySet<AwaitingInputReason> = new Set([\n 'tool_permission',\n 'user_question',\n 'disambiguation',\n 'confirmation',\n 'planning_done',\n 'session_resume',\n])\n\nexport interface PendingOperatorDecisionDeps {\n sessionExists?: (agentId: string) => Promise<boolean>\n detectAwaitingInputForAgent?: (agentId: string) => Promise<AwaitingInputDetection | null>\n countPendingAskUserQuestionsForCurrentAgentSession?: (agentId: string) => Promise<number>\n warn?: (message: string) => void\n}\n\nexport async function detectPendingOperatorDecision(\n agentId: string,\n deps: PendingOperatorDecisionDeps = {},\n): Promise<PendingOperatorDecision | null> {\n const sessionExists = deps.sessionExists\n ?? ((id: string) => Effect.runPromise(sessionExistsEffect(id)))\n const detectAwaitingInputForAgent = deps.detectAwaitingInputForAgent\n ?? ((id: string) => Effect.runPromise(detectAwaitingInputForAgentEffect(id)))\n const countPendingAskUserQuestionsForCurrentAgentSession = deps.countPendingAskUserQuestionsForCurrentAgentSession\n ?? ((id: string) => Effect.runPromise(countPendingAskUserQuestionsForCurrentAgentSessionEffect(id)))\n const warn = deps.warn ?? console.warn\n\n let liveSession = false\n try {\n liveSession = await sessionExists(agentId)\n } catch (error) {\n if (!(error instanceof TmuxError)) throw error\n warn(`[agents] Pending operator decision pane check failed open for ${agentId}: ${error.message}`)\n }\n\n if (liveSession) {\n try {\n const detection = await detectAwaitingInputForAgent(agentId)\n if (detection && DESTRUCTIVE_RECOVERY_BLOCKING_REASONS.has(detection.reason)) {\n return {\n source: 'pane',\n reason: detection.reason,\n prompt: detection.prompt,\n }\n }\n } catch (error) {\n if (!(error instanceof TmuxError)) throw error\n warn(`[agents] Pending operator decision pane check failed open for ${agentId}: ${error.message}`)\n }\n }\n\n const pendingAskUserQuestions = await countPendingAskUserQuestionsForCurrentAgentSession(agentId)\n if (pendingAskUserQuestions > 0) {\n return {\n source: 'jsonl-auq',\n reason: 'ask_user_question',\n }\n }\n\n return null\n}\n","import { resolveMuseSessionPathSync, museSessionId } from '../runtimes/muse-session.js';\nimport { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';\nimport { readdir as readdirAsync } from 'fs/promises';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { Effect } from 'effect';\nimport { BLANKED_PROVIDER_ENV } from '../child-env.js';\nimport { getLatestSessionIdSync } from './activity.js';\nimport { sendGracefulRestartWarning } from '../graceful-restart.js';\nimport { checkHookSync, generateFixedPointPromptSync } from '../hooks.js';\nimport { generateLauncherScriptSync } from '../launcher-generator.js';\nimport { resolveHarness } from '../harness-resolve.js';\nimport { prepareHarnessLaunch } from '../harness-binary.js';\nimport { normalizeModelOverrideSync, requireModelOverrideSync } from '../model-validation.js';\nimport { logAgentLifecycleSync } from '../persistent-logger.js';\nimport { getProviderForModelSync, setupCredentialFileAuthSync, clearCredentialFileAuthSync } from '../providers.js';\nimport type { ModelId } from '../settings.js';\nimport { normalizeHarness } from '../overdeck/conversations.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport {\n createSession,\n createSessionSync,\n killSessionSync,\n listPaneValues,\n sendKeys,\n sessionExists,\n sessionExistsSync,\n} from '../tmux.js';\nimport {\n decideResumeGate,\n getAgentDir,\n getAgentResumeGateBlockReason,\n getAgentStateSync,\n markAgentRunning,\n saveAgentStateSync,\n type AgentState,\n type Role,\n} from './agent-state.js';\nimport { deliverAgentMessage, deliverInitialPromptWithRetry, resilientDeliveryMethod } from './delivery.js';\nimport { clearReadySignal, normalizeAgentId } from './identity.js';\nimport {\n detectPendingOperatorDecision,\n type PendingOperatorDecision,\n} from './pending-decision-gate.js';\nimport { listRunningAgentsSync } from './queries.js';\nimport { getProviderEnvForModel, getProviderExportsForModel } from './provider-env.js';\nimport { saveAgentRuntimeState } from './runtime-state.js';\nimport {\n claudeSystemPromptFiles,\n getCodexLauncherFields,\n getRoleRuntimeBaseCommand,\n hasAgentRuntimeInSubtree,\n waitForPromptReady,\n writeLauncherScriptAtomic,\n writeOhmypiAgentPrompt,\n} from './runtime-command.js';\nimport { assertWorkspaceStackHealthyForSpawn, buildAgentLaunchConfig } from './spawn-prep.js';\nimport { prepareSupervisorForRelaunch, buildResumeContinueMessage } from './supervisor-channels.js';\nimport { stopAgent } from './termination.js';\n\nexport type RecoverAgentResult =\n | { action: 'respawned'; state: AgentState }\n | { action: 'already-running'; state: AgentState };\n\nexport interface RestartAgentOptions {\n model?: string;\n harness?: RuntimeName;\n graceful?: boolean;\n message?: string;\n force?: boolean;\n}\n\nexport interface RestartAgentResult {\n success: boolean;\n error?: string;\n code?: 'pending-operator-decision';\n pendingDecision?: PendingOperatorDecision;\n}\n\nexport interface RestartAgentDeps {\n detectPendingOperatorDecision?: (agentId: string) => Promise<PendingOperatorDecision | null>;\n getAgentStateSync?: typeof getAgentStateSync;\n logAgentLifecycleSync?: typeof logAgentLifecycleSync;\n assertWorkspaceStackHealthyForSpawn?: typeof assertWorkspaceStackHealthyForSpawn;\n resolveHarness?: typeof resolveHarness;\n prepareHarnessLaunch?: typeof prepareHarnessLaunch;\n sessionExists?: (agentId: string) => Promise<boolean>;\n sendGracefulRestartWarning?: typeof sendGracefulRestartWarning;\n stopAgent?: (agentId: string) => Promise<unknown>;\n}\n\nexport function resolveRecoveryResumeSessionId(agentId: string, harness: RuntimeName): string | undefined {\n if (harness === 'muse') {\n const path = resolveMuseSessionPathSync(agentId);\n return path ? museSessionId(path) : undefined;\n }\n if (harness !== 'codex' && harness !== 'acp' && harness !== 'kimi-code' && harness !== 'opencode') return undefined;\n return getLatestSessionIdSync(agentId) ?? undefined;\n}\n\nexport async function restartAgent(\n agentId: string,\n opts: RestartAgentOptions = {},\n deps: RestartAgentDeps = {},\n): Promise<RestartAgentResult> {\n const normalizedId = normalizeAgentId(agentId);\n const { graceful = true, model: rawNewModel, harness: newHarness, message, force = false } = opts;\n const newModel = normalizeModelOverrideSync(rawNewModel);\n const readAgentState = deps.getAgentStateSync ?? getAgentStateSync;\n const detectPendingDecision = deps.detectPendingOperatorDecision ?? detectPendingOperatorDecision;\n const logLifecycle = deps.logAgentLifecycleSync ?? logAgentLifecycleSync;\n const assertWorkspaceHealthy = deps.assertWorkspaceStackHealthyForSpawn\n ?? assertWorkspaceStackHealthyForSpawn;\n const resolveRestartHarness = deps.resolveHarness ?? resolveHarness;\n const prepareRestartHarness = deps.prepareHarnessLaunch ?? prepareHarnessLaunch;\n const restartSessionExists = deps.sessionExists\n ?? ((id: string) => Effect.runPromise(sessionExists(id)));\n const sendRestartWarning = deps.sendGracefulRestartWarning ?? sendGracefulRestartWarning;\n const stopRestartAgent = deps.stopAgent\n ?? ((id: string) => Effect.runPromise(stopAgent(id)));\n\n const agentState = readAgentState(normalizedId);\n if (!agentState) {\n return { success: false, error: `Agent ${normalizedId} not found` };\n }\n const gateDecision = decideResumeGate(getAgentResumeGateBlockReason(agentState), 'operator-start');\n if (gateDecision.decision === 'block') {\n const reason = `Cannot restart ${normalizedId}: ${gateDecision.reason}. Clear the gate before restarting.`;\n logLifecycle(normalizedId, `restartAgent BLOCKED: ${reason}`);\n return { success: false, error: reason };\n }\n const checkPendingDecision = async (): Promise<RestartAgentResult | null> => {\n if (force) return null;\n const pendingDecision = await detectPendingDecision(normalizedId);\n if (!pendingDecision) return null;\n\n const pendingReason = pendingDecision.reason.replaceAll('_', ' ');\n const issueId = agentState.issueId || normalizedId.replace(/^agent-/, '').toUpperCase();\n const reason = `Agent ${normalizedId} is waiting on an operator decision (${pendingReason}). Answer it with 'pan answer ${issueId}' or open the Decisions panel; pass force to discard it deliberately.`;\n logLifecycle(normalizedId, `restartAgent BLOCKED: ${reason}`);\n return {\n success: false,\n error: reason,\n code: 'pending-operator-decision',\n pendingDecision,\n };\n };\n\n const initialPendingDecision = await checkPendingDecision();\n if (initialPendingDecision) return initialPendingDecision;\n if (!agentState.workspace || !existsSync(agentState.workspace)) {\n return { success: false, error: `Agent workspace missing: ${agentState.workspace}` };\n }\n\n logLifecycle(normalizedId, `restartAgent called (graceful=${graceful}, model=${newModel || 'unchanged'}, harness=${newHarness || 'unchanged'})`);\n\n try {\n await assertWorkspaceHealthy(\n agentState.issueId || normalizedId.replace(/^agent-/, '').toUpperCase(),\n agentState.role ?? 'work',\n agentState.hostOverride === true,\n agentState.workspace,\n );\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n logLifecycle(normalizedId, `restartAgent BLOCKED: ${reason}`);\n return { success: false, error: reason };\n }\n\n const effectiveModel = newModel || requireModelOverrideSync(agentState.model || 'claude-sonnet-4-6');\n const effectiveHarness = await resolveRestartHarness({\n explicit: newHarness ?? agentState.harness,\n role: agentState.role,\n model: effectiveModel,\n });\n const harnessLaunch = await prepareRestartHarness(effectiveHarness);\n\n if (graceful && await restartSessionExists(normalizedId)) {\n const warningPendingDecision = await checkPendingDecision();\n if (warningPendingDecision) return warningPendingDecision;\n await sendRestartWarning(normalizedId, agentState.harness, agentState.workspace);\n }\n\n const stopPendingDecision = await checkPendingDecision();\n if (stopPendingDecision) return stopPendingDecision;\n await stopRestartAgent(normalizedId);\n\n if (newModel && newModel !== agentState.model) {\n agentState.model = newModel;\n }\n agentState.harness = effectiveHarness;\n agentState.status = 'starting';\n saveAgentStateSync(agentState);\n\n try {\n clearReadySignal(normalizedId);\n const supervisorLaunch = await prepareSupervisorForRelaunch(normalizedId, agentState, effectiveModel, effectiveHarness);\n saveAgentStateSync(agentState);\n\n const { launcherContent, providerEnv } = await buildAgentLaunchConfig({\n agentId: normalizedId,\n model: effectiveModel,\n workspace: agentState.workspace,\n role: agentState.role,\n isPlanning: agentState.role === 'plan',\n harness: effectiveHarness,\n harnessBinaryPath: harnessLaunch.binaryPath,\n useSupervisor: supervisorLaunch.useSupervisor,\n supervisorScriptPath: supervisorLaunch.supervisorScriptPath,\n extraEnvExports: [harnessLaunch.pathExport],\n });\n\n const launcherScript = join(getAgentDir(normalizedId), 'launcher.sh');\n await writeLauncherScriptAtomic(launcherScript, launcherContent);\n const claudeCmd = `bash ${launcherScript}`;\n\n // PAN-1837: restartAgent always kills and fresh-launches (no resumeSessionId\n // above), so a kimi-code relaunch always starts a brand-new Kimi session —\n // snapshot the bucket before the tmux session exists so the capture below\n // can diff against it (mirrors spawnAgent's fresh-launch capture in\n // spawn.ts).\n //\n // PAN-1837 review fix: clear the stale kimi-session-id pointer BEFORE the\n // fresh launch, so a failed/timed-out capture leaves NO pointer\n // (findKimiWirePath's safe newest-session-by-mtime fallback) rather than a\n // WRONG pointer still pinned to the pre-restart transcript. Review cycle 6:\n // snapshot, createSession, and capture/persist all run inside\n // withKimiSessionCaptureLock — merely awaiting the capture (the cycle-5\n // fix) is not enough on its own, since it only proves *some* new same-cwd\n // directory appeared, not that it's THIS relaunch's. Only the per-\n // workDirKey mutex, held across the whole span, stops a concurrent\n // same-cwd Kimi launch (another work agent, a conversation, or a recovery)\n // from claiming this session or vice versa.\n const launchAndCaptureKimiSession = async (): Promise<void> => {\n let kimiExistingSessionsBefore: Set<string> | undefined;\n if (effectiveHarness === 'kimi-code') {\n try { unlinkSync(join(getAgentDir(normalizedId), 'kimi-session-id')); } catch { /* absent or already cleared */ }\n try {\n const { kimiSessionsRoot } = await import('../runtimes/kimi-code.js');\n kimiExistingSessionsBefore = new Set(await readdirAsync(kimiSessionsRoot(join(homedir(), '.kimi-code'), agentState.workspace)));\n } catch {\n kimiExistingSessionsBefore = new Set();\n }\n }\n\n await Effect.runPromise(createSession(normalizedId, agentState.workspace, claudeCmd, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n TERM: 'xterm-256color',\n OVERDECK_AGENT_ID: normalizedId,\n OVERDECK_ISSUE_ID: agentState.issueId || '',\n OVERDECK_SESSION_TYPE: agentState.role,\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false',\n GIT_SEQUENCE_EDITOR: 'false',\n ...providerEnv,\n },\n }));\n\n if (kimiExistingSessionsBefore) {\n const { waitForNewKimiSessionAsync, writeKimiSessionId } = await import('../runtimes/kimi-code.js');\n const sessionId = await waitForNewKimiSessionAsync(\n join(homedir(), '.kimi-code'),\n agentState.workspace,\n kimiExistingSessionsBefore,\n );\n if (!sessionId) {\n throw new Error(\n `kimi-code session capture timed out after fresh relaunch for ${normalizedId} — no new session directory appeared under the workspace bucket`,\n );\n }\n writeKimiSessionId(normalizedId, sessionId);\n }\n };\n\n if (effectiveHarness === 'kimi-code') {\n const { withKimiSessionCaptureLock } = await import('../runtimes/kimi-code.js');\n await withKimiSessionCaptureLock(join(homedir(), '.kimi-code'), agentState.workspace, launchAndCaptureKimiSession);\n } else {\n await launchAndCaptureKimiSession();\n }\n\n // PAN-2974 (root cause B): the fallback continue-prompt is phase-aware —\n // a handed-off agent (completed marker) gets a passive restore, not a\n // \"pick up where you left off\" that re-drives the pipeline.\n const prompt = message || buildResumeContinueMessage(agentState);\n if (effectiveHarness === 'ohmypi') {\n // ohmypi does not fire the Claude SessionStart hook and does not read tmux\n // input — wait for ready.json and write the continue prompt through the\n // FIFO JSONL protocol.\n try {\n await writeOhmypiAgentPrompt(normalizedId, prompt);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[restartAgent] ohmypi prompt delivery failed for ${normalizedId}: ${msg}`);\n }\n } else {\n const timeout = getHarnessBehavior(effectiveHarness).readyTimeoutSeconds;\n const ready = await waitForPromptReady(normalizedId, effectiveHarness, timeout);\n if (!ready) {\n throw new Error(`${getHarnessBehavior(effectiveHarness).displayName} did not become ready within ${timeout}s for ${normalizedId}`);\n }\n await new Promise(r => setTimeout(r, 500));\n if (effectiveHarness === 'codex' || effectiveHarness === 'acp' || effectiveHarness === 'kimi-code' || effectiveHarness === 'opencode' || effectiveHarness === 'muse') {\n // PAN-1837: kimi-code's deliveryKind is pty-supervisor, same as codex/acp —\n // it must not fall through to the legacy sync sendKeys() branch below,\n // which bypasses the supervisor cascade entirely.\n const delivery = await deliverAgentMessage(\n normalizedId,\n prompt,\n 'restartAgent:continue-prompt',\n effectiveHarness === 'codex' ? resilientDeliveryMethod(agentState.deliveryMethod) : undefined,\n );\n if (!delivery.ok) {\n throw new Error(`${getHarnessBehavior(effectiveHarness).displayName} continue prompt delivery failed`);\n }\n } else {\n await Effect.runPromise(sendKeys(normalizedId, prompt));\n }\n }\n\n markAgentRunning(agentState);\n saveAgentStateSync(agentState);\n\n await saveAgentRuntimeState(normalizedId, {\n state: 'active',\n lastActivity: new Date().toISOString(),\n });\n\n logLifecycle(normalizedId, `restartAgent SUCCESS: model=${effectiveModel}`);\n return { success: true };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n await Effect.runPromise(stopAgent(normalizedId)).catch(() => undefined);\n logLifecycle(normalizedId, `restartAgent FAILED: ${msg}`);\n return { success: false, error: `Failed to restart agent: ${msg}` };\n }\n}\n\n/**\n * Check whether a tmux session has an active agent runtime.\n * A session may exist with only a bare bash shell after Claude exits.\n */\nasync function hasAgentRuntimeInSession(sessionName: string, harness: RuntimeName): Promise<boolean> {\n try {\n const panePids = await Effect.runPromise(listPaneValues(sessionName, '#{pane_pid}'));\n if (panePids.length === 0) return false;\n return hasAgentRuntimeInSubtree(panePids[0]!, harness);\n } catch {\n return false;\n }\n}\n\n/**\n * Detect crashed agents (state shows running but tmux session is gone)\n */\nexport function detectCrashedAgents(): AgentState[] {\n const agents = listRunningAgentsSync();\n return agents.filter(\n (agent) => agent.status === 'running' && !agent.tmuxActive\n );\n}\n\n/**\n * Recover a crashed agent by restarting it with context\n */\nexport async function recoverAgent(\n agentId: string,\n opts: { modelOverride?: string; force?: boolean } = {},\n): Promise<RecoverAgentResult | null> {\n const normalizedId = normalizeAgentId(agentId);\n logAgentLifecycleSync(normalizedId, 'recoverAgent called');\n const state = getAgentStateSync(normalizedId);\n\n if (!state) {\n logAgentLifecycleSync(normalizedId, 'recoverAgent BLOCKED: no state.json');\n return null;\n }\n\n // Runtime state files may lack required fields (PAN-150)\n if (!state.id) state.id = normalizedId;\n const gateDecision = decideResumeGate(getAgentResumeGateBlockReason(state), 'operator-start');\n if (gateDecision.decision === 'block') {\n logAgentLifecycleSync(normalizedId, `recoverAgent BLOCKED: Cannot recover ${normalizedId}: ${gateDecision.reason}. Clear the gate before recovering.`);\n return null;\n }\n if (!opts.force) {\n const pendingDecision = await detectPendingOperatorDecision(normalizedId);\n if (pendingDecision) {\n logAgentLifecycleSync(normalizedId, `recoverAgent BLOCKED: pending operator decision (${pendingDecision.reason})`);\n return null;\n }\n }\n const modelOverride = normalizeModelOverrideSync(opts.modelOverride);\n if (modelOverride) {\n state.model = modelOverride;\n logAgentLifecycleSync(normalizedId, `recoverAgent: model overridden → ${modelOverride}`);\n }\n if (!state.workspace || !state.model) {\n const reason = `[agents] Cannot recover ${normalizedId}: state.json missing workspace or model`;\n console.error(reason);\n logAgentLifecycleSync(normalizedId, `recoverAgent BLOCKED: ${reason}`);\n return null;\n }\n\n const recoveryRole: Role = state.role\n ?? (normalizedId.startsWith('planning-') ? 'plan' : 'work');\n try {\n await assertWorkspaceStackHealthyForSpawn(\n state.issueId || normalizedId.replace(/^agent-/, '').toUpperCase(),\n recoveryRole,\n state.hostOverride === true,\n state.workspace,\n );\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n logAgentLifecycleSync(normalizedId, `recoverAgent BLOCKED: ${reason}`);\n return null;\n }\n\n // Check if already running — session may exist with only a bare shell\n // after Claude exited (zombie session). Kill it and recover.\n if (sessionExistsSync(normalizedId)) {\n const recoveryHarness: RuntimeName = normalizeHarness(state.harness ?? null) ?? 'claude-code';\n if (await hasAgentRuntimeInSession(normalizedId, recoveryHarness)) {\n logAgentLifecycleSync(normalizedId, 'recoverAgent NO_ACTION: live harness runtime is already running');\n return { action: 'already-running', state };\n }\n console.log(`[agents] ${normalizedId} tmux session is a zombie (no ${recoveryHarness} runtime) — killing and recovering`);\n try { killSessionSync(normalizedId); } catch { /* ignore */ }\n }\n\n // Update crash count in health file\n const healthFile = join(getAgentDir(normalizedId), 'health.json');\n let health = { consecutiveFailures: 0, killCount: 0, recoveryCount: 0 };\n if (existsSync(healthFile)) {\n try {\n health = { ...health, ...JSON.parse(readFileSync(healthFile, 'utf-8')) };\n } catch {}\n }\n health.recoveryCount = (health.recoveryCount || 0) + 1;\n writeFileSync(healthFile, JSON.stringify(health, null, 2));\n\n // Build recovery prompt\n const recoveryPrompt = generateRecoveryPrompt(state);\n\n // Get provider env for the agent's model (reads latest API key from settings)\n const recoveryHarness: RuntimeName = normalizeHarness(state.harness ?? null) ?? 'claude-code';\n const providerEnv = state.model ? await getProviderEnvForModel(state.model, recoveryHarness) : {};\n\n // For credential-file providers, ensure apiKeyHelper is configured.\n // For all other providers, clear stale apiKeyHelper from previous runs.\n if (state.model) {\n const provider = getProviderForModelSync(state.model as ModelId);\n if (provider.authType === 'credential-file') {\n setupCredentialFileAuthSync(provider, state.workspace);\n } else {\n clearCredentialFileAuthSync(state.workspace);\n }\n }\n\n // Restart the agent with recovery context. PAN-1048 C4: derive the role from\n // the saved AgentState (or the session-id heuristic for legacy planning-* IDs)\n // and route through getRoleRuntimeBaseCommand so review/test/ship don't get\n // resurrected as work agents.\n const harnessLaunch = await prepareHarnessLaunch(recoveryHarness);\n const recoverySupervisorLaunch = await prepareSupervisorForRelaunch(normalizedId, state, state.model, recoveryHarness);\n saveAgentStateSync(state);\n\n if (recoveryHarness === 'ohmypi') {\n // PAN-1055: ohmypi cannot consume the recovery prompt as a positional shell\n // argument the way the Claude direct command path does — ohmypi reads JSONL\n // commands from its FIFO. Build a real ohmypi launcher (extension path,\n // --session-dir, FIFO redirect) via buildAgentLaunchConfig, then deliver\n // the recovery prompt through the FIFO once omp reports ready.\n const { launcherContent, providerEnv: piProviderEnv } = await buildAgentLaunchConfig({\n agentId: normalizedId,\n model: state.model,\n workspace: state.workspace,\n role: recoveryRole,\n isPlanning: recoveryRole === 'plan',\n harness: 'ohmypi',\n harnessBinaryPath: harnessLaunch.binaryPath,\n extraEnvExports: [harnessLaunch.pathExport],\n });\n const launcherScript = join(getAgentDir(normalizedId), 'launcher.sh');\n await writeLauncherScriptAtomic(launcherScript, launcherContent);\n await Effect.runPromise(createSession(normalizedId, state.workspace, `bash ${launcherScript}`, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n OVERDECK_AGENT_ID: normalizedId,\n OVERDECK_ISSUE_ID: state.issueId || '',\n OVERDECK_SESSION_TYPE: recoveryRole,\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false',\n ...piProviderEnv,\n },\n }));\n try {\n await writeOhmypiAgentPrompt(normalizedId, recoveryPrompt);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[recoverAgent] ohmypi recovery prompt delivery failed for ${normalizedId}: ${msg}`);\n }\n markAgentRunning(state);\n saveAgentStateSync(state);\n logAgentLifecycleSync(normalizedId, `recoverAgent SUCCESS: recoveryCount=${health.recoveryCount} (ohmypi)`);\n return { action: 'respawned', state };\n }\n\n if (recoveryHarness === 'acp' || recoveryHarness === 'opencode' || recoveryHarness === 'muse') {\n const resumeSessionId = resolveRecoveryResumeSessionId(normalizedId, recoveryHarness);\n const { launcherContent, providerEnv: acpProviderEnv } = await buildAgentLaunchConfig({\n agentId: normalizedId,\n model: state.model,\n workspace: state.workspace,\n role: recoveryRole,\n isPlanning: recoveryRole === 'plan',\n ...(resumeSessionId ? { spawnMode: 'resume' as const, resumeSessionId } : {}),\n harness: recoveryHarness,\n useSupervisor: recoverySupervisorLaunch.useSupervisor,\n supervisorScriptPath: recoverySupervisorLaunch.supervisorScriptPath,\n harnessBinaryPath: harnessLaunch.binaryPath,\n extraEnvExports: [harnessLaunch.pathExport],\n });\n const launcherScript = join(getAgentDir(normalizedId), 'launcher.sh');\n await writeLauncherScriptAtomic(launcherScript, launcherContent);\n await Effect.runPromise(createSession(normalizedId, state.workspace, `bash ${launcherScript}`, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n OVERDECK_AGENT_ID: normalizedId,\n OVERDECK_ISSUE_ID: state.issueId || '',\n OVERDECK_SESSION_TYPE: recoveryRole,\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false',\n ...acpProviderEnv,\n },\n }));\n if (recoveryHarness === 'muse' && !await waitForPromptReady(normalizedId, recoveryHarness, getHarnessBehavior(recoveryHarness).readyTimeoutSeconds)) {\n await Effect.runPromise(stopAgent(normalizedId));\n throw new Error(`Muse recovery readiness timed out for ${normalizedId}`);\n }\n const delivery = await deliverInitialPromptWithRetry(\n normalizedId,\n recoveryPrompt,\n `recoverAgent:${recoveryHarness}-recovery-prompt`,\n );\n if (!delivery.ok) {\n await Effect.runPromise(stopAgent(normalizedId));\n throw new Error(\n `${getHarnessBehavior(recoveryHarness).displayName} recovery prompt delivery failed for ${normalizedId}: ${delivery.failure ?? 'unknown failure'}`,\n );\n }\n markAgentRunning(state);\n saveAgentStateSync(state);\n logAgentLifecycleSync(normalizedId, `recoverAgent SUCCESS: recoveryCount=${health.recoveryCount} (${recoveryHarness})`);\n return { action: 'respawned', state };\n }\n\n if (recoveryHarness === 'kimi-code') {\n // PAN-1837: kimi-code has no launcher-writable session.id — its resume id\n // comes from resolveRecoveryResumeSessionId (kimi-session-newest source)\n // and buildAgentLaunchConfig threads kimiCodeLauncherFields (model/yolo)\n // that buildKimiCodeCommand() requires; the generic default branch below\n // never sets those and would throw \"kimi-code launcher requires kimiCodeModel\".\n const resumeSessionId = resolveRecoveryResumeSessionId(normalizedId, recoveryHarness);\n const { launcherContent, providerEnv: kimiProviderEnv } = await buildAgentLaunchConfig({\n agentId: normalizedId,\n model: state.model,\n workspace: state.workspace,\n role: recoveryRole,\n isPlanning: recoveryRole === 'plan',\n ...(resumeSessionId ? { spawnMode: 'resume' as const, resumeSessionId } : {}),\n harness: 'kimi-code',\n harnessBinaryPath: harnessLaunch.binaryPath,\n extraEnvExports: [harnessLaunch.pathExport],\n });\n const launcherScript = join(getAgentDir(normalizedId), 'launcher.sh');\n await writeLauncherScriptAtomic(launcherScript, launcherContent);\n\n // PAN-1837 review fix: snapshot, createSession, and capture/persist all run\n // inside withKimiSessionCaptureLock (review cycle 6) — a fire-and-forget or\n // merely-awaited capture outside the per-workDirKey mutex only proves *some*\n // new same-cwd directory appeared, not that it's THIS recovery's, so a\n // concurrent same-cwd Kimi launch (a work agent, a restart, or a\n // conversation) could otherwise claim this session or vice versa. When\n // there is no captured session id to resume, this recovery is a fresh\n // Kimi launch — snapshot the workspace's session bucket BEFORE the tmux\n // session exists so the capture below can diff against it and persist the\n // new session id for the NEXT recovery.\n const launchAndCaptureKimiSession = async (): Promise<void> => {\n let kimiExistingSessionsBefore: Set<string> | undefined;\n if (!resumeSessionId) {\n try {\n const { kimiSessionsRoot } = await import('../runtimes/kimi-code.js');\n kimiExistingSessionsBefore = new Set(await readdirAsync(kimiSessionsRoot(join(homedir(), '.kimi-code'), state.workspace)));\n } catch {\n kimiExistingSessionsBefore = new Set();\n }\n }\n\n await Effect.runPromise(createSession(normalizedId, state.workspace, `bash ${launcherScript}`, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n OVERDECK_AGENT_ID: normalizedId,\n OVERDECK_ISSUE_ID: state.issueId || '',\n OVERDECK_SESSION_TYPE: recoveryRole,\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false',\n ...kimiProviderEnv,\n },\n }));\n\n if (kimiExistingSessionsBefore) {\n const { waitForNewKimiSessionAsync, writeKimiSessionId } = await import('../runtimes/kimi-code.js');\n const sessionId = await waitForNewKimiSessionAsync(\n join(homedir(), '.kimi-code'),\n state.workspace,\n kimiExistingSessionsBefore,\n );\n if (sessionId) {\n writeKimiSessionId(normalizedId, sessionId);\n } else {\n // PAN-1837 review fix: fail closed like restartAgent/spawnAgent — a\n // missing capture would otherwise leave a running, unowned Kimi\n // session whose transcript lookup falls back to\n // newest-session-by-mtime, which cannot establish ownership in a\n // shared cwd bucket and can display a different session's\n // transcript/cost under this agent.\n throw new Error(\n `kimi-code session capture timed out after fresh relaunch for ${normalizedId} — no new session directory appeared under the workspace bucket`,\n );\n }\n }\n };\n\n const { withKimiSessionCaptureLock } = await import('../runtimes/kimi-code.js');\n try {\n await withKimiSessionCaptureLock(join(homedir(), '.kimi-code'), state.workspace, launchAndCaptureKimiSession);\n } catch (err) {\n await Effect.runPromise(stopAgent(normalizedId)).catch(() => undefined);\n throw err;\n }\n\n const delivery = await deliverInitialPromptWithRetry(\n normalizedId,\n recoveryPrompt,\n 'recoverAgent:kimi-code-recovery-prompt',\n );\n if (!delivery.ok) {\n await Effect.runPromise(stopAgent(normalizedId));\n throw new Error(\n `Kimi Code recovery prompt delivery failed for ${normalizedId}: ${delivery.failure ?? 'unknown failure'}`,\n );\n }\n markAgentRunning(state);\n saveAgentStateSync(state);\n logAgentLifecycleSync(normalizedId, `recoverAgent SUCCESS: recoveryCount=${health.recoveryCount} (kimi-code)`);\n return { action: 'respawned', state };\n }\n\n const recoveryCodexFields = recoveryHarness === 'codex'\n ? getCodexLauncherFields(normalizedId, state.model, state.workspace, recoveryRole)\n : {};\n const recoveryLauncherContent = generateLauncherScriptSync({\n role: recoveryRole,\n workingDir: state.workspace,\n changeDir: false,\n setTerminalEnv: true,\n providerExports: (await getProviderExportsForModel(state.model, recoveryHarness)).trimEnd(),\n extraEnvExports: [harnessLaunch.pathExport],\n baseCommand: await getRoleRuntimeBaseCommand(state.model, normalizedId, recoveryRole, recoveryHarness),\n appendSystemPromptFiles: await claudeSystemPromptFiles(state.workspace, recoveryHarness),\n ...(recoveryHarness === 'codex' ? {} : { promptInline: recoveryPrompt }),\n resumeSessionId: resolveRecoveryResumeSessionId(normalizedId, recoveryHarness),\n useSupervisor: recoverySupervisorLaunch.useSupervisor,\n supervisorScriptPath: recoverySupervisorLaunch.supervisorScriptPath,\n ...recoveryCodexFields,\n });\n const launcherScript = join(getAgentDir(normalizedId), 'launcher.sh');\n await writeLauncherScriptAtomic(launcherScript, recoveryLauncherContent);\n createSessionSync(normalizedId, state.workspace, `bash ${launcherScript}`, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n OVERDECK_AGENT_ID: normalizedId,\n OVERDECK_ISSUE_ID: state.issueId || '',\n OVERDECK_SESSION_TYPE: state.role ?? (normalizedId.startsWith('planning-') ? 'plan' : 'work'),\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: 'false',\n ...providerEnv\n }\n });\n\n saveAgentStateSync(state);\n if (recoveryHarness === 'codex') {\n const delivery = await deliverInitialPromptWithRetry(normalizedId, recoveryPrompt, 'recoverAgent:recovery-prompt', state.deliveryMethod);\n if (!delivery.ok) {\n console.error(`[recoverAgent] Codex recovery prompt delivery failed for ${normalizedId}: ${delivery.failure ?? 'unknown failure'}`);\n }\n }\n // Update state\n markAgentRunning(state);\n saveAgentStateSync(state);\n\n logAgentLifecycleSync(normalizedId, `recoverAgent SUCCESS: recoveryCount=${health.recoveryCount}`);\n return { action: 'respawned', state };\n}\n\n/**\n * Generate a recovery prompt for a crashed agent\n */\nfunction generateRecoveryPrompt(state: AgentState): string {\n const lines: string[] = [\n '# Agent Recovery',\n '',\n '⚠️ This agent session was recovered after a crash.',\n '',\n '## Previous Context',\n `- Issue: ${state.issueId}`,\n `- Workspace: ${state.workspace}`,\n `- Started: ${state.startedAt}`,\n '',\n '## Recovery Steps',\n '1. Check the xBRIEF task state: `pan task show ' + state.issueId + ' <item-id>`',\n '2. Review recent git commits: `git log --oneline -10`',\n '3. Check hook for pending work: `pan admin fpp check`',\n '4. Resume from last known state',\n '',\n '## FPP Reminder',\n '> \"Any runnable action is a fixed point and must resolve before the system can rest.\"',\n '',\n ];\n\n // Add FPP work if available\n const { hasWork } = checkHookSync(state.id);\n if (hasWork) {\n const fixedPointPrompt = generateFixedPointPromptSync(state.id);\n if (fixedPointPrompt) {\n lines.push('---');\n lines.push('');\n lines.push(fixedPointPrompt);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Auto-recover all crashed agents\n */\nexport async function autoRecoverAgents(): Promise<{ recovered: string[]; failed: string[] }> {\n const crashed = detectCrashedAgents();\n const recovered: string[] = [];\n const failed: string[] = [];\n\n for (const agent of crashed) {\n try {\n const result = await recoverAgent(agent.id);\n if (result?.action === 'respawned') {\n recovered.push(agent.id);\n } else {\n failed.push(agent.id);\n }\n } catch (error) {\n failed.push(agent.id);\n }\n }\n\n return { recovered, failed };\n}\n","import { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { readIssueRecordForWorkspaceSync } from '../pan-dir/record.js';\nimport { findProjectByPathSync, getProjectSwarmHotspots } from '../projects.js';\nimport { analyzeSwarmReadiness } from '../xbrief/swarm-readiness.js';\nimport type { XBriefDocument } from '../xbrief/types.js';\nimport { listAgentStates } from './queries.js';\nimport type { AgentState } from './agent-state.js';\nimport { RETAINED_TRANSCRIPTS_PHASE } from '../overdeck/agents.js';\nimport type { PanIssueSwarmSlotCompletion } from '../pan-dir/record.js';\n\nconst execAsync = promisify(exec);\n\nexport type ReconciledSlotItemStatus = 'merged' | 'in_flight' | 'pending';\n\nexport interface ReconciledSlotBranch {\n slotIndex: number;\n branch: string;\n merged: boolean;\n}\n\nexport interface ReconciledSlotAgent {\n slotIndex: number;\n agentId: string;\n status: AgentState['status'];\n slotItemId?: string;\n}\n\nexport interface ReconciledSlotAssignment {\n slotIndex: number;\n itemId: string;\n agentId?: string;\n branch?: string;\n}\n\nexport interface ReconciledSlotItem {\n itemId: string;\n slotIndex: number;\n status: ReconciledSlotItemStatus;\n branch?: string;\n agentId?: string;\n mergedVia?: 'completed-status' | 'branch-ancestry';\n}\n\nexport interface SlotReconcileResult {\n issueId: string;\n merged: ReconciledSlotItem[];\n inFlight: ReconciledSlotItem[];\n pending: ReconciledSlotItem[];\n branches: ReconciledSlotBranch[];\n agents: ReconciledSlotAgent[];\n superseded?: NonNullable<NonNullable<ReturnType<typeof readIssueRecordForWorkspaceSync>>['swarm']>['supersededAttempts'];\n}\n\nexport interface SlotReconcileDeps {\n listBranches: (issueId: string, workspace: string) => Promise<ReconciledSlotBranch[]>;\n listAgents: (issueId: string) => ReconciledSlotAgent[];\n listSlotAssignments: (issueId: string, workspace: string) => ReconciledSlotAssignment[];\n listSlotCompletions: (issueId: string, workspace: string) => Record<string, PanIssueSwarmSlotCompletion>;\n}\n\nexport interface SlotReconcileOptions {\n statusOverrides?: Record<string, string>;\n deps?: Partial<SlotReconcileDeps>;\n}\n\nexport async function reconcileSlotState(\n issueId: string,\n workspace: string,\n doc: XBriefDocument,\n options: SlotReconcileOptions = {},\n): Promise<SlotReconcileResult> {\n const deps: SlotReconcileDeps = {\n listBranches: listSlotBranches,\n listAgents: listSlotAgents,\n listSlotAssignments,\n listSlotCompletions,\n ...options.deps,\n };\n const branches = await deps.listBranches(issueId, workspace);\n const agents = deps.listAgents(issueId);\n const assignments = deps.listSlotAssignments(issueId, workspace);\n const completions = deps.listSlotCompletions(issueId, workspace);\n const releasedSlotIndexes = new Set(Object.keys(\n readIssueRecordForWorkspaceSync(workspace, issueId.toUpperCase())?.swarm?.releasedBlockedSlots ?? {},\n ).map(Number));\n const branchesBySlot = new Map(branches.map(branch => [branch.slotIndex, branch]));\n const agentsBySlot = new Map(agents.map(agent => [agent.slotIndex, agent]));\n const hotspots = getProjectSwarmHotspots(findProjectByPathSync(workspace));\n const slotEligibleItemIds = new Set(analyzeSwarmReadiness(doc, { hotspots }).items\n .filter(item => item.slotEligible)\n .map(item => item.id));\n const itemStatuses = new Map(doc.plan.items.map(item => [item.id, item.status]));\n const slotItems = resolveSlotItemOwnership(slotEligibleItemIds, assignments, agents, releasedSlotIndexes);\n\n const result: SlotReconcileResult = {\n issueId,\n merged: [],\n inFlight: [],\n pending: [],\n branches,\n agents,\n superseded: readIssueRecordForWorkspaceSync(workspace, issueId.toUpperCase())?.swarm?.supersededAttempts ?? [],\n };\n\n for (const slotItem of slotItems) {\n const branch = branchesBySlot.get(slotItem.slotIndex);\n const agent = agentsBySlot.get(slotItem.slotIndex);\n const completed = options.statusOverrides?.[slotItem.itemId] === 'completed'\n || itemStatuses.get(slotItem.itemId) === 'completed';\n // Branch ancestry is only supporting evidence. A polyrepo workspace root\n // can be unchanged while its member repositories still contain live slot\n // work, which makes the wrapper slot branch appear merged immediately.\n // A slot completion marker means the worker is done but the merge door has\n // not consumed the branch yet. mergeReadySlots clears that marker only\n // after verifyAndMergeSlot succeeds, then records the item as completed.\n const awaitingMerge = completions[String(slotItem.slotIndex)]?.itemId === slotItem.itemId;\n const merged = completed && !awaitingMerge;\n const entry: ReconciledSlotItem = {\n ...slotItem,\n status: merged ? 'merged' : agent || branch ? 'in_flight' : 'pending',\n branch: branch?.branch,\n agentId: agent?.agentId,\n ...(merged ? { mergedVia: 'completed-status' as const } : {}),\n };\n\n if (entry.status === 'merged') result.merged.push(entry);\n else if (entry.status === 'in_flight') result.inFlight.push(entry);\n else result.pending.push(entry);\n }\n\n return result;\n}\n\nexport async function listSlotBranches(issueId: string, workspace: string): Promise<ReconciledSlotBranch[]> {\n const issueLower = issueId.toLowerCase();\n const pattern = `feature/${issueLower}-slot-*`;\n const [allBranches, mergedBranches] = await Promise.all([\n gitBranchNames(workspace, pattern, false),\n gitBranchNames(workspace, pattern, true),\n ]);\n const mergedSet = new Set(mergedBranches);\n return allBranches\n .map(branch => ({ branch, slotIndex: slotIndexFromBranch(issueLower, branch), merged: mergedSet.has(branch) }))\n .filter((branch): branch is ReconciledSlotBranch => branch.slotIndex !== null)\n .sort((a, b) => a.slotIndex - b.slotIndex);\n}\n\nexport function listSlotAgents(issueId: string): ReconciledSlotAgent[] {\n const issueLower = issueId.toLowerCase();\n const pattern = new RegExp(`^agent-${escapeRegExp(issueLower)}-slot-(\\\\d+)$`);\n return listAgentStates({ role: 'work' })\n .map(agent => {\n // PAN-3465: tombstoned rows (removeAgent keeps them for transcript\n // linkage) are not live slot occupants — counting them wedged dispatch\n // with \"all slot indexes occupied\" after a swarm reset.\n if (agent.phase === RETAINED_TRANSCRIPTS_PHASE) return null;\n const match = pattern.exec(agent.id);\n if (!match) return null;\n const entry: ReconciledSlotAgent = {\n slotIndex: Number(match[1]),\n agentId: agent.id,\n status: agent.status,\n };\n if (agent.slotItemId) entry.slotItemId = agent.slotItemId;\n return entry;\n })\n .filter((agent): agent is ReconciledSlotAgent => agent !== null)\n .sort((a, b) => a.slotIndex - b.slotIndex);\n}\n\nexport function listSlotAssignments(issueId: string, workspace: string): ReconciledSlotAssignment[] {\n const record = readIssueRecordForWorkspaceSync(workspace, issueId.toUpperCase());\n return (record?.swarm?.slotAssignments ?? [])\n .filter(assignment => Number.isInteger(assignment.slotIndex) && assignment.slotIndex > 0 && assignment.itemId.trim().length > 0)\n .map(assignment => ({\n slotIndex: assignment.slotIndex,\n itemId: assignment.itemId,\n agentId: assignment.agentId,\n branch: assignment.branch,\n }))\n .sort((a, b) => a.slotIndex - b.slotIndex);\n}\n\nexport function listSlotCompletions(issueId: string, workspace: string): Record<string, PanIssueSwarmSlotCompletion> {\n return readIssueRecordForWorkspaceSync(workspace, issueId.toUpperCase())?.swarm?.slotCompletions ?? {};\n}\n\nexport function listSlotOwnership(issueId: string, workspace: string): ReconciledSlotAssignment[] {\n const byItemId = new Map<string, ReconciledSlotAssignment>();\n for (const assignment of listSlotAssignments(issueId, workspace)) {\n byItemId.set(assignment.itemId, assignment);\n }\n for (const agent of listSlotAgents(issueId)) {\n if (!agent.slotItemId || byItemId.has(agent.slotItemId)) continue;\n byItemId.set(agent.slotItemId, {\n slotIndex: agent.slotIndex,\n itemId: agent.slotItemId,\n agentId: agent.agentId,\n });\n }\n return [...byItemId.values()].sort((a, b) => a.slotIndex - b.slotIndex);\n}\n\nasync function gitBranchNames(workspace: string, pattern: string, merged: boolean): Promise<string[]> {\n const { stdout } = await execAsync(\n `git branch ${merged ? '--merged HEAD ' : ''}--list ${JSON.stringify(pattern)}`,\n { cwd: workspace },\n );\n return stdout\n .split('\\n')\n .map(line => line.replace(/^[*+\\s]+/, '').trim())\n .filter(Boolean);\n}\n\nfunction resolveSlotItemOwnership(\n slotEligibleItemIds: Set<string>,\n assignments: ReconciledSlotAssignment[],\n agents: ReconciledSlotAgent[],\n releasedSlotIndexes: Set<number>,\n): Array<{ itemId: string; slotIndex: number }> {\n const ownership = new Map<string, number>();\n\n for (const assignment of assignments) {\n if (!slotEligibleItemIds.has(assignment.itemId)) continue;\n ownership.set(assignment.itemId, assignment.slotIndex);\n }\n\n for (const agent of agents) {\n if (agent.status === 'stopped' && releasedSlotIndexes.has(agent.slotIndex)) continue;\n if (!agent.slotItemId || !slotEligibleItemIds.has(agent.slotItemId) || ownership.has(agent.slotItemId)) continue;\n ownership.set(agent.slotItemId, agent.slotIndex);\n }\n\n return [...ownership.entries()]\n .map(([itemId, slotIndex]) => ({ itemId, slotIndex }))\n .sort((a, b) => a.slotIndex - b.slotIndex);\n}\n\nfunction slotIndexFromBranch(issueLower: string, branch: string): number | null {\n const match = new RegExp(`^feature/${escapeRegExp(issueLower)}-slot-(\\\\d+)(?:-attempt-\\\\d+)?$`).exec(branch);\n if (!match) return null;\n return Number(match[1]);\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","/**\n * ⚠️ RETIRED SCHEDULER (PAN-2397 W3, 2026-07-05) — do not add production callers.\n *\n * StandingTierManager / computeTierRunSchedule shipped with PAN-1791 as a\n * second scheduler alongside the swarm slot allocator and never gained a\n * production caller. The Always Tiered unification made the swarm allocator\n * (deacon-swarm.ts dispatchNextWave + resolveStaffing) THE scheduler.\n *\n * No-loss audit of this module's capabilities at retirement:\n * - lazy per-tier spawn → swarm dispatch waves already spawn lazily\n * - per-bead tier staffing → resolveStaffing (staffing.ts), all spawn paths\n * - cross-bead session reuse → INAPPLICABLE under the swarm's\n * branch-per-item slots (a slot branch carries one bead, merges, is GC'd);\n * adopting it would require a branch-per-tier architecture — a separate,\n * deliberate epic decision, recorded in .pan/drafts/pan-2397.md.\n * - replay on context threshold → tier-replay.ts remains live; its production\n * seam is the slot-recovery/respawn path (PAN-2397 W3b follow-up).\n *\n * computeTierRunSchedule is kept as a pure planning helper (tests cover it);\n * the manager below is test-only until deleted.\n *\n * Original module doc:\n * Standing tier agents for tiered execution (PAN-1791, FR-6 scheduling half).\n *\n * A standing tier agent is a long-lived registered slot — spawned through\n * spawnRun(issueId, 'work', { slotIndex, slotItemId }) exactly like any other\n * registered slot agent (state file + tmux session + dashboard discovery\n * under the issue) — that persists across the many beads of its tier instead\n * of living per-bead. No new agent kind is introduced.\n *\n * The schedule is the plan-time tier-run sequence (PRD §5.6): waves are\n * linearized via groupItemsByWave, each item is annotated with its tier via\n * resolveTier, and consecutive same-tier items are cut into runs at\n * tier-change boundaries. Standing sessions are instantiated lazily — a\n * tier's session spawns when its first run is <=1 run away, not all at\n * start — so a cancelled run never strands sessions it would not have used.\n */\n\nimport type { XBriefDocument, XBriefItem } from '../xbrief/types.js';\nimport { groupItemsByWave } from '../xbrief/dag.js';\nimport { resolveTier, type ResolveTierConfig } from './resolve-tier.js';\nimport type { AgentState } from './agent-state.js';\nimport type { SpawnRunOptions } from './spawn-prep.js';\nimport { spawnRun } from './spawn.js';\n\n/** One tier run: consecutive beads in DAG order that route to the same tier. */\nexport interface TierRun {\n tierName: string;\n itemIds: string[];\n}\n\nexport class StandingTierError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'StandingTierError';\n }\n}\n\n/**\n * Compute the ordered tier-run schedule from a plan and the tier table.\n * Waves are linearized in order (items within a wave in array order), each\n * item resolves to its tier, and a new run starts whenever the tier changes.\n *\n * Unroutable items (no difficulty, unknown tier) propagate resolveTier's\n * named error — scheduling failures are finalize-time errors in the same\n * family as the quality-lint checks, never silent fallbacks.\n */\nexport function computeTierRunSchedule(\n doc: XBriefDocument,\n config: ResolveTierConfig,\n): TierRun[] {\n const itemById = new Map(doc.plan.items.map((item) => [item.id, item]));\n const runs: TierRun[] = [];\n for (const wave of groupItemsByWave(doc)) {\n for (const waveItem of wave.items) {\n const item = itemById.get(waveItem.id);\n if (!item) continue;\n const tier = resolveTier(item, config);\n const currentRun = runs[runs.length - 1];\n if (currentRun && currentRun.tierName === tier.tierName) {\n currentRun.itemIds.push(item.id);\n } else {\n runs.push({ tierName: tier.tierName, itemIds: [item.id] });\n }\n }\n }\n return runs;\n}\n\n/** The exact set of tier names a schedule contains, in first-appearance order. */\nexport function tiersNeededForSchedule(schedule: TierRun[]): string[] {\n const seen = new Set<string>();\n for (const run of schedule) seen.add(run.tierName);\n return Array.from(seen);\n}\n\n/** A spawned standing tier agent tracked in the registry. */\nexport interface StandingTierAgent {\n tierName: string;\n slotIndex: number;\n agentId: string;\n /** The tier's first scheduled bead — the slotItemId its session was spawned for. */\n firstItemId: string;\n}\n\n/**\n * Spawn seam matching spawnRun's registered-slot signature. Injectable so the\n * manager is unit-testable; defaults to spawnRun.\n */\nexport type StandingTierSpawn = (\n issueId: string,\n role: 'work',\n options: SpawnRunOptions,\n) => Promise<AgentState>;\n\nexport interface StandingTierManagerOptions {\n issueId: string;\n schedule: TierRun[];\n /** Injectable for tests. Defaults to spawnRun's registered-slot path. */\n spawn?: StandingTierSpawn;\n /**\n * Slot index allocated to the first standing tier; subsequent tiers take\n * the following indexes. Defaults to 1.\n */\n firstSlotIndex?: number;\n /** Extra prompt appended to each standing slot's kickoff (foreman protocol). */\n prompt?: string;\n}\n\n/**\n * Manages the set of standing tier agents for one issue: which tiers the\n * schedule needs, lazy spawn, a registry keyed by tier name, and bead\n * routing to the standing slot.\n */\n/** The bead currently being implemented by a standing tier agent. */\nexport interface InFlightBead {\n itemId: string;\n tierName: string;\n agentId: string;\n}\n\nexport class StandingTierManager {\n private readonly registry = new Map<string, StandingTierAgent>();\n private nextSlotIndex: number;\n private inFlight: InFlightBead | undefined;\n\n constructor(private readonly options: StandingTierManagerOptions) {\n this.nextSlotIndex = options.firstSlotIndex ?? 1;\n }\n\n /** Exactly the tier names the schedule contains — no tier outside it ever spawns. */\n tiersNeeded(): string[] {\n return tiersNeededForSchedule(this.options.schedule);\n }\n\n /** The standing agent for a tier, if its session has been spawned. */\n getStandingAgent(tierName: string): StandingTierAgent | undefined {\n return this.registry.get(tierName);\n }\n\n /** 0-based index of the first run belonging to a tier, or -1 when absent. */\n firstRunIndexFor(tierName: string): number {\n return this.options.schedule.findIndex((run) => run.tierName === tierName);\n }\n\n /**\n * Lazy instantiation (PRD §5.6): ensure a standing session exists for every\n * scheduled tier whose first run is <=1 run away from currentRunIndex.\n * Already-spawned tiers are reused, never respawned. Returns the agents\n * spawned by this call.\n */\n async ensureStandingTiersForRun(currentRunIndex: number): Promise<StandingTierAgent[]> {\n const spawned: StandingTierAgent[] = [];\n for (const tierName of this.tiersNeeded()) {\n if (this.registry.has(tierName)) continue;\n const firstRun = this.firstRunIndexFor(tierName);\n if (firstRun <= currentRunIndex + 1) {\n spawned.push(await this.spawnStandingTier(tierName));\n }\n }\n return spawned;\n }\n\n /**\n * Ensure the registered slot for a tier exists, spawning the standing\n * session lazily if it does not exist yet, and return that slot's agent id.\n * Throws for a tier the schedule does not contain — the manager never\n * spawns outside the schedule.\n */\n async ensureStandingAgentForTier(tierName: string, bead: Pick<XBriefItem, 'id'>): Promise<string> {\n if (this.firstRunIndexFor(tierName) === -1) {\n throw new StandingTierError(\n `tier '${tierName}' is not in the schedule for ${this.options.issueId}; refusing to route bead '${bead.id}'`,\n );\n }\n const agent = this.registry.get(tierName) ?? await this.spawnStandingTier(tierName);\n return agent.agentId;\n }\n\n /** The bead currently in flight, if any. */\n getInFlightBead(): InFlightBead | undefined {\n return this.inFlight;\n }\n\n /**\n * Foreman dispatch step enforcing the single-implementer invariant: only\n * one implementation agent works a bead at a time — standing tiers share\n * the foreman's worktree, so two in-flight beads would race the same tree.\n * Throws while another bead is in flight; call completeBead after the\n * foreman has staged, committed, and broadcast the result.\n */\n async dispatchBeadToTier(tierName: string, bead: Pick<XBriefItem, 'id'>): Promise<string> {\n if (this.inFlight) {\n throw new StandingTierError(\n `bead '${this.inFlight.itemId}' is still in flight on tier '${this.inFlight.tierName}' for ${this.options.issueId}; `\n + `only one implementation agent works a bead at a time — complete it before dispatching '${bead.id}'`,\n );\n }\n const agentId = await this.ensureStandingAgentForTier(tierName, bead);\n this.inFlight = { itemId: bead.id, tierName, agentId };\n return agentId;\n }\n\n /** Backward-compatible name for callers that already spell the invariant explicitly. */\n async dispatchBeadExclusive(tierName: string, bead: Pick<XBriefItem, 'id'>): Promise<string> {\n return this.dispatchBeadToTier(tierName, bead);\n }\n\n /** Mark the in-flight bead complete (committed + broadcast), freeing dispatch. */\n completeBead(itemId: string): void {\n if (!this.inFlight || this.inFlight.itemId !== itemId) {\n throw new StandingTierError(\n `bead '${itemId}' is not the in-flight bead for ${this.options.issueId}`\n + `${this.inFlight ? ` ('${this.inFlight.itemId}' is)` : ' (nothing is in flight)'}`,\n );\n }\n this.inFlight = undefined;\n }\n\n private async spawnStandingTier(tierName: string): Promise<StandingTierAgent> {\n const firstRun = this.options.schedule.find((run) => run.tierName === tierName);\n const firstItemId = firstRun?.itemIds[0];\n if (!firstItemId) {\n throw new StandingTierError(\n `tier '${tierName}' has no scheduled beads for ${this.options.issueId}; nothing to spawn a standing session for`,\n );\n }\n const slotIndex = this.nextSlotIndex++;\n const spawn = this.options.spawn ?? spawnRun;\n const state = await spawn(this.options.issueId, 'work', {\n slotIndex,\n slotItemId: firstItemId,\n prompt: this.options.prompt,\n startedBy: 'standing-tier',\n });\n const agent: StandingTierAgent = {\n tierName,\n slotIndex,\n agentId: state.id,\n firstItemId,\n };\n this.registry.set(tierName, agent);\n return agent;\n }\n}\n","import type { TierOverridesMap } from '../xbrief/io.js';\nimport type { XBriefDifficulty, XBriefItem } from '../xbrief/types.js';\nimport type { ValidatedEscalationConfig } from './tier-table.js';\nimport { TIERED_EXECUTION_DIFFICULTIES } from './tier-table.js';\n\nexport type EscalationTrigger =\n | { kind: 'supervisor-blocked'; itemId: string; sha: string; attemptsAtCurrentTier?: number }\n | { kind: 'verification-failed'; itemId: string; detail: string; attemptsAtCurrentTier?: number }\n | { kind: 'floundering'; itemId: string; dispatchedAt: string; now: string; attemptsAtCurrentTier?: number };\n\nexport type EscalationAction =\n | { action: 'retry'; attempt: number }\n | { action: 'promote'; from: XBriefDifficulty; to: XBriefDifficulty; reason: string }\n | { action: 'block'; reason: string };\n\nexport interface VerificationFailureEscalationInput {\n bead: Pick<XBriefItem, 'id' | 'metadata'>;\n config: ValidatedEscalationConfig;\n overrides: TierOverridesMap;\n detail: string;\n attemptsAtCurrentTier?: number;\n}\n\nexport interface FlounderingEscalationInput {\n bead: Pick<XBriefItem, 'id' | 'metadata'>;\n config: ValidatedEscalationConfig;\n overrides: TierOverridesMap;\n dispatchedAt: string;\n now: string;\n attemptsAtCurrentTier?: number;\n}\n\nexport function applyEffectiveDifficulty<T extends Pick<XBriefItem, 'id' | 'metadata'>>(\n item: T,\n overrides: TierOverridesMap,\n): T {\n const override = overrides[item.id];\n if (!override) return item;\n\n return {\n ...item,\n metadata: {\n ...(item.metadata ?? {}),\n difficulty: override.effectiveDifficulty,\n },\n };\n}\n\n/**\n * Pure escalation decision engine. It only emits the next action; callers own\n * retry event persistence, tier promotion writes, dispatch, and notifications.\n */\nexport function decideEscalation(\n trigger: EscalationTrigger,\n bead: Pick<XBriefItem, 'id' | 'metadata'>,\n config: ValidatedEscalationConfig,\n overrides: TierOverridesMap,\n): EscalationAction {\n const itemId = trigger.itemId || bead.id;\n const override = overrides[bead.id];\n const currentDifficulty = override?.effectiveDifficulty ?? bead.metadata?.difficulty;\n if (!currentDifficulty) {\n return { action: 'block', reason: `task ${itemId} has no effective difficulty` };\n }\n\n const promotions = override?.promotions ?? 0;\n if (promotions >= config.max_promotions) {\n return {\n action: 'block',\n reason: `max promotions reached for ${itemId} at ${currentDifficulty}`,\n };\n }\n\n if (currentDifficulty === 'expert') {\n return {\n action: 'block',\n reason: `expert task ${itemId} cannot promote beyond expert`,\n };\n }\n\n const attemptsAtCurrentTier = trigger.attemptsAtCurrentTier ?? 0;\n if (attemptsAtCurrentTier < config.retries_at_tier) {\n return { action: 'retry', attempt: attemptsAtCurrentTier + 1 };\n }\n\n const fromIndex = TIERED_EXECUTION_DIFFICULTIES.indexOf(currentDifficulty);\n const to = TIERED_EXECUTION_DIFFICULTIES[fromIndex + 1];\n if (!to) {\n return {\n action: 'block',\n reason: `task ${itemId} cannot promote beyond ${currentDifficulty}`,\n };\n }\n\n return {\n action: 'promote',\n from: currentDifficulty,\n to,\n reason: triggerReason(trigger),\n };\n}\n\nexport function isFloundering(\n dispatchedAtIso: string,\n nowIso: string,\n budgetMinutes?: number,\n): boolean {\n if (budgetMinutes === undefined || budgetMinutes === null) return false;\n if (!Number.isFinite(budgetMinutes) || budgetMinutes <= 0) return false;\n\n const dispatchedAt = Date.parse(dispatchedAtIso);\n const now = Date.parse(nowIso);\n if (!Number.isFinite(dispatchedAt) || !Number.isFinite(now)) return false;\n\n return (now - dispatchedAt) / 60_000 > budgetMinutes;\n}\n\nexport function decideVerificationFailureEscalation(\n input: VerificationFailureEscalationInput,\n): EscalationAction {\n return decideEscalation({\n kind: 'verification-failed',\n itemId: input.bead.id,\n detail: input.detail,\n attemptsAtCurrentTier: input.attemptsAtCurrentTier,\n }, input.bead, input.config, input.overrides);\n}\n\nexport function decideFlounderingEscalation(\n input: FlounderingEscalationInput,\n): EscalationAction | null {\n const effectiveDifficulty = input.overrides[input.bead.id]?.effectiveDifficulty ?? input.bead.metadata?.difficulty;\n const budget = effectiveDifficulty ? input.config.flounder_budget_minutes[effectiveDifficulty] : undefined;\n if (!isFloundering(input.dispatchedAt, input.now, budget)) return null;\n return decideEscalation({\n kind: 'floundering',\n itemId: input.bead.id,\n dispatchedAt: input.dispatchedAt,\n now: input.now,\n attemptsAtCurrentTier: input.attemptsAtCurrentTier,\n }, input.bead, input.config, input.overrides);\n}\n\nfunction triggerReason(trigger: EscalationTrigger): string {\n switch (trigger.kind) {\n case 'supervisor-blocked':\n return `supervisor blocked commit ${trigger.sha}`;\n case 'verification-failed':\n return `verification failed: ${trigger.detail}`;\n case 'floundering':\n return `floundering since ${trigger.dispatchedAt}`;\n }\n}\n","/**\n * Commit feed for standing tier agents (PAN-1791, FR-7 / D10 v1).\n *\n * After each commit lands, broadcastCommit composes one ingestion-only feed\n * message (bead title + `git show <sha>` diff) and delivers it to EVERY\n * standing tier agent through deliverAgentMessage — the existing delivery\n * primitive, no new transport. v1 is everyone-hears-everything: no per-tier\n * relevance filtering, so any agent's feed is reconstructible from\n * `git log <base>..HEAD` plus the standing-tier set — exactly what replay\n * (tier-replay) consumes to rebuild a respawned session's feed.\n *\n * Feed messages are ingestion-only: the message text instructs the recipient\n * not to respond, so per-event output cost stays ~0 (NFR-1).\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { renderWorkspaceGitShowPromise } from '../git-utils.js';\nimport { deliverAgentMessage, type DeliveryResult } from './delivery.js';\nimport type { StandingTierAgent } from './standing-tiers.js';\nimport { estimateFeedDeliveryTokens, recordTierFeedDelivery } from './tier-metrics.js';\nimport type { ValidatedTieredExecutionFeedConfig } from './tier-table.js';\nimport { DEFAULT_TIERED_EXECUTION_CONFIG } from './tier-table.js';\n\nconst execFileAsync = promisify(execFile);\n\nconst DEFAULT_FEED_CONFIG = DEFAULT_TIERED_EXECUTION_CONFIG.feed;\n\nexport interface BroadcastCommitOptions {\n /** Workspace (or any checkout) whose git history contains the commit. */\n workspace: string;\n /** The landed commit to broadcast. */\n sha: string;\n /** Title of the bead the commit implements, shown in the feed header. */\n itemTitle: string;\n /** Id of the bead the commit implements, required for call-out corroboration. */\n itemId?: string;\n /** Commit subject used by feed.exclude_subjects. Defaults to itemTitle for legacy callers. */\n commitSubject?: string;\n /** The standing tier agents to deliver to — every one of them hears it. */\n tiers: Array<Pick<StandingTierAgent, 'tierName' | 'agentId'>>;\n /** Issue id for delivery metrics. */\n issueId?: string;\n /** Dashboard API base URL used in the listener call-out curl snippet. */\n apiUrl?: string;\n /** Feed filtering/rendering config. Defaults preserve today's raw git-show behavior. */\n feedConfig?: ValidatedTieredExecutionFeedConfig;\n /** Injectable delivery seam for tests. Defaults to deliverAgentMessage. */\n deliver?: (agentId: string, message: string, caller?: string) => Promise<DeliveryResult>;\n /** Legacy injectable `git show` runner for tests. Defaults to the shared renderer. */\n gitShow?: (workspace: string, sha: string) => Promise<string>;\n /** Injectable shared renderer seam for tests. Defaults to renderCommitFeedDiff. */\n renderDiff?: (workspace: string, sha: string, feedConfig: ValidatedTieredExecutionFeedConfig) => Promise<string>;\n /** Injectable metric recorder for tests. Defaults to the tier metrics JSONL log. */\n recordDelivery?: typeof recordTierFeedDelivery;\n /** Injectable clock for deterministic delivery metric tests. */\n now?: () => Date;\n}\n\nexport interface BroadcastDelivery {\n tierName: string;\n agentId: string;\n result: DeliveryResult;\n}\n\nexport interface RenderCommitFeedDiffDeps {\n issueId?: string;\n gitShow?: (workspace: string, sha: string, args: string[]) => Promise<string>;\n}\n\nexport interface CommitFeedCalloutContext {\n apiUrl: string;\n issueId: string;\n itemId: string;\n tierName: string;\n agentId: string;\n}\n\nasync function runGitShow(workspace: string, sha: string, args: string[] = []): Promise<string> {\n const { stdout } = await execFileAsync('git', ['show', sha, ...args], {\n cwd: workspace,\n encoding: 'utf-8',\n maxBuffer: 16 * 1024 * 1024,\n });\n return stdout;\n}\n\nexport function shouldSkipFeedSubject(\n subject: string,\n feedConfig: Pick<ValidatedTieredExecutionFeedConfig, 'exclude_subjects'>,\n): boolean {\n return feedConfig.exclude_subjects.some(prefix => subject.startsWith(prefix));\n}\n\nexport async function renderCommitFeedDiff(\n workspace: string,\n sha: string,\n feedConfig: ValidatedTieredExecutionFeedConfig = DEFAULT_FEED_CONFIG,\n deps: RenderCommitFeedDiffDeps = {},\n): Promise<string> {\n const gitShow = deps.gitShow ?? runGitShow;\n const pathspecArgs = feedConfig.exclude.length > 0\n ? ['--', '.', ...feedConfig.exclude.map(glob => `:(exclude)${glob}`)]\n : [];\n const diff = await renderWorkspaceGitShowPromise(deps.issueId, workspace, sha, pathspecArgs, gitShow);\n const maxBytes = feedConfig.max_diff_bytes;\n if (maxBytes === null || Buffer.byteLength(diff, 'utf-8') <= maxBytes) return diff;\n\n const stat = await renderWorkspaceGitShowPromise(deps.issueId, workspace, sha, ['--stat', ...pathspecArgs], gitShow);\n return [\n stat.trimEnd(),\n '',\n `[tiered_execution.feed.max_diff_bytes truncated commit diff: ${Buffer.byteLength(diff, 'utf-8')} bytes exceeded ${maxBytes} bytes]`,\n '',\n ].join('\\n');\n}\n\nexport function resolveFeedApiUrl(env: NodeJS.ProcessEnv = process.env): string {\n return env.OVERDECK_DASHBOARD_URL\n ?? env.DASHBOARD_URL\n ?? `http://localhost:${env.API_PORT ?? env.PORT ?? '3011'}`;\n}\n\n/**\n * Compose the ingestion-only feed message for one commit. Deterministic over\n * (sha, itemTitle, diff) so replay reconstructs byte-identical messages.\n */\nexport function composeCommitFeedMessage(\n sha: string,\n itemTitle: string,\n diff: string,\n callout?: CommitFeedCalloutContext,\n): string {\n const lines = [\n `# Commit feed (ingestion-only): ${sha}`,\n '',\n `Bead: ${itemTitle}`,\n '',\n ];\n\n if (callout) {\n const body = JSON.stringify({\n issueId: callout.issueId,\n sha,\n itemId: callout.itemId,\n tierName: callout.tierName,\n agentId: callout.agentId,\n claim: '<one sentence: what is wrong and where>',\n });\n lines.push(\n 'This is an ingestion-only feed delivery. Read the diff below to stay',\n 'current with work landing on this issue. By default: do NOT respond, do',\n 'NOT take any action, do NOT produce output — wait for your next dispatch.',\n '',\n 'ONE exception. If you believe this commit is WRONG — it misreads the bead\\'s',\n 'spec, hollows out a test, or breaks a stated invariant — you may raise at',\n 'most one call-out, then return to silence:',\n '',\n ` curl -X POST ${callout.apiUrl}/api/tiered/callouts \\\\`,\n ' -H \"Content-Type: application/json\" \\\\',\n ` -d '${body}'`,\n '',\n 'Do not fix it yourself. Do not edit files. A call-out is a flag, not a task.',\n );\n } else {\n lines.push(\n 'This is an ingestion-only feed delivery. Read the diff below to stay',\n 'current with work landing on this issue. Do NOT respond to this message,',\n 'do NOT take any action, and do NOT produce output — wait for your next',\n 'dispatch.',\n );\n }\n\n lines.push('', '```diff', diff.trimEnd(), '```');\n return lines.join('\\n');\n}\n\n/**\n * Broadcast one landed commit to every standing tier agent. Sends exactly one\n * delivery per tier (everyone hears everything). A failed delivery to one\n * tier does not block the others; failures surface in the returned results.\n */\nexport async function broadcastCommit(options: BroadcastCommitOptions): Promise<BroadcastDelivery[]> {\n const feedConfig = options.feedConfig ?? DEFAULT_FEED_CONFIG;\n const subject = options.commitSubject ?? options.itemTitle;\n if (shouldSkipFeedSubject(subject, feedConfig)) return [];\n\n const renderDiff = options.renderDiff\n ?? ((workspace: string, sha: string, config: ValidatedTieredExecutionFeedConfig) =>\n renderCommitFeedDiff(workspace, sha, config, {\n issueId: options.issueId,\n gitShow: options.gitShow\n ? (repoPath, repoSha) => options.gitShow!(repoPath, repoSha)\n : undefined,\n }));\n const deliver = options.deliver ?? deliverAgentMessage;\n const recordDelivery = options.recordDelivery ?? recordTierFeedDelivery;\n const now = options.now ?? (() => new Date());\n\n const diff = await renderDiff(options.workspace, options.sha, feedConfig);\n const baseMessage = composeCommitFeedMessage(options.sha, options.itemTitle, diff);\n const baseTokenCount = estimateFeedDeliveryTokens(baseMessage);\n const includeCallout = feedConfig.callouts !== 'off' && options.issueId !== undefined && options.itemId !== undefined;\n const apiUrl = options.apiUrl ?? resolveFeedApiUrl();\n\n const deliveries: BroadcastDelivery[] = [];\n for (const tier of options.tiers) {\n const message = includeCallout\n ? composeCommitFeedMessage(options.sha, options.itemTitle, diff, {\n apiUrl,\n issueId: options.issueId!,\n itemId: options.itemId!,\n tierName: tier.tierName,\n agentId: tier.agentId,\n })\n : baseMessage;\n const tokenCount = includeCallout ? estimateFeedDeliveryTokens(message) : baseTokenCount;\n let result: DeliveryResult;\n try {\n result = await deliver(tier.agentId, message, 'tier-feed:broadcastCommit');\n } catch (err) {\n result = {\n ok: false,\n path: 'tmux',\n failure: err instanceof Error ? err.message : String(err),\n };\n }\n await recordDelivery({\n ts: now().toISOString(),\n issueId: options.issueId,\n sha: options.sha,\n itemTitle: options.itemTitle,\n tierName: tier.tierName,\n agentId: tier.agentId,\n tokenCount,\n result,\n });\n deliveries.push({ tierName: tier.tierName, agentId: tier.agentId, result });\n }\n return deliveries;\n}\n","/**\n * Standing supervisor agent for tiered execution (PAN-1791).\n *\n * The supervisor is a long-lived, review-only agent spawned once per issue\n * from the tier table's `supervisor` block. It subscribes to the commit feed\n * according to the configured subscription policy and reviews work at commit\n * boundaries — it NEVER implements. This module owns spawn + subscription\n * only; verdict posting to the inspect-status surface is the next bead\n * (supervisor-verdict-surface).\n *\n * Verdict-surface slice (supervisor-verdict-surface, FR-4):\n *\n * This module carries the supervisor-verdict-surface slice (FR-4): for each\n * subscribed commit, compose a review request containing the commit diff plus\n * the bead's acceptance criteria (and the PRD's traced FR text when the item\n * carries metadata.traces), deliver it to the standing supervisor via the\n * existing deliverAgentMessage primitive (FR-7, no new transport), and\n * instruct the supervisor to land its ack or blocking finding on the EXISTING\n * inspect-status surface (POST /api/specialists/done with specialist\n * \"inspect\") so the foreman's wait-for-verdict path is reused unchanged:\n * a pass persists inspectStatus + saves the checkpoint via onInspectComplete,\n * a failure records a blocking finding and never changes tracker status.\n *\n * Sibling slice note: supervisor spawn + subscription policy\n * (shouldSupervise, item supervisor-subscribe) is owned by a separate bead\n * and lands alongside this module.\n */\n\nimport { execFile } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { renderWorkspaceGitShowPromise } from '../git-utils.js';\nimport { getProjectPanPaths } from '../pan-dir/paths.js';\nimport type { XBriefDocument, XBriefEdge, XBriefItem, XBriefSubItem } from '../xbrief/types.js';\nimport type { AgentState } from './agent-state.js';\nimport { deliverAgentMessage } from './delivery.js';\nimport type { DeliveryResult } from './delivery.js';\nimport { runAgentId } from './spawn-prep.js';\nimport { spawnRun } from './spawn.js';\nimport type {\n TieredExecutionSubscription,\n TieredExecutionSupervisorConfig,\n} from './tier-table.js';\n\n/**\n * Sub-role under 'review' for the standing supervisor. Review sub-roles skip\n * the roles/review.md convoy definition injection (roleAgentDefinitionPath\n * returns null for review+subRole), so the supervisor's behavior comes\n * entirely from the prompt delivered here.\n */\nexport const SUPERVISOR_SUB_ROLE = 'supervisor';\n\n/** Default fraction of beads reviewed under the 'sampled' subscription policy. */\nexport const DEFAULT_SUPERVISOR_SAMPLE_RATE = 0.25;\n\nexport function supervisorAgentId(issueId: string): string {\n return runAgentId(issueId, 'review', SUPERVISOR_SUB_ROLE);\n}\n\nexport interface ShouldSuperviseOptions {\n /**\n * Sampling rate in (0, 1] applied under the 'sampled' policy.\n * Defaults to DEFAULT_SUPERVISOR_SAMPLE_RATE.\n */\n sampleRate?: number;\n}\n\n/**\n * Subscription policy: does the supervisor review commits for this bead?\n *\n * - 'all' → every bead.\n * - 'flagged' → only beads with metadata.requiresInspection === true.\n * - 'sampled' → a deterministic per-bead sample at the configured rate.\n */\nexport function shouldSupervise(\n bead: Pick<XBriefItem, 'id' | 'metadata'>,\n policy: TieredExecutionSubscription,\n options: ShouldSuperviseOptions = {},\n): boolean {\n switch (policy) {\n case 'all':\n return true;\n case 'flagged':\n return bead.metadata?.requiresInspection === true;\n case 'sampled': {\n const rate = options.sampleRate ?? DEFAULT_SUPERVISOR_SAMPLE_RATE;\n if (rate <= 0) return false;\n if (rate >= 1) return true;\n return sampleFraction(bead.id) < rate;\n }\n }\n}\n\n/**\n * Deterministic per-bead sample position: FNV-1a over the bead id mapped to\n * [0, 1). Deterministic (never Math.random) so a bead's subscription decision\n * is stable across the live commit feed, replay re-delivery (tier-replay),\n * and tests.\n */\nfunction sampleFraction(itemId: string): number {\n let hash = 0x811c9dc5;\n for (let i = 0; i < itemId.length; i++) {\n hash ^= itemId.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0) / 0x1_0000_0000;\n}\n\nexport interface SpawnTierSupervisorOptions {\n /** Workspace to run in. Defaults to spawnRun's per-issue default workspace. */\n workspace?: string;\n /** Override the generated standing-supervisor kickoff prompt. */\n prompt?: string;\n}\n\n/**\n * Spawn the standing supervisor as a registered role run (agent state +\n * conversation record + tmux session on the overdeck socket), so the\n * dashboard discovers it under its issue like any other specialist.\n *\n * Model comes from the tier table's supervisor block. The configured harness\n * is passed through for the spawn record, but per PAN-1984 harness resolution\n * is provider-default-only — the tier-table validator already guarantees the\n * configured model+harness+auth combination is policy-legal.\n */\nexport async function spawnTierSupervisor(\n issueId: string,\n supervisor: TieredExecutionSupervisorConfig,\n options: SpawnTierSupervisorOptions = {},\n): Promise<AgentState> {\n return spawnRun(issueId, 'review', {\n agentId: supervisorAgentId(issueId),\n subRole: SUPERVISOR_SUB_ROLE,\n model: supervisor.model,\n harness: supervisor.harness,\n workspace: options.workspace,\n prompt: options.prompt ?? buildSupervisorPrompt(issueId, supervisor.subscribe),\n startedBy: 'tier-supervisor',\n });\n}\n\nexport function buildSupervisorPrompt(\n issueId: string,\n subscribe: TieredExecutionSubscription,\n): string {\n const policyLine = {\n all: 'You are subscribed to EVERY commit in this issue.',\n flagged: 'You are subscribed only to commits for items flagged requiresInspection=true.',\n sampled: 'You are subscribed to a deterministic sample of the items in this issue.',\n }[subscribe];\n\n return [\n `# Standing Supervisor: ${issueId}`,\n '',\n 'You are the standing supervisor for tiered execution on this issue. You are a',\n 'review-only agent: you NEVER write, edit, or commit implementation code.',\n '',\n `Subscription policy: ${subscribe}. ${policyLine}`,\n '',\n 'Commit diffs will be delivered to this session as the tier agents land work.',\n 'For each delivered commit, read the diff against the bead\\'s acceptance',\n 'criteria and note whether the work satisfies them. Ingest each delivery and',\n 'wait for the next; do not take action beyond review. The verdict-posting',\n 'surface is wired separately — until it is, record findings in your replies',\n 'to each delivery.',\n '',\n 'Stay resident. Do not exit after a review; the next commit delivery arrives',\n 'in this same session.',\n ].join('\\n');\n}\n\nconst execFileAsync = promisify(execFile);\n\n/** One commit-review event as delivered to the standing supervisor. */\nexport interface SupervisorReviewEvent {\n issueId: string;\n /** xBRIEF item id receiving the verdict. */\n itemId: string;\n itemTitle: string;\n /** Full commit sha being reviewed. */\n sha: string;\n /** Output of `git show <sha>` for the commit. */\n diff: string;\n /** The bead's acceptance-criterion titles, in plan order. */\n acceptanceCriteria: string[];\n /** Traced PRD FR text, present when the item carries metadata.traces. */\n frText?: string;\n /** Dashboard base URL the verdict POST targets. */\n apiUrl: string;\n}\n\nexport interface SupervisorVerdict {\n /** xBRIEF item id receiving the verdict. */\n itemId: string;\n /** Supervisor ack clears prior blocking findings; failed/blocked records one. */\n status: 'passed' | 'ack' | 'failed' | 'blocked';\n}\n\nexport interface DeliverCommitForReviewOptions {\n /** Agent id of the standing supervisor session. */\n supervisorAgentId: string;\n /** Workspace the commit lives in (git worktree on the feature branch). */\n workspacePath: string;\n issueId: string;\n /** The xBRIEF item the commit implements. */\n item: XBriefItem;\n sha: string;\n /** Item id receiving the verdict; defaults to the xBRIEF item id. */\n itemId?: string;\n /**\n * PRD draft markdown to source traced FR text from. When omitted and the\n * item has metadata.traces, callers should pass the result of\n * loadPrdDraft(projectRoot, issueId).\n */\n prdMarkdown?: string;\n apiUrl?: string;\n /** Test seams — default to the real primitives. */\n deps?: {\n deliver?: typeof deliverAgentMessage;\n getDiff?: (workspacePath: string, sha: string) => Promise<string>;\n };\n}\n\nfunction resolveApiUrl(): string {\n return (\n process.env.OVERDECK_DASHBOARD_URL\n || process.env.DASHBOARD_URL\n || `http://localhost:${process.env.API_PORT || process.env.PORT || '3011'}`\n );\n}\n\n/**\n * Does the next bead depend on a bead with an unresolved supervisor block?\n *\n * Verdicts are interpreted chronologically: the latest verdict for each bead is\n * authoritative. A failed/blocked verdict halts dependents until a later\n * passed/ack verdict for that same bead records the fix commit's approval.\n */\nexport function shouldHaltDispatch(\n verdicts: readonly SupervisorVerdict[],\n nextBead: Pick<XBriefItem, 'id'>,\n dag: Pick<XBriefDocument, 'plan'>,\n): boolean {\n const latestByBead = new Map<string, SupervisorVerdict>();\n for (const verdict of verdicts) {\n latestByBead.set(verdict.itemId, verdict);\n }\n\n const unresolvedBlockedBeads = new Set<string>();\n for (const [itemId, verdict] of latestByBead) {\n if (verdict.status === 'failed' || verdict.status === 'blocked') {\n unresolvedBlockedBeads.add(itemId);\n }\n }\n\n if (unresolvedBlockedBeads.size === 0) return false;\n\n for (const dependencyId of dependencyClosure(nextBead.id, dag.plan.edges ?? [])) {\n if (unresolvedBlockedBeads.has(dependencyId)) return true;\n }\n\n return false;\n}\n\nfunction dependencyClosure(itemId: string, edges: readonly XBriefEdge[]): Set<string> {\n const incoming = new Map<string, string[]>();\n for (const edge of edges) {\n if (edge.type !== 'blocks') continue;\n const parents = incoming.get(edge.to) ?? [];\n parents.push(edge.from);\n incoming.set(edge.to, parents);\n }\n\n const dependencies = new Set<string>();\n const stack = [...(incoming.get(itemId) ?? [])];\n while (stack.length > 0) {\n const dependencyId = stack.pop()!;\n if (dependencies.has(dependencyId)) continue;\n dependencies.add(dependencyId);\n stack.push(...(incoming.get(dependencyId) ?? []));\n }\n\n return dependencies;\n}\n\nfunction childItems(item: XBriefItem): XBriefSubItem[] {\n // xBRIEF v0.6 uses `items`; v0.5 documents used `subItems` for the same\n // structure and are still read as a compatibility alias.\n return item.items ?? item.subItems ?? [];\n}\n\n/**\n * Pull the bead's acceptance-criterion titles from its xBRIEF child items\n * (child metadata.kind === 'acceptance_criterion').\n */\nexport function extractAcceptanceCriteria(item: XBriefItem): string[] {\n return childItems(item)\n .filter((child) => child.metadata?.kind === 'acceptance_criterion')\n .map((child) => child.title);\n}\n\n/**\n * Extract the PRD requirement text for the given trace ids (e.g. [\"FR-4\"]).\n *\n * PRD requirement bullets follow the prd-authoring convention:\n * - **FR-4 — Title.** Body text...\n * Each matched requirement is captured from its bullet line through any\n * indented continuation lines, up to the next bullet or heading. Returns\n * undefined when no trace resolves to a requirement in the document.\n */\nexport function extractTracedFrText(prdMarkdown: string, traces: readonly string[]): string | undefined {\n if (traces.length === 0) return undefined;\n const lines = prdMarkdown.split('\\n');\n const sections: string[] = [];\n\n for (const trace of traces) {\n const startPattern = new RegExp(`^\\\\s*[-*]\\\\s+\\\\*\\\\*${trace}\\\\b`);\n const start = lines.findIndex((line) => startPattern.test(line));\n if (start === -1) continue;\n\n const captured = [lines[start]];\n for (let i = start + 1; i < lines.length; i++) {\n const line = lines[i];\n // A new bullet, a heading, or a blank line ends the requirement body.\n if (/^\\s*[-*]\\s/.test(line) || /^#/.test(line) || line.trim() === '') break;\n captured.push(line);\n }\n sections.push(captured.join('\\n'));\n }\n\n return sections.length > 0 ? sections.join('\\n\\n') : undefined;\n}\n\n/**\n * Read the Overdeck-managed PRD draft for an issue through the\n * state-branch-aware path authority, falling back to the lowercase filename.\n * Returns undefined when no draft exists.\n */\nexport async function loadPrdDraft(projectRoot: string, issueId: string): Promise<string | undefined> {\n const draftsDir = getProjectPanPaths(projectRoot).draftsDir;\n for (const name of [`${issueId.toUpperCase()}.md`, `${issueId.toLowerCase()}.md`]) {\n try {\n return await readFile(join(draftsDir, name), 'utf-8');\n } catch {\n // Try the next casing; missing drafts are expected.\n }\n }\n return undefined;\n}\n\n/**\n * Compose the per-commit review request delivered to the standing supervisor.\n *\n * Unlike commit-feed messages this is NOT ingestion-only: the supervisor must\n * respond by posting exactly one verdict to the existing inspect surface.\n */\nexport function buildSupervisorReviewMessage(event: SupervisorReviewEvent): string {\n const shortSha = event.sha.slice(0, 8);\n const criteria = event.acceptanceCriteria.length > 0\n ? event.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join('\\n')\n : '(none recorded on the plan item — review against the bead title)';\n\n const frSection = event.frText\n ? `\\n## Traced requirements (PRD)\\n\\n${event.frText}\\n`\n : '';\n\n return `SUPERVISOR REVIEW REQUEST — commit ${shortSha} on ${event.issueId}\n\nYou are the standing supervisor for ${event.issueId}. Review this commit against the bead's acceptance criteria and respond with a verdict. You never implement — do not edit files, commit, or run write operations.\n\n## Bead under review\n\n- Issue: ${event.issueId}\n- Bead: ${event.itemId} — ${event.itemTitle}\n- Commit: ${event.sha}\n\n## Acceptance criteria\n\n${criteria}\n${frSection}\n## Commit diff (git show ${shortSha})\n\n\\`\\`\\`diff\n${event.diff}\n\\`\\`\\`\n\n## Verdict (REQUIRED — post exactly one)\n\nLand your verdict on the existing inspect-status surface. Pass the item ID structurally; notes are free-form evidence.\n\nIf the diff satisfies the acceptance criteria, post an ack (this persists inspectStatus and saves the bead checkpoint):\n\n\\`\\`\\`bash\npan admin specialists done inspect ${event.issueId} --item ${event.itemId} --status passed --notes \"<one-line summary>\"\n\\`\\`\\`\n\nIf any acceptance criterion is not met, post a blocking finding with specific, actionable violations ([file:line] + what is wrong):\n\n\\`\\`\\`bash\npan admin specialists done inspect ${event.issueId} --item ${event.itemId} --status failed --notes \"<specific violations and required fixes>\"\n\\`\\`\\`\n\nRules:\n- A blocking finding is recorded on the inspect-status surface only — it does NOT change tracker (Linear/GitHub) status. Never run \\`gh issue close\\` or edit tracker state.\n- Do not use any other endpoint or side channel for the verdict.\n- Only review this commit against this bead's acceptance criteria — compile, lint, and tests are the verification gate's job, and code style is the review convoy's job.`;\n}\n\nasync function getCommitDiff(issueId: string, workspacePath: string, sha: string): Promise<string> {\n return renderWorkspaceGitShowPromise(issueId, workspacePath, sha, [], async (repoPath, repoSha, args) => {\n const { stdout } = await execFileAsync('git', ['show', repoSha, ...args], {\n cwd: repoPath,\n encoding: 'utf-8',\n maxBuffer: 10 * 1024 * 1024,\n });\n return stdout;\n });\n}\n\n/**\n * Deliver one subscribed commit to the standing supervisor for review:\n * resolve the diff, pull the bead's acceptance criteria from its xBRIEF item\n * (plus traced FR text when metadata.traces is present and a PRD draft was\n * provided), and send the composed review request via deliverAgentMessage.\n */\nexport async function deliverCommitForReview(options: DeliverCommitForReviewOptions): Promise<DeliveryResult> {\n const deliver = options.deps?.deliver ?? deliverAgentMessage;\n const getDiff = options.deps?.getDiff\n ?? ((workspacePath, sha) => getCommitDiff(options.issueId, workspacePath, sha));\n\n const diff = await getDiff(options.workspacePath, options.sha);\n const traces = Array.isArray(options.item.metadata?.traces)\n ? (options.item.metadata.traces as string[])\n : [];\n const frText = options.prdMarkdown && traces.length > 0\n ? extractTracedFrText(options.prdMarkdown, traces)\n : undefined;\n\n const message = buildSupervisorReviewMessage({\n issueId: options.issueId,\n itemId: options.itemId ?? options.item.id,\n itemTitle: options.item.title,\n sha: options.sha,\n diff,\n acceptanceCriteria: extractAcceptanceCriteria(options.item),\n frText,\n apiUrl: options.apiUrl ?? resolveApiUrl(),\n });\n\n return deliver(options.supervisorAgentId, message, 'tier-supervisor:verdict');\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { renderWorkspaceGitShowPromise } from '../git-utils.js';\nimport type { ModelId } from '../settings.js';\nimport type { XBriefDocument, XBriefItem } from '../xbrief/types.js';\nimport type { TierOverridesMap } from '../xbrief/io.js';\nimport { getModelCapabilitySync, hasModelCapabilitySync, resolveModelIdSync } from '../model-capabilities.js';\nimport type { AgentState } from './agent-state.js';\nimport type { DeliveryResult } from './delivery.js';\nimport { deliverAgentMessage } from './delivery.js';\nimport { spawnRun } from './spawn.js';\nimport type { SpawnRunOptions } from './spawn-prep.js';\nimport { listSlotOwnership } from './slot-reconcile.js';\nimport type { InFlightBead } from './standing-tiers.js';\nimport { computeTierRunSchedule, tiersNeededForSchedule } from './standing-tiers.js';\nimport { applyEffectiveDifficulty } from './tier-escalation.js';\nimport {\n composeCommitFeedMessage,\n renderCommitFeedDiff,\n resolveFeedApiUrl,\n shouldSkipFeedSubject,\n} from './tier-feed.js';\nimport {\n buildSupervisorReviewMessage,\n extractAcceptanceCriteria,\n extractTracedFrText,\n shouldSupervise,\n supervisorAgentId as defaultSupervisorAgentId,\n SUPERVISOR_SUB_ROLE,\n} from './tier-supervisor.js';\nimport type { TieredExecutionSupervisorConfig, ValidatedTieredExecutionConfig, ValidatedTieredExecutionFeedConfig } from './tier-table.js';\nimport { DEFAULT_TIERED_EXECUTION_CONFIG } from './tier-table.js';\nimport { stopAgent } from './termination.js';\n\nconst execFileAsync = promisify(execFile);\nconst DEFAULT_REPLAY_THRESHOLD = 0.5;\n\nexport interface ReplayCommit {\n sha: string;\n subject: string;\n diff: string;\n itemId?: string;\n}\n\nexport interface ReplayDelivery {\n sha: string;\n result: DeliveryResult;\n}\n\nexport interface ReplayResult {\n agent: AgentState;\n commits: ReplayCommit[];\n deliveries: ReplayDelivery[];\n}\n\nexport interface ReplayRerouteContext {\n doc: XBriefDocument;\n config: ValidatedTieredExecutionConfig;\n statusOverrides?: Record<string, string>;\n tierOverrides?: TierOverridesMap;\n}\n\nexport interface ReplayTargetBase {\n issueId: string;\n workspace: string;\n /**\n * Base revision for replay. The feed is reconstructed from\n * `git log --reverse <base>..HEAD`.\n */\n base: string;\n /** Existing dead/stale agent id, if known. */\n agentId?: string;\n /** Optional prompt for the fresh standing session. */\n prompt?: string;\n /** Feed filtering/rendering config. Defaults preserve today's raw git-show behavior. */\n feedConfig?: ValidatedTieredExecutionFeedConfig;\n /** Dashboard API base URL used when replaying callout-enabled tier feed messages. */\n apiUrl?: string;\n /** Optional current scheduling state used only when compaction_reroute is on. */\n reroute?: ReplayRerouteContext;\n}\n\nexport interface ReplayStandingTierTarget extends ReplayTargetBase {\n kind: 'tier';\n tierName: string;\n /** Registered standing slot index. Resolved from slot-reconcile when omitted. */\n slotIndex?: number;\n /** First item assigned to the standing slot. Resolved from slot-reconcile when omitted. */\n slotItemId?: string;\n}\n\nexport interface ReplaySupervisorTarget extends ReplayTargetBase {\n kind: 'supervisor';\n supervisor: TieredExecutionSupervisorConfig;\n /** Plan used to apply the supervisor subscription policy to replay commits. */\n doc: XBriefDocument;\n /** PRD markdown used to replay traced requirement text, when available. */\n prdMarkdown?: string;\n apiUrl?: string;\n}\n\nexport type ReplayTarget = ReplayStandingTierTarget | ReplaySupervisorTarget;\n\nexport interface TierReplayDeps {\n spawn?: typeof spawnRun;\n deliver?: typeof deliverAgentMessage;\n stop?: typeof stopAgent;\n gitLog?: (workspace: string, base: string) => Promise<Array<{ sha: string; subject: string }>>;\n gitShow?: (workspace: string, sha: string) => Promise<string>;\n renderDiff?: (workspace: string, sha: string, feedConfig: ValidatedTieredExecutionFeedConfig) => Promise<string>;\n listSlotOwnership?: typeof listSlotOwnership;\n}\n\nexport interface TierReplayOptions {\n deps?: TierReplayDeps;\n}\n\nexport interface TierRunCompactionInput {\n /** True only between tier runs. Compaction is forbidden inside a bead. */\n atRunBoundary: boolean;\n /** Any in-flight bead blocks compaction, even if the caller says it is at a boundary. */\n inFlightBead?: InFlightBead;\n estimatedContextTokens: number;\n modelContextWindow: number;\n replayThreshold?: number;\n}\n\nexport interface TierRunCompactionOptions {\n target: ReplayTarget;\n compaction: TierRunCompactionInput;\n deps?: TierReplayDeps;\n}\n\n/**\n * Replay a dead standing tier agent or standing supervisor:\n * respawn the registered session and re-deliver its commit feed reconstructed\n * from `git log <base>..HEAD`, oldest commit first.\n */\nexport async function replayStandingAgent(\n target: ReplayTarget,\n options: TierReplayOptions = {},\n): Promise<ReplayResult> {\n const deps = replayDeps(options.deps, target.issueId);\n const replayTarget: ReplayTarget = target.kind === 'tier'\n ? { ...target, ...resolveReplaySlot(target, deps) }\n : target;\n const agent = await spawnReplayTarget(replayTarget, deps);\n const commits = await loadReplayCommits(\n replayTarget.workspace,\n replayTarget.base,\n replayTarget.feedConfig ?? DEFAULT_TIERED_EXECUTION_CONFIG.feed,\n deps,\n );\n const deliveries = replayTarget.kind === 'tier'\n ? await replayTierFeed(agent.id, replayTarget, commits, deps)\n : await replaySupervisorFeed(agent.id, replayTarget, commits, deps);\n\n return { agent, commits, deliveries };\n}\n\n/**\n * Crash/orphan entry point. The caller has already determined the old session\n * is dead; replay respawns and reconstructs the feed without human input.\n */\nexport async function replayCrashedStandingAgent(\n target: ReplayStandingTierTarget & { reroute: ReplayRerouteContext },\n options?: TierReplayOptions,\n): Promise<ReplayResult | null>;\nexport async function replayCrashedStandingAgent(\n target: ReplayTarget,\n options?: TierReplayOptions,\n): Promise<ReplayResult>;\nexport async function replayCrashedStandingAgent(\n target: ReplayTarget,\n options: TierReplayOptions = {},\n): Promise<ReplayResult | null> {\n const rerouted = resolveReroutedReplayTarget(target);\n if (!rerouted) return null;\n return replayStandingAgent(rerouted, options);\n}\n\n/**\n * Threshold-triggered compaction entry point. It only acts at a tier-run\n * boundary and only when no bead is in flight; mid-bead compaction returns\n * `null` by construction.\n */\nexport async function compactAtTierRunBoundary(\n options: TierRunCompactionOptions,\n): Promise<ReplayResult | null> {\n if (!shouldReplayCompactAtTierRunBoundary(options.compaction)) return null;\n const deps = replayDeps(options.deps, options.target.issueId);\n const rerouted = resolveReroutedReplayTarget(options.target);\n if (!rerouted) {\n if (options.target.agentId) await deps.stop(options.target.agentId);\n return null;\n }\n if (rerouted.agentId) {\n await deps.stop(rerouted.agentId);\n }\n return replayStandingAgent(rerouted, { deps });\n}\n\nexport function shouldReplayCompactAtTierRunBoundary(input: TierRunCompactionInput): boolean {\n if (!input.atRunBoundary) return false;\n if (input.inFlightBead) return false;\n if (!Number.isFinite(input.estimatedContextTokens) || input.estimatedContextTokens < 0) return false;\n if (!Number.isFinite(input.modelContextWindow) || input.modelContextWindow <= 0) return false;\n const threshold = input.replayThreshold ?? DEFAULT_REPLAY_THRESHOLD;\n if (!Number.isFinite(threshold) || threshold <= 0 || threshold > 1) return false;\n return input.estimatedContextTokens / input.modelContextWindow >= threshold;\n}\n\nexport function contextWindowForModel(model: string): number | undefined {\n const resolved = resolveModelIdSync(model);\n if (!hasModelCapabilitySync(resolved)) return undefined;\n return getModelCapabilitySync(resolved as ModelId).contextWindow;\n}\n\nasync function spawnReplayTarget(target: ReplayTarget, deps: Required<TierReplayDeps>): Promise<AgentState> {\n if (target.kind === 'supervisor') {\n return deps.spawn(target.issueId, 'review', {\n agentId: target.agentId ?? defaultSupervisorAgentId(target.issueId),\n subRole: SUPERVISOR_SUB_ROLE,\n model: target.supervisor.model,\n harness: target.supervisor.harness,\n workspace: target.workspace,\n prompt: target.prompt,\n startedBy: 'tier-replay',\n });\n }\n\n const slot = resolveReplaySlot(target, deps);\n const spawnOptions: SpawnRunOptions = {\n slotIndex: slot.slotIndex,\n slotItemId: slot.slotItemId,\n prompt: target.prompt,\n startedBy: 'tier-replay',\n ...currentTierSpawnOverride(target),\n };\n return deps.spawn(target.issueId, 'work', spawnOptions);\n}\n\nfunction resolveReroutedReplayTarget(target: ReplayTarget): ReplayTarget | null {\n if (target.kind !== 'tier') return target;\n const reroute = target.reroute;\n if (!reroute || reroute.config.compaction_reroute === 'off') return target;\n\n const scheduleDoc = remainingScheduleDocument(reroute);\n const schedule = computeTierRunSchedule(scheduleDoc, reroute.config);\n if (!tiersNeededForSchedule(schedule).includes(target.tierName)) return null;\n return target;\n}\n\nfunction remainingScheduleDocument(reroute: ReplayRerouteContext): XBriefDocument {\n const statusOverrides = reroute.statusOverrides ?? {};\n const tierOverrides = reroute.tierOverrides ?? {};\n return {\n ...reroute.doc,\n plan: {\n ...reroute.doc.plan,\n items: reroute.doc.plan.items\n .filter((item) => (statusOverrides[item.id] ?? item.status) !== 'completed')\n .map((item) => applyEffectiveDifficulty(item, tierOverrides)),\n },\n };\n}\n\nfunction currentTierSpawnOverride(target: ReplayStandingTierTarget): Pick<SpawnRunOptions, 'model' | 'harness'> {\n const reroute = target.reroute;\n if (!reroute || reroute.config.compaction_reroute === 'off') return {};\n const tier = reroute.config.tiers[target.tierName];\n if (!tier) return {};\n return { model: tier.model, harness: tier.harness };\n}\n\nfunction resolveReplaySlot(\n target: ReplayStandingTierTarget,\n deps: Required<TierReplayDeps>,\n): { slotIndex: number; slotItemId: string } {\n if (target.slotIndex !== undefined && target.slotItemId !== undefined) {\n return { slotIndex: target.slotIndex, slotItemId: target.slotItemId };\n }\n\n const ownership = deps.listSlotOwnership(target.issueId, target.workspace);\n const match = target.slotItemId\n ? ownership.find((assignment) => assignment.itemId === target.slotItemId)\n : ownership.find((assignment) => assignment.agentId === target.agentId);\n if (!match) {\n throw new Error(\n `Cannot replay standing tier '${target.tierName}' for ${target.issueId}: slot ownership was not found`,\n );\n }\n return { slotIndex: match.slotIndex, slotItemId: match.itemId };\n}\n\nasync function replayTierFeed(\n agentId: string,\n target: ReplayStandingTierTarget,\n commits: ReplayCommit[],\n deps: Required<TierReplayDeps>,\n): Promise<ReplayDelivery[]> {\n const deliveries: ReplayDelivery[] = [];\n const feedConfig = target.feedConfig ?? DEFAULT_TIERED_EXECUTION_CONFIG.feed;\n const includeCallout = feedConfig.callouts !== 'off';\n const apiUrl = target.apiUrl ?? resolveFeedApiUrl();\n for (const commit of commits) {\n const itemId = commit.itemId;\n const message = composeCommitFeedMessage(\n commit.sha,\n commit.subject,\n commit.diff,\n includeCallout && itemId\n ? {\n apiUrl,\n issueId: target.issueId,\n itemId,\n tierName: target.tierName,\n agentId,\n }\n : undefined,\n );\n deliveries.push({\n sha: commit.sha,\n result: await deps.deliver(agentId, message, 'tier-replay:tier'),\n });\n }\n return deliveries;\n}\n\nasync function replaySupervisorFeed(\n agentId: string,\n target: ReplaySupervisorTarget,\n commits: ReplayCommit[],\n deps: Required<TierReplayDeps>,\n): Promise<ReplayDelivery[]> {\n const deliveries: ReplayDelivery[] = [];\n for (const commit of commits) {\n const item = findReplayItem(target.doc, commit);\n if (!item || !shouldSupervise(item, target.supervisor.subscribe)) continue;\n const traces = Array.isArray(item.metadata?.traces) ? item.metadata.traces as string[] : [];\n const frText = target.prdMarkdown && traces.length > 0\n ? extractTracedFrText(target.prdMarkdown, traces)\n : undefined;\n const message = buildSupervisorReviewMessage({\n issueId: target.issueId,\n itemId: item.id,\n itemTitle: item.title,\n sha: commit.sha,\n diff: commit.diff,\n acceptanceCriteria: extractAcceptanceCriteria(item),\n frText,\n apiUrl: target.apiUrl ?? 'http://localhost:3011',\n });\n deliveries.push({\n sha: commit.sha,\n result: await deps.deliver(agentId, message, 'tier-replay:supervisor'),\n });\n }\n return deliveries;\n}\n\nfunction findReplayItem(doc: XBriefDocument, commit: Pick<ReplayCommit, 'subject'>): XBriefItem | undefined {\n return doc.plan.items.find((item) => {\n const subject = commit.subject.toLowerCase();\n return subject.includes(item.id.toLowerCase()) || subject.includes(item.title.toLowerCase());\n });\n}\n\nasync function loadReplayCommits(\n workspace: string,\n base: string,\n feedConfig: ValidatedTieredExecutionFeedConfig,\n deps: Required<TierReplayDeps>,\n): Promise<ReplayCommit[]> {\n const entries = await deps.gitLog(workspace, base);\n const commits: ReplayCommit[] = [];\n for (const entry of entries) {\n if (shouldSkipFeedSubject(entry.subject, feedConfig)) continue;\n commits.push({\n ...entry,\n diff: await deps.renderDiff(workspace, entry.sha, feedConfig),\n });\n }\n return commits;\n}\n\nasync function runGitLog(workspace: string, base: string): Promise<Array<{ sha: string; subject: string }>> {\n const { stdout } = await execFileAsync('git', ['log', '--reverse', '--format=%H%x00%s', `${base}..HEAD`], {\n cwd: workspace,\n encoding: 'utf-8',\n maxBuffer: 10 * 1024 * 1024,\n });\n return stdout\n .split('\\n')\n .map((line) => {\n const [sha, subject = ''] = line.split('\\0');\n return { sha, subject };\n })\n .filter((entry) => entry.sha.trim().length > 0);\n}\n\nasync function runGitShow(issueId: string, workspace: string, sha: string): Promise<string> {\n return renderWorkspaceGitShowPromise(issueId, workspace, sha, [], async (repoPath, repoSha, args) => {\n const { stdout } = await execFileAsync('git', ['show', repoSha, ...args], {\n cwd: repoPath,\n encoding: 'utf-8',\n maxBuffer: 16 * 1024 * 1024,\n });\n return stdout;\n });\n}\n\nfunction replayDeps(deps: TierReplayDeps = {}, issueId: string): Required<TierReplayDeps> {\n return {\n spawn: deps.spawn ?? spawnRun,\n deliver: deps.deliver ?? deliverAgentMessage,\n stop: deps.stop ?? stopAgent,\n gitLog: deps.gitLog ?? runGitLog,\n gitShow: deps.gitShow ?? ((workspace, sha) => runGitShow(issueId, workspace, sha)),\n renderDiff: deps.renderDiff\n ?? (deps.gitShow\n ? (workspace, sha) => deps.gitShow!(workspace, sha)\n : (workspace, sha, feedConfig) =>\n renderCommitFeedDiff(workspace, sha, feedConfig, { issueId })),\n listSlotOwnership: deps.listSlotOwnership ?? listSlotOwnership,\n };\n}\n","export {\n clearReadySignal,\n isQualifiedAgentId,\n normalizeAgentId,\n resolveAgentTargetSync,\n waitForAgentIdle,\n waitForReadySignal,\n} from './agents/identity.js';\nexport {\n buildDefaultResumeContinueMessage,\n buildResumeContinueMessage,\n decideChannelsForWorkAgent,\n decideSupervisorForWorkAgent,\n dismissDevChannelsDialog,\n writeChannelsBridgeMcpConfig,\n} from './agents/supervisor-channels.js';\nexport {\n buildCavemanExports,\n buildSpawnEnvForModel,\n determineModel,\n getProviderEnvForModel,\n getProviderExportsForModel,\n getProviderTmuxFlags,\n} from './agents/provider-env.js';\nexport {\n OHMYPI_AGENT_READY_TIMEOUT_SECONDS,\n describeOhmypiSpawnFailure,\n getAgentRuntimeBaseCommand,\n getProviderAuthMode,\n getRoleRuntimeBaseCommand,\n injectPiConversationMemory,\n roleAgentDefinitionPath,\n waitForPromptReady,\n} from './agents/runtime-command.js';\nexport {\n assertWorkspaceStackHealthyForSpawn,\n buildAgentLaunchConfig,\n retrieveSpawnTimeMemoryContext,\n transitionIssueToInProgress,\n transitionIssueToInReview,\n type AgentLaunchConfig,\n type SpawnOptions,\n type SpawnRunOptions,\n} from './agents/spawn-prep.js';\n\nexport { spawnAgent, spawnRun } from './agents/spawn.js';\n\nexport { listRunningAgentsSync, listAgentStates, listRunningAgents, warnOnBareNumericIssueIds, dropLegacyAgentStatesMissingRoleAsync } from './agents/queries.js';\nexport {\n GOVERNOR_SLOT_PAUSE_REASON_PREFIX,\n SESSION_EXITED_BEFORE_KICKOFF,\n __testInternals,\n clearAgentPaused,\n clearAgentPausedSync,\n clearAgentTroubled,\n clearAgentTroubledSync,\n getAgentDir,\n getAgentState,\n getAgentStateFilePath,\n getAgentStateSync,\n isAgentPaused,\n isAgentTroubled,\n isRole,\n markAgentRunningState,\n markAgentStoppedState,\n markAgentTroubled,\n recordAgentFailure,\n recordAgentFailureSync,\n resetAgentFailureCount,\n saveAgentState,\n saveAgentStateSync,\n setAgentPaused,\n setAgentPausedSync,\n setAgentYieldedSync,\n clearYieldForResumeSync,\n wipeAgentStateDirs,\n writeAgentStateJsonSync,\n type AgentState,\n type AgentStopCause,\n type Role,\n} from './agents/agent-state.js';\nexport { stopAgentSync, stopAgent } from './agents/termination.js';\nexport { type ActivityEntry, appendActivity, getActivity, saveSessionId, getSessionId, getLatestSessionIdSync, getLatestSessionId } from './agents/activity.js';\nexport { type AgentResolution, type AgentRuntimeState, getAgentRuntimeStateSync, getAgentRuntimeState, saveAgentRuntimeState } from './agents/runtime-state.js';\nexport { deliverAgentMessage, deliverInitialPromptWithRetry, deliverResumeMessageWithTranscriptConfirmation, deliverAgentPermissionDecision, setAgentDeliveryMethod, type DeliveryResult } from './agents/delivery.js';\nexport { messageAgent } from './agents/messaging.js';\n\nexport { buildCompactRecoverySeed, resumeAgent } from './agents/resume.js';\n\nexport { autoRecoverAgents, detectCrashedAgents, recoverAgent, restartAgent, type RestartAgentOptions } from './agents/recovery.js';\nexport {\n compactAtTierRunBoundary,\n contextWindowForModel,\n replayCrashedStandingAgent,\n replayStandingAgent,\n shouldReplayCompactAtTierRunBoundary,\n type ReplayCommit,\n type ReplayDelivery,\n type ReplayResult,\n type ReplayStandingTierTarget,\n type ReplaySupervisorTarget,\n type ReplayTarget,\n type TierReplayDeps,\n type TierRunCompactionInput,\n type TierRunCompactionOptions,\n} from './agents/tier-replay.js';\n","import { readFile, mkdir, writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport type { AgentRuntimeSnapshot } from '@overdeck/contracts';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport {\n getAgentRuntimeSnapshot as fetchAgentRuntimeSnapshot,\n emitAgentEvent,\n} from '../agent-runtime.js';\nimport { getRuntimeSnapshot, isAgentStateServiceInProcess } from '../agent-runtime-mirror.js';\nimport { normalizeHarness } from '../overdeck/conversations.js';\nimport { getAgentDir } from '../agents.js';\n\nexport type AgentResolution = 'working' | 'done' | 'needs_input' | 'stuck' | 'completed' | 'unclear' | 'abandoned';\n\n/** Callers consume this shape; data comes from AgentRuntimeSnapshot. */\nexport interface AgentRuntimeState {\n // 'suspended' retained for backward-compat with callers that still compare\n // against it defensively. The new event path never emits suspended — PAN-800\n // drops the auto-suspend feature; PAN-188 reintroduces it.\n state: 'active' | 'idle' | 'suspended' | 'stopped' | 'uninitialized' | 'waiting-on-human';\n lastActivity: string;\n currentTool?: string;\n claudeSessionId?: string;\n sessionModel?: string;\n sessionHarness?: RuntimeName;\n /**\n * For specialists: the issue currently being processed. Tracked per-agent in\n * the AgentStateService snapshot (see agent.current_issue_set event).\n */\n currentIssue?: string;\n resolution?: AgentResolution;\n resolutionCount?: number;\n resolutionUpdatedAt?: string;\n waitingReason?: string;\n waitingStartedAt?: string;\n waitingNotification?: string;\n contextSaturatedAt?: string;\n}\n\nexport function sessionResumeDriftReasons(\n runtimeState: AgentRuntimeState | null,\n model: string,\n harness: RuntimeName,\n): string[] {\n if (!runtimeState?.sessionModel || !runtimeState.sessionHarness) return [];\n const reasons: string[] = [];\n if (runtimeState.sessionModel !== model) {\n reasons.push(`model ${runtimeState.sessionModel}→${model}`);\n }\n if (runtimeState.sessionHarness !== harness) {\n reasons.push(`harness ${runtimeState.sessionHarness}→${harness}`);\n }\n return reasons;\n}\n\nfunction snapshotToRuntimeState(snap: AgentRuntimeSnapshot | null): AgentRuntimeState | null {\n if (!snap) return null;\n // Map Activity → legacy state. The legacy 'active' value collapses working\n // and thinking — neither consumer ever distinguished them.\n let state: AgentRuntimeState['state'];\n switch (snap.activity) {\n case 'working': state = 'active'; break;\n case 'thinking': state = 'active'; break;\n case 'idle': state = 'idle'; break;\n case 'stopped': state = 'stopped'; break;\n case 'waiting': state = 'waiting-on-human'; break;\n default: state = 'uninitialized';\n }\n return {\n state,\n lastActivity: snap.lastActivity,\n currentTool: snap.currentTool,\n claudeSessionId: snap.claudeSessionId,\n sessionModel: snap.sessionModel,\n sessionHarness: normalizeHarness(snap.sessionHarness ?? null) ?? undefined,\n currentIssue: snap.currentIssue,\n resolution: snap.resolution as AgentResolution | undefined,\n resolutionCount: snap.resolutionCount,\n resolutionUpdatedAt: snap.resolutionUpdatedAt,\n waitingReason: snap.waiting?.reason,\n waitingStartedAt: snap.waiting?.startedAt,\n waitingNotification: snap.waiting?.message,\n contextSaturatedAt: snap.contextSaturatedAt,\n };\n}\n\nexport function getAgentRuntimeStateSync(agentId: string): AgentRuntimeState | null {\n // Sync path: read from the in-process mirror (empty in fresh CLI processes,\n // populated inside the dashboard server). CLI commands should use\n // getAgentRuntimeStateProgram so they fall through to HTTP.\n return snapshotToRuntimeState(Effect.runSync(getRuntimeSnapshot(agentId)));\n}\n\nexport const getAgentRuntimeState = (agentId: string): Effect.Effect<AgentRuntimeState | null> =>\n Effect.gen(function* () {\n if (yield* isAgentStateServiceInProcess()) {\n return snapshotToRuntimeState(yield* getRuntimeSnapshot(agentId));\n }\n\n const snap = yield* fetchAgentRuntimeSnapshot(agentId);\n return snapshotToRuntimeState(snap);\n });\n\nasync function patchRuntimeJson(agentId: string, patch: Partial<AgentRuntimeState>): Promise<void> {\n const agentDir = getAgentDir(agentId);\n const runtimeFile = join(agentDir, 'runtime.json');\n let runtime: Record<string, unknown> = {};\n\n try {\n runtime = JSON.parse(await readFile(runtimeFile, 'utf-8')) as Record<string, unknown>;\n } catch {\n runtime = {};\n }\n\n if (Object.prototype.hasOwnProperty.call(patch, 'contextSaturatedAt')) {\n if (patch.contextSaturatedAt === undefined) {\n delete runtime.contextSaturatedAt;\n } else {\n runtime.contextSaturatedAt = patch.contextSaturatedAt;\n }\n }\n\n await mkdir(agentDir, { recursive: true });\n await writeFile(runtimeFile, JSON.stringify(runtime, null, 2));\n}\n\n/**\n * Emit events derived from a legacy-shape patch. Callers gradually migrate to\n * direct emitAgentEvent calls; this adapter keeps existing code working.\n */\nexport async function saveAgentRuntimeState(agentId: string, patch: Partial<AgentRuntimeState>): Promise<void> {\n if (patch.currentIssue !== undefined) {\n await Effect.runPromise(emitAgentEvent(agentId, {\n kind: 'current_issue_set',\n currentIssue: patch.currentIssue || undefined,\n }));\n }\n\n if (patch.resolution !== undefined && patch.resolutionCount !== undefined) {\n await Effect.runPromise(emitAgentEvent(agentId, {\n kind: 'resolution_set',\n resolution: patch.resolution,\n resolutionCount: patch.resolutionCount,\n }));\n }\n\n if (patch.state !== undefined) {\n if (patch.state === 'waiting-on-human') {\n await Effect.runPromise(emitAgentEvent(agentId, {\n kind: 'waiting_start',\n reason: (patch.waitingReason as 'tool_permission' | 'user_question' | 'disambiguation' | 'other') || 'other',\n message: patch.waitingNotification,\n }));\n } else if (patch.state === 'active') {\n await Effect.runPromise(emitAgentEvent(agentId, { kind: 'activity', activity: 'working', tool: patch.currentTool }));\n } else if (patch.state === 'idle') {\n await Effect.runPromise(emitAgentEvent(agentId, { kind: 'activity', activity: 'idle' }));\n } else if (patch.state === 'stopped') {\n await Effect.runPromise(emitAgentEvent(agentId, { kind: 'activity', activity: 'stopped' }));\n }\n } else if (patch.currentTool !== undefined) {\n await Effect.runPromise(emitAgentEvent(agentId, { kind: 'activity', activity: 'working', tool: patch.currentTool }));\n }\n\n if (patch.claudeSessionId || patch.sessionModel !== undefined || patch.sessionHarness !== undefined) {\n // model_set requires a model — use existing snapshot's model if present.\n const snap = getAgentRuntimeStateSync(agentId);\n if (snap || patch.claudeSessionId || patch.sessionModel !== undefined || patch.sessionHarness !== undefined) {\n const event: {\n kind: 'model_set';\n model: string;\n claudeSessionId?: string;\n sessionModel?: string;\n sessionHarness?: RuntimeName;\n } = {\n kind: 'model_set',\n model: 'unknown',\n };\n if (patch.claudeSessionId !== undefined) event.claudeSessionId = patch.claudeSessionId;\n if (patch.sessionModel !== undefined) event.sessionModel = patch.sessionModel;\n if (patch.sessionHarness !== undefined) event.sessionHarness = patch.sessionHarness;\n await Effect.runPromise(emitAgentEvent(agentId, {\n ...event,\n }));\n }\n }\n\n if (Object.prototype.hasOwnProperty.call(patch, 'contextSaturatedAt')) {\n await patchRuntimeJson(agentId, patch);\n await Effect.runPromise(emitAgentEvent(agentId, {\n kind: 'context_saturation_changed',\n contextSaturatedAt: patch.contextSaturatedAt,\n }));\n }\n}\n","import { existsSync, readFileSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { resolveBareNumericIdSync } from '../issue-id.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { getRollbackAgentStatePath } from '../overdeck/agent-rollback-state.js';\nimport { getOverdeckAgentStateSync, listOverdeckAgentStatesSync } from '../overdeck/agent-state-sync.js';\nimport { getAgentRuntimeStateSync } from './runtime-state.js';\n\n/** Known agent ID prefixes — IDs with these prefixes are already normalized */\nconst AGENT_PREFIXES = ['agent-', 'planning-', 'conv-', 'strike-', 'inspect-'];\n// Singleton runners spawn under their own bare ID (spawnRun creates the tmux\n// session and agent dir from the raw ID). They MUST be listed here so\n// normalizeAgentId is a no-op for them — otherwise message delivery and state\n// lookups would target `agent-<id>` and miss the real session (PAN-1866: the\n// sequencer spawned but its prompt was delivered to a nonexistent\n// `agent-sequencer-runner` pane, leaving the agent idle).\nconst SINGLETON_AGENT_IDS = new Set(['flywheel-orchestrator', 'sequencer-runner']);\n\n/** Normalize agent ID: preserve known prefixes, add 'agent-' for bare issue IDs */\nexport function normalizeAgentId(agentId: string): string {\n if (SINGLETON_AGENT_IDS.has(agentId)) return agentId;\n if (AGENT_PREFIXES.some(p => agentId.startsWith(p))) {\n return agentId;\n }\n return `agent-${agentId.toLowerCase()}`;\n}\n\n/** True when the input is already a fully-qualified agent ID (known prefix or singleton), not an issue ID. */\nexport function isQualifiedAgentId(input: string): boolean {\n const lower = input.toLowerCase();\n return SINGLETON_AGENT_IDS.has(lower) || AGENT_PREFIXES.some(p => lower.startsWith(p));\n}\n\nfunction agentStateExistsSync(agentId: string): boolean {\n return Boolean(getOverdeckAgentStateSync(agentId)) || existsSync(getRollbackAgentStatePath(agentId));\n}\n\n/**\n * Resolve a CLI-supplied agent target to an on-disk agent ID (PAN-1760).\n * Accepts bare numerics (\"1148\"), issue IDs (\"PAN-1148\"), and fully-qualified\n * agent IDs (\"agent-pan-1148-ship\", \"strike-pan-1723\", \"inspect-pan-1744-x\",\n * \"flywheel-orchestrator\"). For issue IDs, prefers the canonical work-agent\n * directory when present, then falls back to the single registered agent state\n * for that issue. If no single fallback exists, preserves the historical\n * canonical agent-* target.\n */\nexport function resolveAgentTargetSync(input: string): string | null {\n if (isQualifiedAgentId(input)) return input.toLowerCase();\n const issueId = resolveBareNumericIdSync(input);\n if (!issueId) return null;\n\n const canonicalAgentId = normalizeAgentId(issueId);\n if (agentStateExistsSync(canonicalAgentId)) return canonicalAgentId;\n\n try {\n const wantedIssueId = issueId.toUpperCase();\n const matches = listOverdeckAgentStatesSync()\n .filter((agent) => agent.issueId.toUpperCase() === wantedIssueId)\n .map((agent) => agent.id);\n if (matches.length === 1) return matches[0].toLowerCase();\n return canonicalAgentId;\n } catch {\n return canonicalAgentId;\n }\n}\n\n/**\n * Get path to agent's ready signal file (written by SessionStart hook)\n */\nfunction getReadySignalPath(agentId: string): string {\n return join(getOverdeckHome(), 'agents', agentId, 'ready.json');\n}\n\n/**\n * Clear ready signal before spawning (clean slate)\n */\nexport function clearReadySignal(agentId: string): void {\n const readyPath = getReadySignalPath(agentId);\n if (existsSync(readyPath)) {\n try {\n unlinkSync(readyPath);\n } catch {\n // Ignore errors - non-critical\n }\n }\n}\n\nfunction isReadySignalPresent(readyPath: string): boolean {\n if (!existsSync(readyPath)) return false;\n try {\n const signal = JSON.parse(readFileSync(readyPath, 'utf-8'));\n // Accept both the Claude hook shape ({ ready: true, ... }) and the Pi\n // extension shape ({ agentId, sessionId, ... } with no `ready` field).\n return Boolean(signal && typeof signal === 'object' && signal.ready !== false);\n } catch {\n // File exists but mid-write / invalid — keep waiting.\n return false;\n }\n}\n\nexport async function waitForReadySignal(agentId: string, timeoutSeconds = 30): Promise<boolean> {\n const readyPath = getReadySignalPath(agentId);\n\n for (let i = 0; i < timeoutSeconds; i++) {\n if (isReadySignalPresent(readyPath)) return true;\n await new Promise(resolve => setTimeout(resolve, 1000)); // Non-blocking sleep\n }\n\n return isReadySignalPresent(readyPath);\n}\n\n/**\n * Wait until a hook-instrumented agent reports it is idle at the prompt, via the\n * runtime mirror (Stop / SessionStart hook → activity 'idle'), or the timeout\n * elapses. Returns true if idle was observed.\n *\n * PAN-1594/1596: this is the hook-derived \"is the agent idle right now\" check.\n * It replaced the tmux pane-scrape `waitForClaudePrompt` (since removed). Works\n * for any hook-instrumented session — agents AND conversations (`conv-*`), which\n * feed the runtime mirror once their heartbeat POSTs authenticate (PAN-1596). No\n * dependency on tmux output or permission mode.\n *\n * Distinct from waitForReadySignal: that answers the one-time \"has this\n * (re)launched session reached the prompt\" (ready.json gate, used by the\n * conversation reattach/fork paths); this answers \"is the running agent idle at\n * the prompt right now\".\n */\nexport async function waitForAgentIdle(agentId: string, timeoutMs = 5000): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n do {\n if (getAgentRuntimeStateSync(agentId)?.state === 'idle') return true;\n await new Promise(r => setTimeout(r, 250));\n } while (Date.now() < deadline);\n return getAgentRuntimeStateSync(agentId)?.state === 'idle';\n}\n","/**\n * Unified role primitive (PAN-1048) as a dependency leaf. Low-level doors\n * (activity-logger, pan-dir/agents) import the type from here so the\n * agent-state module graph never cycles back into them (lint:circular).\n */\nexport type Role = 'plan' | 'work' | 'review' | 'test' | 'ship' | 'flywheel' | 'strike' | 'sequencer' | 'knowledge';\n\nexport function isRole(value: unknown): value is Role {\n return value === 'plan' || value === 'work' || value === 'review' || value === 'test' || value === 'ship' || value === 'flywheel' || value === 'strike' || value === 'sequencer' || value === 'knowledge';\n}\n","import { readdir, writeFile as writeFileAsync, mkdir as mkdirAsync } from 'fs/promises';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { AGENTS_DIR, getOverdeckHome } from '../paths.js';\nimport { FsError } from '../errors.js';\nimport { emitActivityEntrySync } from '../activity-logger.js';\nimport { resolveAutoResumeConfigForIssue } from '../cloister/auto-resume-config.js';\nimport { getRollbackAgentStatePath, readRollbackAgentStateSync, writeRollbackAgentStateSync } from '../overdeck/agent-rollback-state.js';\nimport { getOverdeckAgentStateSync, saveOverdeckAgentStateSync, listOverdeckAgentStatesSync } from '../overdeck/agent-state-sync.js';\nimport { readAgentHarnessModelRecordSync, writeAgentHarnessModelRecordSync } from '../overdeck/agent-record-sync.js';\nimport { logAgentLifecycleSync } from '../persistent-logger.js';\nimport { recordFeatureRegistryLifecycle } from '../registry/feature-registry-population.js';\nimport { appendAgentPlaneLifecycle } from '../pan-dir/agents.js';\nimport { normalizeAgentId } from './identity.js';\nimport { removeAgentStateDir } from './state-dir-removal.js';\nimport { registerPipelineTelemetryAgentReader } from '../telemetry/pipeline-agent-reader.js';\nimport { isRole } from './role.js';\nimport type { Role } from './role.js';\n\nexport type { Role } from './role.js';\n\nexport const SESSION_EXITED_BEFORE_KICKOFF = 'session-exited-before-kickoff';\n\n/**\n * Why an agent transitioned to `stopped` (PAN-3324).\n *\n * `'operator'` means a human asked for the stop — `pan kill`, `pan pause`, a\n * dashboard stop/pause action, or an explicit flywheel stop/pause/abort. Only\n * that cause sets `stoppedByUser`, and therefore only that cause engages the\n * operator-stop gate that suppresses autonomous re-drive.\n *\n * `'system'` covers every machinery-initiated stop: memory shedding, health\n * force-kills, stalled-review-parent reaping, corruption recovery, close-out,\n * and reconciliation of a process the OOM killer already took. These stops are\n * transient resource or liveness events, so autonomous recovery must stay\n * eligible. It is the default precisely so a caller that omits the cause can\n * never accidentally manufacture a permanent stall.\n */\nexport type AgentStopCause = 'operator' | 'system';\n\nexport interface AgentState {\n id: string;\n issueId: string;\n workspace: string;\n /**\n * The projects/workspaces registry row this agent belongs to (PAN-1990\n * AC-1/FR-4). Resolved from `issueId` at persistence time\n * (agent-state-sync.ts) when not set explicitly — undefined only when no\n * workspace row exists for the issue yet.\n */\n workspaceId?: string;\n /** Coding-agent harness this agent runs under (PAN-636). */\n harness?: RuntimeName;\n /** Unified role primitive (PAN-1048). */\n role: Role;\n /** Parent work agent that owns a swarm's gated orchestration loop. */\n foreman?: boolean;\n model: string;\n /**\n * The exact spawn key fed to the weighted-distribution model picker at spawn\n * (`${role}:${issueId}`), persisted so the dashboard MODEL inspector (PAN-2053)\n * can show the faithful FNV-1a derivation without re-guessing the key's form.\n * Undefined for scalar-role agents and for agents spawned before PAN-2053.\n */\n modelSpawnKey?: string;\n status: 'starting' | 'running' | 'stopped' | 'error';\n startedAt: string;\n lastActivity?: string;\n lastResumeAt?: string;\n /**\n * Tri-state kickoff delivery signal for work-agent lifecycle monitoring:\n * undefined = legacy/pre-feature agent or non-applicable role;\n * false = spawned but kickoff delivery not yet confirmed;\n * true = kickoff delivery confirmed.\n */\n kickoffDelivered?: boolean;\n stoppedAt?: string;\n /** True when the agent was stopped with `cause: 'operator'` — `pan kill`,\n * `pan pause`, or a dashboard stop/pause action. Cleared on resume. Read by\n * deacon's autoResumeStoppedWorkAgents to distinguish a deliberate operator\n * stop from a crash/orphan. PAN-3324: machinery-initiated stops (memory\n * shedding, health force-kills, stalled-parent reaping, OOM reconciliation)\n * pass `cause: 'system'` and must never set this — doing so latches the\n * operator-stop gate and permanently suppresses autonomous recovery. */\n stoppedByUser?: boolean;\n stoppedByPause?: boolean;\n paused?: boolean;\n pausedReason?: string;\n pausedAt?: string;\n /**\n * PAN-2507: true when this work agent was paused by the preemptive scheduler\n * (yielded to free capacity for an advancing dispatch), as distinct from an\n * operator pause. Reuses `paused: true` so every existing no-resume gate\n * protects the yielded agent; this flag lets the deacon resume yielded agents\n * oldest-first and lets `pan unpause` self-clear the yield attribution.\n */\n yieldedByScheduler?: boolean;\n /** PAN-2507: ISO timestamp of the yield (oldest-first resume ordering). */\n yieldedAt?: string;\n /**\n * PAN-2507: ISO timestamp of the most recent resume-from-yield. Enforces the\n * re-yield cooldown (an agent just resumed from a yield may not be re-yielded\n * until `yield_cooldown_secs` elapse). Survives unpause (it is a cooldown\n * tracker, not a pause field).\n */\n lastYieldResumeAt?: string;\n troubled?: boolean;\n troubledAt?: string;\n consecutiveFailures?: number;\n firstFailureInRunAt?: string;\n lastFailureAt?: string;\n lastFailureReason?: string;\n lastFailureNextRetryAt?: string;\n branch?: string; // Git branch name for this agent\n costSoFar?: number;\n sessionId?: string; // For resuming sessions after handoff\n\n // Work type system (PAN-118). 'retained-transcripts' is the tombstone phase\n // (PAN-3465): removeAgent keeps the row for transcript linkage after the\n // state dir is retired — such rows are not live agents.\n phase?: 'exploration' | 'implementation' | 'testing' | 'documentation' | 'review-response' | 'planning' | 'synthesis' | 'retained-transcripts';\n workType?: string; // Current work type ID\n\n /**\n * Whether this work agent was launched with the experimental Claude Code\n * Channels prompt-delivery path enabled. Set at launch time after the\n * eligibility check; never mutated after. Read by deliverAgentMessage to\n * decide whether to attempt the bridge socket before falling back to\n * sendKeysAsync. Absent or false means tmux-only delivery (current default).\n */\n channelsEnabled?: boolean;\n /** True when this work agent was launched through the PTY supervisor wrapper. */\n supervisorEnabled?: boolean;\n /**\n * Delivery method for agent messages. 'auto' tries supervisor, then channels,\n * then tmux; explicit socket methods are strict (throw on failure); 'tmux'\n * bypasses socket transports entirely.\n */\n deliveryMethod?: 'auto' | 'supervisor' | 'channels' | 'tmux';\n\n /**\n * Short HEAD sha (8 chars) of the workspace at the moment this role run was\n * spawned. Used by the reactive scheduler's activeRoleRunExists() to detect a\n * stale/zombie role session: if the workspace HEAD has advanced past this\n * marker, the existing session ran against old code and must not block a\n * fresh re-dispatch for the new HEAD. Set for non-work roles in spawnRun.\n */\n roleRunHead?: string;\n\n /** Flywheel run that spawned this agent, if any. Absent for operator-started agents (PAN-1812). */\n flywheelRunId?: string;\n /** Origin token identifying the command or autonomous path that started this agent. */\n startedBy?: string;\n\n /** True when a planning session was launched with `pan plan --auto`. */\n auto?: boolean;\n\n /** Review-convoy metadata for server-side reviewer lifecycle monitoring. */\n reviewSubRole?: string;\n reviewRunId?: string;\n reviewOutputPath?: string;\n reviewSynthesisAgentId?: string;\n reviewDeadlineAt?: string;\n reviewMonitorSignaled?: 'ready' | 'failed' | 'timeout';\n /** Number of times Deacon has respawned this convoy reviewer (PAN-1806). */\n reviewRetryAttempt?: number;\n /** Path to the run's context manifest, used by missing-reviewer recovery. */\n reviewContextManifestPath?: string;\n hostOverride?: boolean;\n\n /** Inspect sub-role for inspect-* agents (PAN-1834). */\n inspectSubRole?: string;\n\n /** Registered swarm slot index for per-item work agents. */\n slotIndex?: number;\n /** xBRIEF item id explicitly assigned to this registered swarm slot. */\n slotItemId?: string;\n}\n\nconst toAgentFsError = (operation: string, path: string, cause: unknown): FsError =>\n new FsError({ operation, path, cause });\n\nexport function getAgentDir(agentId: string): string {\n return join(getOverdeckHome(), 'agents', agentId);\n}\n\nexport function getAgentStateFilePath(agentId: string): string {\n return getRollbackAgentStatePath(agentId);\n}\n\n/**\n * PAN-1985: wipe agent state directories for an issue, optionally scoped to\n * a role prefix. Used by the restart-fresh and review-restart paths to clean\n * state before respawning — the new agent then reads `.pan/continue.json`,\n * the xBRIEF, the beads, and the branch to pick up where the prior run left\n * off. Refuses to run against an empty or unsafe id.\n *\n * - `rolePrefix` omitted: wipes only the work agent dir (`agent-<id>`).\n * Specialist dirs (review, test, ship, etc.) are left alone.\n * - `rolePrefix` set (e.g. 'review'): wipes the role parent dir\n * (`agent-<id>-<prefix>`) AND any sub-roles (`agent-<id>-<prefix>-<anything>`),\n * leaving the work agent dir alone.\n *\n * Refuses to operate on the root `AGENTS_DIR` itself or on paths that escape\n * it; the `validateAgentId` guard below enforces a conservative id grammar.\n */\nexport async function wipeAgentStateDirs(\n issueId: string,\n opts: { rolePrefix?: string } = {},\n): Promise<{ removed: string[]; path: string }> {\n if (!issueId || !/^[A-Za-z]+-\\d+$/.test(issueId)) {\n throw new Error(`wipeAgentStateDirs: invalid issueId \"${issueId}\"`);\n }\n if (opts.rolePrefix !== undefined && !/^[a-z][a-z0-9-]*$/.test(opts.rolePrefix)) {\n throw new Error(`wipeAgentStateDirs: invalid rolePrefix \"${opts.rolePrefix}\"`);\n }\n const issueLower = issueId.toLowerCase();\n const dirPath = join(AGENTS_DIR, `agent-${issueLower}${opts.rolePrefix ? `-${opts.rolePrefix}` : ''}`);\n let entries: string[];\n try {\n entries = await readdir(AGENTS_DIR);\n } catch {\n return { removed: [], path: dirPath };\n }\n let targets: string[];\n if (opts.rolePrefix) {\n const base = `agent-${issueLower}-${opts.rolePrefix}`;\n targets = entries.filter((name) => name === base || name.startsWith(`${base}-`));\n } else {\n const work = `agent-${issueLower}`;\n targets = entries.filter((name) => name === work);\n }\n for (const name of targets) {\n try {\n await removeAgentStateDir(join(AGENTS_DIR, name));\n } catch { /* non-fatal — best-effort wipe */ }\n }\n return { removed: targets, path: dirPath };\n}\n\nexport { isRole } from './role.js';\n\nfunction cleanAgentState(raw: AgentState): AgentState {\n return {\n id: raw.id,\n issueId: raw.issueId,\n workspace: raw.workspace,\n harness: raw.harness,\n role: raw.role,\n model: raw.model,\n status: raw.status,\n startedAt: raw.startedAt,\n lastActivity: raw.lastActivity,\n lastResumeAt: raw.lastResumeAt,\n kickoffDelivered: raw.kickoffDelivered,\n stoppedAt: raw.stoppedAt,\n stoppedByUser: raw.stoppedByUser,\n stoppedByPause: raw.stoppedByPause,\n paused: raw.paused,\n pausedReason: raw.pausedReason,\n pausedAt: raw.pausedAt,\n yieldedByScheduler: raw.yieldedByScheduler,\n yieldedAt: raw.yieldedAt,\n lastYieldResumeAt: raw.lastYieldResumeAt,\n troubled: raw.troubled,\n troubledAt: raw.troubledAt,\n consecutiveFailures: raw.consecutiveFailures,\n firstFailureInRunAt: raw.firstFailureInRunAt,\n lastFailureAt: raw.lastFailureAt,\n lastFailureReason: raw.lastFailureReason,\n lastFailureNextRetryAt: raw.lastFailureNextRetryAt,\n branch: raw.branch,\n costSoFar: raw.costSoFar,\n sessionId: raw.sessionId,\n roleRunHead: raw.roleRunHead,\n flywheelRunId: raw.flywheelRunId,\n startedBy: raw.startedBy,\n channelsEnabled: raw.channelsEnabled,\n supervisorEnabled: raw.supervisorEnabled,\n deliveryMethod: raw.deliveryMethod,\n reviewSubRole: raw.reviewSubRole,\n reviewRunId: raw.reviewRunId,\n reviewOutputPath: raw.reviewOutputPath,\n reviewSynthesisAgentId: raw.reviewSynthesisAgentId,\n reviewDeadlineAt: raw.reviewDeadlineAt,\n reviewMonitorSignaled: raw.reviewMonitorSignaled,\n reviewRetryAttempt: raw.reviewRetryAttempt,\n reviewContextManifestPath: raw.reviewContextManifestPath,\n hostOverride: raw.hostOverride,\n inspectSubRole: raw.inspectSubRole,\n slotIndex: raw.slotIndex,\n slotItemId: raw.slotItemId,\n };\n}\n\nfunction parseAgentState(content: string, normalizedId: string): AgentState | null {\n try {\n const state = JSON.parse(content) as Partial<AgentState>;\n if (!isRole(state.role)) {\n // Roleless states are invisible to getAgentState; cleanup is handled\n // by warnOnBareNumericIssueIds / dropLegacyAgentStatesMissingRoleAsync.\n return null;\n }\n if (!state.id) state.id = normalizedId;\n return cleanAgentState(state as AgentState);\n } catch {\n return null;\n }\n}\n\nexport function getAgentStateSync(agentId: string): AgentState | null {\n const normalizedId = normalizeAgentId(agentId);\n\n const overdeckState = getOverdeckAgentStateSync(normalizedId);\n if (overdeckState) return cleanAgentState(overdeckState);\n\n const state = readRollbackAgentStateSync(normalizedId, parseAgentState);\n if (!state) return null;\n\n // PAN-1919: harness/model are no longer sourced from state.json. Merge from\n // the per-issue git-tracked record so cross-machine pickup works.\n if (state.issueId) {\n const record = readAgentHarnessModelRecordSync(state.issueId);\n if (record?.harness) state.harness = record.harness;\n if (record?.model) state.model = record.model;\n }\n\n return state;\n}\n\nregisterPipelineTelemetryAgentReader(getAgentStateSync);\n\nexport const getAgentState = (agentId: string): Effect.Effect<AgentState | null, FsError> => {\n return Effect.try({\n try: () => getAgentStateSync(agentId),\n catch: (cause) => toAgentFsError('read', `agents-db:${agentId}`, cause),\n });\n};\n\nfunction prepareAgentStateForSave(state: AgentState): AgentState {\n if (state.status === 'running' || state.status === 'starting') {\n delete state.stoppedAt;\n } else if (state.status === 'stopped' && !state.stoppedAt) {\n state.stoppedAt = new Date().toISOString();\n }\n return state;\n}\n\nasync function recordDurableStoppedTransition(\n state: AgentState,\n oldStatus: AgentState['status'] | undefined,\n): Promise<void> {\n if (state.status !== 'stopped' || oldStatus === 'stopped') return;\n try {\n await appendAgentPlaneLifecycle(state, {\n at: state.stoppedAt ?? new Date().toISOString(),\n event: 'stopped',\n });\n } catch (error) {\n console.warn(\n `[agents] Could not append durable stopped lifecycle for ${state.id}: `\n + `${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\nexport function writeAgentStateJsonSync(state: AgentState): void {\n writeRollbackAgentStateSync(state, (clean) => JSON.stringify(cleanAgentState(clean), null, 2));\n}\n\nexport function saveAgentStateSync(state: AgentState): void {\n // Detect status transition for audit trail\n const oldState = getAgentStateSync(state.id);\n const oldStatus = oldState?.status;\n\n prepareAgentStateForSave(state);\n\n saveOverdeckAgentStateSync(state);\n writeAgentStateJsonSync(state);\n\n // PAN-1919: mirror harness/model into the per-issue git-tracked record so\n // they travel with the branch. Done synchronously at save time; auto-commit\n // is suppressed here because spawn paths explicitly queue the commit.\n if (state.issueId && state.harness && state.model) {\n try {\n writeAgentHarnessModelRecordSync(state.issueId, state.harness, state.model);\n } catch (err) {\n console.warn(`[agents] Failed to mirror harness/model to record for ${state.issueId}: ${(err as Error).message}`);\n }\n }\n\n void recordDurableStoppedTransition(state, oldStatus);\n if (oldStatus && oldStatus !== state.status) {\n logAgentLifecycleSync(state.id, `status changed: ${oldStatus} → ${state.status} (saveAgentState)`);\n }\n}\n\n/**\n * Persist observed harness activity through the agent-state write door without\n * re-running lifecycle side effects such as harness/model record mirroring.\n */\nexport function recordAgentActivitySync(\n agentId: string,\n activity: { at?: string; costSoFar?: number },\n): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n\n state.lastActivity = activity.at ?? new Date().toISOString();\n if (activity.costSoFar !== undefined && Number.isFinite(activity.costSoFar)) {\n state.costSoFar = activity.costSoFar;\n }\n // An activity write is telemetry — it must never take down the caller. On\n // 2026-08-13 an unhandled SQLITE_BUSY here crashed the PAN-3668 review\n // orchestrator's app-server host mid-synthesis. The JSON mirror still runs\n // when the DB write fails.\n try {\n saveOverdeckAgentStateSync(state);\n } catch (err) {\n console.warn(`[agents] activity DB write failed for ${agentId} (non-fatal): ${err instanceof Error ? err.message : String(err)}`);\n }\n writeAgentStateJsonSync(state);\n return true;\n}\n\nexport const saveAgentState = (state: AgentState): Effect.Effect<void, FsError> => {\n const dir = getAgentDir(state.id);\n const stateFile = getRollbackAgentStatePath(state.id);\n\n return Effect.gen(function* () {\n yield* Effect.tryPromise({\n try: () => mkdirAsync(dir, { recursive: true }),\n catch: (cause) => toAgentFsError('mkdir', dir, cause),\n });\n\n const oldState = yield* getAgentState(state.id);\n const oldStatus = oldState?.status;\n\n if (state.status === 'running' || state.status === 'starting') {\n delete state.stoppedAt;\n } else if (state.status === 'stopped' && !state.stoppedAt) {\n state.stoppedAt = new Date().toISOString();\n }\n\n yield* Effect.try({\n try: () => saveOverdeckAgentStateSync(state),\n catch: (cause) => toAgentFsError('write', `agents-db:${state.id}`, cause),\n });\n\n yield* Effect.tryPromise({\n try: () => writeFileAsync(stateFile, JSON.stringify(cleanAgentState(state), null, 2)),\n catch: (cause) => toAgentFsError('write', stateFile, cause),\n });\n recordFeatureRegistryAgentState(state);\n\n // PAN-1919: mirror harness/model into the per-issue git-tracked record.\n if (state.harness && state.model) {\n yield* Effect.try({\n try: () => writeAgentHarnessModelRecordSync(state.issueId, state.harness!, state.model!),\n catch: (cause) => toAgentFsError('write', `record:${state.issueId}`, cause),\n });\n }\n\n yield* Effect.promise(() => recordDurableStoppedTransition(state, oldStatus));\n if (oldStatus && oldStatus !== state.status) {\n logAgentLifecycleSync(state.id, `status changed: ${oldStatus} → ${state.status} (saveAgentStateProgram)`);\n }\n });\n};\n\nfunction recordFeatureRegistryAgentState(state: AgentState): void {\n const status = state.status === 'starting' || state.status === 'running' ? 'active' : 'deferred';\n void recordFeatureRegistryLifecycle({\n issueId: state.issueId,\n workspacePath: state.workspace,\n agentId: state.id,\n status,\n });\n}\n\nfunction clearFailureTrackingFields(state: AgentState): void {\n state.consecutiveFailures = 0;\n delete state.firstFailureInRunAt;\n delete state.lastFailureAt;\n delete state.lastFailureReason;\n delete state.lastFailureNextRetryAt;\n}\n\n/**\n * Marker prefix used by the flywheel orchestrator when pausing an agent solely\n * to free a governor work slot. Pauses for this reason must never leave the\n * agent troubled (PAN-1812).\n */\nexport const GOVERNOR_SLOT_PAUSE_REASON_PREFIX = '[governor-slot]';\n\nfunction isGovernorSlotPauseReason(reason: string | undefined): boolean {\n return reason !== undefined && reason.startsWith(GOVERNOR_SLOT_PAUSE_REASON_PREFIX);\n}\n\n/** Sets the persistent manual pause gate used before stopping or suppressing resume. */\nfunction applyAgentPaused(state: AgentState, reason?: string, stoppedByPause = false): void {\n if (!state.paused) {\n state.pausedAt = new Date().toISOString();\n }\n state.paused = true;\n if (stoppedByPause) {\n state.stoppedByPause = true;\n }\n if (reason === undefined) {\n delete state.pausedReason;\n } else {\n state.pausedReason = reason;\n }\n\n // PAN-1812: a governor slot pause is a resource-hygiene action, not a fault.\n // Clear any existing troubled gate so the agent remains resumable when a slot\n // frees.\n if (isGovernorSlotPauseReason(reason)) {\n delete state.troubled;\n delete state.troubledAt;\n }\n}\n\n/** Sets the persistent manual pause gate used before stopping or suppressing resume. */\nexport function setAgentPausedSync(agentId: string, reason?: string, stoppedByPause = false): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n\n applyAgentPaused(state, reason, stoppedByPause);\n saveAgentStateSync(state);\n return true;\n}\n\nexport const setAgentPaused = (\n agentId: string,\n reason?: string,\n stoppedByPause = false,\n): Effect.Effect<AgentState | null, FsError> =>\n Effect.gen(function* () {\n const state = yield* getAgentState(agentId);\n if (!state) return null;\n\n applyAgentPaused(state, reason, stoppedByPause);\n yield* saveAgentState(state);\n return state;\n });\n\n/**\n * PAN-2507: pause an agent as a scheduler YIELD (distinct from an operator\n * pause). Reuses the pause write path so every no-resume gate protects the\n * yielded agent, and stamps the yield attribution used for oldest-first resume.\n */\nfunction applyAgentYielded(state: AgentState, reason: string): void {\n applyAgentPaused(state, reason, true);\n state.yieldedByScheduler = true;\n state.yieldedAt = new Date().toISOString();\n}\n\n/** PAN-2507: set the yield gate (pause + scheduler attribution) in one write. */\nexport function setAgentYieldedSync(agentId: string, reason: string): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n applyAgentYielded(state, reason);\n saveAgentStateSync(state);\n return true;\n}\n\n/**\n * PAN-2507: clear a scheduler yield ahead of a resume — drops the pause + yield\n * attribution (so `resumeAgent`'s pause gate passes) and stamps\n * `lastYieldResumeAt` to arm the re-yield cooldown, in a single write.\n */\nexport function clearYieldForResumeSync(agentId: string): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n applyAgentUnpaused(state);\n state.lastYieldResumeAt = new Date().toISOString();\n saveAgentStateSync(state);\n return true;\n}\n\nfunction applyAgentUnpaused(state: AgentState): void {\n if (state.stoppedByPause === true) {\n delete state.stoppedByUser;\n }\n delete state.stoppedByPause;\n delete state.paused;\n delete state.pausedReason;\n delete state.pausedAt;\n // PAN-2507 (FR-5): clearing a pause also clears the scheduler-yield\n // attribution, so an operator `pan unpause` on a yielded agent self-clears\n // the yield. `lastYieldResumeAt` is deliberately preserved — it is a\n // re-yield cooldown tracker, not a pause field.\n delete state.yieldedByScheduler;\n delete state.yieldedAt;\n}\n\nfunction isAgentPauseClear(state: AgentState): boolean {\n return !state.paused && state.pausedReason === undefined && state.pausedAt === undefined\n && state.yieldedByScheduler === undefined && state.yieldedAt === undefined;\n}\n\n/** Clears the persistent manual pause gate without spawning the agent. */\nexport function clearAgentPausedSync(agentId: string): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n if (isAgentPauseClear(state)) return true;\n\n applyAgentUnpaused(state);\n saveAgentStateSync(state);\n return true;\n}\n\nexport const clearAgentPaused = (agentId: string): Effect.Effect<AgentState | null, FsError> =>\n Effect.gen(function* () {\n const state = yield* getAgentState(agentId);\n if (!state) return null;\n if (isAgentPauseClear(state)) return state;\n\n applyAgentUnpaused(state);\n yield* saveAgentState(state);\n return state;\n });\n\n/** Marks an agent as troubled after repeated resume failures. */\nexport function markAgentTroubled(agentId: string): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n\n if (!state.troubled) {\n state.troubledAt = new Date().toISOString();\n }\n state.troubled = true;\n saveAgentStateSync(state);\n return true;\n}\n\nfunction isAgentTroubledClear(state: AgentState): boolean {\n return !state.troubled && state.troubledAt === undefined && (state.consecutiveFailures ?? 0) === 0 && state.firstFailureInRunAt === undefined && state.lastFailureAt === undefined && state.lastFailureReason === undefined && state.lastFailureNextRetryAt === undefined;\n}\n\nfunction applyAgentUntroubled(state: AgentState): void {\n delete state.troubled;\n delete state.troubledAt;\n clearFailureTrackingFields(state);\n}\n\n/** Clears the troubled gate and its accumulated failure state. */\nexport function clearAgentTroubledSync(agentId: string): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n if (isAgentTroubledClear(state)) return true;\n\n applyAgentUntroubled(state);\n saveAgentStateSync(state);\n return true;\n}\n\nexport const clearAgentTroubled = (agentId: string): Effect.Effect<AgentState | null, FsError> =>\n Effect.gen(function* () {\n const state = yield* getAgentState(agentId);\n if (!state) return null;\n if (isAgentTroubledClear(state)) return state;\n\n applyAgentUntroubled(state);\n yield* saveAgentState(state);\n return state;\n });\n\n/**\n * Clear operator-gate residue (stoppedByUser, paused, troubled) on STOPPED\n * agent rows whose issueId is in `issueIds` — used by close-out and the\n * terminal-issue residue patrol so a terminal issue's preserved agent rows\n * stop reappearing in the operator-gate parked orbit (PAN-3727). Running/\n * starting agents and scheduler yields are never touched: a yield is a live\n * scheduling decision, not operator residue, and a live agent's gates are not\n * this issue's to clear.\n *\n * Batched (one `listOverdeckAgentStatesSync()` scan for every issueId in the\n * set) so the recurring residue patrol does not perform one full agent-table\n * scan per terminal issue (review finding, PAN-3727) — cost scales with the\n * agent table once per patrol run, not with the number of terminal issues.\n */\nexport function clearAgentOperatorGatesForIssuesSync(issueIds: ReadonlySet<string>): Map<string, string[]> {\n const mutated = new Map<string, string[]>();\n if (issueIds.size === 0) return mutated;\n\n for (const state of listOverdeckAgentStatesSync()) {\n if (state.status !== 'stopped') continue;\n const normalized = state.issueId?.toUpperCase();\n if (!normalized || !issueIds.has(normalized)) continue;\n\n let changed = false;\n if (state.stoppedByUser) {\n delete state.stoppedByUser;\n changed = true;\n }\n if (state.paused === true && state.yieldedByScheduler !== true) {\n applyAgentUnpaused(state);\n changed = true;\n }\n if (state.troubled === true) {\n applyAgentUntroubled(state);\n changed = true;\n }\n if (changed) {\n saveAgentStateSync(state);\n const ids = mutated.get(normalized) ?? [];\n ids.push(state.id);\n mutated.set(normalized, ids);\n }\n }\n return mutated;\n}\n\n/** Single-issue convenience wrapper over {@link clearAgentOperatorGatesForIssuesSync}. */\nexport function clearAgentOperatorGatesForIssueSync(issueId: string): string[] {\n const normalized = issueId.toUpperCase();\n return clearAgentOperatorGatesForIssuesSync(new Set([normalized])).get(normalized) ?? [];\n}\n\nfunction applyAgentFailure(state: AgentState, reason: string): void {\n const config = resolveAutoResumeConfigForIssue(state.issueId);\n const nowMs = Date.now();\n const now = new Date(nowMs).toISOString();\n const firstFailureMs = Date.parse(state.firstFailureInRunAt ?? '');\n const hasValidFirstFailure = Number.isFinite(firstFailureMs);\n const windowElapsed = hasValidFirstFailure\n && nowMs - firstFailureMs > config.troubledWindowMs;\n\n if (windowElapsed || !hasValidFirstFailure) {\n state.consecutiveFailures = 1;\n state.firstFailureInRunAt = now;\n } else {\n state.consecutiveFailures = (state.consecutiveFailures ?? 0) + 1;\n }\n\n const backoffSeconds = config.failureBackoffSchedule[\n Math.min(state.consecutiveFailures - 1, config.failureBackoffSchedule.length - 1)\n ];\n state.lastFailureAt = now;\n state.lastFailureReason = reason;\n state.lastFailureNextRetryAt = new Date(nowMs + backoffSeconds * 1000).toISOString();\n\n const firstFailureInRunMs = Date.parse(state.firstFailureInRunAt ?? '');\n const shouldMarkTroubled = state.consecutiveFailures >= config.maxConsecutiveFailures\n && Number.isFinite(firstFailureInRunMs)\n && nowMs - firstFailureInRunMs <= config.troubledWindowMs;\n\n if (shouldMarkTroubled) {\n if (!state.troubled) {\n state.troubledAt = now;\n }\n state.troubled = true;\n }\n}\n\n/** Records one failed resume/crash observation for later backoff and troubled gating. */\nexport function recordAgentFailureSync(agentId: string, reason: string): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n\n applyAgentFailure(state, reason);\n saveAgentStateSync(state);\n return true;\n}\n\nexport const recordAgentFailure = (agentId: string, reason: string): Effect.Effect<AgentState | null, FsError> =>\n Effect.gen(function* () {\n const state = yield* getAgentState(agentId);\n if (!state) return null;\n\n applyAgentFailure(state, reason);\n yield* saveAgentState(state);\n return state;\n });\n\n/** Resets failure tracking after an agent reaches running state. */\nexport function resetAgentFailureCount(agentId: string): boolean {\n const state = getAgentStateSync(agentId);\n if (!state) return false;\n if ((state.consecutiveFailures ?? 0) === 0 && state.firstFailureInRunAt === undefined && state.lastFailureAt === undefined && state.lastFailureReason === undefined && state.lastFailureNextRetryAt === undefined) return true;\n\n clearFailureTrackingFields(state);\n saveAgentStateSync(state);\n return true;\n}\n\n/** Reports whether callers should block start, resume, auto-resume, or message delivery on the manual pause gate. */\nexport function isAgentPaused(agentId: string): boolean {\n return getAgentStateSync(agentId)?.paused === true;\n}\n\n/** Reports whether callers should block start, resume, auto-resume, or message delivery on the troubled gate. */\nexport function isAgentTroubled(agentId: string): boolean {\n return getAgentStateSync(agentId)?.troubled === true;\n}\n\nexport async function recordStartupSessionExit(state: AgentState, issueId: string, source: Role | 'work-agent'): Promise<never> {\n await Effect.runPromise(recordAgentFailure(state.id, SESSION_EXITED_BEFORE_KICKOFF));\n const failedState = await Effect.runPromise(getAgentState(state.id));\n if (failedState) {\n failedState.status = 'stopped';\n failedState.stoppedAt = new Date().toISOString();\n failedState.kickoffDelivered = false;\n failedState.lastFailureReason = SESSION_EXITED_BEFORE_KICKOFF;\n await Effect.runPromise(saveAgentState(failedState));\n }\n state.status = 'stopped';\n state.stoppedAt = new Date().toISOString();\n state.kickoffDelivered = false;\n state.lastFailureReason = SESSION_EXITED_BEFORE_KICKOFF;\n emitActivityEntrySync({\n source,\n level: 'error',\n message: `${state.id}: session exited before kickoff could be delivered`,\n issueId,\n });\n throw new Error(`Agent ${state.id} exited before kickoff could be delivered`);\n}\n\nexport type ResumeGateBlock =\n | { gate: 'paused'; reason: string; pausedReason?: string; yieldedByScheduler?: boolean }\n | { gate: 'troubled'; reason: string; troubledAt?: string }\n | { gate: 'stopped-by-user'; reason: string }\n | { gate: 'failure-backoff'; reason: string; consecutiveFailures: number };\n\nexport type ResumeIntent = 'autonomous' | 'operator-start' | 'message-delivery' | 'merge-preparation';\n\n/** PAN-2668: messageAgent options — owesRework marks the message as pipeline\n * feedback requiring rework (failed verification/review), which may re-drive\n * a stopped-by-user agent that has a completed handoff. */\nexport interface MessageAgentRedriveOptions {\n owesRework?: boolean;\n /**\n * Idempotency key threaded to the delivery door (PAN-2997). Keyed live\n * deliveries are deduplicated by the crash-independent delivery component\n * (supervisor key set / tmux session option), so a dashboard crash and\n * recovery can never inject the same keyed message twice.\n */\n dedupKey?: string;\n}\n\nexport interface ResumeGateContext {\n hasCompletedHandoff?: boolean;\n owesRework?: boolean;\n}\n\nexport type ResumeGateDecision =\n | { decision: 'proceed'; clearStoppedByUser?: true; clearYield?: true; overrideFailureBackoff?: true; warning?: string }\n | { decision: 'block'; reason: string; needsYou?: true }\n | { decision: 'queue-message'; reason: string };\n\n/** Pure classification only. Call decideResumeGate before acting on the classified gate. */\nexport function getAgentResumeGateBlockReason(\n state: Pick<AgentState, 'paused' | 'pausedReason' | 'yieldedByScheduler' | 'troubled' | 'troubledAt' | 'stoppedByUser' | 'consecutiveFailures'>,\n): ResumeGateBlock | undefined {\n if (state.paused === true) {\n return {\n gate: 'paused',\n reason: state.pausedReason ? `agent is paused (${state.pausedReason})` : 'agent is paused',\n pausedReason: state.pausedReason,\n yieldedByScheduler: state.yieldedByScheduler,\n };\n }\n if (state.troubled === true) {\n const failures = state.consecutiveFailures ?? 0;\n return {\n gate: 'troubled',\n reason: `agent is troubled (${failures} failure${failures === 1 ? '' : 's'})`,\n troubledAt: state.troubledAt,\n };\n }\n if (state.stoppedByUser === true) return { gate: 'stopped-by-user', reason: 'agent was stopped by the operator' };\n const failures = state.consecutiveFailures ?? 0;\n if (failures > 0) return {\n gate: 'failure-backoff',\n reason: `agent is in failure backoff (${failures} failure${failures === 1 ? '' : 's'})`,\n consecutiveFailures: failures,\n };\n return undefined;\n}\n\n/** Intent-aware policy over a classified gate. This function never mutates agent state. */\nexport function decideResumeGate(\n block: ResumeGateBlock | undefined,\n intent: ResumeIntent,\n context: ResumeGateContext = {},\n): ResumeGateDecision {\n if (!block) return { decision: 'proceed' };\n if (intent === 'message-delivery') {\n // PAN-2668: pipeline feedback that owes rework is a re-drive, not a casual\n // message. The same completed-handoff exception as the autonomous intent\n // applies — otherwise verification/review feedback for an operator-stopped\n // agent is silently mailed to a queue nothing will ever drain.\n if (block.gate === 'stopped-by-user' && context.hasCompletedHandoff === true && context.owesRework === true) {\n return { decision: 'proceed', clearStoppedByUser: true };\n }\n return { decision: 'queue-message', reason: block.reason };\n }\n\n // PAN-3120: clicking MERGE is an explicit operator action on this issue, and\n // the polyrepo/rebase path NEEDS the work agent. A scheduler yield is a\n // resource pause the system chose on its own (self-clearing by design), so it\n // must never be what stops a merge the operator asked for. An operator's own\n // pause, or a troubled agent, still blocks — those are decisions/faults a\n // human should see rather than have silently overridden.\n if (intent === 'merge-preparation') {\n if (block.gate === 'paused') {\n if (block.yieldedByScheduler === true) return { decision: 'proceed', clearYield: true };\n return { decision: 'block', reason: `${block.reason}; run pan unpause before merging`, needsYou: true };\n }\n if (block.gate === 'troubled') {\n return { decision: 'block', reason: `${block.reason}; run pan untroubled after investigating the crash`, needsYou: true };\n }\n if (block.gate === 'stopped-by-user') return { decision: 'proceed', clearStoppedByUser: true };\n return { decision: 'proceed', overrideFailureBackoff: true, warning: `Merge preparation overrides ${block.reason}` };\n }\n\n if (intent === 'operator-start') {\n if (block.gate === 'paused') return { decision: 'block', reason: `${block.reason}; run pan unpause first` };\n if (block.gate === 'troubled') return { decision: 'block', reason: `${block.reason}; run pan untroubled first` };\n if (block.gate === 'stopped-by-user') return { decision: 'proceed', clearStoppedByUser: true };\n return {\n decision: 'proceed',\n overrideFailureBackoff: true,\n warning: `Operator start overrides ${block.reason}`,\n };\n }\n\n if (block.gate === 'stopped-by-user') {\n if (context.hasCompletedHandoff === true && context.owesRework === true) {\n return { decision: 'proceed', clearStoppedByUser: true };\n }\n return { decision: 'block', reason: block.reason, needsYou: true };\n }\n return { decision: 'block', reason: block.reason };\n}\n\nfunction assertAgentCanTransitionToRunning(state: AgentState): void {\n const decision = decideResumeGate(getAgentResumeGateBlockReason(state), 'operator-start');\n if (decision.decision === 'block') {\n throw new Error(`Cannot run ${state.id}: ${decision.reason}. Clear the gate before resuming.`);\n }\n}\n\nexport function markAgentRunning(state: AgentState, options?: { preserveFailureTracking?: boolean }): void {\n assertAgentCanTransitionToRunning(state);\n const oldStatus = state.status;\n state.status = 'running';\n // Codex activity comes from app-server notifications or rollout JSONL. A\n // synthetic spawn timestamp becomes permanently stale in the TUI fallback.\n if (state.harness !== 'codex') state.lastActivity = new Date().toISOString();\n if (options?.preserveFailureTracking !== true) {\n clearFailureTrackingFields(state);\n }\n delete state.stoppedAt;\n // Clear user-stop intent so a later crash/orphan can be auto-resumed. Without\n // this the flag is sticky across the stop→resume→crash sequence and autoResume\n // would permanently skip the agent on any subsequent orphan recovery.\n delete state.stoppedByUser;\n logAgentLifecycleSync(state.id, `status changed: ${oldStatus} → running (markAgentRunning)`);\n}\n\nfunction markAgentStopped(state: AgentState, cause: AgentStopCause): void {\n const oldStatus = state.status;\n state.status = 'stopped';\n state.stoppedAt = new Date().toISOString();\n if (cause === 'operator') {\n state.stoppedByUser = true;\n } else {\n // PAN-3324: a crash, OOM kill, or machinery-initiated stop is not operator\n // intent. Recording it as one engages the operator-stop gate, which turns a\n // transient resource event into a permanent stall no patrol can recover.\n delete state.stoppedByUser;\n }\n logAgentLifecycleSync(state.id, `status changed: ${oldStatus} → stopped (markAgentStopped, cause=${cause})`);\n}\n\nexport function markAgentStoppedState(state: AgentState, cause: AgentStopCause = 'system'): AgentState {\n if (!state.id) {\n state.id = normalizeAgentId(state.issueId);\n }\n markAgentStopped(state, cause);\n return state;\n}\n\nexport function markAgentRunningState(state: AgentState): AgentState {\n if (!state.id) {\n state.id = normalizeAgentId(state.issueId);\n }\n markAgentRunning(state);\n return state;\n}\n\n/** Test-only internals. Do not import outside of test files. */\nexport const __testInternals = { markAgentRunning, markAgentStopped };\n"],"mappings":"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"}
|