toolroll 0.9.65 → 0.9.68

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 (264) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +20 -2
  3. package/dist/acp-client.js +1 -3
  4. package/dist/agent-board.d.ts +0 -1
  5. package/dist/agent-board.js +0 -1
  6. package/dist/agent-fence.d.ts +69 -2
  7. package/dist/agent-fence.js +294 -11
  8. package/dist/agentconfig.js +4 -1
  9. package/dist/batch-checks.js +3 -2
  10. package/dist/browser/THIRD_PARTY_NOTICES.txt +46 -0
  11. package/dist/browser/terminal.js +134 -23
  12. package/dist/browser/workspace.css +2 -2
  13. package/dist/browser/workspace.js +31 -29
  14. package/dist/builder.d.ts +4 -0
  15. package/dist/builder.js +56 -37
  16. package/dist/canonical-json.d.ts +14 -0
  17. package/dist/canonical-json.js +36 -0
  18. package/dist/chat-actions.js +1 -2
  19. package/dist/chat-admission.d.ts +19 -0
  20. package/dist/chat-admission.js +22 -0
  21. package/dist/chat-channel.js +1 -1
  22. package/dist/chat-delivery.js +0 -1
  23. package/dist/chat-follow.d.ts +17 -0
  24. package/dist/chat-follow.js +32 -0
  25. package/dist/chat-messages.js +0 -1
  26. package/dist/chat-settings-page.d.ts +23 -0
  27. package/dist/chat-settings-page.js +41 -0
  28. package/dist/claim.js +3 -6
  29. package/dist/claude-print.d.ts +12 -0
  30. package/dist/claude-print.js +34 -0
  31. package/dist/cli-http.d.ts +0 -7
  32. package/dist/cli-http.js +0 -21
  33. package/dist/cli.d.ts +3 -10
  34. package/dist/cli.js +15 -24
  35. package/dist/coding-provider.js +2 -1
  36. package/dist/coding-workspace.js +1 -1
  37. package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
  38. package/dist/connect-cli.js +331 -0
  39. package/dist/containment.d.ts +0 -2
  40. package/dist/containment.js +2 -6
  41. package/dist/contracts/chat-actions.d.ts +0 -1
  42. package/dist/contracts/chat-content.d.ts +0 -4
  43. package/dist/contracts/chat-content.js +0 -2
  44. package/dist/contracts/chat-tables.d.ts +0 -1
  45. package/dist/contracts/cli.d.ts +0 -62
  46. package/dist/contracts/cli.js +0 -4
  47. package/dist/contracts/coding-workspace.js +1 -1
  48. package/dist/contracts/console-api.d.ts +1 -0
  49. package/dist/contracts/console-api.js +1 -0
  50. package/dist/contracts/contract.js +1 -1
  51. package/dist/contracts/flow-card.d.ts +0 -1
  52. package/dist/contracts/flow-propose.d.ts +0 -1
  53. package/dist/contracts/flow-send.js +1 -1
  54. package/dist/contracts/gateway-tools.d.ts +0 -1
  55. package/dist/contracts/integration-metadata.js +1 -1
  56. package/dist/contracts/lead-tools.d.ts +0 -1
  57. package/dist/contracts/memory-pass.d.ts +0 -1
  58. package/dist/contracts/memory-pass.js +1 -1
  59. package/dist/contracts/plan.js +1 -1
  60. package/dist/contracts/project-knowledge.js +1 -1
  61. package/dist/contracts/project-skills.d.ts +0 -1
  62. package/dist/contracts/proof.js +1 -1
  63. package/dist/contracts/recipes.js +1 -1
  64. package/dist/contracts/scope.js +1 -1
  65. package/dist/contracts/scout-report.js +1 -1
  66. package/dist/contracts/teams-callback.d.ts +0 -1
  67. package/dist/converse.d.ts +0 -6
  68. package/dist/converse.js +0 -19
  69. package/dist/daemon.js +3 -2
  70. package/dist/decision.d.ts +1 -3
  71. package/dist/deprecations.d.ts +12 -4
  72. package/dist/deprecations.js +27 -4
  73. package/dist/desktop-update-recovery.js +1 -1
  74. package/dist/desktop-update.js +3 -2
  75. package/dist/discord-chat.js +4 -15
  76. package/dist/discord-settings.d.ts +2 -6
  77. package/dist/discord-settings.js +10 -38
  78. package/dist/discord.js +12 -23
  79. package/dist/evidence.d.ts +0 -1
  80. package/dist/evidence.js +0 -3
  81. package/dist/exec.d.ts +0 -9
  82. package/dist/exec.js +3 -81
  83. package/dist/flow-actions.js +0 -3
  84. package/dist/flow-draft.d.ts +1 -3
  85. package/dist/flow-draft.js +15 -28
  86. package/dist/flow-scripts.d.ts +1 -8
  87. package/dist/flow-scripts.js +0 -13
  88. package/dist/flow-send.js +1 -1
  89. package/dist/flow-share.d.ts +0 -2
  90. package/dist/flow-share.js +1 -6
  91. package/dist/flow-steps.js +1 -1
  92. package/dist/flow-triggers.d.ts +0 -1
  93. package/dist/flow-triggers.js +1 -2
  94. package/dist/flow-unattended.js +1 -12
  95. package/dist/flows.js +1 -1
  96. package/dist/health-cli.d.ts +2 -2
  97. package/dist/health-cli.js +1 -1
  98. package/dist/invoke.d.ts +18 -3
  99. package/dist/invoke.js +142 -17
  100. package/dist/is-record.d.ts +2 -0
  101. package/dist/is-record.js +2 -0
  102. package/dist/lead-doors.d.ts +1 -5
  103. package/dist/lead-doors.js +0 -4
  104. package/dist/lead-tools.js +3 -6
  105. package/dist/live-detail.d.ts +0 -1
  106. package/dist/live-detail.js +0 -6
  107. package/dist/live-terminal.d.ts +24 -13
  108. package/dist/live-terminal.js +81 -43
  109. package/dist/live.d.ts +0 -1
  110. package/dist/live.js +1 -2
  111. package/dist/mcp-core.js +0 -2
  112. package/dist/memory-pass.js +1 -1
  113. package/dist/model-catalog.js +1 -1
  114. package/dist/modes.js +1 -12
  115. package/dist/operate.d.ts +2 -0
  116. package/dist/operate.js +436 -745
  117. package/dist/password-bearer.d.ts +0 -2
  118. package/dist/password-bearer.js +0 -4
  119. package/dist/peek-cli.js +2 -1
  120. package/dist/peek.d.ts +2 -26
  121. package/dist/peek.js +2 -168
  122. package/dist/phase-routing.d.ts +0 -4
  123. package/dist/phase-routing.js +2 -21
  124. package/dist/planner-source.d.ts +2 -1
  125. package/dist/planner-source.js +2 -12
  126. package/dist/planner.js +2 -14
  127. package/dist/process-tree.d.ts +0 -2
  128. package/dist/process-tree.js +0 -10
  129. package/dist/project-delete-ui.d.ts +6 -0
  130. package/dist/project-delete-ui.js +20 -1
  131. package/dist/project-delete.d.ts +0 -2
  132. package/dist/project-delete.js +1 -3
  133. package/dist/project-learning.d.ts +0 -14
  134. package/dist/project-skills.d.ts +0 -1
  135. package/dist/project-skills.js +0 -14
  136. package/dist/project-tools.js +2 -10
  137. package/dist/provider.d.ts +0 -17
  138. package/dist/provider.js +0 -26
  139. package/dist/pulls.d.ts +0 -2
  140. package/dist/pulls.js +0 -9
  141. package/dist/release-adapters.d.ts +9 -0
  142. package/dist/release-adapters.js +81 -4
  143. package/dist/release.d.ts +7 -1
  144. package/dist/release.js +20 -7
  145. package/dist/remote-exec.d.ts +3 -2
  146. package/dist/remote-exec.js +3 -2
  147. package/dist/repos.d.ts +0 -5
  148. package/dist/repos.js +2 -10
  149. package/dist/repository-context.d.ts +0 -2
  150. package/dist/repository-context.js +0 -4
  151. package/dist/restart-certification.d.ts +0 -2
  152. package/dist/restart-certification.js +1 -5
  153. package/dist/result-follow-ups.d.ts +1 -4
  154. package/dist/result-follow-ups.js +4 -8
  155. package/dist/result-review.d.ts +0 -2
  156. package/dist/result-review.js +0 -2
  157. package/dist/retention.d.ts +0 -1
  158. package/dist/retention.js +0 -1
  159. package/dist/review-context.d.ts +0 -1
  160. package/dist/review-context.js +1 -12
  161. package/dist/reviewer.d.ts +0 -16
  162. package/dist/reviewer.js +0 -18
  163. package/dist/run-closeout.js +2 -1
  164. package/dist/scope.d.ts +13 -0
  165. package/dist/scope.js +11 -18
  166. package/dist/scout-browser.js +2 -1
  167. package/dist/serve.js +3 -2
  168. package/dist/server/chrome.d.ts +10 -1
  169. package/dist/server/chrome.js +45 -4
  170. package/dist/server/console.css +60 -137
  171. package/dist/server/flows.js +2 -2
  172. package/dist/server/http.d.ts +2 -0
  173. package/dist/server/http.js +5 -0
  174. package/dist/server/pages.js +2 -2
  175. package/dist/server/people-tokens.js +3 -4
  176. package/dist/server/remote.js +1 -6
  177. package/dist/server/render-chat.d.ts +9 -2
  178. package/dist/server/render-chat.js +53 -7
  179. package/dist/server/render-pages.d.ts +28 -3
  180. package/dist/server/render-pages.js +78 -9
  181. package/dist/server/render-results.d.ts +0 -6
  182. package/dist/server/render-results.js +3 -9
  183. package/dist/server/render-tasks.js +6 -6
  184. package/dist/server/route-table.d.ts +0 -4
  185. package/dist/server/route-table.js +1 -6
  186. package/dist/server/settings.js +43 -22
  187. package/dist/server/task-views.js +1 -1
  188. package/dist/server/tasks.js +10 -7
  189. package/dist/setup-guide.d.ts +2 -2
  190. package/dist/skills.d.ts +0 -6
  191. package/dist/skills.js +0 -8
  192. package/dist/slack-chat.d.ts +1 -7
  193. package/dist/slack-chat.js +5 -18
  194. package/dist/slack-settings.d.ts +2 -6
  195. package/dist/slack-settings.js +10 -38
  196. package/dist/slack.js +12 -23
  197. package/dist/sleep.d.ts +2 -0
  198. package/dist/sleep.js +2 -0
  199. package/dist/store.d.ts +11 -74
  200. package/dist/store.js +36 -179
  201. package/dist/structured-output.d.ts +0 -1
  202. package/dist/structured-output.js +0 -1
  203. package/dist/subagent-work.d.ts +0 -2
  204. package/dist/subagent-work.js +0 -2
  205. package/dist/subagents.d.ts +1 -3
  206. package/dist/subagents.js +20 -33
  207. package/dist/subscription-chat.d.ts +1 -3
  208. package/dist/subscription-chat.js +2 -3
  209. package/dist/task-sizing.d.ts +1 -4
  210. package/dist/task-sizing.js +3 -10
  211. package/dist/teams-chat.js +4 -11
  212. package/dist/teams.js +12 -19
  213. package/dist/teardown-git.d.ts +22 -0
  214. package/dist/teardown-git.js +136 -2
  215. package/dist/telegram-lead.js +2 -2
  216. package/dist/telegram.js +1 -1
  217. package/dist/token-notices.d.ts +0 -11
  218. package/dist/token-notices.js +1 -5
  219. package/dist/tool-broker.d.ts +12 -0
  220. package/dist/tool-broker.js +66 -2
  221. package/dist/tool-secret-store.d.ts +0 -3
  222. package/dist/tool-secret-store.js +2 -11
  223. package/dist/toolroll-update.d.ts +0 -2
  224. package/dist/toolroll-update.js +2 -2
  225. package/dist/unfenced-test-support.d.ts +2 -0
  226. package/dist/unfenced-test-support.js +11 -0
  227. package/dist/verification-evidence.d.ts +0 -1
  228. package/dist/verification-evidence.js +0 -1
  229. package/dist/webhooks.d.ts +0 -1
  230. package/dist/webhooks.js +0 -1
  231. package/dist/workspace-ui.d.ts +0 -8
  232. package/dist/workspace-ui.js +1 -6
  233. package/dist/writes-mode.d.ts +9 -0
  234. package/dist/writes-mode.js +8 -0
  235. package/package.json +5 -1
  236. package/dist/contracts/native-census.d.ts +0 -42
  237. package/dist/contracts/native-census.js +0 -36
  238. package/dist/process-recovery-anchor.d.ts +0 -75
  239. package/dist/process-recovery-anchor.js +0 -188
  240. package/dist/process-recovery-coalition.d.ts +0 -25
  241. package/dist/process-recovery-coalition.js +0 -105
  242. package/dist/process-recovery-eligibility.d.ts +0 -179
  243. package/dist/process-recovery-eligibility.js +0 -174
  244. package/dist/process-recovery-native.d.ts +0 -161
  245. package/dist/process-recovery-native.js +0 -595
  246. package/dist/process-recovery-provenance.d.ts +0 -163
  247. package/dist/process-recovery-provenance.js +0 -292
  248. package/dist/process-recovery-services.d.ts +0 -56
  249. package/dist/process-recovery-services.js +0 -191
  250. package/dist/process-recovery-settlement.d.ts +0 -32
  251. package/dist/process-recovery-settlement.js +0 -133
  252. package/dist/process-recovery.d.ts +0 -41
  253. package/dist/process-recovery.js +0 -91
  254. package/dist/session-brief.d.ts +0 -8
  255. package/dist/session-brief.js +0 -24
  256. package/dist/session-cli.d.ts +0 -42
  257. package/dist/session-cli.js +0 -271
  258. package/dist/session-contract.d.ts +0 -183
  259. package/dist/session-contract.js +0 -122
  260. package/dist/session-remote.d.ts +0 -19
  261. package/dist/session-remote.js +0 -86
  262. package/dist/session-service.d.ts +0 -22
  263. package/dist/session-service.js +0 -157
  264. package/dist/team-cli.js +0 -692
@@ -39,14 +39,14 @@
39
39
  :root {
40
40
  --background: var(--so-ground); --foreground: var(--so-ink); --card: var(--so-paper);
41
41
  --muted: var(--so-raised); --muted-foreground: var(--so-muted); --border: var(--so-line); --input: var(--so-input-line);
42
- --primary: var(--so-accent); --primary-foreground: var(--so-on-accent); --secondary: var(--so-raised); --secondary-foreground: var(--so-ink);
43
- --accent: var(--so-soft); --destructive: var(--so-danger); --destructive-strong: var(--so-danger); --destructive-soft: var(--so-danger-soft);
42
+ --primary: var(--so-accent); --primary-foreground: var(--so-on-accent); --secondary: var(--so-raised);
43
+ --accent: var(--so-soft); --destructive: var(--so-danger); --destructive-soft: var(--so-danger-soft);
44
44
  --success: var(--so-success); --success-soft: var(--so-success-soft); --warning: var(--so-warning); --warning-soft: var(--so-warning-soft);
45
45
  --running: var(--so-info); --running-soft: var(--so-info-soft); --ring: var(--so-signal);
46
46
  /* The accent means one thing: a person is needed (approve, answer, decide). */
47
47
  --brand: var(--so-attention); --brand-foreground: var(--so-on-attention); --brand-soft: var(--so-attention-soft);
48
48
  --glass: var(--so-paper); --glass-strong: var(--so-paper); --glass-border: var(--so-line); --glass-highlight: transparent;
49
- --ambient-one: transparent; --ambient-two: transparent; --user-message: var(--so-user-bubble);
49
+ --user-message: var(--so-user-bubble);
50
50
  --surface: var(--so-paper); --ok: var(--so-success); --danger: var(--so-danger); --fg-muted: var(--so-muted);
51
51
  --shadow: none; --shadow-overlay: var(--so-shadow-overlay);
52
52
  --radius: 0.625rem;
@@ -58,7 +58,7 @@
58
58
  and a long press never selects a control's label. Text stays selectable. */
59
59
  html { -webkit-tap-highlight-color: transparent; }
60
60
  a, button, summary, [role=button], [role=tab] { touch-action: manipulation; }
61
- button, [role=button], [role=tab], .task-context-chip, .approval-chip, .verdict-chip { -webkit-user-select: none; user-select: none; }
61
+ button, [role=button], [role=tab], .task-context-chip, .verdict-chip { -webkit-user-select: none; user-select: none; }
62
62
  ::selection { background: var(--so-selection); color: var(--so-ink); }
63
63
  ::placeholder { color: var(--muted-foreground); }
64
64
  body {
@@ -73,23 +73,9 @@
73
73
  /* Scrollbars belong to the theme, not the platform default. */
74
74
  * { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
75
75
 
76
- .topbar {
77
- position: sticky; top: 0; z-index: 10;
78
- border-bottom: 1px solid var(--border); background: var(--background);
79
- }
80
- .topbar-inner {
81
- max-width: 44rem; margin-inline: auto; padding: 0 1.25rem; height: 3.25rem;
82
- display: flex; align-items: center; gap: 1.25rem;
83
- }
84
76
  .brand { font-weight: 600; letter-spacing: -0.01em; color: var(--foreground); text-decoration: none;
85
77
  display: flex; align-items: center; height: 100%; }
86
78
  .brand .dot { color: var(--muted-foreground); }
87
- .topbar nav { display: flex; gap: .25rem; margin-left: auto; height: 100%; }
88
- .topbar nav a {
89
- color: var(--muted-foreground); text-decoration: none; font-size: 0.8125rem; font-weight: 500;
90
- display: flex; align-items: center; padding: 0 .625rem; transition: color .15s;
91
- }
92
- @media (hover: hover) and (pointer: fine) { .topbar nav a:hover { color: var(--foreground); } }
93
79
  main { max-width: 44rem; margin-inline: auto; padding: 1.75rem 1.25rem 4rem; }
94
80
  h1 { font-size: 1.25rem; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 .25rem; line-height: 1.3; }
95
81
  h1 .meta { font-weight: 400; letter-spacing: 0; }
@@ -118,13 +104,6 @@
118
104
  /* Utility classes replacing the old inline style= attributes. */
119
105
  .tight { margin-top: 0; }
120
106
  .card > h2:first-child, .card > h3:first-child { margin-top: 0; }
121
- .w-xs { width: 4.5rem; } .w-sm { width: 8rem; } .w-md { width: 12rem; } .w-lg { width: 14rem; }
122
- .field-cap { width: 100%; max-width: 28rem; }
123
- .field-cap-sm { width: 100%; max-width: 22rem; }
124
- .field-cap-lg { width: 100%; max-width: 34rem; }
125
- .prewrap { white-space: pre-wrap; }
126
- .wrap-any { overflow-wrap: anywhere; }
127
- .grab { cursor: grab; user-select: none; }
128
107
 
129
108
  .palette {
130
109
  position: fixed; top: 18vh; left: 50%; transform: translateX(-50%); width: min(32rem, 90vw);
@@ -298,12 +277,6 @@
298
277
  main:has(.task-intake) { max-width: 68rem; }
299
278
  .task-intake { width: min(100%, 50rem); margin: clamp(1rem, 5vh, 4rem) auto 0; }
300
279
  .task-intake-hero { text-align: center; margin: 0 auto 1.35rem; max-width: 38rem; }
301
- .task-intake-mark {
302
- display: grid; place-items: center; width: 3rem; height: 3rem; margin: 0 auto .85rem;
303
- border: 1px solid var(--border); border-radius: .75rem;
304
- background: var(--card); box-shadow: var(--so-pill-shadow);
305
- font: 600 .72rem/1 var(--font-mono); letter-spacing: -.05em;
306
- }
307
280
  .task-intake-hero h1 { margin: 0; font-size: clamp(1.65rem, 4vw, 2.2rem); letter-spacing: -.045em; }
308
281
  .task-intake-hero p { margin: .45rem 0 0; color: var(--muted-foreground); }
309
282
  .task-composer { padding: .75rem; border-radius: 1.45rem; box-shadow: var(--shadow-overlay); }
@@ -461,8 +434,6 @@
461
434
  .cockpit-row-meta { display: block; color: var(--muted-foreground); font-size: .68rem; overflow-wrap: anywhere; }
462
435
  .cockpit-why { display: block; color: var(--muted-foreground); font-size: .7rem; line-height: 1.35; overflow-wrap: anywhere; }
463
436
  .refusal-back a { display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; }
464
- .cockpit-primary { margin: .85rem 0 1rem; }
465
- .cockpit-primary .button-link { white-space: nowrap; }
466
437
  .cockpit-detail { min-width: 0; }
467
438
  .cockpit-head h1 { margin: .2rem 0 .55rem; color: var(--foreground); font-size: clamp(1.2rem, 2vw, 1.45rem); font-weight: 600; line-height: 1.3; letter-spacing: -.025em; overflow-wrap: anywhere; }
468
439
  .cockpit-chips { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; margin: 0 0 .35rem; }
@@ -500,22 +471,6 @@
500
471
  .cockpit-proof-group { margin: .65rem 0; border: 1px solid var(--glass-border); border-radius: calc(var(--radius) - 3px); background: color-mix(in srgb, var(--glass-strong) 62%, transparent); }
501
472
  .cockpit-proof-group > summary { padding: .65rem .75rem; cursor: pointer; font-weight: 550; }
502
473
  .cockpit-proof-group .criterion-matrix { padding: 0 .75rem .7rem; }
503
- .cockpit-proof-summary { display: grid; gap: .25rem; margin: .15rem 0 .75rem; line-height: 1.45; }
504
- .cockpit-proof-summary strong { font-size: .88rem; }
505
- .cockpit-evidence-group { margin: .65rem 0 0; scroll-margin-top: 6rem; border: 1px solid var(--glass-border); border-radius: calc(var(--radius) - 3px); overflow: hidden; }
506
- .cockpit-evidence-group > summary { display: flex; align-items: center; justify-content: space-between; gap: .75rem; padding: .75rem; cursor: pointer; list-style: none; font-weight: 550; }
507
- .cockpit-evidence-group > summary::-webkit-details-marker { display: none; }
508
- .cockpit-evidence-group > summary > span { flex: none; white-space: nowrap; }
509
- .cockpit-evidence-group > summary small { min-width: 0; color: var(--muted-foreground); font-size: .7rem; font-weight: 400; text-align: right; }
510
- .cockpit-evidence-group [data-primary-evidence] { scroll-margin-top: 6rem; }
511
- .cockpit-evidence-body { padding: 0 .75rem .75rem; border-top: 1px solid var(--glass-border); }
512
- .cockpit-accept { margin-top: .65rem; padding-top: .65rem; border-top: 1px solid var(--glass-border); }
513
- .cockpit-accept > summary { cursor: pointer; font-size: .78rem; font-weight: 600; }
514
- .cockpit-accept[open] > summary { margin-bottom: .55rem; }
515
- .cockpit-accept p { margin: 0 0 .55rem; }
516
- .cockpit-accept-form { display: flex; gap: .5rem; margin: 0; }
517
- .cockpit-accept-form input { margin: 0; min-width: 10rem; }
518
- .cockpit-accept-form button { flex: none; background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); font-weight: 600; }
519
474
  .diff-review {
520
475
  overflow: hidden; margin: .8rem 0 .5rem; border: 1px solid var(--glass-border);
521
476
  border-radius: var(--radius); background: color-mix(in srgb, var(--card) 82%, transparent);
@@ -757,7 +712,6 @@
757
712
  margin: 0; color: var(--foreground); font-size: 1.0625rem; line-height: 1.35;
758
713
  letter-spacing: -.015em;
759
714
  }
760
- .project-add-intro { margin: .25rem 0 0; max-width: 36rem; }
761
715
  .project-add-actions {
762
716
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
763
717
  gap: .625rem; margin-top: 1rem;
@@ -822,7 +776,6 @@
822
776
  .nav-group > summary .chevron { width: .875rem; height: .875rem; flex: none; transition: transform .15s; }
823
777
  .nav-group[open] > summary .chevron { transform: rotate(180deg); }
824
778
  .nav-group .nav-group-items { display: flex; flex-direction: column; gap: .125rem; margin: .125rem 0 .25rem; }
825
- .side .nav-settings { margin-top: .375rem; }
826
779
  .side nav a {
827
780
  position: relative;
828
781
  display: flex; align-items: center; gap: .625rem; padding: .4375rem .625rem; min-height: 2.125rem;
@@ -1051,15 +1004,6 @@
1051
1004
  .task-control[data-task-control="paused"] .eyebrow { color: var(--destructive); }
1052
1005
  .resume-ceremony .row { margin: .3rem 0; }
1053
1006
  @media (max-width: 480px) { .task-control { align-items: stretch; } .task-control-form, .task-control-button { width: 100%; } .task-control-form .task-control-button { width: 100%; } }
1054
- .review-attempts { margin: .55rem 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: .2rem; font-size: .8125rem; }
1055
- .review-attempts li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .45rem; min-width: 0; }
1056
- .review-attempts .review-attempt-ordinal { font-weight: 600; }
1057
- .review-attempts .meta { min-width: 0; overflow-wrap: anywhere; }
1058
- .review-retry-actions { display: flex; align-items: center; flex-wrap: wrap; gap: .55rem; margin-top: .65rem; }
1059
- .review-retry-actions form.inline { margin: 0; }
1060
- .review-retry-button { width: auto; max-width: 100%; white-space: normal; }
1061
- .review-retry-form .review-retry-button { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); font-weight: 600; }
1062
- .review-retry-button[disabled] { opacity: .65; cursor: not-allowed; }
1063
1007
  .proof-review-actions { display: flex; align-items: center; flex-wrap: wrap; gap: .55rem; margin: .65rem 0; }
1064
1008
  .proof-review-actions > .button-link { flex: none; }
1065
1009
  details.proof-exception { margin: 0; }
@@ -1113,9 +1057,6 @@
1113
1057
  .approve-form .recap { margin: .125rem 0 .5rem; }
1114
1058
  .approval-card { padding: 1.2rem 1.3rem; border-radius: calc(var(--radius) + 3px); }
1115
1059
  .approval-card .ceremony-head { align-items: flex-start; padding-bottom: .85rem; border-bottom: 1px solid var(--glass-border); }
1116
- .approval-title { display: grid; gap: .15rem; }
1117
- .approval-kicker { color: var(--muted-foreground); font: 500 .66rem/1.3 var(--font-mono); letter-spacing: .06em; text-transform: uppercase; }
1118
- .approval-title strong { font-size: 1.05rem; letter-spacing: -.02em; }
1119
1060
  .approval-label { display: block; margin: .9rem 0 .2rem; color: var(--muted-foreground); font: 500 12px/1.4 var(--font-sans); }
1120
1061
  .approval-label::first-letter, .planner-plan .eyebrow::first-letter { text-transform: uppercase; }
1121
1062
  .planner-plan .eyebrow { font: 500 12px/1.4 var(--font-sans); letter-spacing: 0; text-transform: none; }
@@ -1124,7 +1065,6 @@
1124
1065
  .approval-boundary { padding: .7rem .8rem; border: 1px solid var(--glass-border); border-radius: calc(var(--radius) - 3px); background: color-mix(in srgb, var(--muted) 45%, transparent); }
1125
1066
  .approval-boundary .approval-label { margin: 0 0 .2rem; }
1126
1067
  .approval-boundary p { margin: 0; color: var(--muted-foreground); font-size: .8rem; overflow-wrap: anywhere; }
1127
- .approval-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin: .8rem 0 .3rem; }
1128
1068
  /* The filed contract (contract handoff, task 1): preserved, amended, or
1129
1069
  drafted from nothing — the same panel on the task page, in the
1130
1070
  ceremony, and on /next, so an amendment is never a surprise after the
@@ -1189,7 +1129,6 @@
1189
1129
  .task-chat-agents-aside .agents-summary { font-size: .74rem; }
1190
1130
  .task-chat-agents-aside .agents-badges { margin-top: .35rem; }
1191
1131
  .task-chat-agents-aside .agents-roles { grid-template-columns: 1fr; }
1192
- .approval-chip { padding: .25rem .6rem; border: 1px solid var(--glass-border); border-radius: 999px; color: var(--muted-foreground); background: var(--glass); font-size: .7rem; }
1193
1132
  .approval-confirm { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .75rem; align-items: end; margin-top: .9rem; padding-top: .8rem; border-top: 1px solid var(--glass-border); }
1194
1133
  .approval-confirm label { margin: 0; }
1195
1134
  .approval-card .approval-confirm button { min-height: 44px; white-space: nowrap; }
@@ -1508,7 +1447,6 @@
1508
1447
  .result-facts dd form { margin: .35rem 0 0; }
1509
1448
  .result-stat { margin: .2rem 0 .4rem; }
1510
1449
  .result-files { margin: .4rem 0 .6rem; }
1511
- .result-label { display: block; margin: .5rem 0 .25rem; font: 600 .6875rem/1.3 var(--font-mono); color: var(--muted-foreground); letter-spacing: .06em; text-transform: uppercase; }
1512
1450
  .result-verdict { align-items: center; }
1513
1451
  .result-verdict > .meta { min-width: 0; overflow-wrap: anywhere; }
1514
1452
  .result-request { margin-top: 1.1rem; padding-top: .9rem; border-top: 1px solid var(--border); }
@@ -1532,7 +1470,6 @@
1532
1470
  .result-links a { font-weight: 550; }
1533
1471
  .result-links a + a::before { content: "·"; margin-right: .5rem; color: var(--muted-foreground); font-weight: 400; }
1534
1472
  .cockpit-detail .result-panel { margin: .85rem 0; }
1535
- .cockpit-accept { margin-top: .75rem; }
1536
1473
  /* The result beside the conversation (package 3): with room, two
1537
1474
  columns and no second auxiliary panel; without it, the dedicated
1538
1475
  result view with Back to chat and the conversation out of the way. */
@@ -1572,29 +1509,17 @@
1572
1509
  toggle share one wrapping line; an opened disclosure is wider than
1573
1510
  its summary, so it wraps onto its own full-width line by itself. */
1574
1511
  .work-row-status { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .875rem; }
1575
- .work-details, .work-details[open] { max-width: 100%; margin: 0; padding: 0; border: 0; background: none; border-radius: 0; }
1576
- .work-details > summary {
1577
- list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: .25rem;
1578
- min-height: 2.25rem; padding: 0 .25rem; margin: 0 -.25rem; border-radius: .375rem;
1579
- font-size: .8125rem; font-weight: 500; color: var(--muted-foreground);
1580
- }
1581
- .work-details > summary::-webkit-details-marker { display: none; }
1582
- .work-details > summary::before { content: "▸"; }
1583
- .work-details[open] > summary::before { content: "▾"; }
1584
- @media (hover: hover) and (pointer: fine) { .work-details > summary:hover { color: var(--foreground); } }
1585
1512
  .work-title { display: block; font-weight: 600; font-size: .9375rem; line-height: 1.35; color: var(--foreground); text-decoration: none; overflow-wrap: anywhere; }
1586
1513
  @media (hover: hover) and (pointer: fine) { .work-title:hover { text-decoration: underline; } }
1587
1514
  .work-meta { display: flex; flex-wrap: wrap; gap: .25rem .625rem; margin: .25rem 0 0; font-size: .75rem; color: var(--muted-foreground); }
1588
1515
  .work-meta .mono { overflow-wrap: anywhere; }
1589
1516
  .project-label { display: inline-block; padding: 0 .4rem; border: 1px solid var(--border); border-radius: 999px; font-size: .6875rem; line-height: 1.5; color: var(--foreground); }
1590
1517
  .work-detail { margin: 0 0 .25rem; font-size: .8125rem; line-height: 1.45; color: var(--muted-foreground); overflow-wrap: anywhere; }
1591
- .work-details .work-id { margin: 0 0 .25rem; }
1592
1518
  .work-action { display: inline-flex; align-items: center; min-height: 44px; font-size: .8125rem; font-weight: 500; }
1593
1519
  .work-empty { padding: 2rem 0 1rem; max-width: 34rem; }
1594
1520
  .work-empty p { margin: 0 0 .75rem; color: var(--muted-foreground); line-height: 1.5; }
1595
1521
  .work-empty .row { display: flex; align-items: center; gap: 1rem; }
1596
1522
  .work-empty .row > a { display: inline-flex; align-items: center; min-height: 44px; }
1597
- .work-bound { margin-top: .75rem; }
1598
1523
  .status-line { display: inline-flex; align-items: center; gap: .45rem; min-width: 0; font-size: .875rem; font-weight: 600; color: var(--foreground); }
1599
1524
  .status-line .status-label { min-width: 0; overflow-wrap: anywhere; }
1600
1525
  .status-line .status-dot { flex: none; width: .5rem; height: .5rem; border-radius: 50%; background: var(--muted-foreground); }
@@ -1629,7 +1554,7 @@
1629
1554
  .work-row { grid-template-columns: minmax(0, 1fr); gap: .125rem; padding: .625rem 0; }
1630
1555
  .work-head { align-items: center; }
1631
1556
  .work-head h1 { margin-bottom: 0; }
1632
- .work-details > summary, .work-action { min-height: 44px; }
1557
+ .work-action { min-height: 44px; }
1633
1558
  /* The tools control sits at the head's right edge on every width, so
1634
1559
  its menu keeps the desk's right-aligned anchor here too. A phone
1635
1560
  override once re-anchored it at left: 0, which pushed the opened
@@ -1771,7 +1696,6 @@
1771
1696
  /* Caution without a claim on the operator: quiet, not magenta. */
1772
1697
  .dot-warn { background: var(--muted-foreground); }
1773
1698
  .dot-off { background: var(--muted-foreground); opacity: .5; }
1774
- .dot-bad { background: var(--destructive); }
1775
1699
  .pulse { animation: pulse 2s ease-in-out infinite; }
1776
1700
  .dot-ok.pulse { background: var(--running); }
1777
1701
  @keyframes pulse { 50% { opacity: .35; } }
@@ -1811,7 +1735,6 @@
1811
1735
 
1812
1736
  @media (max-width: 640px) {
1813
1737
  button, form.option button { min-height: 2.75rem; }
1814
- .topbar nav a { padding: 0 .5rem; }
1815
1738
  }
1816
1739
 
1817
1740
  /* The board: lanes as columns, the pipeline left to right. */
@@ -1825,7 +1748,7 @@
1825
1748
  building, then the rest. A header is a 2.75rem tap; a drawn chevron
1826
1749
  says which way it folds. */
1827
1750
  @media (max-width: 760px) {
1828
- .board { display: flex; flex-direction: column; gap: .5rem; padding-bottom: 0; }
1751
+ .board { display: flex; flex-direction: column; align-items: stretch; gap: .5rem; padding-bottom: 0; }
1829
1752
  .board .lane { min-height: 0; padding: 0 .625rem .125rem; }
1830
1753
  .board .lane > summary {
1831
1754
  cursor: pointer; display: flex; align-items: center; min-height: 2.75rem; margin: 0;
@@ -1864,7 +1787,7 @@
1864
1787
  }
1865
1788
  .lane-attention h2::before { background: var(--brand); opacity: 1; }
1866
1789
  .lane-building h2::before { background: var(--running); opacity: 1; }
1867
- .lane-done h2::before { background: var(--success); opacity: 1; }
1790
+ .lane-done h2::before { background: var(--muted-foreground); opacity: 1; }
1868
1791
  .lane-card {
1869
1792
  display: block; text-decoration: none; color: inherit;
1870
1793
  background: var(--card); border: 1px solid var(--border);
@@ -1891,6 +1814,37 @@
1891
1814
  font-family: var(--font-mono); font-size: .6875rem; color: var(--running); font-variant-numeric: tabular-nums;
1892
1815
  }
1893
1816
  .lane-card .live-line .clock { color: var(--muted-foreground); }
1817
+ /* The build's steps — set up, writing, checking and committing: three quiet
1818
+ ticks, the current one in the running hue. A stage, never a percent. */
1819
+ .lane-card .steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .1875rem; margin-top: .5rem; }
1820
+ .lane-card .steps i { height: .1875rem; border-radius: 999px; background: var(--border); }
1821
+ .lane-card .steps i.on { background: color-mix(in srgb, var(--running) 45%, var(--border)); }
1822
+ .lane-card .steps i.now { background: var(--running); }
1823
+
1824
+ /* The worker strip: one quiet tile per worker above the lanes — what it is
1825
+ building here, the stage, the clock. Ink and lines only; the running dot
1826
+ is the one hue. */
1827
+ .crew { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: .625rem; margin: .25rem 0 .875rem; }
1828
+ .crew-empty { margin: .25rem 0 .875rem; }
1829
+ .worker {
1830
+ border: 1px solid var(--border); border-radius: var(--radius); background: var(--card);
1831
+ padding: .625rem .75rem; min-width: 0; display: flex; flex-direction: column; gap: .375rem;
1832
+ }
1833
+ .worker-busy { border-color: color-mix(in srgb, var(--running) 30%, var(--border)); }
1834
+ .worker-offline { opacity: .6; }
1835
+ .worker-head { display: flex; align-items: center; gap: .5rem; min-width: 0; }
1836
+ .worker-head .name { font-family: var(--font-mono); font-size: .75rem; font-weight: 500; }
1837
+ .worker-head .kind { font-size: .6875rem; color: var(--muted-foreground); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; min-width: 0; }
1838
+ .worker-head .slots { font-family: var(--font-mono); font-size: .6875rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
1839
+ .worker-job { display: flex; flex-direction: column; gap: .0625rem; text-decoration: none; color: inherit; min-width: 0; margin: 0; }
1840
+ .worker-job .t { font-size: .8125rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1841
+ .worker-job .sub { display: flex; justify-content: space-between; gap: .5rem; font-family: var(--font-mono); font-size: .6875rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
1842
+ .worker-job.idle { font-size: .75rem; color: var(--muted-foreground); }
1843
+ @media (hover: hover) and (pointer: fine) { a.worker-job:hover .t { text-decoration: underline; } }
1844
+ @media (max-width: 760px) {
1845
+ .crew { grid-template-columns: minmax(0, 1fr); gap: .375rem; }
1846
+ .worker { padding: .5rem .625rem; }
1847
+ }
1894
1848
  .lane-empty { margin: .75rem 0 .25rem; }
1895
1849
  .plan-doc { white-space: pre-wrap; overflow-wrap: anywhere; font-size: .8125rem; max-height: 24rem; overflow-y: auto; }
1896
1850
  .lane-more { display: block; margin-top: .5rem; font-size: .75rem; }
@@ -1904,23 +1858,6 @@
1904
1858
  .control-room-head h1 { font-size: 1.375rem; margin-top: .08rem; }
1905
1859
  .control-room-head .actions { display: flex; gap: .45rem; flex-wrap: wrap; justify-content: flex-end; }
1906
1860
  .control-room-head .actions a { text-decoration: none; }
1907
- .command-metrics {
1908
- display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
1909
- gap: .625rem; margin: .85rem 0 1.5rem;
1910
- }
1911
- .command-metric {
1912
- border: 1px solid var(--border); border-radius: calc(var(--radius) - 2px); background: var(--card);
1913
- padding: .8rem .9rem; min-width: 0;
1914
- }
1915
- .command-metric .value { display: block; margin-top: .25rem; font-size: 1.5rem; font-weight: 600; line-height: 1.15; font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
1916
- .command-metric .label { display: flex; align-items: center; gap: .4rem; font-weight: 500; font-size: .8125rem; }
1917
- .command-metric .label::before {
1918
- content: ""; width: .375rem; height: .375rem; border-radius: var(--so-radius-full);
1919
- background: var(--muted-foreground); flex: none;
1920
- }
1921
- .command-metric .detail { display: block; color: var(--muted-foreground); font-size: .6875rem; margin-top: .18rem; }
1922
- .command-metric.attention .label::before { background: var(--brand); }
1923
- .command-metric.live .label::before { background: var(--running); }
1924
1861
  .workspace-pulse { margin: .4rem 0 1.5rem; display: grid; grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr)); gap: .625rem; }
1925
1862
  /* The workspace card: name and status word, four counts, the same counts
1926
1863
  as a bar, and the one tap to its board. Neutral border always; the
@@ -1949,17 +1886,8 @@
1949
1886
  .task-chat-context-head { display: flex; align-items: center; justify-content: space-between; gap: .65rem; }
1950
1887
  .task-chat-context h2 { margin: .7rem 0 .25rem; color: var(--foreground); font-size: 1rem; line-height: 1.35; letter-spacing: -.025em; }
1951
1888
  .task-chat-context > .meta { margin: 0; overflow-wrap: anywhere; font-size: .65rem; }
1952
- .task-chat-status { display: grid; gap: .2rem; margin-top: .85rem; padding: .7rem; border-radius: calc(var(--radius) - 3px); background: var(--warning-soft); }
1953
- .task-chat-status.ready { background: var(--running-soft); }
1954
- .task-chat-status strong { font-size: .75rem; }
1955
- .task-chat-status span { color: var(--muted-foreground); font-size: .68rem; line-height: 1.45; }
1956
- .task-chat-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .4rem; margin: .65rem 0; }
1957
- .task-chat-facts div { min-width: 0; padding: .45rem .5rem; border-radius: calc(var(--radius) - 5px); background: color-mix(in srgb, var(--muted) 58%, transparent); }
1958
- .task-chat-facts dt { color: var(--muted-foreground); font: 400 .58rem/1.2 var(--font-mono); text-transform: uppercase; letter-spacing: .04em; }
1959
- .task-chat-facts dd { margin: .15rem 0 0; font-size: .68rem; overflow-wrap: anywhere; }
1960
1889
  .task-chat-overview-actions { display: grid; justify-items: start; gap: .55rem; margin-top: .75rem; }
1961
1890
  .task-chat-overview-link { display: block; font-size: .72rem; text-decoration: none; }
1962
- .task-chat-recovery-link { min-height: 2rem; padding-inline: .7rem; font-size: .7rem; }
1963
1891
  #task-chat-live { display: grid; gap: 1rem; margin-bottom: 1.1rem; }
1964
1892
  .task-journey {
1965
1893
  position: relative; overflow: hidden; padding: 1rem 1.05rem;
@@ -1967,7 +1895,6 @@
1967
1895
  background: var(--card);
1968
1896
  }
1969
1897
  .task-journey::after { content: ""; position: absolute; width: 9rem; height: 9rem; right: -4rem; top: -5rem; border-radius: 50%; background: color-mix(in srgb, var(--accent) 8%, transparent); filter: blur(12px); pointer-events: none; }
1970
- .task-journey-head { position: relative; z-index: 1; display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
1971
1898
  .task-journey h2 { margin: .2rem 0 0; font-size: 1rem; letter-spacing: -.025em; }
1972
1899
  .task-journey > ol { position: relative; z-index: 1; display: grid; grid-template-columns: repeat(5,minmax(0,1fr)); gap: 0; margin: 1rem 0 .75rem; padding: 0; list-style: none; }
1973
1900
  .task-journey > ol::before { content: ""; position: absolute; top: .7rem; left: 10%; right: 10%; height: 1px; background: var(--border); }
@@ -1995,8 +1922,6 @@
1995
1922
  .task-control-copy { min-width: 0; overflow-wrap: anywhere; }
1996
1923
  [data-primary-action] { max-width: 100%; }
1997
1924
 
1998
- .task-live-build { display: flex; align-items: center; flex-wrap: wrap; gap: .3rem; margin: .75rem 0 0; font-size: .72rem; }
1999
- .task-live-build .live-dot { width: .45rem; height: .45rem; border-radius: 50%; background: var(--success); box-shadow: 0 0 0 4px color-mix(in srgb,var(--success) 10%,transparent); }
2000
1925
  .chat-action-card { padding: 0; overflow: hidden; }
2001
1926
  .chat-action-card:not(details) { padding: 1rem 1.05rem; }
2002
1927
  .chat-action-card > summary { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1rem 1.05rem; cursor: pointer; list-style: none; }
@@ -2005,9 +1930,6 @@
2005
1930
  .chat-action-card > summary strong { font-size: .92rem; }
2006
1931
  .chat-action-card > summary small { color: var(--muted-foreground); font-size: .68rem; font-weight: 400; }
2007
1932
  .chat-action-card[open] > summary { border-bottom: 1px solid var(--border); }
2008
- .chat-run-details { padding: .65rem .75rem; border: 1px solid var(--border); border-radius: calc(var(--radius) - 3px); background: color-mix(in srgb,var(--muted) 45%,transparent); }
2009
- .chat-run-details > summary { color: var(--muted-foreground); cursor: pointer; font-size: .68rem; }
2010
- .chat-run-details > .meta { margin-bottom: 0; }
2011
1933
  .chat-decisions { display: grid; gap: .7rem; }
2012
1934
  .chat-section-head { margin: .15rem .15rem 0; }
2013
1935
  .chat-section-head h2 { margin: .2rem 0 0; font-size: 1rem; }
@@ -2453,9 +2375,6 @@
2453
2375
  .workspace-bar .seg.building { background: var(--running); }
2454
2376
  .workspace-bar .seg.waiting { background: var(--muted-foreground); }
2455
2377
  .workspace-bar .seg.queued { background: color-mix(in srgb, var(--muted-foreground) 45%, transparent); }
2456
- .attention-stack { display: grid; gap: .55rem; }
2457
- .attention-stack .decide-card { margin: 0; }
2458
- .attention-stack .q { display: flex; gap: .5rem; align-items: center; justify-content: space-between; }
2459
2378
  .wb-rail-head {
2460
2379
  display: flex; align-items: flex-end; justify-content: space-between; gap: .75rem;
2461
2380
  padding: 0 .15rem .6rem; border-bottom: 1px solid var(--border);
@@ -2481,13 +2400,11 @@
2481
2400
  @media (max-width: 720px) {
2482
2401
  .control-room-head { display: block; }
2483
2402
  .control-room-head .actions { justify-content: flex-start; margin-top: .75rem; }
2484
- .command-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
2485
2403
  .workspace-pulse { grid-template-columns: 1fr; }
2486
2404
  .workspace-stats .pulse-stat { padding: .375rem .125rem; font-size: .625rem; letter-spacing: -.01em; }
2487
2405
  .permission-toggle { grid-template-columns: 1fr; }
2488
2406
  .task-intake { margin-top: .25rem; }
2489
2407
  .task-intake-hero { text-align: left; margin-bottom: .9rem; }
2490
- .task-intake-mark { display: none; }
2491
2408
  .task-composer { margin-inline: 0; padding: .55rem; border-radius: 1.15rem; }
2492
2409
  .task-prompt textarea { min-height: 7.25rem; padding: .75rem; font-size: 1rem; }
2493
2410
  .task-composer-footer { flex-wrap: wrap; }
@@ -2503,8 +2420,6 @@
2503
2420
  .agents-roles { grid-template-columns: 1fr; }
2504
2421
  .agents-role-row { grid-template-columns: 1fr; }
2505
2422
  .approval-card { padding: 1rem; }
2506
- /* The orientation block says the wait on a phone; the kicker would say it twice. */
2507
- .approval-card .approval-kicker { display: none; }
2508
2423
  .approval-boundaries, .approval-confirm { grid-template-columns: 1fr; }
2509
2424
  /* The approval stays IN FLOW on phones: the password field comes
2510
2425
  first and the button follows it — never a sticky control floating
@@ -2539,20 +2454,10 @@
2539
2454
  .queue-card p.row { padding: 0; border-bottom: none; }
2540
2455
  .tracks { margin-top: 1.5rem; }
2541
2456
  .tracks > .hint { margin-bottom: .75rem; }
2542
- .track-row { margin-bottom: .6rem; }
2543
- .track-row p { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
2544
- .track-row p .right { margin-left: auto; }
2545
- .track-row a { text-decoration: none; }
2546
- @media (hover: hover) and (pointer: fine) { .track-row a:hover { text-decoration: underline; } }
2547
- .track-strip { display: inline-flex; gap: .3rem; align-items: center; vertical-align: middle; }
2548
2457
  .fire {
2549
2458
  display: inline-block; width: .65rem; height: .65rem; border-radius: 50%;
2550
2459
  background: var(--muted);
2551
2460
  }
2552
- .fire-ok { background: var(--success); }
2553
- .fire-bad { background: var(--destructive); }
2554
- .fire-live { background: var(--running); animation: pulse 1.6s ease-in-out infinite; }
2555
- .fire-skip { background: transparent; border: 1.5px solid var(--muted); }
2556
2461
 
2557
2462
  /* the phone: fingers, not cursors — tested at 320/390px */
2558
2463
  input, select, textarea { font-size: 16px; }
@@ -2615,8 +2520,6 @@ button { min-height: 44px; }
2615
2520
  .cockpit-disclosure { padding: 0; }
2616
2521
  .cockpit-disclosure > summary { padding: .8rem; }
2617
2522
  .cockpit-disclosure-body { padding: 0 .8rem .85rem; }
2618
- .cockpit-accept-form { display: grid; }
2619
- .cockpit-accept-form button { width: 100%; }
2620
2523
  .diff-review { margin-inline: -.1rem; }
2621
2524
  .diff-review-bar { padding-left: .7rem; }
2622
2525
  .diff-file > summary { padding-inline: .7rem; }
@@ -2674,8 +2577,10 @@ button { min-height: 44px; }
2674
2577
  @media (hover: hover) and (pointer: fine) { .next-pager .skip:hover { border-color: color-mix(in srgb, var(--border) 60%, var(--muted-foreground)); } }
2675
2578
 
2676
2579
  /* Motion: only where a human caused the change — navigation, presses,
2677
- overlays. Liveness swaps stay instant; the pulse dot is the one "alive"
2678
- signal. Everything dies under prefers-reduced-motion; auto-refresh pages
2580
+ overlays — and one exception, the live board: a card that changes lane
2581
+ slides there and a worker that picks up work flashes once (the board's
2582
+ swap, html.so-swap, below). Every other liveness swap stays instant; the
2583
+ pulse dot is the one "alive" signal. Everything dies under prefers-reduced-motion; auto-refresh pages
2679
2584
  opt out of the navigation cross-fade separately (see shell()). */
2680
2585
  @view-transition { navigation: auto; }
2681
2586
  /* A page change fades THROUGH, not across: the old page leaves before the new
@@ -2776,3 +2681,21 @@ button.pick-file { min-height: 1.75rem; padding: 0 .55rem; font-size: .75rem; }
2776
2681
  .meta, .hint { line-height: 1.35; }
2777
2682
  details { margin: var(--so-phone-row) 0; }
2778
2683
  }
2684
+
2685
+ /* The live board's swap (regionScript animate): only cards move — the page
2686
+ itself does not fade — a short slide for a card that changed lane, a drop
2687
+ for a new one, a fade for one that left. Transform and opacity only, and
2688
+ the reduce block kills it with everything else (the script also skips the
2689
+ transition under reduced motion). */
2690
+ html.so-swap::view-transition-old(root), html.so-swap::view-transition-new(root) { animation: none; }
2691
+ html.so-swap::view-transition-group(*) { animation-duration: 320ms; animation-timing-function: cubic-bezier(.2, .8, .2, 1); }
2692
+ html.so-swap::view-transition-new(*):only-child { animation: so-card-in 240ms ease-out both; }
2693
+ html.so-swap::view-transition-old(*):only-child { animation: so-card-out 160ms ease-in both; }
2694
+ @keyframes so-card-in { from { opacity: 0; transform: translateY(-.375rem); } }
2695
+ @keyframes so-card-out { to { opacity: 0; } }
2696
+ .worker.flash { animation: so-worker-claim 900ms ease-out; }
2697
+ @keyframes so-worker-claim { from { background: color-mix(in srgb, var(--running) 14%, var(--card)); } }
2698
+ @media (prefers-reduced-motion: reduce) {
2699
+ html.so-swap::view-transition-group(*), html.so-swap::view-transition-new(*), html.so-swap::view-transition-old(*) { animation: none !important; }
2700
+ .worker.flash { animation: none; }
2701
+ }
@@ -26,7 +26,7 @@ import { keyStatus } from "../keys.js";
26
26
  import { kitPageHtml, kitsGalleryHtml } from "../kits-ui.js";
27
27
  import { addKitGithubTrigger, addKitSample, kitOf, setUpKit } from "../kits.js";
28
28
  import { connectionsOf } from "../mcp-connect.js";
29
- import { authorizedProject, projectName, rowVisible } from "../project.js";
29
+ import { authorizedProject, projectName, } from "../project.js";
30
30
  import { recipeAnswersFromForm, recipeDefinitionPreviewHtml, recipeEditorHtml, recipeFromForm, recipeLibraryHtml, recipeRunHtml, recipeScript, workflowPreviewHtml } from "../recipe-ui.js";
31
31
  import { createWorkflowPreview, exportRecipe, findRecipe, importRecipe, launchWorkflow, prepareRecipeRun, RecipeError, savedRecipes, saveWorkflowRecipe, starterRecipes, workflowPreview } from "../recipes.js";
32
32
  import { isAlive as runnerAlive } from "../runner.js";
@@ -43,7 +43,7 @@ import { redirect, respond, SAFETY, taskHref } from "./http.js";
43
43
  import { html } from "../html.js";
44
44
  import {} from "./session.js";
45
45
  export function createFlowsHandlers(runtime) {
46
- const { store, clock, sendScreen, chromeFor, consoleProjects, options, visible, flowRooms, identify, liveCeiling, providerHome, toolHome, restricted, evidenceRoot, authenticateApprover, authenticateCredential, projectOf, unscopedMode, ceiling, authorizeMutation } = runtime;
46
+ const { store, clock, sendScreen, chromeFor, consoleProjects, options, visible, liveCeiling, providerHome, toolHome, restricted, evidenceRoot, authenticateApprover, authenticateCredential, projectOf, unscopedMode, ceiling, authorizeMutation } = runtime;
47
47
  // ---- reads ---------------------------------------------------------------
48
48
  /** A gallery template's page (Flows → New → Use this): its questions, the preview of these answers, and Create. */
49
49
  function sendGalleryUse(response, who, template, projects, repo, given, name, notice, said = null) {
@@ -245,6 +245,8 @@ export declare function goOutside(response: ServerResponse, to: string, words: s
245
245
  path: string;
246
246
  secure: boolean;
247
247
  }): void;
248
+ /** A form's answer: back to `page` (which may carry its own query) saying `said` or `problem` words, then an anchor. */
249
+ export declare function sayTo(response: ServerResponse, page: string, anchor?: string): (key: "said" | "problem", words: string, at?: string) => void;
248
250
  export declare function redirect(response: ServerResponse, to: string): void;
249
251
  export declare const taskChatHref: (taskId: string) => string;
250
252
  /** A project's own lead thread (v77). */
@@ -115,6 +115,11 @@ export function goOutside(response, to, words, bind) {
115
115
  "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'" });
116
116
  response.end(htmlString(html `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="refresh" content="0;url=${to}"><title>Toolroll</title>${HANDOFF_STYLE}<p>${words} <a href="${to}">Continue</a></p>`));
117
117
  }
118
+ /** A form's answer: back to `page` (which may carry its own query) saying `said` or `problem` words, then an anchor. */
119
+ export function sayTo(response, page, anchor = "") {
120
+ const join = page.includes("?") ? "&" : "?";
121
+ return (key, words, at = anchor) => redirect(response, `${page}${join}${key}=${encodeURIComponent(words)}${at}`);
122
+ }
118
123
  export function redirect(response, to) {
119
124
  // Set as a header (not only in writeHead) so the answer can be read back after: a refused act's redirect names it.
120
125
  response.setHeader("Location", to);
@@ -38,7 +38,7 @@ import { healthWords } from "../server-telemetry.js";
38
38
  import { tally } from "../summary.js";
39
39
  import { serveInstallAsset } from "./pages-assets.js";
40
40
  import { healthz } from "./remote-hooks.js";
41
- import { projectChip, refuse, regionScript, relativeAge, screen, settingsRows, transcriptScript, WORKSPACE_STYLE, workToolRows } from "./chrome.js";
41
+ import { refuse, regionScript, screen, settingsRows, WORKSPACE_STYLE, workToolRows } from "./chrome.js";
42
42
  import { redirect, respond, safeReturn, SAFETY, taskHref } from "./http.js";
43
43
  import { assignmentOf } from "../assignment.js";
44
44
  import { assignmentTaskStatusOf } from "../assignment-presentation.js";
@@ -500,7 +500,7 @@ export function createPagesHandlers(runtime) {
500
500
  return sendScreen(response, 200, screen("Agents", fallback, { chrome: chromeFor(project, "runs"), workspace: { view } }));
501
501
  }
502
502
  async function fleetPage(ctx) {
503
- const { url, who, response, project } = ctx;
503
+ const { url, response, project } = ctx;
504
504
  // Cross-project by design: which agent is on which project is the
505
505
  // question, so this screen is a survey — reads are still filtered
506
506
  // through the ceiling before a card renders. No project needed.
@@ -9,9 +9,8 @@ import { projectName } from "../project.js";
9
9
  import { requestLimitsHtml, tokenLimitWords } from "../request-budget-ui.js";
10
10
  import { authenticateAccount } from "../scope.js";
11
11
  import { SSO_CALLBACK } from "../sso-settings.js";
12
- import { requestContext } from "./request-context.js";
13
12
  import { personChip, refuse, screen } from "./chrome.js";
14
- import { redirect, respond } from "./http.js";
13
+ import { redirect, respond, sayTo } from "./http.js";
15
14
  /** people handlers, moved without changing their route bodies. */
16
15
  import { randomBytes, timingSafeEqual } from "node:crypto";
17
16
  import {} from "node:http";
@@ -40,7 +39,7 @@ export function sameVisitCheck(authTime, startedAt, now) {
40
39
  return Math.min(Math.max(at, startedAt), now);
41
40
  }
42
41
  export function createPeopleHandlers(runtime) {
43
- const { settingsUpdates, joinBySource, ssoOffer, ssoVisits, ssoSettings, options, clock, ssoHandoffs, managedRepos, store, visible, admissionList, sendScreen, chromeFor, restricted, sessions, consoleProjects, authenticateApprover, authenticateCredential, bustBadge, projectOf, signInLinks, linkKey, fromThisComputer, recordSignIn, passwordAllowed, arrival, defaultProject, cookieSecure, consoleOrigin, providerFor, joinSourceOf, signInBudget, signInActor, minutesWords } = runtime;
42
+ const { joinBySource, ssoOffer, ssoVisits, ssoSettings, options, clock, ssoHandoffs, managedRepos, store, visible, admissionList, sendScreen, chromeFor, restricted, sessions, consoleProjects, authenticateApprover, authenticateCredential, bustBadge, projectOf, signInLinks, linkKey, fromThisComputer, recordSignIn, passwordAllowed, arrival, defaultProject, cookieSecure, consoleOrigin, providerFor, joinSourceOf, signInBudget, signInActor, minutesWords } = runtime;
44
43
  // v101: Settings → Sessions & tokens: where you're signed in, and your API tokens. An instance operator can see everyone's.
45
44
  async function sessionsPage(ctx) {
46
45
  const { url, who, response, project } = ctx;
@@ -167,7 +166,7 @@ ${reauth(html `<label>Password <input type="password" name="token" autocomplete=
167
166
  if (who.via !== "cookie")
168
167
  return refuse(response, who, 403, "Sign in in a browser to manage sessions and tokens.", "/settings");
169
168
  const operator = store.isInstanceOperator(who.name);
170
- const back = (key, words) => redirect(response, `/settings/sessions?${body.get("everyone") === "1" && operator ? "everyone=1&" : ""}${key}=${encodeURIComponent(words)}`);
169
+ const back = sayTo(response, `/settings/sessions${body.get("everyone") === "1" && operator ? "?everyone=1" : ""}`);
171
170
  const action = body.get("action") ?? "";
172
171
  if (action === "end-session") {
173
172
  const target = store.webSessions(null).find(one => one.idHash === (body.get("session") ?? ""));
@@ -7,9 +7,7 @@ import { CodingWorkspace } from '../coding-workspace.js';
7
7
  import { createMcpHttp } from "../mcp-http.js";
8
8
  import { createOAuthHttp, resourceMetadataUrl } from "../mcp-oauth.js";
9
9
  import { reproveRemote } from "../operate-remote.js";
10
- import { rowVisible } from "../project.js";
11
10
  import { RequestBudget, SourceAdmission } from "../request-budget.js";
12
- import { createSessionRunner } from '../session-remote.js';
13
11
  import {} from "../store.js";
14
12
  import { handleTeamHttp, TEAM_PASSWORD_REVERIFY_MS } from '../team-http.js';
15
13
  import { handleTeamsHttp } from "../teams.js";
@@ -17,7 +15,7 @@ import { flowHook, telegramHook } from "./remote-hooks.js";
17
15
  import { respond } from "./http.js";
18
16
  import {} from "./session.js";
19
17
  export function createRemoteHandlers(runtime) {
20
- const { identify, store, admitPasswordSource, admitBearer, allowedHost, team, teamBrowserReply, requestBudget, options, coding, codingProjects, liveCeiling, clock, evidenceRoot, consoleOrigin, managedRepos, joinSourceOf, ssoSettings, oauthTokenBudget, hookContext, teamsSourceBudget, teamsTenantBudget } = runtime;
18
+ const { identify, store, admitPasswordSource, admitBearer, allowedHost, team, teamBrowserReply, requestBudget, options, clock, evidenceRoot, consoleOrigin, managedRepos, joinSourceOf, ssoSettings, oauthTokenBudget, hookContext, teamsSourceBudget, teamsTenantBudget } = runtime;
21
19
  /**
22
20
  * The full principal a live `so_` API token stands for: its person and their generation, the token's own read/act
23
21
  * scope, its project limit narrowed by the person's current access, and its id. Every route that accepts an API token
@@ -97,15 +95,12 @@ export function createRemoteHandlers(runtime) {
97
95
  return who?.via === 'cookie' ? teamBrowserReply(reply, actor, who.session.csrf) : reply;
98
96
  },
99
97
  });
100
- // D7: native session operations are remote commands too (`session …` over /api/cli), run by the session owner.
101
- const sessionRunner = createSessionRunner({ store, workspace: coding, projects: codingProjects, projectAllowed: repo => rowVisible(liveCeiling(), repo), clock });
102
98
  // Remote CLI: one live API token names the person; the shared command boundary decides everything else.
103
99
  const cliEndpoint = (request, response) => handleCliHttp(request, response, {
104
100
  admit: principal => requestBudget.admit(principal.tokenId, "api"),
105
101
  authenticate: tokenPrincipalOf,
106
102
  run: async () => options.cliRunner ?? (await import("../operate.js")).runOperateAs ?? null,
107
103
  modeOf: options.cliModeOf,
108
- session: sessionRunner,
109
104
  store,
110
105
  });
111
106
  const mcpHttp = createMcpHttp({ store, clock, evidenceRoot, ...(options.requestBudgetClock === undefined ? {} : { requestBudgetClock: options.requestBudgetClock }), signedIn: request => identify(request, true, true) !== null, admit: person => requestBudget.admit(person.principal.tokenId, "mcp"),
@@ -142,9 +142,16 @@ export declare function leadPromptStarters(focus?: TaskChatFocus | null): Html;
142
142
  export declare function chatHeading(copy: string, projectCount: number, showProjectToggle?: boolean): Html;
143
143
  /** A deliberately small rich-text grammar for model copy. The reply is shaped first (the lead's voice, enforced:
144
144
  * no headers, at most three bold anchors, labelled links, no internal ids unless `asked` wanted them); input is
145
- * escaped before tags are introduced: bullets, numbered steps, bold, inline code and labelled http(s) links are
146
- * presentation only—never executable HTML. */
145
+ * escaped before tags are introduced: bullets, numbered steps, bold, inline code, fenced code blocks and labelled
146
+ * http(s) links are presentation only—never executable HTML. */
147
147
  export declare function renderChatText(raw: string, asked?: string): Html;
148
+ /** A line comment from the chat Workbench (src/browser/line-steer.tsx steerMessage): On "<file, as a JSON string>":<line>
149
+ * [ (removed line)] (run #<n>): <comment>, then a blank line and "> <the line>" (just ">" for a blank line, once the
150
+ * chat POST has trimmed the message), always last.
151
+ * The words the lead reads stay exactly as sent; the thread shows them in three parts: a small reference line, the quoted
152
+ * code as written, and the comment. Null for any other message, which keeps its own plain rendering. Everything is
153
+ * escaped by the html template. */
154
+ export declare function steerCommentHtml(text: string): Html | null;
148
155
  export declare function chatActivity(activity: string | null): Html;
149
156
  export declare const CHAT_UI_SCRIPT: string;
150
157
  /** Provider, model, and limits under ONE disclosure (UI polish