@dougbots/avenor-core 0.3.7 → 0.4.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/index.d.ts +205 -5
- package/dist/index.js +1209 -146
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,6 +7,22 @@ import * as os from "node:os";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import * as crypto from "node:crypto";
|
|
9
9
|
//#region src/client.ts
|
|
10
|
+
function normalizeSubscribeOptions(options) {
|
|
11
|
+
if (!options) return void 0;
|
|
12
|
+
const normalized = {};
|
|
13
|
+
if (options.runtime_id) normalized.runtime_id = options.runtime_id;
|
|
14
|
+
if (options.replay !== void 0) normalized.replay = options.replay;
|
|
15
|
+
if (options.limit !== void 0) normalized.limit = Math.max(1, Math.min(256, Math.trunc(options.limit)));
|
|
16
|
+
if (options.after_seq !== void 0) normalized.after_seq = Math.max(0, Math.trunc(options.after_seq));
|
|
17
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
18
|
+
}
|
|
19
|
+
function subscribeOptionsKey(options) {
|
|
20
|
+
return JSON.stringify(normalizeSubscribeOptions(options) ?? {});
|
|
21
|
+
}
|
|
22
|
+
function removeResolver(resolvers, target) {
|
|
23
|
+
const index = resolvers.indexOf(target);
|
|
24
|
+
if (index >= 0) resolvers.splice(index, 1);
|
|
25
|
+
}
|
|
10
26
|
var Client = class {
|
|
11
27
|
socket;
|
|
12
28
|
rl;
|
|
@@ -19,6 +35,7 @@ var Client = class {
|
|
|
19
35
|
eventDone = false;
|
|
20
36
|
dropped = 0;
|
|
21
37
|
subscribed = false;
|
|
38
|
+
subscriptionKey = subscribeOptionsKey();
|
|
22
39
|
constructor(socket, opts) {
|
|
23
40
|
this.socket = socket;
|
|
24
41
|
this.rl = readline.createInterface({
|
|
@@ -59,11 +76,10 @@ var Client = class {
|
|
|
59
76
|
if (this.dropped > 0) {
|
|
60
77
|
const lag = this.dropped;
|
|
61
78
|
this.dropped = 0;
|
|
62
|
-
|
|
79
|
+
this.pushEvent({
|
|
63
80
|
event: "client.lagged",
|
|
64
81
|
dropped_count: lag
|
|
65
|
-
};
|
|
66
|
-
this.pushEvent(lagEvent);
|
|
82
|
+
});
|
|
67
83
|
}
|
|
68
84
|
this.pushEvent(ev);
|
|
69
85
|
}
|
|
@@ -75,19 +91,28 @@ var Client = class {
|
|
|
75
91
|
}
|
|
76
92
|
this.pending.clear();
|
|
77
93
|
this.eventDone = true;
|
|
78
|
-
for (const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
for (const resolver of this.eventResolvers) {
|
|
95
|
+
resolver.active = false;
|
|
96
|
+
resolver.resolve({
|
|
97
|
+
value: void 0,
|
|
98
|
+
done: true
|
|
99
|
+
});
|
|
100
|
+
}
|
|
82
101
|
this.eventResolvers = [];
|
|
83
102
|
});
|
|
84
103
|
}
|
|
85
104
|
pushEvent(ev) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
105
|
+
while (this.eventResolvers.length > 0) {
|
|
106
|
+
const resolver = this.eventResolvers.shift();
|
|
107
|
+
if (!resolver.active) continue;
|
|
108
|
+
resolver.active = false;
|
|
109
|
+
resolver.resolve({
|
|
110
|
+
value: ev,
|
|
111
|
+
done: false
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (this.eventQueue.length < 256) this.eventQueue.push(ev);
|
|
91
116
|
else this.dropped++;
|
|
92
117
|
}
|
|
93
118
|
async call(method, params) {
|
|
@@ -119,25 +144,90 @@ var Client = class {
|
|
|
119
144
|
});
|
|
120
145
|
});
|
|
121
146
|
}
|
|
122
|
-
async subscribe() {
|
|
123
|
-
|
|
147
|
+
async subscribe(options) {
|
|
148
|
+
const key = subscribeOptionsKey(options);
|
|
149
|
+
if (this.subscribed) {
|
|
150
|
+
if (key !== subscribeOptionsKey() && key !== this.subscriptionKey) throw new Error("client already subscribed with different options");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
124
153
|
this.subscribed = true;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
154
|
+
this.subscriptionKey = key;
|
|
155
|
+
await this.call("subscribe", normalizeSubscribeOptions(options));
|
|
156
|
+
}
|
|
157
|
+
events(options) {
|
|
158
|
+
let active = true;
|
|
159
|
+
let pendingResolver = null;
|
|
160
|
+
let subscribedPromise = null;
|
|
161
|
+
const ensureSubscribed = async () => {
|
|
162
|
+
if (!subscribedPromise) subscribedPromise = this.subscribe(options);
|
|
163
|
+
await subscribedPromise;
|
|
164
|
+
};
|
|
165
|
+
const next = async () => {
|
|
166
|
+
if (!active || this.eventDone) return {
|
|
167
|
+
value: void 0,
|
|
168
|
+
done: true
|
|
169
|
+
};
|
|
170
|
+
try {
|
|
171
|
+
await ensureSubscribed();
|
|
172
|
+
} catch (error) {
|
|
173
|
+
active = false;
|
|
174
|
+
throw error;
|
|
134
175
|
}
|
|
135
|
-
|
|
136
|
-
|
|
176
|
+
if (!active || this.eventDone) return {
|
|
177
|
+
value: void 0,
|
|
178
|
+
done: true
|
|
179
|
+
};
|
|
180
|
+
if (this.eventQueue.length > 0) return {
|
|
181
|
+
value: this.eventQueue.shift(),
|
|
182
|
+
done: false
|
|
183
|
+
};
|
|
184
|
+
return new Promise((resolve) => {
|
|
185
|
+
pendingResolver = {
|
|
186
|
+
resolve,
|
|
187
|
+
active: true
|
|
188
|
+
};
|
|
189
|
+
this.eventResolvers.push(pendingResolver);
|
|
137
190
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
191
|
+
};
|
|
192
|
+
const stop = async () => {
|
|
193
|
+
if (!active) return {
|
|
194
|
+
value: void 0,
|
|
195
|
+
done: true
|
|
196
|
+
};
|
|
197
|
+
active = false;
|
|
198
|
+
if (pendingResolver) {
|
|
199
|
+
pendingResolver.active = false;
|
|
200
|
+
removeResolver(this.eventResolvers, pendingResolver);
|
|
201
|
+
pendingResolver.resolve({
|
|
202
|
+
value: void 0,
|
|
203
|
+
done: true
|
|
204
|
+
});
|
|
205
|
+
pendingResolver = null;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
value: void 0,
|
|
209
|
+
done: true
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
next,
|
|
214
|
+
return: stop,
|
|
215
|
+
async throw(error) {
|
|
216
|
+
await stop();
|
|
217
|
+
throw error;
|
|
218
|
+
},
|
|
219
|
+
[Symbol.asyncIterator]() {
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async history(options) {
|
|
225
|
+
const params = {
|
|
226
|
+
runtime_id: options.runtime_id,
|
|
227
|
+
limit: options.limit !== void 0 ? Math.max(1, Math.min(256, Math.trunc(options.limit))) : void 0,
|
|
228
|
+
after_seq: options.after_seq !== void 0 ? Math.max(0, Math.trunc(options.after_seq)) : void 0
|
|
229
|
+
};
|
|
230
|
+
return this.call("history", params);
|
|
141
231
|
}
|
|
142
232
|
async status(runtimeId) {
|
|
143
233
|
const params = runtimeId ? { runtime_id: runtimeId } : void 0;
|
|
@@ -190,6 +280,773 @@ function dial(socketPath, opts) {
|
|
|
190
280
|
});
|
|
191
281
|
}
|
|
192
282
|
//#endregion
|
|
283
|
+
//#region src/value-fields.ts
|
|
284
|
+
function asRecord(value) {
|
|
285
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
286
|
+
return value;
|
|
287
|
+
}
|
|
288
|
+
function stringField(record, ...keys) {
|
|
289
|
+
if (!record) return void 0;
|
|
290
|
+
for (const key of keys) {
|
|
291
|
+
const value = record[key];
|
|
292
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function numberField(record, ...keys) {
|
|
296
|
+
if (!record) return void 0;
|
|
297
|
+
for (const key of keys) {
|
|
298
|
+
const value = record[key];
|
|
299
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
300
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
301
|
+
const parsed = Number(value);
|
|
302
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function stringArrayField(record, ...keys) {
|
|
307
|
+
if (!record) return void 0;
|
|
308
|
+
for (const key of keys) {
|
|
309
|
+
const value = record[key];
|
|
310
|
+
if (!Array.isArray(value)) continue;
|
|
311
|
+
const items = value.filter((item) => typeof item === "string" && item.length > 0);
|
|
312
|
+
if (items.length > 0) return items;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region src/run-events.ts
|
|
317
|
+
const MAX_TEXT_EXTRACTION_DEPTH = 12;
|
|
318
|
+
const MAX_TEXT_EXTRACTION_ITEMS = 64;
|
|
319
|
+
function extractEventText(value, depth = 0) {
|
|
320
|
+
if (depth >= MAX_TEXT_EXTRACTION_DEPTH) return void 0;
|
|
321
|
+
if (typeof value === "string") return value.length > 0 ? value : void 0;
|
|
322
|
+
if (Array.isArray(value)) {
|
|
323
|
+
const parts = [];
|
|
324
|
+
for (const item of value.slice(0, MAX_TEXT_EXTRACTION_ITEMS)) {
|
|
325
|
+
const text = extractEventText(item, depth + 1);
|
|
326
|
+
if (text) parts.push(text);
|
|
327
|
+
}
|
|
328
|
+
return parts.length > 0 ? parts.join("") : void 0;
|
|
329
|
+
}
|
|
330
|
+
const record = asRecord(value);
|
|
331
|
+
if (!record) return void 0;
|
|
332
|
+
for (const key of [
|
|
333
|
+
"delta",
|
|
334
|
+
"text",
|
|
335
|
+
"content",
|
|
336
|
+
"partial",
|
|
337
|
+
"message",
|
|
338
|
+
"assistantMessage",
|
|
339
|
+
"assistantMessageEvent",
|
|
340
|
+
"data",
|
|
341
|
+
"result",
|
|
342
|
+
"output"
|
|
343
|
+
]) {
|
|
344
|
+
const text = extractEventText(record[key], depth + 1);
|
|
345
|
+
if (text) return text;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function normalizePermissionOptions(value) {
|
|
349
|
+
if (!Array.isArray(value)) return [];
|
|
350
|
+
const options = [];
|
|
351
|
+
for (const item of value) {
|
|
352
|
+
const record = asRecord(item);
|
|
353
|
+
if (!record) continue;
|
|
354
|
+
const option_id = stringField(record, "option_id", "optionId") ?? "";
|
|
355
|
+
const label = stringField(record, "label") ?? option_id;
|
|
356
|
+
const kind = stringField(record, "kind") ?? "";
|
|
357
|
+
if (!option_id && !label && !kind) continue;
|
|
358
|
+
options.push({
|
|
359
|
+
option_id,
|
|
360
|
+
label,
|
|
361
|
+
kind
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
return options;
|
|
365
|
+
}
|
|
366
|
+
function normalizePermission$1(record) {
|
|
367
|
+
const request_id = stringField(record, "request_id", "requestID", "id");
|
|
368
|
+
const description = stringField(record, "description", "title");
|
|
369
|
+
const question = stringField(record, "question", "message");
|
|
370
|
+
const tool = stringField(record, "tool");
|
|
371
|
+
const options = normalizePermissionOptions(record.options);
|
|
372
|
+
const kind = stringField(record, "kind", "status");
|
|
373
|
+
if (!request_id && !description && !question && !tool && options.length === 0 && !kind) return;
|
|
374
|
+
return {
|
|
375
|
+
request_id,
|
|
376
|
+
description,
|
|
377
|
+
question,
|
|
378
|
+
tool,
|
|
379
|
+
options,
|
|
380
|
+
kind
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function normalizeTool(record, fallbackStatus) {
|
|
384
|
+
const title = stringField(record, "title", "toolName", "name");
|
|
385
|
+
const kind = stringField(record, "kind", "toolKind");
|
|
386
|
+
const id = stringField(record, "tool_call_id", "toolCallId", "call_id", "callId", "id", "message_id", "messageID", "part_id", "partID");
|
|
387
|
+
const status = stringField(record, "status", "state") ?? fallbackStatus;
|
|
388
|
+
const preview = extractEventText(record.preview) ?? extractEventText(record.arguments) ?? extractEventText(record.args) ?? extractEventText(record.input) ?? extractEventText(record.result) ?? extractEventText(record.output) ?? extractEventText(record.delta);
|
|
389
|
+
if (!id && !title && !kind && !status && !preview) return;
|
|
390
|
+
return {
|
|
391
|
+
key: id ?? title ?? kind ?? "tool",
|
|
392
|
+
id,
|
|
393
|
+
title,
|
|
394
|
+
kind,
|
|
395
|
+
status,
|
|
396
|
+
preview
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function semanticFingerprint(event, delta, tool, permission, stopReason) {
|
|
400
|
+
if (delta) return `${event}:${delta.length}:${delta.slice(0, 512)}`;
|
|
401
|
+
if (tool) return `${event}:${tool.id ?? tool.title ?? tool.kind ?? tool.key}:${tool.status ?? ""}:${tool.preview ?? ""}`;
|
|
402
|
+
if (permission) return `${event}:${permission.request_id ?? permission.question ?? permission.description ?? permission.tool ?? ""}:${permission.kind ?? ""}`;
|
|
403
|
+
if (stopReason) return `${event}:${stopReason}`;
|
|
404
|
+
}
|
|
405
|
+
function normalizeRunEvent(input) {
|
|
406
|
+
const raw = asRecord(input);
|
|
407
|
+
if (!raw) return null;
|
|
408
|
+
const source_event = stringField(raw, "event", "type");
|
|
409
|
+
if (!source_event) return null;
|
|
410
|
+
const session_id = stringField(raw, "session_id", "sessionID", "sessionId");
|
|
411
|
+
const runtime_id = stringField(raw, "runtime_id", "runtimeID");
|
|
412
|
+
const run_id = stringField(raw, "run_id", "runId");
|
|
413
|
+
const run_label = stringField(raw, "run_label", "runLabel", "label");
|
|
414
|
+
const seq = numberField(raw, "seq");
|
|
415
|
+
const ts = numberField(raw, "ts", "timestamp");
|
|
416
|
+
const phase = stringField(raw, "phase", "status");
|
|
417
|
+
const phase_label = stringField(raw, "phase_label", "label");
|
|
418
|
+
const stop_reason = stringField(raw, "stop_reason", "stopReason");
|
|
419
|
+
const final_output = stringField(raw, "final_output", "finalOutput");
|
|
420
|
+
const backend = stringField(raw, "backend");
|
|
421
|
+
const agent = stringField(raw, "agent");
|
|
422
|
+
const model = stringField(raw, "model");
|
|
423
|
+
const dir = stringField(raw, "dir");
|
|
424
|
+
const parent_id = stringField(raw, "parent_id", "parentId");
|
|
425
|
+
const children = stringArrayField(raw, "children");
|
|
426
|
+
const event_path = stringField(raw, "event_path", "on_event");
|
|
427
|
+
const usage = asRecord(raw.usage) ?? void 0;
|
|
428
|
+
let event = source_event;
|
|
429
|
+
let alias = false;
|
|
430
|
+
let delta;
|
|
431
|
+
let chunk_role;
|
|
432
|
+
let tool;
|
|
433
|
+
let permission;
|
|
434
|
+
switch (source_event) {
|
|
435
|
+
case "agent.message_chunk":
|
|
436
|
+
chunk_role = "assistant";
|
|
437
|
+
delta = extractEventText(raw);
|
|
438
|
+
break;
|
|
439
|
+
case "agent.thought_chunk":
|
|
440
|
+
chunk_role = "thought";
|
|
441
|
+
delta = extractEventText(raw);
|
|
442
|
+
break;
|
|
443
|
+
case "user.message_chunk":
|
|
444
|
+
chunk_role = "user";
|
|
445
|
+
delta = extractEventText(raw);
|
|
446
|
+
break;
|
|
447
|
+
case "tool.call":
|
|
448
|
+
tool = normalizeTool(raw, "running");
|
|
449
|
+
break;
|
|
450
|
+
case "tool.call_update":
|
|
451
|
+
tool = normalizeTool(raw);
|
|
452
|
+
break;
|
|
453
|
+
case "permission.request":
|
|
454
|
+
case "permission.response":
|
|
455
|
+
permission = normalizePermission$1(raw);
|
|
456
|
+
break;
|
|
457
|
+
case "avenor.message.delta":
|
|
458
|
+
event = "agent.message_chunk";
|
|
459
|
+
alias = true;
|
|
460
|
+
chunk_role = "assistant";
|
|
461
|
+
delta = extractEventText(raw);
|
|
462
|
+
break;
|
|
463
|
+
case "avenor.message.update": {
|
|
464
|
+
alias = true;
|
|
465
|
+
const assistantMessageEvent = asRecord(raw.assistantMessageEvent) ?? raw;
|
|
466
|
+
const assistantType = stringField(assistantMessageEvent, "type");
|
|
467
|
+
if (assistantType === "thinking_delta") {
|
|
468
|
+
event = "agent.thought_chunk";
|
|
469
|
+
chunk_role = "thought";
|
|
470
|
+
delta = extractEventText(assistantMessageEvent);
|
|
471
|
+
} else if (assistantType === "toolcall_start") {
|
|
472
|
+
event = "tool.call";
|
|
473
|
+
tool = normalizeTool(assistantMessageEvent, "running");
|
|
474
|
+
} else if (assistantType === "toolcall_delta" || assistantType === "toolcall_update" || assistantType === "toolcall_end") {
|
|
475
|
+
event = "tool.call_update";
|
|
476
|
+
tool = normalizeTool(assistantMessageEvent, assistantType === "toolcall_end" ? "completed" : "running");
|
|
477
|
+
} else {
|
|
478
|
+
event = "agent.message_chunk";
|
|
479
|
+
chunk_role = "assistant";
|
|
480
|
+
delta = extractEventText(assistantMessageEvent);
|
|
481
|
+
}
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
case "avenor.tool.start":
|
|
485
|
+
event = "tool.call";
|
|
486
|
+
alias = true;
|
|
487
|
+
tool = normalizeTool(raw, "running");
|
|
488
|
+
break;
|
|
489
|
+
case "avenor.tool.update":
|
|
490
|
+
event = "tool.call_update";
|
|
491
|
+
alias = true;
|
|
492
|
+
tool = normalizeTool(raw);
|
|
493
|
+
break;
|
|
494
|
+
case "avenor.tool.end":
|
|
495
|
+
event = "tool.call_update";
|
|
496
|
+
alias = true;
|
|
497
|
+
tool = normalizeTool(raw, "completed");
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
event,
|
|
502
|
+
source_event,
|
|
503
|
+
raw: { ...raw },
|
|
504
|
+
runtime_id,
|
|
505
|
+
session_id,
|
|
506
|
+
run_id,
|
|
507
|
+
run_label,
|
|
508
|
+
seq,
|
|
509
|
+
ts,
|
|
510
|
+
phase,
|
|
511
|
+
phase_label,
|
|
512
|
+
stop_reason,
|
|
513
|
+
final_output,
|
|
514
|
+
usage,
|
|
515
|
+
backend,
|
|
516
|
+
agent,
|
|
517
|
+
model,
|
|
518
|
+
dir,
|
|
519
|
+
parent_id,
|
|
520
|
+
children,
|
|
521
|
+
event_path,
|
|
522
|
+
delta,
|
|
523
|
+
chunk_role,
|
|
524
|
+
tool,
|
|
525
|
+
permission,
|
|
526
|
+
alias,
|
|
527
|
+
semantic_fingerprint: semanticFingerprint(event, delta, tool, permission, stop_reason)
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/run-reducer.ts
|
|
532
|
+
const DEFAULT_RUN_REDUCER_LIMITS = {
|
|
533
|
+
transcript_entries: 64,
|
|
534
|
+
text_chars: 4e3,
|
|
535
|
+
tool_preview_chars: 1e3,
|
|
536
|
+
tools: 24,
|
|
537
|
+
permissions: 16,
|
|
538
|
+
legacy_keys: 128
|
|
539
|
+
};
|
|
540
|
+
function clipText(text, limit) {
|
|
541
|
+
if (!text) return void 0;
|
|
542
|
+
if (text.length <= limit) return text;
|
|
543
|
+
return text.slice(text.length - limit);
|
|
544
|
+
}
|
|
545
|
+
function appendText(current, delta, limit) {
|
|
546
|
+
if (!delta) return current;
|
|
547
|
+
return clipText(`${current}${delta}`, limit) ?? current;
|
|
548
|
+
}
|
|
549
|
+
function pushBounded(items, item, limit) {
|
|
550
|
+
const next = [...items, item];
|
|
551
|
+
if (next.length <= limit) return next;
|
|
552
|
+
return next.slice(next.length - limit);
|
|
553
|
+
}
|
|
554
|
+
function toLegacyKey(event) {
|
|
555
|
+
return JSON.stringify([
|
|
556
|
+
event.runtime_id ?? event.session_id ?? "",
|
|
557
|
+
event.event,
|
|
558
|
+
event.ts ?? "",
|
|
559
|
+
event.delta ?? "",
|
|
560
|
+
event.tool?.id ?? event.tool?.title ?? event.tool?.status ?? "",
|
|
561
|
+
event.permission?.request_id ?? event.permission?.question ?? "",
|
|
562
|
+
event.stop_reason ?? ""
|
|
563
|
+
]);
|
|
564
|
+
}
|
|
565
|
+
function normalizeLimits(limits) {
|
|
566
|
+
return {
|
|
567
|
+
transcript_entries: Math.max(1, Math.trunc(limits?.transcript_entries ?? DEFAULT_RUN_REDUCER_LIMITS.transcript_entries)),
|
|
568
|
+
text_chars: Math.max(1, Math.trunc(limits?.text_chars ?? DEFAULT_RUN_REDUCER_LIMITS.text_chars)),
|
|
569
|
+
tool_preview_chars: Math.max(1, Math.trunc(limits?.tool_preview_chars ?? DEFAULT_RUN_REDUCER_LIMITS.tool_preview_chars)),
|
|
570
|
+
tools: Math.max(1, Math.trunc(limits?.tools ?? DEFAULT_RUN_REDUCER_LIMITS.tools)),
|
|
571
|
+
permissions: Math.max(1, Math.trunc(limits?.permissions ?? DEFAULT_RUN_REDUCER_LIMITS.permissions)),
|
|
572
|
+
legacy_keys: Math.max(1, Math.trunc(limits?.legacy_keys ?? DEFAULT_RUN_REDUCER_LIMITS.legacy_keys))
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function createRunSnapshot(limits) {
|
|
576
|
+
return {
|
|
577
|
+
identity: {},
|
|
578
|
+
limits: normalizeLimits(limits),
|
|
579
|
+
ended: false,
|
|
580
|
+
assistant_text: "",
|
|
581
|
+
thought_text: "",
|
|
582
|
+
user_text: "",
|
|
583
|
+
transcript: [],
|
|
584
|
+
tools: [],
|
|
585
|
+
live_tools: [],
|
|
586
|
+
permissions: [],
|
|
587
|
+
_seen_seq_by_scope: {},
|
|
588
|
+
_recent_legacy_keys: []
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function transcriptText(entry, limits) {
|
|
592
|
+
if (!entry.text) return entry;
|
|
593
|
+
return {
|
|
594
|
+
...entry,
|
|
595
|
+
text: clipText(entry.text, limits.text_chars)
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function appendTranscript(snapshot, entry) {
|
|
599
|
+
const nextEntry = transcriptText(entry, snapshot.limits);
|
|
600
|
+
const last = snapshot.transcript[snapshot.transcript.length - 1];
|
|
601
|
+
if (last && last.kind === nextEntry.kind && nextEntry.text && last.text && last.event === nextEntry.event) {
|
|
602
|
+
const merged = {
|
|
603
|
+
...last,
|
|
604
|
+
text: clipText(`${last.text}${nextEntry.text}`, snapshot.limits.text_chars),
|
|
605
|
+
seq: nextEntry.seq ?? last.seq,
|
|
606
|
+
ts: nextEntry.ts ?? last.ts
|
|
607
|
+
};
|
|
608
|
+
return [...snapshot.transcript.slice(0, -1), merged];
|
|
609
|
+
}
|
|
610
|
+
return pushBounded(snapshot.transcript, nextEntry, snapshot.limits.transcript_entries);
|
|
611
|
+
}
|
|
612
|
+
function mergeIdentity(snapshot, event) {
|
|
613
|
+
return {
|
|
614
|
+
runtime_id: event.runtime_id ?? snapshot.identity.runtime_id,
|
|
615
|
+
session_id: event.session_id ?? snapshot.identity.session_id,
|
|
616
|
+
run_id: event.run_id ?? snapshot.identity.run_id,
|
|
617
|
+
run_label: event.run_label ?? snapshot.identity.run_label,
|
|
618
|
+
backend: event.backend ?? snapshot.identity.backend,
|
|
619
|
+
agent: event.agent ?? snapshot.identity.agent,
|
|
620
|
+
model: event.model ?? snapshot.identity.model,
|
|
621
|
+
dir: event.dir ?? snapshot.identity.dir,
|
|
622
|
+
parent_id: event.parent_id ?? snapshot.identity.parent_id,
|
|
623
|
+
children: event.children ?? snapshot.identity.children,
|
|
624
|
+
event_path: event.event_path ?? snapshot.identity.event_path
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
function withToolPreview(tool, limit) {
|
|
628
|
+
if (!tool) return tool;
|
|
629
|
+
return {
|
|
630
|
+
...tool,
|
|
631
|
+
preview: clipText(tool.preview, limit)
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function mergeToolState(snapshot, event, live) {
|
|
635
|
+
const tool = withToolPreview(event.tool, snapshot.limits.tool_preview_chars);
|
|
636
|
+
if (!tool) return snapshot.tools;
|
|
637
|
+
const tools = snapshot.tools.map((item) => ({ ...item }));
|
|
638
|
+
const index = tools.findIndex((item) => item.key === tool.key);
|
|
639
|
+
const existing = index >= 0 ? tools[index] : void 0;
|
|
640
|
+
const next = {
|
|
641
|
+
key: tool.key,
|
|
642
|
+
id: tool.id ?? existing?.id,
|
|
643
|
+
title: tool.title ?? existing?.title,
|
|
644
|
+
kind: tool.kind ?? existing?.kind,
|
|
645
|
+
status: tool.status ?? existing?.status,
|
|
646
|
+
preview: clipText(tool.preview ?? existing?.preview, snapshot.limits.tool_preview_chars),
|
|
647
|
+
started_at: existing?.started_at ?? event.ts,
|
|
648
|
+
updated_at: event.ts ?? existing?.updated_at,
|
|
649
|
+
ended_at: live ? existing?.ended_at : event.ts ?? existing?.ended_at,
|
|
650
|
+
live
|
|
651
|
+
};
|
|
652
|
+
if (index >= 0) tools[index] = next;
|
|
653
|
+
else tools.push(next);
|
|
654
|
+
return tools.length <= snapshot.limits.tools ? tools : tools.slice(tools.length - snapshot.limits.tools);
|
|
655
|
+
}
|
|
656
|
+
function liveTools(tools) {
|
|
657
|
+
return tools.filter((tool) => tool.live);
|
|
658
|
+
}
|
|
659
|
+
function mergePermissionHistory(snapshot, permission, resolved = false) {
|
|
660
|
+
if (!permission) return {
|
|
661
|
+
pending_permission: resolved ? void 0 : snapshot.pending_permission,
|
|
662
|
+
permissions: snapshot.permissions
|
|
663
|
+
};
|
|
664
|
+
const permissions = snapshot.permissions.map((item) => ({ ...item }));
|
|
665
|
+
const index = permission.request_id ? permissions.findIndex((item) => item.request_id === permission.request_id) : -1;
|
|
666
|
+
const entry = {
|
|
667
|
+
...permission,
|
|
668
|
+
kind: resolved ? permission.kind ?? "resolved" : permission.kind,
|
|
669
|
+
options: permission.options.map((option) => ({ ...option }))
|
|
670
|
+
};
|
|
671
|
+
if (index >= 0) permissions[index] = {
|
|
672
|
+
...permissions[index],
|
|
673
|
+
...entry
|
|
674
|
+
};
|
|
675
|
+
else permissions.push(entry);
|
|
676
|
+
const bounded = permissions.length <= snapshot.limits.permissions ? permissions : permissions.slice(permissions.length - snapshot.limits.permissions);
|
|
677
|
+
return {
|
|
678
|
+
pending_permission: resolved ? void 0 : entry,
|
|
679
|
+
permissions: bounded
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
function shouldSkipSemanticUpdate(snapshot, event) {
|
|
683
|
+
if (!event.alias || !event.semantic_fingerprint) return false;
|
|
684
|
+
return snapshot._last_semantic_fingerprint === event.semantic_fingerprint && snapshot._last_source_event !== event.source_event;
|
|
685
|
+
}
|
|
686
|
+
function updateDedupeState(snapshot, event) {
|
|
687
|
+
const scope = event.runtime_id ?? event.session_id ?? "global";
|
|
688
|
+
if (event.seq !== void 0) {
|
|
689
|
+
const seen = snapshot._seen_seq_by_scope[scope] ?? 0;
|
|
690
|
+
if (event.seq <= seen) return null;
|
|
691
|
+
return {
|
|
692
|
+
_seen_seq_by_scope: {
|
|
693
|
+
...snapshot._seen_seq_by_scope,
|
|
694
|
+
[scope]: event.seq
|
|
695
|
+
},
|
|
696
|
+
_recent_legacy_keys: snapshot._recent_legacy_keys
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
const key = toLegacyKey(event);
|
|
700
|
+
if (snapshot._recent_legacy_keys.includes(key)) return null;
|
|
701
|
+
const nextKeys = [...snapshot._recent_legacy_keys, key];
|
|
702
|
+
return {
|
|
703
|
+
_seen_seq_by_scope: snapshot._seen_seq_by_scope,
|
|
704
|
+
_recent_legacy_keys: nextKeys.length <= snapshot.limits.legacy_keys ? nextKeys : nextKeys.slice(nextKeys.length - snapshot.limits.legacy_keys)
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
function reduceRunSnapshot(current, input, limits) {
|
|
708
|
+
const snapshot = limits ? {
|
|
709
|
+
...current,
|
|
710
|
+
limits: normalizeLimits({
|
|
711
|
+
...current.limits,
|
|
712
|
+
...limits
|
|
713
|
+
})
|
|
714
|
+
} : current;
|
|
715
|
+
const event = normalizeRunEvent(input);
|
|
716
|
+
if (!event) return snapshot;
|
|
717
|
+
const dedupe = updateDedupeState(snapshot, event);
|
|
718
|
+
if (!dedupe) return snapshot;
|
|
719
|
+
let next = {
|
|
720
|
+
...snapshot,
|
|
721
|
+
identity: mergeIdentity(snapshot, event),
|
|
722
|
+
latest_seq: event.seq ?? snapshot.latest_seq,
|
|
723
|
+
last_event: event.event,
|
|
724
|
+
last_ts: event.ts ?? snapshot.last_ts,
|
|
725
|
+
phase: event.event === "session.end" ? "done" : event.phase ?? snapshot.phase,
|
|
726
|
+
phase_label: event.phase_label ?? snapshot.phase_label,
|
|
727
|
+
stop_reason: event.stop_reason ?? snapshot.stop_reason,
|
|
728
|
+
usage: event.usage ? {
|
|
729
|
+
...snapshot.usage ?? {},
|
|
730
|
+
...event.usage
|
|
731
|
+
} : snapshot.usage,
|
|
732
|
+
final_output: clipText(event.final_output ?? snapshot.final_output, snapshot.limits.text_chars),
|
|
733
|
+
_seen_seq_by_scope: dedupe._seen_seq_by_scope,
|
|
734
|
+
_recent_legacy_keys: dedupe._recent_legacy_keys
|
|
735
|
+
};
|
|
736
|
+
if (event.event === "session.end") next.ended = true;
|
|
737
|
+
if (shouldSkipSemanticUpdate(next, event)) return {
|
|
738
|
+
...next,
|
|
739
|
+
_last_semantic_fingerprint: event.semantic_fingerprint ?? next._last_semantic_fingerprint,
|
|
740
|
+
_last_source_event: event.source_event
|
|
741
|
+
};
|
|
742
|
+
switch (event.event) {
|
|
743
|
+
case "agent.message_chunk": {
|
|
744
|
+
const text = clipText(event.delta, next.limits.text_chars);
|
|
745
|
+
next.assistant_text = appendText(next.assistant_text, text, next.limits.text_chars);
|
|
746
|
+
if (text) next.transcript = appendTranscript(next, {
|
|
747
|
+
kind: "assistant",
|
|
748
|
+
event: event.event,
|
|
749
|
+
source_event: event.source_event,
|
|
750
|
+
text,
|
|
751
|
+
seq: event.seq,
|
|
752
|
+
ts: event.ts
|
|
753
|
+
});
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
case "agent.thought_chunk": {
|
|
757
|
+
const text = clipText(event.delta, next.limits.text_chars);
|
|
758
|
+
next.thought_text = appendText(next.thought_text, text, next.limits.text_chars);
|
|
759
|
+
if (text) next.transcript = appendTranscript(next, {
|
|
760
|
+
kind: "thought",
|
|
761
|
+
event: event.event,
|
|
762
|
+
source_event: event.source_event,
|
|
763
|
+
text,
|
|
764
|
+
seq: event.seq,
|
|
765
|
+
ts: event.ts
|
|
766
|
+
});
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
case "user.message_chunk": {
|
|
770
|
+
const text = clipText(event.delta, next.limits.text_chars);
|
|
771
|
+
next.user_text = appendText(next.user_text, text, next.limits.text_chars);
|
|
772
|
+
if (text) next.transcript = appendTranscript(next, {
|
|
773
|
+
kind: "user",
|
|
774
|
+
event: event.event,
|
|
775
|
+
source_event: event.source_event,
|
|
776
|
+
text,
|
|
777
|
+
seq: event.seq,
|
|
778
|
+
ts: event.ts
|
|
779
|
+
});
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
782
|
+
case "tool.call":
|
|
783
|
+
next.tools = mergeToolState(next, event, true);
|
|
784
|
+
next.live_tools = liveTools(next.tools);
|
|
785
|
+
next.transcript = appendTranscript(next, {
|
|
786
|
+
kind: "tool",
|
|
787
|
+
event: event.event,
|
|
788
|
+
source_event: event.source_event,
|
|
789
|
+
title: event.tool?.title ?? event.tool?.kind,
|
|
790
|
+
status: event.tool?.status,
|
|
791
|
+
text: clipText(event.tool?.preview, next.limits.tool_preview_chars),
|
|
792
|
+
seq: event.seq,
|
|
793
|
+
ts: event.ts
|
|
794
|
+
});
|
|
795
|
+
break;
|
|
796
|
+
case "tool.call_update":
|
|
797
|
+
next.tools = mergeToolState(next, event, ![
|
|
798
|
+
"completed",
|
|
799
|
+
"complete",
|
|
800
|
+
"done",
|
|
801
|
+
"failed",
|
|
802
|
+
"cancelled",
|
|
803
|
+
"canceled"
|
|
804
|
+
].includes((event.tool?.status ?? "").toLowerCase()));
|
|
805
|
+
next.live_tools = liveTools(next.tools);
|
|
806
|
+
next.transcript = appendTranscript(next, {
|
|
807
|
+
kind: "tool",
|
|
808
|
+
event: event.event,
|
|
809
|
+
source_event: event.source_event,
|
|
810
|
+
title: event.tool?.title ?? event.tool?.kind,
|
|
811
|
+
status: event.tool?.status,
|
|
812
|
+
text: clipText(event.tool?.preview, next.limits.tool_preview_chars),
|
|
813
|
+
seq: event.seq,
|
|
814
|
+
ts: event.ts
|
|
815
|
+
});
|
|
816
|
+
break;
|
|
817
|
+
case "permission.request": {
|
|
818
|
+
const permission = mergePermissionHistory(next, event.permission);
|
|
819
|
+
next.pending_permission = permission.pending_permission;
|
|
820
|
+
next.permissions = permission.permissions;
|
|
821
|
+
next.phase = "waiting";
|
|
822
|
+
next.phase_label = event.permission?.question ?? event.permission?.description ?? event.permission?.tool ?? next.phase_label;
|
|
823
|
+
next.transcript = appendTranscript(next, {
|
|
824
|
+
kind: "permission",
|
|
825
|
+
event: event.event,
|
|
826
|
+
source_event: event.source_event,
|
|
827
|
+
request_id: event.permission?.request_id,
|
|
828
|
+
text: clipText(event.permission?.question ?? event.permission?.description ?? event.permission?.tool, next.limits.text_chars),
|
|
829
|
+
seq: event.seq,
|
|
830
|
+
ts: event.ts
|
|
831
|
+
});
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
case "permission.response": {
|
|
835
|
+
const permission = mergePermissionHistory(next, event.permission ?? next.pending_permission, true);
|
|
836
|
+
next.pending_permission = permission.pending_permission;
|
|
837
|
+
next.permissions = permission.permissions;
|
|
838
|
+
next.transcript = appendTranscript(next, {
|
|
839
|
+
kind: "permission",
|
|
840
|
+
event: event.event,
|
|
841
|
+
source_event: event.source_event,
|
|
842
|
+
request_id: event.permission?.request_id ?? next.pending_permission?.request_id,
|
|
843
|
+
text: clipText(event.permission?.kind ?? event.permission?.description, next.limits.text_chars),
|
|
844
|
+
seq: event.seq,
|
|
845
|
+
ts: event.ts
|
|
846
|
+
});
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
case "agent.status":
|
|
850
|
+
next.phase = event.phase ?? next.phase;
|
|
851
|
+
next.phase_label = event.phase_label ?? next.phase_label;
|
|
852
|
+
next.transcript = appendTranscript(next, {
|
|
853
|
+
kind: "status",
|
|
854
|
+
event: event.event,
|
|
855
|
+
source_event: event.source_event,
|
|
856
|
+
text: clipText(event.phase_label ?? event.phase, next.limits.text_chars),
|
|
857
|
+
seq: event.seq,
|
|
858
|
+
ts: event.ts
|
|
859
|
+
});
|
|
860
|
+
break;
|
|
861
|
+
case "session.end":
|
|
862
|
+
next.live_tools = [];
|
|
863
|
+
next.tools = next.tools.map((tool) => ({
|
|
864
|
+
...tool,
|
|
865
|
+
live: false
|
|
866
|
+
}));
|
|
867
|
+
if (!next.final_output) next.final_output = clipText(next.assistant_text, next.limits.text_chars);
|
|
868
|
+
next.pending_permission = void 0;
|
|
869
|
+
next.transcript = appendTranscript(next, {
|
|
870
|
+
kind: "session",
|
|
871
|
+
event: event.event,
|
|
872
|
+
source_event: event.source_event,
|
|
873
|
+
text: clipText(event.final_output ?? event.stop_reason, next.limits.text_chars),
|
|
874
|
+
status: event.stop_reason,
|
|
875
|
+
seq: event.seq,
|
|
876
|
+
ts: event.ts
|
|
877
|
+
});
|
|
878
|
+
break;
|
|
879
|
+
case "session.start":
|
|
880
|
+
next.ended = false;
|
|
881
|
+
next.transcript = appendTranscript(next, {
|
|
882
|
+
kind: "session",
|
|
883
|
+
event: event.event,
|
|
884
|
+
source_event: event.source_event,
|
|
885
|
+
text: clipText(event.backend, next.limits.text_chars),
|
|
886
|
+
seq: event.seq,
|
|
887
|
+
ts: event.ts
|
|
888
|
+
});
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
...next,
|
|
893
|
+
_last_semantic_fingerprint: event.semantic_fingerprint ?? next._last_semantic_fingerprint,
|
|
894
|
+
_last_source_event: event.source_event
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
var RunReducer = class {
|
|
898
|
+
state;
|
|
899
|
+
constructor(limits, initial) {
|
|
900
|
+
const base = createRunSnapshot(limits);
|
|
901
|
+
if (!initial) {
|
|
902
|
+
this.state = base;
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
const identity = {
|
|
906
|
+
...base.identity,
|
|
907
|
+
...initial.identity ?? {}
|
|
908
|
+
};
|
|
909
|
+
const scope = identity.runtime_id ?? identity.session_id ?? "global";
|
|
910
|
+
const seenSeq = initial.latest_seq === void 0 ? {} : { [scope]: initial.latest_seq };
|
|
911
|
+
this.state = {
|
|
912
|
+
...base,
|
|
913
|
+
...initial,
|
|
914
|
+
identity,
|
|
915
|
+
limits: base.limits,
|
|
916
|
+
ended: initial.ended ?? base.ended,
|
|
917
|
+
assistant_text: initial.assistant_text ?? base.assistant_text,
|
|
918
|
+
thought_text: initial.thought_text ?? base.thought_text,
|
|
919
|
+
user_text: initial.user_text ?? base.user_text,
|
|
920
|
+
transcript: (initial.transcript ?? base.transcript).map((entry) => ({ ...entry })),
|
|
921
|
+
tools: (initial.tools ?? base.tools).map((tool) => ({ ...tool })),
|
|
922
|
+
live_tools: (initial.live_tools ?? base.live_tools).map((tool) => ({ ...tool })),
|
|
923
|
+
pending_permission: initial.pending_permission ? {
|
|
924
|
+
...initial.pending_permission,
|
|
925
|
+
options: initial.pending_permission.options.map((option) => ({ ...option }))
|
|
926
|
+
} : void 0,
|
|
927
|
+
permissions: (initial.permissions ?? base.permissions).map((permission) => ({
|
|
928
|
+
...permission,
|
|
929
|
+
options: permission.options.map((option) => ({ ...option }))
|
|
930
|
+
})),
|
|
931
|
+
_seen_seq_by_scope: seenSeq,
|
|
932
|
+
_recent_legacy_keys: []
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
apply(input) {
|
|
936
|
+
this.state = reduceRunSnapshot(this.state, input);
|
|
937
|
+
return this.snapshot();
|
|
938
|
+
}
|
|
939
|
+
snapshot() {
|
|
940
|
+
return {
|
|
941
|
+
...this.state,
|
|
942
|
+
identity: { ...this.state.identity },
|
|
943
|
+
usage: this.state.usage ? { ...this.state.usage } : void 0,
|
|
944
|
+
transcript: this.state.transcript.map((entry) => ({ ...entry })),
|
|
945
|
+
tools: this.state.tools.map((tool) => ({ ...tool })),
|
|
946
|
+
live_tools: this.state.live_tools.map((tool) => ({ ...tool })),
|
|
947
|
+
pending_permission: this.state.pending_permission ? {
|
|
948
|
+
...this.state.pending_permission,
|
|
949
|
+
options: this.state.pending_permission.options.map((option) => ({ ...option }))
|
|
950
|
+
} : void 0,
|
|
951
|
+
permissions: this.state.permissions.map((permission) => ({
|
|
952
|
+
...permission,
|
|
953
|
+
options: permission.options.map((option) => ({ ...option }))
|
|
954
|
+
})),
|
|
955
|
+
_seen_seq_by_scope: { ...this.state._seen_seq_by_scope },
|
|
956
|
+
_recent_legacy_keys: [...this.state._recent_legacy_keys]
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
//#endregion
|
|
961
|
+
//#region src/run-observer.ts
|
|
962
|
+
function observeRun(client, options = {}) {
|
|
963
|
+
const reducer = new RunReducer(options.reducer_limits, options.initial_snapshot);
|
|
964
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
965
|
+
const pendingNext = [];
|
|
966
|
+
const streamOptions = {};
|
|
967
|
+
if (options.runtime_id) {
|
|
968
|
+
streamOptions.runtime_id = options.runtime_id;
|
|
969
|
+
streamOptions.replay = options.replay ?? true;
|
|
970
|
+
} else if (options.replay !== void 0) streamOptions.replay = options.replay;
|
|
971
|
+
if (options.limit !== void 0) streamOptions.limit = options.limit;
|
|
972
|
+
if (options.after_seq !== void 0) streamOptions.after_seq = options.after_seq;
|
|
973
|
+
const iterator = client.events(streamOptions)[Symbol.asyncIterator]();
|
|
974
|
+
let closed = false;
|
|
975
|
+
let clientClosed = false;
|
|
976
|
+
let failure = null;
|
|
977
|
+
const closeClient = () => {
|
|
978
|
+
if (!options.close_client || clientClosed) return;
|
|
979
|
+
clientClosed = true;
|
|
980
|
+
client.close?.();
|
|
981
|
+
};
|
|
982
|
+
const flushPending = (snapshot) => {
|
|
983
|
+
const unresolved = pendingNext.splice(0, pendingNext.length);
|
|
984
|
+
for (const waiter of unresolved) {
|
|
985
|
+
if (failure) {
|
|
986
|
+
waiter.reject(failure);
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
if (snapshot && waiter.predicate && !waiter.predicate(snapshot)) {
|
|
990
|
+
pendingNext.push(waiter);
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
waiter.resolve(snapshot);
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
const loop = (async () => {
|
|
997
|
+
try {
|
|
998
|
+
while (!closed) {
|
|
999
|
+
const result = await iterator.next();
|
|
1000
|
+
if (result.done || closed) break;
|
|
1001
|
+
const snapshot = reducer.apply(result.value);
|
|
1002
|
+
for (const listener of listeners) listener(snapshot, result.value);
|
|
1003
|
+
flushPending(snapshot);
|
|
1004
|
+
}
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
failure = error instanceof Error ? error : new Error(String(error));
|
|
1007
|
+
} finally {
|
|
1008
|
+
closed = true;
|
|
1009
|
+
flushPending(null);
|
|
1010
|
+
}
|
|
1011
|
+
})();
|
|
1012
|
+
return {
|
|
1013
|
+
get closed() {
|
|
1014
|
+
return closed;
|
|
1015
|
+
},
|
|
1016
|
+
snapshot() {
|
|
1017
|
+
return reducer.snapshot();
|
|
1018
|
+
},
|
|
1019
|
+
subscribe(listener) {
|
|
1020
|
+
listeners.add(listener);
|
|
1021
|
+
return () => {
|
|
1022
|
+
listeners.delete(listener);
|
|
1023
|
+
};
|
|
1024
|
+
},
|
|
1025
|
+
next(predicate) {
|
|
1026
|
+
if (failure) return Promise.reject(failure);
|
|
1027
|
+
if (closed) return Promise.resolve(null);
|
|
1028
|
+
return new Promise((resolve, reject) => {
|
|
1029
|
+
pendingNext.push({
|
|
1030
|
+
resolve,
|
|
1031
|
+
reject,
|
|
1032
|
+
predicate
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
},
|
|
1036
|
+
async close() {
|
|
1037
|
+
try {
|
|
1038
|
+
if (!closed) {
|
|
1039
|
+
closed = true;
|
|
1040
|
+
if (typeof iterator.return === "function") await iterator.return();
|
|
1041
|
+
}
|
|
1042
|
+
await loop.catch(() => {});
|
|
1043
|
+
} finally {
|
|
1044
|
+
closeClient();
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
//#endregion
|
|
193
1050
|
//#region src/paths.ts
|
|
194
1051
|
function avenorHome() {
|
|
195
1052
|
return process.env.AVENOR_HOME || path.join(os.homedir(), ".avenor");
|
|
@@ -591,33 +1448,77 @@ function translateStatus(rawPhase, sentinelData) {
|
|
|
591
1448
|
default: return "running";
|
|
592
1449
|
}
|
|
593
1450
|
}
|
|
594
|
-
|
|
1451
|
+
function normalizePermission(raw) {
|
|
1452
|
+
if (!raw) return void 0;
|
|
1453
|
+
const request_id = stringField(raw, "request_id", "requestID") ?? "";
|
|
1454
|
+
const description = stringField(raw, "description", "question", "tool", "title") ?? "";
|
|
1455
|
+
const optionsRaw = raw.options;
|
|
1456
|
+
const options = Array.isArray(optionsRaw) ? optionsRaw.map((item) => {
|
|
1457
|
+
const record = asRecord(item);
|
|
1458
|
+
if (!record) return null;
|
|
1459
|
+
return {
|
|
1460
|
+
option_id: stringField(record, "option_id", "optionId") ?? "",
|
|
1461
|
+
label: stringField(record, "label") ?? "",
|
|
1462
|
+
kind: stringField(record, "kind") ?? ""
|
|
1463
|
+
};
|
|
1464
|
+
}).filter((item) => item !== null) : [];
|
|
1465
|
+
if (!request_id && !description && options.length === 0) return;
|
|
1466
|
+
return {
|
|
1467
|
+
request_id,
|
|
1468
|
+
description,
|
|
1469
|
+
options
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
function extractPendingPermission(liveStatus) {
|
|
1473
|
+
if (!liveStatus) return void 0;
|
|
1474
|
+
const permission = normalizePermission(asRecord(liveStatus.permission));
|
|
1475
|
+
if (permission) return permission;
|
|
1476
|
+
const pendingPermission = liveStatus.pending_permission;
|
|
1477
|
+
if (typeof pendingPermission === "boolean") return;
|
|
1478
|
+
return normalizePermission(asRecord(pendingPermission));
|
|
1479
|
+
}
|
|
1480
|
+
function buildBaseStatus(source, fallback, translatedStatus) {
|
|
1481
|
+
return {
|
|
1482
|
+
run_id: stringField(source, "run_id") ?? fallback.run_id,
|
|
1483
|
+
label: stringField(source, "label") ?? fallback.label,
|
|
1484
|
+
status: translatedStatus,
|
|
1485
|
+
runtime_id: stringField(source, "runtime_id", "id") ?? fallback.runtime_id,
|
|
1486
|
+
phase: stringField(source, "phase"),
|
|
1487
|
+
phase_label: stringField(source, "phase_label"),
|
|
1488
|
+
pending_permission: extractPendingPermission(source),
|
|
1489
|
+
session_id: stringField(source, "session_id") ?? fallback.session_id,
|
|
1490
|
+
stop_reason: stringField(source, "stop_reason") ?? fallback.stop_reason,
|
|
1491
|
+
backend: stringField(source, "backend"),
|
|
1492
|
+
agent: stringField(source, "agent"),
|
|
1493
|
+
model: stringField(source, "model"),
|
|
1494
|
+
dir: stringField(source, "dir"),
|
|
1495
|
+
parent_id: stringField(source, "parent_id", "parentId"),
|
|
1496
|
+
children: stringArrayField(source, "children"),
|
|
1497
|
+
event_path: stringField(source, "event_path", "on_event"),
|
|
1498
|
+
usage: asRecord(source.usage) ?? void 0,
|
|
1499
|
+
latest_seq: numberField(source, "latest_seq"),
|
|
1500
|
+
final_output: stringField(source, "final_output", "finalOutput")
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
async function buildRunStatus(runInfo, liveStatus) {
|
|
595
1504
|
let sentinel = null;
|
|
596
1505
|
if (await sentinelExists(runInfo.sentinelPath)) sentinel = await parseSentinel$1(runInfo.sentinelPath);
|
|
597
1506
|
const translated = translateStatus(liveStatus?.phase ?? liveStatus?.status ?? "running", sentinel);
|
|
598
|
-
|
|
1507
|
+
return buildBaseStatus(liveStatus ?? {}, {
|
|
599
1508
|
run_id: runInfo.runId,
|
|
600
1509
|
label: runInfo.label,
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
description: pp.description ?? "",
|
|
613
|
-
options: Array.isArray(pp.options) ? pp.options.map((o) => ({
|
|
614
|
-
option_id: o.option_id ?? "",
|
|
615
|
-
label: o.label ?? "",
|
|
616
|
-
kind: o.kind ?? ""
|
|
617
|
-
})) : []
|
|
618
|
-
};
|
|
1510
|
+
runtime_id: runInfo.runtimeId,
|
|
1511
|
+
session_id: sentinel?.SESSION ?? liveStatus?.session_id ?? runInfo.sessionId,
|
|
1512
|
+
stop_reason: sentinel?._status
|
|
1513
|
+
}, translated);
|
|
1514
|
+
}
|
|
1515
|
+
async function queryLiveStatus(client, runtimeId) {
|
|
1516
|
+
if (!runtimeId) return null;
|
|
1517
|
+
try {
|
|
1518
|
+
return await client.status(runtimeId);
|
|
1519
|
+
} catch {
|
|
1520
|
+
return null;
|
|
619
1521
|
}
|
|
620
|
-
return result;
|
|
621
1522
|
}
|
|
622
1523
|
async function statusTool(args) {
|
|
623
1524
|
if (args.supervisorId) {
|
|
@@ -626,47 +1527,34 @@ async function statusTool(args) {
|
|
|
626
1527
|
if (args.runId) {
|
|
627
1528
|
validateRunId(args.runId);
|
|
628
1529
|
const runInfo = isSingleton && sup ? findRunByLabel$3(sup, args.runId) : void 0;
|
|
629
|
-
if (runInfo)
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
liveStatus = await client
|
|
1530
|
+
if (runInfo) return buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId));
|
|
1531
|
+
let liveStatus = await queryLiveStatus(client, args.runId);
|
|
1532
|
+
if (!liveStatus) try {
|
|
1533
|
+
const entry = (await client.list()).find((item) => {
|
|
1534
|
+
const runtimeId = String(item.runtime_id ?? item.id ?? "");
|
|
1535
|
+
const label = String(item.label ?? "");
|
|
1536
|
+
const runId = String(item.run_id ?? "");
|
|
1537
|
+
return runtimeId === args.runId || label === args.runId || runId === args.runId;
|
|
1538
|
+
});
|
|
1539
|
+
if (entry) liveStatus = await queryLiveStatus(client, String(entry.runtime_id ?? entry.id ?? "")) ?? entry;
|
|
639
1540
|
} catch {}
|
|
640
1541
|
const sentinelPath = liveStatus?.sentinel_file ?? path.join(runsRoot(), args.runId, "sentinel.done");
|
|
641
1542
|
let sentinel = null;
|
|
642
1543
|
if (await sentinelExists(sentinelPath)) sentinel = await parseSentinel$1(sentinelPath);
|
|
643
|
-
return {
|
|
1544
|
+
return buildBaseStatus(liveStatus ?? {}, {
|
|
644
1545
|
run_id: args.runId,
|
|
645
1546
|
label: args.runId,
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
session_id: sentinel?.SESSION ?? liveStatus?.session_id ?? void 0,
|
|
651
|
-
stop_reason: sentinel?._status ?? void 0,
|
|
652
|
-
pending_permission: liveStatus?.pending_permission ? {
|
|
653
|
-
request_id: liveStatus.pending_permission.request_id ?? "",
|
|
654
|
-
description: liveStatus.pending_permission.description ?? "",
|
|
655
|
-
options: Array.isArray(liveStatus.pending_permission.options) ? liveStatus.pending_permission.options.map((o) => ({
|
|
656
|
-
option_id: o.option_id ?? "",
|
|
657
|
-
label: o.label ?? "",
|
|
658
|
-
kind: o.kind ?? ""
|
|
659
|
-
})) : []
|
|
660
|
-
} : void 0
|
|
661
|
-
};
|
|
1547
|
+
runtime_id: stringField(liveStatus ?? {}, "runtime_id", "id"),
|
|
1548
|
+
session_id: sentinel?.SESSION ?? liveStatus?.session_id,
|
|
1549
|
+
stop_reason: sentinel?._status
|
|
1550
|
+
}, translateStatus(liveStatus?.phase ?? liveStatus?.status ?? "running", sentinel));
|
|
662
1551
|
}
|
|
663
|
-
return (await client.list()).map((entry) => ({
|
|
664
|
-
run_id: entry.runtime_id ?? entry.id ?? "",
|
|
665
|
-
label: entry.label ?? "",
|
|
666
|
-
|
|
667
|
-
phase: entry.phase,
|
|
1552
|
+
return (await client.list()).map((entry) => buildBaseStatus(entry, {
|
|
1553
|
+
run_id: String(entry.run_id ?? entry.runtime_id ?? entry.id ?? ""),
|
|
1554
|
+
label: String(entry.label ?? entry.runtime_id ?? entry.id ?? ""),
|
|
1555
|
+
runtime_id: String(entry.runtime_id ?? entry.id ?? ""),
|
|
668
1556
|
session_id: entry.session_id
|
|
669
|
-
}));
|
|
1557
|
+
}, translateStatus(entry.phase ?? entry.status ?? "running", null)));
|
|
670
1558
|
} finally {
|
|
671
1559
|
if (!isSingleton) client.close();
|
|
672
1560
|
}
|
|
@@ -675,58 +1563,34 @@ async function statusTool(args) {
|
|
|
675
1563
|
const client = sup.getClient();
|
|
676
1564
|
if (args.runId) {
|
|
677
1565
|
const runInfo = findRunByLabel$3(sup, args.runId);
|
|
678
|
-
if (runInfo)
|
|
679
|
-
let liveStatus = null;
|
|
680
|
-
if (runInfo.runtimeId) try {
|
|
681
|
-
liveStatus = await client.status(runInfo.runtimeId);
|
|
682
|
-
} catch {}
|
|
683
|
-
return buildRunStatus(client, runInfo, liveStatus);
|
|
684
|
-
}
|
|
1566
|
+
if (runInfo) return buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId));
|
|
685
1567
|
validateRunId(args.runId);
|
|
686
|
-
|
|
687
|
-
try {
|
|
688
|
-
liveStatus = await client.status(args.runId);
|
|
689
|
-
} catch {}
|
|
1568
|
+
const liveStatus = await queryLiveStatus(client, args.runId);
|
|
690
1569
|
const sentinelPath = path.join(runsRoot(), args.runId, "sentinel.done");
|
|
691
1570
|
let sentinel = null;
|
|
692
1571
|
if (await sentinelExists(sentinelPath)) sentinel = await parseSentinel$1(sentinelPath);
|
|
693
|
-
return {
|
|
1572
|
+
return buildBaseStatus(liveStatus ?? {}, {
|
|
694
1573
|
run_id: args.runId,
|
|
695
1574
|
label: args.runId,
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
stop_reason: sentinel?._status ?? void 0,
|
|
701
|
-
pending_permission: liveStatus?.pending_permission ? {
|
|
702
|
-
request_id: liveStatus.pending_permission.request_id ?? "",
|
|
703
|
-
description: liveStatus.pending_permission.description ?? "",
|
|
704
|
-
options: Array.isArray(liveStatus.pending_permission.options) ? liveStatus.pending_permission.options.map((o) => ({
|
|
705
|
-
option_id: o.option_id ?? "",
|
|
706
|
-
label: o.label ?? "",
|
|
707
|
-
kind: o.kind ?? ""
|
|
708
|
-
})) : []
|
|
709
|
-
} : void 0
|
|
710
|
-
};
|
|
1575
|
+
runtime_id: stringField(liveStatus ?? {}, "runtime_id", "id"),
|
|
1576
|
+
session_id: sentinel?.SESSION ?? liveStatus?.session_id,
|
|
1577
|
+
stop_reason: sentinel?._status
|
|
1578
|
+
}, translateStatus(liveStatus?.phase ?? liveStatus?.status ?? "running", sentinel));
|
|
711
1579
|
}
|
|
712
1580
|
const list = await client.list();
|
|
713
1581
|
const results = [];
|
|
714
1582
|
for (const entry of list) {
|
|
715
|
-
const entryId = entry.runtime_id ?? entry.id ?? "";
|
|
716
|
-
const entryLabel = entry.label ?? entryId;
|
|
1583
|
+
const entryId = String(entry.runtime_id ?? entry.id ?? "");
|
|
1584
|
+
const entryLabel = String(entry.label ?? entryId);
|
|
717
1585
|
const runInfo = findRunByLabel$3(sup, entryId) ?? findRunByLabel$3(sup, entryLabel);
|
|
718
|
-
|
|
719
|
-
if (runInfo
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (runInfo && liveStatus) results.push(await buildRunStatus(client, runInfo, liveStatus));
|
|
723
|
-
else results.push({
|
|
724
|
-
run_id: entryId,
|
|
1586
|
+
const liveStatus = runInfo ? await queryLiveStatus(client, runInfo.runtimeId) : null;
|
|
1587
|
+
if (runInfo && liveStatus) results.push(await buildRunStatus(runInfo, liveStatus));
|
|
1588
|
+
else results.push(buildBaseStatus(entry, {
|
|
1589
|
+
run_id: String(entry.run_id ?? entryId),
|
|
725
1590
|
label: entryLabel,
|
|
726
|
-
|
|
727
|
-
phase: entry.phase,
|
|
1591
|
+
runtime_id: entryId,
|
|
728
1592
|
session_id: entry.session_id
|
|
729
|
-
});
|
|
1593
|
+
}, translateStatus(entry.phase ?? entry.status ?? "running", null)));
|
|
730
1594
|
}
|
|
731
1595
|
return results;
|
|
732
1596
|
}
|
|
@@ -843,41 +1707,240 @@ async function followUpTool(args) {
|
|
|
843
1707
|
}
|
|
844
1708
|
//#endregion
|
|
845
1709
|
//#region src/tools/events.ts
|
|
1710
|
+
const MAX_EVENT_LIMIT = 1e3;
|
|
1711
|
+
const MAX_STRING_CHARS = 4e3;
|
|
1712
|
+
const MAX_ARRAY_ITEMS = 32;
|
|
1713
|
+
const MAX_OBJECT_KEYS = 64;
|
|
846
1714
|
function findRunByLabel(sup, runId) {
|
|
847
1715
|
const runs = sup.runs;
|
|
848
1716
|
const byKey = runs.get(runId);
|
|
849
1717
|
if (byKey) return byKey;
|
|
850
1718
|
for (const info of runs.values()) if (info.label === runId) return info;
|
|
851
1719
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
-
|
|
1720
|
+
function normalizeLimit(limit) {
|
|
1721
|
+
return Math.max(1, Math.min(MAX_EVENT_LIMIT, Math.trunc(limit ?? 50)));
|
|
1722
|
+
}
|
|
1723
|
+
function contextualError(context, error) {
|
|
1724
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1725
|
+
return /* @__PURE__ */ new Error(`${context}: ${message}`);
|
|
1726
|
+
}
|
|
1727
|
+
function eventName(record) {
|
|
1728
|
+
const event = record.event;
|
|
1729
|
+
if (typeof event === "string" && event.length > 0) return event;
|
|
1730
|
+
const type = record.type;
|
|
1731
|
+
return typeof type === "string" ? type : "";
|
|
1732
|
+
}
|
|
1733
|
+
function eventSeq(record) {
|
|
1734
|
+
const value = record.seq;
|
|
1735
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1736
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
1737
|
+
const parsed = Number(value);
|
|
1738
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
function clipValue(value) {
|
|
1742
|
+
if (typeof value === "string") {
|
|
1743
|
+
if (value.length <= MAX_STRING_CHARS) return value;
|
|
1744
|
+
return value.slice(value.length - MAX_STRING_CHARS);
|
|
1745
|
+
}
|
|
1746
|
+
if (Array.isArray(value)) return (value.length <= MAX_ARRAY_ITEMS ? value : value.slice(value.length - MAX_ARRAY_ITEMS)).map((item) => clipValue(item));
|
|
1747
|
+
const record = asRecord(value);
|
|
1748
|
+
if (!record) return value;
|
|
1749
|
+
const result = {};
|
|
1750
|
+
const entries = Object.entries(record).slice(0, MAX_OBJECT_KEYS);
|
|
1751
|
+
for (const [key, item] of entries) result[key] = clipValue(item);
|
|
1752
|
+
return result;
|
|
1753
|
+
}
|
|
1754
|
+
function clipEvent(record) {
|
|
1755
|
+
return clipValue(record);
|
|
1756
|
+
}
|
|
1757
|
+
function matchesFilters(record, args) {
|
|
1758
|
+
const name = eventName(record);
|
|
1759
|
+
if (args.types && args.types.length > 0 && !args.types.includes(name)) return false;
|
|
1760
|
+
const seq = eventSeq(record);
|
|
1761
|
+
if (args.after_seq !== void 0 && seq !== void 0 && seq <= args.after_seq) return false;
|
|
1762
|
+
return true;
|
|
1763
|
+
}
|
|
1764
|
+
async function readNdjsonEvents(filePath, args) {
|
|
1765
|
+
const stream = fs.createReadStream(filePath, { encoding: "utf-8" });
|
|
1766
|
+
const rl = readline.createInterface({
|
|
1767
|
+
input: stream,
|
|
1768
|
+
crlfDelay: Infinity
|
|
1769
|
+
});
|
|
1770
|
+
const events = [];
|
|
858
1771
|
try {
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
1772
|
+
for await (const line of rl) {
|
|
1773
|
+
if (!line.trim()) continue;
|
|
1774
|
+
try {
|
|
1775
|
+
const record = asRecord(JSON.parse(line));
|
|
1776
|
+
if (!record || !matchesFilters(record, args)) continue;
|
|
1777
|
+
events.push(clipEvent(record));
|
|
1778
|
+
if (events.length > args.limit) events.splice(0, events.length - args.limit);
|
|
1779
|
+
} catch {}
|
|
1780
|
+
}
|
|
1781
|
+
} finally {
|
|
1782
|
+
rl.close();
|
|
1783
|
+
stream.close();
|
|
1784
|
+
}
|
|
1785
|
+
return events;
|
|
1786
|
+
}
|
|
1787
|
+
async function resolveRun(args) {
|
|
1788
|
+
if (args.supervisorId) {
|
|
1789
|
+
const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
|
|
1790
|
+
const runInfo = isSingleton && sup ? findRunByLabel(sup, args.runId) : void 0;
|
|
1791
|
+
if (runInfo) return {
|
|
1792
|
+
client,
|
|
1793
|
+
isSingleton,
|
|
1794
|
+
runInfo,
|
|
1795
|
+
runtimeId: runInfo.runtimeId,
|
|
1796
|
+
eventLogPath: runInfo.eventLogPath
|
|
1797
|
+
};
|
|
1798
|
+
let runtimeId;
|
|
1799
|
+
let eventLogPath;
|
|
862
1800
|
try {
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1801
|
+
const direct = await client.status(args.runId);
|
|
1802
|
+
runtimeId = direct.runtime_id ?? args.runId;
|
|
1803
|
+
eventLogPath = direct.event_path ?? direct.on_event;
|
|
1804
|
+
} catch (statusError) {
|
|
1805
|
+
let list;
|
|
1806
|
+
try {
|
|
1807
|
+
list = await client.list();
|
|
1808
|
+
} catch (listError) {
|
|
1809
|
+
if (!isSingleton) client.close();
|
|
1810
|
+
throw new AggregateError([contextualError(`status lookup for ${args.runId}`, statusError), contextualError("runtime list fallback", listError)], `unable to resolve run ${args.runId}`);
|
|
1811
|
+
}
|
|
1812
|
+
const match = list.find((entry) => {
|
|
1813
|
+
const runtime = String(entry.runtime_id ?? entry.id ?? "");
|
|
1814
|
+
const label = String(entry.label ?? "");
|
|
1815
|
+
const runId = String(entry.run_id ?? "");
|
|
1816
|
+
return runtime === args.runId || label === args.runId || runId === args.runId;
|
|
1817
|
+
});
|
|
1818
|
+
if (!match) {
|
|
1819
|
+
if (!isSingleton) client.close();
|
|
1820
|
+
throw contextualError(`unable to resolve run ${args.runId}`, statusError);
|
|
1821
|
+
}
|
|
1822
|
+
runtimeId = String(match.runtime_id ?? match.id ?? "") || void 0;
|
|
1823
|
+
eventLogPath = match.event_path ?? match.on_event;
|
|
869
1824
|
}
|
|
870
|
-
|
|
1825
|
+
return {
|
|
1826
|
+
client,
|
|
1827
|
+
isSingleton,
|
|
1828
|
+
runtimeId,
|
|
1829
|
+
eventLogPath
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
const sup = await Supervisor.get();
|
|
1833
|
+
const runInfo = findRunByLabel(sup, args.runId);
|
|
1834
|
+
if (runInfo) return {
|
|
1835
|
+
client: sup.getClient(),
|
|
1836
|
+
isSingleton: true,
|
|
1837
|
+
runInfo,
|
|
1838
|
+
runtimeId: runInfo.runtimeId,
|
|
1839
|
+
eventLogPath: runInfo.eventLogPath
|
|
1840
|
+
};
|
|
1841
|
+
validateRunId(args.runId);
|
|
1842
|
+
let runtimeId;
|
|
1843
|
+
let eventLogPath;
|
|
1844
|
+
try {
|
|
1845
|
+
const direct = await sup.getClient().status(args.runId);
|
|
1846
|
+
runtimeId = direct.runtime_id ?? args.runId;
|
|
1847
|
+
eventLogPath = direct.event_path ?? direct.on_event;
|
|
1848
|
+
} catch (error) {
|
|
1849
|
+
throw contextualError(`unable to resolve run ${args.runId}`, error);
|
|
1850
|
+
}
|
|
1851
|
+
return {
|
|
1852
|
+
client: sup.getClient(),
|
|
1853
|
+
isSingleton: true,
|
|
1854
|
+
runtimeId,
|
|
1855
|
+
eventLogPath
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
async function eventsTool(args) {
|
|
1859
|
+
const limit = normalizeLimit(args.limit);
|
|
1860
|
+
const resolved = await resolveRun(args);
|
|
1861
|
+
try {
|
|
1862
|
+
const failures = [];
|
|
1863
|
+
if (resolved.eventLogPath) try {
|
|
1864
|
+
return { events: await readNdjsonEvents(resolved.eventLogPath, {
|
|
1865
|
+
types: args.types,
|
|
1866
|
+
limit,
|
|
1867
|
+
after_seq: args.after_seq
|
|
1868
|
+
}) };
|
|
1869
|
+
} catch (error) {
|
|
1870
|
+
failures.push(contextualError(`read event log ${resolved.eventLogPath}`, error));
|
|
1871
|
+
}
|
|
1872
|
+
if (resolved.runtimeId) try {
|
|
1873
|
+
const history = await resolved.client.history({
|
|
1874
|
+
runtime_id: resolved.runtimeId,
|
|
1875
|
+
limit,
|
|
1876
|
+
after_seq: args.after_seq
|
|
1877
|
+
});
|
|
1878
|
+
return { events: Array.isArray(history.events) ? history.events.map((event) => asRecord(event)).filter((event) => event !== null).filter((event) => matchesFilters(event, args)).map((event) => clipEvent(event)) : [] };
|
|
1879
|
+
} catch (error) {
|
|
1880
|
+
failures.push(contextualError(`read history for ${resolved.runtimeId}`, error));
|
|
1881
|
+
}
|
|
1882
|
+
if (failures.length > 0) throw new AggregateError(failures, `unable to read events for run ${args.runId}`);
|
|
871
1883
|
return { events: [] };
|
|
1884
|
+
} finally {
|
|
1885
|
+
if (!resolved.isSingleton) resolved.client.close();
|
|
872
1886
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1887
|
+
}
|
|
1888
|
+
//#endregion
|
|
1889
|
+
//#region src/tools/inspect.ts
|
|
1890
|
+
function publicSnapshot(snapshot) {
|
|
1891
|
+
const { _seen_seq_by_scope: _seen, _recent_legacy_keys: _recent, _last_semantic_fingerprint: _fingerprint, _last_source_event: _source, ...rest } = snapshot;
|
|
1892
|
+
return rest;
|
|
1893
|
+
}
|
|
1894
|
+
async function inspectTool(args) {
|
|
1895
|
+
const rawStatus = await statusTool({
|
|
1896
|
+
runId: args.runId,
|
|
1897
|
+
supervisorId: args.supervisorId
|
|
1898
|
+
});
|
|
1899
|
+
const status = Array.isArray(rawStatus) ? rawStatus[0] : rawStatus;
|
|
1900
|
+
if (!status) throw new Error(`run not found: ${args.runId}`);
|
|
1901
|
+
const reducer = new RunReducer();
|
|
1902
|
+
const { events } = await eventsTool({
|
|
1903
|
+
runId: args.runId,
|
|
1904
|
+
supervisorId: args.supervisorId,
|
|
1905
|
+
limit: args.limit,
|
|
1906
|
+
after_seq: args.after_seq
|
|
1907
|
+
});
|
|
1908
|
+
for (const event of events) reducer.apply(event);
|
|
1909
|
+
const snapshot = reducer.snapshot();
|
|
1910
|
+
snapshot.identity = {
|
|
1911
|
+
...snapshot.identity,
|
|
1912
|
+
runtime_id: status.runtime_id ?? snapshot.identity.runtime_id,
|
|
1913
|
+
session_id: status.session_id ?? snapshot.identity.session_id,
|
|
1914
|
+
run_id: status.run_id ?? snapshot.identity.run_id,
|
|
1915
|
+
run_label: status.label ?? snapshot.identity.run_label,
|
|
1916
|
+
backend: status.backend ?? snapshot.identity.backend,
|
|
1917
|
+
agent: status.agent ?? snapshot.identity.agent,
|
|
1918
|
+
model: status.model ?? snapshot.identity.model,
|
|
1919
|
+
dir: status.dir ?? snapshot.identity.dir,
|
|
1920
|
+
parent_id: status.parent_id ?? snapshot.identity.parent_id,
|
|
1921
|
+
children: status.children ?? snapshot.identity.children,
|
|
1922
|
+
event_path: status.event_path ?? snapshot.identity.event_path
|
|
1923
|
+
};
|
|
1924
|
+
snapshot.phase = status.phase ?? snapshot.phase;
|
|
1925
|
+
snapshot.phase_label = status.phase_label ?? snapshot.phase_label;
|
|
1926
|
+
snapshot.latest_seq = status.latest_seq ?? snapshot.latest_seq;
|
|
1927
|
+
snapshot.usage = status.usage ?? snapshot.usage;
|
|
1928
|
+
snapshot.stop_reason = status.stop_reason ?? snapshot.stop_reason;
|
|
1929
|
+
snapshot.final_output = status.final_output ?? snapshot.final_output ?? snapshot.assistant_text;
|
|
1930
|
+
snapshot.pending_permission = status.pending_permission ?? snapshot.pending_permission;
|
|
1931
|
+
const clean = publicSnapshot(snapshot);
|
|
1932
|
+
return {
|
|
1933
|
+
run_id: status.run_id,
|
|
1934
|
+
label: status.label,
|
|
1935
|
+
status,
|
|
1936
|
+
snapshot: clean,
|
|
1937
|
+
transcript: clean.transcript,
|
|
1938
|
+
tools: clean.tools,
|
|
1939
|
+
live_tools: clean.live_tools,
|
|
1940
|
+
permissions: clean.permissions,
|
|
1941
|
+
pending_permission: clean.pending_permission,
|
|
1942
|
+
final_output: clean.final_output
|
|
1943
|
+
};
|
|
881
1944
|
}
|
|
882
1945
|
//#endregion
|
|
883
1946
|
//#region src/tools/shutdown.ts
|
|
@@ -936,4 +1999,4 @@ async function shutdownTool(args) {
|
|
|
936
1999
|
};
|
|
937
2000
|
}
|
|
938
2001
|
//#endregion
|
|
939
|
-
export { Supervisor, answerPermissionTool, dial, eventsTool, findAvenorBinary, followUpTool, getInstallDir, getVersion, installerBinaryPath, shutdownTool, spawnTool, statusTool };
|
|
2002
|
+
export { DEFAULT_RUN_REDUCER_LIMITS, RunReducer, Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, shutdownTool, spawnTool, statusTool };
|