toolroll 0.9.63 → 0.9.67

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 (302) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +33 -5
  3. package/dist/acp-agents.d.ts +43 -0
  4. package/dist/acp-agents.js +62 -0
  5. package/dist/acp-attest.d.ts +44 -0
  6. package/dist/acp-attest.js +269 -0
  7. package/dist/acp-check.d.ts +28 -0
  8. package/dist/acp-check.js +410 -0
  9. package/dist/acp-client.d.ts +163 -0
  10. package/dist/acp-client.js +738 -0
  11. package/dist/acp-inspect.d.ts +23 -0
  12. package/dist/acp-inspect.js +109 -0
  13. package/dist/agent-board.d.ts +19 -6
  14. package/dist/agent-board.js +30 -9
  15. package/dist/agent-fence.d.ts +117 -2
  16. package/dist/agent-fence.js +439 -12
  17. package/dist/agent-pulse.js +9 -2
  18. package/dist/agent-state-words.d.ts +19 -0
  19. package/dist/agent-state-words.js +20 -0
  20. package/dist/agent-state.d.ts +65 -0
  21. package/dist/agent-state.js +229 -0
  22. package/dist/agentconfig.js +39 -13
  23. package/dist/agents-board.d.ts +34 -2
  24. package/dist/agents-board.js +110 -1
  25. package/dist/approval-rules-ui.js +2 -2
  26. package/dist/assignment-delivery.d.ts +14 -0
  27. package/dist/assignment-delivery.js +13 -0
  28. package/dist/assignment.js +13 -0
  29. package/dist/attest.js +7 -0
  30. package/dist/backup-ui.js +2 -2
  31. package/dist/batch-checks.js +3 -2
  32. package/dist/browser/THIRD_PARTY_NOTICES.txt +94 -0
  33. package/dist/browser/terminal.css +37 -0
  34. package/dist/browser/terminal.js +228 -0
  35. package/dist/browser/workspace.css +2 -2
  36. package/dist/browser/workspace.js +42 -38
  37. package/dist/browser-shell.js +6 -3
  38. package/dist/browser-workspace.d.ts +235 -1
  39. package/dist/browser-workspace.js +60 -0
  40. package/dist/builder.d.ts +10 -1
  41. package/dist/builder.js +87 -24
  42. package/dist/chat-actions.d.ts +13 -1
  43. package/dist/chat-actions.js +197 -15
  44. package/dist/chat-ask.d.ts +4 -0
  45. package/dist/chat-ask.js +14 -0
  46. package/dist/chat-channel.js +20 -0
  47. package/dist/chat-continuity.js +5 -4
  48. package/dist/chat-decide.d.ts +0 -14
  49. package/dist/chat-decide.js +4 -1
  50. package/dist/chat-delivery.d.ts +9 -0
  51. package/dist/chat-delivery.js +45 -6
  52. package/dist/chat-messages.d.ts +8 -0
  53. package/dist/chat-messages.js +36 -4
  54. package/dist/check-levels-ui.js +2 -2
  55. package/dist/checkout-proof.d.ts +28 -0
  56. package/dist/checkout-proof.js +62 -0
  57. package/dist/claim.d.ts +12 -0
  58. package/dist/claim.js +19 -0
  59. package/dist/cli-help.d.ts +1 -1
  60. package/dist/cli-help.js +4 -1
  61. package/dist/cli.d.ts +3 -0
  62. package/dist/cli.js +21 -2
  63. package/dist/coding-ui.js +6 -4
  64. package/dist/console-instance.d.ts +124 -0
  65. package/dist/console-instance.js +337 -0
  66. package/dist/contracts/chat-actions.d.ts +16 -0
  67. package/dist/contracts/chat-actions.js +4 -0
  68. package/dist/contracts/chat-content.d.ts +19 -0
  69. package/dist/contracts/chat-content.js +6 -0
  70. package/dist/contracts/console-api.d.ts +13 -4
  71. package/dist/contracts/console-api.js +13 -4
  72. package/dist/contracts/flow-card.d.ts +4 -0
  73. package/dist/contracts/flow-card.js +3 -0
  74. package/dist/contracts/flow.d.ts +46 -0
  75. package/dist/contracts/flow.js +11 -2
  76. package/dist/contracts/lead-tools.d.ts +92 -0
  77. package/dist/contracts/lead-tools.js +13 -0
  78. package/dist/contracts/provider.d.ts +11 -1
  79. package/dist/contracts/provider.js +1 -1
  80. package/dist/contracts/route.d.ts +76 -1
  81. package/dist/contracts/route.js +1 -1
  82. package/dist/contracts/store-json.d.ts +2 -0
  83. package/dist/control-setup.d.ts +1 -1
  84. package/dist/control-ui.js +2 -2
  85. package/dist/converse.d.ts +16 -0
  86. package/dist/converse.js +32 -4
  87. package/dist/demo-chat.js +4 -4
  88. package/dist/demo.js +2 -2
  89. package/dist/desktop-update-gate.js +6 -0
  90. package/dist/desktop-update.d.ts +1 -1
  91. package/dist/discord-chat.js +4 -1
  92. package/dist/doctor.js +1 -1
  93. package/dist/egress-bridge.d.ts +17 -0
  94. package/dist/egress-bridge.js +111 -0
  95. package/dist/egress.d.ts +119 -0
  96. package/dist/egress.js +423 -0
  97. package/dist/exec.d.ts +78 -0
  98. package/dist/exec.js +367 -9
  99. package/dist/export-ui.js +2 -2
  100. package/dist/flow-engine.js +24 -5
  101. package/dist/flow-gallery-ui.js +2 -2
  102. package/dist/flow-gallery.js +27 -8
  103. package/dist/flow-share.js +16 -5
  104. package/dist/flow-triggers.d.ts +77 -8
  105. package/dist/flow-triggers.js +517 -70
  106. package/dist/flows-cli.js +8 -2
  107. package/dist/flows-ui.d.ts +0 -2
  108. package/dist/flows-ui.js +52 -4
  109. package/dist/flows.d.ts +4 -0
  110. package/dist/flows.js +9 -1
  111. package/dist/html.d.ts +9 -0
  112. package/dist/html.js +11 -0
  113. package/dist/invoke.d.ts +35 -0
  114. package/dist/invoke.js +347 -28
  115. package/dist/keys.d.ts +18 -14
  116. package/dist/keys.js +5 -0
  117. package/dist/lead-capabilities.js +3 -2
  118. package/dist/lead-contract.js +2 -0
  119. package/dist/lead-doors.d.ts +2 -0
  120. package/dist/lead-doors.js +7 -0
  121. package/dist/lead-follow.d.ts +2 -0
  122. package/dist/lead-follow.js +58 -2
  123. package/dist/lead-progress.js +1 -0
  124. package/dist/lead-queue-runtime.d.ts +46 -0
  125. package/dist/lead-queue-runtime.js +191 -0
  126. package/dist/lead-queue.d.ts +104 -0
  127. package/dist/lead-queue.js +187 -0
  128. package/dist/lead-sweep.d.ts +67 -0
  129. package/dist/lead-sweep.js +99 -0
  130. package/dist/lead-tools.d.ts +22 -0
  131. package/dist/lead-tools.js +152 -1
  132. package/dist/lead.d.ts +13 -5
  133. package/dist/lead.js +89 -29
  134. package/dist/limits-ui.d.ts +8 -1
  135. package/dist/limits-ui.js +26 -0
  136. package/dist/live-bus.d.ts +8 -0
  137. package/dist/live-bus.js +40 -4
  138. package/dist/live-detail.d.ts +27 -0
  139. package/dist/live-detail.js +57 -0
  140. package/dist/live-notes.d.ts +20 -0
  141. package/dist/live-notes.js +47 -0
  142. package/dist/live-records.d.ts +140 -0
  143. package/dist/live-records.js +935 -0
  144. package/dist/live-terminal.d.ts +269 -0
  145. package/dist/live-terminal.js +686 -0
  146. package/dist/live.d.ts +38 -27
  147. package/dist/live.js +142 -124
  148. package/dist/mcp-oauth.d.ts +6 -3
  149. package/dist/mcp-oauth.js +8 -4
  150. package/dist/memory-pass.js +2 -2
  151. package/dist/mobile-viewport.d.ts +5 -1
  152. package/dist/mobile-viewport.js +18 -2
  153. package/dist/model-catalog.js +4 -0
  154. package/dist/monitoring-ui.js +2 -2
  155. package/dist/needs-you.d.ts +1 -1
  156. package/dist/needs-you.js +11 -4
  157. package/dist/operate.d.ts +4 -0
  158. package/dist/operate.js +335 -19
  159. package/dist/passwordless.d.ts +23 -0
  160. package/dist/passwordless.js +26 -0
  161. package/dist/peek-cli.d.ts +12 -4
  162. package/dist/peek-cli.js +19 -11
  163. package/dist/phase-routing.js +10 -3
  164. package/dist/planner-source.d.ts +3 -0
  165. package/dist/planner-source.js +14 -0
  166. package/dist/planner.d.ts +2 -1
  167. package/dist/planner.js +7 -1
  168. package/dist/policy-ui.js +2 -2
  169. package/dist/policy.d.ts +1 -20
  170. package/dist/policy.js +29 -2
  171. package/dist/project-delete-ui.d.ts +18 -0
  172. package/dist/project-delete-ui.js +60 -9
  173. package/dist/project-delete.js +2 -2
  174. package/dist/project-memory.d.ts +3 -1
  175. package/dist/project-memory.js +57 -25
  176. package/dist/project-tools.d.ts +5 -3
  177. package/dist/project-tools.js +12 -9
  178. package/dist/provider-auth.d.ts +4 -3
  179. package/dist/provider-auth.js +5 -4
  180. package/dist/provider-connection.d.ts +2 -1
  181. package/dist/provider-connection.js +5 -0
  182. package/dist/provider.d.ts +25 -2
  183. package/dist/provider.js +63 -9
  184. package/dist/pull-request-ui.js +2 -2
  185. package/dist/push.js +1 -1
  186. package/dist/quick-start.d.ts +24 -0
  187. package/dist/quick-start.js +127 -0
  188. package/dist/release-adapters.d.ts +6 -0
  189. package/dist/release-adapters.js +70 -3
  190. package/dist/release.d.ts +10 -1
  191. package/dist/release.js +34 -15
  192. package/dist/request-budget-ui.js +2 -2
  193. package/dist/result-follow-ups.js +3 -2
  194. package/dist/result-review.d.ts +1 -1
  195. package/dist/result-review.js +9 -3
  196. package/dist/retention-ui.js +2 -2
  197. package/dist/retention.js +10 -1
  198. package/dist/run-efficiency.d.ts +73 -0
  199. package/dist/run-efficiency.js +228 -0
  200. package/dist/runner.d.ts +5 -1
  201. package/dist/runner.js +22 -1
  202. package/dist/scope.d.ts +183 -5
  203. package/dist/scope.js +256 -31
  204. package/dist/scout-net.d.ts +4 -1
  205. package/dist/scout-net.js +37 -12
  206. package/dist/scout.d.ts +20 -6
  207. package/dist/scout.js +27 -3
  208. package/dist/serve.js +248 -40
  209. package/dist/server/chat.d.ts +1 -1
  210. package/dist/server/chat.js +122 -31
  211. package/dist/server/chrome.d.ts +16 -2
  212. package/dist/server/chrome.js +63 -11
  213. package/dist/server/console.css +107 -42
  214. package/dist/server/flows.js +38 -8
  215. package/dist/server/guards.d.ts +1 -0
  216. package/dist/server/guards.js +23 -9
  217. package/dist/server/http.d.ts +7 -0
  218. package/dist/server/http.js +3 -2
  219. package/dist/server/live.d.ts +34 -1
  220. package/dist/server/live.js +39 -17
  221. package/dist/server/pages-assets.js +5 -0
  222. package/dist/server/pages.js +90 -13
  223. package/dist/server/people-tokens.d.ts +3 -0
  224. package/dist/server/people-tokens.js +55 -17
  225. package/dist/server/remote-hooks.js +12 -6
  226. package/dist/server/remote.d.ts +2 -1
  227. package/dist/server/remote.js +14 -5
  228. package/dist/server/render-chat.d.ts +12 -3
  229. package/dist/server/render-chat.js +47 -16
  230. package/dist/server/render-pages.d.ts +32 -4
  231. package/dist/server/render-pages.js +83 -13
  232. package/dist/server/render-people.d.ts +3 -2
  233. package/dist/server/render-people.js +3 -2
  234. package/dist/server/render-results.d.ts +18 -2
  235. package/dist/server/render-results.js +37 -28
  236. package/dist/server/render-settings.js +6 -5
  237. package/dist/server/render-tasks.d.ts +35 -10
  238. package/dist/server/render-tasks.js +111 -27
  239. package/dist/server/request-context.d.ts +2 -0
  240. package/dist/server/request-context.js +2 -1
  241. package/dist/server/route-table.d.ts +3 -0
  242. package/dist/server/route-table.js +47 -5
  243. package/dist/server/runtime.d.ts +33 -2
  244. package/dist/server/session.d.ts +25 -1
  245. package/dist/server/session.js +18 -4
  246. package/dist/server/settings.js +142 -10
  247. package/dist/server/sweeps.d.ts +4 -0
  248. package/dist/server/sweeps.js +58 -0
  249. package/dist/server/task-views.d.ts +2 -1
  250. package/dist/server/task-views.js +96 -6
  251. package/dist/server/tasks.js +327 -78
  252. package/dist/server/terminals.d.ts +31 -0
  253. package/dist/server/terminals.js +365 -0
  254. package/dist/setup-guide.js +3 -2
  255. package/dist/slack-chat.js +4 -1
  256. package/dist/spend-ui.js +3 -3
  257. package/dist/spend.js +2 -1
  258. package/dist/steer-capabilities.d.ts +32 -0
  259. package/dist/steer-capabilities.js +60 -0
  260. package/dist/steer-controls.d.ts +14 -0
  261. package/dist/steer-controls.js +36 -0
  262. package/dist/steer-status.js +2 -2
  263. package/dist/storage-ui.js +4 -4
  264. package/dist/store-baseline.d.ts +1 -1
  265. package/dist/store-baseline.js +1 -1
  266. package/dist/store.d.ts +440 -15
  267. package/dist/store.js +1072 -52
  268. package/dist/subagent-desk.js +1 -1
  269. package/dist/summary.js +3 -0
  270. package/dist/task-source.d.ts +58 -0
  271. package/dist/task-source.js +93 -0
  272. package/dist/team-leads.js +1 -1
  273. package/dist/teams-chat.js +4 -1
  274. package/dist/teardown-git.d.ts +22 -0
  275. package/dist/teardown-git.js +136 -2
  276. package/dist/telegram-lead.d.ts +3 -0
  277. package/dist/telegram-lead.js +44 -7
  278. package/dist/telegram.d.ts +11 -2
  279. package/dist/telegram.js +51 -17
  280. package/dist/terminal-catalog.d.ts +11 -0
  281. package/dist/terminal-catalog.js +129 -0
  282. package/dist/text-limits.d.ts +2 -0
  283. package/dist/text-limits.js +2 -0
  284. package/dist/tool-broker.d.ts +14 -0
  285. package/dist/tool-broker.js +67 -2
  286. package/dist/toolroll-update-ui.js +2 -2
  287. package/dist/toolroll-update.js +13 -3
  288. package/dist/tools-ui.js +2 -2
  289. package/dist/unfenced-test-support.d.ts +2 -0
  290. package/dist/unfenced-test-support.js +11 -0
  291. package/dist/webhook-limits.d.ts +23 -0
  292. package/dist/webhook-limits.js +30 -0
  293. package/dist/webhook-readers.d.ts +56 -0
  294. package/dist/webhook-readers.js +138 -0
  295. package/dist/webhook-signing.d.ts +27 -0
  296. package/dist/webhook-signing.js +119 -0
  297. package/dist/work-index.d.ts +29 -0
  298. package/dist/work-index.js +16 -2
  299. package/dist/workspace-revision.js +3 -1
  300. package/dist/writes-mode.d.ts +9 -0
  301. package/dist/writes-mode.js +8 -0
  302. package/package.json +15 -2
@@ -1,6 +1,9 @@
1
+ import { approvalProofOf, freshlyApprovable, freshnessFor, withFreshness } from "../scope.js";
2
+ import { liveAttemptOf, steerControlsFor } from "../steer-controls.js";
3
+ import { originOf } from "../task-source.js";
1
4
  import { taskBaseOf } from "../task-base.js";
2
5
  import { assignmentPresentationOf, historicalAssessmentReason } from '../assignment-presentation.js';
3
- import { html, joinHtml } from "../html.js";
6
+ import { html, htmlString, joinHtml } from "../html.js";
4
7
  import { assignmentStatusOf, assignmentWithEvidence } from '../assignment-ui.js';
5
8
  import { assignmentOf } from '../assignment.js';
6
9
  import { knowledgeContextHtml } from "../knowledge-ui.js";
@@ -18,7 +21,7 @@ import { taskFingerprint } from "../task-live.js";
18
21
  import { pullRequestFactOf } from '../task-status.js';
19
22
  import { openWorkDecisionOf } from "../work-summary.js";
20
23
  import { learningHtml } from "../workspace-ui.js";
21
- import { randomBytes } from "node:crypto";
24
+ import { createHash, randomBytes } from "node:crypto";
22
25
  import {} from "node:http";
23
26
  import { agentChoicesFor, routeOfTask } from "../agentconfig.js";
24
27
  import { rulesSummary } from "../approval-rules-ui.js";
@@ -41,6 +44,8 @@ import {} from "./render-pages.js";
41
44
  import { completionReceiptView, diffFileAnchor, evidenceLinksFor, followUpsFor, orderChangedFiles, parseReviewDiff, proofBundleView, receiptStatusOf, withinSignedTouches } from "./render-results.js";
42
45
  import { approvalFormDigest, consentDoorOf, nonceHashOf, publicationFactsOf, resumeCeremonyPage, resumeDigestOf, taskBody, taskPage } from "./render-tasks.js";
43
46
  import {} from "./session.js";
47
+ import { RESULT_REVIEW_SCRIPT } from "../result-review.js";
48
+ import { reviewEvidenceScript } from "./render-results.js";
44
49
  import { budgetHoldWords, budgetLabel, monthOf } from "../spend.js";
45
50
  import {} from "../store.js";
46
51
  import { taskControlOf } from "../task-control.js";
@@ -367,6 +372,8 @@ export function createTaskViews(runtime) {
367
372
  repairChain: completion !== null ? null : runtime.store.repairChainForDraft(taskId),
368
373
  repo: ref?.repo ?? null,
369
374
  filedVia: runtime.store.filedViaOf(taskId),
375
+ // v126: where the work came from (a flow, an outside event), from what was kept when it was filed.
376
+ origin: (() => { const repos = runtime.workAccess().repos; return originOf(runtime.store, taskId, repo => repos === null || repos.includes(repo)); })(),
370
377
  coordinator: (() => {
371
378
  const who = runtime.store.coordinatorProvenanceOf(taskId);
372
379
  if (who === null)
@@ -485,6 +492,31 @@ export function createTaskViews(runtime) {
485
492
  steering: ref === null ? [] : runtime.store.listSteerNotes(ref.id),
486
493
  base: taskBaseOf(runtime.store, taskId),
487
494
  steeringLive: ref !== null && runtime.store.steeringLiveBuild(ref.id, now) !== null,
495
+ steeringRun: ref === null ? null : runtime.store.steeringLiveBuild(ref.id, now),
496
+ approvalProof: (() => {
497
+ // The proof of the approval shown: its approver, its exact sealed digest (approvedDigest === digest when approved).
498
+ const scope = ref === null ? null : runtime.store.getScope(ref.externalId);
499
+ return scope === null || scope.approvedBy === null || scope.approvedDigest === null ? null
500
+ : approvalProofOf(runtime.store, ref.externalId, scope.approvedBy, scope.approvedDigest);
501
+ })(),
502
+ // Cockpit B2: whether this person's remembered password may approve these exact terms (the same rule approve()
503
+ // applies), and since when.
504
+ approvalFresh: (() => {
505
+ const scope = ref === null ? null : runtime.store.getScope(ref.externalId);
506
+ if (scope === null || who?.via !== "cookie")
507
+ return null;
508
+ const fresh = withFreshness("approve-standard", () => freshnessFor(who.name, Date.now(), true));
509
+ if (fresh === null)
510
+ return null;
511
+ const gate = runtime.store.approvalGate(ref.externalId, who.name, "person");
512
+ return freshlyApprovable(runtime.store, ref.externalId, scope, gate.verdict === "seal" && !gate.protectedWork) ? new Date(fresh.at).toISOString() : null;
513
+ })(),
514
+ steerRecent: ref === null ? [] : runtime.store.runsFor(ref.id).filter(one => one.outcome !== null).map(one => one.id).sort((a, b) => b - a).slice(0, 5),
515
+ // The running attempt's composer, for an approver on a browser session (steer-controls.ts).
516
+ steerComposer: (() => {
517
+ const run = ref === null ? null : liveAttemptOf(runtime.store, ref.id, now);
518
+ return run === null ? null : steerControlsFor(runtime.store, ref.externalId, run, now, who?.role === "approver");
519
+ })(),
488
520
  // "publishes as" reads ONLY publicationGrantFor(repo) — the grant
489
521
  // the publisher would act under — never listGrants(), which is
490
522
  // dispatch authority (slice 1c).
@@ -584,6 +616,7 @@ export function createTaskViews(runtime) {
584
616
  deliverable: view.deliverable ?? "branch",
585
617
  revision: view.revision ?? null,
586
618
  coordinator: view.coordinator ?? null,
619
+ origin: view.origin ?? null,
587
620
  repairChain: view.repairChain ?? null,
588
621
  },
589
622
  decisions: (view?.decisions ?? runtime.store.decisionsForTask(ref.id))
@@ -690,7 +723,53 @@ export function createTaskViews(runtime) {
690
723
  return null;
691
724
  return { action: `/r/${runId}/checks`, level: followUps.full ? "full" : "quick", returnTo };
692
725
  }
693
- function taskScreen(response, who, taskId, problem, status, scopeDraft, cancelDraft, editPlan = false) {
726
+ /**
727
+ * One page per task (docs/plans/one-page-task-and-agent.md): the tab the address named, the Result tab's result with
728
+ * every result attempt to pick from, and every link that would open a result page of this family (its /review deep
729
+ * link, a build's /r/<n>) pointed at this page's Result tab instead — the person stays on the task.
730
+ */
731
+ function onePageView(view, family, runs, extras) {
732
+ const root = family?.root.id ?? view.id;
733
+ const current = family?.current.id ?? view.id;
734
+ const versions = new Set(family?.versions.map(one => one.id) ?? [view.id]);
735
+ const results = new Map(runs.filter(run => runIsTaskResult(run) && (extras.admissible === undefined || extras.admissible.has(run.id))).map(run => [run.id, run.taskId]));
736
+ const tabHref = (taskId, run, section, hash) => `${taskHref(root)}?${taskId !== current ? `version=${encodeURIComponent(taskId)}&` : ""}tab=result${run === null ? "" : `&run=${run}`}${section === null || section === "summary" ? "" : `&section=${section}`}${hash}`;
737
+ const toTab = (href) => {
738
+ const review = /^\/review\?result=([A-Za-z0-9._~%-]{1,200})(?:&run=([0-9]{1,15}))?(?:&tab=(summary|changes|checks))?(#[A-Za-z0-9_-]{1,120})?$/.exec(href);
739
+ if (review !== null) {
740
+ const taskId = decodeURIComponent(review[1]);
741
+ if (versions.has(taskId))
742
+ return tabHref(taskId, review[2] === undefined ? null : Number(review[2]), review[3] ?? null, review[4] ?? "");
743
+ }
744
+ const record = /^\/r\/([0-9]{1,15})$/.exec(href);
745
+ if (record !== null && results.has(Number(record[1])))
746
+ return tabHref(results.get(Number(record[1])), Number(record[1]), null, "");
747
+ return href;
748
+ };
749
+ const link = (one) => one === null ? null : { ...one, href: one.href === null ? null : toTab(one.href) };
750
+ const status = view.status === null ? null : { ...view.status, action: view.status.action === null ? null : link(view.status.action),
751
+ status: { ...view.status.status, details: view.status.status.details.map(one => ({ ...one, href: one.href === null ? null : toTab(one.href), action: one.action === null ? null : link(one.action) })) } };
752
+ const selected = extras.result?.selected ?? null;
753
+ const attempts = [...results.entries()].sort((a, b) => b[0] - a[0]).map(([id, taskId]) => {
754
+ const run = runs.find(one => one.id === id);
755
+ const outcome = run.outcome === "built" ? "result" : run.outcome === "no-change" ? "no change" : run.outcome === null ? "never finished" : run.outcome;
756
+ return { label: `${run.role === "scout" ? "Report" : "Build"} #${id} · ${outcome}`, href: tabHref(taskId, id, null, ""), current: selected?.build === id };
757
+ });
758
+ return {
759
+ ...view,
760
+ status,
761
+ ...(view.failure == null ? {} : { failure: { ...view.failure, link: link(view.failure.link) } }),
762
+ ...(view.earlier == null ? {} : { earlier: { ...view.earlier, attempts: view.earlier.attempts.map(one => ({ ...one, href: toTab(one.href) })) } }),
763
+ ...(view.thread === undefined ? {} : { thread: view.thread.map(one => ({ ...one, link: link(one.link) })) }),
764
+ // The previous result's fold moves to the Result tab when there is one.
765
+ sections: selected === null ? view.sections : view.sections.filter(one => one.id !== "result"),
766
+ tab: extras.tab ?? "activity",
767
+ result: selected === null ? null : { selected, current: extras.result.current, attempts,
768
+ // Fresh per-render request tokens (32 hex) are not what it shows: left out, so an unchanged result keeps its stamp.
769
+ revision: createHash("sha256").update(JSON.stringify(selected).replace(/\b[a-f0-9]{32}\b/g, "")).digest("hex").slice(0, 16) },
770
+ };
771
+ }
772
+ function taskScreen(response, who, taskId, problem, status, scopeDraft, cancelDraft, editPlan = false, extras = {}) {
694
773
  const admittedFamily = runtime.familyOf(taskId);
695
774
  if (admittedFamily?.problem != null)
696
775
  return runtime.sendScreen(response, status, screen(admittedFamily.root.title, html `<h1>${admittedFamily.root.title}</h1><p>${admittedFamily.current.state}</p><p class="problem" data-history-problem>${admittedFamily.problem}</p>`, { chrome: runtime.chromeFor(admittedFamily.root.repo, "tasks") }));
@@ -738,10 +817,21 @@ export function createTaskViews(runtime) {
738
817
  catch {
739
818
  return null;
740
819
  } })() } } : {}) } : page.workspace?.view;
820
+ // A refusal that keeps a draft (a scope edit, an armed cancel) or asks for the plan editor reopens on Plan, where the
821
+ // draft and its error are; anything else on Activity unless the address named a tab.
822
+ const tab = extras.tab ?? (scopeDraft !== undefined || cancelDraft !== undefined || editPlan ? "plan" : "activity");
823
+ const oneTaskView = taskView?.kind === "task" ? onePageView(taskView, family, presentedData.family?.runs ?? data.runs.map(run => ({ ...run, taskId: data.task.id })), { ...extras, tab }) : taskView;
824
+ // The Result tab's no-script page, under the task's own (the same HTML its result page serves), and its script.
825
+ if (extras.result != null) {
826
+ page.body = html `${page.body}<section class="task-result-tab" id="task-result" aria-label="Result">${extras.result.html}</section>`;
827
+ // The result's own script, as its result page ships it (a password form there keeps it too: Request changes and
828
+ // the line notes need it).
829
+ page.functional = { script: `${page.functional?.script ?? ""}${reviewEvidenceScript()}${RESULT_REVIEW_SCRIPT}`, fetches: page.functional?.fetches ?? false };
830
+ }
741
831
  if (docked !== null)
742
- page.workspace = { ...page.workspace, ...(taskView === undefined ? {} : { view: taskView }), conversation: docked, pageHtml: page.body };
743
- else if (taskView !== undefined && page.workspace !== undefined)
744
- page.workspace = { ...page.workspace, view: taskView };
832
+ page.workspace = { ...page.workspace, ...(oneTaskView === undefined ? {} : { view: oneTaskView }), conversation: docked, pageHtml: page.body };
833
+ else if (oneTaskView !== undefined && page.workspace !== undefined)
834
+ page.workspace = { ...page.workspace, view: oneTaskView };
745
835
  page.refreshSeconds = runtime.liveRefreshSeconds();
746
836
  return runtime.sendScreen(response, status, page);
747
837
  }