@askexenow/exe-os 0.9.407 → 0.9.409

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (415) hide show
  1. package/deploy/compose/erp-nginx/nginx.conf +8 -2
  2. package/deploy/compose/sso-edge/default.conf.template +55 -0
  3. package/deploy/hosted/public-demo-compose.yml +52 -0
  4. package/deploy/stack-manifests/v0.9.json +1339 -1
  5. package/dist/.dist-verified.json +3 -3
  6. package/dist/{active-agent-U6JYIXZD.js → active-agent-2AMJC3SC.js} +6 -5
  7. package/dist/{active-agent-2OMOWJXI.js → active-agent-5AKPW2N7.js} +6 -5
  8. package/dist/{agent-defs-generator-TZC3QFTT.js → agent-defs-generator-QN3J6FVJ.js} +5 -5
  9. package/dist/{agent-loop-OQUZUGQU.js → agent-loop-FMOA2MG2.js} +1 -1
  10. package/dist/{agentic-ontology-GJI54CFX.js → agentic-ontology-6RS7A7KP.js} +1 -1
  11. package/dist/{artifact-backup-4GCKVN3V.js → artifact-backup-6O4FNYPS.js} +3 -3
  12. package/dist/{backfill-metadata-EVJEKCY3.js → backfill-metadata-6YX6LGPJ.js} +8 -8
  13. package/dist/{backup-verify-PIH5GXLV.js → backup-verify-QIAQ63VC.js} +1 -1
  14. package/dist/{behavior-injection-TKQETCCW.js → behavior-injection-NRXTNROW.js} +11 -11
  15. package/dist/{behavior-promotion-TB53MVMQ.js → behavior-promotion-MW7OQ72V.js} +10 -10
  16. package/dist/{behaviors-2XKHIDUD.js → behaviors-7AOK44QK.js} +9 -9
  17. package/dist/bin/agentic-ontology-backfill.js +9 -9
  18. package/dist/bin/agentic-reflection-backfill.js +10 -10
  19. package/dist/bin/agentic-semantic-label.js +9 -9
  20. package/dist/bin/backfill-conversations.js +8 -8
  21. package/dist/bin/backfill-responses.js +8 -8
  22. package/dist/bin/backfill-vectors.js +8 -8
  23. package/dist/bin/bulk-sync-postgres.js +17 -17
  24. package/dist/bin/cc-doctor.js +12 -11
  25. package/dist/bin/cleanup-stale-review-tasks.js +19 -19
  26. package/dist/bin/cli.js +24 -24
  27. package/dist/bin/deferred-daemon-restart.js +104 -103
  28. package/dist/bin/exe-agent-config.js +5 -5
  29. package/dist/bin/exe-agent.js +10 -10
  30. package/dist/bin/exe-assign.js +9 -9
  31. package/dist/bin/exe-boot.js +44 -43
  32. package/dist/bin/exe-call.js +9 -9
  33. package/dist/bin/exe-cloud.js +18 -18
  34. package/dist/bin/exe-config-dump.js +5 -5
  35. package/dist/bin/exe-dispatch.js +20 -20
  36. package/dist/bin/exe-doctor.js +7 -7
  37. package/dist/bin/exe-export-behaviors.js +13 -13
  38. package/dist/bin/exe-forget.js +11 -11
  39. package/dist/bin/exe-gateway.js +9 -9
  40. package/dist/bin/exe-grant.js +6 -6
  41. package/dist/bin/exe-healthcheck.js +12 -11
  42. package/dist/bin/exe-heartbeat.js +25 -25
  43. package/dist/bin/exe-kill.js +25 -25
  44. package/dist/bin/exe-launch-agent.js +34 -34
  45. package/dist/bin/exe-mcp.js +7 -6
  46. package/dist/bin/exe-memory-reads.js +8 -8
  47. package/dist/bin/exe-new-employee.js +11 -11
  48. package/dist/bin/exe-os-mcp-proxy.js +7 -6
  49. package/dist/bin/exe-pending-messages.js +20 -20
  50. package/dist/bin/exe-pending-notifications.js +19 -19
  51. package/dist/bin/exe-pending-reviews.js +20 -20
  52. package/dist/bin/exe-rename.js +7 -7
  53. package/dist/bin/exe-review.js +24 -24
  54. package/dist/bin/exe-search.js +10 -10
  55. package/dist/bin/exe-session-cleanup.js +26 -26
  56. package/dist/bin/exe-settings.js +19 -19
  57. package/dist/bin/exe-start-codex.js +20 -20
  58. package/dist/bin/exe-start-opencode.js +18 -18
  59. package/dist/bin/exe-status.js +21 -21
  60. package/dist/bin/exe-support.js +3 -3
  61. package/dist/bin/exe-team.js +5 -5
  62. package/dist/bin/git-sweep.js +21 -21
  63. package/dist/bin/graph-backfill.js +9 -9
  64. package/dist/bin/graph-export.js +9 -9
  65. package/dist/bin/hooks-cli.js +4 -4
  66. package/dist/bin/import-history.js +14 -13
  67. package/dist/bin/install.js +106 -105
  68. package/dist/bin/intercom-check.js +5 -5
  69. package/dist/bin/mcp-sessions.js +2 -2
  70. package/dist/bin/orchestration-metrics.js +5 -5
  71. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  72. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  73. package/dist/bin/pre-publish.js +1 -1
  74. package/dist/bin/reconcile-backlog.js +2 -2
  75. package/dist/bin/registry-proxy.js +1 -1
  76. package/dist/bin/repair-document-chunks.js +8 -8
  77. package/dist/bin/scan-tasks.js +20 -20
  78. package/dist/bin/setup.js +3 -3
  79. package/dist/bin/shard-migrate.js +9 -9
  80. package/dist/bin/stack-update.js +4 -4
  81. package/dist/bin/vps-health-gate.js +1 -1
  82. package/dist/{boot-generation-34GD265Q.js → boot-generation-HG6LLZLY.js} +4 -4
  83. package/dist/{boot-review-sections-JYDDUJH5.js → boot-review-sections-STZ5EAIL.js} +18 -18
  84. package/dist/{bug-landed-reconciler-ORYLGRQI.js → bug-landed-reconciler-EO72HUI2.js} +7 -7
  85. package/dist/build-info.json +3 -3
  86. package/dist/build-manifest.json +970 -970
  87. package/dist/{capability-cards-IOFJOGID.js → capability-cards-C3SMHB2E.js} +3 -3
  88. package/dist/{capacity-monitor-CJSDRDLR.js → capacity-monitor-UI553HRL.js} +20 -20
  89. package/dist/{catchup-brief-ELSYIYVL.js → catchup-brief-VE2X4HML.js} +26 -26
  90. package/dist/{chunk-HBOSSJWS.js → chunk-23XUZHB7.js} +5 -5
  91. package/dist/{chunk-BG52IECQ.js → chunk-3AL2LO3L.js} +1 -1
  92. package/dist/{chunk-HPDR3EL7.js → chunk-3MDUKF3N.js} +4 -4
  93. package/dist/{chunk-BHKCSRJG.js → chunk-4M24XCTI.js} +663 -68
  94. package/dist/{chunk-RGPEUXOE.js → chunk-4MK6LOD6.js} +27 -3
  95. package/dist/{chunk-ZB34E2OV.js → chunk-4O7OUBEX.js} +10 -10
  96. package/dist/{chunk-Z5ERWCOL.js → chunk-4Q7H3TO7.js} +1 -1
  97. package/dist/{chunk-YTP6L76N.js → chunk-4RQ62XXK.js} +5 -5
  98. package/dist/{chunk-VDSAXP3N.js → chunk-4S6H2NWS.js} +4 -4
  99. package/dist/{chunk-DXZRXNIC.js → chunk-4W7Y67B5.js} +1 -1
  100. package/dist/{chunk-SS3FTK77.js → chunk-52Z435PH.js} +8 -3
  101. package/dist/{chunk-77EIMVAX.js → chunk-5N34MMKS.js} +1 -1
  102. package/dist/{chunk-UMLDPQ24.js → chunk-6EY6OFBC.js} +2 -2
  103. package/dist/{chunk-IMI5ZAOA.js → chunk-6JLVCMKH.js} +1 -1
  104. package/dist/{chunk-ZGRLIQLE.js → chunk-6RRPVE5O.js} +3 -3
  105. package/dist/{chunk-6SJ4SKWZ.js → chunk-6TB7UXCA.js} +1 -1
  106. package/dist/{chunk-342ZBEDI.js → chunk-6ZW6BRI4.js} +2 -2
  107. package/dist/{chunk-5QJEJA5H.js → chunk-7I6KVKFF.js} +3 -3
  108. package/dist/{chunk-4SLKFUA7.js → chunk-7LTAA55U.js} +5 -1
  109. package/dist/{chunk-FKANX6A7.js → chunk-7P2TDFZL.js} +1 -1
  110. package/dist/{chunk-SLJDYBLE.js → chunk-7R7KDRKV.js} +1 -1
  111. package/dist/{chunk-NRLJOV7M.js → chunk-7YX73K2R.js} +97 -58
  112. package/dist/{chunk-R5YD5DIP.js → chunk-APVU4Q5B.js} +16 -16
  113. package/dist/{chunk-7OX2AACL.js → chunk-B3ISLIKE.js} +2 -2
  114. package/dist/{chunk-CP6XE35Z.js → chunk-BHDDDNC6.js} +2 -2
  115. package/dist/{chunk-TEHJ5TOU.js → chunk-BIV4IHED.js} +12 -12
  116. package/dist/{chunk-5GYA4ERO.js → chunk-BM467PZN.js} +1 -1
  117. package/dist/{chunk-BER2YLZT.js → chunk-BPBEJD7U.js} +1 -1
  118. package/dist/{chunk-T4IDLAZM.js → chunk-BYP3DOUU.js} +13 -0
  119. package/dist/{chunk-U7A2EACP.js → chunk-C426MZMD.js} +3 -3
  120. package/dist/{chunk-TRZXFWRO.js → chunk-DACRR362.js} +6 -6
  121. package/dist/{chunk-232YVOFB.js → chunk-DE2V7547.js} +2 -2
  122. package/dist/{chunk-QHQW3VHV.js → chunk-DIKLSAJE.js} +1 -1
  123. package/dist/{chunk-XC6EXJBA.js → chunk-DM4VTXM6.js} +4 -4
  124. package/dist/{chunk-EEY77PKF.js → chunk-DMYJVGQM.js} +1 -1
  125. package/dist/{chunk-ZSZVJ2FR.js → chunk-E2Z3F26R.js} +1 -1
  126. package/dist/{chunk-GRIGIKC4.js → chunk-EHILXBVT.js} +3 -3
  127. package/dist/{chunk-AIDYWH4D.js → chunk-EIM7OGTD.js} +2 -2
  128. package/dist/{chunk-7E4KZIX7.js → chunk-ESO5JDOT.js} +1 -1
  129. package/dist/{chunk-SPHPROWZ.js → chunk-F5ZRHLG5.js} +2 -2
  130. package/dist/{chunk-BIBBNDM3.js → chunk-FBZ32BD4.js} +1 -1
  131. package/dist/{chunk-FBB2YMKB.js → chunk-FJKUFMNF.js} +1 -1
  132. package/dist/{chunk-MNEVSKUY.js → chunk-FZT4VQP7.js} +1 -1
  133. package/dist/{chunk-ZLMVXK4O.js → chunk-G3ZYXXO7.js} +11 -11
  134. package/dist/{chunk-6RWTLW75.js → chunk-GEQQGN3J.js} +7 -7
  135. package/dist/{chunk-7NR6LMGH.js → chunk-GG73NHZD.js} +2 -2
  136. package/dist/{chunk-VGP4T2L6.js → chunk-GKPPLIMJ.js} +5 -5
  137. package/dist/{chunk-ERIXGUSV.js → chunk-GPWXUU3L.js} +1 -1
  138. package/dist/{chunk-OAP66FCX.js → chunk-GRAMRMWS.js} +2 -2
  139. package/dist/{chunk-IVX4CW6I.js → chunk-GTRB6DGI.js} +2 -2
  140. package/dist/{chunk-WQ7YIU4M.js → chunk-HFSFWMID.js} +3 -3
  141. package/dist/{chunk-5FOJ6N3V.js → chunk-HFXBL7XE.js} +1 -1
  142. package/dist/{chunk-36QUBTID.js → chunk-HK7HIDXP.js} +2 -2
  143. package/dist/{chunk-323DU3UM.js → chunk-HNGAM3U4.js} +3 -3
  144. package/dist/{chunk-XIJIFRPA.js → chunk-HRKN3DQE.js} +8 -8
  145. package/dist/{chunk-AO2HCOE6.js → chunk-HRYWB4TO.js} +3 -3
  146. package/dist/{chunk-Q2RTUZXB.js → chunk-HUMNRJZE.js} +4 -4
  147. package/dist/{chunk-KAKURNNS.js → chunk-IPANAGA3.js} +1 -1
  148. package/dist/{chunk-RZTSXHWF.js → chunk-IPBNHPTE.js} +9 -9
  149. package/dist/{chunk-SDJ3TONY.js → chunk-IUBCS6TA.js} +3 -3
  150. package/dist/{chunk-C7SKFVXQ.js → chunk-IURGBN5B.js} +4 -4
  151. package/dist/{chunk-GWAN2BDB.js → chunk-IVXELUEQ.js} +25 -12
  152. package/dist/{chunk-DG3DWHYJ.js → chunk-IZ24HRDC.js} +3 -3
  153. package/dist/{chunk-G3ABALZ7.js → chunk-JJYAETCO.js} +4 -4
  154. package/dist/{chunk-6XJYUC7K.js → chunk-JR3GTBV3.js} +1 -1
  155. package/dist/{chunk-MLIRYXZ2.js → chunk-K7DAE2HO.js} +1 -1
  156. package/dist/{chunk-ZGNMI2WJ.js → chunk-KBHK2WNO.js} +1 -1
  157. package/dist/{chunk-MAY4VWEG.js → chunk-KCRSJ3VU.js} +3 -3
  158. package/dist/{chunk-NF7JS7XW.js → chunk-KIJRAZWR.js} +1 -1
  159. package/dist/{chunk-35PGCSNN.js → chunk-KPLMY2GT.js} +4 -4
  160. package/dist/{chunk-USZHUBHT.js → chunk-L4ISXVC5.js} +1 -1
  161. package/dist/{chunk-UNNOYMQI.js → chunk-L5CPS34O.js} +1 -1
  162. package/dist/{chunk-SJTFU3YJ.js → chunk-LE3DIA25.js} +3 -3
  163. package/dist/{chunk-DEBKL7WW.js → chunk-LMF2YC5C.js} +4 -4
  164. package/dist/{chunk-LDYQPD5H.js → chunk-LVEMYNYH.js} +3 -3
  165. package/dist/{chunk-G4MVORCE.js → chunk-M4N7I2H5.js} +5 -5
  166. package/dist/{chunk-5F74HGSQ.js → chunk-M7774YLB.js} +1 -1
  167. package/dist/{chunk-3AOPBH5S.js → chunk-ME733Y2F.js} +1 -1
  168. package/dist/{chunk-CCY37D3C.js → chunk-N3JEYUDV.js} +2 -2
  169. package/dist/{chunk-3X733RHO.js → chunk-NDCW4G55.js} +1 -1
  170. package/dist/{chunk-Y73MI7UH.js → chunk-NFNM64A7.js} +12 -12
  171. package/dist/{chunk-3AC6WJNP.js → chunk-NHQWPKX7.js} +2 -2
  172. package/dist/{chunk-YB5IW32Y.js → chunk-NK4JHJNP.js} +4 -4
  173. package/dist/{chunk-IZWMWJZT.js → chunk-NVM3SKJ4.js} +1 -1
  174. package/dist/{chunk-VSJ3Y26C.js → chunk-OE7TAEOG.js} +1 -1
  175. package/dist/{chunk-D6PNMFPF.js → chunk-OEHZ46WQ.js} +2 -2
  176. package/dist/{chunk-4MQDDFRN.js → chunk-OFT5KSBM.js} +1 -1
  177. package/dist/{chunk-FEJK36FV.js → chunk-OISOA6NX.js} +2 -2
  178. package/dist/{chunk-L23CONP7.js → chunk-PCFYNK7V.js} +3 -3
  179. package/dist/{chunk-OMMQNLFE.js → chunk-PKM456D2.js} +1 -1
  180. package/dist/{chunk-G66AOAYN.js → chunk-PZDAPIQL.js} +1 -1
  181. package/dist/{chunk-QZJY4RMF.js → chunk-Q4GS5VW7.js} +3 -3
  182. package/dist/{chunk-DOO6M2XM.js → chunk-QK3Z4TKQ.js} +5 -5
  183. package/dist/{chunk-KZSBPAEJ.js → chunk-QPMKVSHU.js} +2 -2
  184. package/dist/{chunk-ZL3D7XTR.js → chunk-QUK3UDZV.js} +3 -3
  185. package/dist/{chunk-4U2TAOW7.js → chunk-QYIR64RR.js} +1 -1
  186. package/dist/{chunk-QWXBLCVK.js → chunk-R6CKQLEA.js} +2 -2
  187. package/dist/{chunk-X6HOIDIR.js → chunk-R7QZXQPU.js} +3 -3
  188. package/dist/{chunk-G6D6U2EY.js → chunk-RZ23QQCI.js} +4 -4
  189. package/dist/{chunk-PYS43F2X.js → chunk-RZBSVTOL.js} +1 -1
  190. package/dist/{chunk-RHEIOXVL.js → chunk-SA66WRBT.js} +1 -1
  191. package/dist/{chunk-KVPCFVAH.js → chunk-SFGFNJBR.js} +1 -1
  192. package/dist/{chunk-S5OI42DL.js → chunk-SGR4ZQAV.js} +3 -3
  193. package/dist/{chunk-IQI5LAEK.js → chunk-SY53PYLU.js} +1 -1
  194. package/dist/{chunk-7DG3WBGN.js → chunk-SYUJCVJG.js} +1 -1
  195. package/dist/{chunk-HJ2DJJIK.js → chunk-T2PAFWRK.js} +8 -8
  196. package/dist/{chunk-AWNX33WP.js → chunk-TBYWOHTE.js} +1 -1
  197. package/dist/{chunk-DDAOY3DL.js → chunk-TCV3WA5I.js} +1 -1
  198. package/dist/{chunk-3VW5ZP2F.js → chunk-UWGQNJIE.js} +1 -1
  199. package/dist/{chunk-3DIP37BT.js → chunk-UXJAW4IQ.js} +2 -2
  200. package/dist/{chunk-QJTG3DN7.js → chunk-VLAENYCN.js} +1 -1
  201. package/dist/{chunk-TLELTVAN.js → chunk-VYKXRSHU.js} +1 -1
  202. package/dist/{chunk-VFUMCL6H.js → chunk-W2VDJKTB.js} +1 -1
  203. package/dist/{chunk-PCLIU4BP.js → chunk-W5XAB2SW.js} +154 -152
  204. package/dist/{chunk-6GBGKSNQ.js → chunk-WYNIWJRN.js} +5 -5
  205. package/dist/{chunk-7VSPHUIB.js → chunk-XAGIZVYI.js} +26 -26
  206. package/dist/{chunk-XT7ZZJVD.js → chunk-XCPD7NGP.js} +1 -1
  207. package/dist/{chunk-52JOFQOK.js → chunk-XFGS22VE.js} +1 -1
  208. package/dist/{chunk-YV5JIEYI.js → chunk-XGPBHKU3.js} +2 -2
  209. package/dist/{chunk-WJ53NNEV.js → chunk-Y5LJYDX4.js} +1 -0
  210. package/dist/{chunk-GVVWEBTD.js → chunk-YLWKWEJJ.js} +3 -3
  211. package/dist/{chunk-6Y7TK4EA.js → chunk-YUAOQJVV.js} +2 -2
  212. package/dist/{chunk-KP3J52RW.js → chunk-YVS77JPW.js} +3 -3
  213. package/dist/{chunk-XA5FPMYR.js → chunk-ZEHBSA25.js} +2 -2
  214. package/dist/{chunk-6ENEX5UH.js → chunk-ZYOIH5NY.js} +6 -6
  215. package/dist/{cleanup-graph-garbage-M5OYEPRR.js → cleanup-graph-garbage-NKAJSCWZ.js} +9 -9
  216. package/dist/{cli-shutdown-AY2FMXD6.js → cli-shutdown-F3DFDCB5.js} +1 -1
  217. package/dist/{cloud-sync-command-PRC4GV62.js → cloud-sync-command-3YBLSMAL.js} +2 -2
  218. package/dist/{cloud-sync-db-init-IYXPCNSJ.js → cloud-sync-db-init-JWSPAFKY.js} +1 -1
  219. package/dist/{cloud-sync-shutdown-3F6XASFF.js → cloud-sync-shutdown-4R6RIO67.js} +3 -3
  220. package/dist/{co-activation-LB56BYW2.js → co-activation-2ZRANV3Q.js} +3 -3
  221. package/dist/{co-occurrence-TKPZB3IR.js → co-occurrence-6EELW2L2.js} +7 -7
  222. package/dist/{consent-passphrase-YQLAAPPC.js → consent-passphrase-TDR263MC.js} +2 -2
  223. package/dist/{content-extractor-XCS6QGM4.js → content-extractor-OVD7PTSN.js} +2 -2
  224. package/dist/{conversation-entity-extractor-J2UZGFG3.js → conversation-entity-extractor-KUJN3MOH.js} +4 -4
  225. package/dist/{crdt-sync-GE6DVV2F.js → crdt-sync-IX43KVQ3.js} +4 -4
  226. package/dist/{crm-webhook-6HXNLEQV.js → crm-webhook-GSCIW6CE.js} +2 -2
  227. package/dist/{cto-delegation-gate-QSWDPH67.js → cto-delegation-gate-ZWD4EFRR.js} +20 -19
  228. package/dist/{daemon-orchestration-YJ4GMHVM.js → daemon-orchestration-WL6V4QL4.js} +24 -24
  229. package/dist/{doc-graph-extractor-XLAUARWE.js → doc-graph-extractor-NJ7ZZC2C.js} +7 -7
  230. package/dist/{documents-SZ76YQ3Q.js → documents-O7WYZFFE.js} +9 -9
  231. package/dist/{dreaming-ZQGMOZ2E.js → dreaming-TUESPDT3.js} +24 -24
  232. package/dist/{employee-boot-brief-SV5JHCJL.js → employee-boot-brief-UCP6QLN4.js} +28 -28
  233. package/dist/{entity-boost-KTADWOGJ.js → entity-boost-TK43XT24.js} +1 -1
  234. package/dist/{exe-drift-CT36U5DX.js → exe-drift-QD6LUZT2.js} +4 -4
  235. package/dist/{exe-export-EJ4B74OU.js → exe-export-QIUNHFSS.js} +11 -11
  236. package/dist/{exe-import-UA6OIZ4C.js → exe-import-3UFOLHEW.js} +11 -11
  237. package/dist/{exe-key-SKOOVJCT.js → exe-key-D5NDKGHT.js} +104 -103
  238. package/dist/{exe-org-WQARAD3U.js → exe-org-UWDJBOCV.js} +32 -32
  239. package/dist/{exe-snapshot-2OXDANU3.js → exe-snapshot-P6SFODEN.js} +27 -27
  240. package/dist/{fast-db-init-YFUMI4NH.js → fast-db-init-SD7JZSEC.js} +1 -1
  241. package/dist/{founder-context-DMWIIDJ2.js → founder-context-QIXDZIJX.js} +2 -2
  242. package/dist/{founder-monitor-envelope-JYFWXZQ4.js → founder-monitor-envelope-TLOC4UYG.js} +4 -4
  243. package/dist/gateway/index.js +11 -11
  244. package/dist/{gateway-client-ELHI2DWX.js → gateway-client-CQTSYJLP.js} +1 -1
  245. package/dist/{git-staleness-K7HHMBWI.js → git-staleness-JSQTHB4Z.js} +3 -3
  246. package/dist/{global-procedures-ERZJFJUA.js → global-procedures-T3RKI4QX.js} +6 -6
  247. package/dist/{graph-auto-extract-LUQ6QZ4P.js → graph-auto-extract-V75GSTNX.js} +7 -7
  248. package/dist/{graph-query-KRNMWF77.js → graph-query-VAQN2RK3.js} +1 -1
  249. package/dist/{graph-rag-2W26GEGZ.js → graph-rag-GBA5E3QC.js} +4 -4
  250. package/dist/{hook-integrity-QFBZE5FK.js → hook-integrity-II6QK6Y4.js} +1 -1
  251. package/dist/{hook-prefetch-WP3PEE4H.js → hook-prefetch-5EHPOYY3.js} +19 -19
  252. package/dist/hooks/bug-report-worker.js +23 -23
  253. package/dist/hooks/codex-stop-task-finalizer.js +25 -25
  254. package/dist/hooks/commit-complete.js +23 -23
  255. package/dist/hooks/error-recall.js +14 -13
  256. package/dist/hooks/exe-heartbeat-hook.js +7 -6
  257. package/dist/hooks/ingest-worker.js +35 -9
  258. package/dist/hooks/ingest.js +16 -15
  259. package/dist/hooks/instructions-loaded.js +9 -8
  260. package/dist/hooks/manifest.json +20 -20
  261. package/dist/hooks/notification.js +8 -7
  262. package/dist/hooks/post-compact.js +7 -6
  263. package/dist/hooks/post-tool-combined.js +13 -13
  264. package/dist/hooks/pre-compact.js +24 -23
  265. package/dist/hooks/pre-tool-use.js +2 -2
  266. package/dist/hooks/prompt-submit.js +43 -42
  267. package/dist/hooks/session-end.js +29 -28
  268. package/dist/hooks/session-start.js +18 -18
  269. package/dist/hooks/stop.js +27 -26
  270. package/dist/hooks/subagent-stop.js +28 -27
  271. package/dist/hooks/summary-worker.js +27 -27
  272. package/dist/index.js +31 -31
  273. package/dist/{installer-ADGJLDVY.js → installer-ILYC4LI5.js} +9 -8
  274. package/dist/{installer-HCFVVQ6F.js → installer-MPVQ5L22.js} +9 -8
  275. package/dist/{installer-ZNP4S32U.js → installer-XEEZSF3V.js} +9 -8
  276. package/dist/{intercom-queue-NRECOZFL.js → intercom-queue-QGHAW2KO.js} +18 -18
  277. package/dist/lib/agent-config.js +2 -2
  278. package/dist/lib/cloud-sync.js +17 -17
  279. package/dist/lib/consolidation.js +10 -10
  280. package/dist/lib/crypto.js +3 -3
  281. package/dist/lib/database.js +3 -3
  282. package/dist/lib/db-backup.js +1 -1
  283. package/dist/lib/db-write-worker.js +3 -3
  284. package/dist/lib/db.js +3 -3
  285. package/dist/lib/device-registry.js +1 -1
  286. package/dist/lib/employee-templates.js +7 -7
  287. package/dist/lib/employees.js +3 -3
  288. package/dist/lib/exe-daemon.js +90 -90
  289. package/dist/lib/hybrid-search.js +10 -10
  290. package/dist/lib/identity.js +3 -3
  291. package/dist/lib/keychain.js +1 -1
  292. package/dist/lib/license.js +2 -2
  293. package/dist/lib/messaging.js +19 -19
  294. package/dist/lib/post-tool-memory.js +46 -7
  295. package/dist/lib/registry-proxy.js +1 -1
  296. package/dist/lib/reminders.js +4 -4
  297. package/dist/lib/runtime-table.js +1 -1
  298. package/dist/lib/schedules.js +9 -9
  299. package/dist/lib/session-registry.js +5 -5
  300. package/dist/lib/skill-learning.js +10 -10
  301. package/dist/lib/store.js +8 -8
  302. package/dist/lib/task-router.js +4 -4
  303. package/dist/lib/tasks.js +20 -20
  304. package/dist/lib/tmux-routing.js +18 -18
  305. package/dist/lib/token-spend.js +4 -4
  306. package/dist/lib/ws-client.js +1 -1
  307. package/dist/{license-gate-CB2OCT2G.js → license-gate-CARN6AB6.js} +3 -3
  308. package/dist/{license-recheck-ZAEPWMY7.js → license-recheck-7Z3LS6YS.js} +1 -1
  309. package/dist/{license-sync-UW4UH7F5.js → license-sync-PUQV3HSZ.js} +2 -2
  310. package/dist/{local-secret-inventory-2DVUEFKD.js → local-secret-inventory-5J7P24ZR.js} +2 -2
  311. package/dist/mcp/register-tools.js +103 -102
  312. package/dist/mcp/server.js +103 -102
  313. package/dist/mcp/tools/complete-reminder.js +5 -5
  314. package/dist/mcp/tools/create-reminder.js +5 -5
  315. package/dist/mcp/tools/create-task.js +24 -23
  316. package/dist/mcp/tools/deactivate-behavior.js +13 -12
  317. package/dist/mcp/tools/list-reminders.js +5 -5
  318. package/dist/mcp/tools/list-tasks.js +24 -23
  319. package/dist/mcp/tools/send-message.js +23 -22
  320. package/dist/mcp/tools/update-task.js +21 -21
  321. package/dist/{mcp-http-config-IPXCT6Y5.js → mcp-http-config-RJKTZPHZ.js} +7 -6
  322. package/dist/{mcp-session-identity-GRKQ6X6M.js → mcp-session-identity-HN444A3G.js} +18 -18
  323. package/dist/{memory-cards-JN32U2KA.js → memory-cards-AT3QCCWU.js} +8 -8
  324. package/dist/{memory-graph-extractor-3LN2GSMZ.js → memory-graph-extractor-B43UWGV5.js} +8 -8
  325. package/dist/{memory-maintenance-YTNSNHD5.js → memory-maintenance-WDLL3ZX3.js} +2 -2
  326. package/dist/{memory-poisoning-defense-ISKHR76K.js → memory-poisoning-defense-TS4WNW4S.js} +3 -3
  327. package/dist/{memory-pressure-watchdog-P4QHXE3Z.js → memory-pressure-watchdog-6YH7S6W7.js} +19 -19
  328. package/dist/{memory-reflection-R6TAIUOR.js → memory-reflection-N6NSYW6S.js} +8 -8
  329. package/dist/{memory-write-repository-JTJE3AG4.js → memory-write-repository-D35RDO62.js} +1 -1
  330. package/dist/{monitor-grants-KX5J6AQ7.js → monitor-grants-FH4PNYN5.js} +3 -3
  331. package/dist/{monitor-identity-WXIGBLYH.js → monitor-identity-LK5ZCCND.js} +2 -2
  332. package/dist/{notification-ledger-DKHZKGZX.js → notification-ledger-3WJYJGGS.js} +19 -19
  333. package/dist/{notifications-ZQVBRXG5.js → notifications-T5C2CVB3.js} +18 -18
  334. package/dist/{orchestration-events-B3OHNBR4.js → orchestration-events-73VEJT7Q.js} +4 -4
  335. package/dist/{orchestrator-OG6BEUHS.js → orchestrator-3O657734.js} +20 -20
  336. package/dist/{org-brief-session-MHDLBUO4.js → org-brief-session-EGB35QLM.js} +15 -15
  337. package/dist/{org-consent-policy-OP5L627O.js → org-consent-policy-MSVSRI53.js} +5 -5
  338. package/dist/{org-join-H3PBH7US.js → org-join-4BK2XJJI.js} +9 -9
  339. package/dist/{org-key-admin-UEDWV3F4.js → org-key-admin-EUSSGXQ3.js} +11 -11
  340. package/dist/{org-key-epoch-BW72L7RI.js → org-key-epoch-NMEWDT5L.js} +5 -5
  341. package/dist/{org-key-pin-XBLJOF4A.js → org-key-pin-HUSXGINB.js} +3 -3
  342. package/dist/{org-membership-UHVIK3UH.js → org-membership-B74IWEM5.js} +4 -4
  343. package/dist/{org-partner-config-G3BGVBQT.js → org-partner-config-5FJTJABN.js} +8 -8
  344. package/dist/{org-partner-runtime-H6MROHM7.js → org-partner-runtime-GN4GFWI2.js} +15 -15
  345. package/dist/{org-row-visibility-JK5REXVH.js → org-row-visibility-PBZ2TSCR.js} +1 -1
  346. package/dist/{pipeline-router-7L7AV55A.js → pipeline-router-2IELBSVJ.js} +4 -4
  347. package/dist/{plan-limits-RKGJXYKE.js → plan-limits-4GXK5PFT.js} +6 -6
  348. package/dist/{platform-procedures-RTD5E3BD.js → platform-procedures-QP3W2NLH.js} +1 -1
  349. package/dist/{pre-tool-use-impl-Q3JG5RNT.js → pre-tool-use-impl-CY73PRJP.js} +42 -42
  350. package/dist/{project-name-KQUBSY5O.js → project-name-337ERIAX.js} +4 -4
  351. package/dist/{projection-worker-LGELT4E6.js → projection-worker-W476IG6V.js} +2 -2
  352. package/dist/{prospective-memory-ZESC2I6H.js → prospective-memory-5INB7NUL.js} +3 -3
  353. package/dist/{push-notifications-YL2DRTGQ.js → push-notifications-YVOP4KGN.js} +19 -19
  354. package/dist/{reproject-worktree-memories-VPZRYPQM.js → reproject-worktree-memories-OGBR3CMO.js} +18 -18
  355. package/dist/{reranker-XC2LXYD3.js → reranker-W7WCJH6Y.js} +1 -1
  356. package/dist/{retrieval-health-H4QJN3UZ.js → retrieval-health-R6MGXLTP.js} +1 -1
  357. package/dist/{review-polling-67WTTJHH.js → review-polling-TKAWVY6G.js} +19 -19
  358. package/dist/runtime/index.js +23 -23
  359. package/dist/{session-events-7XFO27M5.js → session-events-HUCZ7TOR.js} +19 -19
  360. package/dist/{session-kill-telemetry-2F5INRA6.js → session-kill-telemetry-RWRWOW4C.js} +19 -19
  361. package/dist/{session-scope-GEXQAEYT.js → session-scope-5CDRHV75.js} +18 -18
  362. package/dist/{setup-wizard-NQI6GC2N.js → setup-wizard-RBTJAZOP.js} +3 -3
  363. package/dist/{shard-manager-TJ323N7P.js → shard-manager-LRPAAQA4.js} +2 -2
  364. package/dist/{skill-refinement-EQ3YVAGM.js → skill-refinement-MDXKDCY6.js} +4 -4
  365. package/dist/{skill-retrieval-LABSBPVT.js → skill-retrieval-OWY6MTCX.js} +12 -12
  366. package/dist/{skill-usage-WNCOURZY.js → skill-usage-GNV737U5.js} +4 -4
  367. package/dist/{stack-generate-manifest-RKQBTLEY.js → stack-generate-manifest-S4SBLKBY.js} +4 -4
  368. package/dist/{stack-promote-ZVOIMFRF.js → stack-promote-U4EZNLOV.js} +4 -4
  369. package/dist/{stack-release-ZCHLWJXF.js → stack-release-4VBGOYGK.js} +5 -5
  370. package/dist/{stack-update-AP6OC7LU.js → stack-update-C5YKHE7A.js} +3 -3
  371. package/dist/{steward-gate-BNI4K3GP.js → steward-gate-QFPXQBUZ.js} +4 -4
  372. package/dist/{subagent-brief-injection-ZXKMB5TZ.js → subagent-brief-injection-GDXO5WNS.js} +33 -32
  373. package/dist/{subagent-spawn-ledger-OI23OT4I.js → subagent-spawn-ledger-PDICC43I.js} +2 -1
  374. package/dist/{support-bug-actions-OYM3K4RU.js → support-bug-actions-4XEKXIIO.js} +4 -4
  375. package/dist/{support-inbox-2JOCSJUT.js → support-inbox-Y7AFCOGM.js} +17 -16
  376. package/dist/{support-outbox-6AE2NQTA.js → support-outbox-2WYS3HWR.js} +2 -2
  377. package/dist/{task-enforcement-LUXQXSE6.js → task-enforcement-CN3Q4PEH.js} +19 -19
  378. package/dist/{task-request-inbox-4TOOJ6IR.js → task-request-inbox-AY3C6DND.js} +5 -5
  379. package/dist/{task-scope-OEA6KUTX.js → task-scope-AMR3Z3DJ.js} +18 -18
  380. package/dist/{tasks-crud-GBNROE5E.js → tasks-crud-2GHKIZOM.js} +18 -18
  381. package/dist/{tasks-notify-FMSTX6Q5.js → tasks-notify-Z53446KM.js} +19 -19
  382. package/dist/{tasks-review-EAOT3SKK.js → tasks-review-APRNSHMQ.js} +18 -18
  383. package/dist/{telemetry-upload-OFNKPITF.js → telemetry-upload-5XR42SQN.js} +8 -8
  384. package/dist/{token-budget-7K5PMPP2.js → token-budget-TTNJXI7W.js} +4 -4
  385. package/dist/{tool-telemetry-HC7NHNWD.js → tool-telemetry-36ST422N.js} +1 -1
  386. package/dist/tui/App.js +30 -30
  387. package/dist/{tui-data-C6STFM4Z.js → tui-data-AP5TPUOQ.js} +22 -22
  388. package/dist/{update-support-KOJIIKHY.js → update-support-SFTI2PUD.js} +15 -14
  389. package/dist/{whatsapp-JDAZGYPN.js → whatsapp-5CXE3JHG.js} +2 -2
  390. package/dist/{wiki-acl-7JCCDS7U.js → wiki-acl-N754BC2W.js} +1 -1
  391. package/dist/{work-claims-MDJ3JEUD.js → work-claims-OAICQBWO.js} +4 -4
  392. package/dist/{work-state-heartbeat-GFPR5OVW.js → work-state-heartbeat-MTTUGMPU.js} +1 -1
  393. package/dist/{workflow-engine-YYR2DNFP.js → workflow-engine-RID3QL2U.js} +2 -2
  394. package/dist/{worktree-XJITSSNN.js → worktree-XWVNDBRD.js} +5 -5
  395. package/dist/{worktree-sweep-3FJSAGNK.js → worktree-sweep-6VEHGH7U.js} +6 -6
  396. package/package.json +6 -5
  397. package/release-notes.json +9 -10
  398. package/stack.release.json +3 -3
  399. package/dist/{chunk-ZWQWLUZA.js → chunk-33PG3KNY.js} +0 -0
  400. package/dist/{chunk-MY6P4E5J.js → chunk-64UAHPXA.js} +0 -0
  401. package/dist/{chunk-7GCPTNHK.js → chunk-CLV7ALXV.js} +0 -0
  402. package/dist/{chunk-7QIEF6OZ.js → chunk-EZQ4W3WW.js} +0 -0
  403. package/dist/{chunk-DHRIW7RJ.js → chunk-FLO24BNN.js} +0 -0
  404. package/dist/{chunk-USDMXIPZ.js → chunk-FUZHMV7K.js} +0 -0
  405. package/dist/{chunk-E5RCYOLD.js → chunk-LDHJX4TZ.js} +0 -0
  406. package/dist/{chunk-3NHFXUWK.js → chunk-OEEHLATI.js} +0 -0
  407. package/dist/{chunk-HEEKYE6D.js → chunk-OLMLA377.js} +0 -0
  408. package/dist/{chunk-QYCRJ76J.js → chunk-WIDOLS7T.js} +0 -0
  409. package/dist/{chunk-FLC2S4BD.js → chunk-YBB43SXC.js} +0 -0
  410. package/dist/{chunk-GVTIDG37.js → chunk-YG4DZZ6J.js} +0 -0
  411. package/dist/{core-memory-DQ5A24DD.js → core-memory-WR7I7DIP.js} +0 -0
  412. package/dist/{hook-tamper-log-ARGWCXKJ.js → hook-tamper-log-HTWEP37U.js} +3 -3
  413. /package/dist/{memory-isolation-2M3GFNWG.js → memory-isolation-4RQJ5RCK.js} +0 -0
  414. /package/dist/{oauth-server-NWVJ26NL.js → oauth-server-VBDMZIEV.js} +0 -0
  415. /package/dist/{project-boot-JAGN65MZ.js → project-boot-G7T5WHO6.js} +0 -0
@@ -7,17 +7,17 @@ import {
7
7
  supportProductSchema,
8
8
  supportTaskIdSchema,
9
9
  supportVersionSchema
10
- } from "./chunk-36QUBTID.js";
10
+ } from "./chunk-HK7HIDXP.js";
11
11
  import {
12
12
  queueSupportStatusUpdateSync
13
- } from "./chunk-USZHUBHT.js";
13
+ } from "./chunk-L4ISXVC5.js";
14
14
  import {
15
15
  assessAttribution,
16
16
  assessDisposition,
17
17
  buildVerificationArtifact,
18
18
  isPlaceholderIdentity,
19
19
  isUnresolvedIdentity
20
- } from "./chunk-RGPEUXOE.js";
20
+ } from "./chunk-4MK6LOD6.js";
21
21
  import {
22
22
  SUPPORT_REPORT_SENT_MARKER
23
23
  } from "./chunk-MMRUBN3I.js";
@@ -34,14 +34,14 @@ import {
34
34
  sanitizeUpstreamId,
35
35
  sanitizeVersionField,
36
36
  sanitizedUpstreamBody
37
- } from "./chunk-T4IDLAZM.js";
37
+ } from "./chunk-BYP3DOUU.js";
38
38
  import {
39
39
  getActiveAgent
40
- } from "./chunk-FEJK36FV.js";
40
+ } from "./chunk-OISOA6NX.js";
41
41
  import {
42
42
  loadLicense,
43
43
  readCachedLicenseToken
44
- } from "./chunk-QHQW3VHV.js";
44
+ } from "./chunk-DIKLSAJE.js";
45
45
  import {
46
46
  EXE_AI_DIR,
47
47
  loadConfig
@@ -51,8 +51,8 @@ import {
51
51
  import { z as z3 } from "zod";
52
52
  import { readFileSync } from "fs";
53
53
  import { readFile as readFile2 } from "fs/promises";
54
- import { join } from "path";
55
- import { homedir } from "os";
54
+ import { join as join2 } from "path";
55
+ import { homedir as homedir2 } from "os";
56
56
 
57
57
  // src/mcp/tools/update-bug-report.ts
58
58
  import { z as z2 } from "zod";
@@ -120,6 +120,7 @@ function resolveRef(ref, cwd) {
120
120
  }
121
121
  }
122
122
  var MODEL_HELP = "Set ANTHROPIC_MODEL (employee sessions get it automatically at launch) or pass attribution_model explicitly. It is NOT guessed: a wrong model on the record is worse than no record at all, because it points at the wrong suspect.";
123
+ var ACCOUNTABILITY_PURPOSES = /* @__PURE__ */ new Set(["verdict", "routing_verdict"]);
123
124
  function resolveAttribution(purpose, input = {}) {
124
125
  const stated = input.agent?.trim() ?? "";
125
126
  let observed = "";
@@ -129,24 +130,24 @@ function resolveAttribution(purpose, input = {}) {
129
130
  observed = "";
130
131
  }
131
132
  let agent;
132
- if (purpose === "verdict") {
133
+ if (ACCOUNTABILITY_PURPOSES.has(purpose)) {
133
134
  const observedIsReal = observed !== "" && !isUnresolvedIdentity(observed);
134
135
  if (observedIsReal && stated && stated.toLowerCase() !== observed.toLowerCase()) {
135
136
  return {
136
137
  ok: false,
137
138
  code: "attribution_agent_conflict",
138
- message: `Refused locally [attribution_agent_conflict]: attribution_agent was given as "${stated}", but this session resolves to "${observed}". A verdict is signed by whoever actually made it \u2014 attribution_agent is a fallback for a session that cannot observe its own identity (the MCP daemon), not a way to record the call under another employee's name. Omit attribution_agent to sign as "${observed}", or make the call from that employee's session.`
139
+ message: `Refused locally [attribution_agent_conflict]: attribution_agent was given as "${stated}", but this session resolves to "${observed}". A ${purpose === "verdict" ? "verdict" : "close"} is signed by whoever actually made it \u2014 attribution_agent is a fallback for a session that cannot observe its own identity (the MCP daemon), not a way to record the call under another employee's name. Omit attribution_agent to sign as "${observed}", or make the call from that employee's session.`
139
140
  };
140
141
  }
141
142
  agent = observedIsReal ? observed : stated;
142
143
  } else {
143
144
  agent = stated || observed;
144
145
  }
145
- if (purpose === "verdict" && isUnresolvedIdentity(agent)) {
146
+ if (ACCOUNTABILITY_PURPOSES.has(purpose) && isUnresolvedIdentity(agent)) {
146
147
  agent = "";
147
148
  }
148
149
  if (!agent) {
149
- const message = purpose === "verdict" ? "Refused locally [attribution_agent_missing]: could not resolve which agent is producing this verdict, and it will not be invented. The MCP tools run in the launchd daemon, which carries no active-agent marker, no tmux session and no AGENT_ID, so the session identity is not observable from there. Pass attribution_agent with your own employee name exactly as it appears in the roster \u2014 never a role like 'admin'." : `Refused locally [attribution_agent_missing]: could not resolve which agent is producing this ${purpose}, and it will not be invented. Restore the active-agent marker or set AGENT_ID to your real employee name.`;
150
+ const message = ACCOUNTABILITY_PURPOSES.has(purpose) ? `Refused locally [attribution_agent_missing]: could not resolve which agent is producing this ${purpose === "verdict" ? "verdict" : "close"}, and it will not be invented. The MCP tools run in the launchd daemon, which carries no active-agent marker, no tmux session and no AGENT_ID, so the session identity is not observable from there. Pass attribution_agent with your own employee name exactly as it appears in the roster \u2014 never a role like 'admin'.` : `Refused locally [attribution_agent_missing]: could not resolve which agent is producing this ${purpose}, and it will not be invented. Restore the active-agent marker or set AGENT_ID to your real employee name.`;
150
151
  return {
151
152
  ok: false,
152
153
  code: "attribution_agent_missing",
@@ -225,11 +226,14 @@ var verificationArtifactInputSchema = {
225
226
  parent_result: z.enum(["fail", "pass", "error"]).optional().describe("regression_test only: the result you OBSERVED running that test at the parent commit. Must be 'fail' \u2014 a test that already passed does not detect the defect."),
226
227
  fix_result: z.enum(["fail", "pass", "error"]).optional().describe("regression_test only: the result you OBSERVED running that test at the fix commit. Must be 'pass'."),
227
228
  verifier: z.string().optional().describe("manual and ops_transcript: the identity of the person or employee who confirmed the defect is gone. Checked server-side against the bug's filer and assignee \u2014 it may not be either. Placeholders ('me', 'agent') are refused."),
228
- disposition: z.enum(["fixed", "not_a_defect", "superseded", "wontfix"]).optional().describe(
229
- "WHY the record is closing. Omit (or 'fixed') for the normal case: a defect was repaired, and a verification artifact is required. Use 'not_a_defect' when there was nothing to fix (a smoke-test record, a misfiled report), 'superseded' when another record covers it, 'wontfix' when it is a deliberate decline. Those three require NO fix_commit but DO require disposition_reason \u2014 and they are recorded as closed, not as fixed, because asserting a repair that never happened is its own kind of false record."
229
+ disposition: z.enum(["fixed", "not_a_defect", "superseded", "wontfix", "migrated"]).optional().describe(
230
+ "WHY the record is closing. Omit (or 'fixed') for the normal case: a defect was repaired, and a verification artifact is required. Use 'not_a_defect' when there was nothing to fix (a smoke-test record, a misfiled report), 'superseded' when another record covers it, 'wontfix' when it is a deliberate decline, 'migrated' when the bug was moved to GitHub Issues (requires github_issue_url, NOT disposition_reason). All of these require NO fix_commit \u2014 and they are recorded as closed, not as fixed, because asserting a repair that never happened is its own kind of false record."
230
231
  ),
231
232
  disposition_reason: z.string().optional().describe(
232
- "REQUIRED for any disposition other than 'fixed': what makes this record a non-defect. Explain the call \u2014 a bare label or a hedge is refused."
233
+ "REQUIRED for any disposition other than 'fixed'/'migrated': what makes this record a non-defect. Explain the call \u2014 a bare label or a hedge is refused."
234
+ ),
235
+ github_issue_url: z.string().optional().describe(
236
+ "REQUIRED for disposition='migrated': the GitHub Issue this bug was moved to, https://github.com/AskExe/<repo>/issues/<n>. Also usable on its own (no status/disposition at all) to back-link an ALREADY fixed/closed record to its GitHub issue without changing its status."
233
237
  ),
234
238
  superseded_by: z.string().optional().describe("REQUIRED for disposition='superseded': the id of the record that replaces this one, so the claim is checkable."),
235
239
  superseded_coverage: z.string().optional().describe(
@@ -262,7 +266,8 @@ var verificationArtifactInputSchema = {
262
266
  delivery_proof: z.string().optional().describe('Optional JSON delivery declaration, e.g. {"channel":"npm","version":"0.9.364","tarball_shasum":"\u2026","fix_path_published":true}. Recorded, not required \u2014 delivery does not gate closing. A declaration that is present must still be true.')
263
267
  };
264
268
  function buildVerdictAttribution(input) {
265
- return resolveAttribution("verdict", {
269
+ const purpose = input.disposition === "migrated" ? "routing_verdict" : "verdict";
270
+ return resolveAttribution(purpose, {
266
271
  agent: input.attribution_agent,
267
272
  model: input.attribution_model,
268
273
  observed_at: input.observed_at,
@@ -295,6 +300,7 @@ var DEFERRAL_BLOCKING = {
295
300
  superseded_by: true,
296
301
  disposition_verifier: true,
297
302
  superseded_coverage: true,
303
+ github_issue_url: true,
298
304
  // Attribution inputs — the client turns these into `attribution`, which it likewise
299
305
  // does not build on the deferral branch.
300
306
  attribution_agent: true,
@@ -403,6 +409,7 @@ function resolveVerificationArtifact(status, input, context = {}) {
403
409
  superseded_by: input.superseded_by,
404
410
  disposition_verifier: input.disposition_verifier,
405
411
  superseded_coverage: input.superseded_coverage,
412
+ github_issue_url: input.github_issue_url,
406
413
  symptoms: input.symptoms,
407
414
  context
408
415
  });
@@ -705,7 +712,7 @@ function registerUpdateBugReport(server) {
705
712
  content: [{ type: "text", text: describeOversizeRefusal(oversize) }]
706
713
  };
707
714
  }
708
- const attestsByIdentity = verification.verification_kind === "manual" || verification.verification_kind === "ops_transcript" || verification.disposition !== void 0 && verification.disposition !== "fixed";
715
+ const attestsByIdentity = verification.verification_kind === "manual" || verification.verification_kind === "ops_transcript" || verification.disposition !== void 0 && verification.disposition !== "fixed" && verification.disposition !== "migrated";
709
716
  let resolvedVerification = resolveVerificationArtifact(gatedStatus, verification, { sendUpstream: send_upstream });
710
717
  if (resolvedVerification.ok && isVerifiedStatus(gatedStatus) && attestsByIdentity) {
711
718
  const authorIdentities = await collectBugAuthorIdentities(id);
@@ -797,6 +804,7 @@ These do not match \u2014 not updating to avoid mutating the wrong bug. Verify t
797
804
  if (verification.superseded_by) payload.superseded_by = verification.superseded_by;
798
805
  if (verification.disposition_verifier) payload.disposition_verifier = verification.disposition_verifier;
799
806
  if (verification.superseded_coverage) payload.superseded_coverage = verification.superseded_coverage;
807
+ if (verification.github_issue_url) payload.github_issue_url = verification.github_issue_url;
800
808
  if (verification.symptoms) payload.symptoms = verification.symptoms;
801
809
  if (resolvedVerification.attribution) payload.attribution = resolvedVerification.attribution;
802
810
  } else {
@@ -932,6 +940,487 @@ Upstream status: ${upstreamStatus}`
932
940
  );
933
941
  }
934
942
 
943
+ // src/lib/support-github-issue.ts
944
+ import { execFile } from "child_process";
945
+ import { closeSync, mkdirSync, openSync, statSync, unlinkSync, writeSync } from "fs";
946
+ import { randomUUID } from "crypto";
947
+ import { homedir } from "os";
948
+ import { join } from "path";
949
+ var GITHUB_ORG = "AskExe";
950
+ var PRODUCT_REPO_ROUTE = {
951
+ "exe-os": "exe-os",
952
+ "exe-create": "exe-create",
953
+ "exe-wiki": "exe-wiki",
954
+ "exe-gateway": "exe-gateway",
955
+ "exe-build": "exe-build",
956
+ "exe-chat": "exe-chat",
957
+ "exe-auth": "exe-auth",
958
+ "exe-monitor": "exe-monitor",
959
+ "exe-db": "exe-db",
960
+ "exe-crm": "exe-infra",
961
+ "exe-erp": "exe-infra"
962
+ };
963
+ var EXE_INFRA_REPO = `${GITHUB_ORG}/exe-infra`;
964
+ function routeRepoForProduct(product) {
965
+ if (!product) return null;
966
+ const repo = PRODUCT_REPO_ROUTE[product];
967
+ return repo ? `${GITHUB_ORG}/${repo}` : null;
968
+ }
969
+ var EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
970
+ function scrubEmail(value) {
971
+ return value == null ? "" : String(value).replace(EMAIL_RE, "[redacted-email]");
972
+ }
973
+ function sanitizeIssueText(value) {
974
+ const raw = value == null ? "" : String(value);
975
+ const { text } = redactSecrets(raw);
976
+ return neutralizeHtmlComments(scrubEmail(text));
977
+ }
978
+ function neutralizeHtmlComments(value) {
979
+ return value.replaceAll("<!--", "&lt;!--").replaceAll("-->", "--&gt;");
980
+ }
981
+ function neutralizeBody(body, repo, product) {
982
+ const parts = body.split(/(```[\s\S]*?```|`[^`\n]*`)/g);
983
+ return parts.map((seg, i) => {
984
+ if (i % 2 === 1) return seg;
985
+ let out = seg.replace(/(^|[^\w`/.])@([A-Za-z0-9][A-Za-z0-9-]{0,38})/g, (_m, pre, name) => `${pre}\`@${name}\``);
986
+ if (repo === EXE_INFRA_REPO && product) {
987
+ out = out.replace(/(^|[^\w/])#(\d+)\b/g, (_m, pre, n) => `${pre}${GITHUB_ORG}/${product}#${n}`);
988
+ }
989
+ return out;
990
+ }).join("");
991
+ }
992
+ function truncateTitle(title) {
993
+ const t = String(title || "Untitled bug report").trim();
994
+ return t.length > 250 ? `${t.slice(0, 249)}\u2026` : t;
995
+ }
996
+ function fmt(d) {
997
+ return d ? String(d).replace("T", " ").replace(/\.\d+Z$/, " UTC") : "\u2014";
998
+ }
999
+ function issueMarker(bugId) {
1000
+ return `<!-- exe-support-bug-id: ${bugId} -->`;
1001
+ }
1002
+ function bodyOwnedBy(body, bugId) {
1003
+ return typeof body === "string" && body.trimEnd().endsWith(issueMarker(bugId));
1004
+ }
1005
+ function cell(v) {
1006
+ const s = sanitizeIssueText(v == null ? "" : String(v)).replace(/\|/g, "\\|").replace(/\r?\n/g, " ").trim();
1007
+ return s || "\u2014";
1008
+ }
1009
+ function jsonArray(v) {
1010
+ if (Array.isArray(v)) return v;
1011
+ if (typeof v !== "string" || !v.trim()) return [];
1012
+ try {
1013
+ const parsed = JSON.parse(v);
1014
+ return Array.isArray(parsed) ? parsed : [];
1015
+ } catch {
1016
+ return [v];
1017
+ }
1018
+ }
1019
+ function entryText(e) {
1020
+ if (e == null) return "";
1021
+ if (typeof e === "string") return e;
1022
+ if (typeof e === "object") {
1023
+ const o = e;
1024
+ const when = typeof o.at === "string" ? o.at : typeof o.timestamp === "string" ? o.timestamp : "";
1025
+ const text = typeof o.note === "string" ? o.note : typeof o.text === "string" ? o.text : JSON.stringify(o);
1026
+ return when ? `${when}: ${text}` : text;
1027
+ }
1028
+ return String(e);
1029
+ }
1030
+ function section(L, heading, value) {
1031
+ const text = sanitizeIssueText(value == null ? "" : String(value)).trim();
1032
+ if (text) L.push(`### ${heading}`, "", "```", text, "```", "");
1033
+ }
1034
+ function persistedIssueNumber(url, repo) {
1035
+ if (typeof url !== "string") return null;
1036
+ const m = /^https:\/\/github\.com\/([A-Za-z0-9._-]+\/[A-Za-z0-9._-]+)\/issues\/([1-9][0-9]*)$/.exec(url);
1037
+ if (!m || m[1] !== repo) return null;
1038
+ return Number(m[2]);
1039
+ }
1040
+ function renderIssueBody(bug) {
1041
+ const L = [];
1042
+ L.push("## Summary", "", sanitizeIssueText(bug.summary) || "_No summary recorded._", "");
1043
+ L.push(`Reported via exe-support (${cell(bug.license_plan || "unknown")} plan)`, "");
1044
+ pushFactsTable(L, bug);
1045
+ pushEvidenceSections(L, bug);
1046
+ L.push("---", "", `Migrated from exe-support bug ${bug.id}`, "", issueMarker(bug.id));
1047
+ return L.join("\n");
1048
+ }
1049
+ async function recheckCanonical(repo, canonical, bug) {
1050
+ const view = JSON.parse(await gh(["issue", "view", String(canonical.number), "-R", repo, "--json", "state"]) || "{}");
1051
+ const state = String(view.state ?? "").toUpperCase();
1052
+ if (state !== "OPEN" && state !== "CLOSED") {
1053
+ throw new Error(`Could not verify GitHub issue ${repo}#${canonical.number} before migrating (state: ${state || "unknown"}). The support record was left untouched.`);
1054
+ }
1055
+ if (state !== "CLOSED") return canonical;
1056
+ const fresh = await searchMarkerIssues(repo, bug.id);
1057
+ const others = fresh.filter((i) => i.number !== canonical.number);
1058
+ if (others.length === 0) {
1059
+ await gh(["issue", "reopen", String(canonical.number), "-R", repo]);
1060
+ return { ...canonical, state: "OPEN" };
1061
+ }
1062
+ const survivor = await reconcileMarkerIssues(repo, others);
1063
+ await syncTriageToIssue(repo, survivor, bug);
1064
+ return survivor;
1065
+ }
1066
+ function pushFactsTable(L, bug) {
1067
+ L.push("### Facts", "", "| Field | Value |", "| --- | --- |");
1068
+ L.push(`| Severity | ${cell(bug.severity)} |`);
1069
+ L.push(`| Product | ${cell(bug.product)} |`);
1070
+ L.push(`| Classification | ${cell(bug.classification)} |`);
1071
+ L.push(`| Cluster | ${cell(bug.cluster)} |`);
1072
+ L.push(`| Reported | ${cell(fmt(bug.created_at))} |`);
1073
+ L.push(`| Triaged | ${cell(fmt(bug.triaged_at))} |`);
1074
+ L.push(`| Fixed version | ${cell(bug.fixed_version)} |`);
1075
+ L.push(`| Package version | ${cell(bug.package_version)} |`);
1076
+ L.push("");
1077
+ }
1078
+ var MANAGED_LABEL_PREFIXES = ["cluster:", "severity:", "product:"];
1079
+ async function syncLabels(repo, issueNumber, bug) {
1080
+ const want = buildLabels(bug, repo);
1081
+ const view = JSON.parse(await gh(["issue", "view", String(issueNumber), "-R", repo, "--json", "labels"]) || "{}");
1082
+ const have = (view.labels ?? []).map((l) => l.name ?? "").filter(Boolean);
1083
+ const add = want.filter((l) => !have.includes(l));
1084
+ const remove = have.filter((l) => MANAGED_LABEL_PREFIXES.some((p) => l.startsWith(p)) && !want.includes(l));
1085
+ if (add.length === 0 && remove.length === 0) return;
1086
+ if (add.length) await ensureLabelsExist(repo, add);
1087
+ const args = ["issue", "edit", String(issueNumber), "-R", repo];
1088
+ if (add.length) args.push("--add-label", add.join(","));
1089
+ if (remove.length) args.push("--remove-label", remove.join(","));
1090
+ await gh(args);
1091
+ }
1092
+ function pushEvidenceSections(L, bug) {
1093
+ if (bug.observed_kind || bug.evidence_pointer) {
1094
+ section(L, "Filing evidence", `${bug.observed_kind ? `Kind: ${bug.observed_kind}
1095
+ ` : ""}${bug.evidence_pointer ? `Pointer: ${bug.evidence_pointer}` : ""}`);
1096
+ }
1097
+ section(L, "Customer impact", bug.customer_impact);
1098
+ section(L, "Local patch supplied by the reporter", bug.local_patch_diff);
1099
+ const steps = jsonArray(bug.reproduction_steps).map(entryText).filter((s) => s.trim());
1100
+ if (steps.length) {
1101
+ L.push("### Reproduction steps", "", "```", sanitizeIssueText(steps.map((s, i) => `${i + 1}. ${s}`).join("\n")), "```", "");
1102
+ }
1103
+ section(L, "Expected", bug.expected);
1104
+ section(L, "Actual", bug.actual);
1105
+ section(L, "Workaround", bug.workaround);
1106
+ section(L, "Triage notes", bug.triage_notes);
1107
+ const progress = jsonArray(bug.progress_log).map(entryText).filter((s) => s.trim());
1108
+ if (progress.length) section(L, "Progress log", progress.join("\n"));
1109
+ section(L, "Triage note (this triage)", bug.add_note);
1110
+ }
1111
+ function buildLabels(bug, repo) {
1112
+ const labels = ["bug", "from:exe-support", `severity:${bug.severity || "p3"}`, "status:triaged"];
1113
+ if (bug.cluster) labels.push(`cluster:${bug.cluster}`);
1114
+ if (repo === EXE_INFRA_REPO && bug.product) labels.push(`product:${bug.product}`);
1115
+ return labels;
1116
+ }
1117
+ var GithubCliUnavailableError = class extends Error {
1118
+ constructor(message) {
1119
+ super(message);
1120
+ this.name = "GithubCliUnavailableError";
1121
+ }
1122
+ };
1123
+ function execGh(args, stdin) {
1124
+ return new Promise((resolve, reject) => {
1125
+ const child = execFile(
1126
+ "gh",
1127
+ args,
1128
+ { maxBuffer: 16 * 1024 * 1024 },
1129
+ (err, stdout, stderr) => {
1130
+ if (err) {
1131
+ const e = err;
1132
+ e.stdout = stdout;
1133
+ e.stderr = stderr;
1134
+ reject(e);
1135
+ return;
1136
+ }
1137
+ resolve(stdout);
1138
+ }
1139
+ );
1140
+ child.stdin?.on?.("error", (streamErr) => {
1141
+ const e = streamErr;
1142
+ e.message = `gh stdin write failed: ${streamErr.message}`;
1143
+ reject(e);
1144
+ });
1145
+ if (stdin !== void 0) child.stdin?.end(stdin);
1146
+ else child.stdin?.end();
1147
+ });
1148
+ }
1149
+ async function gh(args, stdin) {
1150
+ try {
1151
+ return await execGh(args, stdin);
1152
+ } catch (err) {
1153
+ const e = err;
1154
+ if (e.code === "ENOENT") {
1155
+ throw new GithubCliUnavailableError(
1156
+ "GitHub CLI (`gh`) is not installed. Install it from https://cli.github.com to push triaged bugs to GitHub, or pass push_to_github: false to skip."
1157
+ );
1158
+ }
1159
+ const out = `${e.stderr || ""}${e.stdout || ""}`;
1160
+ if (/not logged into any github hosts|gh auth login|authentication required|http 401/i.test(out)) {
1161
+ throw new GithubCliUnavailableError(
1162
+ "GitHub CLI (`gh`) is not authenticated. Run `gh auth login`, or pass push_to_github: false to skip."
1163
+ );
1164
+ }
1165
+ throw new Error(`gh ${args[0] ?? ""} failed: ${(out || e.message || String(err)).slice(0, 2e3)}`);
1166
+ }
1167
+ }
1168
+ async function searchMarkerIssues(repo, bugId) {
1169
+ const out = await gh([
1170
+ "issue",
1171
+ "list",
1172
+ "-R",
1173
+ repo,
1174
+ "--state",
1175
+ "all",
1176
+ "--search",
1177
+ `"${bugId}" in:body`,
1178
+ "--json",
1179
+ "number,url,body,state",
1180
+ "--limit",
1181
+ "20"
1182
+ ]);
1183
+ let issues;
1184
+ try {
1185
+ issues = JSON.parse(out || "[]");
1186
+ } catch {
1187
+ issues = [];
1188
+ }
1189
+ return issues.filter((i) => bodyOwnedBy(i.body, bugId)).map((i) => ({ number: i.number, url: i.url, state: i.state }));
1190
+ }
1191
+ async function verifyPersistedIssueMarker(repo, number, url, bugId) {
1192
+ let out;
1193
+ try {
1194
+ out = await gh(["issue", "view", String(number), "-R", repo, "--json", "number,url,state,body"]);
1195
+ } catch (err) {
1196
+ process.stderr.write(
1197
+ `[support-github-issue] persisted github_issue_url ${url} (issue #${number} in ${repo}) could not be verified (${err instanceof Error ? err.message : String(err)}) \u2014 dropped as a reconciliation candidate.
1198
+ `
1199
+ );
1200
+ return null;
1201
+ }
1202
+ let parsed;
1203
+ try {
1204
+ parsed = JSON.parse(out);
1205
+ } catch {
1206
+ process.stderr.write(
1207
+ `[support-github-issue] persisted github_issue_url ${url} (issue #${number} in ${repo}) returned unparseable JSON from \`gh issue view\` \u2014 dropped as a reconciliation candidate.
1208
+ `
1209
+ );
1210
+ return null;
1211
+ }
1212
+ if (!bodyOwnedBy(parsed.body, bugId)) {
1213
+ process.stderr.write(
1214
+ `[support-github-issue] persisted github_issue_url ${url} does not carry bug ${bugId}'s exact marker as its final content \u2014 dropped as a reconciliation candidate (it may have been set to an unrelated issue, e.g. via an admin back-link).
1215
+ `
1216
+ );
1217
+ return null;
1218
+ }
1219
+ return { number: parsed.number, url: parsed.url, state: parsed.state };
1220
+ }
1221
+ async function closeDuplicateIssue(repo, number, canonicalNumber) {
1222
+ await gh([
1223
+ "issue",
1224
+ "close",
1225
+ String(number),
1226
+ "-R",
1227
+ repo,
1228
+ "--reason",
1229
+ "not planned",
1230
+ "--comment",
1231
+ `Duplicate of #${canonicalNumber} (concurrent triage)`
1232
+ ]);
1233
+ }
1234
+ async function reconcileMarkerIssues(repo, candidates) {
1235
+ const byNumber = /* @__PURE__ */ new Map();
1236
+ for (const candidate of candidates) if (!byNumber.has(candidate.number)) byNumber.set(candidate.number, candidate);
1237
+ const unique = [...byNumber.values()];
1238
+ const canonical = unique.reduce((lowest, hit) => hit.number < lowest.number ? hit : lowest, unique[0]);
1239
+ for (const candidate of unique) {
1240
+ if (candidate.number === canonical.number) continue;
1241
+ try {
1242
+ await closeDuplicateIssue(repo, candidate.number, canonical.number);
1243
+ } catch {
1244
+ }
1245
+ }
1246
+ return canonical;
1247
+ }
1248
+ async function createIssue(repo, title, body, labels) {
1249
+ const out = await gh(
1250
+ ["issue", "create", "-R", repo, "--title", title, "--body-file", "-", "--label", labels.join(",")],
1251
+ body
1252
+ );
1253
+ const url = out.trim().split("\n").filter(Boolean).pop() || "";
1254
+ const number = Number(url.match(/\/issues\/(\d+)/)?.[1]);
1255
+ if (!url || !Number.isFinite(number)) {
1256
+ throw new Error(`gh issue create returned an unexpected result: ${out.slice(0, 300)}`);
1257
+ }
1258
+ return { number, url, state: "OPEN" };
1259
+ }
1260
+ var knownRepoLabels = /* @__PURE__ */ new Map();
1261
+ var LABEL_COLORS = {
1262
+ bug: "d73a4a",
1263
+ "from:exe-support": "fbca04",
1264
+ "severity:p0": "b60205",
1265
+ "severity:p1": "d93f0b",
1266
+ "severity:p2": "fbca04",
1267
+ "severity:p3": "cccccc",
1268
+ "status:triaged": "1d76db",
1269
+ default: "5319e7"
1270
+ };
1271
+ function labelColor(label) {
1272
+ return LABEL_COLORS[label] ?? LABEL_COLORS.default;
1273
+ }
1274
+ function labelDescription(label) {
1275
+ if (label.startsWith("cluster:")) return "exe-support cluster";
1276
+ if (label.startsWith("product:")) return "exe-support product";
1277
+ return "created by exe-support triage";
1278
+ }
1279
+ async function listRepoLabels(repo) {
1280
+ const out = await gh(["label", "list", "-R", repo, "--limit", "1000", "--json", "name"]);
1281
+ let items;
1282
+ try {
1283
+ items = JSON.parse(out || "[]");
1284
+ } catch {
1285
+ items = [];
1286
+ }
1287
+ return new Set(items.map((i) => i.name));
1288
+ }
1289
+ async function ensureLabelsExist(repo, labels) {
1290
+ let known = knownRepoLabels.get(repo);
1291
+ if (!known) {
1292
+ known = await listRepoLabels(repo);
1293
+ knownRepoLabels.set(repo, known);
1294
+ }
1295
+ for (const label of labels) {
1296
+ if (known.has(label)) continue;
1297
+ try {
1298
+ await gh(["label", "create", label, "-R", repo, "--color", labelColor(label), "--description", labelDescription(label)]);
1299
+ } catch (err) {
1300
+ const message = err instanceof Error ? err.message : String(err);
1301
+ if (!/already exists/i.test(message)) throw err;
1302
+ }
1303
+ known.add(label);
1304
+ }
1305
+ }
1306
+ async function syncTriageToIssue(repo, canonical, bug) {
1307
+ const wasClosed = !!canonical.state && canonical.state.toUpperCase() === "CLOSED";
1308
+ if (wasClosed) await gh(["issue", "reopen", String(canonical.number), "-R", repo]);
1309
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1310
+ const summary = sanitizeIssueText(bug.summary) || "_No summary recorded._";
1311
+ const lines = [
1312
+ wasClosed ? `Re-triaged from exe-support on ${date} \u2014 reported again, reopening.` : `Re-triaged from exe-support on ${date} \u2014 updated triage details.`,
1313
+ "",
1314
+ "### Summary",
1315
+ "",
1316
+ summary
1317
+ ];
1318
+ lines.push("");
1319
+ pushFactsTable(lines, bug);
1320
+ pushEvidenceSections(lines, bug);
1321
+ const commentBody = neutralizeBody(lines.join("\n"), repo, bug.product ?? null);
1322
+ await syncLabels(repo, canonical.number, bug);
1323
+ await gh(["issue", "comment", String(canonical.number), "-R", repo, "--body-file", "-"], commentBody);
1324
+ }
1325
+ async function pushBugToGithub(bug) {
1326
+ const repo = routeRepoForProduct(bug.product);
1327
+ if (!repo) {
1328
+ throw new Error(
1329
+ `No GitHub route configured for product '${bug.product ?? "unknown"}'. Pass push_to_github: false to skip, or triage with a supported product.`
1330
+ );
1331
+ }
1332
+ const persistedNumber = persistedIssueNumber(bug.github_issue_url, repo);
1333
+ const persistedCandidate = persistedNumber !== null ? await verifyPersistedIssueMarker(repo, persistedNumber, bug.github_issue_url, bug.id) : null;
1334
+ const markerIssues = await searchMarkerIssues(repo, bug.id);
1335
+ const preCreateCandidates = persistedCandidate ? [persistedCandidate, ...markerIssues] : markerIssues;
1336
+ if (preCreateCandidates.length > 0) {
1337
+ const canonical2 = await reconcileMarkerIssues(repo, preCreateCandidates);
1338
+ await syncTriageToIssue(repo, canonical2, bug);
1339
+ const settled2 = await recheckCanonical(repo, canonical2, bug);
1340
+ return { issueUrl: settled2.url, issueNumber: settled2.number, repo, adopted: true };
1341
+ }
1342
+ const body = neutralizeBody(renderIssueBody(bug), repo, bug.product ?? null);
1343
+ const title = truncateTitle(sanitizeIssueText(bug.title || bug.summary));
1344
+ const labels = buildLabels(bug, repo);
1345
+ await ensureLabelsExist(repo, labels);
1346
+ const created = await createIssue(repo, title, body, labels);
1347
+ const postCreateMarkerIssues = await searchMarkerIssues(repo, bug.id);
1348
+ const canonical = await reconcileMarkerIssues(repo, [...postCreateMarkerIssues, created]);
1349
+ if (canonical.number !== created.number) await syncTriageToIssue(repo, canonical, bug);
1350
+ const settled = await recheckCanonical(repo, canonical, bug);
1351
+ return { issueUrl: settled.url, issueNumber: settled.number, repo, adopted: settled.number !== created.number };
1352
+ }
1353
+ function locksDir() {
1354
+ return join(process.env.EXE_OS_DIR ?? join(homedir(), ".exe-os"), "locks");
1355
+ }
1356
+ function lockPath(bugId) {
1357
+ const safe = bugId.replace(/[^A-Za-z0-9_-]/g, "_");
1358
+ return join(locksDir(), `support-github-${safe}.lock`);
1359
+ }
1360
+ var LOCK_WAIT_TIMEOUT_MS = 6e4;
1361
+ var LOCK_POLL_MS = 250;
1362
+ var GithubLockTimeoutError = class extends Error {
1363
+ constructor(message) {
1364
+ super(message);
1365
+ this.name = "GithubLockTimeoutError";
1366
+ }
1367
+ };
1368
+ function sleep(ms) {
1369
+ return new Promise((resolve) => setTimeout(resolve, ms));
1370
+ }
1371
+ async function acquireBugLock(bugId) {
1372
+ mkdirSync(locksDir(), { recursive: true });
1373
+ const path2 = lockPath(bugId);
1374
+ const deadline = Date.now() + LOCK_WAIT_TIMEOUT_MS;
1375
+ const myToken = `${process.pid}:${randomUUID()}`;
1376
+ for (; ; ) {
1377
+ try {
1378
+ const fd = openSync(path2, "wx");
1379
+ try {
1380
+ writeSync(fd, myToken);
1381
+ } finally {
1382
+ closeSync(fd);
1383
+ }
1384
+ return () => {
1385
+ try {
1386
+ unlinkSync(path2);
1387
+ } catch (err) {
1388
+ const e = err;
1389
+ if (e.code !== "ENOENT") {
1390
+ process.stderr.write(
1391
+ `[support-github-issue] could not release bug lock for bug ${bugId} at ${path2}: ${e.code ?? e.message}
1392
+ `
1393
+ );
1394
+ }
1395
+ }
1396
+ };
1397
+ } catch (err) {
1398
+ const e = err;
1399
+ if (e.code !== "EEXIST") throw err;
1400
+ if (Date.now() >= deadline) {
1401
+ let ageText;
1402
+ try {
1403
+ ageText = `${Math.round((Date.now() - statSync(path2).mtimeMs) / 1e3)}s old`;
1404
+ } catch {
1405
+ ageText = "age unknown";
1406
+ }
1407
+ throw new GithubLockTimeoutError(
1408
+ `Could not push bug ${bugId} to GitHub: the per-bug lock ${path2} (${ageText}) was still held after ${LOCK_WAIT_TIMEOUT_MS / 1e3}s. Either another triage call for this bug is in progress on this host \u2014 wait for it to finish and retry \u2014 or a previous call crashed and left its lock behind, in which case delete that file and retry. Nothing was changed here.`
1409
+ );
1410
+ }
1411
+ await sleep(Math.min(LOCK_POLL_MS, Math.max(0, deadline - Date.now())));
1412
+ }
1413
+ }
1414
+ }
1415
+ async function withBugGithubLock(bugId, fn) {
1416
+ const release = await acquireBugLock(bugId);
1417
+ try {
1418
+ return await fn();
1419
+ } finally {
1420
+ release();
1421
+ }
1422
+ }
1423
+
935
1424
  // src/mcp/tools/support-inbox.ts
936
1425
  var DEFAULT_ENDPOINT = "https://api.askexe.com/admin/support/bug-reports";
937
1426
  var DEFAULT_OWNER_ENDPOINT = "https://api.askexe.com/v1/support/bug-reports";
@@ -948,7 +1437,7 @@ function adminToken() {
948
1437
  if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN) return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN;
949
1438
  if (process.env.EXE_SUPPORT_ADMIN_TOKEN) return process.env.EXE_SUPPORT_ADMIN_TOKEN;
950
1439
  try {
951
- const configPath = join(process.env.EXE_OS_DIR ?? join(homedir(), ".exe-os"), "config.json");
1440
+ const configPath = join2(process.env.EXE_OS_DIR ?? join2(homedir2(), ".exe-os"), "config.json");
952
1441
  const config = JSON.parse(readFileSync(configPath, "utf-8"));
953
1442
  return config.support?.adminToken || config.support?.admin_token;
954
1443
  } catch {
@@ -962,7 +1451,7 @@ function ownerEndpoint() {
962
1451
  const routerUrl = process.env.API_ROUTER_URL?.replace(/\/+$/, "");
963
1452
  let configured;
964
1453
  try {
965
- const configPath = join(process.env.EXE_OS_DIR ?? join(homedir(), ".exe-os"), "config.json");
1454
+ const configPath = join2(process.env.EXE_OS_DIR ?? join2(homedir2(), ".exe-os"), "config.json");
966
1455
  const config = JSON.parse(readFileSync(configPath, "utf-8"));
967
1456
  configured = config.support?.bugReportEndpoint;
968
1457
  } catch {
@@ -973,7 +1462,7 @@ function ownerEndpoint() {
973
1462
  function ownerAuthHeaders() {
974
1463
  let cloudApiKey;
975
1464
  try {
976
- const configPath = join(process.env.EXE_OS_DIR ?? join(homedir(), ".exe-os"), "config.json");
1465
+ const configPath = join2(process.env.EXE_OS_DIR ?? join2(homedir2(), ".exe-os"), "config.json");
977
1466
  const config = JSON.parse(readFileSync(configPath, "utf-8"));
978
1467
  cloudApiKey = config.cloud?.apiKey;
979
1468
  } catch {
@@ -1356,63 +1845,169 @@ function registerTriageBugReport(server) {
1356
1845
  "unclear"
1357
1846
  ]).optional().describe("Triage classification: upstream_bug, customer_customization, emergency_hotfix, unclear"),
1358
1847
  cluster: supportClusterClearableSchema.optional().describe("Root-cause class this bug belongs to \u2014 a kebab-case slug (feature d0970342). Fixes are dispatched per cluster; use list_bugs(cluster=...) to find every bug sharing one. Pass an empty string to CLEAR a mis-triaged cluster back to unset."),
1848
+ push_to_github: z3.boolean().optional().describe(
1849
+ "Feature d7ea536b: when status is set to 'triaged' (the moment a bug moves from the untriaged inbox onto the roadmap), file (or adopt) a GitHub Issue in the routed AskExe repo and immediately close this support record with disposition='migrated' + the issue URL \u2014 GitHub becomes the system of record. Defaults to true whenever status='triaged'. Pass false to just set status='triaged' the old way, with no GitHub push."
1850
+ ),
1359
1851
  ...verificationArtifactInputSchema
1360
1852
  }
1361
1853
  },
1362
- async ({ id, status, triage_notes, add_note, linked_task_id, linked_commit, fixed_version, product, classification, cluster, ...verification }) => {
1854
+ async ({ id, status, triage_notes, add_note, linked_task_id, linked_commit, fixed_version, product, classification, cluster, push_to_github, ...verification }) => {
1363
1855
  const bugUrl = `${endpoint()}/${encodeURIComponent(sanitizeUpstreamId(id))}`;
1364
- const attestsByIdentity = verification.verification_kind === "manual" || verification.verification_kind === "ops_transcript" || verification.disposition !== void 0 && verification.disposition !== "fixed";
1365
- let authorIdentities = [];
1366
- if (isVerifiedStatus(status) && attestsByIdentity) {
1367
- try {
1368
- const row = await requestJson(bugUrl);
1369
- authorIdentities = [row?.agent_id, row?.license_email];
1370
- } catch {
1856
+ const wantsGithubPush = status === "triaged" && push_to_github !== false;
1857
+ const errorResult = (text) => ({ isError: true, content: [{ type: "text", text }] });
1858
+ const finishClose = async (effectiveStatus, verificationFinal, githubPush) => {
1859
+ const attestsByIdentity = verificationFinal.verification_kind === "manual" || verificationFinal.verification_kind === "ops_transcript" || verificationFinal.disposition !== void 0 && verificationFinal.disposition !== "fixed" && verificationFinal.disposition !== "migrated";
1860
+ let authorIdentities = [];
1861
+ if (isVerifiedStatus(effectiveStatus) && attestsByIdentity) {
1862
+ try {
1863
+ const row = await requestJson(bugUrl);
1864
+ authorIdentities = [row?.agent_id, row?.license_email];
1865
+ } catch {
1866
+ }
1867
+ }
1868
+ const resolved = resolveVerificationArtifact(effectiveStatus, verificationFinal, { authorIdentities });
1869
+ if (!resolved.ok) {
1870
+ return errorResult(resolved.message);
1871
+ }
1872
+ const attribution = assertVerdictAttribution(effectiveStatus, resolved);
1873
+ if (!attribution.ok) {
1874
+ return errorResult(attribution.message);
1875
+ }
1876
+ const delivery = parseDeliveryProof(verificationFinal.delivery_proof);
1877
+ if (!delivery.ok) {
1878
+ return errorResult(delivery.message);
1879
+ }
1880
+ const data = await requestJson(bugUrl, {
1881
+ method: "PATCH",
1882
+ body: sanitizedUpstreamBody({
1883
+ status: effectiveStatus,
1884
+ triage_notes,
1885
+ add_note,
1886
+ linked_task_id,
1887
+ linked_commit,
1888
+ fixed_version,
1889
+ product,
1890
+ classification,
1891
+ cluster,
1892
+ verification_artifact: resolved.artifact,
1893
+ delivery_proof: delivery.value,
1894
+ // The disposition travels with the close: the server gates on it, and stores
1895
+ // it so the record reads as "closed because there was nothing to fix" rather
1896
+ // than as an unexplained close.
1897
+ disposition: verificationFinal.disposition,
1898
+ disposition_reason: verificationFinal.disposition_reason,
1899
+ superseded_by: verificationFinal.superseded_by,
1900
+ disposition_verifier: verificationFinal.disposition_verifier,
1901
+ superseded_coverage: verificationFinal.superseded_coverage,
1902
+ github_issue_url: verificationFinal.github_issue_url,
1903
+ symptoms: verificationFinal.symptoms,
1904
+ attribution: resolved.attribution
1905
+ }, "support-inbox:triage_bug")
1906
+ });
1907
+ let response = fixed_version && !status ? withStatusNotice(
1908
+ data,
1909
+ `fixed_version ${fixed_version} recorded. Status was not changed and the bug was NOT closed. To close it, pass status: "closed" explicitly.`
1910
+ ) : data;
1911
+ if (githubPush && response && typeof response === "object" && !Array.isArray(response)) {
1912
+ response = {
1913
+ ...response,
1914
+ github_issue_url: githubPush.issueUrl,
1915
+ github_issue_repo: githubPush.repo,
1916
+ github_issue_adopted: githubPush.adopted
1917
+ };
1371
1918
  }
1919
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1920
+ };
1921
+ if (!wantsGithubPush) {
1922
+ return finishClose(status, verification);
1372
1923
  }
1373
- const resolved = resolveVerificationArtifact(status, verification, { authorIdentities });
1374
- if (!resolved.ok) {
1375
- return { isError: true, content: [{ type: "text", text: resolved.message }] };
1924
+ let row1;
1925
+ try {
1926
+ row1 = await requestJson(bugUrl);
1927
+ } catch (err) {
1928
+ return errorResult(
1929
+ `Could not push bug ${id} to GitHub: failed to fetch the bug record first (${err instanceof Error ? err.message : String(err)}). Nothing was changed.`
1930
+ );
1376
1931
  }
1377
- const attribution = assertVerdictAttribution(status, resolved);
1378
- if (!attribution.ok) {
1379
- return { isError: true, content: [{ type: "text", text: attribution.message }] };
1932
+ const canonicalId = row1.id?.trim() || id;
1933
+ const effectiveProduct = product ?? row1.product;
1934
+ const repo = routeRepoForProduct(effectiveProduct);
1935
+ if (!repo) {
1936
+ return errorResult(
1937
+ `No GitHub route configured for product '${effectiveProduct ?? "unknown"}'. Pass push_to_github: false to skip, or triage with a supported product.`
1938
+ );
1380
1939
  }
1381
- const delivery = parseDeliveryProof(verification.delivery_proof);
1382
- if (!delivery.ok) {
1383
- return { isError: true, content: [{ type: "text", text: delivery.message }] };
1940
+ const placeholderIssueUrl = `https://github.com/${repo}/issues/1`;
1941
+ const precheckVerification = { ...verification };
1942
+ precheckVerification.disposition = "migrated";
1943
+ precheckVerification.github_issue_url = placeholderIssueUrl;
1944
+ const precheckResolved = resolveVerificationArtifact("closed", precheckVerification, { authorIdentities: [] });
1945
+ if (!precheckResolved.ok) {
1946
+ return errorResult(precheckResolved.message);
1947
+ }
1948
+ const precheckAttribution = assertVerdictAttribution("closed", precheckResolved);
1949
+ if (!precheckAttribution.ok) {
1950
+ return errorResult(precheckAttribution.message);
1951
+ }
1952
+ const precheckDelivery = parseDeliveryProof(verification.delivery_proof);
1953
+ if (!precheckDelivery.ok) {
1954
+ return errorResult(precheckDelivery.message);
1955
+ }
1956
+ try {
1957
+ return await withBugGithubLock(canonicalId, async () => {
1958
+ let row2;
1959
+ try {
1960
+ row2 = await requestJson(bugUrl);
1961
+ } catch (err) {
1962
+ return errorResult(
1963
+ `Could not push bug ${id} to GitHub: failed to re-fetch the bug record inside the lock (${err instanceof Error ? err.message : String(err)}). Nothing was changed.`
1964
+ );
1965
+ }
1966
+ const bugForIssue = {
1967
+ id: canonicalId,
1968
+ title: row2.title ?? void 0,
1969
+ summary: row2.summary ?? void 0,
1970
+ severity: row2.severity ?? void 0,
1971
+ product: product ?? row2.product,
1972
+ classification: classification ?? row2.classification,
1973
+ cluster: cluster ?? row2.cluster,
1974
+ created_at: row2.created_at ?? void 0,
1975
+ triaged_at: row2.triaged_at ?? (/* @__PURE__ */ new Date()).toISOString(),
1976
+ fixed_version: fixed_version ?? row2.fixed_version,
1977
+ package_version: row2.package_version ?? void 0,
1978
+ license_plan: row2.license_plan ?? void 0,
1979
+ triage_notes: triage_notes ?? row2.triage_notes,
1980
+ github_issue_url: row2.github_issue_url ?? void 0,
1981
+ customer_impact: row2.customer_impact ?? void 0,
1982
+ reproduction_steps: row2.reproduction_steps_json ?? void 0,
1983
+ expected: row2.expected ?? void 0,
1984
+ actual: row2.actual ?? void 0,
1985
+ workaround: row2.workaround ?? void 0,
1986
+ progress_log: row2.progress_log ?? void 0,
1987
+ add_note: add_note ?? void 0,
1988
+ observed_kind: row2.observed_kind ?? void 0,
1989
+ evidence_pointer: row2.evidence_pointer ?? void 0,
1990
+ local_patch_diff: row2.local_patch_diff ?? void 0
1991
+ };
1992
+ let githubPush;
1993
+ try {
1994
+ githubPush = await pushBugToGithub(bugForIssue);
1995
+ } catch (err) {
1996
+ return errorResult(
1997
+ `Could not push bug ${id} to GitHub: ${err instanceof Error ? err.message : String(err)}. The record was left at its current status; nothing was closed.`
1998
+ );
1999
+ }
2000
+ const finalVerification = { ...verification };
2001
+ finalVerification.disposition = "migrated";
2002
+ finalVerification.github_issue_url = githubPush.issueUrl;
2003
+ return finishClose("closed", finalVerification, githubPush);
2004
+ });
2005
+ } catch (err) {
2006
+ if (err instanceof GithubLockTimeoutError) {
2007
+ return errorResult(err.message);
2008
+ }
2009
+ throw err;
1384
2010
  }
1385
- const data = await requestJson(bugUrl, {
1386
- method: "PATCH",
1387
- body: sanitizedUpstreamBody({
1388
- status,
1389
- triage_notes,
1390
- add_note,
1391
- linked_task_id,
1392
- linked_commit,
1393
- fixed_version,
1394
- product,
1395
- classification,
1396
- cluster,
1397
- verification_artifact: resolved.artifact,
1398
- delivery_proof: delivery.value,
1399
- // The disposition travels with the close: the server gates on it, and stores
1400
- // it so the record reads as "closed because there was nothing to fix" rather
1401
- // than as an unexplained close.
1402
- disposition: verification.disposition,
1403
- disposition_reason: verification.disposition_reason,
1404
- superseded_by: verification.superseded_by,
1405
- disposition_verifier: verification.disposition_verifier,
1406
- superseded_coverage: verification.superseded_coverage,
1407
- symptoms: verification.symptoms,
1408
- attribution: resolved.attribution
1409
- }, "support-inbox:triage_bug")
1410
- });
1411
- const response = fixed_version && !status ? withStatusNotice(
1412
- data,
1413
- `fixed_version ${fixed_version} recorded. Status was not changed and the bug was NOT closed. To close it, pass status: "closed" explicitly.`
1414
- ) : data;
1415
- return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1416
2011
  }
1417
2012
  );
1418
2013
  }