@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.
@@ -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";
@@ -1303,8 +1500,7 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
1303
1500
  if (event.type === "message.completed") {
1304
1501
  handlers.onComplete?.();
1305
1502
  }
1306
- if (event.type === "action.result") emitActionResult(event, handlers);
1307
- if (event.type === "input.requested") emitInputRequests(event, handlers);
1503
+ emitVisitorInteractionEvent(event, handlers);
1308
1504
  if (event.type !== "message.appended") return rendered;
1309
1505
  const { messageDelta, messageSoFar } = event.data;
1310
1506
  let delta = messageDelta;
@@ -1354,6 +1550,13 @@ function emitInputRequests(event, handlers) {
1354
1550
  })
1355
1551
  );
1356
1552
  }
1553
+ function emitVisitorInteractionEvent(event, handlers) {
1554
+ if (event.type === "action.result") emitActionResult(event, handlers);
1555
+ if (event.type === "input.requested") emitInputRequests(event, handlers);
1556
+ if (event.type === "subagent.event") {
1557
+ emitVisitorInteractionEvent(event.data.event, handlers);
1558
+ }
1559
+ }
1357
1560
  function isResumeTurnMessage(received, candidate) {
1358
1561
  if (received === candidate) return true;
1359
1562
  return Boolean(candidate) && received.endsWith(`
@@ -1526,11 +1729,14 @@ var AgentSession = class {
1526
1729
  clientHost;
1527
1730
  session;
1528
1731
  activeResponse;
1732
+ childStreams;
1529
1733
  capability;
1530
1734
  getActiveSessionId() {
1531
1735
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
1532
1736
  }
1533
1737
  reset() {
1738
+ this.childStreams?.abortAll();
1739
+ this.childStreams = void 0;
1534
1740
  if (this.activeResponse) {
1535
1741
  void this.activeResponse.cancel().catch(() => {
1536
1742
  });
@@ -1626,6 +1832,12 @@ var AgentSession = class {
1626
1832
  this.persistSessionCursor(session);
1627
1833
  }
1628
1834
  this.activeResponse = response;
1835
+ const childStreams = new SubagentChildStreamCoordinator(
1836
+ client,
1837
+ handlers,
1838
+ signal
1839
+ );
1840
+ this.childStreams = childStreams;
1629
1841
  let streamIndex = session?.state.streamIndex ?? 0;
1630
1842
  let rendered = "";
1631
1843
  const workItems = /* @__PURE__ */ new Map();
@@ -1633,6 +1845,7 @@ var AgentSession = class {
1633
1845
  try {
1634
1846
  for await (const event of response) {
1635
1847
  if (signal.aborted) break;
1848
+ if (event.type === "subagent.called") childStreams.begin(event);
1636
1849
  if (event.type === "input.requested") requestedInput = true;
1637
1850
  rendered = applyMessageEvent(event, rendered, handlers, workItems);
1638
1851
  streamIndex += 1;
@@ -1645,8 +1858,11 @@ var AgentSession = class {
1645
1858
  );
1646
1859
  }
1647
1860
  }
1861
+ await childStreams.waitForAll();
1648
1862
  } finally {
1863
+ childStreams.abortAll();
1649
1864
  this.activeResponse = void 0;
1865
+ if (this.childStreams === childStreams) this.childStreams = void 0;
1650
1866
  if (session) {
1651
1867
  this.persistSessionCursor(session);
1652
1868
  }
@@ -1682,64 +1898,80 @@ var AgentSession = class {
1682
1898
  }
1683
1899
  let rendered = renderTurn(turnEvents);
1684
1900
  const workItems = /* @__PURE__ */ new Map();
1685
- for (const event of turnEvents) {
1686
- applyWorkEvent(event, handlers, workItems);
1687
- if (event.type === "input.requested") emitInputRequests(event, handlers);
1688
- if (event.type === "action.result") emitActionResult(event, handlers);
1689
- }
1690
- if (rendered.startsWith(initialText)) {
1691
- const missedText = rendered.slice(initialText.length);
1692
- if (missedText) handlers.onDelta(missedText);
1693
- } else if (initialText.startsWith(rendered)) {
1694
- rendered = initialText;
1695
- } else if (!initialText.startsWith(rendered)) {
1696
- rendered = initialText + rendered;
1697
- }
1698
- let session = client.sessions.attach(snapshot.session.sessionId, {
1699
- streamIndex: snapshot.session.streamIndex
1700
- });
1701
- this.session = session;
1702
- this.persistSessionCursor(session);
1703
- let snapshotBoundary;
1704
- for (let index = turnEvents.length - 1; index >= 0; index -= 1) {
1705
- const event = turnEvents[index];
1706
- if (event && isTurnBoundary(event)) {
1707
- snapshotBoundary = event;
1708
- 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);
1709
1912
  }
1710
- }
1711
- if (snapshotBoundary) {
1712
- if (snapshotBoundary.type === "session.failed") {
1713
- throw new Error(
1714
- 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
1715
1958
  );
1959
+ if (isTurnBoundary(event)) break;
1716
1960
  }
1717
- handlers.onComplete?.();
1718
- 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) {
1719
1968
  throw new Error("Empty response from runtime");
1720
1969
  }
1721
1970
  return rendered.trim();
1971
+ } finally {
1972
+ childStreams.abortAll();
1973
+ if (this.childStreams === childStreams) this.childStreams = void 0;
1722
1974
  }
1723
- let streamIndex = snapshot.session.streamIndex;
1724
- for await (const event of session.stream({ signal })) {
1725
- if (signal.aborted) break;
1726
- rendered = applyMessageEvent(event, rendered, handlers, workItems);
1727
- streamIndex += 1;
1728
- savePersistedAgentSession(
1729
- this.visitorSessionId,
1730
- session.state.sessionId,
1731
- streamIndex,
1732
- this.storeOptions
1733
- );
1734
- if (isTurnBoundary(event)) break;
1735
- }
1736
- session = client.sessions.attach(session.state.sessionId, { streamIndex });
1737
- this.session = session;
1738
- this.persistSessionCursor(session);
1739
- if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
1740
- throw new Error("Empty response from runtime");
1741
- }
1742
- return rendered.trim();
1743
1975
  }
1744
1976
  async respondTurn(responses, signal, handlers) {
1745
1977
  const client = this.ensureClient();
@@ -1760,6 +1992,12 @@ var AgentSession = class {
1760
1992
  () => session.respond(inputResponses, { signal })
1761
1993
  );
1762
1994
  this.activeResponse = response;
1995
+ const childStreams = new SubagentChildStreamCoordinator(
1996
+ client,
1997
+ handlers,
1998
+ signal
1999
+ );
2000
+ this.childStreams = childStreams;
1763
2001
  let streamIndex = session.state.streamIndex;
1764
2002
  let rendered = "";
1765
2003
  let requestedInput = false;
@@ -1767,6 +2005,7 @@ var AgentSession = class {
1767
2005
  try {
1768
2006
  for await (const event of response) {
1769
2007
  if (signal.aborted) break;
2008
+ if (event.type === "subagent.called") childStreams.begin(event);
1770
2009
  if (event.type === "input.requested") requestedInput = true;
1771
2010
  rendered = applyMessageEvent(event, rendered, handlers, workItems);
1772
2011
  streamIndex += 1;
@@ -1777,8 +2016,11 @@ var AgentSession = class {
1777
2016
  this.storeOptions
1778
2017
  );
1779
2018
  }
2019
+ await childStreams.waitForAll();
1780
2020
  } finally {
2021
+ childStreams.abortAll();
1781
2022
  this.activeResponse = void 0;
2023
+ if (this.childStreams === childStreams) this.childStreams = void 0;
1782
2024
  this.session = client.sessions.attach(session.state.sessionId, {
1783
2025
  streamIndex
1784
2026
  });
@@ -1790,6 +2032,7 @@ var AgentSession = class {
1790
2032
  return rendered.trim();
1791
2033
  }
1792
2034
  cancelActive() {
2035
+ this.childStreams?.abortAll();
1793
2036
  if (this.activeResponse) {
1794
2037
  this.activeResponse.cancel().catch(() => {
1795
2038
  });
@@ -3864,7 +4107,7 @@ function ConfirmationCard({
3864
4107
  // src/react/components/ToolInputCard/ToolInputCard.tsx
3865
4108
  import { useState as useState6 } from "react";
3866
4109
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
3867
- function isRecord4(value) {
4110
+ function isRecord5(value) {
3868
4111
  return value !== null && typeof value === "object" && !Array.isArray(value);
3869
4112
  }
3870
4113
  function pathSegments(path) {
@@ -3878,7 +4121,7 @@ function valueAtPath(root, path) {
3878
4121
  current = Number.isInteger(index) ? current[index] : void 0;
3879
4122
  continue;
3880
4123
  }
3881
- current = isRecord4(current) ? current[segment] : void 0;
4124
+ current = isRecord5(current) ? current[segment] : void 0;
3882
4125
  }
3883
4126
  return current;
3884
4127
  }
@@ -3901,7 +4144,7 @@ function initialValues(surface) {
3901
4144
  }
3902
4145
  function cloneJsonValue(value) {
3903
4146
  if (Array.isArray(value)) return value.map(cloneJsonValue);
3904
- if (isRecord4(value)) {
4147
+ if (isRecord5(value)) {
3905
4148
  return Object.fromEntries(
3906
4149
  Object.entries(value).map(([key, item]) => [key, cloneJsonValue(item)])
3907
4150
  );
@@ -3917,7 +4160,7 @@ function assignPath(target, path, value) {
3917
4160
  return;
3918
4161
  }
3919
4162
  const existing = current[segment];
3920
- if (!isRecord4(existing)) current[segment] = {};
4163
+ if (!isRecord5(existing)) current[segment] = {};
3921
4164
  current = current[segment];
3922
4165
  });
3923
4166
  }
@@ -4213,7 +4456,7 @@ function ToolInputCard({
4213
4456
  (field) => validateField(field, values[field.path] ?? "")
4214
4457
  );
4215
4458
  if (nextErrors.some(Boolean)) return;
4216
- const result = isRecord4(surface.values) ? cloneJsonValue(surface.values) : {};
4459
+ const result = isRecord5(surface.values) ? cloneJsonValue(surface.values) : {};
4217
4460
  for (const field of surface.fields) {
4218
4461
  const parsed = parsedFieldValue(field, values[field.path] ?? "");
4219
4462
  if (parsed !== void 0) assignPath(result, field.path, parsed);
@@ -5414,4 +5657,4 @@ export {
5414
5657
  AssistEdgeTab,
5415
5658
  AgentWidget
5416
5659
  };
5417
- //# sourceMappingURL=chunk-U5IPERNA.js.map
5660
+ //# sourceMappingURL=chunk-U2SU2CGF.js.map