elanous 0.2.10 → 0.2.12

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 (615) 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/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  8. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  18. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  19. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  24. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-5c05b0da4bc1392c.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-a67e11ba962337b5.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-793fa536b3a6ef37.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-e297866051595d81.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
  105. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
  106. package/apps/pwa/out/approvals/index.html +1 -1
  107. package/apps/pwa/out/approvals/index.txt +23 -23
  108. package/apps/pwa/out/autopilot/index.html +1 -1
  109. package/apps/pwa/out/autopilot/index.txt +23 -23
  110. package/apps/pwa/out/board/index.html +1 -1
  111. package/apps/pwa/out/board/index.txt +23 -23
  112. package/apps/pwa/out/botlab/index.html +1 -1
  113. package/apps/pwa/out/botlab/index.txt +22 -22
  114. package/apps/pwa/out/bots/index.html +1 -1
  115. package/apps/pwa/out/bots/index.txt +22 -22
  116. package/apps/pwa/out/chat/index.html +1 -1
  117. package/apps/pwa/out/chat/index.txt +22 -22
  118. package/apps/pwa/out/consult/index.html +1 -1
  119. package/apps/pwa/out/consult/index.txt +23 -23
  120. package/apps/pwa/out/control/index.html +1 -1
  121. package/apps/pwa/out/control/index.txt +23 -23
  122. package/apps/pwa/out/design-check/index.html +1 -1
  123. package/apps/pwa/out/design-check/index.txt +23 -23
  124. package/apps/pwa/out/editor/index.html +1 -1
  125. package/apps/pwa/out/editor/index.txt +23 -23
  126. package/apps/pwa/out/exec/index.html +1 -1
  127. package/apps/pwa/out/exec/index.txt +23 -23
  128. package/apps/pwa/out/field/index.html +1 -1
  129. package/apps/pwa/out/field/index.txt +23 -23
  130. package/apps/pwa/out/healthz +1 -1
  131. package/apps/pwa/out/index.html +1 -1
  132. package/apps/pwa/out/index.txt +22 -22
  133. package/apps/pwa/out/inside/index.html +1 -0
  134. package/apps/pwa/out/inside/index.txt +31 -0
  135. package/apps/pwa/out/intake/index.html +1 -1
  136. package/apps/pwa/out/intake/index.txt +23 -23
  137. package/apps/pwa/out/live/index.html +1 -1
  138. package/apps/pwa/out/live/index.txt +23 -23
  139. package/apps/pwa/out/market/index.html +1 -1
  140. package/apps/pwa/out/market/index.txt +22 -22
  141. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  142. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  143. package/apps/pwa/out/morning/index.html +1 -1
  144. package/apps/pwa/out/morning/index.txt +23 -23
  145. package/apps/pwa/out/observatory/index.html +1 -1
  146. package/apps/pwa/out/observatory/index.txt +23 -23
  147. package/apps/pwa/out/ops/checklist/index.html +1 -1
  148. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  149. package/apps/pwa/out/ops/release/index.html +1 -1
  150. package/apps/pwa/out/ops/release/index.txt +22 -22
  151. package/apps/pwa/out/ops/seats/index.html +1 -1
  152. package/apps/pwa/out/ops/seats/index.txt +22 -22
  153. package/apps/pwa/out/outputs/index.html +1 -0
  154. package/apps/pwa/out/outputs/index.txt +30 -0
  155. package/apps/pwa/out/reflection/index.html +1 -1
  156. package/apps/pwa/out/reflection/index.txt +23 -23
  157. package/apps/pwa/out/scheduler/index.html +1 -1
  158. package/apps/pwa/out/scheduler/index.txt +23 -23
  159. package/apps/pwa/out/sessions/index.html +1 -1
  160. package/apps/pwa/out/sessions/index.txt +22 -22
  161. package/apps/pwa/out/settings/devices/index.html +1 -1
  162. package/apps/pwa/out/settings/devices/index.txt +23 -23
  163. package/apps/pwa/out/settings/index.html +1 -1
  164. package/apps/pwa/out/settings/index.txt +23 -23
  165. package/apps/pwa/out/setup/done/index.html +1 -1
  166. package/apps/pwa/out/setup/done/index.txt +23 -23
  167. package/apps/pwa/out/setup/index.html +1 -1
  168. package/apps/pwa/out/setup/index.txt +23 -23
  169. package/apps/pwa/out/share/index.html +1 -1
  170. package/apps/pwa/out/share/index.txt +23 -23
  171. package/apps/pwa/out/showroom/index.html +1 -1
  172. package/apps/pwa/out/showroom/index.txt +22 -22
  173. package/apps/pwa/out/tasks/index.html +1 -1
  174. package/apps/pwa/out/tasks/index.txt +23 -23
  175. package/apps/pwa/out/term/index.html +1 -1
  176. package/apps/pwa/out/term/index.txt +23 -24
  177. package/apps/pwa/out/today/index.html +1 -1
  178. package/apps/pwa/out/today/index.txt +23 -23
  179. package/apps/pwa/out/trace/index.html +1 -1
  180. package/apps/pwa/out/trace/index.txt +23 -23
  181. package/apps/pwa/out/vault/index.html +1 -1
  182. package/apps/pwa/out/vault/index.txt +23 -23
  183. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  184. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  185. package/apps/pwa/out/workflows/index.html +1 -1
  186. package/apps/pwa/out/workflows/index.txt +23 -23
  187. package/apps/pwa/out/workspace/index.html +1 -1
  188. package/apps/pwa/out/workspace/index.txt +23 -24
  189. package/apps/pwa/out/worktrees/index.html +1 -1
  190. package/apps/pwa/out/worktrees/index.txt +23 -23
  191. package/graphs/context/nightly-condense.yaml +21 -0
  192. package/graphs/context/recipes.yaml +12 -0
  193. package/graphs/demo/inside-seed.yaml +20 -0
  194. package/graphs/demo/recipes.yaml +12 -0
  195. package/graphs/drafts/draft-cleanup.yaml +19 -0
  196. package/graphs/drafts/recipes.yaml +23 -0
  197. package/graphs/intake/intake-daily.yaml +2 -1
  198. package/graphs/ops/recipes.yaml +3 -0
  199. package/graphs/ops/secret-exposure.yaml +24 -0
  200. package/graphs/release/recipes.yaml +3 -0
  201. package/graphs/release/release-loop.yaml +4 -1
  202. package/graphs/video/explainer-line.yaml +2 -2
  203. package/package.json +2 -1
  204. package/plugins/card-followup/examples/sample-card.png +0 -0
  205. package/plugins/elanous-hwp/README.md +25 -0
  206. package/plugins/elanous-hwp/cli.ts +22 -0
  207. package/plugins/elanous-hwp/converter.ts +237 -0
  208. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  209. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  210. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  211. package/plugins/elanous-hwp/plugin.json +16 -0
  212. package/plugins/elanous-hwp/plugin.ts +40 -0
  213. package/scripts/ci-public-export-leak-gate.ts +75 -14
  214. package/scripts/ci-public-leak-gate.ts +44 -9
  215. package/scripts/context-hooks/claude-stop.sh +4 -0
  216. package/scripts/context-hooks/emit.ts +24 -0
  217. package/scripts/context-hooks/post-tool-use.sh +4 -0
  218. package/scripts/coord-channel-watch.sh +12 -0
  219. package/scripts/coord-requests-bridge.ts +143 -0
  220. package/scripts/field-feed/lib.ts +4 -0
  221. package/scripts/fleet/node-setup.sh +20 -3
  222. package/scripts/homebrew-formula-bump.ts +69 -0
  223. package/scripts/lib/telegram-inject-args.ts +31 -0
  224. package/scripts/marketing/measure-site-numbers.ts +169 -0
  225. package/scripts/measure-gate-shards.ts +309 -0
  226. package/scripts/measure-pod-memory-by-goal.ts +130 -0
  227. package/scripts/mission-request-judge.ts +31 -1
  228. package/scripts/morning-report.ts +2 -2
  229. package/scripts/opportunity-scan.ts +1 -1
  230. package/scripts/release-loop/auto-approve-node.ts +21 -4
  231. package/scripts/release-loop/checklist-gate-node.ts +2 -1
  232. package/scripts/release-loop/cut-branch.ts +84 -0
  233. package/scripts/release-loop/gate-node.ts +22 -15
  234. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  235. package/scripts/release-loop/node-verdict.ts +3 -3
  236. package/scripts/release-loop/ops-upgrade-node.ts +47 -6
  237. package/scripts/release-loop/publish-node.ts +4 -1
  238. package/scripts/release-loop/tui-node-checks.ts +42 -0
  239. package/scripts/release-loop/tui-sim-node.ts +32 -8
  240. package/scripts/release-loop/unattended-release.ts +5 -3
  241. package/scripts/release-loop/version-node.ts +74 -13
  242. package/scripts/signal-digest-cycle.ts +2 -7
  243. package/scripts/signal-digest-photo.ts +11 -0
  244. package/scripts/tech1-public-measure.ts +94 -0
  245. package/scripts/telegram-inject.ts +14 -6
  246. package/scripts/test-diet/node.ts +1 -1
  247. package/scripts/test-diet/remeasure-list.ts +112 -0
  248. package/scripts/tui-regress.ts +2 -2
  249. package/scripts/ux/demo-rehearsal.ts +247 -0
  250. package/scripts/ux/demo-seed.ts +108 -0
  251. package/scripts/ux/lib/rehearsal-verdict.ts +80 -0
  252. package/skills/diagram-master/SKILL.md +366 -0
  253. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  254. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  255. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  256. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  257. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  258. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  259. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  260. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  261. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  262. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  263. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  264. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  265. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  266. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  267. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  268. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  269. package/skills/diagram-master/references/examples/README.md +69 -0
  270. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  271. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  272. package/skills/diagram-master/references/pyproject.toml +16 -0
  273. package/skills/diagram-master/references/quality-checklist.md +33 -0
  274. package/skills/diagram-master/references/routing-guide.md +164 -0
  275. package/skills/diagram-master/references/uv.lock +1356 -0
  276. package/skills/google-workspace/SKILL.md +33 -0
  277. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  278. package/skills/grill-me/SKILL.md +1 -1
  279. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  280. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  281. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  282. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  283. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  284. package/skills/omni-crawl/SKILL.md +21 -11
  285. package/skills/omni-crawl/scripts/main.ts +6 -6
  286. package/skills/omni-crawl/src/router.ts +3 -3
  287. package/skills/omni-digest/SKILL.md +17 -9
  288. package/skills/omni-digest/digest.md +2 -2
  289. package/skills/omni-digest/src/fetch.ts +30 -3
  290. package/skills/pitch-deck/SKILL.md +2 -2
  291. package/skills/pitch-deck/template.html +2 -2
  292. package/skills/youtube-master/SKILL.md +163 -0
  293. package/skills/youtube-master/package-lock.json +558 -0
  294. package/skills/youtube-master/package.json +13 -0
  295. package/skills/youtube-master/references/study-note-template.md +124 -0
  296. package/skills/youtube-master/scripts/main.ts +435 -0
  297. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  298. package/skills/youtube-master/src/audio.ts +63 -0
  299. package/skills/youtube-master/src/domain-staging.ts +50 -0
  300. package/skills/youtube-master/src/env.ts +36 -0
  301. package/skills/youtube-master/src/obsidian.ts +155 -0
  302. package/skills/youtube-master/src/router.ts +110 -0
  303. package/skills/youtube-master/src/stt.ts +199 -0
  304. package/skills/youtube-master/src/study-note.ts +175 -0
  305. package/skills/youtube-master/src/summarize.ts +358 -0
  306. package/skills/youtube-master/src/transcript.ts +76 -0
  307. package/skills/youtube-master/src/types.ts +91 -0
  308. package/skills/youtube-master/src/util.ts +93 -0
  309. package/skills/youtube-master/src/youtube.ts +70 -0
  310. package/skills/youtube-master/tsconfig.json +13 -0
  311. package/src/acp/server.ts +5 -2
  312. package/src/agent-mission/driver.ts +215 -23
  313. package/src/agent-mission/pty-decision.ts +71 -0
  314. package/src/agent-mission/review-loop.ts +30 -0
  315. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  316. package/src/autopilot/pty-control-loop.ts +4 -1
  317. package/src/autopilot/tool-registry.ts +2 -0
  318. package/src/boot/daemon-prompt-turn.ts +5 -0
  319. package/src/boot/daemon-runtime.ts +9 -3
  320. package/src/boot/daemon-tools/web-search.ts +3 -0
  321. package/src/card-followup/core.ts +109 -0
  322. package/src/chat/index.ts +2 -0
  323. package/src/cli/drive-input-jail.ts +101 -0
  324. package/src/cli/hooks-cli.ts +17 -3
  325. package/src/cli/make-cli.ts +43 -0
  326. package/src/cli/persona-cli.ts +98 -9
  327. package/src/cli/plugin-cli.ts +31 -22
  328. package/src/cli/pod-cli.ts +36 -9
  329. package/src/cli/pod-memory-advice.ts +35 -0
  330. package/src/cli/pr-cli.ts +51 -15
  331. package/src/cli/pty-drive-cli.ts +41 -7
  332. package/src/cli/pty-drive-verdict.ts +40 -0
  333. package/src/cli/release-cli.ts +63 -10
  334. package/src/cli/setup-cli.ts +43 -1
  335. package/src/cli/skills-cli.ts +17 -1
  336. package/src/context-bus/claude-hooks-install.ts +10 -0
  337. package/src/context-bus/context-now-surfaces.ts +65 -0
  338. package/src/context-bus/context-now.ts +104 -0
  339. package/src/context-bus/coord-events.ts +2 -2
  340. package/src/context-bus/external-events.ts +52 -0
  341. package/src/context-bus/long-term-memory.ts +108 -0
  342. package/src/context-bus/nightly-condense.ts +153 -0
  343. package/src/context-bus/session-events.ts +35 -0
  344. package/src/dashboard/dashboard-status-lines.ts +2 -0
  345. package/src/dashboard/index.ts +17 -4
  346. package/src/dashboard/slash-runtime/dashboard-handlers.ts +31 -1
  347. package/src/decisions/decision-cards.ts +10 -3
  348. package/src/decisions/decision-ledger.ts +13 -5
  349. package/src/discord/sprint21-runtime.ts +5 -1
  350. package/src/discord-self-message.ts +32 -0
  351. package/src/discord-slash-wire.ts +5 -1
  352. package/src/discord.ts +17 -1
  353. package/src/domains/core-tools.ts +3 -2
  354. package/src/domains/outbound-alert.ts +105 -32
  355. package/src/domains/outbound-log-probe.ts +8 -0
  356. package/src/domains/telegram-kind-route.ts +51 -13
  357. package/src/exec-requests/default-outputs.ts +25 -0
  358. package/src/exec-requests/planner.ts +10 -2
  359. package/src/exec-requests/runner.ts +20 -6
  360. package/src/exec-requests/store.ts +3 -0
  361. package/src/execution-loop/launch-gate.ts +109 -0
  362. package/src/field/field-reel.ts +5 -1
  363. package/src/field-note/index.ts +201 -0
  364. package/src/field-note/telegram.ts +142 -0
  365. package/src/field-note/transcribe.ts +248 -0
  366. package/src/graph-kinds/registry.ts +3 -3
  367. package/src/graph-runner/graph-cli.ts +61 -1
  368. package/src/graph-runner/graph-grow-llm.ts +47 -0
  369. package/src/graph-runner/graph-grow.ts +50 -0
  370. package/src/graph-runner/runner.ts +329 -33
  371. package/src/grok/agent-search.ts +8 -2
  372. package/src/guardian/secret-exposure-scan.ts +101 -0
  373. package/src/harness/harness-cli-command.ts +375 -13
  374. package/src/harness/harness-pod-dispatch.ts +43 -23
  375. package/src/harness/harness-seams.ts +38 -4
  376. package/src/harness/harness-stop.ts +23 -4
  377. package/src/harness/launch-registry.ts +91 -0
  378. package/src/harness/process-garbage.ts +5 -3
  379. package/src/hooks/consult-intake.ts +153 -0
  380. package/src/hooks/dispatch.ts +82 -0
  381. package/src/hooks/expose.ts +254 -0
  382. package/src/hooks/github-shadow.ts +108 -0
  383. package/src/hooks/providers.ts +71 -5
  384. package/src/hooks/queue.ts +1 -1
  385. package/src/hooks/receiver.ts +94 -16
  386. package/src/index.ts +76 -14
  387. package/src/intake-plane/categorize.ts +18 -46
  388. package/src/intake-plane/discord-seat-work.ts +38 -2
  389. package/src/intake-plane/front-route-classifier.ts +33 -39
  390. package/src/intake-plane/persona-answer.ts +30 -0
  391. package/src/intake-plane/runtime-callables.ts +8 -1
  392. package/src/intake-plane/telegram-seat-work.ts +36 -2
  393. package/src/intake-plane/wish-card.ts +33 -0
  394. package/src/intake-plane/wish-folder.ts +67 -0
  395. package/src/intent-gate/gate.ts +30 -10
  396. package/src/live/detail-switch.ts +5 -0
  397. package/src/llm/difficulty.ts +57 -0
  398. package/src/llm/judge-layer.ts +101 -0
  399. package/src/llm/mission-router.ts +33 -19
  400. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  401. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  402. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  403. package/src/loops/contract/loop-agent.schema.json +138 -0
  404. package/src/loops/contract/validate.ts +64 -0
  405. package/src/loops/registry.ts +20 -6
  406. package/src/market/signed-index.ts +4 -0
  407. package/src/maturity/bot-command-maturity.ts +45 -0
  408. package/src/maturity/feature-maturity.ts +27 -4
  409. package/src/model-tier/index.ts +2 -2
  410. package/src/model-tier/tier-resolver.ts +20 -20
  411. package/src/model-tier/types.ts +6 -6
  412. package/src/mss/logging/log-store.ts +15 -0
  413. package/src/nexus/api/config-model-tier.ts +5 -4
  414. package/src/nexus/api/fabric-plans.ts +53 -0
  415. package/src/nexus/api/harness-api.ts +5 -1
  416. package/src/nexus/api/health.ts +2 -0
  417. package/src/nexus/api/http-server.ts +67 -8
  418. package/src/nexus/api/inside-events.ts +226 -0
  419. package/src/nexus/api/meta-api.ts +21 -8
  420. package/src/nexus/api/missions.ts +6 -1
  421. package/src/nexus/api/ops-seats.ts +33 -0
  422. package/src/nexus/api/outbound-report.ts +40 -7
  423. package/src/nexus/api/outputs-route.ts +44 -0
  424. package/src/nexus/api/personas.ts +95 -53
  425. package/src/nexus/api/seat-requests.ts +44 -2
  426. package/src/nexus/api/sessions-store.ts +17 -38
  427. package/src/nexus/api/sub-seat-charters.ts +34 -0
  428. package/src/nexus/api/tabs-mutations.ts +3 -1
  429. package/src/nexus/api/task-cards-api.ts +19 -0
  430. package/src/nexus/api/trace.ts +18 -6
  431. package/src/nexus/api/workflow-history.ts +88 -0
  432. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  433. package/src/nexus/api/workflows.ts +132 -6
  434. package/src/nexus/index.ts +2 -0
  435. package/src/nexus/kinds/webterm.ts +2 -1
  436. package/src/nexus/outbound/delivery-ledger.ts +17 -8
  437. package/src/nexus/outbound/router.ts +8 -3
  438. package/src/nexus/webterm/pty.ts +6 -2
  439. package/src/onboarding/cli-auth-status.ts +80 -0
  440. package/src/onboarding/voice-ai.ts +3 -3
  441. package/src/org/seat-budget.ts +48 -0
  442. package/src/org/stall-escalation.ts +63 -0
  443. package/src/outputs/ledger.ts +120 -0
  444. package/src/persona/persona-todo.ts +79 -0
  445. package/src/persona/presets.ts +123 -2
  446. package/src/persona/team-set.ts +155 -0
  447. package/src/plugins/install/plugin-install.ts +14 -0
  448. package/src/plugins/maker/market-bundle.ts +110 -0
  449. package/src/plugins/maker/plugin-maker.ts +254 -11
  450. package/src/plugins/maker/wizard-generate.ts +103 -0
  451. package/src/pod-lease/admission.ts +150 -0
  452. package/src/pod-lease/dependency-state.ts +85 -0
  453. package/src/pod-lease/host-lease.ts +127 -0
  454. package/src/project/project-suggestion.ts +16 -0
  455. package/src/project/session-context.ts +24 -0
  456. package/src/release-loop/checklist.ts +17 -5
  457. package/src/release-loop/feature-store.ts +29 -13
  458. package/src/release-loop/landed-but-yellow.ts +48 -0
  459. package/src/release-loop/next-md-merge.ts +58 -0
  460. package/src/release-loop/release-note.ts +59 -4
  461. package/src/seat-dispatch/seat-ask.ts +130 -0
  462. package/src/seat-dispatch/seat-questions.ts +100 -0
  463. package/src/seat-loop/seat-loop.ts +709 -29
  464. package/src/seat-loop/seat-report.ts +37 -2
  465. package/src/self-dev/dev-cli.ts +4 -1
  466. package/src/self-dev/dev-pipeline.ts +37 -1
  467. package/src/self-dev/draft-sweep.ts +79 -40
  468. package/src/self-dev/draft-triage-rules.ts +62 -7
  469. package/src/self-dev/fabric-plan-core.ts +297 -0
  470. package/src/self-dev/orchestrate.ts +36 -4
  471. package/src/self-dev/release-path-guard.ts +62 -0
  472. package/src/self-dev/shard-aggregate.ts +230 -0
  473. package/src/self-dev/shard-placement.ts +60 -0
  474. package/src/self-dev/shard-plan.ts +48 -0
  475. package/src/self-dev/shard-pod-run.ts +77 -0
  476. package/src/self-dev/shard-run.ts +105 -0
  477. package/src/self-implement/budget-gate.ts +29 -12
  478. package/src/self-implement/gate-baseline.ts +1 -1
  479. package/src/self-implement/gate-candidate-ranking.ts +60 -0
  480. package/src/self-implement/gate-cli.ts +40 -1
  481. package/src/self-implement/gate-scope.ts +44 -13
  482. package/src/self-implement/graph-variant.ts +171 -0
  483. package/src/self-implement/harness-policy-gates.ts +1 -1
  484. package/src/self-implement/headless-elanous-driver.ts +27 -3
  485. package/src/self-implement/host-regate.ts +22 -1
  486. package/src/self-implement/importer-test-index.ts +2 -1
  487. package/src/self-implement/l8-merge-queue.ts +130 -0
  488. package/src/self-implement/orchestrator.ts +257 -9
  489. package/src/self-implement/route-consumer-test-index.ts +122 -0
  490. package/src/self-implement/seams.ts +72 -11
  491. package/src/self-implement/test-seams.ts +2 -0
  492. package/src/self-implement/uncommitted-work.ts +59 -0
  493. package/src/session/index.ts +36 -13
  494. package/src/skills/gift-install.ts +215 -0
  495. package/src/skills/tools/web-search.ts +4 -1
  496. package/src/steward/launch.ts +42 -26
  497. package/src/steward/steward-cards.ts +17 -9
  498. package/src/steward/triage.ts +46 -14
  499. package/src/task-cards/card-cli.ts +25 -1
  500. package/src/task-orchestrator/mission.ts +5 -0
  501. package/src/task-orchestrator/store.ts +12 -3
  502. package/src/task-orchestrator/surfaces/pod-command-job.ts +43 -17
  503. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  504. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  505. package/src/task-orchestrator/surfaces/self-implement-pod.ts +222 -40
  506. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  507. package/src/task-orchestrator/types.ts +2 -0
  508. package/src/telegram-card-followup.ts +16 -96
  509. package/src/telegram-commands.ts +31 -1
  510. package/src/telegram-project-command.ts +145 -0
  511. package/src/telegram-report.ts +21 -17
  512. package/src/telegram.ts +61 -8
  513. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  514. package/src/user-config.ts +110 -24
  515. package/src/version/packed-revision.json +1 -1
  516. package/src/web-search/grok.ts +7 -1
  517. package/src/web-search/index.ts +28 -1
  518. package/src/web-search/provider.ts +4 -0
  519. package/src/web-search/x-intent.ts +5 -0
  520. package/src/workflow-runtime/executor.ts +277 -47
  521. package/src/workflow-runtime/index.ts +1 -0
  522. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  523. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  524. package/src/workflow-runtime/schema.ts +79 -4
  525. package/src/workflow-runtime/types.ts +35 -4
  526. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  536. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  537. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  556. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  614. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  615. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → -6XrAoD3eU7NyMHAE4xPS}/_ssgManifest.js +0 -0
package/src/telegram.ts CHANGED
@@ -25,6 +25,7 @@ import { randomUUID } from 'node:crypto';
25
25
  import { buildTurnOutputTextBlocks } from './input/turn-output-block.js';
26
26
  import { selectTurnOutputTextForSink } from './input/turn-output-sink-registry.js';
27
27
  import { debug } from './debug/log.js';
28
+ import { botAudienceFor, filterBotCommands, readBotAudience } from './maturity/bot-command-maturity.js';
28
29
  import { resolveChannelBotToken } from './channel-bot-token.js';
29
30
  import type { TelegramVoiceAdapter } from './voice/channel-adapters/telegram-voice-adapter.js';
30
31
  import { createTelegramSurfaceHitl, type TelegramSurfaceHitl } from './hitl/telegram-surface-hitl.js';
@@ -67,11 +68,15 @@ import { runUrlRoute } from './skills/url-route-exec.js';
67
68
  import { recordRoutedLinkAbsorbed } from './intake-plane/link-ledger.js';
68
69
  import { classifyFrontInput } from './intake-plane/front-classifier.js';
69
70
  import { handleTelegramSeatWork, type TelegramSeatWorkDeps } from './intake-plane/telegram-seat-work.js';
71
+ import { deliverSeatAnswers } from './seat-dispatch/seat-ask.js';
70
72
  import { effectiveInstanceRoot } from './instance/resolve.js';
71
73
  import { getElanousConfigDir } from './elanous-config-dir.js';
72
74
  import { defaultFieldEvent, FieldUploadError, parseFieldCaption, resolveFieldMime, saveFieldMedia } from './field/field-media.js';
73
75
  import { scheduleFieldReel, type FieldReelOptions } from './field/field-reel.js';
76
+ import { handleTelegramFieldNote } from './field-note/telegram.js';
77
+ import type { TranscriptionEngine, FieldAudio } from './field-note/transcribe.js';
74
78
  import { maybeHandleCardPhoto, type CardPhotoDeps } from './telegram-card-followup.js';
79
+ import { attachTelegramProjectButtons } from './telegram-project-command.js';
75
80
 
76
81
  export type TgAttachmentKind = 'photo' | 'voice' | 'audio' | 'document';
77
82
 
@@ -99,6 +104,8 @@ export interface TgIncoming {
99
104
  text: string;
100
105
  messageId: number;
101
106
  threadId?: number;
107
+ /** Telegram message timestamp, not poll time. */
108
+ receivedAt?: string;
102
109
  isDm: boolean;
103
110
  isGroup: boolean;
104
111
  /** Zero or more attached media — populated by parseUpdate from
@@ -217,6 +224,9 @@ export interface TelegramBotOpts {
217
224
  fieldRootDir?: () => string;
218
225
  /** 시험에서는 실제 렌더 대신 가짜 실행기를 주입한다. */
219
226
  fieldReel?: FieldReelOptions;
227
+ /** NOTE3a engine and audio decoder are injected; no transcription provider is assumed. */
228
+ fieldNoteEngine?: TranscriptionEngine;
229
+ fieldNoteDecodeAudio?: (file: string, recordedAt: string) => FieldAudio;
220
230
  /** Test seam for the business-card graph and its private run root. */
221
231
  cardFollowupDeps?: Pick<CardPhotoDeps, 'runGraph' | 'rootDir' | 'ocrText'>;
222
232
  }
@@ -276,7 +286,7 @@ export function classifyTelegramText(
276
286
 
277
287
  export class TelegramBot {
278
288
  private readonly token: string;
279
- private readonly botId: string;
289
+ readonly botId: string;
280
290
  private readonly allowedUsers: Set<number>;
281
291
  private readonly onMessage: TelegramBotOpts['onMessage'];
282
292
  private readonly fetchImpl: typeof fetch;
@@ -336,8 +346,11 @@ export class TelegramBot {
336
346
  private readonly fieldDefaultEventOpt?: string;
337
347
  private readonly fieldRootDir: () => string;
338
348
  private readonly fieldReel?: FieldReelOptions;
349
+ private readonly fieldNoteEngine?: TranscriptionEngine;
350
+ private readonly fieldNoteDecodeAudio?: (file: string, recordedAt: string) => FieldAudio;
339
351
  private readonly cardFollowupDeps: Pick<CardPhotoDeps, 'runGraph' | 'rootDir' | 'ocrText'>;
340
352
  private readonly seatWorkDeps: TelegramSeatWorkDeps;
353
+ private seatAskTimer: ReturnType<typeof setInterval> | null = null;
341
354
  /** 앨범 id → `#현장` 행사 · 답장 디바운스. 태그된 앨범만 들어온다. */
342
355
  private readonly fieldGroups = new Map<string, {
343
356
  event: string; mode?: 'instant'; at: number; chatId: number; threadId?: number; replyTo: number; count: number;
@@ -372,8 +385,18 @@ export class TelegramBot {
372
385
  this.fieldDefaultEventOpt = opts.fieldDefaultEvent;
373
386
  this.fieldRootDir = opts.fieldRootDir ?? getElanousConfigDir;
374
387
  this.fieldReel = opts.fieldReel;
388
+ this.fieldNoteEngine = opts.fieldNoteEngine;
389
+ this.fieldNoteDecodeAudio = opts.fieldNoteDecodeAudio;
375
390
  this.cardFollowupDeps = opts.cardFollowupDeps ?? {};
376
- this.seatWorkDeps = opts.seatWorkDeps ?? {};
391
+ this.seatWorkDeps = { ...opts.seatWorkDeps, askDeps: { ...opts.seatWorkDeps?.askDeps,
392
+ channel: 'telegram', botId: this.botId,
393
+ send: opts.seatWorkDeps?.askDeps?.send ?? (async (origin, text) => {
394
+ if (origin.channel !== 'telegram') throw new Error('seat ask belongs to another surface');
395
+ const posted = await this.sendMessage(Number(origin.chatId), text, { replyTo: origin.messageId, threadId: origin.threadId });
396
+ if (!posted) throw new Error('Telegram seat ask reply was not posted');
397
+ }),
398
+ } };
399
+
377
400
  if (opts.onTriggerTap) {
378
401
  // Assign through `as unknown` to bypass `readonly` + private —
379
402
  // the tap is set once at construction and never reassigned.
@@ -1120,6 +1143,9 @@ export class TelegramBot {
1120
1143
  async start(): Promise<void> {
1121
1144
  this.running = true;
1122
1145
  this.log(`telegram bot starting (allowlist size ${this.allowedUsers.size})`);
1146
+ this.seatAskTimer = setInterval(() => {
1147
+ void deliverSeatAnswers(this.seatWorkDeps.askDeps!).catch((error) => this.log(`seat ask delivery failed: ${String(error)}`));
1148
+ }, 10_000);
1123
1149
  if (this.allowedUsers.size === 0) this.log('telegram.owner-gate empty-allowlist');
1124
1150
 
1125
1151
  // Publish the slash-command menu to Telegram. Clients pick up the
@@ -1128,9 +1154,16 @@ export class TelegramBot {
1128
1154
  // still processes `/commands` via the incoming-message dispatch
1129
1155
  // path, users just don't get the autocomplete.
1130
1156
  if (this.slashCommands.length > 0) {
1157
+ const audience = this.slashContext
1158
+ ? readBotAudience((this.slashContext.userConfig.raw?.telegram as { commandAudience?: unknown } | undefined)?.commandAudience)
1159
+ : botAudienceFor('telegram');
1160
+ const commands = filterBotCommands('telegram', this.slashCommands, audience);
1161
+ debug.log('telegram.command', 'menu-filtered', {
1162
+ total: this.slashCommands.length, shown: commands.length, ...audience,
1163
+ });
1131
1164
  try {
1132
1165
  const result = await this.apiCall<unknown>('setMyCommands', {
1133
- commands: toTelegramBotCommands(this.slashCommands),
1166
+ commands: toTelegramBotCommands(commands),
1134
1167
  });
1135
1168
  debug.log('telegram.command', 'published', { success: true, result });
1136
1169
  this.log(`telegram: setMyCommands published (${String(result)})`);
@@ -1151,6 +1184,7 @@ export class TelegramBot {
1151
1184
  const CONFLICT_BACKOFF_CAP = 60_000;
1152
1185
  while (this.running) {
1153
1186
  try {
1187
+ await deliverSeatAnswers(this.seatWorkDeps.askDeps!);
1154
1188
  const wantCallbackQuery = this.callbackHandlers.size > 0;
1155
1189
  const wantReaction = this.reactionHandlers.size > 0;
1156
1190
  // allowed_updates 는 명시한 것만 받으므로 등록된 핸들러 종류만 추가(비파괴 — 핸들러
@@ -1192,6 +1226,7 @@ export class TelegramBot {
1192
1226
  userId: cq.from.id,
1193
1227
  userName: cq.from.username ?? cq.from.first_name,
1194
1228
  chatId: cq.message?.chat.id,
1229
+ threadId: cq.message?.message_thread_id,
1195
1230
  messageId: cq.message?.message_id,
1196
1231
  data: cq.data ?? '',
1197
1232
  };
@@ -1292,7 +1327,11 @@ export class TelegramBot {
1292
1327
  this.log('telegram bot stopped');
1293
1328
  }
1294
1329
 
1295
- stop(): void { this.running = false; }
1330
+ stop(): void {
1331
+ this.running = false;
1332
+ if (this.seatAskTimer) clearInterval(this.seatAskTimer);
1333
+ this.seatAskTimer = null;
1334
+ }
1296
1335
 
1297
1336
  /** Download + normalize this message's attachments into the shape
1298
1337
  * the ACP content-blocks module consumes. Called lazily by slash
@@ -1502,6 +1541,13 @@ export class TelegramBot {
1502
1541
  }
1503
1542
  // 현장 업로드 — `#현장` 캡션(또는 태그된 앨범의 나머지)의 사진·영상은 LLM 대신 현장 폴더로.
1504
1543
  if (await this.tryHandleFieldUpload(ctx)) return;
1544
+ if (await handleTelegramFieldNote(ctx, {
1545
+ rootDir: this.fieldRootDir(),
1546
+ download: (fileId) => this.downloadFile(fileId),
1547
+ reply: async (incoming, text) => { await this.sendMessage(incoming.chatId, text, { replyTo: incoming.messageId, threadId: incoming.threadId }); },
1548
+ engine: this.fieldNoteEngine, decodeAudio: this.fieldNoteDecodeAudio,
1549
+ now: () => new Date(this.nowImpl()),
1550
+ })) return;
1505
1551
  if (await maybeHandleCardPhoto(ctx, {
1506
1552
  isOwner: (userId) => this.isOwnerAllowed(userId),
1507
1553
  isFieldAlbum: (id) => this.fieldGroups.has(id),
@@ -1801,6 +1847,7 @@ export class TelegramBot {
1801
1847
  const reply = await slashParse.cmd.handler(slashParse.args, ctx, {
1802
1848
  userConfig: this.slashContext.userConfig,
1803
1849
  allCommands: this.slashCommands,
1850
+ sendButtons: async (text, buttons) => { await this.sendInlineKeyboard(ctx.chatId, text, buttons, { replyTo: ctx.messageId, threadId: ctx.threadId }); },
1804
1851
  downloadAttachments: () => this.downloadCtxAttachments(ctx),
1805
1852
  daemonBridge: this.slashContext.daemonBridge,
1806
1853
  hitlConfirmChannel: this.hitlConfirmChannelForChat(ctx.chatId, ctx.threadId),
@@ -1985,6 +2032,7 @@ export class TelegramBot {
1985
2032
  return await slashParse.cmd.handler(slashParse.args, ctx, {
1986
2033
  userConfig: this.slashContext!.userConfig,
1987
2034
  allCommands: this.slashCommands,
2035
+ sendButtons: async (text, buttons) => { await this.sendInlineKeyboard(ctx.chatId, text, buttons, { replyTo: ctx.messageId, threadId: ctx.threadId }); },
1988
2036
  streamer,
1989
2037
  downloadAttachments: () => this.downloadCtxAttachments(ctx),
1990
2038
  daemonBridge: this.slashContext!.daemonBridge,
@@ -2004,6 +2052,7 @@ export class TelegramBot {
2004
2052
  return await runAcpViaSlash(activeBackend, [ctx.text], ctx, {
2005
2053
  userConfig: this.slashContext!.userConfig,
2006
2054
  allCommands: this.slashCommands,
2055
+ sendButtons: async (text, buttons) => { await this.sendInlineKeyboard(ctx.chatId, text, buttons, { replyTo: ctx.messageId, threadId: ctx.threadId }); },
2007
2056
  streamer,
2008
2057
  downloadAttachments: () => this.downloadCtxAttachments(ctx),
2009
2058
  daemonBridge: this.slashContext!.daemonBridge,
@@ -2244,6 +2293,7 @@ interface RawUpdate {
2244
2293
  update_id: number;
2245
2294
  message?: {
2246
2295
  message_id: number;
2296
+ date?: number;
2247
2297
  from?: { id: number; first_name?: string; username?: string };
2248
2298
  chat: { id: number; type: 'private' | 'group' | 'supergroup' | 'channel' };
2249
2299
  text?: string;
@@ -2260,7 +2310,7 @@ interface RawUpdate {
2260
2310
  callback_query?: {
2261
2311
  id: string;
2262
2312
  from: { id: number; first_name?: string; username?: string };
2263
- message?: { message_id: number; chat: { id: number } };
2313
+ message?: { message_id: number; message_thread_id?: number; chat: { id: number } };
2264
2314
  data?: string;
2265
2315
  chat_instance?: string;
2266
2316
  };
@@ -2279,6 +2329,7 @@ export interface TgCallbackQuery {
2279
2329
  userId: number;
2280
2330
  userName?: string;
2281
2331
  chatId?: number;
2332
+ threadId?: number;
2282
2333
  messageId?: number;
2283
2334
  data: string;
2284
2335
  }
@@ -2371,6 +2422,7 @@ export function parseUpdate(u: RawUpdate): TgIncoming | null {
2371
2422
  text: (typeof m.text === 'string' ? m.text : (m.caption ?? '')),
2372
2423
  messageId: m.message_id,
2373
2424
  threadId: m.message_thread_id,
2425
+ ...(typeof m.date === 'number' ? { receivedAt: new Date(m.date * 1000).toISOString() } : {}),
2374
2426
  isDm,
2375
2427
  isGroup: !isDm,
2376
2428
  attachments,
@@ -2886,9 +2938,10 @@ export function botFromConfig(opts: BotFromConfigOpts): TelegramBot {
2886
2938
  botRef.ensureSurfaceHitl();
2887
2939
  // DEC-TG(대표 10-02) — decision cards on the MAIN bot only (other channel pollers carry other tokens). The ticker
2888
2940
  // itself sends only while this bot polls, so a send-only nexus bot never duplicates the standalone poller's cards.
2889
- if (opts.userConfig.raw?.decisions === undefined || (opts.userConfig.raw.decisions as { cards?: unknown }).cards !== false) {
2890
- const mainToken = resolveChannelBotToken('telegram', opts.userConfig)?.token;
2891
- if (mainToken && botToken === mainToken) {
2941
+ const mainToken = resolveChannelBotToken('telegram', opts.userConfig)?.token;
2942
+ if (mainToken && botToken === mainToken) {
2943
+ attachTelegramProjectButtons(botRef, opts.userConfig);
2944
+ if (opts.userConfig.raw?.decisions === undefined || (opts.userConfig.raw.decisions as { cards?: unknown }).cards !== false) {
2892
2945
  const bot = botRef;
2893
2946
  void import('./decisions/telegram-decision-cards.js')
2894
2947
  .then(({ attachTelegramDecisionCards }) => { attachTelegramDecisionCards(bot, opts.userConfig); })
@@ -11,6 +11,7 @@ export const SELF_COGNITION_TOOL_NAMES = [
11
11
  'logs_query',
12
12
  'ops_status',
13
13
  'memory_recall',
14
+ 'context_now',
14
15
  ] as const;
15
16
 
16
17
  type SelfCognitionToolName = (typeof SELF_COGNITION_TOOL_NAMES)[number];
@@ -23,7 +24,14 @@ function coreTools(): CoreToolsModule {
23
24
  return require('../domains/core-tools.js') as CoreToolsModule;
24
25
  }
25
26
 
27
+ export const CONTEXT_NOW_SPEC: LLMToolSpec = {
28
+ name: 'context_now',
29
+ description: '지금 무엇이 돌고 있는지 릴리스 칸·열린 결정·자리 원장·최근 맥락 이벤트의 출처 있는 요약을 읽는다. 원문 대화는 반환하지 않는다.',
30
+ parameters: { type: 'object', properties: { topic: { type: 'string', description: '칸 id·제목 또는 이벤트 요약에 들어 있는 낱말(선택).' } }, required: [] },
31
+ };
32
+
26
33
  function coreSpec(name: SelfCognitionToolName): LLMToolSpec {
34
+ if (name === 'context_now') return CONTEXT_NOW_SPEC;
27
35
  const spec = coreTools().CORE_TOOL_SPECS.find(candidate => candidate.name === name);
28
36
  if (!spec) throw new Error(`Missing core tool spec for self-cognition runtime '${name}'`);
29
37
  return spec;
@@ -193,9 +201,11 @@ export const SELF_COGNITION_RUNTIMES: readonly ToolRuntime[] = SELF_COGNITION_TO
193
201
  get spec() {
194
202
  return coreSpec(name);
195
203
  },
196
- run: async (args, ctx) => name === 'memory_recall' || name === 'self_recall'
197
- ? runRecall(name, args, ctx)
198
- : toToolRunResult(await coreTools().buildCoreTools().dispatch(name, args)),
204
+ run: async (args, ctx) => name === 'context_now'
205
+ ? (await import('../context-bus/context-now.js')).contextNow({ ...(typeof args.topic === 'string' ? { topic: args.topic } : {}) })
206
+ : name === 'memory_recall' || name === 'self_recall'
207
+ ? runRecall(name, args, ctx)
208
+ : toToolRunResult(await coreTools().buildCoreTools().dispatch(name, args)),
199
209
  }));
200
210
 
201
211
  /** MCP catalog metadata derived from the same name ledger as the runtimes. */
@@ -50,7 +50,7 @@ import type { SkillTier } from './skills/runner.js';
50
50
  import { sharedAgentSkillRoots } from './skills/shared-agent-skill-roots.js';
51
51
  import {
52
52
  isModelTier,
53
- isModelTierPersona,
53
+ isModelTierProfile,
54
54
  type BudgetUserConfig,
55
55
  type ModelTier,
56
56
  type ModelTierUserConfig,
@@ -450,6 +450,8 @@ export interface SkillSource {
450
450
  export interface SkillsConfig {
451
451
  activeSet: SkillSetName;
452
452
  dirs: string[];
453
+ /** Gift redemption API origin; defaults to https://elanous.ai. */
454
+ giftEndpoint?: string;
453
455
  /** Include the existing shared ~/.agents/skills directory. Default true; false opts out. */
454
456
  includeSharedAgentSkills?: boolean;
455
457
  /**
@@ -766,6 +768,7 @@ function skillsDefaults(): SkillsConfig {
766
768
  return {
767
769
  activeSet: 'claudecode',
768
770
  dirs: d ? [d] : [],
771
+ giftEndpoint: 'https://elanous.ai',
769
772
  includeSharedAgentSkills: true,
770
773
  allow: [],
771
774
  deny: [],
@@ -912,8 +915,8 @@ export interface TelegramConfig {
912
915
  /** 누가 Q&A 폴링을 하나 — 기본(없음·`'nexus'`)은 넥서스 데몬, `'standalone'` 이면
913
916
  * 넥서스는 폴링하지 않고 `elanous telegram run` 이 맡는다. */
914
917
  poller?: 'nexus' | 'standalone';
915
- /** 발송 목적(kind) → 채널 역할. 명시 `channels` 가 있을 때만 쓴다 — 표에 없는 kind 는 옛 `reportChannel` 로 간다.
916
- * 기본표는 `DEFAULT_KIND_ROLES`(telegram-report.ts) · 여기 값이 덮어쓴다. */
918
+ /** 레거시 kind→역할 설정(읽기 호환). 선택한 채널의 봇이 kind 정책의
919
+ * 운영/매매 경계를 지킬 때만 발송 라우팅에 적용한다. */
917
920
  kindRoles?: Record<string, string>;
918
921
  /** `#현장` 캡션에 행사 슬러그가 없을 때 쓸 기본 행사(예: `marketers-night-2026-10`). 없으면 `field-<로컬 날짜>`. */
919
922
  fieldDefaultEvent?: string;
@@ -2023,7 +2026,12 @@ function parseModelTierFieldOrUndefined(v: unknown): ModelTier | undefined {
2023
2026
  function parseModelTierConfig(raw: unknown): ModelTierUserConfig | undefined {
2024
2027
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return undefined;
2025
2028
  const r = raw as Record<string, unknown>;
2026
- const persona = isModelTierPersona(r.persona) ? r.persona : undefined;
2029
+ // Compatibility is read-only: the normalized config and subsequent writes use profile.
2030
+ let profile = isModelTierProfile(r.profile) ? r.profile : undefined;
2031
+ if (r.profile === undefined && r.persona !== undefined) {
2032
+ debug.log('config', 'deprecated-key', { from: 'modelTier.persona', to: 'modelTier.profile' });
2033
+ if (isModelTierProfile(r.persona)) profile = r.persona;
2034
+ }
2027
2035
  const preset = typeof r.preset === 'string' && r.preset.trim().length > 0
2028
2036
  ? r.preset.trim()
2029
2037
  : undefined;
@@ -2057,7 +2065,7 @@ function parseModelTierConfig(raw: unknown): ModelTierUserConfig | undefined {
2057
2065
  const embedding = parseModelTierFieldOrUndefined(r.embedding);
2058
2066
  const vision = parseModelTierFieldOrUndefined(r.vision);
2059
2067
  const out: ModelTierUserConfig = {
2060
- ...spreadIfDefined('persona', persona),
2068
+ ...spreadIfDefined('profile', profile),
2061
2069
  ...spreadIfDefined('preset', preset),
2062
2070
  ...spreadIfDefined('voice', voice),
2063
2071
  ...spreadIfDefined('llm', llm),
@@ -3232,7 +3240,7 @@ export interface AutoReviewConfig {
3232
3240
  };
3233
3241
  }
3234
3242
 
3235
- export type ModelRole = 'implement' | 'review' | 'research' | 'planning' | 'audit' | 'classify';
3243
+ export type ModelRole = 'implement' | 'review' | 'research' | 'planning' | 'audit' | 'classify' | 'graph-grow';
3236
3244
 
3237
3245
  export interface RoleModelConfig {
3238
3246
  implement?: string;
@@ -3241,6 +3249,7 @@ export interface RoleModelConfig {
3241
3249
  planning?: string;
3242
3250
  audit?: string;
3243
3251
  classify?: string;
3252
+ 'graph-grow'?: string;
3244
3253
  }
3245
3254
 
3246
3255
  /** Sparse provider-agnostic model tier preferences by execution role. */
@@ -3307,6 +3316,7 @@ export const ROLE_MODEL_DEFAULTS: Record<ModelRole, { environment: string; tier:
3307
3316
  planning: { environment: 'ELANOUS_SKILL_PLAN_MODEL', tier: 'best' },
3308
3317
  audit: { environment: 'ELANOUS_MEMORY_JUDGE_MODEL', tier: 'better' },
3309
3318
  classify: { environment: 'ELANOUS_ACTION_CLASSIFIER_MODEL', tier: 'budget' },
3319
+ 'graph-grow': { environment: 'ELANOUS_GRAPH_GROW_MODEL', tier: 'better' },
3310
3320
  };
3311
3321
 
3312
3322
  /** Resolve `auto` through the same runtime provider selector that dispatches LLM calls.
@@ -3429,7 +3439,9 @@ export interface RoleLlmResolveOptions {
3429
3439
  * ⛔ 아무 override 도 없으면 ③~⑥ 만 타므로 산출이 종전과 «같다»(§4f 불변식 · NL 진입 무변경). */
3430
3440
  export function resolveRoleLlm(role: ModelRole, opts: RoleLlmResolveOptions = {}): RoleLlmResolution {
3431
3441
  const config = opts.config ?? getUserConfig();
3432
- const globalProvider = resolveActiveProvider(config);
3442
+ // 전역 provider 는 필요할 때만 푼다 — 역할이 provider 를 명시했으면 전역 provider 가 없어도 해석된다.
3443
+ let activeProvider: LLMProviderName | undefined;
3444
+ const globalProvider = (): LLMProviderName => (activeProvider ??= resolveActiveProvider(config));
3433
3445
  // 명시 인자가 있으면 그것이 진실 — 없을 때만 발사 시점에 정해진 값을 읽는다(둘 다 source='flag').
3434
3446
  const flagSpec = (opts.overrides ?? launchRoleLlmOverrides)?.[role];
3435
3447
  const layers: ReadonlyArray<readonly [RoleLlmSpec | undefined, RoleLlmSource]> = [
@@ -3438,7 +3450,7 @@ export function resolveRoleLlm(role: ModelRole, opts: RoleLlmResolveOptions = {}
3438
3450
  ];
3439
3451
  for (const [spec, source] of layers) {
3440
3452
  if (!spec) continue;
3441
- const provider = spec.provider ?? globalProvider;
3453
+ const provider = spec.provider ?? globalProvider();
3442
3454
  const pinned = spec.model?.trim();
3443
3455
  let resolution: RoleLlmResolution | undefined;
3444
3456
  if (pinned) resolution = { model: pinned, provider, ...(spec.tier ? { tier: spec.tier } : {}), source };
@@ -3447,7 +3459,7 @@ export function resolveRoleLlm(role: ModelRole, opts: RoleLlmResolveOptions = {}
3447
3459
  if (resolution) { observeRoleLlm(role, resolution, spec); return resolution; }
3448
3460
  // 세 칸이 «다 비었으면» 아래 층으로 흘린다 — 빈 칸은 선언이 아니다.
3449
3461
  }
3450
- const baseline = resolveRoleLlmBaseline(role, config, globalProvider);
3462
+ const baseline = resolveRoleLlmBaseline(role, config, globalProvider());
3451
3463
  if (opts.overrides || launchRoleLlmOverrides || config.roleLlm?.[role]) observeRoleLlm(role, baseline, flagSpec);
3452
3464
  return baseline;
3453
3465
  }
@@ -3463,17 +3475,49 @@ export function resolveRoleModel(role: ModelRole, config: UserConfig = getUserCo
3463
3475
  }
3464
3476
 
3465
3477
 
3478
+ export type StewardLoopMode = 'off' | 'shadow' | 'live';
3479
+
3466
3480
  export type SeatLoopMode = 'off' | 'shadow' | 'on';
3467
3481
 
3482
+ export interface PersonaLoopConfig { enabled: boolean }
3483
+
3468
3484
  export interface SeatLoopConfig {
3469
3485
  /** No seat actions by default; shadow observes without execution, on permits the seat loop to act. */
3470
3486
  mode: SeatLoopMode;
3487
+ /** Seat-to-seat and human questions default to shadow even when the checklist loop is on. */
3488
+ questions?: 'shadow' | 'on';
3471
3489
  seats?: string[];
3472
3490
  podPool?: string;
3473
3491
  reportPr?: number;
3492
+ /** Parent routing and age limits for checklist stalls; omitted values use findStalls defaults. */
3493
+ stall?: { parents?: Record<string, string>; redMinutes?: number; blockedMinutes?: number };
3494
+ }
3495
+
3496
+ export type EventSeat = 'OP' | 'TC' | 'MK' | 'UX';
3497
+ export interface EventRoute { source: 'linear' | 'asana' | 'github'; kind: string; seat: EventSeat; loop: 'seat' }
3498
+ export interface EventsConfig { mode: 'shadow' | 'on'; routes: EventRoute[]; linearAssignees: Record<string, EventSeat> }
3499
+
3500
+ export function parseEventsConfig(input: unknown): EventsConfig {
3501
+ const events = input && typeof input === 'object' && !Array.isArray(input) ? input as Record<string, unknown> : {};
3502
+ const routes: EventRoute[] = [];
3503
+ const linearAssignees: Record<string, EventSeat> = Object.create(null);
3504
+ if (events.linearAssignees && typeof events.linearAssignees === 'object' && !Array.isArray(events.linearAssignees)) {
3505
+ for (const [id, seat] of Object.entries(events.linearAssignees)) {
3506
+ if (id.trim() && (seat === 'OP' || seat === 'TC' || seat === 'MK' || seat === 'UX')) linearAssignees[id] = seat;
3507
+ }
3508
+ }
3509
+ if (Array.isArray(events.routes)) for (const value of events.routes) {
3510
+ if (!value || typeof value !== 'object' || Array.isArray(value)) continue;
3511
+ const route = value as Record<string, unknown>;
3512
+ if ((route.source === 'linear' || route.source === 'asana' || route.source === 'github') && typeof route.kind === 'string' && route.kind.trim()
3513
+ && (route.seat === 'OP' || route.seat === 'TC' || route.seat === 'MK' || route.seat === 'UX') && route.loop === 'seat')
3514
+ routes.push({ source: route.source, kind: route.kind, seat: route.seat, loop: route.loop });
3515
+ }
3516
+ return { mode: events.mode === 'on' ? 'on' : 'shadow', routes, linearAssignees };
3474
3517
  }
3475
3518
 
3476
3519
  export interface UserConfig {
3520
+ events?: EventsConfig;
3477
3521
  /** Explicit HTTPS destination for doctor diagnostics; absent means no upload. */
3478
3522
  diagnostics?: { uploadUrl?: string };
3479
3523
  /** Root CLI help audience; absent or invalid means owner. */
@@ -3482,12 +3526,14 @@ export interface UserConfig {
3482
3526
  hitl?: { executionDeadlineMinutes?: number };
3483
3527
  coo?: { linearProject?: string };
3484
3528
  decisions?: { linearProjection: { enabled: boolean } };
3529
+ /** Per-seat goal and Pod limits; missing or invalid limits use seat-budget defaults. */
3530
+ org?: { budget?: Record<string, { dailyGoals?: number; concurrentPods?: number }> };
3485
3531
  /** Steward and seat loops are parsed independently. An absent seat remains off. */
3486
- loops?: { steward?: { mode?: 'observe' | 'act'; linearTeam?: string; roles?: Record<string, { maxConcurrent?: number }>; budget?: number; tracks?: Record<string, string>; alertAfterFailures?: number; launch?: 'off' | 'shadow' | 'live'; maxParallel?: number; podPool?: string }; seat?: SeatLoopConfig };
3532
+ loops?: { steward?: { mode?: StewardLoopMode; linearTeam?: string; roles?: Record<string, { maxConcurrent?: number }>; budget?: number; tracks?: Record<string, string>; alertAfterFailures?: number; launch?: 'off' | 'shadow' | 'live'; maxParallel?: number; podPool?: string }; seat?: SeatLoopConfig; persona?: PersonaLoopConfig };
3487
3533
  /** 🔐 Pod 의 Grok API 키 과금은 명시 opt-in 만. 생략·잘못된 값은 false.
3488
3534
  * budgetGate 와 같은 `harness` 객체다 — 중복 선언이면 뒤 타입이 앞을 지운다. */
3489
- harness?: { pod?: { grokApiKeyOptIn: boolean }; budgetGate?: HarnessBudgetGateConfig; defaultRepo?: string; substrate?: 'local' | 'pod'; podPool?: string; worktreeAddTimeoutSec?: number };
3490
- /** ☸️ Pod 실행 칸 — `pool` = 기존 기본 풀(`컨텍스트[@ssh호스트][:상한]` 쉼표 · 앞이 우선). harness 실행은 인자·ELANOUS_POD_POOL·harness.podPool 다음으로 읽는다. */
3535
+ harness?: { pod?: { grokApiKeyOptIn: boolean }; budgetGate?: HarnessBudgetGateConfig; difficultyPlacement?: boolean; defaultRepo?: string; substrate?: 'local' | 'pod'; podPool?: string; worktreeAddTimeoutSec?: number };
3536
+ /** ☸️ Pod 실행 칸 — `pool` = 기존 기본 풀(`컨텍스트[@ssh호스트][:상한][#k3d-레지스트리:포트]` 쉼표 · 앞이 우선). harness 실행은 인자·ELANOUS_POD_POOL·harness.podPool 다음으로 읽는다. */
3491
3537
  pod?: { pool?: string; /** 호스트 Git 미러 디렉터리 — Pod Job 에 읽기 전용으로 마운트한다. */ hostMirror?: string; /** 원격 그라운딩 엔드포인트(호스트 nexus · tailnet 주소) — Pod 가 토큰으로 «질의→인용»만 묻는다(P13). */ groundingUrl?: string; /** 읽기 전용 Pod lease 권장에 쓰는 계정별 동시 수. */ lease?: { perAccount?: number } };
3492
3538
  skillRouter: SkillRouterConfig;
3493
3539
  llm: LLMConfig;
@@ -3656,7 +3702,7 @@ export function parseRoleLlmEntry(raw: unknown): { ok: true; spec: RoleLlmSpec }
3656
3702
  return { ok: true, spec };
3657
3703
  }
3658
3704
 
3659
- export const MODEL_ROLES: readonly ModelRole[] = ['implement', 'review', 'research', 'planning', 'audit', 'classify'];
3705
+ export const MODEL_ROLES: readonly ModelRole[] = ['implement', 'review', 'research', 'planning', 'audit', 'classify', 'graph-grow'];
3660
3706
 
3661
3707
  export function isModelRole(v: unknown): v is ModelRole {
3662
3708
  return typeof v === 'string' && (MODEL_ROLES as readonly string[]).includes(v);
@@ -3815,7 +3861,7 @@ function defaultConfig(): UserConfig {
3815
3861
  cli: { helpRole: 'owner' },
3816
3862
  coo: { linearProject: '외부 행정·큰 일 (COO)' },
3817
3863
  decisions: { linearProjection: { enabled: false } },
3818
- loops: { seat: { mode: 'off', seats: ['MK'], podPool: 'pool-node-b@node-b:8' } },
3864
+ loops: { steward: { mode: 'shadow' }, seat: { mode: 'off', questions: 'shadow', seats: ['MK'], podPool: 'pool-node-b@node-b:8' }, persona: { enabled: false } },
3819
3865
  skillRouter: { ...SR_DEFAULTS },
3820
3866
  llm: { ...LLM_DEFAULTS },
3821
3867
  skills: skillsDefaults(),
@@ -3884,7 +3930,7 @@ function defaultConfig(): UserConfig {
3884
3930
  nativeStructure: { ...TOOLS_DEFAULTS.nativeStructure },
3885
3931
  selfImplement: { ...TOOLS_DEFAULTS.selfImplement },
3886
3932
  },
3887
- harness: { budgetGate: { ...DEFAULT_BUDGET_GATE } },
3933
+ harness: { budgetGate: { ...DEFAULT_BUDGET_GATE }, difficultyPlacement: false },
3888
3934
  grounding: { sources: [] },
3889
3935
  raw: {},
3890
3936
  };
@@ -4296,7 +4342,7 @@ function parseStewardTracks(input: unknown): Record<string, string> | undefined
4296
4342
  }
4297
4343
 
4298
4344
  function parseSeatLoopsConfig(input: unknown): SeatLoopConfig {
4299
- const defaults: SeatLoopConfig = { mode: 'off', seats: ['MK'], podPool: 'pool-node-b@node-b:8' };
4345
+ const defaults: SeatLoopConfig = { mode: 'off', questions: 'shadow', seats: ['MK'], podPool: 'pool-node-b@node-b:8' };
4300
4346
  if (!input || typeof input !== 'object' || Array.isArray(input)) return defaults;
4301
4347
  const seat = (input as Record<string, unknown>).seat;
4302
4348
  if (!seat || typeof seat !== 'object' || Array.isArray(seat)) return defaults;
@@ -4304,20 +4350,35 @@ function parseSeatLoopsConfig(input: unknown): SeatLoopConfig {
4304
4350
  const mode = values.mode;
4305
4351
  return {
4306
4352
  mode: mode === 'on' || mode === 'shadow' ? mode : 'off',
4353
+ questions: values.questions === 'on' ? 'on' : 'shadow',
4307
4354
  seats: Array.isArray(values.seats) && values.seats.length > 0
4308
4355
  && values.seats.every((value) => typeof value === 'string' && /^(?:MK|OP|TC|UX)$/.test(value))
4309
4356
  ? [...new Set(values.seats as string[])] : ['MK'],
4310
4357
  podPool: typeof values.podPool === 'string' && values.podPool.trim() ? values.podPool.trim() : defaults.podPool,
4311
4358
  ...(typeof values.reportPr === 'number' && Number.isSafeInteger(values.reportPr) && values.reportPr > 0
4312
4359
  ? { reportPr: values.reportPr } : {}),
4360
+ ...(() => {
4361
+ const stall = values.stall;
4362
+ if (!stall || typeof stall !== 'object' || Array.isArray(stall)) return {};
4363
+ const raw = stall as Record<string, unknown>;
4364
+ const parents = raw.parents && typeof raw.parents === 'object' && !Array.isArray(raw.parents)
4365
+ ? Object.fromEntries(Object.entries(raw.parents).filter(([from, to]) =>
4366
+ /^(?:MK|OP|TC|UX)$/.test(from) && typeof to === 'string' && /^(?:MK|OP|TC|UX)$/.test(to) && from !== to))
4367
+ : undefined;
4368
+ const minutes = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value) && value >= 0;
4369
+ return { stall: {
4370
+ ...(parents ? { parents } : {}),
4371
+ ...(minutes(raw.redMinutes) ? { redMinutes: raw.redMinutes } : {}),
4372
+ ...(minutes(raw.blockedMinutes) ? { blockedMinutes: raw.blockedMinutes } : {}),
4373
+ } };
4374
+ })(),
4313
4375
  };
4314
4376
  }
4315
4377
 
4316
4378
  function parseStewardLoopsConfig(input: unknown): UserConfig['loops'] {
4317
- if (!input || typeof input !== 'object' || Array.isArray(input)) return undefined;
4318
- const value = (input as Record<string, unknown>).steward;
4319
- if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
4320
- const s = value as Record<string, unknown>;
4379
+ const loops = input && typeof input === 'object' && !Array.isArray(input) ? input as Record<string, unknown> : {};
4380
+ const value = loops.steward;
4381
+ const s = value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
4321
4382
  const roles: NonNullable<NonNullable<UserConfig['loops']>['steward']>['roles'] = {};
4322
4383
  if (s.roles && typeof s.roles === 'object' && !Array.isArray(s.roles)) {
4323
4384
  for (const [role, entry] of Object.entries(s.roles)) {
@@ -4327,8 +4388,9 @@ function parseStewardLoopsConfig(input: unknown): UserConfig['loops'] {
4327
4388
  }
4328
4389
  }
4329
4390
  return { steward: {
4330
- mode: s.mode === 'act' ? 'act' : 'observe',
4331
- // Absent or invalid launch keys stay absent: launch.ts applies the defaults (shadow · 3 · pool-node-b@node-b:8).
4391
+ mode: s.mode === 'off' || s.mode === 'shadow' || s.mode === 'live' ? s.mode :
4392
+ s.launch === 'off' || s.launch === 'shadow' || s.launch === 'live' ? s.launch : 'shadow',
4393
+ // A valid legacy launch setting remains effective when mode is absent; explicit mode always wins.
4332
4394
  ...(s.launch === 'off' || s.launch === 'shadow' || s.launch === 'live' ? { launch: s.launch } : {}),
4333
4395
  ...(typeof s.maxParallel === 'number' && Number.isSafeInteger(s.maxParallel) && s.maxParallel > 0 ? { maxParallel: s.maxParallel } : {}),
4334
4396
  ...(typeof s.podPool === 'string' && s.podPool.trim() ? { podPool: s.podPool.trim() } : {}),
@@ -4450,8 +4512,22 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4450
4512
  const rawNotifications = rawGlobalObj?.notifications ?? rawObj.notifications;
4451
4513
  const autopilotConfig = parseAutopilotConfig(rawObj.autopilot);
4452
4514
  const stewardLoops = parseStewardLoopsConfig(rawObj.loops);
4515
+ const orgRaw = rawObj.org && typeof rawObj.org === 'object' && !Array.isArray(rawObj.org) ? rawObj.org as Record<string, unknown> : {};
4516
+ const budgetRaw = orgRaw.budget && typeof orgRaw.budget === 'object' && !Array.isArray(orgRaw.budget) ? orgRaw.budget as Record<string, unknown> : {};
4517
+ const seatBudgets: NonNullable<NonNullable<UserConfig['org']>['budget']> = Object.create(null);
4518
+ for (const [seat, value] of Object.entries(budgetRaw)) {
4519
+ if (!seat || !value || typeof value !== 'object' || Array.isArray(value)) continue;
4520
+ const row = value as Record<string, unknown>;
4521
+ const dailyGoals = row.dailyGoals;
4522
+ const concurrentPods = row.concurrentPods;
4523
+ seatBudgets[seat] = {
4524
+ ...(typeof dailyGoals === 'number' && Number.isSafeInteger(dailyGoals) && dailyGoals >= 0 ? { dailyGoals } : {}),
4525
+ ...(typeof concurrentPods === 'number' && Number.isSafeInteger(concurrentPods) && concurrentPods >= 0 ? { concurrentPods } : {}),
4526
+ };
4527
+ }
4453
4528
 
4454
4529
  return {
4530
+ ...(Object.keys(seatBudgets).length ? { org: { budget: seatBudgets } } : {}),
4455
4531
  pod: {
4456
4532
  ...(typeof legacyPod.pool === 'string' && legacyPod.pool.trim() ? { pool: legacyPod.pool.trim() } : {}),
4457
4533
  ...(typeof legacyPod.hostMirror === 'string' && legacyPod.hostMirror.trim() ? { hostMirror: legacyPod.hostMirror.trim() } : {}),
@@ -4465,6 +4541,7 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4465
4541
  },
4466
4542
  harness: {
4467
4543
  pod: { grokApiKeyOptIn: harnessPod.grokApiKeyOptIn === true },
4544
+ difficultyPlacement: harness.difficultyPlacement === true,
4468
4545
  ...(harness.substrate === 'local' || harness.substrate === 'pod' ? { substrate: harness.substrate } : {}),
4469
4546
  ...(typeof harness.podPool === 'string' && harness.podPool.trim() ? { podPool: harness.podPool.trim() } : {}),
4470
4547
  ...(typeof harness.worktreeAddTimeoutSec === 'number' && Number.isSafeInteger(harness.worktreeAddTimeoutSec)
@@ -4583,6 +4660,7 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4583
4660
  skills: {
4584
4661
  activeSet: normalizeSkillSet(sk.activeSet),
4585
4662
  dirs: strArray(sk.dirs, skillsDefaults().dirs),
4663
+ giftEndpoint: str(sk.giftEndpoint) ?? 'https://elanous.ai',
4586
4664
  includeSharedAgentSkills: sk.includeSharedAgentSkills !== false,
4587
4665
  ...(() => { const sources = parsePersistedSkillSources(sk.sources, sk.connected); return sources ? { sources } : {}; })(),
4588
4666
  allow: strArray(sk.allow, []),
@@ -5112,7 +5190,9 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
5112
5190
  coo: { linearProject: typeof (rawObj.coo as { linearProject?: unknown } | undefined)?.linearProject === 'string' && (rawObj.coo as { linearProject: string }).linearProject.trim()
5113
5191
  ? (rawObj.coo as { linearProject: string }).linearProject.trim() : '외부 행정·큰 일 (COO)' },
5114
5192
  decisions: { linearProjection: { enabled: ((rawObj.decisions as { linearProjection?: { enabled?: unknown } } | undefined)?.linearProjection?.enabled === true) } },
5115
- loops: { ...(stewardLoops ?? {}), seat: parseSeatLoopsConfig(rawObj.loops) },
5193
+ loops: { ...(stewardLoops ?? {}), seat: parseSeatLoopsConfig(rawObj.loops),
5194
+ persona: { enabled: (rawObj.loops as { persona?: { enabled?: unknown } } | undefined)?.persona?.enabled === true } },
5195
+ events: parseEventsConfig(rawObj.events),
5116
5196
  // M1-1: sparse — undefined when the user hasn't set anything, so
5117
5197
  // resolvers fall through to zero-config defaults.
5118
5198
  ...spreadIfDefined('modelTier', parseModelTierConfig(rawObj.modelTier)),
@@ -5692,6 +5772,8 @@ export function saveUserConfig(
5692
5772
  ? rawRest.autopilot as Record<string, unknown> : {};
5693
5773
  const rawLoops = rawRest.loops && typeof rawRest.loops === 'object' && !Array.isArray(rawRest.loops)
5694
5774
  ? rawRest.loops as Record<string, unknown> : {};
5775
+ const rawSteward = rawLoops.steward && typeof rawLoops.steward === 'object' && !Array.isArray(rawLoops.steward)
5776
+ ? rawLoops.steward as Record<string, unknown> : {};
5695
5777
  if (cfg.loops) delete rawRest.loops;
5696
5778
  if (cfg.pod?.hostMirror) {
5697
5779
  const rawPod = rawRest.pod && typeof rawRest.pod === 'object' && !Array.isArray(rawRest.pod)
@@ -5704,6 +5786,7 @@ export function saveUserConfig(
5704
5786
  ...rawHarness,
5705
5787
  pod: cfg.harness?.pod ?? rawHarness.pod,
5706
5788
  budgetGate: cfg.harness?.budgetGate ?? rawHarness.budgetGate,
5789
+ difficultyPlacement: cfg.harness?.difficultyPlacement ?? rawHarness.difficultyPlacement,
5707
5790
  defaultRepo: cfg.harness?.defaultRepo ?? rawHarness.defaultRepo,
5708
5791
  substrate: cfg.harness?.substrate,
5709
5792
  podPool: cfg.harness?.podPool,
@@ -5711,8 +5794,10 @@ export function saveUserConfig(
5711
5794
  ...rawRest,
5712
5795
  ...(cfg.loops ? { loops: {
5713
5796
  ...rawLoops,
5797
+ ...(cfg.loops.steward ? { steward: { ...rawSteward, mode: cfg.loops.steward.mode ?? cfg.loops.steward.launch ?? 'shadow' } } : {}),
5714
5798
  ...(cfg.loops.seat ? { seat: {
5715
5799
  mode: cfg.loops.seat.mode,
5800
+ questions: cfg.loops.seat.questions ?? 'shadow',
5716
5801
  seats: cfg.loops.seat.seats ?? ['MK'],
5717
5802
  podPool: cfg.loops.seat.podPool ?? 'pool-node-b@node-b:8',
5718
5803
  ...(cfg.loops.seat.reportPr === undefined ? {} : { reportPr: cfg.loops.seat.reportPr }),
@@ -5783,6 +5868,7 @@ export function saveUserConfig(
5783
5868
  skills: stripUndef({
5784
5869
  activeSet: cfg.skills.activeSet,
5785
5870
  dirs: [...cfg.skills.dirs],
5871
+ giftEndpoint: cfg.skills.giftEndpoint === 'https://elanous.ai' ? undefined : cfg.skills.giftEndpoint,
5786
5872
  // EN5 — only connected roots are persisted (preset/shared/package are derived). The dev-only `connected` key is not written back.
5787
5873
  sources: cfg.skills.sources && cfg.skills.sources.some((source) => source.kind === 'connected')
5788
5874
  ? cfg.skills.sources.filter((source) => source.kind === 'connected').map(({ id, path, enabled }) => ({ id, path, enabled }))
@@ -6039,7 +6125,7 @@ export function saveUserConfig(
6039
6125
  ...(cfg.tabs && Object.keys(cfg.tabs).length > 0 ? { tabs: cfg.tabs } : {}),
6040
6126
  // M1-2b: sparse — emit only when explicitly set. Keeps Path-A-only
6041
6127
  // users' file clean (no empty modelTier:{} blobs).
6042
- ...(cfg.modelTier ? { modelTier: cfg.modelTier } : {}),
6128
+ ...(cfg.modelTier ? { modelTier: parseModelTierConfig(cfg.modelTier) } : {}),
6043
6129
  ...(cfg.sessionFabric ? { sessionFabric: cfg.sessionFabric } : {}),
6044
6130
  ...(cfg.nextFluent ? { nextFluent: cfg.nextFluent } : {}),
6045
6131
  ...(cfg.taste ? { taste: cfg.taste } : {}),
@@ -1 +1 @@
1
- {"commit":"2740cab836f9926e0d4e88c7343f3aa5523b069d"}
1
+ {"commit":"27045f4612b148543fb0f91b82d37592d1d6c9f2"}
@@ -19,6 +19,7 @@
19
19
 
20
20
  import { getGrokApiKey } from '../config.js';
21
21
  import { grokAgentSearch, GROK_SEARCH_MODEL } from '../grok/agent-search.js';
22
+ import { wantsXSearch } from './x-intent.js';
22
23
  import type { WebSearchHit, WebSearchProvider, WebSearchResult } from './provider.js';
23
24
 
24
25
  const DEFAULT_LIMIT = 5;
@@ -52,10 +53,15 @@ export function buildGrokWebSearchProvider(): WebSearchProvider {
52
53
 
53
54
  if (!r.ok) throw new Error(r.error ?? 'grok search failed');
54
55
 
55
- const hits: WebSearchHit[] = r.citations.slice(0, limit).map(c => ({
56
+ const xSourceUrls = new Set(r.xSearchSources ?? []);
57
+ const citations = wantsXSearch(q.query)
58
+ ? [...r.citations.filter(c => xSourceUrls.has(c.url)), ...r.citations.filter(c => !xSourceUrls.has(c.url))]
59
+ : r.citations;
60
+ const hits: WebSearchHit[] = citations.slice(0, limit).map(c => ({
56
61
  url: c.url,
57
62
  title: c.title ?? c.url,
58
63
  snippet: '',
64
+ ...(xSourceUrls.has(c.url) ? { xSearchSource: true } : {}),
59
65
  }));
60
66
 
61
67
  const note = r.text.length > 0 ? r.text.slice(0, 1500) : undefined;