@foldrun/core 2.4.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 +20 -6
- 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/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 +19 -6
- 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/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
package/dist/src/otel.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// A run as an OpenTelemetry trace — OTLP/HTTP JSON, the body a collector's
|
|
2
|
+
// /v1/traces takes — so an account's agent runs sit in the tracing it
|
|
3
|
+
// already has (Grafana Tempo, Honeycomb, Datadog, Jaeger, Langfuse…) beside
|
|
4
|
+
// the services they act on. Built from buildTraceTree, the same tree the run
|
|
5
|
+
// page draws: run → step → model call → tool call.
|
|
6
|
+
//
|
|
7
|
+
// telemetry:
|
|
8
|
+
// otlp: https://otel.example.com/v1/traces
|
|
9
|
+
// headers:
|
|
10
|
+
// authorization: ${OTEL_TOKEN} # a vault secret, by name
|
|
11
|
+
//
|
|
12
|
+
// In AGENTS.md (the workspace's, else the account's — the block replaces
|
|
13
|
+
// whole, like notify:). Ids are derived from the run and span ids, so a run
|
|
14
|
+
// sent twice is the same trace, not two. No OpenTelemetry SDK: the wire
|
|
15
|
+
// format is small, and a dependency in every runner image is not.
|
|
16
|
+
import crypto from "node:crypto";
|
|
17
|
+
import { buildTraceTree } from "./trace-tree.js";
|
|
18
|
+
export { parseTelemetry, telemetrySecretNames, telemetryHeaders } from "./otel-config.js";
|
|
19
|
+
const attr = (key, v) => {
|
|
20
|
+
if (v === null || v === undefined || v === "")
|
|
21
|
+
return [];
|
|
22
|
+
if (typeof v === "boolean")
|
|
23
|
+
return [{ key, value: { boolValue: v } }];
|
|
24
|
+
if (typeof v === "number")
|
|
25
|
+
return [{ key, value: Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v } }];
|
|
26
|
+
return [{ key, value: { stringValue: v } }];
|
|
27
|
+
};
|
|
28
|
+
const hex = (s, len) => crypto.createHash("sha256").update(s).digest("hex").slice(0, len);
|
|
29
|
+
const nanos = (msEpoch) => (BigInt(Math.round(msEpoch)) * BigInt(1_000_000)).toString();
|
|
30
|
+
/** OTLP span kinds: INTERNAL, and CLIENT for a call that leaves the platform. */
|
|
31
|
+
const SPAN_KIND = { internal: 1, client: 3 };
|
|
32
|
+
/** The ExportTraceServiceRequest for one run. */
|
|
33
|
+
export function runToOtlp(run, ctx, now = Date.now()) {
|
|
34
|
+
const tree = buildTraceTree(run, now);
|
|
35
|
+
const traceId = hex(`foldrun-trace\0${ctx.tenant}\0${ctx.workspace}\0${run.id}`, 32);
|
|
36
|
+
const spans = [];
|
|
37
|
+
const visit = (s, parent, inherited) => {
|
|
38
|
+
const spanId = hex(`${traceId}\0${s.id}`, 16);
|
|
39
|
+
const start = s.start ?? inherited.start;
|
|
40
|
+
const end = Math.max(start, s.end ?? s.start ?? now);
|
|
41
|
+
const step = s.step !== undefined ? run.steps[s.step] : undefined;
|
|
42
|
+
const agent = s.kind === "step" ? step?.agent : inherited.agent;
|
|
43
|
+
spans.push({
|
|
44
|
+
traceId,
|
|
45
|
+
spanId,
|
|
46
|
+
...(parent ? { parentSpanId: parent } : {}),
|
|
47
|
+
name: s.kind === "run" ? `run ${s.label}` : s.kind === "step" ? `step ${s.label}` : s.kind === "model" ? `chat ${s.model ?? s.label}` : s.kind === "tool" ? `tool ${s.label}` : s.label,
|
|
48
|
+
kind: s.kind === "model" ? SPAN_KIND.client : SPAN_KIND.internal,
|
|
49
|
+
startTimeUnixNano: nanos(start),
|
|
50
|
+
endTimeUnixNano: nanos(end),
|
|
51
|
+
attributes: [
|
|
52
|
+
...attr("foldrun.span.kind", s.kind),
|
|
53
|
+
...attr("foldrun.status", s.status),
|
|
54
|
+
...(s.kind === "step" ? [...attr("foldrun.agent", agent), ...attr("foldrun.owner", agent ? ctx.owners?.[agent] ?? null : null), ...attr("foldrun.step", s.step)] : []),
|
|
55
|
+
...attr("gen_ai.request.model", s.model),
|
|
56
|
+
...(s.tokens ? [...attr("gen_ai.usage.input_tokens", s.tokens.input), ...attr("gen_ai.usage.output_tokens", s.tokens.output), ...attr("gen_ai.usage.cache_read_tokens", s.tokens.cacheRead), ...attr("gen_ai.usage.cache_write_tokens", s.tokens.cacheWrite)] : []),
|
|
57
|
+
...attr("gen_ai.response.finish_reason", s.stop),
|
|
58
|
+
...(typeof s.costUsd === "number" ? attr("foldrun.cost_usd", s.costUsd) : []),
|
|
59
|
+
...(s.kind === "tool" ? [...attr("gen_ai.tool.name", s.label), ...attr("gen_ai.tool.call.id", s.call)] : []),
|
|
60
|
+
...attr("foldrun.subagent", s.subagent),
|
|
61
|
+
],
|
|
62
|
+
status: s.status === "error" ? { code: 2, ...(s.text ? { message: s.text.slice(0, 500) } : {}) } : { code: 0 },
|
|
63
|
+
});
|
|
64
|
+
for (const c of s.children)
|
|
65
|
+
visit(c, spanId, { start, agent });
|
|
66
|
+
};
|
|
67
|
+
visit(tree, null, { start: Date.parse(run.startedAt) || now });
|
|
68
|
+
return {
|
|
69
|
+
resourceSpans: [
|
|
70
|
+
{
|
|
71
|
+
resource: {
|
|
72
|
+
attributes: [
|
|
73
|
+
...attr("service.name", "foldrun"),
|
|
74
|
+
...attr("foldrun.account", ctx.tenant),
|
|
75
|
+
...attr("foldrun.workspace", ctx.workspace),
|
|
76
|
+
...attr("foldrun.flow", run.flow),
|
|
77
|
+
...attr("foldrun.run_id", run.id),
|
|
78
|
+
...attr("foldrun.started_by", run.startedBy ?? null),
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
scopeSpans: [{ scope: { name: "foldrun", ...(ctx.version ? { version: ctx.version } : {}) }, spans }],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type Outcome = "running" | "queued" | "waiting" | "good" | "done" | "quiet" | "blocked" | "bad" | "failed" | "stopped";
|
|
2
|
+
export declare const OUTCOMES: readonly Outcome[];
|
|
3
|
+
/** From the display status (store.runDisplayStatus) and the verdict (store.runVerdict). */
|
|
4
|
+
export declare function outcomeOf(status: string, verdict: string | null | undefined): Outcome;
|
|
5
|
+
/** Still going: not an outcome yet. */
|
|
6
|
+
export declare const isLive: (o: Outcome) => o is "queued" | "running" | "waiting";
|
|
7
|
+
/** Ran to the end without failing — whatever it found. */
|
|
8
|
+
export declare const isCompleted: (o: Outcome) => o is "bad" | "blocked" | "done" | "good" | "quiet";
|
|
9
|
+
/** Over and countable for a rate: completed or failed. A run a person stopped is left out. */
|
|
10
|
+
export declare const isFinished: (o: Outcome) => o is "bad" | "blocked" | "done" | "failed" | "good" | "quiet";
|
|
11
|
+
/** Went wrong or found something wrong: failed, BAD, BLOCKED. */
|
|
12
|
+
export declare const needsAttention: (o: Outcome) => o is "bad" | "blocked" | "failed";
|
|
13
|
+
/** A person has something to do: what needsAttention says, and a run
|
|
14
|
+
* waiting on an approval or an answer (Codex review). */
|
|
15
|
+
export declare const needsAction: (o: Outcome) => o is "bad" | "blocked" | "failed" | "waiting";
|
|
16
|
+
/** Completed and reported nothing wrong (GOOD, QUIET, or nothing said). */
|
|
17
|
+
export declare const isSuccess: (o: Outcome) => o is "done" | "good" | "quiet";
|
|
18
|
+
export interface Tally {
|
|
19
|
+
/** Every run counted, live ones included. */
|
|
20
|
+
runs: number;
|
|
21
|
+
/** Running, queued, or waiting on a person. */
|
|
22
|
+
live: number;
|
|
23
|
+
/** Waiting on an approval or an answer. */
|
|
24
|
+
waiting: number;
|
|
25
|
+
stopped: number;
|
|
26
|
+
/** Completed + failed: the runs a rate is taken over. */
|
|
27
|
+
finished: number;
|
|
28
|
+
/** Ran to the end without failing. */
|
|
29
|
+
completed: number;
|
|
30
|
+
failed: number;
|
|
31
|
+
/** What the completed runs found. */
|
|
32
|
+
ok: number;
|
|
33
|
+
quiet: number;
|
|
34
|
+
blocked: number;
|
|
35
|
+
bad: number;
|
|
36
|
+
/** completed / finished, as a whole percent; null with nothing finished. */
|
|
37
|
+
completedPct: number | null;
|
|
38
|
+
}
|
|
39
|
+
/** The one count of a set of outcomes that every surface shows. */
|
|
40
|
+
export declare function tally(outcomes: Iterable<Outcome>): Tally;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// A run's outcome, and the one way every surface counts outcomes (FOL-54).
|
|
2
|
+
//
|
|
3
|
+
// A run is read on three separate lines, never folded into one "success":
|
|
4
|
+
// · execution — did it run to the end without failing? (completed, failed,
|
|
5
|
+
// stopped by a person, or still live)
|
|
6
|
+
// · result — what the work concluded: GOOD, QUIET, BLOCKED, BAD, or
|
|
7
|
+
// nothing said (done)
|
|
8
|
+
// · action — does a person need to do something? (an approval or a
|
|
9
|
+
// question waiting, a failure, a BLOCKED, a BAD finding)
|
|
10
|
+
//
|
|
11
|
+
// Before this, five surfaces computed five different "success rates" (one
|
|
12
|
+
// counted BAD as success, one counted stopped runs as failures, …), so the
|
|
13
|
+
// same desk read four different percentages. Every page, the API and the
|
|
14
|
+
// CLI now read `tally()`. Pure: no node imports, so the browser can use it.
|
|
15
|
+
export const OUTCOMES = ["running", "queued", "waiting", "good", "done", "quiet", "blocked", "bad", "failed", "stopped"];
|
|
16
|
+
/** From the display status (store.runDisplayStatus) and the verdict (store.runVerdict). */
|
|
17
|
+
export function outcomeOf(status, verdict) {
|
|
18
|
+
if (status === "running")
|
|
19
|
+
return "running";
|
|
20
|
+
if (status === "queued")
|
|
21
|
+
return "queued";
|
|
22
|
+
if (status === "awaiting-approval")
|
|
23
|
+
return "waiting";
|
|
24
|
+
if (status === "stopped")
|
|
25
|
+
return "stopped";
|
|
26
|
+
if (status === "failed")
|
|
27
|
+
return "failed";
|
|
28
|
+
switch (verdict) {
|
|
29
|
+
case "GOOD": return "good";
|
|
30
|
+
case "QUIET": return "quiet";
|
|
31
|
+
case "BLOCKED": return "blocked";
|
|
32
|
+
case "BAD": return "bad";
|
|
33
|
+
default: return "done";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Still going: not an outcome yet. */
|
|
37
|
+
export const isLive = (o) => o === "running" || o === "queued" || o === "waiting";
|
|
38
|
+
/** Ran to the end without failing — whatever it found. */
|
|
39
|
+
export const isCompleted = (o) => o === "good" || o === "done" || o === "quiet" || o === "blocked" || o === "bad";
|
|
40
|
+
/** Over and countable for a rate: completed or failed. A run a person stopped is left out. */
|
|
41
|
+
export const isFinished = (o) => isCompleted(o) || o === "failed";
|
|
42
|
+
/** Went wrong or found something wrong: failed, BAD, BLOCKED. */
|
|
43
|
+
export const needsAttention = (o) => o === "failed" || o === "bad" || o === "blocked";
|
|
44
|
+
/** A person has something to do: what needsAttention says, and a run
|
|
45
|
+
* waiting on an approval or an answer (Codex review). */
|
|
46
|
+
export const needsAction = (o) => needsAttention(o) || o === "waiting";
|
|
47
|
+
/** Completed and reported nothing wrong (GOOD, QUIET, or nothing said). */
|
|
48
|
+
export const isSuccess = (o) => o === "good" || o === "done" || o === "quiet";
|
|
49
|
+
/** The one count of a set of outcomes that every surface shows. */
|
|
50
|
+
export function tally(outcomes) {
|
|
51
|
+
const t = { runs: 0, live: 0, waiting: 0, stopped: 0, finished: 0, completed: 0, failed: 0, ok: 0, quiet: 0, blocked: 0, bad: 0, completedPct: null };
|
|
52
|
+
for (const o of outcomes) {
|
|
53
|
+
t.runs++;
|
|
54
|
+
if (isLive(o))
|
|
55
|
+
t.live++;
|
|
56
|
+
if (o === "waiting")
|
|
57
|
+
t.waiting++;
|
|
58
|
+
if (o === "stopped")
|
|
59
|
+
t.stopped++;
|
|
60
|
+
if (isFinished(o))
|
|
61
|
+
t.finished++;
|
|
62
|
+
if (isCompleted(o))
|
|
63
|
+
t.completed++;
|
|
64
|
+
if (o === "failed")
|
|
65
|
+
t.failed++;
|
|
66
|
+
if (o === "good" || o === "done")
|
|
67
|
+
t.ok++;
|
|
68
|
+
if (o === "quiet")
|
|
69
|
+
t.quiet++;
|
|
70
|
+
if (o === "blocked")
|
|
71
|
+
t.blocked++;
|
|
72
|
+
if (o === "bad")
|
|
73
|
+
t.bad++;
|
|
74
|
+
}
|
|
75
|
+
t.completedPct = t.finished ? Math.round((100 * t.completed) / t.finished) : null;
|
|
76
|
+
return t;
|
|
77
|
+
}
|
package/dist/src/package.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
// claim, the files are what will run.
|
|
26
26
|
import fs from "node:fs";
|
|
27
27
|
import path from "node:path";
|
|
28
|
-
import matter from "
|
|
28
|
+
import matter from "./matter.js";
|
|
29
29
|
import { assertSafeName, isEditablePath, listWorkspaceFiles, listWorkspaces, listFlows, parseFlow, readWorkspaceFile, saveWorkspace, blankTemplateFiles, syncBundleFor, notifyWorkspaceChanged, workspaceDir, } from "./store.js";
|
|
30
30
|
import { recordRevision } from "./history.js";
|
|
31
31
|
import { libraryDir, libraryTools } from "./library.js";
|
package/dist/src/platform.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type TriggerAdmission = {
|
|
|
14
14
|
admit: true;
|
|
15
15
|
} | {
|
|
16
16
|
admit: false;
|
|
17
|
-
reason: "duplicate" | "throttled" | "quarantined" | "paused" | "debounced";
|
|
17
|
+
reason: "duplicate" | "throttled" | "quarantined" | "paused" | "halted" | "debounced";
|
|
18
18
|
detail: string;
|
|
19
19
|
firesAt?: number;
|
|
20
20
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { PromptLayerRecord } from "./tokens.ts";
|
|
2
|
+
export { promptLayersChanged, promptCacheNote, type PromptLayerRecord } from "./tokens.ts";
|
|
3
|
+
export interface AssembledPrompt {
|
|
4
|
+
/** The whole prompt, stable layers first. */
|
|
5
|
+
text: string;
|
|
6
|
+
layers: PromptLayerRecord[];
|
|
7
|
+
}
|
|
8
|
+
export declare function hashLayer(text: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* A collector agentContext fills in whatever order it computes its
|
|
11
|
+
* sections. `add` with the same name twice appends to that layer, so a
|
|
12
|
+
* section built in two places (the platform's rules) is one layer.
|
|
13
|
+
*/
|
|
14
|
+
export declare class PromptLayers {
|
|
15
|
+
private readonly order;
|
|
16
|
+
private readonly text;
|
|
17
|
+
private readonly dynamic;
|
|
18
|
+
add(name: string, text: string, opts?: {
|
|
19
|
+
dynamic?: boolean;
|
|
20
|
+
}): void;
|
|
21
|
+
assemble(): AssembledPrompt;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The key an OpenAI-format provider routes a prompt by (`prompt_cache_key`).
|
|
25
|
+
* One per agent: every step of an agent opens with the same prefix, and a
|
|
26
|
+
* key that names the account keeps one tenant's cache from being probed
|
|
27
|
+
* through another's requests. Hashed, since the names are a person's and
|
|
28
|
+
* the parameter is capped at 64 characters.
|
|
29
|
+
*/
|
|
30
|
+
export declare function promptCacheKey(tenant: string, workspace: string, agent: string): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// The system prompt in layers, ordered for the model's prompt cache.
|
|
2
|
+
//
|
|
3
|
+
// Every provider caches a prompt by its prefix: a request whose opening
|
|
4
|
+
// bytes match one it has seen reads the model's work on them instead of
|
|
5
|
+
// paying for it again, and a byte that differs invalidates everything
|
|
6
|
+
// after it. agentContext used to join its sections in the order it
|
|
7
|
+
// computed them, which put the memory index, the state file list and the
|
|
8
|
+
// clock — the three things that change between runs — in the middle.
|
|
9
|
+
//
|
|
10
|
+
// This collector keeps the sections apart by how often they change and
|
|
11
|
+
// assembles them stable first, dynamic last: what Anthropic does for
|
|
12
|
+
// Claude Code ("static content first, dynamic content last"). The prompt
|
|
13
|
+
// still goes to the SDK as one string, cached as one block. The Agent
|
|
14
|
+
// SDK's SYSTEM_PROMPT_DYNAMIC_BOUNDARY would split it, but the CLI honours
|
|
15
|
+
// the marker only when it talks to api.anthropic.com directly — a step
|
|
16
|
+
// behind the egress proxy got one block anyway (live check, 20 Oct 2026;
|
|
17
|
+
// claude-code issue #102518) — and where it does split, it sends the
|
|
18
|
+
// stable half with the CLI's "global" cache scope, which is not a scope to
|
|
19
|
+
// put an account's own prose in. So no marker.
|
|
20
|
+
//
|
|
21
|
+
// Each layer is also hashed, and the hashes ride on the step record
|
|
22
|
+
// (`promptLayers`). A step whose cache writes are high can then say which
|
|
23
|
+
// layer changed since the agent's last step — "memory changed" — instead
|
|
24
|
+
// of leaving a person to diff two 40k-character prompts.
|
|
25
|
+
import crypto from "node:crypto";
|
|
26
|
+
// The record's shape and the readers' comparison live in tokens.ts, which
|
|
27
|
+
// has no node imports and is what the dashboard's client bundle reads.
|
|
28
|
+
export { promptLayersChanged, promptCacheNote } from "./tokens.js";
|
|
29
|
+
const SEP = "\n\n";
|
|
30
|
+
export function hashLayer(text) {
|
|
31
|
+
return crypto.createHash("sha256").update(text).digest("hex").slice(0, 12);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A collector agentContext fills in whatever order it computes its
|
|
35
|
+
* sections. `add` with the same name twice appends to that layer, so a
|
|
36
|
+
* section built in two places (the platform's rules) is one layer.
|
|
37
|
+
*/
|
|
38
|
+
export class PromptLayers {
|
|
39
|
+
order = [];
|
|
40
|
+
text = new Map();
|
|
41
|
+
dynamic = new Set();
|
|
42
|
+
add(name, text, opts = {}) {
|
|
43
|
+
if (!text)
|
|
44
|
+
return;
|
|
45
|
+
if (!this.text.has(name)) {
|
|
46
|
+
this.order.push(name);
|
|
47
|
+
this.text.set(name, []);
|
|
48
|
+
}
|
|
49
|
+
this.text.get(name).push(text);
|
|
50
|
+
if (opts.dynamic)
|
|
51
|
+
this.dynamic.add(name);
|
|
52
|
+
}
|
|
53
|
+
assemble() {
|
|
54
|
+
const parts = [];
|
|
55
|
+
const layers = [];
|
|
56
|
+
const take = (names, isDynamic) => {
|
|
57
|
+
for (const name of names) {
|
|
58
|
+
const joined = this.text.get(name).join(SEP);
|
|
59
|
+
parts.push(joined);
|
|
60
|
+
layers.push({ name, chars: joined.length, hash: hashLayer(joined), ...(isDynamic ? { stable: false } : {}) });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
take(this.order.filter((n) => !this.dynamic.has(n)), false);
|
|
64
|
+
take(this.order.filter((n) => this.dynamic.has(n)), true);
|
|
65
|
+
return { text: parts.join(SEP), layers };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The key an OpenAI-format provider routes a prompt by (`prompt_cache_key`).
|
|
70
|
+
* One per agent: every step of an agent opens with the same prefix, and a
|
|
71
|
+
* key that names the account keeps one tenant's cache from being probed
|
|
72
|
+
* through another's requests. Hashed, since the names are a person's and
|
|
73
|
+
* the parameter is capped at 64 characters.
|
|
74
|
+
*/
|
|
75
|
+
export function promptCacheKey(tenant, workspace, agent) {
|
|
76
|
+
return `fr-${crypto.createHash("sha256").update(`${tenant}\u0000${workspace}\u0000${agent}`).digest("hex").slice(0, 32)}`;
|
|
77
|
+
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { accountModelBlock } from "./model-credential.js";
|
|
21
21
|
import fs from "node:fs";
|
|
22
22
|
import path from "node:path";
|
|
23
|
-
import matter from "
|
|
23
|
+
import matter from "./matter.js";
|
|
24
24
|
import { getSecret } from "./secrets.js";
|
|
25
25
|
import { noteSecretUse, healthKey } from "./secret-health.js";
|
|
26
26
|
import { accountDir, workspaceDir, listWorkspaces, listAgents, parseProvider } from "./store.js";
|
package/dist/src/providers.d.ts
CHANGED
|
@@ -20,6 +20,11 @@ export interface ProviderPreset {
|
|
|
20
20
|
* Off means the translator drops thinking rather than send a parameter
|
|
21
21
|
* the endpoint would reject. */
|
|
22
22
|
reasoningEffort?: boolean;
|
|
23
|
+
/** Whether the endpoint takes `prompt_cache_key` — a hint that requests
|
|
24
|
+
* with the same key share a prompt prefix, so its cache can be routed
|
|
25
|
+
* to. Only set where the parameter is documented; an endpoint that does
|
|
26
|
+
* not know it may refuse the request. */
|
|
27
|
+
promptCacheKey?: boolean;
|
|
23
28
|
/** One line a person should read before relying on it. */
|
|
24
29
|
note?: string;
|
|
25
30
|
/** A tool loop was driven through this endpoint from this runtime. */
|
package/dist/src/providers.js
CHANGED
|
@@ -222,7 +222,7 @@ export const PROVIDERS = [
|
|
|
222
222
|
note: "base_url is your server (default :8000). Start it with --enable-auto-tool-choice and a --tool-call-parser or no tool loop closes." },
|
|
223
223
|
// ------------------------------------------- Chat-Completions, translated
|
|
224
224
|
{ name: "openai", title: "OpenAI", format: "openai", baseUrl: "https://api.openai.com/v1", auth: "bearer", verified: true,
|
|
225
|
-
maxTokensParam: "max_completion_tokens", reasoningEffort: true,
|
|
225
|
+
maxTokensParam: "max_completion_tokens", reasoningEffort: true, promptCacheKey: true,
|
|
226
226
|
note: "Reached over Chat Completions, which OpenAI keeps supporting; the Responses API is not spoken here, so a reasoning model's reasoning does not carry across tool calls.", search: "openai" },
|
|
227
227
|
{ name: "gemini", title: "Google Gemini", format: "openai", baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai", auth: "bearer",
|
|
228
228
|
note: "Google's OpenAI-compatible route. Unknown parameters are ignored silently; reasoning cannot be switched off on the newest models." },
|
|
@@ -230,7 +230,7 @@ export const PROVIDERS = [
|
|
|
230
230
|
maxTokensParam: "max_completion_tokens", reasoningEffort: true },
|
|
231
231
|
{ name: "groq", title: "Groq", format: "openai", baseUrl: "https://api.groq.com/openai/v1", auth: "bearer",
|
|
232
232
|
maxTokensParam: "max_completion_tokens", reasoningEffort: true },
|
|
233
|
-
{ name: "mistral", title: "Mistral", format: "openai", baseUrl: "https://api.mistral.ai/v1", auth: "bearer" },
|
|
233
|
+
{ name: "mistral", title: "Mistral", format: "openai", baseUrl: "https://api.mistral.ai/v1", auth: "bearer", promptCacheKey: true },
|
|
234
234
|
{ name: "together", title: "Together AI", format: "openai", baseUrl: "https://api.together.ai/v1", auth: "bearer" },
|
|
235
235
|
{ name: "cerebras", title: "Cerebras", format: "openai", baseUrl: "https://api.cerebras.ai/v1", auth: "bearer",
|
|
236
236
|
maxTokensParam: "max_completion_tokens", reasoningEffort: true },
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { PeerInfo } from "./operator.ts";
|
|
1
2
|
import { type HookRecord } from "./hooks.ts";
|
|
2
3
|
import type { McpServerConfig } from "@anthropic-ai/claude-agent-sdk";
|
|
3
|
-
import { type ApiSpec, type Effort } from "./store.ts";
|
|
4
|
-
import type { EventExtra, ExecUsage } from "./step-exec.ts";
|
|
4
|
+
import { type ApiSpec, type Effort, type ModelCall } from "./store.ts";
|
|
5
|
+
import type { CallIo, EventExtra, ExecUsage } from "./step-exec.ts";
|
|
5
6
|
import type { TokenPrice } from "./catalog.ts";
|
|
6
7
|
import { type RuntimeSpec } from "./runtime.ts";
|
|
7
8
|
import { type NetworkLevel } from "./network.ts";
|
|
@@ -49,6 +50,8 @@ export interface ContainerStepInput {
|
|
|
49
50
|
/** `schema:` and `max_turns:`, resolved host-side — see ExecOptions. */
|
|
50
51
|
schema?: Record<string, unknown> | boolean;
|
|
51
52
|
maxTurns?: number;
|
|
53
|
+
/** `capture:` resolved host-side (FOL-54 step 4) — see ExecOptions. */
|
|
54
|
+
capture?: "metadata" | "full";
|
|
52
55
|
/** `limits:` resolved host-side, and the SDK-name → foldrun-name map the
|
|
53
56
|
* count reads (limits.ts) — enforced in the pod by the same hook. */
|
|
54
57
|
limits?: Record<string, number>;
|
|
@@ -67,6 +70,13 @@ export interface ContainerStepInput {
|
|
|
67
70
|
ask?: {
|
|
68
71
|
timeoutSec: number;
|
|
69
72
|
};
|
|
73
|
+
/** tools: [peers] — this step's agent and the other steps of its
|
|
74
|
+
* parallel group, gathered host-side at launch. The driver rebuilds the
|
|
75
|
+
* colleagues' tools in here over the lease's /peers door. */
|
|
76
|
+
peers?: {
|
|
77
|
+
self: string;
|
|
78
|
+
siblings: PeerInfo[];
|
|
79
|
+
};
|
|
70
80
|
/** A Chat-Completions provider: the driver starts the runtime's translator
|
|
71
81
|
* on loopback and points the SDK at it. Null or absent: the env's
|
|
72
82
|
* ANTHROPIC_BASE_URL is spoken to directly. */
|
|
@@ -374,6 +384,10 @@ export declare function parseDriverLine(line: string): {
|
|
|
374
384
|
subagent?: string;
|
|
375
385
|
check?: boolean;
|
|
376
386
|
hook?: HookRecord;
|
|
387
|
+
io?: CallIo;
|
|
388
|
+
turn?: ModelCall;
|
|
389
|
+
inCall?: string;
|
|
390
|
+
responding?: true;
|
|
377
391
|
} | ({
|
|
378
392
|
e: "done";
|
|
379
393
|
} & ContainerStepOutcome) | null;
|
|
@@ -23,7 +23,7 @@ import os from "node:os";
|
|
|
23
23
|
import path from "node:path";
|
|
24
24
|
import crypto from "node:crypto";
|
|
25
25
|
import { spawn, spawnSync } from "node:child_process";
|
|
26
|
-
import { isPlatformPath } from "./store.js";
|
|
26
|
+
import { CAPTURE_CAP, isPlatformPath } from "./store.js";
|
|
27
27
|
import { divertedSummary, isDivertedPath, overlayTestWrites } from "./test-mode.js";
|
|
28
28
|
import { isFileValue, fileContent } from "./secrets.js";
|
|
29
29
|
import { safeTenantSegment, systemInstallSh, SYSTEM_FROM_INPUT_SH, INSTALL_SOCKET } from "./runtime.js";
|
|
@@ -475,7 +475,7 @@ try {
|
|
|
475
475
|
const { buildScriptTools } = await import("@foldrun/core/script-tools");
|
|
476
476
|
const { buildConsultTools } = await import("@foldrun/core/agent-tools");
|
|
477
477
|
const { buildSearchTools, buildHistoryTools, buildDeskTools } = await import("@foldrun/core/context-tools");
|
|
478
|
-
const { httpChannel, buildAskTool, inboxReader } = await import("@foldrun/core/operator");
|
|
478
|
+
const { httpChannel, buildAskTool, buildPeerTools, inboxReader } = await import("@foldrun/core/operator");
|
|
479
479
|
const { startTranslator } = await import("@foldrun/core/translator");
|
|
480
480
|
const { prepareRuntime } = await import("@foldrun/core/runtime");
|
|
481
481
|
const { materializeFileSecrets } = await import("@foldrun/core/secret-files");
|
|
@@ -539,11 +539,18 @@ try {
|
|
|
539
539
|
// A long result is kept in a file only when the agent can read it back.
|
|
540
540
|
// (tool-output.ts canReadBack, inlined: the driver imports core by subpath.)
|
|
541
541
|
const keepIn = { dir: agentDir, readable: () => (input.allowed ?? []).some((t) => t === "Read" || t === "Grep") };
|
|
542
|
-
|
|
542
|
+
// A declared secret named like a model variable crosses as
|
|
543
|
+
// FOLDRUN_DECLARED_<NAME> (runner.ts, FOL-19): the model keeps its own
|
|
544
|
+
// settings, and the agent's API and script tools and verify: get the value
|
|
545
|
+
// back under the name it declared. Built here, after the runtime and any
|
|
546
|
+
// mid-step install have added their paths to env.
|
|
547
|
+
const toolEnv = { ...env };
|
|
548
|
+
for (const [k, v] of Object.entries(env)) if (k.startsWith("FOLDRUN_DECLARED_")) toolEnv[k.slice("FOLDRUN_DECLARED_".length)] = v;
|
|
549
|
+
const api = buildApiTools("", input.apis, undefined, { env: toolEnv, missing: [] }, keepIn);
|
|
543
550
|
const script = buildScriptTools(
|
|
544
551
|
agentDir,
|
|
545
552
|
input.scripts,
|
|
546
|
-
|
|
553
|
+
toolEnv,
|
|
547
554
|
"/library/scripts",
|
|
548
555
|
runtime.interpreters,
|
|
549
556
|
null,
|
|
@@ -566,6 +573,7 @@ try {
|
|
|
566
573
|
// ask_person when tools: [ask], and the run's inbox after every tool call.
|
|
567
574
|
const channel = httpChannel(env.FOLDRUN_EGRESS);
|
|
568
575
|
const ask = input.ask ? buildAskTool({ channel, timeoutSec: input.ask.timeoutSec, emit }) : { server: null };
|
|
576
|
+
const peers = input.peers ? buildPeerTools({ channel, self: input.peers.self, siblings: input.peers.siblings, emit }) : { server: null };
|
|
569
577
|
const inbox = channel ? inboxReader(channel, emit) : undefined;
|
|
570
578
|
|
|
571
579
|
mark("tools");
|
|
@@ -587,6 +595,7 @@ try {
|
|
|
587
595
|
...(history.server ? { foldrun_history: history.server } : {}),
|
|
588
596
|
...(desks.server ? { foldrun_desks: desks.server } : {}),
|
|
589
597
|
...(ask.server ? { foldrun_ask: ask.server } : {}),
|
|
598
|
+
...(peers.server ? { foldrun_peers: peers.server } : {}),
|
|
590
599
|
...(install.server ? { foldrun_install: install.server } : {}),
|
|
591
600
|
...input.mcpServers,
|
|
592
601
|
},
|
|
@@ -598,10 +607,11 @@ try {
|
|
|
598
607
|
budgetNote: input.budgetNote,
|
|
599
608
|
price: input.price,
|
|
600
609
|
verify: input.verify,
|
|
601
|
-
verifyEnv:
|
|
610
|
+
verifyEnv: toolEnv,
|
|
602
611
|
output: input.output,
|
|
603
612
|
schema: input.schema,
|
|
604
613
|
maxTurns: input.maxTurns,
|
|
614
|
+
...(input.capture === "metadata" || input.capture === "full" ? { capture: input.capture } : {}),
|
|
605
615
|
...(input.limits && Object.keys(input.limits).length ? { limits: input.limits, toolOwners: input.toolOwners ?? {} } : {}),
|
|
606
616
|
...(input.hookNames?.length ? { hookNames: input.hookNames, hookBase: input.hookBase ?? {} } : {}),
|
|
607
617
|
// Slim browsing: the platform put this step on the slim image to browse
|
|
@@ -1040,6 +1050,44 @@ export function buildFailureSummary(log, opts = {}) {
|
|
|
1040
1050
|
return `${head}\n--- the last ${tail} characters of the build log ---\n${log.slice(-tail)}`;
|
|
1041
1051
|
}
|
|
1042
1052
|
// ---------- running one step ----------
|
|
1053
|
+
const num = (v) => (typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0);
|
|
1054
|
+
/** A tool call's content from the sandbox, as far as it is well-formed and
|
|
1055
|
+
* within the cap; anything else is dropped (FOL-54 step 4). */
|
|
1056
|
+
function callIoOf(v) {
|
|
1057
|
+
if (!v || typeof v !== "object")
|
|
1058
|
+
return null;
|
|
1059
|
+
const o = v;
|
|
1060
|
+
// The cap plus the slack the runner masks inside before it cuts.
|
|
1061
|
+
const text = (x) => (typeof x === "string" ? x.slice(0, CAPTURE_CAP + 4096) : undefined);
|
|
1062
|
+
const io = {
|
|
1063
|
+
...(text(o.input) !== undefined ? { input: text(o.input) } : {}),
|
|
1064
|
+
...(text(o.output) !== undefined ? { output: text(o.output) } : {}),
|
|
1065
|
+
...(typeof o.inputChars === "number" ? { inputChars: num(o.inputChars) } : {}),
|
|
1066
|
+
...(typeof o.outputChars === "number" ? { outputChars: num(o.outputChars) } : {}),
|
|
1067
|
+
...(o.truncated === true ? { truncated: true } : {}),
|
|
1068
|
+
};
|
|
1069
|
+
return io.input !== undefined || io.output !== undefined ? io : null;
|
|
1070
|
+
}
|
|
1071
|
+
/** One model call from the sandbox, well-formed or dropped. */
|
|
1072
|
+
function modelCallOf(v) {
|
|
1073
|
+
if (!v || typeof v !== "object")
|
|
1074
|
+
return null;
|
|
1075
|
+
const o = v;
|
|
1076
|
+
const t = (o.tokens ?? {});
|
|
1077
|
+
if (typeof o.id !== "string" || !o.id || o.id.length > 128 || typeof o.at !== "string" || Number.isNaN(Date.parse(o.at)))
|
|
1078
|
+
return null;
|
|
1079
|
+
return {
|
|
1080
|
+
id: o.id,
|
|
1081
|
+
n: Math.max(1, Math.floor(num(o.n))),
|
|
1082
|
+
at: o.at,
|
|
1083
|
+
ms: num(o.ms),
|
|
1084
|
+
...(typeof o.model === "string" ? { model: o.model.slice(0, 128) } : {}),
|
|
1085
|
+
...(typeof o.stop === "string" ? { stop: o.stop.slice(0, 64) } : {}),
|
|
1086
|
+
tokens: { input: num(t.input), output: num(t.output), cacheRead: num(t.cacheRead), cacheWrite: num(t.cacheWrite) },
|
|
1087
|
+
tools: Math.floor(num(o.tools)),
|
|
1088
|
+
...(typeof o.subagent === "string" && /^[a-z0-9-]{1,64}$/.test(o.subagent) ? { subagent: o.subagent } : {}),
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1043
1091
|
export function parseDriverLine(line) {
|
|
1044
1092
|
const trimmed = line.trim();
|
|
1045
1093
|
if (!trimmed.startsWith("{"))
|
|
@@ -1059,6 +1107,10 @@ export function parseDriverLine(line) {
|
|
|
1059
1107
|
...(typeof parsed.subagent === "string" && /^[a-z0-9-]{1,64}$/.test(parsed.subagent) ? { subagent: parsed.subagent } : {}),
|
|
1060
1108
|
...(parsed.check === true ? { check: true } : {}),
|
|
1061
1109
|
...(isHookRecord(parsed.hook) ? { hook: parsed.hook } : {}),
|
|
1110
|
+
...(callIoOf(parsed.io) ? { io: callIoOf(parsed.io) } : {}),
|
|
1111
|
+
...(typeof parsed.inCall === "string" && parsed.inCall.length <= 128 ? { inCall: parsed.inCall } : {}),
|
|
1112
|
+
...(modelCallOf(parsed.turn) ? { turn: modelCallOf(parsed.turn) } : {}),
|
|
1113
|
+
...(parsed.responding === true ? { responding: true } : {}),
|
|
1062
1114
|
};
|
|
1063
1115
|
}
|
|
1064
1116
|
if (parsed.e === "done") {
|
package/dist/src/runner.d.ts
CHANGED
|
@@ -33,6 +33,46 @@ export declare function slotPool(size: number): {
|
|
|
33
33
|
acquire: () => Promise<void>;
|
|
34
34
|
release: () => void;
|
|
35
35
|
};
|
|
36
|
+
export declare function markResponding(step: StepRecord): void;
|
|
37
|
+
/**
|
|
38
|
+
* Is this step's prompt in the cache yet? An entry is readable from the
|
|
39
|
+
* moment the response to the request that wrote it begins streaming
|
|
40
|
+
* (Anthropic's prompt-caching docs), which is what `responding` marks.
|
|
41
|
+
* The first text, tool call or recorded model call says the same a little
|
|
42
|
+
* later, for an executor that cannot see the stream (the stub, an older
|
|
43
|
+
* driver). Resolves on the first of those, or when the step is over
|
|
44
|
+
* without one (a refusal before the first turn, a failed start).
|
|
45
|
+
*/
|
|
46
|
+
export declare function untilFirstReply(step: StepRecord, done: Promise<unknown>, pollMs?: number): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Steps of one agent launched together share a prompt prefix — tools and
|
|
49
|
+
* system prompt — and that is what the model's cache keeps. The cache is
|
|
50
|
+
* written by the first request that carries a prefix and readable only
|
|
51
|
+
* once that response has begun, so N copies started at once all write it
|
|
52
|
+
* and none reads: a fan-out of twenty paid twenty cold prefixes. The gate
|
|
53
|
+
* lets the first instance of each agent go alone and holds its siblings
|
|
54
|
+
* until its first response has begun (or it ended); then they all start
|
|
55
|
+
* and read what it wrote — Claude Code holds a workflow's fan-out the same
|
|
56
|
+
* way. Not a fixed delay: a sandbox takes seconds to start before its
|
|
57
|
+
* first model call, so a timer from launch would release the siblings
|
|
58
|
+
* before there was anything to read. Keyed by agent and model, since a
|
|
59
|
+
* cache is per model.
|
|
60
|
+
*/
|
|
61
|
+
export declare function warmGate(launching: StepRecord[], pollMs?: number): {
|
|
62
|
+
/** Claim the step's place — synchronously, before anything awaits, or
|
|
63
|
+
* the copies that reach their first await together would all find no
|
|
64
|
+
* lead and all go (a `parallel:` pool's acquire was exactly that).
|
|
65
|
+
* The first of its agent leads and gets null; a later one gets the
|
|
66
|
+
* lead's gate to await before it starts; a lone step gets null too. */
|
|
67
|
+
claim(step: StepRecord): Promise<void> | null;
|
|
68
|
+
/** A lead that will not run after all (the run was stopped while it
|
|
69
|
+
* waited for a pool slot) still opens its gate — or its siblings,
|
|
70
|
+
* which also wake to the stop, would wait on it forever. */
|
|
71
|
+
release(step: StepRecord): void;
|
|
72
|
+
/** Run the step; a lead opens its agent's gate on its first reply (or
|
|
73
|
+
* its end), whatever else happens to it. */
|
|
74
|
+
run(step: StepRecord, launch: () => Promise<void>): Promise<void>;
|
|
75
|
+
};
|
|
36
76
|
export interface DiscoveredSkill {
|
|
37
77
|
name: string;
|
|
38
78
|
description: string;
|
|
@@ -258,6 +298,8 @@ grantsOnly?: boolean): {
|
|
|
258
298
|
* reported by the step, not here, so it lands in the run's trace. */
|
|
259
299
|
clock: ClockChoice;
|
|
260
300
|
systemPrompt: string;
|
|
301
|
+
/** What the record keeps of each layer of that prompt. */
|
|
302
|
+
promptLayers: import("./tokens.ts").PromptLayerRecord[];
|
|
261
303
|
allowed: string[];
|
|
262
304
|
disabled: string[];
|
|
263
305
|
unknownTools: string[];
|
|
@@ -280,6 +322,7 @@ grantsOnly?: boolean): {
|
|
|
280
322
|
ask: {
|
|
281
323
|
timeoutSec: number;
|
|
282
324
|
} | null;
|
|
325
|
+
peers: boolean;
|
|
283
326
|
runtime: {
|
|
284
327
|
log: string[];
|
|
285
328
|
error: string | null;
|
|
@@ -328,6 +371,7 @@ grantsOnly?: boolean): {
|
|
|
328
371
|
maxTokensParam?: "max_tokens" | "max_completion_tokens";
|
|
329
372
|
reasoningEffort?: boolean;
|
|
330
373
|
params?: Record<string, unknown>;
|
|
374
|
+
cacheKey?: string;
|
|
331
375
|
label?: string;
|
|
332
376
|
egress?: string;
|
|
333
377
|
timezone: string;
|
|
@@ -340,6 +384,7 @@ grantsOnly?: boolean): {
|
|
|
340
384
|
maxTokensParam?: "max_tokens" | "max_completion_tokens";
|
|
341
385
|
reasoningEffort?: boolean;
|
|
342
386
|
params?: Record<string, unknown>;
|
|
387
|
+
cacheKey?: string;
|
|
343
388
|
label?: string;
|
|
344
389
|
egress?: string;
|
|
345
390
|
timezone: string;
|
|
@@ -583,7 +628,13 @@ export interface Reconciliation {
|
|
|
583
628
|
* outlives any process. A run whose person already answered is not waiting,
|
|
584
629
|
* and is picked back up — see below.
|
|
585
630
|
*/
|
|
586
|
-
|
|
631
|
+
/** Told when a run reconcile closed, or resumed and saw to its end — so the
|
|
632
|
+
* platform settles, notifies and chains it as it would a run its worker
|
|
633
|
+
* drove (a run closed here had spent tokens, and nobody was charged or
|
|
634
|
+
* told). Not told when a resumed run parks again: whatever resumes it next
|
|
635
|
+
* goes through the worker. */
|
|
636
|
+
export type ReconcileEnd = (tenant: string, workspace: string, runId: string) => void | Promise<void>;
|
|
637
|
+
export declare function reconcileRuns(tenant: string, now?: number, live?: ReadonlySet<string>, onEnd?: ReconcileEnd): Reconciliation[];
|
|
587
638
|
/**
|
|
588
639
|
* The same, for every account this installation holds.
|
|
589
640
|
*
|
|
@@ -591,7 +642,7 @@ export declare function reconcileRuns(tenant: string, now?: number, live?: Reado
|
|
|
591
642
|
* one hard-coded account left every other account's interrupted runs marked
|
|
592
643
|
* `running` forever, with no process left to finish them.
|
|
593
644
|
*/
|
|
594
|
-
export declare function reconcileAllRuns(now?: number, live?: ReadonlySet<string
|
|
645
|
+
export declare function reconcileAllRuns(now?: number, live?: ReadonlySet<string>, onEnd?: ReconcileEnd): Reconciliation[];
|
|
595
646
|
/**
|
|
596
647
|
* Wait for a run to stop being in progress.
|
|
597
648
|
*
|