@foldrun/core 0.4.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 (155) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.js +3 -0
  3. package/dist/src/account.d.ts +45 -0
  4. package/dist/src/account.js +130 -0
  5. package/dist/src/api-tools.d.ts +8 -0
  6. package/dist/src/api-tools.js +22 -4
  7. package/dist/src/browser-pod.d.ts +129 -0
  8. package/dist/src/browser-pod.js +284 -0
  9. package/dist/src/catalog.js +2 -1
  10. package/dist/src/clock.d.ts +49 -0
  11. package/dist/src/clock.js +121 -0
  12. package/dist/src/completions.js +17 -8
  13. package/dist/src/confine.d.ts +22 -0
  14. package/dist/src/confine.js +218 -3
  15. package/dist/src/deploy.d.ts +33 -0
  16. package/dist/src/deploy.js +145 -6
  17. package/dist/src/egress.d.ts +7 -0
  18. package/dist/src/egress.js +6 -0
  19. package/dist/src/eol.d.ts +2 -0
  20. package/dist/src/eol.js +7 -0
  21. package/dist/src/evals.d.ts +5 -0
  22. package/dist/src/evals.js +6 -2
  23. package/dist/src/flow-lint.d.ts +8 -0
  24. package/dist/src/flow-lint.js +223 -1
  25. package/dist/src/flow-patterns.d.ts +141 -0
  26. package/dist/src/flow-patterns.js +568 -0
  27. package/dist/src/gitrepo.d.ts +18 -0
  28. package/dist/src/gitrepo.js +49 -0
  29. package/dist/src/kinds.js +1 -1
  30. package/dist/src/language.d.ts +17 -0
  31. package/dist/src/language.js +55 -0
  32. package/dist/src/layout.d.ts +69 -0
  33. package/dist/src/layout.js +200 -0
  34. package/dist/src/library.d.ts +9 -0
  35. package/dist/src/library.js +30 -3
  36. package/dist/src/limits.d.ts +84 -0
  37. package/dist/src/limits.js +287 -0
  38. package/dist/src/locale.d.ts +47 -0
  39. package/dist/src/locale.js +177 -0
  40. package/dist/src/mail.d.ts +88 -0
  41. package/dist/src/mail.js +245 -0
  42. package/dist/src/model-probe.d.ts +3 -0
  43. package/dist/src/model-probe.js +1 -0
  44. package/dist/src/notify.d.ts +53 -7
  45. package/dist/src/notify.js +194 -126
  46. package/dist/src/openapi.js +5 -3
  47. package/dist/src/operator.d.ts +201 -0
  48. package/dist/src/operator.js +251 -0
  49. package/dist/src/paths.d.ts +28 -0
  50. package/dist/src/paths.js +55 -0
  51. package/dist/src/platform.d.ts +43 -3
  52. package/dist/src/platform.js +9 -1
  53. package/dist/src/provider-health.js +3 -2
  54. package/dist/src/providers.d.ts +481 -0
  55. package/dist/src/providers.js +1105 -6
  56. package/dist/src/refusal.d.ts +83 -0
  57. package/dist/src/refusal.js +281 -0
  58. package/dist/src/run-container.d.ts +149 -13
  59. package/dist/src/run-container.js +403 -42
  60. package/dist/src/runner.d.ts +142 -9
  61. package/dist/src/runner.js +1027 -122
  62. package/dist/src/runtime.d.ts +52 -0
  63. package/dist/src/runtime.js +493 -58
  64. package/dist/src/script-tools.d.ts +3 -2
  65. package/dist/src/script-tools.js +35 -3
  66. package/dist/src/secret-files.d.ts +6 -5
  67. package/dist/src/secret-files.js +24 -11
  68. package/dist/src/secret-health.d.ts +30 -6
  69. package/dist/src/secret-health.js +42 -20
  70. package/dist/src/secrets.d.ts +19 -0
  71. package/dist/src/secrets.js +155 -86
  72. package/dist/src/starter.d.ts +9 -0
  73. package/dist/src/starter.js +92 -11
  74. package/dist/src/step-exec.d.ts +61 -2
  75. package/dist/src/step-exec.js +414 -36
  76. package/dist/src/step-removal.d.ts +15 -0
  77. package/dist/src/step-removal.js +49 -0
  78. package/dist/src/storage.d.ts +23 -4
  79. package/dist/src/storage.js +125 -23
  80. package/dist/src/store.d.ts +218 -37
  81. package/dist/src/store.js +347 -60
  82. package/dist/src/subagents.d.ts +75 -0
  83. package/dist/src/subagents.js +149 -0
  84. package/dist/src/tool-names.d.ts +13 -1
  85. package/dist/src/tool-names.js +38 -11
  86. package/dist/src/tool-programs.d.ts +26 -0
  87. package/dist/src/tool-programs.js +172 -0
  88. package/dist/src/tool-test.js +104 -12
  89. package/dist/src/translator.d.ts +8 -2
  90. package/dist/src/translator.js +69 -23
  91. package/dist/src/trigger-log.d.ts +1 -1
  92. package/dist/src/triggers.d.ts +3 -0
  93. package/dist/src/triggers.js +10 -2
  94. package/dist/src/untrusted-fetch.d.ts +11 -0
  95. package/dist/src/untrusted-fetch.js +77 -0
  96. package/dist/src/web-actions.d.ts +91 -0
  97. package/dist/src/web-actions.js +199 -0
  98. package/dist/src/webhook.d.ts +4 -0
  99. package/dist/src/webhook.js +39 -0
  100. package/index.ts +3 -0
  101. package/package.json +3 -3
  102. package/src/account.ts +149 -0
  103. package/src/api-tools.ts +32 -4
  104. package/src/browser-pod.ts +313 -0
  105. package/src/catalog.ts +2 -1
  106. package/src/clock.ts +149 -0
  107. package/src/completions.ts +17 -8
  108. package/src/confine.ts +218 -3
  109. package/src/deploy.ts +137 -6
  110. package/src/egress.ts +13 -1
  111. package/src/eol.ts +7 -0
  112. package/src/evals.ts +11 -2
  113. package/src/flow-lint.ts +253 -1
  114. package/src/flow-patterns.ts +597 -0
  115. package/src/gitrepo.ts +48 -0
  116. package/src/kinds.ts +1 -1
  117. package/src/language.ts +62 -0
  118. package/src/layout.ts +228 -0
  119. package/src/library.ts +37 -3
  120. package/src/limits.ts +290 -0
  121. package/src/locale.ts +217 -0
  122. package/src/mail.ts +316 -0
  123. package/src/model-probe.ts +4 -0
  124. package/src/notify.ts +189 -103
  125. package/src/openapi.ts +5 -3
  126. package/src/operator.ts +300 -0
  127. package/src/paths.ts +54 -0
  128. package/src/platform.ts +52 -3
  129. package/src/provider-health.ts +3 -2
  130. package/src/providers.ts +1368 -6
  131. package/src/refusal.ts +320 -0
  132. package/src/run-container.ts +439 -43
  133. package/src/runner.ts +1050 -122
  134. package/src/runtime.ts +487 -55
  135. package/src/script-tools.ts +38 -5
  136. package/src/secret-files.ts +22 -10
  137. package/src/secret-health.ts +64 -19
  138. package/src/secrets.ts +149 -87
  139. package/src/starter.ts +93 -11
  140. package/src/step-exec.ts +429 -37
  141. package/src/step-removal.ts +59 -0
  142. package/src/storage.ts +141 -22
  143. package/src/store.ts +500 -68
  144. package/src/subagents.ts +189 -0
  145. package/src/tool-names.ts +40 -11
  146. package/src/tool-programs.ts +179 -0
  147. package/src/tool-test.ts +110 -12
  148. package/src/translator.ts +72 -22
  149. package/src/trigger-log.ts +1 -0
  150. package/src/triggers.ts +10 -2
  151. package/src/untrusted-fetch.ts +88 -0
  152. package/src/web-actions.ts +237 -0
  153. package/src/webhook.ts +41 -0
  154. package/templates/hello/agents/curator/agent.md +1 -1
  155. package/templates/hello/agents/notetaker/agent.md +1 -1
@@ -1,5 +1,7 @@
1
+ import type { BrowserPodOutcome } from "./browser-pod.ts";
1
2
  import { singleWorkspace } from "./paths.ts";
2
3
  import type { TestEffect } from "./test-mode.ts";
4
+ import type { OperatorEvent } from "./operator.ts";
3
5
  /**
4
6
  * The frontmatter of one markdown file, as the runtime reads it. Exported
5
7
  * for the CLI: `foldrun probe` used to import gray-matter itself, which is
@@ -54,9 +56,10 @@ export declare function workspaceDir(tenant: string, workspace: string): string;
54
56
  * The account root — where `library/`, account secrets and the account-wide
55
57
  * AGENTS.md live.
56
58
  *
57
- * On a laptop there is no account, so it is the workspace's parent: `my-desk/`
58
- * sits beside `library/` and an `AGENTS.md` covering everything there. Same
59
- * rule libraryDir uses, so the two never disagree about where "up" is.
59
+ * On a laptop it is wherever `singleAccountRoot` says: the workspace's parent
60
+ * for a flat `my-desk/`, and the account root two levels up for a workspace
61
+ * inside `my-account/workspaces/`. Same rule libraryDir uses, so the two never
62
+ * disagree about where "up" is.
60
63
  */
61
64
  export declare function accountDir(tenant: string): string;
62
65
  /**
@@ -111,6 +114,10 @@ export declare function assertCanonicalCase(rel: string): void;
111
114
  export declare const PLATFORM_FILES: string[];
112
115
  /** True for anything the platform, not the author or the agent, writes. */
113
116
  export declare function isPlatformPath(rel: string): boolean;
117
+ /** What a deploy keeps whatever it ships — the rule above saveWorkspace's
118
+ * body. planDeploy reports removals through the same test, so a plan never
119
+ * lists a file the save would keep. */
120
+ export declare function keptOnDeploy(rel: string, shipped: ReadonlySet<string>): boolean;
114
121
  export declare function saveWorkspace(tenant: string, workspace: string, files: DeployFile[],
115
122
  /** Attribution for the revision this save records. A deploy passes the
116
123
  * commit, which becomes the revision's id. */
@@ -132,19 +139,25 @@ export interface McpSpec {
132
139
  url?: string;
133
140
  headers: Record<string, string>;
134
141
  }
135
- export type ToolDef = {
142
+ /** `outward: true` in a tool's frontmatter: this one sends, posts, buys,
143
+ * publishes or uploads. It changes nothing at run time — `check` uses it to
144
+ * insist the step that grants it has a verify or a gate. */
145
+ export type OutwardMark = {
146
+ outward?: boolean;
147
+ };
148
+ export type ToolDef = ({
136
149
  kind: "http";
137
150
  name: string;
138
151
  spec: ApiSpec;
139
- } | {
152
+ } & OutwardMark) | ({
140
153
  kind: "script";
141
154
  name: string;
142
155
  spec: Record<string, unknown>;
143
- } | {
156
+ } & OutwardMark) | ({
144
157
  kind: "mcp";
145
158
  name: string;
146
159
  spec: McpSpec;
147
- };
160
+ } & OutwardMark);
148
161
  /**
149
162
  * The program inside a single-file script tool: the first fenced block whose
150
163
  * language tag names a runnable language. By tag, not by position — a body
@@ -168,6 +181,13 @@ export declare function fencedCodeBlock(body: string): {
168
181
  start: number;
169
182
  end: number;
170
183
  } | null;
184
+ /**
185
+ * `outward: true` — this tool changes something that is not ours: it sends,
186
+ * posts, buys, publishes, uploads. The runtime does not treat it differently;
187
+ * `check` does, because the step that uses one is the step where a silent
188
+ * failure costs something that cannot be taken back. See `lintFlow`.
189
+ */
190
+ export declare function toolIsOutward(data: Record<string, unknown>): boolean;
171
191
  export declare function parseToolDef(data: Record<string, unknown>, fallbackName: string, body?: string): ToolDef | null;
172
192
  /**
173
193
  * Tools in a directory, in both shapes.
@@ -190,6 +210,19 @@ export declare function parseToolDef(data: Record<string, unknown>, fallbackName
190
210
  *
191
211
  * Flat files keep working unchanged — the shape is a migration, not a break.
192
212
  */
213
+ /**
214
+ * A folder tool's `requirements.txt` and `package.json`, as a `runtime:`
215
+ * block. Requirement lines only: comments and blanks are left out, and so
216
+ * are options — whole option lines (`-r`, `--index-url`, `-e`) and the ones
217
+ * a requirement carries (`--hash=…`). Options never reach the installer,
218
+ * which takes them from the same argv as its operands; their NAMES are kept
219
+ * in `ignored`, never their values (an index URL can carry a password), so
220
+ * the build log can say what was not applied. Anything else that is not a
221
+ * requirement is refused later by parseRuntime with a log line, the same as
222
+ * it would be in tool.md. `dependencies` only — a tool's dev dependencies
223
+ * are its author's, not its runtime's.
224
+ */
225
+ export declare function folderRuntime(folder: string): Record<string, unknown> | null;
193
226
  declare function readToolDir(dir: string, scope?: "workspace" | "account"): Record<string, ToolDef>;
194
227
  /** Definitions that exist on disk but could not be read, for the scopes an
195
228
  * agent draws on. Empty when everything parsed. */
@@ -212,7 +245,9 @@ export interface ApiSpec {
212
245
  openapi?: string;
213
246
  /** Which of the document's operations to expose — operationIds or
214
247
  * `"METHOD /path"` strings. Absent: every operation, capped at 60.
215
- * Present: only these, and the generic `call_<api>` tool is withheld. */
248
+ * Present: only these, and the generic `call_<api>` tool is withheld —
249
+ * always. If none of them resolve (document missing, moved, or an
250
+ * operation renamed) the API gets no tools that run: it fails closed. */
216
251
  operations?: string[];
217
252
  /** Calls per second this API may see from one step: `count` is the burst
218
253
  * the bucket holds, `perSec` the refill rate. `"5/s"` → {5, 5};
@@ -242,9 +277,15 @@ export interface AgentInfo {
242
277
  /** Names still written under the removed `use:` key. Nothing is granted
243
278
  * for them; `foldrun check` and the run log say what to write instead. */
244
279
  legacyUse: string[];
280
+ /** Retired built-in names still in `tools:` (files, bash) — granted, but
281
+ * `check` says what to write instead. */
282
+ retiredTools: string[];
245
283
  /** Colleagues this agent may consult mid-run (`agents:`) — each becomes a
246
284
  * consult_<name> tool. The team's edges, as the author drew them. */
247
285
  consults: string[];
286
+ /** Colleagues this agent may DELEGATE to (`subagents:`) — each runs with
287
+ * its own context and its own tools, no wider than this agent's. */
288
+ subagents: string[];
248
289
  /** The `skills:` allowlist, or null when the field is absent — which is
249
290
  * not the same thing: absent inherits every skill in scope, an empty
250
291
  * list withholds all of them. */
@@ -256,6 +297,32 @@ export interface AgentInfo {
256
297
  budget: number | null;
257
298
  /** What is wrong with the `budget:` line, or null. */
258
299
  budgetProblem: string | null;
300
+ /** `timezone:` — the calendar this agent works to, nearest-wins over its
301
+ * flow's, its workspace's and the account's. Null inherits. */
302
+ timezone: string | null;
303
+ /** What is wrong with the `timezone:` line, or null. */
304
+ timezoneProblem: string | null;
305
+ languageProblem: string | null;
306
+ /** region:/currency:/units:/calendar: values that cannot be read. */
307
+ localeProblems: string[];
308
+ /** `web:` values that cannot work. The run says
309
+ * so in its trail and carries on; check and deploy say so first. */
310
+ webProblems: string[];
311
+ /** `web:` that works but should change — the old `session:` key, a pinned
312
+ * user agent behind the runner image's Chrome (when this process knows
313
+ * that Chrome: FOLDRUN_RUNNER_ENGINES). Warnings, never refusals. */
314
+ webWarnings: string[];
315
+ /** `schedule:` written on an agent, which nothing runs — only a flow has
316
+ * a clock. The message, or null when there is no such line. */
317
+ scheduleProblem: string | null;
318
+ /** `limits:` — the agent's own per-step call limits, as readable. */
319
+ limits: Record<string, number>;
320
+ /** What is wrong with the `limits:` block's shape; key-vs-grant problems
321
+ * need the tool lists, and `check` adds them (limits.ts limitKeyProblems). */
322
+ limitProblems: string[];
323
+ /** Tools this agent declares inline rather than in `tools:` — `apis:`,
324
+ * `scripts:` and `mcpServers:` names — which a limit may also name. */
325
+ inlineTools: string[];
259
326
  }
260
327
  export declare const MODEL_TIERS: Record<string, string>;
261
328
  export type Tier = keyof typeof MODEL_TIERS;
@@ -287,6 +354,11 @@ export declare function parseApis(raw: unknown): ApiSpec[];
287
354
  export interface FlowStep {
288
355
  agent: string;
289
356
  instruction: string;
357
+ /** The instruction was written over more than one line and the parser
358
+ * joined them. It runs, but `foldrun check` says so: a wrapped
359
+ * instruction is one editor keystroke away from being a paragraph of
360
+ * prose the parser has to guess about. */
361
+ wrapped?: boolean;
290
362
  group: number;
291
363
  optional: boolean;
292
364
  /** Set when the step targets another flow (`[[flow:name]]`) instead of an agent. */
@@ -381,6 +453,9 @@ export interface FlowStep {
381
453
  /** `max_turns:` — the most model turns this step may take before it is
382
454
  * stopped, beside `budget:` (money) and `timeout:` (time). */
383
455
  maxTurns?: number;
456
+ /** `limits: {web.search: 10}` — this step's call limits, over the agent's
457
+ * (nearest wins per key). See limits.ts. */
458
+ limits?: Record<string, number>;
384
459
  /** 1-indexed line in the flow file. Diagnostics without a line make you
385
460
  * search; every real linter emits file:line. */
386
461
  line?: number;
@@ -426,7 +501,10 @@ export interface FlowInfo {
426
501
  * with `on:` saying which endings count. The finished run's summary and
427
502
  * result become this run's task. */
428
503
  after: string | null;
429
- on: "completed" | "failed" | "any";
504
+ /** `blocked` is a completed run whose verdict is BLOCKED — the work
505
+ * refused itself. `completed` no longer includes those: a follow-up to a
506
+ * publish that did not happen should not run as if it had. */
507
+ on: "completed" | "failed" | "blocked" | "any";
430
508
  /** `trigger: once` — an ISO 8601 instant; fires the first tick at or after
431
509
  * it, once, then never again. */
432
510
  at: string | null;
@@ -470,6 +548,13 @@ export interface FlowInfo {
470
548
  * failed all week is not going to fix itself, and every one of those runs
471
549
  * costs money and sends mail. Null: never quarantined. */
472
550
  disableAfter: number | null;
551
+ /** `pause_when:` — a marker (DONE, NOTHING LEFT) that, leading a line of
552
+ * any step's reply in the flow's latest run, pauses its schedule: the
553
+ * work is finished, so firing again would only spend money saying so.
554
+ * Derived from that run, like disable_after, so a run that finds work
555
+ * again (a person clicking Run after adding some) lifts it by itself.
556
+ * Null: never paused. */
557
+ pauseWhen: string | null;
473
558
  /** `sla:` — seconds a run of this flow is expected to take. A run still
474
559
  * going well past it is reported once, as a notification, and keeps
475
560
  * running: this is a smoke alarm, not a timeout. Null: no expectation. */
@@ -488,6 +573,15 @@ export interface FlowInfo {
488
573
  * default them to test: in a preview workspace, and as an eval's subject.
489
574
  * Only ever an opt-out of the safe default, never the other way round. */
490
575
  live: boolean;
576
+ /** `description:` — one line on what this flow is for, shown in listings
577
+ * and the trace. Read here so a documented, scaffolded field means
578
+ * something rather than sitting in every file unread. */
579
+ description: string | null;
580
+ /** Flow-level frontmatter values the parser could not read — a bad
581
+ * `overlap:`/`priority:`/`catchup:`/`on:`/`signature:`, or a step line
582
+ * whose `[[target]]` is malformed. Errors, the way a bad step option is,
583
+ * rather than a silent default. */
584
+ frontProblems: string[];
491
585
  steps: FlowStep[];
492
586
  }
493
587
  export declare const FLOW_TRIGGERS: readonly ["manual", "schedule", "webhook", "email", "flow", "once", "watch", "storage"];
@@ -529,7 +623,7 @@ export declare function parseInstant(raw: unknown): string | null;
529
623
  * removed a leading OR a trailing quote independently, so a value that
530
624
  * merely *ended* with one lost it — and
531
625
  *
532
- * verify: test "$(cat ../../storage/draft/published.run)" = "$FOLDRUN_RUN_ID"
626
+ * verify: test "$(cat workspace/storage/draft/published.run)" = "$FOLDRUN_RUN_ID"
533
627
  *
534
628
  * reached bash unbalanced, which exited 2 with "unexpected EOF" on every
535
629
  * run. The step could never pass, whatever the agent did, and nothing said
@@ -563,6 +657,13 @@ export declare function unquote(raw: string): string;
563
657
  * The marker must also END at a boundary, so BLOCKED does not match
564
658
  * BLOCKEDBY.
565
659
  */
660
+ /**
661
+ * The path of a `when: rows of <path>` condition, or null when the condition
662
+ * is a marker. `rows of` is the phrase `each:` uses for the same file, so a
663
+ * flow reads a CSV one way in both. The whole phrase is required: a bare
664
+ * `when: ROWS` stays the marker it always was.
665
+ */
666
+ export declare function whenRowsPath(when: string): string | null;
566
667
  export declare function markerPresent(text: string | null | undefined, marker: string): boolean;
567
668
  export declare function parseFlow(file: string, raw: string): FlowInfo;
568
669
  /** A schema written out: JSON, or YAML (parsed as frontmatter is, by the
@@ -593,40 +694,24 @@ export declare function flowHasLiveRun(tenant: string, workspace: string, flowNa
593
694
  * and the diff is exactly the lines that changed. A flow with no
594
695
  * frontmatter gains the smallest one that can carry the trigger.
595
696
  */
596
- export declare function setFlowTrigger(raw: string, opts: {
697
+ declare function setFlowTriggerLF(raw: string, opts: {
597
698
  trigger: FlowTrigger;
598
699
  schedule?: string;
599
700
  timezone?: string;
600
701
  }): string;
601
- /**
602
- * Rewrite one step's option lines — the board's step editor. Surgical, like
603
- * every flow rewrite: only the managed option lines of block `index` change;
604
- * the step line, its unmanaged options (approve stays the `!` marker's
605
- * business), other steps, comments and frontmatter survive verbatim.
606
- * `null` clears an option; values are clamped exactly as parseFlow reads
607
- * them, so what the editor writes is what the runner will do.
608
- */
609
- /**
610
- * Rewrite one step's instruction, and nothing else.
611
- *
612
- * The step line carries four things — the group number, the optional/approve
613
- * marker, the wikilink, and the instruction — and only the last is being
614
- * edited. Rebuilding the line from the parsed step would silently normalise
615
- * the other three (a "2?" becoming "2.", an en dash becoming an em dash), so
616
- * the prefix is taken verbatim from the file and only the tail replaced. A
617
- * flow's markdown stays the author's.
618
- */
619
- export declare function updateFlowStepInstruction(raw: string, index: number, instruction: string): string;
620
- export declare function updateFlowStep(raw: string, index: number, options: Partial<Record<"model" | "effort" | "retry" | "timeout" | "verify" | "when" | "case" | "else" | "loop" | "until" | "each" | "max", string | number | null>>): string;
621
- export declare function reorderFlowSteps(raw: string, groups: number[][]): string;
702
+ declare function updateFlowStepLF(raw: string, index: number, options: Partial<Record<"model" | "effort" | "retry" | "timeout" | "verify" | "when" | "case" | "else" | "loop" | "until" | "each" | "max" | "limits", string | number | null>>): string;
622
703
  /** Append a step as its own group at the end of the flow. */
623
- export declare function addFlowStep(raw: string, step: {
704
+ declare function addFlowStepLF(raw: string, step: {
624
705
  target: string;
625
706
  subflow?: boolean;
626
707
  instruction?: string;
627
708
  }): string;
628
- /** Drop one step, by its index in the flow's parsed steps. */
629
- export declare function removeFlowStep(raw: string, index: number): string;
709
+ export declare const setFlowTrigger: (raw: string, opts: Parameters<typeof setFlowTriggerLF>[1]) => string;
710
+ export declare const updateFlowStepInstruction: (raw: string, index: number, instruction: string) => string;
711
+ export declare const updateFlowStep: (raw: string, index: number, options: Parameters<typeof updateFlowStepLF>[2]) => string;
712
+ export declare const reorderFlowSteps: (raw: string, groups: number[][]) => string;
713
+ export declare const addFlowStep: (raw: string, step: Parameters<typeof addFlowStepLF>[1]) => string;
714
+ export declare const removeFlowStep: (raw: string, index: number) => string;
630
715
  export declare function listFlows(tenant: string, workspace: string): FlowInfo[];
631
716
  /** One flow of a workspace by name, or null. The same read listFlows makes,
632
717
  * named so callers that want a single flow — the approval rules, the
@@ -784,12 +869,50 @@ export interface RunEvent {
784
869
  * state write, a withheld secret. The run page lists these; the text is
785
870
  * still the trace's line. See test-mode.ts. */
786
871
  effect?: TestEffect;
872
+ /** Set when a person was involved mid-step: a question the agent asked
873
+ * (ask_person), its answer, or a message a person sent in (operator.ts). */
874
+ operator?: OperatorEvent;
875
+ /** Set on a tool event a SUB-AGENT made (`subagents:`): its name. The
876
+ * trace labels the call with it; cost stays the step's. */
877
+ subagent?: string;
878
+ /** Set on an error a failed check wrote — `verify:` (its headline and its
879
+ * detail), `output: json`, a schema. What the runner reads to decline a
880
+ * retry of an outward step whose tools already ran. */
881
+ check?: boolean;
882
+ }
883
+ /** A question an agent asked a person while its step ran (tools: [ask]). */
884
+ export interface StepQuestion {
885
+ id: string;
886
+ question: string;
887
+ options?: string[];
888
+ askedAt: string;
889
+ answer?: string;
890
+ answeredAt?: string;
891
+ by?: string | null;
892
+ /** true when the wait ran out with no answer. */
893
+ unanswered?: boolean;
894
+ }
895
+ /** A message a person sent into a running step, and when the agent got it. */
896
+ export interface StepMessage {
897
+ text: string;
898
+ by?: string | null;
899
+ at: string;
900
+ deliveredAt: string;
787
901
  }
788
902
  /** One attempt of a step — see StepRecord.tries. */
789
903
  export interface StepAttempt {
790
- /** Which attempt, 1-based: `attempts` on the step at the time. */
904
+ /** Which try, 1-based, in the order they ran: a retry is the next try,
905
+ * and so is the full re-run after a slim go the browser pod was lost
906
+ * under. Equal to the step's attempt (`attempts` at the time) unless
907
+ * `attempt` says otherwise. */
791
908
  n: number;
792
- status: "completed" | "failed";
909
+ /** The step's attempt this try belongs to, written only when it is not
910
+ * `n` — after a lost slim go, its full re-run is try 2 of attempt 1. */
911
+ attempt?: number;
912
+ /** `lost`: a slim go the account's browser pod went away under, which
913
+ * the step ran again on the full image within the same attempt — a row
914
+ * of its own (image slim), before the full go's. */
915
+ status: "completed" | "failed" | "lost";
793
916
  costUsd: number | null;
794
917
  tokens: {
795
918
  input: number;
@@ -800,6 +923,11 @@ export interface StepAttempt {
800
923
  finishedAt: string;
801
924
  /** The last error event this attempt wrote, when it failed. */
802
925
  error?: string;
926
+ /** The runner image this attempt ended on, and — when it browsed through
927
+ * the account's pod from slim — what happened to the pod: reconnects,
928
+ * the re-run on full, or why it was not re-run. */
929
+ image?: "full" | "slim";
930
+ browserPod?: string;
803
931
  }
804
932
  /** The step's totals across its attempts, from the rows. Null where no
805
933
  * attempt reported the figure — an in-process step rents no sandbox, and
@@ -808,6 +936,11 @@ export declare function sumAttempts(tries: StepAttempt[]): Pick<StepAttempt, "co
808
936
  export interface StepRecord {
809
937
  agent: string;
810
938
  instruction: string;
939
+ /** Questions the agent asked a person mid-step (tools: [ask]), newest
940
+ * last. One without an answer (and not `unanswered`) is still open. */
941
+ questions?: StepQuestion[];
942
+ /** Messages a person sent into the running step, as delivered. */
943
+ messages?: StepMessage[];
811
944
  group: number;
812
945
  optional: boolean;
813
946
  /** Carried from the flow — see FlowStep. Both are resolved at run
@@ -850,6 +983,27 @@ export interface StepRecord {
850
983
  /** A size class a retry moved the step up to (an evicted or OOM-killed
851
984
  * attempt), overriding the agent's own `size:` for the attempts after. */
852
985
  sizeUp?: "large" | "heavy";
986
+ /** Which runner image the step's sandbox ran: `full` (browsers in it) or
987
+ * `slim`, why, and `pod` when a browsing step ran slim and browsed
988
+ * through the account's browser pod. The last attempt's; absent where
989
+ * the executor has one image (docker, in-process). */
990
+ image?: {
991
+ variant: "full" | "slim";
992
+ why?: string;
993
+ pod?: boolean;
994
+ } | null;
995
+ /** Slim browsing through the account's pod (browser-pod.ts): reconnect
996
+ * tries and how many got through; when the pod was lost, why and what
997
+ * the step had written by then; and what happened — `fallback` (the
998
+ * step had only read and ran again on the full image) or `failure` (it
999
+ * had written, so it was not re-run). */
1000
+ browserPod?: (BrowserPodOutcome & {
1001
+ fallback?: string;
1002
+ failure?: string;
1003
+ }) | null;
1004
+ /** The slim go a lost browser pod ended, set aside while the step re-runs
1005
+ * on full; recordAttempt makes it a `lost` row of `tries` and clears it. */
1006
+ podLostTry?: Omit<StepAttempt, "n" | "attempt" | "startedAt"> | null;
853
1007
  /** When the step actually started and finished executing — not when the
854
1008
  * run reached it. A step's events approximate this (first and last), but
855
1009
  * a model can think for minutes before its first tool call, and a step
@@ -876,6 +1030,7 @@ export interface StepRecord {
876
1030
  schemaPath?: string;
877
1031
  parallel?: number;
878
1032
  maxTurns?: number;
1033
+ limits?: Record<string, number>;
879
1034
  /** The parsed JSON an `output: json` step returned. Kept beside the prose
880
1035
  * result rather than instead of it: the reply is still what a person
881
1036
  * reads on the run page, and the data is what the next step computes on. */
@@ -1001,6 +1156,20 @@ export interface RunRecord {
1001
1156
  * simply gets its first line — which is still better than a status.
1002
1157
  */
1003
1158
  summary?: string | null;
1159
+ /**
1160
+ * What the run's work concluded, when its summary leads with one of the
1161
+ * four verdict words every desk ends on: GOOD, BAD, QUIET, BLOCKED.
1162
+ *
1163
+ * `status` says whether the machine ran — a step crashed, a budget ran
1164
+ * out. The verdict says what the agents found when it did. They are
1165
+ * different facts: a publisher that checks, sees the wrong change in
1166
+ * front of it and refuses to push has run perfectly, and its run is
1167
+ * `completed`. Until this field, that run was a green tick beside a line
1168
+ * reading BLOCKED (run-mu4sdg8z-1otr, 2026-09-17) — the one outcome a
1169
+ * person most needed to see, drawn as success. Null: the summary does
1170
+ * not lead with a verdict, and the run reads exactly as it always did.
1171
+ */
1172
+ verdict?: Verdict | null;
1004
1173
  /** The flow's per-run `budget:` at the time this run started, so the cap
1005
1174
  * a run was held to is on its record rather than read from a file that
1006
1175
  * may since have changed. Null: none. */
@@ -1214,6 +1383,18 @@ export declare function runFailure(run: RunRecord): {
1214
1383
  * runFailure, which puts the reason for a failure on the list for the same
1215
1384
  * reason — the sentence was in the record all along.
1216
1385
  */
1386
+ export declare const VERDICTS: readonly ["GOOD", "BAD", "QUIET", "BLOCKED"];
1387
+ export type Verdict = (typeof VERDICTS)[number];
1388
+ /**
1389
+ * The verdict a headline leads with, or null. Leading, whole-word and
1390
+ * upper-case only — "GOOD — 6 links applied", "**BLOCKED**: nothing
1391
+ * pushed", "BAD: 3 pages dropped". A summary that merely contains the word
1392
+ * ("no BLOCKED items") has no verdict, for the reason markerPresent gives.
1393
+ */
1394
+ export declare function verdictOf(summary: string | null | undefined): Verdict | null;
1395
+ /** A run's verdict: recorded when it finished, else read from its summary,
1396
+ * so runs from before the field existed show one too. */
1397
+ export declare function runVerdict(run: Pick<RunRecord, "verdict" | "summary">): Verdict | null;
1217
1398
  export declare function runSummary(run: RunRecord): string | null;
1218
1399
  /**
1219
1400
  * What one step concluded, in a line.