@tangle-network/agent-runtime 2.3.11 → 3.0.1
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/{activation-BzNeFJDB.js → activation-BE0duxMf.js} +4 -4
- package/dist/{activation-BzNeFJDB.js.map → activation-BE0duxMf.js.map} +1 -1
- package/dist/{activation-B8dLwJj6.d.ts → activation-kREPGozZ.d.ts} +2 -2
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +3 -3
- package/dist/{bundle-DhV73H6U.js → bundle-ZHO7QFZw.js} +2 -2
- package/dist/{bundle-DhV73H6U.js.map → bundle-ZHO7QFZw.js.map} +1 -1
- package/dist/candidate-execution/index.d.ts +3 -3
- package/dist/candidate-execution/index.js +5 -5
- package/dist/{coordination-manager-BluavEgY.js → coordination-manager-B9goecNC.js} +4 -4
- package/dist/{coordination-manager-BluavEgY.js.map → coordination-manager-B9goecNC.js.map} +1 -1
- package/dist/{delegate-CYWL0MrO.js → delegate-C-wmgWCF.js} +2 -2
- package/dist/{delegate-CYWL0MrO.js.map → delegate-C-wmgWCF.js.map} +1 -1
- package/dist/durable.d.ts +1 -1
- package/dist/durable.js +9 -9
- package/dist/{graph-ByNMeS0j.js → graph-C_564r0C.js} +7 -7
- package/dist/graph-C_564r0C.js.map +1 -0
- package/dist/{improve-D9A79N5a.js → improve-BZdFdcUC.js} +7 -7
- package/dist/{improve-D9A79N5a.js.map → improve-BZdFdcUC.js.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -10
- package/dist/intelligence.d.ts +4 -4
- package/dist/intelligence.js +7 -7
- package/dist/kernel.d.ts +3 -3
- package/dist/kernel.js +12 -12
- package/dist/{loop-runner-D9fuOb69.d.ts → loop-runner-D8NZLPE5.d.ts} +2 -2
- package/dist/{loop-runner-DiMfhI_z.js → loop-runner-_9_GtzO5.js} +2 -2
- package/dist/{loop-runner-DiMfhI_z.js.map → loop-runner-_9_GtzO5.js.map} +1 -1
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +6 -6
- package/dist/{prepare-DrHOZFJy.js → prepare-CU0LM1_X.js} +3 -3
- package/dist/{prepare-DrHOZFJy.js.map → prepare-CU0LM1_X.js.map} +1 -1
- package/dist/{profile-BzzmKuIX.js → profile-By6JkIQi.js} +2 -2
- package/dist/{profile-BzzmKuIX.js.map → profile-By6JkIQi.js.map} +1 -1
- package/dist/profiles.d.ts +1 -1
- package/dist/{protected-model-port-IKqhTypD.d.ts → protected-model-port-BBsYuEY0.d.ts} +2 -2
- package/dist/{provision-supervisor-DP5U58V4.js → provision-supervisor-DX4fd6Ib.js} +5 -5
- package/dist/{provision-supervisor-DP5U58V4.js.map → provision-supervisor-DX4fd6Ib.js.map} +1 -1
- package/dist/{run-cancellation-BxoFCpJ_.js → run-cancellation-BTjyVpVW.js} +2 -2
- package/dist/{run-cancellation-BxoFCpJ_.js.map → run-cancellation-BTjyVpVW.js.map} +1 -1
- package/dist/{runtime-DS5AgACg.js → runtime-DsH0QnW0.js} +54 -30
- package/dist/runtime-DsH0QnW0.js.map +1 -0
- package/dist/{scope-DRaBilEX.js → scope-BeI3tmVX.js} +2550 -2487
- package/dist/scope-BeI3tmVX.js.map +1 -0
- package/dist/{search-executor-DLo_9jUv.js → search-executor-C-E-umcI.js} +4 -4
- package/dist/{search-executor-DLo_9jUv.js.map → search-executor-C-E-umcI.js.map} +1 -1
- package/dist/{server-DfHV4ajw.js → server-DAYqKU6o.js} +4 -4
- package/dist/{server-DfHV4ajw.js.map → server-DAYqKU6o.js.map} +1 -1
- package/dist/{structural-rollout-BIjjZ_XX.js → structural-rollout-DHdmB9MR.js} +4 -4
- package/dist/{structural-rollout-BIjjZ_XX.js.map → structural-rollout-DHdmB9MR.js.map} +1 -1
- package/dist/{supervise-6BCQ73Xa.js → supervise-QzwZc6K2.js} +7 -7
- package/dist/{supervise-6BCQ73Xa.js.map → supervise-QzwZc6K2.js.map} +1 -1
- package/dist/{supervision-discovery-Da5zebY2.js → supervision-discovery-CyvJVbTM.js} +3 -3
- package/dist/{supervision-discovery-Da5zebY2.js.map → supervision-discovery-CyvJVbTM.js.map} +1 -1
- package/dist/{supervisor-3JHXy3tz.js → supervisor-REUnT7iX.js} +222 -147
- package/dist/supervisor-REUnT7iX.js.map +1 -0
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +13 -13
- package/dist/{top-web-DK6lTMHG.js → top-web-DRfER2jW.js} +551 -149
- package/dist/top-web-DRfER2jW.js.map +1 -0
- package/dist/tui/bin.js +4 -3
- package/dist/tui/bin.js.map +1 -1
- package/dist/tui/index.d.ts +203 -3
- package/dist/tui/index.js +3 -3
- package/dist/{types-S3RbyJ4x.d.ts → types-Bm4xC_e-.d.ts} +151 -21
- package/dist/{workspace-streams-Dep2L6XD.js → workspace-streams-BSEkDu3H.js} +69 -69
- package/dist/{workspace-streams-Dep2L6XD.js.map → workspace-streams-BSEkDu3H.js.map} +1 -1
- package/dist/{workspace-streams-Dq2Fzk3c.d.ts → workspace-streams-BdZsd12p.d.ts} +7 -2
- package/package.json +2 -1
- package/dist/graph-ByNMeS0j.js.map +0 -1
- package/dist/runtime-DS5AgACg.js.map +0 -1
- package/dist/scope-DRaBilEX.js.map +0 -1
- package/dist/supervisor-3JHXy3tz.js.map +0 -1
- package/dist/top-web-DK6lTMHG.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Jn as harnessTranscriptArtifact, L as FileSpawnJournal, Xi as recordValue, _r as runAbortable, d as legacySupervisorRunsRoot, dn as FileObserverJournal, g as safeWorkerFile, gr as linkAbort, i as nodeStreamFile, n as ROOT_STREAM_FILE, qt as FileResultBlobStore, v as supervisorRunsRoot, z as spawnJournalRecordEvents } from "./scope-BeI3tmVX.js";
|
|
2
2
|
import { r as parseCommittedJsonLines } from "./jsonl-file-BBP9Dz9w.js";
|
|
3
|
-
import { h as rootCoordinationOwner, v as FileCoordinationLog } from "./run-cancellation-
|
|
4
|
-
import { d as projectPursuit, o as readFailureRecord, s as readSettleRecord, t as discoverDurableSupervisionRun } from "./supervision-discovery-
|
|
3
|
+
import { h as rootCoordinationOwner, v as FileCoordinationLog } from "./run-cancellation-BTjyVpVW.js";
|
|
4
|
+
import { d as projectPursuit, o as readFailureRecord, s as readSettleRecord, t as discoverDurableSupervisionRun } from "./supervision-discovery-CyvJVbTM.js";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { isRecord } from "@tangle-network/agent-core";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
@@ -10,71 +10,296 @@ import { basename, dirname, extname, join, resolve, sep } from "node:path";
|
|
|
10
10
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
11
11
|
import { createServer } from "node:http";
|
|
12
12
|
import { StringDecoder } from "node:string_decoder";
|
|
13
|
+
import { once } from "node:events";
|
|
13
14
|
// src/tui/top-transcript.ts
|
|
14
15
|
/** Read selected history on demand, leaving the live topology snapshot bounded and fast. */
|
|
15
16
|
async function transcriptHistory(worker, runDir, node) {
|
|
17
|
+
return readTopAgentHistory(worker, runDir ? new FileResultBlobStore(join(runDir, "blobs")) : void 0, node);
|
|
18
|
+
}
|
|
19
|
+
/** Task, result and progress use the same projection for file-backed and SQL-backed runs. */
|
|
20
|
+
async function readTopAgentHistory(worker, blobs, node) {
|
|
16
21
|
const rows = [];
|
|
17
|
-
const identity = node?.identity;
|
|
18
|
-
if (
|
|
19
|
-
const task = await
|
|
20
|
-
if (task !== void 0) rows.
|
|
22
|
+
const identity = recordValue(node?.identity ?? worker.identity);
|
|
23
|
+
if (blobs && typeof identity?.taskDigest === "string" && worker.startedAt) {
|
|
24
|
+
const task = await blobs.get(identity.taskDigest);
|
|
25
|
+
if (task !== void 0) rows.push({
|
|
21
26
|
at: worker.startedAt,
|
|
22
27
|
event: {
|
|
23
28
|
kind: "notice",
|
|
24
29
|
provenance: "delegated-input",
|
|
25
|
-
source: `parent ${node?.parentId ?? "run caller"}; task ${identity.taskDigest}
|
|
26
|
-
text: typeof task === "string" ? task : JSON.stringify(task, null, 2)
|
|
30
|
+
source: `parent ${worker.parent ?? node?.parentId ?? "run caller"}; task ${identity.taskDigest}; profile ${identity.profileDigest}`,
|
|
31
|
+
text: `Request from ${worker.parent ?? node?.parentId ?? "run caller"}\n${typeof task === "string" ? task : JSON.stringify(task, null, 2)}`
|
|
27
32
|
}
|
|
28
33
|
});
|
|
29
34
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
const progress = transcriptTail(worker);
|
|
36
|
+
if (worker.startedAt) rows.push({
|
|
37
|
+
at: worker.startedAt,
|
|
38
|
+
event: {
|
|
39
|
+
kind: "notice",
|
|
40
|
+
provenance: "capture-unavailable",
|
|
41
|
+
text: "Full conversation capture has not been verified in this progress tail.",
|
|
42
|
+
source: "Runtime progress evidence"
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const outRef = node?.outRef ?? worker.outRef;
|
|
46
|
+
const result = blobs && outRef && worker.endedAt ? await blobs.get(outRef) : void 0;
|
|
47
|
+
if (result !== void 0) progress.push({
|
|
48
|
+
at: worker.endedAt,
|
|
49
|
+
event: {
|
|
50
|
+
kind: "notice",
|
|
51
|
+
provenance: "recorded-result",
|
|
52
|
+
source: `result ${outRef}; Runtime ResultBlobStore`,
|
|
53
|
+
text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return [...rows, ...progress];
|
|
57
|
+
}
|
|
58
|
+
/** Resolve ordinary Runtime transcript evidence, never a product-specific capture registry. */
|
|
59
|
+
async function readTopAgentInspection({ worker, runId, blobs, readSessions, artifacts, signal }) {
|
|
60
|
+
const evidence = worker.harnessTranscript;
|
|
61
|
+
const files = {
|
|
62
|
+
scope: "none",
|
|
63
|
+
complete: true,
|
|
64
|
+
reason: "Transcript evidence does not include a workspace inventory",
|
|
65
|
+
entries: []
|
|
66
|
+
};
|
|
67
|
+
if (!evidence || evidence.status !== "available") return {
|
|
68
|
+
coverage: {
|
|
69
|
+
status: "missing",
|
|
70
|
+
source: "Runtime transcript receipt",
|
|
71
|
+
reason: evidence?.reason ?? "No transcript receipt was recorded"
|
|
72
|
+
},
|
|
73
|
+
files
|
|
74
|
+
};
|
|
75
|
+
const metadata = await harnessTranscriptArtifact(evidence, blobs, {
|
|
76
|
+
content: false,
|
|
77
|
+
signal
|
|
78
|
+
});
|
|
79
|
+
if (!metadata) throw new Error("Available transcript receipt has no metadata");
|
|
80
|
+
const binding = inspectionBinding(metadata, worker, runId);
|
|
81
|
+
if (binding.mismatch.length) return {
|
|
82
|
+
coverage: {
|
|
83
|
+
status: "missing",
|
|
84
|
+
source: "Runtime transcript receipt",
|
|
85
|
+
code: "source-mismatch",
|
|
86
|
+
reason: binding.mismatch.join("; ")
|
|
87
|
+
},
|
|
88
|
+
files
|
|
89
|
+
};
|
|
90
|
+
const artifact = await harnessTranscriptArtifact(evidence, blobs, {
|
|
91
|
+
artifacts,
|
|
92
|
+
signal
|
|
93
|
+
});
|
|
94
|
+
if (!artifact) throw new Error("Available transcript receipt has no artifact");
|
|
95
|
+
const projection = harnessSessionRows(await readSessions(artifact, signal));
|
|
96
|
+
const reasons = [
|
|
97
|
+
...binding.missing,
|
|
98
|
+
...evidence.missing ?? [],
|
|
99
|
+
...artifact.skipped.map((file) => `${file.path}: ${file.reason}`),
|
|
100
|
+
...projection.gaps.map((gap) => `${gap.sessionId}/${gap.recordId}: ${gap.reason}`)
|
|
101
|
+
];
|
|
102
|
+
const complete = evidence.coverageComplete === true && reasons.length === 0 && projection.rows.length > 0;
|
|
103
|
+
return {
|
|
104
|
+
coverage: {
|
|
105
|
+
status: complete ? "complete" : "partial",
|
|
106
|
+
source: `${artifact.harness} Runtime transcript`,
|
|
107
|
+
ref: `urn:${evidence.transcriptRef}`,
|
|
108
|
+
...binding.missing.length ? { code: "source-unbound" } : {},
|
|
109
|
+
...!complete ? { reason: reasons.join("; ") || "Source completeness was not attested" } : {}
|
|
110
|
+
},
|
|
111
|
+
rows: projection.rows,
|
|
112
|
+
files
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** Verify the receipt belongs to the selected node before resolving any native/archive bytes. */
|
|
116
|
+
function inspectionBinding(metadata, worker, runId) {
|
|
117
|
+
const mismatch = [], missing = [];
|
|
118
|
+
if (metadata.binding !== void 0 && !recordValue(metadata.binding)) mismatch.push("Capture Bridge binding is malformed");
|
|
119
|
+
if (metadata.source && metadata.binding) mismatch.push("Capture has conflicting source bindings");
|
|
120
|
+
const source = metadata.source ?? metadata.binding;
|
|
121
|
+
if (!source) return {
|
|
122
|
+
mismatch,
|
|
123
|
+
missing: ["Capture has no recorded node binding"]
|
|
124
|
+
};
|
|
125
|
+
const node = recordValue(source.node);
|
|
126
|
+
if (!node) {
|
|
127
|
+
if (source.node !== void 0) mismatch.push("Capture node binding is malformed");
|
|
128
|
+
else missing.push("Capture has no recorded node identity");
|
|
129
|
+
} else {
|
|
130
|
+
for (const [key, expected] of [
|
|
131
|
+
["rootId", runId],
|
|
132
|
+
["nodeId", worker.id],
|
|
133
|
+
["parentId", worker.parent]
|
|
134
|
+
]) if (expected !== void 0 && node[key] !== expected) mismatch.push(`Capture ${key} does not match selected node`);
|
|
135
|
+
const attempt = worker.executionBindings?.at(-1)?.attemptId;
|
|
136
|
+
if (typeof node.attemptId !== "string" || !node.attemptId) mismatch.push("Capture attempt identity is malformed");
|
|
137
|
+
else if (attempt && node.attemptId !== attempt) mismatch.push("Capture attempt does not match selected node");
|
|
138
|
+
else if (!attempt) missing.push("Selected node has no recorded execution attempt");
|
|
139
|
+
const identity = recordValue(node.identity);
|
|
140
|
+
for (const key of ["profileDigest", "taskDigest"]) {
|
|
141
|
+
const expected = worker.identity?.[key];
|
|
142
|
+
if (expected && identity?.[key] === void 0) missing.push(`Capture has no recorded ${key}`);
|
|
143
|
+
else if (expected && identity?.[key] !== expected) mismatch.push(`Capture ${key} does not match selected node`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const materialization = worker.materialization;
|
|
147
|
+
if (materialization?.status !== "known") missing.push("Selected node has no known materialization");
|
|
148
|
+
else {
|
|
149
|
+
if (source.profileDigest !== materialization.effectiveProfileDigest) mismatch.push("Capture profile does not match selected materialization");
|
|
150
|
+
const execution = materialization.execution;
|
|
151
|
+
const kind = metadata.source ? "environment" : "session";
|
|
152
|
+
const id = metadata.source?.environmentId ?? metadata.binding?.sessionId;
|
|
153
|
+
if (execution.kind !== kind || execution.id !== id) mismatch.push("Capture execution does not match selected materialization");
|
|
154
|
+
}
|
|
155
|
+
if (metadata.binding && (metadata.binding.kind !== "bridge" || !metadata.binding.nativeSessionId || !metadata.binding.sessionId)) mismatch.push("Capture Bridge binding is malformed");
|
|
156
|
+
return {
|
|
157
|
+
mismatch,
|
|
158
|
+
missing
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/** One selected-agent mapping for filesystem and API transports; no execution or I/O. */
|
|
162
|
+
function topAgentWorker(run, workerId) {
|
|
163
|
+
const child = run.workers.find((worker) => worker.id === workerId);
|
|
164
|
+
if (child || run.id !== workerId) return child;
|
|
165
|
+
return {
|
|
166
|
+
id: run.id,
|
|
167
|
+
label: "root",
|
|
168
|
+
status: run.status === "cancelled" ? "cancelled" : ["done", "succeeded"].includes(run.status) ? "done" : ["down", "failed"].includes(run.status) ? "down" : "running",
|
|
169
|
+
startedAt: run.startedAt,
|
|
170
|
+
endedAt: run.completedAt,
|
|
171
|
+
latencyMs: run.totals.latencyMs,
|
|
172
|
+
spend: run.driverSpend,
|
|
173
|
+
metered: {
|
|
174
|
+
iterations: 0,
|
|
175
|
+
tokensInput: 0,
|
|
176
|
+
tokensOutput: 0,
|
|
177
|
+
usd: 0,
|
|
178
|
+
ms: 0
|
|
179
|
+
},
|
|
180
|
+
liveTail: run.progressTail
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** Metadata keeps full recorded context while the conversation can show compact attribution. */
|
|
184
|
+
function topAgentMetadata(inspection, recorded) {
|
|
185
|
+
const { rows, ...metadata } = inspection ?? { coverage: {
|
|
186
|
+
status: "missing",
|
|
187
|
+
source: "Runtime",
|
|
188
|
+
reason: "Verified conversation inspection was not provided"
|
|
189
|
+
} };
|
|
190
|
+
return {
|
|
191
|
+
...metadata,
|
|
192
|
+
context: [
|
|
193
|
+
...metadata.context ?? [],
|
|
194
|
+
...recorded.filter((row) => row.event.provenance === "delegated-input"),
|
|
195
|
+
...(rows ?? []).filter((row) => row.event.provenance === "system-instructions")
|
|
196
|
+
]
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/** Project selected conversation rows into ordered reset and row frames. */
|
|
200
|
+
function createTopAgentStream(generation) {
|
|
201
|
+
let representation;
|
|
202
|
+
let initiatingInput;
|
|
203
|
+
const seen = /* @__PURE__ */ new Set();
|
|
204
|
+
let lastSeq = 0;
|
|
205
|
+
return {
|
|
206
|
+
reset: { generation },
|
|
207
|
+
frame({ recorded, inspection, alive, includeContext }) {
|
|
208
|
+
initiatingInput = recorded.find((row) => row.event.provenance === "delegated-input") ?? initiatingInput;
|
|
209
|
+
const input = initiatingInput?.event.text;
|
|
210
|
+
const task = input?.includes("\n") ? input.slice(input.indexOf("\n") + 1) : void 0;
|
|
211
|
+
const hasNativePrompt = !!task && !!inspection?.rows?.some((row) => row.event.kind === "user" && row.event.actor !== "injected" && row.event.text === task);
|
|
212
|
+
const shownInput = initiatingInput && hasNativePrompt && !includeContext ? {
|
|
213
|
+
...initiatingInput,
|
|
214
|
+
event: {
|
|
215
|
+
...initiatingInput.event,
|
|
216
|
+
text: input.slice(0, input.indexOf("\n")),
|
|
217
|
+
provenance: "recorded-sender"
|
|
218
|
+
}
|
|
219
|
+
} : initiatingInput;
|
|
220
|
+
const next = inspection?.rows?.length ? `capture:${inspection.coverage.ref ?? inspection.coverage.source}:${hasNativePrompt}` : "progress";
|
|
221
|
+
const frames = [];
|
|
222
|
+
if (representation !== void 0 && representation !== next) {
|
|
223
|
+
seen.clear();
|
|
224
|
+
lastSeq = 0;
|
|
225
|
+
generation += 1;
|
|
226
|
+
frames.push({
|
|
227
|
+
event: "reset",
|
|
228
|
+
data: { generation }
|
|
229
|
+
});
|
|
43
230
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
231
|
+
representation = next;
|
|
232
|
+
const events = (inspection?.rows?.length ? [
|
|
233
|
+
...shownInput ? [shownInput] : [],
|
|
234
|
+
...recorded.filter((row) => inspection.coverage.status !== "complete" && row.event.provenance === "recorded-result"),
|
|
235
|
+
...inspection.rows.filter((row) => includeContext || row.event.provenance !== "system-instructions")
|
|
236
|
+
] : recorded).flatMap((row) => {
|
|
237
|
+
const key = "id" in row && typeof row.id === "string" ? row.id : createHash("sha256").update(JSON.stringify(row)).digest("hex");
|
|
238
|
+
if (seen.has(key)) return [];
|
|
239
|
+
seen.add(key);
|
|
240
|
+
if (next === "progress" && seen.size > 2e3) seen.delete(seen.values().next().value);
|
|
241
|
+
return [{
|
|
242
|
+
...row,
|
|
243
|
+
seq: ++lastSeq
|
|
244
|
+
}];
|
|
245
|
+
});
|
|
246
|
+
for (let offset = 0; offset < Math.max(events.length, 1); offset += 100) {
|
|
247
|
+
const batch = events.slice(offset, offset + 100);
|
|
248
|
+
frames.push({
|
|
249
|
+
event: "rows",
|
|
250
|
+
data: {
|
|
251
|
+
generation,
|
|
252
|
+
events: batch,
|
|
253
|
+
lastSeq: batch.at(-1)?.seq ?? lastSeq,
|
|
254
|
+
alive,
|
|
255
|
+
window: inspection?.coverage.status === "complete" ? "verified-native" : "recorded-tail",
|
|
256
|
+
...inspection ? { coverage: inspection.coverage } : {}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
53
259
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
return [...rows, ...transcriptRows({ liveTail: readTranscriptTail(worker.eventFile) })];
|
|
260
|
+
return frames;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
58
263
|
}
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
264
|
+
/** Router records are Runtime's canonical ToolLoopMessageRecord, not a native file heuristic. */
|
|
265
|
+
async function retainedRouterInspection(worker, runDir) {
|
|
266
|
+
const evidence = worker.harnessTranscript;
|
|
267
|
+
if (!runDir || evidence?.status !== "available" || evidence.harness !== "router" || !worker.endedAt) return void 0;
|
|
268
|
+
const artifact = await harnessTranscriptArtifact(evidence, new FileResultBlobStore(join(runDir, "blobs")));
|
|
269
|
+
if (!artifact || !("files" in artifact)) return void 0;
|
|
270
|
+
const rows = artifact.files.flatMap((file) => "content" in file ? file.content.split("\n").filter(Boolean).map((line) => recordValue(JSON.parse(line))).filter((row) => row && typeof row.role === "string") : []).flatMap((message, index) => transcriptRows({ liveTail: [JSON.stringify({
|
|
271
|
+
at: worker.endedAt,
|
|
272
|
+
message
|
|
273
|
+
})] }).map((row, part) => ({
|
|
274
|
+
...row,
|
|
275
|
+
id: JSON.stringify([
|
|
276
|
+
evidence.transcriptRef,
|
|
277
|
+
index,
|
|
278
|
+
part
|
|
279
|
+
]),
|
|
280
|
+
sessionId: `router:${worker.id}`,
|
|
281
|
+
part,
|
|
282
|
+
event: {
|
|
283
|
+
...row.event,
|
|
284
|
+
provenance: "router-transcript",
|
|
285
|
+
source: "Message time unknown; shown at captured settlement time"
|
|
286
|
+
}
|
|
287
|
+
})));
|
|
288
|
+
return {
|
|
289
|
+
coverage: {
|
|
290
|
+
status: "partial",
|
|
291
|
+
source: "Runtime Router conversation, displayed at recorded settlement time",
|
|
292
|
+
reason: "Per-message times were not captured",
|
|
293
|
+
ref: `urn:${evidence.transcriptRef}`
|
|
294
|
+
},
|
|
295
|
+
rows,
|
|
296
|
+
files: {
|
|
297
|
+
scope: "none",
|
|
298
|
+
complete: true,
|
|
299
|
+
reason: "This in-process conversation has no sandbox workspace inventory",
|
|
300
|
+
entries: []
|
|
76
301
|
}
|
|
77
|
-
}
|
|
302
|
+
};
|
|
78
303
|
}
|
|
79
304
|
/** Present the recorded tail through sandbox-ui's existing agent-stream contract. */
|
|
80
305
|
function transcriptRows(worker) {
|
|
@@ -106,12 +331,17 @@ function transcriptRows(worker) {
|
|
|
106
331
|
...event,
|
|
107
332
|
kind
|
|
108
333
|
});
|
|
109
|
-
continue;
|
|
110
334
|
}
|
|
335
|
+
if (event?.kind) continue;
|
|
336
|
+
const legacy = (event) => push({
|
|
337
|
+
...event,
|
|
338
|
+
provenance: "unverified-progress",
|
|
339
|
+
source: "Legacy progress only; full capture is unverified"
|
|
340
|
+
});
|
|
111
341
|
const message = recordValue(value?.message);
|
|
112
342
|
if (!message) continue;
|
|
113
343
|
if (message.role === "tool") {
|
|
114
|
-
|
|
344
|
+
legacy({
|
|
115
345
|
kind: "tool-result",
|
|
116
346
|
toolCallId: String(message.tool_call_id),
|
|
117
347
|
result: message.content
|
|
@@ -120,7 +350,7 @@ function transcriptRows(worker) {
|
|
|
120
350
|
}
|
|
121
351
|
for (const item of Array.isArray(message.tool_calls) ? message.tool_calls : []) {
|
|
122
352
|
const call = recordValue(item), fn = recordValue(call?.function);
|
|
123
|
-
if (call && fn)
|
|
353
|
+
if (call && fn) legacy({
|
|
124
354
|
kind: "tool-call",
|
|
125
355
|
toolCallId: String(call.id),
|
|
126
356
|
toolName: String(fn.name),
|
|
@@ -133,21 +363,21 @@ function transcriptRows(worker) {
|
|
|
133
363
|
}] : Array.isArray(message.content) ? message.content : [];
|
|
134
364
|
for (const part of parts) {
|
|
135
365
|
if (!recordValue(part)) continue;
|
|
136
|
-
if (part.type === "text" && typeof part.text === "string")
|
|
366
|
+
if (part.type === "text" && typeof part.text === "string" && part.text.length > 0) legacy({
|
|
137
367
|
kind: message.role === "user" ? "user" : "text",
|
|
138
368
|
text: part.text
|
|
139
369
|
});
|
|
140
|
-
else if (part.type === "thinking" && typeof part.thinking === "string")
|
|
370
|
+
else if (part.type === "thinking" && typeof part.thinking === "string") legacy({
|
|
141
371
|
kind: "reasoning",
|
|
142
372
|
text: part.thinking
|
|
143
373
|
});
|
|
144
|
-
else if (part.type === "tool_use")
|
|
374
|
+
else if (part.type === "tool_use") legacy({
|
|
145
375
|
kind: "tool-call",
|
|
146
376
|
toolCallId: String(part.id),
|
|
147
377
|
toolName: String(part.name),
|
|
148
378
|
args: part.input
|
|
149
379
|
});
|
|
150
|
-
else if (part.type === "tool_result")
|
|
380
|
+
else if (part.type === "tool_result") legacy({
|
|
151
381
|
kind: "tool-result",
|
|
152
382
|
toolCallId: String(part.tool_use_id),
|
|
153
383
|
result: part.content
|
|
@@ -176,6 +406,9 @@ const activityKinds = /* @__PURE__ */ new Set([
|
|
|
176
406
|
"interaction",
|
|
177
407
|
"child_task"
|
|
178
408
|
]);
|
|
409
|
+
function recordedActivity(line) {
|
|
410
|
+
return transcriptRows({ liveTail: [line] }).find((row) => activityKinds.has(row.event.kind));
|
|
411
|
+
}
|
|
179
412
|
/** Read appended UTF-8 bytes once; complete EOF JSON follows the durable reader contract. */
|
|
180
413
|
function readRecordedEvents(file, maxLines = 8) {
|
|
181
414
|
const fd = openSync(file, "r");
|
|
@@ -215,7 +448,7 @@ function readRecordedEvents(file, maxLines = 8) {
|
|
|
215
448
|
if (!line.trim()) continue;
|
|
216
449
|
lines.push(line);
|
|
217
450
|
if (lines.length > 200) lines.shift();
|
|
218
|
-
const activity =
|
|
451
|
+
const activity = recordedActivity(line);
|
|
219
452
|
if (activity) cursor.times.push(Date.parse(activity.at));
|
|
220
453
|
}
|
|
221
454
|
}
|
|
@@ -225,7 +458,7 @@ function readRecordedEvents(file, maxLines = 8) {
|
|
|
225
458
|
JSON.parse(cursor.pending);
|
|
226
459
|
pending = [cursor.pending];
|
|
227
460
|
} catch {}
|
|
228
|
-
const final =
|
|
461
|
+
const final = pending.length ? recordedActivity(pending[0]) : void 0;
|
|
229
462
|
const times = (final ? [...cursor.times, Date.parse(final.at)] : cursor.times).sort((a, b) => a - b);
|
|
230
463
|
const gaps = times.slice(1).map((at, i) => at - times[i]).sort((a, b) => a - b);
|
|
231
464
|
const mid = Math.floor(gaps.length / 2);
|
|
@@ -283,6 +516,110 @@ function readWorkerEventTails(dir) {
|
|
|
283
516
|
diagnostics
|
|
284
517
|
};
|
|
285
518
|
}
|
|
519
|
+
/** One ordered projection for every strict native reader, with explicit unrenderable evidence. */
|
|
520
|
+
function harnessSessionRows(sessions) {
|
|
521
|
+
const rows = [];
|
|
522
|
+
const gaps = [];
|
|
523
|
+
for (const session of sessions) {
|
|
524
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
525
|
+
const push = (recordId, part, value, event) => {
|
|
526
|
+
if (!value || !Number.isFinite(Date.parse(value))) {
|
|
527
|
+
gaps.push({
|
|
528
|
+
sessionId: session.nativeSessionId,
|
|
529
|
+
recordId,
|
|
530
|
+
reason: "Native record has no valid recorded time"
|
|
531
|
+
});
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
rows.push({
|
|
535
|
+
id: JSON.stringify([
|
|
536
|
+
session.nativeSessionId,
|
|
537
|
+
event.kind,
|
|
538
|
+
recordId,
|
|
539
|
+
part
|
|
540
|
+
]),
|
|
541
|
+
sessionId: session.nativeSessionId,
|
|
542
|
+
part,
|
|
543
|
+
at: new Date(value).toISOString(),
|
|
544
|
+
event: event.actor === "injected" ? {
|
|
545
|
+
...event,
|
|
546
|
+
provenance: "system-instructions"
|
|
547
|
+
} : event
|
|
548
|
+
});
|
|
549
|
+
};
|
|
550
|
+
const tool = (id, result, at, recordId = id, part = 0, actor) => {
|
|
551
|
+
const call = session.toolCalls.find((item) => item.id === id);
|
|
552
|
+
if (!call || result && !call.result) {
|
|
553
|
+
gaps.push({
|
|
554
|
+
sessionId: session.nativeSessionId,
|
|
555
|
+
recordId: id,
|
|
556
|
+
reason: "Referenced tool evidence is missing"
|
|
557
|
+
});
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const key = `${result ? "result" : "call"}:${id}`;
|
|
561
|
+
if (emitted.has(key)) return;
|
|
562
|
+
emitted.add(key);
|
|
563
|
+
const toolCallId = `${session.nativeSessionId}:${id}`;
|
|
564
|
+
push(recordId, part, at ?? (result ? call.endedAt : call.startedAt), result ? {
|
|
565
|
+
kind: "tool-result",
|
|
566
|
+
toolCallId,
|
|
567
|
+
result: call.result?.output ?? call.result?.text,
|
|
568
|
+
actor,
|
|
569
|
+
...actor ? { source: `Native actor ${actor}` } : {}
|
|
570
|
+
} : {
|
|
571
|
+
kind: "tool-call",
|
|
572
|
+
toolCallId,
|
|
573
|
+
toolName: call.name,
|
|
574
|
+
args: call.input ?? call.inputText,
|
|
575
|
+
actor,
|
|
576
|
+
...actor ? { source: `Native actor ${actor}` } : {}
|
|
577
|
+
});
|
|
578
|
+
};
|
|
579
|
+
for (const message of session.messages) for (const [part, value] of message.parts.entries()) if (value.type === "tool-call" || value.type === "tool-result") tool(value.toolCallId ?? "", value.type === "tool-result", message.at, message.id, part, message.actor);
|
|
580
|
+
else if ((value.type === "text" || value.type === "reasoning") && value.text) push(message.id, part, message.at, {
|
|
581
|
+
kind: value.type === "reasoning" ? "reasoning" : message.role === "user" ? "user" : message.role === "assistant" ? "text" : "notice",
|
|
582
|
+
text: value.text,
|
|
583
|
+
actor: message.actor,
|
|
584
|
+
...message.actor ? { source: `Native actor ${message.actor}` } : {},
|
|
585
|
+
...![
|
|
586
|
+
"user",
|
|
587
|
+
"assistant",
|
|
588
|
+
"tool"
|
|
589
|
+
].includes(message.role) ? {
|
|
590
|
+
provenance: "system-instructions",
|
|
591
|
+
source: message.role
|
|
592
|
+
} : {}
|
|
593
|
+
});
|
|
594
|
+
else if (value.type !== "text" && value.type !== "reasoning") gaps.push({
|
|
595
|
+
sessionId: session.nativeSessionId,
|
|
596
|
+
recordId: message.id,
|
|
597
|
+
reason: `Native ${value.type} content is retained but not projected inline`
|
|
598
|
+
});
|
|
599
|
+
for (const call of session.toolCalls) {
|
|
600
|
+
if (!emitted.has(`call:${call.id}`)) {
|
|
601
|
+
gaps.push({
|
|
602
|
+
sessionId: session.nativeSessionId,
|
|
603
|
+
recordId: call.id,
|
|
604
|
+
reason: "Tool call has no ordered message-part reference"
|
|
605
|
+
});
|
|
606
|
+
tool(call.id, false);
|
|
607
|
+
}
|
|
608
|
+
if (call.result && !emitted.has(`result:${call.id}`)) {
|
|
609
|
+
gaps.push({
|
|
610
|
+
sessionId: session.nativeSessionId,
|
|
611
|
+
recordId: call.id,
|
|
612
|
+
reason: "Tool result has no ordered message-part reference"
|
|
613
|
+
});
|
|
614
|
+
tool(call.id, true);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
rows: rows.sort((a, b) => a.at.localeCompare(b.at)),
|
|
620
|
+
gaps
|
|
621
|
+
};
|
|
622
|
+
}
|
|
286
623
|
// src/tui/workflow-journal.ts
|
|
287
624
|
/** Native workflows have no Runtime accounting. Translate recorded lifecycle only. */
|
|
288
625
|
function workflowJournal(runDir, text) {
|
|
@@ -437,6 +774,7 @@ function loadTopSnapshot(rootDir, input = Date.now()) {
|
|
|
437
774
|
supervisors.sort((a, b) => String(b.startedAt ?? "").localeCompare(String(a.startedAt ?? "")));
|
|
438
775
|
return topSnapshot(root, now, supervisors, diagnostics, discovered);
|
|
439
776
|
}
|
|
777
|
+
/** Combine supervisor projections and source diagnostics into one snapshot. */
|
|
440
778
|
function topSnapshot(root, now, supervisors, diagnostics = [], discovered = 1, pursuit) {
|
|
441
779
|
return {
|
|
442
780
|
root,
|
|
@@ -602,13 +940,14 @@ function retainedTranscript(event) {
|
|
|
602
940
|
function recordedReason(event) {
|
|
603
941
|
return isRecord(event) && typeof event.reason === "string" ? event.reason : void 0;
|
|
604
942
|
}
|
|
943
|
+
/** Project committed lifecycle and spend evidence into one supervisor view. */
|
|
605
944
|
function buildSupervisorView(state, stateDir, events, progressTail, now, workerEventTails = /* @__PURE__ */ new Map(), accounting) {
|
|
606
945
|
const workers = /* @__PURE__ */ new Map();
|
|
607
946
|
let driverSpend = emptySpend;
|
|
608
947
|
const journalRootId = events.find((event) => event.kind === "spawned" && !event.parent && event.label === "root")?.id;
|
|
609
948
|
const isRoot = (id) => id === state.id || id === journalRootId;
|
|
610
949
|
const terminal = accounting ? void 0 : events.filter((event) => isRoot(event.id) && (event.kind === "settled" || event.kind === "cancelled")).at(-1);
|
|
611
|
-
const rootReason = recordedReason(terminal);
|
|
950
|
+
const rootReason = recordedReason(terminal) ?? state.reason;
|
|
612
951
|
for (const event of events) {
|
|
613
952
|
if (isRoot(event.id) || event.kind === "spawned" && !event.parent && event.label === "root") {
|
|
614
953
|
if (event.kind === "metered" && event.accountingOnly !== true) driverSpend = addSpend(driverSpend, parseSpend(event.spend));
|
|
@@ -616,20 +955,33 @@ function buildSupervisorView(state, stateDir, events, progressTail, now, workerE
|
|
|
616
955
|
}
|
|
617
956
|
const worker = ensureWorker(workers, event.id);
|
|
618
957
|
if (event.kind === "spawned") {
|
|
958
|
+
for (const key of [
|
|
959
|
+
"endedAt",
|
|
960
|
+
"outRef",
|
|
961
|
+
"harnessTranscript",
|
|
962
|
+
"transcript",
|
|
963
|
+
"reason",
|
|
964
|
+
"infra",
|
|
965
|
+
"verdict"
|
|
966
|
+
]) delete worker[key];
|
|
619
967
|
worker.label = event.label ?? worker.label;
|
|
968
|
+
if (event.identity) worker.identity = event.identity;
|
|
620
969
|
if (event.parent) worker.parent = event.parent;
|
|
621
970
|
if (event.runtime) worker.runtime = event.runtime;
|
|
622
971
|
worker.status = "running";
|
|
623
972
|
if (event.at) worker.startedAt = event.at;
|
|
624
973
|
const budget = parseBudget(event.budget);
|
|
625
974
|
if (budget) worker.budget = budget;
|
|
626
|
-
} else if (event.kind === "
|
|
975
|
+
} else if (event.kind === "materialized" || event.kind === "seat-materialized") worker.materialization = event.receipt;
|
|
976
|
+
else if (event.kind === "execution-bound") worker.executionBindings = [...worker.executionBindings ?? [], event.binding];
|
|
977
|
+
else if (event.kind === "settled" || event.kind === "cancelled") {
|
|
627
978
|
if (event.at) worker.endedAt = event.at;
|
|
628
979
|
const reason = recordedReason(event);
|
|
629
980
|
if (reason !== void 0) worker.reason = reason;
|
|
630
981
|
if (event.kind === "settled") {
|
|
631
982
|
worker.status = event.status === "down" ? "down" : "done";
|
|
632
983
|
if (event.outRef) worker.outRef = event.outRef;
|
|
984
|
+
if (event.harnessTranscript) worker.harnessTranscript = event.harnessTranscript;
|
|
633
985
|
const transcript = retainedTranscript(event);
|
|
634
986
|
if (transcript) worker.transcript = transcript;
|
|
635
987
|
if (event.infra !== void 0) worker.infra = event.infra;
|
|
@@ -641,7 +993,7 @@ function buildSupervisorView(state, stateDir, events, progressTail, now, workerE
|
|
|
641
993
|
worker.metered = emptySpend;
|
|
642
994
|
}
|
|
643
995
|
} else if (event.kind === "progress") worker.metered = parseSpend(event.spend);
|
|
644
|
-
else if (event.accountingOnly !== true) worker.metered = addSpend(worker.metered, parseSpend(event.spend));
|
|
996
|
+
else if (event.kind === "metered" && event.accountingOnly !== true) worker.metered = addSpend(worker.metered, parseSpend(event.spend));
|
|
645
997
|
}
|
|
646
998
|
if (accounting) {
|
|
647
999
|
driverSpend = observerSpend(accounting.totals.exclusiveByNode[accounting.runId]);
|
|
@@ -892,13 +1244,17 @@ function isSupervisorState(value) {
|
|
|
892
1244
|
"workspaceDir"
|
|
893
1245
|
].every((key) => typeof value[key] === "string") && typeof value.budget === "number";
|
|
894
1246
|
}
|
|
1247
|
+
/** Validate supported journal lifecycle and accounting event shapes. */
|
|
895
1248
|
function isSpawnEvent(value) {
|
|
896
1249
|
return isRecord(value) && typeof value.id === "string" && typeof value.kind === "string" && [
|
|
897
1250
|
"spawned",
|
|
898
1251
|
"settled",
|
|
899
1252
|
"cancelled",
|
|
900
1253
|
"metered",
|
|
901
|
-
"progress"
|
|
1254
|
+
"progress",
|
|
1255
|
+
"materialized",
|
|
1256
|
+
"seat-materialized",
|
|
1257
|
+
"execution-bound"
|
|
902
1258
|
].includes(value.kind);
|
|
903
1259
|
}
|
|
904
1260
|
function isDirectory(path) {
|
|
@@ -919,7 +1275,122 @@ function parseTime(value) {
|
|
|
919
1275
|
function sum(values) {
|
|
920
1276
|
return values.reduce((total, value) => total + value, 0);
|
|
921
1277
|
}
|
|
1278
|
+
// src/tui/top-web-agent.ts
|
|
1279
|
+
const SOURCE_READ_TIMEOUT_MS = 3e4;
|
|
1280
|
+
/** Selected reads share the web server's origin, method and lifecycle boundary. */
|
|
1281
|
+
async function serveTopAgent({ request, response, path, base, roots, snapshots, transcripts, source, readSessions }) {
|
|
1282
|
+
const [, , , , rootIndex, runId, workerId] = path.split("/").map(decodeURIComponent);
|
|
1283
|
+
const root = roots[Number(rootIndex)];
|
|
1284
|
+
const run = root && snapshots.get(root)?.supervisors.find((entry) => entry.id === runId);
|
|
1285
|
+
const safeId = (id) => !!id && /^[a-z0-9_.:-]+$/i.test(id) && id !== "." && id !== "..";
|
|
1286
|
+
if (!safeId(runId) || !safeId(workerId) || !run || !run.workers.some((entry) => entry.id === workerId) && run.id !== workerId) {
|
|
1287
|
+
response.writeHead(404).end();
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
const own = root && existsSync(resolve(root, runId, "observer.jsonl")) ? resolve(root, runId) : void 0;
|
|
1291
|
+
const runDir = own ?? root;
|
|
1292
|
+
const pursuit = own ? (await loadTopSnapshot(root, { runDir: own })).pursuit : root ? snapshots.get(root)?.pursuit : void 0;
|
|
1293
|
+
const worker = () => {
|
|
1294
|
+
const run = root ? snapshots.get(root)?.supervisors.find((run) => run.id === runId) : void 0;
|
|
1295
|
+
const node = run && topAgentWorker(run, workerId);
|
|
1296
|
+
return node && node.id === run?.id && existsSync(resolve(runDir, "root-stream.jsonl")) ? {
|
|
1297
|
+
...node,
|
|
1298
|
+
eventFile: resolve(runDir, "root-stream.jsonl")
|
|
1299
|
+
} : node;
|
|
1300
|
+
};
|
|
1301
|
+
if (!worker()) {
|
|
1302
|
+
response.writeHead(404).end();
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
const controller = new AbortController();
|
|
1306
|
+
response.on("close", () => controller.abort());
|
|
1307
|
+
const readSource = async () => {
|
|
1308
|
+
const deadline = new AbortController();
|
|
1309
|
+
const linked = linkAbort(controller.signal, deadline.signal);
|
|
1310
|
+
const timer = setTimeout(() => deadline.abort("Inspection source read exceeded its deadline"), SOURCE_READ_TIMEOUT_MS);
|
|
1311
|
+
timer.unref();
|
|
1312
|
+
try {
|
|
1313
|
+
const result = await runAbortable(() => source ? source({
|
|
1314
|
+
root: runDir,
|
|
1315
|
+
runId,
|
|
1316
|
+
workerId,
|
|
1317
|
+
signal: linked.signal
|
|
1318
|
+
}) : readSessions ? readTopAgentInspection({
|
|
1319
|
+
worker: worker(),
|
|
1320
|
+
runId,
|
|
1321
|
+
blobs: new FileResultBlobStore(join(runDir, "blobs")),
|
|
1322
|
+
readSessions,
|
|
1323
|
+
signal: linked.signal
|
|
1324
|
+
}) : retainedRouterInspection(worker(), runDir), linked.signal, "Inspection read stopped");
|
|
1325
|
+
return result?.coverage.status === "complete" && !result.rows?.length ? {
|
|
1326
|
+
...result,
|
|
1327
|
+
coverage: {
|
|
1328
|
+
...result.coverage,
|
|
1329
|
+
status: "partial",
|
|
1330
|
+
reason: "No verified native conversation rows were supplied"
|
|
1331
|
+
}
|
|
1332
|
+
} : result;
|
|
1333
|
+
} finally {
|
|
1334
|
+
clearTimeout(timer);
|
|
1335
|
+
linked.release();
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
let history = await transcriptHistory(worker(), runDir, pursuit?.nodes.find((node) => node.runId === runId && node.id === workerId));
|
|
1339
|
+
if (!path.endsWith("/stream")) {
|
|
1340
|
+
const inspection = await readSource();
|
|
1341
|
+
response.writeHead(200, { "Content-Type": "application/json" });
|
|
1342
|
+
response.end(request.method === "HEAD" ? void 0 : JSON.stringify(topAgentMetadata(inspection, history ?? [])));
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
response.writeHead(200, {
|
|
1346
|
+
"Content-Type": "text/event-stream",
|
|
1347
|
+
"X-Accel-Buffering": "no"
|
|
1348
|
+
});
|
|
1349
|
+
response.flushHeaders();
|
|
1350
|
+
if (request.method === "HEAD") {
|
|
1351
|
+
response.end();
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
const stream = createTopAgentStream(Date.now());
|
|
1355
|
+
response.write(`event: reset\ndata: ${JSON.stringify(stream.reset)}\n\n`);
|
|
1356
|
+
let updating = false;
|
|
1357
|
+
const update = () => {
|
|
1358
|
+
if (updating || response.destroyed || response.writableEnded) return;
|
|
1359
|
+
updating = true;
|
|
1360
|
+
(async () => {
|
|
1361
|
+
const node = worker();
|
|
1362
|
+
if (!node || response.writableLength > 1048576) {
|
|
1363
|
+
response.destroy();
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
const inspection = await readSource();
|
|
1367
|
+
const recorded = history ?? transcriptTail(node);
|
|
1368
|
+
history = void 0;
|
|
1369
|
+
const alive = node.status === "running";
|
|
1370
|
+
const frames = stream.frame({
|
|
1371
|
+
recorded,
|
|
1372
|
+
inspection,
|
|
1373
|
+
alive,
|
|
1374
|
+
includeContext: new URL(request.url ?? "/", base).searchParams.get("context") === "1"
|
|
1375
|
+
});
|
|
1376
|
+
for (const frame of frames) if (!response.write(`event: ${frame.event}\ndata: ${JSON.stringify(frame.data)}\n\n`)) await once(response, "drain", { signal: controller.signal });
|
|
1377
|
+
if (!alive) {
|
|
1378
|
+
response.write("event: end\ndata: {}\n\n");
|
|
1379
|
+
response.end();
|
|
1380
|
+
}
|
|
1381
|
+
})().catch(() => {
|
|
1382
|
+
if (!response.destroyed) response.end("event: unavailable\ndata: {}\n\n");
|
|
1383
|
+
}).finally(() => {
|
|
1384
|
+
updating = false;
|
|
1385
|
+
});
|
|
1386
|
+
};
|
|
1387
|
+
transcripts.add(update);
|
|
1388
|
+
response.on("close", () => transcripts.delete(update));
|
|
1389
|
+
update();
|
|
1390
|
+
}
|
|
922
1391
|
// src/tui/top-web.ts
|
|
1392
|
+
/** Interval for reconciling missed filesystem notifications in the live viewer. */
|
|
1393
|
+
const TOP_SNAPSHOT_RECONCILE_MS = 500;
|
|
923
1394
|
/** Read-only web transport over the same snapshots as the terminal. No execution or writes. */
|
|
924
1395
|
async function serveTopWeb(options) {
|
|
925
1396
|
if (!options.roots.length) throw new Error("At least one --root is required");
|
|
@@ -1035,87 +1506,18 @@ async function serveTopWeb(options) {
|
|
|
1035
1506
|
snapshots: [...snapshots.values()]
|
|
1036
1507
|
});
|
|
1037
1508
|
response.on("close", () => clients.delete(response));
|
|
1038
|
-
} else if (/^\/api\/top\/agents\/\d+\/[^/]+\/[^/]
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
label: "root",
|
|
1051
|
-
...runDir && existsSync(resolve(runDir, "root-stream.jsonl")) ? { eventFile: resolve(runDir, "root-stream.jsonl") } : {},
|
|
1052
|
-
status: run.status === "cancelled" ? "cancelled" : run.status === "done" ? "done" : run.status === "down" ? "down" : "running",
|
|
1053
|
-
startedAt: run.startedAt,
|
|
1054
|
-
endedAt: run.completedAt,
|
|
1055
|
-
latencyMs: run.totals.latencyMs,
|
|
1056
|
-
spend: run.driverSpend,
|
|
1057
|
-
metered: {
|
|
1058
|
-
iterations: 0,
|
|
1059
|
-
tokensInput: 0,
|
|
1060
|
-
tokensOutput: 0,
|
|
1061
|
-
usd: 0,
|
|
1062
|
-
ms: 0
|
|
1063
|
-
},
|
|
1064
|
-
liveTail: run.progressTail
|
|
1065
|
-
};
|
|
1066
|
-
};
|
|
1067
|
-
if (!worker()) {
|
|
1068
|
-
response.writeHead(404).end();
|
|
1069
|
-
return;
|
|
1070
|
-
}
|
|
1071
|
-
let history = await transcriptHistory(worker(), runDir, pursuit?.nodes.find((node) => node.runId === runId && node.id === workerId));
|
|
1072
|
-
response.writeHead(200, {
|
|
1073
|
-
"Content-Type": "text/event-stream",
|
|
1074
|
-
"X-Accel-Buffering": "no"
|
|
1075
|
-
});
|
|
1076
|
-
response.flushHeaders();
|
|
1077
|
-
if (request.method === "HEAD") {
|
|
1078
|
-
response.end();
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
const generation = Date.now();
|
|
1082
|
-
response.write(`event: reset\ndata: ${JSON.stringify({ generation })}\n\n`);
|
|
1083
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1084
|
-
let lastSeq = 0;
|
|
1085
|
-
const update = () => {
|
|
1086
|
-
const node = worker();
|
|
1087
|
-
if (!node || response.writableLength > 1048576) {
|
|
1088
|
-
response.destroy();
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
const events = (history ?? transcriptTail(node)).flatMap((row) => {
|
|
1092
|
-
const key = createHash("sha256").update(JSON.stringify(row)).digest("hex");
|
|
1093
|
-
if (seen.has(key)) return [];
|
|
1094
|
-
seen.add(key);
|
|
1095
|
-
if (seen.size > 2e3) seen.delete(seen.values().next().value);
|
|
1096
|
-
return [{
|
|
1097
|
-
...row,
|
|
1098
|
-
seq: ++lastSeq
|
|
1099
|
-
}];
|
|
1100
|
-
});
|
|
1101
|
-
history = void 0;
|
|
1102
|
-
const alive = node.status === "running";
|
|
1103
|
-
response.write(`event: rows\ndata: ${JSON.stringify({
|
|
1104
|
-
generation,
|
|
1105
|
-
events,
|
|
1106
|
-
lastSeq,
|
|
1107
|
-
alive,
|
|
1108
|
-
window: "recorded-tail"
|
|
1109
|
-
})}\n\n`);
|
|
1110
|
-
if (!alive) {
|
|
1111
|
-
response.write("event: end\ndata: {}\n\n");
|
|
1112
|
-
response.end();
|
|
1113
|
-
}
|
|
1114
|
-
};
|
|
1115
|
-
transcripts.add(update);
|
|
1116
|
-
response.on("close", () => transcripts.delete(update));
|
|
1117
|
-
update();
|
|
1118
|
-
} else if (path === "/api/top") {
|
|
1509
|
+
} else if (/^\/api\/top\/agents\/\d+\/[^/]+\/[^/]+(?:\/stream)?$/.test(path)) await serveTopAgent({
|
|
1510
|
+
request,
|
|
1511
|
+
response,
|
|
1512
|
+
path,
|
|
1513
|
+
base,
|
|
1514
|
+
roots,
|
|
1515
|
+
snapshots,
|
|
1516
|
+
transcripts,
|
|
1517
|
+
source: options.source,
|
|
1518
|
+
readSessions: options.readSessions
|
|
1519
|
+
});
|
|
1520
|
+
else if (path === "/api/top") {
|
|
1119
1521
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
1120
1522
|
response.end(JSON.stringify({
|
|
1121
1523
|
sequence,
|
|
@@ -1230,6 +1632,6 @@ async function loadRunsRoot(root) {
|
|
|
1230
1632
|
loaded: supervisors.length
|
|
1231
1633
|
};
|
|
1232
1634
|
}
|
|
1233
|
-
export {
|
|
1635
|
+
export { topAgentMetadata as _, buildSupervisorView as a, numberValue as c, topSnapshot as d, totalSpend as f, readTopAgentInspection as g, readTopAgentHistory as h, aggregateSnapshot as i, parseSpend as l, harnessSessionRows as m, runTopWeb as n, isSpawnEvent as o, createTopAgentStream as p, serveTopWeb as r, loadTopSnapshot as s, TOP_SNAPSHOT_RECONCILE_MS as t, parseTime as u, topAgentWorker as v };
|
|
1234
1636
|
|
|
1235
|
-
//# sourceMappingURL=top-web-
|
|
1637
|
+
//# sourceMappingURL=top-web-DRfER2jW.js.map
|