@foldrun/core 2.3.0 → 2.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.
- package/dist/src/agent-activity.d.ts +5 -0
- package/dist/src/agent-activity.js +31 -0
- package/dist/src/agent-import.js +1 -1
- package/dist/src/agent-tools.js +1 -1
- package/dist/src/agents-md.js +1 -1
- package/dist/src/alerts.d.ts +60 -0
- package/dist/src/alerts.js +161 -0
- package/dist/src/browser-pod.js +4 -0
- package/dist/src/capture.d.ts +23 -0
- package/dist/src/capture.js +80 -0
- package/dist/src/compare.d.ts +57 -0
- package/dist/src/compare.js +116 -0
- package/dist/src/completions.js +1 -0
- package/dist/src/deploy.d.ts +36 -1
- package/dist/src/deploy.js +170 -11
- package/dist/src/egress.d.ts +16 -1
- package/dist/src/evals.d.ts +5 -1
- package/dist/src/evals.js +26 -18
- package/dist/src/frontmatter-fields.js +9 -0
- package/dist/src/frontmatter-schema.js +3 -0
- package/dist/src/history.d.ts +4 -0
- package/dist/src/history.js +15 -0
- package/dist/src/hooks.js +1 -1
- package/dist/src/insights.d.ts +30 -0
- package/dist/src/insights.js +152 -0
- package/dist/src/issues.d.ts +140 -0
- package/dist/src/issues.js +302 -0
- package/dist/src/library.js +1 -1
- package/dist/src/limits.d.ts +8 -0
- package/dist/src/limits.js +36 -5
- package/dist/src/linkable.js +1 -1
- package/dist/src/matter.d.ts +8 -0
- package/dist/src/matter.js +28 -0
- package/dist/src/model-credential.d.ts +14 -0
- package/dist/src/model-credential.js +40 -0
- package/dist/src/model-env.d.ts +14 -2
- package/dist/src/model-env.js +34 -9
- package/dist/src/network.d.ts +12 -0
- package/dist/src/network.js +25 -1
- package/dist/src/notify.d.ts +16 -0
- package/dist/src/notify.js +17 -7
- package/dist/src/okf.js +1 -1
- package/dist/src/online-checks.d.ts +76 -0
- package/dist/src/online-checks.js +432 -0
- package/dist/src/operator.d.ts +277 -0
- package/dist/src/operator.js +176 -2
- package/dist/src/otel-config.d.ts +18 -0
- package/dist/src/otel-config.js +69 -0
- package/dist/src/otel.d.ts +38 -0
- package/dist/src/otel.js +85 -0
- package/dist/src/outcome.d.ts +40 -0
- package/dist/src/outcome.js +77 -0
- package/dist/src/package.js +1 -1
- package/dist/src/platform.d.ts +1 -1
- package/dist/src/prompt-layers.d.ts +30 -0
- package/dist/src/prompt-layers.js +77 -0
- package/dist/src/provider-health.js +1 -1
- package/dist/src/providers.d.ts +5 -0
- package/dist/src/providers.js +2 -2
- package/dist/src/run-container.d.ts +16 -2
- package/dist/src/run-container.js +57 -5
- package/dist/src/runner.d.ts +53 -2
- package/dist/src/runner.js +356 -70
- package/dist/src/runs-filter.d.ts +48 -0
- package/dist/src/runs-filter.js +73 -0
- package/dist/src/scan.d.ts +19 -1
- package/dist/src/scan.js +60 -7
- package/dist/src/starter.js +4 -2
- package/dist/src/step-exec.d.ts +29 -1
- package/dist/src/step-exec.js +106 -6
- package/dist/src/store.d.ts +130 -3
- package/dist/src/store.js +145 -6
- package/dist/src/tokens.d.ts +38 -0
- package/dist/src/tokens.js +57 -0
- package/dist/src/tool-names.js +1 -1
- package/dist/src/tool-programs.js +1 -1
- package/dist/src/trace-tree.d.ts +40 -0
- package/dist/src/trace-tree.js +156 -0
- package/dist/src/translator.d.ts +8 -0
- package/dist/src/translator.js +7 -2
- package/dist/src/trigger-log.d.ts +1 -1
- package/dist/src/workspace-check.d.ts +18 -0
- package/dist/src/workspace-check.js +34 -0
- package/package.json +2 -2
- package/src/agent-activity.ts +32 -0
- package/src/agent-import.ts +1 -1
- package/src/agent-tools.ts +1 -1
- package/src/agents-md.ts +1 -1
- package/src/alerts.ts +190 -0
- package/src/browser-pod.ts +3 -0
- package/src/capture.ts +88 -0
- package/src/compare.ts +146 -0
- package/src/completions.ts +1 -0
- package/src/deploy.ts +184 -10
- package/src/egress.ts +12 -2
- package/src/evals.ts +26 -18
- package/src/frontmatter-fields.ts +9 -0
- package/src/frontmatter-schema.ts +3 -0
- package/src/history.ts +13 -0
- package/src/hooks.ts +1 -1
- package/src/insights.ts +174 -0
- package/src/issues.ts +396 -0
- package/src/library.ts +1 -1
- package/src/limits.ts +38 -5
- package/src/linkable.ts +1 -1
- package/src/matter.ts +33 -0
- package/src/model-credential.ts +45 -0
- package/src/model-env.ts +42 -10
- package/src/network.ts +25 -1
- package/src/notify.ts +22 -7
- package/src/okf.ts +1 -1
- package/src/online-checks.ts +450 -0
- package/src/operator.ts +213 -3
- package/src/otel-config.ts +67 -0
- package/src/otel.ts +100 -0
- package/src/outcome.ts +90 -0
- package/src/package.ts +1 -1
- package/src/platform.ts +1 -1
- package/src/prompt-layers.ts +90 -0
- package/src/provider-health.ts +1 -1
- package/src/providers.ts +7 -2
- package/src/run-container.ts +66 -7
- package/src/runner.ts +363 -46
- package/src/runs-filter.ts +88 -0
- package/src/scan.ts +68 -7
- package/src/starter.ts +4 -2
- package/src/step-exec.ts +147 -9
- package/src/store.ts +254 -8
- package/src/tokens.ts +68 -0
- package/src/tool-names.ts +1 -1
- package/src/tool-programs.ts +1 -1
- package/src/trace-tree.ts +187 -0
- package/src/translator.ts +13 -2
- package/src/trigger-log.ts +1 -0
- package/src/workspace-check.ts +41 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// A run as one tree (FOL-72 step 5): run → step → model call → tool call,
|
|
2
|
+
// each with when it started and ended, what it cost in tokens and how it
|
|
3
|
+
// ended — the waterfall every agent-tracing tool opens on. Built from the
|
|
4
|
+
// run record alone (steps, step.calls, the paired tool events), so the run
|
|
5
|
+
// page, GET …/runs/<id>/trace and `foldrun report --tree` read one function.
|
|
6
|
+
//
|
|
7
|
+
// A tool call nests under the model call that asked for it: its event names
|
|
8
|
+
// the call (`inCall`) since 19 Oct 2026; older records are matched by time,
|
|
9
|
+
// to the last model call that answered before the tool started. Pure: no
|
|
10
|
+
// node imports, so the browser can use it.
|
|
11
|
+
|
|
12
|
+
import type { RunRecord, StepRecord, RunEvent, ModelCall } from "./store.ts";
|
|
13
|
+
|
|
14
|
+
export type SpanKind = "run" | "step" | "model" | "tool" | "error";
|
|
15
|
+
export type SpanStatus = "ok" | "error" | "running" | "waiting" | "skipped" | "pending";
|
|
16
|
+
|
|
17
|
+
export interface TraceSpan {
|
|
18
|
+
/** Stable within the run: "run", "s2", "s2/m3", "s2/t:<call>", "s2/e5". */
|
|
19
|
+
id: string;
|
|
20
|
+
kind: SpanKind;
|
|
21
|
+
/** What the line says: the flow, the agent, the model, the tool. */
|
|
22
|
+
label: string;
|
|
23
|
+
/** Epoch ms; null when the record does not say. */
|
|
24
|
+
start: number | null;
|
|
25
|
+
end: number | null;
|
|
26
|
+
status: SpanStatus;
|
|
27
|
+
/** Model and step spans: tokens (input is all input, as on the record). */
|
|
28
|
+
tokens?: { input: number; output: number; cacheRead: number; cacheWrite: number };
|
|
29
|
+
/** Step spans: the provider's token cost, USD. */
|
|
30
|
+
costUsd?: number | null;
|
|
31
|
+
model?: string;
|
|
32
|
+
/** Model spans: why it stopped. */
|
|
33
|
+
stop?: string;
|
|
34
|
+
/** Tool spans: the call id, and whether its input and output were recorded. */
|
|
35
|
+
call?: string;
|
|
36
|
+
captured?: boolean;
|
|
37
|
+
/** Error spans and failed tools: the words. */
|
|
38
|
+
text?: string;
|
|
39
|
+
subagent?: string;
|
|
40
|
+
/** Index into run.steps, on a step and everything under it. */
|
|
41
|
+
step?: number;
|
|
42
|
+
children: TraceSpan[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ms = (iso: string | null | undefined) => {
|
|
46
|
+
if (!iso) return null;
|
|
47
|
+
const t = Date.parse(iso);
|
|
48
|
+
return Number.isNaN(t) ? null : t;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function stepStatus(s: StepRecord): SpanStatus {
|
|
52
|
+
if (s.status === "completed") return "ok";
|
|
53
|
+
if (s.status === "failed") return "error";
|
|
54
|
+
if (s.status === "running") return "running";
|
|
55
|
+
if (s.status === "awaiting-approval") return "waiting";
|
|
56
|
+
if (s.status === "skipped") return "skipped";
|
|
57
|
+
return "pending";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Tool events paired: the call's start, its duration, whether it erred. */
|
|
61
|
+
function toolCalls(events: RunEvent[]): { e: RunEvent; start: number | null; end: number | null; err: boolean; captured: boolean }[] {
|
|
62
|
+
const byCall = new Map<string, { e: RunEvent; start: number | null; end: number | null; err: boolean; captured: boolean }>();
|
|
63
|
+
const out: { e: RunEvent; start: number | null; end: number | null; err: boolean; captured: boolean }[] = [];
|
|
64
|
+
for (const e of events) {
|
|
65
|
+
if (e.type !== "tool") continue;
|
|
66
|
+
if (e.call && e.ms != null && byCall.has(e.call)) {
|
|
67
|
+
const open = byCall.get(e.call)!;
|
|
68
|
+
open.end = (open.start ?? ms(e.t) ?? 0) + e.ms;
|
|
69
|
+
open.err ||= e.err === true;
|
|
70
|
+
open.captured ||= e.captured === true;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const c = { e, start: ms(e.t), end: null as number | null, err: e.err === true, captured: e.captured === true };
|
|
74
|
+
if (e.call) byCall.set(e.call, c);
|
|
75
|
+
out.push(c);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function modelSpan(c: ModelCall, step: number): TraceSpan {
|
|
81
|
+
const at = ms(c.at);
|
|
82
|
+
return {
|
|
83
|
+
id: `s${step}/m${c.attempt && c.attempt > 1 ? `${c.attempt}.` : ""}${c.n}`,
|
|
84
|
+
kind: "model",
|
|
85
|
+
label: c.model ?? "model",
|
|
86
|
+
// The request went out `ms` before the response began arriving.
|
|
87
|
+
start: at !== null ? at - c.ms : null,
|
|
88
|
+
end: at,
|
|
89
|
+
status: "ok",
|
|
90
|
+
tokens: c.tokens,
|
|
91
|
+
...(c.model ? { model: c.model } : {}),
|
|
92
|
+
...(c.stop ? { stop: c.stop } : {}),
|
|
93
|
+
...(c.subagent ? { subagent: c.subagent } : {}),
|
|
94
|
+
step,
|
|
95
|
+
children: [],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function stepSpan(s: StepRecord, i: number, now: number): TraceSpan {
|
|
100
|
+
const first = s.events[0] ? ms(s.events[0].t) : null;
|
|
101
|
+
const last = s.events.length ? ms(s.events[s.events.length - 1]!.t) : null;
|
|
102
|
+
const status = stepStatus(s);
|
|
103
|
+
const span: TraceSpan = {
|
|
104
|
+
id: `s${i}`,
|
|
105
|
+
kind: "step",
|
|
106
|
+
label: s.agent,
|
|
107
|
+
start: ms(s.startedAt) ?? first,
|
|
108
|
+
end: ms(s.finishedAt) ?? (status === "running" ? now : last),
|
|
109
|
+
status,
|
|
110
|
+
...(s.tokens ? { tokens: { input: s.tokens.input, output: s.tokens.output, cacheRead: s.tokens.cacheRead ?? 0, cacheWrite: s.tokens.cacheWrite ?? 0 } } : {}),
|
|
111
|
+
costUsd: s.costUsd ?? null,
|
|
112
|
+
...(s.model ? { model: s.model } : {}),
|
|
113
|
+
step: i,
|
|
114
|
+
children: [],
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const models = (s.calls ?? []).map((c) => ({ c, span: modelSpan(c, i) }));
|
|
118
|
+
const byId = new Map(models.map((m) => [m.c.id, m.span]));
|
|
119
|
+
const homeOf = (e: RunEvent, start: number | null): TraceSpan => {
|
|
120
|
+
if (e.inCall && byId.has(e.inCall)) return byId.get(e.inCall)!;
|
|
121
|
+
// Older records: the last model call (of the same agent) that answered
|
|
122
|
+
// before the tool started.
|
|
123
|
+
const same = models.filter((m) => (m.c.subagent ?? null) === (e.subagent ?? null) && m.span.end !== null && start !== null && m.span.end <= start + 50);
|
|
124
|
+
return same.length ? same[same.length - 1]!.span : span;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
for (const t of toolCalls(s.events)) {
|
|
128
|
+
const home = homeOf(t.e, t.start);
|
|
129
|
+
const tool: TraceSpan = {
|
|
130
|
+
id: `s${i}/t:${t.e.call ?? `${t.start}`}`,
|
|
131
|
+
kind: "tool",
|
|
132
|
+
label: t.e.text,
|
|
133
|
+
start: t.start,
|
|
134
|
+
end: t.end ?? (status === "running" ? now : t.start),
|
|
135
|
+
status: t.err ? "error" : t.end === null && status === "running" ? "running" : "ok",
|
|
136
|
+
...(t.e.call ? { call: t.e.call } : {}),
|
|
137
|
+
...(t.captured ? { captured: true } : {}),
|
|
138
|
+
...(t.e.subagent ? { subagent: t.e.subagent } : {}),
|
|
139
|
+
step: i,
|
|
140
|
+
children: [],
|
|
141
|
+
};
|
|
142
|
+
home.children.push(tool);
|
|
143
|
+
}
|
|
144
|
+
// A model call lasts until the last tool it asked for has finished.
|
|
145
|
+
for (const m of models) {
|
|
146
|
+
const ends = m.span.children.map((c) => c.end).filter((x): x is number => x !== null);
|
|
147
|
+
if (ends.length && m.span.end !== null) m.span.end = Math.max(m.span.end, ...ends);
|
|
148
|
+
}
|
|
149
|
+
span.children.push(...models.map((m) => m.span));
|
|
150
|
+
|
|
151
|
+
// Errors, as leaves: why the step failed, read without opening the trace.
|
|
152
|
+
s.events.forEach((e, k) => {
|
|
153
|
+
if (e.type !== "error") return;
|
|
154
|
+
span.children.push({ id: `s${i}/e${k}`, kind: "error", label: "error", start: ms(e.t), end: ms(e.t), status: "error", text: e.text, step: i, children: [] });
|
|
155
|
+
});
|
|
156
|
+
span.children.sort((a, b) => (a.start ?? Infinity) - (b.start ?? Infinity));
|
|
157
|
+
return span;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function buildTraceTree(run: RunRecord, now = Date.now()): TraceSpan {
|
|
161
|
+
const status: SpanStatus =
|
|
162
|
+
run.status === "completed" ? "ok" : run.status === "failed" ? "error" : run.status === "awaiting-approval" ? "waiting" : run.status === "queued" ? "pending" : "running";
|
|
163
|
+
return {
|
|
164
|
+
id: "run",
|
|
165
|
+
kind: "run",
|
|
166
|
+
label: run.flow,
|
|
167
|
+
start: ms(run.startedAt),
|
|
168
|
+
end: ms(run.finishedAt) ?? (status === "running" || status === "waiting" ? now : null),
|
|
169
|
+
status,
|
|
170
|
+
children: run.steps.map((s, i) => stepSpan(s, i, now)),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The ids from the root to the first thing that went wrong — what the
|
|
175
|
+
* view opens on; [] when nothing did. A failed tool beats the step's error
|
|
176
|
+
* line, which beats the failed step itself. */
|
|
177
|
+
export function failurePath(root: TraceSpan): string[] {
|
|
178
|
+
let best: { path: string[]; rank: number } | null = null;
|
|
179
|
+
const rank = (s: TraceSpan) => (s.kind === "tool" ? 3 : s.kind === "error" ? 2 : s.kind === "step" ? 1 : 0);
|
|
180
|
+
const walk = (s: TraceSpan, path: string[]) => {
|
|
181
|
+
const here = [...path, s.id];
|
|
182
|
+
if (s.status === "error" && s.kind !== "run" && (!best || rank(s) > best.rank)) best = { path: here, rank: rank(s) };
|
|
183
|
+
for (const c of s.children) walk(c, here);
|
|
184
|
+
};
|
|
185
|
+
walk(root, []);
|
|
186
|
+
return best ? (best as { path: string[] }).path : [];
|
|
187
|
+
}
|
package/src/translator.ts
CHANGED
|
@@ -85,6 +85,11 @@ export interface TranslatorSpec {
|
|
|
85
85
|
/** The provider block's `params:` — merged into the outgoing body after
|
|
86
86
|
* the translation, so a file can set what this format does not model. */
|
|
87
87
|
params?: Record<string, unknown>;
|
|
88
|
+
/** `prompt_cache_key` for an endpoint that routes its prompt cache by
|
|
89
|
+
* one (OpenAI, Mistral): the same key for every request that shares a
|
|
90
|
+
* prefix — one per agent (prompt-layers.ts). Unset where the preset
|
|
91
|
+
* does not say the parameter is understood. */
|
|
92
|
+
cacheKey?: string;
|
|
88
93
|
/** How the endpoint is named in the trace. */
|
|
89
94
|
label?: string;
|
|
90
95
|
/** The step's egress lease, when the sandbox must not hold the key: the
|
|
@@ -185,12 +190,14 @@ export function toChatCompletions(
|
|
|
185
190
|
maxTokensParam?: "max_tokens" | "max_completion_tokens";
|
|
186
191
|
reasoningEffort?: boolean;
|
|
187
192
|
stream: boolean;
|
|
193
|
+
cacheKey?: string;
|
|
188
194
|
/** Merged in last, so the file wins over the translation. */
|
|
189
195
|
params?: Record<string, unknown>;
|
|
190
196
|
},
|
|
191
197
|
drop: Dropped = dropped(),
|
|
192
198
|
): Json {
|
|
193
199
|
const out: Json = { model: req.model, stream: opts.stream };
|
|
200
|
+
if (opts.cacheKey) out.prompt_cache_key = opts.cacheKey;
|
|
194
201
|
const messages: Json[] = [];
|
|
195
202
|
|
|
196
203
|
const system = textOf(req.system);
|
|
@@ -335,11 +342,13 @@ export function toResponses(
|
|
|
335
342
|
opts: {
|
|
336
343
|
reasoningEffort?: boolean;
|
|
337
344
|
stream: boolean;
|
|
345
|
+
cacheKey?: string;
|
|
338
346
|
params?: Record<string, unknown>;
|
|
339
347
|
},
|
|
340
348
|
drop: Dropped = dropped(),
|
|
341
349
|
): Json {
|
|
342
350
|
const out: Json = { model: req.model, stream: opts.stream, store: false };
|
|
351
|
+
if (opts.cacheKey) out.prompt_cache_key = opts.cacheKey;
|
|
343
352
|
const system = textOf(req.system);
|
|
344
353
|
if (system) out.instructions = system;
|
|
345
354
|
|
|
@@ -948,8 +957,8 @@ export async function startTranslator(spec: TranslatorSpec): Promise<RunningTran
|
|
|
948
957
|
const drop = dropped();
|
|
949
958
|
const responses = spec.wire === "responses";
|
|
950
959
|
const outbound = responses
|
|
951
|
-
? toResponses(body, { reasoningEffort: spec.reasoningEffort, params: spec.params, stream }, drop)
|
|
952
|
-
: toChatCompletions(body, { maxTokensParam: spec.maxTokensParam, reasoningEffort: spec.reasoningEffort, params: spec.params, stream }, drop);
|
|
960
|
+
? toResponses(body, { reasoningEffort: spec.reasoningEffort, params: spec.params, cacheKey: spec.cacheKey, stream }, drop)
|
|
961
|
+
: toChatCompletions(body, { maxTokensParam: spec.maxTokensParam, reasoningEffort: spec.reasoningEffort, params: spec.params, cacheKey: spec.cacheKey, stream }, drop);
|
|
953
962
|
for (const line of drop.lines()) if (!log.includes(line)) log.push(line);
|
|
954
963
|
|
|
955
964
|
let upstream: Response;
|
|
@@ -1089,6 +1098,7 @@ export function translatorSpecFor(spec: {
|
|
|
1089
1098
|
name?: string | null;
|
|
1090
1099
|
maxTokensParam?: "max_tokens" | "max_completion_tokens";
|
|
1091
1100
|
reasoningEffort?: boolean;
|
|
1101
|
+
cacheKey?: string;
|
|
1092
1102
|
}): TranslatorSpec | null {
|
|
1093
1103
|
if (spec.format !== "openai" && spec.format !== "responses") return null;
|
|
1094
1104
|
return {
|
|
@@ -1099,6 +1109,7 @@ export function translatorSpecFor(spec: {
|
|
|
1099
1109
|
params: spec.params,
|
|
1100
1110
|
maxTokensParam: spec.maxTokensParam,
|
|
1101
1111
|
reasoningEffort: spec.reasoningEffort,
|
|
1112
|
+
...(spec.cacheKey ? { cacheKey: spec.cacheKey } : {}),
|
|
1102
1113
|
label: spec.name ? `${spec.name} (${spec.baseUrl})` : spec.baseUrl,
|
|
1103
1114
|
};
|
|
1104
1115
|
}
|
package/src/trigger-log.ts
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// One workspace's findings, as the Checks page, `GET …/check` and the daily
|
|
2
|
+
// rescan see them (FOL-58): the deploy's checks and the whole scanner, with
|
|
3
|
+
// what the files alone do not carry — the account's network: and the
|
|
4
|
+
// outward tools of the library and the workspace, nearest wins.
|
|
5
|
+
|
|
6
|
+
import { accountDir, workspaceDir, workspaceTools, type OutwardMark } from "./store.ts";
|
|
7
|
+
import { readAgentsMdKeys } from "./agents-md.ts";
|
|
8
|
+
import { libraryTools } from "./library.ts";
|
|
9
|
+
import { checkFiles, readTree } from "./deploy.ts";
|
|
10
|
+
import type { DeployFile } from "./store.ts";
|
|
11
|
+
import type { ScanFinding, ScanOptions } from "./scan.ts";
|
|
12
|
+
|
|
13
|
+
/** What the checks read of a workspace, at most. Data (state/ and memory/,
|
|
14
|
+
* what agents write) past these is left out and named; the rest past its
|
|
15
|
+
* budget is not read at all (TreeTooLarge). On 20 Oct 2026 the daily
|
|
16
|
+
* rescan read 1.2 GB of one desk's state/ into a 2 GB heap and crash-looped
|
|
17
|
+
* the worker. The rest is 64 MiB because a deploy is 10 MiB a push. */
|
|
18
|
+
export const CHECK_LIMITS = {
|
|
19
|
+
maxDataFileBytes: 5 * 1024 * 1024,
|
|
20
|
+
maxDataBytes: 32 * 1024 * 1024,
|
|
21
|
+
maxDataFiles: 5000,
|
|
22
|
+
maxSourceBytes: 64 * 1024 * 1024,
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
/** A workspace as the checks read it: the deploy's tree within CHECK_LIMITS.
|
|
26
|
+
* Read once and hand it to everything that checks. */
|
|
27
|
+
export function checkTree(dir: string, onSkip?: (path: string, bytes: number) => void): DeployFile[] {
|
|
28
|
+
return readTree(dir, { ...CHECK_LIMITS, onSkip });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function checkOptions(tenant: string, workspace: string): ScanOptions {
|
|
32
|
+
const tools = { ...libraryTools(tenant), ...workspaceTools(tenant, workspace) };
|
|
33
|
+
const outwardTools = Object.entries(tools).filter(([, d]) => (d as OutwardMark).outward === true).map(([n]) => n);
|
|
34
|
+
// AGENTS.md, or the older project.md, as the runner reads it (Codex review).
|
|
35
|
+
const accountNetwork = readAgentsMdKeys(accountDir(tenant)).network;
|
|
36
|
+
return { outwardTools, accountNetwork };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function checkWorkspace(tenant: string, workspace: string, tree: DeployFile[] = checkTree(workspaceDir(tenant, workspace))): ScanFinding[] {
|
|
40
|
+
return checkFiles(tree, checkOptions(tenant, workspace));
|
|
41
|
+
}
|