@dougbots/avenor-core 0.3.6 → 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 +1222 -154
- 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");
|
|
@@ -447,26 +1304,23 @@ function validateTimeout(value) {
|
|
|
447
1304
|
return amount;
|
|
448
1305
|
}
|
|
449
1306
|
function validateSupervisorSocketPath(supervisorId) {
|
|
450
|
-
const canonical = fs.realpathSync(supervisorId);
|
|
451
1307
|
const root = fs.realpathSync(socketsRoot());
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
1308
|
+
const parent = fs.realpathSync(path.dirname(supervisorId));
|
|
1309
|
+
const basename = path.basename(supervisorId);
|
|
1310
|
+
if (parent !== root || !/^avenor-mcp-[^/\\]+\.sock$/.test(basename)) throw new Error(`invalid supervisorId: ${supervisorId}`);
|
|
1311
|
+
return path.join(parent, basename);
|
|
455
1312
|
}
|
|
456
1313
|
//#endregion
|
|
457
1314
|
//#region src/tools/get-supervisor-client.ts
|
|
458
1315
|
async function getSupervisorClient(supervisorId) {
|
|
459
1316
|
const id = validateSupervisorSocketPath(supervisorId);
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
supervisorId: id
|
|
468
|
-
};
|
|
469
|
-
}
|
|
1317
|
+
const current = Supervisor.currentInstance();
|
|
1318
|
+
if (current !== null && validateSupervisorSocketPath(current.supervisorId) === id) return {
|
|
1319
|
+
client: current.getClient(),
|
|
1320
|
+
isSingleton: true,
|
|
1321
|
+
sup: current,
|
|
1322
|
+
supervisorId: id
|
|
1323
|
+
};
|
|
470
1324
|
return {
|
|
471
1325
|
client: await dial(id),
|
|
472
1326
|
isSingleton: false,
|
|
@@ -594,74 +1448,113 @@ function translateStatus(rawPhase, sentinelData) {
|
|
|
594
1448
|
default: return "running";
|
|
595
1449
|
}
|
|
596
1450
|
}
|
|
597
|
-
|
|
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) {
|
|
598
1504
|
let sentinel = null;
|
|
599
1505
|
if (await sentinelExists(runInfo.sentinelPath)) sentinel = await parseSentinel$1(runInfo.sentinelPath);
|
|
600
1506
|
const translated = translateStatus(liveStatus?.phase ?? liveStatus?.status ?? "running", sentinel);
|
|
601
|
-
|
|
1507
|
+
return buildBaseStatus(liveStatus ?? {}, {
|
|
602
1508
|
run_id: runInfo.runId,
|
|
603
1509
|
label: runInfo.label,
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
description: pp.description ?? "",
|
|
616
|
-
options: Array.isArray(pp.options) ? pp.options.map((o) => ({
|
|
617
|
-
option_id: o.option_id ?? "",
|
|
618
|
-
label: o.label ?? "",
|
|
619
|
-
kind: o.kind ?? ""
|
|
620
|
-
})) : []
|
|
621
|
-
};
|
|
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;
|
|
622
1521
|
}
|
|
623
|
-
return result;
|
|
624
1522
|
}
|
|
625
1523
|
async function statusTool(args) {
|
|
626
1524
|
if (args.supervisorId) {
|
|
627
|
-
const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
|
|
1525
|
+
const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
|
|
628
1526
|
try {
|
|
629
1527
|
if (args.runId) {
|
|
630
1528
|
validateRunId(args.runId);
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
1529
|
+
const runInfo = isSingleton && sup ? findRunByLabel$3(sup, args.runId) : void 0;
|
|
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;
|
|
634
1540
|
} catch {}
|
|
635
1541
|
const sentinelPath = liveStatus?.sentinel_file ?? path.join(runsRoot(), args.runId, "sentinel.done");
|
|
636
1542
|
let sentinel = null;
|
|
637
1543
|
if (await sentinelExists(sentinelPath)) sentinel = await parseSentinel$1(sentinelPath);
|
|
638
|
-
return {
|
|
1544
|
+
return buildBaseStatus(liveStatus ?? {}, {
|
|
639
1545
|
run_id: args.runId,
|
|
640
1546
|
label: args.runId,
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
session_id: sentinel?.SESSION ?? liveStatus?.session_id ?? void 0,
|
|
646
|
-
stop_reason: sentinel?._status ?? void 0,
|
|
647
|
-
pending_permission: liveStatus?.pending_permission ? {
|
|
648
|
-
request_id: liveStatus.pending_permission.request_id ?? "",
|
|
649
|
-
description: liveStatus.pending_permission.description ?? "",
|
|
650
|
-
options: Array.isArray(liveStatus.pending_permission.options) ? liveStatus.pending_permission.options.map((o) => ({
|
|
651
|
-
option_id: o.option_id ?? "",
|
|
652
|
-
label: o.label ?? "",
|
|
653
|
-
kind: o.kind ?? ""
|
|
654
|
-
})) : []
|
|
655
|
-
} : void 0
|
|
656
|
-
};
|
|
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));
|
|
657
1551
|
}
|
|
658
|
-
return (await client.list()).map((entry) => ({
|
|
659
|
-
run_id: entry.runtime_id ?? entry.id ?? "",
|
|
660
|
-
label: entry.label ?? "",
|
|
661
|
-
|
|
662
|
-
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 ?? ""),
|
|
663
1556
|
session_id: entry.session_id
|
|
664
|
-
}));
|
|
1557
|
+
}, translateStatus(entry.phase ?? entry.status ?? "running", null)));
|
|
665
1558
|
} finally {
|
|
666
1559
|
if (!isSingleton) client.close();
|
|
667
1560
|
}
|
|
@@ -670,58 +1563,34 @@ async function statusTool(args) {
|
|
|
670
1563
|
const client = sup.getClient();
|
|
671
1564
|
if (args.runId) {
|
|
672
1565
|
const runInfo = findRunByLabel$3(sup, args.runId);
|
|
673
|
-
if (runInfo)
|
|
674
|
-
let liveStatus = null;
|
|
675
|
-
if (runInfo.runtimeId) try {
|
|
676
|
-
liveStatus = await client.status(runInfo.runtimeId);
|
|
677
|
-
} catch {}
|
|
678
|
-
return buildRunStatus(client, runInfo, liveStatus);
|
|
679
|
-
}
|
|
1566
|
+
if (runInfo) return buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId));
|
|
680
1567
|
validateRunId(args.runId);
|
|
681
|
-
|
|
682
|
-
try {
|
|
683
|
-
liveStatus = await client.status(args.runId);
|
|
684
|
-
} catch {}
|
|
1568
|
+
const liveStatus = await queryLiveStatus(client, args.runId);
|
|
685
1569
|
const sentinelPath = path.join(runsRoot(), args.runId, "sentinel.done");
|
|
686
1570
|
let sentinel = null;
|
|
687
1571
|
if (await sentinelExists(sentinelPath)) sentinel = await parseSentinel$1(sentinelPath);
|
|
688
|
-
return {
|
|
1572
|
+
return buildBaseStatus(liveStatus ?? {}, {
|
|
689
1573
|
run_id: args.runId,
|
|
690
1574
|
label: args.runId,
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
stop_reason: sentinel?._status ?? void 0,
|
|
696
|
-
pending_permission: liveStatus?.pending_permission ? {
|
|
697
|
-
request_id: liveStatus.pending_permission.request_id ?? "",
|
|
698
|
-
description: liveStatus.pending_permission.description ?? "",
|
|
699
|
-
options: Array.isArray(liveStatus.pending_permission.options) ? liveStatus.pending_permission.options.map((o) => ({
|
|
700
|
-
option_id: o.option_id ?? "",
|
|
701
|
-
label: o.label ?? "",
|
|
702
|
-
kind: o.kind ?? ""
|
|
703
|
-
})) : []
|
|
704
|
-
} : void 0
|
|
705
|
-
};
|
|
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));
|
|
706
1579
|
}
|
|
707
1580
|
const list = await client.list();
|
|
708
1581
|
const results = [];
|
|
709
1582
|
for (const entry of list) {
|
|
710
|
-
const entryId = entry.runtime_id ?? entry.id ?? "";
|
|
711
|
-
const entryLabel = entry.label ?? entryId;
|
|
1583
|
+
const entryId = String(entry.runtime_id ?? entry.id ?? "");
|
|
1584
|
+
const entryLabel = String(entry.label ?? entryId);
|
|
712
1585
|
const runInfo = findRunByLabel$3(sup, entryId) ?? findRunByLabel$3(sup, entryLabel);
|
|
713
|
-
|
|
714
|
-
if (runInfo
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
if (runInfo && liveStatus) results.push(await buildRunStatus(client, runInfo, liveStatus));
|
|
718
|
-
else results.push({
|
|
719
|
-
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),
|
|
720
1590
|
label: entryLabel,
|
|
721
|
-
|
|
722
|
-
phase: entry.phase,
|
|
1591
|
+
runtime_id: entryId,
|
|
723
1592
|
session_id: entry.session_id
|
|
724
|
-
});
|
|
1593
|
+
}, translateStatus(entry.phase ?? entry.status ?? "running", null)));
|
|
725
1594
|
}
|
|
726
1595
|
return results;
|
|
727
1596
|
}
|
|
@@ -838,41 +1707,240 @@ async function followUpTool(args) {
|
|
|
838
1707
|
}
|
|
839
1708
|
//#endregion
|
|
840
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;
|
|
841
1714
|
function findRunByLabel(sup, runId) {
|
|
842
1715
|
const runs = sup.runs;
|
|
843
1716
|
const byKey = runs.get(runId);
|
|
844
1717
|
if (byKey) return byKey;
|
|
845
1718
|
for (const info of runs.values()) if (info.label === runId) return info;
|
|
846
1719
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
const
|
|
852
|
-
|
|
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 = [];
|
|
853
1771
|
try {
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
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;
|
|
857
1800
|
try {
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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;
|
|
864
1824
|
}
|
|
865
|
-
|
|
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}`);
|
|
866
1883
|
return { events: [] };
|
|
1884
|
+
} finally {
|
|
1885
|
+
if (!resolved.isSingleton) resolved.client.close();
|
|
867
1886
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
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
|
+
};
|
|
876
1944
|
}
|
|
877
1945
|
//#endregion
|
|
878
1946
|
//#region src/tools/shutdown.ts
|
|
@@ -931,4 +1999,4 @@ async function shutdownTool(args) {
|
|
|
931
1999
|
};
|
|
932
2000
|
}
|
|
933
2001
|
//#endregion
|
|
934
|
-
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 };
|