toolroll 0.9.63 → 0.9.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +33 -5
  3. package/dist/acp-agents.d.ts +43 -0
  4. package/dist/acp-agents.js +62 -0
  5. package/dist/acp-attest.d.ts +44 -0
  6. package/dist/acp-attest.js +269 -0
  7. package/dist/acp-check.d.ts +28 -0
  8. package/dist/acp-check.js +410 -0
  9. package/dist/acp-client.d.ts +163 -0
  10. package/dist/acp-client.js +738 -0
  11. package/dist/acp-inspect.d.ts +23 -0
  12. package/dist/acp-inspect.js +109 -0
  13. package/dist/agent-board.d.ts +19 -6
  14. package/dist/agent-board.js +30 -9
  15. package/dist/agent-fence.d.ts +117 -2
  16. package/dist/agent-fence.js +439 -12
  17. package/dist/agent-pulse.js +9 -2
  18. package/dist/agent-state-words.d.ts +19 -0
  19. package/dist/agent-state-words.js +20 -0
  20. package/dist/agent-state.d.ts +65 -0
  21. package/dist/agent-state.js +229 -0
  22. package/dist/agentconfig.js +39 -13
  23. package/dist/agents-board.d.ts +34 -2
  24. package/dist/agents-board.js +110 -1
  25. package/dist/approval-rules-ui.js +2 -2
  26. package/dist/assignment-delivery.d.ts +14 -0
  27. package/dist/assignment-delivery.js +13 -0
  28. package/dist/assignment.js +13 -0
  29. package/dist/attest.js +7 -0
  30. package/dist/backup-ui.js +2 -2
  31. package/dist/batch-checks.js +3 -2
  32. package/dist/browser/THIRD_PARTY_NOTICES.txt +94 -0
  33. package/dist/browser/terminal.css +37 -0
  34. package/dist/browser/terminal.js +228 -0
  35. package/dist/browser/workspace.css +2 -2
  36. package/dist/browser/workspace.js +42 -38
  37. package/dist/browser-shell.js +6 -3
  38. package/dist/browser-workspace.d.ts +235 -1
  39. package/dist/browser-workspace.js +60 -0
  40. package/dist/builder.d.ts +10 -1
  41. package/dist/builder.js +87 -24
  42. package/dist/chat-actions.d.ts +13 -1
  43. package/dist/chat-actions.js +197 -15
  44. package/dist/chat-ask.d.ts +4 -0
  45. package/dist/chat-ask.js +14 -0
  46. package/dist/chat-channel.js +20 -0
  47. package/dist/chat-continuity.js +5 -4
  48. package/dist/chat-decide.d.ts +0 -14
  49. package/dist/chat-decide.js +4 -1
  50. package/dist/chat-delivery.d.ts +9 -0
  51. package/dist/chat-delivery.js +45 -6
  52. package/dist/chat-messages.d.ts +8 -0
  53. package/dist/chat-messages.js +36 -4
  54. package/dist/check-levels-ui.js +2 -2
  55. package/dist/checkout-proof.d.ts +28 -0
  56. package/dist/checkout-proof.js +62 -0
  57. package/dist/claim.d.ts +12 -0
  58. package/dist/claim.js +19 -0
  59. package/dist/cli-help.d.ts +1 -1
  60. package/dist/cli-help.js +4 -1
  61. package/dist/cli.d.ts +3 -0
  62. package/dist/cli.js +21 -2
  63. package/dist/coding-ui.js +6 -4
  64. package/dist/console-instance.d.ts +124 -0
  65. package/dist/console-instance.js +337 -0
  66. package/dist/contracts/chat-actions.d.ts +16 -0
  67. package/dist/contracts/chat-actions.js +4 -0
  68. package/dist/contracts/chat-content.d.ts +19 -0
  69. package/dist/contracts/chat-content.js +6 -0
  70. package/dist/contracts/console-api.d.ts +13 -4
  71. package/dist/contracts/console-api.js +13 -4
  72. package/dist/contracts/flow-card.d.ts +4 -0
  73. package/dist/contracts/flow-card.js +3 -0
  74. package/dist/contracts/flow.d.ts +46 -0
  75. package/dist/contracts/flow.js +11 -2
  76. package/dist/contracts/lead-tools.d.ts +92 -0
  77. package/dist/contracts/lead-tools.js +13 -0
  78. package/dist/contracts/provider.d.ts +11 -1
  79. package/dist/contracts/provider.js +1 -1
  80. package/dist/contracts/route.d.ts +76 -1
  81. package/dist/contracts/route.js +1 -1
  82. package/dist/contracts/store-json.d.ts +2 -0
  83. package/dist/control-setup.d.ts +1 -1
  84. package/dist/control-ui.js +2 -2
  85. package/dist/converse.d.ts +16 -0
  86. package/dist/converse.js +32 -4
  87. package/dist/demo-chat.js +4 -4
  88. package/dist/demo.js +2 -2
  89. package/dist/desktop-update-gate.js +6 -0
  90. package/dist/desktop-update.d.ts +1 -1
  91. package/dist/discord-chat.js +4 -1
  92. package/dist/doctor.js +1 -1
  93. package/dist/egress-bridge.d.ts +17 -0
  94. package/dist/egress-bridge.js +111 -0
  95. package/dist/egress.d.ts +119 -0
  96. package/dist/egress.js +423 -0
  97. package/dist/exec.d.ts +78 -0
  98. package/dist/exec.js +367 -9
  99. package/dist/export-ui.js +2 -2
  100. package/dist/flow-engine.js +24 -5
  101. package/dist/flow-gallery-ui.js +2 -2
  102. package/dist/flow-gallery.js +27 -8
  103. package/dist/flow-share.js +16 -5
  104. package/dist/flow-triggers.d.ts +77 -8
  105. package/dist/flow-triggers.js +517 -70
  106. package/dist/flows-cli.js +8 -2
  107. package/dist/flows-ui.d.ts +0 -2
  108. package/dist/flows-ui.js +52 -4
  109. package/dist/flows.d.ts +4 -0
  110. package/dist/flows.js +9 -1
  111. package/dist/html.d.ts +9 -0
  112. package/dist/html.js +11 -0
  113. package/dist/invoke.d.ts +35 -0
  114. package/dist/invoke.js +347 -28
  115. package/dist/keys.d.ts +18 -14
  116. package/dist/keys.js +5 -0
  117. package/dist/lead-capabilities.js +3 -2
  118. package/dist/lead-contract.js +2 -0
  119. package/dist/lead-doors.d.ts +2 -0
  120. package/dist/lead-doors.js +7 -0
  121. package/dist/lead-follow.d.ts +2 -0
  122. package/dist/lead-follow.js +58 -2
  123. package/dist/lead-progress.js +1 -0
  124. package/dist/lead-queue-runtime.d.ts +46 -0
  125. package/dist/lead-queue-runtime.js +191 -0
  126. package/dist/lead-queue.d.ts +104 -0
  127. package/dist/lead-queue.js +187 -0
  128. package/dist/lead-sweep.d.ts +67 -0
  129. package/dist/lead-sweep.js +99 -0
  130. package/dist/lead-tools.d.ts +22 -0
  131. package/dist/lead-tools.js +152 -1
  132. package/dist/lead.d.ts +13 -5
  133. package/dist/lead.js +89 -29
  134. package/dist/limits-ui.d.ts +8 -1
  135. package/dist/limits-ui.js +26 -0
  136. package/dist/live-bus.d.ts +8 -0
  137. package/dist/live-bus.js +40 -4
  138. package/dist/live-detail.d.ts +27 -0
  139. package/dist/live-detail.js +57 -0
  140. package/dist/live-notes.d.ts +20 -0
  141. package/dist/live-notes.js +47 -0
  142. package/dist/live-records.d.ts +140 -0
  143. package/dist/live-records.js +935 -0
  144. package/dist/live-terminal.d.ts +269 -0
  145. package/dist/live-terminal.js +686 -0
  146. package/dist/live.d.ts +38 -27
  147. package/dist/live.js +142 -124
  148. package/dist/mcp-oauth.d.ts +6 -3
  149. package/dist/mcp-oauth.js +8 -4
  150. package/dist/memory-pass.js +2 -2
  151. package/dist/mobile-viewport.d.ts +5 -1
  152. package/dist/mobile-viewport.js +18 -2
  153. package/dist/model-catalog.js +4 -0
  154. package/dist/monitoring-ui.js +2 -2
  155. package/dist/needs-you.d.ts +1 -1
  156. package/dist/needs-you.js +11 -4
  157. package/dist/operate.d.ts +4 -0
  158. package/dist/operate.js +335 -19
  159. package/dist/passwordless.d.ts +23 -0
  160. package/dist/passwordless.js +26 -0
  161. package/dist/peek-cli.d.ts +12 -4
  162. package/dist/peek-cli.js +19 -11
  163. package/dist/phase-routing.js +10 -3
  164. package/dist/planner-source.d.ts +3 -0
  165. package/dist/planner-source.js +14 -0
  166. package/dist/planner.d.ts +2 -1
  167. package/dist/planner.js +7 -1
  168. package/dist/policy-ui.js +2 -2
  169. package/dist/policy.d.ts +1 -20
  170. package/dist/policy.js +29 -2
  171. package/dist/project-delete-ui.d.ts +18 -0
  172. package/dist/project-delete-ui.js +60 -9
  173. package/dist/project-delete.js +2 -2
  174. package/dist/project-memory.d.ts +3 -1
  175. package/dist/project-memory.js +57 -25
  176. package/dist/project-tools.d.ts +5 -3
  177. package/dist/project-tools.js +12 -9
  178. package/dist/provider-auth.d.ts +4 -3
  179. package/dist/provider-auth.js +5 -4
  180. package/dist/provider-connection.d.ts +2 -1
  181. package/dist/provider-connection.js +5 -0
  182. package/dist/provider.d.ts +25 -2
  183. package/dist/provider.js +63 -9
  184. package/dist/pull-request-ui.js +2 -2
  185. package/dist/push.js +1 -1
  186. package/dist/quick-start.d.ts +24 -0
  187. package/dist/quick-start.js +127 -0
  188. package/dist/release-adapters.d.ts +6 -0
  189. package/dist/release-adapters.js +70 -3
  190. package/dist/release.d.ts +10 -1
  191. package/dist/release.js +34 -15
  192. package/dist/request-budget-ui.js +2 -2
  193. package/dist/result-follow-ups.js +3 -2
  194. package/dist/result-review.d.ts +1 -1
  195. package/dist/result-review.js +9 -3
  196. package/dist/retention-ui.js +2 -2
  197. package/dist/retention.js +10 -1
  198. package/dist/run-efficiency.d.ts +73 -0
  199. package/dist/run-efficiency.js +228 -0
  200. package/dist/runner.d.ts +5 -1
  201. package/dist/runner.js +22 -1
  202. package/dist/scope.d.ts +183 -5
  203. package/dist/scope.js +256 -31
  204. package/dist/scout-net.d.ts +4 -1
  205. package/dist/scout-net.js +37 -12
  206. package/dist/scout.d.ts +20 -6
  207. package/dist/scout.js +27 -3
  208. package/dist/serve.js +248 -40
  209. package/dist/server/chat.d.ts +1 -1
  210. package/dist/server/chat.js +122 -31
  211. package/dist/server/chrome.d.ts +16 -2
  212. package/dist/server/chrome.js +63 -11
  213. package/dist/server/console.css +107 -42
  214. package/dist/server/flows.js +38 -8
  215. package/dist/server/guards.d.ts +1 -0
  216. package/dist/server/guards.js +23 -9
  217. package/dist/server/http.d.ts +7 -0
  218. package/dist/server/http.js +3 -2
  219. package/dist/server/live.d.ts +34 -1
  220. package/dist/server/live.js +39 -17
  221. package/dist/server/pages-assets.js +5 -0
  222. package/dist/server/pages.js +90 -13
  223. package/dist/server/people-tokens.d.ts +3 -0
  224. package/dist/server/people-tokens.js +55 -17
  225. package/dist/server/remote-hooks.js +12 -6
  226. package/dist/server/remote.d.ts +2 -1
  227. package/dist/server/remote.js +14 -5
  228. package/dist/server/render-chat.d.ts +12 -3
  229. package/dist/server/render-chat.js +47 -16
  230. package/dist/server/render-pages.d.ts +32 -4
  231. package/dist/server/render-pages.js +83 -13
  232. package/dist/server/render-people.d.ts +3 -2
  233. package/dist/server/render-people.js +3 -2
  234. package/dist/server/render-results.d.ts +18 -2
  235. package/dist/server/render-results.js +37 -28
  236. package/dist/server/render-settings.js +6 -5
  237. package/dist/server/render-tasks.d.ts +35 -10
  238. package/dist/server/render-tasks.js +111 -27
  239. package/dist/server/request-context.d.ts +2 -0
  240. package/dist/server/request-context.js +2 -1
  241. package/dist/server/route-table.d.ts +3 -0
  242. package/dist/server/route-table.js +47 -5
  243. package/dist/server/runtime.d.ts +33 -2
  244. package/dist/server/session.d.ts +25 -1
  245. package/dist/server/session.js +18 -4
  246. package/dist/server/settings.js +142 -10
  247. package/dist/server/sweeps.d.ts +4 -0
  248. package/dist/server/sweeps.js +58 -0
  249. package/dist/server/task-views.d.ts +2 -1
  250. package/dist/server/task-views.js +96 -6
  251. package/dist/server/tasks.js +327 -78
  252. package/dist/server/terminals.d.ts +31 -0
  253. package/dist/server/terminals.js +365 -0
  254. package/dist/setup-guide.js +3 -2
  255. package/dist/slack-chat.js +4 -1
  256. package/dist/spend-ui.js +3 -3
  257. package/dist/spend.js +2 -1
  258. package/dist/steer-capabilities.d.ts +32 -0
  259. package/dist/steer-capabilities.js +60 -0
  260. package/dist/steer-controls.d.ts +14 -0
  261. package/dist/steer-controls.js +36 -0
  262. package/dist/steer-status.js +2 -2
  263. package/dist/storage-ui.js +4 -4
  264. package/dist/store-baseline.d.ts +1 -1
  265. package/dist/store-baseline.js +1 -1
  266. package/dist/store.d.ts +440 -15
  267. package/dist/store.js +1072 -52
  268. package/dist/subagent-desk.js +1 -1
  269. package/dist/summary.js +3 -0
  270. package/dist/task-source.d.ts +58 -0
  271. package/dist/task-source.js +93 -0
  272. package/dist/team-leads.js +1 -1
  273. package/dist/teams-chat.js +4 -1
  274. package/dist/teardown-git.d.ts +22 -0
  275. package/dist/teardown-git.js +136 -2
  276. package/dist/telegram-lead.d.ts +3 -0
  277. package/dist/telegram-lead.js +44 -7
  278. package/dist/telegram.d.ts +11 -2
  279. package/dist/telegram.js +51 -17
  280. package/dist/terminal-catalog.d.ts +11 -0
  281. package/dist/terminal-catalog.js +129 -0
  282. package/dist/text-limits.d.ts +2 -0
  283. package/dist/text-limits.js +2 -0
  284. package/dist/tool-broker.d.ts +14 -0
  285. package/dist/tool-broker.js +67 -2
  286. package/dist/toolroll-update-ui.js +2 -2
  287. package/dist/toolroll-update.js +13 -3
  288. package/dist/tools-ui.js +2 -2
  289. package/dist/unfenced-test-support.d.ts +2 -0
  290. package/dist/unfenced-test-support.js +11 -0
  291. package/dist/webhook-limits.d.ts +23 -0
  292. package/dist/webhook-limits.js +30 -0
  293. package/dist/webhook-readers.d.ts +56 -0
  294. package/dist/webhook-readers.js +138 -0
  295. package/dist/webhook-signing.d.ts +27 -0
  296. package/dist/webhook-signing.js +119 -0
  297. package/dist/work-index.d.ts +29 -0
  298. package/dist/work-index.js +16 -2
  299. package/dist/workspace-revision.js +3 -1
  300. package/dist/writes-mode.d.ts +9 -0
  301. package/dist/writes-mode.js +8 -0
  302. package/package.json +15 -2
package/dist/builder.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { legacyNetwork, repositoryEcosystems } from "./egress.js";
2
+ import { checkoutGit } from "./teardown-git.js";
3
+ import { isAcpProvider } from "./acp-agents.js";
1
4
  import { OBSERVATION_MAILBOX, observationBrief, parseObservationCases, collectObservations } from "./observations.js";
2
5
  import { checkCommandFor, effectiveCheckLevel, recordRunCheckLevel } from "./check-levels.js";
3
6
  import { queueBatchCheck } from "./batch-checks.js";
@@ -39,15 +42,15 @@ import { PREPARED_EVIDENCE_FILE, PREPARED_EVIDENCE_GIT, preparedScreenshotMatche
39
42
  * watchdog and a high runaway-turn breaker still stop a pathological loop;
40
43
  * repair remains narrowly time-bounded because its job is narrowly scoped.
41
44
  */
42
- import { existsSync, unlinkSync, writeFileSync } from "node:fs";
45
+ import { existsSync, realpathSync, unlinkSync, writeFileSync } from "node:fs";
43
46
  import { homedir } from "node:os";
44
- import { join } from "node:path";
47
+ import { join, resolve } from "node:path";
45
48
  import { run, runClaudeStreamJsonl, runOwnerTag, streamsInput } from "./exec.js";
46
49
  import { stopRequestedFor, stopWords, underStopWatch } from "./task-control.js";
47
50
  import { witnessedRunner } from "./process-custody.js";
48
51
  import { runWithIsolatedDatabase } from "./child-database.js";
49
52
  import { recordWorktreeProcess, saveWorkPatch } from "./worktree.js";
50
- import { approvalOf, digestOf, profileDigestOf, routeParityProblem } from "./scope.js";
53
+ import { approvalOf, digestOf, idleClock, isAcpProfile, profileDigestOf, routeParityProblem, watchdogsOf } from "./scope.js";
51
54
  import { legOf, routeDigestOf, routeFromJson } from "./phase-routing.js";
52
55
  import { execFileSync } from "node:child_process";
53
56
  import { currentClaim, heartbeat, SYNC_MAX_AGE_MS, finalize } from "./claim.js";
@@ -56,9 +59,10 @@ import { heartbeat as runnerHeartbeat } from "./runner.js";
56
59
  import { MARKER as LEASE_MARKER } from "./worktree.js";
57
60
  import { parseDecision, parseHandoff, repairPrompt, HEADLESS_RULE, HANDOFF_CONCLUSION_CAP, HANDOFF_ITEM_CAP, HANDOFF_LIST_CAP, HANDOFF_PAYLOAD_CAP } from "./decision.js";
58
61
  import { createHash, randomBytes, randomUUID } from "node:crypto";
59
- import { invokeAgent } from "./invoke.js";
62
+ import { fencedCommands, invokeAgent } from "./invoke.js";
60
63
  import { TELEGRAM_TOKEN_ENVS } from "./names.js";
61
64
  import { OPENROUTER_ENV_KEY, auditOf, ALL_CREDENTIAL_ENV } from "./provider.js";
65
+ import { liveLogContext } from "./live-detail.js";
62
66
  import { openLiveLog } from "./live.js";
63
67
  import { fence } from "./brief-fence.js";
64
68
  import { closeSteerMailbox, openSteerMailbox, STEER_HANDLED } from "./steer-mailbox.js";
@@ -199,12 +203,17 @@ function repairModelOf(profile, request) {
199
203
  function profileWantsSkip(profile) {
200
204
  return ((profile.provider === "claude" && profile.permissionArgv === "bypassPermissions") ||
201
205
  ((profile.provider === "codex" || profile.provider === "openrouter") && profile.sandboxMode === "danger-full-access") ||
202
- (profile.provider === "gemini" && profile.approvalArgv === "yolo"));
206
+ (profile.provider === "gemini" && profile.approvalArgv === "yolo") ||
207
+ // An ACP agent's only posture is full access inside the fence.
208
+ isAcpProfile(profile));
203
209
  }
204
210
  const PROVIDER_VERSIONS = new Map();
205
211
  /** Provenance only (finding 20): a best-effort `--version` probe, cached
206
212
  * per process, null on any failure — never authority, never a refusal. */
207
213
  function providerVersionOf(provider) {
214
+ // An ACP agent is never run outside the gateway, not even for --version: its version is what its check recorded.
215
+ if (isAcpProvider(provider))
216
+ return null;
208
217
  if (PROVIDER_VERSIONS.has(provider))
209
218
  return PROVIDER_VERSIONS.get(provider) ?? null;
210
219
  let version = null;
@@ -221,6 +230,51 @@ function providerVersionOf(provider) {
221
230
  PROVIDER_VERSIONS.set(provider, version);
222
231
  return version;
223
232
  }
233
+ /** A build's network (egress.ts): the one its approval sealed, or — sealed before network policies existed — the
234
+ * narrowed legacy one (provider hosts and the repository's registries, nothing added since). */
235
+ export function launchNetworkOf(store, taskRef, profile) {
236
+ if (profile.network !== undefined)
237
+ return { policy: profile.network };
238
+ const repo = store.refForId(taskRef)?.repo ?? null;
239
+ return { policy: legacyNetwork(profile.provider, repo === null ? [] : repositoryEcosystems(repo)) };
240
+ }
241
+ /** Asking a checkout which branch it is on: reads no file a build wrote and runs nothing. */
242
+ const BRANCH_QUERIES = [
243
+ ["--no-optional-locks", "rev-parse", "--abbrev-ref", "HEAD"], ["--no-optional-locks", "symbolic-ref", "--short", "-q", "HEAD"],
244
+ ];
245
+ /**
246
+ * The plane's git as a build runs it: hardened (teardown-git.ts checkoutGit) for every call, failing closed, but one —
247
+ * asking the project's own main checkout which branch it is on (its config is the operator's, and the query runs
248
+ * nothing). An injected runner (`request.git`: tests only; production always runs git itself) is used as given.
249
+ */
250
+ function buildCheckoutGit(given, worktree, mainCheckout = null) {
251
+ if (given !== run)
252
+ return given;
253
+ const hardened = checkoutGit(given);
254
+ const real = (path) => { try {
255
+ return realpathSync(resolve(path));
256
+ }
257
+ catch {
258
+ return null;
259
+ } };
260
+ const trusted = mainCheckout === null ? null : real(mainCheckout);
261
+ const own = real(worktree);
262
+ return (file, args, options) => {
263
+ const at = options?.cwd === undefined ? null : real(options.cwd);
264
+ const query = BRANCH_QUERIES.some(one => one.length === args.length && one.every((word, index) => args[index] === word));
265
+ return trusted !== null && at === trusted && at !== own && query ? given(file, args, options) : hardened(file, args, options);
266
+ };
267
+ }
268
+ /** A run's setup and check commands, in its build's fence, writing where the approval this build runs under sealed
269
+ * (never the scope as it may since have been rewritten; invoke.ts fencedCommands). */
270
+ function commandRunner(store, runId, profile, inner) {
271
+ return fencedCommands(store, runId, launchWritesOf(profile), inner);
272
+ }
273
+ /** Where a build may write (writes-mode.ts): what its approval sealed, or — sealed before write allowlists — anywhere
274
+ * its fence allows, as it was approved. */
275
+ export function launchWritesOf(profile) {
276
+ return profile.writes ?? "anywhere";
277
+ }
224
278
  export function proveApprovedProfile(scope, given) {
225
279
  // The raw terms first (raw authority repair): a scope whose stored terms
226
280
  // do not read back exactly proves nothing — not the filtered reading.
@@ -293,7 +347,7 @@ export function proveApprovedProfile(scope, given) {
293
347
  if (given.timeoutMs !== undefined && given.timeoutMs !== snapshot.timeoutSeconds * 1000) {
294
348
  return {
295
349
  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)`,
350
+ 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
351
  };
298
352
  }
299
353
  return {
@@ -315,7 +369,9 @@ export function proveApprovedProfile(scope, given) {
315
369
  * that forgot one is exactly the caller this is protecting against.
316
370
  */
317
371
  export async function build(store, request) {
318
- const { taskId, taskRef, runner, worktree, branch, now, permissionMode = "auto", skipPermissions = false, provider = "claude", model, maxTurns = DEFAULT_MAX_TURNS, timeoutMs = DEFAULT_BUILD_TIMEOUT_MS, agent, git = run, } = request;
372
+ const { taskId, taskRef, runner, worktree, branch, now, permissionMode = "auto", skipPermissions = false, provider = "claude", model, maxTurns = DEFAULT_MAX_TURNS, timeoutMs = DEFAULT_BUILD_TIMEOUT_MS, agent, git: givenGit = run, } = request;
373
+ // The plane's own git on this build's checkout runs hardened, and never where the checkout's config names a command.
374
+ const git = buildCheckoutGit(givenGit, worktree, store.getWorktree(worktree)?.repo ?? null);
319
375
  const scope = store.getScope(taskId);
320
376
  const approval = approvalOf(scope);
321
377
  if (!approval.approved) {
@@ -609,7 +665,7 @@ export async function build(store, request) {
609
665
  // Setup runs under the stop watch (v52), owned by this run: an
610
666
  // operator's stop ends the setup's process group and the attempt
611
667
  // settles as interrupted below, never as a setup failure.
612
- const made = await underStopWatch(store, request.runId, () => runWithIsolatedDatabase(witnessedRunner(store, request.runId, request.clock ?? (() => now), runSetup), shell.file, shell.args, {
668
+ const made = await underStopWatch(store, request.runId, () => runWithIsolatedDatabase(witnessedRunner(store, request.runId, request.clock ?? (() => now), commandRunner(store, request.runId, effective.profile, runSetup)), shell.file, shell.args, {
613
669
  cwd: worktree,
614
670
  timeoutMs: setupWanted.timeoutMs,
615
671
  processGroup: true,
@@ -1123,7 +1179,7 @@ export async function build(store, request) {
1123
1179
  maxTurns: effective.maxTurns ?? maxTurns,
1124
1180
  finished: () => handoffFiles.some(name => existsSync(join(worktree, name))),
1125
1181
  // 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),
1182
+ onOpen: () => store.markLiveInputOpened(request.runId, clock(), idleClock(effective.profile) ? effective.timeoutMs : null),
1127
1183
  onClose: () => store.markLiveInputClosed(request.runId, clock()),
1128
1184
  onPaused: () => store.markRunPaused(request.runId, clock()),
1129
1185
  });
@@ -1153,7 +1209,7 @@ export async function build(store, request) {
1153
1209
  // Every streaming transport emits events now (peek); a file that cannot
1154
1210
  // open is a null, and a null never costs a build.
1155
1211
  // 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)
1212
+ const liveLog = withAgentTurns(withRunActivity(openLiveLog(root, request.runId, liveLogContext(store, taskRef, worktree)), store, request.runId, clock), (state, at) => { if (attemptKey !== null)
1157
1213
  recordAgentTurn(store, { run: request.runId, attemptKey, state, at }); }, clock, (open, at) => { if (attemptKey !== null)
1158
1214
  noteOpenTool(store, request.runId, attemptKey, open, at); });
1159
1215
  // The retry base (steering fix for run 1465's proof): the SAME pinned
@@ -1291,10 +1347,10 @@ export async function build(store, request) {
1291
1347
  ...(liveSession === null ? {} : { liveInput: liveSession }),
1292
1348
  // New approvals bind a no-progress watchdog, not a deadline. Legacy
1293
1349
  // snapshots carry no timeoutKind and retain their wall-clock terms.
1294
- ...(effective.profile.timeoutKind === "idle"
1295
- ? { idleTimeoutMs: effective.timeoutMs }
1296
- : { timeoutMs: effective.timeoutMs }),
1350
+ ...watchdogsOf(effective.profile, effective.timeoutMs),
1297
1351
  omitEnv: AGENT_ENV_DENYLIST,
1352
+ network: launchNetworkOf(store, request.taskRef, effective.profile),
1353
+ writes: launchWritesOf(effective.profile),
1298
1354
  ...(agent === undefined ? {} : { runner: agent }),
1299
1355
  onSpawn: (pid) => { spawned = true; request.onProviderSpawn?.(pid); },
1300
1356
  onStreamEvent: (event) => liveLog.observe(event),
@@ -1490,7 +1546,7 @@ export async function settleProviderOutcome(captured, result) {
1490
1546
  return {
1491
1547
  ok: false,
1492
1548
  reason: "timeout",
1493
- message: effective.profile.timeoutKind === "idle"
1549
+ message: idleClock(effective.profile)
1494
1550
  ? `the builder made no observable progress for ${Math.round(timeoutMs / 60_000)} minutes and was stopped — whatever it wrote is still in ${worktree}`
1495
1551
  : `the builder ran past ${Math.round(timeoutMs / 60_000)} minutes and was stopped — whatever it wrote is still in ${worktree}`,
1496
1552
  };
@@ -1759,7 +1815,7 @@ export async function settleProviderOutcome(captured, result) {
1759
1815
  };
1760
1816
  if (!eligible())
1761
1817
  throw Error("The observation source or execution authority changed.");
1762
- const execute = (file, args, options) => underStopWatch(store, request.runId, () => runWithIsolatedDatabase(witnessedRunner(store, request.runId, clock, request.verify ?? run), file, args, {
1818
+ const execute = (file, args, options) => underStopWatch(store, request.runId, () => runWithIsolatedDatabase(witnessedRunner(store, request.runId, clock, commandRunner(store, request.runId, captured.effective.profile, request.verify ?? run)), file, args, {
1763
1819
  ...options, processGroup: true, owner: runOwnerTag(store, request.runId), beforeSpawn: eligible,
1764
1820
  onSpawn: pid => request.onProviderSpawn?.(pid), envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST,
1765
1821
  }));
@@ -2219,7 +2275,7 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2219
2275
  const runVerification = async (label) => {
2220
2276
  const streamed = { stdout: false, stderr: false };
2221
2277
  // The check runs under the stop watch (v52), owned by this run.
2222
- const result = await underStopWatch(store, runId, () => runWithIsolatedDatabase(witnessedRunner(store, runId, now, verifyRunner), verifyShell.file, verifyShell.args, {
2278
+ const result = await underStopWatch(store, runId, () => runWithIsolatedDatabase(witnessedRunner(store, runId, now, commandRunner(store, runId, captured.effective.profile, verifyRunner)), verifyShell.file, verifyShell.args, {
2223
2279
  cwd: worktree,
2224
2280
  timeoutMs: configured.timeoutMs,
2225
2281
  processGroup: true,
@@ -2261,7 +2317,7 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2261
2317
  detachShared(worktree, depsRoot);
2262
2318
  const label = "Own install · approved project setup (package.json or lockfile changed)";
2263
2319
  const setupShell = approvedCommandShell(live.command);
2264
- const installed = await underStopWatch(store, runId, () => runWithIsolatedDatabase(witnessedRunner(store, runId, now, request.setup ?? run), setupShell.file, setupShell.args, {
2320
+ const installed = await underStopWatch(store, runId, () => runWithIsolatedDatabase(witnessedRunner(store, runId, now, commandRunner(store, runId, captured.effective.profile, request.setup ?? run)), setupShell.file, setupShell.args, {
2265
2321
  cwd: worktree,
2266
2322
  timeoutMs: live.timeoutMs,
2267
2323
  processGroup: true,
@@ -2280,7 +2336,7 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2280
2336
  checkLog.push(attemptLog(label, live.command, installed));
2281
2337
  if (installed.notFound || installed.timedOut || installed.code !== 0)
2282
2338
  return "own-install-failed";
2283
- return await sealedTreeState(request.git ?? run) === "clean" ? null : "setup-changed-files";
2339
+ return await sealedTreeState(buildCheckoutGit(request.git ?? run, worktree)) === "clean" ? null : "setup-changed-files";
2284
2340
  };
2285
2341
  const ownInstall = await ownInstallIfChanged();
2286
2342
  const first = ownInstall === null ? await runVerification("Project check · attempt 1") : null;
@@ -2324,7 +2380,7 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2324
2380
  verifyCommand = { configured: true, ran: false, attemptFailed: true, failure: "custody-lost" };
2325
2381
  }
2326
2382
  else {
2327
- const gitRunner = request.git ?? run;
2383
+ const gitRunner = buildCheckoutGit(request.git ?? run, worktree);
2328
2384
  const beforeSetup = await sealedTreeState(gitRunner);
2329
2385
  if (beforeSetup === "head-moved") {
2330
2386
  verifyCommand = { configured: true, ran: false, attemptFailed: true, failure: "checkout-moved" };
@@ -2351,7 +2407,7 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2351
2407
  else {
2352
2408
  const setupRunner = request.setup ?? run;
2353
2409
  const setupShell = approvedCommandShell(liveBeforeSetup.command);
2354
- const restored = await underStopWatch(store, runId, () => runWithIsolatedDatabase(witnessedRunner(store, runId, now, setupRunner), setupShell.file, setupShell.args, {
2410
+ const restored = await underStopWatch(store, runId, () => runWithIsolatedDatabase(witnessedRunner(store, runId, now, commandRunner(store, runId, captured.effective.profile, setupRunner)), setupShell.file, setupShell.args, {
2355
2411
  cwd: worktree,
2356
2412
  timeoutMs: liveBeforeSetup.timeoutMs,
2357
2413
  processGroup: true,
@@ -2614,7 +2670,8 @@ async function resumeUnhandedWork(captured, sessionId) {
2614
2670
  if (request.leaseId !== undefined && !heartbeat(store, request.leaseId, clock()).ok) {
2615
2671
  return { ok: false, reason: "fenced", message: `${taskId}'s lease did not survive the resumed turn — the work is still in ${worktree}` };
2616
2672
  }
2617
- if (store.budgetGate(clock())({ ...store.budgetSubject(request.taskRef), agents: [{ provider, billing: store.runBilling(request.runId) ?? store.agentsFor([provider])[0].billing }] }).over !== null)
2673
+ // An ACP agent's turn bills as its own model's declaration on this runner, never as the attempt it continues.
2674
+ 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
2675
  break;
2619
2676
  if (store.agentPolicyRefusal(provider, model ?? null) !== null)
2620
2677
  break;
@@ -2634,8 +2691,10 @@ async function resumeUnhandedWork(captured, sessionId) {
2634
2691
  ...(request.maxBudgetUsd === undefined ? {} : { maxBudgetUsd: request.maxBudgetUsd }),
2635
2692
  }, {
2636
2693
  cwd: worktree,
2637
- ...(effective.profile.timeoutKind === "idle" ? { idleTimeoutMs: effective.timeoutMs } : { timeoutMs: effective.timeoutMs }),
2694
+ ...watchdogsOf(effective.profile, effective.timeoutMs),
2638
2695
  omitEnv: AGENT_ENV_DENYLIST,
2696
+ network: launchNetworkOf(store, request.taskRef, effective.profile),
2697
+ writes: launchWritesOf(effective.profile),
2639
2698
  ...(agent === undefined ? {} : { runner: agent }),
2640
2699
  ...(request.onProviderSpawn === undefined ? {} : { onSpawn: request.onProviderSpawn }),
2641
2700
  clock,
@@ -2811,7 +2870,8 @@ async function ingestPark(args) {
2811
2870
  }
2812
2871
  // v105: a repair turn spends too — a budget used up since the build began stops further turns (billed to a key).
2813
2872
  // 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)
2873
+ // An ACP repair bills as its own model's declaration on this runner, never as the attempt it mends.
2874
+ 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
2875
  break;
2816
2876
  // Sprint 8: nor does a repair run on a provider or model the organisation policy doesn't allow.
2817
2877
  if (store.agentPolicyRefusal(repairProvider, repairModel ?? null) !== null)
@@ -2845,8 +2905,11 @@ async function ingestPark(args) {
2845
2905
  : {}),
2846
2906
  }, {
2847
2907
  cwd: worktree,
2848
- timeoutMs: args.profile !== undefined ? args.profile.repairTimeoutSeconds * 1000 : REPAIR_TIMEOUT_MS,
2908
+ ...(args.profile !== undefined && isAcpProfile(args.profile)
2909
+ ? watchdogsOf(args.profile, args.profile.repairTimeoutSeconds * 1000, true)
2910
+ : { timeoutMs: args.profile !== undefined ? args.profile.repairTimeoutSeconds * 1000 : REPAIR_TIMEOUT_MS }),
2849
2911
  omitEnv: AGENT_ENV_DENYLIST,
2912
+ ...(args.profile === undefined ? {} : { network: launchNetworkOf(store, request.taskRef, args.profile), writes: launchWritesOf(args.profile) }),
2850
2913
  ...(agent === undefined ? {} : { runner: agent }),
2851
2914
  ...(request.onProviderSpawn === undefined ? {} : { onSpawn: request.onProviderSpawn }),
2852
2915
  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";