@alecsibilia/luca 14.0.0-alpha.2 → 14.0.0-alpha.21

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 (321) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +7 -2
  3. package/board/client/board-look.ts +31 -1
  4. package/board/client/board-panel-parts.tsx +150 -0
  5. package/board/client/board-panel-stages.ts +99 -0
  6. package/board/client/board-panel-styles.ts +223 -0
  7. package/board/client/board-panel.tsx +77 -1275
  8. package/board/client/engine-life.ts +48 -0
  9. package/board/client/final-review-stack.tsx +144 -0
  10. package/board/client/heartbeat.tsx +61 -0
  11. package/board/client/helper-lines.tsx +69 -0
  12. package/board/client/live-feed.tsx +86 -0
  13. package/board/client/live-line.ts +73 -0
  14. package/board/client/model-line.tsx +178 -0
  15. package/board/client/needs-you-section.tsx +133 -0
  16. package/board/client/one-line.tsx +193 -0
  17. package/board/client/repo-wait-banner.tsx +69 -0
  18. package/board/client/run-footer.tsx +68 -0
  19. package/board/client/run-header.tsx +189 -0
  20. package/board/client/run-picker.tsx +98 -0
  21. package/board/client/status-bar.tsx +131 -0
  22. package/board/client/step-dots.tsx +127 -0
  23. package/board/client/stuck-actions.tsx +269 -0
  24. package/board/client/ticket-card-view.tsx +238 -0
  25. package/board/client/ticket-detail.tsx +87 -0
  26. package/board/client/ticket-stack.tsx +119 -0
  27. package/board/client/ticket-wait.ts +188 -0
  28. package/board/client/timeline-rows.tsx +41 -14
  29. package/board/client/usage-line.tsx +51 -0
  30. package/board/client/wait-line.tsx +39 -0
  31. package/board/index.client.tsx +1 -1
  32. package/board/index.server.ts +7 -1
  33. package/board/package.json +1 -1
  34. package/board/server/board-panel-state-helpers.ts +67 -0
  35. package/board/server/board-server-check.ts +294 -0
  36. package/board/server/board-server-engine.ts +129 -0
  37. package/board/server/board-server-events.ts +134 -0
  38. package/board/server/board-server-read.ts +190 -0
  39. package/board/server/board-server-resume.ts +164 -0
  40. package/board/server/board-server-start.ts +174 -0
  41. package/board/server/board-server-store.ts +281 -0
  42. package/board/server/board-server-types.ts +114 -0
  43. package/board/server/board-server-util.ts +4 -0
  44. package/board/server/board-server.ts +52 -943
  45. package/board/server/board-vocabulary-engine.ts +144 -0
  46. package/board/server/board-vocabulary-review.ts +115 -0
  47. package/board/server/board-vocabulary-run.ts +145 -0
  48. package/board/server/board-vocabulary-shapes.ts +153 -0
  49. package/board/server/board-vocabulary.ts +20 -375
  50. package/board/server/current-step-panel-helpers.ts +108 -0
  51. package/board/server/describe-record.ts +403 -0
  52. package/board/server/engine-launch.ts +20 -3
  53. package/board/server/engine-restart-helpers.ts +105 -0
  54. package/board/server/engine-watch.ts +45 -5
  55. package/board/server/journal-rebuild-helpers.ts +169 -0
  56. package/board/server/live-file.ts +130 -0
  57. package/board/server/luca-version.ts +1 -1
  58. package/board/server/make-rows.ts +14 -629
  59. package/board/server/record-texts.ts +350 -0
  60. package/board/server/reduce-board-agent-start.ts +224 -0
  61. package/board/server/reduce-board-agents.ts +160 -0
  62. package/board/server/reduce-board-counters.ts +138 -0
  63. package/board/server/reduce-board-create.ts +148 -0
  64. package/board/server/reduce-board-final-review.ts +174 -0
  65. package/board/server/reduce-board-needs-you.ts +283 -0
  66. package/board/server/reduce-board-reasons.ts +59 -0
  67. package/board/server/reduce-board-run.ts +255 -0
  68. package/board/server/reduce-board-settle.ts +88 -0
  69. package/board/server/reduce-board-steps.ts +226 -0
  70. package/board/server/reduce-board-text.ts +139 -0
  71. package/board/server/reduce-board-ticket-checks.ts +168 -0
  72. package/board/server/reduce-board-ticket-join.ts +144 -0
  73. package/board/server/reduce-board-ticket-start.ts +132 -0
  74. package/board/server/reduce-board-update.ts +117 -0
  75. package/board/server/reduce-board-usage.ts +247 -0
  76. package/board/server/reduce-board.ts +100 -1917
  77. package/board/server/reply-post.ts +407 -0
  78. package/board/server/run-registry.ts +8 -4
  79. package/board/server/ticket-stages-panel-helpers.ts +296 -0
  80. package/board/shared/board-rows.ts +9 -0
  81. package/board/shared/board-rpc.ts +42 -0
  82. package/board/shared/board-state-counts.ts +77 -0
  83. package/board/shared/board-state-live.ts +99 -0
  84. package/board/shared/board-state-review.ts +59 -0
  85. package/board/shared/board-state-run.ts +236 -0
  86. package/board/shared/board-state-ticket.ts +257 -0
  87. package/board/shared/board-state-usage.ts +107 -0
  88. package/board/shared/board-state.ts +97 -567
  89. package/board/shared/reply-actions.ts +207 -0
  90. package/engine/agents/agent-launcher.ts +27 -4
  91. package/engine/agents/claude-launcher-helpers.ts +233 -0
  92. package/engine/agents/claude-launcher.ts +28 -669
  93. package/engine/agents/claude-messages.ts +212 -0
  94. package/engine/agents/claude-options.ts +43 -8
  95. package/engine/agents/common-instructions.ts +138 -0
  96. package/engine/agents/input-channel.ts +36 -0
  97. package/engine/agents/launch-paths.ts +29 -0
  98. package/engine/agents/launch-session.ts +227 -0
  99. package/engine/agents/learner-instructions.ts +31 -0
  100. package/engine/agents/open-session.ts +210 -0
  101. package/engine/agents/prompt-sections.ts +95 -0
  102. package/engine/agents/rejoin-prompts.ts +135 -0
  103. package/engine/agents/reviewer-instructions.ts +99 -0
  104. package/engine/agents/role-instructions.ts +31 -244
  105. package/engine/agents/role-prompts.ts +39 -109
  106. package/engine/agents/role-results.ts +106 -7
  107. package/engine/agents/scripted-launcher.ts +21 -6
  108. package/engine/agents/session-pool.ts +124 -0
  109. package/engine/agents/turn-progress.ts +283 -0
  110. package/engine/agents/writer-instructions.ts +151 -0
  111. package/engine/cli/computer-adapters-real.ts +15 -3
  112. package/engine/cli/computer-adapters.ts +2 -0
  113. package/engine/cli/computer-checks.ts +87 -4
  114. package/engine/cli/config-reload-text.ts +39 -0
  115. package/engine/cli/decision-model-setup.ts +70 -0
  116. package/engine/cli/doctor-command.ts +1 -0
  117. package/engine/cli/doctor.ts +21 -3
  118. package/engine/cli/going-runs.ts +192 -36
  119. package/engine/cli/init-command.ts +1 -0
  120. package/engine/cli/init.ts +23 -1
  121. package/engine/cli/live-runs.ts +73 -0
  122. package/engine/cli/luca-run.ts +42 -7
  123. package/engine/cli/luca-skills.ts +183 -0
  124. package/engine/cli/luca.ts +8 -1
  125. package/engine/cli/models-command.ts +119 -0
  126. package/engine/cli/read-run.ts +81 -0
  127. package/engine/cli/run-args.ts +33 -6
  128. package/engine/cli/run-demo.ts +143 -0
  129. package/engine/cli/run-end.ts +184 -0
  130. package/engine/cli/run-modes-helpers.ts +81 -0
  131. package/engine/cli/run-modes.ts +62 -439
  132. package/engine/cli/setup.ts +5 -48
  133. package/engine/cli/unfinished-runs.ts +122 -0
  134. package/engine/cli/upgrade-command.ts +212 -30
  135. package/engine/cli/upgrade-finish.ts +149 -0
  136. package/engine/cli/upgrade.ts +112 -71
  137. package/engine/config/config-fields.ts +127 -0
  138. package/engine/config/config-reload.ts +250 -0
  139. package/engine/config/engine-config-schema.ts +235 -0
  140. package/engine/config/engine-config.ts +72 -142
  141. package/engine/config/test-commands.ts +102 -0
  142. package/engine/core/agent-guards-helpers.ts +84 -0
  143. package/engine/core/agent-turn.ts +395 -0
  144. package/engine/core/already-done.ts +44 -1
  145. package/engine/core/build-action.ts +214 -0
  146. package/engine/core/build-context.ts +135 -0
  147. package/engine/core/build-launch.ts +259 -0
  148. package/engine/core/commit-in.ts +282 -0
  149. package/engine/core/decide-build-early.ts +108 -0
  150. package/engine/core/decide-build-ending.ts +145 -0
  151. package/engine/core/decide-build-helpers.ts +87 -0
  152. package/engine/core/decide-build-tickets.ts +207 -0
  153. package/engine/core/decide-build-view.ts +103 -0
  154. package/engine/core/decide-build.ts +101 -1368
  155. package/engine/core/decide-final-fix.ts +203 -0
  156. package/engine/core/decide-final-review.ts +55 -310
  157. package/engine/core/decide-red-green.ts +398 -0
  158. package/engine/core/decide-rejoin.ts +259 -0
  159. package/engine/core/decide-repo-wait.ts +129 -0
  160. package/engine/core/decide-run-budget.ts +86 -12
  161. package/engine/core/decide-stuck-helpers.ts +127 -0
  162. package/engine/core/decide-stuck.ts +60 -8
  163. package/engine/core/decide-ticket-review.ts +95 -0
  164. package/engine/core/decide-ticket.ts +267 -0
  165. package/engine/core/decide-waits.ts +47 -0
  166. package/engine/core/decide.ts +74 -23
  167. package/engine/core/engine-chores.ts +163 -0
  168. package/engine/core/environment-rerun.ts +83 -0
  169. package/engine/core/evidence-text.ts +84 -0
  170. package/engine/core/execute-action.ts +303 -0
  171. package/engine/core/execute-already-done-check.ts +133 -0
  172. package/engine/core/execute-build.ts +68 -944
  173. package/engine/core/execute-changeset.ts +5 -7
  174. package/engine/core/execute-final-review.ts +125 -13
  175. package/engine/core/execute-gates.ts +182 -0
  176. package/engine/core/execute-intake.ts +130 -0
  177. package/engine/core/execute-join.ts +276 -0
  178. package/engine/core/execute-memory.ts +12 -0
  179. package/engine/core/execute-pull-request.ts +84 -0
  180. package/engine/core/execute-red-check.ts +219 -0
  181. package/engine/core/execute-repo-wait.ts +129 -0
  182. package/engine/core/execute-stuck.ts +25 -10
  183. package/engine/core/execute-waits.ts +158 -0
  184. package/engine/core/execute-with-jev.ts +118 -0
  185. package/engine/core/execute.ts +124 -831
  186. package/engine/core/final-failure-cap.ts +38 -0
  187. package/engine/core/final-review-action.ts +125 -0
  188. package/engine/core/final-review-text.ts +63 -17
  189. package/engine/core/final-stuck-text.ts +86 -0
  190. package/engine/core/fix-loop-text.ts +49 -2
  191. package/engine/core/left-out.ts +120 -0
  192. package/engine/core/live-file.ts +372 -0
  193. package/engine/core/loop-caps.ts +25 -2
  194. package/engine/core/not-in-run.ts +7 -3
  195. package/engine/core/pull-request-text.ts +255 -6
  196. package/engine/core/repo-wait-text.ts +81 -0
  197. package/engine/core/review-text.ts +55 -1
  198. package/engine/core/run-crash-recovery-helpers.ts +16 -0
  199. package/engine/core/run-session-close-helpers.ts +96 -0
  200. package/engine/core/run-start.ts +148 -0
  201. package/engine/core/run-stuck-text.ts +86 -0
  202. package/engine/core/schedule-rules.ts +193 -0
  203. package/engine/core/stuck-detail-text.ts +89 -0
  204. package/engine/core/stuck-format.ts +22 -0
  205. package/engine/core/stuck-text.ts +29 -332
  206. package/engine/core/ticket-stuck-text.ts +192 -0
  207. package/engine/core/worktree-changes.ts +106 -0
  208. package/engine/gates/already-done-check.ts +120 -0
  209. package/engine/gates/feature-map.ts +96 -0
  210. package/engine/gates/gate-runner.ts +108 -108
  211. package/engine/gates/gate-schemas.ts +61 -2
  212. package/engine/gates/leftover-scan.ts +154 -31
  213. package/engine/gates/new-tests.ts +45 -0
  214. package/engine/gates/own-files-check.ts +98 -0
  215. package/engine/gates/prepare-check.ts +60 -0
  216. package/engine/gates/prepare-slots.ts +99 -0
  217. package/engine/gates/red-check.ts +184 -16
  218. package/engine/gates/shell-check.ts +64 -0
  219. package/engine/gates/slot-waits.ts +55 -0
  220. package/engine/gates/slow-test-runner.ts +59 -0
  221. package/engine/gates/test-checks.ts +161 -0
  222. package/engine/gates/test-runner.ts +164 -5
  223. package/engine/gates/test-slots.ts +48 -0
  224. package/engine/git/git-adapter.ts +55 -396
  225. package/engine/git/git-commits.ts +132 -0
  226. package/engine/git/git-join.ts +139 -0
  227. package/engine/git/git-read.ts +123 -0
  228. package/engine/git/git-run.ts +32 -0
  229. package/engine/git/git-worktrees.ts +143 -0
  230. package/engine/git/outside-links.ts +55 -0
  231. package/engine/guards/after-turn-check.ts +31 -5
  232. package/engine/guards/guard-hook.ts +60 -12
  233. package/engine/guards/guard-roles.ts +68 -0
  234. package/engine/guards/helper-rules.ts +92 -0
  235. package/engine/guards/permission-rules.ts +161 -0
  236. package/engine/guards/role-rules-helpers.ts +48 -0
  237. package/engine/guards/role-rules.ts +34 -606
  238. package/engine/guards/shell-rules.ts +344 -0
  239. package/engine/guards/worktree-git.ts +208 -0
  240. package/engine/guards/worktree-snapshot.ts +412 -0
  241. package/engine/guards/worktree-state.ts +28 -825
  242. package/engine/guards/worktree-undo.ts +260 -0
  243. package/engine/guards/write-rules.ts +149 -0
  244. package/engine/index.ts +30 -9
  245. package/engine/intake/criteria-checks.ts +114 -0
  246. package/engine/intake/intake-checks.ts +164 -30
  247. package/engine/intake/intake-schemas.ts +37 -2
  248. package/engine/jev/clef-client.ts +259 -0
  249. package/engine/jev/decision-model-credentials.ts +212 -0
  250. package/engine/jev/jev-jobs.ts +144 -60
  251. package/engine/jev/jev-schemas.ts +86 -5
  252. package/engine/jev/jev-shadow-helpers.ts +218 -0
  253. package/engine/jev/jev-shadow.ts +109 -64
  254. package/engine/jev/model-pick.ts +275 -0
  255. package/engine/journal/agent-entries.ts +167 -0
  256. package/engine/journal/agent-message-entries.ts +72 -0
  257. package/engine/journal/agent-turn-replay.ts +203 -0
  258. package/engine/journal/already-done-entries.ts +55 -0
  259. package/engine/journal/commit-entries.ts +56 -0
  260. package/engine/journal/cross-ticket-replay.ts +222 -0
  261. package/engine/journal/decision-model-entries.ts +86 -0
  262. package/engine/journal/entry-fields.ts +10 -0
  263. package/engine/journal/final-review-entries.ts +128 -0
  264. package/engine/journal/final-review-replay.ts +324 -0
  265. package/engine/journal/final-review-round.ts +320 -0
  266. package/engine/journal/final-review-state.ts +180 -0
  267. package/engine/journal/gate-entries.ts +81 -0
  268. package/engine/journal/intake-entries.ts +87 -0
  269. package/engine/journal/join-entries.ts +126 -0
  270. package/engine/journal/journal-entry-schema.ts +218 -0
  271. package/engine/journal/journal-kinds.ts +97 -0
  272. package/engine/journal/journal-record-schema.ts +218 -0
  273. package/engine/journal/journal-record.ts +43 -1400
  274. package/engine/journal/journal.ts +6 -1
  275. package/engine/journal/limit-entries.ts +90 -0
  276. package/engine/journal/memory-entries.ts +124 -0
  277. package/engine/journal/memory-replay.ts +157 -0
  278. package/engine/journal/plan-replay.ts +139 -0
  279. package/engine/journal/pull-request-entries.ts +41 -0
  280. package/engine/journal/replay.ts +157 -2144
  281. package/engine/journal/reply-replay.ts +144 -0
  282. package/engine/journal/repo-wait-entries.ts +53 -0
  283. package/engine/journal/run-entries.ts +172 -0
  284. package/engine/journal/run-state.ts +264 -0
  285. package/engine/journal/shared-failure-replay.ts +133 -0
  286. package/engine/journal/stuck-entries.ts +193 -0
  287. package/engine/journal/ticket-progress.ts +392 -0
  288. package/engine/journal/ticket-replay.ts +411 -0
  289. package/engine/journal/ticket-result-replay.ts +364 -0
  290. package/engine/journal/ticket-retry-replay.ts +142 -0
  291. package/engine/journal/worktree-entries.ts +122 -0
  292. package/engine/models/model-score.ts +512 -0
  293. package/engine/shell/run-command.ts +187 -18
  294. package/engine/testing/agent-turn-fixtures.ts +287 -0
  295. package/engine/testing/already-done-fixtures.ts +97 -0
  296. package/engine/testing/build-fixtures.ts +68 -973
  297. package/engine/testing/checkouts-fixtures.ts +48 -0
  298. package/engine/testing/final-review-fixtures.ts +62 -1
  299. package/engine/testing/final-review-steps.ts +143 -0
  300. package/engine/testing/limit-fixtures.ts +85 -0
  301. package/engine/testing/live-file-fixtures.ts +57 -0
  302. package/engine/testing/practice-repo.ts +10 -4
  303. package/engine/testing/practice-run.ts +1 -1
  304. package/engine/testing/reply-fixtures.ts +88 -0
  305. package/engine/testing/run-budget-fixtures.ts +165 -0
  306. package/engine/testing/run-fixtures.ts +219 -0
  307. package/engine/testing/sdk-play.ts +177 -0
  308. package/engine/testing/setup-repo.ts +194 -0
  309. package/engine/testing/step-record-fixtures.ts +55 -0
  310. package/engine/testing/ticket-step-fixtures.ts +272 -0
  311. package/engine/testing/wait-scenarios.ts +186 -0
  312. package/engine/tracker/gh-retry.ts +217 -0
  313. package/engine/tracker/github-tracker.ts +310 -90
  314. package/engine/tracker/in-memory-tracker.ts +8 -4
  315. package/engine/tracker/tracker.ts +131 -6
  316. package/package.json +2 -1
  317. package/skills/luca-retro/SKILL.md +74 -0
  318. package/skills/luca-retro/scripts/retro-summary.ts +1356 -0
  319. package/skills/luca-unstick/SKILL.md +153 -0
  320. package/skills/luca-unstick/scripts/stuck-summary.ts +1063 -0
  321. package/engine/jev/jev-client.ts +0 -173
@@ -1,12 +1,43 @@
1
- import { z } from 'zod'
2
-
3
1
  /**
4
2
  * The board's view of one run, as the plugin's server keeps it and the side
5
3
  * panel reads it. Shared by both runtimes, so it holds only Zod schemas and
6
- * plain values. Words follow CONTEXT.md: ticket, blocked, gate, red check,
4
+ * plain values. Words follow GLOSSARY.md: ticket, blocked, gate, red check,
7
5
  * fix loop, ticket review, final review, lens, finding, stuck, skipped ticket,
8
6
  * limit wait, shadow mode.
7
+ *
8
+ * The pieces are in modules by responsibility, all re-exported from here:
9
+ * `board-state-ticket.ts` (a ticket's card and its steps),
10
+ * `board-state-run.ts` (the run, its status, its budget, what needs you),
11
+ * `board-state-review.ts` (the final review), `board-state-usage.ts` (plan
12
+ * usage) and `board-state-counts.ts` (what is only counted). This module
13
+ * holds the plugin's constants and the whole `BoardState`.
9
14
  */
15
+ import { z } from 'zod'
16
+
17
+ import {
18
+ JevCountsSchema,
19
+ MemoryCountsSchema,
20
+ MessageCountsSchema,
21
+ NO_MEMORY,
22
+ } from './board-state-counts'
23
+ import { LiveAgentSchema, LiveFeedEntrySchema } from './board-state-live'
24
+ import { FinalReviewSchema } from './board-state-review'
25
+ import {
26
+ DEFAULT_CONFIDENCE_BAR,
27
+ DEFAULT_RUN_BUDGET_TOKENS,
28
+ NeedsYouSchema,
29
+ PersonTicketSchema,
30
+ RunInfoSchema,
31
+ } from './board-state-run'
32
+ import { CurrentStepSchema, TicketCardSchema } from './board-state-ticket'
33
+ import { PlanUsedSchema, USAGE_LABEL, UsageSchema } from './board-state-usage'
34
+
35
+ export * from './board-state-counts'
36
+ export * from './board-state-live'
37
+ export * from './board-state-review'
38
+ export * from './board-state-run'
39
+ export * from './board-state-ticket'
40
+ export * from './board-state-usage'
10
41
 
11
42
  /** The plugin id, as in paseo-plugin.json. The engine calls RPCs on it. */
12
43
  export const PLUGIN_ID = 'luca-board'
@@ -15,7 +46,7 @@ export const PLUGIN_ID = 'luca-board'
15
46
  export const POLL_MS = 2000
16
47
 
17
48
  /** How to call the slash command, as shown to people. */
18
- export const RUN_USAGE = '/luca-run <spec number> | demo'
49
+ export const RUN_USAGE = '/luca-run <spec number> | demo | resume <run id>'
19
50
 
20
51
  /** The side panel's text when this workspace has no run yet. */
21
52
  export const EMPTY_BOARD_TEXT =
@@ -24,528 +55,13 @@ export const EMPTY_BOARD_TEXT =
24
55
  /** Fix loops and review rounds are capped at 3. */
25
56
  export const LOOP_CAP = 3
26
57
 
27
- /** The steps of a ticket, shown as dots on its card. */
28
- export const STEP_NAMES = [
29
- 'tests',
30
- 'red check',
31
- 'code',
32
- 'checks',
33
- 'review',
34
- ] as const
35
-
36
- /** Refactor tickets skip the first two steps: no new test can fail first. */
37
- export const REFACTOR_SKIPS_STEPS = 2
38
-
39
- /** The step index a ticket has when every step is done. */
40
- export const ALL_STEPS_DONE = STEP_NAMES.length
41
-
42
- /** Plan usage is yellow from this percent. */
43
- export const USAGE_WARN_FROM = 60
44
-
45
- /** Plan usage is red above this percent. */
46
- export const USAGE_HIGH_ABOVE = 85
47
-
48
- export const UsageLevelSchema = z.enum(['ok', 'warn', 'high'])
49
-
50
- export type UsageLevel = z.infer<typeof UsageLevelSchema>
51
-
52
- /**
53
- * The color level of a plan usage percent: `ok` (green) below 60, `warn`
54
- * (yellow) from 60 to 85, `high` (red) above 85.
55
- *
56
- * @example
57
- * usageLevel({ percent: 59 }) // 'ok'
58
- * usageLevel({ percent: 85 }) // 'warn'
59
- * usageLevel({ percent: 86 }) // 'high'
60
- */
61
- export const usageLevel = ({ percent }: { percent: number }): UsageLevel => {
62
- if (percent > USAGE_HIGH_ABOVE) return 'high'
63
- if (percent >= USAGE_WARN_FROM) return 'warn'
64
- return 'ok'
65
- }
66
-
67
- /** Plan windows in words; any other window keeps its raw name. */
68
- const WINDOW_WORDS: Record<string, string> = {
69
- five_hour: 'five-hour',
70
- seven_day: 'weekly',
71
- seven_day_opus: 'weekly Opus',
72
- seven_day_sonnet: 'weekly Sonnet',
73
- }
74
-
75
- /**
76
- * A plan window's name in words.
77
- *
78
- * @example
79
- * windowWords({ window: 'seven_day_opus' }) // 'weekly Opus'
80
- * windowWords({ window: 'overage' }) // 'overage'
81
- */
82
- export const windowWords = ({ window }: { window: string }): string =>
83
- WINDOW_WORDS[window] ?? window
84
-
85
- /**
86
- * The order windows are shown in: five-hour, weekly, the other weekly ones
87
- * (Opus, Sonnet, ...), then the rest.
88
- */
89
- export const windowRank = ({ window }: { window: string }): number => {
90
- if (window === 'five_hour') return 0
91
- if (window === 'seven_day') return 1
92
- if (window.startsWith('seven_day')) return 2
93
- return 3
94
- }
95
-
96
- /** How much of one plan window something used, as a whole percent. */
97
- export const PlanUsedSchema = z.object({
98
- /** The window in words, such as "five-hour" or "weekly". */
99
- window: z.string(),
100
- percent: z.number(),
101
- })
102
-
103
- export type PlanUsed = z.infer<typeof PlanUsedSchema>
104
-
105
- /**
106
- * The plan used, in words, windows in their shown order.
107
- *
108
- * @example
109
- * planUsedText({ used, sign: '+' }) // 'five-hour +1%, weekly +1%'
110
- * planUsedText({ used, sign: '' }) // 'five-hour 3%, weekly 1%'
111
- */
112
- export const planUsedText = ({
113
- used,
114
- sign,
115
- }: {
116
- used: PlanUsed[]
117
- sign: '+' | ''
118
- }): string =>
119
- used.map(({ window, percent }) => `${window} ${sign}${percent}%`).join(', ')
120
-
121
- /**
122
- * Why the run stopped and what to do, in words. A billing stop, or a stop
123
- * after the same step crashed again and again, won't go on; any other stop
124
- * picks up again when the run is started with its run id.
125
- *
126
- * @example
127
- * stoppedText({ reason: 'overage in use', billing: true })
128
- * // 'Stopped for billing: overage in use. This run will not go on; ...'
129
- */
130
- export const stoppedText = ({
131
- reason,
132
- billing,
133
- crashed,
134
- }: {
135
- reason: string
136
- billing: boolean
137
- /** The same run-level step crashed again and again. */
138
- crashed?: boolean
139
- }): string => {
140
- if (billing) {
141
- return `Stopped for billing: ${reason}. This run will not go on; start a new run once per-token billing is off.`
142
- }
143
- if (crashed === true) {
144
- return `Stopped after crashes: ${reason} This run will not go on; read the engine's log for why it crashed, then start a new run.`
145
- }
146
- return `The run stopped: ${reason}. Start it again with the same run id to pick up where it stopped.`
147
- }
148
-
149
- /** Where a run stands, as one word for the header row and the panel. */
150
- export const RunStatusSchema = z.enum([
151
- 'starting',
152
- 'intake',
153
- 'building',
154
- 'final_review',
155
- 'limit_wait',
156
- 'usage_line_wait',
157
- 'done',
158
- 'refused',
159
- 'nothing_to_do',
160
- 'stuck',
161
- 'stopped',
162
- 'ended_with_error',
163
- ])
164
-
165
- export type RunStatus = z.infer<typeof RunStatusSchema>
166
-
167
- /** The part of a run's status the journal sets directly. */
168
- export const RunPhaseSchema = z.enum([
169
- 'starting',
170
- 'intake',
171
- 'building',
172
- 'final_review',
173
- 'done',
174
- 'refused',
175
- 'nothing_to_do',
176
- ])
177
-
178
- export type RunPhase = z.infer<typeof RunPhaseSchema>
179
-
180
- export const TicketStageSchema = z.enum([
181
- 'blocked',
182
- 'building',
183
- 'reviewing',
184
- 'stuck',
185
- 'done',
186
- 'skipped',
187
- ])
188
-
189
- export type TicketStage = z.infer<typeof TicketStageSchema>
190
-
191
- export const FindingCountsSchema = z.object({
192
- blocker: z.number().int().min(0),
193
- should_fix: z.number().int().min(0),
194
- nit: z.number().int().min(0),
195
- })
196
-
197
- export type FindingCounts = z.infer<typeof FindingCountsSchema>
198
-
199
- /**
200
- * A check whose failure went back to an agent: its fix loop is open. `review`
201
- * is a ticket review that asked for changes: the next fixer starts a review
202
- * fix round.
203
- */
204
- export const OpenCheckSchema = z.enum(['red_check', 'gates', 'review'])
205
-
206
- export type OpenCheck = z.infer<typeof OpenCheckSchema>
207
-
208
- /**
209
- * What the engine is doing right now for a ticket or the run, from its
210
- * `step_started`, such as "running the baseline tests"; `since` is that
211
- * record's time. Cleared by the step's `step_ended`.
212
- */
213
- export const CurrentStepSchema = z.object({
214
- text: z.string(),
215
- since: z.string(),
216
- })
217
-
218
- export type CurrentStep = z.infer<typeof CurrentStepSchema>
219
-
220
- /**
221
- * A time span in words: seconds under a minute, then minutes and seconds,
222
- * then hours and minutes.
223
- *
224
- * @example
225
- * elapsedText({ ms: 125_000 }) // '2m 5s'
226
- * elapsedText({ ms: 3_790_000 }) // '1h 3m'
227
- */
228
- export const elapsedText = ({ ms }: { ms: number }): string => {
229
- const seconds = Math.max(0, Math.floor(ms / 1000))
230
- const hours = Math.floor(seconds / 3600)
231
- const minutes = Math.floor((seconds % 3600) / 60)
232
- if (hours > 0) return `${hours}h ${minutes}m`
233
- if (minutes > 0) return `${minutes}m ${seconds % 60}s`
234
- return `${seconds}s`
235
- }
236
-
237
- /**
238
- * The current step in words, with how long it has been running.
239
- *
240
- * @example
241
- * currentStepText({
242
- * step: { text: 'running the checks', since: '2026-09-23T12:00:00.000Z' },
243
- * now: Date.parse('2026-09-23T12:02:05.000Z'),
244
- * }) // 'running the checks (2m 5s)'
245
- */
246
- export const currentStepText = ({
247
- step,
248
- now,
249
- }: {
250
- step: CurrentStep
251
- now: number
252
- }): string =>
253
- `${step.text} (${elapsedText({ ms: now - Date.parse(step.since) })})`
254
-
255
- export const TicketCardSchema = z.object({
256
- number: z.number().int(),
257
- title: z.string(),
258
- refactor: z.boolean(),
259
- /** Tickets of this run this one waits on. */
260
- blockers: z.array(z.number().int()),
261
- /** The engine has begun work on it (its worktree exists). */
262
- started: z.boolean(),
263
- stage: TicketStageSchema,
264
- /**
265
- * Index into STEP_NAMES of the current step: -1 not started, 5 when every
266
- * step is done. A stuck or skipped ticket keeps the step it stopped at.
267
- */
268
- step: z.number().int().min(-1).max(ALL_STEPS_DONE),
269
- /** One-word (or two) activity, such as "coding" or "reviewing". */
270
- activity: z.string(),
271
- /** The role working on it now, `null` when no agent is. */
272
- role: z.string().nullable(),
273
- /**
274
- * The engine's step on it now, `null` between steps. Defaulted, so a
275
- * board state from before it still parses.
276
- */
277
- current_step: CurrentStepSchema.nullable().default(null),
278
- /** Fix rounds in the current fix loop; back to 0 once its check passes. */
279
- fix_round: z.number().int().min(0),
280
- review_round: z.number().int().min(0),
281
- /** Review fix rounds so far (up to LOOP_CAP): fixes after a review. */
282
- review_fix_round: z.number().int().min(0),
283
- /** The check whose failure the next follow-up answers, if any. */
284
- open_check: OpenCheckSchema.nullable(),
285
- /** The role whose latest turn failed, until an agent finishes again. */
286
- failed_turn: z.string().nullable(),
287
- tests: z
288
- .object({
289
- failing: z.number().int().min(0),
290
- total: z.number().int().min(0),
291
- })
292
- .nullable(),
293
- /**
294
- * Its own baseline's test counts, kept so a ticket that reuses this one's
295
- * baseline (`baseline_reused`) gets them after `tests` has moved on.
296
- * Defaulted, so a board state from before it still parses.
297
- */
298
- baseline_tests: z
299
- .object({
300
- failing: z.number().int().min(0),
301
- total: z.number().int().min(0),
302
- })
303
- .nullable()
304
- .default(null),
305
- findings: FindingCountsSchema.nullable(),
306
- /** Every token the ticket's agents read or wrote, cache reads included. */
307
- tokens: z.number().min(0),
308
- /** The same tokens by role. */
309
- agent_tokens: z.record(z.string(), z.number().min(0)),
310
- /** What was tried on it so far (failed checks, fix rounds), oldest first. */
311
- tried: z.array(z.string()),
312
- /** How much of the plan the ticket used, per window; empty until known. */
313
- plan_used: z.array(PlanUsedSchema),
314
- })
315
-
316
- export type TicketCard = z.infer<typeof TicketCardSchema>
317
-
318
- /** Stuck work waiting for a one-word reply on the spec issue. */
319
- export const NeedsYouSchema = z.object({
320
- /** `ticket-<n>`, `final`, or `run` (the run stuck on its budget). */
321
- key: z.string(),
322
- ticket: z.number().int().nullable(),
323
- subject: z.string(),
324
- reason: z.string(),
325
- detail: z.string(),
326
- /** What was tried before it got stuck, oldest first. */
327
- tried: z.array(z.string()),
328
- /** The exact replies to post as a comment on the spec issue. */
329
- replies: z.array(z.string()),
330
- since: z.string(),
331
- })
332
-
333
- export type NeedsYou = z.infer<typeof NeedsYouSchema>
334
-
335
- export const LENS_NAMES = [
336
- 'architecture',
337
- 'simplification',
338
- 'security',
339
- 'integration',
340
- 'rules',
341
- ] as const
342
-
343
- export const LensNameSchema = z.enum(LENS_NAMES)
344
-
345
- export type LensName = z.infer<typeof LensNameSchema>
346
-
347
- export const LensStateSchema = z.enum([
348
- 'waiting',
349
- 'reviewing',
350
- 'fixing',
351
- 'clean',
352
- ])
353
-
354
- export type LensState = z.infer<typeof LensStateSchema>
355
-
356
- export const LensCardSchema = z.object({
357
- name: LensNameSchema,
358
- state: LensStateSchema,
359
- findings: FindingCountsSchema,
360
- })
361
-
362
- export type LensCard = z.infer<typeof LensCardSchema>
363
-
364
- export const FinalReviewStateSchema = z.enum([
365
- 'waiting',
366
- 'reviewing',
367
- 'fixing',
368
- 'stuck',
369
- 'passed',
370
- ])
371
-
372
- export type FinalReviewState = z.infer<typeof FinalReviewStateSchema>
373
-
374
- export const FinalReviewSchema = z.object({
375
- state: FinalReviewStateSchema,
376
- /** False (drawn dimmed) until every ticket is done or skipped. */
377
- active: z.boolean(),
378
- round: z.number().int().min(0),
379
- fix_round: z.number().int().min(0),
380
- lenses: z.array(LensCardSchema),
381
- /** What was tried on it so far, oldest first. */
382
- tried: z.array(z.string()),
383
- })
384
-
385
- export type FinalReview = z.infer<typeof FinalReviewSchema>
386
-
387
- /**
388
- * The plan's usage, from the rate-limit readings in agents' sessions. Each
389
- * window keeps its latest reading; `null` until one arrives.
390
- */
391
- export const UsageSchema = z.object({
392
- five_hour_percent: z.number().nullable(),
393
- weekly_percent: z.number().nullable(),
394
- five_hour_level: UsageLevelSchema.nullable(),
395
- weekly_level: UsageLevelSchema.nullable(),
396
- /** When the five-hour window resets. */
397
- resets_at: z.string().nullable(),
398
- read_at: z.string(),
399
- })
400
-
401
- export type Usage = z.infer<typeof UsageSchema>
402
-
403
- /**
404
- * The words on the plan usage: its windows are the whole account's, every
405
- * run's and chat's, never one run's.
406
- */
407
- export const USAGE_LABEL = 'plan usage (account-wide)'
408
-
409
- /**
410
- * The engine's default run budget in tokens (its
411
- * `DEFAULT_RUN_BUDGET_TOKENS`, in `engine/src/limits/run-budget.ts`), for a
412
- * run whose engine config sets no `run_budget_tokens`. The plugin never
413
- * loads the engine's code, so the number is kept here too.
414
- */
415
- export const DEFAULT_RUN_BUDGET_TOKENS = 9_000_000
416
-
417
- export const EngineEndedSchema = z.object({
418
- ok: z.boolean(),
419
- message: z.string(),
420
- })
421
-
422
- export type EngineEnded = z.infer<typeof EngineEndedSchema>
423
-
424
- /**
425
- * The run stopped because going on was unsafe (wrong credentials or plan,
426
- * a rejected rate limit, overage, ...). Starting it again with the same run
427
- * id picks the step up again, except after a billing stop, which won't go on.
428
- */
429
- export const RunStoppedSchema = z.object({
430
- reason: z.string(),
431
- role: z.string().nullable(),
432
- ticket: z.number().int().nullable(),
433
- /** The session would bill per token; this run won't go on. */
434
- billing: z.boolean(),
435
- /** The same step crashed again and again; this run won't go on. */
436
- crashed: z.boolean().default(false),
437
- since: z.string(),
438
- })
439
-
440
- export type RunStopped = z.infer<typeof RunStoppedSchema>
441
-
442
- export const RunInfoSchema = z.object({
443
- run_id: z.string(),
444
- /** `null` for a demo run until the engine names its spec. */
445
- spec_number: z.number().int().nullable(),
446
- spec_title: z.string().nullable(),
447
- demo: z.boolean(),
448
- branch: z.string().nullable(),
449
- phase: RunPhaseSchema,
450
- status: RunStatusSchema,
451
- pr_url: z.string().nullable(),
452
- pr_number: z.number().int().nullable(),
453
- /** Why intake refused the run, one line per problem. */
454
- refusal: z.array(z.string()),
455
- started_at: z.string(),
456
- last_time: z.string().nullable(),
457
- engine_ended: EngineEndedSchema.nullable(),
458
- /** Set by `run_stopped`, cleared once the engine moves on. */
459
- stopped: RunStoppedSchema.nullable(),
460
- log_path: z.string().nullable(),
461
- /**
462
- * Luca's version the run started on (#460); `null` when its journal
463
- * doesn't say. Defaulted, so an older board state still parses.
464
- */
465
- started_on: z.string().nullable().default(null),
466
- /**
467
- * Set once the run resumed on a different Luca version than it started
468
- * on, naming both. The run keeps going. Defaulted, so an older board
469
- * state still parses.
470
- */
471
- version_note: z.string().nullable().default(null),
472
- })
473
-
474
- export type RunInfo = z.infer<typeof RunInfoSchema>
475
-
476
- /**
477
- * Jev in shadow mode: how often the engine asked it and what came back. The
478
- * engine never acts on the answers, so the board only counts them.
479
- */
480
- export const JevCountsSchema = z.object({
481
- asked: z.number().int().min(0),
482
- answered: z.number().int().min(0),
483
- failed: z.number().int().min(0),
484
- })
485
-
486
- export type JevCounts = z.infer<typeof JevCountsSchema>
487
-
488
- /**
489
- * Agent messages: `sent` counts the queued and the not delivered ones,
490
- * `refused` the ones the engine turned down, and `delivered` the messages
491
- * handed over, once per receiver (a message to `all` can count more than
492
- * once). Messages happen inside a turn, so the board only counts them.
493
- */
494
- export const MessageCountsSchema = z.object({
495
- sent: z.number().int().min(0),
496
- refused: z.number().int().min(0),
497
- delivered: z.number().int().min(0),
498
- })
499
-
500
- export type MessageCounts = z.infer<typeof MessageCountsSchema>
501
-
502
- /**
503
- * Where the learner stands at the end of a run (#370): `waiting` until it
504
- * starts (a run without memory never starts it), `learning`, `learned`
505
- * once it answered, or `skipped` when the engine gave up on it.
506
- */
507
- export const LearnerStateSchema = z.enum([
508
- 'waiting',
509
- 'learning',
510
- 'learned',
511
- 'skipped',
512
- ])
513
-
514
- export type LearnerState = z.infer<typeof LearnerStateSchema>
515
-
516
- /**
517
- * Memory (#370), counted: the searches at recall points (and the vault
518
- * searches among them that failed), the memories they showed agents, the
519
- * learner, and what became of its memories. Memory changes no ticket, so
520
- * the board only counts it.
521
- */
522
- export const MemoryCountsSchema = z.object({
523
- searches: z.number().int().min(0),
524
- search_errors: z.number().int().min(0),
525
- shown: z.number().int().min(0),
526
- learner: LearnerStateSchema,
527
- added: z.number().int().min(0),
528
- updated: z.number().int().min(0),
529
- refused: z.number().int().min(0),
530
- failed: z.number().int().min(0),
531
- })
532
-
533
- export type MemoryCounts = z.infer<typeof MemoryCountsSchema>
534
-
535
- /** Memory before any of its records. */
536
- export const NO_MEMORY: MemoryCounts = {
537
- searches: 0,
538
- search_errors: 0,
539
- shown: 0,
540
- learner: 'waiting',
541
- added: 0,
542
- updated: 0,
543
- refused: 0,
544
- failed: 0,
545
- }
546
-
547
58
  export const BoardStateSchema = z.object({
548
59
  run: RunInfoSchema,
60
+ /**
61
+ * The open tickets left out for a person (#499), by number. Defaulted,
62
+ * so an older board state still parses.
63
+ */
64
+ for_a_person: z.array(PersonTicketSchema).default([]),
549
65
  usage: UsageSchema.nullable(),
550
66
  /** What `usage` is, in words. Defaulted, so an older board state still parses. */
551
67
  usage_label: z.string().default(USAGE_LABEL),
@@ -558,12 +74,25 @@ export const BoardStateSchema = z.object({
558
74
  run_tokens: z.number().min(0).default(0),
559
75
  /**
560
76
  * The tokens the run may use before it is stuck (#435): one full run
561
- * budget, plus one more for each `retry` the owner gave it. Defaulted,
562
- * so an older board state still parses.
77
+ * budget, plus the top-up of each `retry` the owner gave the run stuck
78
+ * on its budget (#543; one whole budget for a retry journaled with no
79
+ * top-up). Defaulted, so an older board state still parses.
563
80
  */
564
81
  run_budget_tokens: z.number().min(0).default(DEFAULT_RUN_BUDGET_TOKENS),
565
82
  /** One full run budget: the engine config's, else the default. */
566
83
  run_budget_each: z.number().min(0).default(DEFAULT_RUN_BUDGET_TOKENS),
84
+ /**
85
+ * Why the whole run is stuck (`run_stuck`'s reason), until the owner's
86
+ * reply; `null` while it isn't. Defaulted, so an older board state
87
+ * still parses.
88
+ */
89
+ run_stuck_reason: z.string().nullable().default(null),
90
+ /**
91
+ * The engine config's `models.confidence_bar` (#630): the decision
92
+ * model is sharp at or over it. Defaulted, so an older board state
93
+ * still parses.
94
+ */
95
+ confidence_bar: z.number().min(0).max(1).default(DEFAULT_CONFIDENCE_BAR),
567
96
  /** The plan limit was hit; the engine waits and then carries on. */
568
97
  limit_wait: z
569
98
  .object({
@@ -593,6 +122,19 @@ export const BoardStateSchema = z.object({
593
122
  })
594
123
  .nullable()
595
124
  .default(null),
125
+ /**
126
+ * The run waits for the earlier runs on its repo that are going (#615),
127
+ * from `repo_wait_started` until `repo_wait_ended`. Defaulted, so a
128
+ * board state from before it still parses.
129
+ */
130
+ repo_wait: z
131
+ .object({
132
+ /** The run ids of the earlier runs it waits for. */
133
+ runs: z.array(z.string()),
134
+ since: z.string(),
135
+ })
136
+ .nullable()
137
+ .default(null),
596
138
  /** How much of the plan the whole run used, per window. */
597
139
  run_plan_used: z.array(PlanUsedSchema),
598
140
  needs_you: z.array(NeedsYouSchema),
@@ -621,12 +163,41 @@ export const BoardStateSchema = z.object({
621
163
  run_steps: z
622
164
  .array(CurrentStepSchema.extend({ key: z.string() }))
623
165
  .default([]),
166
+ /**
167
+ * Every agent in a turn, from the run's live file (#628), those on no
168
+ * ticket too. Laid over the state on each read, never journaled.
169
+ * Defaulted, so a board state from before it still parses.
170
+ */
171
+ live_agents: z.array(LiveAgentSchema).default([]),
624
172
  /** The newest of `run_steps`, `null` when none runs. */
625
173
  current_step: CurrentStepSchema.nullable().default(null),
174
+ /**
175
+ * The run's last actions across tickets, oldest first, from its live
176
+ * file (#634), never from the journal. Defaulted, so a board state
177
+ * from before it still parses.
178
+ */
179
+ feed: z.array(LiveFeedEntrySchema).default([]),
180
+ /**
181
+ * When the engine last wrote the run's live file, `null` with none: its
182
+ * last sign of life (#635). Defaulted, so a board state from before it
183
+ * still parses.
184
+ */
185
+ live_written_at: z.string().nullable().default(null),
186
+ /**
187
+ * The plugin's process check found no engine for this run, which it
188
+ * didn't start and so never restarts (#635). Defaulted, so a board
189
+ * state from before it still parses.
190
+ */
191
+ engine_gone: z.boolean().default(false),
626
192
  /** Journal records applied so far. */
627
193
  event_count: z.number().int().min(0),
628
194
  /** The latest thing that happened, in words (for the footer). */
629
195
  latest: z.string().nullable(),
196
+ /**
197
+ * When `latest` happened (its record's time), so the footer can say how
198
+ * long ago. Defaulted, so a board state from before it still parses.
199
+ */
200
+ latest_at: z.string().nullable().default(null),
630
201
  })
631
202
 
632
203
  export type BoardState = z.infer<typeof BoardStateSchema>
@@ -640,44 +211,3 @@ export type BoardState = z.infer<typeof BoardStateSchema>
640
211
  */
641
212
  export const showsFinalReview = ({ state }: { state: BoardState }): boolean =>
642
213
  state.run.phase !== 'nothing_to_do' && state.run.phase !== 'refused'
643
-
644
- /** One run in the panel's run list. */
645
- export const RunSummarySchema = z.object({
646
- run_id: z.string(),
647
- spec_number: z.number().int().nullable(),
648
- spec_title: z.string().nullable(),
649
- demo: z.boolean(),
650
- status: RunStatusSchema,
651
- started_at: z.string(),
652
- needs_you: z.number().int().min(0),
653
- })
654
-
655
- export type RunSummary = z.infer<typeof RunSummarySchema>
656
-
657
- /** How many of the newest runs get a tab; the older ones are folded. */
658
- export const RECENT_RUN_TABS = 6
659
-
660
- /**
661
- * Splits runs (newest first) into the ones with a tab and the folded older
662
- * ones. A picked older run keeps a tab too, after the newest ones.
663
- *
664
- * @example
665
- * const { shown, folded } = foldRuns({ runs, selected: picked })
666
- */
667
- export const foldRuns = ({
668
- runs,
669
- selected,
670
- }: {
671
- runs: RunSummary[]
672
- selected: string | null
673
- }): { shown: RunSummary[]; folded: RunSummary[] } => {
674
- const shown = runs.slice(0, RECENT_RUN_TABS)
675
- const older = runs.slice(RECENT_RUN_TABS)
676
- const picked = older.find((run) => run.run_id === selected)
677
- return picked
678
- ? {
679
- shown: [...shown, picked],
680
- folded: older.filter((run) => run !== picked),
681
- }
682
- : { shown, folded: older }
683
- }