cloving 1.0.1 → 1.2.0

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 (309) hide show
  1. package/README.md +251 -20
  2. package/assets/watch-fonts/fira-code-OFL.md +93 -0
  3. package/assets/watch-fonts/fira-code-latin-400-normal.woff2 +0 -0
  4. package/assets/watch-fonts/fira-code-latin-500-normal.woff2 +0 -0
  5. package/assets/watch-fonts/heebo-OFL.md +93 -0
  6. package/assets/watch-fonts/heebo-latin-500-normal.woff2 +0 -0
  7. package/assets/watch-fonts/heebo-latin-700-normal.woff2 +0 -0
  8. package/assets/watch-fonts/heebo-latin-800-normal.woff2 +0 -0
  9. package/assets/watch-fonts/manifest.json +112 -0
  10. package/assets/watch-fonts/space-grotesk-OFL.md +93 -0
  11. package/assets/watch-fonts/space-grotesk-latin-400-normal.woff2 +0 -0
  12. package/assets/watch-fonts/space-grotesk-latin-500-normal.woff2 +0 -0
  13. package/assets/watch-fonts/space-grotesk-latin-600-normal.woff2 +0 -0
  14. package/assets/watch-fonts/space-grotesk-latin-700-normal.woff2 +0 -0
  15. package/dist/src/authority/commands.d.ts +1 -0
  16. package/dist/src/authority/commands.js +33 -4
  17. package/dist/src/authority/dispatch-decision.d.ts +1 -0
  18. package/dist/src/authority/dispatch-decision.js +7 -4
  19. package/dist/src/authority/events.js +2 -2
  20. package/dist/src/authority/handoff-chain.d.ts +24 -0
  21. package/dist/src/authority/handoff-chain.js +46 -0
  22. package/dist/src/authority/reconciliation.d.ts +3 -0
  23. package/dist/src/authority/reconciliation.js +21 -5
  24. package/dist/src/authority/transitions.d.ts +1 -1
  25. package/dist/src/authority/transitions.js +5 -0
  26. package/dist/src/broker/actor/authority-queries.d.ts +4 -0
  27. package/dist/src/broker/actor/authority-queries.js +51 -2
  28. package/dist/src/broker/actor/authority-session.d.ts +2 -0
  29. package/dist/src/broker/actor/authority-session.js +3 -4
  30. package/dist/src/broker/actor/delivery-intents.d.ts +1 -0
  31. package/dist/src/broker/actor/effect-delivery.js +9 -0
  32. package/dist/src/broker/authority-admission.js +13 -5
  33. package/dist/src/chats/analysis-batches.d.ts +42 -0
  34. package/dist/src/chats/analysis-batches.js +97 -0
  35. package/dist/src/chats/analysis-checkpoint.d.ts +29 -0
  36. package/dist/src/chats/analysis-checkpoint.js +31 -0
  37. package/dist/src/chats/analysis-contract.d.ts +80 -0
  38. package/dist/src/chats/analysis-contract.js +16 -0
  39. package/dist/src/chats/analysis-data.d.ts +3 -0
  40. package/dist/src/chats/analysis-data.js +85 -0
  41. package/dist/src/chats/analysis-input.d.ts +18 -0
  42. package/dist/src/chats/analysis-input.js +97 -0
  43. package/dist/src/chats/analysis-links.d.ts +2 -0
  44. package/dist/src/chats/analysis-links.js +25 -0
  45. package/dist/src/chats/analysis-merge.d.ts +16 -0
  46. package/dist/src/chats/analysis-merge.js +134 -0
  47. package/dist/src/chats/analysis-prompt.d.ts +18 -0
  48. package/dist/src/chats/analysis-prompt.js +42 -0
  49. package/dist/src/chats/analysis-proposal.d.ts +4 -0
  50. package/dist/src/chats/analysis-proposal.js +41 -0
  51. package/dist/src/chats/analysis-provenance.d.ts +4 -0
  52. package/dist/src/chats/analysis-provenance.js +99 -0
  53. package/dist/src/chats/analysis-shape.d.ts +4 -0
  54. package/dist/src/chats/analysis-shape.js +235 -0
  55. package/dist/src/chats/analysis.d.ts +3 -0
  56. package/dist/src/chats/analysis.js +42 -0
  57. package/dist/src/chats/corpus-input.d.ts +5 -0
  58. package/dist/src/chats/corpus-input.js +288 -0
  59. package/dist/src/chats/destination.js +3 -1
  60. package/dist/src/chats/import.d.ts +8 -6
  61. package/dist/src/chats/import.js +195 -23
  62. package/dist/src/chats/input-file.d.ts +3 -0
  63. package/dist/src/chats/input-file.js +32 -0
  64. package/dist/src/chats/normalize.d.ts +11 -2
  65. package/dist/src/chats/normalize.js +18 -2
  66. package/dist/src/chats/previous-import.d.ts +5 -0
  67. package/dist/src/chats/previous-import.js +34 -0
  68. package/dist/src/chats/reader.d.ts +7 -1
  69. package/dist/src/chats/reader.js +46 -15
  70. package/dist/src/chats/redact-import.d.ts +14 -0
  71. package/dist/src/chats/redact-import.js +141 -0
  72. package/dist/src/chats/redaction.d.ts +41 -0
  73. package/dist/src/chats/redaction.js +161 -0
  74. package/dist/src/chats/reference-mapping.d.ts +16 -0
  75. package/dist/src/chats/reference-mapping.js +153 -0
  76. package/dist/src/chats/revision-input.d.ts +3 -0
  77. package/dist/src/chats/revision-input.js +180 -0
  78. package/dist/src/chats/revision.d.ts +48 -0
  79. package/dist/src/chats/revision.js +94 -0
  80. package/dist/src/chats/selection.d.ts +25 -0
  81. package/dist/src/chats/selection.js +109 -0
  82. package/dist/src/chats/source.d.ts +23 -0
  83. package/dist/src/chats/source.js +135 -0
  84. package/dist/src/chats/structure.d.ts +57 -0
  85. package/dist/src/chats/structure.js +216 -0
  86. package/dist/src/chats/workflow-generation-projection.d.ts +8 -0
  87. package/dist/src/chats/workflow-generation-projection.js +75 -0
  88. package/dist/src/chats/workflow-generation-source.d.ts +25 -0
  89. package/dist/src/chats/workflow-generation-source.js +124 -0
  90. package/dist/src/chats/workflow-generation-text.d.ts +23 -0
  91. package/dist/src/chats/workflow-generation-text.js +74 -0
  92. package/dist/src/chats/workflow-generation.d.ts +69 -0
  93. package/dist/src/chats/workflow-generation.js +142 -0
  94. package/dist/src/cli/arguments.d.ts +8 -0
  95. package/dist/src/cli/arguments.js +94 -13
  96. package/dist/src/cli/client.d.ts +1 -0
  97. package/dist/src/cli/client.js +38 -1
  98. package/dist/src/cli/commands.d.ts +3 -0
  99. package/dist/src/cli/commands.js +29 -11
  100. package/dist/src/cli/main.d.ts +2 -1
  101. package/dist/src/cli/main.js +61 -14
  102. package/dist/src/cli/render.d.ts +3 -0
  103. package/dist/src/cli/render.js +122 -7
  104. package/dist/src/cli/usage-render.d.ts +3 -0
  105. package/dist/src/cli/usage-render.js +28 -0
  106. package/dist/src/cli/usage.d.ts +3 -2
  107. package/dist/src/cli/usage.js +42 -7
  108. package/dist/src/cli/watch-actions.d.ts +10 -0
  109. package/dist/src/cli/watch-actions.js +58 -0
  110. package/dist/src/cli/watch-board-view.d.ts +44 -1
  111. package/dist/src/cli/watch-board-view.js +137 -18
  112. package/dist/src/cli/watch-board.d.ts +7 -1
  113. package/dist/src/cli/watch-board.js +48 -11
  114. package/dist/src/cli/watch-card-client.d.ts +1 -0
  115. package/dist/src/cli/watch-card-client.js +44 -0
  116. package/dist/src/cli/watch-card-forms.d.ts +2 -0
  117. package/dist/src/cli/watch-card-forms.js +33 -0
  118. package/dist/src/cli/watch-card-reply.d.ts +5 -0
  119. package/dist/src/cli/watch-card-reply.js +34 -0
  120. package/dist/src/cli/watch-card-view.d.ts +40 -0
  121. package/dist/src/cli/watch-card-view.js +86 -0
  122. package/dist/src/cli/watch-client.d.ts +1 -0
  123. package/dist/src/cli/watch-client.js +122 -0
  124. package/dist/src/cli/watch-model.d.ts +10 -4
  125. package/dist/src/cli/watch-model.js +61 -67
  126. package/dist/src/cli/watch-page.d.ts +1 -1
  127. package/dist/src/cli/watch-page.js +89 -86
  128. package/dist/src/cli/watch-phases.js +3 -3
  129. package/dist/src/cli/watch-pr-board.d.ts +3 -0
  130. package/dist/src/cli/watch-pr-board.js +26 -0
  131. package/dist/src/cli/watch-pr-command.d.ts +18 -0
  132. package/dist/src/cli/watch-pr-command.js +74 -0
  133. package/dist/src/cli/watch-pr-identity.d.ts +32 -0
  134. package/dist/src/cli/watch-pr-identity.js +87 -0
  135. package/dist/src/cli/watch-pr-observer.d.ts +20 -0
  136. package/dist/src/cli/watch-pr-observer.js +150 -0
  137. package/dist/src/cli/watch-pr-settings.d.ts +8 -0
  138. package/dist/src/cli/watch-pr-settings.js +47 -0
  139. package/dist/src/cli/watch-pull-request.d.ts +10 -0
  140. package/dist/src/cli/watch-pull-request.js +130 -0
  141. package/dist/src/cli/watch-reads.d.ts +7 -1
  142. package/dist/src/cli/watch-reads.js +44 -18
  143. package/dist/src/cli/watch-render.d.ts +29 -2
  144. package/dist/src/cli/watch-render.js +11 -3
  145. package/dist/src/cli/watch-run-input.d.ts +14 -0
  146. package/dist/src/cli/watch-run-input.js +49 -0
  147. package/dist/src/cli/watch-server.d.ts +3 -0
  148. package/dist/src/cli/watch-server.js +77 -8
  149. package/dist/src/cli/watch-start-view.d.ts +18 -0
  150. package/dist/src/cli/watch-start-view.js +77 -0
  151. package/dist/src/cli/watch-turn.d.ts +1 -0
  152. package/dist/src/cli/watch-turn.js +8 -3
  153. package/dist/src/cli/watch-usage.d.ts +6 -0
  154. package/dist/src/cli/watch-usage.js +65 -0
  155. package/dist/src/cli/watch-view.d.ts +22 -0
  156. package/dist/src/cli/watch-view.js +111 -19
  157. package/dist/src/cli.js +48 -6
  158. package/dist/src/daemon/analysis-launch.d.ts +5 -0
  159. package/dist/src/daemon/analysis-launch.js +81 -0
  160. package/dist/src/daemon/analysis-token.d.ts +6 -0
  161. package/dist/src/daemon/analysis-token.js +88 -0
  162. package/dist/src/daemon/analysis-worker.d.ts +20 -0
  163. package/dist/src/daemon/analysis-worker.js +271 -0
  164. package/dist/src/daemon/chats-batch-binding.d.ts +23 -0
  165. package/dist/src/daemon/chats-batch-binding.js +99 -0
  166. package/dist/src/daemon/chats-capture.d.ts +18 -0
  167. package/dist/src/daemon/chats-capture.js +194 -0
  168. package/dist/src/daemon/chats-checkpoints.d.ts +41 -0
  169. package/dist/src/daemon/chats-checkpoints.js +186 -0
  170. package/dist/src/daemon/chats-files.d.ts +42 -0
  171. package/dist/src/daemon/chats-files.js +252 -0
  172. package/dist/src/daemon/chats-follow-up.d.ts +38 -0
  173. package/dist/src/daemon/chats-follow-up.js +180 -0
  174. package/dist/src/daemon/chats-question.d.ts +15 -0
  175. package/dist/src/daemon/chats-question.js +93 -0
  176. package/dist/src/daemon/chats-worker.d.ts +13 -0
  177. package/dist/src/daemon/chats-worker.js +201 -0
  178. package/dist/src/daemon/claude-turn.d.ts +11 -0
  179. package/dist/src/daemon/claude-turn.js +96 -15
  180. package/dist/src/daemon/claude-worker.d.ts +2 -1
  181. package/dist/src/daemon/claude-worker.js +92 -45
  182. package/dist/src/daemon/codex-stream.js +16 -0
  183. package/dist/src/daemon/daemon.d.ts +8 -1
  184. package/dist/src/daemon/daemon.js +51 -14
  185. package/dist/src/daemon/delivery-router.js +6 -0
  186. package/dist/src/daemon/github-calls.d.ts +4 -0
  187. package/dist/src/daemon/github-calls.js +16 -7
  188. package/dist/src/daemon/github-ci.d.ts +6 -0
  189. package/dist/src/daemon/github-ci.js +170 -0
  190. package/dist/src/daemon/github-merge-call.d.ts +14 -0
  191. package/dist/src/daemon/github-merge-call.js +73 -0
  192. package/dist/src/daemon/github-merge.d.ts +3 -5
  193. package/dist/src/daemon/github-merge.js +52 -95
  194. package/dist/src/daemon/github-methods.d.ts +28 -0
  195. package/dist/src/daemon/github-methods.js +84 -16
  196. package/dist/src/daemon/github-operation.d.ts +2 -0
  197. package/dist/src/daemon/github-retry.d.ts +6 -0
  198. package/dist/src/daemon/github-retry.js +49 -0
  199. package/dist/src/daemon/github-ticket-status.d.ts +3 -0
  200. package/dist/src/daemon/github-ticket-status.js +294 -0
  201. package/dist/src/daemon/github-worker.js +2 -1
  202. package/dist/src/daemon/handoff-worker.d.ts +10 -0
  203. package/dist/src/daemon/handoff-worker.js +232 -0
  204. package/dist/src/daemon/issue-queue.d.ts +2 -0
  205. package/dist/src/daemon/issue-queue.js +186 -43
  206. package/dist/src/daemon/notify-journal.d.ts +18 -0
  207. package/dist/src/daemon/notify-journal.js +108 -0
  208. package/dist/src/daemon/notify-message.js +2 -1
  209. package/dist/src/daemon/notify.js +176 -37
  210. package/dist/src/daemon/operation-journal.d.ts +1 -0
  211. package/dist/src/daemon/operation-journal.js +4 -2
  212. package/dist/src/daemon/retrying-turn.d.ts +12 -0
  213. package/dist/src/daemon/retrying-turn.js +231 -0
  214. package/dist/src/daemon/ticket-dependency-waits.d.ts +18 -0
  215. package/dist/src/daemon/ticket-dependency-waits.js +60 -0
  216. package/dist/src/daemon/ticket-registration.d.ts +8 -4
  217. package/dist/src/daemon/ticket-registration.js +76 -22
  218. package/dist/src/daemon/ticket-source.d.ts +4 -1
  219. package/dist/src/daemon/ticket-source.js +8 -2
  220. package/dist/src/decision-visibility.d.ts +2 -0
  221. package/dist/src/decision-visibility.js +10 -0
  222. package/dist/src/execution-pin.d.ts +4 -0
  223. package/dist/src/execution-pin.js +23 -2
  224. package/dist/src/host/start-delivery.d.ts +2 -1
  225. package/dist/src/host/start-delivery.js +1 -1
  226. package/dist/src/host/step-bridge-host.d.ts +1 -0
  227. package/dist/src/host/step-bridge-host.js +16 -0
  228. package/dist/src/host/world-reconciliation.d.ts +3 -0
  229. package/dist/src/host/world-reconciliation.js +9 -0
  230. package/dist/src/host/world-runs.js +14 -1
  231. package/dist/src/issue-identity.d.ts +6 -0
  232. package/dist/src/issue-identity.js +23 -0
  233. package/dist/src/service/queue-runs.d.ts +3 -0
  234. package/dist/src/service/queue-runs.js +39 -0
  235. package/dist/src/service/registry.d.ts +8 -2
  236. package/dist/src/service/registry.js +20 -8
  237. package/dist/src/service/replies.d.ts +2 -0
  238. package/dist/src/service/run-handlers.js +54 -30
  239. package/dist/src/service/service.d.ts +3 -0
  240. package/dist/src/service/service.js +3 -1
  241. package/dist/src/service/submission-handler.d.ts +1 -0
  242. package/dist/src/service/submission-handler.js +92 -13
  243. package/dist/src/service/worker-totals.d.ts +3 -0
  244. package/dist/src/service/worker-totals.js +52 -0
  245. package/dist/src/step-bridge.d.ts +9 -0
  246. package/dist/src/steps/handoff.d.ts +2 -0
  247. package/dist/src/steps/handoff.js +13 -0
  248. package/dist/src/ticket-dependencies.d.ts +1 -0
  249. package/dist/src/ticket-dependencies.js +15 -0
  250. package/dist/src/ticket-status.d.ts +8 -0
  251. package/dist/src/ticket-status.js +48 -0
  252. package/dist/src/token-prices.d.ts +21 -0
  253. package/dist/src/token-prices.js +108 -0
  254. package/dist/src/worker-usage.d.ts +31 -0
  255. package/dist/src/worker-usage.js +44 -0
  256. package/dist/src/workflow-admission.d.ts +1 -0
  257. package/dist/src/workflow-candidate.d.ts +29 -0
  258. package/dist/src/workflow-candidate.js +60 -0
  259. package/dist/src/workflow-contracts.d.ts +4 -0
  260. package/dist/src/workflow-contracts.js +32 -5
  261. package/dist/src/workflow-declaration.d.ts +12 -1
  262. package/dist/src/workflow-declaration.js +71 -13
  263. package/dist/src/workflow-discovery.d.ts +18 -0
  264. package/dist/src/workflow-discovery.js +33 -0
  265. package/dist/src/workflow-refusals.d.ts +6 -0
  266. package/dist/src/workflow-refusals.js +39 -0
  267. package/dist/src/workflows.d.ts +3 -1
  268. package/dist/src/workflows.js +2 -0
  269. package/dist/tools/admit-workflow.d.ts +13 -0
  270. package/dist/tools/admit-workflow.js +94 -0
  271. package/dist/tools/self-host-processes.js +7 -2
  272. package/dist/tools/self-host-project.d.ts +17 -0
  273. package/dist/tools/self-host-project.js +177 -0
  274. package/dist/tools/self-host-workflows.d.ts +1 -1
  275. package/dist/tools/self-host-workflows.js +2 -2
  276. package/dist/tools/self-host.js +30 -84
  277. package/dist/tools/self-update-host.d.ts +7 -0
  278. package/dist/tools/self-update-host.js +48 -1
  279. package/dist/tools/self-update.d.ts +1 -0
  280. package/dist/tools/self-update.js +85 -13
  281. package/dist/tools/workflow-bundle-dependencies.d.ts +5 -0
  282. package/dist/tools/workflow-bundle-dependencies.js +93 -0
  283. package/dist/tools/workflow-bundles.d.ts +20 -0
  284. package/dist/tools/workflow-bundles.js +173 -0
  285. package/dist/tools/workflow-candidate-counts.d.ts +8 -0
  286. package/dist/tools/workflow-candidate-counts.js +251 -0
  287. package/dist/tools/workflow-candidate-program.d.ts +11 -0
  288. package/dist/tools/workflow-candidate-program.js +78 -0
  289. package/dist/tools/workflow-candidate-source.d.ts +6 -0
  290. package/dist/tools/workflow-candidate-source.js +107 -0
  291. package/dist/tools/workflow-candidate-syntax.d.ts +15 -0
  292. package/dist/tools/workflow-candidate-syntax.js +219 -0
  293. package/dist/tools/workflow-folder-capture.d.ts +17 -0
  294. package/dist/tools/workflow-folder-capture.js +216 -0
  295. package/dist/tools/workflow-folders.d.ts +33 -0
  296. package/dist/tools/workflow-folders.js +150 -0
  297. package/dist/tools/workflow-source-effects.d.ts +10 -0
  298. package/dist/tools/workflow-source-effects.js +213 -0
  299. package/dist/workflows/analyze-chats.d.ts +15 -0
  300. package/dist/workflows/analyze-chats.declaration.json +22 -0
  301. package/dist/workflows/analyze-chats.js +250 -0
  302. package/dist/workflows/develop-prd.js +71 -3
  303. package/dist/workflows/develop-ticket.d.ts +1 -7
  304. package/dist/workflows/develop-ticket.js +47 -141
  305. package/dist/workflows/issue-labels.d.ts +3 -1
  306. package/dist/workflows/issue-labels.js +34 -0
  307. package/dist/workflows/ticket-prompts.d.ts +35 -0
  308. package/dist/workflows/ticket-prompts.js +143 -0
  309. package/package.json +25 -5
@@ -0,0 +1,33 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { isWorkflowDeclaration } from "../workflow-declaration.js";
4
+ import { builtinCardForms } from "./watch-run-input.js";
5
+ export async function watchCardForms(directory) {
6
+ const forms = [...builtinCardForms()];
7
+ const files = await readdir(directory).catch(() => []);
8
+ for (const file of files
9
+ .filter((name) => name.endsWith(".declaration.json"))
10
+ .sort()) {
11
+ const value = await readFile(join(directory, file), "utf8")
12
+ .then((text) => JSON.parse(text))
13
+ .catch(() => null);
14
+ if (!isWorkflowDeclaration(value))
15
+ continue;
16
+ const entries = Object.entries(value.input.fields);
17
+ if (!entries.every(([, rule]) => rule === "string" || rule.type === "string"))
18
+ continue;
19
+ if (forms.some((form) => form.workflowId === value.workflowId))
20
+ continue;
21
+ forms.push({
22
+ workflowId: value.workflowId,
23
+ source: "input",
24
+ fields: entries.map(([name, rule]) => ({
25
+ name,
26
+ label: name,
27
+ multiline: false,
28
+ optional: rule !== "string" && rule.optional === true,
29
+ })),
30
+ });
31
+ }
32
+ return forms;
33
+ }
@@ -0,0 +1,5 @@
1
+ import type { Reply } from "../protocol.ts";
2
+ export declare function cardReply(reply: Reply): {
3
+ readonly admitted: boolean;
4
+ readonly message: string;
5
+ };
@@ -0,0 +1,34 @@
1
+ export function cardReply(reply) {
2
+ if (reply.kind === "result")
3
+ return {
4
+ admitted: true,
5
+ message: `Run ${String(reply.result["runId"])} is admitted`,
6
+ };
7
+ const explanations = {
8
+ "workflow-active": "Add the label cloving: ready to the issue to put it in the queue.",
9
+ "unknown-workflow": "This workflow is no longer registered. Refresh the page.",
10
+ "workflow-unavailable": "A required workflow capability is unavailable.",
11
+ "invalid-submission": "Check the workflow fields.",
12
+ "input-too-large": "Shorten the input and try again.",
13
+ "invalid-request": "The service could not read this run request.",
14
+ "conflicting-command": "This form was already sent with different fields. Open a new form.",
15
+ "service-unavailable": "Start the Cloving service and retry this form.",
16
+ "authority-unavailable": "The service authority is unavailable. Retry this form later.",
17
+ "permission-denied": "The service refused access to this account.",
18
+ "outcome-unknown": "The reply was lost. Retry this form with the same command ID.",
19
+ "resolver-timeout": "The input resolver timed out. Retry this form.",
20
+ "resolver-failure": "The input resolver failed. Check the service before retrying.",
21
+ "invalid-ticket-id": "Enter a valid issue number or ticket ID.",
22
+ "ticket-source-not-configured": "Configure the daemon ticket command first.",
23
+ "ticket-not-available": "The ticket could not be read.",
24
+ "next-source-not-configured": "Configure the daemon next-ticket command first.",
25
+ "next-ticket-not-available": "The next ticket could not be read.",
26
+ "no-ready-ticket": "There is no ready ticket in the queue.",
27
+ "unsupported-prompt": "This workflow requires declared input fields.",
28
+ };
29
+ const { code } = reply.error;
30
+ return {
31
+ admitted: false,
32
+ message: `${code}: ${explanations[code] ?? "The service refused this run."}`,
33
+ };
34
+ }
@@ -0,0 +1,40 @@
1
+ import { type Shown } from "./watch-element.ts";
2
+ import type { CardField, CardForm } from "./watch-run-input.ts";
3
+ export interface CardDraft {
4
+ readonly commandId: string;
5
+ readonly fields: Readonly<Record<string, string>>;
6
+ readonly pending: boolean;
7
+ readonly admitted: boolean;
8
+ readonly message: string;
9
+ }
10
+ export interface CardControls {
11
+ readonly draft: CardDraft | null;
12
+ readonly onOpen: () => void;
13
+ readonly onCancel: () => void;
14
+ readonly onChange: (name: string, value: string) => void;
15
+ readonly onSubmit: () => void;
16
+ }
17
+ export interface CardKeyEvent {
18
+ readonly key: string;
19
+ readonly defaultPrevented?: boolean;
20
+ readonly isComposing?: boolean;
21
+ readonly repeat?: boolean;
22
+ readonly ctrlKey?: boolean;
23
+ readonly metaKey?: boolean;
24
+ readonly target?: {
25
+ readonly localName?: string;
26
+ };
27
+ readonly currentTarget: object;
28
+ readonly preventDefault: () => void;
29
+ }
30
+ export declare function cardSubmitKey(event: CardKeyEvent): boolean;
31
+ export declare function cardKey(event: CardKeyEvent, controls: CardControls): void;
32
+ export declare function CardInput(props: {
33
+ readonly field: CardField;
34
+ readonly controls: CardControls;
35
+ readonly draft: CardDraft;
36
+ }): Shown;
37
+ export declare function AddCard(props: {
38
+ readonly form: CardForm | undefined;
39
+ readonly controls: CardControls | undefined;
40
+ }): Shown;
@@ -0,0 +1,86 @@
1
+ import { component, element } from "./watch-element.js";
2
+ export function cardSubmitKey(event) {
3
+ const name = event.target?.localName;
4
+ return (name === "input" ||
5
+ (name === "textarea" && Boolean(event.ctrlKey || event.metaKey)));
6
+ }
7
+ export function cardKey(event, controls) {
8
+ if (event.defaultPrevented || event.isComposing || event.repeat)
9
+ return;
10
+ if (event.key === "Escape") {
11
+ event.preventDefault();
12
+ controls.onCancel();
13
+ return;
14
+ }
15
+ if (event.key !== "Enter")
16
+ return;
17
+ if (!cardSubmitKey(event))
18
+ return;
19
+ event.preventDefault();
20
+ const prototype = Reflect.getPrototypeOf(event.currentTarget);
21
+ if (prototype === null)
22
+ return;
23
+ const requestSubmit = Reflect.get(prototype, "requestSubmit");
24
+ if (typeof requestSubmit === "function")
25
+ Reflect.apply(requestSubmit, event.currentTarget, []);
26
+ }
27
+ export function CardInput(props) {
28
+ const { field, controls, draft } = props;
29
+ return element("label", {
30
+ children: [
31
+ field.label,
32
+ element(field.multiline ? "textarea" : "input", {
33
+ name: field.name,
34
+ type: field.multiline ? undefined : "text",
35
+ rows: field.multiline ? 5 : undefined,
36
+ value: draft.fields[field.name] ?? "",
37
+ required: !field.optional,
38
+ disabled: draft.pending || draft.admitted,
39
+ onInput: (event) => {
40
+ controls.onChange(field.name, event.currentTarget.value);
41
+ },
42
+ }),
43
+ ],
44
+ });
45
+ }
46
+ export function AddCard(props) {
47
+ const { form, controls } = props;
48
+ if (form === undefined || controls === undefined)
49
+ return null;
50
+ const { draft } = controls;
51
+ if (draft === null)
52
+ return element("button", {
53
+ class: "add-card",
54
+ type: "button",
55
+ onClick: controls.onOpen,
56
+ children: "+ Add a card",
57
+ });
58
+ return element("form", {
59
+ class: "add-card-form",
60
+ onKeyDown: (event) => {
61
+ cardKey(event, controls);
62
+ },
63
+ onSubmit: (event) => {
64
+ event.preventDefault();
65
+ controls.onSubmit();
66
+ },
67
+ children: [
68
+ form.fields.map((field) => component(CardInput, { key: field.name, field, controls, draft })),
69
+ element("button", {
70
+ type: "submit",
71
+ disabled: draft.pending || draft.admitted,
72
+ children: draft.pending ? "Adding…" : "Add card",
73
+ }),
74
+ element("button", {
75
+ type: "button",
76
+ onClick: controls.onCancel,
77
+ children: "Cancel",
78
+ }),
79
+ element("p", {
80
+ role: "status",
81
+ "aria-live": "polite",
82
+ children: draft.message,
83
+ }),
84
+ ],
85
+ });
86
+ }
@@ -0,0 +1 @@
1
+ export declare const watchClient = "\nconst state = { model: null, receivedAt: 0, stale: false, copied: \"\", label: \"\", run: \"\", loading: false, again: false, columns: {}, cards: {}, start: null, hovered: null, focused: null };\nfunction readPreference(key) {\n try { return JSON.parse(localStorage.getItem(key)); }\n catch { return null; }\n}\nfunction readColumnChoices(key) {\n const value = readPreference(key);\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n return Object.fromEntries(Object.entries(value).filter(([, choice]) => typeof choice === \"boolean\"));\n}\nfunction savePreference(key, value) {\n try { localStorage.setItem(key, JSON.stringify(value)); }\n catch {}\n}\nfunction startControls() {\n if (state.start === null) state.start = readPreference(\"cloving.watch.start\") === true;\n return { shown: state.start, onToggle() {\n state.start = !state.start;\n savePreference(\"cloving.watch.start\", state.start);\n draw();\n } };\n}\nfunction columnControls(model, selected) {\n const workflowId = model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId);\n if (workflowId === null) return undefined;\n const key = \"cloving.watch.columns:\" + workflowId;\n if (!Object.hasOwn(state.columns, workflowId))\n state.columns = { ...state.columns, [workflowId]: readColumnChoices(key) };\n const choices = state.columns[workflowId];\n return { choices, onHover(name) { state.hovered = name; }, onFocus(name) { state.focused = name; }, onToggle(name) {\n const changed = { ...choices, [name]: !columnCollapsed(choices, name) };\n state.columns = { ...state.columns, [workflowId]: changed };\n savePreference(key, changed);\n draw();\n } };\n}\nfunction moved(next, followed) {\n const arrived = next === \"waiting_gate\" && next !== state.run;\n state.run = next;\n if (arrived) scrollTo(0, 0);\n else if (followed && next === \"running\") scrollTo(0, document.body.scrollHeight);\n}\nfunction copy(command) {\n const shown = (label) => {\n state.copied = command;\n state.label = label;\n draw();\n };\n navigator.clipboard.writeText(command).then(\n () => { shown(\"Copied\"); },\n () => { shown(\"Copy failed\"); },\n );\n}\nfunction draw() {\n const { model, stale } = state;\n const selected = location.hash.slice(1);\n const followed = innerHeight + scrollY >= document.body.scrollHeight - 80;\n const copying = { command: state.copied, label: state.label, onCopy: copy };\n const now = new Date().toISOString();\n const observationNow = model === null ? now : new Date(Date.parse(model.updatedAt) + performance.now() - state.receivedAt).toISOString();\n const columns = columnControls(model, selected);\n const start = model === null ? undefined : startControls();\n const cards = cardControls(model, model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId));\n const page = component(Page, { model, selected, now, observationNow, stale, copying, columns, start, cards });\n render(page, document.getElementById(\"app\"));\n if (model === null) return;\n document.title = watchTitle(model, selected);\n const { runId } = watchLocation(selected);\n moved(model.runs.find((run) => run.runId === runId)?.state ?? \"\", followed);\n}\nasync function withSelectedRun(model) {\n const { runId } = watchLocation(location.hash);\n if (runId === null || model.runs.some((run) => run.runId === runId)) return model;\n const detail = await fetch(\"data.json?runId=\" + encodeURIComponent(runId), { cache: \"no-store\" });\n if (detail.status === 404) return model;\n if (!detail.ok) throw new Error(\"watch-run-not-available\");\n const { run } = await detail.json();\n if (run?.runId !== runId) throw new Error(\"watch-run-mismatch\");\n return { ...model, runs: [...model.runs, run] };\n}\nasync function load() {\n state.again = state.loading;\n if (state.loading) return;\n state.loading = true;\n try {\n const reply = await fetch(\"data.json\", { cache: \"no-store\" });\n if (!reply.ok) throw new Error(\"watch-not-available\");\n const model = await reply.json();\n const receivedAt = performance.now();\n state.model = await withSelectedRun(model);\n state.receivedAt = receivedAt;\n state.stale = false;\n } catch {\n state.stale = true;\n }\n state.loading = false;\n draw();\n if (state.again) load();\n}\nfunction listen() {\n const source = new EventSource(\"events\");\n source.onmessage = () => { load(); };\n source.onerror = () => { state.stale = true; draw(); };\n}\naddEventListener(\"keydown\", (event) => {\n if (event.key !== String.fromCharCode(92) || event.defaultPrevented || event.repeat || event.isComposing) return;\n if (event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) return;\n if (event.target?.closest?.(\"input, textarea, select, [contenteditable]\")) return;\n const target = state.hovered ?? state.focused;\n if (target === null || watchLocation(location.hash).runId !== null) return;\n const controls = columnControls(state.model, location.hash);\n if (controls === undefined) return;\n event.preventDefault();\n controls.onToggle(target);\n});\naddEventListener(\"hashchange\", () => { state.run = \"\"; state.hovered = null; state.focused = null; draw(); load(); });\ndraw();\nlisten();\nsetInterval(draw, 1000);\nsetInterval(load, 15000);\n";
@@ -0,0 +1,122 @@
1
+ export const watchClient = `
2
+ const state = { model: null, receivedAt: 0, stale: false, copied: "", label: "", run: "", loading: false, again: false, columns: {}, cards: {}, start: null, hovered: null, focused: null };
3
+ function readPreference(key) {
4
+ try { return JSON.parse(localStorage.getItem(key)); }
5
+ catch { return null; }
6
+ }
7
+ function readColumnChoices(key) {
8
+ const value = readPreference(key);
9
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
10
+ return Object.fromEntries(Object.entries(value).filter(([, choice]) => typeof choice === "boolean"));
11
+ }
12
+ function savePreference(key, value) {
13
+ try { localStorage.setItem(key, JSON.stringify(value)); }
14
+ catch {}
15
+ }
16
+ function startControls() {
17
+ if (state.start === null) state.start = readPreference("cloving.watch.start") === true;
18
+ return { shown: state.start, onToggle() {
19
+ state.start = !state.start;
20
+ savePreference("cloving.watch.start", state.start);
21
+ draw();
22
+ } };
23
+ }
24
+ function columnControls(model, selected) {
25
+ const workflowId = model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId);
26
+ if (workflowId === null) return undefined;
27
+ const key = "cloving.watch.columns:" + workflowId;
28
+ if (!Object.hasOwn(state.columns, workflowId))
29
+ state.columns = { ...state.columns, [workflowId]: readColumnChoices(key) };
30
+ const choices = state.columns[workflowId];
31
+ return { choices, onHover(name) { state.hovered = name; }, onFocus(name) { state.focused = name; }, onToggle(name) {
32
+ const changed = { ...choices, [name]: !columnCollapsed(choices, name) };
33
+ state.columns = { ...state.columns, [workflowId]: changed };
34
+ savePreference(key, changed);
35
+ draw();
36
+ } };
37
+ }
38
+ function moved(next, followed) {
39
+ const arrived = next === "waiting_gate" && next !== state.run;
40
+ state.run = next;
41
+ if (arrived) scrollTo(0, 0);
42
+ else if (followed && next === "running") scrollTo(0, document.body.scrollHeight);
43
+ }
44
+ function copy(command) {
45
+ const shown = (label) => {
46
+ state.copied = command;
47
+ state.label = label;
48
+ draw();
49
+ };
50
+ navigator.clipboard.writeText(command).then(
51
+ () => { shown("Copied"); },
52
+ () => { shown("Copy failed"); },
53
+ );
54
+ }
55
+ function draw() {
56
+ const { model, stale } = state;
57
+ const selected = location.hash.slice(1);
58
+ const followed = innerHeight + scrollY >= document.body.scrollHeight - 80;
59
+ const copying = { command: state.copied, label: state.label, onCopy: copy };
60
+ const now = new Date().toISOString();
61
+ const observationNow = model === null ? now : new Date(Date.parse(model.updatedAt) + performance.now() - state.receivedAt).toISOString();
62
+ const columns = columnControls(model, selected);
63
+ const start = model === null ? undefined : startControls();
64
+ const cards = cardControls(model, model === null ? null : boardWorkflow(model, watchLocation(selected).workflowId));
65
+ const page = component(Page, { model, selected, now, observationNow, stale, copying, columns, start, cards });
66
+ render(page, document.getElementById("app"));
67
+ if (model === null) return;
68
+ document.title = watchTitle(model, selected);
69
+ const { runId } = watchLocation(selected);
70
+ moved(model.runs.find((run) => run.runId === runId)?.state ?? "", followed);
71
+ }
72
+ async function withSelectedRun(model) {
73
+ const { runId } = watchLocation(location.hash);
74
+ if (runId === null || model.runs.some((run) => run.runId === runId)) return model;
75
+ const detail = await fetch("data.json?runId=" + encodeURIComponent(runId), { cache: "no-store" });
76
+ if (detail.status === 404) return model;
77
+ if (!detail.ok) throw new Error("watch-run-not-available");
78
+ const { run } = await detail.json();
79
+ if (run?.runId !== runId) throw new Error("watch-run-mismatch");
80
+ return { ...model, runs: [...model.runs, run] };
81
+ }
82
+ async function load() {
83
+ state.again = state.loading;
84
+ if (state.loading) return;
85
+ state.loading = true;
86
+ try {
87
+ const reply = await fetch("data.json", { cache: "no-store" });
88
+ if (!reply.ok) throw new Error("watch-not-available");
89
+ const model = await reply.json();
90
+ const receivedAt = performance.now();
91
+ state.model = await withSelectedRun(model);
92
+ state.receivedAt = receivedAt;
93
+ state.stale = false;
94
+ } catch {
95
+ state.stale = true;
96
+ }
97
+ state.loading = false;
98
+ draw();
99
+ if (state.again) load();
100
+ }
101
+ function listen() {
102
+ const source = new EventSource("events");
103
+ source.onmessage = () => { load(); };
104
+ source.onerror = () => { state.stale = true; draw(); };
105
+ }
106
+ addEventListener("keydown", (event) => {
107
+ if (event.key !== String.fromCharCode(92) || event.defaultPrevented || event.repeat || event.isComposing) return;
108
+ if (event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) return;
109
+ if (event.target?.closest?.("input, textarea, select, [contenteditable]")) return;
110
+ const target = state.hovered ?? state.focused;
111
+ if (target === null || watchLocation(location.hash).runId !== null) return;
112
+ const controls = columnControls(state.model, location.hash);
113
+ if (controls === undefined) return;
114
+ event.preventDefault();
115
+ controls.onToggle(target);
116
+ });
117
+ addEventListener("hashchange", () => { state.run = ""; state.hovered = null; state.focused = null; draw(); load(); });
118
+ draw();
119
+ listen();
120
+ setInterval(draw, 1000);
121
+ setInterval(load, 15000);
122
+ `;
@@ -1,4 +1,10 @@
1
+ import type { WorkflowStages } from "../workflow-declaration.ts";
2
+ import { type WorkflowRefusal } from "../workflow-refusals.ts";
1
3
  import type { WatchDecision, WatchItem, WatchModel, WatchTurn } from "./watch-render.ts";
4
+ import { type GithubRecords } from "./watch-pull-request.ts";
5
+ export type { GithubRecords } from "./watch-pull-request.ts";
6
+ import { type PrRepository } from "./watch-pr-identity.ts";
7
+ type Fields = Readonly<Record<string, unknown>>;
2
8
  export declare function activityOf(line: string): WatchItem[];
3
9
  export interface TurnRecords {
4
10
  readonly key: string;
@@ -10,7 +16,11 @@ export interface TurnRecords {
10
16
  export declare function turnOf(records: TurnRecords): WatchTurn;
11
17
  export declare function decisionCommands(decision: unknown, client: string): WatchDecision;
12
18
  export interface WatchInputs {
19
+ readonly queue?: Fields;
20
+ readonly repository?: PrRepository | null;
13
21
  readonly workflows: readonly string[];
22
+ readonly workflowRefusals?: readonly WorkflowRefusal[];
23
+ readonly workflowStages?: WorkflowStages;
14
24
  readonly summaries?: readonly unknown[];
15
25
  readonly home: string;
16
26
  readonly client: string;
@@ -22,8 +32,4 @@ export interface WatchInputs {
22
32
  readonly turns: readonly WatchTurn[];
23
33
  readonly githubRecords: readonly GithubRecords[];
24
34
  }
25
- export interface GithubRecords {
26
- readonly started: unknown;
27
- readonly result: unknown;
28
- }
29
35
  export declare function watchModel(inputs: WatchInputs): WatchModel;
@@ -1,5 +1,14 @@
1
- import { boardsOf, currentStepOf, roundOf, startWorkflowOf, } from "./watch-board.js";
1
+ import { operationalDecision } from "../decision-visibility.js";
2
+ import { workflowRefusals, } from "../workflow-refusals.js";
3
+ import { isTicketStatus } from "../ticket-status.js";
4
+ import { isPromotionId } from "../workflow-contracts.js";
5
+ import { priceFields, statusTurns } from "./watch-usage.js";
6
+ import { priceOf, usageOf } from "../worker-usage.js";
7
+ import { priceText } from "./usage-render.js";
8
+ import { boardsOf, currentStepOf, roundOf, stagesFor, startWorkflowOf, } from "./watch-board.js";
2
9
  import { decisionOf, phasesOf } from "./watch-phases.js";
10
+ import { pullOf } from "./watch-pull-request.js";
11
+ import { establishedIssue } from "./watch-pr-identity.js";
3
12
  import { displayRun } from "./watch-paths.js";
4
13
  const textLimit = 600;
5
14
  const activityLimit = 400;
@@ -59,7 +68,7 @@ function failed(part) {
59
68
  return [{ kind: "error", text: shown }];
60
69
  }
61
70
  function ended(line) {
62
- const cost = count(line["total_cost_usd"]);
71
+ const cost = priceOf(line["total_cost_usd"]);
63
72
  return [
64
73
  {
65
74
  kind: "result",
@@ -79,13 +88,6 @@ export function activityOf(line) {
79
88
  return parts(value).flatMap(failed);
80
89
  return value["type"] === "result" ? ended(value) : [];
81
90
  }
82
- function costOf(lines) {
83
- const costs = lines
84
- .map((line) => record(parsed(line)))
85
- .filter((value) => value["type"] === "result")
86
- .map((value) => count(value["total_cost_usd"]));
87
- return costs.at(-1) ?? null;
88
- }
89
91
  function concise(items) {
90
92
  return items.map((item, index) => {
91
93
  const before = items[index - 1];
@@ -103,8 +105,9 @@ function shortened(run) {
103
105
  turns: run.turns.map((turn) => ({
104
106
  ...turn,
105
107
  activity: concise(turn.activity.map((item) => {
106
- const cost = item.costUsd;
107
- const spent = cost === undefined ? "" : ` (cost ${cost.toFixed(2)} USD)`;
108
+ const spent = item.kind === "result"
109
+ ? ` (cost ${priceText(item.costUsd, turn.estimatedCostUsd)})`
110
+ : "";
108
111
  return { kind: item.kind, text: `${cut(item.text)}${spent}` };
109
112
  })).slice(-activityLimit),
110
113
  })),
@@ -118,14 +121,26 @@ export function turnOf(records) {
118
121
  const report = record(parsed(text(result["result"])));
119
122
  return {
120
123
  key: records.key,
124
+ operationId: text(started["operationId"]),
125
+ usage: usageOf(report["usage"]),
121
126
  runId: text(operation[2]),
122
127
  target: text(started["target"]),
123
128
  branch: text(started["branch"]),
124
129
  worktree: text(started["worktree"]),
125
130
  startedAt: text(started["startedAt"]),
126
131
  outcome: records.result === null ? "in work" : outcome,
127
- costUsd: costOf(records.lines),
128
- activity: records.lines.flatMap(activityOf).slice(-activityLimit - 1),
132
+ costUsd: priceOf(report["costUsd"]),
133
+ estimatedCostUsd: priceOf(report["estimatedCostUsd"]),
134
+ activity: records.lines
135
+ .flatMap(activityOf)
136
+ .map((item) => item.kind === "result"
137
+ ? {
138
+ kind: item.kind,
139
+ text: item.text,
140
+ ...priceFields(report["costUsd"]),
141
+ }
142
+ : item)
143
+ .slice(-activityLimit - 1),
129
144
  endedAt: stamp(result["endedAt"]),
130
145
  activeAt: records.activeAt ?? null,
131
146
  commits: texts(report["commits"]),
@@ -146,54 +161,6 @@ export function decisionCommands(decision, client) {
146
161
  deny: `${client} deny ${decisionId} --text "<feedback>" ${binding}`,
147
162
  };
148
163
  }
149
- function githubOf(records) {
150
- const started = record(records.started);
151
- const operation = list(parsed(text(started["operationId"])));
152
- const result = record(records.result);
153
- const completed = result["outcome"] === "completed";
154
- return {
155
- runId: text(operation[2]),
156
- round: Number(operation[1]),
157
- method: text(started["method"]),
158
- report: completed ? record(parsed(text(result["result"]))) : {},
159
- };
160
- }
161
- function pullRequestOf(results) {
162
- const opened = results
163
- .filter((item) => item.method === "open-pr")
164
- .map((item) => item.report)
165
- .findLast((report) => text(report["url"]).startsWith("https://"));
166
- if (opened === undefined)
167
- return null;
168
- return { number: Number(opened["number"]), url: text(opened["url"]) };
169
- }
170
- function reviewsOf(results) {
171
- return results
172
- .filter((item) => item.method === "wait-review")
173
- .flatMap(({ round, report }) => {
174
- const score = count(report["score"]);
175
- if (score === null)
176
- return [];
177
- return [{ round, commit: text(report["commit"]), score }];
178
- });
179
- }
180
- function mergeCommitOf(results) {
181
- const merged = results
182
- .filter((item) => item.method === "merge")
183
- .map((item) => stamp(item.report["merged"]));
184
- return merged.find((commit) => commit !== null) ?? null;
185
- }
186
- function pullOf(inputs, runId) {
187
- const results = inputs.githubRecords
188
- .map(githubOf)
189
- .filter((item) => item.runId === runId)
190
- .toSorted((first, second) => first.round - second.round);
191
- return {
192
- pullRequest: pullRequestOf(results),
193
- reviews: reviewsOf(results),
194
- mergeCommit: mergeCommitOf(results),
195
- };
196
- }
197
164
  function feedbackOf(value) {
198
165
  const fields = record(value);
199
166
  return {
@@ -209,21 +176,43 @@ function eventOf(value) {
209
176
  type: text(fields["type"]),
210
177
  };
211
178
  }
179
+ function outcomeOf(value) {
180
+ const fields = record(value);
181
+ const { promotionId, ticketStatus, issueUpdate } = fields;
182
+ return {
183
+ outcome: stamp(fields["kind"]),
184
+ ...(isPromotionId(promotionId) ? { promotionId } : {}),
185
+ ...(isTicketStatus(ticketStatus) ? { ticketStatus } : {}),
186
+ ...(issueUpdate === "failed" ? { issueUpdate } : {}),
187
+ };
188
+ }
212
189
  function runOf(inputs, value) {
213
190
  const status = record(record(value)["status"]);
214
191
  const runId = text(status["runId"]);
215
192
  const workflowId = text(status["workflowId"]);
216
193
  const budget = record(status["budget"]);
217
194
  const ticketId = text(status["ticketId"]);
195
+ const issue = workflowId === "develop-issue"
196
+ ? establishedIssue(inputs.repository ?? null, status["issueIdentity"])
197
+ : null;
218
198
  return {
219
199
  runId,
220
200
  workflowId,
201
+ parentRunId: stamp(status["parentRunId"]),
202
+ chainId: stamp(status["chainId"]) ?? runId,
203
+ childRunIds: texts(status["childRunIds"]),
221
204
  ticketId: ticketId === "" ? null : ticketId,
205
+ ...(issue === null ? {} : { issueIdentity: issue }),
222
206
  title: stamp(status["title"]),
223
- outcome: stamp(record(status["outcome"])["kind"]),
207
+ ...outcomeOf(status["outcome"]),
224
208
  latestAt: stamp(status["latestAt"]),
225
- claudeCostUsd: count(status["claudeCostUsd"]) ?? 0,
226
- currentStep: currentStepOf(list(record(value)["events"]), workflowId),
209
+ claudeCostUsd: priceOf(status["claudeCostUsd"]),
210
+ estimatedCostUsd: priceOf(status["estimatedCostUsd"]),
211
+ usage: usageOf(status["usage"]),
212
+ currentStep: currentStepOf(list(record(value)["events"]), {
213
+ workflowId,
214
+ stages: stagesFor(inputs.workflowStages, workflowId),
215
+ }),
227
216
  round: roundOf(list(record(value)["events"])),
228
217
  state: text(status["state"]),
229
218
  code: text(record(status["reason"])["code"]),
@@ -232,13 +221,16 @@ function runOf(inputs, value) {
232
221
  limit: count(budget["limit"]),
233
222
  decisions: inputs.decisions
234
223
  .filter((decision) => record(decision)["runId"] === runId)
235
- .map((decision) => decisionCommands(decision, inputs.client)),
224
+ .map((decision) => decisionCommands(operationalDecision(record(decision), workflowId), inputs.client)),
236
225
  events: list(record(value)["events"]).map(eventOf),
237
- turns: inputs.turns.filter((turn) => turn.runId === runId),
226
+ turns: statusTurns(inputs.turns, status),
238
227
  phases: phasesOf(list(record(value)["events"]), workflowId),
239
228
  decision: decisionOf(list(record(value)["events"])),
240
229
  feedback: list(status["feedback"]).map(feedbackOf),
241
- ...pullOf(inputs, runId),
230
+ ...pullOf({
231
+ records: inputs.githubRecords,
232
+ repository: workflowId === "develop-issue" ? (inputs.repository ?? null) : null,
233
+ }, runId),
242
234
  };
243
235
  }
244
236
  function began(run) {
@@ -253,6 +245,8 @@ export function watchModel(inputs) {
253
245
  const workflows = boardsOf({ ...inputs, completionCandidates: candidates }, runs);
254
246
  return {
255
247
  workflows,
248
+ ...(inputs.queue === undefined ? {} : { queue: inputs.queue }),
249
+ workflowRefusals: workflowRefusals(inputs.workflowRefusals),
256
250
  startWorkflowId: startWorkflowOf(workflows),
257
251
  updatedAt: inputs.updatedAt,
258
252
  service: inputs.service,
@@ -1 +1 @@
1
- export declare function pageDocument(): string;
1
+ export declare function pageDocument(actionToken?: string | null): string;