@foldrun/core 0.2.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.
- package/LICENSE +202 -0
- package/README.md +239 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +43 -0
- package/dist/src/agent-tools.d.ts +31 -0
- package/dist/src/agent-tools.js +99 -0
- package/dist/src/agents-md.d.ts +9 -0
- package/dist/src/agents-md.js +83 -0
- package/dist/src/api-tools.d.ts +38 -0
- package/dist/src/api-tools.js +313 -0
- package/dist/src/approvals.d.ts +39 -0
- package/dist/src/approvals.js +103 -0
- package/dist/src/arrange.d.ts +8 -0
- package/dist/src/arrange.js +40 -0
- package/dist/src/catalog.d.ts +108 -0
- package/dist/src/catalog.js +255 -0
- package/dist/src/completions.d.ts +61 -0
- package/dist/src/completions.js +578 -0
- package/dist/src/confine.d.ts +31 -0
- package/dist/src/confine.js +298 -0
- package/dist/src/container.d.ts +30 -0
- package/dist/src/container.js +172 -0
- package/dist/src/context-tools.d.ts +81 -0
- package/dist/src/context-tools.js +347 -0
- package/dist/src/deploy.d.ts +100 -0
- package/dist/src/deploy.js +377 -0
- package/dist/src/egress.d.ts +75 -0
- package/dist/src/egress.js +133 -0
- package/dist/src/evals.d.ts +150 -0
- package/dist/src/evals.js +651 -0
- package/dist/src/flow-lint.d.ts +16 -0
- package/dist/src/flow-lint.js +177 -0
- package/dist/src/git-hook.d.ts +1 -0
- package/dist/src/git-hook.js +70 -0
- package/dist/src/git.d.ts +37 -0
- package/dist/src/git.js +101 -0
- package/dist/src/gitrepo.d.ts +95 -0
- package/dist/src/gitrepo.js +355 -0
- package/dist/src/history.d.ts +82 -0
- package/dist/src/history.js +259 -0
- package/dist/src/kinds.d.ts +95 -0
- package/dist/src/kinds.js +421 -0
- package/dist/src/library.d.ts +94 -0
- package/dist/src/library.js +341 -0
- package/dist/src/linkable.d.ts +20 -0
- package/dist/src/linkable.js +113 -0
- package/dist/src/model-probe.d.ts +21 -0
- package/dist/src/model-probe.js +81 -0
- package/dist/src/notify.d.ts +61 -0
- package/dist/src/notify.js +252 -0
- package/dist/src/oauth-presets.d.ts +13 -0
- package/dist/src/oauth-presets.js +39 -0
- package/dist/src/okf.d.ts +220 -0
- package/dist/src/okf.js +656 -0
- package/dist/src/openapi.d.ts +66 -0
- package/dist/src/openapi.js +342 -0
- package/dist/src/paths.d.ts +33 -0
- package/dist/src/paths.js +78 -0
- package/dist/src/platform.d.ts +32 -0
- package/dist/src/platform.js +48 -0
- package/dist/src/providers.d.ts +47 -0
- package/dist/src/providers.js +114 -0
- package/dist/src/refs.d.ts +20 -0
- package/dist/src/refs.js +78 -0
- package/dist/src/run-container.d.ts +227 -0
- package/dist/src/run-container.js +760 -0
- package/dist/src/runner.d.ts +241 -0
- package/dist/src/runner.js +3215 -0
- package/dist/src/runtime.d.ts +41 -0
- package/dist/src/runtime.js +357 -0
- package/dist/src/script-tools.d.ts +62 -0
- package/dist/src/script-tools.js +296 -0
- package/dist/src/secret-files.d.ts +12 -0
- package/dist/src/secret-files.js +92 -0
- package/dist/src/secrets.d.ts +147 -0
- package/dist/src/secrets.js +626 -0
- package/dist/src/starter.d.ts +21 -0
- package/dist/src/starter.js +399 -0
- package/dist/src/step-exec.d.ts +136 -0
- package/dist/src/step-exec.js +391 -0
- package/dist/src/storage.d.ts +123 -0
- package/dist/src/storage.js +640 -0
- package/dist/src/store.d.ts +1052 -0
- package/dist/src/store.js +2378 -0
- package/dist/src/tar.d.ts +30 -0
- package/dist/src/tar.js +111 -0
- package/dist/src/tool-names.d.ts +39 -0
- package/dist/src/tool-names.js +79 -0
- package/dist/src/tool-programs.d.ts +18 -0
- package/dist/src/tool-programs.js +52 -0
- package/dist/src/tool-test.d.ts +19 -0
- package/dist/src/tool-test.js +335 -0
- package/dist/src/translator.d.ts +159 -0
- package/dist/src/translator.js +986 -0
- package/dist/src/triggers.d.ts +60 -0
- package/dist/src/triggers.js +270 -0
- package/dist/src/webhook.d.ts +47 -0
- package/dist/src/webhook.js +148 -0
- package/index.ts +44 -0
- package/package.json +64 -0
- package/src/agent-tools.ts +138 -0
- package/src/agents-md.ts +81 -0
- package/src/api-tools.ts +389 -0
- package/src/approvals.ts +129 -0
- package/src/arrange.ts +42 -0
- package/src/catalog.ts +321 -0
- package/src/completions.ts +652 -0
- package/src/confine.ts +350 -0
- package/src/container.ts +220 -0
- package/src/context-tools.ts +435 -0
- package/src/deploy.ts +458 -0
- package/src/egress.ts +176 -0
- package/src/evals.ts +812 -0
- package/src/flow-lint.ts +215 -0
- package/src/git-hook.ts +73 -0
- package/src/git.ts +117 -0
- package/src/gitrepo.ts +391 -0
- package/src/history.ts +329 -0
- package/src/kinds.ts +507 -0
- package/src/library.ts +419 -0
- package/src/linkable.ts +123 -0
- package/src/model-probe.ts +112 -0
- package/src/notify.ts +281 -0
- package/src/oauth-presets.ts +54 -0
- package/src/okf.ts +768 -0
- package/src/openapi.ts +383 -0
- package/src/paths.ts +79 -0
- package/src/platform.ts +87 -0
- package/src/providers.ts +147 -0
- package/src/refs.ts +83 -0
- package/src/run-container.ts +883 -0
- package/src/runner.ts +3446 -0
- package/src/runtime.ts +404 -0
- package/src/script-tools.ts +383 -0
- package/src/secret-files.ts +101 -0
- package/src/secrets.ts +799 -0
- package/src/starter.ts +415 -0
- package/src/step-exec.ts +498 -0
- package/src/storage.ts +771 -0
- package/src/store.ts +2985 -0
- package/src/tar.ts +124 -0
- package/src/tool-names.ts +91 -0
- package/src/tool-programs.ts +65 -0
- package/src/tool-test.ts +401 -0
- package/src/translator.ts +1029 -0
- package/src/triggers.ts +287 -0
- package/src/webhook.ts +183 -0
- package/templates/blog-desk/AGENTS.md +13 -0
- package/templates/blog-desk/agents/researcher/agent.md +11 -0
- package/templates/blog-desk/agents/writer/agent.md +8 -0
- package/templates/blog-desk/evals/writer-quality.md +10 -0
- package/templates/blog-desk/flows/publish.md +10 -0
- package/templates/blog-desk/knowledge/house-style.md +9 -0
- package/templates/blog-desk/knowledge/index.md +9 -0
- package/templates/hello/AGENTS.md +18 -0
- package/templates/hello/agents/curator/agent.md +32 -0
- package/templates/hello/agents/notetaker/agent.md +24 -0
- package/templates/hello/evals/house-style.md +16 -0
- package/templates/hello/flows/memory-review.md +7 -0
- package/templates/hello/flows/note.md +7 -0
- package/templates/hello/knowledge/house-style.md +15 -0
- package/templates/hello/knowledge/index.md +9 -0
- package/templates/hello/skills/plain-english/SKILL.md +11 -0
- package/templates/hello/state/publishing.md +7 -0
- package/templates/hello/tools/wordcount/run.py +13 -0
- package/templates/hello/tools/wordcount/tool.md +22 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { type FlowStep, type RunRecord } from "./store.ts";
|
|
2
|
+
/** How much of the earlier groups' output a step is handed, in characters.
|
|
3
|
+
* Newest groups are kept when the total is over it. */
|
|
4
|
+
export declare const CONTEXT_CHARS = 30000;
|
|
5
|
+
/** tools: [desks] — how many other-workspace runs cross into a step, in all. */
|
|
6
|
+
export declare const DESK_RUNS_CAP = 100;
|
|
7
|
+
/**
|
|
8
|
+
* What a group is handed from the groups before it: all of them, oldest
|
|
9
|
+
* first, so a group-3 reporter sees what group 1 measured and not only what
|
|
10
|
+
* group 2 diagnosed. When the total is over the cap, the oldest groups are
|
|
11
|
+
* dropped whole before anything is cut mid-text — the newest is the one the
|
|
12
|
+
* step is most likely to be about.
|
|
13
|
+
*/
|
|
14
|
+
export declare function joinEarlierGroups(groupResults: (string | null)[], gi: number, cap?: number): string | null;
|
|
15
|
+
export interface DiscoveredSkill {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
/** Tags that must be present on the run for this skill to load. A skill
|
|
19
|
+
* with none always loads. Mirrors "apply when this check failed". */
|
|
20
|
+
when: string[];
|
|
21
|
+
/** Path to SKILL.md (or the flat .md), relative to the agent directory. */
|
|
22
|
+
path: string;
|
|
23
|
+
/** Skill folder relative to the agent directory, or null for flat skills. */
|
|
24
|
+
dir: string | null;
|
|
25
|
+
hasScripts: boolean;
|
|
26
|
+
/** The rest of the Agent Skills layout: references/ and assets/. */
|
|
27
|
+
hasReferences: boolean;
|
|
28
|
+
hasAssets: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function discoverSkills(agentDir: string, subdir?: string): DiscoveredSkill[];
|
|
31
|
+
/**
|
|
32
|
+
* AGENTS.md, at both scopes it can live at.
|
|
33
|
+
*
|
|
34
|
+
* `AGENTS.md` is the Linux Foundation name; `project.md` is still read so
|
|
35
|
+
* nothing written before the rename breaks.
|
|
36
|
+
*
|
|
37
|
+
* Returns null rather than throwing on unparseable YAML: a broken shared file
|
|
38
|
+
* must not take down every agent under it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function readAgentsMd(dir: string): {
|
|
41
|
+
data: Record<string, unknown>;
|
|
42
|
+
body: string;
|
|
43
|
+
} | null;
|
|
44
|
+
/**
|
|
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.
|
|
50
|
+
*/
|
|
51
|
+
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;
|
|
54
|
+
/**
|
|
55
|
+
* The prose from AGENTS.md, outermost first.
|
|
56
|
+
*
|
|
57
|
+
* This was written by every scaffold, shown in the dashboard, described in the
|
|
58
|
+
* template as "context every agent here shares" — and never read. Only the
|
|
59
|
+
* frontmatter was, so the instructions people actually wrote reached no model.
|
|
60
|
+
*/
|
|
61
|
+
export declare function sharedInstructions(agentDir: string, tenant: string): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Which of an agent's in-scope skills load for this run.
|
|
64
|
+
*
|
|
65
|
+
* Two independent gates: `skills:` in the agent's frontmatter is an allowlist
|
|
66
|
+
* (absent means everything in scope), and a skill's own `when:` names the run
|
|
67
|
+
* tags it is for (absent means every run).
|
|
68
|
+
*
|
|
69
|
+
* Note what an empty `tags` means — a skill with `when:` matches nothing, so
|
|
70
|
+
* an untagged run gets none of them. That is the intent, and it is also why
|
|
71
|
+
* failing to thread the run's tags this far made every `when:` skill
|
|
72
|
+
* permanently invisible instead of conditionally loaded.
|
|
73
|
+
*/
|
|
74
|
+
export declare function applicableSkills<T extends {
|
|
75
|
+
name: string;
|
|
76
|
+
when: string[];
|
|
77
|
+
}>(skills: T[], only: string[], tags: string[]): T[];
|
|
78
|
+
/**
|
|
79
|
+
* [[name]] as a reference to a document or file — the Obsidian instinct,
|
|
80
|
+
* honoured where it is safe. A prompt is text the model acts on, and a
|
|
81
|
+
* model cannot open "[[sources-of-conveyancers]]"; it can open
|
|
82
|
+
* `../../knowledge/sources-of-conveyancers.md`. So links resolve to real
|
|
83
|
+
* agent-relative paths BEFORE the prompt is built, matching by filename or
|
|
84
|
+
* OKF title, hyphen/underscore/case-blind. Anything unmatched — agent
|
|
85
|
+
* names, flow links, prose in brackets — passes through untouched: this is
|
|
86
|
+
* sugar over paths, never a gate in front of them.
|
|
87
|
+
*/
|
|
88
|
+
/**
|
|
89
|
+
* Whether an evaluator's reply actually carries its `until:` marker.
|
|
90
|
+
*
|
|
91
|
+
* Not a substring test. A reviewer that writes "APPROVED once the tribunal
|
|
92
|
+
* sentence is corrected" contains the word, and a substring match reads that
|
|
93
|
+
* conditional as a pass — the loop ends, the correction is never made, and the
|
|
94
|
+
* step after it inherits the problem the loop existed to catch. That happened:
|
|
95
|
+
* a factual error rode a conditional approval past the gate.
|
|
96
|
+
*
|
|
97
|
+
* So the marker has to stand alone on its own line, which is a thing a model
|
|
98
|
+
* does deliberately and cannot do by accident mid-sentence. Trailing
|
|
99
|
+
* punctuation is allowed ("APPROVED." is still a decision), and the line may
|
|
100
|
+
* sit anywhere in the reply, so an approval followed by notes still passes —
|
|
101
|
+
* the point is that the word is a switch, not a clause.
|
|
102
|
+
*/
|
|
103
|
+
export declare function saysUntilMarker(result: string | null, until: string): boolean;
|
|
104
|
+
export declare function resolveDocLinks(text: string, workspaceRoot: string): string;
|
|
105
|
+
/**
|
|
106
|
+
* Copy a directory tree by reading and writing the bytes.
|
|
107
|
+
*
|
|
108
|
+
* Not fs.cpSync: that copies through the kernel's clone/copy_file_range
|
|
109
|
+
* path, which on a bind-mounted or network volume — exactly where a hosted
|
|
110
|
+
* install keeps its data — can report success and leave a zero-length file
|
|
111
|
+
* behind. Every archived run output was empty, and nothing failed to say
|
|
112
|
+
* so, which is the worst shape a bug can take. This works on every
|
|
113
|
+
* filesystem, and normalises the mode while it is here: what an agent
|
|
114
|
+
* produced belongs to the platform, not to the container umask that
|
|
115
|
+
* happened to create it.
|
|
116
|
+
*/
|
|
117
|
+
export declare function copyTreeBytes(from: string, to: string): void;
|
|
118
|
+
/** How many times a busy provider is given another moment, and the longest
|
|
119
|
+
* any one wait may be. Three is the industry's habit and the point where
|
|
120
|
+
* waiting longer stops being kinder than failing. */
|
|
121
|
+
export declare const OVERLOAD_RETRIES = 3;
|
|
122
|
+
/**
|
|
123
|
+
* A refusal the SAME supply may answer in a moment — the provider is busy,
|
|
124
|
+
* not the account broken.
|
|
125
|
+
*
|
|
126
|
+
* The distinction is the whole point. 429 means "too many requests", and a
|
|
127
|
+
* provider says it for two unrelated reasons: the endpoint is momentarily
|
|
128
|
+
* saturated (wait, and it works), or the account is out of credit or over
|
|
129
|
+
* its quota (wait forever, it never works). Money words decide it: a 429
|
|
130
|
+
* that mentions credit, quota or billing is the account, and belongs to the
|
|
131
|
+
* fallback provider, not to a retry that can only fail again.
|
|
132
|
+
*/
|
|
133
|
+
export declare function isTransientOverload(text: string): boolean;
|
|
134
|
+
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;
|
|
137
|
+
/**
|
|
138
|
+
* Drive a run to completion: groups in order, approval gates, retries.
|
|
139
|
+
*
|
|
140
|
+
* Separate from starting one so a run can be picked up again. The loop reads
|
|
141
|
+
* every decision off the run record — `pending` steps are the work left, and
|
|
142
|
+
* a completed group's results are already on it — so re-entering it on a
|
|
143
|
+
* partly-finished record continues rather than restarts.
|
|
144
|
+
*
|
|
145
|
+
* `parkOnApproval` — a queue worker's slot is too expensive to spend polling
|
|
146
|
+
* a file for a person who may answer tomorrow. With it set, an approval gate
|
|
147
|
+
* saves the run as parked and returns; the approval API re-enqueues it and a
|
|
148
|
+
* worker re-enters this loop, which continues where the record says it
|
|
149
|
+
* stopped. Without it (the CLI, whose process belongs to the person waiting)
|
|
150
|
+
* the gate blocks in place exactly as before.
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* Turn a gate's `preview:` patterns into the files under storage/ that
|
|
154
|
+
* exist right now, relative to storage/. `*` matches within a segment,
|
|
155
|
+
* `**` across segments; a pattern with no wildcard is kept only if the file
|
|
156
|
+
* is there. Sorted, deduplicated, capped — a preview is a handful of files,
|
|
157
|
+
* not a directory dump.
|
|
158
|
+
*/
|
|
159
|
+
export declare function resolvePreview(storageDir: string, patterns: string[], cap?: number): string[];
|
|
160
|
+
/** Store what a run has left in storage/ so far. Never fails the run: a
|
|
161
|
+
* store that is down costs the gate its thumbnail, not the run its step. */
|
|
162
|
+
/**
|
|
163
|
+
* Copy onto the in-memory run any gate decision that reached the record on
|
|
164
|
+
* disk while this driver was busy — status, stamps, note, the event line —
|
|
165
|
+
* so the driver's next save keeps it. Returns whether anything landed. Only
|
|
166
|
+
* steps this copy still shows as asking are touched: everything else on the
|
|
167
|
+
* record is this driver's own, and newer in memory than on disk.
|
|
168
|
+
*/
|
|
169
|
+
export declare function adoptDecisions(run: RunRecord, disk: RunRecord | null): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* The runs this process is driving right now. The one fact reconcile needs:
|
|
172
|
+
* a run in here is alive however quiet its step is, and a `running` run not
|
|
173
|
+
* in here has no driver — this is the only process that drives.
|
|
174
|
+
*/
|
|
175
|
+
export declare const drivingRuns: Set<string>;
|
|
176
|
+
export declare function driveRun(tenant: string, workspace: string, run: RunRecord, modelOverride?: string | null, tags?: string[], opts?: {
|
|
177
|
+
parkOnApproval?: boolean;
|
|
178
|
+
effortOverride?: string | null;
|
|
179
|
+
}): Promise<void>;
|
|
180
|
+
export declare function loadFlow(tenant: string, workspace: string, flowName: string): import("./store.ts").FlowInfo | null;
|
|
181
|
+
export interface Reconciliation {
|
|
182
|
+
runId: string;
|
|
183
|
+
tenant: string;
|
|
184
|
+
workspace: string;
|
|
185
|
+
/** What was done about it: closed as dead, or picked back up. */
|
|
186
|
+
action: "closed" | "resumed";
|
|
187
|
+
/** Steps that were mid-flight when the process went away. */
|
|
188
|
+
interrupted: string[];
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Close out runs abandoned by a process that is no longer here.
|
|
192
|
+
*
|
|
193
|
+
* Runs execute in the server process, and nothing writes a terminal status
|
|
194
|
+
* when that process goes away — so a deploy, a crash or a Ctrl-C during a run
|
|
195
|
+
* leaves `status: "running"` on disk forever. The run history then contains
|
|
196
|
+
* entries that are not merely stale but false: they claim to be in progress,
|
|
197
|
+
* the dashboard renders them as live, and no one can tell them apart from a
|
|
198
|
+
* step that is genuinely still thinking.
|
|
199
|
+
*
|
|
200
|
+
* Called at startup, before anything reads run history. A run still being
|
|
201
|
+
* worked on by a live process is protected by the idle window, not by a lock —
|
|
202
|
+
* so this is safe to call while runs are in flight, and deliberately
|
|
203
|
+
* conservative about what it declares dead.
|
|
204
|
+
*
|
|
205
|
+
* A run genuinely waiting for a person is left alone: that state legitimately
|
|
206
|
+
* outlives any process. A run whose person already answered is not waiting,
|
|
207
|
+
* and is picked back up — see below.
|
|
208
|
+
*/
|
|
209
|
+
export declare function reconcileRuns(tenant: string, now?: number): Reconciliation[];
|
|
210
|
+
/**
|
|
211
|
+
* The same, for every account this installation holds.
|
|
212
|
+
*
|
|
213
|
+
* A server reconciles on boot, and it boots for everyone — reconciling only
|
|
214
|
+
* one hard-coded account left every other account's interrupted runs marked
|
|
215
|
+
* `running` forever, with no process left to finish them.
|
|
216
|
+
*/
|
|
217
|
+
export declare function reconcileAllRuns(now?: number): Reconciliation[];
|
|
218
|
+
/**
|
|
219
|
+
* Wait for a run to stop being in progress.
|
|
220
|
+
*
|
|
221
|
+
* Runs start and return immediately, which is right for a schedule and wrong
|
|
222
|
+
* for a caller putting an agent behind their own request: they want the
|
|
223
|
+
* answer, not a receipt. Polls the run record rather than holding a callback,
|
|
224
|
+
* so it works from a different process than the one doing the work.
|
|
225
|
+
*
|
|
226
|
+
* `awaiting-approval` counts as stopping. It is not finished, but it is not
|
|
227
|
+
* going to progress without a person, and a caller blocked on it would hang
|
|
228
|
+
* for as long as the approval window allows.
|
|
229
|
+
*/
|
|
230
|
+
export declare function waitForRun(tenant: string, workspace: string, runId: string, timeoutMs?: number): Promise<{
|
|
231
|
+
run: RunRecord | null;
|
|
232
|
+
timedOut: boolean;
|
|
233
|
+
}>;
|
|
234
|
+
/**
|
|
235
|
+
* What a run produced, for a caller that wanted an answer.
|
|
236
|
+
*
|
|
237
|
+
* The last step that actually returned something: a flow's final step is the
|
|
238
|
+
* conclusion, and skipping back past steps that were skipped or produced
|
|
239
|
+
* nothing is what makes `?wait=true` useful rather than merely blocking.
|
|
240
|
+
*/
|
|
241
|
+
export declare function runResult(run: RunRecord): string | null;
|