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
@@ -1,4 +1,13 @@
1
+ import { BriefItemsLedger } from '../briefing/brief-items.js';
2
+ import { EventLedger } from '../connectors/event-ledger.js';
3
+ import { commentLinearIssue, moveLinearIssueToStateType } from '../connectors/linear.js';
4
+ import { getSecretAsync } from '../nexus/config/secrets/index.js';
1
5
  import { captureAndDraftIntakeRecord } from './capture.js';
6
+ import { createHash, randomUUID } from 'node:crypto';
7
+ import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { effectiveInstanceRoot } from '../instance/resolve.js';
10
+ import { addSource, canonicalSeat, listSources, removeSource, runDue, type RunDueDeps, type RegisteredIntakeSource } from './intake-sources.js';
2
11
  import {
3
12
  applyClarifyAnswerToDraft,
4
13
  buildDecisionFromIntent,
@@ -10,6 +19,351 @@ import {
10
19
  proposeIntakeSessionToTox,
11
20
  } from './tox-adapter.js';
12
21
  import type { IntakeSession, RawIntakeRecord } from './types.js';
22
+ import type { BriefItem } from '../briefing/brief-items.js';
23
+
24
+ export interface TopicBriefingDeliveryDeps {
25
+ sendIssueCard: (input: { linearIssueId: string; text: string }) => Promise<void>;
26
+ commentIssue: (linearIssueId: string, text: string) => Promise<void>;
27
+ issueIsDone: (linearIssueId: string) => Promise<boolean>;
28
+ isDecisionImpacting: (items: readonly BriefItem[]) => Promise<boolean>;
29
+ reopenIssue: (linearIssueId: string) => Promise<void>;
30
+ sendMemoUpdateCard: (input: { linearIssueId: string; text: string }) => Promise<void>;
31
+ }
32
+
33
+ interface TopicBriefingDeliveryReceipt {
34
+ telegramSent: boolean;
35
+ linearCommented: boolean;
36
+ reopened: boolean;
37
+ memoSent: boolean;
38
+ updatedAt: string;
39
+ }
40
+
41
+ function topicBriefingReceiptsFile(stateDir: string): string { return join(stateDir, 'intake', 'topic-briefing-deliveries.json'); }
42
+
43
+ function readTopicBriefingReceipts(stateDir: string): Record<string, TopicBriefingDeliveryReceipt> {
44
+ const file = topicBriefingReceiptsFile(stateDir);
45
+ if (!existsSync(file)) return {};
46
+ const parsed: unknown = JSON.parse(readFileSync(file, 'utf8'));
47
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error(`topic briefing receipt ledger shape invalid: ${file}`);
48
+ return parsed as Record<string, TopicBriefingDeliveryReceipt>;
49
+ }
50
+
51
+ function saveTopicBriefingReceipts(stateDir: string, receipts: Record<string, TopicBriefingDeliveryReceipt>): void {
52
+ const file = topicBriefingReceiptsFile(stateDir);
53
+ mkdirSync(join(stateDir, 'intake'), { recursive: true });
54
+ const temp = `${file}.${randomUUID()}.tmp`;
55
+ try {
56
+ const fd = openSync(temp, 'wx', 0o600);
57
+ try { writeFileSync(fd, JSON.stringify(receipts, null, 2) + '\n'); } finally { closeSync(fd); }
58
+ chmodSync(temp, 0o600);
59
+ renameSync(temp, file);
60
+ } catch (error) {
61
+ if (existsSync(temp)) unlinkSync(temp);
62
+ throw error;
63
+ }
64
+ }
65
+
66
+ /** Persist each external delivery stage so a later channel failure cannot resend a successful Telegram card/comment. */
67
+ export async function deliverTopicBriefingWithReceipts(input: {
68
+ linearIssueId: string;
69
+ issueLabel?: string;
70
+ summary: string;
71
+ items: readonly BriefItem[];
72
+ }, stateDir: string, deps: TopicBriefingDeliveryDeps, mode: 'live' | 'telegram-dry-run' = 'live'): Promise<{ related: number; reopened: boolean }> {
73
+ const receiptKey = createHash('sha256').update(`${mode}\n${input.linearIssueId.toLowerCase()}\n${input.items.map(item => item.id).sort((a, b) => a - b).join(',')}`).digest('hex');
74
+ const receipts = readTopicBriefingReceipts(stateDir);
75
+ const receipt = receipts[receiptKey] ?? { telegramSent: false, linearCommented: false, reopened: false, memoSent: false, updatedAt: '' };
76
+ const saveReceipt = (): void => {
77
+ receipt.updatedAt = new Date().toISOString();
78
+ receipts[receiptKey] = receipt;
79
+ const expiry = Date.now() - 90 * 86_400_000;
80
+ for (const [key, value] of Object.entries(receipts)) if (Date.parse(value.updatedAt) < expiry) delete receipts[key];
81
+ saveTopicBriefingReceipts(stateDir, receipts);
82
+ };
83
+ return deliverTopicBriefing(input, {
84
+ ...deps,
85
+ sendIssueCard: async card => { if (!receipt.telegramSent) { await deps.sendIssueCard(card); receipt.telegramSent = true; saveReceipt(); } },
86
+ commentIssue: async (issueId, text) => { if (!receipt.linearCommented) { await deps.commentIssue(issueId, text); receipt.linearCommented = true; saveReceipt(); } },
87
+ issueIsDone: async issueId => receipt.reopened || await deps.issueIsDone(issueId),
88
+ isDecisionImpacting: async items => receipt.reopened || await deps.isDecisionImpacting(items),
89
+ reopenIssue: async issueId => { if (!receipt.reopened) { await deps.reopenIssue(issueId); receipt.reopened = true; saveReceipt(); } },
90
+ sendMemoUpdateCard: async card => { if (!receipt.memoSent) { await deps.sendMemoUpdateCard(card); receipt.memoSent = true; saveReceipt(); } },
91
+ });
92
+ }
93
+
94
+ export function isDecisionImpactingTopicEvidence(items: readonly BriefItem[]): boolean {
95
+ const decisionTerms = /(?:파산|부도|법정관리|사업 철수|공장 폐쇄|생산 중단|공급 중단|제재|소송|(?:계약|주문|공급|생산|투자|가이던스|실적|매출|수익|전망)(?:이|가|을|를|은|는)?\s*(?:취소|파기|철회|중단|감산|급감|감소|축소|하향|하락|적자 전환|대폭 삭감)|\b(?:bankruptcy|default|plant closure|business exit|production halt|supply halt|sanction|lawsuit|withdrawn|terminated)\b|\b(?:contract|order|supply|production|investment|guidance|earnings|revenue|profit|outlook)\s+(?:cancelled?|terminated|withdrawn|suspended|cut|reduced|downgraded|loss|miss)\b)/iu;
96
+ return items.some(item => decisionTerms.test(item.text));
97
+ }
98
+
99
+ /** Project only issue-linked findings; scheduled callers may invoke this after every run, including after Done. */
100
+ export async function deliverTopicBriefing(input: {
101
+ linearIssueId: string;
102
+ issueLabel?: string;
103
+ summary: string;
104
+ items: readonly BriefItem[];
105
+ }, deps: TopicBriefingDeliveryDeps): Promise<{ related: number; reopened: boolean }> {
106
+ const related = input.items.filter(item => item.linearIssueId?.toLowerCase() === input.linearIssueId.toLowerCase());
107
+ if (related.length === 0) return { related: 0, reopened: false };
108
+ const sources = related.map(item => item.evidence).filter((url): url is string => !!url);
109
+ const text = `관련 이슈 · ${input.issueLabel?.trim() || input.linearIssueId}\n\n${input.summary.trim()}${sources.length ? `\n\n출처\n${sources.map(url => `- ${url}`).join('\n')}` : ''}`;
110
+ await deps.sendIssueCard({ linearIssueId: input.linearIssueId, text });
111
+ await deps.commentIssue(input.linearIssueId, text);
112
+ const reopened = await deps.issueIsDone(input.linearIssueId) && await deps.isDecisionImpacting(related);
113
+ if (reopened) {
114
+ await deps.reopenIssue(input.linearIssueId);
115
+ await deps.sendMemoUpdateCard({ linearIssueId: input.linearIssueId,
116
+ text: `관련 이슈 · ${input.issueLabel?.trim() || input.linearIssueId}\n메모 갱신?\n\n${input.summary.trim()}` });
117
+ }
118
+ return { related: related.length, reopened };
119
+ }
120
+
121
+ /** Production adapter used by both the immediate subscription run and each existing daemon schedule fire. */
122
+ export function appendTopicBriefingTelegramDryRun(file: string, input: { linearIssueId: string; text: string; kind: 'issue' | 'memo' }): void {
123
+ appendFileSync(file, `${JSON.stringify({ recordedAt: new Date().toISOString(), ...input })}\n`, { encoding: 'utf8', mode: 0o600 });
124
+ chmodSync(file, 0o600);
125
+ }
126
+
127
+ export async function projectTopicBriefing(source: RegisteredIntakeSource, items: BriefItem[], stateDir = effectiveInstanceRoot(),
128
+ options: { telegramDryRunFile?: string } = {}): Promise<void> {
129
+ if (!source.linearIssueId || items.length === 0) return;
130
+ const [{ getSecretAsync }, { commentLinearIssue, fetchLinearIssueStateType, moveLinearIssueToStateType },
131
+ { kindRouteTarget }, { getUserConfig }, { sendTelegramTo }] = await Promise.all([
132
+ import('../nexus/config/secrets/index.js'), import('../connectors/linear.js'), import('../domains/telegram-kind-route.js'),
133
+ import('../user-config.js'), import('../autopilot/mission-notify.js'),
134
+ ]);
135
+ const target = options.telegramDryRunFile ? undefined : kindRouteTarget(getUserConfig(), 'ops-report');
136
+ if (!options.telegramDryRunFile && !target) throw new Error('관련 이슈 브리핑 Telegram ops-report 대상이 없습니다');
137
+ const send = async (text: string, kind: 'issue' | 'memo'): Promise<void> => {
138
+ if (options.telegramDryRunFile) {
139
+ appendTopicBriefingTelegramDryRun(options.telegramDryRunFile, { linearIssueId: source.linearIssueId!, text, kind });
140
+ return;
141
+ }
142
+ if (!target || !sendTelegramTo(target.botToken, target.chatId, text)) throw new Error('관련 이슈 브리핑 Telegram 발송 실패');
143
+ };
144
+ const apiKey = await getSecretAsync('connector.linear.apiKey');
145
+ if (!apiKey) throw new Error('Linear 키가 없습니다');
146
+ await deliverTopicBriefingWithReceipts({ linearIssueId: source.linearIssueId, issueLabel: source.spec,
147
+ summary: items.map(item => item.text).join('\n'), items }, stateDir, {
148
+ sendIssueCard: async ({ text }) => send(text, 'issue'),
149
+ commentIssue: async (issueId, body) => commentLinearIssue({ apiKey, issueId, body }),
150
+ issueIsDone: async issueId => await fetchLinearIssueStateType({ apiKey, issueId }) === 'completed',
151
+ isDecisionImpacting: async related => isDecisionImpactingTopicEvidence(related),
152
+ reopenIssue: async issueId => moveLinearIssueToStateType({ apiKey, issueId, type: 'started' }),
153
+ sendMemoUpdateCard: async ({ text }) => send(text, 'memo'),
154
+ }, options.telegramDryRunFile ? 'telegram-dry-run' : 'live');
155
+ }
156
+
157
+ export interface SubscribeTopicSearchInput {
158
+ seat: string;
159
+ topic: string;
160
+ /** Parent Linear issue.id UUID (`ExternalTaskEvent.ref` / task generatedBy.ref). */
161
+ linearIssueId: string;
162
+ every: string;
163
+ /** Per-subscription five-field cron, e.g. `0 8 * * 1` for Mondays at 08:00. */
164
+ scheduleCron: string;
165
+ /** Absolute path receiving Telegram message bodies instead of live sends. */
166
+ telegramDryRunFile?: string;
167
+ }
168
+
169
+ export interface SubscribeTopicSearchDeps {
170
+ stateDir?: string;
171
+ now?: Date;
172
+ runDue?: typeof runDue;
173
+ sourceAdd?: typeof addSource;
174
+ sourceList?: typeof listSources;
175
+ sourceRemove?: typeof removeSource;
176
+ scheduleDispatch?: (args: Record<string, unknown>) => Promise<unknown>;
177
+ runDeps?: RunDueDeps;
178
+ }
179
+
180
+ function scheduleError(result: unknown): string | null {
181
+ return result && typeof result === 'object' && 'error' in result ? String((result as { error: unknown }).error) : null;
182
+ }
183
+
184
+ /**
185
+ * Linear secretary handoff: register one issue-linked topic source, create and
186
+ * migrate exactly one existing schedule-registry job to the workflow daemon,
187
+ * then run that same source immediately through runDue.
188
+ */
189
+ export async function subscribeTopicSearch(input: SubscribeTopicSearchInput, deps: SubscribeTopicSearchDeps = {}): Promise<{
190
+ source: RegisteredIntakeSource;
191
+ scheduleId: string;
192
+ immediate: Awaited<ReturnType<typeof runDue>>;
193
+ }> {
194
+ const stateDir = deps.stateDir ?? effectiveInstanceRoot();
195
+ const normalizedTopic = input.topic.trim();
196
+ if (!normalizedTopic) throw new Error('topic-search topic이 비었다');
197
+ const seat = canonicalSeat(input.seat);
198
+ const sourceId = `topic-${createHash('sha256').update(`${seat}\n${input.linearIssueId.toLowerCase()}\n${normalizedTopic.toLowerCase()}`)
199
+ .digest('hex').slice(0, 20)}`;
200
+ const sources = deps.sourceList ?? listSources;
201
+ const add = deps.sourceAdd ?? addSource;
202
+ const remove = deps.sourceRemove ?? removeSource;
203
+ const existing = sources({}, stateDir).find((source) => source.id === sourceId);
204
+ let source: RegisteredIntakeSource;
205
+ let created = false;
206
+ let scheduleCreatedNow = false;
207
+ if (existing) {
208
+ if (existing.kind !== 'topic-search' || existing.linearIssueId?.toLowerCase() !== input.linearIssueId.toLowerCase()
209
+ || existing.spec.toLowerCase() !== normalizedTopic.toLowerCase() || existing.scheduleCron !== input.scheduleCron) {
210
+ throw new Error(`topic-search source identity collision: ${sourceId}`);
211
+ }
212
+ source = existing;
213
+ } else {
214
+ source = add({ id: sourceId, seat, kind: 'topic-search', spec: normalizedTopic,
215
+ every: input.every, scheduleCron: input.scheduleCron, linearIssueId: input.linearIssueId }, stateDir);
216
+ created = true;
217
+ }
218
+
219
+ const dispatch = deps.scheduleDispatch ?? (async (args: Record<string, unknown>) => {
220
+ const { dispatchScheduleManage } = await import('../domains/schedule-manage-tool.js');
221
+ return dispatchScheduleManage(args);
222
+ });
223
+ const dryRunArg = input.telegramDryRunFile
224
+ ? ` --telegram-dry-run-file '${input.telegramDryRunFile.replaceAll("'", "'\\''")}'` : '';
225
+ const command = `eln intake source run-due --source-id ${sourceId} --project-issue-briefing${dryRunArg}`;
226
+ let scheduleId: string | undefined;
227
+ const createSchedule = async (): Promise<void> => {
228
+ const creation = await dispatch({ action: 'create', cron: input.scheduleCron, command, category: 'ingest' });
229
+ const createError = scheduleError(creation);
230
+ if (createError) throw new Error(`topic-search 일정 등록 실패: ${createError}`);
231
+ scheduleCreatedNow = true;
232
+ // Crontab inventory derives category from the command, so the created
233
+ // job may appear under maintenance instead of the requested ingest
234
+ // category. Match by exact command + cron across the registry.
235
+ const listing = await dispatch({ action: 'list' }) as { schedules?: Array<{ id?: string; command?: string; cron?: string }> };
236
+ const listError = scheduleError(listing);
237
+ if (listError) throw new Error(`topic-search 일정 목록 확인 실패: ${listError}`);
238
+ const match = listing.schedules?.find((row) => row.command === command && row.cron === input.scheduleCron);
239
+ if (!match?.id) throw new Error('topic-search 일정이 schedule registry에 나타나지 않았다');
240
+ scheduleId = match.id;
241
+ const migrated = await dispatch({ action: 'migrate', id: scheduleId, yes: true });
242
+ const migrateError = scheduleError(migrated);
243
+ if (migrateError) throw new Error(`topic-search 일정을 데몬으로 이관하지 못했다: ${migrateError}`);
244
+ const noted = await dispatch({ action: 'note', id: scheduleId,
245
+ note: `topic-search · ${sourceId} · parent Linear UUID ${input.linearIssueId}` });
246
+ const noteError = scheduleError(noted);
247
+ if (noteError) throw new Error(`topic-search 일정 설명 저장 실패: ${noteError}`);
248
+ };
249
+ try {
250
+ if (!existing) await createSchedule();
251
+ else {
252
+ const listing = await dispatch({ action: 'list' }) as { schedules?: Array<{ id?: string; command?: string; cron?: string }> };
253
+ const listError = scheduleError(listing);
254
+ if (listError) throw new Error(`기존 topic-search 일정 목록 확인 실패: ${listError}`);
255
+ const sourceCommandPrefix = `eln intake source run-due --source-id ${sourceId} --project-issue-briefing`;
256
+ const conflicting = listing.schedules?.find((row) => row.cron === input.scheduleCron
257
+ && row.command?.startsWith(sourceCommandPrefix) && row.command !== command);
258
+ if (conflicting) throw new Error(`기존 topic-search 일정의 Telegram 실행 모드가 다르다; 중복 예약을 피하려면 기존 일정부터 제거해야 한다: ${conflicting.id ?? sourceId}`);
259
+ const match = listing.schedules?.find((row) => row.command === command && row.cron === input.scheduleCron);
260
+ if (match?.id) scheduleId = match.id;
261
+ else await createSchedule();
262
+ }
263
+ } catch (error) {
264
+ if (scheduleCreatedNow) {
265
+ let orphan = scheduleId;
266
+ if (!orphan) {
267
+ try {
268
+ const listing = await dispatch({ action: 'list' }) as { schedules?: Array<{ id?: string; command?: string; cron?: string }> };
269
+ orphan = listing.schedules?.find((row) => row.command === command && row.cron === input.scheduleCron)?.id;
270
+ } catch { /* source cleanup below remains primary */ }
271
+ }
272
+ if (orphan) try { await dispatch({ action: 'delete', id: orphan, yes: true }); } catch { /* preserve primary error */ }
273
+ }
274
+ if (created) {
275
+ remove(sourceId, stateDir);
276
+ }
277
+ throw error;
278
+ }
279
+
280
+ const immediate = await (deps.runDue ?? runDue)({ now: deps.now ?? new Date(), seat, sourceId,
281
+ deps: { ...deps.runDeps, stateDir, projectTopicBriefing: deps.runDeps?.projectTopicBriefing ?? (input.telegramDryRunFile
282
+ ? (registered, items, dir) => projectTopicBriefing(registered, items, dir, { telegramDryRunFile: input.telegramDryRunFile })
283
+ : projectTopicBriefing) } });
284
+ return { source, scheduleId: scheduleId!, immediate };
285
+ }
286
+
287
+ export interface IntakeIssueBriefingEvidence {
288
+ title: string;
289
+ url: string;
290
+ summary: string;
291
+ reopensIssue?: boolean;
292
+ }
293
+
294
+ export interface IntakeIssueBriefingInput {
295
+ issueRef: string;
296
+ topic: string;
297
+ state: 'open' | 'done';
298
+ evidence: readonly IntakeIssueBriefingEvidence[];
299
+ collected?: boolean;
300
+ }
301
+
302
+ export interface IntakeIssueBriefingDeps {
303
+ stateDir: string;
304
+ ledger?: BriefItemsLedger;
305
+ registerSource?: (source: RegisteredIntakeSource) => void;
306
+ runSources?: () => Promise<{ ran: Array<{ id: string; items: number; error?: string }> }>;
307
+ commentIssue?: (issueRef: string, body: string) => Promise<void>;
308
+ reopenIssue?: (issueRef: string) => Promise<void>;
309
+ }
310
+
311
+ function issueWatchId(issueRef: string): string {
312
+ return `watch-${issueRef.replace(/[^\w.-]+/g, '-')}`;
313
+ }
314
+
315
+ function issueBriefingComment(input: IntakeIssueBriefingInput, evidence: readonly IntakeIssueBriefingEvidence[]): string {
316
+ return [`## 흡수 브리핑 · ${input.issueRef}`, ...evidence.map(item => `- ${item.title}: ${item.summary} (${item.url})`)].join('\n');
317
+ }
318
+
319
+ async function defaultCommentIssue(stateDir: string, issueRef: string, body: string): Promise<void> {
320
+ const apiKey = await getSecretAsync('connector.linear.apiKey');
321
+ if (!apiKey?.trim()) return;
322
+ await commentLinearIssue({ apiKey, issueId: issueRef, body });
323
+ const hash = createHash('sha256').update(body).digest('hex');
324
+ new EventLedger(join(stateDir, 'connectors', 'events.jsonl')).record('linear', `outgoing:comment:brief:${issueRef}:${hash}`, {
325
+ ref: issueRef, occurredAt: new Date().toISOString(),
326
+ });
327
+ }
328
+
329
+ async function defaultReopenIssue(stateDir: string, issueRef: string): Promise<void> {
330
+ const apiKey = await getSecretAsync('connector.linear.apiKey');
331
+ if (!apiKey?.trim()) return;
332
+ await moveLinearIssueToStateType({ apiKey, issueId: issueRef, type: 'started' });
333
+ new EventLedger(join(stateDir, 'connectors', 'events.jsonl')).record('linear', `outgoing:reopen:brief:${issueRef}:${Date.now()}`, {
334
+ ref: issueRef, occurredAt: new Date().toISOString(),
335
+ });
336
+ }
337
+
338
+ /**
339
+ * The intake-daily bridge preserves the Linear secretary identifier from subscription through the briefing ledger.
340
+ * Open issues receive evidence cards and Linear comments; a material event on a done issue records a memo-update card and reopens it.
341
+ */
342
+ export async function runIntakeIssueBriefing(input: IntakeIssueBriefingInput, deps: IntakeIssueBriefingDeps): Promise<void> {
343
+ if (!input.issueRef.trim() || !input.topic.trim()) throw new Error('issueRef and topic are required');
344
+ const source: RegisteredIntakeSource = {
345
+ id: issueWatchId(input.issueRef), seat: 'user', kind: 'github-query', spec: input.topic, every: '1d',
346
+ why: `linear-secretary:${input.issueRef}`, issueRef: input.issueRef,
347
+ };
348
+ if (deps.registerSource) deps.registerSource(source);
349
+ else if (!listSources({}, deps.stateDir).some(entry => entry.id === source.id)) addSource(source, deps.stateDir);
350
+ if (!input.collected) await (deps.runSources ?? (() => runDue({ deps: { stateDir: deps.stateDir } })))();
351
+
352
+ const relevant = input.state === 'open' ? input.evidence : input.evidence.filter(item => item.reopensIssue === true);
353
+ if (relevant.length === 0) return;
354
+ const ledger = deps.ledger ?? new BriefItemsLedger({ stateDir: deps.stateDir });
355
+ for (const item of relevant) {
356
+ ledger.add({
357
+ text: input.state === 'done' ? `메모 갱신? ${item.title}: ${item.summary}` : `${item.title}: ${item.summary}`,
358
+ domain: '흡수', priority: item.reopensIssue ? 'P0' : 'P1', source: 'intake-daily', evidence: item.url, issueRef: input.issueRef,
359
+ });
360
+ }
361
+ const body = issueBriefingComment(input, relevant);
362
+ await (deps.commentIssue ?? ((issueRef, comment) => defaultCommentIssue(deps.stateDir, issueRef, comment)))(input.issueRef, body);
363
+ if (input.state === 'done' && relevant.some(item => item.reopensIssue)) {
364
+ await (deps.reopenIssue ?? (issueRef => defaultReopenIssue(deps.stateDir, issueRef)))(input.issueRef);
365
+ }
366
+ }
13
367
 
14
368
  export interface ReplayIntakeResult {
15
369
  sourceId: string;
@@ -1,6 +1,8 @@
1
1
  import { maybeHandleAmbientTextIntake } from '../ambient.js';
2
2
  import type { IntakeAmbientCaptureMode } from '../../user-config.js';
3
3
 
4
+ const LINEAR_SECRETARY_INTENT = /(?:리니어|linear)(?:로|에|에다)?\s*(?:등록해(?:줘요?|주세요)?|정리해(?:줘요?|주세요)|쪼개(?:줘요?|주세요)|맡겨(?:줘요?|주세요)|만들어(?:줘요?|주세요))(?!\s*(?:라고|하던데|했는데|하라던데|해달라고|하라며))/i;
5
+
4
6
  export interface TelegramAmbientMessageContext {
5
7
  text: string;
6
8
  chatId: number | string;
@@ -10,22 +12,36 @@ export interface TelegramAmbientMessageContext {
10
12
  ambientCapture?: IntakeAmbientCaptureMode;
11
13
  }
12
14
 
13
- export async function maybeHandleTelegramAmbientMessage(
14
- ctx: TelegramAmbientMessageContext,
15
- ): Promise<string | null> {
16
- if (!ctx.text || ctx.text.startsWith('/')) return null;
17
- return maybeHandleAmbientTextIntake({
18
- surface: 'telegram',
19
- source: 'telegram',
20
- text: ctx.text,
21
- actor: {
22
- id: String(ctx.userId),
23
- ...(ctx.userName ? { display: ctx.userName } : {}),
24
- },
25
- channelContext: {
26
- chatId: String(ctx.chatId),
27
- ...(ctx.threadId != null ? { threadId: String(ctx.threadId) } : {}),
28
- },
29
- mode: ctx.ambientCapture ?? 'off',
30
- });
15
+ export function createTelegramAmbientMessageHandler(deps: {
16
+ runSecretary?: (request: string) => Promise<{ replyCard: string }>;
17
+ } = {}) {
18
+ return async function maybeHandleTelegramAmbientMessage(ctx: TelegramAmbientMessageContext): Promise<string | null> {
19
+ if (!ctx.text || ctx.text.startsWith('/')) return null;
20
+ if (LINEAR_SECRETARY_INTENT.test(ctx.text)) {
21
+ const runSecretary = deps.runSecretary ?? (async (request: string) =>
22
+ (await import('../linear-secretary-runtime.js')).runLinearSecretaryRequest(request));
23
+ try {
24
+ const result = await runSecretary(ctx.text);
25
+ return result.replyCard;
26
+ } catch {
27
+ return 'Linear 등록을 보류했습니다. 요청 날짜와 근거를 확인한 뒤 다시 시도해 주세요.';
28
+ }
29
+ }
30
+ return maybeHandleAmbientTextIntake({
31
+ surface: 'telegram',
32
+ source: 'telegram',
33
+ text: ctx.text,
34
+ actor: {
35
+ id: String(ctx.userId),
36
+ ...(ctx.userName ? { display: ctx.userName } : {}),
37
+ },
38
+ channelContext: {
39
+ chatId: String(ctx.chatId),
40
+ ...(ctx.threadId != null ? { threadId: String(ctx.threadId) } : {}),
41
+ },
42
+ mode: ctx.ambientCapture ?? 'off',
43
+ });
44
+ };
31
45
  }
46
+
47
+ export const maybeHandleTelegramAmbientMessage = createTelegramAmbientMessageHandler();
@@ -2,20 +2,27 @@ import { randomUUID } from 'node:crypto';
2
2
  import { execFileSync } from 'node:child_process';
3
3
  import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
+ import cron from 'node-cron';
5
6
  import { effectiveInstanceRoot } from '../instance/resolve.js';
6
7
  import { findTrack } from '../autopilot/mission-phase-track.js';
7
8
  import { appendGithubStarSnapshots, collectGithubStars, ghApiSearchRepos, type SearchGithubRepos } from './collect-github.js';
8
9
  import { ingestIntakeItems, parseRawIntakeJsonl, type IntakeSource, type RegisteredIntakeSourceKind, type RawIntakeItem } from './items.js';
9
10
 
10
- export type SourceKind = 'github-query' | 'rss' | 'command';
11
+ export type SourceKind = 'github-query' | 'rss' | 'command' | 'topic-search';
11
12
  export interface RegisteredIntakeSource {
12
13
  id: string;
13
14
  seat: string;
14
15
  kind: SourceKind;
15
16
  spec: string;
16
17
  every: string;
18
+ /** Per-topic recurring fire time, interpreted by the existing schedule registry/daemon path. */
19
+ scheduleCron?: string;
17
20
  until?: string;
18
21
  why?: string;
22
+ /** Existing Linear secretary issue identifier; absent for ordinary sources. */
23
+ issueRef?: string;
24
+ /** Parent Linear issue UUID; never replace with the display identifier. */
25
+ linearIssueId?: string;
19
26
  lastRunAt?: string;
20
27
  }
21
28
 
@@ -88,11 +95,19 @@ function commandArgv(spec: string): string[] {
88
95
 
89
96
  export function addSource(input: RegisteredIntakeSource, stateDir = effectiveInstanceRoot()): RegisteredIntakeSource {
90
97
  const seat = canonicalSeat(input.seat);
91
- if (!['github-query', 'rss', 'command'].includes(input.kind)) throw new Error(`알 수 없는 원천 종류: ${input.kind}`);
98
+ if (!['github-query', 'rss', 'command', 'topic-search'].includes(input.kind)) throw new Error(`알 수 없는 원천 종류: ${input.kind}`);
92
99
  cadenceMs(input.every);
93
100
  if (input.until !== undefined) untilMs(input.until);
94
101
  if (!input.id?.trim() || !/^[\w.-]+$/.test(input.id)) throw new Error('원천 id 는 영문·숫자·_·-·. 만 허용한다');
95
102
  if (!input.spec?.trim()) throw new Error('원천 spec 이 비었다');
103
+ if (input.linearIssueId !== undefined && (input.kind !== 'topic-search'
104
+ || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(input.linearIssueId))) {
105
+ throw new Error('topic-search linearIssueId 는 부모 Linear issue UUID여야 한다');
106
+ }
107
+ if (input.kind === 'topic-search' && (!input.linearIssueId || !input.scheduleCron || !cron.validate(input.scheduleCron))) {
108
+ throw new Error('topic-search 는 부모 Linear UUID와 유효한 개별 schedule cron 이 필요하다');
109
+ }
110
+ if (input.kind !== 'topic-search' && input.scheduleCron !== undefined) throw new Error('scheduleCron 은 topic-search 원천만 가질 수 있다');
96
111
  if (input.kind === 'rss') {
97
112
  let protocol = '';
98
113
  try { protocol = new URL(input.spec).protocol; } catch { /* validation below */ }
@@ -118,8 +133,11 @@ export function removeSource(id: string, stateDir = effectiveInstanceRoot()): bo
118
133
  return true;
119
134
  }
120
135
 
121
- export function dueSources(now: Date = new Date(), stateDir = effectiveInstanceRoot()): RegisteredIntakeSource[] {
122
- return listSources({}, stateDir).filter((source) => sourceIsDue(source, now));
136
+ export function dueSources(now: Date = new Date(), stateDir = effectiveInstanceRoot(), sourceId?: string): RegisteredIntakeSource[] {
137
+ return listSources({}, stateDir).filter((source) => (sourceId === undefined || source.id === sourceId)
138
+ && (sourceId !== undefined && source.kind === 'topic-search'
139
+ ? !expiredSource(source, now)
140
+ : sourceIsDue(source, now)));
123
141
  }
124
142
 
125
143
  export interface RunDueDeps {
@@ -127,7 +145,39 @@ export interface RunDueDeps {
127
145
  searchGithub?: SearchGithubRepos;
128
146
  fetchFeed?: (url: string) => Promise<string>;
129
147
  runCommand?: (command: string) => Promise<string>;
148
+ searchTopic?: (topic: string) => Promise<RawIntakeItem[]>;
149
+ addBriefItem?: (input: import('../briefing/brief-items.js').AddBriefItem) => unknown;
150
+ projectTopicBriefing?: (source: RegisteredIntakeSource, items: import('../briefing/brief-items.js').BriefItem[], stateDir?: string) => Promise<void>;
130
151
  ingest?: typeof ingestIntakeItems;
152
+ onCollected?: (source: RegisteredIntakeSource, raws: RawIntakeItem[], now: Date) => Promise<void> | void;
153
+ }
154
+
155
+ async function searchTopicWithResearchBridge(topic: string): Promise<RawIntakeItem[]> {
156
+ const { invokeResearchAxes } = await import('../research-bridge/index.js');
157
+ const fanout = await invokeResearchAxes([{ id: 'intake-topic', prompt: topic, level: 3, searchTerms: [topic] }]);
158
+ const evidence = fanout.axes.find((axis) => axis.questionId === 'intake-topic')?.evidence ?? [];
159
+ return evidence.filter((entry) => entry.outcome !== 'failed' && /^https?:\/\//i.test(entry.source))
160
+ .map((entry) => {
161
+ const [title, ...summary] = entry.content.split(' — ');
162
+ return { url: entry.source, title, text: summary.join(' — ') || entry.content, kind: 'article' };
163
+ });
164
+ }
165
+
166
+ async function addTopicBriefItems(stateDir: string, source: RegisteredIntakeSource, items: readonly RawIntakeItem[], createdAt: string,
167
+ addBriefItem?: RunDueDeps['addBriefItem']): Promise<void> {
168
+ const ledger = addBriefItem ? null : new (await import('../briefing/brief-items.js')).BriefItemsLedger({ stateDir });
169
+ for (const item of items) {
170
+ if (!item.url) continue;
171
+ const text = `새 토픽 자료: ${(item.title ?? item.text ?? item.url).replace(/[\r\n]+/g, ' ').trim()}`;
172
+ const input = {
173
+ text, domain: '흡수' as const, priority: 'P1' as const, source: `intake.topic-search:${source.id}`,
174
+ evidence: item.url, createdAt,
175
+ ...(source.linearIssueId ? { linearIssueId: source.linearIssueId } : {}),
176
+ dedupeKey: `${source.linearIssueId ?? source.id}:${item.url}`, dedupeWithinMs: Math.max(86_400_000, cadenceMs(source.every)),
177
+ };
178
+ if (addBriefItem) addBriefItem(input);
179
+ else ledger!.add(input);
180
+ }
131
181
  }
132
182
 
133
183
  function xmlText(value: string): string {
@@ -150,11 +200,13 @@ export function parseFeed(xml: string): RawIntakeItem[] {
150
200
  }).filter((entry) => !!entry.url);
151
201
  }
152
202
 
153
- export async function runDue({ now = new Date(), seat, deps = {} }: { now?: Date; seat?: string; deps?: RunDueDeps } = {}): Promise<{ ran: Array<{ id: string; items: number; error?: string }> }> {
203
+ export async function runDue({ now = new Date(), seat, sourceId, deps = {} }: { now?: Date; seat?: string; sourceId?: string; deps?: RunDueDeps } = {}): Promise<{ ran: Array<{ id: string; items: number; error?: string }> }> {
154
204
  const stateDir = deps.stateDir ?? effectiveInstanceRoot();
155
205
  const selectedSeat = seat === undefined ? undefined : canonicalSeat(seat);
156
206
  const ran: Array<{ id: string; items: number; error?: string }> = [];
157
- for (const source of dueSources(now, stateDir).filter((source) => selectedSeat === undefined || source.seat === selectedSeat)) {
207
+ for (const source of dueSources(now, stateDir, sourceId).filter((source) =>
208
+ (selectedSeat === undefined || source.seat === selectedSeat)
209
+ && (source.kind !== 'topic-search' || !!deps.projectTopicBriefing))) {
158
210
  try {
159
211
  let raws: RawIntakeItem[];
160
212
  let githubSnapshots: { day: string; repo: string; stars: number }[] | undefined;
@@ -176,6 +228,9 @@ export async function runDue({ now = new Date(), seat, deps = {} }: { now?: Date
176
228
  }))(source.spec);
177
229
  raws = parseFeed(feed);
178
230
  intakeSource = 'rss';
231
+ } else if (source.kind === 'topic-search') {
232
+ raws = await (deps.searchTopic ?? searchTopicWithResearchBridge)(source.spec);
233
+ intakeSource = 'topic-search';
179
234
  } else {
180
235
  const argv = commandArgv(source.spec);
181
236
  const stdout = await (deps.runCommand ?? (async () => execFileSync(argv[0]!, argv.slice(1), { encoding: 'utf8', timeout: 600_000, maxBuffer: 16 * 1024 * 1024 })))(source.spec);
@@ -188,9 +243,24 @@ export async function runDue({ now = new Date(), seat, deps = {} }: { now?: Date
188
243
  raws = parsed.raws;
189
244
  intakeSource = 'command';
190
245
  }
191
- const items = (deps.ingest ?? ingestIntakeItems)(stateDir, intakeSource, raws.map((raw) => ({ ...raw, seat: source.seat })), now.toISOString()).shaped;
246
+ raws = raws.map((raw) => ({ ...raw, seat: source.seat,
247
+ ...(source.kind === 'topic-search' && source.linearIssueId ? { linearIssueId: source.linearIssueId } : {}) }));
248
+ const items = (deps.ingest ?? ingestIntakeItems)(stateDir, intakeSource, raws, now.toISOString()).shaped;
249
+ if (source.kind === 'topic-search') await addTopicBriefItems(stateDir, source, raws, now.toISOString(), deps.addBriefItem);
250
+ await deps.onCollected?.(source, raws, now);
192
251
  if (githubSnapshots) appendGithubStarSnapshots(stateDir, githubSnapshots);
193
- // Re-read after each run so one failure cannot overwrite another source's successful timestamp.
252
+ if (source.kind === 'topic-search' && source.linearIssueId && deps.projectTopicBriefing) {
253
+ const { BriefItemsLedger } = await import('../briefing/brief-items.js');
254
+ const ledger = new BriefItemsLedger({ stateDir });
255
+ const pending = ledger.list().filter(item => item.source === `intake.topic-search:${source.id}`
256
+ && item.linearIssueId?.toLowerCase() === source.linearIssueId!.toLowerCase() && item.sent_at === null);
257
+ if (pending.length) {
258
+ await deps.projectTopicBriefing(source, pending, stateDir);
259
+ // This dedicated issue card/comment is the delivery for these brief items.
260
+ ledger.markSent(pending.map(item => item.id), 'realtime');
261
+ }
262
+ }
263
+ // A projection failure must remain retryable; advance cadence only after delivery succeeds.
194
264
  const current = readSources(stateDir);
195
265
  const found = current.find((entry) => entry.id === source.id);
196
266
  if (found) saveSources(stateDir, current.map((entry) => entry.id === source.id ? { ...entry, lastRunAt: now.toISOString() } : entry));
@@ -8,7 +8,7 @@ import { findTrack } from '../autopilot/mission-phase-track.js';
8
8
  import { wasIntakeEventScheduled } from './route.js';
9
9
 
10
10
  export const INTAKE_SOURCES = ['x', 'youtube', 'github', 'telegram-saved', 'telegram-bot', 'memo', 'pwa'] as const;
11
- export type RegisteredIntakeSourceKind = 'rss' | 'command';
11
+ export type RegisteredIntakeSourceKind = 'rss' | 'command' | 'topic-search';
12
12
  export type IntakeSource = typeof INTAKE_SOURCES[number];
13
13
  export type IntakeKind = 'video' | 'repo' | 'post' | 'article' | 'note' | 'rss';
14
14
  export type IntakePrivacy = 'public' | 'user-private';
@@ -21,6 +21,8 @@ export interface IntakeItem {
21
21
  /** 같은 항목이 다른 입력원에서도 왔으면 여기 모인다 — «두 입력원에서 동시에 뜬 것»이 신호다. */
22
22
  sources: (IntakeSource | RegisteredIntakeSourceKind)[];
23
23
  seat?: string;
24
+ /** Parent Linear issue UUID, kept separate from its human-readable identifier. */
25
+ linearIssueId?: string;
24
26
  url?: string;
25
27
  kind: IntakeKind;
26
28
  title?: string;
@@ -39,6 +41,7 @@ export interface IntakeItem {
39
41
  /** 수집기가 내는 한 줄 — 모양 맞추기 전. */
40
42
  export interface RawIntakeItem {
41
43
  seat?: string;
44
+ linearIssueId?: string;
42
45
  url?: string;
43
46
  title?: string;
44
47
  text?: string;
@@ -90,10 +93,12 @@ function normalizedSeat(seat: unknown): string | undefined {
90
93
  return track.id;
91
94
  }
92
95
 
93
- export function intakeItemId(source: IntakeSource | RegisteredIntakeSourceKind, raw: Pick<RawIntakeItem, 'url' | 'text'> & { seat?: string }): string {
96
+ export function intakeItemId(source: IntakeSource | RegisteredIntakeSourceKind,
97
+ raw: Pick<RawIntakeItem, 'url' | 'text' | 'linearIssueId'> & { seat?: string }): string {
94
98
  const seat = normalizedSeat(raw.seat);
95
99
  const url = raw.url ? canonicalUrl(raw.url) : undefined;
96
- const basis = url ?? `${source}:${(raw.text ?? '').trim().slice(0, 200)}`;
100
+ const itemKey = url ?? `${source}:${(raw.text ?? '').trim().slice(0, 200)}`;
101
+ const basis = raw.linearIssueId ? JSON.stringify(['linear-issue', raw.linearIssueId.toLowerCase(), itemKey]) : itemKey;
97
102
  // Unassigned items retain their original IDs; assigned items have an independent lifecycle per seat.
98
103
  return createHash('sha256').update(seat ? JSON.stringify([seat, basis]) : basis).digest('hex').slice(0, 16);
99
104
  }
@@ -114,6 +119,9 @@ export function privacyFor(source: IntakeSource | RegisteredIntakeSourceKind): I
114
119
  /** ① 모양 맞추기 — 수집기 한 줄 → IntakeItem. 비어 있으면(URL·본문 둘 다 없음) null. */
115
120
  export function shapeIntakeItem(source: IntakeSource | RegisteredIntakeSourceKind, raw: RawIntakeItem, now: string): IntakeItem | null {
116
121
  const seat = normalizedSeat(raw.seat);
122
+ if (raw.linearIssueId !== undefined && !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(raw.linearIssueId)) {
123
+ throw new Error('invalid Linear issue UUID');
124
+ }
117
125
  const url = raw.url ? canonicalUrl(raw.url) : undefined;
118
126
  const text = raw.text?.trim() || undefined;
119
127
  if (!url && !text) return null;
@@ -121,10 +129,11 @@ export function shapeIntakeItem(source: IntakeSource | RegisteredIntakeSourceKin
121
129
  const signals: Record<string, number> = {};
122
130
  for (const [k, v] of Object.entries(raw.signals ?? {})) if (Number.isFinite(v)) signals[`${source}.${k}`] = v;
123
131
  return {
124
- id: intakeItemId(source, { url, text, seat }),
132
+ id: intakeItemId(source, { url, text, seat, linearIssueId: raw.linearIssueId }),
125
133
  source,
126
134
  sources: [source],
127
135
  ...(seat ? { seat } : {}),
136
+ ...(raw.linearIssueId ? { linearIssueId: raw.linearIssueId } : {}),
128
137
  ...(url ? { url } : {}),
129
138
  kind: raw.kind ?? kindFor(source, url),
130
139
  ...(raw.title?.trim() ? { title: raw.title.trim().slice(0, 300) } : {}),
@@ -191,6 +200,7 @@ export function ingestIntakeItems(instanceRoot: string, source: IntakeSource | R
191
200
  signals: { ...prev.signals, ...shaped.signals },
192
201
  title: prev.title ?? shaped.title,
193
202
  text: prev.text ?? shaped.text,
203
+ linearIssueId: prev.linearIssueId ?? shaped.linearIssueId,
194
204
  axis: shaped.axis ?? prev.axis,
195
205
  judgement: shaped.judgement ?? prev.judgement,
196
206
  // 한 번이라도 개인 입력원에서 왔으면 개인으로 남긴다(공개로 풀지 않는다).