@foldrun/core 0.3.0 → 0.5.0

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 (175) hide show
  1. package/README.md +27 -10
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +3 -0
  4. package/dist/src/account.d.ts +45 -0
  5. package/dist/src/account.js +130 -0
  6. package/dist/src/api-tools.d.ts +8 -0
  7. package/dist/src/api-tools.js +22 -4
  8. package/dist/src/approvals.d.ts +104 -1
  9. package/dist/src/approvals.js +192 -2
  10. package/dist/src/browser-pod.d.ts +129 -0
  11. package/dist/src/browser-pod.js +284 -0
  12. package/dist/src/budget.d.ts +32 -0
  13. package/dist/src/budget.js +177 -0
  14. package/dist/src/catalog.js +3 -1
  15. package/dist/src/clock.d.ts +49 -0
  16. package/dist/src/clock.js +121 -0
  17. package/dist/src/completions.js +17 -8
  18. package/dist/src/confine.d.ts +22 -0
  19. package/dist/src/confine.js +224 -4
  20. package/dist/src/deploy.d.ts +33 -0
  21. package/dist/src/deploy.js +145 -6
  22. package/dist/src/egress.d.ts +26 -2
  23. package/dist/src/egress.js +8 -1
  24. package/dist/src/eol.d.ts +2 -0
  25. package/dist/src/eol.js +7 -0
  26. package/dist/src/evals.d.ts +9 -0
  27. package/dist/src/evals.js +26 -13
  28. package/dist/src/flow-lint.d.ts +12 -0
  29. package/dist/src/flow-lint.js +331 -1
  30. package/dist/src/flow-patterns.d.ts +141 -0
  31. package/dist/src/flow-patterns.js +568 -0
  32. package/dist/src/gitrepo.d.ts +18 -0
  33. package/dist/src/gitrepo.js +49 -0
  34. package/dist/src/host-env.d.ts +8 -0
  35. package/dist/src/host-env.js +48 -0
  36. package/dist/src/json-schema.d.ts +12 -0
  37. package/dist/src/json-schema.js +202 -0
  38. package/dist/src/kinds.js +1 -1
  39. package/dist/src/language.d.ts +17 -0
  40. package/dist/src/language.js +55 -0
  41. package/dist/src/layout.d.ts +69 -0
  42. package/dist/src/layout.js +200 -0
  43. package/dist/src/library.d.ts +9 -0
  44. package/dist/src/library.js +43 -4
  45. package/dist/src/limits.d.ts +84 -0
  46. package/dist/src/limits.js +287 -0
  47. package/dist/src/locale.d.ts +47 -0
  48. package/dist/src/locale.js +177 -0
  49. package/dist/src/mail.d.ts +88 -0
  50. package/dist/src/mail.js +245 -0
  51. package/dist/src/model-probe.d.ts +3 -0
  52. package/dist/src/model-probe.js +1 -0
  53. package/dist/src/notify.d.ts +132 -8
  54. package/dist/src/notify.js +360 -58
  55. package/dist/src/openapi.js +5 -3
  56. package/dist/src/operator.d.ts +201 -0
  57. package/dist/src/operator.js +251 -0
  58. package/dist/src/paths.d.ts +33 -0
  59. package/dist/src/paths.js +87 -0
  60. package/dist/src/platform.d.ts +144 -4
  61. package/dist/src/platform.js +27 -3
  62. package/dist/src/provider-health.d.ts +55 -0
  63. package/dist/src/provider-health.js +233 -0
  64. package/dist/src/providers.d.ts +481 -0
  65. package/dist/src/providers.js +1105 -6
  66. package/dist/src/refusal.d.ts +83 -0
  67. package/dist/src/refusal.js +281 -0
  68. package/dist/src/run-container.d.ts +206 -17
  69. package/dist/src/run-container.js +547 -80
  70. package/dist/src/runner.d.ts +179 -10
  71. package/dist/src/runner.js +1423 -169
  72. package/dist/src/runtime.d.ts +52 -0
  73. package/dist/src/runtime.js +493 -58
  74. package/dist/src/script-tools.d.ts +15 -2
  75. package/dist/src/script-tools.js +44 -4
  76. package/dist/src/secret-files.d.ts +6 -5
  77. package/dist/src/secret-files.js +24 -11
  78. package/dist/src/secret-health.d.ts +83 -0
  79. package/dist/src/secret-health.js +177 -0
  80. package/dist/src/secrets.d.ts +38 -1
  81. package/dist/src/secrets.js +197 -96
  82. package/dist/src/starter.d.ts +9 -0
  83. package/dist/src/starter.js +95 -11
  84. package/dist/src/step-exec.d.ts +106 -1
  85. package/dist/src/step-exec.js +558 -85
  86. package/dist/src/step-removal.d.ts +15 -0
  87. package/dist/src/step-removal.js +49 -0
  88. package/dist/src/storage.d.ts +23 -4
  89. package/dist/src/storage.js +125 -23
  90. package/dist/src/store.d.ts +492 -38
  91. package/dist/src/store.js +882 -69
  92. package/dist/src/subagents.d.ts +75 -0
  93. package/dist/src/subagents.js +149 -0
  94. package/dist/src/tar.js +2 -1
  95. package/dist/src/test-mode.d.ts +111 -0
  96. package/dist/src/test-mode.js +285 -0
  97. package/dist/src/tool-names.d.ts +13 -1
  98. package/dist/src/tool-names.js +38 -11
  99. package/dist/src/tool-programs.d.ts +26 -0
  100. package/dist/src/tool-programs.js +172 -0
  101. package/dist/src/tool-test.js +152 -33
  102. package/dist/src/translator.d.ts +8 -2
  103. package/dist/src/translator.js +71 -24
  104. package/dist/src/trigger-log.d.ts +43 -0
  105. package/dist/src/trigger-log.js +122 -0
  106. package/dist/src/triggers.d.ts +23 -0
  107. package/dist/src/triggers.js +101 -12
  108. package/dist/src/untrusted-fetch.d.ts +11 -0
  109. package/dist/src/untrusted-fetch.js +77 -0
  110. package/dist/src/web-actions.d.ts +91 -0
  111. package/dist/src/web-actions.js +199 -0
  112. package/dist/src/webhook.d.ts +19 -3
  113. package/dist/src/webhook.js +50 -11
  114. package/index.ts +3 -0
  115. package/package.json +5 -5
  116. package/src/account.ts +149 -0
  117. package/src/api-tools.ts +32 -4
  118. package/src/approvals.ts +253 -2
  119. package/src/browser-pod.ts +313 -0
  120. package/src/budget.ts +178 -0
  121. package/src/catalog.ts +3 -1
  122. package/src/clock.ts +149 -0
  123. package/src/completions.ts +17 -8
  124. package/src/confine.ts +224 -4
  125. package/src/deploy.ts +137 -6
  126. package/src/egress.ts +32 -4
  127. package/src/eol.ts +7 -0
  128. package/src/evals.ts +36 -11
  129. package/src/flow-lint.ts +371 -1
  130. package/src/flow-patterns.ts +597 -0
  131. package/src/gitrepo.ts +48 -0
  132. package/src/host-env.ts +51 -0
  133. package/src/json-schema.ts +184 -0
  134. package/src/kinds.ts +1 -1
  135. package/src/language.ts +62 -0
  136. package/src/layout.ts +228 -0
  137. package/src/library.ts +50 -4
  138. package/src/limits.ts +290 -0
  139. package/src/locale.ts +217 -0
  140. package/src/mail.ts +316 -0
  141. package/src/model-probe.ts +4 -0
  142. package/src/notify.ts +391 -44
  143. package/src/openapi.ts +5 -3
  144. package/src/operator.ts +300 -0
  145. package/src/paths.ts +83 -0
  146. package/src/platform.ts +158 -5
  147. package/src/provider-health.ts +275 -0
  148. package/src/providers.ts +1368 -6
  149. package/src/refusal.ts +320 -0
  150. package/src/run-container.ts +611 -76
  151. package/src/runner.ts +1463 -165
  152. package/src/runtime.ts +487 -55
  153. package/src/script-tools.ts +55 -6
  154. package/src/secret-files.ts +22 -10
  155. package/src/secret-health.ts +230 -0
  156. package/src/secrets.ts +191 -95
  157. package/src/starter.ts +96 -11
  158. package/src/step-exec.ts +548 -41
  159. package/src/step-removal.ts +59 -0
  160. package/src/storage.ts +149 -24
  161. package/src/store.ts +1227 -74
  162. package/src/subagents.ts +189 -0
  163. package/src/tar.ts +2 -1
  164. package/src/test-mode.ts +311 -0
  165. package/src/tool-names.ts +40 -11
  166. package/src/tool-programs.ts +179 -0
  167. package/src/tool-test.ts +159 -32
  168. package/src/translator.ts +74 -23
  169. package/src/trigger-log.ts +160 -0
  170. package/src/triggers.ts +102 -12
  171. package/src/untrusted-fetch.ts +88 -0
  172. package/src/web-actions.ts +237 -0
  173. package/src/webhook.ts +58 -11
  174. package/templates/hello/agents/curator/agent.md +1 -1
  175. package/templates/hello/agents/notetaker/agent.md +1 -1
@@ -1,13 +1,15 @@
1
1
  import { type RunRecord } from "./store.ts";
2
2
  /**
3
- * The platform's own mail: an invite, a low balance.
3
+ * The platform's own mail: an invite, a password reset, a low balance.
4
4
  *
5
- * These are from foldrun, about the platform, to the account's owner — so
6
- * they go through the platform's Resend key and sender (FOLDRUN_RESEND_API_KEY,
7
- * FOLDRUN_EMAIL_FROM), never through anything the customer configured.
8
- *
9
- * Without a platform key — the CLI on a laptop, a test — the account's own
10
- * key and EMAIL_FROM are the fallback.
5
+ * An account that configured its own mail — RESEND_API_KEY and EMAIL_FROM
6
+ * on the account — gets ALL of its mail from that sender, these included:
7
+ * a business that sends from its own domain does not want a second, foreign
8
+ * address turning up in its people's inboxes (2026-09-17, an owner found
9
+ * foldrun.io mail beside their own and asked for none of it). Only an
10
+ * account with no sender of its own falls to the platform's key and sender
11
+ * (FOLDRUN_RESEND_API_KEY, FOLDRUN_EMAIL_FROM), so invites still work
12
+ * before anyone has set mail up.
11
13
  */
12
14
  export declare function platformMail(tenant: string): {
13
15
  key: string;
@@ -36,20 +38,102 @@ export interface NotifyConfig {
36
38
  url?: string;
37
39
  email?: string;
38
40
  events: string[];
41
+ /** `signing_secret:` — the NAME of a vault secret, never its value. With
42
+ * one, every webhook carries an HMAC of the body it delivers, so the
43
+ * receiver can tell a real notification from anyone who learned the URL.
44
+ * Inbound hooks have verified a signature since they existed; a delivery
45
+ * going the other way is the same problem in the same shape. */
46
+ signingSecret?: string;
39
47
  }
40
48
  export declare function notifyConfig(tenant: string, workspace: string): NotifyConfig | null;
49
+ /**
50
+ * The headers that prove a delivery came from this install.
51
+ *
52
+ * `x-foldrun-signature` is a hex SHA-256 HMAC over "<timestamp>.<body>",
53
+ * keyed by the named vault secret — the timestamp is inside the signed
54
+ * string so a captured delivery cannot be replayed later with a fresh one.
55
+ * `x-signature` is the plain HMAC of the body beside it, the scheme the
56
+ * inbound `signature: hmac` verifies.
57
+ *
58
+ * No secret named, no headers: signing is opt-in, and a receiver that does
59
+ * not check one is no worse off than before.
60
+ */
61
+ export declare function signatureHeaders(tenant: string, workspace: string, config: NotifyConfig, body: string): Record<string, string>;
62
+ /** When each attempt is made, counted from the one before: at once, then
63
+ * 1m, 5m, 30m, 2h and 6h. Six attempts over about eight and a half hours,
64
+ * then the delivery is given up and stays in the log to be redelivered. */
65
+ export declare const WEBHOOK_BACKOFF_MS: readonly [0, 60000, number, number, number, number];
66
+ /** When attempt `n + 1` is due after attempt `n` (1-based) failed at
67
+ * `at`, or null when `n` was the last. */
68
+ export declare function nextAttemptAt(n: number, at: number): number | null;
69
+ /** The delivery id: sent as X-Foldrun-Delivery on every attempt of one
70
+ * delivery, redeliveries included, so a receiver dedupes on it. */
71
+ export declare function newDeliveryId(): string;
72
+ /** The URL with ${SECRET}s resolved, and how it may be shown: a Slack hook
73
+ * URL is itself a credential, so it is never echoed resolved. */
74
+ export declare function webhookTarget(tenant: string, workspace: string, config?: NotifyConfig | null): {
75
+ url: string | null;
76
+ shown: string;
77
+ };
78
+ export interface WebhookAttempt {
79
+ /** 1-based. */
80
+ n: number;
81
+ at: string;
82
+ /** The receiver's status, or null when none came back (a timeout, DNS). */
83
+ statusCode: number | null;
84
+ durationMs: number;
85
+ /** The first 500 characters of what the receiver answered. */
86
+ response: string | null;
87
+ error: string | null;
88
+ /** A person pressed redeliver or test; not part of the backoff. */
89
+ manual?: boolean;
90
+ }
91
+ /**
92
+ * One attempt at one delivery, against the workspace's notify: as it reads
93
+ * NOW — a URL fixed between attempts is the one the retry goes to. Signed
94
+ * afresh each time (the timestamp is inside the signature, so a stale
95
+ * signature is one a receiver should refuse). Never throws.
96
+ */
97
+ export declare function attemptWebhook(tenant: string, workspace: string, d: {
98
+ id: string;
99
+ event: string;
100
+ body: string;
101
+ attempt: number;
102
+ manual?: boolean;
103
+ }): Promise<WebhookAttempt & {
104
+ ok: boolean;
105
+ }>;
106
+ /** One waiting step's pair of links. */
107
+ export interface StepLinks {
108
+ /** Index into run.steps. */
109
+ step: number;
110
+ agent: string;
111
+ approveUrl: string;
112
+ rejectUrl: string;
113
+ }
41
114
  /**
42
115
  * The approve/reject links for a run waiting on a person, or null when the
43
116
  * install cannot say where it lives. A notification that only says "waiting
44
117
  * for you" sends the reader to find a laptop; one that carries the decision
45
118
  * lets them make it where they read it.
46
119
  *
120
+ * One pair per waiting step, each bound to that step and to a moment (the
121
+ * flow's `approve_within:`, else a week) — see approvals.ts. A link bound
122
+ * to the run alone approved every later gate of the run for as long as it
123
+ * ran. When the notification goes to a named approver — an address in the
124
+ * flow's `approvers:` — the links are bound to that person too, so a
125
+ * forwarded mail decides as nobody. `approveUrl`/`rejectUrl` on the result
126
+ * are the first waiting step's pair, for receivers written against that.
127
+ *
47
128
  * The reject link goes to the same page with the choice pre-selected — a
48
129
  * GET must not decide anything, because inbox link-checkers follow links.
49
130
  */
50
- export declare function approvalLinks(tenant: string, workspace: string, runId: string): {
131
+ export declare function approvalLinks(tenant: string, workspace: string, run: RunRecord,
132
+ /** Who the notification is going to, when it is going to one address. */
133
+ recipient?: string | null): {
51
134
  approveUrl: string;
52
135
  rejectUrl: string;
136
+ links: StepLinks[];
53
137
  } | null;
54
138
  /**
55
139
  * Fire the webhook for a run's state, if the workspace asked to hear about
@@ -58,4 +142,44 @@ export declare function approvalLinks(tenant: string, workspace: string, runId:
58
142
  */
59
143
  /** Flows whose completion is nobody's news: eval cases and adhoc runs. */
60
144
  export declare function isQuietFlow(flow: string): boolean;
145
+ /**
146
+ * A notification that is not about one run finishing.
147
+ *
148
+ * Three things need to say something to the same destination a run
149
+ * notification goes to, and none of them has a RunRecord to hand: a flow
150
+ * that has been quarantined, a run that has outlived its `sla:`, and a
151
+ * budget that is nearly spent. They are the messages a person most needs
152
+ * and least expects, because each one is about something NOT happening —
153
+ * the failure mode that never sends mail is the one that costs a week.
154
+ *
155
+ * The event name is matched against `events:` exactly like a run status,
156
+ * so a workspace that wants none of this writes its own list and gets none
157
+ * of it. They are on by default: an alert nobody opted into is the point.
158
+ */
159
+ export declare function sendPlainNotification(tenant: string, workspace: string, msg: {
160
+ event: string;
161
+ headline: string;
162
+ detail: string;
163
+ flow?: string;
164
+ runId?: string;
165
+ }): Promise<boolean>;
166
+ /**
167
+ * Prove the notification path works, now, while someone is looking.
168
+ *
169
+ * Every run notification is sent when nobody is watching, to a destination
170
+ * nobody has tested, through a mail domain nobody has verified. On
171
+ * 2026-09-06 every one of them had been going to a bin for weeks and the
172
+ * only symptom was silence — which is indistinguishable from nothing having
173
+ * gone wrong. An alert that has never been proven to arrive is not an alert.
174
+ *
175
+ * Unlike every other send here, this one reports its failure in full: the
176
+ * provider's own words, which are what actually name the problem ("domain
177
+ * not verified", "you can only send to your own address"). Swallowing them
178
+ * is right for a run notification and useless for a test.
179
+ */
180
+ export declare function sendTestNotification(tenant: string, workspace: string): Promise<{
181
+ ok: boolean;
182
+ destination: string;
183
+ detail: string;
184
+ }>;
61
185
  export declare function sendRunNotification(tenant: string, workspace: string, run: RunRecord): Promise<boolean>;