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
@@ -1,27 +1,13 @@
1
- // NEXUS · personas REST handlers (§6.4 · 2026-05-09).
2
- //
3
- // Endpoints
4
- // GET /v1/personas — list all loaded personas
5
- // GET /v1/personas/:personaId — single persona profile
6
- //
7
- // Architecture: read-only thin wrapper over the global PersonaRegistry
8
- // (`src/persona/global-registry.ts`). Disk yaml is the source of truth;
9
- // editing requires text editor + `elanous persona reload` (or fs.watch
10
- // auto-reload). PWA Showroom consumes this for the per-panel persona
11
- // picker (§6.4 Q2 = REST read-only).
12
- //
13
- // PLAN: 내부 문서 `PLAN-cv-3-showroom-mvp-2026-05-07` §6.4
14
- // Cross-ref: src/persona/global-registry.ts (singleton accessor)
15
- // src/persona/types.ts (PersonaProfile shape)
16
-
17
- import { homedir } from 'node:os';
18
- import { join } from 'node:path';
1
+ // NEXUS · personas REST handlers. Presets clone into the state persona store;
2
+ // writes reload the global registry so subsequent reads see the saved profile.
19
3
 
20
4
  import {
21
5
  awaitGlobalPersonaLoad,
22
6
  getGlobalPersonaRegistry,
23
7
  reloadGlobalPersonaRegistry,
8
+ resolveStatePersonaDir,
24
9
  } from '../../persona/global-registry.js';
10
+ import { clonePreset, editPersona, loadPresets } from '../../persona/presets.js';
25
11
  import type { PersonaRegistryEvent } from '../../persona/registry.js';
26
12
  import type { PersonaProfile } from '../../persona/types.js';
27
13
  import {
@@ -193,22 +179,41 @@ export function handlePersonasEvents(
193
179
  });
194
180
  }
195
181
 
196
- /** Resolve personas dir same way `global-registry.ts` does, so the PATCH
197
- * endpoint targets the file the registry will reload. */
198
- function resolvePersonasDir(): string {
199
- const env = process.env.ELANOUS_PERSONAS_DIR;
200
- if (env && env.length > 0) return env;
201
- return join(homedir(), '.elanous', 'personas');
182
+ /** GET /v1/persona-presets — shipped preset catalog in index order. */
183
+ export function handlePersonaPresets(req: Request, opts: PersonaRouteOpts = {}): Response {
184
+ if (opts.checkAuth && !opts.checkAuth(req)) return jsonResponse({ error: 'unauthorized' }, 401);
185
+ return jsonResponse({ presets: loadPresets() }, 200);
202
186
  }
203
187
 
204
- /** PATCH /v1/personas/:personaId — update description.
205
- *
206
- * Body: `{ description: "<string, ≤ 280 chars>" }` — empty string clears.
207
- * Other fields ignored (yaml surgical edit pattern).
208
- *
209
- * Returns 200 with the updated `PersonaWire` after reload, 400 / 404 /
210
- * 500 with structured `{ error, reason? }`.
211
- */
188
+ /** POST /v1/personas — clone a shipped preset into the user's persona store. */
189
+ export async function handlePersonaCreate(req: Request, opts: PersonaRouteOpts = {}): Promise<Response> {
190
+ if (opts.checkAuth && !opts.checkAuth(req)) return jsonResponse({ error: 'unauthorized' }, 401);
191
+ let body: unknown;
192
+ try { body = await req.json(); }
193
+ catch { return jsonResponse({ error: 'invalid-json' }, 400); }
194
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'invalid-body' }, 400);
195
+ const { preset, name } = body as Record<string, unknown>;
196
+ if (typeof preset !== 'string' || !preset.trim() || typeof name !== 'string' || !name.trim()) {
197
+ return jsonResponse({ error: 'preset-and-name-required' }, 400);
198
+ }
199
+ await awaitGlobalPersonaLoad();
200
+ if (!loadPresets().some((p) => p.personaId === preset)) return notFound(`preset ${preset} not found`);
201
+ try {
202
+ const created = clonePreset(preset, name.trim(), { dir: resolveStatePersonaDir() });
203
+ await reloadGlobalPersonaRegistry();
204
+ const persona = getGlobalPersonaRegistry().get(created.personaId);
205
+ if (!persona) return notFound(`persona ${created.personaId} not found after reload`);
206
+ return jsonResponse({ persona: toWire(persona) }, 201);
207
+ } catch (error) {
208
+ if (error instanceof Error && error.message.startsWith('persona name already exists:')) {
209
+ return jsonResponse({ error: 'persona-name-conflict' }, 409);
210
+ }
211
+ return jsonResponse({ error: 'create-failed' }, 500);
212
+ }
213
+ }
214
+
215
+ /** PATCH /v1/personas/:personaId — description-only edits retain the
216
+ * existing surgical YAML behavior; multi-field edits use the persona store. */
212
217
  export async function handlePersonaPatch(
213
218
  req: Request,
214
219
  personaId: string,
@@ -217,17 +222,23 @@ export async function handlePersonaPatch(
217
222
  if (opts.checkAuth && !opts.checkAuth(req)) {
218
223
  return jsonResponse({ error: 'unauthorized' }, 401);
219
224
  }
220
- let body: { description?: unknown };
221
- try {
222
- body = (await req.json()) as { description?: unknown };
223
- } catch {
224
- return jsonResponse({ error: 'invalid-json' }, 400);
225
+ let parsed: unknown;
226
+ try { parsed = await req.json(); }
227
+ catch { return jsonResponse({ error: 'invalid-json' }, 400); }
228
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
229
+ return jsonResponse({ error: 'invalid-body' }, 400);
225
230
  }
226
- if (typeof body.description !== 'string') {
227
- return jsonResponse({ error: 'description-required' }, 400);
231
+ const body = parsed as Record<string, unknown>;
232
+ const fields = Object.keys(body);
233
+ if (!fields.length) return jsonResponse({ error: 'description-required' }, 400);
234
+ if (fields.some((field) => !['displayName', 'description', 'systemPrompt'].includes(field))) {
235
+ return jsonResponse({ error: 'invalid-persona-field' }, 400);
228
236
  }
229
- if (body.description.length > MAX_PERSONA_DESCRIPTION_LENGTH * 4) {
230
- // Guard against pathological inputs before the helper trims.
237
+ if (fields.some((field) => typeof body[field] !== 'string')
238
+ || (body.displayName !== undefined && !(body.displayName as string).trim())) {
239
+ return jsonResponse({ error: 'invalid-persona-field' }, 400);
240
+ }
241
+ if (typeof body.description === 'string' && body.description.length > MAX_PERSONA_DESCRIPTION_LENGTH * 4) {
231
242
  return jsonResponse({ error: 'description-too-long' }, 400);
232
243
  }
233
244
 
@@ -237,14 +248,37 @@ export async function handlePersonaPatch(
237
248
  return notFound(`persona ${personaId} not found`);
238
249
  }
239
250
 
240
- const dir = resolvePersonasDir();
241
- const result = updatePersonaDescription(dir, personaId, body.description);
242
- if (!result.ok) {
243
- const status = result.reason === 'file-not-found' ? 404 : 400;
244
- return jsonResponse({ error: 'patch-failed', reason: result.reason }, status);
251
+ const dir = resolveStatePersonaDir();
252
+ if (fields.length === 1 && typeof body.description === 'string') {
253
+ const result = updatePersonaDescription(dir, personaId, body.description);
254
+ if (!result.ok) {
255
+ const status = result.reason === 'file-not-found' ? 404 : 400;
256
+ return jsonResponse({ error: 'patch-failed', reason: result.reason }, status);
257
+ }
258
+ } else {
259
+ const edits: Record<string, unknown> = {};
260
+ for (const field of fields) edits[field] = body[field];
261
+ if (typeof edits.description === 'string') {
262
+ const description = edits.description.trim();
263
+ edits.description = description;
264
+ if (description.length > MAX_PERSONA_DESCRIPTION_LENGTH) {
265
+ return jsonResponse({ error: 'description-too-long' }, 400);
266
+ }
267
+ }
268
+ try {
269
+ editPersona(personaId, edits, { dir });
270
+ } catch (error) {
271
+ if (error instanceof Error && error.message.startsWith('persona not found:')) {
272
+ return notFound(`persona ${personaId} not found in state store`);
273
+ }
274
+ if (error instanceof Error && error.message.startsWith('persona name already exists:')) {
275
+ return jsonResponse({ error: 'persona-name-conflict' }, 409);
276
+ }
277
+ return jsonResponse({ error: 'patch-failed', reason: error instanceof Error ? error.message : 'invalid-persona' }, 400);
278
+ }
245
279
  }
246
280
 
247
- // Reload so the in-memory registry reflects the new description on the
281
+ // Reload so the in-memory registry reflects the saved fields on the
248
282
  // next GET. fs.watch would auto-reload but is async + race-y vs the
249
283
  // immediate echo below.
250
284
  await reloadGlobalPersonaRegistry();
@@ -255,7 +289,7 @@ export async function handlePersonaPatch(
255
289
  return jsonResponse({ persona: toWire(reloaded) }, 200);
256
290
  }
257
291
 
258
- /** Combined dispatcher — `/v1/personas[/:id]` + `/v1/personas/events`.
292
+ /** Combined dispatcher — `/v1/persona-presets` + `/v1/personas[/:id]` + `/v1/personas/events`.
259
293
  * Returns null when the pathname doesn't match so the caller chains. */
260
294
  export async function dispatchPersonaRoute(
261
295
  req: Request,
@@ -263,11 +297,14 @@ export async function dispatchPersonaRoute(
263
297
  opts: PersonaRouteOpts = {},
264
298
  ): Promise<Response | null> {
265
299
  const method = req.method.toUpperCase();
300
+ if (pathname === '/v1/persona-presets') {
301
+ if (method !== 'GET') return jsonResponse({ error: 'method not allowed' }, 405);
302
+ return handlePersonaPresets(req, opts);
303
+ }
266
304
  if (pathname === '/v1/personas') {
267
- if (method !== 'GET') {
268
- return jsonResponse({ error: 'method not allowed' }, 405);
269
- }
270
- return handlePersonasList(req, opts);
305
+ if (method === 'GET') return handlePersonasList(req, opts);
306
+ if (method === 'POST') return handlePersonaCreate(req, opts);
307
+ return jsonResponse({ error: 'method not allowed' }, 405);
271
308
  }
272
309
  // SSE — must come BEFORE the single-persona regex so that the
273
310
  // literal "events" segment isn't misread as a personaId.
@@ -279,8 +316,13 @@ export async function dispatchPersonaRoute(
279
316
  }
280
317
  const match = pathname.match(/^\/v1\/personas\/([^/]+)$/);
281
318
  if (!match) return null;
282
- const personaId = decodeURIComponent(match[1]!);
319
+ let personaId: string;
320
+ try { personaId = decodeURIComponent(match[1]!); }
321
+ catch { return jsonResponse({ error: 'invalid-persona-id' }, 400); }
283
322
  if (method === 'PATCH') {
323
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(personaId)) {
324
+ return jsonResponse({ error: 'invalid-persona-id' }, 400);
325
+ }
284
326
  return handlePersonaPatch(req, personaId, opts);
285
327
  }
286
328
  if (method !== 'GET') {
@@ -6,12 +6,14 @@ import { resolveAttachmentPath } from '../../boot/attachment-store.js';
6
6
  import { runGh } from '../../decisions/decision-cards.js';
7
7
  import { openMsgStore, canonicalSeatId } from '../../msg/msg-store.js';
8
8
  import { dispatchCeoTask, type CeoCommandDeps } from '../../seat-dispatch/ceo-commands.js';
9
+ import { acknowledgeSeatAnswers, askSeat, deliverSeatAnswers, parseSeatAsk, type SeatAskDeps } from '../../seat-dispatch/seat-ask.js';
9
10
  import { getUserConfig } from '../../user-config.js';
10
11
  import { effectiveInstanceRoot } from '../../instance/resolve.js';
11
12
  import { submitIntakeWork } from '../../intake-plane/submit-intake-work.js';
12
13
  import { parseSeatAddress, resolveSeat } from '../../seat-address/seat-address.js';
13
14
  import trackData from '../../../scripts/coord-tracks.json';
14
15
  import { jsonResponse } from './json-response.js';
16
+ import { publishInsideEvent } from './inside-events.js';
15
17
 
16
18
  export const SEAT_REQUESTS_PATH = '/v1/seat-requests';
17
19
 
@@ -50,6 +52,7 @@ export interface SeatRequestsDeps {
50
52
  resolveAttachment?: typeof resolveAttachmentPath;
51
53
  dispatch?: typeof dispatchCeoTask;
52
54
  ceoDeps?: () => CeoCommandDeps;
55
+ askDeps?: SeatAskDeps;
53
56
  }
54
57
 
55
58
  const seats = trackData.tracks.filter((entry): entry is typeof entry & { title: string } => 'title' in entry)
@@ -189,6 +192,18 @@ export async function handleSeatRequests(req: Request, deps: SeatRequestsDeps =
189
192
  const path = file((deps.root ?? effectiveInstanceRoot)());
190
193
  if (req.method === 'GET') {
191
194
  const params = new URL(req.url).searchParams;
195
+ if (params.get('answers') === '1') {
196
+ const clientId = req.headers.get('x-seat-ask-client');
197
+ if (!clientId || !/^[a-f0-9-]{36}$/i.test(clientId)) return jsonResponse({ error: 'bad_request' }, 400);
198
+ const base = deps.askDeps;
199
+ const messages: Array<{ id: string; text: string; status: 'answered' | 'expired' }> = [];
200
+ await deliverSeatAnswers({ ...base, channel: 'pwa', clientId, send: async (origin, text) => {
201
+ if (origin.channel !== 'pwa' || origin.clientId !== clientId) return;
202
+ const match = /\(([^)]+)\)/.exec(text);
203
+ if (match) messages.push({ id: match[1]!, text, status: text.startsWith('CTO 답변') ? 'answered' : 'expired' });
204
+ } });
205
+ return jsonResponse({ items: messages });
206
+ }
192
207
  const requestedSeat = params.get('seat');
193
208
  const seat = requestedSeat ? requestSeat(requestedSeat) : undefined;
194
209
  if (requestedSeat && !seat) return unknownSeat();
@@ -204,6 +219,19 @@ export async function handleSeatRequests(req: Request, deps: SeatRequestsDeps =
204
219
  }
205
220
  if (req.method !== 'POST') return jsonResponse({ error: 'method-not-allowed' }, 405);
206
221
 
222
+ if (new URL(req.url).searchParams.get('answers') === 'ack') {
223
+ const clientId = req.headers.get('x-seat-ask-client');
224
+ if (!clientId || !/^[a-f0-9-]{36}$/i.test(clientId)) return jsonResponse({ error: 'bad_request' }, 400);
225
+ let body: unknown;
226
+ try { body = await req.json(); } catch { return jsonResponse({ error: 'bad_request' }, 400); }
227
+ const ids = (body as { ids?: unknown } | null)?.ids;
228
+ if (!Array.isArray(ids) || ids.length > 100 || ids.some((id) => typeof id !== 'string' || !/^[a-f0-9-]{36}$/i.test(id))) {
229
+ return jsonResponse({ error: 'bad_request' }, 400);
230
+ }
231
+ acknowledgeSeatAnswers(clientId, ids as string[], deps.askDeps?.open);
232
+ return jsonResponse({ ok: true });
233
+ }
234
+
207
235
  let body: unknown;
208
236
  try { body = await req.json(); }
209
237
  catch { return jsonResponse({ error: 'bad_request' }, 400); }
@@ -219,10 +247,22 @@ export async function handleSeatRequests(req: Request, deps: SeatRequestsDeps =
219
247
  const prefix = parsed ? `@${parsed.seats.join(',')}` : '';
220
248
  const firstAddress = parsed && input.text.startsWith(prefix)
221
249
  && (input.text.length === prefix.length || /[ \t\r\n]/.test(input.text[prefix.length]!)) ? parsed : null;
222
- const address = input.seat ?? (firstAddress?.seats.length === 1 ? firstAddress.seats[0] : undefined);
250
+ const address = input.seat ?? (firstAddress?.seats.length === 1 ? firstAddress.seats[0] : undefined)
251
+ ?? (parseSeatAsk(input.text) ? 'TC' : undefined);
223
252
  const seat = address ? requestSeat(address) : undefined;
224
253
  if (!seat) return unknownSeat();
225
- const text = input.seat === undefined ? firstAddress!.body.trim() : input.text.trim();
254
+ const text = input.seat === undefined ? (firstAddress?.body ?? input.text).trim() : input.text.trim();
255
+ if (canonicalSeatIdSafe(seat.id) === 'TC' && parseSeatAsk(text)) {
256
+ if (input.attachments !== undefined) return jsonResponse({ error: 'attachments-unsupported' }, 400);
257
+ const clientId = req.headers.get('x-seat-ask-client');
258
+ if (!clientId || !/^[a-f0-9-]{36}$/i.test(clientId)) return jsonResponse({ error: 'bad_request' }, 400);
259
+ let ask: string;
260
+ try {
261
+ ask = await askSeat(text, { channel: 'pwa', clientId }, (deps.ceoDeps ?? defaultCeoDeps)(),
262
+ deps.askDeps ?? { channel: 'pwa', send: async () => { throw new Error('PWA seat ask requires polling'); } });
263
+ } catch { return unavailable(); }
264
+ return jsonResponse({ reply: ask }, 202);
265
+ }
226
266
  if (!text) {
227
267
  debug.log('seat-address.pwa', 'rejected', { reason: 'empty-text', seat: seat.id });
228
268
  return jsonResponse({ error: 'bad_request' }, 400);
@@ -304,6 +344,7 @@ export async function handleSeatRequests(req: Request, deps: SeatRequestsDeps =
304
344
  };
305
345
  try { persistOutcome(path, item, deps.append ?? append); }
306
346
  catch { return unavailable(); }
347
+ publishInsideEvent({ kind: 'seat', seat: item.seat, receiptId: item.receiptId, status: item.status, ts: item.queuedAt });
307
348
  return receipt(item);
308
349
  }
309
350
  let result: Awaited<ReturnType<typeof submitIntakeWork>>;
@@ -325,6 +366,7 @@ export async function handleSeatRequests(req: Request, deps: SeatRequestsDeps =
325
366
  try { persistOutcome(path, item, deps.append ?? append); }
326
367
  catch { return unavailable(); }
327
368
  debug.log('seat-address.pwa', 'enqueued', { seat: seat.id, receiptId: item.receiptId });
369
+ publishInsideEvent({ kind: 'seat', seat: item.seat, receiptId: item.receiptId, status: item.status, ts: item.queuedAt });
328
370
  return receipt(item);
329
371
  })();
330
372
  inFlight.set(lock, { payload, task });
@@ -18,11 +18,9 @@ import {
18
18
  isHarnessSessionOrigin,
19
19
  type SessionMeta,
20
20
  } from '../../session/index.js';
21
- import { existsSync, mkdirSync, readFileSync, renameSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
22
- import { join } from 'node:path';
23
- import { randomUUID } from 'node:crypto';
24
21
  import { getProject } from '../../project/project-store.js';
25
- import { sessionRoot } from '../../session/index.js';
22
+ import { sessionRoot, updateSessionMeta } from '../../session/index.js';
23
+ import { debug } from '../../debug/log.js';
26
24
  import { checkAuth, type MetaApiOpts } from './meta-api.js';
27
25
  import { jsonResponse } from './http-server.js';
28
26
 
@@ -181,43 +179,24 @@ export async function handleSessionsStorePatch(req: Request, id: string, opts: M
181
179
  return jsonResponse({ ok: false, error: 'invalid_project_id' }, 400);
182
180
  }
183
181
  const projectId = (body as { projectId: unknown }).projectId;
184
- if (projectId !== null && (typeof projectId !== 'string' || !getProject(projectId))) {
182
+ if (projectId !== null && typeof projectId !== 'string') {
185
183
  return jsonResponse({ ok: false, error: 'invalid_project_id' }, 400);
186
184
  }
187
-
188
- const root = sessionRoot();
189
- const lock = join(root, '.index.lock');
190
- const deadline = Date.now() + 10_000;
191
- for (;;) {
192
- try { mkdirSync(lock); break; }
193
- catch {
194
- if (!existsSync(lock)) continue;
195
- try {
196
- if (Date.now() - statSync(lock).mtimeMs > 30_000) { rmdirSync(lock); continue; }
197
- } catch { continue; }
198
- if (Date.now() >= deadline) throw new Error(`timed out acquiring lock ${lock}`);
199
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
200
- }
201
- }
202
- try {
203
- const path = join(root, 'index.json');
204
- const entries = JSON.parse(readFileSync(path, 'utf8')) as SessionMeta[];
205
- const pos = entries.findIndex((meta) => meta.id === id);
206
- if (pos < 0) return jsonResponse({ ok: false, error: 'not_found' }, 404);
207
- const meta = { ...entries[pos] };
208
- if (projectId === null) delete meta.projectId;
209
- else meta.projectId = projectId;
210
- entries[pos] = meta;
211
- const temp = `${path}.${process.pid}.${randomUUID().slice(0, 8)}.tmp`;
212
- writeFileSync(temp, JSON.stringify(entries, null, 2) + '\n', 'utf8');
213
- renameSync(temp, path);
214
- return jsonResponse({ ok: true, meta }, 200);
215
- } catch (error) {
216
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return jsonResponse({ ok: false, error: 'not_found' }, 404);
217
- throw error;
218
- } finally {
219
- rmdirSync(lock);
185
+ // 모양은 맞는데 없는 프로젝트 = 찾을 수 없음(404) — 요청 형식 오류(400)와 가른다(IA1b).
186
+ if (projectId !== null && !getProject(projectId)) {
187
+ return jsonResponse({ ok: false, error: 'project-not-found' }, 404);
220
188
  }
189
+
190
+ let previous: string | null = null;
191
+ // 잠금 안에서 최신 목록 위에 고친다 — 동시 appendMessage 가 이 projectId 를 덮지 않는다.
192
+ const meta = updateSessionMeta(id, (m) => {
193
+ previous = m.projectId ?? null;
194
+ if (projectId === null) delete m.projectId;
195
+ else m.projectId = projectId;
196
+ }, sessionRoot());
197
+ if (!meta) return jsonResponse({ ok: false, error: 'not_found' }, 404);
198
+ debug.log('project', 'assign', { sessionId: id, projectId, previous });
199
+ return jsonResponse({ ok: true, meta }, 200);
221
200
  }
222
201
 
223
202
  /** DELETE /v1/sessions/store/:id — on-disk 세션 삭제(index.json + <id>.jsonl 제거).
@@ -0,0 +1,34 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { debug } from '../../debug/log.js';
4
+
5
+ export interface SubSeatCharter { id: string; seat: string; sub: string; title: string }
6
+
7
+ export function listSubSeatCharters(root = join(import.meta.dir, '..', '..', '..', 'docs', 'roles'), onUnreadable?: () => void): SubSeatCharter[] {
8
+ try {
9
+ readdirSync(root);
10
+ const charters: SubSeatCharter[] = [];
11
+ for (const seat of ['OP', 'MK', 'TC', 'UX']) {
12
+ let entries;
13
+ try { entries = readdirSync(join(root, seat), { withFileTypes: true }); }
14
+ catch (error) {
15
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue;
16
+ throw error;
17
+ }
18
+ for (const entry of entries) {
19
+ if (!entry.isFile() || !/^[a-z][a-z0-9-]{0,31}\.md$/.test(entry.name)) continue;
20
+ const sub = entry.name.slice(0, -3);
21
+ const id = `${seat}/${sub}`;
22
+ const content = readFileSync(join(root, seat, entry.name), 'utf8');
23
+ const heading = content.split(/\r?\n/).find((line) => line.startsWith('# '));
24
+ const title = heading?.slice(2).startsWith(`${id} — `) ? heading.slice(2 + id.length + 3).trim() || id : id;
25
+ charters.push({ id, seat, sub, title });
26
+ }
27
+ }
28
+ return charters.sort((a, b) => a.id.localeCompare(b.id));
29
+ } catch (error) {
30
+ debug.log('ops.seats', 'charters-unreadable', { root, error: error instanceof Error ? error.message : String(error) });
31
+ onUnreadable?.();
32
+ return [];
33
+ }
34
+ }
@@ -78,6 +78,8 @@ interface CreateTabBody {
78
78
  kind?: string;
79
79
  id?: string;
80
80
  label?: string;
81
+ /** Conversation owning a newly created webterm. */
82
+ sessionId?: string;
81
83
  /** Kind-specific factory opts (passed through to the factory). */
82
84
  kindOpts?: Record<string, unknown>;
83
85
  /** When true (default), supervisor.startTab is called immediately
@@ -95,7 +97,7 @@ function buildTabSpec(body: CreateTabBody, id: string): { spec: TabSpec; reason?
95
97
  case 'chat':
96
98
  return { spec: createChatTabSpec(opts as Parameters<typeof createChatTabSpec>[0]) };
97
99
  case 'webterm':
98
- return { spec: createWebtermTabSpec(opts as Parameters<typeof createWebtermTabSpec>[0]) };
100
+ return { spec: createWebtermTabSpec({ ...opts, sessionId: body.sessionId ?? opts.sessionId } as Parameters<typeof createWebtermTabSpec>[0]) };
99
101
  case 'daemon':
100
102
  return { spec: createDaemonTabSpec(opts as Parameters<typeof createDaemonTabSpec>[0]) };
101
103
  case 'pwa-host':
@@ -1,6 +1,25 @@
1
1
  import { CardStore } from '../../task-cards/card-store.js';
2
+ import { createWishCard } from '../../intake-plane/wish-card.js';
2
3
  import { jsonResponse } from './json-response.js';
3
4
 
5
+ export async function handleTaskCardsWishPost(req: Request, root?: string): Promise<Response> {
6
+ let body: unknown;
7
+ try { body = await req.json(); }
8
+ catch { return jsonResponse({ error: 'bad_request' }, 400); }
9
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'bad_request' }, 400);
10
+ const { text, ref } = body as Record<string, unknown>;
11
+ if (typeof text !== 'string' || !text.trim() || typeof ref !== 'string' || !ref.trim()) {
12
+ return jsonResponse({ error: 'bad_request' }, 400);
13
+ }
14
+ const store = new CardStore(root);
15
+ try {
16
+ const card = createWishCard({ text, source: 'pwa', ref }, store);
17
+ return jsonResponse(card, card.created ? 201 : 200);
18
+ } finally {
19
+ store.close();
20
+ }
21
+ }
22
+
4
23
  /** Read-only card API; route registration and authorization belong to the HTTP server. */
5
24
  export function handleTaskCardsGet(pathname: string, root?: string): Response {
6
25
  if (pathname !== '/v1/task-cards' && !/^\/v1\/task-cards\/[^/]+$/.test(pathname)) {
@@ -9,6 +9,7 @@ import { checkAuth, type MetaApiOpts } from './meta-api.js';
9
9
  import { jsonResponse } from './json-response.js';
10
10
  import { runLedgerDir, runLedgerPath } from '../../self-implement/run-ledger.js';
11
11
  import { scanStoresForRun } from './trace-run-scan.js';
12
+ import { listSessions } from '../../session/index.js';
12
13
 
13
14
  export type TraceLevel = 'L0' | 'L1' | 'L2' | 'L3';
14
15
  export interface TraceEvent {
@@ -17,6 +18,7 @@ export interface TraceEvent {
17
18
  universe: string;
18
19
  machine?: string;
19
20
  runId?: string;
21
+ projectId?: string;
20
22
  parentRunId?: string;
21
23
  phase?: string;
22
24
  shard?: string;
@@ -83,6 +85,7 @@ function eventFor(row: LogStoreRow): TraceEvent {
83
85
  id: evidenceRef(universe, row.id), ts: row.ts, universe,
84
86
  ...(machine ? { machine } : {}),
85
87
  ...(runId ? { runId } : {}),
88
+ ...(text(data.projectId) ? { projectId: text(data.projectId) } : {}),
86
89
  ...(text(data.parentRunId) ? { parentRunId: text(data.parentRunId) } : {}),
87
90
  ...(text(data.phase) ? { phase: text(data.phase) } : {}),
88
91
  ...(text(data.shard) ? { shard: text(data.shard) } : {}),
@@ -259,11 +262,15 @@ function options(url: URL): { level: TraceLevel; limit: number; from?: number; t
259
262
  if (from === null || to === null || (from !== undefined && to !== undefined && from > to)) return { level, limit: n, error: 'invalid time range' };
260
263
  return { level, limit: Math.min(n, TRACE_LIMIT), ...(from !== undefined ? { from } : {}), ...(to !== undefined ? { to } : {}) };
261
264
  }
262
- function matches(row: LogStoreRow, url: URL): boolean {
265
+ function matches(row: LogStoreRow, url: URL, projectSessions?: ReadonlySet<string>): boolean {
263
266
  const data = dataFor(row);
264
267
  const runId = url.searchParams.get('runId');
265
268
  const universe = url.searchParams.get('universe');
266
269
  const kind = url.searchParams.get('kind');
270
+ const projectId = url.searchParams.get('projectId');
271
+ if (projectId !== null && (Object.hasOwn(data, 'projectId')
272
+ ? data.projectId !== projectId
273
+ : !projectSessions?.has(row.session_id ?? '') && !projectSessions?.has(text(data.sessionId) ?? ''))) return false;
267
274
  const q = url.searchParams.get('q')?.toLowerCase();
268
275
  if (runId && data.runId !== runId && data.run_id !== runId) return false;
269
276
  if (universe && row.instance !== universe) return false;
@@ -295,6 +302,8 @@ function findRunUniverse(runId: string, selected: readonly string[], deps: Trace
295
302
  export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {}): Response {
296
303
  if (!checkAuth(req, opts)) return jsonResponse({ error: 'unauthorized' }, 401);
297
304
  const url = new URL(req.url);
305
+ const projectId = url.searchParams.get('projectId');
306
+ const projectSessions = projectId === null ? undefined : new Set(listSessions({ projectId }).map(session => session.id));
298
307
  const parsed = options(url);
299
308
  if (parsed.error) return jsonResponse({ ok: false, error: 'bad_request', reason: parsed.error }, 400);
300
309
  const names = storeNames(url, deps);
@@ -315,7 +324,8 @@ export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {
315
324
  for (const entry of stores) {
316
325
  if (!entry.store) { failures.push({ name: entry.name, reason: entry.error ?? 'log-store-unavailable' }); continue; }
317
326
  try {
318
- const page = entry.store.aggregateRuns({ sinceMs: parsed.from, untilMs: parsed.to }, RUN_LIMIT);
327
+ const page = entry.store.aggregateRuns({ sinceMs: parsed.from, untilMs: parsed.to,
328
+ ...(projectId !== null ? { projectId, projectSessionIds: [...projectSessions!] } : {}) }, RUN_LIMIT);
319
329
  if (page.length >= RUN_LIMIT) runLimitReached = true;
320
330
  runs.push(...page.map((run) => ({ ...run, universe: entry.name })));
321
331
  opened++;
@@ -334,7 +344,8 @@ export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {
334
344
  for (const entry of stores) {
335
345
  if (!entry.store) { failures.push({ name: entry.name, reason: entry.error ?? 'log-store-unavailable' }); continue; }
336
346
  try {
337
- const query: LogQuery = { sinceMs: parsed.from, untilMs: parsed.to, limit: TRACE_LIMIT + 1 };
347
+ const query: LogQuery = { sinceMs: parsed.from, untilMs: parsed.to, limit: TRACE_LIMIT + 1,
348
+ ...(projectId !== null ? { projectId, projectSessionIds: [...projectSessions!] } : {}) };
338
349
  const search = url.searchParams.get('q');
339
350
  if (search) query.grep = search;
340
351
  // Restrict a run before paging; other JSON-only filters still apply to the bounded window.
@@ -349,7 +360,7 @@ export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {
349
360
  const runId = url.searchParams.get('runId');
350
361
  let resolution: { universe?: string; checked: number; truncated?: boolean; unresolved?: boolean } | undefined;
351
362
  let resolvedFrom: 'ledger' | 'log-scan' | undefined;
352
- if ((parsed.level === 'L2' || parsed.level === 'L3') && runId && !rows.some((row) => matches(row, url)) && !requestedUniverse) {
363
+ if ((parsed.level === 'L2' || parsed.level === 'L3') && runId && !rows.some((row) => matches(row, url, projectSessions)) && !requestedUniverse) {
353
364
  // A store with no log db at all («has no log store yet» — e.g. the daemon's own tree-derived universe) cannot
354
365
  // hold the run, so it must not block the ledger lookup. A store that exists but failed to open/read still does:
355
366
  // «could not read» is never answered as «not there». The reply keeps `failedStores` either way.
@@ -379,7 +390,8 @@ export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {
379
390
  if (!entry?.store) return jsonResponse({ ok: false, error: 'log-store-unavailable', failedStores: [{ name, reason: entry?.error ?? 'log-store-unavailable' }] }, 503);
380
391
  stores.push(entry);
381
392
  try {
382
- const query: LogQuery = { sinceMs: parsed.from, untilMs: parsed.to, limit: TRACE_LIMIT + 1 };
393
+ const query: LogQuery = { sinceMs: parsed.from, untilMs: parsed.to, limit: TRACE_LIMIT + 1,
394
+ ...(projectId !== null ? { projectId, projectSessionIds: [...projectSessions!] } : {}) };
383
395
  const search = url.searchParams.get('q');
384
396
  if (search) query.grep = search;
385
397
  const page = entry.store.queryTraceRun(runId, query);
@@ -388,7 +400,7 @@ export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {
388
400
  } catch (e) { return jsonResponse({ ok: false, error: 'log-store-unavailable', failedStores: [{ name, reason: e instanceof Error ? e.message : String(e) }] }, 503); }
389
401
  }
390
402
  }
391
- const filtered = rows.filter((row) => matches(row, url)).sort(sortRows);
403
+ const filtered = rows.filter((row) => matches(row, url, projectSessions)).sort(sortRows);
392
404
  const trace = buildTrace(filtered.slice(0, parsed.limit), parsed.level, filtered.length > parsed.limit || candidateLimitReached || failures.length > 0 || resolution?.truncated === true);
393
405
  try { (deps.queryLog ?? ((details) => debug.log('logs.trace', 'query', details)))({ stores: names, level: parsed.level, count: trace.events.length, truncated: trace.truncated, mode: 'rows', runs: trace.nodes.filter((node) => node.level === 'L1' && node.count > 0).length }); } catch { /* query logging must not block reads */ }
394
406
  return jsonResponse({ ok: true, ...trace, stores: names, registeredStores: registeredStoreCount(deps), ...(resolution ? resolution.universe ? { resolvedFrom, runUniverse: resolution.universe, ...(resolvedFrom === 'log-scan' ? { checked: resolution.checked } : {}) } : { runUniverse: resolution.truncated || resolution.unresolved ? 'unresolved' as const : 'not-found' as const, checked: resolution.checked } : {}), ...(failures.length ? { failedStores: failures } : {}) }, 200);
@@ -0,0 +1,88 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { getGlobalWorkflowDir, getProjectWorkflowDir } from '../../workflow-runtime/discovery.js';
5
+
6
+ export interface WorkflowHistoryOpts {
7
+ scope?: 'project' | 'global';
8
+ cwd?: string;
9
+ }
10
+
11
+ export interface WorkflowHistoryVersion {
12
+ id: string;
13
+ createdAt: string;
14
+ size: number;
15
+ }
16
+
17
+ const MAX_VERSIONS = 20;
18
+ const VERSION_ID = /^(\d{13})-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
19
+
20
+ function safeName(name: string): void {
21
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name) || name.includes('..')) {
22
+ throw new Error('invalid workflow name');
23
+ }
24
+ }
25
+
26
+ function safeVersionId(id: string): void {
27
+ if (!/^[A-Za-z0-9_-]+$/.test(id)) throw new Error('invalid workflow history version id');
28
+ }
29
+
30
+ function workflowDir(opts: WorkflowHistoryOpts): string {
31
+ return opts.scope === 'global'
32
+ ? getGlobalWorkflowDir()
33
+ : getProjectWorkflowDir(opts.cwd ?? process.cwd());
34
+ }
35
+
36
+ function historyDir(name: string, opts: WorkflowHistoryOpts): string {
37
+ safeName(name);
38
+ return join(workflowDir(opts), '.history', name);
39
+ }
40
+
41
+ /** Capture the current YAML before its caller overwrites it; a missing file has no history. */
42
+ export function snapshotBeforeSave(name: string, opts: WorkflowHistoryOpts = {}): WorkflowHistoryVersion | null {
43
+ const dir = historyDir(name, opts);
44
+ const source = join(workflowDir(opts), `${name}.yaml`);
45
+ if (!existsSync(source)) return null;
46
+ const content = readFileSync(source);
47
+ const previous = listWorkflowHistory(name, opts)[0];
48
+ if (previous && readFileSync(join(dir, `${previous.id}.yaml`)).equals(content)) return null;
49
+
50
+ mkdirSync(dir, { recursive: true });
51
+ const timestamp = Math.max(Date.now(), previous ? Number(previous.id.slice(0, 13)) + 1 : 0);
52
+ const id = `${String(timestamp).padStart(13, '0')}-${randomUUID()}`;
53
+ const version: WorkflowHistoryVersion = {
54
+ id,
55
+ createdAt: new Date(Number(id.slice(0, 13))).toISOString(),
56
+ size: content.byteLength,
57
+ };
58
+ writeFileSync(join(dir, `${id}.yaml`), content, { flag: 'wx' });
59
+ for (const old of listWorkflowHistory(name, opts).slice(MAX_VERSIONS)) {
60
+ unlinkSync(join(dir, `${old.id}.yaml`));
61
+ }
62
+ return version;
63
+ }
64
+
65
+ /** Enumerate only this workflow's snapshots, newest first. */
66
+ export function listWorkflowHistory(name: string, opts: WorkflowHistoryOpts = {}): WorkflowHistoryVersion[] {
67
+ const dir = historyDir(name, opts);
68
+ if (!existsSync(dir)) return [];
69
+ return readdirSync(dir)
70
+ .filter(file => file.endsWith('.yaml') && VERSION_ID.test(file.slice(0, -5)))
71
+ .map(file => {
72
+ const id = file.slice(0, -5);
73
+ const filePath = join(dir, file);
74
+ const stat = statSync(filePath);
75
+ return stat.isFile() ? { id, createdAt: new Date(Number(id.slice(0, 13))).toISOString(), size: stat.size } : null;
76
+ })
77
+ .filter((version): version is WorkflowHistoryVersion => version !== null)
78
+ .sort((a, b) => b.id.localeCompare(a.id));
79
+ }
80
+
81
+ /** A missing (well-formed) version returns null rather than an empty YAML draft. */
82
+ export function getWorkflowHistoryVersion(name: string, id: string, opts: WorkflowHistoryOpts = {}): string | null {
83
+ const dir = historyDir(name, opts);
84
+ safeVersionId(id);
85
+ const file = join(dir, `${id}.yaml`);
86
+ if (!VERSION_ID.test(id) || !existsSync(file)) return null;
87
+ return readFileSync(file, 'utf-8');
88
+ }