toolroll 0.9.62 → 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 (345) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +66 -8
  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 +203 -10
  16. package/dist/agent-fence.js +623 -41
  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 +263 -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 +199 -17
  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/check-levels.js +3 -0
  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 +20 -1
  59. package/dist/cli-help.d.ts +1 -1
  60. package/dist/cli-help.js +5 -1
  61. package/dist/cli.d.ts +3 -0
  62. package/dist/cli.js +24 -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/cli.d.ts +8 -0
  71. package/dist/contracts/cli.js +3 -1
  72. package/dist/contracts/console-api.d.ts +30 -4
  73. package/dist/contracts/console-api.js +13 -4
  74. package/dist/contracts/flow-card.d.ts +4 -0
  75. package/dist/contracts/flow-card.js +3 -0
  76. package/dist/contracts/flow.d.ts +46 -0
  77. package/dist/contracts/flow.js +11 -2
  78. package/dist/contracts/integration-metadata.d.ts +2 -1
  79. package/dist/contracts/integration-metadata.js +2 -1
  80. package/dist/contracts/lead-tools.d.ts +92 -0
  81. package/dist/contracts/lead-tools.js +13 -0
  82. package/dist/contracts/provider.d.ts +11 -1
  83. package/dist/contracts/provider.js +1 -1
  84. package/dist/contracts/route.d.ts +76 -1
  85. package/dist/contracts/route.js +1 -1
  86. package/dist/contracts/store-json.d.ts +2 -0
  87. package/dist/control-setup.d.ts +1 -1
  88. package/dist/control-ui.js +2 -2
  89. package/dist/converse.d.ts +16 -0
  90. package/dist/converse.js +32 -4
  91. package/dist/demo-chat.js +4 -4
  92. package/dist/demo.js +2 -2
  93. package/dist/desktop-update-gate.js +6 -0
  94. package/dist/discord-chat.js +4 -1
  95. package/dist/dispatch.js +7 -4
  96. package/dist/doctor.d.ts +53 -0
  97. package/dist/doctor.js +427 -0
  98. package/dist/egress-bridge.d.ts +17 -0
  99. package/dist/egress-bridge.js +111 -0
  100. package/dist/egress.d.ts +119 -0
  101. package/dist/egress.js +423 -0
  102. package/dist/evidence-pack.d.ts +1 -1
  103. package/dist/evidence-pack.js +2 -2
  104. package/dist/exec.d.ts +81 -0
  105. package/dist/exec.js +368 -9
  106. package/dist/export-ui.js +2 -2
  107. package/dist/flow-code.d.ts +2 -2
  108. package/dist/flow-code.js +4 -15
  109. package/dist/flow-engine.js +35 -11
  110. package/dist/flow-gallery-ui.js +2 -2
  111. package/dist/flow-gallery.js +27 -8
  112. package/dist/flow-share.js +16 -5
  113. package/dist/flow-triggers.d.ts +84 -11
  114. package/dist/flow-triggers.js +554 -87
  115. package/dist/flow-unattended-words.d.ts +28 -0
  116. package/dist/flow-unattended-words.js +50 -0
  117. package/dist/flow-unattended.d.ts +227 -0
  118. package/dist/flow-unattended.js +776 -0
  119. package/dist/flows-cli.d.ts +28 -1
  120. package/dist/flows-cli.js +63 -12
  121. package/dist/flows-ui.d.ts +2 -3
  122. package/dist/flows-ui.js +56 -5
  123. package/dist/flows.d.ts +4 -0
  124. package/dist/flows.js +9 -1
  125. package/dist/google-mail.d.ts +1 -0
  126. package/dist/google-mail.js +61 -18
  127. package/dist/html.d.ts +9 -0
  128. package/dist/html.js +11 -0
  129. package/dist/integrations-ui.d.ts +2 -1
  130. package/dist/integrations-ui.js +6 -3
  131. package/dist/integrations.d.ts +17 -3
  132. package/dist/integrations.js +38 -18
  133. package/dist/invoke.d.ts +17 -0
  134. package/dist/invoke.js +510 -182
  135. package/dist/keys.d.ts +18 -14
  136. package/dist/keys.js +5 -0
  137. package/dist/landed-cleanup.d.ts +3 -2
  138. package/dist/landed-cleanup.js +12 -5
  139. package/dist/lead-capabilities.js +3 -2
  140. package/dist/lead-contract.js +2 -0
  141. package/dist/lead-doors.d.ts +2 -0
  142. package/dist/lead-doors.js +7 -0
  143. package/dist/lead-follow.d.ts +2 -0
  144. package/dist/lead-follow.js +58 -2
  145. package/dist/lead-progress.js +1 -0
  146. package/dist/lead-queue-runtime.d.ts +46 -0
  147. package/dist/lead-queue-runtime.js +191 -0
  148. package/dist/lead-queue.d.ts +104 -0
  149. package/dist/lead-queue.js +187 -0
  150. package/dist/lead-sweep.d.ts +67 -0
  151. package/dist/lead-sweep.js +99 -0
  152. package/dist/lead-tools.d.ts +22 -0
  153. package/dist/lead-tools.js +163 -4
  154. package/dist/lead.d.ts +13 -5
  155. package/dist/lead.js +89 -29
  156. package/dist/limits-ui.d.ts +8 -1
  157. package/dist/limits-ui.js +26 -0
  158. package/dist/live-bus.d.ts +8 -0
  159. package/dist/live-bus.js +40 -4
  160. package/dist/live-detail.d.ts +27 -0
  161. package/dist/live-detail.js +57 -0
  162. package/dist/live-notes.d.ts +20 -0
  163. package/dist/live-notes.js +47 -0
  164. package/dist/live-records.d.ts +140 -0
  165. package/dist/live-records.js +935 -0
  166. package/dist/live-terminal.d.ts +258 -0
  167. package/dist/live-terminal.js +649 -0
  168. package/dist/live.d.ts +38 -27
  169. package/dist/live.js +142 -124
  170. package/dist/mcp-connect.d.ts +8 -7
  171. package/dist/mcp-connect.js +49 -51
  172. package/dist/mcp-oauth.d.ts +6 -3
  173. package/dist/mcp-oauth.js +8 -4
  174. package/dist/memory-pass.js +2 -2
  175. package/dist/mobile-viewport.d.ts +5 -1
  176. package/dist/mobile-viewport.js +18 -2
  177. package/dist/model-catalog.js +4 -0
  178. package/dist/monitoring-ui.js +2 -2
  179. package/dist/needs-you.d.ts +1 -1
  180. package/dist/needs-you.js +11 -4
  181. package/dist/oauth-run-token.d.ts +51 -0
  182. package/dist/oauth-run-token.js +127 -0
  183. package/dist/operate.d.ts +2 -0
  184. package/dist/operate.js +282 -18
  185. package/dist/passwordless.d.ts +23 -0
  186. package/dist/passwordless.js +26 -0
  187. package/dist/peek-cli.d.ts +12 -4
  188. package/dist/peek-cli.js +19 -11
  189. package/dist/phase-routing.js +10 -3
  190. package/dist/plan-auto.d.ts +11 -0
  191. package/dist/plan-auto.js +62 -11
  192. package/dist/planner-source.d.ts +3 -0
  193. package/dist/planner-source.js +14 -0
  194. package/dist/planner.d.ts +2 -1
  195. package/dist/planner.js +7 -1
  196. package/dist/policy-ui.js +2 -2
  197. package/dist/policy.d.ts +1 -20
  198. package/dist/policy.js +29 -2
  199. package/dist/process-recovery-eligibility.d.ts +1 -1
  200. package/dist/project-access.d.ts +3 -0
  201. package/dist/project-access.js +3 -0
  202. package/dist/project-delete-ui.d.ts +12 -0
  203. package/dist/project-delete-ui.js +41 -9
  204. package/dist/project-delete.d.ts +3 -1
  205. package/dist/project-delete.js +12 -6
  206. package/dist/project-file.d.ts +3 -0
  207. package/dist/project-file.js +16 -0
  208. package/dist/project-memory.d.ts +3 -1
  209. package/dist/project-memory.js +57 -25
  210. package/dist/project-tools.d.ts +103 -27
  211. package/dist/project-tools.js +399 -100
  212. package/dist/provider-auth.d.ts +4 -3
  213. package/dist/provider-auth.js +5 -4
  214. package/dist/provider-connection.d.ts +2 -1
  215. package/dist/provider-connection.js +5 -0
  216. package/dist/provider-home.d.ts +22 -0
  217. package/dist/provider-home.js +99 -0
  218. package/dist/provider.d.ts +42 -5
  219. package/dist/provider.js +97 -19
  220. package/dist/pull-request-ui.js +2 -2
  221. package/dist/push.js +1 -1
  222. package/dist/quick-start.d.ts +24 -0
  223. package/dist/quick-start.js +127 -0
  224. package/dist/release.d.ts +3 -0
  225. package/dist/release.js +14 -8
  226. package/dist/remote-main.d.ts +4 -0
  227. package/dist/remote-main.js +3 -3
  228. package/dist/request-budget-ui.js +2 -2
  229. package/dist/result-review.d.ts +1 -1
  230. package/dist/result-review.js +9 -3
  231. package/dist/retention-ui.js +2 -2
  232. package/dist/retention.js +10 -1
  233. package/dist/run-efficiency.d.ts +73 -0
  234. package/dist/run-efficiency.js +228 -0
  235. package/dist/runner.d.ts +5 -1
  236. package/dist/runner.js +22 -1
  237. package/dist/scope.d.ts +171 -6
  238. package/dist/scope.js +251 -31
  239. package/dist/scout-net.d.ts +4 -1
  240. package/dist/scout-net.js +37 -12
  241. package/dist/scout.d.ts +20 -6
  242. package/dist/scout.js +27 -3
  243. package/dist/serve.js +258 -43
  244. package/dist/server/chat.d.ts +1 -1
  245. package/dist/server/chat.js +122 -31
  246. package/dist/server/chrome.d.ts +6 -1
  247. package/dist/server/chrome.js +18 -7
  248. package/dist/server/console.css +52 -38
  249. package/dist/server/flows.js +65 -13
  250. package/dist/server/guards.d.ts +1 -0
  251. package/dist/server/guards.js +23 -9
  252. package/dist/server/http.d.ts +7 -0
  253. package/dist/server/http.js +3 -2
  254. package/dist/server/live.d.ts +34 -1
  255. package/dist/server/live.js +39 -17
  256. package/dist/server/pages-assets.js +5 -0
  257. package/dist/server/pages.js +90 -13
  258. package/dist/server/people-tokens.d.ts +3 -0
  259. package/dist/server/people-tokens.js +55 -17
  260. package/dist/server/remote-hooks.js +12 -6
  261. package/dist/server/remote.d.ts +2 -1
  262. package/dist/server/remote.js +14 -5
  263. package/dist/server/render-chat.d.ts +12 -3
  264. package/dist/server/render-chat.js +47 -16
  265. package/dist/server/render-pages.d.ts +4 -1
  266. package/dist/server/render-pages.js +5 -4
  267. package/dist/server/render-people.d.ts +3 -2
  268. package/dist/server/render-people.js +3 -2
  269. package/dist/server/render-results.d.ts +18 -2
  270. package/dist/server/render-results.js +37 -28
  271. package/dist/server/render-settings.js +6 -5
  272. package/dist/server/render-tasks.d.ts +35 -10
  273. package/dist/server/render-tasks.js +110 -27
  274. package/dist/server/request-context.d.ts +2 -0
  275. package/dist/server/request-context.js +2 -1
  276. package/dist/server/route-table.d.ts +3 -0
  277. package/dist/server/route-table.js +49 -5
  278. package/dist/server/runtime.d.ts +34 -3
  279. package/dist/server/session.d.ts +25 -1
  280. package/dist/server/session.js +18 -4
  281. package/dist/server/settings.js +132 -13
  282. package/dist/server/sweeps.d.ts +4 -0
  283. package/dist/server/sweeps.js +58 -0
  284. package/dist/server/task-views.d.ts +2 -1
  285. package/dist/server/task-views.js +96 -6
  286. package/dist/server/tasks.js +320 -74
  287. package/dist/server/terminals.d.ts +31 -0
  288. package/dist/server/terminals.js +365 -0
  289. package/dist/setup-guide.js +3 -2
  290. package/dist/slack-chat.js +4 -1
  291. package/dist/spend-ui.js +3 -3
  292. package/dist/spend.js +2 -1
  293. package/dist/steer-capabilities.d.ts +32 -0
  294. package/dist/steer-capabilities.js +60 -0
  295. package/dist/steer-controls.d.ts +14 -0
  296. package/dist/steer-controls.js +36 -0
  297. package/dist/steer-status.js +2 -2
  298. package/dist/storage-sweep-child.js +3 -1
  299. package/dist/storage-sweep-process.d.ts +12 -5
  300. package/dist/storage-sweep-process.js +23 -6
  301. package/dist/storage-ui.js +4 -4
  302. package/dist/store-baseline.d.ts +1 -1
  303. package/dist/store-baseline.js +1 -1
  304. package/dist/store.d.ts +466 -19
  305. package/dist/store.js +1185 -63
  306. package/dist/subagent-desk.js +1 -1
  307. package/dist/subagent-tools.d.ts +1 -1
  308. package/dist/summary.js +3 -0
  309. package/dist/surface.js +6 -4
  310. package/dist/task-source.d.ts +58 -0
  311. package/dist/task-source.js +93 -0
  312. package/dist/team-leads.js +1 -1
  313. package/dist/teams-chat.js +4 -1
  314. package/dist/teardown-git.d.ts +14 -0
  315. package/dist/teardown-git.js +119 -0
  316. package/dist/telegram-lead.d.ts +3 -0
  317. package/dist/telegram-lead.js +44 -7
  318. package/dist/telegram.d.ts +11 -2
  319. package/dist/telegram.js +51 -17
  320. package/dist/terminal-catalog.d.ts +11 -0
  321. package/dist/terminal-catalog.js +129 -0
  322. package/dist/text-limits.d.ts +2 -0
  323. package/dist/text-limits.js +2 -0
  324. package/dist/tool-broker.d.ts +39 -0
  325. package/dist/tool-broker.js +370 -0
  326. package/dist/tool-launcher.js +5 -4
  327. package/dist/tool-secret-store.d.ts +170 -0
  328. package/dist/tool-secret-store.js +834 -0
  329. package/dist/toolroll-update-ui.js +2 -2
  330. package/dist/toolroll-update.js +14 -3
  331. package/dist/tools-ui.d.ts +7 -1
  332. package/dist/tools-ui.js +19 -10
  333. package/dist/webhook-limits.d.ts +23 -0
  334. package/dist/webhook-limits.js +30 -0
  335. package/dist/webhook-readers.d.ts +56 -0
  336. package/dist/webhook-readers.js +138 -0
  337. package/dist/webhook-signing.d.ts +27 -0
  338. package/dist/webhook-signing.js +119 -0
  339. package/dist/work-index.d.ts +29 -0
  340. package/dist/work-index.js +21 -5
  341. package/dist/workspace-revision.js +3 -1
  342. package/dist/worktree.d.ts +10 -4
  343. package/dist/worktree.js +70 -29
  344. package/package.json +13 -2
  345. package/scripts/provider-canary.mjs +210 -31
@@ -14,16 +14,17 @@
14
14
  */
15
15
  import type { Store } from "./store.js";
16
16
  import { type ProviderId } from "./provider.js";
17
+ import type { AuthMode } from "./keys.js";
17
18
  /** Every sign-in message's dedupe key starts here: `signin:<kind>:<provider>:<ordinal>:<person>`.
18
19
  * Not a lifecycle (`life:`) key: those are progress chatter, and an undelivered
19
20
  * one stays out of the attention list — this one needs a person. */
20
21
  export declare const SIGN_IN_KEY_PREFIX = "signin:";
21
- export declare const PROVIDER_AUTH_SCHEMA = "\nCREATE TABLE IF NOT EXISTS provider_auth_pause (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n ordinal INTEGER NOT NULL,\n auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key')),\n opened_at TEXT NOT NULL,\n first_run INTEGER,\n task_ref INTEGER,\n runs INTEGER NOT NULL DEFAULT 1,\n -- 1 once a sign-in probe during this pause said the provider is signed out:\n -- only a probe that then sees it signed in again may lift the pause.\n probe_saw_out INTEGER NOT NULL DEFAULT 0,\n lifted_at TEXT,\n lifted_by TEXT,\n lifted_how TEXT CHECK (lifted_how IS NULL OR lifted_how IN ('run', 'probe', 'person')),\n resumed INTEGER,\n -- The last task let through as a trial while paused: a sign-in check can\n -- say \"logged in\" for a session that no longer works, so a real run decides.\n last_trial_at TEXT,\n UNIQUE (provider, ordinal)\n);\nCREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;\n";
22
+ export declare const PROVIDER_AUTH_SCHEMA = "\nCREATE TABLE IF NOT EXISTS provider_auth_pause (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n ordinal INTEGER NOT NULL,\n auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key', 'agent-login')),\n opened_at TEXT NOT NULL,\n first_run INTEGER,\n task_ref INTEGER,\n runs INTEGER NOT NULL DEFAULT 1,\n -- 1 once a sign-in probe during this pause said the provider is signed out:\n -- only a probe that then sees it signed in again may lift the pause.\n probe_saw_out INTEGER NOT NULL DEFAULT 0,\n lifted_at TEXT,\n lifted_by TEXT,\n lifted_how TEXT CHECK (lifted_how IS NULL OR lifted_how IN ('run', 'probe', 'person')),\n resumed INTEGER,\n -- The last task let through as a trial while paused: a sign-in check can\n -- say \"logged in\" for a session that no longer works, so a real run decides.\n last_trial_at TEXT,\n UNIQUE (provider, ordinal)\n);\nCREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;\n";
22
23
  export type AuthPause = {
23
24
  id: number;
24
25
  provider: ProviderId;
25
26
  ordinal: number;
26
- authMode: "subscription" | "api-key";
27
+ authMode: AuthMode;
27
28
  openedAt: string;
28
29
  firstRun: number | null;
29
30
  taskRef: number | null;
@@ -63,7 +64,7 @@ export declare function openAuthPauses(store: Store): AuthPause[];
63
64
  */
64
65
  export declare function pauseForAuth(store: Store, args: {
65
66
  provider: string;
66
- authMode: "subscription" | "api-key" | null;
67
+ authMode: AuthMode | null;
67
68
  runId: number;
68
69
  taskRef: number;
69
70
  now: Date;
@@ -1,4 +1,5 @@
1
1
  import { PROVIDER_IDS } from "./provider.js";
2
+ import { withAcp } from "./acp-agents.js";
2
3
  /** Every sign-in message's dedupe key starts here: `signin:<kind>:<provider>:<ordinal>:<person>`.
3
4
  * Not a lifecycle (`life:`) key: those are progress chatter, and an undelivered
4
5
  * one stays out of the attention list — this one needs a person. */
@@ -8,7 +9,7 @@ CREATE TABLE IF NOT EXISTS provider_auth_pause (
8
9
  id INTEGER PRIMARY KEY AUTOINCREMENT,
9
10
  provider TEXT NOT NULL,
10
11
  ordinal INTEGER NOT NULL,
11
- auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key')),
12
+ auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key', 'agent-login')),
12
13
  opened_at TEXT NOT NULL,
13
14
  first_run INTEGER,
14
15
  task_ref INTEGER,
@@ -27,8 +28,8 @@ CREATE TABLE IF NOT EXISTS provider_auth_pause (
27
28
  );
28
29
  CREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;
29
30
  `;
30
- const NAMES = { claude: "Claude", codex: "Codex", openrouter: "OpenRouter", gemini: "Gemini" };
31
- const SIGN_IN = { claude: "claude /login", codex: "codex login", gemini: "gemini", openrouter: "toolroll keys set openrouter" };
31
+ const NAMES = withAcp({ claude: "Claude", codex: "Codex", openrouter: "OpenRouter", gemini: "Gemini" }, agent => agent.name);
32
+ const SIGN_IN = withAcp({ claude: "claude /login", codex: "codex login", gemini: "gemini", openrouter: "toolroll keys set openrouter" }, agent => agent.signIn);
32
33
  export function providerName(provider) {
33
34
  return PROVIDER_IDS.includes(provider) ? NAMES[provider] : provider;
34
35
  }
@@ -57,7 +58,7 @@ function readPause(row) {
57
58
  id: Number(row["id"]),
58
59
  provider: String(row["provider"]),
59
60
  ordinal: Number(row["ordinal"]),
60
- authMode: row["auth_mode"] === "api-key" ? "api-key" : "subscription",
61
+ authMode: row["auth_mode"] === "api-key" ? "api-key" : row["auth_mode"] === "agent-login" ? "agent-login" : "subscription",
61
62
  openedAt: String(row["opened_at"]),
62
63
  firstRun: row["first_run"] == null ? null : Number(row["first_run"]),
63
64
  taskRef: row["task_ref"] == null ? null : Number(row["task_ref"]),
@@ -1,5 +1,6 @@
1
1
  import { run, type ExecResult } from "./exec.js";
2
2
  import { type ProviderId } from "./provider.js";
3
+ import { type KeyedProviderId } from "./acp-agents.js";
3
4
  import { type AuthMode, type KeyVerdict } from "./keys.js";
4
5
  export type ProviderConnection = {
5
6
  state: "connected" | "signed-out" | "not-installed" | "unverified" | "key-present" | "key-works" | "key-refused" | "missing-key";
@@ -18,5 +19,5 @@ export declare function createConnectionChecker(options?: {
18
19
  home?: string;
19
20
  clock?: () => Date;
20
21
  env?: NodeJS.ProcessEnv;
21
- verify?: (provider: ProviderId, key: string) => Promise<KeyVerdict>;
22
+ verify?: (provider: KeyedProviderId, key: string) => Promise<KeyVerdict>;
22
23
  }): ConnectionChecker;
@@ -2,6 +2,7 @@
2
2
  import { homedir } from "node:os";
3
3
  import { run } from "./exec.js";
4
4
  import { ALL_CREDENTIAL_ENV, inspectionOf } from "./provider.js";
5
+ import { isAcpProvider } from "./acp-agents.js";
5
6
  import { keyStatus, PROVIDER_KEY_ENV, readAuthMode, readProviderKey, verifyProviderKey } from "./keys.js";
6
7
  export function signInFacts(provider, result, now = new Date()) {
7
8
  const base = { mode: "subscription", checkedAt: now.toISOString() };
@@ -46,6 +47,10 @@ export function createConnectionChecker(options = {}) {
46
47
  const cache = new Map();
47
48
  const pending = new Map();
48
49
  return async (provider, fresh = false) => {
50
+ // An ACP agent is never run to ask how it is signed in (nothing executes it outside the gateway): what is known
51
+ // of it is its conformance check, read elsewhere. Here it is simply unverified.
52
+ if (isAcpProvider(provider))
53
+ return { state: "unverified", mode: "agent-login", checkedAt: clock().toISOString() };
49
54
  const mode = readAuthMode(provider, home);
50
55
  const keyFacts = keyStatus(provider, home);
51
56
  const ambient = !!(options.env ?? process.env)[PROVIDER_KEY_ENV[provider]];
@@ -0,0 +1,22 @@
1
+ /** The operator's own choices a no-model build keeps. */
2
+ export type ProviderDefaults = {
3
+ model?: string;
4
+ alwaysThinking?: boolean;
5
+ effort?: string;
6
+ };
7
+ export declare function claudeConfigDir(env?: Readonly<Record<string, string | undefined>>, home?: string): string;
8
+ export declare function codexHomeDir(env?: Readonly<Record<string, string | undefined>>, home?: string): string;
9
+ /** Claude's user settings: its `model` and `alwaysThinkingEnabled`, when valid. */
10
+ export declare function claudeDefaults(configDir: string): ProviderDefaults;
11
+ /** Codex's config.toml: its top-level `model` and `model_reasoning_effort`, or the selected profile's. */
12
+ export declare function codexDefaults(codexHome: string): ProviderDefaults;
13
+ /**
14
+ * One Codex launch's CODEX_HOME: a private folder linking the operator's
15
+ * sign-in and sessions. A missing `auth.json` stays missing (an API-key
16
+ * build needs none); a missing `sessions/` is made where Codex would make
17
+ * it, so a resumed build finds the thread its first launch wrote.
18
+ */
19
+ export declare function isolatedCodexHome(source: string, root?: string): {
20
+ dir: string;
21
+ cleanup: () => void;
22
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * What a build keeps of the provider's own home, and nothing more.
3
+ *
4
+ * A build loads none of the operator's Claude settings or Codex config
5
+ * (provider.ts), so two things from them are carried explicitly:
6
+ *
7
+ * - A build routed with no model runs as the operator's own CLI would: the
8
+ * model they chose (Claude's settings.json `model`, Codex's config.toml
9
+ * `model`), Claude's `alwaysThinkingEnabled` and Codex's reasoning effort.
10
+ * Nothing else of either file (hooks, plugins, MCP servers) is read.
11
+ * - A Codex build gets its own CODEX_HOME for the launch: links to the
12
+ * operator's `auth.json` and `sessions/` (so it signs in, and a repair
13
+ * resumes the same thread), and no skills, AGENTS.md or anything else from
14
+ * theirs. Links, never copies: a refreshed sign-in lands in the real file.
15
+ * The folder is fenced from the agent's commands and removed after the run.
16
+ */
17
+ import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync } from "node:fs";
18
+ import { homedir, tmpdir } from "node:os";
19
+ import { join } from "node:path";
20
+ import { validModelId } from "./provider.js";
21
+ const EFFORT = /^[a-z]{1,16}$/;
22
+ export function claudeConfigDir(env = process.env, home = homedir()) {
23
+ return env["CLAUDE_CONFIG_DIR"] || join(home, ".claude");
24
+ }
25
+ export function codexHomeDir(env = process.env, home = homedir()) {
26
+ return env["CODEX_HOME"] || join(home, ".codex");
27
+ }
28
+ /** Claude's user settings: its `model` and `alwaysThinkingEnabled`, when valid. */
29
+ export function claudeDefaults(configDir) {
30
+ let settings;
31
+ try {
32
+ settings = JSON.parse(readFileSync(join(configDir, "settings.json"), "utf8"));
33
+ }
34
+ catch {
35
+ return {};
36
+ }
37
+ if (settings === null || typeof settings !== "object" || Array.isArray(settings))
38
+ return {};
39
+ const { model, alwaysThinkingEnabled } = settings;
40
+ return {
41
+ ...(typeof model === "string" && model !== "" && validModelId(model) ? { model } : {}),
42
+ ...(typeof alwaysThinkingEnabled === "boolean" ? { alwaysThinking: alwaysThinkingEnabled } : {}),
43
+ };
44
+ }
45
+ /** Codex's config.toml: its top-level `model` and `model_reasoning_effort`, or the selected profile's. */
46
+ export function codexDefaults(codexHome) {
47
+ let text;
48
+ try {
49
+ text = readFileSync(join(codexHome, "config.toml"), "utf8");
50
+ }
51
+ catch {
52
+ return {};
53
+ }
54
+ const tables = new Map([["", {}]]);
55
+ let table = "";
56
+ for (const raw of text.split(/\r?\n/)) {
57
+ const line = raw.trim();
58
+ const header = /^\[\s*([^\]]+?)\s*\]$/.exec(line);
59
+ if (header !== null) {
60
+ table = header[1].replace(/"/g, "");
61
+ if (!tables.has(table))
62
+ tables.set(table, {});
63
+ continue;
64
+ }
65
+ const pair = /^([A-Za-z0-9_]+)\s*=\s*"([^"\\]*)"\s*(?:#.*)?$/.exec(line);
66
+ if (pair !== null)
67
+ tables.get(table)[pair[1]] = pair[2];
68
+ }
69
+ const top = tables.get("");
70
+ const profile = top["profile"] === undefined ? {} : tables.get(`profiles.${top["profile"]}`) ?? {};
71
+ const model = profile["model"] ?? top["model"];
72
+ const effort = profile["model_reasoning_effort"] ?? top["model_reasoning_effort"];
73
+ return {
74
+ ...(model !== undefined && model !== "" && validModelId(model) ? { model } : {}),
75
+ ...(effort !== undefined && EFFORT.test(effort) ? { effort } : {}),
76
+ };
77
+ }
78
+ /**
79
+ * One Codex launch's CODEX_HOME: a private folder linking the operator's
80
+ * sign-in and sessions. A missing `auth.json` stays missing (an API-key
81
+ * build needs none); a missing `sessions/` is made where Codex would make
82
+ * it, so a resumed build finds the thread its first launch wrote.
83
+ */
84
+ export function isolatedCodexHome(source, root = tmpdir()) {
85
+ const dir = mkdtempSync(join(root, "so-codex-"));
86
+ try {
87
+ chmodSync(dir, 0o700);
88
+ if (existsSync(join(source, "auth.json")))
89
+ symlinkSync(join(source, "auth.json"), join(dir, "auth.json"));
90
+ mkdirSync(join(source, "sessions"), { recursive: true, mode: 0o700 });
91
+ symlinkSync(join(source, "sessions"), join(dir, "sessions"), "dir");
92
+ }
93
+ catch (error) {
94
+ rmSync(dir, { recursive: true, force: true });
95
+ throw error;
96
+ }
97
+ // rm removes the links themselves, never what they point to.
98
+ return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
99
+ }
@@ -18,6 +18,7 @@
18
18
  * TOML-quoted deterministically, and the API key excluded from every
19
19
  * shell the model itself launches (Codex provider review, Q5).
20
20
  */
21
+ import { type FenceGrants } from "./agent-fence.js";
21
22
  import { type ExecResult, type RunOptions } from "./exec.js";
22
23
  import { MODEL_ID, type AgentSpec, type ProviderId } from "./contracts/provider.js";
23
24
  export type { AgentSpec, ProviderId } from "./contracts/provider.js";
@@ -64,6 +65,17 @@ export type Invocation = {
64
65
  * Codex renders it as its own sandbox profile; Claude as deny rules for
65
66
  * its file tools (its shell is fenced by the macOS sandbox around it). */
66
67
  fence?: readonly string[];
68
+ /** What the fence gives back (agent-fence.ts): Codex renders read-only
69
+ * paths as "read", Claude's file tools never edit them. */
70
+ fenceGrants?: FenceGrants;
71
+ /** The operator's own model choices, for a build routed with no model
72
+ * (provider-home.ts): the model, Claude's always-on thinking and Codex's
73
+ * reasoning effort. Never rendered for a review or an explicit model. */
74
+ inherited?: {
75
+ model?: string;
76
+ alwaysThinking?: boolean;
77
+ effort?: string;
78
+ };
67
79
  /** More paths Claude's own file tools refuse, beside the fence: home
68
80
  * configuration its CLI still reads itself (so the sandbox around the
69
81
  * whole process can't deny them). */
@@ -163,6 +175,29 @@ export type ParsedEnvelope = {
163
175
  * failure terminal seen.
164
176
  */
165
177
  structuralTerminal: import("./exhaustion.js").StructuralTerminal | null;
178
+ /** What an ACP agent itself reported it used — kept on the run, never priced. Absent for every other dialect. */
179
+ agentReportedUsage?: string | null;
180
+ /** An ACP conversation's facts the conformance check reads (acp-client.ts AcpEnvelope): nothing else does. */
181
+ acp?: {
182
+ offered: string[];
183
+ exit: {
184
+ how: "eof" | "term" | "kill" | "self" | "error";
185
+ leftover: boolean;
186
+ timedOut: boolean;
187
+ } | null;
188
+ agent: {
189
+ name: string;
190
+ version: string;
191
+ } | null;
192
+ stop: string | null;
193
+ /** The conversation broke the protocol (acpParse's protocolError), or null. */
194
+ protocolError: string | null;
195
+ /** The plane's isolation probes for a check's first step (invoke.ts), when it ran them. */
196
+ isolation?: {
197
+ fileDenied: boolean;
198
+ envClear: boolean;
199
+ };
200
+ };
166
201
  };
167
202
  type Adapter = {
168
203
  binary: string;
@@ -199,6 +234,7 @@ export declare const OPENROUTER_ENV_KEY = "OPENROUTER_API_KEY";
199
234
  * planted user, project and local hooks and MCP servers: none loaded.
200
235
  */
201
236
  export declare const CLAUDE_BUILD_ISOLATION_ARGV: readonly string[];
237
+ export declare function claudePluginEnv(env: Readonly<Record<string, string | undefined>>): string[];
202
238
  /** Claude's own tools that only make sense with a person to wake them. */
203
239
  export declare const HEADLESS_DISALLOWED_TOOLS: readonly string[];
204
240
  /**
@@ -221,9 +257,10 @@ export declare function isolationUnsupported(provider: ProviderId, stderr: strin
221
257
  * A Codex build's own setup, the equivalent of Claude's: the operator's
222
258
  * config.toml (its MCP servers, model choices, profiles) and execpolicy
223
259
  * rules ignored, ChatGPT apps and plugins off (plugins carry their own MCP
224
- * servers), and hooks off. Sign-in still comes from CODEX_HOME, which
225
- * `--ignore-user-config` leaves alone. The project's named tools follow as
226
- * `-c mcp_servers.*` (project-tools.ts).
260
+ * servers), and hooks off. The gateway gives each launch its own CODEX_HOME
261
+ * linking only the operator's sign-in and sessions (provider-home.ts), so no
262
+ * skills or AGENTS.md of theirs load either. The project's named tools follow
263
+ * as `-c mcp_servers.*` (project-tools.ts).
227
264
  */
228
265
  export declare const CODEX_BUILD_ISOLATION_ARGV: readonly string[];
229
266
  /**
@@ -252,13 +289,13 @@ export declare function adapterFor(provider: ProviderId): Adapter;
252
289
  */
253
290
  export type ProviderAudit = {
254
291
  /** How output reaches us — and therefore which signals can exist at all. */
255
- transport: "buffered-json" | "streaming-jsonl";
292
+ transport: "buffered-json" | "streaming-jsonl" | "acp-jsonrpc";
256
293
  /** Whether a later invocation can resume this provider's session. */
257
294
  resume: "native" | "none";
258
295
  /** The event whose absence on a failed run means "never initialized".
259
296
  * CAPABILITY metadata, not a per-run observation (arc 1 finding 16):
260
297
  * whether a given run actually saw it lives in ParsedEnvelope.initObserved. */
261
- initSignal: "thread.started" | "system-init" | "init-event" | "none";
298
+ initSignal: "thread.started" | "system-init" | "init-event" | "acp-session" | "none";
262
299
  /** How session identity is established: "announced" = read back from the
263
300
  * harness's own stream; "minted" = the PLANE chooses the id pre-spawn
264
301
  * (gemini --session-id) and the envelope must echo it (Phase 3 A5). */
package/dist/provider.js CHANGED
@@ -18,13 +18,16 @@
18
18
  * TOML-quoted deterministically, and the API key excluded from every
19
19
  * shell the model itself launches (Codex provider review, Q5).
20
20
  */
21
+ import { INTERPRETER_INJECTION_ENV } from "./acp-attest.js";
21
22
  import { claudeFenceSettings, codexFenceArgv } from "./agent-fence.js";
22
23
  import { claudeSteerHooks } from "./steer-mailbox.js";
23
24
  import {} from "./exec.js";
24
- import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl, CLAUDE_COST_UNKNOWN } from "./exec.js";
25
+ import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl, runAcpSession, CLAUDE_COST_UNKNOWN } from "./exec.js";
25
26
  import { scanForSecrets } from "./evidence.js";
26
27
  import { FINDINGS_MODEL_SCHEMA } from "./contracts/review-findings.js";
27
28
  import { REVIEW_OUTPUT_LIMITS } from "./structured-output.js";
29
+ import { ACP_AGENTS, isAcpProvider, withAcp } from "./acp-agents.js";
30
+ import { acpParse } from "./acp-client.js";
28
31
  import { agentSpecSchema, MODEL_ID, modelIdSchema, PROVIDER_ID_VALUES, providerIdSchema } from "./contracts/provider.js";
29
32
  export const PROVIDER_IDS = PROVIDER_ID_VALUES;
30
33
  export function isProviderId(value) {
@@ -181,6 +184,16 @@ const CLAUDE_REVIEW_JSON_SCHEMA = {
181
184
  * planted user, project and local hooks and MCP servers: none loaded.
182
185
  */
183
186
  export const CLAUDE_BUILD_ISOLATION_ARGV = ["--setting-sources", "", "--strict-mcp-config"];
187
+ /**
188
+ * Plugins also ride the environment: a plugin named in CLAUDE_CODE_PLUGIN_DIRS
189
+ * loads its hooks whatever `--setting-sources` says (Claude Code 2.1.290,
190
+ * the provider canary's plugin sentinel). A build's launch sheds every
191
+ * plugin variable of the environment it would inherit.
192
+ */
193
+ const CLAUDE_PLUGIN_ENV = /^CLAUDE_(CODE_\w*PLUGIN|AI_PLUGINS)\w*$/;
194
+ export function claudePluginEnv(env) {
195
+ return Object.keys(env).filter(name => CLAUDE_PLUGIN_ENV.test(name));
196
+ }
184
197
  const EMPTY_MCP_CONFIG = '{"mcpServers":{}}';
185
198
  /** Claude's own tools that only make sense with a person to wake them. */
186
199
  export const HEADLESS_DISALLOWED_TOOLS = ["ScheduleWakeup", "CronCreate", "Monitor"];
@@ -203,7 +216,7 @@ const claudeArgv = (invocation) => [
203
216
  : invocation.skipPermissions
204
217
  ? ["--dangerously-skip-permissions"]
205
218
  : ["--permission-mode", invocation.permissionMode]),
206
- ...(invocation.model === null ? [] : ["--model", invocation.model]),
219
+ ...modelArgv("--model", invocation),
207
220
  ...(invocation.maxBudgetUsd === undefined ? [] : ["--max-budget-usd", String(invocation.maxBudgetUsd)]),
208
221
  ...(invocation.phase === "review"
209
222
  ? [...CLAUDE_REVIEW_ISOLATION_ARGV, "--json-schema", JSON.stringify(CLAUDE_REVIEW_JSON_SCHEMA)]
@@ -222,12 +235,22 @@ const claudeArgv = (invocation) => [
222
235
  ...(invocation.jsonSchema === undefined ? [] : ["--json-schema", JSON.stringify(invocation.jsonSchema)])
223
236
  ]),
224
237
  ];
238
+ /** The route's model, or for a build routed with none, the operator's own (never for a review). */
239
+ function modelArgv(flag, invocation) {
240
+ const model = invocation.model ?? (invocation.phase === "review" ? undefined : invocation.inherited?.model);
241
+ return model === undefined ? [] : [flag, model];
242
+ }
243
+ /** What else a no-model build keeps of the operator's setup. */
244
+ const inheritedFrom = (invocation) => invocation.model === null && invocation.phase !== "review" ? invocation.inherited ?? {} : {};
225
245
  /** The build's one --settings: no personal memory, plus the fence and steering doorbell. */
226
246
  function claudeSettingsArgv(invocation) {
227
247
  const denied = [...(invocation.fence ?? []), ...(invocation.fileToolFence ?? [])];
248
+ const readOnly = invocation.fenceGrants?.readOnly ?? [];
249
+ const thinking = inheritedFrom(invocation).alwaysThinking;
228
250
  return ["--settings", JSON.stringify({
229
251
  autoMemoryEnabled: false,
230
- ...(denied.length > 0 ? JSON.parse(claudeFenceSettings(denied)) : {}),
252
+ ...(thinking === undefined ? {} : { alwaysThinkingEnabled: thinking }),
253
+ ...(denied.length > 0 || readOnly.length > 0 ? JSON.parse(claudeFenceSettings(denied, readOnly)) : {}),
231
254
  ...(invocation.steerFolder === undefined ? {} : { hooks: claudeSteerHooks(invocation.steerFolder.dir, invocation.steerFolder.name) }),
232
255
  })];
233
256
  }
@@ -421,9 +444,10 @@ const codexSandboxArgv = (mode, resuming) => resuming ? ["-c", `sandbox_mode="${
421
444
  * A Codex build's own setup, the equivalent of Claude's: the operator's
422
445
  * config.toml (its MCP servers, model choices, profiles) and execpolicy
423
446
  * rules ignored, ChatGPT apps and plugins off (plugins carry their own MCP
424
- * servers), and hooks off. Sign-in still comes from CODEX_HOME, which
425
- * `--ignore-user-config` leaves alone. The project's named tools follow as
426
- * `-c mcp_servers.*` (project-tools.ts).
447
+ * servers), and hooks off. The gateway gives each launch its own CODEX_HOME
448
+ * linking only the operator's sign-in and sessions (provider-home.ts), so no
449
+ * skills or AGENTS.md of theirs load either. The project's named tools follow
450
+ * as `-c mcp_servers.*` (project-tools.ts).
427
451
  */
428
452
  export const CODEX_BUILD_ISOLATION_ARGV = [
429
453
  "--ignore-user-config",
@@ -469,11 +493,12 @@ const codexArgv = (extra) => (invocation) => {
469
493
  // denied. Full access becomes that sandbox widened to write anywhere
470
494
  // with network, instead of no sandbox (which fenced nothing).
471
495
  : invocation.fence !== undefined && invocation.fence.length > 0
472
- ? codexFenceArgv(invocation.fence, invocation.skipPermissions)
496
+ ? codexFenceArgv(invocation.fence, invocation.skipPermissions, invocation.fenceGrants)
473
497
  : invocation.skipPermissions
474
498
  ? ["--dangerously-bypass-approvals-and-sandbox"]
475
499
  : codexSandboxArgv("workspace-write", resuming)),
476
- ...(invocation.model === null ? [] : ["-m", invocation.model]),
500
+ ...modelArgv("-m", invocation),
501
+ ...(inheritedFrom(invocation).effort === undefined ? [] : ["-c", `model_reasoning_effort=${toml(inheritedFrom(invocation).effort)}`]),
477
502
  ...extra,
478
503
  // After `extra`: the tools' shell_environment_policy must be the last word (it keeps OpenRouter's key out too).
479
504
  ...(invocation.phase === "review" ? [] : invocation.toolArgv ?? []),
@@ -765,12 +790,13 @@ export function safeDiagnostic(text) {
765
790
  * provider gets its OWN keys and nobody else's: S4 (live spike) confirmed
766
791
  * what inheritance makes true by construction — an agent's shell reads its
767
792
  * process env, so a foreign key in that env is a foreign key disclosed. */
768
- const CREDENTIAL_ENV = {
793
+ const CREDENTIAL_ENV = withAcp({
769
794
  claude: ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"],
770
795
  codex: ["OPENAI_API_KEY", "CODEX_API_KEY"],
771
796
  openrouter: [OPENROUTER_ENV_KEY],
772
797
  gemini: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
773
- };
798
+ // An ACP agent signs in its own way; Toolroll holds no key for it, so it sheds every key above.
799
+ }, () => []);
774
800
  const foreignCredentialEnv = (own) => Object.entries(CREDENTIAL_ENV)
775
801
  .filter(([provider]) => provider !== own)
776
802
  .flatMap(([, keys]) => [...keys]);
@@ -778,7 +804,40 @@ const foreignCredentialEnv = (own) => Object.entries(CREDENTIAL_ENV)
778
804
  * since a feature check needs no key at all (Codex gemini verify,
779
805
  * finding 2). Agent spawns keep their own; probes keep none. */
780
806
  export const ALL_CREDENTIAL_ENV = Object.values(CREDENTIAL_ENV).flat();
781
- const ADAPTERS = {
807
+ /**
808
+ * An ACP agent's retained record (acp-client.ts) read into the gateway's
809
+ * envelope. Its own usage report is never priced: tokens and cost stay null.
810
+ */
811
+ function acpEnvelope(stdout) {
812
+ const read = acpParse(stdout);
813
+ return {
814
+ sessionId: read.sessionId,
815
+ protocolError: read.protocolError,
816
+ finalMessage: read.finalMessage,
817
+ tokensIn: null,
818
+ tokensOut: null,
819
+ costUsd: null,
820
+ usageRaw: null,
821
+ initObserved: read.initObserved,
822
+ promptConsumed: read.promptConsumed,
823
+ diagnostic: read.diagnostic === null ? null : safeDiagnostic(read.diagnostic),
824
+ structuralTerminal: read.terminal === null ? null : { failed: true, text: read.terminal.text === null ? null : safeDiagnostic(read.terminal.text), code: read.terminal.code.slice(0, 128) },
825
+ agentReportedUsage: read.reportedUsage,
826
+ acp: { offered: read.offered, exit: read.exit, agent: read.agent, stop: read.stop, protocolError: read.protocolError },
827
+ };
828
+ }
829
+ function acpAdapter(provider) {
830
+ return {
831
+ binary: ACP_AGENTS[provider].binary,
832
+ argv: () => [...ACP_AGENTS[provider].argv],
833
+ parse: acpEnvelope,
834
+ defaultRunner: runAcpSession,
835
+ // Every key Toolroll knows, and whatever would make the pinned interpreter load code beside the checked script.
836
+ extraOmitEnv: [...foreignCredentialEnv(provider), ...INTERPRETER_INJECTION_ENV],
837
+ clampTimeout: (_phase, requested) => requested,
838
+ };
839
+ }
840
+ const ADAPTERS = withAcp({
782
841
  claude: {
783
842
  binary: "claude",
784
843
  argv: claudeArgv,
@@ -830,7 +889,7 @@ const ADAPTERS = {
830
889
  extraOmitEnv: foreignCredentialEnv("gemini"),
831
890
  clampTimeout: (phase, requested) => Math.min(requested, GEMINI_TIMEOUT_CAP_MS[phase]),
832
891
  },
833
- };
892
+ }, (_agent, provider) => acpAdapter(provider));
834
893
  /**
835
894
  * The spawning surface. Imported by invoke.ts and NOWHERE else — the
836
895
  * architecture test reads imports, not string literals, because provider
@@ -839,7 +898,7 @@ const ADAPTERS = {
839
898
  export function adapterFor(provider) {
840
899
  return ADAPTERS[provider];
841
900
  }
842
- const AUDITS = {
901
+ const AUDITS = withAcp({
843
902
  claude: {
844
903
  transport: "streaming-jsonl",
845
904
  resume: "native",
@@ -851,6 +910,7 @@ const AUDITS = {
851
910
  isolation: { flag: CLAUDE_BUILD_ISOLATION_ARGV.filter(one => one !== "").join(" "), resumeSafe: true, enforced: true },
852
911
  configSurface: [
853
912
  "Toolroll's own --settings (the fence and steering hook)",
913
+ "your model and alwaysThinkingEnabled, when the route names no model",
854
914
  "the project's named tools (Settings → Tools)",
855
915
  "repository CLAUDE.md",
856
916
  "organisation-managed settings",
@@ -863,10 +923,11 @@ const AUDITS = {
863
923
  sessionIdentity: "announced",
864
924
  terminalContract: "none",
865
925
  // Builds ignore config.toml and execpolicy rules, with apps, plugins and
866
- // hooks off (CODEX_BUILD_ISOLATION_ARGV); sign-in stays in CODEX_HOME.
926
+ // hooks off (CODEX_BUILD_ISOLATION_ARGV), in a per-launch CODEX_HOME that
927
+ // links only the sign-in and sessions (provider-home.ts).
867
928
  // Never --ephemeral: repair resumes sessions.
868
929
  isolation: { flag: "--ignore-user-config --ignore-rules", resumeSafe: true, enforced: true },
869
- configSurface: ["the project's named tools (Settings → Tools)", "~/.codex/AGENTS.md and repository AGENTS.md", "sign-in from CODEX_HOME"],
930
+ configSurface: ["the project's named tools (Settings → Tools)", "repository AGENTS.md", "sign-in and sessions linked from CODEX_HOME (no skills or AGENTS.md)", "your model and reasoning effort, when the route names no model"],
870
931
  },
871
932
  openrouter: {
872
933
  transport: "streaming-jsonl",
@@ -876,7 +937,7 @@ const AUDITS = {
876
937
  terminalContract: "none",
877
938
  // Codex's build setup, with the model provider pinned by constant -c overrides.
878
939
  isolation: { flag: "--ignore-user-config --ignore-rules", resumeSafe: true, enforced: true },
879
- configSurface: ["the project's named tools (Settings → Tools)", "~/.codex/AGENTS.md and repository AGENTS.md", `${OPENROUTER_ENV_KEY}`],
940
+ configSurface: ["the project's named tools (Settings → Tools)", "repository AGENTS.md", `${OPENROUTER_ENV_KEY}`],
880
941
  },
881
942
  gemini: {
882
943
  transport: "streaming-jsonl",
@@ -899,8 +960,17 @@ const AUDITS = {
899
960
  "trusted-folder store (S2, live at 0.57.0: headless REFUSES untrusted directories; the plane grants --skip-trust per-invocation to its own leased worktrees)",
900
961
  ],
901
962
  },
902
- };
903
- export const MONEY_CAPABILITIES = {
963
+ }, agent => ({
964
+ // One ACP conversation per build: the prompt's answer is the only success (acp-client.ts), so exit 0 alone is never believed.
965
+ transport: "acp-jsonrpc",
966
+ resume: "none",
967
+ initSignal: "acp-session",
968
+ sessionIdentity: "announced",
969
+ terminalContract: "required",
970
+ isolation: { flag: agent.argv.filter(one => one.startsWith("--") && one !== "--acp").join(" ") || null, resumeSafe: null, enforced: agent.argv.some(one => one.startsWith("--") && one !== "--acp") },
971
+ configSurface: [...agent.configSurface],
972
+ }));
973
+ export const MONEY_CAPABILITIES = withAcp({
904
974
  claude: {
905
975
  incrementalUsage: true,
906
976
  nativeDollarCapFlag: "--max-budget-usd",
@@ -923,7 +993,12 @@ export const MONEY_CAPABILITIES = {
923
993
  // numbers cover that invocation only (conformance fixture j).
924
994
  usageSemantics: "per-invocation",
925
995
  },
926
- };
996
+ }, () => ({
997
+ // ACP carries no dollars Toolroll trusts: an agent's own usage report is kept apart, unpriced.
998
+ incrementalUsage: false,
999
+ nativeDollarCapFlag: null,
1000
+ usageSemantics: "per-invocation",
1001
+ }));
927
1002
  /**
928
1003
  * The fail-closed budget-flag probe (finding 24's amendment): resolve
929
1004
  * the EXACT executable that will spawn, read its version, and prove the
@@ -973,6 +1048,9 @@ export function validateSpec(spec) {
973
1048
  if (spec.provider === "gemini" && spec.model === null) {
974
1049
  return { ok: false, problem: "model: gemini needs an explicit model — the harness default drifts with its releases" };
975
1050
  }
1051
+ if (isAcpProvider(spec.provider) && spec.model === null) {
1052
+ return { ok: false, problem: `model: ${ACP_AGENTS[spec.provider].name} needs an explicit model — one it offered when it was checked` };
1053
+ }
976
1054
  return { ok: true };
977
1055
  }
978
1056
  /**
@@ -1,11 +1,11 @@
1
1
  /** Settings → Projects → Pull requests: one project's pull-request setup, in plain words. Off shows what setup
2
2
  * found (or what to fix) and one Turn on; on shows where pull requests go and how they merge. Every change takes
3
3
  * the password; turning on restates the exact repository and branch that were checked. */
4
- import { html, postForm } from "./html.js";
4
+ import { html, postForm, reauth } from "./html.js";
5
5
  const METHOD_WORDS = { squash: "Squash and merge", merge: "Merge commit", rebase: "Rebase and merge" };
6
6
  export function pullRequestSettingsHtml(view) {
7
7
  const post = (act, body, hidden = {}) => postForm("/settings/pull-requests", body, { attrs: { class: "card pr-settings" }, hidden: { repo: view.repo, act, ...hidden } });
8
- const password = html `<label>Password<input type="password" name="password" autocomplete="current-password" required></label>`;
8
+ const password = html `${reauth(html `<label>Password<input type="password" name="password" autocomplete="current-password" required></label>`)}`;
9
9
  const notes = [
10
10
  view.said === null ? "" : html `<p class="notice" role="status">${view.said}</p>`,
11
11
  view.problem === null ? "" : html `<p class="problem" role="alert">${view.problem}</p>`,
package/dist/push.js CHANGED
@@ -259,7 +259,7 @@ export async function pushPass(store, options) {
259
259
  // Renew and re-fence immediately before the network (finding 12).
260
260
  if (!store.renewPushClaim(pair.id, owner, pair.claimGeneration, PUSH_CLAIM_TTL_MS, clock()))
261
261
  continue;
262
- const fenced = store.pushSendFence(pair.id, owner, pair.claimGeneration);
262
+ const fenced = store.pushSendFence(pair.id, owner, pair.claimGeneration, clock());
263
263
  if (fenced === null)
264
264
  continue; // resolved, retired, rotated, or lost — nothing to send
265
265
  const subscription = fenced.subscriptionRow;
@@ -0,0 +1,24 @@
1
+ import { type AttachSeams } from "./console-instance.js";
2
+ export type QuickStartSeams = {
3
+ databaseFile: string;
4
+ cwd: string;
5
+ env: Record<string, string | undefined>;
6
+ build: string;
7
+ /** The repository's top folder for a directory, or null outside one. */
8
+ findRepo: (cwd: string) => Promise<string | null>;
9
+ /** Whether this installation already admitted the repository. */
10
+ enrolled: (repo: string) => Promise<boolean>;
11
+ /** The ordinary admission (`repos add`): its registry lock and ceilings, from this CLI's own database. */
12
+ admit: (repo: string) => Promise<number>;
13
+ /** `up`, for this repository. */
14
+ startUp: () => Promise<number>;
15
+ openBrowser: (url: string) => void | Promise<void>;
16
+ terminal: boolean;
17
+ attach?: AttachSeams;
18
+ /** Tests: the agent check (default: the environment and the process's ancestors). */
19
+ underAgent?: boolean;
20
+ };
21
+ /** Whether argv is a bare `toolroll` (nothing, or only --json). */
22
+ export declare const isBareInvocation: (argv: readonly string[]) => boolean;
23
+ /** null: not this command (outside a repository) — the caller scans as before. */
24
+ export declare function runQuickStart(argv: readonly string[], write: (line: string) => void, seams: QuickStartSeams): Promise<number | null>;