eve 0.44.0 → 0.44.2

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/src/chunks/{use-eve-agent-C0SG1Ttk.js → use-eve-agent-BsLJiIbc.js} +224 -62
  3. package/dist/src/chunks/{use-eve-agent-D71oDil7.js → use-eve-agent-DpUEwS23.js} +222 -62
  4. package/dist/src/cli/commands/extension-init.js +1 -1
  5. package/dist/src/cli/commands/init.js +1 -1
  6. package/dist/src/cli/dev/tui/setup-commands.js +1 -1
  7. package/dist/src/client/eve-agent-store-helpers.d.ts +12 -0
  8. package/dist/src/client/eve-agent-store-helpers.js +2 -0
  9. package/dist/src/client/eve-agent-store.d.ts +8 -6
  10. package/dist/src/client/eve-agent-store.js +1 -2
  11. package/dist/src/client/ndjson.d.ts +3 -1
  12. package/dist/src/client/ndjson.js +3 -3
  13. package/dist/src/client/open-stream.d.ts +3 -0
  14. package/dist/src/client/open-stream.js +1 -1
  15. package/dist/src/context/build-dynamic-tools.js +1 -1
  16. package/dist/src/context/dynamic-tool-lifecycle.d.ts +5 -1
  17. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  18. package/dist/src/context/keys.d.ts +4 -2
  19. package/dist/src/context/keys.js +1 -1
  20. package/dist/src/execution/cancel-descendant-turns-step.js +1 -1
  21. package/dist/src/execution/dispatch-turn-step.js +1 -1
  22. package/dist/src/execution/eve-workflow-attributes.d.ts +3 -0
  23. package/dist/src/execution/eve-workflow-attributes.js +1 -1
  24. package/dist/src/execution/pending-turn-state.d.ts +9 -0
  25. package/dist/src/execution/pending-turn-state.js +1 -0
  26. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  27. package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
  28. package/dist/src/execution/turn-workflow.js +1 -1
  29. package/dist/src/execution/workflow-steps.js +1 -1
  30. package/dist/src/harness/channel-delivery-instrumentation.js +1 -1
  31. package/dist/src/harness/instrumentation/content-policy.d.ts +6 -0
  32. package/dist/src/harness/instrumentation/content-policy.js +1 -0
  33. package/dist/src/harness/instrumentation/lifecycle.d.ts +2 -0
  34. package/dist/src/harness/instrumentation/runtime-context.js +1 -1
  35. package/dist/src/harness/runtime-actions.js +1 -1
  36. package/dist/src/harness/tool-loop.js +2 -2
  37. package/dist/src/internal/application/package.js +1 -1
  38. package/dist/src/internal/workflow-bundle/dynamic-remote-agent-transform.js +2 -2
  39. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +4 -4
  40. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +3 -4
  41. package/dist/src/internal/workflow-bundle/stable-module-id.d.ts +6 -0
  42. package/dist/src/internal/workflow-bundle/stable-module-id.js +1 -0
  43. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  44. package/dist/src/public/channels/eve.js +3 -3
  45. package/dist/src/public/tools/approval/approval-helpers.js +1 -1
  46. package/dist/src/react/use-eve-agent.d.ts +10 -1
  47. package/dist/src/react/use-eve-agent.js +1 -1
  48. package/dist/src/runtime/attributes/emit.d.ts +1 -1
  49. package/dist/src/runtime/attributes/normalize.d.ts +1 -1
  50. package/dist/src/runtime/attributes/normalize.js +1 -1
  51. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +2 -2
  52. package/dist/src/runtime/framework-tools/subagent/local.js +1 -1
  53. package/dist/src/runtime/sessions/runtime-context-keys.js +1 -1
  54. package/dist/src/setup/integrations/linq/connect.js +1 -1
  55. package/dist/src/setup/primitives/pm/npm.js +1 -1
  56. package/dist/src/setup/primitives/pm/pnpm.js +5 -9
  57. package/dist/src/setup/primitives/pm/types.d.ts +0 -2
  58. package/dist/src/setup/scaffold/create/project.js +1 -1
  59. package/dist/src/setup/scaffold/create/web-template.d.ts +9 -4
  60. package/dist/src/setup/scaffold/create/web-template.js +296 -73
  61. package/dist/src/shared/durable-dynamic-tool-callbacks.d.ts +43 -19
  62. package/dist/src/shared/durable-dynamic-tool-callbacks.js +1 -1
  63. package/dist/src/svelte/index.js +1 -1
  64. package/dist/src/svelte/use-eve-agent.d.ts +4 -0
  65. package/dist/src/svelte/use-eve-agent.js +1 -1
  66. package/dist/src/tasks/delivery-context.d.ts +9 -0
  67. package/dist/src/tasks/delivery-context.js +5 -2
  68. package/dist/src/vue/index.js +1 -1
  69. package/dist/src/vue/use-eve-agent.d.ts +4 -0
  70. package/dist/src/vue/use-eve-agent.js +1 -1
  71. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  72. package/docs/guides/dynamic-capabilities.md +10 -2
  73. package/docs/guides/frontend/nextjs.mdx +23 -0
  74. package/docs/guides/frontend/overview.mdx +24 -9
  75. package/docs/guides/frontend/use-eve-agent-svelte.mdx +9 -9
  76. package/docs/guides/frontend/use-eve-agent-vue.mdx +9 -9
  77. package/docs/tools/overview.mdx +5 -4
  78. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # eve
2
2
 
3
+ ## 0.44.2
4
+
5
+ ### Patch Changes
6
+
7
+ - e79dd2f: Generated Web Chat apps now include their new-session and resumable-session routes. Abandoned browser session streams also release their local workflow listeners instead of accumulating them across navigation.
8
+
9
+ ## 0.44.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 7c99773: Linq setup now recognizes Vercel CLI versions that lack required trigger options and offers to upgrade the CLI from the `/add` flow instead of reporting a generic connector failure.
14
+ - 02403b9: Dynamic tool callbacks are now identified by tool name and phase instead of byte offsets in the authored source. Editing an agent file no longer risks a parked approval replaying the wrong tool: after a redeploy or crash, parked calls run the latest deployed callback code under the same name, and a tool that no longer exists fails closed with an explicit error. Session-scoped resolvers may run once more on resume to rebind callbacks, so keep them idempotent.
15
+ - 84ddb09: Apply release-age policies during project and extension setup instead of bypassing them. New standalone pnpm projects use strict enforcement, while projects inside an existing workspace retain that workspace's policy.
16
+ - 85b2dc8: Redact model, tool, approval, and delivery content from hosted instrumentation for private and unknown channel audiences. Content-denied Workflow runs now carry a metadata marker and omit their content-derived title so dashboards can exclude them without reading the event stream. Local `eve dev` tracing continues to retain content for unknown TUI and HTTP sessions.
17
+ - a4fd288: Add declarative `resume: true` and imperative `resume()` support for replaying durable frontend sessions and following in-flight turns. Generated Web Chat apps now keep session IDs in `/s/{sessionId}` URLs, restore conversations on reload, and provide a sessionless `/s` route for starting a new chat.
18
+ - 923921c: Give models runtime-authored task state after background work starts so initiating turns acknowledge launch, partial task wakes stay silent, and settled cohorts produce one combined report.
19
+ - 673def2: Allow `useEveAgent` message sends with `turnPolicy: "steer"` while a turn is active, keeping the local projection attached to the durable replacement stream. Generated web chats now keep the composer enabled during responses and steer by default when a follow-up is submitted. Cancelled turns preserve their accepted user input in durable history, so replacement turns retain the interrupted request as context.
20
+
3
21
  ## 0.44.0
4
22
 
5
23
  ### Minor Changes
@@ -527,14 +527,14 @@ function isStreamDisconnectError(error) {
527
527
  const errorCode = "code" in error && typeof error.code === "string" ? error.code : void 0;
528
528
  return error.name === "AbortError" || error.message === "terminated" || errorCode === "UND_ERR_SOCKET" || error instanceof TypeError && /^(?:failed to fetch|fetch failed)$/i.test(error.message) || /abort|cancel|disconnect|premature close|socket|terminated/i.test(error.message);
529
529
  }
530
- async function* readNdjsonStream(body) {
530
+ async function* readNdjsonStream(body, options) {
531
531
  const reader = body.getReader();
532
532
  const decoder = new TextDecoder();
533
533
  let buffer = "";
534
534
  let reachedEof = false;
535
535
  try {
536
536
  while (true) {
537
- const result = await reader.read();
537
+ const result = await readWithIdleTimeout(reader, options?.idleTimeoutMs);
538
538
  if (result.done) {
539
539
  reachedEof = true;
540
540
  buffer += decoder.decode();
@@ -556,6 +556,17 @@ async function* readNdjsonStream(body) {
556
556
  reader.releaseLock();
557
557
  }
558
558
  }
559
+ async function readWithIdleTimeout(reader, idleTimeoutMs) {
560
+ if (idleTimeoutMs === void 0) return await reader.read();
561
+ let timeout;
562
+ try {
563
+ return await Promise.race([reader.read(), new Promise((_resolve, reject) => {
564
+ timeout = setTimeout(() => reject(new DOMException("Session stream was idle.", "AbortError")), idleTimeoutMs);
565
+ })]);
566
+ } finally {
567
+ if (timeout !== void 0) clearTimeout(timeout);
568
+ }
569
+ }
559
570
 
560
571
  //#endregion
561
572
  //#region src/client/url.ts
@@ -600,6 +611,7 @@ function formatSearch(searchParams) {
600
611
 
601
612
  //#endregion
602
613
  //#region src/client/open-stream.ts
614
+ const DEFAULT_STREAM_READ_IDLE_TIMEOUT_MS = 15e3;
603
615
  const DEFAULT_STREAM_RECONNECT_POLICY = {
604
616
  retryableErrorStatuses: /* @__PURE__ */ new Set([
605
617
  404,
@@ -672,17 +684,17 @@ async function* followStreamIterable(input) {
672
684
  if (input.follow === false && tailIndex === void 0) {
673
685
  tailIndex = connection.tailIndex;
674
686
  if (tailIndex === void 0) {
675
- await connection.body.cancel().catch(() => {});
687
+ connection.close();
676
688
  throw new Error(`stream({ follow: false }) requires the server to report the ${EVE_STREAM_TAIL_INDEX_HEADER} header. The agent may be running an older eve version.`);
677
689
  }
678
690
  }
679
691
  if (tailIndex !== void 0 && startIndex > tailIndex) {
680
- await connection.body.cancel().catch(() => {});
692
+ connection.close();
681
693
  return;
682
694
  }
683
695
  let deliveredEvent = false;
684
696
  try {
685
- for await (const event of readNdjsonStream(connection.body)) {
697
+ for await (const event of readNdjsonStream(connection.body, { idleTimeoutMs: input.streamReadIdleTimeoutMs ?? DEFAULT_STREAM_READ_IDLE_TIMEOUT_MS })) {
686
698
  startIndex += 1;
687
699
  deliveredEvent = true;
688
700
  reconnectDelayMs = idleRetryPolicy.baseDelayMs;
@@ -692,6 +704,8 @@ async function* followStreamIterable(input) {
692
704
  }
693
705
  } catch (error) {
694
706
  if (!isStreamDisconnectError(error)) throw error;
707
+ } finally {
708
+ connection.close();
695
709
  }
696
710
  if (input.signal?.aborted || input.startIndex < 0 || idleRetryPolicy.maxAttempts === 0) return;
697
711
  if (input.keepAlive !== true && !deliveredEvent && !initialConnection && (idleReconnects += 1) >= idleRetryPolicy.maxAttempts) return;
@@ -714,13 +728,15 @@ async function openStreamBody(input) {
714
728
  for (let attempt = 0; attempt < openRetryPolicy.maxAttempts; attempt += 1) {
715
729
  const url = createClientUrl(input.host, createEveSessionStreamRoutePath(input.sessionId), Object.keys(searchParams).length > 0 ? searchParams : void 0);
716
730
  const headers = await input.resolveHeaders();
731
+ const connectionController = new AbortController();
732
+ const signal = input.signal ? AbortSignal.any([input.signal, connectionController.signal]) : connectionController.signal;
717
733
  let response;
718
734
  try {
719
735
  response = await fetch(url, {
720
736
  cache: "no-store",
721
737
  headers,
722
738
  redirect: input.redirect,
723
- signal: input.signal ?? null
739
+ signal
724
740
  });
725
741
  } catch (error) {
726
742
  if (input.signal?.aborted || !isStreamDisconnectError(error) || attempt === openRetryPolicy.maxAttempts - 1) throw error;
@@ -732,6 +748,7 @@ async function openStreamBody(input) {
732
748
  if (!response.body) throw new ClientError(response.status, "Response body is null.", response.headers);
733
749
  return {
734
750
  body: response.body,
751
+ close: () => connectionController.abort(),
735
752
  tailIndex: parseTailIndexHeader(response.headers)
736
753
  };
737
754
  }
@@ -1317,6 +1334,51 @@ function createEventDeduper() {
1317
1334
  };
1318
1335
  }
1319
1336
 
1337
+ //#endregion
1338
+ //#region src/client/eve-agent-store-helpers.ts
1339
+ function isSettledSessionTail(events) {
1340
+ const tail = events.at(-1);
1341
+ return tail !== void 0 && isCurrentTurnBoundaryEvent(tail) && (tail.type !== "session.waiting" || collectPendingAuthorizations(events).size === 0);
1342
+ }
1343
+ function collectPendingAuthorizations(events) {
1344
+ const pending = /* @__PURE__ */ new Set();
1345
+ for (const event of events) updatePendingAuthorizations(pending, event);
1346
+ return pending;
1347
+ }
1348
+ function updatePendingAuthorizations(pending, event) {
1349
+ if (event.type === "authorization.required" && event.data.webhookUrl !== void 0) pending.add(event.data.name);
1350
+ else if (event.type === "authorization.completed") pending.delete(event.data.name);
1351
+ }
1352
+ function assertExclusiveTurnInput(input) {
1353
+ if (input.message !== void 0 === (input.inputResponses !== void 0)) throw new Error("A turn requires exactly one of message or inputResponses.");
1354
+ }
1355
+ let submissionSequence = 0;
1356
+ function createSubmissionId() {
1357
+ const randomUUID = globalThis.crypto?.randomUUID;
1358
+ if (randomUUID !== void 0) return randomUUID.call(globalThis.crypto);
1359
+ submissionSequence += 1;
1360
+ return `submission_${submissionSequence.toString()}`;
1361
+ }
1362
+ function createAbortSignal(first, second) {
1363
+ return first ? AbortSignal.any([first, second]) : second;
1364
+ }
1365
+ function summarizeUserContent(message) {
1366
+ if (typeof message === "string") return message;
1367
+ const parts = [];
1368
+ for (const part of message) if (part.type === "text") parts.push(part.text);
1369
+ else if (part.type === "file") parts.push(part.filename ? `[file: ${part.filename}]` : "[file]");
1370
+ return parts.join("\n");
1371
+ }
1372
+ function isAbortError(error) {
1373
+ return error instanceof Error && error.name === "AbortError";
1374
+ }
1375
+ function toTerminalStreamFailureError(event) {
1376
+ if (event.type !== "session.failed") return void 0;
1377
+ const error = new Error(event.data.message);
1378
+ error.name = event.data.code;
1379
+ return error;
1380
+ }
1381
+
1320
1382
  //#endregion
1321
1383
  //#region src/client/eve-agent-store.ts
1322
1384
  const detachStore = Symbol("detachEveAgentStore");
@@ -1332,8 +1394,9 @@ var EveAgentStore = class {
1332
1394
  #data;
1333
1395
  #error;
1334
1396
  #events;
1335
- #pendingMessageSubmission;
1397
+ #pendingMessageSubmissions = [];
1336
1398
  #projectionEvents;
1399
+ #resumePromise;
1337
1400
  #session;
1338
1401
  #snapshot;
1339
1402
  #status = "ready";
@@ -1367,10 +1430,17 @@ var EveAgentStore = class {
1367
1430
  };
1368
1431
  }
1369
1432
  async send(input) {
1370
- if (this.#status === "streaming" || this.#status === "submitted") throw new Error("eve session is already processing a turn.");
1433
+ if (this.#activeTurn !== void 0) return await this.#sendFollowUp(this.#activeTurn, input);
1371
1434
  const response = Promise.withResolvers();
1435
+ const completion = Promise.withResolvers();
1372
1436
  const turn = {
1373
1437
  abortController: new AbortController(),
1438
+ acceptedFollowUps: 0,
1439
+ cancel: () => turn.acceptedFollowUps > 0 && this.#session !== void 0 ? this.#session.cancel() : response.promise.then((messageResponse) => messageResponse === void 0 ? { status: "no_active_turn" } : messageResponse.cancel()),
1440
+ completion: completion.promise,
1441
+ followUpDispatches: /* @__PURE__ */ new Set(),
1442
+ receivedFollowUps: 0,
1443
+ resolveCompletion: completion.resolve,
1374
1444
  response: response.promise,
1375
1445
  resolveResponse: response.resolve
1376
1446
  };
@@ -1392,21 +1462,13 @@ var EveAgentStore = class {
1392
1462
  const response = await this.#dispatchTurn(turnInput);
1393
1463
  if (!this.#isActiveTurn(turn)) return;
1394
1464
  turn.resolveResponse(response);
1395
- let sawEvent = false;
1396
1465
  for await (const event of response) {
1397
1466
  if (!this.#isActiveTurn(turn)) return;
1398
- if (!sawEvent) {
1399
- sawEvent = true;
1400
- this.#status = "streaming";
1401
- }
1402
- if (!this.#seenEvents.admit(event)) continue;
1403
- this.#events = [...this.#events, event];
1404
- this.#applyServerEvent(event);
1405
- this.#callbacks.onEvent?.(event);
1406
- this.#applyTerminalStreamFailure(event);
1407
- this.#publish();
1467
+ this.#acceptServerEvent(event);
1408
1468
  }
1409
1469
  if (!this.#isActiveTurn(turn)) return;
1470
+ await this.#followSteeredTurns(turn);
1471
+ if (!this.#isActiveTurn(turn)) return;
1410
1472
  this.#status = this.#error === void 0 ? "ready" : "error";
1411
1473
  } catch (error) {
1412
1474
  if (!this.#isActiveTurn(turn)) return;
@@ -1426,13 +1488,89 @@ var EveAgentStore = class {
1426
1488
  this.#callbacks.onSessionChange?.(this.#session?.state);
1427
1489
  this.#publish();
1428
1490
  this.#callbacks.onFinish?.(this.#snapshot);
1491
+ turn.resolveCompletion();
1492
+ }
1493
+ }
1494
+ }
1495
+ resume() {
1496
+ if (this.#resumePromise !== void 0) return this.#resumePromise;
1497
+ const promise = this.#resume();
1498
+ this.#resumePromise = promise;
1499
+ const clear = () => {
1500
+ if (this.#resumePromise === promise) this.#resumePromise = void 0;
1501
+ };
1502
+ promise.then(clear, clear);
1503
+ return promise;
1504
+ }
1505
+ async #resume() {
1506
+ if (this.#status === "streaming" || this.#status === "submitted") throw new Error("eve session is already processing a turn.");
1507
+ if (this.#session === void 0) throw new Error("An eve session is required before resuming.");
1508
+ const response = Promise.withResolvers();
1509
+ const completion = Promise.withResolvers();
1510
+ const session = this.#session;
1511
+ const turn = {
1512
+ abortController: new AbortController(),
1513
+ acceptedFollowUps: 0,
1514
+ cancel: () => session.cancel(),
1515
+ completion: completion.promise,
1516
+ followUpDispatches: /* @__PURE__ */ new Set(),
1517
+ receivedFollowUps: 0,
1518
+ resolveCompletion: completion.resolve,
1519
+ response: response.promise,
1520
+ resolveResponse: response.resolve
1521
+ };
1522
+ this.#activeTurn = turn;
1523
+ turn.resolveResponse(void 0);
1524
+ this.#error = void 0;
1525
+ this.#status = "submitted";
1526
+ this.#publish();
1527
+ try {
1528
+ const replayed = [];
1529
+ for await (const event of session.stream({
1530
+ follow: false,
1531
+ signal: turn.abortController.signal,
1532
+ startIndex: 0
1533
+ })) {
1534
+ if (!this.#isActiveTurn(turn)) return;
1535
+ replayed.push(event);
1536
+ this.#acceptServerEvent(event, false);
1537
+ }
1538
+ if (!this.#isActiveTurn(turn)) return;
1539
+ this.#publish();
1540
+ if (!isSettledSessionTail(replayed)) {
1541
+ const pendingAuthorizations = collectPendingAuthorizations(replayed);
1542
+ for await (const event of session.stream({ signal: turn.abortController.signal })) {
1543
+ if (!this.#isActiveTurn(turn)) return;
1544
+ this.#acceptServerEvent(event);
1545
+ updatePendingAuthorizations(pendingAuthorizations, event);
1546
+ if (isCurrentTurnBoundaryEvent(event) && (event.type !== "session.waiting" || pendingAuthorizations.size === 0)) break;
1547
+ }
1548
+ }
1549
+ await this.#followSteeredTurns(turn);
1550
+ if (this.#isActiveTurn(turn)) this.#status = this.#error === void 0 ? "ready" : "error";
1551
+ } catch (error) {
1552
+ if (!this.#isActiveTurn(turn)) return;
1553
+ if (isAbortError(error)) this.#status = "ready";
1554
+ else {
1555
+ this.#error = toError(error);
1556
+ this.#status = "error";
1557
+ this.#callbacks.onError?.(this.#error);
1558
+ }
1559
+ } finally {
1560
+ if (this.#isActiveTurn(turn)) {
1561
+ turn.resolveResponse(void 0);
1562
+ this.#activeTurn = void 0;
1563
+ this.#callbacks.onSessionChange?.(session.state);
1564
+ this.#publish();
1565
+ this.#callbacks.onFinish?.(this.#snapshot);
1566
+ turn.resolveCompletion();
1429
1567
  }
1430
1568
  }
1431
1569
  }
1432
1570
  cancel() {
1433
1571
  const turn = this.#activeTurn;
1434
1572
  if (turn === void 0) return Promise.resolve({ status: "no_active_turn" });
1435
- return turn.response.then((response) => response === void 0 ? { status: "no_active_turn" } : response.cancel());
1573
+ return turn.cancel();
1436
1574
  }
1437
1575
  [detachStore]() {
1438
1576
  this.#activeTurn?.abortController.abort();
@@ -1441,11 +1579,12 @@ var EveAgentStore = class {
1441
1579
  const turn = this.#activeTurn;
1442
1580
  this.#activeTurn = void 0;
1443
1581
  turn?.resolveResponse(void 0);
1582
+ turn?.resolveCompletion();
1444
1583
  turn?.abortController.abort();
1445
1584
  if (!this.#externalSession) this.#session = void 0;
1446
1585
  this.#events = [];
1447
1586
  this.#seenEvents = createEventDeduper();
1448
- this.#pendingMessageSubmission = void 0;
1587
+ this.#pendingMessageSubmissions = [];
1449
1588
  this.#projectionEvents = [];
1450
1589
  this.#data = this.#reducer.initial();
1451
1590
  this.#error = void 0;
@@ -1453,6 +1592,47 @@ var EveAgentStore = class {
1453
1592
  this.#callbacks.onSessionChange?.(this.#session?.state);
1454
1593
  this.#publish();
1455
1594
  }
1595
+ async #sendFollowUp(turn, input) {
1596
+ if (input.message === void 0 || input.turnPolicy !== "steer") throw new Error("eve session is already processing a turn. Send a message with turnPolicy: \"steer\" to replace it.");
1597
+ const preparedInput = await this.#callbacks.prepareSend?.(input) ?? input;
1598
+ assertExclusiveTurnInput(preparedInput);
1599
+ if (preparedInput.message === void 0 || preparedInput.turnPolicy !== "steer") throw new Error("An in-flight follow-up requires a message with turnPolicy: \"steer\".");
1600
+ if (!this.#isActiveTurn(turn)) return await this.send(preparedInput);
1601
+ const submissionId = this.#projectOptimisticMessage(preparedInput);
1602
+ this.#publish();
1603
+ let dispatch;
1604
+ dispatch = (async () => {
1605
+ try {
1606
+ await turn.response;
1607
+ if (!this.#isActiveTurn(turn) || this.#session === void 0) throw new Error("The active eve turn ended before the follow-up could be sent.");
1608
+ const { message, ...options } = preparedInput;
1609
+ await this.#session.send(message, options);
1610
+ turn.acceptedFollowUps += 1;
1611
+ } catch (error) {
1612
+ this.#failPendingMessageSubmission(toError(error), submissionId);
1613
+ this.#publish();
1614
+ throw error;
1615
+ } finally {
1616
+ turn.followUpDispatches.delete(dispatch);
1617
+ }
1618
+ })();
1619
+ turn.followUpDispatches.add(dispatch);
1620
+ await dispatch;
1621
+ await turn.completion;
1622
+ }
1623
+ async #followSteeredTurns(turn) {
1624
+ while (turn.followUpDispatches.size > 0) await Promise.allSettled(turn.followUpDispatches);
1625
+ if (turn.receivedFollowUps >= turn.acceptedFollowUps || this.#session === void 0) return;
1626
+ for await (const event of this.#session.stream({ signal: turn.abortController.signal })) {
1627
+ if (!this.#isActiveTurn(turn)) return;
1628
+ if (event.type === "message.received") turn.receivedFollowUps += 1;
1629
+ this.#acceptServerEvent(event);
1630
+ if (isCurrentTurnBoundaryEvent(event)) {
1631
+ while (turn.followUpDispatches.size > 0) await Promise.allSettled(turn.followUpDispatches);
1632
+ if (turn.receivedFollowUps >= turn.acceptedFollowUps) return;
1633
+ }
1634
+ }
1635
+ }
1456
1636
  async #createFirstTurn(input) {
1457
1637
  if (this.#client === void 0) throw new Error("An external eve session is required before sending.");
1458
1638
  if (input.message === void 0) throw new Error("Cannot answer an input request before the session starts.");
@@ -1485,7 +1665,7 @@ var EveAgentStore = class {
1485
1665
  id,
1486
1666
  message: summarizeUserContent(input.message)
1487
1667
  };
1488
- this.#pendingMessageSubmission = pending;
1668
+ this.#pendingMessageSubmissions = [...this.#pendingMessageSubmissions, pending];
1489
1669
  this.#appendProjectionEvent({
1490
1670
  data: {
1491
1671
  createdAt: pending.createdAt,
@@ -1494,6 +1674,7 @@ var EveAgentStore = class {
1494
1674
  },
1495
1675
  type: "client.message.submitted"
1496
1676
  });
1677
+ return id;
1497
1678
  }
1498
1679
  #projectInputResponses(input) {
1499
1680
  if (input.inputResponses === void 0 || input.inputResponses.length === 0) return;
@@ -1505,10 +1686,20 @@ var EveAgentStore = class {
1505
1686
  type: "client.input.responded"
1506
1687
  });
1507
1688
  }
1689
+ #acceptServerEvent(event, publish = true) {
1690
+ if (!this.#seenEvents.admit(event)) return;
1691
+ this.#events = [...this.#events, event];
1692
+ this.#applyServerEvent(event);
1693
+ this.#callbacks.onEvent?.(event);
1694
+ this.#status = "streaming";
1695
+ this.#applyTerminalStreamFailure(event);
1696
+ if (publish) this.#publish();
1697
+ }
1508
1698
  #applyServerEvent(event) {
1509
- if (event.type === "message.received" && this.#pendingMessageSubmission !== void 0) {
1510
- const submissionId = this.#pendingMessageSubmission.id;
1511
- this.#pendingMessageSubmission = void 0;
1699
+ const pendingSubmission = this.#pendingMessageSubmissions[0];
1700
+ if (event.type === "message.received" && pendingSubmission !== void 0) {
1701
+ const submissionId = pendingSubmission.id;
1702
+ this.#pendingMessageSubmissions = this.#pendingMessageSubmissions.slice(1);
1512
1703
  this.#replaceProjectionEvent((candidate) => candidate.type === "client.message.submitted" && candidate.data.submissionId === submissionId, event);
1513
1704
  return;
1514
1705
  }
@@ -1524,10 +1715,10 @@ var EveAgentStore = class {
1524
1715
  this.#callbacks.onError?.(error);
1525
1716
  }
1526
1717
  }
1527
- #failPendingMessageSubmission(error) {
1528
- const pending = this.#pendingMessageSubmission;
1718
+ #failPendingMessageSubmission(error, submissionId) {
1719
+ const pending = submissionId === void 0 ? this.#pendingMessageSubmissions[0] : this.#pendingMessageSubmissions.find((candidate) => candidate.id === submissionId);
1529
1720
  if (pending === void 0) return;
1530
- this.#pendingMessageSubmission = void 0;
1721
+ this.#pendingMessageSubmissions = this.#pendingMessageSubmissions.filter((candidate) => candidate.id !== pending.id);
1531
1722
  this.#replaceProjectionEvent((event) => event.type === "client.message.submitted" && event.data.submissionId === pending.id, {
1532
1723
  data: {
1533
1724
  createdAt: pending.createdAt,
@@ -1576,40 +1767,6 @@ var EveAgentStore = class {
1576
1767
  function detachEveAgentStore(store) {
1577
1768
  store[detachStore]();
1578
1769
  }
1579
- function assertExclusiveTurnInput(input) {
1580
- if (input.message !== void 0 === (input.inputResponses !== void 0)) throw new Error("A turn requires exactly one of message or inputResponses.");
1581
- }
1582
- let submissionSequence = 0;
1583
- function createSubmissionId() {
1584
- const randomUUID = globalThis.crypto?.randomUUID;
1585
- if (randomUUID !== void 0) return randomUUID.call(globalThis.crypto);
1586
- submissionSequence += 1;
1587
- return `submission_${submissionSequence.toString()}`;
1588
- }
1589
- function createAbortSignal(first, second) {
1590
- return first ? AbortSignal.any([first, second]) : second;
1591
- }
1592
- function summarizeUserContent(message) {
1593
- if (typeof message === "string") return message;
1594
- const parts = [];
1595
- for (const part of message) {
1596
- if (part.type === "text") {
1597
- parts.push(part.text);
1598
- continue;
1599
- }
1600
- if (part.type === "file") parts.push(part.filename ? `[file: ${part.filename}]` : "[file]");
1601
- }
1602
- return parts.join("\n");
1603
- }
1604
- function isAbortError(error) {
1605
- return error instanceof Error && error.name === "AbortError";
1606
- }
1607
- function toTerminalStreamFailureError(event) {
1608
- if (event.type !== "session.failed") return;
1609
- const error = new Error(event.data.message);
1610
- error.name = event.data.code;
1611
- return error;
1612
- }
1613
1770
 
1614
1771
  //#endregion
1615
1772
  //#region src/client/agent-host.ts
@@ -2233,6 +2390,9 @@ var SvelteEveAgent = class {
2233
2390
  reset = () => {
2234
2391
  this.#store.reset();
2235
2392
  };
2393
+ resume = () => {
2394
+ return this.#store.resume();
2395
+ };
2236
2396
  respond = (inputResponses, options) => {
2237
2397
  return this.#store.send({
2238
2398
  ...options,
@@ -2247,6 +2407,7 @@ var SvelteEveAgent = class {
2247
2407
  };
2248
2408
  };
2249
2409
  function useEveAgent(options = {}) {
2410
+ if (options.resume && options.initialSession === void 0 && options.session === void 0) throw new Error("useEveAgent({ resume: true }) requires initialSession or session.");
2250
2411
  const reducer = options.reducer ?? defaultMessageReducer();
2251
2412
  const store = new EveAgentStore({
2252
2413
  auth: options.auth,
@@ -2268,6 +2429,7 @@ function useEveAgent(options = {}) {
2268
2429
  onSessionChange: options.onSessionChange,
2269
2430
  prepareSend: options.prepareSend
2270
2431
  });
2432
+ if ("window" in globalThis && options.resume) store.resume();
2271
2433
  return new SvelteEveAgent(store);
2272
2434
  }
2273
2435