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
@@ -1,4 +1,5 @@
1
- import { type ExecResult } from "./exec.js";
1
+ import type { ExecResult } from "./exec.js";
2
+ import type { Runner } from "./worktree.js";
2
3
  import { type Store } from "./store.js";
3
4
  /** What Toolroll names the branches it makes: toolroll/…, and standing-orders/… from before the rename. */
4
5
  export declare const OWN_BRANCH = "toolroll/";
@@ -52,4 +53,5 @@ export declare function deleteProject(store: Store, repo: string, options: {
52
53
  evidenceRoot: string | null;
53
54
  poolRoot: string | null;
54
55
  git?: GitRunner;
56
+ runner?: Runner;
55
57
  }): Promise<ProjectDeleteResult>;
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { existsSync, lstatSync, rmSync } from "node:fs";
16
16
  import { isAbsolute, relative, resolve } from "node:path";
17
- import { run } from "./exec.js";
17
+ import { teardownGit, refusal } from "./teardown-git.js";
18
18
  import { BUILT_IN } from "./store.js";
19
19
  import { BRANCH_PREFIX, BRANCH_PREFIXES, isOwnBranch } from "./names.js";
20
20
  /** The tables whose history is the audit record itself: never touched. */
@@ -48,7 +48,7 @@ function doomed(db, repo) {
48
48
  const SETTINGS = [
49
49
  ["approval_policy", "repo"], ["verify_command", "repo"], ["worktree_setup", "repo"], ["backend_grant", "repo"], ["publication_grant", "repo"],
50
50
  ["intake_grant", "repo"], ["capability", "repo"], ["project_tool", "repo"], ["phase_config", "scope"], ["phase_tier_config", "scope"],
51
- ["learning_policy", "repo"], ["operating_mode", "repo"], ["project_knowledge", "repo"],
51
+ ["learning_policy", "repo"], ["operating_mode", "repo"], ["project_knowledge", "repo"], ["live_detail_setting", "repo"],
52
52
  ["project_decision", "repo"], ["flow_script", "repo"], ["workflow_recipe", "repo"], ["team_lead_project", "project"],
53
53
  ];
54
54
  /** What Toolroll holds for a project, counted. */
@@ -100,7 +100,6 @@ export function projectRunning(store, repo, now) {
100
100
  ...(checkouts > 0 && building === 0 ? [`${plural(checkouts, "checkout")} ${checkouts === 1 ? "is" : "are"} in use`] : []),
101
101
  ];
102
102
  }
103
- const defaultGit = (args, cwd) => run("git", ["--no-optional-locks", ...args], { cwd, timeoutMs: 60_000 });
104
103
  /** Whether `path` is inside `root` (not the root itself). */
105
104
  const inside = (root, path) => { const rel = relative(resolve(root), resolve(path)); return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel); };
106
105
  /**
@@ -121,7 +120,10 @@ async function removeCheckoutsAndBranches(store, repo, d, git, poolRoot) {
121
120
  const ours = new Set(named);
122
121
  const left = [];
123
122
  let checkouts = 0, branches = 0;
124
- const isRepo = existsSync(repo) && (await git(["rev-parse", "--git-dir"], repo)).code === 0;
123
+ const probe = existsSync(repo) ? await git(["rev-parse", "--git-dir"], repo) : null;
124
+ if (probe !== null && refusal(probe) !== null)
125
+ return { checkouts, branches, left: [refusal(probe)] };
126
+ const isRepo = probe?.code === 0;
125
127
  if (!isRepo) {
126
128
  // No repository to ask: only checkouts in Toolroll's own folder go.
127
129
  for (const row of recorded) {
@@ -277,7 +279,7 @@ function deleteRows(store, repo, d, now) {
277
279
  const mirrors = json(list(db, `SELECT local_task_id FROM external_mirror WHERE ${IN("local_task_id")}`, K));
278
280
  del("external_intent", IN("mirror"), mirrors);
279
281
  del("external_mirror", IN("local_task_id"), mirrors);
280
- for (const [table, column] of [["scope_approval_vote", "task_id"], ["scope_author", "task_id"], ["plan_authorization", "task_id"], ["tool_seal", "task"], ["mcp_idempotency", "task_id"], ["coordinator_event", "task_id"], ["workflow_preview", "task_id"]])
282
+ for (const [table, column] of [["scope_approval_vote", "task_id"], ["scope_author", "task_id"], ["plan_authorization", "task_id"], ["tool_seal", "task"], ["mcp_idempotency", "task_id"], ["coordinator_event", "task_id"], ["workflow_preview", "task_id"], ["task_source", "task_id"]])
281
283
  del(table, IN(column), K);
282
284
  del("workflow_preview", "repo = ?", repo);
283
285
  del("coding_handoff_scope", `id IN (SELECT id FROM coding_handoff WHERE json_extract(payload, '$.repo') = ?)`, repo);
@@ -291,6 +293,8 @@ function deleteRows(store, repo, d, now) {
291
293
  // Flows, cards and subagents.
292
294
  del("flow_trigger_event", `${IN("card")} OR trigger IN (SELECT id FROM flow_trigger WHERE ${IN("flow")})`, C, F);
293
295
  del("flow_trigger", IN("flow"), F);
296
+ del("flow_unattended_run", `grant_id IN (SELECT id FROM flow_unattended_grant WHERE ${IN("flow")})`, F);
297
+ del("flow_unattended_grant", IN("flow"), F);
294
298
  for (const table of ["flow_card_watcher", "flow_comment", "flow_event", "flow_mail", "flow_step_run"])
295
299
  del(table, IN("card"), C);
296
300
  del("subagent_question", IN("id"), questions);
@@ -323,7 +327,9 @@ function deleteRows(store, repo, d, now) {
323
327
  * starts while the checkouts and branches go.
324
328
  */
325
329
  export async function deleteProject(store, repo, options) {
326
- const git = options.git ?? defaultGit;
330
+ const hardened = teardownGit(options.runner ?? (options.git === undefined ? undefined :
331
+ (_file, args, execOptions) => options.git(args, execOptions.cwd)));
332
+ const git = (args, cwd) => hardened(cwd, args);
327
333
  const running = projectRunning(store, repo, options.now);
328
334
  if (running.length > 0)
329
335
  return { ok: false, reason: "running", said: `Nothing was deleted: ${running.join(", ")}. Stop it, then try again.`, running };
@@ -0,0 +1,3 @@
1
+ /** A project file a script names, if it is one: inside the project, and there. Kept free of the runner's imports so a
2
+ * flow's "runs on its own" grant (flow-unattended.ts) resolves the very file a script would run. */
3
+ export declare function projectFile(root: string, path: string): string | null;
@@ -0,0 +1,16 @@
1
+ import { realpathSync, statSync } from "node:fs";
2
+ import { isAbsolute, join, relative } from "node:path";
3
+ /** A project file a script names, if it is one: inside the project, and there. Kept free of the runner's imports so a
4
+ * flow's "runs on its own" grant (flow-unattended.ts) resolves the very file a script would run. */
5
+ export function projectFile(root, path) {
6
+ if (path === "" || isAbsolute(path) || path.split(/[\\/]/).includes(".."))
7
+ return null;
8
+ try {
9
+ const base = realpathSync(root), full = realpathSync(join(root, path));
10
+ const inside = relative(base, full);
11
+ return inside !== "" && !inside.startsWith("..") && !isAbsolute(inside) && statSync(full).isFile() ? full : null;
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
@@ -44,7 +44,9 @@ export type MemoryHit = {
44
44
  };
45
45
  /** Rebuild the index rows for one project from the stores of record. */
46
46
  export declare function reindexProjectMemory(store: Store, repo: string): void;
47
- /** Index conversation messages incrementally: private threads under their person, team conversations under their id and each project they cover. */
47
+ /** Index conversation messages incrementally: private threads under their person, team conversations under their id and each project they cover.
48
+ * A message still waiting for a turn is not indexed: it is indexed when a turn takes it (the admitted-turn pass below,
49
+ * with the words the turn took), so a withdrawn, removed or edited waiting message never reaches search. */
48
50
  export declare function reindexConversations(store: Store): void;
49
51
  /** Search what this person may read: project entries for their projects, their own private chats, and team conversations they are in. */
50
52
  export declare function searchMemory(store: Store, args: {
@@ -218,38 +218,63 @@ export function reindexProjectMemory(store, repo) {
218
218
  insert.run('decision', String(row['id']), repo, 'project', String(row['claim']), String(row['why']));
219
219
  });
220
220
  }
221
- /** Index conversation messages incrementally: private threads under their person, team conversations under their id and each project they cover. */
221
+ /** Index conversation messages incrementally: private threads under their person, team conversations under their id and each project they cover.
222
+ * A message still waiting for a turn is not indexed: it is indexed when a turn takes it (the admitted-turn pass below,
223
+ * with the words the turn took), so a withdrawn, removed or edited waiting message never reaches search. */
222
224
  export function reindexConversations(store) {
223
- const key = 'memory-search:messages';
225
+ const key = 'memory-search:messages', admittedKey = 'memory-search:admitted';
224
226
  const cursor = store.serviceCursor(key);
225
- const rows = store.handle.prepare('SELECT m.id,m.thread,m.role,m.text,t.approver FROM lead_message m JOIN lead_thread t ON t.id=m.thread WHERE m.id>? ORDER BY m.id LIMIT 500').all(cursor);
226
- if (rows.length === 0)
227
- return;
227
+ const rows = store.handle.prepare('SELECT m.id,m.thread,m.role,m.text,m.pending,t.approver FROM lead_message m JOIN lead_thread t ON t.id=m.thread WHERE m.id>? ORDER BY m.id LIMIT 500').all(cursor);
228
228
  const insert = store.handle.prepare('INSERT INTO memory_search(kind,ref,repo,scope,title,body) VALUES (?,?,?,?,?,?)');
229
229
  const teamOf = store.handle.prepare('SELECT id, projects_json FROM team_conversation WHERE thread=?');
230
+ const indexOne = (row) => {
231
+ const id = Number(row['id']);
232
+ // Indexed exactly as stored (the search proves a hit against the stored words); blank means nothing to index.
233
+ const text = String(row['text']);
234
+ if (!text.trim() || text.length > 20_000)
235
+ return;
236
+ const team = teamOf.get(Number(row['thread']));
237
+ if (team) {
238
+ let projects = [];
239
+ try {
240
+ projects = JSON.parse(String(team['projects_json']));
241
+ }
242
+ catch {
243
+ projects = [];
244
+ }
245
+ for (const repo of projects.length ? projects : [''])
246
+ insert.run('conversation', `message:${id}`, repo, `conversation:${String(team['id'])}`, `${row['role']} in a team conversation`, text);
247
+ }
248
+ else
249
+ insert.run('conversation', `message:${id}`, '', `approver:${String(row['approver'])}`, `${row['role']} in a private chat`, text);
250
+ };
230
251
  store.transact(() => {
231
252
  let last = cursor;
232
253
  for (const row of rows) {
233
254
  last = Number(row['id']);
234
- const text = String(row['text']).trim();
235
- if (!text || text.length > 20_000)
255
+ if (Number(row['pending']) === 1)
236
256
  continue;
237
- const team = teamOf.get(Number(row['thread']));
238
- if (team) {
239
- let projects = [];
240
- try {
241
- projects = JSON.parse(String(team['projects_json']));
242
- }
243
- catch {
244
- projects = [];
245
- }
246
- for (const repo of projects.length ? projects : [''])
247
- insert.run('conversation', `message:${last}`, repo, `conversation:${String(team['id'])}`, `${row['role']} in a team conversation`, text);
248
- }
249
- else
250
- insert.run('conversation', `message:${last}`, '', `approver:${String(row['approver'])}`, `${row['role']} in a private chat`, text);
257
+ indexOne(row);
258
+ }
259
+ if (rows.length > 0)
260
+ store.setServiceCursor(key, last, new Date());
261
+ // Messages that waited and were then admitted (a personal queue row or a team room message bound to a turn since the
262
+ // last pass): one the walk passed while it waited (at or below the cursor this pass started from) is indexed now,
263
+ // with the words its turn took. One past it is left to the walk, so nothing is indexed twice. Found by the turn
264
+ // indexes on both queues: only queued messages are visited, never every turn. (After an update from before v128, a
265
+ // team message already indexed when it was saved gains a second copy once; search counts each entry once.)
266
+ const turnCursor = store.serviceCursor(admittedKey);
267
+ const admitted = store.handle.prepare(`SELECT message, turn FROM (SELECT message, turn_id AS turn FROM lead_queue WHERE turn_id > ?
268
+ UNION ALL SELECT message, turn_id AS turn FROM team_message WHERE turn_id > ?) ORDER BY turn LIMIT 500`).all(turnCursor, turnCursor);
269
+ if (admitted.length === 0)
270
+ return;
271
+ const taken = store.handle.prepare("SELECT m.id,m.thread,m.role,m.text,t.approver FROM lead_message m JOIN lead_thread t ON t.id=m.thread WHERE m.id=? AND m.pending=0 AND m.id<=?");
272
+ for (const one of admitted) {
273
+ const row = taken.get(Number(one['message']), cursor);
274
+ if (row !== undefined)
275
+ indexOne(row);
251
276
  }
252
- store.setServiceCursor(key, last, new Date());
277
+ store.setServiceCursor(admittedKey, Number(admitted.at(-1)['turn']), new Date());
253
278
  });
254
279
  }
255
280
  const ftsQuery = (query) => {
@@ -269,11 +294,18 @@ export function searchMemory(store, args) {
269
294
  reindexProjectMemory(store, repo);
270
295
  const conversations = store.handle.prepare('SELECT conversation FROM team_participant WHERE account=? AND active=1').all(args.actor).map(row => `conversation:${String(row['conversation'])}`);
271
296
  const scopes = ['project', `approver:${args.actor}`, ...conversations];
297
+ const limit = Math.min(50, Math.max(1, args.limit ?? 12));
272
298
  const match = ftsQuery(query);
273
299
  if (!match)
274
300
  return [];
275
301
  const rows = store.handle.prepare(`SELECT kind, ref, repo, scope, title, snippet(memory_search, 5, '[', ']', '…', 24) AS snippet FROM memory_search
276
- WHERE memory_search MATCH ? AND scope IN (SELECT value FROM json_each(?)) AND (repo='' OR repo IN (SELECT value FROM json_each(?))) ORDER BY bm25(memory_search) LIMIT ?`)
277
- .all(match, JSON.stringify(scopes), JSON.stringify(repos), Math.min(50, Math.max(1, args.limit ?? 12)));
278
- return rows.map(row => ({ kind: row['kind'], ref: String(row['ref']), repo: String(row['repo']), title: String(row['title']).slice(0, 200), snippet: String(row['snippet']).slice(0, 400) }));
302
+ WHERE memory_search MATCH ? AND scope IN (SELECT value FROM json_each(?)) AND (repo='' OR repo IN (SELECT value FROM json_each(?)))
303
+ AND (kind<>'conversation' OR EXISTS (SELECT 1 FROM lead_message m WHERE m.id=CAST(substr(memory_search.ref, 9) AS INTEGER) AND m.pending=0
304
+ AND (m.text = memory_search.body OR trim(m.text, ' '||char(9)||char(10)||char(13)) = memory_search.body)))
305
+ ORDER BY bm25(memory_search) LIMIT ?`)
306
+ .all(match, JSON.stringify(scopes), JSON.stringify(repos), limit * 2);
307
+ // One hit per entry and place: a message indexed before it waited and again when its turn took it counts once.
308
+ const seen = new Set();
309
+ return rows.filter(row => { const key = `${row['kind']}\u0000${row['ref']}\u0000${row['repo']}\u0000${row['scope']}`; return !seen.has(key) && seen.add(key) !== undefined; }).slice(0, limit)
310
+ .map(row => ({ kind: row['kind'], ref: String(row['ref']), repo: String(row['repo']), title: String(row['title']).slice(0, 200), snippet: String(row['snippet']).slice(0, 400) }));
279
311
  }
@@ -1,4 +1,8 @@
1
1
  import type { Store, ToolActionInfo } from "./store.js";
2
+ import { type BrokerOptions } from "./tool-broker.js";
3
+ import type { FenceMethod } from "./agent-fence.js";
4
+ import type { ProviderId } from "./contracts/provider.js";
5
+ import { type SecretStoreKind } from "./tool-secret-store.js";
2
6
  import { type ToolSecret, type ToolSpec } from "./contracts/integration-metadata.js";
3
7
  export type { ToolSecret, ToolSpec };
4
8
  export type ToolTest = {
@@ -55,12 +59,36 @@ export declare function toolDigest(spec: ToolSpec): string;
55
59
  export declare function validateToolSpec(input: Record<string, unknown>): ToolSpec;
56
60
  /** How the tool starts, in one line a person can check. Never a secret value. */
57
61
  export declare function toolCommandLine(spec: ToolSpec): string;
58
- export declare function toolSecretsDir(home?: string): string;
59
- export declare function readToolSecrets(repo: string, tool: string, home?: string): Record<string, string>;
62
+ /**
63
+ * Where tool secrets were kept before (an existing ~/.toolroll/tool-secrets, or an older name's), read once when
64
+ * Toolroll starts so their files move in on first use. Never where new secrets go.
65
+ */
66
+ export declare function legacyToolSecretsDir(home?: string): string | null;
67
+ /** Commands that can start builds or serve the console: Toolroll's folder for secrets is made before any build runs. */
68
+ export declare const SECRETS_FIRST: ReadonlySet<string>;
69
+ /**
70
+ * Toolroll's folder for the secrets it holds, beside `databaseFile`, checked against the sealing key the database
71
+ * recorded: made now when `eager` (a command that can start builds), else the first time a secret is used.
72
+ * A folder that can't be used is said in Settings and `toolroll integrations`; every secret is refused until then.
73
+ */
74
+ export declare function startToolSecrets(store: Pick<Store, "toolSecretKeyDigest" | "swapToolSecretKeyDigest" | "toolSecretLegacyDir">, databaseFile: string, clock: () => Date, options: {
75
+ eager: boolean;
76
+ }): void;
77
+ /** Toolroll's folder for secrets (tool-secret-store.ts): beside the database Toolroll started with, or a test's `state`. */
78
+ export declare function toolSecretsDir(state?: string): string;
79
+ /** A tool's secret values. Read only to test it, change it or start a run: a page reads which are set (secretsSetFor). */
80
+ export declare function readToolSecrets(repo: string, tool: string, state?: string): Record<string, string>;
60
81
  /** Set (or, with an empty value, clear) one secret of one tool. */
61
- export declare function setToolSecret(repo: string, tool: string, name: string, value: string, home?: string): void;
62
- export declare function setToolSecrets(repo: string, tool: string, values: Record<string, string>, home?: string): void;
63
- export declare function clearToolSecrets(repo: string, tool: string, home?: string): void;
82
+ export declare function setToolSecret(repo: string, tool: string, name: string, value: string, state?: string): void;
83
+ /** Set (or, with empty values, clear) some secrets of one tool, in one change. */
84
+ export declare function setToolSecrets(repo: string, tool: string, values: Record<string, string>, state?: string): void;
85
+ export declare function clearToolSecrets(repo: string, tool: string, state?: string): void;
86
+ /**
87
+ * One renewal of a sign-in at a time, across Toolroll's processes (a lock in its folder) and within one: a service
88
+ * that rotates its refresh token on every use would otherwise see the same one twice, and whichever answer was saved
89
+ * last would be the newest only by luck.
90
+ */
91
+ export declare function renewingSignIn<T>(repo: string, tool: string, state: string | undefined, work: () => Promise<T>): Promise<T>;
64
92
  /** Required secrets with no value yet: the tool is left out of builds until they are set. */
65
93
  export declare function missingSecrets(spec: ToolSpec, values: Record<string, string>): string[];
66
94
  /** A server already configured on this computer, ready to add to a project (values moved, never shown). */
@@ -123,42 +151,54 @@ export declare function callProjectTool(spec: ToolSpec, values: Record<string, s
123
151
  env?: NodeJS.ProcessEnv;
124
152
  omitEnv?: readonly string[];
125
153
  }): Promise<ToolCall>;
126
- /** The exact servers one launch gets, and those left out with why. */
154
+ /** The exact servers one launch gets, and those left out with why (`repo`: the project, from the run's record). */
127
155
  export type ToolLaunch = {
128
156
  tools: ResolvedTool[];
129
157
  skipped: {
130
158
  name: string;
131
159
  reason: string;
132
160
  }[];
161
+ repo?: string;
133
162
  };
134
163
  /** Everything a provider spawn needs to use exactly these tools, and the
135
- * cleanup. `privateDir` holds the run's secret launch files: Codex starts
136
- * tool servers outside its command sandbox, so its agent can be fenced out
137
- * of that folder while the tools still read it. */
164
+ * cleanup. `privateDir` holds the run's launch files, which name the broker
165
+ * and its token, never a tool's secret (tool-broker.ts). */
138
166
  export type ToolLaunchArgs = {
139
167
  argv: string[];
140
168
  env: Record<string, string>;
141
169
  omitEnv: string[];
142
170
  cleanup: () => void;
143
171
  privateDir?: string;
172
+ serverPids?: () => number[];
173
+ brokerPort?: number;
174
+ };
175
+ /** How a launch's tools are offered: research steps' servers only their read-only actions. */
176
+ export type ToolLaunchOptions = {
177
+ broker?: BrokerOptions;
178
+ enabledTools?: Readonly<Record<string, readonly string[]>>;
144
179
  };
145
- /** Claude: only these servers, written to a private 0600 file removed after the run (the provider adds `--strict-mcp-config`). */
146
- export declare function claudeToolArgs(launch: ToolLaunch): ToolLaunchArgs;
180
+ /**
181
+ * Claude: only these servers, in a private 0600 file removed after the run
182
+ * (the provider adds `--strict-mcp-config`). Each is the broker's address
183
+ * with this launch's token: Claude and its shell share one sandbox, so the
184
+ * file holds nothing a tool signs in with.
185
+ */
186
+ export declare function claudeToolArgs(launch: ToolLaunch, options?: ToolLaunchOptions): Promise<ToolLaunchArgs>;
147
187
  /** Where the tool launcher lives beside this module, built. */
148
188
  export declare function toolLauncherPath(): string;
149
189
  /**
150
- * Codex: each tool is named by `-c` and started through the launcher. The
151
- * operator's config.toml, rules, apps, plugins and hooks are already off
152
- * (the provider's CODEX_BUILD_ISOLATION_ARGV), and nothing of config.toml
153
- * is carried. Codex gives its own environment to the agent's shell whatever
154
- * its policy says, so no secret rides it: each tool's values sit in the
155
- * run's private 0600 file and reach only that tool's process.
190
+ * Codex: each tool is named by `-c` and started through the launcher, which
191
+ * relays it to the launch's broker. The operator's config.toml, rules, apps,
192
+ * plugins and hooks are already off (the provider's
193
+ * CODEX_BUILD_ISOLATION_ARGV), and nothing of config.toml is carried. Codex
194
+ * gives its own environment to the agent's shell whatever its policy says,
195
+ * so nothing rides it: the launch files name the broker and its token, and
196
+ * the folder holding them is fenced from the agent.
156
197
  */
157
- export declare function codexToolArgs(launch: ToolLaunch, options?: {
198
+ export declare function codexToolArgs(launch: ToolLaunch, options?: ToolLaunchOptions & {
158
199
  launcher?: string;
159
200
  node?: string;
160
- enabledTools?: Readonly<Record<string, readonly string[]>>;
161
- }): ToolLaunchArgs;
201
+ }): Promise<ToolLaunchArgs>;
162
202
  /** Gemini: no way yet to hand it exactly these servers, so it gets none. */
163
203
  export declare const GEMINI_NO_TOOLS_ARGV: readonly string[];
164
204
  type ToolRows = Pick<Store, "projectTools">;
@@ -171,24 +211,44 @@ export declare function projectToolsOf(store: ToolRows, repo: string): ProjectTo
171
211
  * left out says why, in words the task page can show.
172
212
  */
173
213
  export declare function toolLaunchFor(store: Pick<Store, "projectTools" | "getRun" | "refById" | "getScope" | "toolSealFor" | "orgPolicy">, runId: number, home?: string): ToolLaunch;
214
+ /** What stops a build reading a local tool server's environment: this computer's platform and the OS fence its run
215
+ * gets, both fixed by the gateway before any tool is prepared (never from a launch's own files or the checkout). */
216
+ export type ToolContainment = {
217
+ platform: NodeJS.Platform;
218
+ fence: FenceMethod;
219
+ };
220
+ /**
221
+ * Why a local tool that carries secrets can't start for this launch, or null when it can. Its server's environment
222
+ * holds the values, and a process of the same user can read another's (KERN_PROCARGS2, /proc/<pid>/environ) unless
223
+ * an OS fence stops it: only the macOS sandbox around a Claude build does. Codex's filesystem policy can't deny that
224
+ * read (OpenRouter uses the same harness); Claude's own file rules and Linux's bubblewrap don't stop it, and on Linux
225
+ * hidepid hides nothing from the same user, so Linux never starts one.
226
+ */
227
+ export declare function secretToolRefusal(provider: ProviderId, containment: ToolContainment): string | null;
174
228
  /**
175
229
  * One attempt's tools, ready for its provider, and recorded on the run:
176
230
  * what it launched with and what was left out and why. The caller spawns
177
231
  * with `argv`/`env` and calls `cleanup` when the process is done.
178
232
  */
179
- export declare function prepareRunTools(store: Pick<Store, "projectTools" | "getRun" | "refById" | "getScope" | "toolSealFor" | "recordRunTools" | "orgPolicy">, runId: number, provider: "claude" | "codex" | "openrouter" | "gemini", options: {
233
+ export declare function prepareRunTools(store: Pick<Store, "projectTools" | "getRun" | "refById" | "getScope" | "toolSealFor" | "recordRunTools" | "orgPolicy">, runId: number, provider: ProviderId, options: {
180
234
  home?: string;
181
235
  now: Date;
236
+ containment: ToolContainment;
182
237
  readOnly?: Readonly<Record<string, readonly string[]>>;
183
238
  withheld?: Readonly<Record<string, string>>;
184
- }): ToolLaunchArgs;
239
+ broker?: BrokerOptions;
240
+ fetcher?: typeof fetch;
241
+ }): Promise<ToolLaunchArgs>;
242
+ /** A run whose sign-ins couldn't all be made and kept: it doesn't start, and every token made for it is revoked. */
243
+ export declare class RunSignInError extends Error {
244
+ }
185
245
  /** When a launch's tools cannot be prepared, it still gets none of anything else's: the same isolation with an empty list. */
186
- export declare function noToolsArgs(provider: "claude" | "codex" | "openrouter" | "gemini"): ToolLaunchArgs;
246
+ export declare function noToolsArgs(provider: ProviderId): ToolLaunchArgs;
187
247
  type ToolWrites = Pick<Store, "projectTools" | "addProjectTool" | "removeProjectTool" | "recordProjectToolTest">;
188
248
  /** Add one tool (and, for an import, the values it brings) to a project's builds. */
189
249
  export declare function addToolTo(store: ToolWrites, repo: string, input: ToolSpec, source: string, by: string, now: Date, options?: {
190
250
  values?: Record<string, string>;
191
- home?: string;
251
+ home?: string | undefined;
192
252
  }): {
193
253
  ok: true;
194
254
  spec: ToolSpec;
@@ -200,7 +260,7 @@ export declare function addToolTo(store: ToolWrites, repo: string, input: ToolSp
200
260
  export declare function removeToolFrom(store: ToolWrites, repo: string, name: string, by: string, now: Date, home?: string): boolean;
201
261
  /** Test one tool of a project and keep the answer on it. */
202
262
  export declare function testToolOf(store: ToolWrites, repo: string, name: string, now: Date, options?: {
203
- home?: string;
263
+ home?: string | undefined;
204
264
  timeoutMs?: number;
205
265
  omitEnv?: readonly string[];
206
266
  }): Promise<ToolTest | null>;
@@ -209,7 +269,23 @@ export declare function toolStanding(tool: ProjectTool, secretsSet: readonly str
209
269
  ready: boolean;
210
270
  words: string;
211
271
  };
212
- /** Names of a tool's secrets that have a value (never the values). */
213
- export declare function secretsSetFor(repo: string, tool: ToolSpec, home?: string): string[];
272
+ /** How a tool's runs get its credentials, in the words Settings and `toolroll integrations` share. */
273
+ export type CredentialMode = {
274
+ mode: "run-token" | "held-key" | "no-key";
275
+ words: string;
276
+ detail: string | null;
277
+ };
278
+ export declare const CREDENTIAL_MODES: Readonly<Record<CredentialMode["mode"], Omit<CredentialMode, "mode">>>;
279
+ /** A tool's credential mode from the names of its secrets that are set (never their values); null while a key it needs is missing. */
280
+ export declare function credentialModeOf(spec: ToolSpec, secretsSet: readonly string[]): CredentialMode | null;
281
+ /** Names of a tool's secrets that have a value, never the values: in the keychain, from the names kept beside them. */
282
+ export declare function secretsSetFor(repo: string, tool: ToolSpec, state?: string): string[];
283
+ /** Where this computer keeps tool secrets, in the words Settings and `toolroll integrations` share; `problem` when they can't be used. */
284
+ export declare function toolSecretStorage(): {
285
+ kind: SecretStoreKind;
286
+ words: string;
287
+ filesOnly: boolean;
288
+ problem?: string;
289
+ };
214
290
  /** A command line split into program and arguments, honouring "double" and 'single' quotes. */
215
291
  export declare function splitCommandLine(line: string): string[];