@webless/agent 0.6.7 → 0.6.9
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/{chunk-U5IPERNA.js → chunk-U2SU2CGF.js} +302 -59
- package/dist/chunk-U2SU2CGF.js.map +1 -0
- package/dist/embed.cjs +310 -67
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.js +1 -1
- package/dist/index.cjs +305 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +305 -62
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +310 -67
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-U5IPERNA.js.map +0 -1
package/dist/embed.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -309,6 +309,203 @@ function clearPersistedAgentSession(visitorSessionId, options) {
|
|
|
309
309
|
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
// src/runtime/subagent-child-stream.ts
|
|
313
|
+
var INITIAL_RETRY_DELAY_MS = 100;
|
|
314
|
+
var MAX_RETRY_DELAY_MS = 2e3;
|
|
315
|
+
var MAX_CONSECUTIVE_RETRIES = 6;
|
|
316
|
+
function isRecord2(value) {
|
|
317
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
318
|
+
}
|
|
319
|
+
function parseError(value) {
|
|
320
|
+
if (!isRecord2(value)) return void 0;
|
|
321
|
+
const { code, message } = value;
|
|
322
|
+
if (typeof code !== "string" || typeof message !== "string") {
|
|
323
|
+
return void 0;
|
|
324
|
+
}
|
|
325
|
+
return { code, message };
|
|
326
|
+
}
|
|
327
|
+
function parseChildStreamEvent(value) {
|
|
328
|
+
if (!isRecord2(value) || typeof value.type !== "string") {
|
|
329
|
+
return { type: "other" };
|
|
330
|
+
}
|
|
331
|
+
if (value.type === "session.waiting" || value.type === "session.completed" || value.type === "session.failed") {
|
|
332
|
+
return { type: "session.boundary" };
|
|
333
|
+
}
|
|
334
|
+
if (value.type === "subagent.event" && isRecord2(value.data) && Object.hasOwn(value.data, "event")) {
|
|
335
|
+
return parseChildStreamEvent(value.data.event);
|
|
336
|
+
}
|
|
337
|
+
if (value.type === "subagent.called" && isRecord2(value.data)) {
|
|
338
|
+
const { childStreamPath } = value.data;
|
|
339
|
+
if (typeof childStreamPath === "string") {
|
|
340
|
+
return { childStreamPath, type: "subagent.called" };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (value.type !== "action.result" || !isRecord2(value.data)) {
|
|
344
|
+
return { type: "other" };
|
|
345
|
+
}
|
|
346
|
+
const { data } = value;
|
|
347
|
+
if (data.status !== "completed" && data.status !== "failed" && data.status !== "rejected") {
|
|
348
|
+
return { type: "other" };
|
|
349
|
+
}
|
|
350
|
+
if (!isRecord2(data.result) || data.result.kind !== "tool-result") {
|
|
351
|
+
return { type: "other" };
|
|
352
|
+
}
|
|
353
|
+
const result = data.result;
|
|
354
|
+
if (typeof result.callId !== "string" || typeof result.toolName !== "string") {
|
|
355
|
+
return { type: "other" };
|
|
356
|
+
}
|
|
357
|
+
const error = parseError(data.error);
|
|
358
|
+
return {
|
|
359
|
+
type: "action.result",
|
|
360
|
+
hasOutput: Object.hasOwn(result, "output"),
|
|
361
|
+
result: {
|
|
362
|
+
callId: result.callId,
|
|
363
|
+
toolName: result.toolName,
|
|
364
|
+
status: data.status,
|
|
365
|
+
...Object.hasOwn(result, "output") ? { output: result.output } : {},
|
|
366
|
+
...error ? { error } : {}
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
async function* readNdjsonStream(body) {
|
|
371
|
+
const reader = body.getReader();
|
|
372
|
+
const decoder = new TextDecoder();
|
|
373
|
+
let buffer = "";
|
|
374
|
+
try {
|
|
375
|
+
while (true) {
|
|
376
|
+
const { done, value } = await reader.read();
|
|
377
|
+
buffer += decoder.decode(value, { stream: !done });
|
|
378
|
+
const lines = buffer.split("\n");
|
|
379
|
+
buffer = lines.pop() ?? "";
|
|
380
|
+
for (const line of lines) {
|
|
381
|
+
const trimmed2 = line.trim();
|
|
382
|
+
if (!trimmed2) continue;
|
|
383
|
+
try {
|
|
384
|
+
const parsed = JSON.parse(trimmed2);
|
|
385
|
+
yield parsed;
|
|
386
|
+
} catch {
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (done) break;
|
|
390
|
+
}
|
|
391
|
+
const trimmed = buffer.trim();
|
|
392
|
+
if (trimmed) {
|
|
393
|
+
try {
|
|
394
|
+
const parsed = JSON.parse(trimmed);
|
|
395
|
+
yield parsed;
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
} finally {
|
|
400
|
+
reader.releaseLock();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function streamPathAt(path, streamIndex) {
|
|
404
|
+
if (streamIndex === 0) return path;
|
|
405
|
+
return `${path}${path.includes("?") ? "&" : "?"}startIndex=${streamIndex}`;
|
|
406
|
+
}
|
|
407
|
+
function abortableDelay(delayMs, signal) {
|
|
408
|
+
if (signal.aborted) return Promise.resolve();
|
|
409
|
+
return new Promise((resolve) => {
|
|
410
|
+
const finish = () => {
|
|
411
|
+
clearTimeout(timeout);
|
|
412
|
+
signal.removeEventListener("abort", finish);
|
|
413
|
+
resolve();
|
|
414
|
+
};
|
|
415
|
+
const timeout = setTimeout(finish, delayMs);
|
|
416
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
var SubagentChildStreamCoordinator = class {
|
|
420
|
+
constructor(client, handlers, parentSignal) {
|
|
421
|
+
this.client = client;
|
|
422
|
+
this.handlers = handlers;
|
|
423
|
+
this.parentSignal = parentSignal;
|
|
424
|
+
}
|
|
425
|
+
client;
|
|
426
|
+
handlers;
|
|
427
|
+
parentSignal;
|
|
428
|
+
controllers = /* @__PURE__ */ new Map();
|
|
429
|
+
tasks = /* @__PURE__ */ new Map();
|
|
430
|
+
begin(event) {
|
|
431
|
+
this.beginPath(event.data.childStreamPath);
|
|
432
|
+
}
|
|
433
|
+
async waitForAll() {
|
|
434
|
+
let observedTaskCount = -1;
|
|
435
|
+
while (observedTaskCount !== this.tasks.size) {
|
|
436
|
+
observedTaskCount = this.tasks.size;
|
|
437
|
+
await Promise.all(this.tasks.values());
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
abortAll() {
|
|
441
|
+
for (const controller of this.controllers.values()) controller.abort();
|
|
442
|
+
this.controllers.clear();
|
|
443
|
+
}
|
|
444
|
+
beginPath(childStreamPath) {
|
|
445
|
+
if (this.tasks.has(childStreamPath)) return;
|
|
446
|
+
const controller = new AbortController();
|
|
447
|
+
const abort = () => controller.abort();
|
|
448
|
+
if (this.parentSignal.aborted) {
|
|
449
|
+
controller.abort();
|
|
450
|
+
} else {
|
|
451
|
+
this.parentSignal.addEventListener("abort", abort, { once: true });
|
|
452
|
+
}
|
|
453
|
+
this.controllers.set(childStreamPath, controller);
|
|
454
|
+
const task = this.consume(childStreamPath, controller.signal).finally(
|
|
455
|
+
() => {
|
|
456
|
+
this.parentSignal.removeEventListener("abort", abort);
|
|
457
|
+
if (this.controllers.get(childStreamPath) === controller) {
|
|
458
|
+
this.controllers.delete(childStreamPath);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
);
|
|
462
|
+
this.tasks.set(childStreamPath, task);
|
|
463
|
+
}
|
|
464
|
+
async consume(path, signal) {
|
|
465
|
+
let streamIndex = 0;
|
|
466
|
+
let consecutiveRetries = 0;
|
|
467
|
+
let retryDelayMs = INITIAL_RETRY_DELAY_MS;
|
|
468
|
+
while (!signal.aborted && consecutiveRetries < MAX_CONSECUTIVE_RETRIES) {
|
|
469
|
+
let receivedEvent = false;
|
|
470
|
+
try {
|
|
471
|
+
const response = await this.client.fetch(
|
|
472
|
+
streamPathAt(path, streamIndex),
|
|
473
|
+
{
|
|
474
|
+
cache: "no-store",
|
|
475
|
+
signal
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
if (!response.ok || response.body === null) {
|
|
479
|
+
await response.body?.cancel().catch(() => {
|
|
480
|
+
});
|
|
481
|
+
throw new Error(`Child stream returned ${response.status}.`);
|
|
482
|
+
}
|
|
483
|
+
for await (const rawEvent of readNdjsonStream(response.body)) {
|
|
484
|
+
if (signal.aborted) return;
|
|
485
|
+
receivedEvent = true;
|
|
486
|
+
streamIndex += 1;
|
|
487
|
+
const event = parseChildStreamEvent(rawEvent);
|
|
488
|
+
if (event.type === "session.boundary") return;
|
|
489
|
+
if (event.type === "subagent.called") {
|
|
490
|
+
this.beginPath(event.childStreamPath);
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
if (event.type !== "action.result") continue;
|
|
494
|
+
this.handlers.onToolResult?.(event.result);
|
|
495
|
+
if (event.hasOutput) {
|
|
496
|
+
this.handlers.onActionResult?.(event.result.output);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
} catch {
|
|
500
|
+
if (signal.aborted) return;
|
|
501
|
+
}
|
|
502
|
+
consecutiveRetries = receivedEvent ? 0 : consecutiveRetries + 1;
|
|
503
|
+
retryDelayMs = receivedEvent ? INITIAL_RETRY_DELAY_MS : Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
504
|
+
await abortableDelay(retryDelayMs, signal);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
312
509
|
// src/runtime/tool-ui.ts
|
|
313
510
|
var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
|
|
314
511
|
var AGENT_STRUCTURED_TOOL_INPUT_SCHEMA_VERSION = "webless.structured-tool-input.v1";
|
|
@@ -322,7 +519,7 @@ function formatAgentStructuredToolInput(surface, values) {
|
|
|
322
519
|
};
|
|
323
520
|
return `${AGENT_STRUCTURED_TOOL_INPUT_HEADER} ${JSON.stringify(payload)}`;
|
|
324
521
|
}
|
|
325
|
-
function
|
|
522
|
+
function isRecord3(value) {
|
|
326
523
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
327
524
|
}
|
|
328
525
|
function isJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
@@ -371,7 +568,7 @@ function isFieldKind(value) {
|
|
|
371
568
|
].includes(value);
|
|
372
569
|
}
|
|
373
570
|
function parseField(value) {
|
|
374
|
-
if (!
|
|
571
|
+
if (!isRecord3(value)) return null;
|
|
375
572
|
if (!hasOnlyKeys(value, [
|
|
376
573
|
"description",
|
|
377
574
|
"kind",
|
|
@@ -422,7 +619,7 @@ function parseField(value) {
|
|
|
422
619
|
if (!Array.isArray(value.options) || value.options.length > 100)
|
|
423
620
|
return null;
|
|
424
621
|
for (const option of value.options) {
|
|
425
|
-
if (!
|
|
622
|
+
if (!isRecord3(option) || !boundedString(option.label, 160) || !isJsonValue(option.value)) {
|
|
426
623
|
return null;
|
|
427
624
|
}
|
|
428
625
|
}
|
|
@@ -445,7 +642,7 @@ function parseField(value) {
|
|
|
445
642
|
};
|
|
446
643
|
}
|
|
447
644
|
function parseStep(value) {
|
|
448
|
-
if (!
|
|
645
|
+
if (!isRecord3(value)) return null;
|
|
449
646
|
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
450
647
|
return null;
|
|
451
648
|
}
|
|
@@ -465,7 +662,7 @@ function parseStep(value) {
|
|
|
465
662
|
};
|
|
466
663
|
}
|
|
467
664
|
function parseAction(value) {
|
|
468
|
-
if (!
|
|
665
|
+
if (!isRecord3(value)) return null;
|
|
469
666
|
if (!hasOnlyKeys(value, ["id", "label"])) return null;
|
|
470
667
|
const label = boundedString(value.label, 80);
|
|
471
668
|
if (value.id !== "submit" && value.id !== "back" && value.id !== "next" && value.id !== "reset" || !label) {
|
|
@@ -477,7 +674,7 @@ function parseAction(value) {
|
|
|
477
674
|
};
|
|
478
675
|
}
|
|
479
676
|
function parseAgentToolUiSurface(value) {
|
|
480
|
-
if (!
|
|
677
|
+
if (!isRecord3(value) || value.schemaVersion !== AGENT_TOOL_UI_SCHEMA_VERSION)
|
|
481
678
|
return null;
|
|
482
679
|
if (!hasOnlyKeys(value, [
|
|
483
680
|
"actions",
|
|
@@ -515,7 +712,7 @@ function parseAgentToolUiSurface(value) {
|
|
|
515
712
|
if (value.operationId !== void 0 && !operationId) return null;
|
|
516
713
|
if (value.requestId !== void 0 && !requestId) return null;
|
|
517
714
|
if (value.submitLabel !== void 0 && !submitLabel) return null;
|
|
518
|
-
const values = value.values !== void 0 &&
|
|
715
|
+
const values = value.values !== void 0 && isRecord3(value.values) ? value.values : void 0;
|
|
519
716
|
if (value.values !== void 0) {
|
|
520
717
|
if (!values || !Object.values(values).every((item) => isJsonValue(item)))
|
|
521
718
|
return null;
|
|
@@ -552,8 +749,7 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
552
749
|
if (event.type === "message.completed") {
|
|
553
750
|
handlers.onComplete?.();
|
|
554
751
|
}
|
|
555
|
-
|
|
556
|
-
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
752
|
+
emitVisitorInteractionEvent(event, handlers);
|
|
557
753
|
if (event.type !== "message.appended") return rendered;
|
|
558
754
|
const { messageDelta, messageSoFar } = event.data;
|
|
559
755
|
let delta = messageDelta;
|
|
@@ -603,6 +799,13 @@ function emitInputRequests(event, handlers) {
|
|
|
603
799
|
})
|
|
604
800
|
);
|
|
605
801
|
}
|
|
802
|
+
function emitVisitorInteractionEvent(event, handlers) {
|
|
803
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
804
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
805
|
+
if (event.type === "subagent.event") {
|
|
806
|
+
emitVisitorInteractionEvent(event.data.event, handlers);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
606
809
|
function isResumeTurnMessage(received, candidate) {
|
|
607
810
|
if (received === candidate) return true;
|
|
608
811
|
return Boolean(candidate) && received.endsWith(`
|
|
@@ -775,11 +978,14 @@ var AgentSession = class {
|
|
|
775
978
|
clientHost;
|
|
776
979
|
session;
|
|
777
980
|
activeResponse;
|
|
981
|
+
childStreams;
|
|
778
982
|
capability;
|
|
779
983
|
getActiveSessionId() {
|
|
780
984
|
return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
|
|
781
985
|
}
|
|
782
986
|
reset() {
|
|
987
|
+
this.childStreams?.abortAll();
|
|
988
|
+
this.childStreams = void 0;
|
|
783
989
|
if (this.activeResponse) {
|
|
784
990
|
void this.activeResponse.cancel().catch(() => {
|
|
785
991
|
});
|
|
@@ -875,6 +1081,12 @@ var AgentSession = class {
|
|
|
875
1081
|
this.persistSessionCursor(session);
|
|
876
1082
|
}
|
|
877
1083
|
this.activeResponse = response;
|
|
1084
|
+
const childStreams = new SubagentChildStreamCoordinator(
|
|
1085
|
+
client,
|
|
1086
|
+
handlers,
|
|
1087
|
+
signal
|
|
1088
|
+
);
|
|
1089
|
+
this.childStreams = childStreams;
|
|
878
1090
|
let streamIndex = session?.state.streamIndex ?? 0;
|
|
879
1091
|
let rendered = "";
|
|
880
1092
|
const workItems = /* @__PURE__ */ new Map();
|
|
@@ -882,6 +1094,7 @@ var AgentSession = class {
|
|
|
882
1094
|
try {
|
|
883
1095
|
for await (const event of response) {
|
|
884
1096
|
if (signal.aborted) break;
|
|
1097
|
+
if (event.type === "subagent.called") childStreams.begin(event);
|
|
885
1098
|
if (event.type === "input.requested") requestedInput = true;
|
|
886
1099
|
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
887
1100
|
streamIndex += 1;
|
|
@@ -894,8 +1107,11 @@ var AgentSession = class {
|
|
|
894
1107
|
);
|
|
895
1108
|
}
|
|
896
1109
|
}
|
|
1110
|
+
await childStreams.waitForAll();
|
|
897
1111
|
} finally {
|
|
1112
|
+
childStreams.abortAll();
|
|
898
1113
|
this.activeResponse = void 0;
|
|
1114
|
+
if (this.childStreams === childStreams) this.childStreams = void 0;
|
|
899
1115
|
if (session) {
|
|
900
1116
|
this.persistSessionCursor(session);
|
|
901
1117
|
}
|
|
@@ -931,64 +1147,80 @@ var AgentSession = class {
|
|
|
931
1147
|
}
|
|
932
1148
|
let rendered = renderTurn(turnEvents);
|
|
933
1149
|
const workItems = /* @__PURE__ */ new Map();
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
rendered = initialText + rendered;
|
|
946
|
-
}
|
|
947
|
-
let session = client.sessions.attach(snapshot.session.sessionId, {
|
|
948
|
-
streamIndex: snapshot.session.streamIndex
|
|
949
|
-
});
|
|
950
|
-
this.session = session;
|
|
951
|
-
this.persistSessionCursor(session);
|
|
952
|
-
let snapshotBoundary;
|
|
953
|
-
for (let index = turnEvents.length - 1; index >= 0; index -= 1) {
|
|
954
|
-
const event = turnEvents[index];
|
|
955
|
-
if (event && isTurnBoundary(event)) {
|
|
956
|
-
snapshotBoundary = event;
|
|
957
|
-
break;
|
|
1150
|
+
const childStreams = new SubagentChildStreamCoordinator(
|
|
1151
|
+
client,
|
|
1152
|
+
handlers,
|
|
1153
|
+
signal
|
|
1154
|
+
);
|
|
1155
|
+
this.childStreams = childStreams;
|
|
1156
|
+
try {
|
|
1157
|
+
for (const event of turnEvents) {
|
|
1158
|
+
if (event.type === "subagent.called") childStreams.begin(event);
|
|
1159
|
+
applyWorkEvent(event, handlers, workItems);
|
|
1160
|
+
emitVisitorInteractionEvent(event, handlers);
|
|
958
1161
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1162
|
+
if (rendered.startsWith(initialText)) {
|
|
1163
|
+
const missedText = rendered.slice(initialText.length);
|
|
1164
|
+
if (missedText) handlers.onDelta(missedText);
|
|
1165
|
+
} else if (initialText.startsWith(rendered)) {
|
|
1166
|
+
rendered = initialText;
|
|
1167
|
+
} else if (!initialText.startsWith(rendered)) {
|
|
1168
|
+
rendered = initialText + rendered;
|
|
1169
|
+
}
|
|
1170
|
+
let session = client.sessions.attach(snapshot.session.sessionId, {
|
|
1171
|
+
streamIndex: snapshot.session.streamIndex
|
|
1172
|
+
});
|
|
1173
|
+
this.session = session;
|
|
1174
|
+
this.persistSessionCursor(session);
|
|
1175
|
+
let snapshotBoundary;
|
|
1176
|
+
for (let index = turnEvents.length - 1; index >= 0; index -= 1) {
|
|
1177
|
+
const event = turnEvents[index];
|
|
1178
|
+
if (event && isTurnBoundary(event)) {
|
|
1179
|
+
snapshotBoundary = event;
|
|
1180
|
+
break;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
if (snapshotBoundary) {
|
|
1184
|
+
if (snapshotBoundary.type === "session.failed") {
|
|
1185
|
+
throw new Error(
|
|
1186
|
+
snapshotBoundary.data.message || snapshotBoundary.data.code
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
handlers.onComplete?.();
|
|
1190
|
+
await childStreams.waitForAll();
|
|
1191
|
+
if (!rendered.trim() && !hasInputRequest) {
|
|
1192
|
+
throw new Error("Empty response from runtime");
|
|
1193
|
+
}
|
|
1194
|
+
return rendered.trim();
|
|
1195
|
+
}
|
|
1196
|
+
let streamIndex = snapshot.session.streamIndex;
|
|
1197
|
+
for await (const event of session.stream({ signal })) {
|
|
1198
|
+
if (signal.aborted) break;
|
|
1199
|
+
if (event.type === "subagent.called") childStreams.begin(event);
|
|
1200
|
+
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
1201
|
+
streamIndex += 1;
|
|
1202
|
+
savePersistedAgentSession(
|
|
1203
|
+
this.visitorSessionId,
|
|
1204
|
+
session.state.sessionId,
|
|
1205
|
+
streamIndex,
|
|
1206
|
+
this.storeOptions
|
|
964
1207
|
);
|
|
1208
|
+
if (isTurnBoundary(event)) break;
|
|
965
1209
|
}
|
|
966
|
-
|
|
967
|
-
|
|
1210
|
+
await childStreams.waitForAll();
|
|
1211
|
+
session = client.sessions.attach(session.state.sessionId, {
|
|
1212
|
+
streamIndex
|
|
1213
|
+
});
|
|
1214
|
+
this.session = session;
|
|
1215
|
+
this.persistSessionCursor(session);
|
|
1216
|
+
if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
|
|
968
1217
|
throw new Error("Empty response from runtime");
|
|
969
1218
|
}
|
|
970
1219
|
return rendered.trim();
|
|
1220
|
+
} finally {
|
|
1221
|
+
childStreams.abortAll();
|
|
1222
|
+
if (this.childStreams === childStreams) this.childStreams = void 0;
|
|
971
1223
|
}
|
|
972
|
-
let streamIndex = snapshot.session.streamIndex;
|
|
973
|
-
for await (const event of session.stream({ signal })) {
|
|
974
|
-
if (signal.aborted) break;
|
|
975
|
-
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
976
|
-
streamIndex += 1;
|
|
977
|
-
savePersistedAgentSession(
|
|
978
|
-
this.visitorSessionId,
|
|
979
|
-
session.state.sessionId,
|
|
980
|
-
streamIndex,
|
|
981
|
-
this.storeOptions
|
|
982
|
-
);
|
|
983
|
-
if (isTurnBoundary(event)) break;
|
|
984
|
-
}
|
|
985
|
-
session = client.sessions.attach(session.state.sessionId, { streamIndex });
|
|
986
|
-
this.session = session;
|
|
987
|
-
this.persistSessionCursor(session);
|
|
988
|
-
if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
|
|
989
|
-
throw new Error("Empty response from runtime");
|
|
990
|
-
}
|
|
991
|
-
return rendered.trim();
|
|
992
1224
|
}
|
|
993
1225
|
async respondTurn(responses, signal, handlers) {
|
|
994
1226
|
const client = this.ensureClient();
|
|
@@ -1009,6 +1241,12 @@ var AgentSession = class {
|
|
|
1009
1241
|
() => session.respond(inputResponses, { signal })
|
|
1010
1242
|
);
|
|
1011
1243
|
this.activeResponse = response;
|
|
1244
|
+
const childStreams = new SubagentChildStreamCoordinator(
|
|
1245
|
+
client,
|
|
1246
|
+
handlers,
|
|
1247
|
+
signal
|
|
1248
|
+
);
|
|
1249
|
+
this.childStreams = childStreams;
|
|
1012
1250
|
let streamIndex = session.state.streamIndex;
|
|
1013
1251
|
let rendered = "";
|
|
1014
1252
|
let requestedInput = false;
|
|
@@ -1016,6 +1254,7 @@ var AgentSession = class {
|
|
|
1016
1254
|
try {
|
|
1017
1255
|
for await (const event of response) {
|
|
1018
1256
|
if (signal.aborted) break;
|
|
1257
|
+
if (event.type === "subagent.called") childStreams.begin(event);
|
|
1019
1258
|
if (event.type === "input.requested") requestedInput = true;
|
|
1020
1259
|
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
1021
1260
|
streamIndex += 1;
|
|
@@ -1026,8 +1265,11 @@ var AgentSession = class {
|
|
|
1026
1265
|
this.storeOptions
|
|
1027
1266
|
);
|
|
1028
1267
|
}
|
|
1268
|
+
await childStreams.waitForAll();
|
|
1029
1269
|
} finally {
|
|
1270
|
+
childStreams.abortAll();
|
|
1030
1271
|
this.activeResponse = void 0;
|
|
1272
|
+
if (this.childStreams === childStreams) this.childStreams = void 0;
|
|
1031
1273
|
this.session = client.sessions.attach(session.state.sessionId, {
|
|
1032
1274
|
streamIndex
|
|
1033
1275
|
});
|
|
@@ -1039,6 +1281,7 @@ var AgentSession = class {
|
|
|
1039
1281
|
return rendered.trim();
|
|
1040
1282
|
}
|
|
1041
1283
|
cancelActive() {
|
|
1284
|
+
this.childStreams?.abortAll();
|
|
1042
1285
|
if (this.activeResponse) {
|
|
1043
1286
|
this.activeResponse.cancel().catch(() => {
|
|
1044
1287
|
});
|
|
@@ -1154,7 +1397,7 @@ var ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
|
1154
1397
|
"presentationKinds",
|
|
1155
1398
|
"ui"
|
|
1156
1399
|
]);
|
|
1157
|
-
function
|
|
1400
|
+
function isRecord4(value) {
|
|
1158
1401
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1159
1402
|
}
|
|
1160
1403
|
function isJsonValue2(value, seen = /* @__PURE__ */ new Set()) {
|
|
@@ -1181,7 +1424,7 @@ function decodeEnvelope(value) {
|
|
|
1181
1424
|
}
|
|
1182
1425
|
function parseAgentToolResultEnvelope(value) {
|
|
1183
1426
|
const decoded = decodeEnvelope(value);
|
|
1184
|
-
if (!
|
|
1427
|
+
if (!isRecord4(decoded)) return null;
|
|
1185
1428
|
const keys = Object.keys(decoded);
|
|
1186
1429
|
if (keys.some((key) => !ENVELOPE_KEYS.has(key)) || decoded.schemaVersion !== "webless.tool-result.v1" || !isJsonValue2(decoded.output) || !Array.isArray(decoded.presentationKinds) || decoded.presentationKinds.length < 1 || decoded.presentationKinds.length > 16) {
|
|
1187
1430
|
return null;
|