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
@@ -0,0 +1,104 @@
1
+ import type { Store } from "./store.js";
2
+ /** At most this many messages wait per person, and this many characters in all; a waiting message expires after 7 days. */
3
+ export declare const LEAD_QUEUE_MAX_MESSAGES = 10;
4
+ export declare const LEAD_QUEUE_MAX_CHARS = 20000;
5
+ export declare const LEAD_QUEUE_EXPIRY_MS: number;
6
+ /** What a queued message was sent with: resolved and validated at send, re-validated before its turn. */
7
+ export type LeadQueueContext = {
8
+ task?: string;
9
+ result?: number;
10
+ mode?: string;
11
+ project?: string;
12
+ };
13
+ export type LeadQueueStatus = "queued" | "running" | "answered" | "failed" | "cancelled" | "uncertain";
14
+ export type LeadQueueRow = {
15
+ message: number;
16
+ thread: number;
17
+ approver: string;
18
+ approverGeneration: number;
19
+ session: number;
20
+ ceilingDigest: string;
21
+ requestId: string;
22
+ payloadHash: string;
23
+ context: LeadQueueContext | null;
24
+ status: LeadQueueStatus;
25
+ revision: number;
26
+ generation: number;
27
+ runner: string | null;
28
+ turnId: number | null;
29
+ error: string | null;
30
+ createdAt: string;
31
+ text: string;
32
+ };
33
+ /** A claimed row: the fence every later step re-proves (status running, this runner, this generation and revision). */
34
+ export type LeadQueueClaim = LeadQueueRow & {
35
+ runner: string;
36
+ status: "running";
37
+ };
38
+ /** The identity of a send: the same request with the same words and context is the same message. */
39
+ export declare function leadQueuePayloadHash(thread: number, text: string, context: LeadQueueContext | null): string;
40
+ export type SendDecision = {
41
+ kind: "run";
42
+ } | {
43
+ kind: "queued";
44
+ row: LeadQueueRow;
45
+ already: boolean;
46
+ } | {
47
+ kind: "conflict";
48
+ } | {
49
+ kind: "full";
50
+ message: string;
51
+ };
52
+ export declare class PersonalQueue {
53
+ private readonly store;
54
+ constructor(store: Store);
55
+ private get db();
56
+ /** The approver's backlog: rows waiting or claimed but not yet bound to a turn, oldest first. */
57
+ backlog(approver: string): LeadQueueRow[];
58
+ /** Every queue row of a thread (what the thread shows next to its messages). */
59
+ forThread(thread: number): LeadQueueRow[];
60
+ get(message: number): LeadQueueRow | null;
61
+ /**
62
+ * Send: run now, or wait — in ONE transaction, so a turn ending between the check and the insert can't strand a
63
+ * message. A request already saved with the same words and context is that message (never a second send, never a
64
+ * removed one resurrected); with different ones it conflicts. Busy means a personal lead turn of this person is
65
+ * queued or running, or something of theirs already waits.
66
+ */
67
+ sendOrQueue(input: {
68
+ approver: string;
69
+ approverGeneration: number;
70
+ session: number;
71
+ ceilingDigest: string;
72
+ thread: number;
73
+ text: string;
74
+ requestId: string;
75
+ context: LeadQueueContext | null;
76
+ }, now: Date): SendDecision;
77
+ /**
78
+ * The next message to run: per person, only the oldest of their backlog, only while none of theirs runs, and only when
79
+ * `eligible` agrees (the runtime's admission stamp). Claiming keeps it in the backlog until its turn binds it.
80
+ */
81
+ claimNext(runner: string, now: Date, eligible?: (row: LeadQueueRow) => boolean): LeadQueueClaim | null;
82
+ /** The claim still stands: running, this runner, this generation and revision, unbound or bound to `turn`. */
83
+ current(claim: LeadQueueClaim): boolean;
84
+ /** Inside the engine's admission transaction (onAdmitted): the claim binds to the turn that took it, exactly once. */
85
+ bindTurn(claim: LeadQueueClaim, turnId: number): boolean;
86
+ /** The claim ends: answered (its turn's reply is in the thread), failed or cancelled (words kept as the error), or
87
+ * uncertain (an outcome that can't be confirmed — never sent again). A message that never got a turn loses its words. */
88
+ finish(claim: LeadQueueClaim, result: {
89
+ status: "answered" | "failed" | "cancelled" | "uncertain";
90
+ error?: string;
91
+ turnId?: number;
92
+ }, now: Date): boolean;
93
+ /** A refusal before admission (a cap, a budget, a latch, another turn): back to waiting with the words to show. */
94
+ defer(claim: LeadQueueClaim, error: string): boolean;
95
+ /** Remove a waiting message (its author only, while it still waits): its words are forgotten, its request kept. */
96
+ remove(approver: string, message: number, now: Date): "removed" | "started" | "not-found";
97
+ /** Settle rows whose bound turn ended (by its recorded state), expire week-old waiting rows, and mark a claim that
98
+ * never bound by its turn deadline uncertain (the binding is the proof; elapsed time never resends). */
99
+ reconcile(now: Date, turnDeadlineMs: number): number;
100
+ /** End every waiting message sent under a session (it ended, or its person's chat was revoked). */
101
+ cancelForSession(session: number, why: string, now: Date): number;
102
+ /** A message that never ran keeps no words, context or search copy (it stays pending, so never history). */
103
+ private forgetWords;
104
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * The personal lead queue (docs/plans/lead-message-queue.md): a message sent while one of its author's personal lead
3
+ * turns runs (or while earlier ones wait) waits here and runs one turn per message, in order — the same machinery
4
+ * shared team conversations use (team-leads.ts), for the person's own threads (/chat, a task's or a project's chat).
5
+ *
6
+ * The row's words are its lead_message (pending until the engine's admission binds it, which clears pending in the
7
+ * same statement). Admission itself is the engine's (runLeadTurn with queuedMessageId + onAdmitted): this module only
8
+ * decides queue-or-run at send, claims the backlog's oldest row, binds, finishes, defers, removes and expires.
9
+ */
10
+ import { createHash } from "node:crypto";
11
+ /** At most this many messages wait per person, and this many characters in all; a waiting message expires after 7 days. */
12
+ export const LEAD_QUEUE_MAX_MESSAGES = 10;
13
+ export const LEAD_QUEUE_MAX_CHARS = 20_000;
14
+ export const LEAD_QUEUE_EXPIRY_MS = 7 * 86_400_000;
15
+ /** The identity of a send: the same request with the same words and context is the same message. */
16
+ export function leadQueuePayloadHash(thread, text, context) {
17
+ return createHash("sha256").update(JSON.stringify([thread, text, context === null ? null : canonical(context)])).digest("hex");
18
+ }
19
+ const canonical = (context) => Object.fromEntries(Object.entries(context).filter(([, value]) => value !== undefined).sort(([a], [b]) => a.localeCompare(b)));
20
+ const readRow = (row) => ({
21
+ message: Number(row["message"]), thread: Number(row["thread"]), approver: String(row["approver"]), approverGeneration: Number(row["approver_generation"]),
22
+ session: Number(row["session"]), ceilingDigest: String(row["ceiling_digest"]), requestId: String(row["request_id"]), payloadHash: String(row["payload_hash"]),
23
+ context: row["context_json"] === null ? null : JSON.parse(String(row["context_json"])), status: String(row["status"]),
24
+ revision: Number(row["revision"]), generation: Number(row["generation"]), runner: row["runner"] === null ? null : String(row["runner"]),
25
+ turnId: row["turn_id"] === null ? null : Number(row["turn_id"]), error: row["error"] === null ? null : String(row["error"]),
26
+ createdAt: String(row["created_at"]), text: String(row["text"] ?? ""),
27
+ });
28
+ const SELECT = "SELECT q.*, m.text FROM lead_queue q JOIN lead_message m ON m.id = q.message";
29
+ export class PersonalQueue {
30
+ store;
31
+ constructor(store) {
32
+ this.store = store;
33
+ }
34
+ get db() { return this.store.handle; }
35
+ /** The approver's backlog: rows waiting or claimed but not yet bound to a turn, oldest first. */
36
+ backlog(approver) {
37
+ return this.db.prepare(`${SELECT} WHERE q.approver = ? AND (q.status = 'queued' OR (q.status = 'running' AND q.turn_id IS NULL)) ORDER BY q.message`).all(approver).map(readRow);
38
+ }
39
+ /** Every queue row of a thread (what the thread shows next to its messages). */
40
+ forThread(thread) {
41
+ return this.db.prepare(`${SELECT} WHERE q.thread = ? ORDER BY q.message`).all(thread).map(readRow);
42
+ }
43
+ get(message) {
44
+ const row = this.db.prepare(`${SELECT} WHERE q.message = ?`).get(message);
45
+ return row === undefined ? null : readRow(row);
46
+ }
47
+ /**
48
+ * Send: run now, or wait — in ONE transaction, so a turn ending between the check and the insert can't strand a
49
+ * message. A request already saved with the same words and context is that message (never a second send, never a
50
+ * removed one resurrected); with different ones it conflicts. Busy means a personal lead turn of this person is
51
+ * queued or running, or something of theirs already waits.
52
+ */
53
+ sendOrQueue(input, now) {
54
+ return this.store.transact(() => {
55
+ const hash = leadQueuePayloadHash(input.thread, input.text, input.context);
56
+ const seen = this.db.prepare(`${SELECT} WHERE q.session = ? AND q.request_id = ?`).get(input.session, input.requestId);
57
+ if (seen !== undefined)
58
+ return String(seen["payload_hash"]) === hash ? { kind: "queued", row: readRow(seen), already: true } : { kind: "conflict" };
59
+ const live = this.db.prepare(`SELECT 1 FROM mate_turn WHERE approver = ? AND state IN ('queued','running')
60
+ AND NOT EXISTS (SELECT 1 FROM team_conversation tc WHERE tc.thread = mate_turn.thread) LIMIT 1`).get(input.approver);
61
+ const waiting = this.backlog(input.approver);
62
+ if (live === undefined && waiting.length === 0)
63
+ return { kind: "run" };
64
+ if (waiting.length >= LEAD_QUEUE_MAX_MESSAGES)
65
+ return { kind: "full", message: `${LEAD_QUEUE_MAX_MESSAGES} messages are already waiting. Remove one, or wait for a reply.` };
66
+ if (waiting.reduce((sum, one) => sum + one.text.length, 0) + input.text.length > LEAD_QUEUE_MAX_CHARS) {
67
+ return { kind: "full", message: "The messages waiting are already long. Remove one, or wait for a reply." };
68
+ }
69
+ const message = this.store.appendLeadMessage({ thread: input.thread, turn: null, role: "operator", text: input.text, pending: true }, now);
70
+ this.db.prepare(`INSERT INTO lead_queue (message, thread, approver, approver_generation, session, ceiling_digest, request_id, payload_hash, context_json, status, created_at)
71
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'queued', ?)`).run(message, input.thread, input.approver, input.approverGeneration, input.session, input.ceilingDigest, input.requestId, hash, input.context === null ? null : JSON.stringify(canonical(input.context)), now.toISOString());
72
+ return { kind: "queued", row: this.get(message), already: false };
73
+ });
74
+ }
75
+ /**
76
+ * The next message to run: per person, only the oldest of their backlog, only while none of theirs runs, and only when
77
+ * `eligible` agrees (the runtime's admission stamp). Claiming keeps it in the backlog until its turn binds it.
78
+ */
79
+ claimNext(runner, now, eligible = () => true) {
80
+ return this.store.transact(() => {
81
+ // An uncertain row does not hold the person's later messages (unlike a team room): its outcome is shown on it, and
82
+ // an unknown cost already pauses chat through its own latch.
83
+ const heads = this.db.prepare(`${SELECT} WHERE q.status = 'queued'
84
+ AND NOT EXISTS (SELECT 1 FROM lead_queue r WHERE r.approver = q.approver AND r.status = 'running')
85
+ AND NOT EXISTS (SELECT 1 FROM lead_queue e WHERE e.approver = q.approver AND e.status = 'queued' AND e.message < q.message)
86
+ ORDER BY q.message LIMIT 50`).all().map(readRow);
87
+ for (const row of heads) {
88
+ if (!eligible(row))
89
+ continue;
90
+ const changed = this.db.prepare("UPDATE lead_queue SET status = 'running', generation = generation + 1, runner = ?, claimed_at = ? WHERE message = ? AND status = 'queued'")
91
+ .run(runner, now.toISOString(), row.message);
92
+ if (Number(changed.changes) !== 1)
93
+ continue;
94
+ return { ...row, status: "running", runner, generation: row.generation + 1 };
95
+ }
96
+ return null;
97
+ });
98
+ }
99
+ /** The claim still stands: running, this runner, this generation and revision, unbound or bound to `turn`. */
100
+ current(claim) {
101
+ return this.db.prepare("SELECT 1 FROM lead_queue WHERE message = ? AND status = 'running' AND runner = ? AND generation = ? AND revision = ?")
102
+ .get(claim.message, claim.runner, claim.generation, claim.revision) !== undefined;
103
+ }
104
+ /** Inside the engine's admission transaction (onAdmitted): the claim binds to the turn that took it, exactly once. */
105
+ bindTurn(claim, turnId) {
106
+ if (!this.current(claim))
107
+ return false;
108
+ const turn = this.store.getLeadTurn(turnId);
109
+ if (turn === null || turn.thread !== claim.thread || turn.approver !== claim.approver)
110
+ return false;
111
+ return Number(this.db.prepare("UPDATE lead_queue SET turn_id = ? WHERE message = ? AND turn_id IS NULL AND status = 'running' AND generation = ?")
112
+ .run(turnId, claim.message, claim.generation).changes) === 1;
113
+ }
114
+ /** The claim ends: answered (its turn's reply is in the thread), failed or cancelled (words kept as the error), or
115
+ * uncertain (an outcome that can't be confirmed — never sent again). A message that never got a turn loses its words. */
116
+ finish(claim, result, now) {
117
+ return this.store.transact(() => {
118
+ const row = this.db.prepare("SELECT turn_id FROM lead_queue WHERE message = ? AND status = 'running' AND runner = ? AND generation = ?").get(claim.message, claim.runner, claim.generation);
119
+ if (row === undefined)
120
+ return false;
121
+ if (result.turnId !== undefined && row["turn_id"] !== null && Number(row["turn_id"]) !== result.turnId)
122
+ return false;
123
+ this.db.prepare("UPDATE lead_queue SET status = ?, error = ?, turn_id = COALESCE(turn_id, ?), revision = revision + 1, settled_at = ? WHERE message = ?")
124
+ .run(result.status, result.error ?? null, result.turnId ?? null, now.toISOString(), claim.message);
125
+ if (row["turn_id"] === null && result.turnId === undefined)
126
+ this.forgetWords(claim.message);
127
+ return true;
128
+ });
129
+ }
130
+ /** A refusal before admission (a cap, a budget, a latch, another turn): back to waiting with the words to show. */
131
+ defer(claim, error) {
132
+ return Number(this.db.prepare(`UPDATE lead_queue SET status = 'queued', generation = generation + 1, revision = revision + 1, runner = NULL, claimed_at = NULL, error = ?
133
+ WHERE message = ? AND status = 'running' AND turn_id IS NULL AND runner = ? AND generation = ?`).run(error, claim.message, claim.runner, claim.generation).changes) === 1;
134
+ }
135
+ /** Remove a waiting message (its author only, while it still waits): its words are forgotten, its request kept. */
136
+ remove(approver, message, now) {
137
+ return this.store.transact(() => {
138
+ const row = this.db.prepare("SELECT status FROM lead_queue WHERE message = ? AND approver = ?").get(message, approver);
139
+ if (row === undefined)
140
+ return "not-found";
141
+ if (row["status"] !== "queued")
142
+ return "started";
143
+ this.db.prepare("UPDATE lead_queue SET status = 'cancelled', error = 'Removed.', revision = revision + 1, settled_at = ? WHERE message = ?").run(now.toISOString(), message);
144
+ this.forgetWords(message);
145
+ return "removed";
146
+ });
147
+ }
148
+ /** Settle rows whose bound turn ended (by its recorded state), expire week-old waiting rows, and mark a claim that
149
+ * never bound by its turn deadline uncertain (the binding is the proof; elapsed time never resends). */
150
+ reconcile(now, turnDeadlineMs) {
151
+ return this.store.transact(() => {
152
+ let changed = 0;
153
+ for (const row of this.db.prepare(`SELECT q.message, t.state, t.failure_reason FROM lead_queue q JOIN mate_turn t ON t.id = q.turn_id
154
+ WHERE q.status = 'running' AND t.state IN ('answered','failed')`).all()) {
155
+ const uncertain = this.store.leadTurnCostUnconfirmed(Number(this.db.prepare("SELECT turn_id FROM lead_queue WHERE message = ?").get(Number(row["message"]))["turn_id"]));
156
+ const status = row["state"] === "answered" ? "answered" : uncertain ? "uncertain" : "failed";
157
+ changed += Number(this.db.prepare("UPDATE lead_queue SET status = ?, error = ?, revision = revision + 1, settled_at = ? WHERE message = ? AND status = 'running'")
158
+ .run(status, status === "answered" ? null : String(row["failure_reason"] ?? "failed"), now.toISOString(), Number(row["message"])).changes);
159
+ }
160
+ const stale = new Date(now.getTime() - turnDeadlineMs).toISOString();
161
+ changed += Number(this.db.prepare("UPDATE lead_queue SET status = 'uncertain', error = 'Delivery could not be confirmed. Nothing was sent again.', revision = revision + 1, settled_at = ? WHERE status = 'running' AND turn_id IS NULL AND claimed_at < ?")
162
+ .run(now.toISOString(), stale).changes);
163
+ const old = new Date(now.getTime() - LEAD_QUEUE_EXPIRY_MS).toISOString();
164
+ for (const row of this.db.prepare("SELECT message FROM lead_queue WHERE status = 'queued' AND created_at < ?").all(old)) {
165
+ this.db.prepare("UPDATE lead_queue SET status = 'failed', error = 'Not sent: it waited too long.', revision = revision + 1, settled_at = ? WHERE message = ?").run(now.toISOString(), Number(row["message"]));
166
+ this.forgetWords(Number(row["message"]));
167
+ changed++;
168
+ }
169
+ return changed;
170
+ });
171
+ }
172
+ /** End every waiting message sent under a session (it ended, or its person's chat was revoked). */
173
+ cancelForSession(session, why, now) {
174
+ return this.store.transact(() => {
175
+ const rows = this.db.prepare("SELECT message FROM lead_queue WHERE session = ? AND status = 'queued'").all(session);
176
+ for (const row of rows) {
177
+ this.db.prepare("UPDATE lead_queue SET status = 'cancelled', error = ?, revision = revision + 1, settled_at = ? WHERE message = ?").run(why, now.toISOString(), Number(row["message"]));
178
+ this.forgetWords(Number(row["message"]));
179
+ }
180
+ return rows.length;
181
+ });
182
+ }
183
+ /** A message that never ran keeps no words, context or search copy (it stays pending, so never history). */
184
+ forgetWords(message) {
185
+ this.store.forgetQueuedLeadMessage(message);
186
+ }
187
+ }
@@ -0,0 +1,67 @@
1
+ import { type AssignmentSnapshot } from "./assignment.js";
2
+ import { type Store } from "./store.js";
3
+ import { type StopRequest } from "./task-control.js";
4
+ export declare const SWEEP_VERDICTS: readonly ["fixed", "not reproducible", "blocked", "done"];
5
+ export type SweepVerdict = typeof SWEEP_VERDICTS[number];
6
+ /** The line every sweep child's brief asks for, appended to the shared rules. */
7
+ export declare const VERDICT_RULE = "Start your handoff's conclusion with one line: `Verdict: fixed`, `Verdict: not reproducible`, `Verdict: blocked` or `Verdict: done`, then ` \u2014 ` and one sentence why (the root cause, what you tried, or what blocks it).";
8
+ /** A conclusion's verdict line, read strictly. */
9
+ export declare function verdictOf(conclusion: string | null | undefined): {
10
+ verdict: SweepVerdict;
11
+ why: string;
12
+ } | null;
13
+ export type SweepRow = {
14
+ ordinal: number;
15
+ label: string;
16
+ /** The child's family root: its identity in the sweep. */
17
+ task: string;
18
+ /** The task the family is on now (a revision of the child, after one): what an act on this row acts on. */
19
+ active: string;
20
+ state: AssignmentSnapshot["state"] | "missing";
21
+ detail: string;
22
+ verdict: {
23
+ verdict: SweepVerdict;
24
+ why: string;
25
+ } | null;
26
+ /** The child's result, when it has one. */
27
+ result: {
28
+ taskId: string;
29
+ runId: number;
30
+ } | null;
31
+ };
32
+ export type SweepTable = {
33
+ id: number;
34
+ title: string;
35
+ repo: string;
36
+ maxParallel: number;
37
+ rows: SweepRow[];
38
+ counts: {
39
+ settled: number;
40
+ working: number;
41
+ total: number;
42
+ byVerdict: Partial<Record<SweepVerdict, number>>;
43
+ };
44
+ };
45
+ /** A sweep as its table shows it; null when there is no such sweep or this reader may not see its project. */
46
+ export declare function sweepTable(store: Store, id: number, now: Date, repos: readonly string[], root?: string): SweepTable | null;
47
+ /** "8 of 11 settled — 5 fixed, 2 not reproducible, 1 blocked": the sweep in one line. */
48
+ export declare function sweepLine(table: SweepTable): string;
49
+ /** Not ready yet: working, being checked, or waiting on a decision — what "cancel the rest" acts on. */
50
+ export declare const SWEEP_OPEN: ReadonlySet<SweepRow["state"]>;
51
+ /** One open item exactly as it stands now: its active task, state, attempt (a running build to stop) and last change. */
52
+ export type SweepOpenItem = {
53
+ label: string;
54
+ task: string;
55
+ state: string;
56
+ run: number | null;
57
+ at: string | null;
58
+ scope: string | null;
59
+ };
60
+ /** Exactly what "cancel the rest" would act on now; any change makes a card or a form for it stale. */
61
+ export declare function sweepOpen(store: Store, table: SweepTable, now: Date): SweepOpenItem[];
62
+ export declare const sweepOpenDigest: (open: readonly SweepOpenItem[]) => string;
63
+ /**
64
+ * Cancel the rest: a running build is stopped through the ordinary stop (process-exit safeguards and all); anything
65
+ * else is cancelled. Every refusal is said, never swallowed. The caller has proved `open` is what stands now.
66
+ */
67
+ export declare function cancelSweepRest(store: Store, actor: string, open: readonly SweepOpenItem[], now: Date, via: StopRequest["via"]): string;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Lead fan-out (docs/plans/lead-fan-out.md): a sweep's table and verdicts, read from facts — never a model.
3
+ *
4
+ * Each row is a child's assignment (the same state words the board uses), its verdict, and its result. The verdict is
5
+ * the first line of the child's conclusion, `Verdict: fixed | not reproducible | blocked | done — why`, which the
6
+ * sweep's brief asks for. It is read strictly: anything else reads as no verdict, never a guess.
7
+ */
8
+ import { createHash } from "node:crypto";
9
+ import { withActor } from "./actor.js";
10
+ import { assignmentOf } from "./assignment.js";
11
+ import { chatTaskRun } from "./chat-task-actions.js";
12
+ import { verifiedAuthor } from "./store.js";
13
+ import { requestTaskStop } from "./task-control.js";
14
+ export const SWEEP_VERDICTS = ["fixed", "not reproducible", "blocked", "done"];
15
+ /** The line every sweep child's brief asks for, appended to the shared rules. */
16
+ export const VERDICT_RULE = "Start your handoff's conclusion with one line: `Verdict: fixed`, `Verdict: not reproducible`, `Verdict: blocked` or `Verdict: done`, then ` — ` and one sentence why (the root cause, what you tried, or what blocks it).";
17
+ /** A conclusion's verdict line, read strictly. */
18
+ export function verdictOf(conclusion) {
19
+ if (typeof conclusion !== "string")
20
+ return null;
21
+ const first = conclusion.split("\n", 1)[0].trim();
22
+ const match = /^Verdict:\s*(fixed|not reproducible|not-reproducible|blocked|done)\s*(?:—|–|-|:)\s*(.{1,300})$/i.exec(first);
23
+ if (match === null)
24
+ return null;
25
+ const verdict = match[1].toLowerCase().replace("-", " ");
26
+ return SWEEP_VERDICTS.includes(verdict) ? { verdict, why: match[2].trim() } : null;
27
+ }
28
+ const SETTLED = new Set(["ready-to-check", "complete", "cancelled", "needs-decision", "missing"]);
29
+ /** A sweep as its table shows it; null when there is no such sweep or this reader may not see its project. */
30
+ export function sweepTable(store, id, now, repos, root) {
31
+ const sweep = store.getSweep(id);
32
+ if (sweep === null || !repos.includes(sweep.repo))
33
+ return null;
34
+ const rows = store.sweepTasks(id).map(child => {
35
+ const assignment = assignmentOf(store, child.rootTask, now, { principal: "operator", repos }, root);
36
+ const run = assignment?.receipt?.runId ?? null;
37
+ const resultTask = assignment?.receipt?.taskId ?? null;
38
+ const conclusion = run === null ? null : store.getRun(run)?.handoff ?? null;
39
+ return {
40
+ ordinal: child.ordinal, label: child.label, task: child.rootTask, active: assignment?.activeTaskId ?? child.rootTask,
41
+ state: assignment?.state ?? "missing", detail: assignment?.detail ?? "This task is gone.",
42
+ verdict: verdictOf(conclusion),
43
+ result: run === null || resultTask === null ? null : { taskId: resultTask, runId: run },
44
+ };
45
+ });
46
+ const byVerdict = {};
47
+ for (const row of rows)
48
+ if (row.verdict !== null)
49
+ byVerdict[row.verdict.verdict] = (byVerdict[row.verdict.verdict] ?? 0) + 1;
50
+ const settled = rows.filter(row => SETTLED.has(row.state)).length;
51
+ return { id, title: sweep.title, repo: sweep.repo, maxParallel: sweep.maxParallel, rows,
52
+ counts: { settled, working: rows.length - settled, total: rows.length, byVerdict } };
53
+ }
54
+ /** "8 of 11 settled — 5 fixed, 2 not reproducible, 1 blocked": the sweep in one line. */
55
+ export function sweepLine(table) {
56
+ const verdicts = SWEEP_VERDICTS.filter(one => (table.counts.byVerdict[one] ?? 0) > 0).map(one => `${table.counts.byVerdict[one]} ${one}`);
57
+ return `${table.counts.settled} of ${table.counts.total} settled${verdicts.length === 0 ? "" : ` — ${verdicts.join(", ")}`}`;
58
+ }
59
+ /** Not ready yet: working, being checked, or waiting on a decision — what "cancel the rest" acts on. */
60
+ export const SWEEP_OPEN = new Set(["working", "checking", "needs-decision"]);
61
+ /** Exactly what "cancel the rest" would act on now; any change makes a card or a form for it stale. */
62
+ export function sweepOpen(store, table, now) {
63
+ return table.rows.filter(row => SWEEP_OPEN.has(row.state)).map(row => ({
64
+ label: row.label, task: row.active, state: row.state, run: chatTaskRun(store, row.active, "stop", now), at: store.getTask(row.active)?.updatedAt ?? null,
65
+ // The scope's identity too: a goal rewritten in between makes a card or form for it stale (Codex sweep r2).
66
+ scope: store.getScope(row.active)?.digest ?? null,
67
+ }));
68
+ }
69
+ export const sweepOpenDigest = (open) => createHash("sha256").update(JSON.stringify(open)).digest("hex");
70
+ /**
71
+ * Cancel the rest: a running build is stopped through the ordinary stop (process-exit safeguards and all); anything
72
+ * else is cancelled. Every refusal is said, never swallowed. The caller has proved `open` is what stands now.
73
+ */
74
+ export function cancelSweepRest(store, actor, open, now, via) {
75
+ let cancelled = 0, stopping = 0;
76
+ const refused = [];
77
+ for (const item of open) {
78
+ if (item.run !== null) {
79
+ const stop = requestTaskStop(store, { taskId: item.task, runId: item.run, by: verifiedAuthor(actor), via }, now);
80
+ if (stop.ok)
81
+ stopping += 1;
82
+ else
83
+ refused.push(`${item.label} (${stop.detail})`);
84
+ continue;
85
+ }
86
+ const result = withActor({ account: actor, lead: false }, () => store.cancelTask(item.task, now));
87
+ if (result.ok)
88
+ cancelled += 1;
89
+ else
90
+ refused.push(`${item.label} (${String(result.reason ?? "refused")})`);
91
+ }
92
+ const parts = [`Cancelled ${cancelled} item${cancelled === 1 ? "" : "s"}.`];
93
+ if (stopping > 0)
94
+ parts.push(`Stop requested for ${stopping} running build${stopping === 1 ? "" : "s"}: ${stopping === 1 ? "it stops" : "they stop"} at the next safe point; cancel ${stopping === 1 ? "it" : "them"} from the task page once stopped.`);
95
+ if (refused.length > 0)
96
+ parts.push(`Not cancelled: ${refused.join("; ")}.`);
97
+ parts.push("Finished results stay.");
98
+ return parts.join(" ");
99
+ }
@@ -48,6 +48,12 @@ export type LeadToolContext = {
48
48
  checkedCapabilities?: Map<string, number>;
49
49
  /** Records this turn's one question to its owner; false when it already asked one. Absent: this surface cannot ask. */
50
50
  ask?: (question: string, options: readonly string[]) => boolean;
51
+ /** This turn's tool discovery (find_tools): how many searches it made and which deferred tools it found, which the
52
+ * conversation loop advertises from its next request. Absent (a direct call outside a turn): nothing to add to. */
53
+ findTools?: {
54
+ searches: number;
55
+ found: Set<string>;
56
+ };
51
57
  };
52
58
  export type LeadToolResult = {
53
59
  ok: true;
@@ -105,6 +111,22 @@ export declare const ISO_STAMP_RULE: RegExp;
105
111
  * through a confirmed card into the one authenticated edit transaction.
106
112
  */
107
113
  export declare function agentsOver(store: Store, taskId: string, now: Date): Record<string, unknown> | null;
114
+ /**
115
+ * The tools every request advertises (docs/plans/lead-deferred-tools.md): the ones most replies use, and every tool a
116
+ * guard requires read in an earlier step (search_project_memory before any proposal, get_capabilities before work that
117
+ * needs an agent, get_result before propose_review, get_decision before propose_answer). The rest are found with
118
+ * find_tools and advertised from the next request of the same reply.
119
+ */
120
+ export declare const LEAD_CORE_TOOLS: ReadonlySet<string>;
121
+ /** At most this many find_tools searches per reply, results per search, and bytes of found tool schemas per reply. */
122
+ export declare const FIND_TOOLS_PER_TURN = 3;
123
+ export declare const FIND_TOOLS_MAX_RESULTS = 6;
124
+ export declare const FIND_TOOLS_SCHEMA_BYTES = 48000;
125
+ /** The deferred tools a query finds, best first: an exact name, then words in the name, keywords and description. */
126
+ export declare function findLeadTools(query: string): {
127
+ name: string;
128
+ summary: string;
129
+ }[];
108
130
  /** The lead's tools, in the order the model is shown them: each one's inputSchema is its contract, derived. */
109
131
  export declare const LEAD_TOOLS: LeadTool[];
110
132
  /** A unified diff, file by file, with each file's added and removed lines. */