elanous 0.2.15 → 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 (254) 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-33ffcc49ab45123b.js → 1307-b46d45b705e8c9ae.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-e05a77df7e005b04.js → page-3cb52e5ea8fc2fa5.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-5491c115b1f9a923.js → page-c1e20d7d338713f1.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/bots/{page-b8c49f6b4fa2df91.js → page-482c5dbde699947c.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/chat/{page-976813c91d7394ea.js → page-8c2d9526164f5d09.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/control/page-a0e2a6d1bfb52fc5.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-72d3e7ff35a8c954.js → page-c53598cfce6156f2.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/app/editor/{page-60c7a8e1a4fe62ce.js → page-13a63953f53c6a23.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/app/exec/{page-2a52b316cb143d9c.js → page-5d3c8429d04f3831.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/app/field/{page-c3ce483c8d5db370.js → page-3193bdc0bbab047a.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/app/intake/{page-96f074f6538e8a2c.js → page-6107ca8ddd5e3bdd.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/{layout-e97be5334d83a9f5.js → layout-dce686e582c23937.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/loops/page-1a61c7ca1ceb3ae7.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/app/market/{page-955ec2da3be4c560.js → page-51f811fa119308b6.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-9f88fbc15d9058d1.js → page-dacff46049e1557e.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/app/morning/{page-c1343f9499d86f3f.js → page-4167348049166852.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/{not-found-8739a2e0ae793130.js → not-found-de52e8fdf9095f7a.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/observatory/{page-c9fc5706b8abaa11.js → page-36a7d994f4ca0299.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-d23b12bb92242558.js → page-ca3952cd78a83ca2.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-5b042736aa113756.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-c7fea475488201a8.js → page-acb749f1e432bcf3.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-5efbf81124c474ae.js → page-1ebfe049c640e171.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-85f1a1fc0a82738c.js → page-57924393e3e4e539.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-06ee266953d9c879.js → page-31fa1ce4c4aff9ae.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/settings/{page-153c2b3bcc89e8de.js → page-0f5cd57ca928f42b.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/app/share/{page-0d6d23b40263a7ea.js → page-d28573d6792b4275.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/showroom/page-59f03fda76c03d22.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d751de5f8062ff5e.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/term/{page-f5597e894c02cd33.js → page-fe1759f94003b32b.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/today/{page-0337fdf7fb5e37b6.js → page-d64c984e855c8c68.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/vault/{page-8fcec4c9ea2e7f70.js → page-9afdee66b820c76e.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-f7e631e9b4daf66e.js → page-d4aef67abf264874.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/workflows/page-1ea64acadd704ace.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-9817c7f9007466cc.js → page-3544f154c87b43ee.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +1 -0
  51. package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
  52. package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
  53. package/apps/pwa/out/approvals/index.html +1 -1
  54. package/apps/pwa/out/approvals/index.txt +15 -15
  55. package/apps/pwa/out/autopilot/index.html +1 -1
  56. package/apps/pwa/out/autopilot/index.txt +15 -15
  57. package/apps/pwa/out/board/index.html +1 -1
  58. package/apps/pwa/out/board/index.txt +15 -15
  59. package/apps/pwa/out/botlab/index.html +1 -1
  60. package/apps/pwa/out/botlab/index.txt +15 -15
  61. package/apps/pwa/out/bots/index.html +1 -1
  62. package/apps/pwa/out/bots/index.txt +15 -15
  63. package/apps/pwa/out/ceo/index.html +1 -1
  64. package/apps/pwa/out/ceo/index.txt +15 -15
  65. package/apps/pwa/out/chat/index.html +1 -1
  66. package/apps/pwa/out/chat/index.txt +15 -15
  67. package/apps/pwa/out/consult/index.html +1 -1
  68. package/apps/pwa/out/consult/index.txt +15 -15
  69. package/apps/pwa/out/control/index.html +1 -1
  70. package/apps/pwa/out/control/index.txt +15 -15
  71. package/apps/pwa/out/design-check/index.html +1 -1
  72. package/apps/pwa/out/design-check/index.txt +15 -15
  73. package/apps/pwa/out/editor/index.html +1 -1
  74. package/apps/pwa/out/editor/index.txt +15 -15
  75. package/apps/pwa/out/exec/index.html +1 -1
  76. package/apps/pwa/out/exec/index.txt +15 -15
  77. package/apps/pwa/out/field/index.html +1 -1
  78. package/apps/pwa/out/field/index.txt +15 -15
  79. package/apps/pwa/out/healthz +1 -1
  80. package/apps/pwa/out/index.html +1 -1
  81. package/apps/pwa/out/index.txt +15 -15
  82. package/apps/pwa/out/inside/index.html +1 -1
  83. package/apps/pwa/out/inside/index.txt +15 -15
  84. package/apps/pwa/out/intake/index.html +1 -1
  85. package/apps/pwa/out/intake/index.txt +15 -15
  86. package/apps/pwa/out/live/index.html +1 -1
  87. package/apps/pwa/out/live/index.txt +15 -15
  88. package/apps/pwa/out/loops/index.html +1 -1
  89. package/apps/pwa/out/loops/index.txt +15 -15
  90. package/apps/pwa/out/market/index.html +1 -1
  91. package/apps/pwa/out/market/index.txt +15 -15
  92. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  93. package/apps/pwa/out/missions/placeholder/index.txt +15 -15
  94. package/apps/pwa/out/morning/index.html +1 -1
  95. package/apps/pwa/out/morning/index.txt +15 -15
  96. package/apps/pwa/out/observatory/index.html +1 -1
  97. package/apps/pwa/out/observatory/index.txt +15 -15
  98. package/apps/pwa/out/ops/checklist/index.html +1 -1
  99. package/apps/pwa/out/ops/checklist/index.txt +15 -15
  100. package/apps/pwa/out/ops/release/index.html +1 -1
  101. package/apps/pwa/out/ops/release/index.txt +15 -15
  102. package/apps/pwa/out/ops/seats/index.html +1 -1
  103. package/apps/pwa/out/ops/seats/index.txt +15 -15
  104. package/apps/pwa/out/outputs/index.html +1 -1
  105. package/apps/pwa/out/outputs/index.txt +15 -15
  106. package/apps/pwa/out/reflection/index.html +1 -1
  107. package/apps/pwa/out/reflection/index.txt +16 -16
  108. package/apps/pwa/out/scheduler/index.html +1 -1
  109. package/apps/pwa/out/scheduler/index.txt +15 -15
  110. package/apps/pwa/out/sessions/index.html +1 -1
  111. package/apps/pwa/out/sessions/index.txt +15 -15
  112. package/apps/pwa/out/settings/devices/index.html +1 -1
  113. package/apps/pwa/out/settings/devices/index.txt +15 -15
  114. package/apps/pwa/out/settings/index.html +1 -1
  115. package/apps/pwa/out/settings/index.txt +15 -15
  116. package/apps/pwa/out/setup/done/index.html +1 -1
  117. package/apps/pwa/out/setup/done/index.txt +15 -15
  118. package/apps/pwa/out/setup/index.html +1 -1
  119. package/apps/pwa/out/setup/index.txt +15 -15
  120. package/apps/pwa/out/share/index.html +1 -1
  121. package/apps/pwa/out/share/index.txt +15 -15
  122. package/apps/pwa/out/showroom/index.html +1 -1
  123. package/apps/pwa/out/showroom/index.txt +15 -15
  124. package/apps/pwa/out/tasks/index.html +1 -1
  125. package/apps/pwa/out/tasks/index.txt +15 -15
  126. package/apps/pwa/out/term/index.html +1 -1
  127. package/apps/pwa/out/term/index.txt +15 -15
  128. package/apps/pwa/out/today/index.html +1 -1
  129. package/apps/pwa/out/today/index.txt +15 -15
  130. package/apps/pwa/out/trace/index.html +1 -1
  131. package/apps/pwa/out/trace/index.txt +15 -15
  132. package/apps/pwa/out/vault/index.html +1 -1
  133. package/apps/pwa/out/vault/index.txt +15 -15
  134. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  135. package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
  136. package/apps/pwa/out/workflows/index.html +1 -1
  137. package/apps/pwa/out/workflows/index.txt +15 -15
  138. package/apps/pwa/out/workspace/index.html +1 -1
  139. package/apps/pwa/out/workspace/index.txt +15 -15
  140. package/apps/pwa/out/worktrees/index.html +1 -1
  141. package/apps/pwa/out/worktrees/index.txt +15 -15
  142. package/graphs/doc-gen/doc-gen.yaml +24 -0
  143. package/graphs/doc-gen/recipes.yaml +3 -0
  144. package/graphs/orchestrator/orchestrator.yaml +7 -0
  145. package/graphs/orchestrator/recipes.yaml +3 -0
  146. package/graphs/steward/recipes.yaml +2 -1
  147. package/package.json +1 -1
  148. package/scripts/ci-public-export-import-gate.ts +154 -0
  149. package/scripts/ci-test-interference-gate.ts +5 -1
  150. package/scripts/ci-typecheck-changed.ts +34 -15
  151. package/scripts/docs/doc-rot.ts +164 -0
  152. package/scripts/docs/generate-graph-anatomy.ts +131 -9
  153. package/scripts/docs-cli-check.ts +4 -4
  154. package/scripts/goals/goal-census.ts +343 -0
  155. package/scripts/measure/author-section-contribution.ts +84 -0
  156. package/scripts/release-loop/cut-branch.ts +43 -13
  157. package/scripts/release-loop/gate-node.ts +3 -1
  158. package/scripts/release-loop/vault-note-node.ts +19 -2
  159. package/scripts/rhythm/monthly.ts +97 -0
  160. package/src/acp/client.ts +17 -2
  161. package/src/agent-mission/driver.ts +51 -1
  162. package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
  163. package/src/agent-mission/supervisor-metrics.ts +218 -0
  164. package/src/autopilot/track-agent-shadow.ts +2 -1
  165. package/src/briefing/brief-items.ts +191 -36
  166. package/src/budget/usage-outcomes.ts +204 -0
  167. package/src/cli/brief-cli.ts +73 -0
  168. package/src/cli/know-cli.ts +20 -0
  169. package/src/cli/logs-cli.ts +43 -1
  170. package/src/cli/pr-cli.ts +80 -12
  171. package/src/cli/release-cli.ts +11 -8
  172. package/src/cli/self-update.ts +200 -1
  173. package/src/cli/usage-cli.ts +26 -0
  174. package/src/discord-self-message.ts +9 -0
  175. package/src/flow-loop/split.ts +24 -0
  176. package/src/harness/harness-cli-command.ts +36 -2
  177. package/src/harness/harness-pod-dispatch.ts +25 -55
  178. package/src/harness/harness-seams.ts +13 -3
  179. package/src/harness/helper-repair.ts +139 -8
  180. package/src/harness/helper-scan.ts +13 -2
  181. package/src/harness/nested-elanous-policy.ts +114 -0
  182. package/src/hq/hq.ts +8 -2
  183. package/src/hq/lease.ts +5 -3
  184. package/src/hq/transfer.ts +185 -0
  185. package/src/index.ts +112 -6
  186. package/src/intake-plane/digest.ts +48 -26
  187. package/src/knowledge/know-find.ts +105 -0
  188. package/src/llm.ts +75 -19
  189. package/src/loops/orchestrator/finish-rate.ts +14 -3
  190. package/src/loops/orchestrator/tick.ts +96 -25
  191. package/src/maturity/bot-slash-catalog.ts +11 -2
  192. package/src/maturity/feature-maturity.ts +2 -2
  193. package/src/maturity/tui-slash-availability.ts +9 -0
  194. package/src/oauth/overload-failover-launch.ts +75 -0
  195. package/src/oauth/overload-failover.ts +137 -0
  196. package/src/release-loop/checklist.ts +10 -5
  197. package/src/release-loop/feature-store.ts +10 -9
  198. package/src/release-loop/placement.ts +17 -8
  199. package/src/seat-dispatch/seat-ask.ts +33 -6
  200. package/src/seat-loop/seat-loop.ts +90 -3
  201. package/src/self-dev/ask-launch-flow.ts +22 -1
  202. package/src/self-dev/authored-goal-trace.ts +64 -0
  203. package/src/self-dev/dev-cli.ts +19 -1
  204. package/src/self-dev/dev-pipeline.ts +6 -5
  205. package/src/self-dev/entrance-registry.ts +10 -6
  206. package/src/self-dev/old-door.ts +73 -0
  207. package/src/self-implement/app-api-budget.ts +166 -0
  208. package/src/self-implement/budget-gate.ts +7 -5
  209. package/src/self-implement/follow-up-goals.ts +289 -0
  210. package/src/self-implement/goal-author.ts +19 -13
  211. package/src/self-implement/heal-triage.ts +44 -0
  212. package/src/self-implement/orchestrator.ts +138 -7
  213. package/src/self-implement/run-ledger.ts +4 -0
  214. package/src/self-implement/seams.ts +44 -15
  215. package/src/session-runtime/index.ts +2 -0
  216. package/src/session-runtime/retry-api.ts +87 -0
  217. package/src/session-runtime/retry-policy.ts +107 -6
  218. package/src/steward/working-backwards.ts +16 -1
  219. package/src/task-cards/card-cli.ts +25 -0
  220. package/src/task-cards/card-store.ts +5 -3
  221. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
  222. package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
  223. package/src/task-orchestrator/surfaces/pod-pool.ts +14 -5
  224. package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
  225. package/src/task-orchestrator/surfaces/self-implement-pod.ts +19 -10
  226. package/src/task-orchestrator/surfaces/self-implement.ts +2 -0
  227. package/src/telegram-commands.ts +5 -0
  228. package/src/telegram.ts +47 -0
  229. package/src/user-config.ts +36 -3
  230. package/src/version/packed-revision.json +1 -1
  231. package/apps/pwa/out/_next/static/chunks/app/approvals/page-0e2c57fe6add4b2a.js +0 -1
  232. package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +0 -1
  233. package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +0 -1
  234. package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +0 -1
  235. package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +0 -1
  236. package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +0 -1
  237. package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +0 -1
  238. package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +0 -1
  239. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +0 -1
  240. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +0 -1
  241. package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +0 -1
  242. package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +0 -1
  243. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +0 -1
  244. package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +0 -1
  245. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +0 -1
  246. package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +0 -1
  247. package/apps/pwa/out/_next/static/chunks/app/trace/page-73735b64bfe1ae67.js +0 -1
  248. package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +0 -1
  249. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +0 -1
  250. package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +0 -1
  251. package/src/launch-head/seat-goal-queue-lock.fixture.ts +0 -15
  252. package/src/launch-head/seat-goal-queue.ts +0 -198
  253. /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → rv9cierV0jSOvDBm1g0XB}/_buildManifest.js +0 -0
  254. /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
@@ -1,198 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { mkdirSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { Database } from 'bun:sqlite';
5
- import { effectiveInstanceRoot } from '../instance/resolve.js';
6
- import type { QueueSeat } from '../harness/harness-queue.js';
7
-
8
- export type SeatGoalStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'expired';
9
- export type SeatGoal = {
10
- id: string;
11
- seat: QueueSeat;
12
- goal: string;
13
- prerequisites: string[];
14
- notBefore?: string;
15
- endAt?: string;
16
- queuedAt: string;
17
- status: SeatGoalStatus;
18
- startedAt?: string;
19
- finishedAt?: string;
20
- };
21
- export type SeatGoalInput = {
22
- id?: string;
23
- seat: QueueSeat;
24
- goal: string;
25
- prerequisites?: string[];
26
- notBefore?: string;
27
- endAt?: string;
28
- };
29
- export type SeatGoalQueueOptions = { root?: string; now?: () => Date };
30
- export type SeatGoalScheduleOptions = SeatGoalQueueOptions & {
31
- maxConcurrent: number;
32
- seatCaps: Partial<Record<QueueSeat, number>>;
33
- launch: (goal: SeatGoal) => Promise<void> | void;
34
- };
35
- export type SeatGoalScheduleResult = { outcome: 'started' | 'waiting' | 'empty'; goal?: SeatGoal; reason: string };
36
-
37
- const SEATS = new Set(['OP', 'TC', 'MK', 'UX']);
38
- const TERMINAL = new Set<SeatGoalStatus>(['succeeded', 'failed', 'expired']);
39
-
40
- export function seatGoalQueuePath(root = effectiveInstanceRoot()): string {
41
- return join(root, 'launch-head', 'seat-goals.sqlite');
42
- }
43
-
44
- function openQueue(root: string): Database {
45
- const path = seatGoalQueuePath(root);
46
- mkdirSync(join(root, 'launch-head'), { recursive: true, mode: 0o700 });
47
- const db = new Database(path, { create: true, strict: true });
48
- db.exec('PRAGMA busy_timeout = 30000');
49
- db.exec(`CREATE TABLE IF NOT EXISTS seat_goals (
50
- id TEXT PRIMARY KEY, seat TEXT NOT NULL, goal TEXT NOT NULL, prerequisites TEXT NOT NULL,
51
- not_before TEXT, end_at TEXT, queued_at TEXT NOT NULL, status TEXT NOT NULL,
52
- started_at TEXT, finished_at TEXT
53
- )`);
54
- return db;
55
- }
56
-
57
- type GoalRow = {
58
- id: string; seat: QueueSeat; goal: string; prerequisites: string;
59
- not_before: string | null; end_at: string | null; queued_at: string;
60
- status: SeatGoalStatus; started_at: string | null; finished_at: string | null;
61
- };
62
-
63
- function toGoal(row: GoalRow): SeatGoal {
64
- return {
65
- id: row.id, seat: row.seat, goal: row.goal, prerequisites: JSON.parse(row.prerequisites) as string[],
66
- queuedAt: row.queued_at, status: row.status,
67
- ...(row.not_before ? { notBefore: row.not_before } : {}),
68
- ...(row.end_at ? { endAt: row.end_at } : {}),
69
- ...(row.started_at ? { startedAt: row.started_at } : {}),
70
- ...(row.finished_at ? { finishedAt: row.finished_at } : {}),
71
- };
72
- }
73
-
74
- function rows(db: Database): SeatGoal[] {
75
- return (db.query('SELECT * FROM seat_goals ORDER BY queued_at, rowid').all() as GoalRow[]).map(toGoal);
76
- }
77
-
78
- function instant(value: string, field: string): number {
79
- const time = Date.parse(value);
80
- if (!Number.isFinite(time) || new Date(time).toISOString() !== value) throw new Error(`seat goal queue: invalid ${field}`);
81
- return time;
82
- }
83
-
84
- function clock(now?: () => Date): Date {
85
- const date = now?.() ?? new Date();
86
- if (!Number.isFinite(date.getTime())) throw new Error('seat goal queue: invalid clock');
87
- return date;
88
- }
89
-
90
- function cap(value: number, field: string): number {
91
- if (!Number.isSafeInteger(value) || value < 0) throw new Error(`seat goal queue: invalid ${field}`);
92
- return value;
93
- }
94
-
95
- /** The launcher owns this store; producers enqueue only, never start a goal. */
96
- export function enqueueSeatGoal(input: SeatGoalInput, options: SeatGoalQueueOptions = {}): SeatGoal {
97
- if (!SEATS.has(input.seat)) throw new Error('seat goal queue: invalid seat');
98
- if (!input.goal?.trim()) throw new Error('seat goal queue: empty goal');
99
- const id = input.id ?? `sg-${randomUUID()}`;
100
- if (!/^[a-zA-Z0-9][a-zA-Z0-9._:-]*$/.test(id)) throw new Error('seat goal queue: invalid id');
101
- const prerequisites = input.prerequisites ?? [];
102
- if (!Array.isArray(prerequisites) || prerequisites.some((key) => typeof key !== 'string' || !key.trim())
103
- || new Set(prerequisites).size !== prerequisites.length || prerequisites.includes(id)) {
104
- throw new Error('seat goal queue: invalid prerequisites');
105
- }
106
- const start = input.notBefore === undefined ? undefined : instant(input.notBefore, 'notBefore');
107
- const end = input.endAt === undefined ? undefined : instant(input.endAt, 'endAt');
108
- if (start !== undefined && end !== undefined && end <= start) throw new Error('seat goal queue: endAt must follow notBefore');
109
- const queuedAt = clock(options.now).toISOString();
110
- if (end !== undefined && end <= Date.parse(queuedAt)) throw new Error('seat goal queue: endAt has passed');
111
- const db = openQueue(options.root ?? effectiveInstanceRoot());
112
- try {
113
- db.exec('BEGIN IMMEDIATE');
114
- try {
115
- const all = rows(db);
116
- if (all.some((row) => row.id === id)) throw new Error(`seat goal queue: duplicate id ${id}`);
117
- for (const key of prerequisites) {
118
- if (!all.some((row) => row.id === key)) throw new Error(`seat goal queue: missing prerequisite ${key}`);
119
- }
120
- db.query(`INSERT INTO seat_goals (id, seat, goal, prerequisites, not_before, end_at, queued_at, status)
121
- VALUES (?, ?, ?, ?, ?, ?, ?, 'queued')`).run(id, input.seat, input.goal, JSON.stringify(prerequisites),
122
- input.notBefore ?? null, input.endAt ?? null, queuedAt);
123
- db.exec('COMMIT');
124
- return { id, seat: input.seat, goal: input.goal, prerequisites: [...prerequisites], queuedAt, status: 'queued',
125
- ...(input.notBefore ? { notBefore: input.notBefore } : {}), ...(input.endAt ? { endAt: input.endAt } : {}) };
126
- } catch (error) { db.exec('ROLLBACK'); throw error; }
127
- } finally { db.close(); }
128
- }
129
-
130
- export function listSeatGoals(options: SeatGoalQueueOptions = {}): SeatGoal[] {
131
- const db = openQueue(options.root ?? effectiveInstanceRoot());
132
- try { return rows(db); } finally { db.close(); }
133
- }
134
-
135
- /** Completion is an explicit launcher receipt: an uncertain or crashed launch keeps its slot reserved. */
136
- export function finishSeatGoal(id: string, status: 'succeeded' | 'failed', options: SeatGoalQueueOptions = {}): SeatGoal {
137
- const db = openQueue(options.root ?? effectiveInstanceRoot());
138
- try {
139
- const finishedAt = clock(options.now).toISOString();
140
- const result = db.query("UPDATE seat_goals SET status = ?, finished_at = ? WHERE id = ? AND status = 'running'")
141
- .run(status, finishedAt, id);
142
- if (result.changes !== 1) throw new Error(`seat goal queue: goal is not running: ${id}`);
143
- return toGoal(db.query('SELECT * FROM seat_goals WHERE id = ?').get(id) as GoalRow);
144
- } finally { db.close(); }
145
- }
146
-
147
- /** One tick claims at most one eligible goal atomically across launcher processes. */
148
- export async function scheduleSeatGoals(options: SeatGoalScheduleOptions): Promise<SeatGoalScheduleResult> {
149
- const totalLimit = cap(options.maxConcurrent, 'maxConcurrent');
150
- for (const [seat, value] of Object.entries(options.seatCaps)) {
151
- if (!SEATS.has(seat) || value === undefined) throw new Error('seat goal queue: invalid seatCaps');
152
- cap(value, `seatCaps.${seat}`);
153
- }
154
- const db = openQueue(options.root ?? effectiveInstanceRoot());
155
- let selected: SeatGoal | undefined;
156
- let reason = 'empty';
157
- try {
158
- db.exec('BEGIN IMMEDIATE');
159
- try {
160
- const at = clock(options.now);
161
- const all = rows(db);
162
- const byId = new Map(all.map((goal) => [goal.id, goal]));
163
- for (const goal of all) {
164
- if (goal.status !== 'queued') continue;
165
- const failed = goal.prerequisites.some((id) => {
166
- const prerequisite = byId.get(id);
167
- return !prerequisite || (TERMINAL.has(prerequisite.status) && prerequisite.status !== 'succeeded');
168
- });
169
- if (failed || (goal.endAt && Date.parse(goal.endAt) <= at.getTime())) {
170
- db.query("UPDATE seat_goals SET status = 'expired', finished_at = ? WHERE id = ? AND status = 'queued'")
171
- .run(at.toISOString(), goal.id);
172
- goal.status = 'expired';
173
- goal.finishedAt = at.toISOString();
174
- }
175
- }
176
- const running = all.filter((goal) => goal.status === 'running');
177
- for (const goal of all) {
178
- if (goal.status !== 'queued') continue;
179
- if (goal.notBefore && Date.parse(goal.notBefore) > at.getTime()) { reason = 'not-before'; continue; }
180
- if (goal.prerequisites.some((id) => byId.get(id)?.status !== 'succeeded')) { reason = 'prerequisites'; continue; }
181
- if (running.length >= totalLimit) { reason = 'global-cap'; continue; }
182
- if (running.filter((row) => row.seat === goal.seat).length >= (options.seatCaps[goal.seat] ?? totalLimit)) {
183
- reason = 'seat-cap'; continue;
184
- }
185
- const startedAt = at.toISOString();
186
- selected = { ...goal, status: 'running', startedAt };
187
- db.query("UPDATE seat_goals SET status = 'running', started_at = ? WHERE id = ? AND status = 'queued'")
188
- .run(startedAt, goal.id);
189
- break;
190
- }
191
- db.exec('COMMIT');
192
- if (!selected) return { outcome: all.some((goal) => goal.status === 'queued') ? 'waiting' : 'empty', reason };
193
- } catch (error) { db.exec('ROLLBACK'); throw error; }
194
- } finally { db.close(); }
195
- // Keep the claim on an uncertain launch. Only a positive completion receipt can release it.
196
- await options.launch(selected);
197
- return { outcome: 'started', goal: selected, reason: 'launched' };
198
- }