elanous 0.2.14 → 0.2.16

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 (350) 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/chunks/{1307-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +1 -0
  53. package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
  54. package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
  55. package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
  56. package/apps/pwa/out/approvals/index.html +1 -1
  57. package/apps/pwa/out/approvals/index.txt +15 -15
  58. package/apps/pwa/out/autopilot/index.html +1 -1
  59. package/apps/pwa/out/autopilot/index.txt +15 -15
  60. package/apps/pwa/out/board/index.html +1 -1
  61. package/apps/pwa/out/board/index.txt +15 -15
  62. package/apps/pwa/out/botlab/index.html +1 -1
  63. package/apps/pwa/out/botlab/index.txt +15 -15
  64. package/apps/pwa/out/bots/index.html +1 -1
  65. package/apps/pwa/out/bots/index.txt +15 -15
  66. package/apps/pwa/out/ceo/index.html +1 -0
  67. package/apps/pwa/out/ceo/index.txt +33 -0
  68. package/apps/pwa/out/chat/index.html +1 -1
  69. package/apps/pwa/out/chat/index.txt +15 -15
  70. package/apps/pwa/out/consult/index.html +1 -1
  71. package/apps/pwa/out/consult/index.txt +15 -15
  72. package/apps/pwa/out/control/index.html +1 -1
  73. package/apps/pwa/out/control/index.txt +15 -15
  74. package/apps/pwa/out/design-check/index.html +1 -1
  75. package/apps/pwa/out/design-check/index.txt +15 -15
  76. package/apps/pwa/out/editor/index.html +1 -1
  77. package/apps/pwa/out/editor/index.txt +15 -15
  78. package/apps/pwa/out/exec/index.html +1 -1
  79. package/apps/pwa/out/exec/index.txt +15 -15
  80. package/apps/pwa/out/field/index.html +1 -1
  81. package/apps/pwa/out/field/index.txt +15 -15
  82. package/apps/pwa/out/healthz +1 -1
  83. package/apps/pwa/out/index.html +1 -1
  84. package/apps/pwa/out/index.txt +15 -15
  85. package/apps/pwa/out/inside/index.html +1 -1
  86. package/apps/pwa/out/inside/index.txt +15 -15
  87. package/apps/pwa/out/intake/index.html +1 -1
  88. package/apps/pwa/out/intake/index.txt +15 -15
  89. package/apps/pwa/out/live/index.html +1 -1
  90. package/apps/pwa/out/live/index.txt +15 -15
  91. package/apps/pwa/out/loops/index.html +1 -1
  92. package/apps/pwa/out/loops/index.txt +15 -15
  93. package/apps/pwa/out/market/index.html +1 -1
  94. package/apps/pwa/out/market/index.txt +15 -15
  95. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  96. package/apps/pwa/out/missions/placeholder/index.txt +15 -15
  97. package/apps/pwa/out/morning/index.html +1 -1
  98. package/apps/pwa/out/morning/index.txt +15 -15
  99. package/apps/pwa/out/observatory/index.html +1 -1
  100. package/apps/pwa/out/observatory/index.txt +15 -15
  101. package/apps/pwa/out/ops/checklist/index.html +1 -1
  102. package/apps/pwa/out/ops/checklist/index.txt +15 -15
  103. package/apps/pwa/out/ops/release/index.html +1 -1
  104. package/apps/pwa/out/ops/release/index.txt +15 -15
  105. package/apps/pwa/out/ops/seats/index.html +1 -1
  106. package/apps/pwa/out/ops/seats/index.txt +15 -15
  107. package/apps/pwa/out/outputs/index.html +1 -1
  108. package/apps/pwa/out/outputs/index.txt +15 -15
  109. package/apps/pwa/out/reflection/index.html +1 -1
  110. package/apps/pwa/out/reflection/index.txt +16 -16
  111. package/apps/pwa/out/scheduler/index.html +1 -1
  112. package/apps/pwa/out/scheduler/index.txt +15 -15
  113. package/apps/pwa/out/sessions/index.html +1 -1
  114. package/apps/pwa/out/sessions/index.txt +15 -15
  115. package/apps/pwa/out/settings/devices/index.html +1 -1
  116. package/apps/pwa/out/settings/devices/index.txt +15 -15
  117. package/apps/pwa/out/settings/index.html +1 -1
  118. package/apps/pwa/out/settings/index.txt +15 -15
  119. package/apps/pwa/out/setup/done/index.html +1 -1
  120. package/apps/pwa/out/setup/done/index.txt +15 -15
  121. package/apps/pwa/out/setup/index.html +1 -1
  122. package/apps/pwa/out/setup/index.txt +15 -15
  123. package/apps/pwa/out/share/index.html +1 -1
  124. package/apps/pwa/out/share/index.txt +15 -15
  125. package/apps/pwa/out/showroom/index.html +1 -1
  126. package/apps/pwa/out/showroom/index.txt +15 -15
  127. package/apps/pwa/out/tasks/index.html +1 -1
  128. package/apps/pwa/out/tasks/index.txt +15 -15
  129. package/apps/pwa/out/term/index.html +1 -1
  130. package/apps/pwa/out/term/index.txt +15 -15
  131. package/apps/pwa/out/today/index.html +1 -1
  132. package/apps/pwa/out/today/index.txt +15 -15
  133. package/apps/pwa/out/trace/index.html +1 -1
  134. package/apps/pwa/out/trace/index.txt +15 -15
  135. package/apps/pwa/out/vault/index.html +1 -1
  136. package/apps/pwa/out/vault/index.txt +15 -15
  137. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  138. package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
  139. package/apps/pwa/out/workflows/index.html +1 -1
  140. package/apps/pwa/out/workflows/index.txt +15 -15
  141. package/apps/pwa/out/workspace/index.html +1 -1
  142. package/apps/pwa/out/workspace/index.txt +15 -15
  143. package/apps/pwa/out/worktrees/index.html +1 -1
  144. package/apps/pwa/out/worktrees/index.txt +15 -15
  145. package/graphs/doc-gen/doc-gen.yaml +24 -0
  146. package/graphs/doc-gen/recipes.yaml +3 -0
  147. package/graphs/heal/heal-loop.yaml +1 -1
  148. package/graphs/implement-loop.yaml +1 -1
  149. package/graphs/orchestrator/orchestrator.yaml +7 -0
  150. package/graphs/orchestrator/recipes.yaml +3 -0
  151. package/graphs/release/recipes.yaml +3 -0
  152. package/graphs/release/release-loop.yaml +4 -2
  153. package/graphs/steward/recipes.yaml +2 -1
  154. package/package.json +1 -1
  155. package/scripts/brand/check.ts +7 -5
  156. package/scripts/ci-public-export-import-gate.ts +154 -0
  157. package/scripts/ci-test-interference-gate.ts +5 -1
  158. package/scripts/ci-typecheck-changed.ts +82 -31
  159. package/scripts/docs/doc-rot.ts +164 -0
  160. package/scripts/docs/generate-graph-anatomy.ts +437 -0
  161. package/scripts/docs-cli-check.ts +4 -4
  162. package/scripts/expose-rubric-suggest.ts +18 -12
  163. package/scripts/goals/goal-census.ts +343 -0
  164. package/scripts/lecture-note/node.ts +31 -11
  165. package/scripts/lessons/handbook-page.ts +30 -3
  166. package/scripts/marketing/generate-ax-brief.ts +140 -0
  167. package/scripts/marketing/generate-feature-map.ts +118 -0
  168. package/scripts/measure/author-section-contribution.ts +84 -0
  169. package/scripts/release-loop/checklist-gate-node.ts +53 -14
  170. package/scripts/release-loop/cut-branch.ts +43 -13
  171. package/scripts/release-loop/gate-node.ts +82 -14
  172. package/scripts/release-loop/vault-note-node.ts +257 -8
  173. package/scripts/release-story/draft.ts +57 -9
  174. package/scripts/rhythm/daily.ts +263 -76
  175. package/scripts/rhythm/monthly.ts +97 -0
  176. package/scripts/rhythm/weekly.ts +30 -9
  177. package/scripts/smart-merge/resync-shadow.ts +147 -0
  178. package/src/acp/client.ts +17 -2
  179. package/src/agent-env/profile.ts +161 -0
  180. package/src/agent-mission/driver.ts +51 -1
  181. package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
  182. package/src/agent-mission/supervisor-metrics.ts +218 -0
  183. package/src/agent-substrate/pr-reviewer.ts +2 -0
  184. package/src/ask-user-question/pending-questions.ts +5 -0
  185. package/src/auth/github-app-token.ts +112 -13
  186. package/src/autopilot/build/llm-conflict-merge.ts +32 -6
  187. package/src/autopilot/track-agent-shadow.ts +2 -1
  188. package/src/briefing/brief-items.ts +276 -0
  189. package/src/budget/usage-outcomes.ts +204 -0
  190. package/src/claims/claims-render.ts +14 -5
  191. package/src/cli/agent-env-cli.ts +21 -0
  192. package/src/cli/brief-cli.ts +73 -0
  193. package/src/cli/decisions-cli.ts +9 -0
  194. package/src/cli/doctor-cli.ts +23 -1
  195. package/src/cli/doctor-node-modules.ts +147 -0
  196. package/src/cli/intake-cli.ts +46 -17
  197. package/src/cli/know-cli.ts +20 -0
  198. package/src/cli/logs-cli.ts +43 -1
  199. package/src/cli/pod-cli.ts +2 -0
  200. package/src/cli/pr-cli.ts +80 -12
  201. package/src/cli/release-cli.ts +66 -29
  202. package/src/cli/seat-requests-cli.ts +52 -0
  203. package/src/cli/self-update.ts +200 -1
  204. package/src/cli/usage-cli.ts +26 -0
  205. package/src/context-bus/context-now-public.ts +16 -6
  206. package/src/context-bus/context-now.ts +3 -1
  207. package/src/context-bus/coord-events.ts +6 -4
  208. package/src/decisions/decision-cards.ts +33 -7
  209. package/src/decisions/proact-meter.ts +286 -0
  210. package/src/decisions/proact1-lite.ts +86 -0
  211. package/src/decisions/web-push-decision-cards.ts +1 -1
  212. package/src/discord-self-message.ts +9 -0
  213. package/src/domains/alert-audit.ts +271 -0
  214. package/src/domains/core-tools.ts +4 -3
  215. package/src/domains/ops-facts-tool.ts +31 -1
  216. package/src/flow-loop/split.ts +24 -0
  217. package/src/harness/harness-cli-command.ts +147 -17
  218. package/src/harness/harness-incidents.ts +2 -0
  219. package/src/harness/harness-pod-dispatch.ts +67 -8
  220. package/src/harness/harness-queue.ts +380 -50
  221. package/src/harness/harness-seams.ts +13 -3
  222. package/src/harness/helper-repair.ts +218 -0
  223. package/src/harness/helper-scan.ts +27 -4
  224. package/src/harness/launch-stamp.ts +236 -0
  225. package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
  226. package/src/harness/ledger-write-guard.ts +35 -0
  227. package/src/harness/nested-elanous-policy.ts +114 -0
  228. package/src/harness/pod-memory-policy.ts +15 -0
  229. package/src/harness/repository-name.ts +55 -0
  230. package/src/hq/fence-audit.ts +90 -0
  231. package/src/hq/hq.ts +70 -6
  232. package/src/hq/lease.ts +9 -7
  233. package/src/hq/move-plan.ts +126 -0
  234. package/src/hq/promote.ts +31 -0
  235. package/src/hq/transfer.ts +185 -0
  236. package/src/index.ts +189 -13
  237. package/src/intake-plane/check.ts +63 -0
  238. package/src/intake-plane/digest.ts +65 -21
  239. package/src/intake-plane/front-classifier.ts +6 -0
  240. package/src/intake-plane/items.ts +3 -2
  241. package/src/intake-plane/lens.ts +177 -0
  242. package/src/intake-plane/route.ts +145 -1
  243. package/src/knowledge/know-find.ts +105 -0
  244. package/src/llm.ts +75 -19
  245. package/src/loops/budget.ts +36 -5
  246. package/src/loops/checker.ts +2 -2
  247. package/src/loops/orchestrator/author-depth.ts +65 -9
  248. package/src/loops/orchestrator/author-input-check.ts +8 -3
  249. package/src/loops/orchestrator/author-ledger.ts +3 -1
  250. package/src/loops/orchestrator/finish-rate.ts +14 -3
  251. package/src/loops/orchestrator/seat-share.ts +56 -0
  252. package/src/loops/orchestrator/tick.ts +107 -29
  253. package/src/loops/orchestrator/traffic.ts +117 -11
  254. package/src/loops/registry.ts +142 -61
  255. package/src/maturity/bot-slash-catalog.ts +11 -2
  256. package/src/maturity/feature-maturity.ts +4 -3
  257. package/src/maturity/tui-slash-availability.ts +40 -0
  258. package/src/mss/logging/log-store.ts +35 -4
  259. package/src/nexus/api/http-server.ts +28 -14
  260. package/src/nexus/api/loop-edges.ts +173 -0
  261. package/src/nexus/api/schedules-read.ts +13 -0
  262. package/src/nexus/api/seat-requests.ts +79 -56
  263. package/src/oauth/overload-failover-launch.ts +75 -0
  264. package/src/oauth/overload-failover.ts +137 -0
  265. package/src/prompt-enhance/enhance.ts +6 -1
  266. package/src/release-loop/auto-start.ts +6 -3
  267. package/src/release-loop/checklist.ts +19 -5
  268. package/src/release-loop/feature-store.ts +59 -31
  269. package/src/release-loop/merged-pr-checklist.ts +125 -0
  270. package/src/release-loop/placement.ts +98 -29
  271. package/src/seat-dispatch/seat-ask.ts +33 -6
  272. package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
  273. package/src/seat-dispatch/seat-request-ledger.ts +133 -0
  274. package/src/seat-loop/seat-loop.ts +215 -12
  275. package/src/self-dev/ask-launch-flow.ts +22 -1
  276. package/src/self-dev/authored-goal-trace.ts +64 -0
  277. package/src/self-dev/dev-cli.ts +19 -1
  278. package/src/self-dev/dev-pipeline.ts +14 -9
  279. package/src/self-dev/draft-sweep.ts +90 -10
  280. package/src/self-dev/draft-triage-rules.ts +25 -8
  281. package/src/self-dev/entrance-registry.ts +10 -6
  282. package/src/self-dev/old-door.ts +73 -0
  283. package/src/self-dev/orchestrate.ts +15 -1
  284. package/src/self-dev/review-provider-fallback.ts +6 -1
  285. package/src/self-dev/run-supervisor.ts +49 -2
  286. package/src/self-implement/app-api-budget.ts +166 -0
  287. package/src/self-implement/budget-gate.ts +7 -5
  288. package/src/self-implement/follow-up-goals.ts +289 -0
  289. package/src/self-implement/frozen-merges.ts +3 -2
  290. package/src/self-implement/goal-author.ts +50 -6
  291. package/src/self-implement/goal-clarification-escalation.ts +5 -0
  292. package/src/self-implement/heal-intake.ts +120 -2
  293. package/src/self-implement/heal-triage.ts +44 -0
  294. package/src/self-implement/host-regate.ts +9 -2
  295. package/src/self-implement/l8-merge-queue.ts +132 -0
  296. package/src/self-implement/orchestrator.ts +325 -47
  297. package/src/self-implement/pipeline-shape.ts +1 -1
  298. package/src/self-implement/revert-guard.ts +42 -7
  299. package/src/self-implement/run-ledger.ts +6 -0
  300. package/src/self-implement/run-status-mapping.ts +2 -0
  301. package/src/self-implement/seams.ts +45 -16
  302. package/src/self-implement/self-implement-cli.ts +1 -0
  303. package/src/self-implement/speed-claims.ts +338 -0
  304. package/src/session-runtime/index.ts +2 -0
  305. package/src/session-runtime/retry-api.ts +87 -0
  306. package/src/session-runtime/retry-policy.ts +107 -6
  307. package/src/steward/triage.ts +74 -3
  308. package/src/steward/working-backwards.ts +20 -2
  309. package/src/task-cards/card-cli.ts +25 -0
  310. package/src/task-cards/card-store.ts +5 -3
  311. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
  312. package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
  313. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
  314. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
  315. package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
  316. package/src/task-orchestrator/surfaces/pod-pool.ts +70 -9
  317. package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
  318. package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
  319. package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
  320. package/src/telegram-commands.ts +5 -0
  321. package/src/telegram.ts +72 -4
  322. package/src/user-config.ts +64 -7
  323. package/src/version/packed-revision.json +1 -1
  324. package/src/workflow-runtime/nodes/bash.ts +9 -1
  325. package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
  344. package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
  345. package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
  346. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
  347. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
  348. package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
  349. package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
  350. /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
@@ -0,0 +1,271 @@
1
+ // 대표에게 가는 알림·브리핑 전수 재고 (ALERT-AUDIT).
2
+ //
3
+ // «전달 ok» 는 발송 시도 기록이다. 도착은 배송 원장의 채널 ok 한 건뿐이다.
4
+ // 발송 설정이 꺼져 있으면 시도 기록만 남고 도착은 0건이 된다 — 그 칸을
5
+ // claimed-without-arrival 로 표시한다. 같은 내용이 둘 이상의 채널·봇에
6
+ // 도착하면 overlap, 레지스트리에 있는데 아무도 안 보내면 gap.
7
+
8
+ import type { ScheduleRow } from './schedule-registry.js';
9
+ import type { DeliveryRow } from '../nexus/outbound/delivery-ledger.js';
10
+
11
+ export type AlertSenderKind = 'loop' | 'cron' | 'script';
12
+ export type AlertMark = 'claimed-without-arrival' | 'overlap' | 'gap' | 'overlap-unknown' | 'none';
13
+
14
+ export interface AlertSenderRef {
15
+ kind: AlertSenderKind;
16
+ id: string;
17
+ }
18
+
19
+ export interface AlertRegistrySender {
20
+ kind: AlertSenderKind;
21
+ id: string;
22
+ /** 대표에게 보내는 알림·브리핑이면 true. false 는 대조 모집단에서 빠진다. */
23
+ representative: boolean;
24
+ what: string;
25
+ when: string;
26
+ channel: string;
27
+ bot: string;
28
+ /** 같은 내용으로 겹치는지 가르는 키. 비면 겹침은 미상이다. */
29
+ contentKey?: string;
30
+ }
31
+
32
+ export interface AlertClaim {
33
+ sender: AlertSenderRef;
34
+ what: string;
35
+ when: string;
36
+ channel: string;
37
+ bot: string;
38
+ contentKey?: string;
39
+ /** «전달 ok» 류의 발송 기록. 도착 증거가 아니다. */
40
+ recordedOk: boolean;
41
+ /** 발송 기록 시각. 도착이 없을 때 표의 «언제»는 이 시각이다. */
42
+ at?: string;
43
+ }
44
+
45
+ export interface AlertArrival {
46
+ sender: AlertSenderRef;
47
+ what: string;
48
+ channel: string;
49
+ bot: string;
50
+ contentKey?: string;
51
+ /** 채널이 실제로 받았다고 원장에 남은 시각. */
52
+ arrivedAt: string;
53
+ ok: boolean;
54
+ }
55
+
56
+ export interface AlertAuditInput {
57
+ registry: readonly AlertRegistrySender[];
58
+ /** 크론 레지스트리. representative 크론 발신자가 여기 없으면 누락이다. */
59
+ crons: readonly Pick<ScheduleRow, 'id' | 'name' | 'cron' | 'command' | 'enabled'>[];
60
+ /** 루프 레지스트리 id. representative 루프 발신자가 여기 없으면 누락이다. */
61
+ loops: readonly { id: string }[];
62
+ claims: readonly AlertClaim[];
63
+ arrivals: readonly AlertArrival[];
64
+ }
65
+
66
+ export interface AlertAuditRow {
67
+ who: string;
68
+ senderKind: AlertSenderKind;
69
+ senderId: string;
70
+ what: string;
71
+ when: string;
72
+ channel: string;
73
+ bot: string;
74
+ lastArrivedAt: string | null;
75
+ marks: AlertMark[];
76
+ }
77
+
78
+ export interface AlertAudit {
79
+ rows: AlertAuditRow[];
80
+ /** 레지스트리 representative 발신자 중 크론·루프 대조에서 빠진 수. 스크립트는 대조 대상이 아니다. */
81
+ missingFromRegistry: number;
82
+ missingSenders: AlertSenderRef[];
83
+ claimedWithoutArrival: AlertAuditRow[];
84
+ overlaps: AlertAuditRow[];
85
+ gaps: AlertAuditRow[];
86
+ /** contentKey 를 모르는 도착이 있어 겹침을 판정하지 못한 행. */
87
+ overlapUnknown: AlertAuditRow[];
88
+ verdict: 'pass' | 'fail';
89
+ }
90
+
91
+ export interface DeliveryArrivalView {
92
+ sender: AlertSenderRef;
93
+ what: string;
94
+ channel: string;
95
+ bot: string;
96
+ contentKey?: string;
97
+ }
98
+
99
+ const MARK_ORDER: AlertMark[] = ['claimed-without-arrival', 'overlap', 'gap', 'overlap-unknown'];
100
+
101
+ function senderKey(sender: AlertSenderRef): string {
102
+ return `${sender.kind}:${sender.id}`;
103
+ }
104
+
105
+ /** 세 표시(+미상)를 MARK_ORDER 순으로 남긴다. none 은 표시가 없을 때만. */
106
+ export function markOrder(marks: readonly AlertMark[]): AlertMark[] {
107
+ const ordered = MARK_ORDER.filter(mark => marks.includes(mark));
108
+ return ordered.length ? ordered : ['none'];
109
+ }
110
+
111
+ function knownContentKey(item: { contentKey?: string }): string | null {
112
+ const key = item.contentKey?.trim();
113
+ return key ? key : null;
114
+ }
115
+
116
+ function latestIso(values: readonly string[]): string | null {
117
+ const finite = values.filter(value => Number.isFinite(Date.parse(value)));
118
+ if (!finite.length) return null;
119
+ return finite.reduce((best, value) => Date.parse(value) > Date.parse(best) ? value : best);
120
+ }
121
+
122
+ function marksOf(row: { claimedWithoutArrival: boolean; overlap: boolean; gap: boolean; overlapUnknown: boolean }): AlertMark[] {
123
+ return markOrder([
124
+ ...(row.claimedWithoutArrival ? ['claimed-without-arrival' as const] : []),
125
+ ...(row.overlap ? ['overlap' as const] : []),
126
+ ...(row.gap ? ['gap' as const] : []),
127
+ ...(row.overlapUnknown ? ['overlap-unknown' as const] : []),
128
+ ]);
129
+ }
130
+
131
+ /** 배송 원장에서 ok 채널만 도착으로 읽는다. 실패 채널·빈 채널은 도착이 아니다. */
132
+ export function arrivalsFromDeliveries(
133
+ rows: readonly DeliveryRow[],
134
+ view: (row: DeliveryRow) => DeliveryArrivalView | null,
135
+ ): AlertArrival[] {
136
+ const arrivals: AlertArrival[] = [];
137
+ for (const row of rows) {
138
+ let channels: Array<{ type?: unknown; ok?: unknown; bot?: unknown }> = [];
139
+ try { channels = JSON.parse(row.channels) as typeof channels; } catch { channels = []; }
140
+ const mapped = view(row);
141
+ if (!mapped) continue;
142
+ const okChannels = channels.filter(channel => channel.ok === true);
143
+ if (!okChannels.length) continue;
144
+ const bots = [...new Set(okChannels.map(channel => typeof channel.bot === 'string' && channel.bot ? channel.bot : mapped.bot))];
145
+ for (const bot of bots) {
146
+ arrivals.push({
147
+ sender: mapped.sender,
148
+ what: mapped.what,
149
+ channel: mapped.channel,
150
+ bot,
151
+ ...(mapped.contentKey ? { contentKey: mapped.contentKey } : {}),
152
+ arrivedAt: row.ts,
153
+ ok: true,
154
+ });
155
+ }
156
+ }
157
+ return arrivals;
158
+ }
159
+
160
+ /** 발신자 전수를 한 장으로 접는다. 판정은 누락 0 이고, 세 표시는 행에 남는다. */
161
+ export function auditAlerts(input: AlertAuditInput): AlertAudit {
162
+ const cronIds = new Set(input.crons.map(row => row.id));
163
+ const loopIds = new Set(input.loops.map(row => row.id));
164
+ const representative = input.registry.filter(sender => sender.representative);
165
+ const missingSenders = representative.filter(sender => {
166
+ if (sender.kind === 'cron') return !cronIds.has(sender.id);
167
+ if (sender.kind === 'loop') return !loopIds.has(sender.id);
168
+ return false;
169
+ }).map(sender => ({ kind: sender.kind, id: sender.id }));
170
+
171
+ const arrivalsBySender = new Map<string, AlertArrival[]>();
172
+ for (const arrival of input.arrivals) {
173
+ if (!arrival.ok) continue;
174
+ const key = senderKey(arrival.sender);
175
+ const list = arrivalsBySender.get(key) ?? [];
176
+ list.push(arrival);
177
+ arrivalsBySender.set(key, list);
178
+ }
179
+ const claimsBySender = new Map<string, AlertClaim[]>();
180
+ for (const claim of input.claims) {
181
+ const key = senderKey(claim.sender);
182
+ const list = claimsBySender.get(key) ?? [];
183
+ list.push(claim);
184
+ claimsBySender.set(key, list);
185
+ }
186
+
187
+ const overlapKeys = new Set<string>();
188
+ const byContent = new Map<string, Set<string>>();
189
+ for (const arrival of input.arrivals) {
190
+ if (!arrival.ok) continue;
191
+ const key = knownContentKey(arrival);
192
+ if (!key) continue;
193
+ const routes = byContent.get(key) ?? new Set<string>();
194
+ routes.add(`${arrival.channel}|${arrival.bot}`);
195
+ byContent.set(key, routes);
196
+ if (routes.size >= 2) overlapKeys.add(key);
197
+ }
198
+
199
+ const rows: AlertAuditRow[] = representative.map(sender => {
200
+ const key = senderKey(sender);
201
+ const arrivals = arrivalsBySender.get(key) ?? [];
202
+ const claims = claimsBySender.get(key) ?? [];
203
+ const recordedOk = claims.some(claim => claim.recordedOk);
204
+ const claimedWithoutArrival = recordedOk && arrivals.length === 0;
205
+ const gap = !recordedOk && arrivals.length === 0;
206
+ const registryKey = knownContentKey(sender);
207
+ const arrivalKeys = arrivals.map(arrival => arrival.contentKey !== undefined ? knownContentKey(arrival) : registryKey);
208
+ const claimKeys = claims.map(claim => claim.contentKey !== undefined ? knownContentKey(claim) : registryKey);
209
+ const overlap = arrivalKeys.some(contentKey => contentKey !== null && overlapKeys.has(contentKey));
210
+ const overlapUnknown = !overlap && (
211
+ arrivals.some((_, index) => arrivalKeys[index] === null)
212
+ || (arrivals.length === 0 && claims.some((_, index) => claimKeys[index] === null && claims[index]?.recordedOk))
213
+ );
214
+ const last = latestIso(arrivals.map(arrival => arrival.arrivedAt));
215
+ const recorded = claims.find(claim => claim.recordedOk) ?? claims[0];
216
+ const channel = arrivals[0]?.channel ?? recorded?.channel ?? sender.channel;
217
+ const bot = arrivals[0]?.bot ?? recorded?.bot ?? sender.bot;
218
+ const what = arrivals[0]?.what ?? recorded?.what ?? sender.what;
219
+ const when = last ?? recorded?.at ?? recorded?.when ?? sender.when;
220
+ return {
221
+ who: `${sender.kind}:${sender.id}`,
222
+ senderKind: sender.kind,
223
+ senderId: sender.id,
224
+ what,
225
+ when,
226
+ channel,
227
+ bot,
228
+ lastArrivedAt: last,
229
+ marks: marksOf({ claimedWithoutArrival, overlap, gap, overlapUnknown }),
230
+ };
231
+ }).sort((a, b) => a.who.localeCompare(b.who) || a.what.localeCompare(b.what));
232
+
233
+ const claimedWithoutArrival = rows.filter(row => row.marks.includes('claimed-without-arrival'));
234
+ const overlaps = rows.filter(row => row.marks.includes('overlap'));
235
+ const gaps = rows.filter(row => row.marks.includes('gap'));
236
+ const overlapUnknown = rows.filter(row => row.marks.includes('overlap-unknown'));
237
+ return {
238
+ rows,
239
+ missingFromRegistry: missingSenders.length,
240
+ missingSenders,
241
+ claimedWithoutArrival,
242
+ overlaps,
243
+ gaps,
244
+ overlapUnknown,
245
+ verdict: missingSenders.length === 0 ? 'pass' : 'fail',
246
+ };
247
+ }
248
+
249
+ /** 한 장의 표. 세 표시가 비면 «없음» 과 그 근거를 표 아래에 적는다. */
250
+ export function formatAlertAudit(audit: AlertAudit): string {
251
+ const header = '| 누가 | 무엇을 | 언제 | 채널 | 봇 | 마지막 도착 | 표시 |';
252
+ const rule = '| --- | --- | --- | --- | --- | --- | --- |';
253
+ const body = audit.rows.map(row => {
254
+ const marks = markOrder(row.marks).filter(mark => mark !== 'none').join(', ') || 'none';
255
+ const arrived = row.lastArrivedAt ?? '없음';
256
+ return `| ${row.who} | ${row.what} | ${row.when} | ${row.channel} | ${row.bot} | ${arrived} | ${marks} |`;
257
+ });
258
+ const lines = [header, rule, ...body, '', `대조 누락: ${audit.missingFromRegistry}`, `판정: ${audit.verdict}`];
259
+ const note = (label: string, found: readonly AlertAuditRow[], emptyReason: string) => {
260
+ if (found.length) lines.push(`${label}: ${found.map(row => row.who).join(', ')}`);
261
+ else lines.push(`${label}: 없음 — ${emptyReason}`);
262
+ };
263
+ note('보낸다고 기록됐는데 도착 증거가 없는 것', audit.claimedWithoutArrival, 'recordedOk 인 발신자마다 도착이 있다');
264
+ if (audit.overlapUnknown.length) {
265
+ lines.push(`겹치는 것: 미상 — contentKey 를 모르는 도착이 있어 겹침을 판정하지 않음 (${audit.overlapUnknown.map(row => row.who).join(', ')})`);
266
+ } else {
267
+ note('겹치는 것', audit.overlaps, '같은 contentKey 가 둘 이상의 채널·봇에 도착한 적이 없다');
268
+ }
269
+ note('아무도 안 보내는 것', audit.gaps, 'representative 발신자는 모두 발송 기록이 있거나 도착이 있다');
270
+ return `${lines.join('\n')}\n`;
271
+ }
@@ -30,7 +30,7 @@ import { LOGS_QUERY_SPEC, dispatchLogsQuery } from './logs-tool.js';
30
30
  import { MISSION_DECIDE_SPEC, dispatchMissionDecide } from '../autopilot/mission-decide-tool.js';
31
31
  import { COO_ADMIN_SPEC, dispatchCooAdmin } from './coo-admin-tool.js';
32
32
  import { RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, dispatchReleaseStatus, dispatchReleaseChange } from './release-tool.js';
33
- import { OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, dispatchOpsSeats, dispatchDecisionsPending } from './ops-facts-tool.js';
33
+ import { OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, PROACT_METER_SPEC, dispatchOpsSeats, dispatchDecisionsPending, dispatchProactMeter } from './ops-facts-tool.js';
34
34
 
35
35
  /** memory_recall — 크로스서피스 기억(자기 발송 원장 회상). 도메인 무관(domain 필터는 옵션).
36
36
  * finance-tools 에서 L2 로 이관(2026-07-08). */
@@ -142,7 +142,7 @@ const SELF_RECALL_BUDGET_SPEC: LLMToolSpec = {
142
142
  },
143
143
  },
144
144
  };
145
- export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, CONTEXT_NOW_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC];
145
+ export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, CONTEXT_NOW_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, PROACT_METER_SPEC];
146
146
 
147
147
  export interface CoreTools {
148
148
  specs: LLMToolSpec[];
@@ -159,10 +159,11 @@ export function buildCoreTools(recallSearch?: RecallSearch): CoreTools {
159
159
  names,
160
160
  dispatch: async (name, args, onPartial, context) => {
161
161
  const h = CORE_TOOL_HANDLERS[name];
162
- if (!h && name !== 'release_change' && name !== 'ops_seats' && name !== 'decisions_pending') return { error: `unknown core tool: ${name}` };
162
+ if (!h && name !== 'release_change' && name !== 'ops_seats' && name !== 'decisions_pending' && name !== 'proact_meter') return { error: `unknown core tool: ${name}` };
163
163
  if (name === 'release_change') return dispatchReleaseChange(args, context);
164
164
  if (name === 'ops_seats') return dispatchOpsSeats(args, context);
165
165
  if (name === 'decisions_pending') return dispatchDecisionsPending(context);
166
+ if (name === 'proact_meter') return dispatchProactMeter(args, context);
166
167
  if (name === 'memory_recall' || name === 'self_recall') {
167
168
  return runRecall(name, args, context ?? { surface: 'skill' }, recallSearch, onPartial);
168
169
  }
@@ -2,6 +2,7 @@ import type { LLMToolSpec } from '../llm.js';
2
2
  import type { ToolRuntimeContext } from '../tool-runtime/types.js';
3
3
  import { debug } from '../debug/log.js';
4
4
  import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
5
+ import { readProactMeter, type ProactMeter } from '../decisions/proact-meter.js';
5
6
  import { buildSeatsBoard, todayKst, type SeatsSources } from '../nexus/api/ops-seats.js';
6
7
  import { liveSeatsSources } from '../nexus/api/ops-seats-sources.js';
7
8
 
@@ -11,6 +12,12 @@ export const OPS_SEATS_SPEC: LLMToolSpec = {
11
12
  parameters: { type: 'object', properties: { date: { type: 'string', description: '조회할 KST 날짜 YYYY-MM-DD. 생략하면 KST 오늘.' } }, required: [] },
12
13
  };
13
14
 
15
+ export const PROACT_METER_SPEC: LLMToolSpec = {
16
+ name: 'proact_meter',
17
+ description: '오너 전용 읽기 — 선제성 기준선. 지난 7 KST일마다 대표가 시키지 않고 시스템이 먼저 낸 수(offered), 그중 채택된 수(adopted), 대표가 먼저 물어서야 드러난 수(asked). 표본은 결정·지시·조율·루프 원문 링크를 가진다. 못 읽은 원천은 sources.read=false 이며 0이 아니다.',
18
+ parameters: { type: 'object', properties: { days: { type: 'number', description: 'KST 일수. 생략하면 7.' } }, required: [] },
19
+ };
20
+
14
21
  export const DECISIONS_PENDING_SPEC: LLMToolSpec = {
15
22
  name: 'decisions_pending',
16
23
  description: '오너 전용 읽기 — 결정 원장의 열린 결정을 기한 순으로 조회한다. 결정 선택·기록은 하지 않는다.',
@@ -21,13 +28,14 @@ export interface OpsFactsDeps {
21
28
  seatsSources?: () => SeatsSources;
22
29
  listDecisions?: (filters: { status: 'open' }) => DecisionEntry[];
23
30
  today?: () => string;
31
+ readProact?: (days: number) => ProactMeter;
24
32
  }
25
33
 
26
34
  function ownerVerified(context?: ToolRuntimeContext): boolean {
27
35
  return context?.requestOrigin !== 'external-agent' && !!context?.sessionId && !!context?.verifiedOwner?.id.trim();
28
36
  }
29
37
 
30
- function logCall(tool: 'ops_seats' | 'decisions_pending', ok: boolean, nulls: number): void {
38
+ function logCall(tool: 'ops_seats' | 'decisions_pending' | 'proact_meter', ok: boolean, nulls: number): void {
31
39
  debug.log('ops.facts', 'call', { tool, ok, nulls });
32
40
  }
33
41
 
@@ -56,6 +64,28 @@ export async function dispatchOpsSeats(args: Record<string, unknown>, context?:
56
64
  }
57
65
  }
58
66
 
67
+ /** Seven KST days. An unreadable ledger stays sources.read=false; it is not a zero day. */
68
+ export function dispatchProactMeter(args: Record<string, unknown>, context?: ToolRuntimeContext, deps: OpsFactsDeps = {}) {
69
+ if (!ownerVerified(context)) {
70
+ logCall('proact_meter', false, 0);
71
+ return { error: '오너 확인이 필요한 조회다' };
72
+ }
73
+ const days = args.days === undefined ? 7 : args.days;
74
+ if (typeof days !== 'number' || !Number.isInteger(days) || days < 1 || days > 31) {
75
+ logCall('proact_meter', false, 0);
76
+ return { error: 'days 는 1 이상 31 이하의 정수여야 한다' };
77
+ }
78
+ try {
79
+ const meter = (deps.readProact ?? ((n: number) => readProactMeter({ days: n })))(days);
80
+ const unread = Object.values(meter.sources).filter(source => !source.read).length;
81
+ logCall('proact_meter', true, unread);
82
+ return meter;
83
+ } catch {
84
+ logCall('proact_meter', false, 1);
85
+ return { error: '선제성 원장을 읽지 못했다', days: null };
86
+ }
87
+ }
88
+
59
89
  /** Only the five requested fields leave the decision ledger. Undated entries follow dated entries. */
60
90
  export function dispatchDecisionsPending(context?: ToolRuntimeContext, deps: OpsFactsDeps = {}) {
61
91
  if (!ownerVerified(context)) {
@@ -0,0 +1,24 @@
1
+ import type { TaskCard } from '../task-cards/card-store.js';
2
+
3
+ export interface SplitCell { id: string; title: string; seat?: string; host?: string }
4
+
5
+ /** FLOW1: only explicitly assigned work may enter a seat's queue. The same proposal is used in shadow and live. */
6
+ export function splitCard(card: TaskCard, _opts: { shadow?: boolean } = {}): SplitCell[] {
7
+ const intake = [...card.sections].reverse().find(section => section.key.startsWith('intake:wish:'));
8
+ let text = card.title;
9
+ if (intake) {
10
+ let body: unknown;
11
+ try { body = JSON.parse(intake.content); } catch { body = intake.content; }
12
+ if (typeof body === 'string') text = body;
13
+ else if (body && typeof body === 'object' && typeof (body as { text?: unknown }).text === 'string') text = (body as { text: string }).text;
14
+ }
15
+ const lines = text.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
16
+ const cells: SplitCell[] = lines.map((line, index) => {
17
+ const match = /^(?:[-*]|\d+[.)])?\s*\[(OP|MK|TC|UX)\]\s*(.+)$/u.exec(line);
18
+ if (match) return { id: `${card.id}-${index + 1}`, title: match[2]!.trim(), seat: match[1]! };
19
+ // 담당 없는 행도 칸으로 남긴다 — seat 가 없으니 대기열엔 못 들어가고 tick 이 unplaced-no-owner 로 센다.
20
+ return { id: `${card.id}-${index + 1}`, title: line.replace(/^(?:[-*]|\d+[.)])\s*/u, '') };
21
+ });
22
+ // 의뢰 본문이 있으면 담당이 하나도 없어도 행마다 칸으로 남긴다. 본문 없이 제목뿐일 때만 카드 한 칸.
23
+ return intake || cells.some(cell => cell.seat) ? cells : [{ id: card.id, title: card.title }];
24
+ }