@tangle-network/agent-runtime 2.3.4 → 2.3.8
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-SA2R6STL.js → activation-CDPbi8JA.js} +2 -2
- package/dist/{activation-SA2R6STL.js.map → activation-CDPbi8JA.js.map} +1 -1
- package/dist/{activation-BAUOO9T-.d.ts → activation-Cks1h3Nw.d.ts} +2 -2
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +3 -3
- package/dist/{coordination-manager-BxnEJXsv.js → coordination-manager-DNrb7zMg.js} +4 -4
- package/dist/{coordination-manager-BxnEJXsv.js.map → coordination-manager-DNrb7zMg.js.map} +1 -1
- package/dist/{delegate-Drwq3e6S.js → delegate-CHdWyLz9.js} +2 -2
- package/dist/{delegate-Drwq3e6S.js.map → delegate-CHdWyLz9.js.map} +1 -1
- package/dist/durable.d.ts +1 -1
- package/dist/durable.js +6 -6
- package/dist/{graph-Bio7TWRa.js → graph-oOdDpXFH.js} +6 -6
- package/dist/graph-oOdDpXFH.js.map +1 -0
- package/dist/{improve-BaVcOScf.js → improve-BCjhWbVk.js} +5 -5
- package/dist/{improve-BaVcOScf.js.map → improve-BCjhWbVk.js.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +8 -8
- package/dist/intelligence.d.ts +2 -2
- package/dist/intelligence.js +3 -3
- package/dist/kernel.d.ts +1 -1
- package/dist/kernel.js +11 -11
- package/dist/{loop-runner-t4qfcieO.d.ts → loop-runner-Bkvx4Saw.d.ts} +2 -2
- package/dist/{loop-runner-w2R6GUSp.js → loop-runner-Cy1K7f-A.js} +2 -2
- package/dist/{loop-runner-w2R6GUSp.js.map → loop-runner-Cy1K7f-A.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 +4 -4
- package/dist/profiles.d.ts +1 -1
- package/dist/{provision-supervisor-DG4E0Viy.js → provision-supervisor-Pd95wA1X.js} +4 -4
- package/dist/{provision-supervisor-DG4E0Viy.js.map → provision-supervisor-Pd95wA1X.js.map} +1 -1
- package/dist/{root-stream-To4qAc0e.js → root-stream-Bsh5WFcg.js} +2 -2
- package/dist/{root-stream-To4qAc0e.js.map → root-stream-Bsh5WFcg.js.map} +1 -1
- package/dist/{run-cancellation-BhP0Nw6G.js → run-cancellation-CzQZUlHr.js} +2 -2
- package/dist/{run-cancellation-BhP0Nw6G.js.map → run-cancellation-CzQZUlHr.js.map} +1 -1
- package/dist/{runtime-DVwFHx7i.js → runtime-CWZ9q7lU.js} +11 -11
- package/dist/{runtime-DVwFHx7i.js.map → runtime-CWZ9q7lU.js.map} +1 -1
- package/dist/{search-executor-DRzb2WwZ.js → search-executor-CMPbMvpr.js} +3 -3
- package/dist/{search-executor-DRzb2WwZ.js.map → search-executor-CMPbMvpr.js.map} +1 -1
- package/dist/{server-BNsxRqPY.js → server-DyTbsg7n.js} +3 -3
- package/dist/{server-BNsxRqPY.js.map → server-DyTbsg7n.js.map} +1 -1
- package/dist/{structural-rollout-C49iL5Cn.js → structural-rollout-BUqZsIhX.js} +3 -3
- package/dist/{structural-rollout-C49iL5Cn.js.map → structural-rollout-BUqZsIhX.js.map} +1 -1
- package/dist/{supervise-BwUx_gjv.js → supervise-D1eYde7p.js} +49 -10
- package/dist/supervise-D1eYde7p.js.map +1 -0
- package/dist/{supervision-discovery-CVpmTroK.js → supervision-discovery-DmDWHwLu.js} +2 -2
- package/dist/{supervision-discovery-CVpmTroK.js.map → supervision-discovery-DmDWHwLu.js.map} +1 -1
- package/dist/{supervisor-ByarsrwH.js → supervisor-B8beIEqH.js} +23 -37
- package/dist/supervisor-B8beIEqH.js.map +1 -0
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +11 -11
- package/dist/{top-web-Dh1i-KLd.js → top-web-CUDlOKu0.js} +245 -233
- package/dist/top-web-CUDlOKu0.js.map +1 -0
- package/dist/tui/bin.js +3 -3
- package/dist/tui/index.d.ts +12 -9
- package/dist/tui/index.js +2 -2
- package/dist/{types-BGGZDz0I.d.ts → types-DzS0kBeK.d.ts} +4 -2
- package/package.json +1 -1
- package/dist/graph-Bio7TWRa.js.map +0 -1
- package/dist/supervise-BwUx_gjv.js.map +0 -1
- package/dist/supervisor-ByarsrwH.js.map +0 -1
- package/dist/top-web-Dh1i-KLd.js.map +0 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as projectPursuit, t as discoverDurableSupervisionRun } from "./supervision-discovery-
|
|
1
|
+
import { C as FileSpawnJournal, E as FileResultBlobStore, Jt as recordValue, M as FileObserverJournal, T as spawnJournalRecordEvents, _ as supervisorRunsRoot, h as safeWorkerFile, r as nodeStreamFile, t as ROOT_STREAM_FILE, u as legacySupervisorRunsRoot } from "./root-stream-Bsh5WFcg.js";
|
|
2
|
+
import { r as parseCommittedJsonLines } from "./jsonl-file-BBP9Dz9w.js";
|
|
3
|
+
import { n as projectPursuit, t as discoverDurableSupervisionRun } from "./supervision-discovery-DmDWHwLu.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
|
+
import { isRecord } from "@tangle-network/agent-core";
|
|
5
6
|
import { createHash } from "node:crypto";
|
|
6
|
-
import { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, statSync, watch } from "node:fs";
|
|
7
|
+
import { closeSync, existsSync, fstatSync, openSync, readFileSync, readSync, readdirSync, statSync, watch } from "node:fs";
|
|
7
8
|
import { basename, dirname, extname, join, resolve, sep } from "node:path";
|
|
8
9
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
9
10
|
import { createServer } from "node:http";
|
|
11
|
+
import { StringDecoder } from "node:string_decoder";
|
|
10
12
|
// src/tui/top-transcript.ts
|
|
11
13
|
/** Read selected history on demand, leaving the live topology snapshot bounded and fast. */
|
|
12
14
|
async function transcriptHistory(worker, runDir, node) {
|
|
@@ -51,15 +53,7 @@ async function transcriptHistory(worker, runDir, node) {
|
|
|
51
53
|
});
|
|
52
54
|
return [...rows, ...native];
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
for await (const row of readCommittedJsonLines(worker.eventFile)) {
|
|
56
|
-
lines.push(JSON.stringify(row));
|
|
57
|
-
if (lines.length > 200) lines.shift();
|
|
58
|
-
}
|
|
59
|
-
return [...rows, ...transcriptRows({
|
|
60
|
-
...worker,
|
|
61
|
-
liveTail: lines
|
|
62
|
-
})];
|
|
56
|
+
return [...rows, ...transcriptRows({ liveTail: readTranscriptTail(worker.eventFile) })];
|
|
63
57
|
}
|
|
64
58
|
/**
|
|
65
59
|
* A settled worker's retained Router conversation as progress lines, for an executor that ran its
|
|
@@ -69,33 +63,16 @@ async function transcriptHistory(worker, runDir, node) {
|
|
|
69
63
|
async function retainedTranscriptLines(worker, runDir) {
|
|
70
64
|
const at = worker.endedAt;
|
|
71
65
|
if (worker.transcript?.harness !== "router" || at === void 0) return [];
|
|
72
|
-
|
|
73
|
-
const line = (event) => JSON.stringify({
|
|
74
|
-
at,
|
|
75
|
-
event
|
|
76
|
-
});
|
|
77
|
-
return (artifact?.files ?? []).flatMap((file) => (typeof file.content === "string" ? file.content.split("\n") : []).flatMap((text) => {
|
|
78
|
-
let message;
|
|
66
|
+
return ((await new FileResultBlobStore(join(runDir, "blobs")).get(worker.transcript.ref))?.files ?? []).flatMap((file) => (typeof file.content === "string" ? file.content.split("\n") : []).flatMap((text) => {
|
|
79
67
|
try {
|
|
80
|
-
message = JSON.parse(text);
|
|
68
|
+
const message = recordValue(JSON.parse(text));
|
|
69
|
+
return message?.role === "assistant" || message?.role === "tool" ? [JSON.stringify({
|
|
70
|
+
at,
|
|
71
|
+
message
|
|
72
|
+
})] : [];
|
|
81
73
|
} catch {
|
|
82
74
|
return [];
|
|
83
75
|
}
|
|
84
|
-
if (message.role === "tool") return [line({
|
|
85
|
-
kind: "tool_result",
|
|
86
|
-
toolCallId: message.tool_call_id,
|
|
87
|
-
result: message.content
|
|
88
|
-
})];
|
|
89
|
-
if (message.role !== "assistant") return [];
|
|
90
|
-
return [...typeof message.content === "string" && message.content.length > 0 ? [line({
|
|
91
|
-
kind: "text_delta",
|
|
92
|
-
text: message.content
|
|
93
|
-
})] : [], ...(message.tool_calls ?? []).map((call) => line({
|
|
94
|
-
kind: "tool_call",
|
|
95
|
-
toolCallId: call.id,
|
|
96
|
-
toolName: call.function?.name,
|
|
97
|
-
args: call.function?.arguments
|
|
98
|
-
}))];
|
|
99
76
|
}));
|
|
100
77
|
}
|
|
101
78
|
/** Present the recorded tail through sandbox-ui's existing agent-stream contract. */
|
|
@@ -104,83 +81,83 @@ function transcriptRows(worker) {
|
|
|
104
81
|
for (const line of worker.liveTail) {
|
|
105
82
|
let value;
|
|
106
83
|
try {
|
|
107
|
-
value = JSON.parse(line);
|
|
84
|
+
value = recordValue(JSON.parse(line));
|
|
108
85
|
} catch {
|
|
109
86
|
continue;
|
|
110
87
|
}
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
88
|
+
const timestamp = value?.timestamp ?? value?.at;
|
|
89
|
+
const time = typeof timestamp === "string" ? Date.parse(timestamp) : timestamp;
|
|
90
|
+
if (typeof time !== "number" || !Number.isFinite(new Date(time).getTime())) continue;
|
|
91
|
+
const at = new Date(time).toISOString();
|
|
92
|
+
const push = (event) => rows.push({
|
|
93
|
+
at,
|
|
94
|
+
event
|
|
95
|
+
});
|
|
96
|
+
const event = recordValue(value?.event);
|
|
97
|
+
if (typeof event?.kind === "string") {
|
|
115
98
|
const kind = {
|
|
116
99
|
text_delta: "text",
|
|
117
100
|
reasoning_delta: "reasoning",
|
|
118
101
|
tool_call: "tool-call",
|
|
119
102
|
tool_result: "tool-result"
|
|
120
103
|
}[event.kind] ?? event.kind;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
...event,
|
|
125
|
-
kind
|
|
126
|
-
}
|
|
104
|
+
push({
|
|
105
|
+
...event,
|
|
106
|
+
kind
|
|
127
107
|
});
|
|
128
108
|
continue;
|
|
129
109
|
}
|
|
130
|
-
const message = value
|
|
110
|
+
const message = recordValue(value?.message);
|
|
131
111
|
if (!message) continue;
|
|
112
|
+
if (message.role === "tool") {
|
|
113
|
+
push({
|
|
114
|
+
kind: "tool-result",
|
|
115
|
+
toolCallId: String(message.tool_call_id),
|
|
116
|
+
result: message.content
|
|
117
|
+
});
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
for (const item of Array.isArray(message.tool_calls) ? message.tool_calls : []) {
|
|
121
|
+
const call = recordValue(item), fn = recordValue(call?.function);
|
|
122
|
+
if (call && fn) push({
|
|
123
|
+
kind: "tool-call",
|
|
124
|
+
toolCallId: String(call.id),
|
|
125
|
+
toolName: String(fn.name),
|
|
126
|
+
args: fn.arguments
|
|
127
|
+
});
|
|
128
|
+
}
|
|
132
129
|
const parts = typeof message.content === "string" ? [{
|
|
133
130
|
type: "text",
|
|
134
131
|
text: message.content
|
|
135
|
-
}] : message.content
|
|
136
|
-
for (const part of parts)
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
}] : Array.isArray(message.content) ? message.content : [];
|
|
133
|
+
for (const part of parts) {
|
|
134
|
+
if (!recordValue(part)) continue;
|
|
135
|
+
if (part.type === "text" && typeof part.text === "string") push({
|
|
139
136
|
kind: message.role === "user" ? "user" : "text",
|
|
140
137
|
text: part.text
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
else if (part.type === "thinking" && typeof part.thinking === "string") rows.push({
|
|
144
|
-
at,
|
|
145
|
-
event: {
|
|
138
|
+
});
|
|
139
|
+
else if (part.type === "thinking" && typeof part.thinking === "string") push({
|
|
146
140
|
kind: "reasoning",
|
|
147
141
|
text: part.thinking
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
else if (part.type === "tool_use") rows.push({
|
|
151
|
-
at,
|
|
152
|
-
event: {
|
|
142
|
+
});
|
|
143
|
+
else if (part.type === "tool_use") push({
|
|
153
144
|
kind: "tool-call",
|
|
154
145
|
toolCallId: String(part.id),
|
|
155
146
|
toolName: String(part.name),
|
|
156
147
|
args: part.input
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
else if (part.type === "tool_result") rows.push({
|
|
160
|
-
at,
|
|
161
|
-
event: {
|
|
148
|
+
});
|
|
149
|
+
else if (part.type === "tool_result") push({
|
|
162
150
|
kind: "tool-result",
|
|
163
151
|
toolCallId: String(part.tool_use_id),
|
|
164
152
|
result: part.content
|
|
165
|
-
}
|
|
166
|
-
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
167
155
|
}
|
|
168
156
|
return rows;
|
|
169
157
|
}
|
|
170
158
|
/** The bounded native/normalized tail shared by snapshot adapters and selected streams. */
|
|
171
159
|
function readTranscriptTail(file) {
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
const size = statSync(file).size;
|
|
175
|
-
const buffer = Buffer.alloc(Math.min(size, 65536));
|
|
176
|
-
readSync(fd, buffer, 0, buffer.length, Math.max(0, size - buffer.length));
|
|
177
|
-
const lines = buffer.toString().split("\n");
|
|
178
|
-
lines.pop();
|
|
179
|
-
if (size > buffer.length) lines.shift();
|
|
180
|
-
return lines.filter(Boolean).slice(-200);
|
|
181
|
-
} finally {
|
|
182
|
-
closeSync(fd);
|
|
183
|
-
}
|
|
160
|
+
return readRecordedEvents(file, 200).lines;
|
|
184
161
|
}
|
|
185
162
|
function transcriptTail(worker) {
|
|
186
163
|
return transcriptRows(worker.eventFile ? {
|
|
@@ -188,6 +165,89 @@ function transcriptTail(worker) {
|
|
|
188
165
|
liveTail: readTranscriptTail(worker.eventFile)
|
|
189
166
|
} : worker);
|
|
190
167
|
}
|
|
168
|
+
const cursors = /* @__PURE__ */ new Map();
|
|
169
|
+
const activityKinds = /* @__PURE__ */ new Set([
|
|
170
|
+
"text",
|
|
171
|
+
"reasoning",
|
|
172
|
+
"user",
|
|
173
|
+
"tool-call",
|
|
174
|
+
"tool-result",
|
|
175
|
+
"interaction",
|
|
176
|
+
"child_task"
|
|
177
|
+
]);
|
|
178
|
+
/** Read appended UTF-8 bytes once; complete EOF JSON follows the durable reader contract. */
|
|
179
|
+
function readRecordedEvents(file, maxLines = 8) {
|
|
180
|
+
const fd = openSync(file, "r");
|
|
181
|
+
try {
|
|
182
|
+
const stat = fstatSync(fd);
|
|
183
|
+
let cursor = cursors.get(file);
|
|
184
|
+
if (!cursor || cursor.dev !== stat.dev || cursor.ino !== stat.ino || stat.size < cursor.offset) {
|
|
185
|
+
cursor = {
|
|
186
|
+
dev: stat.dev,
|
|
187
|
+
ino: stat.ino,
|
|
188
|
+
offset: 0,
|
|
189
|
+
pending: "",
|
|
190
|
+
decoder: new StringDecoder("utf8"),
|
|
191
|
+
times: [],
|
|
192
|
+
lines: [],
|
|
193
|
+
result: {
|
|
194
|
+
file,
|
|
195
|
+
lines: [],
|
|
196
|
+
activity: {}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
cursors.set(file, cursor);
|
|
200
|
+
}
|
|
201
|
+
if (stat.size === cursor.offset) return {
|
|
202
|
+
...cursor.result,
|
|
203
|
+
lines: cursor.result.lines.slice(-maxLines)
|
|
204
|
+
};
|
|
205
|
+
const lines = [...cursor.lines];
|
|
206
|
+
const buffer = Buffer.alloc(Math.min(65536, stat.size - cursor.offset));
|
|
207
|
+
while (cursor.offset < stat.size) {
|
|
208
|
+
const bytes = readSync(fd, buffer, 0, Math.min(buffer.length, stat.size - cursor.offset), cursor.offset);
|
|
209
|
+
if (!bytes) break;
|
|
210
|
+
cursor.offset += bytes;
|
|
211
|
+
const rows = (cursor.pending + cursor.decoder.write(buffer.subarray(0, bytes))).split("\n");
|
|
212
|
+
cursor.pending = rows.pop();
|
|
213
|
+
for (const line of rows) {
|
|
214
|
+
if (!line.trim()) continue;
|
|
215
|
+
lines.push(line);
|
|
216
|
+
if (lines.length > 200) lines.shift();
|
|
217
|
+
const activity = transcriptRows({ liveTail: [line] }).find((row) => activityKinds.has(row.event.kind));
|
|
218
|
+
if (activity) cursor.times.push(Date.parse(activity.at));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
cursor.lines = lines;
|
|
222
|
+
let pending = [];
|
|
223
|
+
if (cursor.pending.trim()) try {
|
|
224
|
+
JSON.parse(cursor.pending);
|
|
225
|
+
pending = [cursor.pending];
|
|
226
|
+
} catch {}
|
|
227
|
+
const final = transcriptRows({ liveTail: pending }).find((row) => activityKinds.has(row.event.kind));
|
|
228
|
+
const times = (final ? [...cursor.times, Date.parse(final.at)] : cursor.times).sort((a, b) => a - b);
|
|
229
|
+
const gaps = times.slice(1).map((at, i) => at - times[i]).sort((a, b) => a - b);
|
|
230
|
+
const mid = Math.floor(gaps.length / 2);
|
|
231
|
+
cursor.result = {
|
|
232
|
+
file,
|
|
233
|
+
lines: [...lines, ...pending].slice(-200),
|
|
234
|
+
...times.length ? { firstActivityAt: times[0] } : {},
|
|
235
|
+
activity: {
|
|
236
|
+
...times.length ? { lastActivityAt: times.at(-1) } : {},
|
|
237
|
+
...gaps.length ? { medianEventGapMs: (gaps[Math.floor((gaps.length - 1) / 2)] + gaps[mid]) / 2 } : {}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
return {
|
|
241
|
+
...cursor.result,
|
|
242
|
+
lines: cursor.result.lines.slice(-maxLines)
|
|
243
|
+
};
|
|
244
|
+
} catch (error) {
|
|
245
|
+
cursors.delete(file);
|
|
246
|
+
throw error;
|
|
247
|
+
} finally {
|
|
248
|
+
closeSync(fd);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
191
251
|
// src/tui/workflow-journal.ts
|
|
192
252
|
/** Native workflows have no Runtime accounting. Translate recorded lifecycle only. */
|
|
193
253
|
function workflowJournal(runDir, text) {
|
|
@@ -204,21 +264,10 @@ function workflowJournal(runDir, text) {
|
|
|
204
264
|
let start;
|
|
205
265
|
let end;
|
|
206
266
|
try {
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
readSync(fd, prefix, 0, prefix.length, 0);
|
|
212
|
-
start = /"timestamp":"([^"]+)"/.exec(prefix.toString())?.[1];
|
|
213
|
-
const lines = readTranscriptTail(file).slice(-8);
|
|
214
|
-
end = [...lines.join("\n").matchAll(/"timestamp":"([^"]+)"/g)].at(-1)?.[1];
|
|
215
|
-
tails.set(id, {
|
|
216
|
-
file,
|
|
217
|
-
lines
|
|
218
|
-
});
|
|
219
|
-
} finally {
|
|
220
|
-
closeSync(fd);
|
|
221
|
-
}
|
|
267
|
+
const recorded = readRecordedEvents(file);
|
|
268
|
+
tails.set(id, recorded);
|
|
269
|
+
if (recorded.firstActivityAt !== void 0) start = new Date(recorded.firstActivityAt).toISOString();
|
|
270
|
+
if (recorded.activity.lastActivityAt !== void 0) end = new Date(recorded.activity.lastActivityAt).toISOString();
|
|
222
271
|
} catch (error) {
|
|
223
272
|
if (error.code !== "ENOENT") throw error;
|
|
224
273
|
}
|
|
@@ -240,12 +289,14 @@ function workflowJournal(runDir, text) {
|
|
|
240
289
|
}
|
|
241
290
|
});
|
|
242
291
|
} else if (row.type === "result" || row.type === "error" || row.type === "cancelled") {
|
|
243
|
-
const
|
|
244
|
-
const at =
|
|
292
|
+
const activity = tails.get(id)?.activity.lastActivityAt;
|
|
293
|
+
const at = activity === void 0 ? void 0 : new Date(activity).toISOString();
|
|
294
|
+
const reason = typeof row.reason === "string" ? { reason: row.reason } : {};
|
|
245
295
|
events.push(row.type === "cancelled" ? {
|
|
246
296
|
kind: "cancelled",
|
|
247
297
|
id,
|
|
248
|
-
...at ? { at } : {}
|
|
298
|
+
...at ? { at } : {},
|
|
299
|
+
...reason
|
|
249
300
|
} : {
|
|
250
301
|
kind: "settled",
|
|
251
302
|
id,
|
|
@@ -254,7 +305,8 @@ function workflowJournal(runDir, text) {
|
|
|
254
305
|
spent: {
|
|
255
306
|
usdKnown: false,
|
|
256
307
|
tokensKnown: false
|
|
257
|
-
}
|
|
308
|
+
},
|
|
309
|
+
...reason
|
|
258
310
|
});
|
|
259
311
|
}
|
|
260
312
|
}
|
|
@@ -267,12 +319,8 @@ function workflowJournal(runDir, text) {
|
|
|
267
319
|
};
|
|
268
320
|
}
|
|
269
321
|
// src/tui/top-snapshot.ts
|
|
270
|
-
/**
|
|
271
|
-
* Read-only operator snapshots over the writer-owned supervisor layout and explicit durable runs.
|
|
272
|
-
* Workspace reads preserve legacy files; durable reads use discovery, journals and the canonical
|
|
273
|
-
* pursuit projection for model identity, failure and accounting evidence. Run totals count once.
|
|
322
|
+
/** Read-only topology, activity and accounting over writer-owned workspace and durable journals.
|
|
274
323
|
* ANSI presentation lives in `./top-frame`.
|
|
275
|
-
*
|
|
276
324
|
* @experimental
|
|
277
325
|
*/
|
|
278
326
|
const emptySpend = {
|
|
@@ -283,7 +331,7 @@ const emptySpend = {
|
|
|
283
331
|
ms: 0
|
|
284
332
|
};
|
|
285
333
|
function loadTopSnapshot(rootDir, input = Date.now()) {
|
|
286
|
-
if (typeof input !== "number" && input.format === "journal") return
|
|
334
|
+
if (typeof input !== "number" && input.format === "journal") return loadDurableTopSnapshot(rootDir, input.runDir, input.now ?? Date.now(), false);
|
|
287
335
|
if (typeof input !== "number" && input.format === "workflow") {
|
|
288
336
|
const root = resolve(rootDir), dir = resolve(input.runDir), now = input.now ?? Date.now();
|
|
289
337
|
const read = readSourceFile(join(dir, "journal.jsonl"), "journal", "journal.jsonl");
|
|
@@ -347,7 +395,7 @@ function loadTopSnapshot(rootDir, input = Date.now()) {
|
|
|
347
395
|
if (!state.value) continue;
|
|
348
396
|
const journal = readJournal(dir);
|
|
349
397
|
const progress = readSourceFile(join(dir, "progress.ndjson"), "progress", "progress.ndjson");
|
|
350
|
-
const workerEvents = readWorkerEventTails(
|
|
398
|
+
const workerEvents = readWorkerEventTails(dir);
|
|
351
399
|
report(entry, [
|
|
352
400
|
...journal.diagnostics,
|
|
353
401
|
...progress.diagnostics,
|
|
@@ -371,78 +419,52 @@ function topSnapshot(root, now, supervisors, diagnostics = [], discovered = 1, p
|
|
|
371
419
|
...pursuit ? { pursuit } : {}
|
|
372
420
|
};
|
|
373
421
|
}
|
|
374
|
-
|
|
422
|
+
/** Observer-backed and older spawn-only runs share topology; missing accounting stays unknown. */
|
|
423
|
+
async function loadDurableTopSnapshot(rootDir, runDir, now, observed = true) {
|
|
375
424
|
const root = resolve(rootDir);
|
|
376
425
|
let pursuit;
|
|
377
|
-
let source = "observer-journal";
|
|
378
|
-
let path = "observer.jsonl";
|
|
426
|
+
let source = observed ? "observer-journal" : "journal";
|
|
427
|
+
let path = observed ? "observer.jsonl" : "spawn-journal.jsonl";
|
|
379
428
|
try {
|
|
380
429
|
if (typeof runDir !== "string" || !runDir.trim()) throw new TypeError("runDir must be non-empty");
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
430
|
+
if (observed) {
|
|
431
|
+
const journal = await FileObserverJournal.open(join(resolve(runDir), path));
|
|
432
|
+
if (!journal) return topSnapshot(root, now, [], [{
|
|
433
|
+
source,
|
|
434
|
+
path,
|
|
435
|
+
reason: "missing"
|
|
436
|
+
}]);
|
|
437
|
+
pursuit = projectPursuit(await journal.read());
|
|
438
|
+
}
|
|
388
439
|
source = "journal";
|
|
389
440
|
path = ".";
|
|
390
441
|
const discovery = await discoverDurableSupervisionRun(runDir);
|
|
391
|
-
const latest = new Map(pursuit.runs.map((run) => [run.runId, run]));
|
|
392
442
|
path = "spawn-journal.jsonl";
|
|
393
|
-
const spawnJournal = new FileSpawnJournal(discovery.spawnJournalPath);
|
|
394
|
-
const rows = [...latest.values()].filter((run) => !discovery.roots.some((id) => run.runId.startsWith(`${id}/`)));
|
|
395
|
-
return topSnapshot(root, now, await Promise.all(rows.map(async (run) => {
|
|
396
|
-
const binding = discovery.roots.find((id) => id === run.runId);
|
|
397
|
-
if (!binding && existsSync(discovery.spawnJournalPath)) throw new Error("observer run has no discovered spawn root");
|
|
398
|
-
const events = binding ? await spawnJournal.loadTree(binding) ?? [] : [];
|
|
399
|
-
const rootSpawn = events.find((event) => event.kind === "spawned" && event.id === binding);
|
|
400
|
-
const budget = rootSpawn?.kind === "spawned" ? rootSpawn.budget?.maxTokens : void 0;
|
|
401
|
-
const terminal = events.filter((e) => e.id === binding && (e.kind === "settled" || e.kind === "cancelled")).at(-1);
|
|
402
|
-
const settledAt = run.settledAt === void 0 ? terminal?.at : new Date(run.settledAt).toISOString();
|
|
403
|
-
return buildSupervisorView({
|
|
404
|
-
id: run.runId,
|
|
405
|
-
status: run.status !== "running" || terminal === void 0 ? run.status : terminal.kind === "settled" ? terminal.status : "cancelled",
|
|
406
|
-
task: run.runId,
|
|
407
|
-
workspaceDir: root,
|
|
408
|
-
budget: budget ?? 0,
|
|
409
|
-
budgetKnown: budget !== void 0,
|
|
410
|
-
startedAt: new Date(run.firstObservedAt).toISOString(),
|
|
411
|
-
...settledAt === void 0 ? {} : { completedAt: settledAt }
|
|
412
|
-
}, discovery.runDir, events.filter(isSpawnEvent), run.error ? [run.error] : [], now, void 0, run);
|
|
413
|
-
})), [], 1, pursuit);
|
|
414
|
-
} catch (error) {
|
|
415
|
-
const reason = isRecord(error) && typeof error.code === "string" ? "unreadable" : "invalid-state";
|
|
416
|
-
return topSnapshot(root, now, [], [{
|
|
417
|
-
source,
|
|
418
|
-
path,
|
|
419
|
-
reason
|
|
420
|
-
}], 1, pursuit);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
/** Old Discovery and factory roots may predate observer.jsonl; absence never establishes known totals. */
|
|
424
|
-
async function loadJournalTopSnapshot(rootDir, runDir, now) {
|
|
425
|
-
const root = resolve(rootDir), dir = resolve(runDir);
|
|
426
|
-
try {
|
|
427
|
-
const discovery = await discoverDurableSupervisionRun(dir);
|
|
428
443
|
const journal = new FileSpawnJournal(discovery.spawnJournalPath);
|
|
429
|
-
|
|
430
|
-
|
|
444
|
+
const latest = new Map(pursuit?.runs.map((run) => [run.runId, run]));
|
|
445
|
+
const ids = pursuit ? [...latest.keys()].filter((id) => !discovery.roots.some((parent) => id.startsWith(`${parent}/`))) : discovery.roots;
|
|
446
|
+
const tails = readWorkerEventTails(discovery.runDir);
|
|
447
|
+
return topSnapshot(root, now, await Promise.all(ids.map(async (id) => {
|
|
448
|
+
const accounting = latest.get(id);
|
|
449
|
+
const binding = discovery.roots.indexOf(id);
|
|
450
|
+
if (binding < 0 && existsSync(discovery.spawnJournalPath)) throw new Error("observer run has no discovered spawn root");
|
|
451
|
+
const events = binding < 0 ? [] : await journal.loadTree(id) ?? [];
|
|
452
|
+
const spawn = events.find((event) => event.kind === "spawned" && event.id === id);
|
|
453
|
+
const budget = spawn?.kind === "spawned" ? spawn.budget?.maxTokens : void 0;
|
|
431
454
|
const terminal = events.filter((event) => event.id === id && (event.kind === "settled" || event.kind === "cancelled")).at(-1);
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
const tails = readWorkerEventTails(supervisorWorkersDir(dir), 8);
|
|
455
|
+
const startedAt = accounting ? new Date(accounting.firstObservedAt).toISOString() : discovery.rootsBegunAt[binding];
|
|
456
|
+
const completedAt = accounting?.settledAt === void 0 ? terminal?.at : new Date(accounting.settledAt).toISOString();
|
|
435
457
|
const view = buildSupervisorView({
|
|
436
458
|
id,
|
|
437
459
|
task: id,
|
|
438
460
|
workspaceDir: root,
|
|
439
|
-
status:
|
|
461
|
+
status: accounting && accounting.status !== "running" ? accounting.status : terminal?.kind === "settled" ? terminal.status : terminal ? "cancelled" : "running",
|
|
440
462
|
budget: budget ?? 0,
|
|
441
463
|
budgetKnown: budget !== void 0,
|
|
442
|
-
...
|
|
443
|
-
...
|
|
444
|
-
},
|
|
445
|
-
return {
|
|
464
|
+
...startedAt == null ? {} : { startedAt },
|
|
465
|
+
...completedAt === void 0 ? {} : { completedAt }
|
|
466
|
+
}, discovery.runDir, events.filter(isSpawnEvent), accounting?.error ? [accounting.error] : [], now, tails.value, accounting);
|
|
467
|
+
return accounting ? view : {
|
|
446
468
|
...view,
|
|
447
469
|
totals: {
|
|
448
470
|
...view.totals,
|
|
@@ -450,17 +472,18 @@ async function loadJournalTopSnapshot(rootDir, runDir, now) {
|
|
|
450
472
|
tokensKnown: false
|
|
451
473
|
}
|
|
452
474
|
};
|
|
453
|
-
})), [{
|
|
475
|
+
})), [...!observed ? [{
|
|
454
476
|
source: "observer-journal",
|
|
455
477
|
path: "observer.jsonl",
|
|
456
478
|
reason: "missing"
|
|
457
|
-
}],
|
|
458
|
-
} catch {
|
|
479
|
+
}] : [], ...tails.diagnostics], observed ? 1 : ids.length, pursuit);
|
|
480
|
+
} catch (error) {
|
|
481
|
+
const reason = isRecord(error) && typeof error.code === "string" ? "unreadable" : "invalid-state";
|
|
459
482
|
return topSnapshot(root, now, [], [{
|
|
460
|
-
source
|
|
461
|
-
path
|
|
462
|
-
reason
|
|
463
|
-
}]);
|
|
483
|
+
source,
|
|
484
|
+
path,
|
|
485
|
+
reason
|
|
486
|
+
}], 1, pursuit);
|
|
464
487
|
}
|
|
465
488
|
}
|
|
466
489
|
function observerSpend(spend, gaps = []) {
|
|
@@ -471,12 +494,7 @@ function observerSpend(spend, gaps = []) {
|
|
|
471
494
|
tokensKnown: spend !== void 0 && parsed.tokensKnown !== false && !gaps.includes("tokens")
|
|
472
495
|
};
|
|
473
496
|
}
|
|
474
|
-
/**
|
|
475
|
-
* The journal file a run dir actually carries. `createFileRunContext` — this repo's own durable
|
|
476
|
-
* writer — names it `spawn-journal.jsonl`; `journal.jsonl` is the retired bare-event file older
|
|
477
|
-
* runs left behind. First existing name wins; a reader that knew only the retired name would show
|
|
478
|
-
* zero workers for every current run.
|
|
479
|
-
*/
|
|
497
|
+
/** Current writer-owned journal first; retain the retired name for recorded runs. */
|
|
480
498
|
const journalFileNames = ["spawn-journal.jsonl", "journal.jsonl"];
|
|
481
499
|
function readJournal(eventDir) {
|
|
482
500
|
const path = journalFileNames.find((candidate) => existsSync(join(eventDir, candidate)));
|
|
@@ -522,11 +540,17 @@ function retainedTranscript(event) {
|
|
|
522
540
|
harness
|
|
523
541
|
} : void 0;
|
|
524
542
|
}
|
|
543
|
+
/** The canonical spawn receipt owns the cause; the public journal view stays compatible. */
|
|
544
|
+
function recordedReason(event) {
|
|
545
|
+
const reason = event?.reason;
|
|
546
|
+
return typeof reason === "string" ? reason : void 0;
|
|
547
|
+
}
|
|
525
548
|
function buildSupervisorView(state, stateDir, events, progressTail, now, workerEventTails = /* @__PURE__ */ new Map(), accounting) {
|
|
526
549
|
const workers = /* @__PURE__ */ new Map();
|
|
527
550
|
let driverSpend = emptySpend;
|
|
528
551
|
const journalRootId = events.find((event) => event.kind === "spawned" && !event.parent && event.label === "root")?.id;
|
|
529
552
|
const isRoot = (id) => id === state.id || id === journalRootId;
|
|
553
|
+
const rootReason = recordedReason(events.filter((event) => isRoot(event.id) && (event.kind === "settled" || event.kind === "cancelled")).at(-1));
|
|
530
554
|
for (const event of events) {
|
|
531
555
|
if (isRoot(event.id) || event.kind === "spawned" && !event.parent && event.label === "root") {
|
|
532
556
|
if (event.kind === "metered" && event.accountingOnly !== true) driverSpend = addSpend(driverSpend, parseSpend(event.spend));
|
|
@@ -543,6 +567,8 @@ function buildSupervisorView(state, stateDir, events, progressTail, now, workerE
|
|
|
543
567
|
if (budget) worker.budget = budget;
|
|
544
568
|
} else if (event.kind === "settled" || event.kind === "cancelled") {
|
|
545
569
|
if (event.at) worker.endedAt = event.at;
|
|
570
|
+
const reason = recordedReason(event);
|
|
571
|
+
if (reason !== void 0) worker.reason = reason;
|
|
546
572
|
if (event.kind === "settled") {
|
|
547
573
|
worker.status = event.status === "down" ? "down" : "done";
|
|
548
574
|
if (event.outRef) worker.outRef = event.outRef;
|
|
@@ -551,10 +577,7 @@ function buildSupervisorView(state, stateDir, events, progressTail, now, workerE
|
|
|
551
577
|
if (event.infra !== void 0) worker.infra = event.infra;
|
|
552
578
|
const verdict = describeVerdict(event.verdict);
|
|
553
579
|
if (verdict) worker.verdict = verdict;
|
|
554
|
-
} else
|
|
555
|
-
worker.status = "cancelled";
|
|
556
|
-
if (event.reason) worker.reason = event.reason;
|
|
557
|
-
}
|
|
580
|
+
} else worker.status = "cancelled";
|
|
558
581
|
if (event.kind === "settled" || event.spent !== void 0) {
|
|
559
582
|
worker.spend = addSpend(worker.spend, parseSpend(event.spent));
|
|
560
583
|
worker.metered = emptySpend;
|
|
@@ -570,12 +593,11 @@ function buildSupervisorView(state, stateDir, events, progressTail, now, workerE
|
|
|
570
593
|
}
|
|
571
594
|
}
|
|
572
595
|
const workerViews = [...workers.values()].map((worker) => {
|
|
573
|
-
const tail = workerEventTails.get(worker.label) ?? workerEventTails.get(safeWorkerFile(worker.label)) ?? workerEventTails.get(safeWorkerFile(worker.id)) ?? workerEventTails.get(worker.id);
|
|
596
|
+
const tail = workerEventTails.get(join(stateDir, nodeStreamFile(worker.id))) ?? workerEventTails.get(worker.label) ?? workerEventTails.get(safeWorkerFile(worker.label)) ?? workerEventTails.get(safeWorkerFile(worker.id)) ?? workerEventTails.get(worker.id);
|
|
574
597
|
worker.liveTail = tail?.lines ?? [];
|
|
575
|
-
if (tail
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if (existsSync(stream)) worker.eventFile = stream;
|
|
598
|
+
if (tail) {
|
|
599
|
+
worker.eventFile = tail.file;
|
|
600
|
+
Object.assign(worker, tail.activity);
|
|
579
601
|
}
|
|
580
602
|
return finalizeWorker(worker, now);
|
|
581
603
|
}).sort((a, b) => compareWorker(a, b));
|
|
@@ -587,6 +609,8 @@ function buildSupervisorView(state, stateDir, events, progressTail, now, workerE
|
|
|
587
609
|
return {
|
|
588
610
|
id: state.id,
|
|
589
611
|
stateDir,
|
|
612
|
+
...rootReason === void 0 ? {} : { reason: rootReason },
|
|
613
|
+
...workerEventTails.get(join(stateDir, ROOT_STREAM_FILE))?.activity,
|
|
590
614
|
status: state.status,
|
|
591
615
|
task: state.task,
|
|
592
616
|
workspaceDir: state.workspaceDir,
|
|
@@ -724,11 +748,11 @@ function parseSpend(value) {
|
|
|
724
748
|
}
|
|
725
749
|
function parseBudget(value) {
|
|
726
750
|
if (!isRecord(value)) return void 0;
|
|
727
|
-
const budget =
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
751
|
+
const budget = selectFields(value, [
|
|
752
|
+
"maxIterations",
|
|
753
|
+
"maxTokens",
|
|
754
|
+
"maxUsd"
|
|
755
|
+
], (entry) => typeof entry === "number");
|
|
732
756
|
return Object.keys(budget).length ? budget : void 0;
|
|
733
757
|
}
|
|
734
758
|
function describeVerdict(value) {
|
|
@@ -750,11 +774,7 @@ function percentile(sorted, p) {
|
|
|
750
774
|
if (sorted.length === 0) return 0;
|
|
751
775
|
return sorted[Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * p) - 1))] ?? 0;
|
|
752
776
|
}
|
|
753
|
-
/**
|
|
754
|
-
* Read one source file under a run directory. An absent file is not a defect on its own — the
|
|
755
|
-
* writer may not have reached it yet — so it reads as `undefined` with no diagnostic; only a
|
|
756
|
-
* failed read raises one. `path` stays run-relative and never carries file contents.
|
|
757
|
-
*/
|
|
777
|
+
/** Missing files can predate their writer; failed reads report a path-only diagnostic. */
|
|
758
778
|
function readSourceFile(file, source, path) {
|
|
759
779
|
if (!existsSync(file)) return {
|
|
760
780
|
value: void 0,
|
|
@@ -780,37 +800,33 @@ function tailLines(raw, maxLines) {
|
|
|
780
800
|
if (raw === void 0) return [];
|
|
781
801
|
return raw.trim().split("\n").filter(Boolean).slice(-maxLines);
|
|
782
802
|
}
|
|
783
|
-
function readWorkerEventTails(dir
|
|
803
|
+
function readWorkerEventTails(dir) {
|
|
784
804
|
const out = /* @__PURE__ */ new Map();
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
805
|
+
const diagnostics = [];
|
|
806
|
+
const read = (path, key = join(dir, path)) => {
|
|
807
|
+
try {
|
|
808
|
+
if (existsSync(join(dir, path))) out.set(key, readRecordedEvents(join(dir, path)));
|
|
809
|
+
} catch {
|
|
810
|
+
diagnostics.push({
|
|
811
|
+
source: "worker-tail",
|
|
812
|
+
path,
|
|
813
|
+
reason: "unreadable"
|
|
814
|
+
});
|
|
815
|
+
}
|
|
788
816
|
};
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
diagnostics
|
|
817
|
+
read(ROOT_STREAM_FILE);
|
|
818
|
+
for (const folder of ["workers", "streams"]) {
|
|
819
|
+
if (!existsSync(join(dir, folder))) continue;
|
|
820
|
+
try {
|
|
821
|
+
for (const entry of readdirSync(join(dir, folder))) if (folder === "workers" && entry.endsWith(".ndjson") && !entry.endsWith(".inbox.ndjson")) read(join(folder, entry), entry.slice(0, -7));
|
|
822
|
+
else if (folder === "streams" && entry.endsWith(".jsonl")) read(join(folder, entry));
|
|
823
|
+
} catch {
|
|
824
|
+
diagnostics.push({
|
|
797
825
|
source: "worker-tail",
|
|
798
|
-
path:
|
|
826
|
+
path: folder,
|
|
799
827
|
reason: "unreadable"
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
const diagnostics = [];
|
|
804
|
-
for (const entry of entries) {
|
|
805
|
-
if (!entry.endsWith(".ndjson") || entry.endsWith(".inbox.ndjson")) continue;
|
|
806
|
-
const file = join(dir, entry);
|
|
807
|
-
const read = readSourceFile(file, "worker-tail", join(workersDir, entry));
|
|
808
|
-
diagnostics.push(...read.diagnostics);
|
|
809
|
-
const lines = tailLines(read.value, maxLines);
|
|
810
|
-
out.set(entry.slice(0, -7), {
|
|
811
|
-
file,
|
|
812
|
-
lines
|
|
813
|
-
});
|
|
828
|
+
});
|
|
829
|
+
}
|
|
814
830
|
}
|
|
815
831
|
return {
|
|
816
832
|
value: out,
|
|
@@ -844,13 +860,12 @@ function readSupervisorState(dir) {
|
|
|
844
860
|
}
|
|
845
861
|
}
|
|
846
862
|
function isSupervisorState(value) {
|
|
847
|
-
return isRecord(value) &&
|
|
848
|
-
id
|
|
849
|
-
status
|
|
850
|
-
task
|
|
851
|
-
workspaceDir
|
|
852
|
-
|
|
853
|
-
}).every(([key, type]) => typeof value[key] === type);
|
|
863
|
+
return isRecord(value) && [
|
|
864
|
+
"id",
|
|
865
|
+
"status",
|
|
866
|
+
"task",
|
|
867
|
+
"workspaceDir"
|
|
868
|
+
].every((key) => typeof value[key] === "string") && typeof value.budget === "number";
|
|
854
869
|
}
|
|
855
870
|
function isSpawnEvent(value) {
|
|
856
871
|
return isRecord(value) && typeof value.id === "string" && typeof value.kind === "string" && [
|
|
@@ -861,9 +876,6 @@ function isSpawnEvent(value) {
|
|
|
861
876
|
"progress"
|
|
862
877
|
].includes(value.kind);
|
|
863
878
|
}
|
|
864
|
-
function isRecord(value) {
|
|
865
|
-
return typeof value === "object" && value !== null;
|
|
866
|
-
}
|
|
867
879
|
function isDirectory(path) {
|
|
868
880
|
try {
|
|
869
881
|
return statSync(path).isDirectory();
|
|
@@ -1195,4 +1207,4 @@ async function loadRunsRoot(root) {
|
|
|
1195
1207
|
}
|
|
1196
1208
|
export { numberValue as a, totalSpend as c, loadTopSnapshot as i, serveTopWeb as n, parseSpend as o, aggregateSnapshot as r, parseTime as s, runTopWeb as t };
|
|
1197
1209
|
|
|
1198
|
-
//# sourceMappingURL=top-web-
|
|
1210
|
+
//# sourceMappingURL=top-web-CUDlOKu0.js.map
|