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
@@ -6,12 +6,14 @@
6
6
  * waits for people. See flows.ts for what each zone does.
7
7
  */
8
8
  import { assignmentOf } from "./assignment.js";
9
- import { filerFor } from "./approval-policy.js";
10
9
  import { diagnoseTaskDispatch } from "./dispatch.js";
11
10
  import { passOn, TEXT_LIMITS } from "./text-limits.js";
12
11
  import { deciderOf, durationWords, fillFlowText, flowGoal, flowValueLabel, flowWorkTemplate, validateFlowDefinition, withinHours } from "./flows.js";
12
+ import { readSnapshot, referenceWords, snapshotOf } from "./task-source.js";
13
13
  import { reportFillIns, reportSummaryFor } from "./report-summary.js";
14
14
  import { fileTaskProposal } from "./proposal.js";
15
+ import { FLOW_ACCEPTANCE, unattendedCoverageFor } from "./flow-unattended.js";
16
+ import { sealFlowFilingUnderGrant } from "./plan-auto.js";
15
17
  import { requestResultChanges } from "./result-actions.js";
16
18
  import { revisionSourceOf } from "./result-review.js";
17
19
  import { readVerifiedReport, scanForSecrets } from "./evidence.js";
@@ -33,10 +35,7 @@ export function flowDefinitionOf(flow) {
33
35
  export const flowCardHref = (flow, card) => `/flows/${flow}?card=${card}`;
34
36
  /** Where a confirmed flow action may point: a flow's canvas, maybe with one card open. */
35
37
  export const FLOW_HREF = /^\/flows\/[1-9][0-9]{0,9}(\?card=[1-9][0-9]{0,9})?$/;
36
- const ACCEPTANCE = {
37
- report: [{ id: "c1", statement: "The report answers the request, citing where in the repository each point comes from", how: null, evidence: ["manual-review"] }],
38
- task: [{ id: "c1", statement: "The requested change is made and the project's existing checks still pass", how: null, evidence: ["manual-review"] }],
39
- };
38
+ const ACCEPTANCE = FLOW_ACCEPTANCE;
40
39
  function fill(template, card) {
41
40
  return fillFlowText(template, { title: card.title, description: card.description, note: card.note, outputs: card.outputs });
42
41
  }
@@ -266,18 +265,31 @@ function workStage(store, flow, definition, stage, card, now, options, outcome,
266
265
  const fresh = store.getFlowCard(card.id);
267
266
  if (fresh === null || fresh.state !== "active" || fresh.entry !== card.entry || fresh.stage !== stage.id || fresh.task !== null)
268
267
  return null;
268
+ // A card this flow's own schedule added, while the flow runs on its own (flow-unattended.ts): the grant's frozen
269
+ // criteria and budget, sealed here, in the same transaction. Any other card files as it always did and waits.
270
+ const grant = unattendedCoverageFor(store, flow, fresh, repo, now);
271
+ const kind = report ? "report" : "task";
269
272
  const made = fileTaskProposal(store, {
270
273
  title: (report ? `${stage.title}: ${card.title}` : card.title).slice(0, 200),
271
274
  repo,
272
275
  goal,
273
- filedVia: `flow:${flow.id}`, filedBy: filerFor(card.createdBy), // who made the card asked for the work (an owner can be reassigned by anyone)
276
+ filedVia: `flow:${flow.id}`,
277
+ // Who made the card asked for the work (an owner can be reassigned by anyone). v126: an automation's card (a
278
+ // delivery, a poll, a schedule, a form) files as automation, by the kind kept on the card, never by its name.
279
+ filedBy: { name: card.createdBy, kind: card.createdKind },
274
280
  deliverable: report ? "report" : "branch",
275
281
  planning: report ? "skip" : stage.planning ?? "auto",
276
- acceptance: report ? ACCEPTANCE.report : ACCEPTANCE.task,
282
+ acceptance: grant === null ? ACCEPTANCE[kind] : grant.terms.acceptance[kind],
283
+ ...(grant?.terms.perAttemptBudgetMicrousd == null ? {} : { budgetMicrousd: grant.terms.perAttemptBudgetMicrousd }),
277
284
  admittedRepos: [repo],
278
285
  }, now);
286
+ // v126: a card an outside event's delivery made gives its task a snapshot of where it came from, in this transaction.
287
+ const snapshot = made.ok ? snapshotOf(fresh.source) : null;
288
+ if (made.ok && snapshot !== null)
289
+ store.recordTaskSource(made.id, { flow: flow.id, card: fresh.id, entry: fresh.entry }, JSON.stringify(snapshot), now);
290
+ const ranOnItsOwn = made.ok && grant !== null && sealFlowFilingUnderGrant(store, made.id, grant, fresh, now);
279
291
  if (made.ok)
280
- store.updateFlowCard(card.id, { task: made.id, ...(report || fresh.primaryTask !== null ? {} : { primaryTask: made.id }), waiting: "Filed as a task" }, now);
292
+ store.updateFlowCard(card.id, { task: made.id, ...(report || fresh.primaryTask !== null ? {} : { primaryTask: made.id }), waiting: ranOnItsOwn ? "Filed as a task, approved: this flow runs on its own" : "Filed as a task" }, now);
281
293
  return made;
282
294
  });
283
295
  if (filed === null)
@@ -354,12 +366,24 @@ function workStage(store, flow, definition, stage, card, now, options, outcome,
354
366
  * 64 KiB), so in practice an attachment is never shortened. */
355
367
  const FLOW_ATTACHED_LIMIT = 128_000;
356
368
  export function flowGoalCuts(store, taskId, goal) {
369
+ let cuts = [];
357
370
  try {
358
- return goalCuts(store, taskId, goal);
371
+ cuts = goalCuts(store, taskId, goal);
359
372
  }
360
373
  catch {
361
- return [];
374
+ cuts = [];
362
375
  } // context, never a reason a build can't start
376
+ // v126: where the work came from, from the task's own kept source (or the task it revises) — never from the card's
377
+ // current task link or zone, which move on. Given whole whenever the goal itself couldn't carry it.
378
+ try {
379
+ const kept = store.taskSourceOf(taskId);
380
+ const snapshot = kept === null ? null : readSnapshot(kept.sourceJson);
381
+ const words = snapshot === null ? "" : referenceWords(snapshot);
382
+ if (words !== "" && !goal.includes(words) && !cuts.some(one => one.text === words))
383
+ cuts.push({ label: flowValueLabel("card.source"), text: words });
384
+ }
385
+ catch { /* context, never a reason a build can't start */ }
386
+ return cuts;
363
387
  }
364
388
  function goalCuts(store, taskId, goal) {
365
389
  const root = store.taskFamilyOf(taskId, null, true)?.root.id ?? taskId;
@@ -413,7 +437,7 @@ export function addCardToFlow(store, flow, input, actor, now) {
413
437
  if ("problem" in text)
414
438
  return { ok: false, message: text.problem };
415
439
  const stage = definition.stages.find(one => one.id === input.stage) ?? definition.stages.find(one => one.id === definition.start);
416
- const card = store.addFlowCard({ flow: flow.id, title: text.title, description: text.description, stage: stage.id, by: actor }, now);
440
+ const card = store.addFlowCard({ flow: flow.id, title: text.title, description: text.description, stage: stage.id, by: actor, byKind: "person" }, now);
417
441
  // Whoever adds a card follows it; owning it is a separate, deliberate step.
418
442
  store.setFlowCardWatcher(card, actor, true, now);
419
443
  return { ok: true, said: `Card added to ${zoneWords(stage.title)}`, card };
@@ -7,7 +7,7 @@
7
7
  import { brandMarkHtml } from "./brand-mark.js";
8
8
  import { BLANK, GALLERY, GALLERY_GROUPS, galleryDiagram, galleryToolsOf, OUTDATED_COMMANDS, SEND_RESULT, sendsAlready } from "./flow-gallery.js";
9
9
  import { choiceTargets } from "./flows.js";
10
- import { html, joinHtml, postForm } from "./html.js";
10
+ import { html, joinHtml, postForm, reauth } from "./html.js";
11
11
  export const GALLERY_CSS = `.gallery{max-width:1120px;min-width:0}.gallery h2{margin:28px 0 10px;font-size:.9375rem}.gallery h2:first-of-type{margin-top:8px}` +
12
12
  `.gallery-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:12px}` +
13
13
  `.gallery-card{display:flex;flex-direction:column;gap:8px;padding:16px;border:1px solid var(--so-line);border-radius:10px;background:var(--so-paper);min-width:0}` +
@@ -89,7 +89,7 @@ export function galleryUseHtml(input) {
89
89
  if (tool.state === "connected" || tool.state === "taken" || input.csrf === "")
90
90
  return html `<div class="gallery-connect" data-tool="${tool.id}" data-state="${String(tool.state)}">${head}</div>`;
91
91
  const how = tool.id === "figma-desktop" ? "Open the Figma desktop app and turn on its Dev Mode MCP server in Preferences." : `You sign in on ${tool.label}, then come back here.`;
92
- return html `<div class="gallery-connect" data-tool="${tool.id}" data-state="open">${head}${postForm("/settings/tools/connect", html `<p class="meta">${how}</p><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label><button name="service" value="${tool.id}">Connect ${tool.label}</button>`, { hidden: { repo: input.repo, shown: input.repo, template: template.id } })}</div>`;
92
+ return html `<div class="gallery-connect" data-tool="${tool.id}" data-state="open">${head}${postForm("/settings/tools/connect", html `<p class="meta">${how}</p>${reauth(html `<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label>`)}<button name="service" value="${tool.id}">Connect ${tool.label}</button>`, { hidden: { repo: input.repo, shown: input.repo, template: template.id } })}</div>`;
93
93
  };
94
94
  const toolsBlock = tools.length === 0 ? "" : html `<section class="gallery-tools-use" aria-label="Tools">${tools.map(connect)}</section>`;
95
95
  // Made without a tool, the preview names the zone that needs it.
@@ -266,10 +266,26 @@ export const GALLERY = [
266
266
  does: ["Checks for outdated {{ask.outdated}} packages {{ask.schedule-words}}, with no AI.", "When some are out of date, updates them as a task, under your usual approvals.", "What you approve opens a pull request."],
267
267
  never: NEVER_MERGES,
268
268
  },
269
+ {
270
+ // v126: the direct road from a signed outside request to a build that waits for your approval.
271
+ id: "webhook-to-build", name: "Webhook to build", group: "health",
272
+ promise: "A signed request from any tool becomes a build, under your usual approvals.",
273
+ needs: ["A tool that can send signed webhooks", "GitHub"], asks: [],
274
+ steps: [
275
+ { id: "build", title: "Build it", kind: "task", instructions: work("{{card.title}}\n\n{{card.description}}") },
276
+ owner("review", "Review the build"),
277
+ { ...pr, next: "Done" },
278
+ { id: "done", title: "Done", kind: "done" },
279
+ ],
280
+ triggers: [{ kind: "webhook", sender: "standard", title: "Request" }],
281
+ does: ["Adds a webhook signed the Standard Webhooks way. Once the flow is made, open its Triggers and choose Set up under Signing: it shows the address and the secret together, once; give both to the sender.",
282
+ "Each signed request becomes a card and is built as a task. The task waits for your approval, like any other.", "You review the build; what you approve opens a pull request."],
283
+ never: NEVER_MERGES,
284
+ },
269
285
  {
270
286
  id: "error-to-fix", name: "Error to fix", group: "health", tools: ["sentry"],
271
- promise: "New errors from Sentry or any error tracker are sorted, researched and fixed.",
272
- needs: ["A webhook from Sentry or another tracker", SORTS], asks: [],
287
+ promise: "New Sentry errors are sorted, researched and fixed.",
288
+ needs: ["A Sentry Internal Integration", SORTS], asks: [],
273
289
  steps: [
274
290
  { id: "worth-fixing", title: "Worth fixing?", kind: "sort", question: "Is this error in our own code, and new or frequent enough to fix now?",
275
291
  answers: [
@@ -284,8 +300,9 @@ export const GALLERY = [
284
300
  { id: "done", title: "Done", kind: "done" },
285
301
  { id: "ignored", title: "Ignored", kind: "done" },
286
302
  ],
287
- triggers: [{ kind: "webhook", title: "Error", titleField: "data.issue.title", bodyField: "data.issue.culprit" }],
288
- does: ["Adds a webhook. Once the flow is made, open its Triggers and choose New address, then paste that into Sentry or another error tracker.", "Jev keeps errors in your code that are new or frequent, and drops noise; ones it isn't sure about wait for you.",
303
+ // v126: signed by Sentry and read by Toolroll's Sentry reader: one card per issue, linked to it.
304
+ triggers: [{ kind: "webhook", sender: "sentry", title: "Error" }],
305
+ does: ["Adds a Sentry webhook. Once the flow is made, open its Triggers and choose New address; in Sentry create an Internal Integration (Settings → Developer Settings), paste that address as its Webhook URL, turn on Alert Rule Action and Issue events, then paste its Client Secret under Signing on the flow's Triggers.", "One card per Sentry issue, however often it alerts, linked to the issue.", "Jev keeps errors in your code that are new or frequent, and drops noise; ones it isn't sure about wait for you.",
289
306
  "Each kept error is researched, then fixed as a task, under your usual approvals.", "What you approve opens a pull request."],
290
307
  never: NEVER_MERGES,
291
308
  },
@@ -300,7 +317,7 @@ export const GALLERY = [
300
317
  pr,
301
318
  ],
302
319
  triggers: [{ kind: "webhook", title: "Incident", titleField: "data.attributes.name" }],
303
- does: ["Adds a webhook. Once the flow is made, open its Triggers and choose New address, then paste that into Better Stack.", "Reads the incident, its monitor's history and your recent commits and deploys, and sends you the likely cause.", "You choose Fix or Ignore; a fix is built as a task, under your usual approvals, and opens a pull request."],
320
+ does: ["Adds a webhook. Once the flow is made, open its Triggers and choose New address, then paste that into Better Stack. Better Stack doesn't sign its webhooks, so anyone with the address could add cards: keep it private.", "Reads the incident, its monitor's history and your recent commits and deploys, and sends you the likely cause.", "You choose Fix or Ignore; a fix is built as a task, under your usual approvals, and opens a pull request."],
304
321
  never: NEVER_MERGES,
305
322
  },
306
323
  {
@@ -335,8 +352,9 @@ export const GALLERY = [
335
352
  pr,
336
353
  send("Send the fix"),
337
354
  ],
338
- triggers: [{ kind: "webhook", title: "Failed deployment", titleField: "payload.deployment.name" }],
339
- does: ["Adds a webhook. Once the flow is made, open its Triggers and choose New address, then paste that into Vercel for failed deployments.", "Reads the build log and finds the cause.", "Builds a fix as a task, under your usual approvals, opens a pull request and sends it to you."],
355
+ // v126: signed by Vercel and read by Toolroll's Vercel reader: failed deployments only, one card each.
356
+ triggers: [{ kind: "webhook", sender: "vercel", title: "Failed deployment" }],
357
+ does: ["Adds a Vercel webhook. Once the flow is made, open its Triggers and choose New address; in Vercel (Settings → Webhooks) paste that address and choose Deployment Error, then paste the secret Vercel shows under Signing on the flow's Triggers. Put your Vercel project ID in Signing too, so only its deployments add cards.", "Reads the build log and finds the cause.", "Builds a fix as a task, under your usual approvals, opens a pull request and sends it to you."],
340
358
  never: NEVER_MERGES,
341
359
  },
342
360
  // ---------------------------------------------------------- Hear from users
@@ -637,7 +655,8 @@ export function useGalleryTemplate(store, template, repo, given, input) {
637
655
  const id = store.createFlow({ repo, name, definitionJson: JSON.stringify(built.definition), by: input.by }, input.now);
638
656
  const made = store.getFlow(id);
639
657
  for (const trigger of built.triggers) {
640
- const added = addFlowTriggerTo(store, made, { ...trigger, zone: trigger["zone"] ?? flowDefinitionOf(made).start }, input.by, input.now, input.dir);
658
+ // No one is shown the gallery's addresses: a webhook is made with none until its panel makes one (v126).
659
+ const added = addFlowTriggerTo(store, made, { ...trigger, zone: trigger["zone"] ?? flowDefinitionOf(made).start }, input.by, input.now, input.dir, { shown: false });
641
660
  if (!added.ok)
642
661
  throw new Error(added.message);
643
662
  if (added.reveal !== null)
@@ -24,7 +24,7 @@ import { addFlowTriggerTo, describeTrigger, HOOK_PATH, readHooksBase, triggerCon
24
24
  import { durationMinutes, durationWords, FlowContractError, flowTerms, inStepWords, LANGUAGE_WORDS, validateFlowDefinition } from "./flows.js";
25
25
  import { stageReferenceProblems } from "./contracts/stage-output.js";
26
26
  import { readVersioned } from "./contracts/contract.js";
27
- import { FLOW_ALIASES, FLOW_FILE_FORMAT, legacyFlowNeed, legacySubagentStep, FLOW_FILE_VERSION, flowFileSchema, PARAMETER_ID } from "./contracts/flow.js";
27
+ import { FLOW_ALIASES, WEBHOOK_SENDERS, FLOW_FILE_FORMAT, legacyFlowNeed, legacySubagentStep, FLOW_FILE_VERSION, flowFileSchema, PARAMETER_ID } from "./contracts/flow.js";
28
28
  import { parseSchedule } from "./flow-schedule.js";
29
29
  export { FLOW_FILE_FORMAT, FLOW_FILE_VERSION };
30
30
  /** The largest flow file Toolroll reads or fetches. */
@@ -111,7 +111,13 @@ function triggerOf(config, ask) {
111
111
  ...(config.state === null ? {} : { state: config.state }),
112
112
  ...(config.label === null ? {} : { label: ask({ id: "linear-label", about: "The Linear label issues need", default: config.label }) }),
113
113
  delivery: config.delivery, ...zone };
114
- case "webhook": return { kind: "webhook", title: config.title, ...(config.titleField === null ? {} : { titleField: config.titleField }), ...(config.bodyField === null ? {} : { bodyField: config.bodyField }), ...zone };
114
+ // v126: who signs it travels (never its secret or address: an imported signed webhook isn't set up until its
115
+ // secret is made or pasted there); the project and team it is scoped to are asked again, as another install's own.
116
+ case "webhook": return { kind: "webhook", title: config.title, ...(config.titleField == null ? {} : { titleField: config.titleField }), ...(config.bodyField == null ? {} : { bodyField: config.bodyField }),
117
+ ...((config.sender ?? "none") === "none" ? {} : { sender: config.sender }),
118
+ ...(config.project == null ? {} : { project: ask({ id: "webhook-project", about: "The project the signed webhook's deliveries must come from", default: config.project }) }),
119
+ ...(config.team == null ? {} : { team: ask({ id: "webhook-team", about: "The Vercel team the deliveries must come from", default: config.team }) }),
120
+ ...(config.idField == null ? {} : { idField: config.idField }), ...(config.urlField == null ? {} : { urlField: config.urlField }), ...zone };
115
121
  case "email": return { kind: "email", folder: config.folder,
116
122
  ...(config.sender === null ? {} : { sender: ask({ id: "email-sender", about: "Whose mail starts cards: addresses or domains, comma-separated (left empty: anyone's)", optional: true }) }),
117
123
  ...(config.subject === null ? {} : { subject: config.subject }), ...zone };
@@ -192,8 +198,13 @@ function scrubDeep(value, scrub, key, inScripts) {
192
198
  return key !== null && (STRUCTURAL.has(key) || (inScripts && key === "name")) ? value : scrub(value);
193
199
  if (Array.isArray(value))
194
200
  return value.map(one => scrubDeep(one, scrub, key, inScripts));
195
- if (value !== null && typeof value === "object")
196
- return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, scrubDeep(v, scrub, k, inScripts || k === "scripts")]));
201
+ if (value !== null && typeof value === "object") {
202
+ // v126: a webhook's sender is one of a fixed few words (who signs it), never prose: kept as it is, even when an
203
+ // account happens to share its name. An email trigger's sender is people's addresses, and is still scrubbed.
204
+ const record = value;
205
+ const webhookSender = record["kind"] === "webhook" && typeof record["sender"] === "string" && WEBHOOK_SENDERS.includes(record["sender"]);
206
+ return Object.fromEntries(Object.entries(record).map(([k, v]) => [k, webhookSender && k === "sender" ? v : scrubDeep(v, scrub, k, inScripts || k === "scripts")]));
207
+ }
197
208
  return value;
198
209
  }
199
210
  /** A flow as a file: its zones and paths, its triggers' settings and the scripts it runs, nothing of this installation's. */
@@ -530,7 +541,7 @@ export function importFlow(store, plan, actor, now, dir) {
530
541
  }
531
542
  const id = store.createFlow({ repo: plan.repo, name: plan.name, definitionJson: JSON.stringify(plan.definition), by: actor }, now);
532
543
  for (const trigger of plan.triggers) {
533
- const made = addFlowTriggerTo(store, store.getFlow(id), trigger, actor, now, dir);
544
+ const made = addFlowTriggerTo(store, store.getFlow(id), trigger, actor, now, dir, { shown: false });
534
545
  if (!made.ok)
535
546
  throw new FlowFileError(`A ${String(trigger["kind"])} trigger: ${made.message}`);
536
547
  store.updateFlowTrigger(made.id, { state: "paused" }, now);
@@ -1,6 +1,7 @@
1
1
  import type { Runner } from "./backend.js";
2
2
  import { type FlowAct } from "./flow-engine.js";
3
3
  import { type FlowDefinition } from "./flows.js";
4
+ import { type DistinctHeaders, type WebhookSender } from "./webhook-signing.js";
4
5
  import { type ContractResult } from "./contracts/contract.js";
5
6
  import { FLOW_TRIGGER_KINDS, type ChatApp, type FlowTriggerKind, type TriggerConfig, type TriggerInput } from "./contracts/flow.js";
6
7
  import { type MailReader } from "./mailbox.js";
@@ -49,13 +50,20 @@ export declare function triggerHeadline(config: TriggerConfig, store: Store): {
49
50
  export declare function triggerConfigOf(trigger: FlowTriggerRow): TriggerConfig | null;
50
51
  /** A saved trigger's config (`configJson`, which carries no version) read by its one schema, what it leaves out filled. */
51
52
  export declare function readTriggerConfig(raw: unknown): ContractResult<TriggerConfig>;
53
+ /** v126: who signs a webhook trigger's deliveries; one saved before senders were chosen is unsigned. */
54
+ export declare const senderOf: (config: Extract<TriggerConfig, {
55
+ kind: "webhook";
56
+ }>) => WebhookSender;
52
57
  /** Whether this trigger takes deliveries at a secret address rather than being checked. */
58
+ /** Whether a raw trigger the caller is about to add would take deliveries (an address): read leniently, before validation. */
59
+ export declare function rawTakesDeliveries(raw: unknown): boolean;
53
60
  export declare function takesDeliveries(config: TriggerConfig): boolean;
54
- /** A webhook's signing secret (GitHub: made here; Linear: pasted from Linear). 0600, beside the database, never a column. */
55
- export declare function saveHookSecret(dir: string, trigger: number, secret: string): void;
56
- export declare function readHookSecret(dir: string, trigger: number): string | null;
61
+ /** A webhook's signing secret (made here, or pasted from its sender). In the keychain (or an owner-only file where there's none), never a column. */
62
+ export declare function saveHookSecret(dir: string, trigger: number, secret: string, credential?: string): void;
63
+ /** Exactly this credential's secret, or null: never another's. */
64
+ export declare function readHookSecret(dir: string, trigger: number, credential?: string): string | null;
57
65
  export declare const LINEAR_ENV = "TOOLROLL_LINEAR_KEY";
58
- /** The Linear API key the worker checks Linear with: the environment, or a 0600 file beside the database. */
66
+ /** The Linear API key the worker checks Linear with: the environment, or kept by Toolroll (the keychain, or an owner-only file beside the database). */
59
67
  export declare function readLinearKey(dir: string | null): string | null;
60
68
  export declare function saveLinearKey(dir: string, key: string): {
61
69
  ok: true;
@@ -89,20 +97,32 @@ export type TriggerMade = {
89
97
  message: string;
90
98
  };
91
99
  /** Add a trigger to a flow. A webhook trigger's address (and GitHub's signing secret) is returned once and never stored readable. */
92
- export declare function addFlowTriggerTo(store: Store, flow: FlowRow, raw: unknown, actor: string, now: Date, dir: string | null): TriggerMade;
100
+ /**
101
+ * `ingress: "proved"`: the caller proved a password typed now (a credential act, docs/plans/passwordless.md) — only then
102
+ * may a trigger that takes deliveries be made with an address. Without it a webhook is made with none (its panel's
103
+ * set-up, which asks for the password, makes one), and GitHub/Linear webhook delivery is refused.
104
+ */
105
+ export declare function addFlowTriggerTo(store: Store, flow: FlowRow, raw: unknown, actor: string, now: Date, dir: string | null, options?: {
106
+ shown?: boolean;
107
+ ingress?: "proved";
108
+ }): TriggerMade;
93
109
  /** A new secret address for a webhook trigger (the old one stops working at once). */
94
110
  export declare function renewFlowHook(store: Store, trigger: FlowTriggerRow, now: Date, dir: string): TriggerMade;
95
111
  /**
96
112
  * Pause a trigger, or turn it on again. A schedule that never had a time (a routine moved to a flow while it waited
97
113
  * for approval, v115) starts counting from now, as a new schedule does; one that was due while it was off fires once.
98
114
  */
99
- export declare function setFlowTriggerOn(store: Store, trigger: FlowTriggerRow, on: boolean, now: Date): void;
100
- export declare function removeFlowTrigger(store: Store, trigger: FlowTriggerRow, now: Date, dir: string | null): void;
115
+ export declare function setFlowTriggerOn(store: Store, trigger: FlowTriggerRow, on: boolean, now: Date, by?: string): void;
116
+ export declare function removeFlowTrigger(store: Store, trigger: FlowTriggerRow, now: Date, dir: string | null, by?: string): void;
117
+ /** `onTime`: the clock's own firing of a schedule (never Run now or Check now); only those can run on their own.
118
+ * `ranScript`: for a script's, what code it ran (scriptRanDigestOf), which must be the code a grant signed. */
101
119
  export type Incoming = {
102
120
  key: string;
103
121
  title: string;
104
122
  description: string | null;
105
123
  source: FlowCardSource;
124
+ onTime?: boolean;
125
+ ranScript?: string | null;
106
126
  };
107
127
  type Made = "added" | "seen" | "skipped";
108
128
  /** One outside thing into one card through a trigger (a chat channel's message, v89): at most once per key. */
@@ -185,24 +205,77 @@ export declare const LINEAR_QUERY = "query FlowTrigger($filter: IssueFilter) { i
185
205
  export declare function linearItem(config: Extract<TriggerConfig, {
186
206
  kind: "linear";
187
207
  }>, raw: unknown): Found | null;
208
+ /** v126: headers as Node's `headersDistinct` gives them, one value per line they arrived on, so a security header sent
209
+ * twice is seen twice (Node's `headers` joins repeated custom headers with commas). */
188
210
  export type HookDelivery = {
189
- headers: Record<string, string | string[] | undefined>;
211
+ headers: DistinctHeaders;
190
212
  body: Buffer;
191
213
  };
214
+ /** `retryAfter` (seconds): a 429's Retry-After. */
192
215
  export type HookAnswer = {
193
216
  status: number;
194
217
  said: string;
218
+ retryAfter?: number;
195
219
  };
196
220
  /** A webhook delivery at a trigger's secret address: prove it, read it, make at most the cards it names. Nothing runs here. */
197
221
  export declare function receiveFlowHook(store: Store, token: string, delivery: HookDelivery, dir: string, now: Date): HookAnswer;
198
- /** Save Linear's signing secret for a Linear webhook trigger (pasted on the secure panel, never in chat). */
199
- export declare function saveLinearSigningSecret(trigger: FlowTriggerRow, secret: string, dir: string): {
222
+ /**
223
+ * v126: make a trigger's held deliveries into their cards while the last hour has room, oldest first. Each card in its
224
+ * own write transaction, which reads everything it acts on under the write lock: the trigger (still active, still
225
+ * taking deliveries), the hour's room, the oldest held receipt, the flow; then the same guards intake has (a failing
226
+ * run already owned by another card in the project is left out). The card and the receipt's move to accepted land
227
+ * together, or neither does.
228
+ */
229
+ export declare function drainHeldDeliveries(store: Store, trigger: FlowTriggerRow, now: Date): number;
230
+ /**
231
+ * v126: a delivery refused before it could be read (too large, a body that wouldn't read): it proves nothing, so it
232
+ * counts toward its address's minute like any unproven delivery. The answer to give instead when that count is over.
233
+ */
234
+ export declare function hookRefusedUnread(store: Store, token: string, now: Date): HookAnswer | null;
235
+ /** A signing secret pasted on the secure panel behind the password, never in chat: Linear's, or a signed webhook's
236
+ * sender's (Sentry's or Vercel's own, or a Standard or GitHub-style one made elsewhere). */
237
+ export declare function saveSigningSecret(store: Store, trigger: FlowTriggerRow, secret: string, now: Date, dir: string): {
200
238
  ok: true;
239
+ said: string;
240
+ } | {
241
+ ok: false;
242
+ message: string;
243
+ };
244
+ /**
245
+ * v126: a new secret for a webhook whose sender Toolroll makes secrets for (Standard Webhooks, GitHub-style), shown once.
246
+ * Its address stays as it is (New address is its own action). Sentry's and Vercel's secrets are theirs, pasted instead.
247
+ * The only place such a secret is made: creating a trigger (from the panel, the gallery or an import) never does.
248
+ */
249
+ export declare function setUpFlowHook(store: Store, trigger: FlowTriggerRow, now: Date, dir: string): TriggerMade;
250
+ /**
251
+ * v126: change who signs a webhook's deliveries, in place: the trigger keeps its id, address and what it has received
252
+ * (keys carry the sender, so nothing received unsigned stands for anything signed). Its credentials move to the next
253
+ * generation with no secret: it isn't ready until it is set up again. Settings the new sender doesn't read are left out.
254
+ */
255
+ export declare function changeFlowHookSender(store: Store, trigger: FlowTriggerRow, sender: unknown, actor: string, now: Date, dir: string): {
256
+ ok: true;
257
+ said: string;
258
+ } | {
259
+ ok: false;
260
+ message: string;
261
+ };
262
+ /**
263
+ * v126: which project (and, for Vercel, team) a Sentry or Vercel webhook takes deliveries from, set in place: the
264
+ * trigger keeps its id, address, secret and history; blank takes any project the secret covers. Only from the settings
265
+ * read here, so a change racing it doesn't land on top of it.
266
+ */
267
+ export declare function setFlowHookScope(store: Store, trigger: FlowTriggerRow, scope: {
268
+ project: unknown;
269
+ team: unknown;
270
+ }, actor: string, now: Date): {
271
+ ok: true;
272
+ said: string;
201
273
  } | {
202
274
  ok: false;
203
275
  message: string;
204
276
  };
205
- /** Whether a webhook trigger can prove its deliveries: GitHub always (its secret is made here), Linear once its secret is pasted. */
277
+ /** Whether a webhook trigger can prove its deliveries: an unsigned webhook always (its address is all it has), the others
278
+ * once the current generation's secret is saved. */
206
279
  export declare function hookReady(trigger: FlowTriggerRow, dir: string | null): boolean;
207
280
  export declare const FORM_PATH = "/hooks/form/";
208
281
  /** Share a button as a secret link anyone can fill in, without signing in. The link is shown once; only its hash is kept. */