@askexenow/exe-os 0.9.409 → 0.9.410
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/deploy/stack-manifests/v0.9.json +408 -1
- package/dist/.dist-verified.json +3 -3
- package/dist/{active-agent-5AKPW2N7.js → active-agent-2ELFTYNT.js} +4 -4
- package/dist/{active-agent-2AMJC3SC.js → active-agent-SS4SRXYZ.js} +4 -4
- package/dist/agent-defs-generator-6S2BSYCP.js +32 -0
- package/dist/{agentic-ontology-6RS7A7KP.js → agentic-ontology-TJOAUAPD.js} +1 -1
- package/dist/{artifact-backup-6O4FNYPS.js → artifact-backup-STQQZDZ5.js} +3 -3
- package/dist/{backfill-metadata-6YX6LGPJ.js → backfill-metadata-I2FVENKJ.js} +7 -7
- package/dist/{backup-verify-QIAQ63VC.js → backup-verify-AOTKX5DA.js} +69 -4
- package/dist/{behavior-injection-NRXTNROW.js → behavior-injection-CTSIKHTH.js} +10 -10
- package/dist/{behavior-promotion-MW7OQ72V.js → behavior-promotion-NZMTTFHK.js} +9 -9
- package/dist/{behaviors-7AOK44QK.js → behaviors-RUNSC5WI.js} +8 -8
- package/dist/bin/agentic-ontology-backfill.js +8 -8
- package/dist/bin/agentic-reflection-backfill.js +9 -9
- package/dist/bin/agentic-semantic-label.js +8 -8
- package/dist/bin/backfill-conversations.js +7 -7
- package/dist/bin/backfill-responses.js +7 -7
- package/dist/bin/backfill-vectors.js +7 -7
- package/dist/bin/bulk-sync-postgres.js +16 -16
- package/dist/bin/cc-doctor.js +8 -8
- package/dist/bin/cleanup-stale-review-tasks.js +13 -16
- package/dist/bin/cli.js +24 -24
- package/dist/bin/deferred-daemon-restart.js +95 -95
- package/dist/bin/exe-agent-config.js +3 -3
- package/dist/bin/exe-agent.js +11 -11
- package/dist/bin/exe-assign.js +8 -8
- package/dist/bin/exe-boot.js +33 -36
- package/dist/bin/exe-call.js +8 -8
- package/dist/bin/exe-cloud.js +26 -22
- package/dist/bin/exe-config-dump.js +3 -3
- package/dist/bin/exe-dispatch.js +14 -17
- package/dist/bin/exe-doctor.js +5 -5
- package/dist/bin/exe-export-behaviors.js +12 -12
- package/dist/bin/exe-forget.js +12 -12
- package/dist/bin/exe-gateway.js +9 -9
- package/dist/bin/exe-grant.js +6 -6
- package/dist/bin/exe-healthcheck.js +8 -8
- package/dist/bin/exe-heartbeat.js +19 -22
- package/dist/bin/exe-kill.js +18 -21
- package/dist/bin/exe-launch-agent.js +215 -100
- package/dist/bin/exe-mcp.js +5 -5
- package/dist/bin/exe-memory-reads.js +7 -7
- package/dist/bin/exe-new-employee.js +10 -10
- package/dist/bin/exe-os-mcp-proxy.js +5 -5
- package/dist/bin/exe-pending-messages.js +14 -17
- package/dist/bin/exe-pending-notifications.js +13 -16
- package/dist/bin/exe-pending-reviews.js +14 -17
- package/dist/bin/exe-rename.js +6 -6
- package/dist/bin/exe-review.js +17 -20
- package/dist/bin/exe-search.js +11 -11
- package/dist/bin/exe-session-cleanup.js +18 -21
- package/dist/bin/exe-settings.js +16 -16
- package/dist/bin/exe-start-codex.js +18 -18
- package/dist/bin/exe-start-opencode.js +16 -16
- package/dist/bin/exe-status.js +15 -18
- package/dist/bin/exe-support.js +3 -3
- package/dist/bin/exe-team.js +5 -5
- package/dist/bin/git-sweep.js +15 -17
- package/dist/bin/graph-backfill.js +8 -8
- package/dist/bin/graph-export.js +8 -8
- package/dist/bin/hooks-cli.js +1 -1
- package/dist/bin/import-history.js +11 -11
- package/dist/bin/install.js +97 -97
- package/dist/bin/intercom-check.js +5 -5
- package/dist/bin/mcp-sessions.js +2 -2
- package/dist/bin/orchestration-metrics.js +5 -5
- package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
- package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
- package/dist/bin/reconcile-backlog.js +2 -2
- package/dist/bin/registry-proxy.js +1 -1
- package/dist/bin/repair-document-chunks.js +7 -7
- package/dist/bin/scan-tasks.js +14 -17
- package/dist/bin/setup.js +2 -2
- package/dist/bin/shard-migrate.js +8 -8
- package/dist/bin/stack-update.js +4 -4
- package/dist/bin/vps-health-gate.js +1 -1
- package/dist/{boot-generation-HG6LLZLY.js → boot-generation-4W4CDJSX.js} +4 -4
- package/dist/{boot-review-sections-STZ5EAIL.js → boot-review-sections-HJN25GMU.js} +12 -15
- package/dist/{bug-landed-reconciler-EO72HUI2.js → bug-landed-reconciler-GGQU6G2L.js} +6 -6
- package/dist/build-info.json +3 -3
- package/dist/build-manifest.json +989 -989
- package/dist/{capability-cards-C3SMHB2E.js → capability-cards-EV7VHL3W.js} +3 -3
- package/dist/{capacity-monitor-UI553HRL.js → capacity-monitor-YO3WRSRY.js} +14 -17
- package/dist/{catchup-brief-VE2X4HML.js → catchup-brief-4XFFW3H2.js} +21 -23
- package/dist/{chunk-R7QZXQPU.js → chunk-22XJWSWN.js} +216 -147
- package/dist/{chunk-XGPBHKU3.js → chunk-26CY4EAR.js} +2 -2
- package/dist/{chunk-E2Z3F26R.js → chunk-2A556FNI.js} +1 -1
- package/dist/{chunk-EIM7OGTD.js → chunk-2AXJRXA6.js} +2 -2
- package/dist/{chunk-DE2V7547.js → chunk-2CG3LHI6.js} +2 -2
- package/dist/{chunk-VLAENYCN.js → chunk-2K7IKLBL.js} +1 -1
- package/dist/{chunk-6JLVCMKH.js → chunk-2LJ2GZEE.js} +1 -1
- package/dist/{chunk-7I6KVKFF.js → chunk-2OIVIPU7.js} +3 -3
- package/dist/{chunk-6TB7UXCA.js → chunk-2Q2U3NB2.js} +1 -1
- package/dist/{chunk-QUK3UDZV.js → chunk-2WOHFX4Z.js} +1 -1
- package/dist/{chunk-23XUZHB7.js → chunk-2YK5HP2B.js} +245 -36
- package/dist/{chunk-LMF2YC5C.js → chunk-2YZZR2BW.js} +4 -4
- package/dist/{chunk-VMHZSZQJ.js → chunk-3ARSKDHC.js} +52 -0
- package/dist/{chunk-IZ24HRDC.js → chunk-3UEDFBDE.js} +3 -3
- package/dist/{chunk-TCV3WA5I.js → chunk-3WNDI5KT.js} +1 -1
- package/dist/{chunk-W5XAB2SW.js → chunk-47UBJKS3.js} +271 -167
- package/dist/{chunk-TBYWOHTE.js → chunk-4THJIDJE.js} +1 -1
- package/dist/{chunk-HRYWB4TO.js → chunk-4XY33GRO.js} +2 -2
- package/dist/{chunk-PCFYNK7V.js → chunk-53GNYXNR.js} +2 -2
- package/dist/{chunk-OFT5KSBM.js → chunk-54Q7M2AG.js} +1 -1
- package/dist/{chunk-GG73NHZD.js → chunk-575FWHQJ.js} +2 -2
- package/dist/{chunk-4W7Y67B5.js → chunk-5BWN6A5E.js} +1 -1
- package/dist/{chunk-KBHK2WNO.js → chunk-5F3E57TQ.js} +1 -1
- package/dist/{chunk-4S6H2NWS.js → chunk-5JHIAGZY.js} +1 -1
- package/dist/{chunk-IPANAGA3.js → chunk-5Q64Q5ZD.js} +1 -1
- package/dist/{chunk-LVEMYNYH.js → chunk-5WNZGNQL.js} +3 -3
- package/dist/{chunk-HYZV25LY.js → chunk-67IVG6CB.js} +1 -11
- package/dist/{chunk-5N34MMKS.js → chunk-6APOLYIZ.js} +43 -3
- package/dist/{chunk-HFXBL7XE.js → chunk-6T5RC3L4.js} +1 -1
- package/dist/{chunk-HUMNRJZE.js → chunk-6TOV7UVU.js} +4 -4
- package/dist/{chunk-HNGAM3U4.js → chunk-7QFRZT4H.js} +3 -3
- package/dist/{chunk-52Z435PH.js → chunk-7RVTQXST.js} +15 -10
- package/dist/{chunk-SY53PYLU.js → chunk-7WCYJXOF.js} +1 -1
- package/dist/{chunk-IURGBN5B.js → chunk-AEEAQZ3E.js} +4 -4
- package/dist/{chunk-HFSFWMID.js → chunk-AEOB4JCJ.js} +3 -3
- package/dist/{chunk-DM4VTXM6.js → chunk-AGXV5HLA.js} +4 -4
- package/dist/{chunk-NDCW4G55.js → chunk-AGY2KUQ6.js} +1 -1
- package/dist/{chunk-6RRPVE5O.js → chunk-APKEGWFB.js} +3 -3
- package/dist/{chunk-FJKUFMNF.js → chunk-ASA2UJDH.js} +1 -1
- package/dist/{chunk-GRAMRMWS.js → chunk-AWTLUJZL.js} +2 -2
- package/dist/{chunk-BHDDDNC6.js → chunk-B34RSIUP.js} +2 -2
- package/dist/{chunk-BPBEJD7U.js → chunk-B55U5BKF.js} +1 -1
- package/dist/{chunk-UXJAW4IQ.js → chunk-BBTNF72N.js} +2 -2
- package/dist/{chunk-IPBNHPTE.js → chunk-BIHPY7JW.js} +9 -9
- package/dist/{chunk-SYUJCVJG.js → chunk-BPXWWD7P.js} +1 -1
- package/dist/{chunk-G2M7WQ2M.js → chunk-CBC3JCIF.js} +1 -1
- package/dist/{chunk-GKPPLIMJ.js → chunk-CEHGXPMO.js} +4 -4
- package/dist/{chunk-L5CPS34O.js → chunk-CKRZBQ2K.js} +1 -1
- package/dist/{chunk-NHQWPKX7.js → chunk-CPC75PXE.js} +2 -2
- package/dist/{chunk-OEHZ46WQ.js → chunk-DLHE4PCM.js} +4 -4
- package/dist/{chunk-RZ23QQCI.js → chunk-DWK5HEKY.js} +4 -4
- package/dist/{chunk-YUAOQJVV.js → chunk-E2346GKI.js} +2 -2
- package/dist/{chunk-VYKXRSHU.js → chunk-E3ZO4EL3.js} +1 -1
- package/dist/{chunk-GTRB6DGI.js → chunk-FCX2XHIC.js} +2 -2
- package/dist/{chunk-K7DAE2HO.js → chunk-FOZSHNET.js} +19 -6
- package/dist/{chunk-7P2TDFZL.js → chunk-FRJICRED.js} +1 -1
- package/dist/{chunk-XCPD7NGP.js → chunk-FUEKSCTN.js} +1 -1
- package/dist/{chunk-B3ISLIKE.js → chunk-FYKEL2L6.js} +2 -2
- package/dist/{chunk-SFGFNJBR.js → chunk-FZT7CBXB.js} +1 -1
- package/dist/{chunk-NFNM64A7.js → chunk-GGT6Y5AN.js} +22 -18
- package/dist/{chunk-Q4GS5VW7.js → chunk-H5H2LGVE.js} +3 -3
- package/dist/{chunk-GEQQGN3J.js → chunk-HCBISGUU.js} +7 -7
- package/dist/{chunk-FZT4VQP7.js → chunk-HKAWEIB2.js} +1 -1
- package/dist/{chunk-QK3Z4TKQ.js → chunk-HLNRLXUS.js} +5 -5
- package/dist/{chunk-FBZ32BD4.js → chunk-HNJLAMD3.js} +1 -1
- package/dist/{chunk-6EY6OFBC.js → chunk-I35OMCL3.js} +2 -2
- package/dist/{chunk-OISOA6NX.js → chunk-IHBYJG7N.js} +1 -1
- package/dist/{chunk-KIJRAZWR.js → chunk-IQCC6WFI.js} +1 -1
- package/dist/{chunk-EHILXBVT.js → chunk-IUAW7FVX.js} +3 -3
- package/dist/{chunk-OE7TAEOG.js → chunk-IXRYS5X3.js} +1 -1
- package/dist/{chunk-4Q7H3TO7.js → chunk-J6RTX7XT.js} +1 -1
- package/dist/{chunk-BIV4IHED.js → chunk-JDZNGC4O.js} +15 -28
- package/dist/{chunk-JJYAETCO.js → chunk-JTXXLNYD.js} +4 -4
- package/dist/{chunk-YVS77JPW.js → chunk-JZ23D5YF.js} +3 -3
- package/dist/{chunk-QYIR64RR.js → chunk-K464YYT5.js} +1 -1
- package/dist/{chunk-BM467PZN.js → chunk-K6XWIJN7.js} +1 -1
- package/dist/{chunk-DIKLSAJE.js → chunk-KNEEOZWN.js} +1 -1
- package/dist/{chunk-3MDUKF3N.js → chunk-LBEBSXZA.js} +5 -4
- package/dist/{chunk-NK4JHJNP.js → chunk-LCXWQVOD.js} +4 -4
- package/dist/{chunk-3AL2LO3L.js → chunk-LZS2B2JT.js} +1 -1
- package/dist/{chunk-W2VDJKTB.js → chunk-MNWN3SQ3.js} +1 -1
- package/dist/{chunk-ZEHBSA25.js → chunk-MQD3JJCO.js} +1 -1
- package/dist/{chunk-KPLMY2GT.js → chunk-NUEDA2US.js} +4 -4
- package/dist/{chunk-F5ZRHLG5.js → chunk-NYV4A5IG.js} +2 -2
- package/dist/{chunk-M7774YLB.js → chunk-O5RKAXCG.js} +1 -1
- package/dist/{chunk-GPWXUU3L.js → chunk-OUUNFTS4.js} +1 -1
- package/dist/{chunk-G3ZYXXO7.js → chunk-PCF4QMJS.js} +10 -10
- package/dist/{chunk-HRKN3DQE.js → chunk-PONGX3YG.js} +7 -7
- package/dist/{chunk-JR3GTBV3.js → chunk-QH2V67NX.js} +1 -1
- package/dist/{chunk-4O7OUBEX.js → chunk-QKHQTHE6.js} +27 -15
- package/dist/{chunk-4M24XCTI.js → chunk-R2ZHPVF2.js} +53 -7
- package/dist/{chunk-HK7HIDXP.js → chunk-R3N5BCMM.js} +1 -1
- package/dist/{chunk-IUBCS6TA.js → chunk-RWM6TZH6.js} +3 -3
- package/dist/{chunk-N3JEYUDV.js → chunk-RXICR3D7.js} +1 -1
- package/dist/{chunk-QPMKVSHU.js → chunk-S2DTXCV6.js} +2 -2
- package/dist/{chunk-YLWKWEJJ.js → chunk-SLG3T26A.js} +3 -3
- package/dist/{chunk-7R7KDRKV.js → chunk-SUGAHHVR.js} +1 -1
- package/dist/{chunk-SGR4ZQAV.js → chunk-T7W75LII.js} +3 -3
- package/dist/{agent-defs-generator-QN3J6FVJ.js → chunk-TU5Y5ZNC.js} +4 -21
- package/dist/{chunk-LE3DIA25.js → chunk-UBTKHJCB.js} +6 -6
- package/dist/{chunk-ZYOIH5NY.js → chunk-UF5BDAF6.js} +6 -6
- package/dist/{chunk-4RQ62XXK.js → chunk-UG3YOQAC.js} +5 -5
- package/dist/{chunk-7LTAA55U.js → chunk-V7AID6MC.js} +1 -1
- package/dist/{chunk-SA66WRBT.js → chunk-WLV27PAO.js} +1 -1
- package/dist/{chunk-XAGIZVYI.js → chunk-WSYIQKZ6.js} +180 -1624
- package/dist/{chunk-KCRSJ3VU.js → chunk-WZPD4QSE.js} +2 -2
- package/dist/{chunk-APVU4Q5B.js → chunk-X2EBM5BQ.js} +16 -16
- package/dist/{chunk-XFGS22VE.js → chunk-XBCIRNNU.js} +1 -1
- package/dist/{chunk-M4N7I2H5.js → chunk-XC2KAJK4.js} +5 -5
- package/dist/{chunk-WYNIWJRN.js → chunk-XCKTCOK3.js} +5 -5
- package/dist/{chunk-PZDAPIQL.js → chunk-XJQGS4IM.js} +1 -1
- package/dist/{chunk-DACRR362.js → chunk-XMMWBLQK.js} +6 -6
- package/dist/{chunk-T2PAFWRK.js → chunk-YC6SZFVD.js} +5 -5
- package/dist/{chunk-6ZW6BRI4.js → chunk-YJ7MCZSY.js} +2 -2
- package/dist/{chunk-DMYJVGQM.js → chunk-YSDSQRE2.js} +1 -1
- package/dist/{chunk-R6CKQLEA.js → chunk-YWZGDFAL.js} +1 -1
- package/dist/{chunk-C426MZMD.js → chunk-ZBXZFOIF.js} +3 -3
- package/dist/{chunk-ME733Y2F.js → chunk-ZEDLED43.js} +37 -9
- package/dist/{cleanup-graph-garbage-NKAJSCWZ.js → cleanup-graph-garbage-N7FTWU6C.js} +8 -8
- package/dist/{cli-shutdown-F3DFDCB5.js → cli-shutdown-NCBQTN7G.js} +1 -1
- package/dist/{cloud-sync-command-3YBLSMAL.js → cloud-sync-command-UL7GNN3J.js} +2 -2
- package/dist/{cloud-sync-db-init-JWSPAFKY.js → cloud-sync-db-init-ASM7ZXBH.js} +1 -1
- package/dist/{cloud-sync-shutdown-4R6RIO67.js → cloud-sync-shutdown-HER4ISBU.js} +3 -3
- package/dist/{co-activation-2ZRANV3Q.js → co-activation-V5XXR327.js} +3 -3
- package/dist/{co-occurrence-6EELW2L2.js → co-occurrence-MKZ6YWSQ.js} +6 -6
- package/dist/{consent-passphrase-TDR263MC.js → consent-passphrase-YI3J4UEL.js} +2 -2
- package/dist/content-extractor-GWF2T7FK.js +13 -0
- package/dist/{conversation-entity-extractor-KUJN3MOH.js → conversation-entity-extractor-66G7ROQ3.js} +3 -3
- package/dist/{crdt-sync-IX43KVQ3.js → crdt-sync-Z62SWOKD.js} +4 -4
- package/dist/{crm-webhook-GSCIW6CE.js → crm-webhook-6GBACWJX.js} +2 -2
- package/dist/{cto-delegation-gate-ZWD4EFRR.js → cto-delegation-gate-FUKPBFCM.js} +12 -15
- package/dist/{daemon-orchestration-WL6V4QL4.js → daemon-orchestration-O3QPSJEP.js} +19 -21
- package/dist/{doc-graph-extractor-NJ7ZZC2C.js → doc-graph-extractor-BBPKLIVM.js} +6 -6
- package/dist/{documents-O7WYZFFE.js → documents-NILTWYSQ.js} +8 -8
- package/dist/{dreaming-TUESPDT3.js → dreaming-KMDGPIFY.js} +17 -20
- package/dist/{employee-boot-brief-UCP6QLN4.js → employee-boot-brief-KSH3MWIF.js} +23 -25
- package/dist/{entity-boost-TK43XT24.js → entity-boost-XETN7C2S.js} +2 -2
- package/dist/{exe-drift-QD6LUZT2.js → exe-drift-SFD7SHXC.js} +4 -4
- package/dist/{exe-export-QIUNHFSS.js → exe-export-NHWKEZL4.js} +9 -9
- package/dist/{exe-import-3UFOLHEW.js → exe-import-SPI5HJBU.js} +9 -9
- package/dist/{exe-key-D5NDKGHT.js → exe-key-TY5LFUEM.js} +95 -95
- package/dist/{exe-org-UWDJBOCV.js → exe-org-LVQLXA7C.js} +32 -32
- package/dist/{exe-snapshot-P6SFODEN.js → exe-snapshot-WH2DSRZW.js} +20 -23
- package/dist/{fast-db-init-SD7JZSEC.js → fast-db-init-UN225WS7.js} +1 -1
- package/dist/{founder-context-QIXDZIJX.js → founder-context-DVHSVBM5.js} +2 -2
- package/dist/{founder-monitor-envelope-TLOC4UYG.js → founder-monitor-envelope-HMPPUBUT.js} +4 -4
- package/dist/gateway/index.js +14 -14
- package/dist/{gateway-client-CQTSYJLP.js → gateway-client-TXHCHOVM.js} +1 -1
- package/dist/{git-staleness-JSQTHB4Z.js → git-staleness-CUVLXQOJ.js} +3 -3
- package/dist/{global-procedures-T3RKI4QX.js → global-procedures-T5W3S6OU.js} +5 -5
- package/dist/{graph-auto-extract-V75GSTNX.js → graph-auto-extract-S7OIZXYP.js} +6 -6
- package/dist/{graph-query-VAQN2RK3.js → graph-query-CRIH4DXF.js} +1 -1
- package/dist/{graph-rag-GBA5E3QC.js → graph-rag-LH34CEGC.js} +3 -3
- package/dist/{hook-integrity-II6QK6Y4.js → hook-integrity-TSJYZFZ6.js} +1 -1
- package/dist/{hook-prefetch-5EHPOYY3.js → hook-prefetch-SNW5DFCZ.js} +13 -16
- package/dist/hooks/bug-report-worker.js +17 -20
- package/dist/hooks/codex-stop-task-finalizer.js +19 -22
- package/dist/hooks/commit-complete.js +16 -19
- package/dist/hooks/error-recall.js +14 -14
- package/dist/hooks/exe-heartbeat-hook.js +5 -5
- package/dist/hooks/ingest-worker.js +18 -20
- package/dist/hooks/ingest.js +16 -16
- package/dist/hooks/instructions-loaded.js +7 -7
- package/dist/hooks/manifest.json +20 -20
- package/dist/hooks/notification.js +6 -6
- package/dist/hooks/post-compact.js +5 -5
- package/dist/hooks/post-tool-combined.js +13 -13
- package/dist/hooks/pre-compact.js +16 -19
- package/dist/hooks/pre-tool-use.js +2 -2
- package/dist/hooks/prompt-submit.js +40 -39
- package/dist/hooks/session-end.js +21 -24
- package/dist/hooks/session-start.js +18 -18
- package/dist/hooks/stop.js +19 -22
- package/dist/hooks/subagent-stop.js +18 -21
- package/dist/hooks/summary-worker.js +20 -23
- package/dist/index.js +25 -28
- package/dist/{installer-XEEZSF3V.js → installer-4IVP2PTX.js} +7 -8
- package/dist/{installer-ILYC4LI5.js → installer-CJWNUASI.js} +7 -8
- package/dist/{installer-MPVQ5L22.js → installer-UFRZHMX5.js} +7 -8
- package/dist/{intercom-queue-QGHAW2KO.js → intercom-queue-II6RRK3J.js} +12 -15
- package/dist/lib/cloud-sync.js +16 -16
- package/dist/lib/consolidation.js +9 -9
- package/dist/lib/crypto.js +3 -3
- package/dist/lib/database.js +3 -3
- package/dist/lib/db-backup.js +1 -1
- package/dist/lib/db-write-worker.js +3 -3
- package/dist/lib/db.js +3 -3
- package/dist/lib/device-registry.js +1 -1
- package/dist/lib/employee-templates.js +6 -6
- package/dist/lib/employees.js +3 -3
- package/dist/lib/error-detector.js +2 -2
- package/dist/lib/exe-daemon.js +83 -86
- package/dist/lib/hybrid-search.js +11 -11
- package/dist/lib/identity.js +5 -3
- package/dist/lib/keychain.js +1 -1
- package/dist/lib/license.js +2 -2
- package/dist/lib/messaging.js +13 -16
- package/dist/lib/post-tool-memory.js +16 -18
- package/dist/lib/registry-proxy.js +1 -1
- package/dist/lib/reminders.js +4 -4
- package/dist/lib/schedules.js +8 -8
- package/dist/lib/session-registry.js +5 -5
- package/dist/lib/skill-learning.js +9 -9
- package/dist/lib/store.js +7 -7
- package/dist/lib/task-router.js +4 -4
- package/dist/lib/tasks.js +14 -17
- package/dist/lib/tmux-routing.js +14 -41
- package/dist/lib/token-spend.js +4 -4
- package/dist/lib/ws-client.js +1 -1
- package/dist/{license-gate-CARN6AB6.js → license-gate-M7EDJLAA.js} +3 -3
- package/dist/{license-recheck-7Z3LS6YS.js → license-recheck-DLTOHAH2.js} +1 -1
- package/dist/{license-sync-PUQV3HSZ.js → license-sync-YTDCUN3S.js} +2 -2
- package/dist/mcp/register-tools.js +94 -94
- package/dist/mcp/server.js +97 -97
- package/dist/mcp/tools/complete-reminder.js +5 -5
- package/dist/mcp/tools/create-reminder.js +5 -5
- package/dist/mcp/tools/create-task.js +16 -19
- package/dist/mcp/tools/deactivate-behavior.js +10 -10
- package/dist/mcp/tools/list-reminders.js +5 -5
- package/dist/mcp/tools/list-tasks.js +16 -19
- package/dist/mcp/tools/send-message.js +15 -18
- package/dist/mcp/tools/update-task.js +15 -18
- package/dist/{mcp-http-config-RJKTZPHZ.js → mcp-http-config-652YK3CI.js} +5 -5
- package/dist/{mcp-session-identity-HN444A3G.js → mcp-session-identity-6QGXD52Y.js} +12 -15
- package/dist/{memory-cards-AT3QCCWU.js → memory-cards-FOPSLULE.js} +8 -8
- package/dist/{memory-graph-extractor-B43UWGV5.js → memory-graph-extractor-7G3CIGN7.js} +7 -7
- package/dist/{memory-maintenance-WDLL3ZX3.js → memory-maintenance-5XIGUPYL.js} +1 -1
- package/dist/{memory-poisoning-defense-TS4WNW4S.js → memory-poisoning-defense-MI7Y5XBP.js} +3 -3
- package/dist/{memory-pressure-watchdog-6YH7S6W7.js → memory-pressure-watchdog-SPIDPGU2.js} +13 -16
- package/dist/{memory-reflection-N6NSYW6S.js → memory-reflection-DVAT4QKS.js} +8 -8
- package/dist/{memory-write-repository-D35RDO62.js → memory-write-repository-YC7HJTV5.js} +1 -1
- package/dist/{monitor-grants-FH4PNYN5.js → monitor-grants-3A77O6XK.js} +3 -3
- package/dist/{monitor-identity-LK5ZCCND.js → monitor-identity-BTFFMFEX.js} +2 -2
- package/dist/{notification-ledger-3WJYJGGS.js → notification-ledger-AV5242RM.js} +13 -16
- package/dist/{notifications-T5C2CVB3.js → notifications-75FVPJLY.js} +12 -15
- package/dist/{orchestration-events-73VEJT7Q.js → orchestration-events-ARGZU6KS.js} +4 -4
- package/dist/{orchestrator-3O657734.js → orchestrator-TCXZMLSB.js} +14 -17
- package/dist/{org-brief-session-EGB35QLM.js → org-brief-session-B455POUI.js} +15 -15
- package/dist/{org-consent-policy-MSVSRI53.js → org-consent-policy-7K5H6FOB.js} +5 -5
- package/dist/{org-join-4BK2XJJI.js → org-join-ETR5KO27.js} +9 -9
- package/dist/{org-key-admin-EUSSGXQ3.js → org-key-admin-IRC6H3TL.js} +11 -11
- package/dist/{org-key-epoch-NMEWDT5L.js → org-key-epoch-EG4YQ47A.js} +5 -5
- package/dist/{org-key-pin-HUSXGINB.js → org-key-pin-GLQEU3P3.js} +3 -3
- package/dist/{org-membership-B74IWEM5.js → org-membership-JC5YVU6N.js} +4 -4
- package/dist/{org-partner-config-5FJTJABN.js → org-partner-config-QFSVGHEA.js} +8 -8
- package/dist/{org-partner-runtime-GN4GFWI2.js → org-partner-runtime-7AD7WY3Y.js} +15 -15
- package/dist/{org-row-visibility-PBZ2TSCR.js → org-row-visibility-AXZKVOA6.js} +1 -1
- package/dist/{pipeline-router-2IELBSVJ.js → pipeline-router-2KY4X6PO.js} +4 -4
- package/dist/{plan-limits-4GXK5PFT.js → plan-limits-PG6HG2DT.js} +6 -6
- package/dist/{pre-tool-use-impl-CY73PRJP.js → pre-tool-use-impl-45B4NFIF.js} +34 -32
- package/dist/{project-name-337ERIAX.js → project-name-LHYHR4VN.js} +4 -4
- package/dist/{projection-worker-W476IG6V.js → projection-worker-POT2AOR5.js} +2 -2
- package/dist/{prospective-memory-5INB7NUL.js → prospective-memory-ECXSAEAW.js} +3 -3
- package/dist/{push-notifications-YVOP4KGN.js → push-notifications-W3UNHDC5.js} +13 -16
- package/dist/{reproject-worktree-memories-OGBR3CMO.js → reproject-worktree-memories-PIXMJBHG.js} +12 -15
- package/dist/{reranker-W7WCJH6Y.js → reranker-ZBPYIRBB.js} +1 -1
- package/dist/{retrieval-health-R6MGXLTP.js → retrieval-health-42MFLOEP.js} +1 -1
- package/dist/{review-polling-TKAWVY6G.js → review-polling-V4CSK4TZ.js} +13 -16
- package/dist/runtime/index.js +16 -19
- package/dist/{session-events-HUCZ7TOR.js → session-events-C3NUY6MR.js} +13 -16
- package/dist/{session-kill-telemetry-RWRWOW4C.js → session-kill-telemetry-ZOSAIYU2.js} +13 -16
- package/dist/{session-scope-5CDRHV75.js → session-scope-BZALGKYZ.js} +12 -15
- package/dist/{setup-wizard-RBTJAZOP.js → setup-wizard-DNA2UT4J.js} +2 -2
- package/dist/{shard-manager-LRPAAQA4.js → shard-manager-NG4SXV7L.js} +2 -2
- package/dist/{skill-refinement-MDXKDCY6.js → skill-refinement-Y3M7XBRE.js} +4 -4
- package/dist/{skill-retrieval-OWY6MTCX.js → skill-retrieval-XDY352AS.js} +11 -11
- package/dist/{skill-usage-GNV737U5.js → skill-usage-QOLIJQJC.js} +4 -4
- package/dist/{stack-generate-manifest-S4SBLKBY.js → stack-generate-manifest-VFHCOLAD.js} +4 -4
- package/dist/{stack-promote-U4EZNLOV.js → stack-promote-24BY6AXF.js} +4 -4
- package/dist/{stack-release-4VBGOYGK.js → stack-release-AXH7ZIOR.js} +5 -5
- package/dist/{stack-update-C5YKHE7A.js → stack-update-7ET7S3ED.js} +3 -3
- package/dist/{steward-gate-QFPXQBUZ.js → steward-gate-2MUESY3V.js} +4 -4
- package/dist/{subagent-brief-injection-GDXO5WNS.js → subagent-brief-injection-6PM3WMMR.js} +25 -27
- package/dist/{support-bug-actions-4XEKXIIO.js → support-bug-actions-WWKDXT74.js} +3 -3
- package/dist/{support-inbox-Y7AFCOGM.js → support-inbox-AN44HIOG.js} +12 -12
- package/dist/{task-enforcement-CN3Q4PEH.js → task-enforcement-QO7RXYK2.js} +13 -16
- package/dist/{task-request-inbox-AY3C6DND.js → task-request-inbox-RXAT2QJF.js} +5 -5
- package/dist/{task-scope-AMR3Z3DJ.js → task-scope-4EKZBTTM.js} +12 -15
- package/dist/{tasks-crud-2GHKIZOM.js → tasks-crud-MB7WXGPW.js} +14 -15
- package/dist/{tasks-notify-Z53446KM.js → tasks-notify-BCWO7AP6.js} +13 -16
- package/dist/{tasks-review-APRNSHMQ.js → tasks-review-HXHYA76X.js} +12 -15
- package/dist/{telemetry-upload-5XR42SQN.js → telemetry-upload-Q7DLE5I3.js} +8 -8
- package/dist/{token-budget-TTNJXI7W.js → token-budget-EL6BEBDP.js} +4 -4
- package/dist/{tool-telemetry-36ST422N.js → tool-telemetry-SNWF4WDH.js} +1 -1
- package/dist/tui/App.js +22 -25
- package/dist/{tui-data-AP5TPUOQ.js → tui-data-ZMSBEPJT.js} +16 -19
- package/dist/{update-support-SFTI2PUD.js → update-support-QY7WALUZ.js} +11 -11
- package/dist/{whatsapp-5CXE3JHG.js → whatsapp-NCZ43BB3.js} +2 -2
- package/dist/{wiki-acl-N754BC2W.js → wiki-acl-3L7BRJUV.js} +1 -1
- package/dist/{work-claims-OAICQBWO.js → work-claims-XQXT2VWJ.js} +4 -4
- package/dist/{work-state-heartbeat-MTTUGMPU.js → work-state-heartbeat-GAIQ7M73.js} +1 -1
- package/dist/{workflow-engine-RID3QL2U.js → workflow-engine-FCOOI5M6.js} +2 -2
- package/dist/{worktree-XWVNDBRD.js → worktree-GS4ODQ4C.js} +5 -5
- package/dist/{worktree-sweep-6VEHGH7U.js → worktree-sweep-6LMC7H5Y.js} +6 -6
- package/package.json +1 -1
- package/release-notes.json +27 -9
- package/stack.release.json +3 -3
- package/dist/chunk-ESO5JDOT.js +0 -70
- package/dist/content-extractor-OVD7PTSN.js +0 -9
- /package/dist/{chunk-EZQ4W3WW.js → chunk-7ZP2PAW3.js} +0 -0
- /package/dist/{chunk-YBB43SXC.js → chunk-AC36VWNB.js} +0 -0
- /package/dist/{chunk-YG4DZZ6J.js → chunk-CS4SHHLY.js} +0 -0
- /package/dist/{chunk-33PG3KNY.js → chunk-FDQXQBBC.js} +0 -0
- /package/dist/{chunk-OLMLA377.js → chunk-IKJUZ3VO.js} +0 -0
- /package/dist/{chunk-FUZHMV7K.js → chunk-JXGOO53U.js} +0 -0
- /package/dist/{chunk-WIDOLS7T.js → chunk-LAWI42NC.js} +0 -0
- /package/dist/{chunk-PKM456D2.js → chunk-LK6OG47X.js} +0 -0
- /package/dist/{chunk-FLO24BNN.js → chunk-R6H2IRCF.js} +0 -0
- /package/dist/{chunk-64UAHPXA.js → chunk-UQKGV6SX.js} +0 -0
- /package/dist/{chunk-CLV7ALXV.js → chunk-XDWIJBTY.js} +0 -0
- /package/dist/{chunk-LDHJX4TZ.js → chunk-XJDSTA2C.js} +0 -0
- /package/dist/{chunk-OEEHLATI.js → chunk-ZQS4S4AX.js} +0 -0
- /package/dist/{core-memory-WR7I7DIP.js → core-memory-3TZV3ZDW.js} +0 -0
- /package/dist/{memory-isolation-4RQJ5RCK.js → memory-isolation-TE4AOMQX.js} +0 -0
- /package/dist/{oauth-server-VBDMZIEV.js → oauth-server-CWQ6EBGQ.js} +0 -0
- /package/dist/{project-boot-G7T5WHO6.js → project-boot-MX2FNKTJ.js} +0 -0
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ensureAgentSymlink
|
|
3
|
-
} from "./chunk-ESO5JDOT.js";
|
|
4
|
-
import {
|
|
5
|
-
ensureWorktree,
|
|
6
|
-
ensureWorktreeAsync,
|
|
7
|
-
getGitRoot,
|
|
8
|
-
getMainRepoRoot,
|
|
9
|
-
isInsideGitRepo,
|
|
10
|
-
isWorktreeDirty
|
|
11
|
-
} from "./chunk-VYKXRSHU.js";
|
|
12
1
|
import {
|
|
13
2
|
runBudgetedSweep
|
|
14
3
|
} from "./chunk-ZKGWHT56.js";
|
|
15
4
|
import {
|
|
16
5
|
resolveRowVisibility
|
|
17
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5F3E57TQ.js";
|
|
18
7
|
import {
|
|
19
8
|
LEASE_MINUTES,
|
|
20
9
|
isLeaseExpired,
|
|
@@ -35,9 +24,8 @@ import {
|
|
|
35
24
|
writeMemoryViaDaemon
|
|
36
25
|
} from "./chunk-QDMZ5DX2.js";
|
|
37
26
|
import {
|
|
38
|
-
listSessions
|
|
39
|
-
|
|
40
|
-
} from "./chunk-HFXBL7XE.js";
|
|
27
|
+
listSessions
|
|
28
|
+
} from "./chunk-6T5RC3L4.js";
|
|
41
29
|
import {
|
|
42
30
|
getTransport
|
|
43
31
|
} from "./chunk-VI4ATC6I.js";
|
|
@@ -46,7 +34,7 @@ import {
|
|
|
46
34
|
} from "./chunk-6L4VIYLZ.js";
|
|
47
35
|
import {
|
|
48
36
|
recordOrchestrationEventBestEffort
|
|
49
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-WLV27PAO.js";
|
|
50
38
|
import {
|
|
51
39
|
appendDispatchJournalEvent,
|
|
52
40
|
releaseDispatchClaim,
|
|
@@ -55,30 +43,22 @@ import {
|
|
|
55
43
|
} from "./chunk-T5SM4RXY.js";
|
|
56
44
|
import {
|
|
57
45
|
incrementSkillSuccess
|
|
58
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-ASA2UJDH.js";
|
|
59
47
|
import {
|
|
60
48
|
assertRuntimeModelCompatible,
|
|
61
49
|
getAgentRuntime,
|
|
62
|
-
normalizeCcModelName,
|
|
63
50
|
resolveSpawnRuntimeConfig
|
|
64
51
|
} from "./chunk-IVXELUEQ.js";
|
|
65
52
|
import {
|
|
66
53
|
PlanLimitError,
|
|
67
54
|
assertEmployeeLimitSync
|
|
68
|
-
} from "./chunk-
|
|
69
|
-
import {
|
|
70
|
-
AGENT_IDENTITY_TOKEN_ENV,
|
|
71
|
-
getDaemonAgentIdentityBroker
|
|
72
|
-
} from "./chunk-PCV52RYM.js";
|
|
55
|
+
} from "./chunk-2AXJRXA6.js";
|
|
73
56
|
import {
|
|
74
57
|
isValidSessionScope
|
|
75
58
|
} from "./chunk-BTSWFMEH.js";
|
|
76
59
|
import {
|
|
77
60
|
getProjectName
|
|
78
|
-
} from "./chunk-
|
|
79
|
-
import {
|
|
80
|
-
expandDualPrefixTools
|
|
81
|
-
} from "./chunk-HYZV25LY.js";
|
|
61
|
+
} from "./chunk-J6RTX7XT.js";
|
|
82
62
|
import {
|
|
83
63
|
getSessionKey
|
|
84
64
|
} from "./chunk-CVYC6DUW.js";
|
|
@@ -99,176 +79,41 @@ import {
|
|
|
99
79
|
getEmployee,
|
|
100
80
|
isCoordinatorName,
|
|
101
81
|
loadEmployees,
|
|
102
|
-
loadEmployeesState,
|
|
103
82
|
loadEmployeesSync,
|
|
104
83
|
offloadReadIfMain,
|
|
105
84
|
offloadReadQuery
|
|
106
|
-
} from "./chunk-
|
|
107
|
-
import {
|
|
108
|
-
RUNTIME_TABLE
|
|
109
|
-
} from "./chunk-UWGQNJIE.js";
|
|
85
|
+
} from "./chunk-LBEBSXZA.js";
|
|
110
86
|
import {
|
|
111
87
|
loadDeviceId
|
|
112
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-KNEEOZWN.js";
|
|
113
89
|
import {
|
|
114
90
|
EXE_AI_DIR
|
|
115
91
|
} from "./chunk-MMS2HBRT.js";
|
|
116
92
|
import {
|
|
117
|
-
atomicWriteJsonSync
|
|
118
|
-
atomicWriteSync
|
|
93
|
+
atomicWriteJsonSync
|
|
119
94
|
} from "./chunk-HSGBZSCA.js";
|
|
120
95
|
|
|
121
96
|
// src/lib/tmux-routing.ts
|
|
122
|
-
import { execFile
|
|
123
|
-
import { promisify as
|
|
124
|
-
import { readFileSync as
|
|
125
|
-
import
|
|
126
|
-
import
|
|
127
|
-
import { fileURLToPath } from "url";
|
|
128
|
-
|
|
129
|
-
// src/lib/cc-help-cache.ts
|
|
130
|
-
import { execSync } from "child_process";
|
|
131
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
132
|
-
import path from "path";
|
|
133
|
-
import os from "os";
|
|
134
|
-
var CACHE_DIR = path.join(os.homedir(), ".exe-os", "cache");
|
|
135
|
-
var _cached = null;
|
|
136
|
-
function getCcVersion() {
|
|
137
|
-
try {
|
|
138
|
-
const out = execSync("claude --version 2>/dev/null || echo unknown", {
|
|
139
|
-
encoding: "utf8",
|
|
140
|
-
timeout: 5e3
|
|
141
|
-
}).trim();
|
|
142
|
-
const m = out.match(/(\d+\.\d+\.\d+)/);
|
|
143
|
-
return m ? m[1] : "unknown";
|
|
144
|
-
} catch {
|
|
145
|
-
return "unknown";
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function getCcHelpOutputCached() {
|
|
149
|
-
if (_cached !== null) return _cached;
|
|
150
|
-
const version = getCcVersion();
|
|
151
|
-
const cacheFile = path.join(CACHE_DIR, `cc-help-${version}.txt`);
|
|
152
|
-
if (existsSync(cacheFile)) {
|
|
153
|
-
try {
|
|
154
|
-
_cached = readFileSync(cacheFile, "utf8");
|
|
155
|
-
return _cached;
|
|
156
|
-
} catch {
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
let helpText = "";
|
|
160
|
-
try {
|
|
161
|
-
const result = execSync("claude --help 2>&1", {
|
|
162
|
-
encoding: "utf8",
|
|
163
|
-
timeout: 5e3
|
|
164
|
-
});
|
|
165
|
-
helpText = typeof result === "string" ? result : "";
|
|
166
|
-
} catch {
|
|
167
|
-
helpText = "";
|
|
168
|
-
}
|
|
169
|
-
try {
|
|
170
|
-
mkdirSync(CACHE_DIR, { recursive: true });
|
|
171
|
-
writeFileSync(cacheFile, helpText, "utf8");
|
|
172
|
-
} catch {
|
|
173
|
-
}
|
|
174
|
-
_cached = helpText;
|
|
175
|
-
return _cached;
|
|
176
|
-
}
|
|
177
|
-
function _resetCcHelpCache() {
|
|
178
|
-
_cached = null;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// src/lib/cc-agent-support.ts
|
|
182
|
-
function _resetCcAgentSupportCache() {
|
|
183
|
-
_resetCcHelpCache();
|
|
184
|
-
}
|
|
185
|
-
function claudeSupportsAgentFlag() {
|
|
186
|
-
return /(^|\s)--agent(\b|=)/.test(getCcHelpOutputCached());
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// src/lib/provider-table.ts
|
|
190
|
-
var PROVIDER_TABLE = {
|
|
191
|
-
opencode: {
|
|
192
|
-
baseUrl: "https://opencode.ai/zen/go",
|
|
193
|
-
apiKeyEnv: "OPENCODE_API_KEY",
|
|
194
|
-
defaultModel: "kimi-k2.7-code"
|
|
195
|
-
},
|
|
196
|
-
glm: {
|
|
197
|
-
baseUrl: "https://api.z.ai/api/anthropic",
|
|
198
|
-
apiKeyEnv: "ZAI_API_KEY",
|
|
199
|
-
defaultModel: "glm-5.3",
|
|
200
|
-
authVar: "ANTHROPIC_AUTH_TOKEN",
|
|
201
|
-
apiKeyFallbackFile: ".glm-mcp/config.json",
|
|
202
|
-
contextByModel: {
|
|
203
|
-
// glm-5.3 released 2026-08-14 (confirmed real via
|
|
204
|
-
// api.z.ai/api/paas/v4/models — Z.ai's endpoint validates model ids and
|
|
205
|
-
// this one isn't rejected). Z.ai's model-list/model-detail endpoints
|
|
206
|
-
// don't expose context length via API, so this 1M figure is founder-
|
|
207
|
-
// confirmed (2026-08-24), not independently API-verified — if that
|
|
208
|
-
// ever turns out wrong, don't silently "fix" it back down, ask first.
|
|
209
|
-
"glm-5.3": 1e6,
|
|
210
|
-
"glm-5.2": 1e6,
|
|
211
|
-
"glm-5": 2e5,
|
|
212
|
-
"glm-5-turbo": 2e5,
|
|
213
|
-
"glm-4.7": 2e5,
|
|
214
|
-
"glm-4.7-flash": 2e5
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
kimi: {
|
|
218
|
-
// Kimi subscription via kimi-proxy (~/.kimi-code/kimi-proxy.mjs, launchd
|
|
219
|
-
// com.exeai.kimi-proxy, port 8791). The subscription API
|
|
220
|
-
// https://api.kimi.ai/coding/v1/messages speaks NATIVE Anthropic protocol
|
|
221
|
-
// but authenticates with the Kimi CLI's OAuth token (15-min expiry,
|
|
222
|
-
// refresh via auth.kimi.ai/v1/oauth/token — client_id 17e5f671-…,
|
|
223
|
-
// anthropic-beta oauth-2025-04-20; extracted from @moonshot-ai/kimi-code
|
|
224
|
-
// 2.0.2 dist/main.mjs). The proxy handles refresh + Cloudflare UA
|
|
225
|
-
// fingerprinting (upstream 403s any non-kimi UA) and is a pure byte
|
|
226
|
-
// passthrough, so Claude Code just points at 127.0.0.1:8791 and CC's
|
|
227
|
-
// /v1/messages lands on /coding/v1/messages. Verified E2E 2026-09-22.
|
|
228
|
-
//
|
|
229
|
-
// Model ids are the Kimi Code registry's wire ids (NOT display names):
|
|
230
|
-
// kimi-for-coding = "K2.8 Preview" (1M context, default per founder
|
|
231
|
-
// 2026-09-22), kimi-for-coding-highspeed = "K2.7 Code Highspeed" (256k),
|
|
232
|
-
// k3-256k. K3's 1M variant (wire id "k3") is deliberately NOT declared —
|
|
233
|
-
// founder policy 2026-09-22: never use K3 1M, it burns 2x tokens; K3
|
|
234
|
-
// work goes through k3-256k. The fallback api-key.json exists only to
|
|
235
|
-
// satisfy the launcher's key check — the proxy owns real credentials.
|
|
236
|
-
baseUrl: "http://127.0.0.1:8791",
|
|
237
|
-
apiKeyEnv: "KIMI_API_KEY",
|
|
238
|
-
defaultModel: "kimi-for-coding",
|
|
239
|
-
authVar: "ANTHROPIC_AUTH_TOKEN",
|
|
240
|
-
apiKeyFallbackFile: ".kimi-code/api-key.json",
|
|
241
|
-
contextByModel: {
|
|
242
|
-
"kimi-for-coding": 1e6,
|
|
243
|
-
"kimi-for-coding-highspeed": 262144,
|
|
244
|
-
"k3-256k": 262144
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
var DEFAULT_PROVIDER = "default";
|
|
249
|
-
function detectActiveProvider(env = process.env) {
|
|
250
|
-
const baseUrl = env.ANTHROPIC_BASE_URL;
|
|
251
|
-
if (!baseUrl) return DEFAULT_PROVIDER;
|
|
252
|
-
for (const [name, cfg] of Object.entries(PROVIDER_TABLE)) {
|
|
253
|
-
if (cfg.baseUrl === baseUrl) return name;
|
|
254
|
-
}
|
|
255
|
-
return DEFAULT_PROVIDER;
|
|
256
|
-
}
|
|
97
|
+
import { execFile } from "child_process";
|
|
98
|
+
import { promisify as promisify2 } from "util";
|
|
99
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync7, appendFileSync as appendFileSync3, openSync as openSync2, closeSync as closeSync2, writeSync as writeSync2, renameSync as renameSync3, constants as constants2, statSync as statSync4 } from "fs";
|
|
100
|
+
import path6 from "path";
|
|
101
|
+
import os5 from "os";
|
|
257
102
|
|
|
258
103
|
// src/lib/intercom-queue.ts
|
|
259
|
-
import { readFileSync as
|
|
260
|
-
import
|
|
261
|
-
import
|
|
104
|
+
import { readFileSync as readFileSync2, writeFileSync, renameSync, existsSync as existsSync2, mkdirSync, appendFileSync, openSync, closeSync, writeSync, unlinkSync as unlinkSync2, constants, statSync } from "fs";
|
|
105
|
+
import path2 from "path";
|
|
106
|
+
import os2 from "os";
|
|
262
107
|
|
|
263
108
|
// src/lib/notifications.ts
|
|
264
109
|
import crypto from "crypto";
|
|
265
|
-
import
|
|
266
|
-
import
|
|
110
|
+
import path from "path";
|
|
111
|
+
import os from "os";
|
|
267
112
|
import {
|
|
268
|
-
readFileSync
|
|
113
|
+
readFileSync,
|
|
269
114
|
readdirSync,
|
|
270
115
|
unlinkSync,
|
|
271
|
-
existsSync
|
|
116
|
+
existsSync,
|
|
272
117
|
rmdirSync
|
|
273
118
|
} from "fs";
|
|
274
119
|
var CLEANUP_DAYS = 7;
|
|
@@ -415,9 +260,9 @@ function formatNotifications(notifications) {
|
|
|
415
260
|
return lines.join("\n");
|
|
416
261
|
}
|
|
417
262
|
async function migrateJsonNotifications() {
|
|
418
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR ||
|
|
419
|
-
const notifDir =
|
|
420
|
-
if (!
|
|
263
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path.join(os.homedir(), ".exe-os");
|
|
264
|
+
const notifDir = path.join(base, "notifications");
|
|
265
|
+
if (!existsSync(notifDir)) return 0;
|
|
421
266
|
let migrated = 0;
|
|
422
267
|
try {
|
|
423
268
|
const files = readdirSync(notifDir).filter((f) => f.endsWith(".json"));
|
|
@@ -425,8 +270,8 @@ async function migrateJsonNotifications() {
|
|
|
425
270
|
const client = getClient();
|
|
426
271
|
for (const file of files) {
|
|
427
272
|
try {
|
|
428
|
-
const filePath =
|
|
429
|
-
const data = JSON.parse(
|
|
273
|
+
const filePath = path.join(notifDir, file);
|
|
274
|
+
const data = JSON.parse(readFileSync(filePath, "utf8"));
|
|
430
275
|
await client.execute({
|
|
431
276
|
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
432
277
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -495,14 +340,14 @@ function formatTimeAgo(timestamp) {
|
|
|
495
340
|
}
|
|
496
341
|
|
|
497
342
|
// src/lib/intercom-queue.ts
|
|
498
|
-
var QUEUE_PATH =
|
|
343
|
+
var QUEUE_PATH = path2.join(os2.homedir(), ".exe-os", "intercom-queue.json");
|
|
499
344
|
var QUEUE_LOCK_PATH = `${QUEUE_PATH}.lock`;
|
|
500
345
|
var MAX_RETRIES = 10;
|
|
501
346
|
var TTL_MS = 60 * 60 * 1e3;
|
|
502
347
|
var HARD_TTL_MS = 2 * TTL_MS;
|
|
503
348
|
function ensureDir() {
|
|
504
|
-
const dir =
|
|
505
|
-
if (!
|
|
349
|
+
const dir = path2.dirname(QUEUE_PATH);
|
|
350
|
+
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
506
351
|
}
|
|
507
352
|
function acquireQueueLock() {
|
|
508
353
|
ensureDir();
|
|
@@ -517,7 +362,7 @@ function acquireQueueLock() {
|
|
|
517
362
|
} catch (err) {
|
|
518
363
|
if (err.code !== "EEXIST") return false;
|
|
519
364
|
try {
|
|
520
|
-
const lock = JSON.parse(
|
|
365
|
+
const lock = JSON.parse(readFileSync2(QUEUE_LOCK_PATH, "utf8"));
|
|
521
366
|
if (Date.now() - Number(lock.timestamp ?? 0) > 3e4) unlinkSync2(QUEUE_LOCK_PATH);
|
|
522
367
|
} catch {
|
|
523
368
|
try {
|
|
@@ -538,8 +383,8 @@ function releaseQueueLock() {
|
|
|
538
383
|
}
|
|
539
384
|
function readQueue() {
|
|
540
385
|
try {
|
|
541
|
-
if (!
|
|
542
|
-
return JSON.parse(
|
|
386
|
+
if (!existsSync2(QUEUE_PATH)) return [];
|
|
387
|
+
return JSON.parse(readFileSync2(QUEUE_PATH, "utf8"));
|
|
543
388
|
} catch {
|
|
544
389
|
return [];
|
|
545
390
|
}
|
|
@@ -547,7 +392,7 @@ function readQueue() {
|
|
|
547
392
|
function writeQueue(queue) {
|
|
548
393
|
ensureDir();
|
|
549
394
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
550
|
-
|
|
395
|
+
writeFileSync(tmp, JSON.stringify(queue, null, 2), { mode: 384 });
|
|
551
396
|
renameSync(tmp, QUEUE_PATH);
|
|
552
397
|
}
|
|
553
398
|
function queueIntercom(targetSession, reason, intercomReason = "nudge") {
|
|
@@ -783,7 +628,7 @@ function clearQueueForAgent(agentName) {
|
|
|
783
628
|
function _resetDrainGuard() {
|
|
784
629
|
_draining = false;
|
|
785
630
|
}
|
|
786
|
-
var INTERCOM_LOG =
|
|
631
|
+
var INTERCOM_LOG = path2.join(os2.homedir(), ".exe-os", "intercom.log");
|
|
787
632
|
var INTERCOM_LOG_MAX_BYTES = 1024 * 1024;
|
|
788
633
|
function logQueue(msg) {
|
|
789
634
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [queue] ${msg}
|
|
@@ -791,7 +636,7 @@ function logQueue(msg) {
|
|
|
791
636
|
process.stderr.write(`[intercom-queue] ${msg}
|
|
792
637
|
`);
|
|
793
638
|
try {
|
|
794
|
-
if (
|
|
639
|
+
if (existsSync2(INTERCOM_LOG) && statSync(INTERCOM_LOG).size > INTERCOM_LOG_MAX_BYTES) {
|
|
795
640
|
renameSync(INTERCOM_LOG, `${INTERCOM_LOG}.1`);
|
|
796
641
|
}
|
|
797
642
|
appendFileSync(INTERCOM_LOG, line);
|
|
@@ -803,16 +648,16 @@ function logQueue(msg) {
|
|
|
803
648
|
import { unlinkSync as unlinkSync4 } from "fs";
|
|
804
649
|
|
|
805
650
|
// src/lib/routing-log.ts
|
|
806
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
807
|
-
import
|
|
808
|
-
import
|
|
809
|
-
var LOG_DIR =
|
|
810
|
-
var LOG_FILE =
|
|
651
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync3, statSync as statSync2, renameSync as renameSync2 } from "fs";
|
|
652
|
+
import path3 from "path";
|
|
653
|
+
import os3 from "os";
|
|
654
|
+
var LOG_DIR = path3.join(os3.homedir(), ".exe-os", "logs");
|
|
655
|
+
var LOG_FILE = path3.join(LOG_DIR, "routing.log");
|
|
811
656
|
var dirChecked = false;
|
|
812
657
|
var MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
813
658
|
function ensureDir2() {
|
|
814
659
|
if (dirChecked) return;
|
|
815
|
-
if (!
|
|
660
|
+
if (!existsSync3(LOG_DIR)) mkdirSync2(LOG_DIR, { recursive: true });
|
|
816
661
|
dirChecked = true;
|
|
817
662
|
}
|
|
818
663
|
function rotateIfNeeded() {
|
|
@@ -910,21 +755,21 @@ function logTaskDispatch(opts) {
|
|
|
910
755
|
}
|
|
911
756
|
|
|
912
757
|
// src/lib/tasks-review.ts
|
|
913
|
-
import
|
|
914
|
-
import { existsSync as
|
|
758
|
+
import path5 from "path";
|
|
759
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
915
760
|
import { homedir as homedir2 } from "os";
|
|
916
761
|
|
|
917
762
|
// src/lib/tasks-crud.ts
|
|
918
763
|
import crypto2 from "crypto";
|
|
919
|
-
import
|
|
920
|
-
import
|
|
921
|
-
import { execSync
|
|
764
|
+
import path4 from "path";
|
|
765
|
+
import os4 from "os";
|
|
766
|
+
import { execSync, execFile as execFileCb, execFileSync } from "child_process";
|
|
922
767
|
import { promisify } from "util";
|
|
923
768
|
import { mkdir, writeFile, appendFile, readdir, unlink, stat } from "fs/promises";
|
|
924
|
-
import { existsSync as
|
|
769
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
925
770
|
|
|
926
771
|
// src/lib/completion-nudge-dedup.ts
|
|
927
|
-
import { existsSync as
|
|
772
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
928
773
|
import { join, dirname } from "path";
|
|
929
774
|
import { homedir } from "os";
|
|
930
775
|
var COMPLETION_NUDGE_COOLDOWN_MS = Number(process.env.EXE_COMPLETION_NUDGE_COOLDOWN_MS) || 6 * 60 * 60 * 1e3;
|
|
@@ -943,8 +788,8 @@ function load(ledger) {
|
|
|
943
788
|
if (ledger.state) return ledger.state;
|
|
944
789
|
ledger.state = {};
|
|
945
790
|
try {
|
|
946
|
-
if (
|
|
947
|
-
const raw = JSON.parse(
|
|
791
|
+
if (existsSync4(ledger.path)) {
|
|
792
|
+
const raw = JSON.parse(readFileSync3(ledger.path, "utf8"));
|
|
948
793
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
949
794
|
for (const [k, v] of Object.entries(raw)) {
|
|
950
795
|
if (typeof v === "number") ledger.state[k] = v;
|
|
@@ -957,8 +802,8 @@ function load(ledger) {
|
|
|
957
802
|
}
|
|
958
803
|
function persist(ledger, state) {
|
|
959
804
|
try {
|
|
960
|
-
|
|
961
|
-
|
|
805
|
+
mkdirSync3(dirname(ledger.path), { recursive: true });
|
|
806
|
+
writeFileSync2(ledger.path, JSON.stringify(state), "utf8");
|
|
962
807
|
} catch {
|
|
963
808
|
}
|
|
964
809
|
}
|
|
@@ -1011,6 +856,7 @@ function clearStaleReviewEscalation(taskId) {
|
|
|
1011
856
|
// src/lib/tasks-crud.ts
|
|
1012
857
|
var execFileAsync = promisify(execFileCb);
|
|
1013
858
|
var DEFAULT_TASK_PRIORITY = "p1";
|
|
859
|
+
var DEFAULT_NEW_TASK_EXECUTION_MODE = "checklist";
|
|
1014
860
|
function normalizeTaskPriority(value) {
|
|
1015
861
|
const v = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
1016
862
|
if (v === "p0" || v === "p1" || v === "p2") return v;
|
|
@@ -1531,8 +1377,8 @@ ${f.context}
|
|
|
1531
1377
|
`;
|
|
1532
1378
|
}
|
|
1533
1379
|
async function writeTaskMarkdownAtomic(finalPath, content) {
|
|
1534
|
-
const dir =
|
|
1535
|
-
if (!
|
|
1380
|
+
const dir = path4.dirname(finalPath);
|
|
1381
|
+
if (!existsSync5(dir)) await mkdir(dir, { recursive: true });
|
|
1536
1382
|
const tmpPath = `${finalPath}.${crypto2.randomBytes(6).toString("hex")}.tmp`;
|
|
1537
1383
|
try {
|
|
1538
1384
|
await writeFile(tmpPath, content, "utf-8");
|
|
@@ -1610,7 +1456,7 @@ async function createTaskCore(input, deps = {}) {
|
|
|
1610
1456
|
if (isCoordinatorSession) {
|
|
1611
1457
|
earlySessionScope = resolved;
|
|
1612
1458
|
} else {
|
|
1613
|
-
const { getSessionProject } = await import("./session-scope-
|
|
1459
|
+
const { getSessionProject } = await import("./session-scope-BZALGKYZ.js");
|
|
1614
1460
|
const sessionProject = getSessionProject(resolved);
|
|
1615
1461
|
if (sessionProject && sessionProject !== input.projectName) {
|
|
1616
1462
|
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
@@ -1681,8 +1527,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
1681
1527
|
}
|
|
1682
1528
|
if (input.baseDir) {
|
|
1683
1529
|
try {
|
|
1684
|
-
await mkdir(
|
|
1685
|
-
await mkdir(
|
|
1530
|
+
await mkdir(path4.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
1531
|
+
await mkdir(path4.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
1686
1532
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
1687
1533
|
await ensureGitignoreExe(input.baseDir);
|
|
1688
1534
|
} catch {
|
|
@@ -1712,7 +1558,7 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
1712
1558
|
}
|
|
1713
1559
|
const groupId = input.groupId ?? null;
|
|
1714
1560
|
if (groupId) {
|
|
1715
|
-
const requestedExecutionMode = input.executionMode ??
|
|
1561
|
+
const requestedExecutionMode = input.executionMode ?? DEFAULT_NEW_TASK_EXECUTION_MODE;
|
|
1716
1562
|
const groupCheck = await client.execute({
|
|
1717
1563
|
sql: `SELECT id, execution_mode FROM task_groups WHERE id = ? AND session_scope IS ?`,
|
|
1718
1564
|
args: [groupId, sessionScope]
|
|
@@ -1757,7 +1603,7 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
1757
1603
|
sessionScope,
|
|
1758
1604
|
input.spawnRuntime ?? null,
|
|
1759
1605
|
input.spawnModel ?? null,
|
|
1760
|
-
input.executionMode ??
|
|
1606
|
+
input.executionMode ?? DEFAULT_NEW_TASK_EXECUTION_MODE,
|
|
1761
1607
|
groupId,
|
|
1762
1608
|
loadDeviceId(),
|
|
1763
1609
|
input.founderId ?? null,
|
|
@@ -1781,7 +1627,7 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
1781
1627
|
if (existing.rows.length > 0) {
|
|
1782
1628
|
const r = existing.rows[0];
|
|
1783
1629
|
const persistedMode = r.execution_mode === "checklist" ? "checklist" : "durable_tmux";
|
|
1784
|
-
const requestedMode = input.executionMode ??
|
|
1630
|
+
const requestedMode = input.executionMode ?? DEFAULT_NEW_TASK_EXECUTION_MODE;
|
|
1785
1631
|
if (persistedMode !== requestedMode) {
|
|
1786
1632
|
throw new Error(
|
|
1787
1633
|
`Idempotency execution_mode mismatch: existing task is ${persistedMode}, requested ${requestedMode}`
|
|
@@ -1862,8 +1708,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
1862
1708
|
});
|
|
1863
1709
|
if (input.baseDir) {
|
|
1864
1710
|
try {
|
|
1865
|
-
const EXE_OS_DIR =
|
|
1866
|
-
const mdPath =
|
|
1711
|
+
const EXE_OS_DIR = path4.join(os4.homedir(), ".exe-os");
|
|
1712
|
+
const mdPath = path4.join(EXE_OS_DIR, taskFile);
|
|
1867
1713
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
1868
1714
|
const mdContent = renderTaskMarkdown({
|
|
1869
1715
|
id,
|
|
@@ -1880,7 +1726,7 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
1880
1726
|
});
|
|
1881
1727
|
await writeTaskMarkdownAtomic(mdPath, mdContent);
|
|
1882
1728
|
try {
|
|
1883
|
-
const { injectBehaviors } = await import("./behavior-injection-
|
|
1729
|
+
const { injectBehaviors } = await import("./behavior-injection-CTSIKHTH.js");
|
|
1884
1730
|
await injectBehaviors({
|
|
1885
1731
|
taskFile: mdPath,
|
|
1886
1732
|
title: input.title,
|
|
@@ -1921,7 +1767,7 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
1921
1767
|
tokensWarnedAt: null,
|
|
1922
1768
|
spawnRuntime: input.spawnRuntime ?? null,
|
|
1923
1769
|
spawnModel: input.spawnModel ?? null,
|
|
1924
|
-
executionMode: input.executionMode ??
|
|
1770
|
+
executionMode: input.executionMode ?? DEFAULT_NEW_TASK_EXECUTION_MODE,
|
|
1925
1771
|
sessionScope
|
|
1926
1772
|
};
|
|
1927
1773
|
}
|
|
@@ -1975,7 +1821,7 @@ async function buildTaskRowsStatement(opts) {
|
|
|
1975
1821
|
}
|
|
1976
1822
|
if (opts.founderId !== null) {
|
|
1977
1823
|
try {
|
|
1978
|
-
const { founderScopeFilter } = await import("./founder-context-
|
|
1824
|
+
const { founderScopeFilter } = await import("./founder-context-DVHSVBM5.js");
|
|
1979
1825
|
const founderFilter = founderScopeFilter(opts.founderId);
|
|
1980
1826
|
const cleaned = founderFilter.sql.replace(/^\s*AND\s+/, "");
|
|
1981
1827
|
conditions.push(cleaned);
|
|
@@ -2191,7 +2037,7 @@ function refreshTmuxAliveCacheSync() {
|
|
|
2191
2037
|
_tmuxAliveSessions = /* @__PURE__ */ new Set();
|
|
2192
2038
|
_tmuxAlivePanes = /* @__PURE__ */ new Set();
|
|
2193
2039
|
try {
|
|
2194
|
-
const output =
|
|
2040
|
+
const output = execSync(tmuxCmd("list-panes -a -F '#{session_name} #{pane_id} #{pane_dead}'"), {
|
|
2195
2041
|
timeout: 2e3,
|
|
2196
2042
|
encoding: "utf8",
|
|
2197
2043
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2465,12 +2311,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
2465
2311
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
2466
2312
|
try {
|
|
2467
2313
|
const since = new Date(taskCreatedAt).toISOString();
|
|
2468
|
-
const branch =
|
|
2314
|
+
const branch = execSync(
|
|
2469
2315
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
2470
2316
|
{ encoding: "utf8", timeout: 3e3 }
|
|
2471
2317
|
).trim();
|
|
2472
2318
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
2473
|
-
const commitCount =
|
|
2319
|
+
const commitCount = execSync(
|
|
2474
2320
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
2475
2321
|
{ encoding: "utf8", timeout: 5e3 }
|
|
2476
2322
|
).trim();
|
|
@@ -3377,8 +3223,8 @@ ${input.result ?? ""}`;
|
|
|
3377
3223
|
try {
|
|
3378
3224
|
const scope = row.session_scope ? String(row.session_scope) : "default";
|
|
3379
3225
|
const dir = taskSignalDir(scope);
|
|
3380
|
-
|
|
3381
|
-
|
|
3226
|
+
mkdirSync4(dir, { recursive: true });
|
|
3227
|
+
writeFileSync3(
|
|
3382
3228
|
reviewSignalPath(scope, reviewer, taskId),
|
|
3383
3229
|
JSON.stringify({
|
|
3384
3230
|
task_id: taskId,
|
|
@@ -3663,9 +3509,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3663
3509
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3664
3510
|
}
|
|
3665
3511
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3666
|
-
const archPath =
|
|
3512
|
+
const archPath = path4.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3667
3513
|
try {
|
|
3668
|
-
if (
|
|
3514
|
+
if (existsSync5(archPath)) return;
|
|
3669
3515
|
const template = [
|
|
3670
3516
|
`# ${projectName} \u2014 System Architecture`,
|
|
3671
3517
|
"",
|
|
@@ -3699,10 +3545,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3699
3545
|
}
|
|
3700
3546
|
var GITIGNORE_EXE_BLOCK = "# Employee task assignments (private)\n# exe/output/ stays tracked \u2014 it is the sanctioned deliverable handoff\n# location (bug 4c8d4c4b). Never collapse this back to a blanket /exe/.\n/exe/*\n!/exe/output/\n";
|
|
3701
3547
|
async function ensureGitignoreExe(baseDir) {
|
|
3702
|
-
const gitignorePath =
|
|
3548
|
+
const gitignorePath = path4.join(baseDir, ".gitignore");
|
|
3703
3549
|
try {
|
|
3704
|
-
if (
|
|
3705
|
-
const content =
|
|
3550
|
+
if (existsSync5(gitignorePath)) {
|
|
3551
|
+
const content = readFileSync4(gitignorePath, "utf-8");
|
|
3706
3552
|
if (/^\/?exe\/\*$/m.test(content) && /^!\/?exe\/output\/?$/m.test(content)) return;
|
|
3707
3553
|
if (/^\/?exe\/?$/m.test(content)) {
|
|
3708
3554
|
const narrowed = content.replace(/^\/?exe\/?$/m, "/exe/*\n!/exe/output/");
|
|
@@ -3727,7 +3573,7 @@ async function collectTaskMarkdownPaths(root) {
|
|
|
3727
3573
|
return;
|
|
3728
3574
|
}
|
|
3729
3575
|
for (const entry of entries) {
|
|
3730
|
-
const fullPath =
|
|
3576
|
+
const fullPath = path4.join(dir, entry.name);
|
|
3731
3577
|
if (entry.isDirectory()) {
|
|
3732
3578
|
await walkDir(fullPath);
|
|
3733
3579
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -3740,14 +3586,14 @@ async function collectTaskMarkdownPaths(root) {
|
|
|
3740
3586
|
}
|
|
3741
3587
|
var TASK_FILE_CONFLICT_MARKER_SUFFIX = ".conflict";
|
|
3742
3588
|
async function cleanOrphanedTaskFiles() {
|
|
3743
|
-
const EXE_TASKS_DIR =
|
|
3744
|
-
if (!
|
|
3589
|
+
const EXE_TASKS_DIR = path4.join(os4.homedir(), ".exe-os", "tasks");
|
|
3590
|
+
if (!existsSync5(EXE_TASKS_DIR)) return 0;
|
|
3745
3591
|
let cleaned = 0;
|
|
3746
3592
|
const paths = await collectTaskMarkdownPaths(EXE_TASKS_DIR);
|
|
3747
3593
|
await runBudgetedSweep(paths, async (fullPath) => {
|
|
3748
|
-
const entryName =
|
|
3749
|
-
if (
|
|
3750
|
-
const relativePath =
|
|
3594
|
+
const entryName = path4.basename(fullPath);
|
|
3595
|
+
if (existsSync5(`${fullPath}${TASK_FILE_CONFLICT_MARKER_SUFFIX}`)) return;
|
|
3596
|
+
const relativePath = path4.relative(path4.join(os4.homedir(), ".exe-os"), fullPath);
|
|
3751
3597
|
try {
|
|
3752
3598
|
const result = await offloadReadQuery("SELECT id FROM tasks WHERE task_file = ? LIMIT 1", [relativePath]);
|
|
3753
3599
|
if (result.rows.length === 0) {
|
|
@@ -3830,7 +3676,7 @@ async function createTaskGroup(input) {
|
|
|
3830
3676
|
input.projectName ?? null,
|
|
3831
3677
|
input.timeoutMinutes ?? 120,
|
|
3832
3678
|
input.onPartialFailure ?? "notify",
|
|
3833
|
-
input.executionMode ??
|
|
3679
|
+
input.executionMode ?? DEFAULT_NEW_TASK_EXECUTION_MODE,
|
|
3834
3680
|
now,
|
|
3835
3681
|
now
|
|
3836
3682
|
]
|
|
@@ -3851,7 +3697,7 @@ async function createTaskGroup(input) {
|
|
|
3851
3697
|
projectName: input.projectName ?? null,
|
|
3852
3698
|
timeoutMinutes: input.timeoutMinutes ?? 120,
|
|
3853
3699
|
onPartialFailure: input.onPartialFailure ?? "notify",
|
|
3854
|
-
executionMode: input.executionMode ??
|
|
3700
|
+
executionMode: input.executionMode ?? DEFAULT_NEW_TASK_EXECUTION_MODE,
|
|
3855
3701
|
barrierFired: false,
|
|
3856
3702
|
barrierFiredAt: null,
|
|
3857
3703
|
aggregatedResult: null,
|
|
@@ -4114,8 +3960,8 @@ async function checkTaskFileConsistency() {
|
|
|
4114
3960
|
for (const row of result.rows) {
|
|
4115
3961
|
const taskFile = String(row.task_file ?? "");
|
|
4116
3962
|
if (!taskFile) continue;
|
|
4117
|
-
const fullPath = taskFile.startsWith("/") ? taskFile :
|
|
4118
|
-
if (
|
|
3963
|
+
const fullPath = taskFile.startsWith("/") ? taskFile : path4.join(os4.homedir(), ".exe-os", taskFile);
|
|
3964
|
+
if (existsSync5(fullPath)) continue;
|
|
4119
3965
|
let repaired = false;
|
|
4120
3966
|
try {
|
|
4121
3967
|
const mdContent = renderTaskMarkdown({
|
|
@@ -4269,10 +4115,10 @@ async function invalidateReviewArtifacts(row) {
|
|
|
4269
4115
|
}
|
|
4270
4116
|
try {
|
|
4271
4117
|
const dir = taskSignalDir(row.session_scope || "default");
|
|
4272
|
-
if (
|
|
4118
|
+
if (existsSync6(dir)) {
|
|
4273
4119
|
for (const f of readdirSync2(dir)) {
|
|
4274
4120
|
if (f.endsWith(`.review-${row.id}.pending`)) {
|
|
4275
|
-
unlinkSync3(
|
|
4121
|
+
unlinkSync3(path5.join(dir, f));
|
|
4276
4122
|
}
|
|
4277
4123
|
}
|
|
4278
4124
|
}
|
|
@@ -4322,10 +4168,10 @@ async function closeReviewRows(client, rows, status, now) {
|
|
|
4322
4168
|
function sweepExpiredSignalFiles(now = Date.now()) {
|
|
4323
4169
|
let deleted = 0;
|
|
4324
4170
|
try {
|
|
4325
|
-
const base =
|
|
4326
|
-
if (!
|
|
4171
|
+
const base = path5.join(homedir2(), ".exe-os", "task-signals");
|
|
4172
|
+
if (!existsSync6(base)) return 0;
|
|
4327
4173
|
for (const scopeName of readdirSync2(base)) {
|
|
4328
|
-
const scopeDir =
|
|
4174
|
+
const scopeDir = path5.join(base, scopeName);
|
|
4329
4175
|
let entries;
|
|
4330
4176
|
try {
|
|
4331
4177
|
entries = readdirSync2(scopeDir);
|
|
@@ -4334,7 +4180,7 @@ function sweepExpiredSignalFiles(now = Date.now()) {
|
|
|
4334
4180
|
}
|
|
4335
4181
|
for (const f of entries) {
|
|
4336
4182
|
if (!f.endsWith(".pending")) continue;
|
|
4337
|
-
const filePath =
|
|
4183
|
+
const filePath = path5.join(scopeDir, f);
|
|
4338
4184
|
try {
|
|
4339
4185
|
if (now - statSync3(filePath).mtimeMs > SIGNAL_FILE_TTL_MS) {
|
|
4340
4186
|
unlinkSync3(filePath);
|
|
@@ -4648,11 +4494,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4648
4494
|
);
|
|
4649
4495
|
}
|
|
4650
4496
|
try {
|
|
4651
|
-
const cacheDir =
|
|
4652
|
-
if (
|
|
4497
|
+
const cacheDir = path5.join(EXE_AI_DIR, "session-cache");
|
|
4498
|
+
if (existsSync6(cacheDir)) {
|
|
4653
4499
|
for (const f of readdirSync2(cacheDir)) {
|
|
4654
4500
|
if (f.startsWith("review-notified-")) {
|
|
4655
|
-
unlinkSync3(
|
|
4501
|
+
unlinkSync3(path5.join(cacheDir, f));
|
|
4656
4502
|
}
|
|
4657
4503
|
}
|
|
4658
4504
|
}
|
|
@@ -4660,193 +4506,6 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4660
4506
|
}
|
|
4661
4507
|
}
|
|
4662
4508
|
|
|
4663
|
-
// src/lib/worktree-guard.ts
|
|
4664
|
-
import { execFile, execFileSync as execFileSync2 } from "child_process";
|
|
4665
|
-
import { promisify as promisify2 } from "util";
|
|
4666
|
-
import { existsSync as existsSync8, realpathSync } from "fs";
|
|
4667
|
-
import path7 from "path";
|
|
4668
|
-
var GIT_TIMEOUT_MS = 1e4;
|
|
4669
|
-
var execFileAsync2 = promisify2(execFile);
|
|
4670
|
-
function realpath(p) {
|
|
4671
|
-
try {
|
|
4672
|
-
return realpathSync(p);
|
|
4673
|
-
} catch {
|
|
4674
|
-
return p;
|
|
4675
|
-
}
|
|
4676
|
-
}
|
|
4677
|
-
var defaultGitExecutor = async (args, cwd) => {
|
|
4678
|
-
const { stdout } = await execFileAsync2("git", args, {
|
|
4679
|
-
cwd,
|
|
4680
|
-
encoding: "utf8",
|
|
4681
|
-
timeout: GIT_TIMEOUT_MS
|
|
4682
|
-
});
|
|
4683
|
-
return String(stdout ?? "");
|
|
4684
|
-
};
|
|
4685
|
-
function listWorktreesPorcelain(repoRoot) {
|
|
4686
|
-
try {
|
|
4687
|
-
const output = execFileSync2("git", ["worktree", "list", "--porcelain"], {
|
|
4688
|
-
cwd: repoRoot,
|
|
4689
|
-
encoding: "utf8",
|
|
4690
|
-
timeout: GIT_TIMEOUT_MS,
|
|
4691
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4692
|
-
});
|
|
4693
|
-
const worktrees = [];
|
|
4694
|
-
let currentPath = "";
|
|
4695
|
-
for (const line of output.split("\n")) {
|
|
4696
|
-
if (line.startsWith("worktree ")) {
|
|
4697
|
-
currentPath = line.slice("worktree ".length);
|
|
4698
|
-
} else if (line.startsWith("branch ") && currentPath) {
|
|
4699
|
-
const branch = line.slice("branch refs/heads/".length);
|
|
4700
|
-
worktrees.push({ path: currentPath, branch });
|
|
4701
|
-
currentPath = "";
|
|
4702
|
-
}
|
|
4703
|
-
}
|
|
4704
|
-
return worktrees;
|
|
4705
|
-
} catch {
|
|
4706
|
-
return [];
|
|
4707
|
-
}
|
|
4708
|
-
}
|
|
4709
|
-
function inferSessionOwner(wtPath, branch) {
|
|
4710
|
-
const dirName = path7.basename(wtPath);
|
|
4711
|
-
if (dirName && dirName !== ".worktrees") {
|
|
4712
|
-
return dirName;
|
|
4713
|
-
}
|
|
4714
|
-
const dashIdx = branch.indexOf("-");
|
|
4715
|
-
if (dashIdx > 0) {
|
|
4716
|
-
return branch.slice(0, dashIdx);
|
|
4717
|
-
}
|
|
4718
|
-
return null;
|
|
4719
|
-
}
|
|
4720
|
-
function hasUnpushedCommits(wtPath, branch) {
|
|
4721
|
-
for (const mainRef of ["main", "master"]) {
|
|
4722
|
-
try {
|
|
4723
|
-
const ahead = execFileSync2(
|
|
4724
|
-
"git",
|
|
4725
|
-
["-C", wtPath, "rev-list", `${mainRef}..${branch}`, "--count"],
|
|
4726
|
-
{ encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "ignore"] }
|
|
4727
|
-
).trim();
|
|
4728
|
-
return parseInt(ahead, 10) > 0;
|
|
4729
|
-
} catch {
|
|
4730
|
-
}
|
|
4731
|
-
}
|
|
4732
|
-
return true;
|
|
4733
|
-
}
|
|
4734
|
-
function checkWorktreeCollisions(repoPath) {
|
|
4735
|
-
const repoRoot = getGitRoot(repoPath);
|
|
4736
|
-
if (!repoRoot) return [];
|
|
4737
|
-
const warnings = [];
|
|
4738
|
-
const worktrees = listWorktreesPorcelain(repoRoot);
|
|
4739
|
-
for (const wt of worktrees) {
|
|
4740
|
-
if (!wt.path.includes("/.worktrees/")) continue;
|
|
4741
|
-
if (!existsSync8(wt.path)) continue;
|
|
4742
|
-
const owner = inferSessionOwner(wt.path, wt.branch);
|
|
4743
|
-
const dirty = isWorktreeDirty(wt.path);
|
|
4744
|
-
const hasCommits = hasUnpushedCommits(wt.path, wt.branch);
|
|
4745
|
-
if (dirty) {
|
|
4746
|
-
warnings.push({
|
|
4747
|
-
worktreePath: wt.path,
|
|
4748
|
-
branch: wt.branch,
|
|
4749
|
-
sessionOwner: owner,
|
|
4750
|
-
dirty: true,
|
|
4751
|
-
reason: `Worktree has uncommitted changes (agent: ${owner ?? "unknown"}, branch: ${wt.branch})`
|
|
4752
|
-
});
|
|
4753
|
-
} else if (hasCommits) {
|
|
4754
|
-
warnings.push({
|
|
4755
|
-
worktreePath: wt.path,
|
|
4756
|
-
branch: wt.branch,
|
|
4757
|
-
sessionOwner: owner,
|
|
4758
|
-
dirty: false,
|
|
4759
|
-
reason: `Worktree has unpushed commits on branch ${wt.branch} (agent: ${owner ?? "unknown"})`
|
|
4760
|
-
});
|
|
4761
|
-
}
|
|
4762
|
-
}
|
|
4763
|
-
return warnings;
|
|
4764
|
-
}
|
|
4765
|
-
async function getGitRootAsync(dir, git) {
|
|
4766
|
-
try {
|
|
4767
|
-
const root = (await git(["rev-parse", "--show-toplevel"], dir)).trim();
|
|
4768
|
-
return root ? realpath(root) : null;
|
|
4769
|
-
} catch {
|
|
4770
|
-
return null;
|
|
4771
|
-
}
|
|
4772
|
-
}
|
|
4773
|
-
async function listWorktreesPorcelainAsync(repoRoot, git) {
|
|
4774
|
-
try {
|
|
4775
|
-
const output = await git(["worktree", "list", "--porcelain"], repoRoot);
|
|
4776
|
-
const worktrees = [];
|
|
4777
|
-
let currentPath = "";
|
|
4778
|
-
for (const line of output.split("\n")) {
|
|
4779
|
-
if (line.startsWith("worktree ")) {
|
|
4780
|
-
currentPath = line.slice("worktree ".length);
|
|
4781
|
-
} else if (line.startsWith("branch ") && currentPath) {
|
|
4782
|
-
const branch = line.slice("branch refs/heads/".length);
|
|
4783
|
-
worktrees.push({ path: currentPath, branch });
|
|
4784
|
-
currentPath = "";
|
|
4785
|
-
}
|
|
4786
|
-
}
|
|
4787
|
-
return worktrees;
|
|
4788
|
-
} catch {
|
|
4789
|
-
return [];
|
|
4790
|
-
}
|
|
4791
|
-
}
|
|
4792
|
-
async function isWorktreeDirtyAsync(wtPath, git) {
|
|
4793
|
-
try {
|
|
4794
|
-
return (await git(["status", "--porcelain"], wtPath)).trim().length > 0;
|
|
4795
|
-
} catch {
|
|
4796
|
-
return true;
|
|
4797
|
-
}
|
|
4798
|
-
}
|
|
4799
|
-
async function hasUnpushedCommitsAsync(wtPath, branch, git) {
|
|
4800
|
-
for (const mainRef of ["main", "master"]) {
|
|
4801
|
-
try {
|
|
4802
|
-
const ahead = (await git(["-C", wtPath, "rev-list", `${mainRef}..${branch}`, "--count"], wtPath)).trim();
|
|
4803
|
-
return parseInt(ahead, 10) > 0;
|
|
4804
|
-
} catch {
|
|
4805
|
-
}
|
|
4806
|
-
}
|
|
4807
|
-
return true;
|
|
4808
|
-
}
|
|
4809
|
-
async function checkWorktreeCollisionsAsync(repoPath, git = defaultGitExecutor) {
|
|
4810
|
-
const worktreeListCache = /* @__PURE__ */ new Map();
|
|
4811
|
-
const listWorktreesOnce = (root) => {
|
|
4812
|
-
let cached = worktreeListCache.get(root);
|
|
4813
|
-
if (!cached) {
|
|
4814
|
-
cached = listWorktreesPorcelainAsync(root, git);
|
|
4815
|
-
worktreeListCache.set(root, cached);
|
|
4816
|
-
}
|
|
4817
|
-
return cached;
|
|
4818
|
-
};
|
|
4819
|
-
const repoRoot = await getGitRootAsync(repoPath, git);
|
|
4820
|
-
if (!repoRoot) return [];
|
|
4821
|
-
const warnings = [];
|
|
4822
|
-
const worktrees = await listWorktreesOnce(repoRoot);
|
|
4823
|
-
for (const wt of worktrees) {
|
|
4824
|
-
if (!wt.path.includes("/.worktrees/")) continue;
|
|
4825
|
-
if (!existsSync8(wt.path)) continue;
|
|
4826
|
-
const owner = inferSessionOwner(wt.path, wt.branch);
|
|
4827
|
-
const dirty = await isWorktreeDirtyAsync(wt.path, git);
|
|
4828
|
-
const hasCommits = await hasUnpushedCommitsAsync(wt.path, wt.branch, git);
|
|
4829
|
-
if (dirty) {
|
|
4830
|
-
warnings.push({
|
|
4831
|
-
worktreePath: wt.path,
|
|
4832
|
-
branch: wt.branch,
|
|
4833
|
-
sessionOwner: owner,
|
|
4834
|
-
dirty: true,
|
|
4835
|
-
reason: `Worktree has uncommitted changes (agent: ${owner ?? "unknown"}, branch: ${wt.branch})`
|
|
4836
|
-
});
|
|
4837
|
-
} else if (hasCommits) {
|
|
4838
|
-
warnings.push({
|
|
4839
|
-
worktreePath: wt.path,
|
|
4840
|
-
branch: wt.branch,
|
|
4841
|
-
sessionOwner: owner,
|
|
4842
|
-
dirty: false,
|
|
4843
|
-
reason: `Worktree has unpushed commits on branch ${wt.branch} (agent: ${owner ?? "unknown"})`
|
|
4844
|
-
});
|
|
4845
|
-
}
|
|
4846
|
-
}
|
|
4847
|
-
return warnings;
|
|
4848
|
-
}
|
|
4849
|
-
|
|
4850
4509
|
// src/lib/intercom-prompts.ts
|
|
4851
4510
|
function formatIntercomPrompt(reason = "nudge") {
|
|
4852
4511
|
switch (reason) {
|
|
@@ -4863,6 +4522,15 @@ function formatIntercomPrompt(reason = "nudge") {
|
|
|
4863
4522
|
}
|
|
4864
4523
|
}
|
|
4865
4524
|
|
|
4525
|
+
// src/lib/tmux-dispatch-gate.ts
|
|
4526
|
+
var TMUX_DISPATCH_RETIRED_MESSAGE = "tmux dispatch is retired; dispatch a named subagent: Agent(subagent_type='<name>') \u2014 worktree isolation for code, brief it with a file fence and the guardrails, and persist its result into the task checklist row + memory before you finish. task(action='create') stays a durable checklist row for tracking and never launches compute.";
|
|
4527
|
+
function tmuxDispatchRefusal(toolName) {
|
|
4528
|
+
return `${toolName}: ${TMUX_DISPATCH_RETIRED_MESSAGE} Nothing was created or dispatched.`;
|
|
4529
|
+
}
|
|
4530
|
+
function spawnOverrideNoopNotice(fields) {
|
|
4531
|
+
return `Ignored ${fields.join(", ")} \u2014 ${TMUX_DISPATCH_RETIRED_MESSAGE} Set a subagent's model per employee with set_agent_config, or per dispatch with the Agent tool's own model argument.`;
|
|
4532
|
+
}
|
|
4533
|
+
|
|
4866
4534
|
// src/lib/session-busy-pattern.ts
|
|
4867
4535
|
var BUSY_PATTERN = /[✻✽✶✳].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt|Waiting for \d+ background agents?/i;
|
|
4868
4536
|
|
|
@@ -4870,13 +4538,13 @@ var BUSY_PATTERN = /[✻✽✶✳].*…|Running…|• Working|• Ran |• Expl
|
|
|
4870
4538
|
var _cachedExeBinDir;
|
|
4871
4539
|
function resolveExeBinDir() {
|
|
4872
4540
|
if (_cachedExeBinDir !== void 0) return _cachedExeBinDir;
|
|
4873
|
-
const nodeBinDir =
|
|
4874
|
-
if (
|
|
4541
|
+
const nodeBinDir = path6.dirname(process.execPath);
|
|
4542
|
+
if (existsSync7(path6.join(nodeBinDir, "exe-launch-agent"))) {
|
|
4875
4543
|
_cachedExeBinDir = nodeBinDir;
|
|
4876
4544
|
return _cachedExeBinDir;
|
|
4877
4545
|
}
|
|
4878
|
-
const localBin =
|
|
4879
|
-
if (
|
|
4546
|
+
const localBin = path6.join(os5.homedir(), ".local", "bin");
|
|
4547
|
+
if (existsSync7(path6.join(localBin, "exe-launch-agent"))) {
|
|
4880
4548
|
_cachedExeBinDir = localBin;
|
|
4881
4549
|
return _cachedExeBinDir;
|
|
4882
4550
|
}
|
|
@@ -4887,9 +4555,9 @@ function _resetExeBinDirCache() {
|
|
|
4887
4555
|
_cachedExeBinDir = void 0;
|
|
4888
4556
|
}
|
|
4889
4557
|
function buildSpawnPath(inheritedPath = process.env.PATH, execPath = process.execPath) {
|
|
4890
|
-
const sep =
|
|
4891
|
-
const nodeBinDir =
|
|
4892
|
-
const localBin =
|
|
4558
|
+
const sep = path6.delimiter;
|
|
4559
|
+
const nodeBinDir = path6.dirname(execPath);
|
|
4560
|
+
const localBin = path6.join(os5.homedir(), ".local", "bin");
|
|
4893
4561
|
const exeBinDir = resolveExeBinDir();
|
|
4894
4562
|
const prefixDirs = [nodeBinDir, exeBinDir, localBin].filter(
|
|
4895
4563
|
(d) => Boolean(d)
|
|
@@ -4907,17 +4575,17 @@ function buildSpawnPath(inheritedPath = process.env.PATH, execPath = process.exe
|
|
|
4907
4575
|
return ordered.join(sep);
|
|
4908
4576
|
}
|
|
4909
4577
|
function spawnLockDir() {
|
|
4910
|
-
return process.env.EXE_SPAWN_LOCK_DIR ||
|
|
4578
|
+
return process.env.EXE_SPAWN_LOCK_DIR || path6.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
4911
4579
|
}
|
|
4912
4580
|
function spawnLockPath(sessionName) {
|
|
4913
|
-
return
|
|
4581
|
+
return path6.join(spawnLockDir(), `${sessionName}.lock`);
|
|
4914
4582
|
}
|
|
4915
4583
|
function sanitizeLockKey(value) {
|
|
4916
4584
|
return value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
4917
4585
|
}
|
|
4918
4586
|
function spawnMutexPath(employeeName, exeSession) {
|
|
4919
4587
|
const key = `${sanitizeLockKey(baseAgentName(employeeName).toLowerCase())}--${sanitizeLockKey(exeSession)}`;
|
|
4920
|
-
return
|
|
4588
|
+
return path6.join(spawnLockDir(), `${key}.spawn-mutex.lock`);
|
|
4921
4589
|
}
|
|
4922
4590
|
function isProcessAlive(pid) {
|
|
4923
4591
|
try {
|
|
@@ -4929,8 +4597,8 @@ function isProcessAlive(pid) {
|
|
|
4929
4597
|
}
|
|
4930
4598
|
function acquireSpawnLock(sessionName) {
|
|
4931
4599
|
const lockDir = spawnLockDir();
|
|
4932
|
-
if (!
|
|
4933
|
-
|
|
4600
|
+
if (!existsSync7(lockDir)) {
|
|
4601
|
+
mkdirSync5(lockDir, { recursive: true });
|
|
4934
4602
|
}
|
|
4935
4603
|
const lockFile = spawnLockPath(sessionName);
|
|
4936
4604
|
const lockData = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
|
|
@@ -4945,7 +4613,7 @@ function acquireSpawnLock(sessionName) {
|
|
|
4945
4613
|
}
|
|
4946
4614
|
}
|
|
4947
4615
|
try {
|
|
4948
|
-
const lock = JSON.parse(
|
|
4616
|
+
const lock = JSON.parse(readFileSync5(lockFile, "utf8"));
|
|
4949
4617
|
const age = Date.now() - lock.timestamp;
|
|
4950
4618
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4951
4619
|
return false;
|
|
@@ -4957,7 +4625,7 @@ function acquireSpawnLock(sessionName) {
|
|
|
4957
4625
|
unlinkSync4(tmpName);
|
|
4958
4626
|
} catch {
|
|
4959
4627
|
}
|
|
4960
|
-
|
|
4628
|
+
writeFileSync4(lockFile, lockData);
|
|
4961
4629
|
return true;
|
|
4962
4630
|
} catch {
|
|
4963
4631
|
return false;
|
|
@@ -4970,7 +4638,7 @@ function acquireSpawnLock(sessionName) {
|
|
|
4970
4638
|
unlinkSync4(tmpName);
|
|
4971
4639
|
} catch {
|
|
4972
4640
|
}
|
|
4973
|
-
|
|
4641
|
+
writeFileSync4(lockFile, lockData);
|
|
4974
4642
|
return true;
|
|
4975
4643
|
} catch {
|
|
4976
4644
|
return false;
|
|
@@ -4985,7 +4653,7 @@ function releaseSpawnLock(sessionName) {
|
|
|
4985
4653
|
}
|
|
4986
4654
|
function acquireAgentSpawnMutex(employeeName, exeSession) {
|
|
4987
4655
|
const lockDir = spawnLockDir();
|
|
4988
|
-
if (!
|
|
4656
|
+
if (!existsSync7(lockDir)) mkdirSync5(lockDir, { recursive: true });
|
|
4989
4657
|
const lockFile = spawnMutexPath(employeeName, exeSession);
|
|
4990
4658
|
const lockData = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
|
|
4991
4659
|
try {
|
|
@@ -4997,7 +4665,7 @@ function acquireAgentSpawnMutex(employeeName, exeSession) {
|
|
|
4997
4665
|
if (err?.code !== "EEXIST") return true;
|
|
4998
4666
|
}
|
|
4999
4667
|
try {
|
|
5000
|
-
const lock = JSON.parse(
|
|
4668
|
+
const lock = JSON.parse(readFileSync5(lockFile, "utf8"));
|
|
5001
4669
|
const age = Date.now() - lock.timestamp;
|
|
5002
4670
|
if (isProcessAlive(lock.pid) && age < 6e4) return false;
|
|
5003
4671
|
} catch {
|
|
@@ -5009,7 +4677,7 @@ function acquireAgentSpawnMutex(employeeName, exeSession) {
|
|
|
5009
4677
|
unlinkSync4(tmpName);
|
|
5010
4678
|
} catch {
|
|
5011
4679
|
}
|
|
5012
|
-
|
|
4680
|
+
writeFileSync4(lockFile, lockData);
|
|
5013
4681
|
return true;
|
|
5014
4682
|
} catch {
|
|
5015
4683
|
return false;
|
|
@@ -5041,59 +4709,7 @@ function reconcileStaleSpawnLock(sessionName, isAlive = isEmployeeAlive) {
|
|
|
5041
4709
|
});
|
|
5042
4710
|
return { skip: false, brokeStaleLock: true };
|
|
5043
4711
|
}
|
|
5044
|
-
var SESSION_CACHE =
|
|
5045
|
-
var BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5046
|
-
function resolveBehaviorsExporterScript() {
|
|
5047
|
-
try {
|
|
5048
|
-
const thisFile = fileURLToPath(import.meta.url);
|
|
5049
|
-
const scriptPath = path8.join(
|
|
5050
|
-
path8.dirname(thisFile),
|
|
5051
|
-
"..",
|
|
5052
|
-
"bin",
|
|
5053
|
-
"exe-export-behaviors.js"
|
|
5054
|
-
);
|
|
5055
|
-
return existsSync9(scriptPath) ? scriptPath : null;
|
|
5056
|
-
} catch {
|
|
5057
|
-
return null;
|
|
5058
|
-
}
|
|
5059
|
-
}
|
|
5060
|
-
function exportBehaviorsSync(agentId, projectName, sessionKey) {
|
|
5061
|
-
const script = resolveBehaviorsExporterScript();
|
|
5062
|
-
if (!script) return null;
|
|
5063
|
-
try {
|
|
5064
|
-
const output = execFileSync3(
|
|
5065
|
-
process.execPath,
|
|
5066
|
-
[script, agentId, projectName, sessionKey],
|
|
5067
|
-
{ encoding: "utf-8", timeout: BEHAVIORS_EXPORT_TIMEOUT_MS }
|
|
5068
|
-
).trim();
|
|
5069
|
-
return output.length > 0 ? output : null;
|
|
5070
|
-
} catch (err) {
|
|
5071
|
-
process.stderr.write(
|
|
5072
|
-
`[tmux-routing] behaviors export failed for ${agentId}: ${err instanceof Error ? err.message : String(err)}
|
|
5073
|
-
`
|
|
5074
|
-
);
|
|
5075
|
-
return null;
|
|
5076
|
-
}
|
|
5077
|
-
}
|
|
5078
|
-
async function exportBehaviorsAsync(agentId, projectName, sessionKey) {
|
|
5079
|
-
const script = resolveBehaviorsExporterScript();
|
|
5080
|
-
if (!script) return null;
|
|
5081
|
-
try {
|
|
5082
|
-
const { stdout } = await execFileAsync3(
|
|
5083
|
-
process.execPath,
|
|
5084
|
-
[script, agentId, projectName, sessionKey],
|
|
5085
|
-
{ encoding: "utf-8", timeout: BEHAVIORS_EXPORT_TIMEOUT_MS }
|
|
5086
|
-
);
|
|
5087
|
-
const output = stdout.trim();
|
|
5088
|
-
return output.length > 0 ? output : null;
|
|
5089
|
-
} catch (err) {
|
|
5090
|
-
process.stderr.write(
|
|
5091
|
-
`[tmux-routing] behaviors export failed for ${agentId}: ${err instanceof Error ? err.message : String(err)}
|
|
5092
|
-
`
|
|
5093
|
-
);
|
|
5094
|
-
return null;
|
|
5095
|
-
}
|
|
5096
|
-
}
|
|
4712
|
+
var SESSION_CACHE = path6.join(os5.homedir(), ".exe-os", "session-cache");
|
|
5097
4713
|
function getMySession() {
|
|
5098
4714
|
return getTransport().getMySession();
|
|
5099
4715
|
}
|
|
@@ -5156,8 +4772,8 @@ function extractRootExe(name) {
|
|
|
5156
4772
|
return tail.length > 0 ? tail : null;
|
|
5157
4773
|
}
|
|
5158
4774
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5159
|
-
if (!
|
|
5160
|
-
|
|
4775
|
+
if (!existsSync7(SESSION_CACHE)) {
|
|
4776
|
+
mkdirSync5(SESSION_CACHE, { recursive: true });
|
|
5161
4777
|
}
|
|
5162
4778
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5163
4779
|
if (!isValidSessionScope(rootExe)) {
|
|
@@ -5167,7 +4783,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5167
4783
|
);
|
|
5168
4784
|
return;
|
|
5169
4785
|
}
|
|
5170
|
-
const filePath =
|
|
4786
|
+
const filePath = path6.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5171
4787
|
atomicWriteJsonSync(filePath, {
|
|
5172
4788
|
parentExe: rootExe,
|
|
5173
4789
|
dispatchedBy: dispatchedBy || rootExe,
|
|
@@ -5177,7 +4793,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5177
4793
|
var PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
5178
4794
|
function getParentExe(sessionKey) {
|
|
5179
4795
|
try {
|
|
5180
|
-
const data = JSON.parse(
|
|
4796
|
+
const data = JSON.parse(readFileSync5(path6.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5181
4797
|
if (data.registeredAt) {
|
|
5182
4798
|
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
5183
4799
|
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
@@ -5189,8 +4805,8 @@ function getParentExe(sessionKey) {
|
|
|
5189
4805
|
}
|
|
5190
4806
|
function getDispatchedBy(sessionKey) {
|
|
5191
4807
|
try {
|
|
5192
|
-
const data = JSON.parse(
|
|
5193
|
-
|
|
4808
|
+
const data = JSON.parse(readFileSync5(
|
|
4809
|
+
path6.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5194
4810
|
"utf8"
|
|
5195
4811
|
));
|
|
5196
4812
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5465,40 +5081,6 @@ function liveAgentSessionsForScope(employeeName, exeSession, listSessions2 = ()
|
|
|
5465
5081
|
function firstLiveAgentSessionForScope(employeeName, exeSession) {
|
|
5466
5082
|
return liveAgentSessionsForScope(employeeName, exeSession)[0] ?? null;
|
|
5467
5083
|
}
|
|
5468
|
-
function realpathForCompare(pathname) {
|
|
5469
|
-
try {
|
|
5470
|
-
return realpathSync2(pathname);
|
|
5471
|
-
} catch {
|
|
5472
|
-
return path8.resolve(pathname);
|
|
5473
|
-
}
|
|
5474
|
-
}
|
|
5475
|
-
function sameRealPath(a, b) {
|
|
5476
|
-
try {
|
|
5477
|
-
return path8.resolve(a) === path8.resolve(b);
|
|
5478
|
-
} catch {
|
|
5479
|
-
return a === b;
|
|
5480
|
-
}
|
|
5481
|
-
}
|
|
5482
|
-
function detectCoordinatorCheckoutCwd(cwd) {
|
|
5483
|
-
if (cwd.includes("/.worktrees/") || cwd.includes("/.claude/worktrees/")) return null;
|
|
5484
|
-
const cwdRepoRoot = getMainRepoRoot(cwd);
|
|
5485
|
-
if (!cwdRepoRoot) return null;
|
|
5486
|
-
const cwdResolved = realpathForCompare(cwd);
|
|
5487
|
-
if (!sameRealPath(cwdRepoRoot, cwdResolved)) return null;
|
|
5488
|
-
try {
|
|
5489
|
-
for (const session of listSessions()) {
|
|
5490
|
-
if (!isRootSession(session.windowName)) continue;
|
|
5491
|
-
if (!session.projectDir || !existsSync9(session.projectDir)) continue;
|
|
5492
|
-
const coordinatorRepoRoot = getMainRepoRoot(session.projectDir);
|
|
5493
|
-
if (coordinatorRepoRoot && sameRealPath(coordinatorRepoRoot, cwdRepoRoot)) {
|
|
5494
|
-
return { coordinatorSession: session.windowName, repoRoot: cwdRepoRoot };
|
|
5495
|
-
}
|
|
5496
|
-
}
|
|
5497
|
-
} catch {
|
|
5498
|
-
return null;
|
|
5499
|
-
}
|
|
5500
|
-
return null;
|
|
5501
|
-
}
|
|
5502
5084
|
var VERIFY_PANE_LINES = 200;
|
|
5503
5085
|
async function verifyPaneAtCapacity(sessionName) {
|
|
5504
5086
|
const transport = getTransport();
|
|
@@ -5514,7 +5096,7 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5514
5096
|
reason: `capture-pane failed: ${err instanceof Error ? err.message : String(err)}`
|
|
5515
5097
|
};
|
|
5516
5098
|
}
|
|
5517
|
-
const { isAtCapacity } = await import("./capacity-monitor-
|
|
5099
|
+
const { isAtCapacity } = await import("./capacity-monitor-YO3WRSRY.js");
|
|
5518
5100
|
if (!isAtCapacity(pane)) {
|
|
5519
5101
|
return {
|
|
5520
5102
|
atCapacity: false,
|
|
@@ -5528,15 +5110,15 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5528
5110
|
}
|
|
5529
5111
|
var INTERCOM_DEBOUNCE_MS = 3e5;
|
|
5530
5112
|
var CODEX_DEBOUNCE_MS = 3e5;
|
|
5531
|
-
var INTERCOM_LOG2 =
|
|
5113
|
+
var INTERCOM_LOG2 = path6.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
5532
5114
|
var INTERCOM_LOG_MAX_BYTES2 = 1024 * 1024;
|
|
5533
|
-
var
|
|
5534
|
-
var DEBOUNCE_FILE =
|
|
5115
|
+
var execFileAsync2 = promisify2(execFile);
|
|
5116
|
+
var DEBOUNCE_FILE = path6.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5535
5117
|
var DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5536
5118
|
function readDebounceState() {
|
|
5537
5119
|
try {
|
|
5538
|
-
if (!
|
|
5539
|
-
const raw = JSON.parse(
|
|
5120
|
+
if (!existsSync7(DEBOUNCE_FILE)) return {};
|
|
5121
|
+
const raw = JSON.parse(readFileSync5(DEBOUNCE_FILE, "utf8"));
|
|
5540
5122
|
const state = {};
|
|
5541
5123
|
for (const [key, val] of Object.entries(raw)) {
|
|
5542
5124
|
if (typeof val === "number") {
|
|
@@ -5552,7 +5134,7 @@ function readDebounceState() {
|
|
|
5552
5134
|
}
|
|
5553
5135
|
function writeDebounceState(state) {
|
|
5554
5136
|
try {
|
|
5555
|
-
if (!
|
|
5137
|
+
if (!existsSync7(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
|
|
5556
5138
|
atomicWriteJsonSync(DEBOUNCE_FILE, state);
|
|
5557
5139
|
} catch {
|
|
5558
5140
|
}
|
|
@@ -5560,9 +5142,9 @@ function writeDebounceState(state) {
|
|
|
5560
5142
|
function resolveSessionRuntime(targetSession) {
|
|
5561
5143
|
const agentName = baseAgentName(targetSession.split("-")[0] ?? "");
|
|
5562
5144
|
try {
|
|
5563
|
-
const diPath =
|
|
5564
|
-
if (
|
|
5565
|
-
const di = JSON.parse(
|
|
5145
|
+
const diPath = path6.join(SESSION_CACHE, `dispatch-info-${targetSession}.json`);
|
|
5146
|
+
if (existsSync7(diPath)) {
|
|
5147
|
+
const di = JSON.parse(readFileSync5(diPath, "utf-8"));
|
|
5566
5148
|
if (di.runtime) return di.runtime;
|
|
5567
5149
|
}
|
|
5568
5150
|
return getAgentRuntime(agentName).runtime;
|
|
@@ -5602,7 +5184,7 @@ function logIntercom(msg, meta) {
|
|
|
5602
5184
|
process.stderr.write(`[intercom] ${msg}${metaStr}
|
|
5603
5185
|
`);
|
|
5604
5186
|
try {
|
|
5605
|
-
if (
|
|
5187
|
+
if (existsSync7(INTERCOM_LOG2) && statSync4(INTERCOM_LOG2).size > INTERCOM_LOG_MAX_BYTES2) {
|
|
5606
5188
|
renameSync3(INTERCOM_LOG2, `${INTERCOM_LOG2}.1`);
|
|
5607
5189
|
}
|
|
5608
5190
|
appendFileSync3(INTERCOM_LOG2, line);
|
|
@@ -5629,7 +5211,7 @@ function getSessionState(sessionName) {
|
|
|
5629
5211
|
}
|
|
5630
5212
|
}
|
|
5631
5213
|
async function captureSessionPaneAsync(sessionName, lines) {
|
|
5632
|
-
const { stdout } = await
|
|
5214
|
+
const { stdout } = await execFileAsync2(
|
|
5633
5215
|
"tmux",
|
|
5634
5216
|
["capture-pane", "-t", sessionName, "-p", "-S", `-${lines}`],
|
|
5635
5217
|
{ encoding: "utf8", timeout: 3e3 }
|
|
@@ -5678,7 +5260,7 @@ async function confirmNudgeSubmittedAsync(targetSession, runtime) {
|
|
|
5678
5260
|
if (verdict.state === "confirmed") return true;
|
|
5679
5261
|
if (!isSubmitRepairable(verdict.reason)) return false;
|
|
5680
5262
|
try {
|
|
5681
|
-
await
|
|
5263
|
+
await execFileAsync2("tmux", ["send-keys", "-t", targetSession, isCodex ? "C-m" : "Enter"], { timeout: 1e3 });
|
|
5682
5264
|
} catch {
|
|
5683
5265
|
}
|
|
5684
5266
|
await new Promise((r) => setTimeout(r, SUBMIT_CONFIRM_DELAY_MS));
|
|
@@ -5821,7 +5403,7 @@ async function countScopedPendingReviews(targetSession, reviewer) {
|
|
|
5821
5403
|
}
|
|
5822
5404
|
async function countScopedOpenTasks(targetSession, agentId) {
|
|
5823
5405
|
try {
|
|
5824
|
-
const { fastDbInit } = await import("./fast-db-init-
|
|
5406
|
+
const { fastDbInit } = await import("./fast-db-init-UN225WS7.js");
|
|
5825
5407
|
const client = await fastDbInit();
|
|
5826
5408
|
const scope = extractRootExe(targetSession);
|
|
5827
5409
|
if (!scope) return 0;
|
|
@@ -5878,7 +5460,7 @@ function buildScopedInProgressTaskQuery(targetSession) {
|
|
|
5878
5460
|
async function findScopedInProgressTaskId(targetSession) {
|
|
5879
5461
|
const q = buildScopedInProgressTaskQuery(targetSession);
|
|
5880
5462
|
if (!q) return null;
|
|
5881
|
-
const { fastDbInit } = await import("./fast-db-init-
|
|
5463
|
+
const { fastDbInit } = await import("./fast-db-init-UN225WS7.js");
|
|
5882
5464
|
const client = await fastDbInit();
|
|
5883
5465
|
const result = await client.execute(q);
|
|
5884
5466
|
const id = result.rows[0]?.id;
|
|
@@ -5999,7 +5581,7 @@ function sendIntercom(targetSession, opts) {
|
|
|
5999
5581
|
const ledgerEnv = (process.env.EXE_NOTIFICATION_LEDGER ?? "").trim().toLowerCase();
|
|
6000
5582
|
if (ledgerEnv === "shadow") {
|
|
6001
5583
|
const ledgerScope = opts && "callerSessionScope" in opts ? opts.callerSessionScope : void 0;
|
|
6002
|
-
void import("./notification-ledger-
|
|
5584
|
+
void import("./notification-ledger-AV5242RM.js").then((m) => m.recordShadowNudge(targetSession, reason, ledgerScope, { force: opts?.force })).catch(() => {
|
|
6003
5585
|
});
|
|
6004
5586
|
}
|
|
6005
5587
|
if (!opts?.force && isDebounced(targetSession)) {
|
|
@@ -6040,8 +5622,8 @@ function sendIntercom(targetSession, opts) {
|
|
|
6040
5622
|
try {
|
|
6041
5623
|
const rawAgent2 = targetSession.split("-")[0] ?? targetSession;
|
|
6042
5624
|
const agent = baseAgentName(rawAgent2);
|
|
6043
|
-
const markerPath =
|
|
6044
|
-
if (
|
|
5625
|
+
const markerPath = path6.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
5626
|
+
if (existsSync7(markerPath)) {
|
|
6045
5627
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
6046
5628
|
recordResult("debounced", "in_progress_marker");
|
|
6047
5629
|
return "debounced";
|
|
@@ -6101,89 +5683,22 @@ function sendIntercom(targetSession, opts) {
|
|
|
6101
5683
|
return "failed";
|
|
6102
5684
|
}
|
|
6103
5685
|
}
|
|
6104
|
-
var DEFERRED_SPAWN_NUDGE_ATTEMPTS = 3;
|
|
6105
|
-
var DEFERRED_SPAWN_NUDGE_RETRY_MS = Number(process.env.EXE_DEFERRED_SPAWN_NUDGE_RETRY_MS || 5e3);
|
|
6106
|
-
function scheduleDeferredSpawnNudge(input) {
|
|
6107
|
-
const attemptSend = (attempt) => {
|
|
6108
|
-
setTimeout(() => {
|
|
6109
|
-
void (async () => {
|
|
6110
|
-
try {
|
|
6111
|
-
if (!input.transport.isAlive(input.sessionName)) {
|
|
6112
|
-
throw new Error("session not alive at deferred nudge time");
|
|
6113
|
-
}
|
|
6114
|
-
input.transport.sendKeysLiteral(input.sessionName, input.message, input.runtime);
|
|
6115
|
-
const verdict = await verifyNudgeDeliveredAsync(input.sessionName, input.runtime, true);
|
|
6116
|
-
if (verdict.state !== "confirmed") {
|
|
6117
|
-
throw new Error(`nudge unconfirmed at target pane (${verdict.reason})`);
|
|
6118
|
-
}
|
|
6119
|
-
process.stderr.write(
|
|
6120
|
-
`[tmux-routing] deferred spawn nudge delivered to ${input.sessionName} (attempt ${attempt}/${DEFERRED_SPAWN_NUDGE_ATTEMPTS})
|
|
6121
|
-
`
|
|
6122
|
-
);
|
|
6123
|
-
recordOrchestrationEventBestEffort({
|
|
6124
|
-
eventType: "dispatch.spawn_nudge.delivered",
|
|
6125
|
-
source: "tmux-routing.scheduleDeferredSpawnNudge",
|
|
6126
|
-
agentId: input.employeeName,
|
|
6127
|
-
sessionScope: input.exeSession,
|
|
6128
|
-
tmuxSession: input.sessionName,
|
|
6129
|
-
runtime: input.runtime,
|
|
6130
|
-
result: "delivered",
|
|
6131
|
-
payload: { attempt }
|
|
6132
|
-
});
|
|
6133
|
-
} catch (err) {
|
|
6134
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
6135
|
-
process.stderr.write(
|
|
6136
|
-
`[tmux-routing] deferred spawn nudge failed for ${input.sessionName} (attempt ${attempt}/${DEFERRED_SPAWN_NUDGE_ATTEMPTS}): ${msg}
|
|
6137
|
-
`
|
|
6138
|
-
);
|
|
6139
|
-
recordOrchestrationEventBestEffort({
|
|
6140
|
-
eventType: "dispatch.spawn_nudge.failed",
|
|
6141
|
-
source: "tmux-routing.scheduleDeferredSpawnNudge",
|
|
6142
|
-
severity: "warn",
|
|
6143
|
-
agentId: input.employeeName,
|
|
6144
|
-
sessionScope: input.exeSession,
|
|
6145
|
-
tmuxSession: input.sessionName,
|
|
6146
|
-
runtime: input.runtime,
|
|
6147
|
-
result: "failed",
|
|
6148
|
-
errorCode: "deferred_nudge_failed",
|
|
6149
|
-
payload: { attempt, error: msg }
|
|
6150
|
-
});
|
|
6151
|
-
if (attempt < DEFERRED_SPAWN_NUDGE_ATTEMPTS) {
|
|
6152
|
-
attemptSend(attempt + 1);
|
|
6153
|
-
return;
|
|
6154
|
-
}
|
|
6155
|
-
queueIntercom(input.sessionName, `spawn nudge failed after ${attempt} attempts: ${msg}`, "signal");
|
|
6156
|
-
void import("./notifications-T5C2CVB3.js").then(({ writeNotification: writeNotification2 }) => writeNotification2({
|
|
6157
|
-
agentId: getCoordinatorName(),
|
|
6158
|
-
agentRole: "COO",
|
|
6159
|
-
event: "error_spike",
|
|
6160
|
-
project: "",
|
|
6161
|
-
summary: `Spawned ${input.sessionName}, but the initial task nudge failed after ${attempt} attempts (${msg}). Worker may be idle with open task; check session and re-dispatch if needed.`,
|
|
6162
|
-
sessionScope: input.exeSession
|
|
6163
|
-
})).catch(() => {
|
|
6164
|
-
});
|
|
6165
|
-
}
|
|
6166
|
-
})();
|
|
6167
|
-
}, attempt === 1 ? 1e4 : DEFERRED_SPAWN_NUDGE_RETRY_MS);
|
|
6168
|
-
};
|
|
6169
|
-
attemptSend(1);
|
|
6170
|
-
}
|
|
6171
5686
|
var CODEX_SUBMIT_DELAY_MS = Number(process.env.EXE_CODEX_SUBMIT_DELAY_MS || 250);
|
|
6172
5687
|
var SUBMIT_DELAY_MS = Number(process.env.EXE_SUBMIT_DELAY_MS || 60);
|
|
6173
5688
|
async function sendKeysLiteralAsync(targetSession, text, runtime) {
|
|
6174
5689
|
const isCodex = runtime === "codex";
|
|
6175
5690
|
const submit = async () => {
|
|
6176
5691
|
await new Promise((r) => setTimeout(r, isCodex ? CODEX_SUBMIT_DELAY_MS : SUBMIT_DELAY_MS));
|
|
6177
|
-
await
|
|
5692
|
+
await execFileAsync2("tmux", ["send-keys", "-t", targetSession, isCodex ? "C-m" : "Enter"], { timeout: 1e3 });
|
|
6178
5693
|
};
|
|
6179
5694
|
if (text.length >= 500) {
|
|
6180
|
-
const tmpDir =
|
|
6181
|
-
|
|
6182
|
-
const tmpFile =
|
|
5695
|
+
const tmpDir = path6.join(os5.tmpdir(), "exe-os-intercom");
|
|
5696
|
+
mkdirSync5(tmpDir, { recursive: true });
|
|
5697
|
+
const tmpFile = path6.join(tmpDir, `msg-${process.pid}-${Date.now()}.txt`);
|
|
6183
5698
|
try {
|
|
6184
|
-
|
|
6185
|
-
await
|
|
6186
|
-
await
|
|
5699
|
+
writeFileSync4(tmpFile, text, "utf-8");
|
|
5700
|
+
await execFileAsync2("tmux", ["load-buffer", tmpFile], { timeout: 3e3 });
|
|
5701
|
+
await execFileAsync2("tmux", ["paste-buffer", "-p", "-t", targetSession], { timeout: 3e3 });
|
|
6187
5702
|
await submit();
|
|
6188
5703
|
} finally {
|
|
6189
5704
|
try {
|
|
@@ -6193,7 +5708,7 @@ async function sendKeysLiteralAsync(targetSession, text, runtime) {
|
|
|
6193
5708
|
}
|
|
6194
5709
|
return;
|
|
6195
5710
|
}
|
|
6196
|
-
await
|
|
5711
|
+
await execFileAsync2("tmux", ["send-keys", "-t", targetSession, "-l", text], { timeout: 3e3 });
|
|
6197
5712
|
await submit();
|
|
6198
5713
|
}
|
|
6199
5714
|
async function sendIntercomAsync(targetSession, opts) {
|
|
@@ -6248,7 +5763,7 @@ async function sendIntercomAsync(targetSession, opts) {
|
|
|
6248
5763
|
let ledgerClaim = null;
|
|
6249
5764
|
if (authoritativeLedger) {
|
|
6250
5765
|
try {
|
|
6251
|
-
const { claimAuthoritativeNudge } = await import("./notification-ledger-
|
|
5766
|
+
const { claimAuthoritativeNudge } = await import("./notification-ledger-AV5242RM.js");
|
|
6252
5767
|
const claim2 = await claimAuthoritativeNudge(
|
|
6253
5768
|
targetSession,
|
|
6254
5769
|
reason,
|
|
@@ -6271,7 +5786,7 @@ async function sendIntercomAsync(targetSession, opts) {
|
|
|
6271
5786
|
const markLedgerDelivered = async () => {
|
|
6272
5787
|
if (!ledgerClaim) return;
|
|
6273
5788
|
try {
|
|
6274
|
-
const { markNudgeDelivered } = await import("./notification-ledger-
|
|
5789
|
+
const { markNudgeDelivered } = await import("./notification-ledger-AV5242RM.js");
|
|
6275
5790
|
await markNudgeDelivered(ledgerClaim.id, ledgerClaim.sessionScope);
|
|
6276
5791
|
} catch {
|
|
6277
5792
|
}
|
|
@@ -6279,7 +5794,7 @@ async function sendIntercomAsync(targetSession, opts) {
|
|
|
6279
5794
|
const markLedgerFailed = async (error) => {
|
|
6280
5795
|
if (!ledgerClaim) return;
|
|
6281
5796
|
try {
|
|
6282
|
-
const { markNudgeDeliveryFailed } = await import("./notification-ledger-
|
|
5797
|
+
const { markNudgeDeliveryFailed } = await import("./notification-ledger-AV5242RM.js");
|
|
6283
5798
|
await markNudgeDeliveryFailed(ledgerClaim.id, error, ledgerClaim.sessionScope);
|
|
6284
5799
|
} catch {
|
|
6285
5800
|
}
|
|
@@ -6290,7 +5805,7 @@ async function sendIntercomAsync(targetSession, opts) {
|
|
|
6290
5805
|
return "debounced";
|
|
6291
5806
|
}
|
|
6292
5807
|
try {
|
|
6293
|
-
const { stdout } = await
|
|
5808
|
+
const { stdout } = await execFileAsync2("tmux", ["list-sessions", "-F", "#{session_name}"], { encoding: "utf8", timeout: 3e3 });
|
|
6294
5809
|
const sessions = stdout.trim().split("\n").filter(Boolean);
|
|
6295
5810
|
if (!sessions.includes(targetSession)) {
|
|
6296
5811
|
logIntercom(`SKIP \u2192 ${targetSession} (session not found)`);
|
|
@@ -6321,10 +5836,10 @@ async function sendIntercomAsync(targetSession, opts) {
|
|
|
6321
5836
|
return "queued";
|
|
6322
5837
|
}
|
|
6323
5838
|
try {
|
|
6324
|
-
const mode = await
|
|
5839
|
+
const mode = await execFileAsync2("tmux", ["display-message", "-p", "-t", targetSession, "#{pane_in_mode}"], { encoding: "utf8", timeout: 1e3 });
|
|
6325
5840
|
if (mode.stdout.trim() === "1") {
|
|
6326
5841
|
logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
|
|
6327
|
-
await
|
|
5842
|
+
await execFileAsync2("tmux", ["send-keys", "-t", targetSession, "q"], { timeout: 1e3 });
|
|
6328
5843
|
}
|
|
6329
5844
|
} catch {
|
|
6330
5845
|
}
|
|
@@ -6428,23 +5943,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
|
|
|
6428
5943
|
return false;
|
|
6429
5944
|
}
|
|
6430
5945
|
}
|
|
6431
|
-
function
|
|
6432
|
-
process.stderr.write(
|
|
6433
|
-
`[tmux-routing] WORKTREE ISOLATION BYPASSED by explicit spawn_worktree=false for code-role ${employeeName} in real repo ${projectDir} \u2014 commits can land on main. This is a deliberate operator opt-out.
|
|
6434
|
-
`
|
|
6435
|
-
);
|
|
6436
|
-
}
|
|
6437
|
-
function worktreeIsolationGate(employeeName, projectDir, opts) {
|
|
6438
|
-
const nonGit = !isInsideGitRepo(projectDir);
|
|
6439
|
-
if (opts?.spawnWorktree === false) {
|
|
6440
|
-
if (!nonGit) emitOptOutBypassWarning(employeeName, projectDir);
|
|
6441
|
-
return { allow: true, nonGit };
|
|
6442
|
-
}
|
|
6443
|
-
if (nonGit) return { allow: true, nonGit: true };
|
|
6444
|
-
const message = `Worktree isolation required for code-role ${employeeName}; refusing to spawn in ${projectDir}. ${projectDir} is a git repo but the worktree could not be created (git error/timeout) \u2014 spawning here would let commits land on main. To dispatch a code-role agent here anyway, pass spawn_worktree=false (create_task) or opts.spawnWorktree=false.`;
|
|
6445
|
-
return { allow: false, message };
|
|
6446
|
-
}
|
|
6447
|
-
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
5946
|
+
function ensureEmployee(employeeName, exeSession, _projectDir, opts) {
|
|
6448
5947
|
employeeName = employeeName.toLowerCase();
|
|
6449
5948
|
if (isCoordinatorName(employeeName)) {
|
|
6450
5949
|
return { status: "failed", sessionName: "", error: "The coordinator is not a dispatchable employee" };
|
|
@@ -6542,12 +6041,12 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6542
6041
|
} else {
|
|
6543
6042
|
const liveSession = firstLiveAgentSessionForScope(employeeName, exeSession);
|
|
6544
6043
|
if (liveSession) {
|
|
6545
|
-
const
|
|
6044
|
+
const result = sendIntercom(liveSession, {
|
|
6546
6045
|
force: true,
|
|
6547
6046
|
reason: "signal",
|
|
6548
6047
|
callerSessionScope: exeSession
|
|
6549
6048
|
});
|
|
6550
|
-
const status = classifyIntercomHandoff(
|
|
6049
|
+
const status = classifyIntercomHandoff(result);
|
|
6551
6050
|
if (status === "failed") {
|
|
6552
6051
|
return { status, sessionName: liveSession, error: "intercom delivery failed during spawn mutex handoff" };
|
|
6553
6052
|
}
|
|
@@ -6633,114 +6132,19 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6633
6132
|
if (isEmployeeAlive(sessionName)) {
|
|
6634
6133
|
releaseIfOwned();
|
|
6635
6134
|
releaseSpawnMutexIfOwned();
|
|
6636
|
-
const
|
|
6637
|
-
const status = classifyIntercomHandoff(
|
|
6135
|
+
const result = sendIntercom(sessionName, { force: true, reason: "signal", callerSessionScope: exeSession });
|
|
6136
|
+
const status = classifyIntercomHandoff(result);
|
|
6638
6137
|
if (status === "failed") {
|
|
6639
6138
|
return { status, sessionName, error: "intercom delivery failed" };
|
|
6640
6139
|
}
|
|
6641
6140
|
return { status, sessionName };
|
|
6642
6141
|
}
|
|
6643
|
-
|
|
6644
|
-
if (!spawnOpts.cwd) {
|
|
6645
|
-
const COORDINATOR_ROLES = /* @__PURE__ */ new Set(["COO", "CTO", "CMO"]);
|
|
6646
|
-
try {
|
|
6647
|
-
const roster = loadEmployeesSync();
|
|
6648
|
-
const emp = roster.find((e) => baseAgentName(e.name) === baseAgentName(employeeName));
|
|
6649
|
-
if (emp && !COORDINATOR_ROLES.has(emp.role)) {
|
|
6650
|
-
try {
|
|
6651
|
-
const collisions = spawnOpts.collisionGuardHandled ? [] : checkWorktreeCollisions(projectDir);
|
|
6652
|
-
if (collisions.length > 0) {
|
|
6653
|
-
for (const c of collisions) {
|
|
6654
|
-
process.stderr.write(
|
|
6655
|
-
`[worktree-guard] WARNING: ${c.reason} (path: ${c.worktreePath})
|
|
6656
|
-
`
|
|
6657
|
-
);
|
|
6658
|
-
}
|
|
6659
|
-
recordOrchestrationEventBestEffort({
|
|
6660
|
-
eventType: "worktree.collision_warning",
|
|
6661
|
-
source: "tmux-routing.ensureEmployee",
|
|
6662
|
-
agentId: employeeName,
|
|
6663
|
-
sessionScope: exeSession,
|
|
6664
|
-
instanceId: effectiveInstance != null ? String(effectiveInstance) : null,
|
|
6665
|
-
payload: { collisionCount: collisions.length, collisions: collisions.map((c) => ({ branch: c.branch, owner: c.sessionOwner, dirty: c.dirty })) }
|
|
6666
|
-
});
|
|
6667
|
-
}
|
|
6668
|
-
} catch {
|
|
6669
|
-
}
|
|
6670
|
-
const wtPath = ensureWorktree(projectDir, employeeName, effectiveInstance);
|
|
6671
|
-
if (wtPath) {
|
|
6672
|
-
spawnOpts.cwd = wtPath;
|
|
6673
|
-
process.stderr.write(`[tmux-routing] worktree isolation: ${employeeName} \u2192 ${wtPath}
|
|
6674
|
-
`);
|
|
6675
|
-
} else {
|
|
6676
|
-
const gate = worktreeIsolationGate(employeeName, projectDir, opts);
|
|
6677
|
-
if (gate.allow) {
|
|
6678
|
-
spawnOpts.cwd = projectDir;
|
|
6679
|
-
spawnOpts.spawnWorktree = false;
|
|
6680
|
-
const reason = gate.nonGit ? "non_git_project" : "opt_out_override";
|
|
6681
|
-
process.stderr.write(
|
|
6682
|
-
`[tmux-routing] WORKTREE BYPASSED: code-role ${employeeName} spawning in ${projectDir} (reason=${reason}) \u2014 no git worktree isolation
|
|
6683
|
-
`
|
|
6684
|
-
);
|
|
6685
|
-
recordOrchestrationEventBestEffort({
|
|
6686
|
-
eventType: "worktree.bypassed",
|
|
6687
|
-
source: "tmux-routing.ensureEmployee",
|
|
6688
|
-
severity: "warn",
|
|
6689
|
-
agentId: employeeName,
|
|
6690
|
-
sessionScope: exeSession,
|
|
6691
|
-
instanceId: effectiveInstance != null ? String(effectiveInstance) : null,
|
|
6692
|
-
tmuxSession: sessionName,
|
|
6693
|
-
payload: {
|
|
6694
|
-
agent: employeeName,
|
|
6695
|
-
cwd: projectDir,
|
|
6696
|
-
reason,
|
|
6697
|
-
risk: "code-role agent runs in project root without git worktree isolation"
|
|
6698
|
-
}
|
|
6699
|
-
});
|
|
6700
|
-
} else {
|
|
6701
|
-
releaseIfOwned();
|
|
6702
|
-
releaseSpawnMutexIfOwned();
|
|
6703
|
-
process.stderr.write(`[tmux-routing] ${gate.message}
|
|
6704
|
-
`);
|
|
6705
|
-
return { status: "failed", sessionName, error: gate.message };
|
|
6706
|
-
}
|
|
6707
|
-
}
|
|
6708
|
-
}
|
|
6709
|
-
} catch (e) {
|
|
6710
|
-
process.stderr.write(`[tmux-routing] worktree setup failed for ${employeeName}: ${e instanceof Error ? e.message : String(e)}
|
|
6711
|
-
`);
|
|
6712
|
-
}
|
|
6713
|
-
}
|
|
6714
|
-
let result;
|
|
6715
|
-
try {
|
|
6716
|
-
result = spawnEmployee(employeeName, exeSession, projectDir, spawnOpts);
|
|
6717
|
-
} catch (err) {
|
|
6718
|
-
releaseSpawnLock(sessionName);
|
|
6719
|
-
releaseSpawnMutexIfOwned();
|
|
6720
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
6721
|
-
process.stderr.write(
|
|
6722
|
-
`[ensureEmployee] spawnEmployee THREW for ${sessionName}: ${msg} \u2014 released spawn lock to prevent permanent dispatch block
|
|
6723
|
-
`
|
|
6724
|
-
);
|
|
6725
|
-
recordOrchestrationEventBestEffort({
|
|
6726
|
-
eventType: "tmux.spawn.completed",
|
|
6727
|
-
source: "tmux-routing.ensureEmployee",
|
|
6728
|
-
severity: "warn",
|
|
6729
|
-
agentId: employeeName,
|
|
6730
|
-
sessionScope: exeSession,
|
|
6731
|
-
tmuxSession: sessionName,
|
|
6732
|
-
result: "failed",
|
|
6733
|
-
errorCode: "spawn_threw",
|
|
6734
|
-
payload: { error: msg }
|
|
6735
|
-
});
|
|
6736
|
-
return { status: "failed", sessionName, error: `spawn threw: ${msg}` };
|
|
6737
|
-
}
|
|
6738
|
-
if (result.error) {
|
|
6739
|
-
releaseSpawnMutexIfOwned();
|
|
6740
|
-
return { status: "failed", sessionName, error: result.error };
|
|
6741
|
-
}
|
|
6142
|
+
releaseIfOwned();
|
|
6742
6143
|
releaseSpawnMutexIfOwned();
|
|
6743
|
-
|
|
6144
|
+
const refusal = tmuxDispatchRefusal(`ensureEmployee(${employeeName})`);
|
|
6145
|
+
process.stderr.write(`[ensureEmployee] ${refusal}
|
|
6146
|
+
`);
|
|
6147
|
+
return { status: "failed", sessionName, error: refusal };
|
|
6744
6148
|
}
|
|
6745
6149
|
async function ensureEmployeeAsync(employeeName, exeSession, projectDir, opts) {
|
|
6746
6150
|
let asyncOpts = { ...opts, behaviorsExported: true };
|
|
@@ -6825,87 +6229,6 @@ async function ensureEmployeeAsync(employeeName, exeSession, projectDir, opts) {
|
|
|
6825
6229
|
spawnMutexHeld: true
|
|
6826
6230
|
};
|
|
6827
6231
|
}
|
|
6828
|
-
if (!asyncOpts.cwd) {
|
|
6829
|
-
const COORDINATOR_ROLES = /* @__PURE__ */ new Set(["COO", "CTO", "CMO"]);
|
|
6830
|
-
try {
|
|
6831
|
-
const roster = loadEmployeesSync();
|
|
6832
|
-
const emp = roster.find((e) => baseAgentName(e.name) === baseAgentName(employeeName));
|
|
6833
|
-
if (emp && !COORDINATOR_ROLES.has(emp.role)) {
|
|
6834
|
-
try {
|
|
6835
|
-
const collisions = await checkWorktreeCollisionsAsync(projectDir);
|
|
6836
|
-
if (collisions.length > 0) {
|
|
6837
|
-
for (const c of collisions) {
|
|
6838
|
-
process.stderr.write(
|
|
6839
|
-
`[worktree-guard] WARNING: ${c.reason} (path: ${c.worktreePath})
|
|
6840
|
-
`
|
|
6841
|
-
);
|
|
6842
|
-
}
|
|
6843
|
-
recordOrchestrationEventBestEffort({
|
|
6844
|
-
eventType: "worktree.collision_warning",
|
|
6845
|
-
source: "tmux-routing.ensureEmployeeAsync",
|
|
6846
|
-
agentId: employeeName,
|
|
6847
|
-
sessionScope: exeSession,
|
|
6848
|
-
instanceId: asyncOpts.instance != null ? String(asyncOpts.instance) : null,
|
|
6849
|
-
payload: { collisionCount: collisions.length, collisions: collisions.map((c) => ({ branch: c.branch, owner: c.sessionOwner, dirty: c.dirty })) }
|
|
6850
|
-
});
|
|
6851
|
-
}
|
|
6852
|
-
} catch {
|
|
6853
|
-
}
|
|
6854
|
-
const wtPath = await ensureWorktreeAsync(projectDir, employeeName, asyncOpts.instance);
|
|
6855
|
-
if (wtPath) {
|
|
6856
|
-
asyncOpts.cwd = wtPath;
|
|
6857
|
-
} else {
|
|
6858
|
-
const sessionName = employeeSessionName(employeeName, exeSession, asyncOpts.instance);
|
|
6859
|
-
const gate = worktreeIsolationGate(employeeName, projectDir, asyncOpts);
|
|
6860
|
-
if (gate.allow) {
|
|
6861
|
-
asyncOpts.cwd = projectDir;
|
|
6862
|
-
asyncOpts.spawnWorktree = false;
|
|
6863
|
-
const reason = gate.nonGit ? "non_git_project" : "opt_out_override";
|
|
6864
|
-
process.stderr.write(
|
|
6865
|
-
`[ensureEmployeeAsync] WORKTREE BYPASSED: code-role ${employeeName} spawning in ${projectDir} (reason=${reason}) \u2014 no git worktree isolation
|
|
6866
|
-
`
|
|
6867
|
-
);
|
|
6868
|
-
recordOrchestrationEventBestEffort({
|
|
6869
|
-
eventType: "worktree.bypassed",
|
|
6870
|
-
source: "tmux-routing.ensureEmployeeAsync",
|
|
6871
|
-
severity: "warn",
|
|
6872
|
-
agentId: employeeName,
|
|
6873
|
-
sessionScope: exeSession,
|
|
6874
|
-
instanceId: asyncOpts.instance != null ? String(asyncOpts.instance) : null,
|
|
6875
|
-
tmuxSession: sessionName,
|
|
6876
|
-
payload: {
|
|
6877
|
-
agent: employeeName,
|
|
6878
|
-
cwd: projectDir,
|
|
6879
|
-
reason,
|
|
6880
|
-
risk: "code-role agent runs in project root without git worktree isolation"
|
|
6881
|
-
}
|
|
6882
|
-
});
|
|
6883
|
-
} else {
|
|
6884
|
-
process.stderr.write(`[ensureEmployeeAsync] ${gate.message}
|
|
6885
|
-
`);
|
|
6886
|
-
if (asyncOpts.spawnLockHeld) releaseSpawnLock(sessionName);
|
|
6887
|
-
releaseSpawnMutexIfOwned();
|
|
6888
|
-
return { status: "failed", sessionName, error: gate.message };
|
|
6889
|
-
}
|
|
6890
|
-
}
|
|
6891
|
-
}
|
|
6892
|
-
} catch (err) {
|
|
6893
|
-
process.stderr.write(
|
|
6894
|
-
`[ensureEmployeeAsync] worktree setup failed for ${employeeName}: ${err instanceof Error ? err.message : String(err)}
|
|
6895
|
-
`
|
|
6896
|
-
);
|
|
6897
|
-
}
|
|
6898
|
-
}
|
|
6899
|
-
try {
|
|
6900
|
-
const sessionName = employeeSessionName(employeeName, exeSession, asyncOpts.instance);
|
|
6901
|
-
await exportBehaviorsAsync(employeeName, path8.basename(asyncOpts.cwd ?? projectDir), sessionName);
|
|
6902
|
-
} catch (err) {
|
|
6903
|
-
process.stderr.write(
|
|
6904
|
-
`[ensureEmployeeAsync] behavior export failed for ${employeeName}: ${err instanceof Error ? err.message : String(err)}
|
|
6905
|
-
`
|
|
6906
|
-
);
|
|
6907
|
-
}
|
|
6908
|
-
asyncOpts = { ...asyncOpts, collisionGuardHandled: true };
|
|
6909
6232
|
const result = ensureEmployee(employeeName, exeSession, projectDir, asyncOpts);
|
|
6910
6233
|
if (asyncOpts.spawnLockHeld && result.sessionName) {
|
|
6911
6234
|
releaseSpawnLock(result.sessionName);
|
|
@@ -6913,764 +6236,12 @@ async function ensureEmployeeAsync(employeeName, exeSession, projectDir, opts) {
|
|
|
6913
6236
|
releaseSpawnMutexIfOwned();
|
|
6914
6237
|
return result;
|
|
6915
6238
|
}
|
|
6916
|
-
function
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
if (input.optOut) return "bypass";
|
|
6921
|
-
return input.spawnCwdIsGitRepo ? "refuse" : "bypass";
|
|
6922
|
-
}
|
|
6923
|
-
if (input.optOut) return "bypass";
|
|
6924
|
-
return input.spawnCwdIsGitRepo ? "refuse" : "bypass";
|
|
6925
|
-
}
|
|
6926
|
-
function resolveSpawnCwd(requestedCwd, projectDir, dirExists = existsSync9) {
|
|
6927
|
-
if (dirExists(requestedCwd)) return { cwd: requestedCwd, fellBack: false, requestedCwd };
|
|
6928
|
-
if (dirExists(projectDir)) return { cwd: projectDir, fellBack: true, requestedCwd };
|
|
6929
|
-
return { cwd: null, fellBack: false, requestedCwd };
|
|
6930
|
-
}
|
|
6931
|
-
function spawnExitMarkerPath(sessionName) {
|
|
6932
|
-
const safe = sessionName.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
6933
|
-
return path8.join(SESSION_CACHE, `spawn-exit-${safe}`);
|
|
6934
|
-
}
|
|
6935
|
-
function buildSpawnCommand(launchCommand, exitMarkerPath, cleanupSuffix) {
|
|
6936
|
-
const list = `${launchCommand}; printf '%s' "$?" > "${exitMarkerPath}"${cleanupSuffix}`;
|
|
6937
|
-
return `/bin/sh -c '${list.replace(/'/g, `'\\''`)}'`;
|
|
6938
|
-
}
|
|
6939
|
-
function readSpawnExitMarker(markerPath, read = (p) => readFileSync6(p, "utf8")) {
|
|
6940
|
-
let raw;
|
|
6941
|
-
try {
|
|
6942
|
-
raw = read(markerPath);
|
|
6943
|
-
} catch {
|
|
6944
|
-
return null;
|
|
6945
|
-
}
|
|
6946
|
-
const trimmed = raw.trim();
|
|
6947
|
-
if (!/^\d+$/.test(trimmed)) return null;
|
|
6948
|
-
return Number(trimmed);
|
|
6949
|
-
}
|
|
6950
|
-
function paneShowsRuntimeBanner(pane, runtime) {
|
|
6951
|
-
if (runtime === "exe-agent") return pane.includes("[exe-agent]") || pane.includes("online");
|
|
6952
|
-
if (runtime === "codex") {
|
|
6953
|
-
return pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex");
|
|
6954
|
-
}
|
|
6955
|
-
if (runtime === "opencode") {
|
|
6956
|
-
return pane.includes("opencode") || pane.includes("OpenCode") || pane.includes("exe-start-opencode");
|
|
6957
|
-
}
|
|
6958
|
-
return pane.includes("Claude Code") || pane.includes("\u276F");
|
|
6959
|
-
}
|
|
6960
|
-
function classifySpawnFailurePane(pane) {
|
|
6961
|
-
const paneLc = pane.toLowerCase();
|
|
6962
|
-
if (paneLc.includes("command not found") || paneLc.includes(": not found")) {
|
|
6963
|
-
return "path_not_found";
|
|
6964
|
-
}
|
|
6965
|
-
if (paneLc.includes("exit 127") || paneLc.includes("claude") && paneLc.includes("not found") || paneLc.includes("not support") || // existing "unsupported launch flag" hint
|
|
6966
|
-
paneLc.includes("unknown option") || paneLc.includes("enoent")) {
|
|
6967
|
-
return "claude_launch_failed";
|
|
6968
|
-
}
|
|
6969
|
-
return "timeout";
|
|
6970
|
-
}
|
|
6971
|
-
function resolveStartupWatchCapMs(input) {
|
|
6972
|
-
const explicit = Number(input.override) || Number(input.legacyOverride);
|
|
6973
|
-
if (explicit > 0) return explicit;
|
|
6974
|
-
if (input.useCodex) return 2500;
|
|
6975
|
-
return input.callerIsDaemon ? 2e3 : 3e3;
|
|
6976
|
-
}
|
|
6977
|
-
function watchSpawnStartup(probes, opts) {
|
|
6978
|
-
const interval = Math.max(1, opts.intervalMs);
|
|
6979
|
-
const iterations = Math.max(0, Math.ceil(Math.max(0, opts.capMs) / interval));
|
|
6980
|
-
const capture = () => {
|
|
6981
|
-
try {
|
|
6982
|
-
return probes.capturePane();
|
|
6983
|
-
} catch {
|
|
6984
|
-
return "";
|
|
6985
|
-
}
|
|
6986
|
-
};
|
|
6987
|
-
let polls = 0;
|
|
6988
|
-
for (let i = 0; i <= iterations; i++) {
|
|
6989
|
-
polls++;
|
|
6990
|
-
const exitCode = probes.readLauncherExit();
|
|
6991
|
-
if (exitCode !== null) return { outcome: "exited", exitCode, pane: capture(), polls };
|
|
6992
|
-
if (!probes.isAlive()) {
|
|
6993
|
-
return { outcome: "exited", exitCode: probes.readLauncherExit(), pane: capture(), polls };
|
|
6994
|
-
}
|
|
6995
|
-
if (i < iterations) probes.sleep(interval);
|
|
6996
|
-
}
|
|
6997
|
-
return { outcome: "running", polls };
|
|
6998
|
-
}
|
|
6999
|
-
function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
7000
|
-
const transport = getTransport();
|
|
7001
|
-
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
7002
|
-
const spawnStartedAt = Date.now();
|
|
7003
|
-
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
7004
|
-
const logDir = path8.join(os6.homedir(), ".exe-os", "session-logs");
|
|
7005
|
-
const logFile = path8.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
7006
|
-
{
|
|
7007
|
-
const earlyRt = resolveSpawnRuntimeConfig(
|
|
7008
|
-
employeeName,
|
|
7009
|
-
opts?.runtimeOverride,
|
|
7010
|
-
opts?.modelOverride
|
|
7011
|
-
);
|
|
7012
|
-
const compat = assertRuntimeModelCompatible(earlyRt.runtime, earlyRt.model);
|
|
7013
|
-
if (!compat.ok) {
|
|
7014
|
-
process.stderr.write(`[spawnEmployee] ${compat.error}
|
|
7015
|
-
`);
|
|
7016
|
-
recordOrchestrationEventBestEffort({
|
|
7017
|
-
eventType: "tmux.spawn.completed",
|
|
7018
|
-
source: "tmux-routing.spawnEmployee",
|
|
7019
|
-
severity: "error",
|
|
7020
|
-
agentId: employeeName,
|
|
7021
|
-
sessionScope: exeSession,
|
|
7022
|
-
tmuxSession: sessionName,
|
|
7023
|
-
runtime: earlyRt.runtime,
|
|
7024
|
-
result: "failed",
|
|
7025
|
-
errorCode: "runtime_model_mismatch",
|
|
7026
|
-
payload: { runtime: earlyRt.runtime, model: earlyRt.model }
|
|
7027
|
-
});
|
|
7028
|
-
return { sessionName, error: compat.error };
|
|
7029
|
-
}
|
|
7030
|
-
}
|
|
7031
|
-
recordOrchestrationEventBestEffort({
|
|
7032
|
-
eventType: "tmux.spawn.attempted",
|
|
7033
|
-
source: "tmux-routing.spawnEmployee",
|
|
7034
|
-
agentId: employeeName,
|
|
7035
|
-
sessionScope: exeSession,
|
|
7036
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7037
|
-
tmuxSession: sessionName,
|
|
7038
|
-
runtime: opts?.runtimeOverride ?? null,
|
|
7039
|
-
payload: { hasCwdOverride: Boolean(opts?.cwd), autoInstance: Boolean(opts?.autoInstance) }
|
|
7040
|
-
});
|
|
7041
|
-
if (!existsSync9(logDir)) {
|
|
7042
|
-
mkdirSync6(logDir, { recursive: true });
|
|
7043
|
-
}
|
|
7044
|
-
const cwdResolution = resolveSpawnCwd(opts?.cwd ?? projectDir, projectDir);
|
|
7045
|
-
if (cwdResolution.cwd === null) {
|
|
7046
|
-
process.stderr.write(
|
|
7047
|
-
`[tmux-routing] spawn cwd does not exist: ${cwdResolution.requestedCwd} \u2014 and project root ${projectDir} is also missing; refusing spawn
|
|
7048
|
-
`
|
|
7049
|
-
);
|
|
7050
|
-
recordOrchestrationEventBestEffort({
|
|
7051
|
-
eventType: "worktree.stale_cwd_fallback",
|
|
7052
|
-
source: "tmux-routing.spawnEmployee",
|
|
7053
|
-
severity: "error",
|
|
7054
|
-
agentId: employeeName,
|
|
7055
|
-
sessionScope: exeSession,
|
|
7056
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7057
|
-
tmuxSession: sessionName,
|
|
7058
|
-
result: "failed",
|
|
7059
|
-
errorCode: "spawn_cwd_missing",
|
|
7060
|
-
payload: { requestedCwd: cwdResolution.requestedCwd, fallbackCwd: null, projectDir }
|
|
7061
|
-
});
|
|
7062
|
-
releaseSpawnLock(sessionName);
|
|
7063
|
-
return { sessionName, error: `spawn cwd missing: ${cwdResolution.requestedCwd} (project root ${projectDir} also missing)` };
|
|
7064
|
-
}
|
|
7065
|
-
if (cwdResolution.fellBack) {
|
|
7066
|
-
process.stderr.write(
|
|
7067
|
-
`[tmux-routing] spawn cwd does not exist: ${cwdResolution.requestedCwd} \u2014 falling back to project root ${cwdResolution.cwd} (never spawning into a stale/deleted worktree)
|
|
7068
|
-
`
|
|
7069
|
-
);
|
|
7070
|
-
recordOrchestrationEventBestEffort({
|
|
7071
|
-
eventType: "worktree.stale_cwd_fallback",
|
|
7072
|
-
source: "tmux-routing.spawnEmployee",
|
|
7073
|
-
severity: "warn",
|
|
7074
|
-
agentId: employeeName,
|
|
7075
|
-
sessionScope: exeSession,
|
|
7076
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7077
|
-
tmuxSession: sessionName,
|
|
7078
|
-
payload: { requestedCwd: cwdResolution.requestedCwd, fallbackCwd: cwdResolution.cwd, projectDir }
|
|
7079
|
-
});
|
|
7080
|
-
}
|
|
7081
|
-
const spawnCwd = cwdResolution.cwd;
|
|
7082
|
-
const optedOutOfWorktree = opts?.spawnWorktree === false;
|
|
7083
|
-
const coordinatorCheckout = !isCoordinatorName(employeeName) && optedOutOfWorktree ? detectCoordinatorCheckoutCwd(spawnCwd) : null;
|
|
7084
|
-
if (coordinatorCheckout) {
|
|
7085
|
-
const message = `Coordinator checkout refusal: refusing to spawn employee ${employeeName} in registered coordinator repo root ${coordinatorCheckout.repoRoot} (coordinator session ${coordinatorCheckout.coordinatorSession}). Spawned implementation agents must run from an isolated worktree; launching here would let commits land on the coordinator's checked-out branch.`;
|
|
7086
|
-
process.stderr.write(`[tmux-routing] ${message}
|
|
6239
|
+
function spawnEmployee(employeeName, _exeSession, _projectDir, opts) {
|
|
6240
|
+
const sessionName = employeeSessionName(employeeName, _exeSession, opts?.instance);
|
|
6241
|
+
const message = tmuxDispatchRefusal(`spawnEmployee(${employeeName})`);
|
|
6242
|
+
process.stderr.write(`[spawnEmployee] ${message}
|
|
7087
6243
|
`);
|
|
7088
|
-
|
|
7089
|
-
recordOrchestrationEventBestEffort({
|
|
7090
|
-
eventType: "worktree.refused",
|
|
7091
|
-
source: "tmux-routing.spawnEmployee",
|
|
7092
|
-
severity: "error",
|
|
7093
|
-
agentId: employeeName,
|
|
7094
|
-
sessionScope: exeSession,
|
|
7095
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7096
|
-
tmuxSession: sessionName,
|
|
7097
|
-
result: "failed",
|
|
7098
|
-
errorCode: "coordinator_checkout_cwd",
|
|
7099
|
-
payload: {
|
|
7100
|
-
cwd: spawnCwd,
|
|
7101
|
-
coordinatorSession: coordinatorCheckout.coordinatorSession,
|
|
7102
|
-
repoRoot: coordinatorCheckout.repoRoot
|
|
7103
|
-
}
|
|
7104
|
-
});
|
|
7105
|
-
return { sessionName, error: message };
|
|
7106
|
-
}
|
|
7107
|
-
transport.kill(sessionName, exeSession);
|
|
7108
|
-
let cleanupSuffix = "";
|
|
7109
|
-
try {
|
|
7110
|
-
const thisFile = fileURLToPath(import.meta.url);
|
|
7111
|
-
const cleanupScript = path8.join(path8.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7112
|
-
if (existsSync9(cleanupScript)) {
|
|
7113
|
-
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
7114
|
-
}
|
|
7115
|
-
} catch {
|
|
7116
|
-
}
|
|
7117
|
-
try {
|
|
7118
|
-
const claudeJsonPath = path8.join(os6.homedir(), ".claude.json");
|
|
7119
|
-
let claudeJson = {};
|
|
7120
|
-
try {
|
|
7121
|
-
claudeJson = JSON.parse(readFileSync6(claudeJsonPath, "utf8"));
|
|
7122
|
-
} catch {
|
|
7123
|
-
}
|
|
7124
|
-
if (!claudeJson.projects) claudeJson.projects = {};
|
|
7125
|
-
const projects = claudeJson.projects;
|
|
7126
|
-
const trustDir = opts?.cwd ?? projectDir;
|
|
7127
|
-
if (!projects[trustDir]) projects[trustDir] = {};
|
|
7128
|
-
projects[trustDir].hasTrustDialogAccepted = true;
|
|
7129
|
-
atomicWriteSync(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
7130
|
-
} catch {
|
|
7131
|
-
}
|
|
7132
|
-
try {
|
|
7133
|
-
const settingsDir = path8.join(os6.homedir(), ".claude", "projects");
|
|
7134
|
-
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
7135
|
-
const projSettingsDir = path8.join(settingsDir, normalizedKey);
|
|
7136
|
-
const settingsPath = path8.join(projSettingsDir, "settings.json");
|
|
7137
|
-
let settings = {};
|
|
7138
|
-
try {
|
|
7139
|
-
settings = JSON.parse(readFileSync6(settingsPath, "utf8"));
|
|
7140
|
-
} catch {
|
|
7141
|
-
}
|
|
7142
|
-
const perms = settings.permissions ?? {};
|
|
7143
|
-
const allow = perms.allow ?? [];
|
|
7144
|
-
const toolNames = [
|
|
7145
|
-
"recall_my_memory",
|
|
7146
|
-
"store_memory",
|
|
7147
|
-
"create_task",
|
|
7148
|
-
"update_task",
|
|
7149
|
-
"list_tasks",
|
|
7150
|
-
"get_task",
|
|
7151
|
-
// Work claims: spawned agents must be able to claim/release without a
|
|
7152
|
-
// permission prompt, or the dispatch gate becomes advisory in practice.
|
|
7153
|
-
"claim",
|
|
7154
|
-
"ask_team_memory",
|
|
7155
|
-
"store_behavior",
|
|
7156
|
-
"get_identity",
|
|
7157
|
-
"send_message"
|
|
7158
|
-
];
|
|
7159
|
-
const requiredTools = expandDualPrefixTools(toolNames);
|
|
7160
|
-
let changed = false;
|
|
7161
|
-
for (const tool of requiredTools) {
|
|
7162
|
-
if (!allow.includes(tool)) {
|
|
7163
|
-
allow.push(tool);
|
|
7164
|
-
changed = true;
|
|
7165
|
-
}
|
|
7166
|
-
}
|
|
7167
|
-
if (changed) {
|
|
7168
|
-
perms.allow = allow;
|
|
7169
|
-
settings.permissions = perms;
|
|
7170
|
-
mkdirSync6(projSettingsDir, { recursive: true });
|
|
7171
|
-
atomicWriteSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
7172
|
-
}
|
|
7173
|
-
} catch {
|
|
7174
|
-
}
|
|
7175
|
-
let disciplineRefusal = null;
|
|
7176
|
-
try {
|
|
7177
|
-
const inWorktree = spawnCwd.includes("/.worktrees/") || spawnCwd.includes("/.claude/worktrees/");
|
|
7178
|
-
const COORDINATOR_ROLES = /* @__PURE__ */ new Set(["COO", "CTO", "CMO"]);
|
|
7179
|
-
const rosterResult = loadEmployeesState();
|
|
7180
|
-
const emp = rosterResult.employees.find(
|
|
7181
|
-
(e) => baseAgentName(e.name) === baseAgentName(employeeName)
|
|
7182
|
-
);
|
|
7183
|
-
const roleKnown = emp != null;
|
|
7184
|
-
const isCodeRole = emp != null && !COORDINATOR_ROLES.has(emp.role);
|
|
7185
|
-
const optOut = opts?.spawnWorktree === false;
|
|
7186
|
-
const spawnCwdIsGitRepo = isInsideGitRepo(spawnCwd);
|
|
7187
|
-
const decision = decideSpawnWorktreeDiscipline({
|
|
7188
|
-
inWorktree,
|
|
7189
|
-
roleKnown,
|
|
7190
|
-
isCodeRole,
|
|
7191
|
-
spawnCwdIsGitRepo,
|
|
7192
|
-
optOut
|
|
7193
|
-
});
|
|
7194
|
-
if (decision !== "ok") {
|
|
7195
|
-
const reason = rosterResult.status === "error" && !roleKnown ? "roster_load_error" : opts?.cwd != null ? "preset_cwd" : !roleKnown ? "roster_miss" : "git_timeout";
|
|
7196
|
-
const role = emp?.role ?? "unknown";
|
|
7197
|
-
if (decision === "refuse") {
|
|
7198
|
-
const rosterNote = reason === "roster_load_error" ? ` The roster failed to load (unreadable/corrupt/truncated), so ${employeeName}'s role could not be verified and is treated as code-role.` : reason === "roster_miss" ? ` ${employeeName} is not in the roster, so its role could not be verified and is treated as code-role.` : "";
|
|
7199
|
-
disciplineRefusal = `Worktree isolation required for ${roleKnown ? `code-role ${employeeName} (role=${role})` : employeeName}; refusing to spawn in MAIN checkout ${spawnCwd} (reason=${reason}) \u2014 commits would land on main.${rosterNote} Pass spawn_worktree=false (create_task) or opts.spawnWorktree=false to dispatch here anyway (e.g. a non-git automation project).`;
|
|
7200
|
-
process.stderr.write(`[tmux-routing] ${disciplineRefusal}
|
|
7201
|
-
`);
|
|
7202
|
-
recordOrchestrationEventBestEffort({
|
|
7203
|
-
eventType: "worktree.refused",
|
|
7204
|
-
source: "tmux-routing.spawnEmployee",
|
|
7205
|
-
severity: "warn",
|
|
7206
|
-
agentId: employeeName,
|
|
7207
|
-
sessionScope: exeSession,
|
|
7208
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7209
|
-
tmuxSession: sessionName,
|
|
7210
|
-
payload: { agent: employeeName, role, cwd: spawnCwd, reason, rosterStatus: rosterResult.status }
|
|
7211
|
-
});
|
|
7212
|
-
} else {
|
|
7213
|
-
const viaOptOut = optOut;
|
|
7214
|
-
process.stderr.write(
|
|
7215
|
-
`[tmux-routing] WORKTREE BYPASSED: ${role} ${employeeName} spawning in MAIN checkout ${spawnCwd} (reason=${reason}${viaOptOut ? ", opt-out" : ""}) \u2014 commits will land on main
|
|
7216
|
-
`
|
|
7217
|
-
);
|
|
7218
|
-
if (viaOptOut && spawnCwdIsGitRepo) emitOptOutBypassWarning(employeeName, spawnCwd);
|
|
7219
|
-
recordOrchestrationEventBestEffort({
|
|
7220
|
-
eventType: "worktree.bypassed",
|
|
7221
|
-
source: "tmux-routing.spawnEmployee",
|
|
7222
|
-
severity: "warn",
|
|
7223
|
-
agentId: employeeName,
|
|
7224
|
-
sessionScope: exeSession,
|
|
7225
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7226
|
-
tmuxSession: sessionName,
|
|
7227
|
-
payload: { agent: employeeName, role, cwd: spawnCwd, reason, optOut: viaOptOut, rosterStatus: rosterResult.status }
|
|
7228
|
-
});
|
|
7229
|
-
}
|
|
7230
|
-
}
|
|
7231
|
-
} catch {
|
|
7232
|
-
}
|
|
7233
|
-
if (disciplineRefusal) {
|
|
7234
|
-
releaseSpawnLock(sessionName);
|
|
7235
|
-
return { sessionName, error: disciplineRefusal };
|
|
7236
|
-
}
|
|
7237
|
-
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
7238
|
-
const agentRtConfig = resolveSpawnRuntimeConfig(
|
|
7239
|
-
employeeName,
|
|
7240
|
-
opts?.runtimeOverride,
|
|
7241
|
-
opts?.modelOverride
|
|
7242
|
-
);
|
|
7243
|
-
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
7244
|
-
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
7245
|
-
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
7246
|
-
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
7247
|
-
let legacyFallbackWarned = false;
|
|
7248
|
-
if (!useExeAgent && !useBinSymlink) {
|
|
7249
|
-
const identityPath = path8.join(
|
|
7250
|
-
os6.homedir(),
|
|
7251
|
-
".exe-os",
|
|
7252
|
-
"identity",
|
|
7253
|
-
`${employeeName}.md`
|
|
7254
|
-
);
|
|
7255
|
-
_resetCcAgentSupportCache();
|
|
7256
|
-
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
7257
|
-
if (hasAgentFlag) {
|
|
7258
|
-
const symlink = ensureAgentSymlink(employeeName);
|
|
7259
|
-
if (symlink.action === "conflict") {
|
|
7260
|
-
process.stderr.write(
|
|
7261
|
-
`[tmux-routing] WARN: respecting customer-owned ~/.claude/agents/${employeeName}.md (${symlink.conflict}); not overwriting or bypassing it.
|
|
7262
|
-
`
|
|
7263
|
-
);
|
|
7264
|
-
}
|
|
7265
|
-
} else if (existsSync9(identityPath)) {
|
|
7266
|
-
legacyFallbackWarned = true;
|
|
7267
|
-
}
|
|
7268
|
-
if (!opts?.behaviorsExported) {
|
|
7269
|
-
const behaviorsFile = exportBehaviorsSync(
|
|
7270
|
-
employeeName,
|
|
7271
|
-
path8.basename(spawnCwd),
|
|
7272
|
-
sessionName
|
|
7273
|
-
);
|
|
7274
|
-
void behaviorsFile;
|
|
7275
|
-
}
|
|
7276
|
-
}
|
|
7277
|
-
if (legacyFallbackWarned) {
|
|
7278
|
-
process.stderr.write(
|
|
7279
|
-
`[tmux-routing] claude --agent not supported by installed CC. Falling back to --append-system-prompt-file for ${employeeName}. Upgrade Claude Code to enable native --agent launch.
|
|
7280
|
-
`
|
|
7281
|
-
);
|
|
7282
|
-
}
|
|
7283
|
-
try {
|
|
7284
|
-
const ctxDir = path8.join(os6.homedir(), ".exe-os", "session-cache");
|
|
7285
|
-
mkdirSync6(ctxDir, { recursive: true });
|
|
7286
|
-
const ctxFile = path8.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7287
|
-
const ctxContent = [
|
|
7288
|
-
`## Session Context`,
|
|
7289
|
-
`You are running in tmux session: ${sessionName}.`,
|
|
7290
|
-
`Your parent coordinator session is ${exeSession}.`,
|
|
7291
|
-
`Your employees (if any) use the -${exeSession} suffix.`
|
|
7292
|
-
].join("\n");
|
|
7293
|
-
atomicWriteSync(ctxFile, ctxContent);
|
|
7294
|
-
void ctxFile;
|
|
7295
|
-
} catch {
|
|
7296
|
-
}
|
|
7297
|
-
const launchEnv = {
|
|
7298
|
-
AGENT_ID: employeeName,
|
|
7299
|
-
EXE_SESSION: exeSession,
|
|
7300
|
-
EXE_SESSION_NAME: sessionName,
|
|
7301
|
-
EXE_SESSION_START_ISO: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7302
|
-
EXE_OS_ENABLE_LEAN_MCP: "1",
|
|
7303
|
-
// Bug 1ed715ec: dispatcher-reply path — spawned agents need to know which
|
|
7304
|
-
// coordinator session dispatched them so they can address replies to the
|
|
7305
|
-
// exact originator. exeSession is the coordinator's session name (e.g.
|
|
7306
|
-
// "exe1", "exe2") that spawned this agent.
|
|
7307
|
-
EXE_DISPATCHER_SESSION: exeSession,
|
|
7308
|
-
// Bug bbf7824c: tmux inherits a minimal PATH from the launchd-spawned
|
|
7309
|
-
// daemon, which omits the nvm/.local bin dir where exe-launch-agent,
|
|
7310
|
-
// exe-agent, and claude live — so the spawn command is unresolvable and
|
|
7311
|
-
// every auto-dispatch fails. Carry an explicit, PATH that includes the
|
|
7312
|
-
// running node's bin dir and preserves the inherited PATH.
|
|
7313
|
-
PATH: buildSpawnPath()
|
|
7314
|
-
};
|
|
7315
|
-
if (process.env.EXE_IS_DAEMON === "1") {
|
|
7316
|
-
try {
|
|
7317
|
-
let identityRole = "employee";
|
|
7318
|
-
try {
|
|
7319
|
-
const emp = loadEmployeesState().employees.find(
|
|
7320
|
-
(e) => baseAgentName(e.name) === baseAgentName(employeeName)
|
|
7321
|
-
);
|
|
7322
|
-
if (emp?.role) identityRole = emp.role;
|
|
7323
|
-
} catch {
|
|
7324
|
-
}
|
|
7325
|
-
const { token } = getDaemonAgentIdentityBroker().issue({
|
|
7326
|
-
agentId: employeeName,
|
|
7327
|
-
role: identityRole,
|
|
7328
|
-
sessionId: sessionName
|
|
7329
|
-
});
|
|
7330
|
-
launchEnv[AGENT_IDENTITY_TOKEN_ENV] = token;
|
|
7331
|
-
} catch (e) {
|
|
7332
|
-
process.stderr.write(
|
|
7333
|
-
`[tmux-routing] WARN: could not mint agent identity credential for ${sessionName}: ${e instanceof Error ? e.message : String(e)}
|
|
7334
|
-
`
|
|
7335
|
-
);
|
|
7336
|
-
}
|
|
7337
|
-
}
|
|
7338
|
-
if (ccProvider !== DEFAULT_PROVIDER) {
|
|
7339
|
-
const cfg = PROVIDER_TABLE[ccProvider];
|
|
7340
|
-
if (cfg?.apiKeyEnv) {
|
|
7341
|
-
const keyVal = process.env[cfg.apiKeyEnv];
|
|
7342
|
-
if (keyVal) {
|
|
7343
|
-
launchEnv[cfg.apiKeyEnv] = keyVal;
|
|
7344
|
-
}
|
|
7345
|
-
}
|
|
7346
|
-
}
|
|
7347
|
-
if (useCodex) {
|
|
7348
|
-
const codexCfg = RUNTIME_TABLE.codex;
|
|
7349
|
-
if (codexCfg?.apiKeyEnv) {
|
|
7350
|
-
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
7351
|
-
if (keyVal) {
|
|
7352
|
-
launchEnv[codexCfg.apiKeyEnv] = keyVal;
|
|
7353
|
-
}
|
|
7354
|
-
}
|
|
7355
|
-
if (agentRtConfig.model) launchEnv.EXE_AGENT_MODEL = agentRtConfig.model;
|
|
7356
|
-
if (agentRtConfig.reasoning_effort) {
|
|
7357
|
-
launchEnv.EXE_AGENT_REASONING_EFFORT = agentRtConfig.reasoning_effort;
|
|
7358
|
-
}
|
|
7359
|
-
}
|
|
7360
|
-
if (useOpencode) {
|
|
7361
|
-
const ocCfg = PROVIDER_TABLE.opencode;
|
|
7362
|
-
if (ocCfg?.apiKeyEnv) {
|
|
7363
|
-
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
7364
|
-
if (keyVal) {
|
|
7365
|
-
launchEnv[ocCfg.apiKeyEnv] = keyVal;
|
|
7366
|
-
}
|
|
7367
|
-
}
|
|
7368
|
-
if (agentRtConfig.model) launchEnv.ANTHROPIC_MODEL = agentRtConfig.model;
|
|
7369
|
-
}
|
|
7370
|
-
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
7371
|
-
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
7372
|
-
const ccModel = normalizeCcModelName(agentRtConfig.model);
|
|
7373
|
-
launchEnv.ANTHROPIC_MODEL = ccModel;
|
|
7374
|
-
if (opts?.modelOverride) {
|
|
7375
|
-
launchEnv.EXE_AGENT_MODEL = ccModel;
|
|
7376
|
-
}
|
|
7377
|
-
}
|
|
7378
|
-
}
|
|
7379
|
-
let launchCommand;
|
|
7380
|
-
if (useExeAgent) {
|
|
7381
|
-
launchCommand = `exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}`;
|
|
7382
|
-
} else if (useCodex) {
|
|
7383
|
-
process.stderr.write(
|
|
7384
|
-
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
7385
|
-
`
|
|
7386
|
-
);
|
|
7387
|
-
launchCommand = `exe-start-codex --agent ${employeeName} --session ${sessionName}`;
|
|
7388
|
-
} else if (useOpencode) {
|
|
7389
|
-
const binName = `${employeeName}-opencode`;
|
|
7390
|
-
process.stderr.write(
|
|
7391
|
-
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
7392
|
-
`
|
|
7393
|
-
);
|
|
7394
|
-
launchCommand = binName;
|
|
7395
|
-
} else if (useBinSymlink) {
|
|
7396
|
-
const binName = `${employeeName}-${ccProvider}`;
|
|
7397
|
-
process.stderr.write(
|
|
7398
|
-
`[tmux-routing] provider cascade: ${ccProvider} \u2192 spawning ${binName}
|
|
7399
|
-
`
|
|
7400
|
-
);
|
|
7401
|
-
launchCommand = binName;
|
|
7402
|
-
} else {
|
|
7403
|
-
let launcherAbs = "";
|
|
7404
|
-
try {
|
|
7405
|
-
const launcherJs = path8.join(path8.dirname(fileURLToPath(import.meta.url)), "..", "bin", "exe-launch-agent.js");
|
|
7406
|
-
if (existsSync9(launcherJs)) launcherAbs = launcherJs;
|
|
7407
|
-
} catch {
|
|
7408
|
-
}
|
|
7409
|
-
launchCommand = launcherAbs ? `"${process.execPath}" "${launcherAbs}" --agent ${employeeName}` : `exe-launch-agent --agent ${employeeName}`;
|
|
7410
|
-
}
|
|
7411
|
-
const exitMarkerPath = spawnExitMarkerPath(sessionName);
|
|
7412
|
-
try {
|
|
7413
|
-
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
7414
|
-
} catch {
|
|
7415
|
-
}
|
|
7416
|
-
try {
|
|
7417
|
-
unlinkSync4(exitMarkerPath);
|
|
7418
|
-
} catch {
|
|
7419
|
-
}
|
|
7420
|
-
const spawnCommand = buildSpawnCommand(launchCommand, exitMarkerPath, cleanupSuffix);
|
|
7421
|
-
const tmuxEnvArgs = Object.entries(launchEnv).flatMap(([key, val]) => ["-e", `${key}=${val}`]);
|
|
7422
|
-
let spawnResult;
|
|
7423
|
-
try {
|
|
7424
|
-
execFileSync3("tmux", [
|
|
7425
|
-
"new-session",
|
|
7426
|
-
"-d",
|
|
7427
|
-
...tmuxEnvArgs,
|
|
7428
|
-
"-s",
|
|
7429
|
-
sessionName,
|
|
7430
|
-
"-x",
|
|
7431
|
-
"200",
|
|
7432
|
-
"-y",
|
|
7433
|
-
"50",
|
|
7434
|
-
"-c",
|
|
7435
|
-
spawnCwd,
|
|
7436
|
-
spawnCommand
|
|
7437
|
-
], { timeout: 1e4 });
|
|
7438
|
-
spawnResult = { sessionName };
|
|
7439
|
-
} catch (e) {
|
|
7440
|
-
spawnResult = { sessionName, error: `spawn failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
7441
|
-
}
|
|
7442
|
-
if (spawnResult.error) {
|
|
7443
|
-
releaseSpawnLock(sessionName);
|
|
7444
|
-
recordOrchestrationEventBestEffort({
|
|
7445
|
-
eventType: "tmux.spawn.completed",
|
|
7446
|
-
source: "tmux-routing.spawnEmployee",
|
|
7447
|
-
severity: "warn",
|
|
7448
|
-
agentId: employeeName,
|
|
7449
|
-
sessionScope: exeSession,
|
|
7450
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7451
|
-
tmuxSession: sessionName,
|
|
7452
|
-
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
7453
|
-
durationMs: Date.now() - spawnStartedAt,
|
|
7454
|
-
result: "failed",
|
|
7455
|
-
errorCode: "tmux_new_session_failed"
|
|
7456
|
-
});
|
|
7457
|
-
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
7458
|
-
}
|
|
7459
|
-
transport.pipeLog(sessionName, logFile);
|
|
7460
|
-
try {
|
|
7461
|
-
const mySession = getMySession();
|
|
7462
|
-
const dispatchInfo = path8.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7463
|
-
atomicWriteJsonSync(dispatchInfo, {
|
|
7464
|
-
dispatchedBy: mySession,
|
|
7465
|
-
rootExe: exeSession,
|
|
7466
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
7467
|
-
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
7468
|
-
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
7469
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7470
|
-
});
|
|
7471
|
-
} catch {
|
|
7472
|
-
}
|
|
7473
|
-
const callerIsDaemon = process.env.EXE_IS_DAEMON === "1";
|
|
7474
|
-
const POLL_INTERVAL_MS = 500;
|
|
7475
|
-
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : useOpencode ? "opencode" : "claude";
|
|
7476
|
-
const LIVENESS_CAP_MS = Number(process.env.EXE_SPAWN_LIVENESS_CAP_MS) || (callerIsDaemon ? 6e3 : 8e3);
|
|
7477
|
-
const livenessIterations = Math.max(1, Math.ceil(LIVENESS_CAP_MS / POLL_INTERVAL_MS));
|
|
7478
|
-
let sessionLive = false;
|
|
7479
|
-
for (let i = 0; i < livenessIterations; i++) {
|
|
7480
|
-
if (transport.isAlive(sessionName)) {
|
|
7481
|
-
sessionLive = true;
|
|
7482
|
-
break;
|
|
7483
|
-
}
|
|
7484
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, POLL_INTERVAL_MS);
|
|
7485
|
-
}
|
|
7486
|
-
if (!sessionLive) sessionLive = transport.isAlive(sessionName);
|
|
7487
|
-
if (!sessionLive) {
|
|
7488
|
-
releaseSpawnLock(sessionName);
|
|
7489
|
-
let lastPane = "";
|
|
7490
|
-
try {
|
|
7491
|
-
lastPane = transport.capturePane(sessionName, 50);
|
|
7492
|
-
} catch {
|
|
7493
|
-
}
|
|
7494
|
-
const spawnFailureCause = classifySpawnFailurePane(lastPane);
|
|
7495
|
-
const errMsg2 = `spawn did not start: tmux session "${sessionName}" never came alive within ${LIVENESS_CAP_MS / 1e3}s (${runtimeLabel} process failed to launch or exited immediately; cause=${spawnFailureCause}). Check ~/.exe-os/session-logs/${instanceLabel}-*.log \u2014 common cause: installed Claude Code does not support a launch flag, or the daemon PATH excludes the node/launcher bin dir (cause=path_not_found).`;
|
|
7496
|
-
process.stderr.write(`[tmux-routing] SPAWN FAILED ${sessionName} (${spawnFailureCause}): ${errMsg2}
|
|
7497
|
-
`);
|
|
7498
|
-
recordOrchestrationEventBestEffort({
|
|
7499
|
-
eventType: "tmux.spawn.completed",
|
|
7500
|
-
source: "tmux-routing.spawnEmployee",
|
|
7501
|
-
severity: "warn",
|
|
7502
|
-
agentId: employeeName,
|
|
7503
|
-
sessionScope: exeSession,
|
|
7504
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7505
|
-
tmuxSession: sessionName,
|
|
7506
|
-
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
7507
|
-
durationMs: Date.now() - spawnStartedAt,
|
|
7508
|
-
result: "failed",
|
|
7509
|
-
// Refined, classified errorCode drives the spawnFailureByCause breakdown
|
|
7510
|
-
// in orchestration-events. Keep the generic detail in payload for context.
|
|
7511
|
-
errorCode: spawnFailureCause,
|
|
7512
|
-
payload: { spawnFailureCause, detail: "spawn_session_not_live", spawnCommand }
|
|
7513
|
-
});
|
|
7514
|
-
return { sessionName, error: errMsg2 };
|
|
7515
|
-
}
|
|
7516
|
-
const STARTUP_WATCH_MS = resolveStartupWatchCapMs({
|
|
7517
|
-
useCodex,
|
|
7518
|
-
callerIsDaemon,
|
|
7519
|
-
override: process.env.EXE_SPAWN_STARTUP_WATCH_MS,
|
|
7520
|
-
legacyOverride: process.env.EXE_SPAWN_STABILITY_MS
|
|
7521
|
-
});
|
|
7522
|
-
const STARTUP_WATCH_INTERVAL_MS = 250;
|
|
7523
|
-
const startupWatch = watchSpawnStartup(
|
|
7524
|
-
{
|
|
7525
|
-
readLauncherExit: () => readSpawnExitMarker(exitMarkerPath),
|
|
7526
|
-
isAlive: () => transport.isAlive(sessionName),
|
|
7527
|
-
capturePane: () => transport.capturePane(sessionName, 50),
|
|
7528
|
-
sleep: (ms) => {
|
|
7529
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
7530
|
-
}
|
|
7531
|
-
},
|
|
7532
|
-
{ capMs: STARTUP_WATCH_MS, intervalMs: STARTUP_WATCH_INTERVAL_MS }
|
|
7533
|
-
);
|
|
7534
|
-
if (startupWatch.outcome === "exited") {
|
|
7535
|
-
try {
|
|
7536
|
-
execFileSync3("tmux", ["kill-session", "-t", sessionName], { timeout: 3e3 });
|
|
7537
|
-
} catch {
|
|
7538
|
-
}
|
|
7539
|
-
releaseSpawnLock(sessionName);
|
|
7540
|
-
const { exitCode } = startupWatch;
|
|
7541
|
-
const refused = exitCode === 78;
|
|
7542
|
-
const errorCode = refused ? "launch_refused" : "startup_exit";
|
|
7543
|
-
const paneCause = classifySpawnFailurePane(startupWatch.pane);
|
|
7544
|
-
const errMsg2 = refused ? `spawn refused: ${runtimeLabel} launcher for "${sessionName}" exited 78 (refusal) within ${STARTUP_WATCH_MS / 1e3}s \u2014 it declined to start the agent. Most common cause: running as root (uid 0), where Claude Code rejects --dangerously-skip-permissions / bypassPermissions. Run the daemon as a non-root user. Check ~/.exe-os/session-logs/${instanceLabel}-*.log. Pane tail: ${startupWatch.pane.trim().slice(-500) || "(empty)"}` : `spawn died during startup: ${runtimeLabel} launcher for "${sessionName}" ` + (exitCode === null ? `disappeared within ${STARTUP_WATCH_MS / 1e3}s of launch` : `exited ${exitCode} within ${STARTUP_WATCH_MS / 1e3}s of launch`) + ` (cause=${paneCause}). Check ~/.exe-os/session-logs/${instanceLabel}-*.log. Pane tail: ${startupWatch.pane.trim().slice(-500) || "(empty)"}`;
|
|
7545
|
-
process.stderr.write(`[tmux-routing] SPAWN FAILED ${sessionName} (${errorCode}): ${errMsg2}
|
|
7546
|
-
`);
|
|
7547
|
-
recordOrchestrationEventBestEffort({
|
|
7548
|
-
eventType: "tmux.spawn.completed",
|
|
7549
|
-
source: "tmux-routing.spawnEmployee",
|
|
7550
|
-
severity: "warn",
|
|
7551
|
-
agentId: employeeName,
|
|
7552
|
-
sessionScope: exeSession,
|
|
7553
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7554
|
-
tmuxSession: sessionName,
|
|
7555
|
-
runtime: runtimeLabel,
|
|
7556
|
-
durationMs: Date.now() - spawnStartedAt,
|
|
7557
|
-
result: "failed",
|
|
7558
|
-
errorCode,
|
|
7559
|
-
payload: {
|
|
7560
|
-
detail: "spawn_exited_during_startup",
|
|
7561
|
-
exitCode,
|
|
7562
|
-
spawnFailureCause: paneCause,
|
|
7563
|
-
watchCapMs: STARTUP_WATCH_MS,
|
|
7564
|
-
polls: startupWatch.polls,
|
|
7565
|
-
spawnCommand
|
|
7566
|
-
}
|
|
7567
|
-
});
|
|
7568
|
-
return { sessionName, error: errMsg2 };
|
|
7569
|
-
}
|
|
7570
|
-
if (useCodex) {
|
|
7571
|
-
let pane = "";
|
|
7572
|
-
try {
|
|
7573
|
-
pane = transport.capturePane(sessionName, 80);
|
|
7574
|
-
} catch {
|
|
7575
|
-
}
|
|
7576
|
-
if (isBlockedCodexPrompt(pane)) {
|
|
7577
|
-
try {
|
|
7578
|
-
execFileSync3("tmux", ["kill-session", "-t", sessionName], { timeout: 3e3 });
|
|
7579
|
-
} catch {
|
|
7580
|
-
}
|
|
7581
|
-
releaseSpawnLock(sessionName);
|
|
7582
|
-
const errMsg2 = `codex spawn blocked by an interactive model prompt in ${sessionName}; killed session so it can respawn non-interactively`;
|
|
7583
|
-
process.stderr.write(`[tmux-routing] SPAWN FAILED ${sessionName} (codex_blocked_prompt): ${errMsg2}
|
|
7584
|
-
`);
|
|
7585
|
-
recordOrchestrationEventBestEffort({
|
|
7586
|
-
eventType: "tmux.spawn.completed",
|
|
7587
|
-
source: "tmux-routing.spawnEmployee",
|
|
7588
|
-
severity: "warn",
|
|
7589
|
-
agentId: employeeName,
|
|
7590
|
-
sessionScope: exeSession,
|
|
7591
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7592
|
-
tmuxSession: sessionName,
|
|
7593
|
-
runtime: "codex",
|
|
7594
|
-
durationMs: Date.now() - spawnStartedAt,
|
|
7595
|
-
result: "failed",
|
|
7596
|
-
errorCode: "codex_blocked_prompt",
|
|
7597
|
-
payload: { detail: "interactive_model_prompt" }
|
|
7598
|
-
});
|
|
7599
|
-
return { sessionName, error: errMsg2 };
|
|
7600
|
-
}
|
|
7601
|
-
}
|
|
7602
|
-
const POLL_CAP_MS = callerIsDaemon ? 0 : 45e3;
|
|
7603
|
-
const pollIterations = Math.ceil(POLL_CAP_MS / POLL_INTERVAL_MS);
|
|
7604
|
-
let booted = false;
|
|
7605
|
-
for (let i = 0; i < pollIterations; i++) {
|
|
7606
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, POLL_INTERVAL_MS);
|
|
7607
|
-
try {
|
|
7608
|
-
if (paneShowsRuntimeBanner(transport.capturePane(sessionName), runtimeLabel)) {
|
|
7609
|
-
booted = true;
|
|
7610
|
-
break;
|
|
7611
|
-
}
|
|
7612
|
-
} catch {
|
|
7613
|
-
}
|
|
7614
|
-
}
|
|
7615
|
-
if (!booted && !callerIsDaemon) {
|
|
7616
|
-
process.stderr.write(
|
|
7617
|
-
`[tmux-routing] ${runtimeLabel} boot banner poll capped at ${POLL_CAP_MS / 1e3}s for ${sessionName} (session is live) \u2014 sending task nudge anyway
|
|
7618
|
-
`
|
|
7619
|
-
);
|
|
7620
|
-
}
|
|
7621
|
-
if (!useExeAgent && !useCodex) {
|
|
7622
|
-
if (callerIsDaemon) {
|
|
7623
|
-
try {
|
|
7624
|
-
const empRoster = loadEmployeesSync();
|
|
7625
|
-
const bootCache = {
|
|
7626
|
-
agentId: employeeName,
|
|
7627
|
-
agentRole: empRoster.find((e) => baseAgentName(e.name) === baseAgentName(employeeName))?.role ?? "employee",
|
|
7628
|
-
sessionName,
|
|
7629
|
-
exeSession,
|
|
7630
|
-
dispatchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7631
|
-
};
|
|
7632
|
-
const cachePath = path8.join(os6.homedir(), ".exe-os", "session-cache", `boot-cache-${sessionName}.json`);
|
|
7633
|
-
writeFileSync5(cachePath, JSON.stringify(bootCache));
|
|
7634
|
-
} catch {
|
|
7635
|
-
}
|
|
7636
|
-
scheduleDeferredSpawnNudge({
|
|
7637
|
-
sessionName,
|
|
7638
|
-
employeeName,
|
|
7639
|
-
exeSession,
|
|
7640
|
-
runtime: useOpencode ? "opencode" : "claude",
|
|
7641
|
-
transport,
|
|
7642
|
-
message: `You have pending tasks. Run list_tasks and start your highest priority task immediately.`
|
|
7643
|
-
});
|
|
7644
|
-
} else {
|
|
7645
|
-
try {
|
|
7646
|
-
transport.sendKeysLiteral(sessionName, `You have pending tasks. Run list_tasks and start immediately.`);
|
|
7647
|
-
} catch (e) {
|
|
7648
|
-
process.stderr.write(`[tmux-routing] CLI nudge ${employeeName}: ${e instanceof Error ? e.message : String(e)}
|
|
7649
|
-
`);
|
|
7650
|
-
}
|
|
7651
|
-
}
|
|
7652
|
-
}
|
|
7653
|
-
registerSession({
|
|
7654
|
-
windowName: sessionName,
|
|
7655
|
-
agentId: employeeName,
|
|
7656
|
-
projectDir: spawnCwd,
|
|
7657
|
-
parentExe: exeSession,
|
|
7658
|
-
pid: 0,
|
|
7659
|
-
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7660
|
-
});
|
|
7661
|
-
releaseSpawnLock(sessionName);
|
|
7662
|
-
recordOrchestrationEventBestEffort({
|
|
7663
|
-
eventType: "tmux.spawn.completed",
|
|
7664
|
-
source: "tmux-routing.spawnEmployee",
|
|
7665
|
-
agentId: employeeName,
|
|
7666
|
-
sessionScope: exeSession,
|
|
7667
|
-
instanceId: opts?.instance != null ? String(opts.instance) : null,
|
|
7668
|
-
tmuxSession: sessionName,
|
|
7669
|
-
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
7670
|
-
durationMs: Date.now() - spawnStartedAt,
|
|
7671
|
-
result: booted ? "spawned_booted" : "spawned_unverified"
|
|
7672
|
-
});
|
|
7673
|
-
return { sessionName };
|
|
6244
|
+
return { sessionName, error: message };
|
|
7674
6245
|
}
|
|
7675
6246
|
|
|
7676
6247
|
// src/lib/task-scope.ts
|
|
@@ -7909,12 +6480,6 @@ function canonicalTaskFilePath(oldTaskFile, assignedTo, newSessionScope) {
|
|
|
7909
6480
|
}
|
|
7910
6481
|
|
|
7911
6482
|
export {
|
|
7912
|
-
getCcHelpOutputCached,
|
|
7913
|
-
_resetCcHelpCache,
|
|
7914
|
-
_resetCcAgentSupportCache,
|
|
7915
|
-
claudeSupportsAgentFlag,
|
|
7916
|
-
PROVIDER_TABLE,
|
|
7917
|
-
DEFAULT_PROVIDER,
|
|
7918
6483
|
isAmbiguousMultiCoordinatorContext,
|
|
7919
6484
|
hasLiveCoordinatorLanes,
|
|
7920
6485
|
isMultiCoordinatorSharedProject,
|
|
@@ -7945,6 +6510,7 @@ export {
|
|
|
7945
6510
|
_resetDrainGuard,
|
|
7946
6511
|
logTaskDispatch,
|
|
7947
6512
|
DEFAULT_TASK_PRIORITY,
|
|
6513
|
+
DEFAULT_NEW_TASK_EXECUTION_MODE,
|
|
7948
6514
|
normalizeTaskPriority,
|
|
7949
6515
|
isStructuredTaskCompletionReport,
|
|
7950
6516
|
resolveCompletionChangedFiles,
|
|
@@ -8007,6 +6573,8 @@ export {
|
|
|
8007
6573
|
getReviewChecklist,
|
|
8008
6574
|
createReviewForCompletedTask,
|
|
8009
6575
|
cleanupReviewFile,
|
|
6576
|
+
tmuxDispatchRefusal,
|
|
6577
|
+
spawnOverrideNoopNotice,
|
|
8010
6578
|
BUSY_PATTERN,
|
|
8011
6579
|
_resetExeBinDirCache,
|
|
8012
6580
|
buildSpawnPath,
|
|
@@ -8025,7 +6593,6 @@ export {
|
|
|
8025
6593
|
resolveExeSession,
|
|
8026
6594
|
isEmployeeAlive,
|
|
8027
6595
|
findFreeInstance,
|
|
8028
|
-
detectCoordinatorCheckoutCwd,
|
|
8029
6596
|
verifyPaneAtCapacity,
|
|
8030
6597
|
resolveSessionRuntime,
|
|
8031
6598
|
classifyPaneSessionState,
|
|
@@ -8058,18 +6625,7 @@ export {
|
|
|
8058
6625
|
sendIntercomAsync,
|
|
8059
6626
|
notifyParentExe,
|
|
8060
6627
|
notifyCoordinatorTaskCompletion,
|
|
8061
|
-
emitOptOutBypassWarning,
|
|
8062
|
-
worktreeIsolationGate,
|
|
8063
6628
|
ensureEmployee,
|
|
8064
6629
|
ensureEmployeeAsync,
|
|
8065
|
-
decideSpawnWorktreeDiscipline,
|
|
8066
|
-
resolveSpawnCwd,
|
|
8067
|
-
spawnExitMarkerPath,
|
|
8068
|
-
buildSpawnCommand,
|
|
8069
|
-
readSpawnExitMarker,
|
|
8070
|
-
paneShowsRuntimeBanner,
|
|
8071
|
-
classifySpawnFailurePane,
|
|
8072
|
-
resolveStartupWatchCapMs,
|
|
8073
|
-
watchSpawnStartup,
|
|
8074
6630
|
spawnEmployee
|
|
8075
6631
|
};
|