elanous 0.2.10 → 0.2.11

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 (584) 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/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  7. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  19. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  20. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  25. package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.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-1dcdc58fc2de2454.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-b12b6d2f8d2d1aea.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.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-50cb2a0eb15e6c83.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-35414e0a007b4082.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.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-239c17b911b84e72.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.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/543f56bcb1f79fca.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/drafts/draft-cleanup.yaml +19 -0
  192. package/graphs/drafts/recipes.yaml +23 -0
  193. package/graphs/intake/intake-daily.yaml +2 -1
  194. package/graphs/ops/recipes.yaml +3 -0
  195. package/graphs/ops/secret-exposure.yaml +24 -0
  196. package/graphs/release/recipes.yaml +3 -0
  197. package/graphs/release/release-loop.yaml +4 -1
  198. package/graphs/video/explainer-line.yaml +2 -2
  199. package/package.json +2 -1
  200. package/plugins/elanous-hwp/README.md +25 -0
  201. package/plugins/elanous-hwp/cli.ts +22 -0
  202. package/plugins/elanous-hwp/converter.ts +237 -0
  203. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  204. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  205. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  206. package/plugins/elanous-hwp/plugin.json +16 -0
  207. package/plugins/elanous-hwp/plugin.ts +40 -0
  208. package/scripts/ci-public-export-leak-gate.ts +75 -14
  209. package/scripts/ci-public-leak-gate.ts +44 -9
  210. package/scripts/context-hooks/claude-stop.sh +4 -0
  211. package/scripts/context-hooks/emit.ts +24 -0
  212. package/scripts/context-hooks/post-tool-use.sh +4 -0
  213. package/scripts/coord-channel-watch.sh +12 -0
  214. package/scripts/coord-requests-bridge.ts +143 -0
  215. package/scripts/field-feed/lib.ts +4 -0
  216. package/scripts/fleet/node-setup.sh +20 -3
  217. package/scripts/homebrew-formula-bump.ts +69 -0
  218. package/scripts/marketing/measure-site-numbers.ts +169 -0
  219. package/scripts/measure-gate-shards.ts +309 -0
  220. package/scripts/mission-request-judge.ts +31 -1
  221. package/scripts/morning-report.ts +2 -2
  222. package/scripts/opportunity-scan.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +21 -4
  224. package/scripts/release-loop/cut-branch.ts +84 -0
  225. package/scripts/release-loop/gate-node.ts +14 -13
  226. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  227. package/scripts/release-loop/node-verdict.ts +3 -3
  228. package/scripts/release-loop/tui-node-checks.ts +42 -0
  229. package/scripts/release-loop/tui-sim-node.ts +32 -8
  230. package/scripts/release-loop/unattended-release.ts +5 -3
  231. package/scripts/release-loop/version-node.ts +61 -10
  232. package/scripts/signal-digest-cycle.ts +2 -7
  233. package/scripts/signal-digest-photo.ts +11 -0
  234. package/scripts/tech1-public-measure.ts +94 -0
  235. package/scripts/test-diet/node.ts +1 -1
  236. package/scripts/tui-regress.ts +2 -2
  237. package/scripts/ux/demo-rehearsal.ts +243 -0
  238. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  239. package/skills/diagram-master/SKILL.md +366 -0
  240. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  241. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  242. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  243. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  244. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  245. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  246. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  247. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  248. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  249. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  250. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  251. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  252. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  253. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  254. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  255. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  256. package/skills/diagram-master/references/examples/README.md +69 -0
  257. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  258. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  259. package/skills/diagram-master/references/pyproject.toml +16 -0
  260. package/skills/diagram-master/references/quality-checklist.md +33 -0
  261. package/skills/diagram-master/references/routing-guide.md +164 -0
  262. package/skills/diagram-master/references/uv.lock +1356 -0
  263. package/skills/google-workspace/SKILL.md +33 -0
  264. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  265. package/skills/grill-me/SKILL.md +1 -1
  266. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  267. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  268. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  269. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  270. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  271. package/skills/omni-crawl/SKILL.md +21 -11
  272. package/skills/omni-crawl/scripts/main.ts +6 -6
  273. package/skills/omni-crawl/src/router.ts +3 -3
  274. package/skills/omni-digest/SKILL.md +17 -9
  275. package/skills/omni-digest/digest.md +2 -2
  276. package/skills/omni-digest/src/fetch.ts +30 -3
  277. package/skills/pitch-deck/SKILL.md +2 -2
  278. package/skills/pitch-deck/template.html +2 -2
  279. package/skills/youtube-master/SKILL.md +163 -0
  280. package/skills/youtube-master/package-lock.json +558 -0
  281. package/skills/youtube-master/package.json +13 -0
  282. package/skills/youtube-master/references/study-note-template.md +124 -0
  283. package/skills/youtube-master/scripts/main.ts +435 -0
  284. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  285. package/skills/youtube-master/src/audio.ts +63 -0
  286. package/skills/youtube-master/src/domain-staging.ts +50 -0
  287. package/skills/youtube-master/src/env.ts +36 -0
  288. package/skills/youtube-master/src/obsidian.ts +155 -0
  289. package/skills/youtube-master/src/router.ts +110 -0
  290. package/skills/youtube-master/src/stt.ts +199 -0
  291. package/skills/youtube-master/src/study-note.ts +175 -0
  292. package/skills/youtube-master/src/summarize.ts +358 -0
  293. package/skills/youtube-master/src/transcript.ts +76 -0
  294. package/skills/youtube-master/src/types.ts +91 -0
  295. package/skills/youtube-master/src/util.ts +93 -0
  296. package/skills/youtube-master/src/youtube.ts +70 -0
  297. package/skills/youtube-master/tsconfig.json +13 -0
  298. package/src/acp/server.ts +5 -2
  299. package/src/agent-mission/driver.ts +80 -10
  300. package/src/agent-mission/pty-decision.ts +71 -0
  301. package/src/agent-mission/review-loop.ts +30 -0
  302. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  303. package/src/autopilot/tool-registry.ts +2 -0
  304. package/src/boot/daemon-prompt-turn.ts +5 -0
  305. package/src/boot/daemon-runtime.ts +9 -3
  306. package/src/boot/daemon-tools/web-search.ts +3 -0
  307. package/src/card-followup/core.ts +109 -0
  308. package/src/chat/index.ts +1 -0
  309. package/src/cli/drive-input-jail.ts +101 -0
  310. package/src/cli/hooks-cli.ts +17 -3
  311. package/src/cli/make-cli.ts +43 -0
  312. package/src/cli/persona-cli.ts +90 -4
  313. package/src/cli/plugin-cli.ts +31 -22
  314. package/src/cli/pod-cli.ts +19 -9
  315. package/src/cli/pr-cli.ts +51 -15
  316. package/src/cli/pty-drive-cli.ts +41 -7
  317. package/src/cli/pty-drive-verdict.ts +40 -0
  318. package/src/cli/release-cli.ts +17 -4
  319. package/src/cli/setup-cli.ts +43 -1
  320. package/src/cli/skills-cli.ts +17 -1
  321. package/src/context-bus/claude-hooks-install.ts +10 -0
  322. package/src/context-bus/context-now-surfaces.ts +48 -0
  323. package/src/context-bus/context-now.ts +104 -0
  324. package/src/context-bus/coord-events.ts +2 -2
  325. package/src/context-bus/external-events.ts +52 -0
  326. package/src/context-bus/long-term-memory.ts +108 -0
  327. package/src/dashboard/index.ts +9 -4
  328. package/src/dashboard/slash-runtime/dashboard-handlers.ts +7 -1
  329. package/src/decisions/decision-cards.ts +10 -3
  330. package/src/decisions/decision-ledger.ts +13 -5
  331. package/src/discord/sprint21-runtime.ts +5 -1
  332. package/src/discord-self-message.ts +32 -0
  333. package/src/discord-slash-wire.ts +5 -1
  334. package/src/discord.ts +17 -1
  335. package/src/domains/core-tools.ts +3 -2
  336. package/src/domains/outbound-alert.ts +6 -1
  337. package/src/domains/telegram-kind-route.ts +51 -13
  338. package/src/exec-requests/default-outputs.ts +25 -0
  339. package/src/exec-requests/planner.ts +10 -2
  340. package/src/exec-requests/runner.ts +20 -6
  341. package/src/exec-requests/store.ts +3 -0
  342. package/src/execution-loop/launch-gate.ts +100 -0
  343. package/src/field/field-reel.ts +5 -1
  344. package/src/field-note/index.ts +201 -0
  345. package/src/field-note/transcribe.ts +248 -0
  346. package/src/graph-kinds/registry.ts +3 -3
  347. package/src/graph-runner/graph-cli.ts +61 -1
  348. package/src/graph-runner/graph-grow-llm.ts +37 -0
  349. package/src/graph-runner/graph-grow.ts +47 -0
  350. package/src/graph-runner/runner.ts +317 -25
  351. package/src/grok/agent-search.ts +8 -2
  352. package/src/guardian/secret-exposure-scan.ts +101 -0
  353. package/src/harness/harness-cli-command.ts +247 -7
  354. package/src/harness/harness-pod-dispatch.ts +43 -23
  355. package/src/harness/harness-seams.ts +38 -4
  356. package/src/harness/harness-stop.ts +23 -4
  357. package/src/harness/launch-registry.ts +91 -0
  358. package/src/harness/process-garbage.ts +5 -3
  359. package/src/hooks/dispatch.ts +82 -0
  360. package/src/hooks/expose.ts +254 -0
  361. package/src/hooks/github-shadow.ts +108 -0
  362. package/src/hooks/providers.ts +71 -5
  363. package/src/hooks/queue.ts +1 -1
  364. package/src/hooks/receiver.ts +21 -20
  365. package/src/index.ts +52 -14
  366. package/src/intake-plane/categorize.ts +18 -46
  367. package/src/intake-plane/discord-seat-work.ts +10 -0
  368. package/src/intake-plane/front-route-classifier.ts +33 -39
  369. package/src/intake-plane/persona-answer.ts +30 -0
  370. package/src/intake-plane/runtime-callables.ts +8 -1
  371. package/src/intake-plane/telegram-seat-work.ts +36 -2
  372. package/src/intake-plane/wish-folder.ts +67 -0
  373. package/src/intent-gate/gate.ts +30 -10
  374. package/src/live/detail-switch.ts +5 -0
  375. package/src/llm/difficulty.ts +57 -0
  376. package/src/llm/judge-layer.ts +101 -0
  377. package/src/llm/mission-router.ts +33 -19
  378. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  379. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  380. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  381. package/src/loops/contract/loop-agent.schema.json +138 -0
  382. package/src/loops/contract/validate.ts +64 -0
  383. package/src/loops/registry.ts +20 -6
  384. package/src/market/signed-index.ts +4 -0
  385. package/src/maturity/bot-command-maturity.ts +45 -0
  386. package/src/maturity/feature-maturity.ts +26 -4
  387. package/src/model-tier/index.ts +2 -2
  388. package/src/model-tier/tier-resolver.ts +20 -20
  389. package/src/model-tier/types.ts +6 -6
  390. package/src/mss/logging/log-store.ts +15 -0
  391. package/src/nexus/api/config-model-tier.ts +5 -4
  392. package/src/nexus/api/fabric-plans.ts +53 -0
  393. package/src/nexus/api/harness-api.ts +5 -1
  394. package/src/nexus/api/health.ts +2 -0
  395. package/src/nexus/api/http-server.ts +36 -0
  396. package/src/nexus/api/inside-events.ts +226 -0
  397. package/src/nexus/api/meta-api.ts +21 -8
  398. package/src/nexus/api/missions.ts +6 -1
  399. package/src/nexus/api/ops-seats.ts +33 -0
  400. package/src/nexus/api/outputs-route.ts +44 -0
  401. package/src/nexus/api/seat-requests.ts +44 -2
  402. package/src/nexus/api/sessions-store.ts +17 -38
  403. package/src/nexus/api/sub-seat-charters.ts +34 -0
  404. package/src/nexus/api/tabs-mutations.ts +3 -1
  405. package/src/nexus/api/trace.ts +18 -6
  406. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  407. package/src/nexus/api/workflows.ts +80 -6
  408. package/src/nexus/index.ts +2 -0
  409. package/src/nexus/kinds/webterm.ts +2 -1
  410. package/src/nexus/webterm/pty.ts +6 -2
  411. package/src/onboarding/cli-auth-status.ts +80 -0
  412. package/src/onboarding/voice-ai.ts +3 -3
  413. package/src/org/seat-budget.ts +48 -0
  414. package/src/org/stall-escalation.ts +63 -0
  415. package/src/outputs/ledger.ts +120 -0
  416. package/src/persona/persona-todo.ts +79 -0
  417. package/src/persona/presets.ts +116 -2
  418. package/src/persona/team-set.ts +155 -0
  419. package/src/plugins/install/plugin-install.ts +14 -0
  420. package/src/plugins/maker/market-bundle.ts +109 -0
  421. package/src/plugins/maker/plugin-maker.ts +254 -11
  422. package/src/plugins/maker/wizard-generate.ts +103 -0
  423. package/src/pod-lease/admission.ts +150 -0
  424. package/src/pod-lease/dependency-state.ts +85 -0
  425. package/src/pod-lease/host-lease.ts +127 -0
  426. package/src/project/project-suggestion.ts +16 -0
  427. package/src/project/session-context.ts +24 -0
  428. package/src/release-loop/checklist.ts +2 -2
  429. package/src/release-loop/feature-store.ts +5 -3
  430. package/src/release-loop/next-md-merge.ts +58 -0
  431. package/src/release-loop/release-note.ts +59 -4
  432. package/src/seat-dispatch/seat-ask.ts +130 -0
  433. package/src/seat-dispatch/seat-questions.ts +100 -0
  434. package/src/seat-loop/seat-loop.ts +548 -29
  435. package/src/seat-loop/seat-report.ts +37 -2
  436. package/src/self-dev/dev-cli.ts +4 -1
  437. package/src/self-dev/dev-pipeline.ts +37 -1
  438. package/src/self-dev/draft-sweep.ts +38 -33
  439. package/src/self-dev/draft-triage-rules.ts +8 -5
  440. package/src/self-dev/fabric-plan-core.ts +297 -0
  441. package/src/self-dev/orchestrate.ts +36 -4
  442. package/src/self-dev/release-path-guard.ts +62 -0
  443. package/src/self-dev/shard-aggregate.ts +230 -0
  444. package/src/self-dev/shard-placement.ts +60 -0
  445. package/src/self-dev/shard-plan.ts +48 -0
  446. package/src/self-dev/shard-pod-run.ts +77 -0
  447. package/src/self-dev/shard-run.ts +105 -0
  448. package/src/self-implement/gate-baseline.ts +1 -1
  449. package/src/self-implement/gate-cli.ts +40 -1
  450. package/src/self-implement/gate-scope.ts +37 -7
  451. package/src/self-implement/graph-variant.ts +171 -0
  452. package/src/self-implement/harness-policy-gates.ts +1 -1
  453. package/src/self-implement/headless-elanous-driver.ts +22 -3
  454. package/src/self-implement/host-regate.ts +22 -1
  455. package/src/self-implement/importer-test-index.ts +2 -1
  456. package/src/self-implement/l8-merge-queue.ts +130 -0
  457. package/src/self-implement/orchestrator.ts +242 -9
  458. package/src/self-implement/route-consumer-test-index.ts +122 -0
  459. package/src/self-implement/seams.ts +72 -11
  460. package/src/self-implement/test-seams.ts +1 -0
  461. package/src/self-implement/uncommitted-work.ts +59 -0
  462. package/src/session/index.ts +36 -13
  463. package/src/skills/gift-install.ts +215 -0
  464. package/src/skills/tools/web-search.ts +4 -1
  465. package/src/steward/launch.ts +42 -26
  466. package/src/steward/steward-cards.ts +17 -9
  467. package/src/steward/triage.ts +46 -14
  468. package/src/task-cards/card-cli.ts +25 -1
  469. package/src/task-orchestrator/mission.ts +5 -0
  470. package/src/task-orchestrator/store.ts +12 -3
  471. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  472. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  473. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  474. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  475. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  476. package/src/task-orchestrator/types.ts +2 -0
  477. package/src/telegram-card-followup.ts +16 -96
  478. package/src/telegram-commands.ts +10 -1
  479. package/src/telegram-project-command.ts +71 -0
  480. package/src/telegram-report.ts +21 -17
  481. package/src/telegram.ts +29 -3
  482. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  483. package/src/user-config.ts +102 -20
  484. package/src/version/packed-revision.json +1 -1
  485. package/src/web-search/grok.ts +7 -1
  486. package/src/web-search/index.ts +28 -1
  487. package/src/web-search/provider.ts +4 -0
  488. package/src/web-search/x-intent.ts +5 -0
  489. package/src/workflow-runtime/executor.ts +277 -47
  490. package/src/workflow-runtime/index.ts +1 -0
  491. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  492. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  493. package/src/workflow-runtime/schema.ts +79 -4
  494. package/src/workflow-runtime/types.ts +35 -4
  495. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  505. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  506. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  525. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  583. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  584. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -3,6 +3,7 @@
3
3
  import { debug, redactSecretText } from '../../debug/log.js';
4
4
  import { parseOwner, type ChecklistItem } from '../../release-loop/checklist.js';
5
5
  import { canonicalSeatId } from '../../msg/msg-store.js';
6
+ import { listSubSeatCharters, type SubSeatCharter } from './sub-seat-charters.js';
6
7
 
7
8
  export const SEATS = [
8
9
  { seat: 'OP', role: 'COO' }, { seat: 'TC', role: 'CTO' }, { seat: 'MK', role: 'CMO' }, { seat: 'UX', role: 'CXO' },
@@ -26,6 +27,7 @@ export interface SeatsSources {
26
27
  checklist(): { current: ChecklistItem[]; all: ChecklistItem[] } | null;
27
28
  /** `raisedBy.agent` of every open decision. */
28
29
  openDecisionRaisers(): string[] | null;
30
+ charters?(): SubSeatCharter[] | null;
29
31
  }
30
32
 
31
33
  export interface SeatRow {
@@ -36,6 +38,7 @@ export interface SeatRow {
36
38
  blocked: Array<{ id: string; title: string; status: 'red' }> | null;
37
39
  pendingDecisions: number | null;
38
40
  checklist: { green: number; yellow: number; red: number; done: number } | null;
41
+ subSeats: Array<{ id: string; title: string; open: number | null; landed: number | null; blocked: Array<{ id: string; title: string }> | null }> | null;
39
42
  }
40
43
 
41
44
  function seatOf(value: string | undefined): string | null {
@@ -65,16 +68,37 @@ export async function buildSeatsBoard(date: string, sources: SeatsSources): Prom
65
68
  try { checklist = sources.checklist(); } catch { checklist = null; }
66
69
  let raisers: string[] | null = null;
67
70
  try { raisers = sources.openDecisionRaisers(); } catch { raisers = null; }
71
+ let charters: SubSeatCharter[] | null = null;
72
+ try {
73
+ if (sources.charters) charters = sources.charters();
74
+ else {
75
+ let unreadable = false;
76
+ const listed = listSubSeatCharters(undefined, () => { unreadable = true; });
77
+ charters = unreadable ? null : listed;
78
+ }
79
+ } catch { charters = null; }
68
80
 
69
81
  const owners = new Map<string, string>();
70
82
  for (const item of checklist?.all ?? []) { const seat = seatOf(item.owner); if (seat) owners.set(item.id, seat); }
71
83
  const ids = [...owners.keys()];
72
84
  const landedBySeat = new Map<string, NonNullable<SeatRow['landed']>>();
85
+ const landedBySub = new Map<string, number>();
86
+ const subOwner = (owner: string | undefined, charter: SubSeatCharter): boolean => {
87
+ if (!owner) return false;
88
+ try { const parsed = parseOwner(owner); return owner === charter.id && parsed.seat === charter.seat && parsed.sub === charter.sub; }
89
+ catch { return false; }
90
+ };
91
+ const subOwners = new Map(checklist?.all.flatMap((item) => {
92
+ const charter = charters?.find((candidate) => subOwner(item.owner, candidate));
93
+ return charter ? [[item.id, charter.id] as const] : [];
94
+ }) ?? []);
73
95
  for (const pr of merged ?? []) {
74
96
  const id = checklistIdOf(pr, ids);
75
97
  const seat = id ? owners.get(id) : undefined;
76
98
  if (!seat) continue; // ⛔ not by commit author — every seat lands through the same account.
77
99
  landedBySeat.set(seat, [...(landedBySeat.get(seat) ?? []), { pr: pr.number, title: clip(pr.title, 80), at: pr.mergedAt, checklistId: id }]);
100
+ const sub = id ? subOwners.get(id) : undefined;
101
+ if (sub) landedBySub.set(sub, (landedBySub.get(sub) ?? 0) + 1);
78
102
  }
79
103
 
80
104
  const seats = SEATS.map(({ seat, role }): SeatRow => {
@@ -91,6 +115,15 @@ export async function buildSeatsBoard(date: string, sources: SeatsSources): Prom
91
115
  green: mine.filter((i) => i.status === 'green').length, yellow: mine.filter((i) => i.status === 'yellow').length,
92
116
  red: mine.filter((i) => i.status === 'red').length, done: mine.filter((i) => i.status === 'done').length,
93
117
  } : null,
118
+ subSeats: charters?.filter((charter) => charter.seat === seat).map((charter) => {
119
+ const items = checklist?.current.filter((item) => subOwner(item.owner, charter));
120
+ return {
121
+ id: charter.id, title: clip(charter.title, 80),
122
+ open: items ? items.filter((item) => item.status !== 'done').length : null,
123
+ landed: merged !== null && checklist !== null ? landedBySub.get(charter.id) ?? 0 : null,
124
+ blocked: items ? items.filter((item) => item.status === 'red').map((item) => ({ id: item.id, title: clip(item.title, 80) })) : null,
125
+ };
126
+ }) ?? null,
94
127
  };
95
128
  });
96
129
  debug.log('ops.seats', 'built', {
@@ -0,0 +1,44 @@
1
+ import { basename } from 'node:path';
2
+ import { debug } from '../../debug/log.js';
3
+ import { OUTPUT_KINDS } from '../../exec-requests/default-outputs.js';
4
+ import { listOutputs } from '../../outputs/ledger.js';
5
+ import { jsonResponse } from './json-response.js';
6
+
7
+ export interface OutputsDeps {
8
+ list?: typeof listOutputs;
9
+ }
10
+
11
+ export function handleOutputsGet(req: Request, deps: OutputsDeps = {}): Response {
12
+ const params = new URL(req.url).searchParams;
13
+ const rawLimit = params.get('limit');
14
+ if (rawLimit !== null && (!/^[1-9]\d*$/.test(rawLimit) || Number(rawLimit) > 200)) {
15
+ return jsonResponse({ error: 'invalid_limit' }, 400);
16
+ }
17
+ const limit = rawLimit === null ? 50 : Number(rawLimit);
18
+ const rawSource = params.get('source');
19
+ if (rawSource !== null && rawSource !== 'exec' && rawSource !== 'field-reel' && rawSource !== 'field-feed') {
20
+ return jsonResponse({ error: 'invalid_source' }, 400);
21
+ }
22
+ const source = rawSource ?? undefined;
23
+ const rawSince = params.get('since');
24
+ if (rawSince !== null && (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/.test(rawSince)
25
+ || !Number.isFinite(Date.parse(rawSince))
26
+ || !Number.isFinite(Date.parse(`${rawSince.slice(0, 10)}T00:00:00Z`))
27
+ || new Date(`${rawSince.slice(0, 10)}T00:00:00Z`).toISOString().slice(0, 10) !== rawSince.slice(0, 10))) {
28
+ return jsonResponse({ error: 'invalid_since' }, 400);
29
+ }
30
+ const since = rawSince ? new Date(rawSince).toISOString() : undefined;
31
+ const rows = (deps.list ?? listOutputs)({ limit, ...(source ? { source } : {}), ...(since ? { since } : {}) });
32
+ const outputs = rows.map(({ kind, title, path, url, source: entrySource, seat, at }) => ({
33
+ kind,
34
+ kindLabel: kind === 'file' ? '파일' : OUTPUT_KINDS[kind]?.cardTitle ?? '파일',
35
+ title,
36
+ ...(path ? { fileName: basename(path) } : {}),
37
+ ...(url ? { url } : {}),
38
+ source: entrySource,
39
+ ...(seat ? { seat } : {}),
40
+ at,
41
+ }));
42
+ debug.log('outputs.route', 'listed', { count: outputs.length, source, limit });
43
+ return jsonResponse({ outputs });
44
+ }
@@ -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':
@@ -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);
@@ -73,6 +73,7 @@ export function publishWorkflowRunEvent(
73
73
  const detail: Record<string, unknown> = {
74
74
  runId: ctx.runId,
75
75
  workflowName: ctx.workflowName,
76
+ ...(evt.mode !== undefined ? { mode: evt.mode } : {}),
76
77
  };
77
78
  if (evt.type === 'node_start' || evt.type === 'node_skipped' || evt.type === 'node_done') {
78
79
  detail.nodeId = evt.nodeId;
@@ -40,8 +40,12 @@ import {
40
40
  parseWorkflowYaml,
41
41
  runWorkflow,
42
42
  saveWorkflow,
43
+ topoSort,
43
44
  type WorkflowDeps,
44
45
  type WorkflowEvent,
46
+ type WorkflowRunMode,
47
+ type NodeOutput,
48
+ type RunWorkflowOpts,
45
49
  } from '../../workflow-runtime/index.js';
46
50
  import { buildRunCft, buildRunSkill } from '../../workflow-runtime/deps-bridge.js';
47
51
  import {
@@ -59,6 +63,7 @@ import { runApprovalAcrossChannels } from './workflow-approval-multi-channel.js'
59
63
  interface RunRecord {
60
64
  runId: string;
61
65
  workflowName: string;
66
+ mode: WorkflowRunMode;
62
67
  startedAt: number;
63
68
  events: WorkflowEvent[];
64
69
  /** Settled state: undefined while running, true on workflow_done,
@@ -384,10 +389,51 @@ export async function handleWorkflowRunStart(
384
389
  // so the PWA "▶ Run now" button runs the dependent chain straight
385
390
  // through. Default false preserves the production wire.
386
391
  const dryRun: boolean = Boolean(body && typeof body === 'object' && (body as { dryRun?: unknown }).dryRun === true);
392
+ const input = body && typeof body === 'object' ? body as Record<string, unknown> : {};
393
+ const { onlyNode, fromNode, fromRunId } = input;
394
+ if ('usePins' in input || (onlyNode !== undefined && (fromNode !== undefined || fromRunId !== undefined))
395
+ || (onlyNode !== undefined && (typeof onlyNode !== 'string' || !onlyNode.trim()))
396
+ || (fromNode !== undefined && (typeof fromNode !== 'string' || !fromNode.trim()))
397
+ || (fromRunId !== undefined && (typeof fromRunId !== 'string' || !fromRunId.trim()))
398
+ || (fromNode === undefined) !== (fromRunId === undefined)
399
+ || (input.mode !== undefined && input.mode !== 'test')) {
400
+ return jsonResponse({ error: 'bad_request', reason: 'invalid run selection' }, 400);
401
+ }
402
+ if ((onlyNode && !entry.definition.nodes.some(n => n.id === onlyNode))
403
+ || (fromNode && !entry.definition.nodes.some(n => n.id === fromNode))) {
404
+ return jsonResponse({ error: 'bad_request', reason: 'unknown selected node' }, 400);
405
+ }
406
+ let previousOutputs: Record<string, NodeOutput> | undefined;
407
+ if (typeof fromRunId === 'string') {
408
+ const previous = RUN_REGISTRY.get(fromRunId);
409
+ if (previous) {
410
+ if (previous.workflowName !== name || previous.ok === undefined) {
411
+ return jsonResponse({ error: 'bad_request', reason: 'source run must be settled for this workflow' }, 400);
412
+ }
413
+ const terminal = previous.events.find(e => e.type === 'workflow_done' || e.type === 'workflow_failed');
414
+ previousOutputs = terminal?.type === 'workflow_done' ? terminal.outputs
415
+ : terminal?.type === 'workflow_failed' ? terminal.partial : {};
416
+ } else {
417
+ const disk = readRunOutputSnapshot(fromRunId);
418
+ if (!disk || disk.workflowName !== name || (disk.status !== 'done' && disk.status !== 'failed')) {
419
+ return jsonResponse({ error: 'bad_request', reason: 'source run must be settled for this workflow' }, 400);
420
+ }
421
+ previousOutputs = disk.outputs;
422
+ }
423
+ let order: string[];
424
+ try { order = topoSort(entry.definition.nodes); }
425
+ catch { return jsonResponse({ error: 'bad_request', reason: 'invalid workflow topology' }, 400); }
426
+ const upstream = order.slice(0, order.indexOf(fromNode as string));
427
+ if (upstream.some(id => previousOutputs?.[id] === undefined)) {
428
+ return jsonResponse({ error: 'bad_request', reason: 'source run missing upstream outputs' }, 400);
429
+ }
430
+ }
431
+ const mode: WorkflowRunMode = input.mode === 'test' ? 'test' : onlyNode ? 'only' : fromNode ? 'from' : 'full';
387
432
  const runId = `wf-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
388
433
  const record: RunRecord = {
389
434
  runId,
390
435
  workflowName: name,
436
+ mode,
391
437
  startedAt: Date.now(),
392
438
  events: [],
393
439
  ok: undefined,
@@ -400,10 +446,14 @@ export async function handleWorkflowRunStart(
400
446
  void (async () => {
401
447
  const deps = buildDefaultWorkflowDeps({ runId });
402
448
  try {
403
- for await (const evt of runWorkflow(
404
- { workflow: entry.definition, arguments: args, runId, dryRun },
405
- deps,
406
- )) {
449
+ const runOpts: RunWorkflowOpts = {
450
+ workflow: entry.definition, arguments: args, runId, dryRun, mode,
451
+ ...(typeof onlyNode === 'string' ? { onlyNode } : {}),
452
+ ...(typeof fromNode === 'string' ? { fromNode } : {}),
453
+ ...(previousOutputs ? { previousOutputs } : {}),
454
+ };
455
+ for await (const runtimeEvent of runWorkflow(runOpts, deps)) {
456
+ const evt = { ...runtimeEvent, mode };
407
457
  record.events.push(evt);
408
458
  // §15.8(b) — fan into NexusEventBus → PWA invalidates queries
409
459
  // on push instead of polling. No-op when bus is unwired
@@ -423,13 +473,14 @@ export async function handleWorkflowRunStart(
423
473
  type: 'workflow_failed' as const,
424
474
  error: err instanceof Error ? err.message : String(err),
425
475
  partial: {},
476
+ mode,
426
477
  };
427
478
  record.events.push(failEvt);
428
479
  publishWorkflowRunEvent({ runId, workflowName: name }, failEvt);
429
480
  }
430
481
  })();
431
482
 
432
- return jsonResponse({ ok: true, runId }, 202);
483
+ return jsonResponse({ ok: true, runId, mode }, 202);
433
484
  }
434
485
 
435
486
  export function handleWorkflowRunGet(
@@ -444,6 +495,7 @@ export function handleWorkflowRunGet(
444
495
  {
445
496
  runId: rec.runId,
446
497
  workflowName: rec.workflowName,
498
+ mode: rec.mode,
447
499
  startedAt: rec.startedAt,
448
500
  ok: rec.ok,
449
501
  events: rec.events,
@@ -597,6 +649,21 @@ interface DiskRunSummary {
597
649
  * enough that abandoned runs don't pollute the list across sessions. */
598
650
  const STALE_RUNNING_AFTER_MS = 30 * 60 * 1000;
599
651
 
652
+ function readRunOutputSnapshot(runId: string): {
653
+ workflowName: string;
654
+ status: string;
655
+ outputs: Record<string, NodeOutput>;
656
+ } | null {
657
+ if (!/^wf-[A-Za-z0-9-]+$/.test(runId) || runId.includes('..')) return null;
658
+ try {
659
+ const parsed = JSON.parse(readFileSync(join(workflowsRunsRoot(), runId, 'run.json'), 'utf-8')) as {
660
+ workflowName?: string; status?: string; outputs?: Record<string, NodeOutput>;
661
+ };
662
+ if (!parsed.workflowName || !parsed.status || !parsed.outputs || typeof parsed.outputs !== 'object') return null;
663
+ return { workflowName: parsed.workflowName, status: parsed.status, outputs: parsed.outputs };
664
+ } catch { return null; }
665
+ }
666
+
600
667
  /** Read `<root>/<runId>/run.json` and translate into the same shape as
601
668
  * GET /runs/<id> serves from memory. Returns null when the file is
602
669
  * missing or unreadable. Events are reconstructed minimally from the
@@ -608,6 +675,7 @@ function loadRunFromDisk(runId: string): {
608
675
  ok: boolean | undefined;
609
676
  events: WorkflowEvent[];
610
677
  outputs: Record<string, unknown>;
678
+ mode?: WorkflowRunMode;
611
679
  } | null {
612
680
  if (!runId || runId.includes('/') || runId.includes('..')) return null;
613
681
  const root = workflowsRunsRoot();
@@ -623,6 +691,7 @@ function loadRunFromDisk(runId: string): {
623
691
  completedAt?: number;
624
692
  ok?: boolean;
625
693
  status?: string;
694
+ mode?: WorkflowRunMode;
626
695
  outputs?: Record<string, { output?: unknown }>;
627
696
  };
628
697
  if (!parsed.runId || !parsed.workflowName) return null;
@@ -634,7 +703,8 @@ function loadRunFromDisk(runId: string): {
634
703
  // so a polling client still sees the node_done sequence even
635
704
  // though the live run.json doesn't store events.
636
705
  const events: WorkflowEvent[] = [];
637
- events.push({ type: 'workflow_start', workflow: parsed.workflowName, runId: parsed.runId });
706
+ events.push({ type: 'workflow_start', workflow: parsed.workflowName, runId: parsed.runId,
707
+ ...(parsed.mode !== undefined ? { mode: parsed.mode } : {}) });
638
708
  const nodesDir = join(runDir, 'nodes');
639
709
  // Iterate `parsed.outputs` keys instead of `readdirSync(nodesDir)`
640
710
  // so the reconstructed node_done sequence is in execution order
@@ -673,6 +743,7 @@ function loadRunFromDisk(runId: string): {
673
743
  events.push({
674
744
  type: 'node_done',
675
745
  nodeId,
746
+ ...(parsed.mode !== undefined ? { mode: parsed.mode } : {}),
676
747
  result: {
677
748
  ok: nodeData.ok,
678
749
  output: nodeData.output ?? '',
@@ -684,6 +755,7 @@ function loadRunFromDisk(runId: string): {
684
755
  if (parsed.status === 'done') {
685
756
  events.push({
686
757
  type: 'workflow_done',
758
+ ...(parsed.mode !== undefined ? { mode: parsed.mode } : {}),
687
759
  outputs: Object.fromEntries(
688
760
  Object.entries(parsed.outputs ?? {}).map(([k, v]) => [
689
761
  k,
@@ -694,6 +766,7 @@ function loadRunFromDisk(runId: string): {
694
766
  } else if (parsed.status === 'failed') {
695
767
  events.push({
696
768
  type: 'workflow_failed',
769
+ ...(parsed.mode !== undefined ? { mode: parsed.mode } : {}),
697
770
  error: 'failed (see node events)',
698
771
  partial: Object.fromEntries(
699
772
  Object.entries(parsed.outputs ?? {}).map(([k, v]) => [
@@ -706,6 +779,7 @@ function loadRunFromDisk(runId: string): {
706
779
  return {
707
780
  runId: parsed.runId,
708
781
  workflowName: parsed.workflowName,
782
+ ...(parsed.mode !== undefined ? { mode: parsed.mode } : {}),
709
783
  startedAt: parsed.startedAt ?? 0,
710
784
  ok: parsed.status === 'done' ? true : parsed.status === 'failed' ? false : undefined,
711
785
  events,