elanous 0.2.42 → 0.2.43

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 (685) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/{X1ucLw10Cds_h614NoEnh → Om5xS9jpjSXJCeTNGYEi4}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{7a98bc80.fa935989e829f9c2.js → 0966f8a1.745fcd44f64a87ea.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{73d2eb0e.4966ad55293c801a.js → 0ad6802e.ae7afb63fdcb4491.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/114.2b90424d1a3ce84c.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1151-5c2bb8f002ca2dd6.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1200.6a37c202f9f17e9a.js +226 -0
  9. package/apps/pwa/out/_next/static/chunks/1261-a616d5728e02e624.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1313-19f737e0222c4444.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1357.3bbef3976c37f94a.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1362-20adddfaf662b0fa.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{3507.97e1977bc4d5fb0a.js → 1406.a1dc2ab6f8449374.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/1737-9491fc6ab29b9ec6.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1803-4f91a8e7dcaf042c.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1961.a81b12872c3e93a9.js +23 -0
  17. package/apps/pwa/out/_next/static/chunks/{53c73f84.373b6160adfa89a4.js → 1970cf59.2d4d8ce5d6c81853.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/1971-8890f3962ba7ee0a.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/{9211-330b6736a63d4cb3.js → 2073-ba274e00e4f9efb4.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/{7411.9b4b8b747dc7b802.js → 2144.25b292396712196d.js} +2 -2
  21. package/apps/pwa/out/_next/static/chunks/2268-53861f339e0b65a2.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2312.0c8b61e22d3136d7.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{6632.0eac1c554f8f2ef3.js → 2573.387a707f6f9d720c.js} +2 -2
  24. package/apps/pwa/out/_next/static/chunks/273.f156eee28f11af77.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2873.d2140e5a82546401.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/{1262.e804feb8fd1acb8c.js → 3072.01338851ca21a6f7.js} +2 -2
  27. package/apps/pwa/out/_next/static/chunks/3091.a5a4143dea33d197.js +90 -0
  28. package/apps/pwa/out/_next/static/chunks/{2456.b6de0e1c05f28843.js → 3095.dc44586a5f83b86d.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/{3207.a756c95e9ec8c3c8.js → 3113.d7472bbc27d580c7.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/3228-9650b6447b6fa288.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/3402.44b565b314f2263c.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/3557ac4a.c8931684475018ad.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/{8505.fd803c6c33b54cc3.js → 360.ae9f9f4c33c395e8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3638.80412ea63aae8f3f.js +286 -0
  35. package/apps/pwa/out/_next/static/chunks/{b450776b.3db34d8d965f82d3.js → 363bfab3.9623b9f89363541f.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/3694.e0099884345f0198.js +64 -0
  37. package/apps/pwa/out/_next/static/chunks/3744.54f87db52acfd1ab.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3903.3ef4656c67572764.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3935.16701e33f8982dc9.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3955.6922286237f2ac2b.js +24 -0
  41. package/apps/pwa/out/_next/static/chunks/3969.272ee7f9fd6c62e5.js +34 -0
  42. package/apps/pwa/out/_next/static/chunks/{8138.6c10b2a356d52fca.js → 4199.ce86b281fd0a60b7.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/4311-6dd3144002cc0c2d.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4426-2b230fad00c88930.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{2690.0521c738b7749931.js → 4445.7990c76b3751a918.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4447-14c5161fa59166c7.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4856-b0b8075bba8c53f4.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4870-de370d65e73c0bb5.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4885-2c989165c4b29fad.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/{1945.7b54990d578c119b.js → 4894.fec2ca9e99caefa2.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/5093.8f04b7f8cb899a95.js +94 -0
  52. package/apps/pwa/out/_next/static/chunks/5122-2bb177cebf89e19a.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5154.5f56dc3902e3d275.js +36 -0
  54. package/apps/pwa/out/_next/static/chunks/529.64ff7257180b2ac9.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5394.5dabeb0c62293a28.js +93 -0
  56. package/apps/pwa/out/_next/static/chunks/5461.08bf2404ff38ec85.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5474-22e08a85583209b2.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{9427.96aa912ddb5801e0.js → 5564.9e72532188f57586.js} +16 -16
  59. package/apps/pwa/out/_next/static/chunks/5567-265df39ed8fe9dcb.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5570-a94ae19811cb2046.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5574-b47c9ef2dceff1fd.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/{4043.6b91203436c9c86b.js → 5628.6ddb2d5ffba4ff4c.js} +4 -4
  63. package/apps/pwa/out/_next/static/chunks/{1319.b9b815ae0f181e06.js → 5730.43d7ccc09988e646.js} +1 -1
  64. package/apps/pwa/out/_next/static/chunks/{3482.a9ae9d2ad6a5bd97.js → 5789.2da35c258292e1e5.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/5824.da9527b15a3221e5.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5866.366966af47fdde5d.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/{1487.2f67b79444062da6.js → 5930.8b4df3b46c32b05a.js} +1 -1
  68. package/apps/pwa/out/_next/static/chunks/5963-c4d8469c52a0828b.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/6001.ec4049ceb3c43418.js +27 -0
  70. package/apps/pwa/out/_next/static/chunks/6120-471f03db1e299c0c.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6175-41479e643aca1878.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6291.a2de4f9787637075.js +104 -0
  73. package/apps/pwa/out/_next/static/chunks/6400-6699907c3537436a.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/6428.1f4327d6ae5fdf9a.js +287 -0
  75. package/apps/pwa/out/_next/static/chunks/644.02cb026fa44bcd0a.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6466-6c87d60d3dd174fb.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{3347.f846266b5e5f3ede.js → 6540.c7bef57ad8f90a87.js} +3 -3
  78. package/apps/pwa/out/_next/static/chunks/6773.d4264665ab31bec0.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6822.3c789f1a8443e774.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/{8262.ddc03e06b733f184.js → 6944.309f4e3ea54d1537.js} +1 -1
  81. package/apps/pwa/out/_next/static/chunks/7261-8ff69d3b2ee671fd.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7266.084c6bbc70035140.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/7271.5c0100e7320aa856.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7429-bf5731358c8f2f68.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7464.01f7a0ae1215fbe7.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7479.0fcad5f37eb64f0c.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/{4770.769c9cd9c7034eba.js → 7483.427a2fe2049e388d.js} +2 -2
  88. package/apps/pwa/out/_next/static/chunks/{1147-5196d19c222051b3.js → 7502-be74718178e13e56.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/7530.0889722b19572065.js +24 -0
  90. package/apps/pwa/out/_next/static/chunks/7645.5b2ee3ad45c55ea3.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7788.7d1c79f1a8e18946.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7900.096f455c7c1efc53.js +77 -0
  93. package/apps/pwa/out/_next/static/chunks/7910-6f139bcafd238b9f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7966.f71c8958d1637548.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{8673-bab689a55f7e13b9.js → 8062-7cd0bcc7f01455ba.js} +3 -3
  96. package/apps/pwa/out/_next/static/chunks/8073.f6cf2f2c71aafbd0.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/8201.190b94088980f430.js +74 -0
  98. package/apps/pwa/out/_next/static/chunks/8205.282ca4bb62d54a0a.js +62 -0
  99. package/apps/pwa/out/_next/static/chunks/8313-dfa1021bb58a46d6.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/837-81336ebaca8198a8.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8447.7963352ef5f71b76.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8574-868e8129525ef6d9.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8588.d1c6a2903727ce87.js +149 -0
  104. package/apps/pwa/out/_next/static/chunks/8637.90f92d5c8db4732c.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/868.6e953be05bcf6dca.js +41 -0
  106. package/apps/pwa/out/_next/static/chunks/8706-e1fe0309343a2273.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8749-06384cdfc4520add.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/{9656-374232558fd528d0.js → 8861-cc1fa1c7b920fa8e.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/{5440-4236de26c5ea5856.js → 8967-7dfebf710c9dec9d.js} +1 -1
  110. package/apps/pwa/out/_next/static/chunks/{1063.2fb8a9dc55c731f2.js → 8986.8e2e4efbfa655c59.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/8997-3c564dbb3b58b055.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/{4320.583cbab64db6be02.js → 923.2034d10ae91e9c20.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/9312.bc1b89c5d4368033.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/{1574.878c18ecc97b1bd4.js → 943.39ae5e82f4f1893f.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/9469.0500480de55fb205.js +32 -0
  116. package/apps/pwa/out/_next/static/chunks/9770.5f75bc6088b16109.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9810-2543267050e58b82.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9870.8419702475254745.js +8 -0
  119. package/apps/pwa/out/_next/static/chunks/9941-376d1b74d51b01a4.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9958.385434ab04241758.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/aa57532f.510551854ff239b1.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-deea1450b17cb8dc.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-70985b904f8650ef.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a2e15e33e56a4353.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-fc72ce2db12d6ca9.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-be03e41f9436ebd9.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-35a35999382da3f2.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-3ff5b1e6ca88b848.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-a65101300a0ee5c1.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-3aca5b6d67dba511.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-45b8ab0a71e61314.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-53046e282d4cda8e.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-17809e60e8354c17.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-fd2442d347bf5898.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-e9d720d6fc082e21.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-6b8b4267bab0a677.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-deea1450b17cb8dc.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-8bcf6233ac7c17af.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-7cc25591cfc2126f.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-053e871f35c9beba.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-eff819f0c2d5e084.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-4d7e9d8db5274a87.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-2b2d8f5aa5f69ad4.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-0b8f92c5062e68b1.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1bd9d0fdac2ced3d.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/{page-0f291d3c8fd55a3a.js → page-9ee28f7df07428ea.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-44b7a1ff468c6648.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-cbc6523711dc1b65.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-fdad47899c2695be.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-79a8b5fee4c20871.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-03e8ac9606aeaffb.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-4cd55c55ec287e2f.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-db17b4305404161e.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-30041d848350fd02.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c540da022dbf86b0.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9399efc538da057f.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ca2d09128c585ca4.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-0e2635aecd343038.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ef7f4399b6b7726e.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-deea1450b17cb8dc.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-caf530535b875468.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-eaa20c1543a5fd8e.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c541eb1b75120f61.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-ab5e213eda9479f4.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-0a815f34367b6db6.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-e858ba0ed9393b68.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-1638ee9dd6e0582f.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-4cf5a399109391f2.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b4396321238798ec.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-cb72bfd21006781a.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6135d0242afbc434.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a1dccf907eddb856.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/c7945435-b44d4dbe90ba0dcb.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/{3d0154af.5331fec737ffe8c3.js → cb0f8df8.09a4a6be97e3fc5f.js} +1 -1
  175. package/apps/pwa/out/_next/static/chunks/{e8543907.5b040a5f22a0b0e5.js → d96660fb.cbc21a2a88539dbb.js} +7 -7
  176. package/apps/pwa/out/_next/static/chunks/{50061469.0326382241ef3b2d.js → ea514caa.05a24f78d9dffe5f.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/fb7d20d5-6892502b6ee39ca6.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/framework-e8a911e19b1be41f.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-7022f418fabba021.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-e5f15df755756a1e.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-4525f91e36940c9b.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-140f0e519c085b0f.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-8f22ef63dcb865b1.js +1 -0
  184. package/apps/pwa/out/_next/static/css/d1f8bb401740a549.css +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +23 -23
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +22 -22
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -23
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +23 -23
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/catalog/external-commands.yaml +5 -3
  279. package/catalog/meta/catalogVersion.yaml +2 -1
  280. package/catalog/signing/catalog-signing-public.pem +3 -0
  281. package/graphs/intake/intake-daily.yaml +16 -0
  282. package/graphs/intake/news-sources.yaml +1 -0
  283. package/graphs/intake/recipes.yaml +7 -0
  284. package/graphs/overlays/examples/research-runtime-empty-terminal.yaml +28 -0
  285. package/graphs/profiles/local.yaml +5 -0
  286. package/graphs/profiles/pod.yaml +4 -0
  287. package/package.json +6 -2
  288. package/plugins/demo-absorption-briefing/plugin.json +18 -0
  289. package/plugins/demo-absorption-briefing/skills/demo-absorption-briefing/SKILL.md +8 -0
  290. package/plugins/demo-browser-hand/plugin.json +13 -0
  291. package/plugins/demo-browser-hand/skills/demo-browser-hand/SKILL.md +8 -0
  292. package/plugins/demo-linear-secretary/plugin.json +15 -0
  293. package/plugins/demo-linear-secretary/skills/demo-linear-secretary/SKILL.md +8 -0
  294. package/plugins/demo-plugin-wizard/plugin.json +11 -0
  295. package/plugins/demo-plugin-wizard/skills/demo-plugin-wizard/SKILL.md +8 -0
  296. package/plugins/demo-research/plugin.json +16 -0
  297. package/plugins/demo-research/skills/demo-research/SKILL.md +8 -0
  298. package/plugins/demo-video-briefing/plugin.json +15 -0
  299. package/plugins/demo-video-briefing/skills/demo-video-briefing/SKILL.md +8 -0
  300. package/scripts/build-signed-catalog.ts +48 -0
  301. package/scripts/ci-typecheck-main-delta.sh +88 -0
  302. package/scripts/codex-fallback-live-probe.ts +1 -1
  303. package/scripts/demo/demo1104-parallel-scenes.ts +301 -0
  304. package/scripts/demo/demo1104-stage.config.ts +21 -0
  305. package/scripts/demo/demo1104-stage.ts +316 -0
  306. package/scripts/goal-method-audit.ts +17 -1
  307. package/scripts/graph-units/land-enqueue.ts +90 -0
  308. package/scripts/hooks/codex-notify.sh +32 -0
  309. package/scripts/install.sh +10 -38
  310. package/scripts/landing-heal/land-plan.ts +57 -0
  311. package/scripts/release-loop/auto-approve-node.ts +1 -1
  312. package/scripts/release-loop/gate-autoheal-sim-evidence.ts +60 -0
  313. package/scripts/release-loop/gate-method.ts +139 -0
  314. package/scripts/release-loop/gate-node.ts +180 -20
  315. package/scripts/release-loop/gate-traffic-mode.ts +151 -3
  316. package/scripts/release-loop/node-verdict.ts +38 -25
  317. package/scripts/release-loop/preflight.ts +362 -8
  318. package/scripts/release-loop/unattended-release.ts +20 -9
  319. package/scripts/rhythm/daily.ts +36 -4
  320. package/scripts/rhythm/linear-progress-sync.ts +298 -0
  321. package/scripts/video-pipeline.ts +9 -1
  322. package/scripts/warm-pool-provider-config.ts +29 -6
  323. package/src/acp/agent-manager.ts +1 -0
  324. package/src/acp/codex-app-server-agent.ts +33 -0
  325. package/src/acp/codex-app-server-client.ts +153 -16
  326. package/src/acp/core-turn-bridge.ts +17 -12
  327. package/src/acp/event-router.ts +20 -1
  328. package/src/acp/server.ts +6 -0
  329. package/src/agent-mission/browser-roundtrip-coordinator.ts +85 -0
  330. package/src/agent-mission/claude-headless.ts +4 -1
  331. package/src/agent-mission/codex-exec.ts +105 -0
  332. package/src/agent-mission/driver.ts +192 -11
  333. package/src/agent-mission/grok-auth-status.ts +47 -0
  334. package/src/agent-mission/hand-hooks.ts +101 -0
  335. package/src/agent-mission/mission-cli.ts +56 -21
  336. package/src/agent-mission/mission-hand-reply.ts +118 -0
  337. package/src/agent-mission/mission-line.ts +180 -0
  338. package/src/agent-mission/mission-out.ts +256 -18
  339. package/src/agent-mission/tool-install.ts +122 -11
  340. package/src/agents/general-purpose.md +1 -1
  341. package/src/auto-research/research-turn.ts +95 -10
  342. package/src/autopilot/pr-manager.ts +31 -3
  343. package/src/boot/daemon-tools/self-implement.ts +3 -1
  344. package/src/boot/daemon-tools/web-search.ts +1 -2
  345. package/src/briefing/brief-items.ts +55 -11
  346. package/src/cli/agent-cli.ts +2 -1
  347. package/src/cli/catalog-cli.ts +31 -0
  348. package/src/cli/connector-cli.ts +16 -0
  349. package/src/cli/doctor-fix.ts +2 -2
  350. package/src/cli/intake-source-cli.ts +44 -8
  351. package/src/cli/msg-cli.ts +32 -3
  352. package/src/cli/pr-cli.ts +21 -1
  353. package/src/cli/pr-land-heavy-remote.ts +44 -6
  354. package/src/cli/pr-land-overlap-evidence.ts +13 -1
  355. package/src/cli/pr-land-pod-shadow-job.ts +143 -5
  356. package/src/cli/pr-land-pod-shadow.ts +9 -2
  357. package/src/cli/release-checklist-read-cli.ts +44 -16
  358. package/src/cli/release-cli.ts +23 -3
  359. package/src/cli/research-cli.ts +9 -0
  360. package/src/cli/setup-cli.ts +166 -6
  361. package/src/cli/setup-keys.ts +187 -0
  362. package/src/cli/task-plugin-wizard-host.ts +114 -0
  363. package/src/cli/tasks-cli.ts +54 -9
  364. package/src/cli/tui-test-cli.ts +186 -0
  365. package/src/config.ts +4 -2
  366. package/src/connectors/linear-daily-progress.ts +228 -0
  367. package/src/connectors/linear-projector.ts +34 -12
  368. package/src/connectors/linear.ts +151 -7
  369. package/src/core-turn/deep-turn-bloom.ts +67 -0
  370. package/src/core-turn/research-policy.ts +37 -0
  371. package/src/core-turn/run-core-turn.ts +320 -30
  372. package/src/core-turn/types.ts +2 -0
  373. package/src/dashboard/chat/acp-chat.ts +10 -0
  374. package/src/dashboard/classic-sidetab-mission-job.ts +103 -0
  375. package/src/dashboard/classic-sidetab-mission-pair.ts +81 -0
  376. package/src/dashboard/classic-sidetab-mission-trio.ts +219 -0
  377. package/src/dashboard/classic-sidetab-mission.ts +167 -5
  378. package/src/dashboard/index.ts +378 -13
  379. package/src/dashboard/input/chat-main-acp-dispatch.ts +23 -0
  380. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +8 -0
  381. package/src/dashboard/mission-cli-consumer.ts +20 -8
  382. package/src/dashboard/plugin-wizard-runtime.ts +50 -0
  383. package/src/dashboard/plugin-wizard-tool.ts +55 -0
  384. package/src/dashboard/sidetab-runtime.ts +1 -1
  385. package/src/dashboard/terminal-popup-runtime.ts +6 -3
  386. package/src/decisions/linear-progress-summary.ts +78 -0
  387. package/src/decisions/linear-progress-sync.ts +152 -0
  388. package/src/domains/outbound-alert.ts +49 -11
  389. package/src/domains/telegram-kind-route.ts +16 -9
  390. package/src/graph-runner/arg-allowance.ts +13 -0
  391. package/src/graph-runner/graph-run-api.ts +3 -1
  392. package/src/graph-runner/runner.ts +85 -17
  393. package/src/graph-wizard/mission-compose.ts +407 -12
  394. package/src/harness/dispatch-backpressure.ts +54 -1
  395. package/src/harness/dispatch-shadow-compare.ts +74 -0
  396. package/src/harness/harness-cli-command.ts +11 -1
  397. package/src/harness/skill-exec.ts +21 -9
  398. package/src/harness/skill-select.ts +1 -1
  399. package/src/hooks/error-report-cloud.ts +101 -0
  400. package/src/hooks/receiver.ts +29 -2
  401. package/src/index.ts +127 -10
  402. package/src/intake-plane/actions.ts +354 -0
  403. package/src/intake-plane/adapters/telegram-ambient.ts +34 -18
  404. package/src/intake-plane/intake-sources.ts +78 -8
  405. package/src/intake-plane/items.ts +14 -4
  406. package/src/intake-plane/linear-mission-sync.ts +80 -0
  407. package/src/intake-plane/linear-secretary-runtime.ts +178 -0
  408. package/src/intake-plane/linear-wish.ts +227 -1
  409. package/src/life/life-cli.ts +168 -0
  410. package/src/life/life-kinds.ts +160 -0
  411. package/src/live-supply/budget-consumer.ts +78 -0
  412. package/src/live-supply/read.ts +3 -0
  413. package/src/live-supply/snapshot.ts +45 -0
  414. package/src/llm/provider-credentials.ts +8 -2
  415. package/src/llm.ts +2 -1
  416. package/src/maturity/feature-maturity.ts +3 -3
  417. package/src/msg/msg-sse-server.ts +3 -3
  418. package/src/msg/msg-store.ts +177 -19
  419. package/src/native-tool-catalog.ts +36 -2
  420. package/src/nexus/api/http-server.ts +37 -1
  421. package/src/nexus/api/mcp-public-allowlist.ts +2 -0
  422. package/src/nexus/index.ts +7 -2
  423. package/src/nexus/outbound/format.ts +9 -0
  424. package/src/nexus/outbound/router.ts +98 -13
  425. package/src/nexus/setup-status.ts +9 -0
  426. package/src/nexus/state/state.ts +3 -0
  427. package/src/oauth/codex-account-store.ts +20 -7
  428. package/src/oauth/codex-owner-mirror.ts +44 -0
  429. package/src/oauth/codex.ts +17 -2
  430. package/src/oauth/fallback-chain.ts +47 -17
  431. package/src/onboarding/first-run-doctor.ts +8 -1
  432. package/src/onboarding/first-run.ts +85 -26
  433. package/src/onboarding/key-setup.ts +103 -0
  434. package/src/onboarding/menu-answer.ts +13 -0
  435. package/src/onboarding/secret-prompt.ts +37 -0
  436. package/src/plugins/install/plugin-install.ts +59 -0
  437. package/src/pr-tool/census.ts +130 -0
  438. package/src/pty-shell/pty-control-ipc.ts +4 -1
  439. package/src/registry/catalog-server.ts +232 -0
  440. package/src/registry/loader.ts +45 -1
  441. package/src/registry/types.ts +1 -2
  442. package/src/research/axis-fanout.ts +43 -28
  443. package/src/self-dev/ask-launch-flow.ts +72 -1
  444. package/src/self-dev/dev-cli.ts +4 -1
  445. package/src/self-dev/dev-pipeline.ts +5 -0
  446. package/src/self-dev/draft-residue.ts +64 -5
  447. package/src/self-dev/graph-front-nodes.ts +2 -1
  448. package/src/self-dev/graph-journey-route.ts +76 -21
  449. package/src/self-dev/launch-preflight.ts +58 -0
  450. package/src/self-dev/orchestrate.ts +1 -1
  451. package/src/self-dev/shard-aggregate.ts +4 -0
  452. package/src/self-dev/shard-run.ts +36 -15
  453. package/src/self-implement/abandoned-classification.ts +12 -3
  454. package/src/self-implement/budget-gate.ts +7 -0
  455. package/src/self-implement/child-llm-preference.ts +10 -1
  456. package/src/self-implement/doctor-visit.ts +30 -8
  457. package/src/self-implement/elanous-tui-spawn.ts +3 -0
  458. package/src/self-implement/fixtures/tdd-green-missing-passing-child-transcript.txt +2 -0
  459. package/src/self-implement/gate-artifacts.ts +2 -1
  460. package/src/self-implement/goal-author-cli.ts +4 -2
  461. package/src/self-implement/goal-author.ts +358 -4
  462. package/src/self-implement/graph-overlay-yaml.ts +30 -4
  463. package/src/self-implement/graph-overlay.ts +3 -1
  464. package/src/self-implement/graph-profile-yaml.ts +107 -0
  465. package/src/self-implement/graph-yaml.ts +26 -3
  466. package/src/self-implement/off-diff-evidence.ts +249 -5
  467. package/src/self-implement/orchestrator.ts +240 -15
  468. package/src/self-implement/run-chart.ts +6 -3
  469. package/src/self-implement/run-ledger.ts +51 -1
  470. package/src/self-implement/runtime-overlay-run-steps.ts +95 -0
  471. package/src/self-implement/tdd-evidence-tags.ts +9 -0
  472. package/src/self-implement/train-gate-receipt.ts +166 -0
  473. package/src/self-implement/train-gate-remote.ts +62 -18
  474. package/src/session/chat.ts +4 -2
  475. package/src/session/session-fanout.ts +2 -0
  476. package/src/session/streaming/acp-stream-sink.ts +2 -1
  477. package/src/session/streaming/chunk-producer.ts +6 -0
  478. package/src/session/streaming/stream-compositor.ts +3 -2
  479. package/src/session-runtime/index.ts +37 -1
  480. package/src/session-runtime/retry-policy.ts +1 -1
  481. package/src/setup/llm-env-detect.ts +3 -0
  482. package/src/setup/service-key-catalog.ts +46 -0
  483. package/src/setup/service-keys.ts +241 -0
  484. package/src/sidetab-core/visible-agent-mission.ts +98 -22
  485. package/src/skills/tools/agent.ts +23 -4
  486. package/src/skills/tools/omni-search.ts +123 -25
  487. package/src/task-agent/parent-host.ts +10 -0
  488. package/src/task-agent/repair-outcomes.ts +65 -0
  489. package/src/task-agent/task-hand.ts +20 -7
  490. package/src/task-orchestrator/dispatcher.ts +14 -1
  491. package/src/task-orchestrator/linear-capture-events.ts +55 -0
  492. package/src/task-orchestrator/store.ts +16 -5
  493. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +4 -0
  494. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +92 -1
  495. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +113 -14
  496. package/src/task-orchestrator/surfaces/pod-source-receive.ts +23 -11
  497. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +2 -0
  498. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +35 -9
  499. package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -6
  500. package/src/task-orchestrator/surfaces/self-implement.ts +27 -3
  501. package/src/task-orchestrator/surfaces/warm-reuse-evidence.ts +169 -0
  502. package/src/task-orchestrator/types.ts +14 -0
  503. package/src/telegram.ts +14 -16
  504. package/src/terminal/coding-agent.ts +14 -5
  505. package/src/terminal/session-registry.ts +1 -1
  506. package/src/tool-hints/gate.ts +1 -0
  507. package/src/tool-runtime/agent-runtime.ts +1 -0
  508. package/src/tool-runtime/browser-hand-runtime.ts +381 -0
  509. package/src/tool-runtime/browser-runtime.ts +2 -1
  510. package/src/tool-runtime/elanous-linear-issue-runtime.ts +76 -0
  511. package/src/tool-runtime/elanous-linear-secretary-runtime.ts +57 -0
  512. package/src/tool-runtime/graph-mcp-runtimes.ts +240 -0
  513. package/src/tool-runtime/index.ts +6 -0
  514. package/src/tool-runtime/types.ts +3 -0
  515. package/src/tui/entry.ts +24 -1
  516. package/src/tui/sidetab/attach.ts +140 -11
  517. package/src/tui/sidetab/herdr-portal.ts +225 -0
  518. package/src/user-config.ts +34 -9
  519. package/src/user-intent/types.ts +4 -3
  520. package/src/ux-sim/browser-hand.ts +165 -0
  521. package/src/ux-sim/draft-inventory-builder.ts +198 -0
  522. package/src/ux-sim/fake-browser-page.ts +81 -0
  523. package/src/ux-sim/fake-linear-device.ts +31 -0
  524. package/src/ux-sim/graph-engine-sim.ts +47 -0
  525. package/src/ux-sim/linear-secretary.ts +216 -0
  526. package/src/ux-sim/mock-scenario-catalog.json +75 -23
  527. package/src/ux-sim/mock-scenario-catalog.ts +120 -2
  528. package/src/ux-sim/onboarding-first-run-device.ts +31 -21
  529. package/src/ux-sim/scenario.ts +70 -26
  530. package/src/ux-sim/service-key-setup.ts +50 -0
  531. package/src/ux-sim/term-slash-device.ts +74 -0
  532. package/src/ux-sim/turn-throw-sim.ts +31 -0
  533. package/src/version/packed-revision.json +1 -1
  534. package/src/web-search/exa.ts +77 -0
  535. package/src/web-search/index.ts +30 -9
  536. package/src/web-search/provider.ts +4 -1
  537. package/src/web-search/x-intent.ts +5 -0
  538. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +0 -36
  540. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/1455-530ca16c7e71baff.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +0 -32
  543. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +0 -286
  548. package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +0 -23
  549. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +0 -34
  552. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/2974.595d1e29d040cb6a.js +0 -64
  557. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +0 -93
  558. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +0 -41
  559. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +0 -24
  561. package/apps/pwa/out/_next/static/chunks/3741.9cf76acaa2ee5298.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +0 -77
  567. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/4276.336d76d858fcdf1b.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +0 -74
  572. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +0 -149
  573. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +0 -27
  575. package/apps/pwa/out/_next/static/chunks/5431-72a1dbde59400691.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +0 -287
  583. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/6131.348b7d6bdcd267ce.js +0 -226
  586. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +0 -90
  587. package/apps/pwa/out/_next/static/chunks/6377.bc7df239edbff244.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +0 -104
  594. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +0 -94
  595. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +0 -24
  602. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +0 -8
  610. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/8546-6f03f8ea34dd61d9.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/9249.976f1e69a9d80754.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/9633.ce5bddc56784da72.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +0 -62
  626. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0e3d7b9421b79dac.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/term/page-bf067887b3307423.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +0 -1
  684. package/apps/pwa/out/_next/static/css/be9019f696217521.css +0 -1
  685. /package/apps/pwa/out/_next/static/{X1ucLw10Cds_h614NoEnh → Om5xS9jpjSXJCeTNGYEi4}/_ssgManifest.js +0 -0
@@ -3,6 +3,7 @@ import { randomUUID } from 'node:crypto';
3
3
  import { existsSync, mkdirSync } from 'node:fs';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { elanousStateRoot } from '../autopilot/state-paths.js';
6
+ import { debug } from '../debug/log.js';
6
7
 
7
8
  export interface MessageEnvelope {
8
9
  from: string;
@@ -37,13 +38,33 @@ export function canonicalSeatId(value: string): string {
37
38
  return Object.hasOwn(SEAT_ALIASES, seat) ? SEAT_ALIASES[seat]! : seat;
38
39
  }
39
40
 
41
+ /** Normalize the public message address syntax while keeping seat IDs backward compatible. */
42
+ export function canonicalMessageAddress(value: string): string {
43
+ if (typeof value !== 'string') throw new TypeError('message address must be a string');
44
+ const address = value.trim();
45
+ if (/^seat:/i.test(address)) return canonicalSeatId(address.slice(address.indexOf(':') + 1));
46
+ if (/^group:/i.test(address)) {
47
+ const name = address.slice(address.indexOf(':') + 1).trim().toLowerCase();
48
+ if (!/^[a-z0-9][a-z0-9_-]{0,62}$/.test(name)) throw new Error('invalid group name');
49
+ return name === 'all' ? 'ALL' : `group:${name}`;
50
+ }
51
+ return canonicalSeatId(address);
52
+ }
53
+
54
+ function canonicalGroupAddress(value: string): string {
55
+ const address = canonicalMessageAddress(/^group:/i.test(value.trim()) ? value : `group:${value}`);
56
+ if (!address.startsWith('group:')) throw new Error('group address must be group:<name>');
57
+ return address;
58
+ }
59
+
40
60
  /** Check untrusted input before opening the DB or executing a write. */
41
61
  export function validateMessageEnvelope(input: unknown): MessageEnvelope {
42
62
  if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('message must be an object');
43
63
  const record = input as Record<string, unknown>;
44
- const from = canonicalSeatId(record.from as string);
45
- const to = canonicalSeatId(record.to as string);
64
+ const from = canonicalMessageAddress(record.from as string);
65
+ const to = canonicalMessageAddress(record.to as string);
46
66
  if (from === 'ALL') throw new Error('ALL cannot send messages');
67
+ if (from.startsWith('group:')) throw new Error('sender must be a seat');
47
68
  if (typeof record.body !== 'string' || !record.body.trim()) throw new Error('message body must be nonempty');
48
69
  if (record.kind !== undefined && (typeof record.kind !== 'string' || !/^[a-z][a-z0-9_-]{0,63}$/.test(record.kind))) {
49
70
  throw new Error('invalid message kind');
@@ -65,20 +86,35 @@ function validCursor(cursor: number): number {
65
86
  return cursor;
66
87
  }
67
88
 
89
+ function hasGroupMembershipTable(db: Database): boolean {
90
+ return (db.query("SELECT COUNT(*) AS count FROM sqlite_master WHERE type = 'table' AND name = 'msg_group_memberships'")
91
+ .get() as { count: number }).count > 0;
92
+ }
93
+
68
94
  /** Unread count ⊕ newest unread ID for one recipient (same predicate as `unreadCountOn`). */
69
95
  function unreadSummaryOn(db: Database, to: string): { count: number; maxId: number | null } {
96
+ const groupClause = hasGroupMembershipTable(db) ? ` OR EXISTS (
97
+ SELECT 1 FROM msg_group_memberships gm WHERE gm.address = m.recipient AND gm.seat = ?
98
+ AND m.id > gm.joined_after_id AND (gm.left_after_id IS NULL OR m.id <= gm.left_after_id)
99
+ )` : '';
100
+ const params = hasGroupMembershipTable(db) ? [to, to, to, to] : [to, to, to];
70
101
  const row = db.query(`SELECT COUNT(*) AS count, MAX(m.id) AS max_id FROM msg_messages m
71
102
  WHERE m.id > COALESCE((SELECT last_id FROM msg_cursors WHERE recipient = ?), 0)
72
- AND (m.recipient = ? OR (m.recipient = 'ALL' AND m.sender <> ?))`)
73
- .get(to, to, to) as { count: number; max_id: number | null };
103
+ AND (m.recipient = ? OR (m.recipient = 'ALL' AND m.sender <> ?)${groupClause})`)
104
+ .get(...params) as { count: number; max_id: number | null };
74
105
  return { count: row.count, maxId: row.max_id ?? null };
75
106
  }
76
107
 
77
108
  function unreadCountOn(db: Database, to: string): number {
109
+ const groupClause = hasGroupMembershipTable(db) ? ` OR EXISTS (
110
+ SELECT 1 FROM msg_group_memberships gm WHERE gm.address = m.recipient AND gm.seat = ?
111
+ AND m.id > gm.joined_after_id AND (gm.left_after_id IS NULL OR m.id <= gm.left_after_id)
112
+ )` : '';
113
+ const params = hasGroupMembershipTable(db) ? [to, to, to, to] : [to, to, to];
78
114
  return (db.query(`SELECT COUNT(*) AS count FROM msg_messages m
79
115
  WHERE m.id > COALESCE((SELECT last_id FROM msg_cursors WHERE recipient = ?), 0)
80
- AND (m.recipient = ? OR (m.recipient = 'ALL' AND m.sender <> ?))`)
81
- .get(to, to, to) as { count: number }).count;
116
+ AND (m.recipient = ? OR (m.recipient = 'ALL' AND m.sender <> ?)${groupClause})`)
117
+ .get(...params) as { count: number }).count;
82
118
  }
83
119
 
84
120
  interface MessageRow {
@@ -101,6 +137,7 @@ function fromRow(row: MessageRow): Message {
101
137
  /** SQLite owns both append order and per-recipient acknowledgement progress. */
102
138
  export class MsgStore {
103
139
  readonly db: Database;
140
+ private readonly clock: () => Date;
104
141
 
105
142
  // A read-only SSE connection may outlive another process's in-place migration.
106
143
  private get hasReplyTo(): boolean {
@@ -108,13 +145,19 @@ export class MsgStore {
108
145
  .get() as { count: number }).count > 0;
109
146
  }
110
147
 
111
- private readWithSchema<T>(read: (hasReplyTo: boolean) => T): T {
148
+ private get hasGroupMemberships(): boolean {
149
+ return (this.db.query("SELECT COUNT(*) AS count FROM sqlite_master WHERE type = 'table' AND name = 'msg_group_memberships'")
150
+ .get() as { count: number }).count > 0;
151
+ }
152
+
153
+ private readWithSchema<T>(read: (hasReplyTo: boolean, hasGroupMemberships: boolean) => T): T {
112
154
  // The schema projection and rows must share one SQLite snapshot. Otherwise a writer can
113
155
  // migrate between them and a reader could advance past a reply with its link erased.
114
- return this.db.transaction(() => read(this.hasReplyTo)).deferred();
156
+ return this.db.transaction(() => read(this.hasReplyTo, this.hasGroupMemberships)).deferred();
115
157
  }
116
158
 
117
- constructor(path: string = defaultMsgStorePath(), options: { readOnly?: boolean } = {}) {
159
+ constructor(path: string = defaultMsgStorePath(), options: { readOnly?: boolean; now?: () => Date } = {}) {
160
+ this.clock = options.now ?? (() => new Date());
118
161
  if (options.readOnly && (path === ':memory:' || !existsSync(path))) throw new Error('message ledger unavailable for read-only SSE service');
119
162
  if (!options.readOnly && path !== ':memory:') mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
120
163
  this.db = options.readOnly ? new Database(path, { readonly: true }) : new Database(path);
@@ -142,9 +185,27 @@ export class MsgStore {
142
185
  request_id INTEGER PRIMARY KEY REFERENCES msg_messages(id),
143
186
  status TEXT NOT NULL CHECK(status IN ('pending', 'delivered'))
144
187
  );
145
- CREATE TABLE IF NOT EXISTS msg_ta_claim (
188
+ CREATE TABLE IF NOT EXISTS msg_ta_claim (
146
189
  request_id INTEGER PRIMARY KEY REFERENCES msg_messages(id),
147
190
  token TEXT NOT NULL
191
+ );
192
+ CREATE TABLE IF NOT EXISTS msg_groups (
193
+ address TEXT PRIMARY KEY
194
+ );
195
+ CREATE TABLE IF NOT EXISTS msg_group_memberships (
196
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
197
+ address TEXT NOT NULL REFERENCES msg_groups(address),
198
+ seat TEXT NOT NULL,
199
+ joined_after_id INTEGER NOT NULL DEFAULT 0 CHECK(joined_after_id >= 0),
200
+ left_after_id INTEGER CHECK(left_after_id IS NULL OR left_after_id >= joined_after_id)
201
+ );
202
+ CREATE INDEX IF NOT EXISTS idx_msg_group_memberships_seat_interval ON msg_group_memberships(seat, joined_after_id, left_after_id);`);
203
+ if (!options.readOnly) this.db.exec(`CREATE TABLE IF NOT EXISTS msg_group_all_budget (
204
+ sender TEXT NOT NULL,
205
+ hour_start TEXT NOT NULL,
206
+ broadcast_count INTEGER NOT NULL CHECK(broadcast_count BETWEEN 1 AND 2),
207
+ summary_id INTEGER REFERENCES msg_messages(id),
208
+ PRIMARY KEY(sender, hour_start)
148
209
  );`);
149
210
  // Serialize the schema check and ALTER across processes opening an old ledger at once.
150
211
  if (!options.readOnly) this.db.transaction(() => {
@@ -155,9 +216,7 @@ export class MsgStore {
155
216
  }).immediate();
156
217
  }
157
218
 
158
- append(input: MessageEnvelope): Message {
159
- return this.insertMessage(input);
160
- }
219
+ append(input: MessageEnvelope): Message { return this.post(input); }
161
220
 
162
221
  /** Append once for a coordination comment, task failure, or decided notification. */
163
222
  appendWithProvenance(input: MessageEnvelope, provenanceKey: string): Message {
@@ -171,9 +230,16 @@ export class MsgStore {
171
230
  FROM msg_provenance p JOIN msg_messages m ON m.id = p.message_id WHERE p.provenance_key = ?`)
172
231
  .get(provenanceKey) as MessageRow | null;
173
232
  if (existing) return fromRow(existing);
233
+ if (msg.to.startsWith('group:') && !this.db.query('SELECT 1 FROM msg_groups WHERE address = ?').get(msg.to)) {
234
+ throw new Error(`group not found: ${msg.to}`);
235
+ }
174
236
  const message = this.insertMessage(msg);
175
237
  this.db.query('INSERT INTO msg_provenance(provenance_key, message_id) VALUES (?, ?)')
176
238
  .run(provenanceKey, message.id);
239
+ if (msg.to === 'ALL') {
240
+ try { debug.log('msg.broadcast-budget', 'provenance-exempt', { recipient: msg.to, count: 1, messageId: message.id, provenanceKey }); }
241
+ catch { /* observability is fail-open; preserve the idempotent event */ }
242
+ }
177
243
  return message;
178
244
  }).immediate();
179
245
  }
@@ -189,7 +255,85 @@ export class MsgStore {
189
255
  return { id: Number(result.lastInsertRowid), ...msg, createdAt };
190
256
  }
191
257
 
192
- post(input: MessageEnvelope): Message { return this.append(input); }
258
+ post(input: MessageEnvelope): Message {
259
+ const message = validateMessageEnvelope(input);
260
+ if (message.to.startsWith('group:')) return this.db.transaction(() => {
261
+ if (!this.db.query('SELECT 1 FROM msg_groups WHERE address = ?').get(message.to)) throw new Error(`group not found: ${message.to}`);
262
+ return this.insertMessage(message);
263
+ }).immediate();
264
+ if (message.to !== 'ALL') return this.insertMessage(message);
265
+ const now = this.clock();
266
+ const hourStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours())).toISOString();
267
+ return this.db.transaction(() => {
268
+ const row = this.db.query(`SELECT broadcast_count, summary_id FROM msg_group_all_budget
269
+ WHERE sender = ? AND hour_start = ?`).get(message.from, hourStart) as { broadcast_count: number; summary_id: number | null } | null;
270
+ if (row && row.broadcast_count >= 2) {
271
+ if (row.summary_id !== null) {
272
+ return this.getMessage(row.summary_id)!;
273
+ }
274
+ const summary = this.insertMessage({ from: message.from, to: 'ALL', kind: 'summary',
275
+ body: 'group:all 시간당 상한(2건)을 초과한 방송은 이 요약 한 건으로 접었습니다.' });
276
+ this.db.query('UPDATE msg_group_all_budget SET summary_id = ? WHERE sender = ? AND hour_start = ?')
277
+ .run(summary.id, message.from, hourStart);
278
+ return summary;
279
+ }
280
+ const posted = this.insertMessage(message);
281
+ this.db.query(`INSERT INTO msg_group_all_budget(sender, hour_start, broadcast_count, summary_id)
282
+ VALUES (?, ?, 1, NULL) ON CONFLICT(sender, hour_start)
283
+ DO UPDATE SET broadcast_count = msg_group_all_budget.broadcast_count + 1`)
284
+ .run(message.from, hourStart);
285
+ return posted;
286
+ }).immediate();
287
+ }
288
+
289
+ createGroup(name: string, owner: string): string {
290
+ const address = canonicalGroupAddress(name.startsWith('group:') ? name : `group:${name}`);
291
+ const seat = canonicalSeatId(owner);
292
+ return this.db.transaction(() => {
293
+ this.db.query('INSERT INTO msg_groups(address) VALUES (?)').run(address);
294
+ this.db.query('INSERT INTO msg_group_memberships(address, seat, joined_after_id) VALUES (?, ?, ?)')
295
+ .run(address, seat, this.latestId());
296
+ return address;
297
+ }).immediate();
298
+ }
299
+
300
+ joinGroup(group: string, member: string): boolean {
301
+ const address = canonicalGroupAddress(group);
302
+ const seat = canonicalSeatId(member);
303
+ return this.db.transaction(() => {
304
+ if (!this.db.query('SELECT 1 FROM msg_groups WHERE address = ?').get(address)) throw new Error(`group not found: ${address}`);
305
+ const existing = this.db.query('SELECT 1 FROM msg_group_memberships WHERE address = ? AND seat = ? AND left_after_id IS NULL')
306
+ .get(address, seat);
307
+ if (existing) return false;
308
+ this.db.query('INSERT INTO msg_group_memberships(address, seat, joined_after_id) VALUES (?, ?, ?)')
309
+ .run(address, seat, this.latestId());
310
+ return true;
311
+ }).immediate();
312
+ }
313
+
314
+ leaveGroup(group: string, member: string): boolean {
315
+ const address = canonicalGroupAddress(group);
316
+ const seat = canonicalSeatId(member);
317
+ return this.db.transaction(() => {
318
+ const result = this.db.query(`UPDATE msg_group_memberships SET left_after_id = ?
319
+ WHERE id = (SELECT id FROM msg_group_memberships WHERE address = ? AND seat = ? AND left_after_id IS NULL ORDER BY id DESC LIMIT 1)`)
320
+ .run(this.latestId(), address, seat);
321
+ return result.changes > 0;
322
+ }).immediate();
323
+ }
324
+
325
+ listGroups(member: string): string[] {
326
+ const seat = canonicalSeatId(member);
327
+ return (this.db.query('SELECT DISTINCT address FROM msg_group_memberships WHERE seat = ? AND left_after_id IS NULL ORDER BY address')
328
+ .all(seat) as Array<{ address: string }>).map(row => row.address);
329
+ }
330
+
331
+ isGroupDelivery(message: Message, recipient: string): boolean {
332
+ const seat = canonicalSeatId(recipient);
333
+ return message.to.startsWith('group:') && Boolean(this.db.query(`SELECT 1 FROM msg_group_memberships
334
+ WHERE address = ? AND seat = ? AND ? > joined_after_id AND (left_after_id IS NULL OR ? <= left_after_id)`)
335
+ .get(message.to, seat, message.id, message.id));
336
+ }
193
337
 
194
338
  /** Delivery state is separate from the single linked reply; an uncertain send keeps its claim. */
195
339
  taDeliveryStatus(requestId: number): 'pending' | 'delivered' | null {
@@ -253,9 +397,16 @@ export class MsgStore {
253
397
  if (to === 'ALL') throw new Error('ALL is a topic, not a recipient');
254
398
  validCursor(after);
255
399
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1000) throw new Error('limit must be between 1 and 1000');
256
- return this.readWithSchema(hasReplyTo => (this.db.query(`SELECT id, sender, recipient, body, kind, ${hasReplyTo ? 'reply_to' : 'NULL AS reply_to'}, created_at FROM msg_messages
257
- WHERE id > ? AND (recipient = ? OR (recipient = 'ALL' AND sender <> ?))
258
- ORDER BY id ASC LIMIT ?`).all(after, to, to, limit) as MessageRow[]).map(fromRow));
400
+ return this.readWithSchema((hasReplyTo, hasGroupMemberships) => {
401
+ const groupClause = hasGroupMemberships ? ` OR EXISTS (
402
+ SELECT 1 FROM msg_group_memberships gm WHERE gm.address = msg_messages.recipient AND gm.seat = ?
403
+ AND msg_messages.id > gm.joined_after_id AND (gm.left_after_id IS NULL OR msg_messages.id <= gm.left_after_id)
404
+ )` : '';
405
+ const params = hasGroupMemberships ? [after, to, to, to, limit] : [after, to, to, limit];
406
+ return (this.db.query(`SELECT id, sender, recipient, body, kind, ${hasReplyTo ? 'reply_to' : 'NULL AS reply_to'}, created_at FROM msg_messages
407
+ WHERE id > ? AND (recipient = ? OR (recipient = 'ALL' AND sender <> ?)${groupClause}) ORDER BY id ASC LIMIT ?`)
408
+ .all(...params) as MessageRow[]).map(fromRow);
409
+ });
259
410
  }
260
411
 
261
412
  /** One message by durable ID, or null when absent. */
@@ -350,16 +501,23 @@ export class MsgStore {
350
501
  }
351
502
 
352
503
  unreadRecipients(): UnreadRecipient[] {
504
+ const hasGroups = this.hasGroupMemberships;
505
+ const membershipRecipients = hasGroups ? ' UNION SELECT seat FROM msg_group_memberships' : '';
506
+ const groupClause = hasGroups ? ` OR EXISTS (
507
+ SELECT 1 FROM msg_group_memberships gm WHERE gm.address = m.recipient AND gm.seat = r.recipient
508
+ AND m.id > gm.joined_after_id AND (gm.left_after_id IS NULL OR m.id <= gm.left_after_id)
509
+ )` : '';
353
510
  return this.db.query(`WITH recipients(recipient) AS (
354
511
  SELECT 'OP' UNION SELECT 'MK' UNION SELECT 'TC' UNION SELECT 'UX'
355
- UNION SELECT recipient FROM msg_messages WHERE recipient <> 'ALL'
512
+ UNION SELECT recipient FROM msg_messages WHERE recipient <> 'ALL' AND recipient NOT LIKE 'group:%'
356
513
  UNION SELECT recipient FROM msg_cursors WHERE recipient <> 'ALL'
514
+ ${membershipRecipients}
357
515
  )
358
516
  SELECT r.recipient AS recipient, COUNT(m.id) AS count
359
517
  FROM recipients r
360
518
  LEFT JOIN msg_cursors c ON c.recipient = r.recipient
361
519
  JOIN msg_messages m ON m.id > COALESCE(c.last_id, 0)
362
- AND (m.recipient = r.recipient OR (m.recipient = 'ALL' AND m.sender <> r.recipient))
520
+ AND (m.recipient = r.recipient OR (m.recipient = 'ALL' AND m.sender <> r.recipient)${groupClause})
363
521
  GROUP BY r.recipient ORDER BY r.recipient ASC`).all() as UnreadRecipient[];
364
522
  }
365
523
 
@@ -731,6 +731,27 @@ export const nativeToolCatalog: NativeToolCatalogEntry[] = [
731
731
  host: ['mcp'], safety: ['read-only', 'network'], supportsParallel: true, defaultEnabled: true,
732
732
  intentScope: 'ops-fleet',
733
733
  },
734
+ {
735
+ id: 'elanous_linear_secretary',
736
+ kind: 'other', aliases: ['ElanousLinearSecretary'], displayName: 'ElanousLinearSecretary',
737
+ description: 'Ground a request in repository evidence and create a Linear parent issue with 3–5 assigned subtasks. Requires an explicit due date and verified evidence. Uses the configured project unless the request contains `projectless=true` and exactly one explicit `teamKey=<KEY>` token; never guesses a destination, date or citation.',
738
+ promptSummary: '`elanous_linear_secretary` (ground and create a parent with 3–5 evidence-backed subtasks; team-only needs projectless=true plus one explicit teamKey=<KEY>)',
739
+ host: ['tui', 'mcp'], safety: ['mutating', 'network'], supportsParallel: false, defaultEnabled: true,
740
+ intentScope: 'ops-fleet',
741
+ },
742
+ {
743
+ id: 'elanous_linear_issue',
744
+ kind: 'other',
745
+ aliases: ['ElanousLinearIssue'],
746
+ displayName: 'ElanousLinearIssue',
747
+ description: 'Perform one explicitly requested Linear issue action: create directly in a team without a project, add a comment, or mark an issue complete. Requires connector.linear.apiKey.',
748
+ promptSummary: '`elanous_linear_issue` (create a team issue without a project, comment, or complete an issue when explicitly requested)',
749
+ host: ['tui', 'mcp'],
750
+ safety: ['mutating', 'network'],
751
+ supportsParallel: false,
752
+ defaultEnabled: true,
753
+ intentScope: 'ops-fleet',
754
+ },
734
755
  {
735
756
  id: 'elanous_asana_list_tasks',
736
757
  kind: 'other', aliases: ['ElanousAsanaListTasks'], displayName: 'ElanousAsanaListTasks',
@@ -818,6 +839,19 @@ export const nativeToolCatalog: NativeToolCatalogEntry[] = [
818
839
  defaultEnabled: true,
819
840
  intentScope: 'coding',
820
841
  },
842
+ ...(['graph_compose', 'graph_run_status', 'graph_run_start', 'graph_node_op'] as const).map((id) => ({
843
+ id,
844
+ kind: 'other' as const,
845
+ aliases: [],
846
+ displayName: id,
847
+ description: `MCP graph tool: ${id}`,
848
+ promptSummary: `\`${id}\` (MCP graph control)`,
849
+ host: ['mcp'] as NativeToolHost[],
850
+ safety: (id === 'graph_compose' || id === 'graph_run_status' ? ['read-only'] : ['mutating']) as ('read-only' | 'mutating')[],
851
+ supportsParallel: id === 'graph_compose' || id === 'graph_run_status',
852
+ defaultEnabled: true,
853
+ intentScope: 'coding' as const,
854
+ })),
821
855
  {
822
856
  id: 'elanous_autopilot_launch',
823
857
  kind: 'other',
@@ -1282,8 +1316,8 @@ export const nativeToolCatalog: NativeToolCatalogEntry[] = [
1282
1316
  kind: 'web',
1283
1317
  aliases: ['WebSearch', 'web_search'],
1284
1318
  displayName: 'WebSearch',
1285
- description: 'Search the public web via a configured provider (Grok live-search / Firecrawl / MCP / custom); when no key is available, fall back to keyless DuckDuckGo.',
1286
- promptSummary: '`WebSearch` (provider-backed web search — Grok live-search by default; when no key is available, fall back to keyless DuckDuckGo; pair with WebFetch for full bodies)',
1319
+ description: 'Search the public web via Exa then Firecrawl; explicit X/Twitter intent may use Grok.',
1320
+ promptSummary: '`WebSearch` (general web search via Exa then Firecrawl; explicit X/Twitter intent may use Grok; pair with WebFetch for full bodies)',
1287
1321
  host: ['skill'],
1288
1322
  safety: ['network', 'read-only'],
1289
1323
  supportsParallel: true,
@@ -32,8 +32,10 @@ import { CARD_FOLLOWUP_PATH, createCardFollowupJobs, type CardFollowupJobs } fro
32
32
  import { SKILL_EXEC_PATH, createSkillExecJobs, type SkillExecJobs } from './skill-exec-route.js';
33
33
  import { compareTokenConstTime } from '../../acp/transport/auth.js';
34
34
  import { ensureAuthToken } from '../../auth/acp-token.js';
35
- import type { NexusState } from '../state/state.js';
35
+ import { pushEvent, type NexusState } from '../state/state.js';
36
36
  import { DEFAULT_NEXUS_HTTP_PORT } from '../default-port.js';
37
+ import { effectiveInstanceRoot } from '../../instance/resolve.js';
38
+ import { handleMissionLineRequest, MissionLineHttpError } from '../../agent-mission/mission-line.js';
37
39
 
38
40
  export { DEFAULT_NEXUS_HTTP_PORT } from '../default-port.js';
39
41
  import type { TabRegistry } from '../state/tab-registry.js';
@@ -497,6 +499,8 @@ export interface NexusHttpServerOpts {
497
499
  projectFolderRoots?: string[];
498
500
  /** Optional installer state root for isolated API consumers and tests. */
499
501
  pluginStateRoot?: string;
502
+ /** Mission-addressed hand line storage; production uses the current instance state root. */
503
+ missionLineRoot?: string;
500
504
  outputs?: OutputsDeps;
501
505
  /** Read-only grid measurement seam; omitted in production for live HQ and Pod pool reads. */
502
506
  grid?: GridDeps;
@@ -1008,6 +1012,38 @@ export async function routeRequest(
1008
1012
  registerAuthPeerAddress(req, (server as BunServerLike & {
1009
1013
  requestIP?: (request: Request) => { address: string } | null;
1010
1014
  }).requestIP?.(req)?.address);
1015
+ // Mission lines use a scoped per-mission bearer issued by the mission
1016
+ // parent, not the owner/API bearer checked by the general scope guard.
1017
+ if (new URL(req.url).pathname.startsWith('/v1/missions/')) {
1018
+ // MissionOutStore uses effectiveInstanceRoot too; share the resolver
1019
+ // directly so custom ELANOUS_STATE_DIR/config-dir cannot split the token
1020
+ // authority from the reply ledger.
1021
+ const root = opts.missionLineRoot ?? effectiveInstanceRoot();
1022
+ const response = await handleMissionLineRequest(req, root, async (input, authority) => {
1023
+ const { createMissionOutStore, MissionReplyStateError } = await import('../../agent-mission/mission-out.js');
1024
+ const reply = await createMissionOutStore(root).reply(input.missionId, input.text, input.type === 'final', {
1025
+ lineId: input.lineId,
1026
+ ...(input.runId ? { runId: input.runId } : {}), ...(input.ptyId ? { ptyId: input.ptyId } : {}),
1027
+ ...(input.replyTo ? { replyTo: input.replyTo } : {}),
1028
+ attemptId: input.attemptId, agent: input.agent, lineType: input.type, source: input.source,
1029
+ ...(input.kind ? { kind: input.kind } : {}),
1030
+ ...(input.defaults ? { defaults: input.defaults } : {}),
1031
+ ...(input.deadline ? { deadline: input.deadline } : {}),
1032
+ ...(input.scope ? { scope: input.scope } : {}),
1033
+ ...(input.stop !== undefined ? { stop: input.stop } : {}),
1034
+ ...(input.priority ? { priority: input.priority } : {}),
1035
+ ...(input.state ? { state: input.state } : {}), who: authority.who, what: authority.what,
1036
+ ...(input.type === 'ask' || input.type === 'answer' || input.type === 'correct' ? { kind: input.type } : {}),
1037
+ }).catch((error: unknown) => {
1038
+ if (error instanceof MissionReplyStateError) throw new MissionLineHttpError(error.status);
1039
+ throw error;
1040
+ });
1041
+ return { ...input, sequence: reply.sequence, createdAt: reply.createdAt, done: reply.done, ...(reply.deadline ? { deadline: reply.deadline } : {}), ...(reply.replyTo ? { replyTo: reply.replyTo } : {}), ...(reply.graphEvents ? { graphEvents: reply.graphEvents } : {}) };
1042
+ }, (event) => {
1043
+ pushEvent(opts.state, { kind: 'mission.line', detail: event as unknown as Record<string, unknown> });
1044
+ });
1045
+ if (response) return response;
1046
+ }
1011
1047
  if (opts.metaApi && setupBearerScopeDenied(req, opts.metaApi)) {
1012
1048
  return jsonResponse({ error: 'forbidden' }, 403);
1013
1049
  }
@@ -14,6 +14,8 @@ export const MCP_PUBLIC_TOOLS = new Set([
14
14
  'elanous_obsidian_search',
15
15
  'agent_list',
16
16
  'agent_output',
17
+ 'graph_compose',
18
+ 'graph_run_status',
17
19
  ]);
18
20
 
19
21
  function catalogId(name: string): string | undefined {
@@ -252,7 +252,7 @@ import type { HealthProbeBackend } from './supervisor/health.js';
252
252
  import { gracefulExit, cleanExit } from './supervisor/graceful-exit.js';
253
253
  import { restoreFromPending } from './supervisor/restore-state.js';
254
254
  import { resolveHeadlessMode } from './headless-mode.js';
255
- import { checkSetupStatus, readNexusSetupMode, renderSetupStatus, setupBootMode, setupModeBootPlan } from './setup-status.js';
255
+ import { checkSetupStatus, readNexusSetupMode, renderSetupStatus, setupBootMode, setupModeBootPlan, shouldCheckHeadlessSetup } from './setup-status.js';
256
256
  import { probeTailscale } from './onboarding/tailscale-probe.js';
257
257
  import {
258
258
  runPwaSharePrompt,
@@ -859,7 +859,12 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
859
859
  }
860
860
 
861
861
  const headless = resolveHeadlessMode({ ...(opts.headless !== undefined ? { headless: opts.headless } : {}) });
862
- if (headless && !opts.detachForTesting && !opts.skipHeadlessSetupCheckForTesting) {
862
+ if (shouldCheckHeadlessSetup({
863
+ headless,
864
+ detachedForTesting: opts.detachForTesting === true,
865
+ skipForTesting: opts.skipHeadlessSetupCheckForTesting === true,
866
+ testInstance: resolveCurrentInstance().kind === 'test',
867
+ })) {
863
868
  const setup = checkSetupStatus({ argvBin: process.argv[1] ?? '' });
864
869
  const boot = setupBootMode(setup);
865
870
  if (boot.mode === 'refuse') {
@@ -14,6 +14,15 @@ export interface OutboundMsg {
14
14
  kind: string;
15
15
  /** Optional routing role; absent preserves per-kind fan-out. */
16
16
  role?: string;
17
+ /** Optional transport metadata. A router must not send these fields to a connector that does not declare support. */
18
+ threadId?: string;
19
+ topicId?: string;
20
+ persona?: { name: string; avatarUrl?: string };
21
+ attachments?: readonly { url: string; name?: string }[];
22
+ buttons?: readonly { label: string; value: string }[];
23
+ choices?: readonly { label: string; value: string }[];
24
+ /** Requested channel-side authorization; delivery fails closed when unsupported or denied. */
25
+ permissionAction?: string;
17
26
  }
18
27
 
19
28
  export interface FormattedMessage {
@@ -61,6 +61,23 @@ export interface ChannelCapabilities {
61
61
  rateLimit: boolean;
62
62
  }
63
63
 
64
+ const CHANNEL_CAPABILITY_KEYS = {
65
+ send: true,
66
+ receive: true,
67
+ thread: true,
68
+ topic: true,
69
+ persona: true,
70
+ channelLifecycle: true,
71
+ permissions: true,
72
+ commands: true,
73
+ attachments: true,
74
+ buttons: true,
75
+ choices: true,
76
+ deliveryConfirmation: true,
77
+ rateLimit: true,
78
+ } satisfies Record<keyof ChannelCapabilities, true>;
79
+ const CHANNEL_CAPABILITY_KEY_LIST = Object.keys(CHANNEL_CAPABILITY_KEYS) as (keyof ChannelCapabilities)[];
80
+
64
81
  export interface ChannelEnvelope {
65
82
  text: string;
66
83
  kind: string;
@@ -98,7 +115,34 @@ export interface ChannelAdapter {
98
115
  export function receiveChannelEvent(type: string, payload: unknown): ChannelEnvelope | null | Promise<ChannelEnvelope | null> {
99
116
  const adapter = outboundAdapters.get(type) ?? installedAdapters.get(type);
100
117
  if (!adapter?.capabilities.receive || !adapter.receive) return null;
101
- return adapter.receive(payload);
118
+ const recordOutcome = (outcome: 'accepted' | 'rejected' | 'error'): void => {
119
+ try {
120
+ const runId = process.env.ELANOUS_RUN_ID;
121
+ debug.log('nexus.outbound', 'receive-conformance', {
122
+ ...(runId ? { runId } : {}),
123
+ provider: type,
124
+ outcome,
125
+ });
126
+ } catch { /* observation must not change channel receive behavior */ }
127
+ };
128
+ const recordResult = (envelope: ChannelEnvelope | null): ChannelEnvelope | null => {
129
+ recordOutcome(envelope ? 'accepted' : 'rejected');
130
+ return envelope;
131
+ };
132
+
133
+ try {
134
+ const result = adapter.receive(payload);
135
+ if (result && typeof (result as Promise<ChannelEnvelope | null>).then === 'function') {
136
+ return Promise.resolve(result).then(recordResult, error => {
137
+ recordOutcome('error');
138
+ throw error;
139
+ });
140
+ }
141
+ return recordResult(result as ChannelEnvelope | null);
142
+ } catch (error) {
143
+ recordOutcome('error');
144
+ throw error;
145
+ }
102
146
  }
103
147
 
104
148
  export interface OutboundChannelConfig {
@@ -113,21 +157,43 @@ export interface OutboundChannelConfig {
113
157
  }
114
158
 
115
159
  /** Connector providers register while the host is running; unregister on plugin
116
- * deactivation. Built-ins cannot be replaced by a plugin or removed. */
160
+ * deactivation. Invalid contracts throw so the plugin host can fail activation
161
+ * closed. Built-ins cannot be replaced by a plugin or removed. */
117
162
  const installedAdapters = new Map<string, ChannelAdapter>();
118
163
  export function registerOutboundAdapter(adapter: ChannelAdapter): () => void {
119
- if (!/^[a-z][a-z0-9-]*$/.test(adapter.type) || outboundAdapters.has(adapter.type) || installedAdapters.has(adapter.type)
120
- || !adapter.capabilities.send || typeof adapter.send !== 'function' || typeof adapter.parseConfig !== 'function'
121
- || (adapter.capabilities.receive && typeof adapter.receive !== 'function')
122
- || (adapter.capabilities.channelLifecycle && (typeof adapter.createChannel !== 'function' || typeof adapter.archiveChannel !== 'function'))
123
- || (adapter.capabilities.permissions && typeof adapter.checkPermission !== 'function')
124
- || (adapter.capabilities.commands && typeof adapter.registerCommands !== 'function')
125
- || (adapter.capabilities.deliveryConfirmation && typeof adapter.confirmDelivery !== 'function')
126
- || (adapter.capabilities.rateLimit && typeof adapter.retryAfterMs !== 'function')) {
127
- throw new Error(`invalid or duplicate outbound connector: ${adapter.type}`);
164
+ let adapterType = '';
165
+ let validContract = false;
166
+ try {
167
+ adapterType = adapter?.type ?? '';
168
+ const capabilities = adapter?.capabilities;
169
+ let completeCapabilities = false;
170
+ if (capabilities && typeof capabilities === 'object') {
171
+ const ownKeys = Reflect.ownKeys(capabilities);
172
+ completeCapabilities = ownKeys.length === CHANNEL_CAPABILITY_KEY_LIST.length
173
+ && ownKeys.every((key) => typeof key === 'string' && CHANNEL_CAPABILITY_KEY_LIST.includes(key as keyof ChannelCapabilities))
174
+ && CHANNEL_CAPABILITY_KEY_LIST.every((key) => {
175
+ const descriptor = Object.getOwnPropertyDescriptor(capabilities, key);
176
+ return !!descriptor && Object.hasOwn(descriptor, 'value') && typeof descriptor.value === 'boolean';
177
+ });
178
+ }
179
+ validContract = /^[a-z][a-z0-9-]*$/.test(adapterType)
180
+ && !outboundAdapters.has(adapterType) && !installedAdapters.has(adapterType)
181
+ && completeCapabilities && !!capabilities?.send
182
+ && typeof adapter.send === 'function' && typeof adapter.parseConfig === 'function'
183
+ && (!capabilities.receive || typeof adapter.receive === 'function')
184
+ && (!capabilities.channelLifecycle || (typeof adapter.createChannel === 'function' && typeof adapter.archiveChannel === 'function'))
185
+ && (!capabilities.permissions || typeof adapter.checkPermission === 'function')
186
+ && (!capabilities.commands || typeof adapter.registerCommands === 'function')
187
+ && (!capabilities.deliveryConfirmation || typeof adapter.confirmDelivery === 'function')
188
+ && (!capabilities.rateLimit || typeof adapter.retryAfterMs === 'function');
189
+ } catch {
190
+ // Capability objects with throwing reflection traps fail closed at the plugin boundary.
128
191
  }
129
- installedAdapters.set(adapter.type, adapter);
130
- return () => { if (installedAdapters.get(adapter.type) === adapter) installedAdapters.delete(adapter.type); };
192
+ if (!validContract) {
193
+ throw new Error(`invalid or duplicate outbound connector: ${adapterType || '[unknown]'}`);
194
+ }
195
+ installedAdapters.set(adapterType, adapter);
196
+ return () => { if (installedAdapters.get(adapterType) === adapter) installedAdapters.delete(adapterType); };
131
197
  }
132
198
 
133
199
  export interface OutboundFanoutConfig {
@@ -394,6 +460,25 @@ export async function routeOutbound(cfg: UserConfig, msg: OutboundMsg, deps: Rou
394
460
  try {
395
461
  const adapter = deps.adapters?.get(ch.type) ?? outboundAdapters.get(ch.type) ?? installedAdapters.get(ch.type);
396
462
  if (!adapter?.capabilities.send) return { type: ch.type, ok: false, error: 'unsupported-channel' };
463
+ const requirements: Array<[keyof ChannelCapabilities, boolean]> = [
464
+ ['thread', outMsg.threadId !== undefined],
465
+ ['topic', outMsg.topicId !== undefined],
466
+ ['persona', outMsg.persona !== undefined],
467
+ ['attachments', outMsg.attachments !== undefined],
468
+ ['buttons', outMsg.buttons !== undefined],
469
+ ['choices', outMsg.choices !== undefined],
470
+ ];
471
+ if (requirements.some(([capability, requested]) => requested && !adapter.capabilities[capability]))
472
+ return { type: ch.type, ok: false, error: 'unsupported-capability' };
473
+ if (outMsg.permissionAction !== undefined) {
474
+ if (!adapter.capabilities.permissions || typeof adapter.checkPermission !== 'function')
475
+ return { type: ch.type, ok: false, error: 'unsupported-capability' };
476
+ if (!outMsg.permissionAction.trim()) return { type: ch.type, ok: false, error: 'invalid-permission-action' };
477
+ let allowed = false;
478
+ try { allowed = await adapter.checkPermission(outMsg.permissionAction); }
479
+ catch { return { type: ch.type, ok: false, error: 'permission-check-failed' }; }
480
+ if (!allowed) return { type: ch.type, ok: false, error: 'permission-denied' };
481
+ }
397
482
  const result = await adapter.send(ch, outMsg, cfg, deps);
398
483
  // A connector may not impersonate another channel or echo credentials
399
484
  // through the response. Legacy built-in result fields stay unchanged.
@@ -33,6 +33,15 @@ export interface SetupCheckResult {
33
33
 
34
34
  export type SetupBootMode = 'normal' | 'setup' | 'refuse';
35
35
 
36
+ export function shouldCheckHeadlessSetup(opts: {
37
+ headless: boolean;
38
+ detachedForTesting: boolean;
39
+ skipForTesting: boolean;
40
+ testInstance: boolean;
41
+ }): boolean {
42
+ return opts.headless && !opts.detachedForTesting && !opts.skipForTesting && !opts.testInstance;
43
+ }
44
+
36
45
  export interface SetupBootDecision {
37
46
  mode: SetupBootMode;
38
47
  missing: string[];
@@ -93,6 +93,9 @@ export interface NexusEvent {
93
93
  // SSE 로 **내용을 담은** 메시지레벨 갱신을 받는다(content-less `session.updated` 와 달리
94
94
  // role/text 포함). 스트리밍(ACP peer broadcast)은 C5 별도. `detail` = { sessionId, role, text }.
95
95
  | 'session.output'
96
+ // Hand mission addressed line. Durable rows live in agent-missions; this event
97
+ // is only the existing /v1/events SSE notification edge.
98
+ | 'mission.line'
96
99
  // Rich-dev-feedback opportunistic followup §6.2 #3 (2026-05-13) —
97
100
  // AgentStatusStore lifecycle fan-out. The bridge in
98
101
  // `agent-status-event-bridge.ts` subscribes to the store and