@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
package/dist/src/store.d.ts
CHANGED
|
@@ -282,6 +282,12 @@ export interface ApiSpec {
|
|
|
282
282
|
export interface AgentInfo {
|
|
283
283
|
name: string;
|
|
284
284
|
description: string;
|
|
285
|
+
/** `owner:` — the person accountable for this agent: its own, else the
|
|
286
|
+
* workspace AGENTS.md's, else the account's. Null when none of the three
|
|
287
|
+
* names one. */
|
|
288
|
+
owner: string | null;
|
|
289
|
+
/** Which of the three `owner` came from. */
|
|
290
|
+
ownerFrom: OwnerSource | null;
|
|
285
291
|
model: string;
|
|
286
292
|
/** The other half of model selection. listAgents has always returned it;
|
|
287
293
|
* the interface did not say so, so every consumer had to re-read the file
|
|
@@ -352,6 +358,10 @@ export interface AgentInfo {
|
|
|
352
358
|
/** Tools this agent declares inline rather than in `tools:` — `apis:`,
|
|
353
359
|
* `scripts:` and `mcpServers:` names — which a limit may also name. */
|
|
354
360
|
inlineTools: string[];
|
|
361
|
+
/** agent.md's frontmatter does not parse (bad YAML): the agent is listed
|
|
362
|
+
* with defaults and this says why, rather than the listing throwing —
|
|
363
|
+
* one broken file once took down every account's worker (20 Oct 2026). */
|
|
364
|
+
frontmatterError?: string;
|
|
355
365
|
}
|
|
356
366
|
export declare const MODEL_TIERS: Record<string, string>;
|
|
357
367
|
export type Tier = keyof typeof MODEL_TIERS;
|
|
@@ -512,6 +522,9 @@ export interface FlowInfo {
|
|
|
512
522
|
* overlap) — a default that changed underfoot would surprise every flow
|
|
513
523
|
* that legitimately runs in parallel. */
|
|
514
524
|
overlap: "skip" | "queue" | null;
|
|
525
|
+
/** `issues: auto-close`: a run that goes fine closes this flow's open
|
|
526
|
+
* issues (FOL-72 step 7). Unset is `stay-open`: a person resolves them. */
|
|
527
|
+
issues?: "auto-close" | "stay-open";
|
|
515
528
|
/** Where this flow's runs stand in the queue against everyone else's:
|
|
516
529
|
* "high" is claimed before the round-robin, "low" after it. Adds to the
|
|
517
530
|
* account's own priority; the account's cap still bounds how many run
|
|
@@ -606,6 +619,12 @@ export interface FlowInfo {
|
|
|
606
619
|
* and the trace. Read here so a documented, scaffolded field means
|
|
607
620
|
* something rather than sitting in every file unread. */
|
|
608
621
|
description: string | null;
|
|
622
|
+
/** `owner:` — the person accountable for this flow: its own, else the
|
|
623
|
+
* workspace AGENTS.md's, else the account's. parseFlow sees only the
|
|
624
|
+
* file, so it fills the flow's own; listFlows and readFlow add the rest. */
|
|
625
|
+
owner: string | null;
|
|
626
|
+
/** Which of the three `owner` came from. */
|
|
627
|
+
ownerFrom: OwnerSource | null;
|
|
609
628
|
/** Flow-level frontmatter values the parser could not read — a bad
|
|
610
629
|
* `overlap:`/`priority:`/`catchup:`/`on:`/`signature:`, or a step line
|
|
611
630
|
* whose `[[target]]` is malformed. Errors, the way a bad step option is,
|
|
@@ -621,6 +640,9 @@ export interface AgentAssets {
|
|
|
621
640
|
}
|
|
622
641
|
export declare function agentAssets(tenant: string, workspace: string, agent: string): AgentAssets;
|
|
623
642
|
export declare function listAgents(tenant: string, workspace: string): AgentInfo[];
|
|
643
|
+
/** The first line of a YAML parse error, with where it is: what a person
|
|
644
|
+
* needs to fix the file, without the parser's excerpt. */
|
|
645
|
+
export declare function frontmatterErrorText(err: unknown): string;
|
|
624
646
|
/** The option keys a step actually has. An indented `word: rest` that is
|
|
625
647
|
* NOT one of these is a line of the instruction — "Warning: do not
|
|
626
648
|
* publish" is prose that happens to contain a colon, and it used to be
|
|
@@ -748,6 +770,14 @@ export declare const reorderFlowSteps: (raw: string, groups: number[][]) => stri
|
|
|
748
770
|
export declare const addFlowStep: (raw: string, step: Parameters<typeof addFlowStepLF>[1]) => string;
|
|
749
771
|
export declare const removeFlowStep: (raw: string, index: number) => string;
|
|
750
772
|
export declare function listFlows(tenant: string, workspace: string): FlowInfo[];
|
|
773
|
+
export type OwnerSource = "agent" | "flow" | "workspace" | "account";
|
|
774
|
+
/** `owner:` down the cascade: the file's own, the workspace AGENTS.md's, the
|
|
775
|
+
* account's. A blank or non-string value is no value. `own` is labelled
|
|
776
|
+
* "agent" or "flow" by the caller's kind; here it is whichever is given. */
|
|
777
|
+
export declare function resolveOwner(own: unknown, workspace?: unknown, account?: unknown, ownKind?: "agent" | "flow"): {
|
|
778
|
+
owner: string | null;
|
|
779
|
+
ownerFrom: OwnerSource | null;
|
|
780
|
+
};
|
|
751
781
|
/** One flow of a workspace by name, or null. The same read listFlows makes,
|
|
752
782
|
* named so callers that want a single flow — the approval rules, the
|
|
753
783
|
* runner's budget stamp — do not each re-implement the directory walk. */
|
|
@@ -922,6 +952,62 @@ export interface RunEvent {
|
|
|
922
952
|
reason?: string;
|
|
923
953
|
ms: number;
|
|
924
954
|
};
|
|
955
|
+
/** Set on a tool event whose content was recorded (FOL-54 step 4): the
|
|
956
|
+
* call's input, on the call; its output, on the completion. The content is
|
|
957
|
+
* under runs/<run>/calls/<call>.json, secrets masked — readCapturedCall.
|
|
958
|
+
* Absent when the workspace says `capture: metadata`, and before 19 Oct
|
|
959
|
+
* 2026. */
|
|
960
|
+
captured?: boolean;
|
|
961
|
+
/** On a tool call: the id of the model call that asked for it (a
|
|
962
|
+
* ModelCall's `id`), so the trace can nest it. Since 19 Oct 2026. */
|
|
963
|
+
inCall?: string;
|
|
964
|
+
}
|
|
965
|
+
/** One model call in a step (FOL-54 step 4): what the model was sent back
|
|
966
|
+
* for and what came of it. A step's turns, one by one, where the step
|
|
967
|
+
* record had only their sum. */
|
|
968
|
+
export interface ModelCall {
|
|
969
|
+
/** The provider's message id. */
|
|
970
|
+
id: string;
|
|
971
|
+
/** 1, 2, 3 … in the order they came back. */
|
|
972
|
+
n: number;
|
|
973
|
+
/** When the response arrived. */
|
|
974
|
+
at: string;
|
|
975
|
+
/** From the request going out (the step's start, or the last tool result
|
|
976
|
+
* coming back) to the response arriving. */
|
|
977
|
+
ms: number;
|
|
978
|
+
model?: string;
|
|
979
|
+
/** Why the model stopped: end_turn, tool_use, max_tokens, … */
|
|
980
|
+
stop?: string;
|
|
981
|
+
/** All input (fresh + cache), as the step's own `tokens` counts it. */
|
|
982
|
+
tokens: {
|
|
983
|
+
input: number;
|
|
984
|
+
output: number;
|
|
985
|
+
cacheRead: number;
|
|
986
|
+
cacheWrite: number;
|
|
987
|
+
};
|
|
988
|
+
/** How many tools it called in this turn. */
|
|
989
|
+
tools: number;
|
|
990
|
+
/** Set when a sub-agent (`subagents:`) made the call. */
|
|
991
|
+
subagent?: string;
|
|
992
|
+
/** Which attempt of the step made it, from the second on. */
|
|
993
|
+
attempt?: number;
|
|
994
|
+
}
|
|
995
|
+
/** The most of one tool input or output a run records (FOL-54 step 4). */
|
|
996
|
+
export declare const CAPTURE_CAP: number;
|
|
997
|
+
/** What a recorded tool call took and gave, secrets masked, each part capped. */
|
|
998
|
+
export interface CapturedCall {
|
|
999
|
+
id: string;
|
|
1000
|
+
tool: string;
|
|
1001
|
+
input?: string;
|
|
1002
|
+
output?: string;
|
|
1003
|
+
/** The whole input's / output's length in characters, before masking and
|
|
1004
|
+
* the cap; `truncated` says whether the text was cut. */
|
|
1005
|
+
inputChars?: number;
|
|
1006
|
+
outputChars?: number;
|
|
1007
|
+
/** Either part cut; and which. */
|
|
1008
|
+
truncated?: boolean;
|
|
1009
|
+
inputCut?: boolean;
|
|
1010
|
+
outputCut?: boolean;
|
|
925
1011
|
}
|
|
926
1012
|
/** A question an agent asked a person while its step ran (tools: [ask]). */
|
|
927
1013
|
export interface StepQuestion {
|
|
@@ -935,12 +1021,18 @@ export interface StepQuestion {
|
|
|
935
1021
|
/** true when the wait ran out with no answer. */
|
|
936
1022
|
unanswered?: boolean;
|
|
937
1023
|
}
|
|
938
|
-
/** A message a person sent into a running step, and when the agent got it
|
|
1024
|
+
/** A message a person sent into a running step, and when the agent got it —
|
|
1025
|
+
* or, with `from`, one a colleague in the same parallel group sent it
|
|
1026
|
+
* (tools: [peers]); with `to`, one this step sent its colleagues. */
|
|
939
1027
|
export interface StepMessage {
|
|
940
1028
|
text: string;
|
|
941
1029
|
by?: string | null;
|
|
942
1030
|
at: string;
|
|
943
1031
|
deliveredAt: string;
|
|
1032
|
+
/** The sibling agent that sent it (`by` is `peer:<agent>` then). */
|
|
1033
|
+
from?: string;
|
|
1034
|
+
/** The sibling agents this step sent it to. */
|
|
1035
|
+
to?: string[];
|
|
944
1036
|
}
|
|
945
1037
|
/** One attempt of a step — see StepRecord.tries. */
|
|
946
1038
|
export interface StepAttempt {
|
|
@@ -975,8 +1067,8 @@ export interface StepAttempt {
|
|
|
975
1067
|
* attempt reported the figure — an in-process step rents no sandbox, and
|
|
976
1068
|
* billing reads null as zero, not as "unknown, charge something". */
|
|
977
1069
|
export declare function sumAttempts(tries: StepAttempt[]): Pick<StepAttempt, "costUsd" | "tokens" | "computeSecs" | "turns">;
|
|
978
|
-
export { type TokenUsage, tokensOf, sumTokens, cacheHitRate, measuredHitRate, runTokens } from "./tokens.ts";
|
|
979
|
-
import { type TokenUsage } from "./tokens.ts";
|
|
1070
|
+
export { type TokenUsage, type PromptLayerRecord, tokensOf, sumTokens, cacheHitRate, measuredHitRate, runTokens, promptLayersChanged, promptCacheNote } from "./tokens.ts";
|
|
1071
|
+
import { type TokenUsage, type PromptLayerRecord } from "./tokens.ts";
|
|
980
1072
|
export interface StepRecord {
|
|
981
1073
|
agent: string;
|
|
982
1074
|
instruction: string;
|
|
@@ -1060,6 +1152,9 @@ export interface StepRecord {
|
|
|
1060
1152
|
* records from before this existed; readers fall back to the events. */
|
|
1061
1153
|
startedAt?: string;
|
|
1062
1154
|
finishedAt?: string;
|
|
1155
|
+
/** Every model call the step made, in order (FOL-54 step 4). Absent before
|
|
1156
|
+
* 19 Oct 2026. */
|
|
1157
|
+
calls?: ModelCall[];
|
|
1063
1158
|
/** Why a step was skipped, shown in the trace. */
|
|
1064
1159
|
skipReason?: string;
|
|
1065
1160
|
/** Evaluator loop, carried from the flow — see FlowStep. `loopRemaining`
|
|
@@ -1170,6 +1265,11 @@ export interface StepRecord {
|
|
|
1170
1265
|
* what says where a step's input tokens went. Absent before 18 Oct 2026. */
|
|
1171
1266
|
turns?: number | null;
|
|
1172
1267
|
systemPromptChars?: number;
|
|
1268
|
+
/** The system prompt's layers — name, length and hash of each, stable
|
|
1269
|
+
* ones first, the ones that change between runs marked. Two steps of
|
|
1270
|
+
* one agent compared layer by layer say why the model's cache missed
|
|
1271
|
+
* (prompt-layers.ts). Absent before 20 Oct 2026. */
|
|
1272
|
+
promptLayers?: PromptLayerRecord[];
|
|
1173
1273
|
/** Why the model's last turn ended (end_turn, max_tokens, tool_use…), as
|
|
1174
1274
|
* the SDK's closing result says. */
|
|
1175
1275
|
stopReason?: string | null;
|
|
@@ -1237,6 +1337,21 @@ export interface RunRecord {
|
|
|
1237
1337
|
* the agents decided was worth remembering, listed so a person can review
|
|
1238
1338
|
* the lesson without diffing the folder. */
|
|
1239
1339
|
memoryWrites?: string[];
|
|
1340
|
+
/** The workspace's deployed commit when the run was created — which
|
|
1341
|
+
* version of the files it ran (FOL-54 step 4). Absent for a workspace
|
|
1342
|
+
* never deployed, and before 19 Oct 2026. */
|
|
1343
|
+
revision?: string | null;
|
|
1344
|
+
/** When a worker first picked the run up: startedAt is when it was
|
|
1345
|
+
* created, so the gap is its time in the queue. */
|
|
1346
|
+
drivenAt?: string | null;
|
|
1347
|
+
/** A re-run's source (FOL-72 step 6): which run, the 1-based step it
|
|
1348
|
+
* started from, and the instruction it was given there when a person
|
|
1349
|
+
* edited it for this re-run alone. */
|
|
1350
|
+
rerunOf?: {
|
|
1351
|
+
run: string;
|
|
1352
|
+
step: number;
|
|
1353
|
+
instruction?: string;
|
|
1354
|
+
};
|
|
1240
1355
|
/** Who asked for this run, when a person did: their email address. A
|
|
1241
1356
|
* schedule, a webhook and a chained flow have no one, and leave it unset.
|
|
1242
1357
|
* Kept so a gate can refuse the one approval nobody should be able to
|
|
@@ -1259,6 +1374,11 @@ export interface RunRecord {
|
|
|
1259
1374
|
approvalLinksUsed?: string[];
|
|
1260
1375
|
steps: StepRecord[];
|
|
1261
1376
|
}
|
|
1377
|
+
/** Merge one part (the input, on the call; the output, on its completion)
|
|
1378
|
+
* into the call's file. */
|
|
1379
|
+
export declare function writeCapturedCall(workspaceRoot: string, runId: string, part: CapturedCall): void;
|
|
1380
|
+
/** One recorded call of a run, or null (not recorded, or not a call). */
|
|
1381
|
+
export declare function readCapturedCall(tenant: string, workspace: string, runId: string, callId: string): CapturedCall | null;
|
|
1262
1382
|
export declare function runFilePath(tenant: string, workspace: string, runId: string): string;
|
|
1263
1383
|
/**
|
|
1264
1384
|
* Erase a run from history: its record and the outputs archived under its
|
|
@@ -1346,6 +1466,11 @@ export interface RunSummaryStep {
|
|
|
1346
1466
|
instruction?: string;
|
|
1347
1467
|
/** When a parked step last spoke — the moment the banner sorts by. */
|
|
1348
1468
|
waitingSince?: string;
|
|
1469
|
+
/** For search (FOL-72 step 9): the tools it called, how many of its calls
|
|
1470
|
+
* errored, and its model. Absent on rows written before 19 Oct 2026. */
|
|
1471
|
+
tools?: string[];
|
|
1472
|
+
toolErrors?: number;
|
|
1473
|
+
model?: string;
|
|
1349
1474
|
startedAt?: string;
|
|
1350
1475
|
finishedAt?: string;
|
|
1351
1476
|
}
|
|
@@ -1363,6 +1488,8 @@ export interface RunSummary {
|
|
|
1363
1488
|
tags?: string[];
|
|
1364
1489
|
startedBy?: string | null;
|
|
1365
1490
|
parkedAt?: string | null;
|
|
1491
|
+
/** The revision it ran (RunRecord.revision), for issues' "since". */
|
|
1492
|
+
revision?: string | null;
|
|
1366
1493
|
/** A step is parked for a person — what the approvals banner counts. */
|
|
1367
1494
|
awaitingApproval: boolean;
|
|
1368
1495
|
steps: RunSummaryStep[];
|
package/dist/src/store.js
CHANGED
|
@@ -27,7 +27,8 @@ import { recordRevision, registerTreeReader } from "./history.js";
|
|
|
27
27
|
import { dataRoot, singleWorkspace, singleAccountRoot, entriesNoFollow } from "./paths.js";
|
|
28
28
|
import { platform } from "./platform.js";
|
|
29
29
|
import { toLF, asRaw } from "./eol.js";
|
|
30
|
-
import matter from "
|
|
30
|
+
import matter from "./matter.js";
|
|
31
|
+
import { readAgentsMdKeys } from "./agents-md.js";
|
|
31
32
|
/**
|
|
32
33
|
* The frontmatter of one markdown file, as the runtime reads it. Exported
|
|
33
34
|
* for the CLI: `foldrun probe` used to import gray-matter itself, which is
|
|
@@ -1118,7 +1119,18 @@ export function listAgents(tenant, workspace) {
|
|
|
1118
1119
|
.filter((n) => fs.existsSync(path.join(dir, n, "agent.md")))
|
|
1119
1120
|
.sort()
|
|
1120
1121
|
.map((name) => {
|
|
1121
|
-
|
|
1122
|
+
// One agent whose YAML does not parse is that agent's problem, not
|
|
1123
|
+
// the account's: listed with defaults and the reason, never thrown
|
|
1124
|
+
// out of a listing every page, the scheduler and the worker's boot
|
|
1125
|
+
// read.
|
|
1126
|
+
let data = {};
|
|
1127
|
+
let frontmatterError;
|
|
1128
|
+
try {
|
|
1129
|
+
({ data } = matter(fs.readFileSync(path.join(dir, name, "agent.md"), "utf8")));
|
|
1130
|
+
}
|
|
1131
|
+
catch (err) {
|
|
1132
|
+
frontmatterError = frontmatterErrorText(err);
|
|
1133
|
+
}
|
|
1122
1134
|
const tools = refNames(data.tools);
|
|
1123
1135
|
const actions = webConfig(data, { ...accountFront, ...workspaceFront }).actions;
|
|
1124
1136
|
const readsWeb = tools.includes("web") && (!actions || actions.some((a) => a === "fetch" || a === "browse"));
|
|
@@ -1141,6 +1153,7 @@ export function listAgents(tenant, workspace) {
|
|
|
1141
1153
|
network,
|
|
1142
1154
|
name,
|
|
1143
1155
|
description: data.description ?? "",
|
|
1156
|
+
...resolveOwner(data.owner, workspaceFront.owner, accountFront.owner),
|
|
1144
1157
|
model: data.model ?? "default",
|
|
1145
1158
|
effort: data.effort ?? null,
|
|
1146
1159
|
tools,
|
|
@@ -1164,9 +1177,22 @@ export function listAgents(tenant, workspace) {
|
|
|
1164
1177
|
...(() => { const l = readLimits(data.limits); return { limits: l.limits, limitProblems: l.problems }; })(),
|
|
1165
1178
|
networkProblems: networkProblems(data.network),
|
|
1166
1179
|
inlineTools: inlineToolNames(data),
|
|
1180
|
+
...(frontmatterError ? { frontmatterError } : {}),
|
|
1167
1181
|
};
|
|
1168
1182
|
});
|
|
1169
1183
|
}
|
|
1184
|
+
/** The first line of a YAML parse error, with where it is: what a person
|
|
1185
|
+
* needs to fix the file, without the parser's excerpt. */
|
|
1186
|
+
export function frontmatterErrorText(err) {
|
|
1187
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1188
|
+
return `frontmatter does not parse — ${msg.split("\n")[0].trim()}. Quote a value that contains a colon followed by a space.`;
|
|
1189
|
+
}
|
|
1190
|
+
/** The body of a file whose frontmatter would not parse: what follows the
|
|
1191
|
+
* closing fence, so a flow's steps can still be read and shown. */
|
|
1192
|
+
function bodyAfterFrontmatter(raw) {
|
|
1193
|
+
const m = raw.match(/^---\n[\s\S]*?\n---[ \t]*(?:\n|$)/);
|
|
1194
|
+
return m ? raw.slice(m[0].length) : raw;
|
|
1195
|
+
}
|
|
1170
1196
|
// A step targets an agent — `[[writer]]` — or another flow — `[[flow:digest]]`,
|
|
1171
1197
|
// which is how one flow triggers another.
|
|
1172
1198
|
// `(\S.*|)` rather than `(.*)`: the instruction either starts with a
|
|
@@ -1326,11 +1352,26 @@ export function parseFlow(file, raw) {
|
|
|
1326
1352
|
// every line kept its "\r", which STEP_RE's `.` will not match. Read it as
|
|
1327
1353
|
// LF; the line count, and so every step's `line`, is the same.
|
|
1328
1354
|
raw = raw.replace(/\r\n/g, "\n");
|
|
1329
|
-
|
|
1355
|
+
// Frontmatter that does not parse is a flow-level problem (an error in
|
|
1356
|
+
// check), not a throw: listFlows feeds every workspace listing, the
|
|
1357
|
+
// scheduler and the worker's boot. With no frontmatter read the flow is
|
|
1358
|
+
// manual, so a broken schedule never fires.
|
|
1359
|
+
let data = {};
|
|
1360
|
+
let content;
|
|
1361
|
+
let frontError = null;
|
|
1362
|
+
try {
|
|
1363
|
+
({ data, content } = matter(raw));
|
|
1364
|
+
}
|
|
1365
|
+
catch (err) {
|
|
1366
|
+
frontError = frontmatterErrorText(err);
|
|
1367
|
+
content = bodyAfterFrontmatter(raw);
|
|
1368
|
+
}
|
|
1330
1369
|
const steps = [];
|
|
1331
1370
|
// Flow-level problems: a malformed step target (below) and unreadable
|
|
1332
1371
|
// frontmatter enums (before the return). Reported as errors by flow-lint.
|
|
1333
1372
|
const frontProblems = [];
|
|
1373
|
+
if (frontError)
|
|
1374
|
+
frontProblems.push(frontError);
|
|
1334
1375
|
// Frontmatter is stripped by matter(), so add it back to keep line numbers
|
|
1335
1376
|
// pointing at the real file rather than the body.
|
|
1336
1377
|
const offset = raw.slice(0, raw.length - content.length).split("\n").length - 1;
|
|
@@ -1573,6 +1614,7 @@ export function parseFlow(file, raw) {
|
|
|
1573
1614
|
}
|
|
1574
1615
|
};
|
|
1575
1616
|
badEnum("overlap", data.overlap, ["skip", "queue"]);
|
|
1617
|
+
badEnum("issues", data.issues, ["stay-open", "auto-close"]);
|
|
1576
1618
|
badEnum("priority", data.priority, ["high", "normal", "low"]);
|
|
1577
1619
|
badEnum("catchup", data.catchup, ["none", "last"]);
|
|
1578
1620
|
badEnum("on", data.on, ["completed", "failed", "blocked", "any"]);
|
|
@@ -1587,6 +1629,7 @@ export function parseFlow(file, raw) {
|
|
|
1587
1629
|
model: data.model ?? null,
|
|
1588
1630
|
effort: data.effort ?? null,
|
|
1589
1631
|
overlap: data.overlap === "skip" || data.overlap === "queue" ? data.overlap : null,
|
|
1632
|
+
...(data.issues === "auto-close" || data.issues === "stay-open" ? { issues: data.issues } : {}),
|
|
1590
1633
|
live: data.live === true,
|
|
1591
1634
|
priority: data.priority === "high" || data.priority === "low" || data.priority === "normal" ? data.priority : null,
|
|
1592
1635
|
budget: runBudget(data.budget),
|
|
@@ -1622,6 +1665,7 @@ export function parseFlow(file, raw) {
|
|
|
1622
1665
|
approvers: approverList(data.approvers),
|
|
1623
1666
|
approveWithin: durationOf(data.approve_within),
|
|
1624
1667
|
description: typeof data.description === "string" ? data.description.trim() || null : null,
|
|
1668
|
+
...resolveOwner(data.owner, undefined, undefined, "flow"),
|
|
1625
1669
|
frontProblems,
|
|
1626
1670
|
steps,
|
|
1627
1671
|
};
|
|
@@ -1932,11 +1976,38 @@ export function listFlows(tenant, workspace) {
|
|
|
1932
1976
|
const dir = path.join(workspaceDir(tenant, workspace), "flows");
|
|
1933
1977
|
if (!fs.existsSync(dir))
|
|
1934
1978
|
return [];
|
|
1979
|
+
const owned = flowOwner(tenant, workspace);
|
|
1935
1980
|
return fs
|
|
1936
1981
|
.readdirSync(dir)
|
|
1937
1982
|
.filter((f) => f.endsWith(".md"))
|
|
1938
1983
|
.sort()
|
|
1939
|
-
.map((f) => parseFlow(f, fs.readFileSync(path.join(dir, f), "utf8")));
|
|
1984
|
+
.map((f) => owned(parseFlow(f, fs.readFileSync(path.join(dir, f), "utf8"))));
|
|
1985
|
+
}
|
|
1986
|
+
/** A flow with its owner filled from the workspace and account when its own
|
|
1987
|
+
* file names none. */
|
|
1988
|
+
function flowOwner(tenant, workspace) {
|
|
1989
|
+
let above = null;
|
|
1990
|
+
return (flow) => {
|
|
1991
|
+
if (flow.owner)
|
|
1992
|
+
return flow;
|
|
1993
|
+
above ??= {
|
|
1994
|
+
workspace: readAgentsMdKeys(workspaceDir(tenant, workspace)).owner,
|
|
1995
|
+
account: readAgentsMdKeys(accountDir(tenant)).owner,
|
|
1996
|
+
};
|
|
1997
|
+
return { ...flow, ...resolveOwner(undefined, above.workspace, above.account) };
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
/** `owner:` down the cascade: the file's own, the workspace AGENTS.md's, the
|
|
2001
|
+
* account's. A blank or non-string value is no value. `own` is labelled
|
|
2002
|
+
* "agent" or "flow" by the caller's kind; here it is whichever is given. */
|
|
2003
|
+
export function resolveOwner(own, workspace, account, ownKind = "agent") {
|
|
2004
|
+
const text = (v) => (typeof v === "string" && v.trim() ? v.trim() : null);
|
|
2005
|
+
for (const [v, from] of [[own, ownKind], [workspace, "workspace"], [account, "account"]]) {
|
|
2006
|
+
const t = text(v);
|
|
2007
|
+
if (t)
|
|
2008
|
+
return { owner: t, ownerFrom: from };
|
|
2009
|
+
}
|
|
2010
|
+
return { owner: null, ownerFrom: null };
|
|
1940
2011
|
}
|
|
1941
2012
|
/** One flow of a workspace by name, or null. The same read listFlows makes,
|
|
1942
2013
|
* named so callers that want a single flow — the approval rules, the
|
|
@@ -1948,7 +2019,7 @@ export function readFlow(tenant, workspace, flowName) {
|
|
|
1948
2019
|
for (const f of fs.readdirSync(dir).filter((f) => f.endsWith(".md"))) {
|
|
1949
2020
|
const flow = parseFlow(f, fs.readFileSync(path.join(dir, f), "utf8"));
|
|
1950
2021
|
if (flow.name === flowName)
|
|
1951
|
-
return flow;
|
|
2022
|
+
return flowOwner(tenant, workspace)(flow);
|
|
1952
2023
|
}
|
|
1953
2024
|
return null;
|
|
1954
2025
|
}
|
|
@@ -2568,6 +2639,8 @@ export function flowPatternTemplate(pattern, name, agents) {
|
|
|
2568
2639
|
return FLOW_TEMPLATE(name, a(0, "my-agent"));
|
|
2569
2640
|
}
|
|
2570
2641
|
}
|
|
2642
|
+
/** The most of one tool input or output a run records (FOL-54 step 4). */
|
|
2643
|
+
export const CAPTURE_CAP = 64 * 1024;
|
|
2571
2644
|
/** The step's totals across its attempts, from the rows. Null where no
|
|
2572
2645
|
* attempt reported the figure — an in-process step rents no sandbox, and
|
|
2573
2646
|
* billing reads null as zero, not as "unknown, charge something". */
|
|
@@ -2583,8 +2656,53 @@ export function sumAttempts(tries) {
|
|
|
2583
2656
|
...(turns.length ? { turns: turns.reduce((a, b) => a + b, 0) } : {}),
|
|
2584
2657
|
};
|
|
2585
2658
|
}
|
|
2586
|
-
export { tokensOf, sumTokens, cacheHitRate, measuredHitRate, runTokens } from "./tokens.js";
|
|
2659
|
+
export { tokensOf, sumTokens, cacheHitRate, measuredHitRate, runTokens, promptLayersChanged, promptCacheNote } from "./tokens.js";
|
|
2587
2660
|
import { sumTokens } from "./tokens.js";
|
|
2661
|
+
// ---------------------------------------------------------------- captured calls
|
|
2662
|
+
//
|
|
2663
|
+
// What each tool call took and gave (FOL-54 step 4), beside the run under
|
|
2664
|
+
// runs/<run>/calls/<call>.json — out of the run record, which every list
|
|
2665
|
+
// and every page reads whole, and gone with the run (deleteRun removes
|
|
2666
|
+
// runs/<run>/). The runner writes them, secrets already masked.
|
|
2667
|
+
const SAFE_ID = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
2668
|
+
function capturedCallFile(workspaceRoot, runId, callId) {
|
|
2669
|
+
if (!SAFE_ID.test(runId) || !SAFE_ID.test(callId) || callId.startsWith(".") || runId.startsWith("."))
|
|
2670
|
+
return null;
|
|
2671
|
+
return path.join(workspaceRoot, "runs", runId, "calls", `${callId}.json`);
|
|
2672
|
+
}
|
|
2673
|
+
/** Merge one part (the input, on the call; the output, on its completion)
|
|
2674
|
+
* into the call's file. */
|
|
2675
|
+
export function writeCapturedCall(workspaceRoot, runId, part) {
|
|
2676
|
+
const file = capturedCallFile(workspaceRoot, runId, part.id);
|
|
2677
|
+
if (!file)
|
|
2678
|
+
return;
|
|
2679
|
+
let prev = {};
|
|
2680
|
+
try {
|
|
2681
|
+
prev = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
2682
|
+
}
|
|
2683
|
+
catch {
|
|
2684
|
+
/* the first part */
|
|
2685
|
+
}
|
|
2686
|
+
const next = { ...prev, ...part, truncated: Boolean(prev.truncated || part.truncated) || undefined };
|
|
2687
|
+
if (!next.truncated)
|
|
2688
|
+
delete next.truncated;
|
|
2689
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
2690
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
2691
|
+
fs.writeFileSync(tmp, JSON.stringify(next));
|
|
2692
|
+
fs.renameSync(tmp, file);
|
|
2693
|
+
}
|
|
2694
|
+
/** One recorded call of a run, or null (not recorded, or not a call). */
|
|
2695
|
+
export function readCapturedCall(tenant, workspace, runId, callId) {
|
|
2696
|
+
const file = capturedCallFile(workspaceDir(tenant, workspace), runId, callId);
|
|
2697
|
+
if (!file)
|
|
2698
|
+
return null;
|
|
2699
|
+
try {
|
|
2700
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
2701
|
+
}
|
|
2702
|
+
catch {
|
|
2703
|
+
return null;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2588
2706
|
export function runFilePath(tenant, workspace, runId) {
|
|
2589
2707
|
assertSafeName(runId, "run id");
|
|
2590
2708
|
return path.join(workspaceDir(tenant, workspace), "runs", `${runId}.json`);
|
|
@@ -2763,6 +2881,7 @@ export function summarizeRun(run) {
|
|
|
2763
2881
|
...(run.tags?.length ? { tags: run.tags } : {}),
|
|
2764
2882
|
...(run.startedBy ? { startedBy: run.startedBy } : {}),
|
|
2765
2883
|
...(run.parkedAt ? { parkedAt: run.parkedAt } : {}),
|
|
2884
|
+
...(run.revision ? { revision: run.revision } : {}),
|
|
2766
2885
|
awaitingApproval: run.steps.some((s) => s.status === "awaiting-approval" && s.waitFor !== "event"),
|
|
2767
2886
|
steps: run.steps.map((s) => {
|
|
2768
2887
|
const error = s.status === "failed" ? s.events.filter((e) => e.type === "error").at(-1)?.text : undefined;
|
|
@@ -2781,10 +2900,30 @@ export function summarizeRun(run) {
|
|
|
2781
2900
|
...(s.status === "awaiting-approval" ? { instruction: s.instruction, waitingSince: s.events.at(-1)?.t ?? run.startedAt } : {}),
|
|
2782
2901
|
...(s.startedAt ? { startedAt: s.startedAt } : {}),
|
|
2783
2902
|
...(s.finishedAt ? { finishedAt: s.finishedAt } : {}),
|
|
2903
|
+
...searchFacts(s),
|
|
2784
2904
|
};
|
|
2785
2905
|
}),
|
|
2786
2906
|
};
|
|
2787
2907
|
}
|
|
2908
|
+
/** What a step's row carries for search: the tools it called (once each),
|
|
2909
|
+
* how many tool calls errored, and its model. */
|
|
2910
|
+
function searchFacts(s) {
|
|
2911
|
+
const tools = new Set();
|
|
2912
|
+
let errors = 0;
|
|
2913
|
+
for (const e of s.events) {
|
|
2914
|
+
if (e.type !== "tool")
|
|
2915
|
+
continue;
|
|
2916
|
+
if (e.ms == null)
|
|
2917
|
+
tools.add(e.text);
|
|
2918
|
+
else if (e.err)
|
|
2919
|
+
errors++;
|
|
2920
|
+
}
|
|
2921
|
+
return {
|
|
2922
|
+
...(tools.size ? { tools: [...tools].slice(0, 40) } : {}),
|
|
2923
|
+
...(errors ? { toolErrors: errors } : {}),
|
|
2924
|
+
...(s.model ? { model: s.model } : {}),
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2788
2927
|
/** A RunRecord with the shape a list reads and nothing else — see listAllRuns. */
|
|
2789
2928
|
export function runFromSummary(s) {
|
|
2790
2929
|
const { awaitingApproval: _awaiting, ...run } = s;
|
package/dist/src/tokens.d.ts
CHANGED
|
@@ -36,3 +36,41 @@ export declare function runTokens(run: {
|
|
|
36
36
|
tokens?: TokenUsage | null;
|
|
37
37
|
}[];
|
|
38
38
|
}): TokenUsage | null;
|
|
39
|
+
/**
|
|
40
|
+
* One layer of a step's system prompt as the record keeps it — no text,
|
|
41
|
+
* only what tells two steps' prompts apart. The layers are assembled in
|
|
42
|
+
* prompt-layers.ts; this is the shape the run page and the CLI read.
|
|
43
|
+
*/
|
|
44
|
+
export interface PromptLayerRecord {
|
|
45
|
+
name: string;
|
|
46
|
+
chars: number;
|
|
47
|
+
/** First 12 hex characters of the layer's SHA-256. */
|
|
48
|
+
hash: string;
|
|
49
|
+
/** False for a layer that changes between runs (memory, state, clock),
|
|
50
|
+
* placed after the cache boundary. Omitted when true. */
|
|
51
|
+
stable?: false;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Which layers differ between two steps' prompts, by name: changed, added
|
|
55
|
+
* or removed. Empty when the prompts are the same, or either step has no
|
|
56
|
+
* layers on record.
|
|
57
|
+
*/
|
|
58
|
+
export declare function promptLayersChanged(prev: readonly PromptLayerRecord[] | null | undefined, next: readonly PromptLayerRecord[] | null | undefined): string[];
|
|
59
|
+
/** A step that wrote this share of its input to the cache, or more, gets a
|
|
60
|
+
* note saying why. Below it the cache did its job: a step's turns each
|
|
61
|
+
* write their new tail, which is a few percent of a long step's input. */
|
|
62
|
+
export declare const CACHE_WRITE_NOTE_SHARE = 0.2;
|
|
63
|
+
/**
|
|
64
|
+
* Why a step's prompt cache missed, in one line, or null when it did not:
|
|
65
|
+
* the share of input written to the cache, and what differed from the same
|
|
66
|
+
* agent's previous step in the run — "memory, clock changed since step 2".
|
|
67
|
+
* The agent's first step in a run writes its prefix whatever happens, and
|
|
68
|
+
* says so; the same prompt written again means the entry had expired
|
|
69
|
+
* (five minutes on an API key) or siblings started before it was readable.
|
|
70
|
+
* Steps without layers on record (before 20 Oct 2026) get no note.
|
|
71
|
+
*/
|
|
72
|
+
export declare function promptCacheNote(steps: readonly {
|
|
73
|
+
agent: string;
|
|
74
|
+
tokens?: TokenUsage | null;
|
|
75
|
+
promptLayers?: readonly PromptLayerRecord[] | null;
|
|
76
|
+
}[], index: number): string | null;
|
package/dist/src/tokens.js
CHANGED
|
@@ -56,3 +56,60 @@ export function runTokens(run) {
|
|
|
56
56
|
const toks = run.steps.map((s) => s.tokens).filter((t) => !!t);
|
|
57
57
|
return toks.length ? sumTokens(toks) : null;
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Which layers differ between two steps' prompts, by name: changed, added
|
|
61
|
+
* or removed. Empty when the prompts are the same, or either step has no
|
|
62
|
+
* layers on record.
|
|
63
|
+
*/
|
|
64
|
+
export function promptLayersChanged(prev, next) {
|
|
65
|
+
if (!prev || !next)
|
|
66
|
+
return [];
|
|
67
|
+
const before = new Map(prev.map((l) => [l.name, l.hash]));
|
|
68
|
+
const after = new Map(next.map((l) => [l.name, l.hash]));
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const [name, hash] of after)
|
|
71
|
+
if (before.get(name) !== hash)
|
|
72
|
+
out.push(name);
|
|
73
|
+
for (const name of before.keys())
|
|
74
|
+
if (!after.has(name))
|
|
75
|
+
out.push(name);
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** A step that wrote this share of its input to the cache, or more, gets a
|
|
79
|
+
* note saying why. Below it the cache did its job: a step's turns each
|
|
80
|
+
* write their new tail, which is a few percent of a long step's input. */
|
|
81
|
+
export const CACHE_WRITE_NOTE_SHARE = 0.2;
|
|
82
|
+
/**
|
|
83
|
+
* Why a step's prompt cache missed, in one line, or null when it did not:
|
|
84
|
+
* the share of input written to the cache, and what differed from the same
|
|
85
|
+
* agent's previous step in the run — "memory, clock changed since step 2".
|
|
86
|
+
* The agent's first step in a run writes its prefix whatever happens, and
|
|
87
|
+
* says so; the same prompt written again means the entry had expired
|
|
88
|
+
* (five minutes on an API key) or siblings started before it was readable.
|
|
89
|
+
* Steps without layers on record (before 20 Oct 2026) get no note.
|
|
90
|
+
*/
|
|
91
|
+
export function promptCacheNote(steps, index) {
|
|
92
|
+
const step = steps[index];
|
|
93
|
+
if (!step?.promptLayers)
|
|
94
|
+
return null;
|
|
95
|
+
const t = step.tokens;
|
|
96
|
+
if (!t || t.cacheWrite === undefined || !(t.input > 0))
|
|
97
|
+
return null;
|
|
98
|
+
const share = t.cacheWrite / t.input;
|
|
99
|
+
if (share < CACHE_WRITE_NOTE_SHARE)
|
|
100
|
+
return null;
|
|
101
|
+
const head = `${Math.round(share * 100)}% of input written to the cache`;
|
|
102
|
+
let prev = -1;
|
|
103
|
+
for (let j = index - 1; j >= 0; j--) {
|
|
104
|
+
if (steps[j].agent === step.agent && steps[j].promptLayers) {
|
|
105
|
+
prev = j;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (prev < 0)
|
|
110
|
+
return `${head} — the agent's first step in this run`;
|
|
111
|
+
const changed = promptLayersChanged(steps[prev].promptLayers, step.promptLayers);
|
|
112
|
+
if (!changed.length)
|
|
113
|
+
return `${head} — the same prompt as step ${prev + 1}; the entry had expired or was still being written`;
|
|
114
|
+
return `${head} — ${changed.join(", ")} changed since step ${prev + 1}`;
|
|
115
|
+
}
|
package/dist/src/tool-names.js
CHANGED
|
@@ -47,7 +47,7 @@ export const BUILTIN_TOOLS = new Set([
|
|
|
47
47
|
/** The platform's own groups, served in-process rather than by the SDK.
|
|
48
48
|
* `install` is granted to every agent anyway (install-tool.ts); naming it is
|
|
49
49
|
* accepted so a file that says so is not flagged. */
|
|
50
|
-
export const PLATFORM_GROUPS = new Set(["search", "history", "desks", "ask", "install"]);
|
|
50
|
+
export const PLATFORM_GROUPS = new Set(["search", "history", "desks", "ask", "peers", "install"]);
|
|
51
51
|
/** Would this name resolve to something the runtime provides? Built-ins win a
|
|
52
52
|
* clash, so a tool file of the same name is shadowed rather than granted. */
|
|
53
53
|
export function isRuntimeTool(name) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import fs from "node:fs";
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import { builtinModules } from "node:module";
|
|
18
|
-
import matter from "
|
|
18
|
+
import matter from "./matter.js";
|
|
19
19
|
import { parseRuntime } from "./runtime.js";
|
|
20
20
|
import { workspaceDir, workspaceTools } from "./store.js";
|
|
21
21
|
import { libraryDir, libraryTools } from "./library.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { RunRecord } from "./store.ts";
|
|
2
|
+
export type SpanKind = "run" | "step" | "model" | "tool" | "error";
|
|
3
|
+
export type SpanStatus = "ok" | "error" | "running" | "waiting" | "skipped" | "pending";
|
|
4
|
+
export interface TraceSpan {
|
|
5
|
+
/** Stable within the run: "run", "s2", "s2/m3", "s2/t:<call>", "s2/e5". */
|
|
6
|
+
id: string;
|
|
7
|
+
kind: SpanKind;
|
|
8
|
+
/** What the line says: the flow, the agent, the model, the tool. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Epoch ms; null when the record does not say. */
|
|
11
|
+
start: number | null;
|
|
12
|
+
end: number | null;
|
|
13
|
+
status: SpanStatus;
|
|
14
|
+
/** Model and step spans: tokens (input is all input, as on the record). */
|
|
15
|
+
tokens?: {
|
|
16
|
+
input: number;
|
|
17
|
+
output: number;
|
|
18
|
+
cacheRead: number;
|
|
19
|
+
cacheWrite: number;
|
|
20
|
+
};
|
|
21
|
+
/** Step spans: the provider's token cost, USD. */
|
|
22
|
+
costUsd?: number | null;
|
|
23
|
+
model?: string;
|
|
24
|
+
/** Model spans: why it stopped. */
|
|
25
|
+
stop?: string;
|
|
26
|
+
/** Tool spans: the call id, and whether its input and output were recorded. */
|
|
27
|
+
call?: string;
|
|
28
|
+
captured?: boolean;
|
|
29
|
+
/** Error spans and failed tools: the words. */
|
|
30
|
+
text?: string;
|
|
31
|
+
subagent?: string;
|
|
32
|
+
/** Index into run.steps, on a step and everything under it. */
|
|
33
|
+
step?: number;
|
|
34
|
+
children: TraceSpan[];
|
|
35
|
+
}
|
|
36
|
+
export declare function buildTraceTree(run: RunRecord, now?: number): TraceSpan;
|
|
37
|
+
/** The ids from the root to the first thing that went wrong — what the
|
|
38
|
+
* view opens on; [] when nothing did. A failed tool beats the step's error
|
|
39
|
+
* line, which beats the failed step itself. */
|
|
40
|
+
export declare function failurePath(root: TraceSpan): string[];
|