@foldrun/core 0.4.0 → 0.6.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 (170) hide show
  1. package/README.md +7 -4
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.js +4 -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/browser-pod.d.ts +148 -0
  9. package/dist/src/browser-pod.js +333 -0
  10. package/dist/src/catalog.js +2 -1
  11. package/dist/src/clock.d.ts +49 -0
  12. package/dist/src/clock.js +121 -0
  13. package/dist/src/completions.js +17 -8
  14. package/dist/src/confine.d.ts +22 -0
  15. package/dist/src/confine.js +218 -3
  16. package/dist/src/container.d.ts +9 -0
  17. package/dist/src/container.js +115 -5
  18. package/dist/src/context-tools.d.ts +1 -1
  19. package/dist/src/deploy.d.ts +33 -0
  20. package/dist/src/deploy.js +145 -6
  21. package/dist/src/egress.d.ts +7 -0
  22. package/dist/src/egress.js +6 -0
  23. package/dist/src/eol.d.ts +2 -0
  24. package/dist/src/eol.js +7 -0
  25. package/dist/src/evals.d.ts +5 -0
  26. package/dist/src/evals.js +9 -2
  27. package/dist/src/flow-lint.d.ts +8 -0
  28. package/dist/src/flow-lint.js +223 -1
  29. package/dist/src/flow-patterns.d.ts +141 -0
  30. package/dist/src/flow-patterns.js +568 -0
  31. package/dist/src/gitrepo.d.ts +18 -0
  32. package/dist/src/gitrepo.js +49 -0
  33. package/dist/src/host-env.js +5 -0
  34. package/dist/src/kinds.js +1 -1
  35. package/dist/src/language.d.ts +17 -0
  36. package/dist/src/language.js +55 -0
  37. package/dist/src/layout.d.ts +69 -0
  38. package/dist/src/layout.js +200 -0
  39. package/dist/src/library.d.ts +9 -0
  40. package/dist/src/library.js +30 -3
  41. package/dist/src/limits.d.ts +84 -0
  42. package/dist/src/limits.js +287 -0
  43. package/dist/src/locale.d.ts +47 -0
  44. package/dist/src/locale.js +177 -0
  45. package/dist/src/mail.d.ts +88 -0
  46. package/dist/src/mail.js +247 -0
  47. package/dist/src/model-credential.d.ts +14 -0
  48. package/dist/src/model-credential.js +58 -0
  49. package/dist/src/model-probe.d.ts +3 -0
  50. package/dist/src/model-probe.js +1 -0
  51. package/dist/src/notify.d.ts +53 -7
  52. package/dist/src/notify.js +194 -126
  53. package/dist/src/openapi.js +5 -3
  54. package/dist/src/operator.d.ts +201 -0
  55. package/dist/src/operator.js +251 -0
  56. package/dist/src/paths.d.ts +28 -0
  57. package/dist/src/paths.js +55 -0
  58. package/dist/src/platform.d.ts +43 -3
  59. package/dist/src/platform.js +9 -1
  60. package/dist/src/provider-health.js +3 -2
  61. package/dist/src/providers.d.ts +481 -0
  62. package/dist/src/providers.js +1105 -6
  63. package/dist/src/refusal.d.ts +83 -0
  64. package/dist/src/refusal.js +281 -0
  65. package/dist/src/run-container.d.ts +149 -13
  66. package/dist/src/run-container.js +424 -43
  67. package/dist/src/runner.d.ts +177 -9
  68. package/dist/src/runner.js +1146 -154
  69. package/dist/src/runtime.d.ts +52 -0
  70. package/dist/src/runtime.js +493 -58
  71. package/dist/src/script-tools.d.ts +17 -2
  72. package/dist/src/script-tools.js +82 -10
  73. package/dist/src/secret-files.d.ts +6 -5
  74. package/dist/src/secret-files.js +24 -11
  75. package/dist/src/secret-health.d.ts +30 -6
  76. package/dist/src/secret-health.js +42 -20
  77. package/dist/src/secrets.d.ts +19 -0
  78. package/dist/src/secrets.js +155 -86
  79. package/dist/src/starter.d.ts +9 -0
  80. package/dist/src/starter.js +92 -11
  81. package/dist/src/step-exec.d.ts +72 -2
  82. package/dist/src/step-exec.js +431 -37
  83. package/dist/src/step-removal.d.ts +15 -0
  84. package/dist/src/step-removal.js +49 -0
  85. package/dist/src/storage.d.ts +23 -4
  86. package/dist/src/storage.js +125 -23
  87. package/dist/src/store.d.ts +222 -37
  88. package/dist/src/store.js +349 -62
  89. package/dist/src/subagents.d.ts +75 -0
  90. package/dist/src/subagents.js +149 -0
  91. package/dist/src/test-mode.d.ts +17 -2
  92. package/dist/src/test-mode.js +53 -13
  93. package/dist/src/tool-names.d.ts +13 -1
  94. package/dist/src/tool-names.js +38 -11
  95. package/dist/src/tool-programs.d.ts +26 -0
  96. package/dist/src/tool-programs.js +172 -0
  97. package/dist/src/tool-test.js +104 -12
  98. package/dist/src/translator.d.ts +8 -2
  99. package/dist/src/translator.js +69 -23
  100. package/dist/src/trigger-log.d.ts +1 -1
  101. package/dist/src/triggers.d.ts +3 -0
  102. package/dist/src/triggers.js +10 -2
  103. package/dist/src/untrusted-fetch.d.ts +11 -0
  104. package/dist/src/untrusted-fetch.js +77 -0
  105. package/dist/src/web-actions.d.ts +91 -0
  106. package/dist/src/web-actions.js +199 -0
  107. package/dist/src/webhook.d.ts +4 -0
  108. package/dist/src/webhook.js +39 -0
  109. package/index.ts +4 -0
  110. package/package.json +3 -3
  111. package/src/account.ts +149 -0
  112. package/src/api-tools.ts +32 -4
  113. package/src/browser-pod.ts +350 -0
  114. package/src/catalog.ts +2 -1
  115. package/src/clock.ts +149 -0
  116. package/src/completions.ts +17 -8
  117. package/src/confine.ts +218 -3
  118. package/src/container.ts +112 -5
  119. package/src/context-tools.ts +1 -1
  120. package/src/deploy.ts +137 -6
  121. package/src/egress.ts +13 -1
  122. package/src/eol.ts +7 -0
  123. package/src/evals.ts +14 -2
  124. package/src/flow-lint.ts +253 -1
  125. package/src/flow-patterns.ts +597 -0
  126. package/src/gitrepo.ts +48 -0
  127. package/src/host-env.ts +5 -0
  128. package/src/kinds.ts +1 -1
  129. package/src/language.ts +62 -0
  130. package/src/layout.ts +228 -0
  131. package/src/library.ts +37 -3
  132. package/src/limits.ts +290 -0
  133. package/src/locale.ts +217 -0
  134. package/src/mail.ts +318 -0
  135. package/src/model-credential.ts +71 -0
  136. package/src/model-probe.ts +4 -0
  137. package/src/notify.ts +189 -103
  138. package/src/openapi.ts +5 -3
  139. package/src/operator.ts +300 -0
  140. package/src/paths.ts +54 -0
  141. package/src/platform.ts +52 -3
  142. package/src/provider-health.ts +3 -2
  143. package/src/providers.ts +1368 -6
  144. package/src/refusal.ts +320 -0
  145. package/src/run-container.ts +460 -44
  146. package/src/runner.ts +1168 -155
  147. package/src/runtime.ts +487 -55
  148. package/src/script-tools.ts +81 -10
  149. package/src/secret-files.ts +22 -10
  150. package/src/secret-health.ts +64 -19
  151. package/src/secrets.ts +149 -87
  152. package/src/starter.ts +93 -11
  153. package/src/step-exec.ts +447 -38
  154. package/src/step-removal.ts +59 -0
  155. package/src/storage.ts +141 -22
  156. package/src/store.ts +506 -70
  157. package/src/subagents.ts +189 -0
  158. package/src/test-mode.ts +51 -11
  159. package/src/tool-names.ts +40 -11
  160. package/src/tool-programs.ts +179 -0
  161. package/src/tool-test.ts +110 -12
  162. package/src/translator.ts +72 -22
  163. package/src/trigger-log.ts +1 -0
  164. package/src/triggers.ts +10 -2
  165. package/src/untrusted-fetch.ts +88 -0
  166. package/src/web-actions.ts +237 -0
  167. package/src/webhook.ts +41 -0
  168. package/templates/blog-desk/agents/writer/agent.md +1 -1
  169. package/templates/hello/agents/curator/agent.md +4 -4
  170. package/templates/hello/agents/notetaker/agent.md +3 -3
@@ -1,4 +1,12 @@
1
+ import { type SubagentSpec } from "./subagents.ts";
1
2
  import { type FlowStep, type RunRecord, type StepRecord } from "./store.ts";
3
+ import { type Catalog } from "./catalog.ts";
4
+ import { type ScriptSpec } from "./script-tools.ts";
5
+ import { type RuntimeSpec } from "./runtime.ts";
6
+ import { type LanguageChoice } from "./language.ts";
7
+ import { type RegionChoice } from "./locale.ts";
8
+ import { type ClockChoice } from "./clock.ts";
9
+ export { localDate } from "./clock.ts";
2
10
  /** How much of the earlier groups' output a step is handed, in characters.
3
11
  * Newest groups are kept when the total is over it. */
4
12
  export declare const CONTEXT_CHARS = 30000;
@@ -48,20 +56,73 @@ export declare function discoverSkills(agentDir: string, subdir?: string): Disco
48
56
  * Returns null rather than throwing on unparseable YAML: a broken shared file
49
57
  * must not take down every agent under it.
50
58
  */
59
+ /**
60
+ * AGENTS.md without the block a coding tool reads.
61
+ *
62
+ * AGENTS.md is two things in a foldrun folder: the account's (or
63
+ * workspace's) own context, which every running agent is given, and — the
64
+ * convention Next.js set — the file a coding agent (Claude Code, Cursor,
65
+ * Codex) reads before it edits the folder. The CLI writes a short managed
66
+ * block for the second reader between these markers; it is about editing
67
+ * the files, not about doing the work, so no running agent is shown it.
68
+ */
69
+ export declare const AGENT_RULES_START = "<!-- BEGIN:foldrun-agent-rules -->";
70
+ export declare const AGENT_RULES_END = "<!-- END:foldrun-agent-rules -->";
71
+ export declare function withoutAgentRules(body: string): string;
51
72
  export declare function readAgentsMd(dir: string): {
52
73
  data: Record<string, unknown>;
53
74
  body: string;
54
75
  } | null;
55
76
  /**
56
- * The calendar an agent works to. `timezone:` in AGENTS.md, nearest-wins like
57
- * every other key there; a flow's cron already means its own timezone, this
58
- * makes "today" mean it too. Unset is UTC — what the pods run — which for a
59
- * Sydney desk made an article dated today "one day in the future" until 10am.
60
- * An unknown name falls back to UTC rather than failing every step.
77
+ * The runtime one agent's steps are built with: its own declaration (else its
78
+ * workspace's), merged with every script tool it grants. The runner builds
79
+ * exactly this for a step; the platform builds it ahead of time when the
80
+ * workspace is deployed, so a package that will not install is reported at
81
+ * deploy and a scheduled step finds its environment ready. One function, so
82
+ * the two can never disagree about what an agent needs.
83
+ */
84
+ export declare function agentRuntimeSpec(tenant: string, workspace: string, agentDir: string): RuntimeSpec | null;
85
+ /** Every distinct runtime a workspace's agents need, with who needs it. */
86
+ export declare function workspaceRuntimePlan(tenant: string, workspace: string): {
87
+ fingerprint: string;
88
+ spec: RuntimeSpec;
89
+ agents: string[];
90
+ }[];
91
+ /**
92
+ * The calendar a set of frontmatter asks for, with no cascade around it —
93
+ * one merged record in, one zone out. The full cascade lives in
94
+ * `agentClock` below and in clock.ts; this is what the account-level parts
95
+ * of the platform (a wallet's month, a budget window) call.
61
96
  */
62
97
  export declare function resolveTimezone(front: Record<string, unknown>): string;
63
- /** YYYY-MM-DD in a timezone — the date the workspace believes it is. */
64
- export declare function localDate(timeZone: string, now?: Date): string;
98
+ /**
99
+ * The clock this step works to: agent, then flow, then workspace, then
100
+ * account, then FOLDRUN_TIMEZONE, then UTC. Nearest wins, and a value that
101
+ * is not a zone falls through to the next level with a line to say so —
102
+ * see clock.ts for why this exists at all.
103
+ */
104
+ export declare function agentClock(agentDir: string, tenant: string, agentFront: Record<string, unknown>, flowTimezone?: string | null): ClockChoice;
105
+ /**
106
+ * The language this step works in: agent, then workspace, then account, then
107
+ * FOLDRUN_LANGUAGE, then English. The flow level is not read yet — a flow's
108
+ * timezone: travels through three signatures to get here, and this will
109
+ * take the same road when a flow needs it.
110
+ */
111
+ export declare function agentLanguage(agentDir: string, tenant: string, agentFront: Record<string, unknown>): LanguageChoice;
112
+ /**
113
+ * The country this step works for, on the same road as the language, then
114
+ * FOLDRUN_REGION, then whatever the language tag carried (`en-AU` says AU).
115
+ * Everything a country changes — currency, units, calendar, weekend, text
116
+ * direction — is derived from it in locale.ts; the three overrides
117
+ * (`currency:`, `calendar:`, `units:`) are read at the nearest level that
118
+ * wrote them.
119
+ */
120
+ export declare function agentRegion(agentDir: string, tenant: string, agentFront: Record<string, unknown>, languageTag: string): RegionChoice;
121
+ export declare function agentLocaleOverrides(agentDir: string, tenant: string, agentFront: Record<string, unknown>): {
122
+ currency?: unknown;
123
+ calendar?: unknown;
124
+ units?: unknown;
125
+ };
65
126
  /**
66
127
  * The prose from AGENTS.md, outermost first.
67
128
  *
@@ -69,6 +130,23 @@ export declare function localDate(timeZone: string, now?: Date): string;
69
130
  * template as "context every agent here shares" — and never read. Only the
70
131
  * frontmatter was, so the instructions people actually wrote reached no model.
71
132
  */
133
+ /**
134
+ * The sub-agents a step may delegate to (`subagents:`), as the SDK will get
135
+ * them: each resolved through agentContext exactly as its own step would be
136
+ * (grants only — no runtime; it runs inside its parent's), then cut to what
137
+ * the parent holds. Problems are lines on the step, never a failed step.
138
+ */
139
+ export declare function buildSubagentSpecs(o: {
140
+ names: unknown;
141
+ self: string;
142
+ workspaceRoot: string;
143
+ tenant: string;
144
+ tags?: string[];
145
+ runId?: string;
146
+ flowTimezone?: string | null;
147
+ parentAllowed: string[];
148
+ push: (type: "info" | "error", text: string) => void;
149
+ }): SubagentSpec[];
72
150
  export declare function sharedInstructions(agentDir: string, tenant: string): string | null;
73
151
  /**
74
152
  * Which of an agent's in-scope skills load for this run.
@@ -113,6 +191,14 @@ export declare function applicableSkills<T extends {
113
191
  */
114
192
  export declare function saysUntilMarker(result: string | null, until: string): boolean;
115
193
  export declare function resolveDocLinks(text: string, workspaceRoot: string): string;
194
+ export declare function repriced(catalog: Catalog | null, model: string, usage: {
195
+ inputTokens: number;
196
+ outputTokens: number;
197
+ } | null, sdkCost: number | null, push: (type: "info" | "error", text: string) => void,
198
+ /** The step's turns each priced at its own model — set when a sub-agent
199
+ * ran on a different model (ExecOutcome.turnsCostUsd). The lump `usage`
200
+ * priced at the step's model would bill those turns at the wrong rate. */
201
+ turnsCostUsd?: number | null): number | null;
116
202
  /**
117
203
  * Write one attempt onto the step's history and re-total the step.
118
204
  *
@@ -123,10 +209,29 @@ export declare function resolveDocLinks(text: string, workspaceRoot: string): st
123
209
  * step used to carry only its last attempt's cost, so a `retry: 2` step
124
210
  * could spend three shares while reporting one.
125
211
  */
126
- export declare function recordAttempt(step: StepRecord, n: number, status: "completed" | "failed", startedAt: string, eventsBefore: number,
212
+ export declare function recordAttempt(step: StepRecord,
213
+ /** The step's attempt (`attempts`) these rows belong to. */
214
+ attempt: number, status: "completed" | "failed", startedAt: string, eventsBefore: number,
127
215
  /** The attempt re-attached to a sandbox a previous driver left: same
128
216
  * attempt, so its row is replaced rather than repeated. */
129
217
  reattach?: boolean): void;
218
+ /**
219
+ * Did this attempt call its tools and then fail only on the check after —
220
+ * `verify:`, `output: json`, a schema? For a step whose tools act outward
221
+ * that is the one failure a retry must not answer: the email went, the post
222
+ * is up, and the check failing says nothing about whether they happened. A
223
+ * second attempt sends them again. Read off the attempt's own events, which
224
+ * both executors write the same way (step-exec.ts): every event a failed
225
+ * check writes carries `check: true`. It used to read only the LAST error's
226
+ * wording, and a failed shell verify or judge writes its detail after its
227
+ * headline — so the step was retried and the email went twice. The wording
228
+ * is still honoured, for a driver image older than the tag.
229
+ */
230
+ export declare function actedThenFailedCheck(events: {
231
+ type: string;
232
+ text: string;
233
+ check?: boolean;
234
+ }[]): boolean;
130
235
  /**
131
236
  * Copy a directory tree by reading and writing the bytes.
132
237
  *
@@ -140,6 +245,13 @@ reattach?: boolean): void;
140
245
  * happened to create it.
141
246
  */
142
247
  export declare function copyTreeBytes(from: string, to: string): void;
248
+ /**
249
+ * Wait, briefly, for the mounted credential to become one we have not
250
+ * already been refused on. Returns the new value, or null when the window
251
+ * passes without a change — which is the answer that means "this key is
252
+ * wrong", not "this key is stale".
253
+ */
254
+ export declare function awaitRotatedCredential(before: string | undefined, now?: () => number, read?: () => string | undefined, waitMs?: number, pollMs?: number): Promise<string | null>;
143
255
  /** How many times a busy provider is given another moment, and the longest
144
256
  * any one wait may be. Three is the industry's habit and the point where
145
257
  * waiting longer stops being kinder than failing. */
@@ -157,9 +269,31 @@ export declare const OVERLOAD_RETRIES = 3;
157
269
  */
158
270
  export declare function isTransientOverload(text: string): boolean;
159
271
  export declare function backoffMs(attempt: number, text?: string, random?: () => number): number;
272
+ /**
273
+ * The platform's model credential, read at the moment a step needs it.
274
+ *
275
+ * It used to come only from this process's environment, which meant the
276
+ * worker held whatever token it was started with. Refreshing an OAuth token
277
+ * REVOKES the old one, so a refresh without a restart left every run
278
+ * answering 401 — on 2026-09-18 that took every desk down for two hours, and
279
+ * nothing said so until a person read a failed run. The refresher's own
280
+ * comment had named the fix years of incidents earlier: stop shipping the
281
+ * token as an env var.
282
+ *
283
+ * So: when `FOLDRUN_MODEL_KEY_FILE` names a file (the Kubernetes secret,
284
+ * mounted rather than injected), it is read fresh every time. The kubelet
285
+ * updates a mounted secret in place within about a minute of it changing, so
286
+ * a refreshed token reaches the next step on its own, with no restart, no
287
+ * timing window, and nothing to skip while work is in flight.
288
+ *
289
+ * A missing or empty file falls back to the environment, which is what a
290
+ * local install, the CLI and every compose setup use.
291
+ */
292
+ export declare function platformModelCredential(env?: NodeJS.ProcessEnv): string | undefined;
293
+ export declare function platformModelEnv(): Record<string, string | undefined>;
160
294
  /**
161
295
  * `schema: <path>` — the file, read and parsed, or null. Agent-relative like
162
- * every other path in a flow (`../../schemas/lead.json`), confined to the
296
+ * every other path in a flow (`workspace/schemas/lead.json`), confined to the
163
297
  * workspace: a schema is the author's document, never something a step
164
298
  * can point outside.
165
299
  */
@@ -275,3 +409,37 @@ export declare function waitForRun(tenant: string, workspace: string, runId: str
275
409
  * nothing is what makes `?wait=true` useful rather than merely blocking.
276
410
  */
277
411
  export declare function runResult(run: RunRecord): string | null;
412
+ /**
413
+ * What a Docker-run script sees of the workspace and the account: their
414
+ * scripts/ and their tools/, read-only. A folder tool's code lives in its
415
+ * tools/<name>/ folder, not in scripts/ — without those two mounts every
416
+ * folder tool (the hello template's wordcount among them) was handed a host
417
+ * path the container did not have, and failed on every call. A gallery tool
418
+ * nobody installed runs from the gallery's own shelf, so that is mounted too
419
+ * when there is one. A folder that does not exist is skipped by the copy.
420
+ */
421
+ export declare function scriptMounts(agentDir: string, tenant: string): Record<string, string>;
422
+ /**
423
+ * The skills an agent can see, nearest first, each with the path the agent
424
+ * is told to read it at. Own skills, then the workspace's, then the
425
+ * workspace's `.agents/skills/` (the cross-client convention, scanned after
426
+ * the native location so a foldrun-native skill wins a name clash), then the
427
+ * account library. The nearest definition of a name wins.
428
+ *
429
+ * The workspace's are written `workspace/skills/…`, the one spelling for the
430
+ * workspace root, which the file tools expand themselves. `../../skills/…`
431
+ * left the model to resolve it against a path it had to guess, and on the
432
+ * hello template it climbed one level too far and was refused.
433
+ */
434
+ export declare function skillsInScope(agentDir: string, tenant: string): DiscoveredSkill[];
435
+ /**
436
+ * The interpreters a set of scripts needs, read from what they are: the
437
+ * extension of `run:` (or of inline code), or an explicit `interpreter:`.
438
+ * Docker's script image is built from the runtime spec, and the base one is
439
+ * Python only, so a JavaScript tool that declared no `runtime:` (the demo's
440
+ * inline crm-upload, any folder tool's run.mjs) failed with no node to run
441
+ * it. Python is named too whenever a .py script is present, because asking
442
+ * for node alone builds on node:22-slim, which has no python3. Null when
443
+ * nothing beyond the default is needed.
444
+ */
445
+ export declare function scriptLanguages(scripts: ScriptSpec[]): RuntimeSpec | null;