toolroll 0.9.63 → 0.9.65

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 (292) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +15 -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 +50 -2
  16. package/dist/agent-fence.js +157 -10
  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 +36 -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/browser/THIRD_PARTY_NOTICES.txt +48 -0
  32. package/dist/browser/terminal.css +37 -0
  33. package/dist/browser/terminal.js +117 -0
  34. package/dist/browser/workspace.css +2 -2
  35. package/dist/browser/workspace.js +42 -38
  36. package/dist/browser-shell.js +6 -3
  37. package/dist/browser-workspace.d.ts +235 -1
  38. package/dist/browser-workspace.js +60 -0
  39. package/dist/builder.d.ts +6 -1
  40. package/dist/builder.js +34 -13
  41. package/dist/chat-actions.d.ts +13 -1
  42. package/dist/chat-actions.js +197 -15
  43. package/dist/chat-ask.d.ts +4 -0
  44. package/dist/chat-ask.js +14 -0
  45. package/dist/chat-channel.js +20 -0
  46. package/dist/chat-continuity.js +5 -4
  47. package/dist/chat-decide.d.ts +0 -14
  48. package/dist/chat-decide.js +4 -1
  49. package/dist/chat-delivery.d.ts +9 -0
  50. package/dist/chat-delivery.js +45 -6
  51. package/dist/chat-messages.d.ts +8 -0
  52. package/dist/chat-messages.js +36 -4
  53. package/dist/check-levels-ui.js +2 -2
  54. package/dist/checkout-proof.d.ts +28 -0
  55. package/dist/checkout-proof.js +62 -0
  56. package/dist/claim.d.ts +12 -0
  57. package/dist/claim.js +19 -0
  58. package/dist/cli-help.d.ts +1 -1
  59. package/dist/cli-help.js +4 -1
  60. package/dist/cli.d.ts +3 -0
  61. package/dist/cli.js +21 -2
  62. package/dist/coding-ui.js +6 -4
  63. package/dist/console-instance.d.ts +124 -0
  64. package/dist/console-instance.js +337 -0
  65. package/dist/contracts/chat-actions.d.ts +16 -0
  66. package/dist/contracts/chat-actions.js +4 -0
  67. package/dist/contracts/chat-content.d.ts +19 -0
  68. package/dist/contracts/chat-content.js +6 -0
  69. package/dist/contracts/console-api.d.ts +12 -4
  70. package/dist/contracts/console-api.js +12 -4
  71. package/dist/contracts/flow-card.d.ts +4 -0
  72. package/dist/contracts/flow-card.js +3 -0
  73. package/dist/contracts/flow.d.ts +46 -0
  74. package/dist/contracts/flow.js +11 -2
  75. package/dist/contracts/lead-tools.d.ts +92 -0
  76. package/dist/contracts/lead-tools.js +13 -0
  77. package/dist/contracts/provider.d.ts +11 -1
  78. package/dist/contracts/provider.js +1 -1
  79. package/dist/contracts/route.d.ts +76 -1
  80. package/dist/contracts/route.js +1 -1
  81. package/dist/contracts/store-json.d.ts +2 -0
  82. package/dist/control-setup.d.ts +1 -1
  83. package/dist/control-ui.js +2 -2
  84. package/dist/converse.d.ts +16 -0
  85. package/dist/converse.js +32 -4
  86. package/dist/demo-chat.js +4 -4
  87. package/dist/demo.js +2 -2
  88. package/dist/desktop-update-gate.js +6 -0
  89. package/dist/desktop-update.d.ts +1 -1
  90. package/dist/discord-chat.js +4 -1
  91. package/dist/doctor.js +1 -1
  92. package/dist/egress-bridge.d.ts +17 -0
  93. package/dist/egress-bridge.js +111 -0
  94. package/dist/egress.d.ts +119 -0
  95. package/dist/egress.js +423 -0
  96. package/dist/exec.d.ts +78 -0
  97. package/dist/exec.js +367 -9
  98. package/dist/export-ui.js +2 -2
  99. package/dist/flow-engine.js +24 -5
  100. package/dist/flow-gallery-ui.js +2 -2
  101. package/dist/flow-gallery.js +27 -8
  102. package/dist/flow-share.js +16 -5
  103. package/dist/flow-triggers.d.ts +77 -8
  104. package/dist/flow-triggers.js +517 -70
  105. package/dist/flows-cli.js +8 -2
  106. package/dist/flows-ui.d.ts +0 -2
  107. package/dist/flows-ui.js +52 -4
  108. package/dist/flows.d.ts +4 -0
  109. package/dist/flows.js +9 -1
  110. package/dist/html.d.ts +9 -0
  111. package/dist/html.js +11 -0
  112. package/dist/invoke.d.ts +17 -0
  113. package/dist/invoke.js +217 -21
  114. package/dist/keys.d.ts +18 -14
  115. package/dist/keys.js +5 -0
  116. package/dist/lead-capabilities.js +3 -2
  117. package/dist/lead-contract.js +2 -0
  118. package/dist/lead-doors.d.ts +2 -0
  119. package/dist/lead-doors.js +7 -0
  120. package/dist/lead-follow.d.ts +2 -0
  121. package/dist/lead-follow.js +58 -2
  122. package/dist/lead-progress.js +1 -0
  123. package/dist/lead-queue-runtime.d.ts +46 -0
  124. package/dist/lead-queue-runtime.js +191 -0
  125. package/dist/lead-queue.d.ts +104 -0
  126. package/dist/lead-queue.js +187 -0
  127. package/dist/lead-sweep.d.ts +67 -0
  128. package/dist/lead-sweep.js +99 -0
  129. package/dist/lead-tools.d.ts +22 -0
  130. package/dist/lead-tools.js +152 -1
  131. package/dist/lead.d.ts +13 -5
  132. package/dist/lead.js +89 -29
  133. package/dist/limits-ui.d.ts +8 -1
  134. package/dist/limits-ui.js +26 -0
  135. package/dist/live-bus.d.ts +8 -0
  136. package/dist/live-bus.js +40 -4
  137. package/dist/live-detail.d.ts +27 -0
  138. package/dist/live-detail.js +57 -0
  139. package/dist/live-notes.d.ts +20 -0
  140. package/dist/live-notes.js +47 -0
  141. package/dist/live-records.d.ts +140 -0
  142. package/dist/live-records.js +935 -0
  143. package/dist/live-terminal.d.ts +258 -0
  144. package/dist/live-terminal.js +649 -0
  145. package/dist/live.d.ts +38 -27
  146. package/dist/live.js +142 -124
  147. package/dist/mcp-oauth.d.ts +6 -3
  148. package/dist/mcp-oauth.js +8 -4
  149. package/dist/memory-pass.js +2 -2
  150. package/dist/mobile-viewport.d.ts +5 -1
  151. package/dist/mobile-viewport.js +18 -2
  152. package/dist/model-catalog.js +4 -0
  153. package/dist/monitoring-ui.js +2 -2
  154. package/dist/needs-you.d.ts +1 -1
  155. package/dist/needs-you.js +11 -4
  156. package/dist/operate.d.ts +2 -0
  157. package/dist/operate.js +275 -17
  158. package/dist/passwordless.d.ts +23 -0
  159. package/dist/passwordless.js +26 -0
  160. package/dist/peek-cli.d.ts +12 -4
  161. package/dist/peek-cli.js +19 -11
  162. package/dist/phase-routing.js +10 -3
  163. package/dist/planner-source.d.ts +3 -0
  164. package/dist/planner-source.js +14 -0
  165. package/dist/planner.d.ts +2 -1
  166. package/dist/planner.js +7 -1
  167. package/dist/policy-ui.js +2 -2
  168. package/dist/policy.d.ts +1 -20
  169. package/dist/policy.js +29 -2
  170. package/dist/project-delete-ui.d.ts +12 -0
  171. package/dist/project-delete-ui.js +41 -9
  172. package/dist/project-delete.js +2 -2
  173. package/dist/project-memory.d.ts +3 -1
  174. package/dist/project-memory.js +57 -25
  175. package/dist/project-tools.d.ts +5 -3
  176. package/dist/project-tools.js +12 -9
  177. package/dist/provider-auth.d.ts +4 -3
  178. package/dist/provider-auth.js +5 -4
  179. package/dist/provider-connection.d.ts +2 -1
  180. package/dist/provider-connection.js +5 -0
  181. package/dist/provider.d.ts +25 -2
  182. package/dist/provider.js +63 -9
  183. package/dist/pull-request-ui.js +2 -2
  184. package/dist/push.js +1 -1
  185. package/dist/quick-start.d.ts +24 -0
  186. package/dist/quick-start.js +127 -0
  187. package/dist/release.d.ts +3 -0
  188. package/dist/release.js +14 -8
  189. package/dist/request-budget-ui.js +2 -2
  190. package/dist/result-review.d.ts +1 -1
  191. package/dist/result-review.js +9 -3
  192. package/dist/retention-ui.js +2 -2
  193. package/dist/retention.js +10 -1
  194. package/dist/run-efficiency.d.ts +73 -0
  195. package/dist/run-efficiency.js +228 -0
  196. package/dist/runner.d.ts +5 -1
  197. package/dist/runner.js +22 -1
  198. package/dist/scope.d.ts +170 -5
  199. package/dist/scope.js +251 -31
  200. package/dist/scout-net.d.ts +4 -1
  201. package/dist/scout-net.js +37 -12
  202. package/dist/scout.d.ts +20 -6
  203. package/dist/scout.js +27 -3
  204. package/dist/serve.js +247 -40
  205. package/dist/server/chat.d.ts +1 -1
  206. package/dist/server/chat.js +122 -31
  207. package/dist/server/chrome.d.ts +6 -1
  208. package/dist/server/chrome.js +18 -7
  209. package/dist/server/console.css +52 -38
  210. package/dist/server/flows.js +38 -8
  211. package/dist/server/guards.d.ts +1 -0
  212. package/dist/server/guards.js +23 -9
  213. package/dist/server/http.d.ts +7 -0
  214. package/dist/server/http.js +3 -2
  215. package/dist/server/live.d.ts +34 -1
  216. package/dist/server/live.js +39 -17
  217. package/dist/server/pages-assets.js +5 -0
  218. package/dist/server/pages.js +90 -13
  219. package/dist/server/people-tokens.d.ts +3 -0
  220. package/dist/server/people-tokens.js +55 -17
  221. package/dist/server/remote-hooks.js +12 -6
  222. package/dist/server/remote.d.ts +2 -1
  223. package/dist/server/remote.js +14 -5
  224. package/dist/server/render-chat.d.ts +12 -3
  225. package/dist/server/render-chat.js +47 -16
  226. package/dist/server/render-pages.d.ts +4 -1
  227. package/dist/server/render-pages.js +5 -4
  228. package/dist/server/render-people.d.ts +3 -2
  229. package/dist/server/render-people.js +3 -2
  230. package/dist/server/render-results.d.ts +18 -2
  231. package/dist/server/render-results.js +37 -28
  232. package/dist/server/render-settings.js +6 -5
  233. package/dist/server/render-tasks.d.ts +35 -10
  234. package/dist/server/render-tasks.js +110 -27
  235. package/dist/server/request-context.d.ts +2 -0
  236. package/dist/server/request-context.js +2 -1
  237. package/dist/server/route-table.d.ts +3 -0
  238. package/dist/server/route-table.js +46 -5
  239. package/dist/server/runtime.d.ts +33 -2
  240. package/dist/server/session.d.ts +25 -1
  241. package/dist/server/session.js +18 -4
  242. package/dist/server/settings.js +121 -10
  243. package/dist/server/sweeps.d.ts +4 -0
  244. package/dist/server/sweeps.js +58 -0
  245. package/dist/server/task-views.d.ts +2 -1
  246. package/dist/server/task-views.js +96 -6
  247. package/dist/server/tasks.js +320 -74
  248. package/dist/server/terminals.d.ts +31 -0
  249. package/dist/server/terminals.js +365 -0
  250. package/dist/setup-guide.js +3 -2
  251. package/dist/slack-chat.js +4 -1
  252. package/dist/spend-ui.js +3 -3
  253. package/dist/spend.js +2 -1
  254. package/dist/steer-capabilities.d.ts +32 -0
  255. package/dist/steer-capabilities.js +60 -0
  256. package/dist/steer-controls.d.ts +14 -0
  257. package/dist/steer-controls.js +36 -0
  258. package/dist/steer-status.js +2 -2
  259. package/dist/storage-ui.js +4 -4
  260. package/dist/store-baseline.d.ts +1 -1
  261. package/dist/store-baseline.js +1 -1
  262. package/dist/store.d.ts +430 -15
  263. package/dist/store.js +1041 -52
  264. package/dist/subagent-desk.js +1 -1
  265. package/dist/summary.js +3 -0
  266. package/dist/task-source.d.ts +58 -0
  267. package/dist/task-source.js +93 -0
  268. package/dist/team-leads.js +1 -1
  269. package/dist/teams-chat.js +4 -1
  270. package/dist/telegram-lead.d.ts +3 -0
  271. package/dist/telegram-lead.js +44 -7
  272. package/dist/telegram.d.ts +11 -2
  273. package/dist/telegram.js +51 -17
  274. package/dist/terminal-catalog.d.ts +11 -0
  275. package/dist/terminal-catalog.js +129 -0
  276. package/dist/text-limits.d.ts +2 -0
  277. package/dist/text-limits.js +2 -0
  278. package/dist/tool-broker.d.ts +2 -0
  279. package/dist/tool-broker.js +1 -0
  280. package/dist/toolroll-update-ui.js +2 -2
  281. package/dist/toolroll-update.js +13 -3
  282. package/dist/tools-ui.js +2 -2
  283. package/dist/webhook-limits.d.ts +23 -0
  284. package/dist/webhook-limits.js +30 -0
  285. package/dist/webhook-readers.d.ts +56 -0
  286. package/dist/webhook-readers.js +138 -0
  287. package/dist/webhook-signing.d.ts +27 -0
  288. package/dist/webhook-signing.js +119 -0
  289. package/dist/work-index.d.ts +29 -0
  290. package/dist/work-index.js +16 -2
  291. package/dist/workspace-revision.js +3 -1
  292. package/package.json +11 -2
@@ -4,6 +4,9 @@ import { readFileSync } from 'node:fs';
4
4
  import {} from './browser-workspace.js';
5
5
  import { html, jsonScript, replaceMarkup, scriptElement } from './html.js';
6
6
  const assets = new Map();
7
+ /** The fixed public assets: the workspace, Live mode's own small bundle, and the notices. */
8
+ const BROWSER_ASSETS = [['workspace.js', 'text/javascript; charset=utf-8'], ['workspace.css', 'text/css; charset=utf-8'], ['terminal.js', 'text/javascript; charset=utf-8'],
9
+ ['terminal.css', 'text/css; charset=utf-8'], ['THIRD_PARTY_NOTICES.txt', 'text/plain; charset=utf-8']];
7
10
  function readAsset(name) {
8
11
  // Compiled installation first; the second location supports source-based
9
12
  // development/tests after the browser build. No request supplies a file path.
@@ -16,9 +19,9 @@ function readAsset(name) {
16
19
  return null;
17
20
  }
18
21
  export function browserAssetsAvailable() {
19
- if (assets.size === 3)
22
+ if (assets.size === BROWSER_ASSETS.length)
20
23
  return true;
21
- for (const [name, type] of [['workspace.js', 'text/javascript; charset=utf-8'], ['workspace.css', 'text/css; charset=utf-8'], ['THIRD_PARTY_NOTICES.txt', 'text/plain; charset=utf-8']]) {
24
+ for (const [name, type] of BROWSER_ASSETS) {
22
25
  const body = readAsset(name);
23
26
  if (body === null)
24
27
  return false;
@@ -27,7 +30,7 @@ export function browserAssetsAvailable() {
27
30
  return true;
28
31
  }
29
32
  export function serveBrowserAsset(request, response, path) {
30
- if (path !== '/assets/workspace.js' && path !== '/assets/workspace.css' && path !== '/assets/THIRD_PARTY_NOTICES.txt')
33
+ if (!BROWSER_ASSETS.some(([name]) => path === `/assets/${name}`))
31
34
  return false;
32
35
  if (request.method !== 'GET' && request.method !== 'HEAD') {
33
36
  response.writeHead(405, { allow: 'GET, HEAD', 'cache-control': 'no-store' });
@@ -1,6 +1,7 @@
1
1
  /** The browser consumes this contract with a type-only import. All HTML is
2
2
  * produced by the existing trusted server renderers, never by a model-supplied
3
3
  * fragment. These projections do not authenticate, mutate or grant authority. */
4
+ import { type DisplayState, type StateCounts } from './agent-state-words.js';
4
5
  import type { TeamSnapshot } from './team-contract.js';
5
6
  import type { RunActivity } from './activity-line.js';
6
7
  import type { BrandIconId } from './brand-icons.js';
@@ -21,6 +22,50 @@ export type BrowserProject = {
21
22
  href: string;
22
23
  knowledgeHref: string;
23
24
  };
25
+ /** A project's state for one person (agent-state.ts): the strongest of its counts, the counts, and its tasks with
26
+ * something live, asking, failed or unread (strongest first, `more` beyond those shown). `path` null: work with no
27
+ * project yet. */
28
+ export type BrowserProjectState = {
29
+ path: string | null;
30
+ name: string;
31
+ href: string;
32
+ state: DisplayState;
33
+ counts: StateCounts;
34
+ tasks: BrowserRailTask[];
35
+ more: number;
36
+ /** Everything here that needs this person, as opaque keys (one per obligation and version: the chime's subjects),
37
+ * with the task and agent each is about so a tab already showing one stays quiet. */
38
+ asks: BrowserAsk[];
39
+ /** The projects folded together to keep a snapshot inside one live frame: their counts summed, `more` of them. */
40
+ others?: true;
41
+ };
42
+ /** The most a summary snapshot may take in UTF-8 bytes, as the summary room sends it (JSON inside JSON, plus the
43
+ * frame's envelope), well inside one live frame (live-bus.ts STREAM_LIMIT_BYTES). */
44
+ export declare const SUMMARY_BUDGET = 24000;
45
+ /** A snapshot's size on the wire: UTF-8 bytes of the JSON string the frame carries, plus its envelope. */
46
+ export declare function summaryFrameBytes(value: unknown): number;
47
+ /**
48
+ * A summary snapshot that fits one live frame, with every count kept: long words are clipped, then rail tasks become
49
+ * `more` (from the project listing most), then the quietest projects fold into one `others` entry with their counts
50
+ * summed. Nothing is dropped silently: whatever is not listed is counted.
51
+ */
52
+ export declare function boundProjectSummary(rows: readonly BrowserProjectState[], budget?: number): BrowserProjectState[];
53
+ /** One thing that needs this person, as the chime knows it: an opaque key (never words), and what it is about. */
54
+ export type BrowserAsk = {
55
+ key: string;
56
+ task: string | null;
57
+ run: number | null;
58
+ /** When it last changed (ISO): one older than the chime's memory reaches back is never news. */
59
+ at: string;
60
+ };
61
+ /** One task on the rail: its state, and its agent's last act when one is at work (else the state's words). */
62
+ export type BrowserRailTask = {
63
+ id: string;
64
+ title: string;
65
+ href: string;
66
+ state: DisplayState;
67
+ line: string;
68
+ };
24
69
  /** count: tasks waiting on a person, shown beside Tasks when above zero. */
25
70
  export type BrowserNavigationItem = {
26
71
  label: string;
@@ -185,6 +230,14 @@ export type BrowserLimitTile = {
185
230
  export type BrowserLimits = {
186
231
  tiles: BrowserLimitTile[];
187
232
  };
233
+ /** The sidebar's usage chip (limits-ui.ts usageChipOf): the most-used subscription window, stale when its reading is old. */
234
+ export type BrowserUsageChip = {
235
+ text: string;
236
+ tone: BrowserLimitTile['tone'];
237
+ stale: boolean;
238
+ href: string;
239
+ title: string;
240
+ };
188
241
  export type BrowserSettingsView = {
189
242
  kind: 'settings';
190
243
  said: string | null;
@@ -440,6 +493,18 @@ export type BrowserTaskView = {
440
493
  action: string;
441
494
  run: number;
442
495
  } | null;
496
+ /** The running attempt's composer (steer-controls.ts): what it takes, said plainly, and its note box when it takes
497
+ * notes. Pause and Stop stay as `pause` and `stop`. */
498
+ steering?: {
499
+ controls: BrowserAgentControls;
500
+ steer: {
501
+ action: string;
502
+ run: number;
503
+ } | null;
504
+ } | null;
505
+ /** This task's latest attempts, newest first: a draft this tab wrote for one that has since ended shows with a way
506
+ * to send it to the next attempt. */
507
+ steerRecent?: number[];
443
508
  /** Pause and Pause and steer, beside Stop: the same exact live Claude build, one request id per form (Pause, the note,
444
509
  * Resume), where its latest pause stands (null: running), and until when a paused build can wait before it ends. */
445
510
  pause?: {
@@ -486,7 +551,47 @@ export type BrowserTaskView = {
486
551
  details?: BrowserTaskDetailGroup[];
487
552
  /** Where to message the agent when this page carries no conversation (null: messaging isn't available here). */
488
553
  chatHref?: string | null;
554
+ /** One page per task (docs/plans/one-page-task-and-agent.md): the tab the address names (Activity when none). */
555
+ tab?: BrowserTaskTab;
556
+ /** The Result tab: the selected attempt's result exactly as its result page shows it, whether it is the task's
557
+ * current one (the header then owns its status and ceremonies), and every result attempt to pick from. Null: none yet. */
558
+ result?: {
559
+ selected: NonNullable<BrowserResultView["selected"]>;
560
+ current: boolean;
561
+ attempts: {
562
+ label: string;
563
+ href: string;
564
+ current: boolean;
565
+ }[];
566
+ /** What the result shows, as one stamp (every check, note and control in it; never a form's fresh request token): the
567
+ * page holds the result it loaded and says when a newer one is ready, so its evidence and decision always agree. */
568
+ revision: string;
569
+ } | null;
570
+ /** Where the approval sits: under the header when it is the thing to do now, else at the top of the Plan tab. */
571
+ approvalPlacement?: "lead" | "plan";
572
+ /** ⋯ More actions: the task's secondary acts, the same order on the task and agent pages, destructive last. */
573
+ more?: BrowserTaskMore[];
489
574
  };
575
+ export type BrowserTaskTab = "activity" | "result" | "plan";
576
+ /** One ⋯ item: a plain link, a copy of this page's address, a one-click POST (never a ceremony), or a go-to that switches
577
+ * to the tab holding the item's own form, opens it and focuses it (`target`: that element's id). */
578
+ export type BrowserTaskMore = {
579
+ group: "work" | "share" | "danger";
580
+ label: string;
581
+ } & ({
582
+ kind: "link";
583
+ href: string;
584
+ } | {
585
+ kind: "copy";
586
+ } | {
587
+ kind: "post";
588
+ action: string;
589
+ fields: Record<string, string>;
590
+ } | {
591
+ kind: "goto";
592
+ tab: BrowserTaskTab;
593
+ target: string;
594
+ });
490
595
  /** One project on the Projects page; opening it is a POST to /projects/open. */
491
596
  export type BrowserProjectRow = {
492
597
  name: string;
@@ -689,7 +794,12 @@ export type BrowserResultView = {
689
794
  accept?: {
690
795
  note: string | null;
691
796
  } | null;
797
+ returnTo?: string;
692
798
  } | null;
799
+ /** How its run went (docs/plans/run-efficiency.md): recorded numbers only, unknowns null. */
800
+ efficiency?: import("./run-efficiency.js").RunEfficiency | null;
801
+ /** What its network was and what its proxy saw (egress.ts): display only. */
802
+ network?: import("./egress.js").RunNetworkRecord | null;
693
803
  /** Words for the result's Accept: Accept and finish when offered, else the Needs you acceptance. */
694
804
  decision: BrowserResultDecision | null;
695
805
  checks: {
@@ -882,6 +992,14 @@ export type BrowserFlowCard = {
882
992
  task: {
883
993
  id: string;
884
994
  href: string;
995
+ /** Its running attempt's composer (steer-controls.ts), for an approver: what it takes, its controls, its note box. */
996
+ composer?: {
997
+ controls: BrowserAgentControls;
998
+ steer: {
999
+ action: string;
1000
+ run: number;
1001
+ } | null;
1002
+ };
885
1003
  } | null;
886
1004
  createdBy: string;
887
1005
  updatedAt: string;
@@ -896,10 +1014,15 @@ export type BrowserFlowCard = {
896
1014
  at: string;
897
1015
  }[];
898
1016
  /** Where a trigger found it: a GitHub or Linear issue, another flow's card, a button, a schedule. */
1017
+ /** `proved` and `host` (v126): how an outside event's delivery was proved ("signed by Sentry", "unsigned") and its
1018
+ * link's host, always shown beside the link. */
899
1019
  source: {
900
1020
  kind: string;
901
1021
  label: string;
902
1022
  url: string | null;
1023
+ proved?: string;
1024
+ host?: string | null;
1025
+ shown?: string | null;
903
1026
  } | null;
904
1027
  /** Who is responsible for it, who follows it, and whether the viewer does. */
905
1028
  owner: string | null;
@@ -1017,10 +1140,27 @@ export type BrowserFlowTrigger = {
1017
1140
  label: string;
1018
1141
  questions: string[];
1019
1142
  } | null;
1020
- /** A webhook trigger: whether it can prove deliveries yet (Linear needs its signing secret pasted). */
1143
+ /** A webhook trigger: whether it can prove deliveries yet (Linear, Sentry and Vercel need their signing secret pasted).
1144
+ * v126: a webhook's sender (who signs it; "none" is the address alone), whether Toolroll makes its secret, and a
1145
+ * warning in words when anyone could add cards (unsigned) or any project could (a Sentry or Vercel webhook unscoped). */
1021
1146
  hook: {
1022
1147
  ready: boolean;
1023
1148
  needsSecret: boolean;
1149
+ sender: string | null;
1150
+ makesSecret: boolean;
1151
+ warning: string | null;
1152
+ readerNote: string | null;
1153
+ /** v126: deliveries turned away because too many were already held, until a person clears it, and how to resend them. */
1154
+ dropped: {
1155
+ count: number;
1156
+ since: string;
1157
+ resend: string;
1158
+ } | null;
1159
+ /** v126: the Sentry or Vercel project (and Vercel team) it takes deliveries from; null: any the secret covers. */
1160
+ scope: {
1161
+ project: string | null;
1162
+ team: string | null;
1163
+ } | null;
1024
1164
  } | null;
1025
1165
  checkable: boolean;
1026
1166
  /** A button shared as a public form (its link works). */
@@ -1183,6 +1323,22 @@ export type BrowserAgentQuestion = {
1183
1323
  text: string;
1184
1324
  action: string;
1185
1325
  };
1326
+ export type BrowserAgentControls = {
1327
+ send: "live" | "mailbox" | null;
1328
+ words: string;
1329
+ pause: {
1330
+ action: string;
1331
+ run: number;
1332
+ state: "pausing" | "paused" | null;
1333
+ requests: [string, string, string];
1334
+ until: string | null;
1335
+ } | null;
1336
+ stop: {
1337
+ action: string;
1338
+ run: number;
1339
+ label: string;
1340
+ } | null;
1341
+ };
1186
1342
  export type BrowserAgentDetail = BrowserAgentRow & {
1187
1343
  live: boolean;
1188
1344
  question: BrowserAgentQuestion | null;
@@ -1201,11 +1357,39 @@ export type BrowserAgentDetail = BrowserAgentRow & {
1201
1357
  action: string;
1202
1358
  run: number;
1203
1359
  } | null;
1360
+ /** What this running attempt takes (steer-capabilities.ts): how a note reaches it (or why none does), Pause and
1361
+ * Stop for the exact run each names. Null when it is not running or this person may not act. */
1362
+ controls?: BrowserAgentControls | null;
1363
+ /** Cockpit E: this running attempt's checkout, proved to be on this machine — the cd command to copy, and where Show
1364
+ * in Finder posts. Absent off this machine, for a viewer, or once it ends. Nothing is ever launched in it. */
1365
+ /** `reveal` is null where Finder can't be asked (not a Mac): the path and the command stand alone. */
1366
+ checkout?: {
1367
+ path: string;
1368
+ cd: string;
1369
+ reveal: string | null;
1370
+ } | null;
1204
1371
  /** Whether this person may reply or steer (an approver, outside the demo); a viewer sees the question but no composer. */
1205
1372
  canAct: boolean;
1206
1373
  /** The task, and once finished the run's own page (its result page when it built one). */
1207
1374
  taskHref: string;
1208
1375
  resultHref: string | null;
1376
+ /** The task this agent works on, as its page heads it: the status words, and its one action as a way to the task page's
1377
+ * tab that holds it (never a form here: the task page's own form is the one that acts). Null when it can't be read. */
1378
+ task?: {
1379
+ headline: string;
1380
+ sentence: string;
1381
+ tone: string;
1382
+ action: {
1383
+ label: string;
1384
+ href: string;
1385
+ };
1386
+ } | null;
1387
+ /** Where this run opens on its task's page: a result → its Result tab at this run, a plan → the Plan tab, anything
1388
+ * else (a review, an attempt that isn't a result) → its own record. Null while it runs. */
1389
+ open?: {
1390
+ label: string;
1391
+ href: string;
1392
+ } | null;
1209
1393
  /** The exact-run live room: updates, and a watch that keeps this person's pings quiet. */
1210
1394
  room: string;
1211
1395
  said: {
@@ -1213,6 +1397,42 @@ export type BrowserAgentDetail = BrowserAgentRow & {
1213
1397
  text: string;
1214
1398
  } | null;
1215
1399
  };
1400
+ /** One run on the timeline: its role, and when it started and ended (null: still running). */
1401
+ export type BrowserLaneSegment = {
1402
+ runId: number;
1403
+ role: 'planner' | 'builder' | 'reviewer' | 'scout';
1404
+ agent: string;
1405
+ startedAt: string;
1406
+ endedAt: string | null;
1407
+ outcome: string | null;
1408
+ href: string;
1409
+ };
1410
+ /** One task's row on the timeline: its runs in order, and the board's word for where it stands ('planning' before a build). */
1411
+ export type BrowserAgentLane = {
1412
+ taskId: string;
1413
+ title: string;
1414
+ project: string | null;
1415
+ href: string;
1416
+ group: AgentGroupKey | 'planning';
1417
+ segments: BrowserLaneSegment[];
1418
+ };
1419
+ /** The agent timeline (after the "Agent lanes" pattern): one row per task over a window ending now. */
1420
+ export type BrowserAgentLanes = {
1421
+ since: string;
1422
+ now: string;
1423
+ windows: {
1424
+ key: AgentLaneWindow;
1425
+ label: string;
1426
+ href: string;
1427
+ active: boolean;
1428
+ }[];
1429
+ lanes: BrowserAgentLane[];
1430
+ /** Tasks in the window past the rows shown. */
1431
+ more: number;
1432
+ /** Every task in the window with a live run, not just the rows shown. */
1433
+ running: number;
1434
+ };
1435
+ export type AgentLaneWindow = '30m' | '2h' | '24h';
1216
1436
  export type BrowserAgentsView = {
1217
1437
  kind: 'agents';
1218
1438
  groups: {
@@ -1220,6 +1440,8 @@ export type BrowserAgentsView = {
1220
1440
  label: string;
1221
1441
  rows: BrowserAgentRow[];
1222
1442
  }[];
1443
+ /** The timeline above the board; empty lanes when nothing ran in the window but something did today; null when nothing ran in a day. */
1444
+ lanes?: BrowserAgentLanes | null;
1223
1445
  selected: BrowserAgentDetail | null;
1224
1446
  /** Shown when nothing is on the board. */
1225
1447
  empty: string | null;
@@ -1232,6 +1454,8 @@ export type BrowserWorkspace = {
1232
1454
  user: string;
1233
1455
  csrf: string;
1234
1456
  sensitive: boolean;
1457
+ /** Passwordless is on for this signed-in browser: ceremony password fields are left out (docs/plans/passwordless.md). */
1458
+ passwordless?: boolean;
1235
1459
  /** What this person named their lead (Settings → Lead): the chat header and every lead message say it. */
1236
1460
  leadName?: string;
1237
1461
  refreshUrl: string;
@@ -1242,6 +1466,16 @@ export type BrowserWorkspace = {
1242
1466
  projects: BrowserProject[];
1243
1467
  crew: BrowserCrewItem[];
1244
1468
  crewTruncated: boolean;
1469
+ /** The sidebar's project states; absent on a sensitive page. The summary room keeps them current. */
1470
+ projectStates?: BrowserProjectState[];
1471
+ /** Cockpit B2: this session's remembered password — minutes left, and where Forget posts. Absent unless it is
1472
+ * live on a direct local request; never on a sensitive page. */
1473
+ fresh?: {
1474
+ minutes: number;
1475
+ forget: string;
1476
+ };
1477
+ /** The most-used subscription window, for an operator; absent for everyone else and on a sensitive page. */
1478
+ usage?: BrowserUsageChip;
1245
1479
  conversation: BrowserConversation | null;
1246
1480
  team?: TeamSnapshot;
1247
1481
  focus: {
@@ -1,6 +1,66 @@
1
+ /** The browser consumes this contract with a type-only import. All HTML is
2
+ * produced by the existing trusted server renderers, never by a model-supplied
3
+ * fragment. These projections do not authenticate, mutate or grant authority. */
4
+ import { DISPLAY_STATES as DISPLAY_ORDER } from './agent-state-words.js';
1
5
  import { browserCrewFromIndex } from './browser-crew.js';
2
6
  export { browserCrewFromIndex, browserWorkActionHref } from './browser-crew.js';
3
7
  import { workIndexPage } from './work-index.js';
8
+ /** The most a summary snapshot may take in UTF-8 bytes, as the summary room sends it (JSON inside JSON, plus the
9
+ * frame's envelope), well inside one live frame (live-bus.ts STREAM_LIMIT_BYTES). */
10
+ export const SUMMARY_BUDGET = 24_000;
11
+ /** The frame around a snapshot: its event line, room, revision and the JSON around `at`. Generous on purpose. */
12
+ const SUMMARY_ENVELOPE_BYTES = 512;
13
+ const utf8 = new TextEncoder();
14
+ /** A snapshot's size on the wire: UTF-8 bytes of the JSON string the frame carries, plus its envelope. */
15
+ export function summaryFrameBytes(value) {
16
+ return utf8.encode(JSON.stringify({ at: JSON.stringify(value) })).length + SUMMARY_ENVELOPE_BYTES;
17
+ }
18
+ const clip = (text, max) => text.length <= max ? text : `${text.slice(0, max - 1)}…`;
19
+ /**
20
+ * A summary snapshot that fits one live frame, with every count kept: long words are clipped, then rail tasks become
21
+ * `more` (from the project listing most), then the quietest projects fold into one `others` entry with their counts
22
+ * summed. Nothing is dropped silently: whatever is not listed is counted.
23
+ */
24
+ export function boundProjectSummary(rows, budget = SUMMARY_BUDGET) {
25
+ const size = summaryFrameBytes;
26
+ let out = rows.map(one => ({ ...one, name: clip(one.name, 80), tasks: one.tasks.map(task => ({ ...task, title: clip(task.title, 120), line: clip(task.line, 120) })) }));
27
+ while (size(out) > budget) {
28
+ const fullest = out.reduce((best, one, index) => one.tasks.length > (out[best]?.tasks.length ?? -1) ? index : best, -1);
29
+ if (fullest === -1 || out[fullest].tasks.length === 0)
30
+ break;
31
+ const one = out[fullest];
32
+ out[fullest] = { ...one, tasks: one.tasks.slice(0, -1), more: one.more + 1 };
33
+ }
34
+ if (size(out) <= budget)
35
+ return out;
36
+ // Fold the quietest projects (weakest state last in the list) into one entry until it fits.
37
+ const strength = (one) => DISPLAY_ORDER.indexOf(one.state);
38
+ const order = out.map((one, index) => ({ one, index })).sort((a, b) => strength(b.one) - strength(a.one) || b.index - a.index);
39
+ const folded = new Set();
40
+ const othersOf = () => {
41
+ if (folded.size === 0)
42
+ return null;
43
+ const counts = Object.fromEntries(DISPLAY_ORDER.map(key => [key, [...folded].reduce((sum, index) => sum + out[index].counts[key], 0)]));
44
+ const state = DISPLAY_ORDER.find(key => key !== 'idle' && counts[key] > 0) ?? 'idle';
45
+ return { path: null, name: `${folded.size} more ${folded.size === 1 ? 'project' : 'projects'}`, href: '/projects', state, counts, tasks: [], more: folded.size, others: true,
46
+ asks: [...folded].flatMap(index => out[index].asks) };
47
+ };
48
+ const assemble = () => { const others = othersOf(); return [...out.filter((_, index) => !folded.has(index)), ...(others === null ? [] : [others])]; };
49
+ for (const { index } of order) {
50
+ if (size(assemble()) <= budget)
51
+ break;
52
+ folded.add(index);
53
+ }
54
+ // Still too large: the oldest asks go last (their counts stay; only their chimes are lost).
55
+ let assembled = assemble();
56
+ while (size(assembled) > budget) {
57
+ const fullest = assembled.reduce((best, one, index) => one.asks.length > (assembled[best]?.asks.length ?? -1) ? index : best, -1);
58
+ if (fullest === -1 || assembled[fullest].asks.length === 0)
59
+ break;
60
+ assembled = assembled.map((one, index) => index === fullest ? { ...one, asks: one.asks.slice(0, -1) } : one);
61
+ }
62
+ return assembled;
63
+ }
4
64
  /** The Tasks badge's words: the count and the projects it covers
5
65
  * ("3 need you in shop", "1 needs you across all your projects"). */
6
66
  export function needsYouLabelOf(count, projectName, saturated = false) {
package/dist/builder.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { LaunchNetwork } from "./invoke.js";
2
+ import type { ProviderId } from "./contracts/provider.js";
1
3
  import { type PreparedEvidence } from "./prepared-evidence.js";
2
4
  import { type ExecResult, type RunOptions } from "./exec.js";
3
5
  import type { Decision, Store } from "./store.js";
@@ -85,7 +87,7 @@ export type BuildRequest = {
85
87
  skipPermissions?: boolean;
86
88
  /** The harness this build runs on. Repair ALWAYS inherits it — a session
87
89
  * resumed across providers is not a session (Codex provider review, Q3). */
88
- provider?: "claude" | "codex" | "openrouter" | "gemini";
90
+ provider?: ProviderId;
89
91
  model?: string;
90
92
  /**
91
93
  * The model repair turns run on. Repair is a few-k, one-job resumption —
@@ -181,6 +183,9 @@ export declare const SETUP_ENV_DENYLIST: readonly string[];
181
183
  export declare function redactSecretText(text: string): string;
182
184
  /** The same blanking with no length cap, for a log that keeps its length (a flow script's output). */
183
185
  export declare function redactSecretAssignments(text: string): string;
186
+ /** A build's network (egress.ts): the one its approval sealed, or — sealed before network policies existed — the
187
+ * narrowed legacy one (provider hosts and the repository's registries, nothing added since). */
188
+ export declare function launchNetworkOf(store: Store, taskRef: number, profile: ExecutionProfile): LaunchNetwork;
184
189
  export declare function proveApprovedProfile(scope: Scope | null, given: {
185
190
  provider: string;
186
191
  model: string | undefined;
package/dist/builder.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { legacyNetwork, repositoryEcosystems } from "./egress.js";
2
+ import { isAcpProvider } from "./acp-agents.js";
1
3
  import { OBSERVATION_MAILBOX, observationBrief, parseObservationCases, collectObservations } from "./observations.js";
2
4
  import { checkCommandFor, effectiveCheckLevel, recordRunCheckLevel } from "./check-levels.js";
3
5
  import { queueBatchCheck } from "./batch-checks.js";
@@ -47,7 +49,7 @@ import { stopRequestedFor, stopWords, underStopWatch } from "./task-control.js";
47
49
  import { witnessedRunner } from "./process-custody.js";
48
50
  import { runWithIsolatedDatabase } from "./child-database.js";
49
51
  import { recordWorktreeProcess, saveWorkPatch } from "./worktree.js";
50
- import { approvalOf, digestOf, profileDigestOf, routeParityProblem } from "./scope.js";
52
+ import { approvalOf, digestOf, idleClock, isAcpProfile, profileDigestOf, routeParityProblem, watchdogsOf } from "./scope.js";
51
53
  import { legOf, routeDigestOf, routeFromJson } from "./phase-routing.js";
52
54
  import { execFileSync } from "node:child_process";
53
55
  import { currentClaim, heartbeat, SYNC_MAX_AGE_MS, finalize } from "./claim.js";
@@ -59,6 +61,7 @@ import { createHash, randomBytes, randomUUID } from "node:crypto";
59
61
  import { invokeAgent } from "./invoke.js";
60
62
  import { TELEGRAM_TOKEN_ENVS } from "./names.js";
61
63
  import { OPENROUTER_ENV_KEY, auditOf, ALL_CREDENTIAL_ENV } from "./provider.js";
64
+ import { liveLogContext } from "./live-detail.js";
62
65
  import { openLiveLog } from "./live.js";
63
66
  import { fence } from "./brief-fence.js";
64
67
  import { closeSteerMailbox, openSteerMailbox, STEER_HANDLED } from "./steer-mailbox.js";
@@ -199,12 +202,17 @@ function repairModelOf(profile, request) {
199
202
  function profileWantsSkip(profile) {
200
203
  return ((profile.provider === "claude" && profile.permissionArgv === "bypassPermissions") ||
201
204
  ((profile.provider === "codex" || profile.provider === "openrouter") && profile.sandboxMode === "danger-full-access") ||
202
- (profile.provider === "gemini" && profile.approvalArgv === "yolo"));
205
+ (profile.provider === "gemini" && profile.approvalArgv === "yolo") ||
206
+ // An ACP agent's only posture is full access inside the fence.
207
+ isAcpProfile(profile));
203
208
  }
204
209
  const PROVIDER_VERSIONS = new Map();
205
210
  /** Provenance only (finding 20): a best-effort `--version` probe, cached
206
211
  * per process, null on any failure — never authority, never a refusal. */
207
212
  function providerVersionOf(provider) {
213
+ // An ACP agent is never run outside the gateway, not even for --version: its version is what its check recorded.
214
+ if (isAcpProvider(provider))
215
+ return null;
208
216
  if (PROVIDER_VERSIONS.has(provider))
209
217
  return PROVIDER_VERSIONS.get(provider) ?? null;
210
218
  let version = null;
@@ -221,6 +229,14 @@ function providerVersionOf(provider) {
221
229
  PROVIDER_VERSIONS.set(provider, version);
222
230
  return version;
223
231
  }
232
+ /** A build's network (egress.ts): the one its approval sealed, or — sealed before network policies existed — the
233
+ * narrowed legacy one (provider hosts and the repository's registries, nothing added since). */
234
+ export function launchNetworkOf(store, taskRef, profile) {
235
+ if (profile.network !== undefined)
236
+ return { policy: profile.network };
237
+ const repo = store.refForId(taskRef)?.repo ?? null;
238
+ return { policy: legacyNetwork(profile.provider, repo === null ? [] : repositoryEcosystems(repo)) };
239
+ }
224
240
  export function proveApprovedProfile(scope, given) {
225
241
  // The raw terms first (raw authority repair): a scope whose stored terms
226
242
  // do not read back exactly proves nothing — not the filtered reading.
@@ -293,7 +309,7 @@ export function proveApprovedProfile(scope, given) {
293
309
  if (given.timeoutMs !== undefined && given.timeoutMs !== snapshot.timeoutSeconds * 1000) {
294
310
  return {
295
311
  ok: false,
296
- message: `approved with a ${snapshot.timeoutSeconds}s ${snapshot.timeoutKind === "idle" ? "no-progress window" : "clock"}, asked for ${Math.round(given.timeoutMs / 1000)}s — re-approve to change it (stale-approval)`,
312
+ message: `approved with a ${snapshot.timeoutSeconds}s ${idleClock(snapshot) ? "no-progress window" : "clock"}, asked for ${Math.round(given.timeoutMs / 1000)}s — re-approve to change it (stale-approval)`,
297
313
  };
298
314
  }
299
315
  return {
@@ -1123,7 +1139,7 @@ export async function build(store, request) {
1123
1139
  maxTurns: effective.maxTurns ?? maxTurns,
1124
1140
  finished: () => handoffFiles.some(name => existsSync(join(worktree, name))),
1125
1141
  // A paused build can wait as long as the idle limit allows (live-input.ts idleLimit); a wall-clock profile has none.
1126
- onOpen: () => store.markLiveInputOpened(request.runId, clock(), effective.profile.timeoutKind === "idle" ? effective.timeoutMs : null),
1142
+ onOpen: () => store.markLiveInputOpened(request.runId, clock(), idleClock(effective.profile) ? effective.timeoutMs : null),
1127
1143
  onClose: () => store.markLiveInputClosed(request.runId, clock()),
1128
1144
  onPaused: () => store.markRunPaused(request.runId, clock()),
1129
1145
  });
@@ -1153,7 +1169,7 @@ export async function build(store, request) {
1153
1169
  // Every streaming transport emits events now (peek); a file that cannot
1154
1170
  // open is a null, and a null never costs a build.
1155
1171
  // What the agent did last rides the same stream into one row (live tasks).
1156
- const liveLog = withAgentTurns(withRunActivity(openLiveLog(root, request.runId), store, request.runId, clock), (state, at) => { if (attemptKey !== null)
1172
+ const liveLog = withAgentTurns(withRunActivity(openLiveLog(root, request.runId, liveLogContext(store, taskRef, worktree)), store, request.runId, clock), (state, at) => { if (attemptKey !== null)
1157
1173
  recordAgentTurn(store, { run: request.runId, attemptKey, state, at }); }, clock, (open, at) => { if (attemptKey !== null)
1158
1174
  noteOpenTool(store, request.runId, attemptKey, open, at); });
1159
1175
  // The retry base (steering fix for run 1465's proof): the SAME pinned
@@ -1291,10 +1307,9 @@ export async function build(store, request) {
1291
1307
  ...(liveSession === null ? {} : { liveInput: liveSession }),
1292
1308
  // New approvals bind a no-progress watchdog, not a deadline. Legacy
1293
1309
  // snapshots carry no timeoutKind and retain their wall-clock terms.
1294
- ...(effective.profile.timeoutKind === "idle"
1295
- ? { idleTimeoutMs: effective.timeoutMs }
1296
- : { timeoutMs: effective.timeoutMs }),
1310
+ ...watchdogsOf(effective.profile, effective.timeoutMs),
1297
1311
  omitEnv: AGENT_ENV_DENYLIST,
1312
+ network: launchNetworkOf(store, request.taskRef, effective.profile),
1298
1313
  ...(agent === undefined ? {} : { runner: agent }),
1299
1314
  onSpawn: (pid) => { spawned = true; request.onProviderSpawn?.(pid); },
1300
1315
  onStreamEvent: (event) => liveLog.observe(event),
@@ -1490,7 +1505,7 @@ export async function settleProviderOutcome(captured, result) {
1490
1505
  return {
1491
1506
  ok: false,
1492
1507
  reason: "timeout",
1493
- message: effective.profile.timeoutKind === "idle"
1508
+ message: idleClock(effective.profile)
1494
1509
  ? `the builder made no observable progress for ${Math.round(timeoutMs / 60_000)} minutes and was stopped — whatever it wrote is still in ${worktree}`
1495
1510
  : `the builder ran past ${Math.round(timeoutMs / 60_000)} minutes and was stopped — whatever it wrote is still in ${worktree}`,
1496
1511
  };
@@ -2614,7 +2629,8 @@ async function resumeUnhandedWork(captured, sessionId) {
2614
2629
  if (request.leaseId !== undefined && !heartbeat(store, request.leaseId, clock()).ok) {
2615
2630
  return { ok: false, reason: "fenced", message: `${taskId}'s lease did not survive the resumed turn — the work is still in ${worktree}` };
2616
2631
  }
2617
- if (store.budgetGate(clock())({ ...store.budgetSubject(request.taskRef), agents: [{ provider, billing: store.runBilling(request.runId) ?? store.agentsFor([provider])[0].billing }] }).over !== null)
2632
+ // An ACP agent's turn bills as its own model's declaration on this runner, never as the attempt it continues.
2633
+ if (store.budgetGate(clock())({ ...store.budgetSubject(request.taskRef), agents: [isAcpProvider(provider) ? store.agentFor(provider, model ?? null, request.runner) : { provider, billing: store.runBilling(request.runId) ?? store.agentsFor([provider])[0].billing }] }).over !== null)
2618
2634
  break;
2619
2635
  if (store.agentPolicyRefusal(provider, model ?? null) !== null)
2620
2636
  break;
@@ -2634,8 +2650,9 @@ async function resumeUnhandedWork(captured, sessionId) {
2634
2650
  ...(request.maxBudgetUsd === undefined ? {} : { maxBudgetUsd: request.maxBudgetUsd }),
2635
2651
  }, {
2636
2652
  cwd: worktree,
2637
- ...(effective.profile.timeoutKind === "idle" ? { idleTimeoutMs: effective.timeoutMs } : { timeoutMs: effective.timeoutMs }),
2653
+ ...watchdogsOf(effective.profile, effective.timeoutMs),
2638
2654
  omitEnv: AGENT_ENV_DENYLIST,
2655
+ network: launchNetworkOf(store, request.taskRef, effective.profile),
2639
2656
  ...(agent === undefined ? {} : { runner: agent }),
2640
2657
  ...(request.onProviderSpawn === undefined ? {} : { onSpawn: request.onProviderSpawn }),
2641
2658
  clock,
@@ -2811,7 +2828,8 @@ async function ingestPark(args) {
2811
2828
  }
2812
2829
  // v105: a repair turn spends too — a budget used up since the build began stops further turns (billed to a key).
2813
2830
  // It bills as the attempt it mends did.
2814
- if (store.budgetGate(clock())({ ...store.budgetSubject(request.taskRef), agents: [{ provider: repairProvider, billing: store.runBilling(request.runId) ?? store.agentsFor([repairProvider])[0].billing }] }).over !== null)
2831
+ // An ACP repair bills as its own model's declaration on this runner, never as the attempt it mends.
2832
+ if (store.budgetGate(clock())({ ...store.budgetSubject(request.taskRef), agents: [isAcpProvider(repairProvider) ? store.agentFor(repairProvider, repairModel ?? null, request.runner) : { provider: repairProvider, billing: store.runBilling(request.runId) ?? store.agentsFor([repairProvider])[0].billing }] }).over !== null)
2815
2833
  break;
2816
2834
  // Sprint 8: nor does a repair run on a provider or model the organisation policy doesn't allow.
2817
2835
  if (store.agentPolicyRefusal(repairProvider, repairModel ?? null) !== null)
@@ -2845,8 +2863,11 @@ async function ingestPark(args) {
2845
2863
  : {}),
2846
2864
  }, {
2847
2865
  cwd: worktree,
2848
- timeoutMs: args.profile !== undefined ? args.profile.repairTimeoutSeconds * 1000 : REPAIR_TIMEOUT_MS,
2866
+ ...(args.profile !== undefined && isAcpProfile(args.profile)
2867
+ ? watchdogsOf(args.profile, args.profile.repairTimeoutSeconds * 1000, true)
2868
+ : { timeoutMs: args.profile !== undefined ? args.profile.repairTimeoutSeconds * 1000 : REPAIR_TIMEOUT_MS }),
2849
2869
  omitEnv: AGENT_ENV_DENYLIST,
2870
+ ...(args.profile === undefined ? {} : { network: launchNetworkOf(store, request.taskRef, args.profile) }),
2850
2871
  ...(agent === undefined ? {} : { runner: agent }),
2851
2872
  ...(request.onProviderSpawn === undefined ? {} : { onSpawn: request.onProviderSpawn }),
2852
2873
  clock,
@@ -68,6 +68,16 @@ export declare const CHAT_ACTIONS: {
68
68
  readonly protected: false;
69
69
  readonly password: false;
70
70
  };
71
+ readonly sweep_file: {
72
+ readonly label: "Start sweep";
73
+ readonly protected: true;
74
+ readonly password: true;
75
+ };
76
+ readonly sweep_cancel_rest: {
77
+ readonly label: "Cancel the rest";
78
+ readonly protected: false;
79
+ readonly password: false;
80
+ };
71
81
  readonly flow_edit: {
72
82
  readonly label: "Save flow";
73
83
  readonly protected: false;
@@ -256,7 +266,7 @@ export declare function sharedActionReviewPath(id: number): string;
256
266
  export declare function mintSharedActionReview(store: Store, who: VerifiedApprover, id: number, root: string, now: Date): {
257
267
  payload: {
258
268
  version: 1;
259
- operation: "decision_record" | "decision_retire" | "flow_card_add" | "flow_card_approve" | "flow_card_assign" | "flow_card_cancel" | "flow_card_choose" | "flow_card_comment" | "flow_card_move" | "flow_card_send_back" | "flow_card_watch" | "flow_create" | "flow_edit" | "flow_script_save" | "flow_starter" | "flow_trigger_add" | "flow_trigger_pause" | "flow_trigger_remove" | "flow_trigger_resume" | "kit_setup" | "knowledge_instructions" | "knowledge_remove" | "knowledge_restore" | "knowledge_save" | "lead_about_you" | "result_accept" | "scope_approve" | "skill_disable" | "skill_enable" | "skill_import" | "skill_restore" | "skill_test" | "subagent_answer" | "subagent_ask" | "subagent_create" | "subagent_memory" | "subagent_note" | "subagent_routine" | "subagent_soul" | "subagent_state" | "subagent_tools" | "subagent_undo" | "task_cancel" | "task_resume" | "tool_add" | "tool_remove";
269
+ operation: "decision_record" | "decision_retire" | "flow_card_add" | "flow_card_approve" | "flow_card_assign" | "flow_card_cancel" | "flow_card_choose" | "flow_card_comment" | "flow_card_move" | "flow_card_send_back" | "flow_card_watch" | "flow_create" | "flow_edit" | "flow_script_save" | "flow_starter" | "flow_trigger_add" | "flow_trigger_pause" | "flow_trigger_remove" | "flow_trigger_resume" | "kit_setup" | "knowledge_instructions" | "knowledge_remove" | "knowledge_restore" | "knowledge_save" | "lead_about_you" | "result_accept" | "scope_approve" | "skill_disable" | "skill_enable" | "skill_import" | "skill_restore" | "skill_test" | "subagent_answer" | "subagent_ask" | "subagent_create" | "subagent_memory" | "subagent_note" | "subagent_routine" | "subagent_soul" | "subagent_state" | "subagent_tools" | "subagent_undo" | "sweep_cancel_rest" | "sweep_file" | "task_cancel" | "task_resume" | "tool_add" | "tool_remove";
260
270
  request: {
261
271
  [x: string]: unknown;
262
272
  };
@@ -286,6 +296,8 @@ export declare function executeSharedAction(store: Store, who: VerifiedApprover,
286
296
  said: string;
287
297
  taskId: string | null;
288
298
  href?: string;
299
+ awaiting?: true;
300
+ already?: true;
289
301
  } | {
290
302
  ok: false;
291
303
  reason: "needs-confirm" | "stale" | "refused";