@webless/agent 0.6.8 → 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.
@@ -1291,6 +1291,203 @@ function clearPersistedAgentSession(visitorSessionId, options) {
1291
1291
  sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
1292
1292
  }
1293
1293
 
1294
+ // src/runtime/subagent-child-stream.ts
1295
+ var INITIAL_RETRY_DELAY_MS = 100;
1296
+ var MAX_RETRY_DELAY_MS = 2e3;
1297
+ var MAX_CONSECUTIVE_RETRIES = 6;
1298
+ function isRecord4(value) {
1299
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1300
+ }
1301
+ function parseError(value) {
1302
+ if (!isRecord4(value)) return void 0;
1303
+ const { code, message } = value;
1304
+ if (typeof code !== "string" || typeof message !== "string") {
1305
+ return void 0;
1306
+ }
1307
+ return { code, message };
1308
+ }
1309
+ function parseChildStreamEvent(value) {
1310
+ if (!isRecord4(value) || typeof value.type !== "string") {
1311
+ return { type: "other" };
1312
+ }
1313
+ if (value.type === "session.waiting" || value.type === "session.completed" || value.type === "session.failed") {
1314
+ return { type: "session.boundary" };
1315
+ }
1316
+ if (value.type === "subagent.event" && isRecord4(value.data) && Object.hasOwn(value.data, "event")) {
1317
+ return parseChildStreamEvent(value.data.event);
1318
+ }
1319
+ if (value.type === "subagent.called" && isRecord4(value.data)) {
1320
+ const { childStreamPath } = value.data;
1321
+ if (typeof childStreamPath === "string") {
1322
+ return { childStreamPath, type: "subagent.called" };
1323
+ }
1324
+ }
1325
+ if (value.type !== "action.result" || !isRecord4(value.data)) {
1326
+ return { type: "other" };
1327
+ }
1328
+ const { data } = value;
1329
+ if (data.status !== "completed" && data.status !== "failed" && data.status !== "rejected") {
1330
+ return { type: "other" };
1331
+ }
1332
+ if (!isRecord4(data.result) || data.result.kind !== "tool-result") {
1333
+ return { type: "other" };
1334
+ }
1335
+ const result = data.result;
1336
+ if (typeof result.callId !== "string" || typeof result.toolName !== "string") {
1337
+ return { type: "other" };
1338
+ }
1339
+ const error = parseError(data.error);
1340
+ return {
1341
+ type: "action.result",
1342
+ hasOutput: Object.hasOwn(result, "output"),
1343
+ result: {
1344
+ callId: result.callId,
1345
+ toolName: result.toolName,
1346
+ status: data.status,
1347
+ ...Object.hasOwn(result, "output") ? { output: result.output } : {},
1348
+ ...error ? { error } : {}
1349
+ }
1350
+ };
1351
+ }
1352
+ async function* readNdjsonStream(body) {
1353
+ const reader = body.getReader();
1354
+ const decoder = new TextDecoder();
1355
+ let buffer = "";
1356
+ try {
1357
+ while (true) {
1358
+ const { done, value } = await reader.read();
1359
+ buffer += decoder.decode(value, { stream: !done });
1360
+ const lines = buffer.split("\n");
1361
+ buffer = lines.pop() ?? "";
1362
+ for (const line of lines) {
1363
+ const trimmed2 = line.trim();
1364
+ if (!trimmed2) continue;
1365
+ try {
1366
+ const parsed = JSON.parse(trimmed2);
1367
+ yield parsed;
1368
+ } catch {
1369
+ }
1370
+ }
1371
+ if (done) break;
1372
+ }
1373
+ const trimmed = buffer.trim();
1374
+ if (trimmed) {
1375
+ try {
1376
+ const parsed = JSON.parse(trimmed);
1377
+ yield parsed;
1378
+ } catch {
1379
+ }
1380
+ }
1381
+ } finally {
1382
+ reader.releaseLock();
1383
+ }
1384
+ }
1385
+ function streamPathAt(path, streamIndex) {
1386
+ if (streamIndex === 0) return path;
1387
+ return `${path}${path.includes("?") ? "&" : "?"}startIndex=${streamIndex}`;
1388
+ }
1389
+ function abortableDelay(delayMs, signal) {
1390
+ if (signal.aborted) return Promise.resolve();
1391
+ return new Promise((resolve) => {
1392
+ const finish = () => {
1393
+ clearTimeout(timeout);
1394
+ signal.removeEventListener("abort", finish);
1395
+ resolve();
1396
+ };
1397
+ const timeout = setTimeout(finish, delayMs);
1398
+ signal.addEventListener("abort", finish, { once: true });
1399
+ });
1400
+ }
1401
+ var SubagentChildStreamCoordinator = class {
1402
+ constructor(client, handlers, parentSignal) {
1403
+ this.client = client;
1404
+ this.handlers = handlers;
1405
+ this.parentSignal = parentSignal;
1406
+ }
1407
+ client;
1408
+ handlers;
1409
+ parentSignal;
1410
+ controllers = /* @__PURE__ */ new Map();
1411
+ tasks = /* @__PURE__ */ new Map();
1412
+ begin(event) {
1413
+ this.beginPath(event.data.childStreamPath);
1414
+ }
1415
+ async waitForAll() {
1416
+ let observedTaskCount = -1;
1417
+ while (observedTaskCount !== this.tasks.size) {
1418
+ observedTaskCount = this.tasks.size;
1419
+ await Promise.all(this.tasks.values());
1420
+ }
1421
+ }
1422
+ abortAll() {
1423
+ for (const controller of this.controllers.values()) controller.abort();
1424
+ this.controllers.clear();
1425
+ }
1426
+ beginPath(childStreamPath) {
1427
+ if (this.tasks.has(childStreamPath)) return;
1428
+ const controller = new AbortController();
1429
+ const abort = () => controller.abort();
1430
+ if (this.parentSignal.aborted) {
1431
+ controller.abort();
1432
+ } else {
1433
+ this.parentSignal.addEventListener("abort", abort, { once: true });
1434
+ }
1435
+ this.controllers.set(childStreamPath, controller);
1436
+ const task = this.consume(childStreamPath, controller.signal).finally(
1437
+ () => {
1438
+ this.parentSignal.removeEventListener("abort", abort);
1439
+ if (this.controllers.get(childStreamPath) === controller) {
1440
+ this.controllers.delete(childStreamPath);
1441
+ }
1442
+ }
1443
+ );
1444
+ this.tasks.set(childStreamPath, task);
1445
+ }
1446
+ async consume(path, signal) {
1447
+ let streamIndex = 0;
1448
+ let consecutiveRetries = 0;
1449
+ let retryDelayMs = INITIAL_RETRY_DELAY_MS;
1450
+ while (!signal.aborted && consecutiveRetries < MAX_CONSECUTIVE_RETRIES) {
1451
+ let receivedEvent = false;
1452
+ try {
1453
+ const response = await this.client.fetch(
1454
+ streamPathAt(path, streamIndex),
1455
+ {
1456
+ cache: "no-store",
1457
+ signal
1458
+ }
1459
+ );
1460
+ if (!response.ok || response.body === null) {
1461
+ await response.body?.cancel().catch(() => {
1462
+ });
1463
+ throw new Error(`Child stream returned ${response.status}.`);
1464
+ }
1465
+ for await (const rawEvent of readNdjsonStream(response.body)) {
1466
+ if (signal.aborted) return;
1467
+ receivedEvent = true;
1468
+ streamIndex += 1;
1469
+ const event = parseChildStreamEvent(rawEvent);
1470
+ if (event.type === "session.boundary") return;
1471
+ if (event.type === "subagent.called") {
1472
+ this.beginPath(event.childStreamPath);
1473
+ continue;
1474
+ }
1475
+ if (event.type !== "action.result") continue;
1476
+ this.handlers.onToolResult?.(event.result);
1477
+ if (event.hasOutput) {
1478
+ this.handlers.onActionResult?.(event.result.output);
1479
+ }
1480
+ }
1481
+ } catch {
1482
+ if (signal.aborted) return;
1483
+ }
1484
+ consecutiveRetries = receivedEvent ? 0 : consecutiveRetries + 1;
1485
+ retryDelayMs = receivedEvent ? INITIAL_RETRY_DELAY_MS : Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
1486
+ await abortableDelay(retryDelayMs, signal);
1487
+ }
1488
+ }
1489
+ };
1490
+
1294
1491
  // src/runtime/client.ts
1295
1492
  function isTurnBoundary(event) {
1296
1493
  return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
@@ -1532,11 +1729,14 @@ var AgentSession = class {
1532
1729
  clientHost;
1533
1730
  session;
1534
1731
  activeResponse;
1732
+ childStreams;
1535
1733
  capability;
1536
1734
  getActiveSessionId() {
1537
1735
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
1538
1736
  }
1539
1737
  reset() {
1738
+ this.childStreams?.abortAll();
1739
+ this.childStreams = void 0;
1540
1740
  if (this.activeResponse) {
1541
1741
  void this.activeResponse.cancel().catch(() => {
1542
1742
  });
@@ -1632,6 +1832,12 @@ var AgentSession = class {
1632
1832
  this.persistSessionCursor(session);
1633
1833
  }
1634
1834
  this.activeResponse = response;
1835
+ const childStreams = new SubagentChildStreamCoordinator(
1836
+ client,
1837
+ handlers,
1838
+ signal
1839
+ );
1840
+ this.childStreams = childStreams;
1635
1841
  let streamIndex = session?.state.streamIndex ?? 0;
1636
1842
  let rendered = "";
1637
1843
  const workItems = /* @__PURE__ */ new Map();
@@ -1639,6 +1845,7 @@ var AgentSession = class {
1639
1845
  try {
1640
1846
  for await (const event of response) {
1641
1847
  if (signal.aborted) break;
1848
+ if (event.type === "subagent.called") childStreams.begin(event);
1642
1849
  if (event.type === "input.requested") requestedInput = true;
1643
1850
  rendered = applyMessageEvent(event, rendered, handlers, workItems);
1644
1851
  streamIndex += 1;
@@ -1651,8 +1858,11 @@ var AgentSession = class {
1651
1858
  );
1652
1859
  }
1653
1860
  }
1861
+ await childStreams.waitForAll();
1654
1862
  } finally {
1863
+ childStreams.abortAll();
1655
1864
  this.activeResponse = void 0;
1865
+ if (this.childStreams === childStreams) this.childStreams = void 0;
1656
1866
  if (session) {
1657
1867
  this.persistSessionCursor(session);
1658
1868
  }
@@ -1688,63 +1898,80 @@ var AgentSession = class {
1688
1898
  }
1689
1899
  let rendered = renderTurn(turnEvents);
1690
1900
  const workItems = /* @__PURE__ */ new Map();
1691
- for (const event of turnEvents) {
1692
- applyWorkEvent(event, handlers, workItems);
1693
- emitVisitorInteractionEvent(event, handlers);
1694
- }
1695
- if (rendered.startsWith(initialText)) {
1696
- const missedText = rendered.slice(initialText.length);
1697
- if (missedText) handlers.onDelta(missedText);
1698
- } else if (initialText.startsWith(rendered)) {
1699
- rendered = initialText;
1700
- } else if (!initialText.startsWith(rendered)) {
1701
- rendered = initialText + rendered;
1702
- }
1703
- let session = client.sessions.attach(snapshot.session.sessionId, {
1704
- streamIndex: snapshot.session.streamIndex
1705
- });
1706
- this.session = session;
1707
- this.persistSessionCursor(session);
1708
- let snapshotBoundary;
1709
- for (let index = turnEvents.length - 1; index >= 0; index -= 1) {
1710
- const event = turnEvents[index];
1711
- if (event && isTurnBoundary(event)) {
1712
- snapshotBoundary = event;
1713
- break;
1901
+ const childStreams = new SubagentChildStreamCoordinator(
1902
+ client,
1903
+ handlers,
1904
+ signal
1905
+ );
1906
+ this.childStreams = childStreams;
1907
+ try {
1908
+ for (const event of turnEvents) {
1909
+ if (event.type === "subagent.called") childStreams.begin(event);
1910
+ applyWorkEvent(event, handlers, workItems);
1911
+ emitVisitorInteractionEvent(event, handlers);
1714
1912
  }
1715
- }
1716
- if (snapshotBoundary) {
1717
- if (snapshotBoundary.type === "session.failed") {
1718
- throw new Error(
1719
- snapshotBoundary.data.message || snapshotBoundary.data.code
1913
+ if (rendered.startsWith(initialText)) {
1914
+ const missedText = rendered.slice(initialText.length);
1915
+ if (missedText) handlers.onDelta(missedText);
1916
+ } else if (initialText.startsWith(rendered)) {
1917
+ rendered = initialText;
1918
+ } else if (!initialText.startsWith(rendered)) {
1919
+ rendered = initialText + rendered;
1920
+ }
1921
+ let session = client.sessions.attach(snapshot.session.sessionId, {
1922
+ streamIndex: snapshot.session.streamIndex
1923
+ });
1924
+ this.session = session;
1925
+ this.persistSessionCursor(session);
1926
+ let snapshotBoundary;
1927
+ for (let index = turnEvents.length - 1; index >= 0; index -= 1) {
1928
+ const event = turnEvents[index];
1929
+ if (event && isTurnBoundary(event)) {
1930
+ snapshotBoundary = event;
1931
+ break;
1932
+ }
1933
+ }
1934
+ if (snapshotBoundary) {
1935
+ if (snapshotBoundary.type === "session.failed") {
1936
+ throw new Error(
1937
+ snapshotBoundary.data.message || snapshotBoundary.data.code
1938
+ );
1939
+ }
1940
+ handlers.onComplete?.();
1941
+ await childStreams.waitForAll();
1942
+ if (!rendered.trim() && !hasInputRequest) {
1943
+ throw new Error("Empty response from runtime");
1944
+ }
1945
+ return rendered.trim();
1946
+ }
1947
+ let streamIndex = snapshot.session.streamIndex;
1948
+ for await (const event of session.stream({ signal })) {
1949
+ if (signal.aborted) break;
1950
+ if (event.type === "subagent.called") childStreams.begin(event);
1951
+ rendered = applyMessageEvent(event, rendered, handlers, workItems);
1952
+ streamIndex += 1;
1953
+ savePersistedAgentSession(
1954
+ this.visitorSessionId,
1955
+ session.state.sessionId,
1956
+ streamIndex,
1957
+ this.storeOptions
1720
1958
  );
1959
+ if (isTurnBoundary(event)) break;
1721
1960
  }
1722
- handlers.onComplete?.();
1723
- if (!rendered.trim() && !hasInputRequest) {
1961
+ await childStreams.waitForAll();
1962
+ session = client.sessions.attach(session.state.sessionId, {
1963
+ streamIndex
1964
+ });
1965
+ this.session = session;
1966
+ this.persistSessionCursor(session);
1967
+ if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
1724
1968
  throw new Error("Empty response from runtime");
1725
1969
  }
1726
1970
  return rendered.trim();
1971
+ } finally {
1972
+ childStreams.abortAll();
1973
+ if (this.childStreams === childStreams) this.childStreams = void 0;
1727
1974
  }
1728
- let streamIndex = snapshot.session.streamIndex;
1729
- for await (const event of session.stream({ signal })) {
1730
- if (signal.aborted) break;
1731
- rendered = applyMessageEvent(event, rendered, handlers, workItems);
1732
- streamIndex += 1;
1733
- savePersistedAgentSession(
1734
- this.visitorSessionId,
1735
- session.state.sessionId,
1736
- streamIndex,
1737
- this.storeOptions
1738
- );
1739
- if (isTurnBoundary(event)) break;
1740
- }
1741
- session = client.sessions.attach(session.state.sessionId, { streamIndex });
1742
- this.session = session;
1743
- this.persistSessionCursor(session);
1744
- if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
1745
- throw new Error("Empty response from runtime");
1746
- }
1747
- return rendered.trim();
1748
1975
  }
1749
1976
  async respondTurn(responses, signal, handlers) {
1750
1977
  const client = this.ensureClient();
@@ -1765,6 +1992,12 @@ var AgentSession = class {
1765
1992
  () => session.respond(inputResponses, { signal })
1766
1993
  );
1767
1994
  this.activeResponse = response;
1995
+ const childStreams = new SubagentChildStreamCoordinator(
1996
+ client,
1997
+ handlers,
1998
+ signal
1999
+ );
2000
+ this.childStreams = childStreams;
1768
2001
  let streamIndex = session.state.streamIndex;
1769
2002
  let rendered = "";
1770
2003
  let requestedInput = false;
@@ -1772,6 +2005,7 @@ var AgentSession = class {
1772
2005
  try {
1773
2006
  for await (const event of response) {
1774
2007
  if (signal.aborted) break;
2008
+ if (event.type === "subagent.called") childStreams.begin(event);
1775
2009
  if (event.type === "input.requested") requestedInput = true;
1776
2010
  rendered = applyMessageEvent(event, rendered, handlers, workItems);
1777
2011
  streamIndex += 1;
@@ -1782,8 +2016,11 @@ var AgentSession = class {
1782
2016
  this.storeOptions
1783
2017
  );
1784
2018
  }
2019
+ await childStreams.waitForAll();
1785
2020
  } finally {
2021
+ childStreams.abortAll();
1786
2022
  this.activeResponse = void 0;
2023
+ if (this.childStreams === childStreams) this.childStreams = void 0;
1787
2024
  this.session = client.sessions.attach(session.state.sessionId, {
1788
2025
  streamIndex
1789
2026
  });
@@ -1795,6 +2032,7 @@ var AgentSession = class {
1795
2032
  return rendered.trim();
1796
2033
  }
1797
2034
  cancelActive() {
2035
+ this.childStreams?.abortAll();
1798
2036
  if (this.activeResponse) {
1799
2037
  this.activeResponse.cancel().catch(() => {
1800
2038
  });
@@ -3869,7 +4107,7 @@ function ConfirmationCard({
3869
4107
  // src/react/components/ToolInputCard/ToolInputCard.tsx
3870
4108
  import { useState as useState6 } from "react";
3871
4109
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
3872
- function isRecord4(value) {
4110
+ function isRecord5(value) {
3873
4111
  return value !== null && typeof value === "object" && !Array.isArray(value);
3874
4112
  }
3875
4113
  function pathSegments(path) {
@@ -3883,7 +4121,7 @@ function valueAtPath(root, path) {
3883
4121
  current = Number.isInteger(index) ? current[index] : void 0;
3884
4122
  continue;
3885
4123
  }
3886
- current = isRecord4(current) ? current[segment] : void 0;
4124
+ current = isRecord5(current) ? current[segment] : void 0;
3887
4125
  }
3888
4126
  return current;
3889
4127
  }
@@ -3906,7 +4144,7 @@ function initialValues(surface) {
3906
4144
  }
3907
4145
  function cloneJsonValue(value) {
3908
4146
  if (Array.isArray(value)) return value.map(cloneJsonValue);
3909
- if (isRecord4(value)) {
4147
+ if (isRecord5(value)) {
3910
4148
  return Object.fromEntries(
3911
4149
  Object.entries(value).map(([key, item]) => [key, cloneJsonValue(item)])
3912
4150
  );
@@ -3922,7 +4160,7 @@ function assignPath(target, path, value) {
3922
4160
  return;
3923
4161
  }
3924
4162
  const existing = current[segment];
3925
- if (!isRecord4(existing)) current[segment] = {};
4163
+ if (!isRecord5(existing)) current[segment] = {};
3926
4164
  current = current[segment];
3927
4165
  });
3928
4166
  }
@@ -4218,7 +4456,7 @@ function ToolInputCard({
4218
4456
  (field) => validateField(field, values[field.path] ?? "")
4219
4457
  );
4220
4458
  if (nextErrors.some(Boolean)) return;
4221
- const result = isRecord4(surface.values) ? cloneJsonValue(surface.values) : {};
4459
+ const result = isRecord5(surface.values) ? cloneJsonValue(surface.values) : {};
4222
4460
  for (const field of surface.fields) {
4223
4461
  const parsed = parsedFieldValue(field, values[field.path] ?? "");
4224
4462
  if (parsed !== void 0) assignPath(result, field.path, parsed);
@@ -5419,4 +5657,4 @@ export {
5419
5657
  AssistEdgeTab,
5420
5658
  AgentWidget
5421
5659
  };
5422
- //# sourceMappingURL=chunk-CXIKIQJL.js.map
5660
+ //# sourceMappingURL=chunk-U2SU2CGF.js.map