@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,4 +1,11 @@
1
- import { type FlowStep, type RunRecord } from "./store.ts";
1
+ import { type SubagentSpec } from "./subagents.ts";
2
+ import { type FlowStep, type RunRecord, type StepRecord } from "./store.ts";
3
+ import { type Catalog } from "./catalog.ts";
4
+ import { type RuntimeSpec } from "./runtime.ts";
5
+ import { type LanguageChoice } from "./language.ts";
6
+ import { type RegionChoice } from "./locale.ts";
7
+ import { type ClockChoice } from "./clock.ts";
8
+ export { localDate } from "./clock.ts";
2
9
  /** How much of the earlier groups' output a step is handed, in characters.
3
10
  * Newest groups are kept when the total is over it. */
4
11
  export declare const CONTEXT_CHARS = 30000;
@@ -12,6 +19,17 @@ export declare const DESK_RUNS_CAP = 100;
12
19
  * step is most likely to be about.
13
20
  */
14
21
  export declare function joinEarlierGroups(groupResults: (string | null)[], gi: number, cap?: number): string | null;
22
+ /**
23
+ * `parallel: N` — a pool of N slots the instances of one fan-out share.
24
+ * Fan-out always ran every instance at once, and still does unless the
25
+ * step says otherwise: twenty browser steps against one site, or one
26
+ * vendor's rate limit, is what the cap is for. Nothing is rented while an
27
+ * instance waits for a slot.
28
+ */
29
+ export declare function slotPool(size: number): {
30
+ acquire: () => Promise<void>;
31
+ release: () => void;
32
+ };
15
33
  export interface DiscoveredSkill {
16
34
  name: string;
17
35
  description: string;
@@ -37,20 +55,73 @@ export declare function discoverSkills(agentDir: string, subdir?: string): Disco
37
55
  * Returns null rather than throwing on unparseable YAML: a broken shared file
38
56
  * must not take down every agent under it.
39
57
  */
58
+ /**
59
+ * AGENTS.md without the block a coding tool reads.
60
+ *
61
+ * AGENTS.md is two things in a foldrun folder: the account's (or
62
+ * workspace's) own context, which every running agent is given, and — the
63
+ * convention Next.js set — the file a coding agent (Claude Code, Cursor,
64
+ * Codex) reads before it edits the folder. The CLI writes a short managed
65
+ * block for the second reader between these markers; it is about editing
66
+ * the files, not about doing the work, so no running agent is shown it.
67
+ */
68
+ export declare const AGENT_RULES_START = "<!-- BEGIN:foldrun-agent-rules -->";
69
+ export declare const AGENT_RULES_END = "<!-- END:foldrun-agent-rules -->";
70
+ export declare function withoutAgentRules(body: string): string;
40
71
  export declare function readAgentsMd(dir: string): {
41
72
  data: Record<string, unknown>;
42
73
  body: string;
43
74
  } | null;
44
75
  /**
45
- * The calendar an agent works to. `timezone:` in AGENTS.md, nearest-wins like
46
- * every other key there; a flow's cron already means its own timezone, this
47
- * makes "today" mean it too. Unset is UTC — what the pods run — which for a
48
- * Sydney desk made an article dated today "one day in the future" until 10am.
49
- * An unknown name falls back to UTC rather than failing every step.
76
+ * The runtime one agent's steps are built with: its own declaration (else its
77
+ * workspace's), merged with every script tool it grants. The runner builds
78
+ * exactly this for a step; the platform builds it ahead of time when the
79
+ * workspace is deployed, so a package that will not install is reported at
80
+ * deploy and a scheduled step finds its environment ready. One function, so
81
+ * the two can never disagree about what an agent needs.
82
+ */
83
+ export declare function agentRuntimeSpec(tenant: string, workspace: string, agentDir: string): RuntimeSpec | null;
84
+ /** Every distinct runtime a workspace's agents need, with who needs it. */
85
+ export declare function workspaceRuntimePlan(tenant: string, workspace: string): {
86
+ fingerprint: string;
87
+ spec: RuntimeSpec;
88
+ agents: string[];
89
+ }[];
90
+ /**
91
+ * The calendar a set of frontmatter asks for, with no cascade around it —
92
+ * one merged record in, one zone out. The full cascade lives in
93
+ * `agentClock` below and in clock.ts; this is what the account-level parts
94
+ * of the platform (a wallet's month, a budget window) call.
50
95
  */
51
96
  export declare function resolveTimezone(front: Record<string, unknown>): string;
52
- /** YYYY-MM-DD in a timezone — the date the workspace believes it is. */
53
- export declare function localDate(timeZone: string, now?: Date): string;
97
+ /**
98
+ * The clock this step works to: agent, then flow, then workspace, then
99
+ * account, then FOLDRUN_TIMEZONE, then UTC. Nearest wins, and a value that
100
+ * is not a zone falls through to the next level with a line to say so —
101
+ * see clock.ts for why this exists at all.
102
+ */
103
+ export declare function agentClock(agentDir: string, tenant: string, agentFront: Record<string, unknown>, flowTimezone?: string | null): ClockChoice;
104
+ /**
105
+ * The language this step works in: agent, then workspace, then account, then
106
+ * FOLDRUN_LANGUAGE, then English. The flow level is not read yet — a flow's
107
+ * timezone: travels through three signatures to get here, and this will
108
+ * take the same road when a flow needs it.
109
+ */
110
+ export declare function agentLanguage(agentDir: string, tenant: string, agentFront: Record<string, unknown>): LanguageChoice;
111
+ /**
112
+ * The country this step works for, on the same road as the language, then
113
+ * FOLDRUN_REGION, then whatever the language tag carried (`en-AU` says AU).
114
+ * Everything a country changes — currency, units, calendar, weekend, text
115
+ * direction — is derived from it in locale.ts; the three overrides
116
+ * (`currency:`, `calendar:`, `units:`) are read at the nearest level that
117
+ * wrote them.
118
+ */
119
+ export declare function agentRegion(agentDir: string, tenant: string, agentFront: Record<string, unknown>, languageTag: string): RegionChoice;
120
+ export declare function agentLocaleOverrides(agentDir: string, tenant: string, agentFront: Record<string, unknown>): {
121
+ currency?: unknown;
122
+ calendar?: unknown;
123
+ units?: unknown;
124
+ };
54
125
  /**
55
126
  * The prose from AGENTS.md, outermost first.
56
127
  *
@@ -58,6 +129,23 @@ export declare function localDate(timeZone: string, now?: Date): string;
58
129
  * template as "context every agent here shares" — and never read. Only the
59
130
  * frontmatter was, so the instructions people actually wrote reached no model.
60
131
  */
132
+ /**
133
+ * The sub-agents a step may delegate to (`subagents:`), as the SDK will get
134
+ * them: each resolved through agentContext exactly as its own step would be
135
+ * (grants only — no runtime; it runs inside its parent's), then cut to what
136
+ * the parent holds. Problems are lines on the step, never a failed step.
137
+ */
138
+ export declare function buildSubagentSpecs(o: {
139
+ names: unknown;
140
+ self: string;
141
+ workspaceRoot: string;
142
+ tenant: string;
143
+ tags?: string[];
144
+ runId?: string;
145
+ flowTimezone?: string | null;
146
+ parentAllowed: string[];
147
+ push: (type: "info" | "error", text: string) => void;
148
+ }): SubagentSpec[];
61
149
  export declare function sharedInstructions(agentDir: string, tenant: string): string | null;
62
150
  /**
63
151
  * Which of an agent's in-scope skills load for this run.
@@ -102,6 +190,47 @@ export declare function applicableSkills<T extends {
102
190
  */
103
191
  export declare function saysUntilMarker(result: string | null, until: string): boolean;
104
192
  export declare function resolveDocLinks(text: string, workspaceRoot: string): string;
193
+ export declare function repriced(catalog: Catalog | null, model: string, usage: {
194
+ inputTokens: number;
195
+ outputTokens: number;
196
+ } | null, sdkCost: number | null, push: (type: "info" | "error", text: string) => void,
197
+ /** The step's turns each priced at its own model — set when a sub-agent
198
+ * ran on a different model (ExecOutcome.turnsCostUsd). The lump `usage`
199
+ * priced at the step's model would bill those turns at the wrong rate. */
200
+ turnsCostUsd?: number | null): number | null;
201
+ /**
202
+ * Write one attempt onto the step's history and re-total the step.
203
+ *
204
+ * runStep reports the attempt it just ran — its cost, tokens and sandbox
205
+ * seconds — by assigning the step's fields. Those become this attempt's row;
206
+ * the step's fields then become the sum of every row, which is what the
207
+ * run's spend, the flow's budget: and the platform's bill read. A retried
208
+ * step used to carry only its last attempt's cost, so a `retry: 2` step
209
+ * could spend three shares while reporting one.
210
+ */
211
+ export declare function recordAttempt(step: StepRecord,
212
+ /** The step's attempt (`attempts`) these rows belong to. */
213
+ attempt: number, status: "completed" | "failed", startedAt: string, eventsBefore: number,
214
+ /** The attempt re-attached to a sandbox a previous driver left: same
215
+ * attempt, so its row is replaced rather than repeated. */
216
+ reattach?: boolean): void;
217
+ /**
218
+ * Did this attempt call its tools and then fail only on the check after —
219
+ * `verify:`, `output: json`, a schema? For a step whose tools act outward
220
+ * that is the one failure a retry must not answer: the email went, the post
221
+ * is up, and the check failing says nothing about whether they happened. A
222
+ * second attempt sends them again. Read off the attempt's own events, which
223
+ * both executors write the same way (step-exec.ts): every event a failed
224
+ * check writes carries `check: true`. It used to read only the LAST error's
225
+ * wording, and a failed shell verify or judge writes its detail after its
226
+ * headline — so the step was retried and the email went twice. The wording
227
+ * is still honoured, for a driver image older than the tag.
228
+ */
229
+ export declare function actedThenFailedCheck(events: {
230
+ type: string;
231
+ text: string;
232
+ check?: boolean;
233
+ }[]): boolean;
105
234
  /**
106
235
  * Copy a directory tree by reading and writing the bytes.
107
236
  *
@@ -115,6 +244,13 @@ export declare function resolveDocLinks(text: string, workspaceRoot: string): st
115
244
  * happened to create it.
116
245
  */
117
246
  export declare function copyTreeBytes(from: string, to: string): void;
247
+ /**
248
+ * Wait, briefly, for the mounted credential to become one we have not
249
+ * already been refused on. Returns the new value, or null when the window
250
+ * passes without a change — which is the answer that means "this key is
251
+ * wrong", not "this key is stale".
252
+ */
253
+ export declare function awaitRotatedCredential(before: string | undefined, now?: () => number, read?: () => string | undefined, waitMs?: number, pollMs?: number): Promise<string | null>;
118
254
  /** How many times a busy provider is given another moment, and the longest
119
255
  * any one wait may be. Three is the industry's habit and the point where
120
256
  * waiting longer stops being kinder than failing. */
@@ -132,8 +268,41 @@ export declare const OVERLOAD_RETRIES = 3;
132
268
  */
133
269
  export declare function isTransientOverload(text: string): boolean;
134
270
  export declare function backoffMs(attempt: number, text?: string, random?: () => number): number;
135
- export declare function createFlowRun(tenant: string, workspace: string, steps: FlowStep[], flowName: string, status: "queued" | "running", tags?: string[]): RunRecord;
136
- export declare function startFlowRun(tenant: string, workspace: string, steps: FlowStep[], flowName: string, modelOverride?: string | null, tags?: string[], effortOverride?: string | null): RunRecord;
271
+ /**
272
+ * The platform's model credential, read at the moment a step needs it.
273
+ *
274
+ * It used to come only from this process's environment, which meant the
275
+ * worker held whatever token it was started with. Refreshing an OAuth token
276
+ * REVOKES the old one, so a refresh without a restart left every run
277
+ * answering 401 — on 2026-09-18 that took every desk down for two hours, and
278
+ * nothing said so until a person read a failed run. The refresher's own
279
+ * comment had named the fix years of incidents earlier: stop shipping the
280
+ * token as an env var.
281
+ *
282
+ * So: when `FOLDRUN_MODEL_KEY_FILE` names a file (the Kubernetes secret,
283
+ * mounted rather than injected), it is read fresh every time. The kubelet
284
+ * updates a mounted secret in place within about a minute of it changing, so
285
+ * a refreshed token reaches the next step on its own, with no restart, no
286
+ * timing window, and nothing to skip while work is in flight.
287
+ *
288
+ * A missing or empty file falls back to the environment, which is what a
289
+ * local install, the CLI and every compose setup use.
290
+ */
291
+ export declare function platformModelCredential(env?: NodeJS.ProcessEnv): string | undefined;
292
+ export declare function platformModelEnv(): Record<string, string | undefined>;
293
+ /**
294
+ * `schema: <path>` — the file, read and parsed, or null. Agent-relative like
295
+ * every other path in a flow (`workspace/schemas/lead.json`), confined to the
296
+ * workspace: a schema is the author's document, never something a step
297
+ * can point outside.
298
+ */
299
+ export declare function readSchemaFile(workspaceRoot: string, rel: string): Record<string, unknown> | boolean | null;
300
+ export declare function createFlowRun(tenant: string, workspace: string, steps: FlowStep[], flowName: string, status: "queued" | "running", tags?: string[], startedBy?: string | null, opts?: {
301
+ test?: boolean;
302
+ }): RunRecord;
303
+ export declare function startFlowRun(tenant: string, workspace: string, steps: FlowStep[], flowName: string, modelOverride?: string | null, tags?: string[], effortOverride?: string | null, startedBy?: string | null, opts?: {
304
+ test?: boolean;
305
+ }): RunRecord;
137
306
  /**
138
307
  * Drive a run to completion: groups in order, approval gates, retries.
139
308
  *