toolroll 0.9.85 → 0.9.87

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 (220) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/agent-fence.d.ts +6 -2
  3. package/dist/agent-fence.js +3 -2
  4. package/dist/agents-board.d.ts +12 -0
  5. package/dist/agents-board.js +26 -0
  6. package/dist/approval-rules-ui.d.ts +4 -1
  7. package/dist/approval-rules-ui.js +31 -15
  8. package/dist/assignment-ui.js +1 -1
  9. package/dist/assignment.js +4 -3
  10. package/dist/backup-ui.d.ts +5 -4
  11. package/dist/backup-ui.js +65 -32
  12. package/dist/browser/terminal.css +1 -1
  13. package/dist/browser/terminal.js +57 -54
  14. package/dist/browser/workspace.css +2 -2
  15. package/dist/browser/workspace.js +35 -68
  16. package/dist/browser-crew.js +1 -1
  17. package/dist/browser-workspace.d.ts +363 -177
  18. package/dist/browser-workspace.js +10 -9
  19. package/dist/chat-actions.d.ts +11 -1
  20. package/dist/chat-actions.js +53 -1
  21. package/dist/chat-channel.js +1 -0
  22. package/dist/chat-controls.d.ts +1 -1
  23. package/dist/chat-controls.js +1 -1
  24. package/dist/chat-quiet.js +3 -2
  25. package/dist/chat-settings-page.d.ts +29 -9
  26. package/dist/chat-settings-page.js +59 -28
  27. package/dist/check-levels-ui.d.ts +2 -1
  28. package/dist/check-levels-ui.js +67 -58
  29. package/dist/claim.d.ts +3 -0
  30. package/dist/claim.js +4 -1
  31. package/dist/contracts/chat-actions.d.ts +9 -0
  32. package/dist/contracts/chat-actions.js +3 -0
  33. package/dist/contracts/cli.d.ts +43 -0
  34. package/dist/contracts/cli.js +4 -0
  35. package/dist/contracts/console-api.d.ts +4 -1
  36. package/dist/contracts/console-api.js +4 -1
  37. package/dist/contracts/flow-propose.d.ts +4 -1
  38. package/dist/contracts/flow-propose.js +1 -1
  39. package/dist/contracts/flow.d.ts +41 -0
  40. package/dist/contracts/flow.js +4 -2
  41. package/dist/contracts/lead-tools.d.ts +21 -0
  42. package/dist/contracts/lead-tools.js +2 -0
  43. package/dist/contracts/store-json.d.ts +2 -0
  44. package/dist/contracts/trigger-payloads.d.ts +20 -0
  45. package/dist/contracts/trigger-payloads.js +4 -0
  46. package/dist/coordinator.js +1 -0
  47. package/dist/credentials-ui.d.ts +5 -3
  48. package/dist/credentials-ui.js +58 -31
  49. package/dist/deploy-gate.js +4 -0
  50. package/dist/design/kit.css +685 -0
  51. package/dist/design/kit.d.ts +208 -0
  52. package/dist/design/kit.js +177 -0
  53. package/dist/design/tokens.css +19 -1
  54. package/dist/discord-settings.js +8 -4
  55. package/dist/dispatch.d.ts +3 -0
  56. package/dist/dispatch.js +4 -1
  57. package/dist/export-ui.d.ts +6 -4
  58. package/dist/export-ui.js +18 -8
  59. package/dist/flow-browser.d.ts +103 -0
  60. package/dist/flow-browser.js +204 -0
  61. package/dist/flow-changed.d.ts +14 -0
  62. package/dist/flow-changed.js +53 -0
  63. package/dist/flow-code.d.ts +5 -1
  64. package/dist/flow-code.js +4 -1
  65. package/dist/flow-engine.js +16 -0
  66. package/dist/flow-gallery-ui.d.ts +41 -4
  67. package/dist/flow-gallery-ui.js +95 -63
  68. package/dist/flow-live.js +9 -1
  69. package/dist/flow-pull-request.js +15 -7
  70. package/dist/flow-share.js +3 -1
  71. package/dist/flow-starters-ui.d.ts +3 -9
  72. package/dist/flow-starters-ui.js +12 -23
  73. package/dist/flow-steps.d.ts +9 -1
  74. package/dist/flow-steps.js +55 -5
  75. package/dist/flow-triggers.js +55 -0
  76. package/dist/flow-unattended-words.d.ts +20 -0
  77. package/dist/flow-unattended-words.js +24 -0
  78. package/dist/flow-unattended.d.ts +9 -5
  79. package/dist/flow-unattended.js +45 -23
  80. package/dist/flows-cli.d.ts +19 -0
  81. package/dist/flows-cli.js +54 -5
  82. package/dist/flows-ui.d.ts +12 -6
  83. package/dist/flows-ui.js +152 -32
  84. package/dist/flows.js +5 -2
  85. package/dist/integrations-ui.d.ts +6 -4
  86. package/dist/integrations-ui.js +34 -40
  87. package/dist/integrations.d.ts +2 -2
  88. package/dist/integrations.js +4 -4
  89. package/dist/kits-ui.d.ts +8 -10
  90. package/dist/kits-ui.js +29 -33
  91. package/dist/kits.js +1 -1
  92. package/dist/knowledge-ui.js +76 -19
  93. package/dist/lead-tools.js +35 -6
  94. package/dist/lead-voice.js +16 -5
  95. package/dist/ledger-view.d.ts +2 -0
  96. package/dist/ledger-view.js +41 -38
  97. package/dist/models-ui.d.ts +8 -3
  98. package/dist/models-ui.js +82 -59
  99. package/dist/monitoring-ui.d.ts +4 -2
  100. package/dist/monitoring-ui.js +44 -13
  101. package/dist/needs-you.d.ts +1 -1
  102. package/dist/needs-you.js +8 -0
  103. package/dist/operate-remote-arguments.js +1 -0
  104. package/dist/operate-remote.js +1 -1
  105. package/dist/operate.d.ts +1 -1
  106. package/dist/operate.js +249 -6
  107. package/dist/passwordless.d.ts +1 -1
  108. package/dist/passwordless.js +1 -1
  109. package/dist/people-audit-ui.d.ts +2 -1
  110. package/dist/people-audit-ui.js +25 -23
  111. package/dist/policy-ui.d.ts +3 -2
  112. package/dist/policy-ui.js +27 -21
  113. package/dist/policy.d.ts +3 -0
  114. package/dist/policy.js +14 -1
  115. package/dist/project-delete-ui.d.ts +4 -2
  116. package/dist/project-delete-ui.js +135 -126
  117. package/dist/project-delete.js +7 -3
  118. package/dist/project-knowledge.d.ts +10 -10
  119. package/dist/publish.d.ts +11 -0
  120. package/dist/publish.js +16 -3
  121. package/dist/pull-request-flow.js +35 -4
  122. package/dist/pull-request-ui.d.ts +2 -1
  123. package/dist/pull-request-ui.js +39 -28
  124. package/dist/recipe-ui.d.ts +4 -2
  125. package/dist/recipe-ui.js +14 -4
  126. package/dist/release-adapters.d.ts +3 -0
  127. package/dist/release-adapters.js +22 -2
  128. package/dist/release-cli.js +81 -9
  129. package/dist/release.d.ts +21 -0
  130. package/dist/release.js +40 -4
  131. package/dist/releases.js +1 -1
  132. package/dist/request-budget-ui.d.ts +4 -2
  133. package/dist/request-budget-ui.js +30 -19
  134. package/dist/result-follow-ups.js +4 -0
  135. package/dist/retention-ui.d.ts +4 -3
  136. package/dist/retention-ui.js +15 -12
  137. package/dist/scope.d.ts +8 -0
  138. package/dist/scope.js +13 -0
  139. package/dist/serve.d.ts +0 -1
  140. package/dist/serve.js +51 -7
  141. package/dist/server/agents-computers.d.ts +9 -0
  142. package/dist/server/agents-computers.js +67 -0
  143. package/dist/server/chat.js +20 -18
  144. package/dist/server/chrome.d.ts +1 -1
  145. package/dist/server/chrome.js +19 -9
  146. package/dist/server/console.css +31 -2
  147. package/dist/server/flows.js +79 -36
  148. package/dist/server/home.d.ts +35 -0
  149. package/dist/server/home.js +104 -0
  150. package/dist/server/http.d.ts +3 -0
  151. package/dist/server/http.js +5 -0
  152. package/dist/server/kit-standalone.d.ts +22 -0
  153. package/dist/server/kit-standalone.js +69 -0
  154. package/dist/server/kit.d.ts +405 -0
  155. package/dist/server/kit.js +361 -0
  156. package/dist/server/pages.d.ts +2 -1
  157. package/dist/server/pages.js +213 -174
  158. package/dist/server/people-tokens.js +26 -85
  159. package/dist/server/render-history.d.ts +51 -0
  160. package/dist/server/render-history.js +228 -0
  161. package/dist/server/render-new-task.d.ts +29 -0
  162. package/dist/server/render-new-task.js +96 -0
  163. package/dist/server/render-next.d.ts +63 -0
  164. package/dist/server/render-next.js +109 -0
  165. package/dist/server/render-pages.d.ts +35 -190
  166. package/dist/server/render-pages.js +154 -563
  167. package/dist/server/render-people.d.ts +24 -3
  168. package/dist/server/render-people.js +93 -72
  169. package/dist/server/render-projects.d.ts +103 -0
  170. package/dist/server/render-projects.js +247 -0
  171. package/dist/server/render-results.d.ts +5 -24
  172. package/dist/server/render-results.js +48 -185
  173. package/dist/server/render-settings.d.ts +5 -74
  174. package/dist/server/render-settings.js +102 -493
  175. package/dist/server/render-tasks.d.ts +43 -5
  176. package/dist/server/render-tasks.js +131 -91
  177. package/dist/server/route-table.js +31 -5
  178. package/dist/server/settings.js +276 -255
  179. package/dist/server/task-views.d.ts +8 -0
  180. package/dist/server/task-views.js +90 -2
  181. package/dist/server/tasks.d.ts +18 -0
  182. package/dist/server/tasks.js +148 -41
  183. package/dist/server/terminals.js +1 -1
  184. package/dist/server/welcome.d.ts +22 -0
  185. package/dist/server/welcome.js +69 -0
  186. package/dist/settings-groups-ui.d.ts +279 -0
  187. package/dist/settings-groups-ui.js +615 -0
  188. package/dist/settings-kit.d.ts +42 -6
  189. package/dist/settings-kit.js +69 -7
  190. package/dist/settings-proofs.js +1 -0
  191. package/dist/setup-guide.d.ts +2 -2
  192. package/dist/skills-ui.js +59 -7
  193. package/dist/slack-settings.js +12 -5
  194. package/dist/spend-ui.d.ts +45 -26
  195. package/dist/spend-ui.js +124 -56
  196. package/dist/sso-ui.d.ts +5 -2
  197. package/dist/sso-ui.js +63 -31
  198. package/dist/storage-ui.d.ts +3 -2
  199. package/dist/storage-ui.js +56 -50
  200. package/dist/store.d.ts +182 -5
  201. package/dist/store.js +512 -38
  202. package/dist/surface.js +23 -3
  203. package/dist/task-archive.d.ts +10 -0
  204. package/dist/task-archive.js +16 -0
  205. package/dist/task-control.d.ts +1 -1
  206. package/dist/teams-settings.d.ts +2 -1
  207. package/dist/teams-settings.js +24 -41
  208. package/dist/telegram-settings.d.ts +0 -4
  209. package/dist/telegram-settings.js +52 -25
  210. package/dist/tidy.d.ts +130 -0
  211. package/dist/tidy.js +503 -0
  212. package/dist/toolroll-update-ui.d.ts +8 -4
  213. package/dist/toolroll-update-ui.js +55 -45
  214. package/dist/tools-ui.d.ts +5 -3
  215. package/dist/tools-ui.js +107 -42
  216. package/dist/work-index.d.ts +24 -0
  217. package/dist/work-index.js +56 -30
  218. package/dist/workspace-ui.d.ts +1 -1
  219. package/dist/workspace-ui.js +8 -4
  220. package/package.json +1 -1
@@ -362,12 +362,6 @@ export declare function evidenceWords(kind: string): string;
362
362
  * orphaned run keeps saying what actually became of it. */
363
363
  export declare function runOutcomeBadge(run: Run, live: boolean): Html;
364
364
  export declare const runNoun: (run: Pick<Run, "role">) => string;
365
- export declare function runsPage(chrome: Chrome, rows: (Run & {
366
- taskId: string;
367
- })[], liveIds: ReadonlySet<number>, nextCursor: number | null, verdicts?: Map<number, {
368
- verdict: ProofVerdict;
369
- matrix?: CriterionMatrixRow[];
370
- }>, accepted?: ReadonlySet<number>): Screen;
371
365
  /** The evidence bundle (Priority 2): the closed machine-authored verdict,
372
366
  * the agent's proof (or why it cannot be shown), the plane's own re-run
373
367
  * check, and every validated screenshot — each row labeled by source so
@@ -732,24 +726,11 @@ export declare function resultPrimaryAction(detail: ResultDetail, o: ResultPanel
732
726
  /** The run's route provenance in plain words (v47): the exact agent it
733
727
  * spent as, in which role, and how that agent was chosen. */
734
728
  export declare function runAgentWords(route: RouteStamp | null | undefined): string | null;
735
- export declare function runFactsRows(run: Run, taskId: string, live: boolean, route?: RouteStamp | null): Html;
736
- export declare function runPage(chrome: Chrome, run: Run, taskId: string, artifacts: Artifact[], terminal?: TerminalDiffView | null, notes?: {
737
- id: number;
738
- author: string;
739
- note: string;
740
- createdAt: string;
741
- }[], csrf?: string, comments?: DiffComment[], ciRepair?: {
742
- pr: number;
743
- } | null, liveScript?: string, peekable?: boolean, running?: boolean, editor?: {
744
- worktree: string;
745
- } | null, editorToggle?: {
746
- on: boolean;
747
- } | null, noted?: boolean, structuredHandoff?: StructuredHandoffView | null, proofBundle?: ProofBundleView | null, route?: RouteStamp | null, publicationRow?: Publication | null, review?: ReviewFacts | null, result?: {
748
- detail: ResultDetail;
749
- tab: ResultTab;
750
- user: string;
751
- requestToken: string;
752
- } | null, sourceDigest?: string | null): Screen;
729
+ /** A run's outcome in the kit's tones: built or no change finished, failed or refused failed, anything else neutral. */
730
+ export declare function runOutcomeChip(run: Pick<Run, "outcome" | "reason" | "role">, live: boolean): Html;
731
+ /** A run's facts as the kit's facts list: how it ended, who ran it where, when, what it cost and where it ended. The
732
+ * live poll (`?fragment=facts`) swaps the same list in place. (The page's header names the run and its task.) */
733
+ export declare function runFactsRows(run: Run, _taskId: string, live: boolean, route?: RouteStamp | null): Html;
753
734
  /**
754
735
  * Click-to-prefill (arc 6, finding 4): client-side FORM mutation, named as
755
736
  * such — normal viewing stays the default; "Annotate" reveals line pins.
@@ -27,6 +27,7 @@ import { createHash, randomBytes } from "node:crypto";
27
27
  import { buildsViews, oneLineOf, projectChip, safePrUrl, screen, sentenceCase, strokeIcon, when, whenTime } from "./chrome.js";
28
28
  import { chatResultHref, reviewHref, taskChatHref, taskHref } from "./http.js";
29
29
  import { attributes, html, htmlString, joinHtml, postForm, reauth } from "../html.js";
30
+ import { chip, facts } from "./kit.js";
30
31
  import { publicationFactsOf, statusActionHref, statusLineHtml } from "./render-tasks.js";
31
32
  export const RUNS_PAGE = 50;
32
33
  /**
@@ -948,27 +949,6 @@ export function runOutcomeBadge(run, live) {
948
949
  : html `<span class="badge badge-${run.outcome ?? "cut"}">${sentenceCase(run.outcome ?? "never finished")}</span>`;
949
950
  }
950
951
  export const runNoun = (run) => run.role === "planner" ? "plan" : run.role === "reviewer" ? "review" : run.role === "scout" ? "report" : "build";
951
- export function runsPage(chrome, rows, liveIds, nextCursor, verdicts = new Map(), accepted = new Set()) {
952
- const list = rows.length === 0
953
- ? html `<p class="meta">No builds yet \u2014 they appear once an approved task is dispatched.</p>`
954
- : joinHtml(rows
955
- .map(run => {
956
- const verdict = verdicts.get(run.id)?.verdict ?? null;
957
- const needsVerification = (verdict === "short" || verdict === "refuted") && !accepted.has(run.id)
958
- ? html ` <span class="badge badge-failed">${verdict === "refuted" ? "conflicting evidence" : "missing evidence"}</span>`
959
- : "";
960
- return html `<p class="row"><a href="/r/${run.id}" class="mono">#${run.id}</a> <a href="${taskHref(run.taskId)}" class="mono">${run.taskId}</a> ${[
961
- runOutcomeBadge(run, liveIds.has(run.id)),
962
- run.qualityMode === "strict" ? html ` <span class="badge">Strict review</span>` : "",
963
- needsVerification,
964
- criterionMatrixSummary(verdicts.get(run.id)?.matrix ?? []),
965
- run.provider === "claude" ? "" : html ` <span class="meta mono">${run.provider}</span>`,
966
- html `<span class="right meta mono">${whenTime(run.startedAt)}${run.providerStartedAt === null && run.tokensIn === null && run.tokensOut === null && run.costUsd === null ? "" : ` \u00b7 ${runCostWords(run, liveIds.has(run.id))}`}</span></p>`,
967
- ]}`;
968
- }), "\n");
969
- const older = nextCursor === null ? "" : html `<p><a href="/runs?before=${nextCursor}">older →</a></p>`;
970
- return screen("builds", joinHtml([html `<h1>Builds <a class="badge" href="/peek">Peek at the live ones \u2192</a></h1>`, buildsViews("builds"), html `<p class="hint">one build = one attempt by an agent to complete a task, on its own branch</p>`, list, older], "\n"), { chrome });
971
- }
972
952
  /** What a scout found, each with its link and picture, then any screenshot no item shows. Every link is the cited
973
953
  * http(s) address the report parser admitted; every picture is served from verified evidence. */
974
954
  export function reportItemsHtml(items, shots, runId) {
@@ -2096,173 +2076,56 @@ export function runAgentWords(route) {
2096
2076
  : "recommended";
2097
2077
  return `${route.provider}${route.model === null ? "" : ` · ${route.model}`} as the ${role} — ${how}`;
2098
2078
  }
2099
- export function runFactsRows(run, taskId, live, route = null) {
2100
- const facts = [
2101
- ["task", taskId, true],
2102
- ["role", run.role],
2103
- ["agent", runAgentWords(route)],
2104
- ["quality", qualityModeTitle(run.qualityMode ?? "default")],
2105
- ["outcome", live ? "running" : (run.outcome ?? "never finished")],
2106
- ["phase", live && run.phase !== null ? phaseWords(run.phase) : null],
2107
- ["reason", run.reason === null ? null : isInternalErrorReason(run.reason) ? "stopped with an internal error (below)" : reasonWords(run.reason)],
2108
- ["runner", run.runner, true],
2109
- ["branch", run.branch, true],
2110
- ["model", run.model, true],
2111
- ["starting point", run.baseRevision === null ? null : `${run.baseRevision.slice(0, 12)}…`, true],
2112
- ["ended at", run.headRevision === null ? null : `${run.headRevision.slice(0, 12)}…`, true],
2113
- ["started", when(run.startedAt), true],
2114
- ["finished", when(run.finishedAt), true],
2115
- ["provider started", when(run.providerStartedAt), true],
2116
- ["tokens in", run.tokensIn === null ? null : run.tokensIn.toLocaleString(), true],
2117
- ["tokens out", run.tokensOut === null ? null : run.tokensOut.toLocaleString(), true],
2118
- // Auth is part of the economic fact: Claude's subscription harness
2119
- // reports an API-price equivalent, not a separate API-key charge.
2120
- [
2121
- "usage",
2122
- run.providerStartedAt === null && run.tokensIn === null && run.tokensOut === null && run.costUsd === null ? null : runCostWords(run, live),
2123
- run.costUsd !== null && run.authMode !== "subscription",
2124
- ],
2079
+ /** A run's outcome in the kit's tones: built or no change finished, failed or refused failed, anything else neutral. */
2080
+ export function runOutcomeChip(run, live) {
2081
+ if (live)
2082
+ return chip("Running", "live");
2083
+ if (run.role === "planner" && run.reason === "plan-drafted")
2084
+ return chip("Planned", "done");
2085
+ if (run.reason === "interrupted")
2086
+ return chip("Interrupted");
2087
+ const word = sentenceCase(run.outcome ?? "never finished");
2088
+ return chip(word, run.outcome === "built" || run.outcome === "no-change" ? "done" : run.outcome === "failed" || run.outcome === "refused" ? "failed" : "");
2089
+ }
2090
+ /** How long a finished run ran, in whole minutes (seconds under one). */
2091
+ function ranWords(run) {
2092
+ if (run.startedAt === null || run.finishedAt === null)
2093
+ return null;
2094
+ const ms = Date.parse(run.finishedAt) - Date.parse(run.startedAt);
2095
+ if (!Number.isFinite(ms) || ms < 0)
2096
+ return null;
2097
+ return ms < 60_000 ? `${Math.max(1, Math.round(ms / 1000))} s` : ms < 3_600_000 ? `${Math.round(ms / 60_000)} min` : `${Math.floor(ms / 3_600_000)} h ${Math.round((ms % 3_600_000) / 60_000)} min`;
2098
+ }
2099
+ /** A run's facts as the kit's facts list: how it ended, who ran it where, when, what it cost and where it ended. The
2100
+ * live poll (`?fragment=facts`) swaps the same list in place. (The page's header names the run and its task.) */
2101
+ export function runFactsRows(run, _taskId, live, route = null) {
2102
+ const mono = (value) => html `<span class="k-mono">${value}</span>`;
2103
+ const rows = [
2104
+ ["Outcome", runOutcomeChip(run, live)],
2105
+ ["Step", live && run.phase !== null ? phaseWords(run.phase) : null],
2106
+ ["Reason", run.reason === null ? null : isInternalErrorReason(run.reason) ? "stopped with an internal error (below)" : reasonWords(run.reason)],
2107
+ ["Agent", runAgentWords(route) ?? `${run.provider}${run.model === null ? "" : ` · ${run.model}`}`],
2108
+ ["Quality", qualityModeTitle(run.qualityMode ?? "default")],
2109
+ ["Computer", run.runner === null ? null : mono(run.runner)],
2110
+ ["Branch", run.branch === null ? null : mono(run.branch)],
2111
+ ["Started", run.startedAt === null ? null : whenTime(run.startedAt)],
2112
+ ["Agent started", run.providerStartedAt === null ? null : whenTime(run.providerStartedAt)],
2113
+ ["Finished", run.finishedAt === null ? null : whenTime(run.finishedAt)],
2114
+ live && run.startedAt !== null ? ["Elapsed", html `<time class="k-num" data-elapsed-since="${run.startedAt}"></time>`] : ["Ran", ranWords(run)],
2115
+ ["Starting point", run.baseRevision === null ? null : mono(run.baseRevision.slice(0, 12))],
2116
+ ["Commit", run.headRevision === null ? null : mono(run.headRevision.slice(0, 12))],
2117
+ ["Tokens", run.tokensIn === null && run.tokensOut === null ? null : html `<span class="k-num">${(run.tokensIn ?? 0).toLocaleString()} in · ${(run.tokensOut ?? 0).toLocaleString()} out</span>`],
2118
+ // Auth is part of the economic fact: Claude's subscription harness reports an API-price equivalent, not a separate
2119
+ // API-key charge.
2120
+ ["Spend", run.providerStartedAt === null && run.tokensIn === null && run.tokensOut === null && run.costUsd === null ? null : runCostWords(run, live)],
2125
2121
  ];
2126
- const elapsed = live && run.startedAt !== null
2127
- ? html `<p class="row"><span class="meta" style="min-width:8.5rem">elapsed</span> <time class="mono" data-elapsed-since="${run.startedAt}"></time></p>`
2128
- : "";
2129
- return html `${joinHtml(facts
2130
- .filter((fact) => fact[1] !== null && fact[1] !== "")
2131
- .map(([label, value, mono]) => html `<p class="row"><span class="meta" style="min-width:8.5rem">${label}</span> <span${mono === true ? html ` class="mono"` : ""}>${value}</span></p>`), "\n")}${elapsed}${
2122
+ const shown = rows.filter((row) => row[1] !== null && row[1] !== "");
2123
+ return html `${facts(shown)}${
2132
2124
  // Machine output (a stack trace, a path, "Error:" text) reads as an internal error everywhere else; its detail is here, as recorded.
2133
2125
  run.reason !== null && isInternalErrorReason(run.reason)
2134
- ? html `<p class="row"><span class="meta" style="min-width:8.5rem">recorded error</span></p><pre class="mono" id="run-reason-detail" style="white-space:pre-wrap;overflow-wrap:anywhere">${run.reason}</pre>`
2126
+ ? html `<p class="k-note">Recorded error</p><pre class="k-mono h-pre" id="run-reason-detail">${run.reason}</pre>`
2135
2127
  : ""}`;
2136
2128
  }
2137
- export function runPage(chrome, run, taskId, artifacts, terminal = null, notes = [], csrf = "", comments = [], ciRepair = null, liveScript, peekable = false, running = false, editor = null, editorToggle = null, noted = false, structuredHandoff = null, proofBundle = null, route = null, publicationRow = null, review = null, result = null, sourceDigest = null) {
2138
- const rows = runFactsRows(run, taskId, running, route);
2139
- // The live peek region (A2): the poller fills it only on a serve that
2140
- // asserted its runner. Without the assertion the section still appears
2141
- // for a running build and says honestly why it is empty \u2014 a page that
2142
- // silently lacked the region while the task screen promised a live view
2143
- // read as broken (round-4, A1).
2144
- const peek = !running
2145
- ? ""
2146
- : peekable
2147
- ? html `<h2>What is changing right now</h2><div id="run-peek"><p class="meta">Watching\u2026 the first look lands within 15 seconds</p></div><p class="meta" id="run-peek-stamp"></p>`
2148
- : html `<h2>What is changing right now</h2><p class="meta">The live file view is off \u2014 start serve with ${"--runner <name>"} naming this machine's worker, and it appears here</p>`;
2149
- // The live transcript (arc 1): the agent's own words, streamed to a file
2150
- // beside the run and polled as raw text. Honesty stated on the surface:
2151
- // this is display only, and the machine running the agent could alter it.
2152
- const transcript = !running
2153
- ? ""
2154
- : !peekable
2155
- ? html `<h2>What the agent is saying</h2><p class="meta">The live transcript is off \u2014 start serve with ${"--runner <name>"} naming this machine's worker, and it appears here</p>`
2156
- : run.provider !== "claude"
2157
- ? html `<h2>What the agent is saying</h2><p class="meta">The live transcript needs the claude harness for now \u2014 this build runs on ${run.provider}</p>`
2158
- : html `<h2>What the agent is saying</h2><p class="meta">Display only \u2014 this is not evidence, and the machine running the agent could alter it</p><pre id="live-transcript" class="mono" style="max-height:24rem;overflow:auto;white-space:pre-wrap"></pre><p class="meta" id="live-transcript-state"></p>`;
2159
- const resultSummary = structuredHandoff?.conclusion ?? run.handoff;
2160
- const resultList = (label, items) => items.length === 0
2161
- ? ""
2162
- : html `<div class="result-section"><strong>${label}</strong><ul>${items.map(one => html `<li>${one}</li>`)}</ul></div>`;
2163
- const handoff = resultSummary === null
2164
- ? ""
2165
- : html `<section class="card result-card"><h2>Result</h2><p class="recap">${resultSummary}</p>${structuredHandoff === null
2166
- ? ""
2167
- : [resultList("completed", structuredHandoff.changes),
2168
- resultList("checks reported by the agent", structuredHandoff.verification),
2169
- resultList("follow-up", structuredHandoff.followUps)]}</section>`;
2170
- const evidence = artifacts.length === 0
2171
- ? ""
2172
- : html `<details class="evidence-files"><summary>Saved files <span class="meta">(${artifacts.length})</span></summary><ul>${artifacts
2173
- .map(artifact => html `<li><a href="/r/${run.id}/evidence/${artifact.id}">${evidenceWords(artifact.kind)}</a> <span class="meta" title="${artifact.bytesStored} bytes stored${artifact.truncated ? ` of ${artifact.bytesOriginal}` : ""}">${artifact.truncated ? `${humanBytes(artifact.bytesStored)} of ${humanBytes(artifact.bytesOriginal)} · partial` : humanBytes(artifact.bytesStored)}</span></li>`)}</ul></details>`;
2174
- // Review comments on the immutable terminal diff (M6.8): listed, added,
2175
- // and sealed into ONE unapproved revision task. The seal is deliberately
2176
- // plain — the ceremony lives on the revision task's approval screen,
2177
- // which restates the batch; this button only creates the unapproved task.
2178
- const hasTerminalDiff = terminal !== null && terminal.patch !== null && !("problem" in terminal.patch);
2179
- const commentPathWords = (path, line) => {
2180
- const shown = `${path}${line === null ? "" : `:${line}`}`;
2181
- const href = editor === null ? null : editorFileHref(editor.worktree, path, line);
2182
- return href === null
2183
- ? html `<span class="mono">${shown}</span> `
2184
- : html `<a class="mono" href="${href}">${shown}</a> `;
2185
- };
2186
- const commentRows = comments
2187
- .map(one => html `<div class="diff-comment"><span class="diff-comment-pin" aria-hidden="true"></span><p>${one.path === null ? "" : commentPathWords(one.path, one.line)}${one.note}</p><span class="meta">${one.author} · ${whenTime(one.createdAt)}</span></div>`);
2188
- const commentForm = csrf === "" || !hasTerminalDiff
2189
- ? null
2190
- : postForm(`/r/${run.id}/comment`, html `<div class="diff-comment-target"><label>File<input type="text" name="path" placeholder="select a line above" aria-label="file" class="mono"></label><label>Line<input type="text" name="line" placeholder="—" aria-label="line" inputmode="numeric"></label></div><label>Change requested<textarea name="note" rows="2" maxlength="${LIMITS.note}" placeholder="Explain what should change and why" aria-label="review comment" aria-describedby="comment-note-limit"${noted ? " autofocus" : ""}></textarea></label><span class="meta diff-comment-limit" id="comment-note-limit">up to ${LIMITS.note} characters</span><button type="submit">Add annotation</button>`, { attrs: { class: "card diff-comment-form", id: "comment-form" } });
2191
- // The device-side half of the editor-link activation (arc 6, finding 1):
2192
- // rendered only when the server capability exists and this run belongs
2193
- // to this machine's runner — the person at the browser flips it.
2194
- const editorToggleForm = editorToggle === null || csrf === ""
2195
- ? ""
2196
- : postForm("/session/editor-links", html `<button type="submit">${editorToggle.on ? "stop opening files in VS Code from this device" : "open files in VS Code from this device"}</button><span class="meta"> — only useful when this browser runs on the machine that holds the worktrees</span>`, { attrs: { class: "row" }, returnTo: `/r/${run.id}`, hidden: { on: editorToggle.on ? "0" : "1" } });
2197
- const reviseForm = csrf === "" || comments.length === 0
2198
- ? ""
2199
- : postForm(`/r/${run.id}/revise`, html `${revisionSealFields(comments, sourceDigest)}<div><strong>${comments.length} annotation${comments.length === 1 ? "" : "s"} ready</strong><span class="meta">Creates one revision carrying this exact batch. You review its scope before anything builds.</span></div><button type="submit">Create revision from annotations</button>`, { attrs: { class: "card revision-from-comments" } });
2200
- // CI repair, suggestion-first (M8.18): the observed red episode earns a
2201
- // button; the button drafts ONE unapproved task; a person approves it.
2202
- const repairCard = ciRepair === null || csrf === ""
2203
- ? ""
2204
- : html `<div class="card"><p><strong>CI is failing on PR #${ciRepair.pr}</strong> <span class="meta">observed by the episode watcher</span></p>${postForm(`/r/${run.id}/draft-repair`, html `<button type="submit">Draft a repair task</button><span class="meta"> — one unapproved task; you approve its scope before anything builds</span>`)}</div>`;
2205
- const reviewCard = html `${commentRows.length === 0 && commentForm === null ? "" : html `<h2 id="review">Review and revise</h2><p class="meta">Annotate the diff above. When the batch is ready, create one scoped revision task from it.</p>${commentRows.length === 0 ? "" : html `<div class="diff-comments">${joinHtml(commentRows, "\n")}</div>`}${commentForm}${reviseForm}${editorToggleForm}`}${repairCard}`;
2206
- const noteRows = joinHtml(notes.map(one => html `<p class="row"><span class="meta">${one.author} · ${whenTime(one.createdAt)}</span> ${one.note}</p>`), "\n");
2207
- const noteForm = csrf === ""
2208
- ? null
2209
- : postForm(`/r/${run.id}/note`, html `<input type="text" name="note" placeholder="a note for whoever reads this run next" aria-label="run note" style="width:100%;max-width:28rem"><button type="submit">Add note</button>`, { attrs: { class: "row" } });
2210
- const notesCard = notes.length === 0 && noteForm === null ? "" : html `<h2>Operator notes</h2>${noteRows}${noteForm}`;
2211
- // Outcome first (UI polish 2026-09-13): a finished build leads with its
2212
- // result, proof, and diff; the machine facts fold under "Build details".
2213
- // A live build keeps the facts open — they are what a watcher polls.
2214
- const facts = running
2215
- ? html `<div id="run-facts">${rows}</div><p class="meta" id="run-facts-stamp"></p>`
2216
- : html `<details class="run-facts-details"><summary>Run record<span class="meta">${[run.runner, run.provider, run.outcome ?? "never finished"].join(" · ")}</span></summary><div id="run-facts">${rows}</div></details>`;
2217
- // A finished result (package 3) is the ONE shared panel — Summary /
2218
- // Changes / Checks with Request changes beside it — the same markup the
2219
- // review cockpit and the chat's result view render. Every other run
2220
- // (live, failed, interrupted) keeps its record-by-record page.
2221
- const resultPanel = result === null
2222
- ? null
2223
- : resultPanelHtml(result.detail, {
2224
- place: "run",
2225
- tab: result.tab,
2226
- csrf,
2227
- user: result.user,
2228
- noted,
2229
- requestToken: result.requestToken,
2230
- hrefFor: one => (one === "summary" ? `/r/${run.id}` : `/r/${run.id}?tab=${one}`),
2231
- returnTo: `/r/${run.id}?record=1`,
2232
- back: null,
2233
- });
2234
- return screen(`build #${run.id}`, joinHtml([
2235
- html `<h1>Build #${run.id} <span class="meta"><a href="${taskHref(taskId)}">${taskId}</a></span></h1>`,
2236
- running ? facts : "",
2237
- transcript,
2238
- peek,
2239
- ...(resultPanel === null
2240
- ? [
2241
- handoff,
2242
- evidenceBundleCard(proofBundle, run, publicationFactsOf(publicationRow), review),
2243
- terminal === null ? "" : terminalDiffCard(terminal, run.id, editor, commentForm !== null),
2244
- reviewCard,
2245
- ]
2246
- : [resultPanel, editorToggleForm]),
2247
- running ? "" : facts,
2248
- evidence,
2249
- notesCard,
2250
- ], "\n"), {
2251
- chrome,
2252
- // One composed functional script (arc 4 contract): the pollers when the
2253
- // run is live, the result panel's tabs/draft script or the legacy
2254
- // comment prefill when a form exists. Neither fetches — they earn
2255
- // neither connect-src nor the noscript refresh.
2256
- ...(liveScript === undefined && commentForm === null && resultPanel === null
2257
- ? {}
2258
- : {
2259
- functional: {
2260
- script: (liveScript ?? "") + (resultPanel !== null ? RESULT_REVIEW_SCRIPT : commentForm === null ? "" : prefillScript()),
2261
- fetches: liveScript !== undefined,
2262
- },
2263
- }),
2264
- });
2265
- }
2266
2129
  /**
2267
2130
  * Click-to-prefill (arc 6, finding 4): client-side FORM mutation, named as
2268
2131
  * such — normal viewing stays the default; "Annotate" reveals line pins.
@@ -2302,10 +2165,10 @@ export function reviewEvidenceScript() {
2302
2165
  * finding 15). */
2303
2166
  export function runFactsFragment(run, taskId, live, route = null) {
2304
2167
  if (run.outcome !== null) {
2305
- return html `<p class="meta" data-region-stop>finished — <a href="/r/${run.id}">reload for the final record</a></p>`;
2168
+ return html `<p class="k-note" data-region-stop>It finished. <a class="k-link" href="/r/${run.id}">Reload for the final record</a></p>`;
2306
2169
  }
2307
2170
  if (!live) {
2308
- return html `<p class="meta" data-region-stop>this build stopped without finishing — <a href="/r/${run.id}">reload for the record</a></p>`;
2171
+ return html `<p class="k-note" data-region-stop>It stopped without finishing. <a class="k-link" href="/r/${run.id}">Reload for the record</a></p>`;
2309
2172
  }
2310
2173
  return runFactsRows(run, taskId, live, route);
2311
2174
  }
@@ -1,14 +1,10 @@
1
- import { type BrandIconId } from "../brand-icons.js";
2
- import { type BrowserPhoneCard, type BrowserSettingsGroup, type BrowserSettingsView, type BrowserUpdates } from "../browser-workspace.js";
3
1
  import { type Gap } from "../gaps.js";
4
2
  import { type LeadIdentity } from "../lead-identity.js";
5
- import { type ProviderConnection } from "../provider-connection.js";
6
- import { type QualityMode } from "../quality.js";
7
- import { type UnattendedPermissionMode } from "../scope.js";
8
- import { type Capability, type PushSubscription, type ResultScreenshots, type Store } from "../store.js";
3
+ import { type Capability, type Store } from "../store.js";
9
4
  import { type TokenSource } from "../telegram.js";
10
5
  import { type Chrome, type Screen } from "./chrome.js";
11
6
  import { type Html } from "../html.js";
7
+ import { type WorkerFacts } from "../settings-groups-ui.js";
12
8
  /** What the lead's settings form shows beside the saved configuration: where keys come from (never the keys), the
13
9
  * live model lists, and where saving goes back to. */
14
10
  export type LeadFormFacts = {
@@ -34,6 +30,7 @@ export declare function leadSettingsHtml(data: {
34
30
  signedIn: string | null;
35
31
  command: string;
36
32
  said: string | null;
33
+ problem?: string | null;
37
34
  saved?: boolean;
38
35
  identity?: LeadIdentity;
39
36
  about?: string[];
@@ -52,7 +49,7 @@ export declare function leadSettingsHtml(data: {
52
49
  paused: boolean;
53
50
  }[];
54
51
  }): Html;
55
- /** The lead's full settings (Settings → Lead → Advanced): provider, model, limits and a direct API key. */
52
+ /** The lead's full settings (Settings → Lead → Advanced): provider, model, limits and the lead's own key. */
56
53
  export declare function leadConfigForm(current: import("../store.js").ChatConfig | null, data: LeadFormFacts): Html;
57
54
  export declare function capsPage(chrome: Chrome, caps: Capability[] | null, gaps: Gap[], repo: string, now?: Date): Screen;
58
55
  /** Whether the bot's saved delivery state reports a problem (no new check runs). */
@@ -65,55 +62,8 @@ export declare function notificationProjects(store: Store, account: string): {
65
62
  name: string;
66
63
  muted: boolean;
67
64
  }[];
68
- export declare function settingsPage(chrome: Chrome, existing: TokenSource | null, hasEnv: boolean, csrf: string, problem: string | null, messaging?: {
69
- channel: string | null;
70
- implicit: boolean;
71
- configured: string[];
72
- legacyWarning?: string;
73
- } | null, push?: {
74
- available: boolean;
75
- devices: PushSubscription[];
76
- } | null, providerKeys?: {
77
- provider: string;
78
- envName: string;
79
- set: boolean;
80
- updatedAt: string | null;
81
- ambient: boolean;
82
- mode: "subscription" | "api-key";
83
- subscriptionCapable: boolean;
84
- connection?: ProviderConnection;
85
- }[] | null, digest?: {
86
- everyMs: number | null;
87
- lastSentAt: string | null;
88
- held: number;
89
- } | null, permissionDefault?: {
90
- mode: UnattendedPermissionMode;
91
- updatedAt: string | null;
92
- updatedBy: string | null;
93
- canManage: boolean;
94
- } | null, qualityDefault?: {
95
- mode: QualityMode;
96
- updatedAt: string | null;
97
- updatedBy: string | null;
98
- canManage: boolean;
99
- } | null, email?: NonNullable<BrowserSettingsView["email"]> | null, telegramDelivery?: string | null, workers?: NonNullable<BrowserSettingsView["workers"]> | null, updates?: BrowserUpdates | null, firstResult?: string | null, chatNotices?: {
100
- mode: "quiet" | "all";
101
- digestAt: string | null;
102
- screenshots?: ResultScreenshots;
103
- projects?: {
104
- repo: string;
105
- name: string;
106
- muted: boolean;
107
- }[];
108
- } | null, telegramFailing?: boolean, phone?: BrowserPhoneCard | null): Screen;
109
- /** The phone setup for the server-rendered Settings page: the same choices as the React card. */
110
- export declare function phoneSetupHtml(phone: BrowserPhoneCard): Html;
111
65
  /** Every worker that is not retired: its capacity, and the tasks it holds now. */
112
- export declare function settingsWorkers(store: Store, now: Date): NonNullable<BrowserSettingsView["workers"]>;
113
- /** The page's workers section, for browsers without the app script. */
114
- export declare function workersCard(workers: NonNullable<BrowserSettingsView["workers"]> | null): Html;
115
- /** Settings → Updates, for browsers without the app script. */
116
- export declare function updatesCard(updates: BrowserUpdates | null): Html;
66
+ export declare function settingsWorkers(store: Store, now: Date): WorkerFacts[];
117
67
  /** Choices save the moment they change; without the script the Save
118
68
  * button stays and the form works the same. */
119
69
  export declare const SETTINGS_AUTOSAVE_SCRIPT = "(function(){document.querySelectorAll('form[data-autosave]').forEach(function(form){form.classList.add('js-autosave');form.addEventListener('change',function(ev){var t=ev.target;if(t&&(t.type==='radio'||t.tagName==='SELECT')){if(form.requestSubmit)form.requestSubmit();else form.submit();}});});})();";
@@ -133,22 +83,3 @@ export declare function latestReleaseFor(latest: (() => Promise<{
133
83
  } | {
134
84
  problem: string;
135
85
  }>;
136
- /** Settings destinations under short headings: an icon and a name each, and a chat app's logo and state. */
137
- export declare function settingsTiles(groups: BrowserSettingsGroup[]): Html;
138
- /** Each destination once, in order: a group's heading, then its [href, label, icon] tiles. */
139
- export declare const SETTINGS_GROUPS: [string, [string, string, Html][]][];
140
- export declare const SETTINGS_TILE_ICONS: Map<string, Html>;
141
- export declare const CHAT_APP_TILES: Record<string, BrandIconId>;
142
- /**
143
- * The settings groups, with each chat app's state from what the server already holds: not set up,
144
- * connected, or getting alerts (only when that service was chosen, or is the only one).
145
- * A Telegram bot whose saved delivery state reports a problem says so instead. With no config folder
146
- * the state is unknown, so the tiles carry none.
147
- */
148
- export declare function settingsGroups(messaging: {
149
- channel: string | null;
150
- implicit: boolean;
151
- configured: string[];
152
- } | null, telegramFailing?: boolean): BrowserSettingsGroup[];
153
- /** Light, dark or the device's choice, one tap each. Per browser (a cookie). */
154
- export declare function appearanceCard(csrf: string): Html;