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
@@ -0,0 +1,96 @@
1
+ /**
2
+ * New task (Wave E, src/design/KIT.md): one describe box, the project it lands in and "Read code before planning" in
3
+ * the box's bottom row, the round send; every other choice behind More options; ideas under it. The form posts the
4
+ * same fields /tasks/add has always read (title, repo, plan-first with planning-policy=choice, quality-mode, after,
5
+ * goal, acceptance, not, touches, scout, id, permission-mode, projectRevision), so every server check is unchanged.
6
+ */
7
+ import { html, joinHtml, postForm } from "../html.js";
8
+ import { projectName } from "../project.js";
9
+ import { screen } from "./chrome.js";
10
+ import { btn, composer, disclose, doc, docHeader, dock, field, ideas, input, note, phoneHeader, radios, select, sendButton, switchInput, textarea } from "./kit.js";
11
+ const FORM_ID = "nt-form";
12
+ const PLAN_ON = "The agent reads the code and asks you anything unclear, then writes the plan.";
13
+ const PLAN_OFF = "The agent writes the plan straight from your words. Faster for small, obvious changes.";
14
+ /** The projects as the select offers them: names, with the parent folder when two share a name; the open one first
15
+ * picked. Values are the paths the server proves. */
16
+ function projectItems(projects, open) {
17
+ const all = open !== null && !projects.some(one => one.path === open) ? [{ path: open, name: projectName(open) }, ...projects] : [...projects];
18
+ const counts = new Map();
19
+ for (const one of all)
20
+ counts.set(one.name, (counts.get(one.name) ?? 0) + 1);
21
+ return all.map(one => {
22
+ if ((counts.get(one.name) ?? 0) < 2)
23
+ return [one.path, one.name];
24
+ const parent = one.path.split(/[\\/]/).filter(Boolean).slice(-2, -1)[0] ?? one.path;
25
+ return [one.path, `${one.name} (${parent})`];
26
+ });
27
+ }
28
+ export function newTaskPage(chrome, project,
29
+ /** undefined: keep the revision the refused form posted (its draft), so a project switch since still refuses. */
30
+ projectRevision, problem, candidates = [], permissionDefault = "auto", qualityDefault = "default", projects = [],
31
+ /** A shipped template's fields, or a refused filing's own words, back in the form. */
32
+ prefill = null,
33
+ /** Words an idea's link put in the box (GET ?text=). */
34
+ draft = null,
35
+ /** Ideas for the chosen project: one tap drafts the words into the box. */
36
+ suggestions = []) {
37
+ const head = html `${phoneHeader("New task", { back: "/work" })}`;
38
+ const header = docHeader({ title: "New task", back: ["/work", "Tasks"], meta: "Nothing is built until you approve its plan.", same: true });
39
+ const items = projectItems(projects, project);
40
+ const values = prefill?.values;
41
+ // No project to choose from: the task names its folder (the server proves it), and adding a project is one link away.
42
+ const typed = items.length === 0;
43
+ const chosen = values?.get("repo") ?? project ?? items[0]?.[0] ?? "";
44
+ const planOn = values === undefined || values.get("plan-first") === "1";
45
+ const quality = values?.get("quality-mode") ?? qualityDefault;
46
+ const after = values?.get("after") ?? "";
47
+ const permission = values?.get("permission-mode") === "bypassPermissions" ? "bypassPermissions" : values?.get("permission-mode") === "auto" ? "auto" : permissionDefault;
48
+ const text = prefill?.title ?? draft ?? "";
49
+ // More options opens on a template, or when a refused filing had used any of them.
50
+ const optionsUsed = prefill !== null && (values === undefined || ["goal", "acceptance", "not", "touches", "id", "after"].some(name => (values.get(name) ?? "") !== "") || values.get("scout") === "1");
51
+ const folder = !typed ? "" : field({ id: "nt-repo", label: "Project folder", hint: "No projects yet: name the folder of a git repository.",
52
+ control: input({ id: "nt-repo", name: "repo", value: chosen, hint: true, attrs: { required: true, placeholder: "/path/to/repository", autocapitalize: "none", spellcheck: "false" } }) });
53
+ const bar = joinHtml([
54
+ typed ? "" : select({ id: "nt-project", name: "repo", label: "Project", items, current: chosen, sm: true }),
55
+ switchInput({ id: "nt-plan", name: "plan-first", label: "Read code before planning", on: planOn, attrs: { value: "1" } }),
56
+ ]);
57
+ const box = composer({
58
+ id: "nt", name: "title", label: "What should change?", placeholder: "What should change? One or two sentences is enough.",
59
+ value: text, rows: 4, maxRows: 10, before: bar, error: problem,
60
+ send: sendButton({ label: "File task" }),
61
+ note: html `<span class="k-on-note">${PLAN_ON}</span><span class="k-off-note">${PLAN_OFF}</span>`,
62
+ textAttrs: { maxlength: 200, autofocus: true },
63
+ });
64
+ const options = html `<div class="k-form">\
65
+ ${field({ id: "nt-quality", label: "Checks", control: select({ id: "nt-quality", name: "quality-mode", items: [["default", "Default checks"], ["strict", "Strict (release)"]], current: quality, wide: true }) })}\
66
+ ${candidates.length === 0 ? "" : field({ id: "nt-after", label: "Starts", control: select({ id: "nt-after", name: "after", wide: true, current: after,
67
+ items: [["", "Right away"], ...candidates.map(one => [one.id, `After ${one.id}: ${one.title}`])] }) })}\
68
+ ${field({ id: "nt-goal", label: "Goal (optional)", hint: "With acceptance below, automatic approval can sign an unchanged plan.", control: textarea({ id: "nt-goal", name: "goal", rows: 2, value: prefill?.goal ?? "", attrs: { placeholder: "What success looks like" } }) })}\
69
+ ${field({ id: "nt-acceptance", label: "Acceptance", hint: "Needed with a goal. One per line: statement | evidence,kinds | how", control: textarea({ id: "nt-acceptance", name: "acceptance", rows: 2, value: prefill?.acceptance ?? "", attrs: { placeholder: "Requests over the limit return 429 | check" } }) })}\
70
+ ${field({ id: "nt-not", label: "Not this (optional)", control: input({ id: "nt-not", name: "not", value: prefill?.not ?? "" }) })}\
71
+ ${field({ id: "nt-touches", label: "Likely touches (optional)", hint: "Paths, separated by commas.", control: input({ id: "nt-touches", name: "touches", value: prefill?.touches ?? "", hint: true }) })}\
72
+ <div class="k-checks"><label><input type="checkbox" name="scout" value="1"${values?.get("scout") === "1" ? html ` checked` : ""}>Research only: a read-only report, no code changes</label></div>\
73
+ ${field({ id: "nt-id", label: "Task id (optional)", control: input({ id: "nt-id", name: "id", value: values?.get("id") ?? "", attrs: { placeholder: "Made from the request" } }) })}\
74
+ <div class="k-field"><span class="k-label">Agent permissions</span>${radios({ name: "permission-mode", legend: "Agent permissions", current: permission, items: [
75
+ { value: "auto", label: "Auto", text: "Asks before risky actions." },
76
+ { value: "bypassPermissions", label: "Full access", text: "Never asks and can change files anywhere on this computer. Trusted repositories only." },
77
+ ] })}<p class="k-hint">From Settings. You can still change it on the plan before you approve.</p></div></div>`;
78
+ const form = postForm("/tasks/add", joinHtml([
79
+ prefill !== null && values === undefined ? note("Filled in from a template. Change anything; it still waits for your approval.") : "",
80
+ folder,
81
+ typed ? note(html `<a class="k-link" href="/projects?return=%2Ftasks%2Fnew">Add a project</a> to choose it here next time.`) : "",
82
+ box,
83
+ disclose("More options", options, optionsUsed),
84
+ ]), {
85
+ attrs: { class: "k-form k-dock-send", id: FORM_ID },
86
+ projectRevision: projectRevision === undefined && values?.get("projectRevision") == null ? null : String(projectRevision ?? values?.get("projectRevision") ?? ""),
87
+ hidden: { "planning-policy": "choice" },
88
+ });
89
+ const chosenName = items.find(([path]) => path === chosen)?.[1] ?? (chosen === "" ? "your project" : projectName(chosen));
90
+ const ideaList = ideas({
91
+ label: `Ideas for ${chosenName}`, into: "nt",
92
+ items: suggestions.map(one => ({ label: one.label, draft: one.draft, href: `/tasks/new?text=${encodeURIComponent(one.draft)}` })),
93
+ });
94
+ const recipes = note(html `Work you do often? <a class="k-link" href="/recipes">Use a saved recipe</a>.`);
95
+ return screen("New task", html `${head}${doc(header, form, ideaList, recipes)}${dock(btn("File task", { variant: "primary", type: "submit", attrs: { form: FORM_ID } }))}`, { chrome });
96
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * /next (Wave E, src/design/KIT.md): everything waiting on a person, one at a time, each in the approval slots with
3
+ * its one act, every act landing back here. The forms post exactly what they always have (the approve form's nonce,
4
+ * digest and password; a decision's choice, confirm and note; a requeue), so every server check is unchanged. The
5
+ * approval shows the password only when the rules ask (approveForm, standard): none with Passwordless or a remembered
6
+ * password the task page's own preview says may approve these exact terms; otherwise the password, required.
7
+ */
8
+ import { type TaskOrigin } from "../task-source.js";
9
+ import type { Gap } from "../gaps.js";
10
+ import type { Scope } from "../scope.js";
11
+ import type { Decision } from "../store.js";
12
+ import { type Chrome, type Screen } from "./chrome.js";
13
+ import { type PlanContractView, type RouteView } from "./render-tasks.js";
14
+ export type NextItem = {
15
+ key: string;
16
+ kind: "decision";
17
+ decision: Decision & {
18
+ taskId: string;
19
+ };
20
+ } | {
21
+ key: string;
22
+ kind: "approval";
23
+ approval: {
24
+ taskId: string;
25
+ title: string;
26
+ goal: string;
27
+ digest: string;
28
+ proposedAt: string;
29
+ };
30
+ } | {
31
+ key: string;
32
+ kind: "requeue";
33
+ stalled: {
34
+ taskId: string;
35
+ title: string;
36
+ strikes: number;
37
+ incidentCount: number;
38
+ };
39
+ } | {
40
+ key: string;
41
+ kind: "gap";
42
+ gap: Gap;
43
+ } | null;
44
+ export declare function nextPage(chrome: Chrome, data: {
45
+ item: NextItem;
46
+ scope: Scope | null;
47
+ planDocument: string | null;
48
+ planContract?: PlanContractView | null;
49
+ approvalDigest: string | null;
50
+ /** v34: said inside the ceremony when the yes buys a report, not a branch. */
51
+ deliverable?: "branch" | "report";
52
+ /** v48: the agents the yes freezes — the one block every ceremony shows. */
53
+ route: RouteView | null;
54
+ /** v126: where the work came from (an outside event's line of context, never a term agreed to). */
55
+ origin?: TaskOrigin | null;
56
+ /** A remembered password stands in for these exact terms (the task page's preview): one click, no field. */
57
+ remembered?: boolean;
58
+ csrf: string;
59
+ nonce: string;
60
+ remaining: number;
61
+ skipped: string[];
62
+ now: Date;
63
+ }): Screen;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * /next (Wave E, src/design/KIT.md): everything waiting on a person, one at a time, each in the approval slots with
3
+ * its one act, every act landing back here. The forms post exactly what they always have (the approve form's nonce,
4
+ * digest and password; a decision's choice, confirm and note; a requeue), so every server check is unchanged. The
5
+ * approval shows the password only when the rules ask (approveForm, standard): none with Passwordless or a remembered
6
+ * password the task page's own preview says may approve these exact terms; otherwise the password, required.
7
+ */
8
+ import { originNote } from "../task-source.js";
9
+ import { html, joinHtml, postForm } from "../html.js";
10
+ import { screen, when } from "./chrome.js";
11
+ import { taskHref } from "./http.js";
12
+ import { acceptanceCeremonyHtml, agentsCeremonyHtml, consentClosedHtml, consentDoorOf, executionPlanHtml, planContractHtml, profileWords, runtimeDetailsHtml, } from "./render-tasks.js";
13
+ import { approvalFactsNow, approvalTerms, approveForm, btn, chip, disclose, doc, docHeader, empty, facts, note, phoneHeader, slots } from "./kit.js";
14
+ /** One decision option: a one-click answer (an irreversible one behind its own disclosure, with the confirm the server
15
+ * requires) and its consequence. A note travels from the task's own page. */
16
+ function optionForm(decision, option) {
17
+ const recommended = option.id === decision.recommendation;
18
+ const form = postForm(`/d/${decision.id}/answer`, html `<div class="k-acts">${btn(option.label, { variant: option.reversible ? (recommended ? "primary" : "outline") : "danger", type: "submit" })}</div>\
19
+ <p class="k-note">${recommended ? html `${chip("Recommended", "", { dot: false })} ` : ""}${option.consequence}</p>`, { attrs: { class: "k-form" }, returnTo: "next", hidden: { choice: option.id, confirm: option.reversible ? null : "yes" } });
20
+ return option.reversible ? form : disclose(`${option.label} (can't be undone)`, form);
21
+ }
22
+ export function nextPage(chrome, data) {
23
+ const { item } = data;
24
+ const title = "Needs you";
25
+ if (item === null) {
26
+ const held = data.skipped.length;
27
+ return screen(title, html `${phoneHeader(title)}${doc(docHeader({ title, same: true }), held > 0
28
+ ? empty({ title: `Only the ${held} you set aside ${held === 1 ? "is" : "are"} left.`, text: "Look at them again now, or come back later.", action: btn("Look at them again", { sm: true, href: "/next" }) })
29
+ : empty({ title: "Nothing needs you.", text: "Everything is either working or waiting on its own clock.", action: btn("Open Tasks", { sm: true, href: "/work" }) }))}`, { chrome });
30
+ }
31
+ const skipHref = `/next?skip=${encodeURIComponent([...data.skipped, item.key].join(","))}`;
32
+ const header = docHeader({
33
+ title, same: true,
34
+ meta: data.remaining === 1 ? "The last thing waiting on you" : `1 of ${data.remaining} waiting on you`,
35
+ right: btn("Not now", { variant: "ghost", sm: true, href: skipHref }),
36
+ });
37
+ const openTask = (taskId, words = "Open the task") => html `<a class="k-link" href="${taskHref(taskId)}">${words}</a>`;
38
+ let card;
39
+ if (item.kind === "decision") {
40
+ const { decision } = item;
41
+ card = slots({
42
+ wait: true, attrs: { "data-decision": String(decision.id) },
43
+ status: html `${chip("Question", "ask")}<h2>${decision.question}</h2>`,
44
+ text: decision.recap,
45
+ acts: joinHtml(decision.options.map(option => optionForm(decision, option))),
46
+ terms: html `${decision.taskId} · asked ${when(decision.createdAt)} · ${openTask(decision.taskId, "Answer with a note on the task")}`,
47
+ });
48
+ }
49
+ else if (item.kind === "approval" && !consentDoorOf(data.scope, data.route).open) {
50
+ const door = consentDoorOf(data.scope, data.route);
51
+ card = slots({
52
+ wait: true,
53
+ status: html `${chip("Plan", "ask")}<h2>${item.approval.title}</h2>`,
54
+ text: item.approval.taskId,
55
+ acts: consentClosedHtml(item.approval.taskId, door, "next"),
56
+ terms: openTask(item.approval.taskId),
57
+ });
58
+ }
59
+ else if (item.kind === "approval") {
60
+ const scope = data.scope;
61
+ const facts_ = approvalFactsNow({ remembered: data.remembered === true });
62
+ const terms = joinHtml([
63
+ data.deliverable === "report" ? note("Research only: a read-only session investigates this goal and delivers a report. No branch; nothing in the repository changes.") : "",
64
+ scope === null || scope.profileState !== "unresolved" ? "" : profileWords(scope),
65
+ originNote(data.origin) === null ? "" : html `<p class="k-note" data-approval-origin>${originNote(data.origin)}</p>`,
66
+ facts([
67
+ ["Goal", scope?.goal ?? item.approval.goal],
68
+ ["Not this", scope?.outOfScope == null ? "No exclusions" : scope.outOfScope],
69
+ ["Touches", scope === null || scope.touches.length === 0 ? "Anything" : scope.touches.join(", ")],
70
+ ]),
71
+ scope === null ? "" : acceptanceCeremonyHtml(scope.acceptance),
72
+ // The agents the yes freezes (v48): the same block the task page and chat sign under, before the password.
73
+ agentsCeremonyHtml(data.route),
74
+ scope === null ? "" : runtimeDetailsHtml(scope),
75
+ ]);
76
+ const plan = data.planDocument === null ? "" : html `<section class="k-sec"><h3>The plan</h3>${executionPlanHtml(data.planDocument, true)}${planContractHtml(data.planContract ?? null, "ceremony")}</section>`;
77
+ const digest = data.approvalDigest ?? item.approval.digest;
78
+ // The terms ride inside the form, before the password: what the yes covers is on the form that says it.
79
+ card = html `${plan}${slots({
80
+ wait: true, attrs: { "data-approval": digest },
81
+ status: html `${chip("Plan", "ask")}<h2>${item.approval.title}</h2>`,
82
+ text: html `${item.approval.taskId} · ${openTask(item.approval.taskId, "Open the task to change the scope first")}`,
83
+ acts: approveForm({ action: `${taskHref(item.approval.taskId)}/approve`, kind: "standard", facts: facts_, label: "Approve and start", primary: true,
84
+ before: html `<section class="k-sec"><h3>Approve exactly this</h3>${terms}</section>`,
85
+ form: { returnTo: "next", hidden: { nonce: data.nonce, digest }, attrs: { class: "k-form" } } }),
86
+ terms: approvalTerms("standard", facts_),
87
+ })}`;
88
+ }
89
+ else if (item.kind === "requeue") {
90
+ card = slots({
91
+ wait: true,
92
+ status: html `${chip("Stopped", "ask")}<h2>${item.stalled.title}</h2>`,
93
+ text: `Stopped with ${item.stalled.incidentCount} incident${item.stalled.incidentCount === 1 ? "" : "s"}${item.stalled.strikes > 0 ? ` after ${item.stalled.strikes} attempt${item.stalled.strikes === 1 ? "" : "s"}` : ""}. Retrying resolves the incidents, clears the failed attempts and puts it back in line.`,
94
+ acts: postForm(`${taskHref(item.stalled.taskId)}/requeue`, btn("Retry this work", { variant: "primary", type: "submit" }), { returnTo: "next" }),
95
+ terms: html `${item.stalled.taskId} · ${openTask(item.stalled.taskId, "Read the attempts first")}`,
96
+ });
97
+ }
98
+ else {
99
+ const { gap } = item;
100
+ card = slots({
101
+ wait: true,
102
+ status: html `${chip("Missing", "ask")}<h2>Supply ${gap.key}</h2>`,
103
+ text: html `${gap.state}. Filling it starts ${gap.unblocks.length} task${gap.unblocks.length === 1 ? "" : "s"}: ${joinHtml(gap.unblocks.map(one => html `<span class="k-mono">${one}</span>`), ", ")}.`,
104
+ acts: "",
105
+ terms: html `Prove it is filled from a terminal: <code class="k-mono">${gap.verify}</code>`,
106
+ });
107
+ }
108
+ return screen(title, html `${phoneHeader(title)}${doc(header, card)}`, { chrome });
109
+ }
@@ -1,25 +1,21 @@
1
1
  /** Everyday console pages: home, inbox, board, work, projects, fleet and the queue. */
2
- import { type TaskOrigin } from "../task-source.js";
2
+ import { type TidyItem } from "../tidy.js";
3
3
  import { type Html } from "../html.js";
4
4
  import { type AssignmentSnapshot } from "../assignment.js";
5
5
  import { type BoardCard } from "../board.js";
6
- import { type BrowserLimits, type BrowserProjectsView } from "../browser-workspace.js";
7
- import { type CheckLevel } from "../check-levels.js";
6
+ import { type BrowserLimits } from "../browser-workspace.js";
8
7
  import { type DispatchDiagnosis } from "../dispatch.js";
9
8
  import { type FirstRunStep } from "../first-run.js";
10
9
  import { type Gap } from "../gaps.js";
11
- import { type ListOutcome } from "../onboard.js";
12
10
  import { type parseBaseTreeSnapshot } from "../peek.js";
13
11
  import { type CriterionMatrixRow } from "../proof.js";
14
- import { type QualityMode } from "../quality.js";
15
12
  import { type Runner } from "../runner.js";
16
- import { type Scope, type UnattendedPermissionMode } from "../scope.js";
17
13
  import { type Decision, type Incident, type RepairChainRow, type Run, type Store, type TaskState, type WorktreeRow } from "../store.js";
18
14
  import { tally } from "../summary.js";
19
- import { type WorkIndexGroup, type WorkIndexPage } from "../work-index.js";
15
+ import { type WorkIndexGroup, type WorkIndexItem, type WorkIndexPage } from "../work-index.js";
16
+ import { type ChipTone } from "../design/kit.js";
20
17
  import { type WorkFacts, type WorkStatus, type WorkView } from "../workspace-ui.js";
21
18
  import { type Chrome, type Screen } from "./chrome.js";
22
- import { type PlanContractView, type RouteView } from "./render-tasks.js";
23
19
  export declare const TASK_STATES: readonly TaskState[];
24
20
  /**
25
21
  * The inbox (v3): only things stalling progress without a person, one card
@@ -111,9 +107,8 @@ export declare function inboxPage(chrome: Chrome, data: {
111
107
  }[];
112
108
  unread?: readonly InboxTab[];
113
109
  }): Screen;
114
- /** System: the machinery — workers, background service, workspaces. */
115
110
  /** Start a builder: the one command, as text to select (/start-builder, where every "Connect a builder" leads, and
116
- * /system#builder). `project`: the open project's name, when the viewer may see it; `connected`: whether a builder
111
+ * the Agents page). `project`: the open project's name, when the viewer may see it; `connected`: whether a builder
117
112
  * that may build there is checking in (null: say nothing); `managed`: whether this person can start one themselves
118
113
  * (registering a builder takes an instance operator), else who to ask. */
119
114
  export declare function startBuilderHtml(data: {
@@ -122,40 +117,6 @@ export declare function startBuilderHtml(data: {
122
117
  connected: boolean | null;
123
118
  managed: boolean;
124
119
  }): Html;
125
- export declare function systemPage(chrome: Chrome, data: {
126
- agents: {
127
- phase: "plan" | "build" | "repair" | "review";
128
- provider?: string;
129
- model?: string | null;
130
- source?: "pinned" | "flag" | "project" | "installation" | "default";
131
- problem?: string;
132
- setBy: string | null;
133
- }[];
134
- building: {
135
- taskId: string;
136
- runner: string;
137
- claimedAt: string;
138
- expiresAt: string;
139
- model: string | null;
140
- }[];
141
- runners: Runner[];
142
- worktrees: WorktreeRow[];
143
- episode: {
144
- id: number;
145
- startedAt: string;
146
- endedAt: string | null;
147
- ticks: number;
148
- built: number;
149
- broke: number;
150
- } | null;
151
- outboxPending: number;
152
- externalWork?: {
153
- remoteRepo: string;
154
- blocked: string | null;
155
- openEpisode: string | null;
156
- }[];
157
- now: Date;
158
- }): Screen;
159
120
  /**
160
121
  * A build's stage as one of three steps — set up, writing, checking and
161
122
  * committing — read from the machine's own phase, never a percent. The
@@ -202,8 +163,6 @@ export declare function boardBody(data: {
202
163
  questions: number;
203
164
  } | null;
204
165
  }, ciRed: (pr: number) => boolean): Html;
205
- /** Completed work: one row per done task, its final run and PR attached. */
206
- export declare function donePage(chrome: Chrome, rows: ReturnType<Store["listCompletedWorkScoped"]>, ciRed: (pr: number) => boolean): Screen;
207
166
  export declare function homePage(chrome: Chrome, data: {
208
167
  csrf: string;
209
168
  taskCount: number;
@@ -243,40 +202,6 @@ export declare function homePage(chrome: Chrome, data: {
243
202
  settings: boolean;
244
203
  now: Date;
245
204
  }): Screen;
246
- export type TaskComposerPrefill = {
247
- title: string;
248
- goal: string;
249
- not: string;
250
- touches: string;
251
- acceptance: string;
252
- values?: URLSearchParams;
253
- };
254
- /** The one front door for new work. The common path is one prompt and one
255
- * button; the detailed contract remains available in-place for templates,
256
- * experts, and the rare task that should skip repository-aware planning. */
257
- /** Where new work goes when no project is open: a choice from the projects
258
- * this person already has, never a typed path; a new project is one link
259
- * away. Same-named checkouts show their parent folder to tell them apart. */
260
- export declare function projectPickerHtml(projects: {
261
- path: string;
262
- name: string;
263
- }[], chosen: string): Html;
264
- export declare function taskComposerHtml(data: {
265
- project: string | null;
266
- projectRevision?: number;
267
- prefill?: TaskComposerPrefill | null;
268
- candidates?: {
269
- id: string;
270
- title: string;
271
- }[];
272
- permissionDefault: UnattendedPermissionMode;
273
- qualityDefault: QualityMode;
274
- /** Projects this person may place work in, most recently opened first. */
275
- projects?: {
276
- path: string;
277
- name: string;
278
- }[];
279
- }): Html;
280
205
  export type WorkRow = WorkFacts & {
281
206
  assignment?: AssignmentSnapshot | null;
282
207
  assignmentProblem?: boolean;
@@ -286,6 +211,19 @@ export type WorkRow = WorkFacts & {
286
211
  resultRunId: number | null;
287
212
  };
288
213
  export declare const TASK_GROUP_LABEL: Readonly<Record<WorkIndexGroup, string>>;
214
+ /** A list row's chip (src/design/KIT.md): a waiting row names its ask ("Plan", "Result") in the ask tone, or says
215
+ * Needs you; every other row wears its headline in the tone of the status it belongs to. */
216
+ export declare function workRowChip(row: Pick<WorkIndexItem, 'ask' | 'chip' | 'status' | 'archived'>): {
217
+ word: string;
218
+ tone: ChipTone;
219
+ };
220
+ /** The board's four columns: what needs you, what will start on its own, what runs, what finished. */
221
+ export type WorkBoard = {
222
+ needs: WorkIndexPage;
223
+ queued: WorkIndexPage;
224
+ running: WorkIndexPage;
225
+ finished: WorkIndexPage;
226
+ };
289
227
  export declare function workPage(chrome: Chrome, data: {
290
228
  view: WorkView;
291
229
  projectFilter?: string;
@@ -295,19 +233,13 @@ export declare function workPage(chrome: Chrome, data: {
295
233
  now: Date;
296
234
  limits?: BrowserLimits | null;
297
235
  state?: TaskState | null;
298
- }): Screen;
299
- export declare function browsePage(chrome: Chrome, data: {
300
- at: string;
301
- root: string;
302
- roots: string[];
303
- parent: string | null;
304
- entries: {
305
- name: string;
306
- path: string;
307
- git: boolean;
308
- }[];
309
- /** Unused: postForm adds the request's CSRF field. Kept for callers. */
310
- csrf: string;
236
+ /** An approver in a browser session: rows offer Put away (or Restore, archived), and the page offers Put away old tasks. */
237
+ canPutAway?: boolean;
238
+ /** The project this list shows (null: every project in view) and whether All was asked for by name (`all=1`). */
239
+ lens?: string | null;
240
+ all?: boolean;
241
+ /** The Board view's columns, read from the same index as the list; absent for the List view. */
242
+ board?: WorkBoard | null;
311
243
  }): Screen;
312
244
  /** The compact completed row shared by both halves of the control room. */
313
245
  export type WorkbenchDone = {
@@ -432,26 +364,6 @@ export type ProjectPeek = {
432
364
  * GitHub identity. Everything else is null.
433
365
  */
434
366
  export declare function githubIdentityOf(remoteUrl: string): string | null;
435
- /** The GitHub listing page: the account's repositories with ONE honest
436
- * action each. Names, descriptions, and paths are gh/filesystem DATA —
437
- * escaped at the sink like everything else. */
438
- export declare function githubReposPage(chrome: Chrome, data: {
439
- /** The last listing; null until someone lists (a POST: listing asks GitHub, so a page visit never does). */
440
- listed: ListOutcome | null;
441
- local: Map<string, string>;
442
- registered: Set<string>;
443
- /** Unused: postForm adds the request's CSRF field. Kept for callers. */
444
- csrf: string;
445
- cloneReady: boolean;
446
- openProject: string | null;
447
- /** A projects folder didn't answer in time: local copies weren't matched this time. */
448
- localUnanswered?: boolean;
449
- }): Screen;
450
- export declare function projectsPage(chrome: Chrome, recent: {
451
- path: string;
452
- name: string;
453
- lastOpenedAt: string;
454
- }[], candidates: string[], open: string | null, problem: string | null, _unscopedMode: boolean, browsable?: boolean, onboard?: OnboardCardState | null, peeks?: Record<string, ProjectPeek | null>, returnTo?: string, pullRequestsOn?: (path: string) => boolean, checkLevelOf?: (path: string) => CheckLevel, activity?: BrowserProjectsView["activity"]): Screen;
455
367
  /** The no-script "move to the front" sentinel: the form cannot name the
456
368
  * front of a partition, so the handler resolves it (slice 1b, fix 1). */
457
369
  export declare const QUEUE_FRONT = "__TOP__";
@@ -470,18 +382,6 @@ export declare function queueBody(tasks: QueueCardTask[], workers: {
470
382
  capacity: number;
471
383
  building: number;
472
384
  }[], revision: number, queueRevision: number): Html;
473
- /**
474
- * The fleet screen — one lane per runner, and the work in front of it.
475
- * Building claims pin to the top of their worker's lane (live — never
476
- * draggable); queued reservations sit below it (draggable to another
477
- * worker, re-reserving them). Every card wears its project chip, which is
478
- * the whole point: which agent is on which project is the page's answer.
479
- * Form-free like the board, except the per-worker note, so the lane stack
480
- * can re-render itself while somebody watches.
481
- */
482
- export declare function fleetBody(queued: ReturnType<Store["fleetQueue"]>, building: ReturnType<Store["liveClaims"]>, runners: Runner[], queueRevision: number, visibleRepo: (repo: string | null) => boolean): Html;
483
- /** Identical drag mechanics to the queue — the pointer events land on the worker's column. */
484
- export declare function fleetScript(): string;
485
385
  /** The /queue page's one nonce'd script: delegated pointer-event drag (it
486
386
  * survives every fragment swap — finding 17) plus a poller that re-checks
487
387
  * focus, dirty inputs, and an in-flight drag AT SWAP TIME, never only
@@ -489,71 +389,6 @@ export declare function fleetScript(): string;
489
389
  * (finding 18); missing that, a select counts clean.
490
390
  */
491
391
  export declare function queueScript(): string;
492
- export declare function newTaskPage(chrome: Chrome, project: string | null,
493
- /** undefined: keep the revision the refused form posted (its draft), so a project switch since still refuses. */
494
- projectRevision: number | undefined, problem: string | null, candidates?: {
495
- id: string;
496
- title: string;
497
- }[], permissionDefault?: UnattendedPermissionMode, qualityDefault?: QualityMode, projects?: {
498
- path: string;
499
- name: string;
500
- }[],
501
- /** A shipped template's fields, or a refused filing's own words, back in the form. */
502
- prefill?: TaskComposerPrefill | null): Screen;
503
- /**
504
- * The triage flow: everything waiting on a person, one card at a time.
505
- * Full context ON the card, the act inline, and every act lands back here
506
- * — clearing the queue is taps, not navigation. Read state travels in the
507
- * URL (the bounded skip cursor), never in the session: two tabs cannot
508
- * fight, and a shared link shows the same queue.
509
- */
510
- export declare function nextPage(chrome: Chrome, data: {
511
- item: {
512
- key: string;
513
- kind: "decision";
514
- decision: Decision & {
515
- taskId: string;
516
- };
517
- } | {
518
- key: string;
519
- kind: "approval";
520
- approval: {
521
- taskId: string;
522
- title: string;
523
- goal: string;
524
- digest: string;
525
- proposedAt: string;
526
- };
527
- } | {
528
- key: string;
529
- kind: "requeue";
530
- stalled: {
531
- taskId: string;
532
- title: string;
533
- strikes: number;
534
- incidentCount: number;
535
- };
536
- } | {
537
- key: string;
538
- kind: "gap";
539
- gap: Gap;
540
- } | null;
541
- scope: Scope | null;
542
- planDocument: string | null;
543
- planContract?: PlanContractView | null;
544
- approvalDigest: string | null;
545
- /** v34: said inside the ceremony when the yes buys a report, not a branch. */
546
- deliverable?: "branch" | "report";
547
- /** v48: the agents the yes freezes — the one block every ceremony shows. */
548
- route: RouteView | null;
549
- /** v126: where the work came from (an outside event's line of context, never a term agreed to). */
550
- origin?: TaskOrigin | null;
551
- csrf: string;
552
- nonce: string;
553
- remaining: number;
554
- skipped: string[];
555
- now: Date;
556
- }): Screen;
557
392
  export type PeekAdmission = {
558
393
  run: Run;
559
394
  /** The run's checkout, PROVEN non-null by the guards: a reviewer run
@@ -562,3 +397,13 @@ export type PeekAdmission = {
562
397
  epoch: string;
563
398
  entries: ReturnType<typeof parseBaseTreeSnapshot>;
564
399
  };
400
+ /** Put away old tasks (docs/plans/task-tidy.md): exactly what would be put away, grouped, each with why, every one
401
+ * ticked; one step-up puts away the ticked ones, bound to this preview's digest. The CLI twin is named beside it. */
402
+ export declare function putAwayPage(chrome: Chrome, data: {
403
+ items: readonly TidyItem[];
404
+ days: number;
405
+ digest: string;
406
+ now: Date;
407
+ problem: string | null;
408
+ releasesSkipped: string | null;
409
+ }): Screen;