eve 0.60.1 → 0.61.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/src/chunks/{use-eve-agent-Dmc6sX4d.js → use-eve-agent-C5-yqRuq.js} +177 -85
  3. package/dist/src/chunks/{use-eve-agent-jSR8P4nK.js → use-eve-agent-CVWk5KKy.js} +177 -85
  4. package/dist/src/cli/agent-command.js +1 -1
  5. package/dist/src/cli/dev/local-server-child.js +1 -1
  6. package/dist/src/cli/dev/tui/runner.js +1 -1
  7. package/dist/src/client/client.d.ts +3 -1
  8. package/dist/src/client/client.js +1 -1
  9. package/dist/src/client/eve-agent-projection.d.ts +1 -0
  10. package/dist/src/client/eve-agent-projection.js +1 -1
  11. package/dist/src/client/eve-agent-store-helpers.js +1 -1
  12. package/dist/src/client/eve-agent-store-state.d.ts +4 -1
  13. package/dist/src/client/eve-agent-store.js +1 -1
  14. package/dist/src/client/message-reducer.js +1 -1
  15. package/dist/src/client/message-response.d.ts +5 -0
  16. package/dist/src/client/message-response.js +1 -1
  17. package/dist/src/client/optimistic-message-submissions.d.ts +22 -0
  18. package/dist/src/client/optimistic-message-submissions.js +1 -0
  19. package/dist/src/client/session.js +1 -1
  20. package/dist/src/compiled/.vendor-stamp.json +6 -6
  21. package/dist/src/compiled/@workflow/core/index.js +1 -1
  22. package/dist/src/compiled/@workflow/core/log-format.d.ts +4 -0
  23. package/dist/src/compiled/@workflow/core/runtime/helpers.js +1 -1
  24. package/dist/src/compiled/@workflow/core/runtime.js +5 -3
  25. package/dist/src/compiled/@workflow/core/serialization.js +1 -1
  26. package/dist/src/compiled/@workflow/core/types.d.ts +22 -0
  27. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  28. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  29. package/dist/src/compiled/@workflow/world/runs.d.ts +12 -1
  30. package/dist/src/compiled/@workflow/world-local/index.js +3 -3
  31. package/dist/src/compiled/@workflow/world-vercel/index.js +7 -7
  32. package/dist/src/compiled/_chunks/workflow/{attribute-changes-3SKHZsSX.js → attribute-changes-A6HfMNG3.js} +1 -1
  33. package/dist/src/compiled/_chunks/workflow/{dist-DearXsdS.js → dist-D38gP7YF.js} +1 -1
  34. package/dist/src/compiled/_chunks/workflow/frames-QcJfYHmh.js +3 -0
  35. package/dist/src/compiled/_chunks/workflow/helpers-6kObVbfS.js +1 -0
  36. package/dist/src/compiled/_chunks/workflow/{logger-CxBCNokg.js → logger-NN5YHpYy.js} +6 -5
  37. package/dist/src/compiled/_chunks/workflow/{resume-hook-B7_MC7FC.js → resume-hook-CAqlhwic.js} +1 -1
  38. package/dist/src/compiled/_chunks/workflow/{run-jGFog1uQ.js → run-DJevv8zz.js} +1 -1
  39. package/dist/src/compiled/_chunks/workflow/{serialization-VRHEKI6l.js → serialization-C5KgPESk.js} +3 -3
  40. package/dist/src/compiled/_chunks/workflow/sleep-CbUwdv6O.js +1 -0
  41. package/dist/src/compiled/_chunks/workflow/{wait-until-DDJySKt-.js → wait-until-BMY_tOr-.js} +1 -1
  42. package/dist/src/compiled/_chunks/workflow/{ws-transport-CEFNBXPC.js → ws-transport-DSDcKH08.js} +1 -1
  43. package/dist/src/compiler/default-tool-policy.js +1 -1
  44. package/dist/src/compiler/extension-compatibility.d.ts +18 -18
  45. package/dist/src/compiler/extension-compatibility.js +1 -1
  46. package/dist/src/compiler/manifest.d.ts +5 -2
  47. package/dist/src/compiler/manifest.js +1 -1
  48. package/dist/src/compiler/normalize-tool.js +1 -1
  49. package/dist/src/compiler/project-sources.js +1 -1
  50. package/dist/src/compiler/validate-artifact.js +1 -1
  51. package/dist/src/context/build-dynamic-tools.js +1 -1
  52. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  53. package/dist/src/context/dynamic-tool-metadata.d.ts +1 -0
  54. package/dist/src/evals/cli/eval.js +1 -1
  55. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  56. package/dist/src/execution/node-step.js +1 -1
  57. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  58. package/dist/src/execution/tools/sleep-workflow.js +1 -1
  59. package/dist/src/execution/tools/subagent/metadata.js +1 -1
  60. package/dist/src/harness/advertised-tools.js +1 -1
  61. package/dist/src/harness/emission.js +1 -1
  62. package/dist/src/harness/execute-tool.d.ts +1 -0
  63. package/dist/src/internal/application/package.js +1 -1
  64. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  65. package/dist/src/internal/authored-runtime-modules.js +1 -1
  66. package/dist/src/internal/nitro/dev-runtime-artifacts.d.ts +1 -0
  67. package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
  68. package/dist/src/internal/nitro/development-generation.js +1 -1
  69. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
  70. package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
  71. package/dist/src/internal/nitro/host/extension-distribution.js +1 -1
  72. package/dist/src/internal/nitro/host.js +1 -1
  73. package/dist/src/internal/node-esm-compat-banner.js +3 -3
  74. package/dist/src/internal/workflow-bundle/authored-workflow-directives.js +1 -1
  75. package/dist/src/internal/workflow-bundle/builder-support.d.ts +1 -0
  76. package/dist/src/internal/workflow-bundle/builder-support.js +1 -1
  77. package/dist/src/internal/workflow-bundle/builder.js +1 -1
  78. package/dist/src/internal/workflow-bundle/nitro-step-entry.d.ts +1 -0
  79. package/dist/src/internal/workflow-bundle/nitro-step-entry.js +1 -1
  80. package/dist/src/internal/workflow-bundle/workflow-directive-ast.d.ts +1 -0
  81. package/dist/src/internal/workflow-bundle/workflow-directive-ast.js +1 -1
  82. package/dist/src/internal/workflow-bundle/workflow-transformer.js +1 -1
  83. package/dist/src/protocol/message.d.ts +4 -0
  84. package/dist/src/protocol/message.js +1 -1
  85. package/dist/src/runtime/resolve-agent.js +1 -1
  86. package/dist/src/runtime/resolve-tool.js +1 -1
  87. package/dist/src/runtime/tools/registry.js +1 -1
  88. package/dist/src/services/dev-client/agent-info-probe.d.ts +1 -0
  89. package/dist/src/services/dev-client/agent-info-probe.js +1 -1
  90. package/dist/src/setup/primitives/pm/pnpm.js +1 -1
  91. package/dist/src/setup/scaffold/create/project.js +1 -1
  92. package/dist/src/shared/map-concurrent.d.ts +2 -0
  93. package/dist/src/shared/map-concurrent.js +1 -0
  94. package/dist/src/svelte/index.js +1 -1
  95. package/dist/src/svelte/use-eve-agent.js +1 -1
  96. package/dist/src/tools/definition.d.ts +2 -0
  97. package/dist/src/tools/dynamic.d.ts +1 -0
  98. package/dist/src/tools/provided/agent-router.d.ts +1 -1
  99. package/dist/src/tools/provided/agent-router.js +1 -1
  100. package/dist/src/tools/workflow-definition.d.ts +1 -1
  101. package/dist/src/vue/index.js +1 -1
  102. package/dist/src/vue/use-eve-agent.js +1 -1
  103. package/docs/concepts/built-in-tools.md +1 -1
  104. package/docs/concepts/sessions-runs-and-streaming.md +32 -32
  105. package/docs/guides/client/overview.mdx +2 -0
  106. package/docs/guides/frontend/overview.mdx +7 -3
  107. package/docs/reference/typescript-api.md +1 -1
  108. package/docs/subagents/index.mdx +3 -3
  109. package/docs/tools/overview.mdx +17 -0
  110. package/docs/tools/workflows.mdx +19 -8
  111. package/package.json +7 -7
  112. package/dist/src/compiled/_chunks/workflow/frames-Bm_yZi8X.js +0 -3
  113. package/dist/src/compiled/_chunks/workflow/helpers-K7otPd_U.js +0 -1
  114. package/dist/src/compiled/_chunks/workflow/sleep-60eDbac6.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # eve
2
2
 
3
+ ## 0.61.0
4
+
5
+ ### Minor Changes
6
+
7
+ - cbfc425: Root workflow tools now receive the built-in root-copy target at `ctx.agents.agent`, with its authored description or an empty string, and `agent` is reserved from use as a declared subagent name. Tool definitions can set `availableInSubagents: false`; `agentRouter()` sets it automatically, routes only to entries with non-empty descriptions, and can replace the model-facing tool at `agent/tools/agent.ts` without being inherited by the root copy.
8
+
9
+ ### Patch Changes
10
+
11
+ - 7e95b64: Keep Web Chat attached for background task results without rendering runtime-authored task input as a user message. Task wake-ups now retain explicit provenance in the durable session stream while the default frontend reducer omits them from participant messages.
12
+ - 7259b96: Set `minimumReleaseAge: 0` in newly generated pnpm workspace files so dependencies installed by `eve init` also pass pnpm's checks when starting the dev server or installing again.
13
+ - 260a359: Reconcile frontend optimistic messages with their server delivery identities instead of stream order. Concurrent, coalesced, identical, and structured message submissions now resolve the correct placeholders, and separately delivered messages within one turn remain separate chat bubbles.
14
+ - 5f17320: Reduce development startup and production build time by removing duplicate bundler work, skipping unnecessary parsing, and overlapping independent preparation. Development terminal inspection now has a deadline, and `Client.info()` accepts an abort signal so slow inspection requests do not hold up startup.
15
+ - 6b99f12: Upgrade eve's Workflow runtime dependencies to the latest beta releases, including the Postgres world fix for steering pending generations.
16
+
3
17
  ## 0.60.1
4
18
 
5
19
  ### Patch Changes
@@ -733,8 +733,10 @@ function isResultCompletedEvent(event) {
733
733
  //#endregion
734
734
  //#region src/client/message-response.ts
735
735
  const consumeResponse = Symbol("consumeMessageResponse");
736
+ const acceptedDeliveryId = Symbol("acceptedDeliveryId");
736
737
  var MessageResponse = class {
737
738
  sessionId;
739
+ [acceptedDeliveryId];
738
740
  #cancelTurn;
739
741
  #cancellation;
740
742
  #consumed = false;
@@ -744,6 +746,7 @@ var MessageResponse = class {
744
746
  constructor(input) {
745
747
  this.#cancelTurn = input.cancelTurn;
746
748
  this.sessionId = input.sessionId;
749
+ this[acceptedDeliveryId] = input.deliveryId;
747
750
  this.#createStream = input.createStream;
748
751
  }
749
752
  cancel() {
@@ -792,6 +795,9 @@ var MessageResponse = class {
792
795
  }
793
796
  }
794
797
  };
798
+ function getMessageResponseDeliveryId(response) {
799
+ return response[acceptedDeliveryId];
800
+ }
795
801
  function consumeMessageResponse(response, source) {
796
802
  return response[consumeResponse](source);
797
803
  }
@@ -1584,6 +1590,7 @@ var ClientSession = class ClientSession {
1584
1590
  return new MessageResponse({
1585
1591
  cancelTurn: async (turnId) => await this.cancel({ turnId }),
1586
1592
  createStream: (source) => this.#createEventStream(initialStreamIndex, input, deliveryId, source),
1593
+ deliveryId,
1587
1594
  sessionId: this.#state.sessionId
1588
1595
  });
1589
1596
  }
@@ -1800,8 +1807,8 @@ var Client = class {
1800
1807
  }));
1801
1808
  return result.data;
1802
1809
  }
1803
- async info() {
1804
- const response = await this.fetch(EVE_INFO_ROUTE_PATH);
1810
+ async info(options = {}) {
1811
+ const response = await this.fetch(EVE_INFO_ROUTE_PATH, options);
1805
1812
  if (!response.ok) {
1806
1813
  const body = await response.text();
1807
1814
  throw new ClientError(response.status, body, response.headers);
@@ -2008,6 +2015,10 @@ var EveAgentProjection = class {
2008
2015
  this.#events = [...this.#events, event];
2009
2016
  this.#data = this.#reducer.reduce(this.#data, event);
2010
2017
  }
2018
+ remove(predicate) {
2019
+ this.#events = this.#events.filter((event) => !predicate(event));
2020
+ this.#data = this.#reduce();
2021
+ }
2011
2022
  replace(predicate, replacement) {
2012
2023
  const index = this.#events.findIndex(predicate);
2013
2024
  this.#events = index === -1 ? [...this.#events, replacement] : this.#events.map((event, i) => i === index ? replacement : event);
@@ -2020,24 +2031,6 @@ var EveAgentProjection = class {
2020
2031
  }
2021
2032
  };
2022
2033
 
2023
- //#endregion
2024
- //#region src/protocol/event-dedupe.ts
2025
- function createEventDeduper() {
2026
- const seen = /* @__PURE__ */ new Set();
2027
- return {
2028
- admit(event) {
2029
- const id = event.meta?.id;
2030
- if (id === void 0) return true;
2031
- if (seen.has(id)) return false;
2032
- seen.add(id);
2033
- return true;
2034
- },
2035
- get size() {
2036
- return seen.size;
2037
- }
2038
- };
2039
- }
2040
-
2041
2034
  //#endregion
2042
2035
  //#region src/client/eve-agent-store-helpers.ts
2043
2036
  function isSettledSessionTail(events) {
@@ -2088,7 +2081,7 @@ function createActiveTurn(cancel) {
2088
2081
  completion: completion.promise,
2089
2082
  followUpDispatches: /* @__PURE__ */ new Set(),
2090
2083
  receivedFollowUps: 0,
2091
- receivedFollowUpEvents: /* @__PURE__ */ new Set(),
2084
+ receivedFollowUpEvents: /* @__PURE__ */ new Map(),
2092
2085
  followUpSubmissionIds: /* @__PURE__ */ new Set(),
2093
2086
  resolveCompletion: completion.resolve,
2094
2087
  response: response.promise,
@@ -2101,7 +2094,8 @@ async function followSteeredTurns(turn, events, isActive) {
2101
2094
  if (turn.receivedFollowUps >= turn.acceptedFollowUps) return;
2102
2095
  for await (const event of events) {
2103
2096
  if (!isActive()) return;
2104
- if (turn.receivedFollowUpEvents.delete(event)) turn.receivedFollowUps += 1;
2097
+ turn.receivedFollowUps += turn.receivedFollowUpEvents.get(event) ?? 0;
2098
+ turn.receivedFollowUpEvents.delete(event);
2105
2099
  if (isCurrentTurnBoundaryEvent(event)) {
2106
2100
  while (turn.followUpDispatches.size > 0) await Promise.allSettled(turn.followUpDispatches);
2107
2101
  if (turn.receivedFollowUps >= turn.acceptedFollowUps) return;
@@ -2121,6 +2115,125 @@ async function waitWithSignal(promise, signal) {
2121
2115
  }
2122
2116
  }
2123
2117
 
2118
+ //#endregion
2119
+ //#region src/client/optimistic-message-submissions.ts
2120
+ var OptimisticMessageSubmissions = class {
2121
+ #optimistic;
2122
+ #projection;
2123
+ #pending = [];
2124
+ constructor(projection, optimistic) {
2125
+ this.#projection = projection;
2126
+ this.#optimistic = optimistic;
2127
+ }
2128
+ reset() {
2129
+ this.#pending = [];
2130
+ }
2131
+ submit(input, eventStartIndex) {
2132
+ if (input.message === void 0) return void 0;
2133
+ const pending = {
2134
+ createdAt: Date.now(),
2135
+ eventStartIndex,
2136
+ id: createSubmissionId(),
2137
+ message: summarizeUserContent(input.message),
2138
+ requiresDeliveryId: true
2139
+ };
2140
+ this.#pending = [...this.#pending, pending];
2141
+ if (this.#optimistic) this.#projection.append({
2142
+ data: {
2143
+ createdAt: pending.createdAt,
2144
+ message: pending.message,
2145
+ submissionId: pending.id
2146
+ },
2147
+ type: "client.message.submitted"
2148
+ });
2149
+ return pending.id;
2150
+ }
2151
+ apply(event) {
2152
+ if (event.type !== "message.received") {
2153
+ this.#projection.append(event);
2154
+ return;
2155
+ }
2156
+ if (event.data.kind === "execution.background_task") {
2157
+ this.#projection.append(event);
2158
+ return;
2159
+ }
2160
+ const matching = this.#matching(event);
2161
+ if (matching.length === 0) {
2162
+ this.#projection.append(event);
2163
+ return;
2164
+ }
2165
+ return this.#reconcile(matching, event, false);
2166
+ }
2167
+ correlate(submissionId, deliveryId, events) {
2168
+ if (submissionId === void 0) return void 0;
2169
+ this.#pending = this.#pending.map((pending) => pending.id === submissionId ? {
2170
+ ...pending,
2171
+ deliveryId,
2172
+ requiresDeliveryId: deliveryId !== void 0
2173
+ } : pending);
2174
+ const pending = this.#pending.find((candidate) => candidate.id === submissionId);
2175
+ if (pending === void 0) return void 0;
2176
+ for (const event of events.slice(pending.eventStartIndex)) {
2177
+ if (event.type !== "message.received" || event.data.kind === "execution.background_task") continue;
2178
+ const matching = this.#matching(event);
2179
+ if (matching.some((candidate) => candidate.id === submissionId)) return this.#reconcile(matching, event, true);
2180
+ }
2181
+ }
2182
+ fail(error, submissionId) {
2183
+ const pending = submissionId === void 0 ? this.#pending[0] : this.#pending.find((candidate) => candidate.id === submissionId);
2184
+ if (pending === void 0) return;
2185
+ this.#pending = this.#pending.filter((candidate) => candidate.id !== pending.id);
2186
+ this.#projection.replace((event) => event.type === "client.message.submitted" && event.data.submissionId === pending.id, {
2187
+ data: {
2188
+ createdAt: pending.createdAt,
2189
+ error: { message: error.message },
2190
+ message: pending.message,
2191
+ submissionId: pending.id
2192
+ },
2193
+ type: "client.message.failed"
2194
+ });
2195
+ }
2196
+ failAll(error) {
2197
+ for (const pending of this.#pending) this.fail(error, pending.id);
2198
+ }
2199
+ #matching(event) {
2200
+ return this.#pending.filter((pending) => pending.deliveryId === void 0 ? !pending.requiresDeliveryId : event.meta.deliveryIds?.includes(pending.deliveryId) === true);
2201
+ }
2202
+ #reconcile(submissions, event, alreadyProjected) {
2203
+ const ids = submissions.map((pending) => pending.id);
2204
+ const idSet = new Set(ids);
2205
+ this.#pending = this.#pending.filter((pending) => !idSet.has(pending.id));
2206
+ if (alreadyProjected) this.#projection.remove((candidate) => candidate.type === "client.message.submitted" && idSet.has(candidate.data.submissionId));
2207
+ else {
2208
+ this.#projection.replace((candidate) => candidate.type === "client.message.submitted" && candidate.data.submissionId === ids[0], event);
2209
+ this.#projection.remove((candidate) => candidate.type === "client.message.submitted" && idSet.has(candidate.data.submissionId));
2210
+ }
2211
+ return {
2212
+ alreadyProjected,
2213
+ event,
2214
+ ids
2215
+ };
2216
+ }
2217
+ };
2218
+
2219
+ //#endregion
2220
+ //#region src/protocol/event-dedupe.ts
2221
+ function createEventDeduper() {
2222
+ const seen = /* @__PURE__ */ new Set();
2223
+ return {
2224
+ admit(event) {
2225
+ const id = event.meta?.id;
2226
+ if (id === void 0) return true;
2227
+ if (seen.has(id)) return false;
2228
+ seen.add(id);
2229
+ return true;
2230
+ },
2231
+ get size() {
2232
+ return seen.size;
2233
+ }
2234
+ };
2235
+ }
2236
+
2124
2237
  //#endregion
2125
2238
  //#region src/client/eve-agent-store.ts
2126
2239
  const detachStore = Symbol("detachEveAgentStore");
@@ -2140,7 +2253,7 @@ var EveAgentStore = class {
2140
2253
  #callbacks = {};
2141
2254
  #error;
2142
2255
  #events;
2143
- #pendingMessageSubmissions = [];
2256
+ #messageSubmissions;
2144
2257
  #prewarmGeneration = 0;
2145
2258
  #prewarmPromise;
2146
2259
  #prewarmController;
@@ -2164,6 +2277,7 @@ var EveAgentStore = class {
2164
2277
  this.#events = initialEvents;
2165
2278
  this.#projection = new EveAgentProjection(init.reducer, this.#events);
2166
2279
  this.#optimistic = init.optimistic ?? true;
2280
+ this.#messageSubmissions = new OptimisticMessageSubmissions(this.#projection, this.#optimistic);
2167
2281
  this.#session = init.session ?? (init.initialSession === void 0 ? void 0 : this.#client?.sessions.attach(init.initialSession.sessionId, { streamIndex: init.initialSession.streamIndex }));
2168
2282
  this.#snapshot = this.#createSnapshot();
2169
2283
  }
@@ -2238,7 +2352,7 @@ var EveAgentStore = class {
2238
2352
  const preparedInput = await (prepareSend === void 0 ? input : waitWithSignal(Promise.resolve(prepareSend(input)), createAbortSignal(input.signal, turn.abortController.signal)));
2239
2353
  assertExclusiveTurnInput(preparedInput);
2240
2354
  if (!this.#isActiveTurn(turn)) return;
2241
- this.#projectOptimisticMessage(preparedInput);
2355
+ const submissionId = this.#messageSubmissions.submit(preparedInput, this.#events.length);
2242
2356
  this.#projectInputResponses(preparedInput);
2243
2357
  this.#publish();
2244
2358
  const turnInput = {
@@ -2250,9 +2364,11 @@ var EveAgentStore = class {
2250
2364
  reader = dispatched.reader;
2251
2365
  if (!this.#isActiveTurn(turn)) return;
2252
2366
  turn.resolveResponse(response);
2367
+ if (this.#handleReconciliation(this.#messageSubmissions.correlate(submissionId, getMessageResponseDeliveryId(response), this.#events))) this.#publish();
2253
2368
  for await (const event of consumeMessageResponse(response, reader)) {
2254
2369
  if (!this.#isActiveTurn(turn)) return;
2255
- if (turn.receivedFollowUpEvents.delete(event)) turn.receivedFollowUps += 1;
2370
+ turn.receivedFollowUps += turn.receivedFollowUpEvents.get(event) ?? 0;
2371
+ turn.receivedFollowUpEvents.delete(event);
2256
2372
  }
2257
2373
  if (!this.#isActiveTurn(turn)) return;
2258
2374
  await followSteeredTurns(turn, reader, () => this.#isActiveTurn(turn));
@@ -2262,12 +2378,12 @@ var EveAgentStore = class {
2262
2378
  if (!this.#isActiveTurn(turn)) return;
2263
2379
  if (isAbortError(error)) {
2264
2380
  this.#status = "ready";
2265
- this.#failPendingMessageSubmission(toError(error));
2381
+ this.#messageSubmissions.fail(toError(error));
2266
2382
  } else {
2267
2383
  const reported = this.#error !== void 0;
2268
2384
  this.#error ??= toError(error);
2269
2385
  this.#status = "error";
2270
- this.#failPendingMessageSubmission(this.#error);
2386
+ this.#messageSubmissions.fail(this.#error);
2271
2387
  if (!reported) this.#callbacks.onError?.(this.#error);
2272
2388
  }
2273
2389
  } finally {
@@ -2311,7 +2427,8 @@ var EveAgentStore = class {
2311
2427
  this.#publish();
2312
2428
  for await (const event of reader) {
2313
2429
  if (!this.#isActiveTurn(turn)) return;
2314
- if (turn.receivedFollowUpEvents.delete(event)) turn.receivedFollowUps += 1;
2430
+ turn.receivedFollowUps += turn.receivedFollowUpEvents.get(event) ?? 0;
2431
+ turn.receivedFollowUpEvents.delete(event);
2315
2432
  if (isCurrentTurnBoundaryEvent(event) && this.#pendingAuthorizations.size === 0) break;
2316
2433
  }
2317
2434
  }
@@ -2360,7 +2477,7 @@ var EveAgentStore = class {
2360
2477
  if (!this.#externalSession) this.#session = void 0;
2361
2478
  this.#events = [];
2362
2479
  this.#seenEvents = createEventDeduper();
2363
- this.#pendingMessageSubmissions = [];
2480
+ this.#messageSubmissions.reset();
2364
2481
  this.#projection.reset();
2365
2482
  this.#error = void 0;
2366
2483
  this.#status = "ready";
@@ -2377,7 +2494,7 @@ var EveAgentStore = class {
2377
2494
  assertExclusiveTurnInput(preparedInput);
2378
2495
  if (preparedInput.message === void 0 || preparedInput.turnPolicy !== "steer") throw new Error("An in-flight follow-up requires a message with turnPolicy: \"steer\".");
2379
2496
  if (!this.#isActiveTurn(turn)) return await this.#submit(preparedInput);
2380
- const submissionId = this.#projectOptimisticMessage(preparedInput);
2497
+ const submissionId = this.#messageSubmissions.submit(preparedInput, this.#events.length);
2381
2498
  if (submissionId !== void 0) turn.followUpSubmissionIds.add(submissionId);
2382
2499
  this.#publish();
2383
2500
  this.#ensureStream({
@@ -2391,14 +2508,15 @@ var EveAgentStore = class {
2391
2508
  await waitWithSignal(turn.response, signal);
2392
2509
  if (!this.#isActiveTurn(turn) || this.#session === void 0) throw new Error("The active eve turn ended before the follow-up could be sent.");
2393
2510
  const { message, ...options } = preparedInput;
2394
- await this.#session.send(message, {
2511
+ const response = await this.#session.send(message, {
2395
2512
  ...options,
2396
2513
  signal
2397
2514
  });
2398
2515
  turn.acceptedFollowUps += 1;
2516
+ if (this.#handleReconciliation(this.#messageSubmissions.correlate(submissionId, getMessageResponseDeliveryId(response), this.#events))) this.#publish();
2399
2517
  } catch (error) {
2400
2518
  if (this.#isActiveTurn(turn)) {
2401
- this.#failPendingMessageSubmission(toError(error), submissionId);
2519
+ this.#messageSubmissions.fail(toError(error), submissionId);
2402
2520
  this.#publish();
2403
2521
  }
2404
2522
  throw error;
@@ -2502,25 +2620,6 @@ var EveAgentStore = class {
2502
2620
  #isActiveTurn(turn) {
2503
2621
  return this.#activeTurn === turn;
2504
2622
  }
2505
- #projectOptimisticMessage(input) {
2506
- if (input.message === void 0) return;
2507
- const id = createSubmissionId();
2508
- const pending = {
2509
- createdAt: Date.now(),
2510
- id,
2511
- message: summarizeUserContent(input.message)
2512
- };
2513
- this.#pendingMessageSubmissions = [...this.#pendingMessageSubmissions, pending];
2514
- if (this.#optimistic) this.#projection.append({
2515
- data: {
2516
- createdAt: pending.createdAt,
2517
- message: pending.message,
2518
- submissionId: pending.id
2519
- },
2520
- type: "client.message.submitted"
2521
- });
2522
- return id;
2523
- }
2524
2623
  #projectInputResponses(input) {
2525
2624
  if (input.inputResponses === void 0 || input.inputResponses.length === 0) return;
2526
2625
  this.#projection.append({
@@ -2536,7 +2635,7 @@ var EveAgentStore = class {
2536
2635
  const wasStreaming = this.#status === "streaming";
2537
2636
  updatePendingAuthorizations(this.#pendingAuthorizations, event);
2538
2637
  this.#events = [...this.#events, event];
2539
- this.#applyServerEvent(event);
2638
+ this.#handleReconciliation(this.#messageSubmissions.apply(event));
2540
2639
  this.#callbacks.onEvent?.(event);
2541
2640
  this.#applyTerminalStreamFailure(event);
2542
2641
  const settled = isCurrentTurnBoundaryEvent(event) && this.#pendingAuthorizations.size === 0;
@@ -2548,41 +2647,29 @@ var EveAgentStore = class {
2548
2647
  this.#publish();
2549
2648
  if (this.#activeTurn === void 0 && wasStreaming && settled) this.#callbacks.onFinish?.(this.#snapshot);
2550
2649
  }
2551
- #applyServerEvent(event) {
2552
- const pendingSubmission = this.#pendingMessageSubmissions[0];
2553
- if (event.type === "message.received" && pendingSubmission !== void 0) {
2554
- const submissionId = pendingSubmission.id;
2555
- if (this.#activeTurn?.followUpSubmissionIds.delete(submissionId)) this.#activeTurn.receivedFollowUpEvents.add(event);
2556
- this.#pendingMessageSubmissions = this.#pendingMessageSubmissions.slice(1);
2557
- this.#projection.replace((candidate) => candidate.type === "client.message.submitted" && candidate.data.submissionId === submissionId, event);
2558
- return;
2650
+ #handleReconciliation(reconciliation) {
2651
+ if (reconciliation === void 0) return false;
2652
+ let followed = 0;
2653
+ for (const id of reconciliation.ids) if (this.#activeTurn?.followUpSubmissionIds.delete(id)) followed += 1;
2654
+ if (followed > 0 && this.#activeTurn !== void 0) {
2655
+ if (reconciliation.alreadyProjected) this.#activeTurn.receivedFollowUps += followed;
2656
+ else {
2657
+ const previous = this.#activeTurn.receivedFollowUpEvents.get(reconciliation.event) ?? 0;
2658
+ this.#activeTurn.receivedFollowUpEvents.set(reconciliation.event, previous + followed);
2659
+ }
2559
2660
  }
2560
- this.#projection.append(event);
2661
+ return true;
2561
2662
  }
2562
2663
  #applyTerminalStreamFailure(event) {
2563
2664
  const error = toTerminalStreamFailureError(event);
2564
2665
  if (error === void 0) return;
2565
2666
  this.#status = "error";
2566
- this.#failPendingMessageSubmission(error);
2667
+ this.#messageSubmissions.failAll(error);
2567
2668
  if (this.#error === void 0) {
2568
2669
  this.#error = error;
2569
2670
  this.#callbacks.onError?.(error);
2570
2671
  }
2571
2672
  }
2572
- #failPendingMessageSubmission(error, submissionId) {
2573
- const pending = submissionId === void 0 ? this.#pendingMessageSubmissions[0] : this.#pendingMessageSubmissions.find((candidate) => candidate.id === submissionId);
2574
- if (pending === void 0) return;
2575
- this.#pendingMessageSubmissions = this.#pendingMessageSubmissions.filter((candidate) => candidate.id !== pending.id);
2576
- this.#projection.replace((event) => event.type === "client.message.submitted" && event.data.submissionId === pending.id, {
2577
- data: {
2578
- createdAt: pending.createdAt,
2579
- error: { message: error.message },
2580
- message: pending.message,
2581
- submissionId: pending.id
2582
- },
2583
- type: "client.message.failed"
2584
- });
2585
- }
2586
2673
  #createSnapshot() {
2587
2674
  return {
2588
2675
  data: this.#projection.data,
@@ -2855,6 +2942,9 @@ const messageRun = {
2855
2942
 
2856
2943
  //#endregion
2857
2944
  //#region src/client/message-reducer.ts
2945
+ function receivedMessageEventId(event) {
2946
+ return event.meta.id ?? `${event.data.turnId}:${event.data.sequence}`;
2947
+ }
2858
2948
  function defaultMessageReducer() {
2859
2949
  return {
2860
2950
  initial() {
@@ -2901,15 +2991,17 @@ function reduceMessageData(data, event) {
2901
2991
  for (const resolution of event.data.resolutions) next = resolveInputRequest(next, resolution);
2902
2992
  return next;
2903
2993
  }
2904
- case "message.received": return upsertMessage(data, {
2905
- id: `${event.data.turnId}:user`,
2906
- metadata: {
2907
- status: "complete",
2908
- turnId: event.data.turnId
2909
- },
2910
- parts: projectReceivedParts(event.data.parts, event.data.message),
2911
- role: "user"
2912
- });
2994
+ case "message.received":
2995
+ if (event.data.kind === "execution.background_task") return data;
2996
+ return upsertMessage(data, {
2997
+ id: `${receivedMessageEventId(event)}:user`,
2998
+ metadata: {
2999
+ status: "complete",
3000
+ turnId: event.data.turnId
3001
+ },
3002
+ parts: projectReceivedParts(event.data.parts, event.data.message),
3003
+ role: "user"
3004
+ });
2913
3005
  case "step.started": return updateAssistantMessage(data, event.data.turnId, (message) => ensureStepStartPart(message, event.data.stepIndex));
2914
3006
  case "reasoning.appended": return updateAssistantMessage(data, event.data.turnId, (message) => messageRun.append(ensureStepStartPart(message, event.data.stepIndex), {
2915
3007
  delta: event.data.reasoningDelta,