eve 0.76.0 → 0.76.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 (171) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/src/channel/channel-address.d.ts +3 -1
  3. package/dist/src/channel/channel-address.js +1 -1
  4. package/dist/src/channel/session-stranded-error.d.ts +25 -0
  5. package/dist/src/channel/session-stranded-error.js +1 -0
  6. package/dist/src/channel/session.d.ts +13 -6
  7. package/dist/src/channel/types.d.ts +29 -1
  8. package/dist/src/chunks/{use-eve-agent-QmriPA-w.js → use-eve-agent-BtKWTEpm.js} +31 -14
  9. package/dist/src/chunks/{use-eve-agent-D2V9UhLt.js → use-eve-agent-jI6EsYAu.js} +31 -14
  10. package/dist/src/cli/commands/registry-project.d.ts +10 -3
  11. package/dist/src/cli/commands/registry-project.js +2 -2
  12. package/dist/src/cli/commands/registry.js +1 -1
  13. package/dist/src/cli/dev/tui/runner.js +3 -3
  14. package/dist/src/cli/dev/tui/stranded-session.d.ts +9 -0
  15. package/dist/src/cli/dev/tui/stranded-session.js +1 -0
  16. package/dist/src/client/client-error.d.ts +16 -0
  17. package/dist/src/client/client-error.js +1 -1
  18. package/dist/src/client/client.js +1 -1
  19. package/dist/src/client/index.d.ts +3 -2
  20. package/dist/src/client/index.js +1 -1
  21. package/dist/src/client/open-stream.d.ts +5 -3
  22. package/dist/src/client/open-stream.js +1 -1
  23. package/dist/src/client/session-controls.js +1 -1
  24. package/dist/src/client/session.js +1 -1
  25. package/dist/src/client/transcript-reducer.d.ts +46 -0
  26. package/dist/src/client/transcript-reducer.js +2 -0
  27. package/dist/src/compiled/.vendor-stamp.json +1 -1
  28. package/dist/src/compiler/extension-compatibility.d.ts +20 -20
  29. package/dist/src/compiler/extension-compatibility.js +1 -1
  30. package/dist/src/compiler/manifest.js +1 -1
  31. package/dist/src/compiler/normalize-agent-config.js +1 -1
  32. package/dist/src/context/accessors.d.ts +1 -1
  33. package/dist/src/context/build-callback-context.js +1 -1
  34. package/dist/src/context/dynamic-resolve-context.js +1 -1
  35. package/dist/src/context/keys.d.ts +5 -0
  36. package/dist/src/context/keys.js +1 -1
  37. package/dist/src/context/providers/session.d.ts +1 -0
  38. package/dist/src/context/providers/session.js +1 -1
  39. package/dist/src/context/session-context.d.ts +8 -2
  40. package/dist/src/dynamic/definition.d.ts +7 -1
  41. package/dist/src/evals/types.d.ts +2 -1
  42. package/dist/src/eve-channel/index.js +1 -1
  43. package/dist/src/eve-channel/request.js +2 -2
  44. package/dist/src/eve-channel/support.d.ts +2 -0
  45. package/dist/src/eve-channel/support.js +1 -1
  46. package/dist/src/execution/agent-sessions/steps.js +1 -1
  47. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  48. package/dist/src/execution/eve-workflow-attributes.d.ts +15 -0
  49. package/dist/src/execution/eve-workflow-attributes.js +1 -1
  50. package/dist/src/execution/install-workflow-world.d.ts +7 -0
  51. package/dist/src/execution/install-workflow-world.js +1 -0
  52. package/dist/src/execution/legacy-session/authorization.js +1 -1
  53. package/dist/src/execution/legacy-session/inbox.js +1 -1
  54. package/dist/src/execution/legacy-session/turn-workflow.js +1 -1
  55. package/dist/src/execution/session/entry.js +1 -1
  56. package/dist/src/execution/session/handoff-steps.d.ts +11 -0
  57. package/dist/src/execution/session/handoff-steps.js +1 -1
  58. package/dist/src/execution/session/program.js +1 -1
  59. package/dist/src/execution/session/timeout-steps.js +1 -1
  60. package/dist/src/execution/session-event-stream.d.ts +6 -0
  61. package/dist/src/execution/session-event-stream.js +1 -1
  62. package/dist/src/execution/session-inbox/address.d.ts +4 -0
  63. package/dist/src/execution/session-inbox/address.js +1 -1
  64. package/dist/src/execution/session-inbox/end-stranded-session.d.ts +22 -0
  65. package/dist/src/execution/session-inbox/end-stranded-session.js +1 -0
  66. package/dist/src/execution/session-inbox/hook-release.d.ts +4 -0
  67. package/dist/src/execution/session-inbox/hook-release.js +1 -0
  68. package/dist/src/execution/session-inbox/owner.d.ts +48 -0
  69. package/dist/src/execution/session-inbox/owner.js +1 -0
  70. package/dist/src/execution/session-inbox/resume.d.ts +12 -0
  71. package/dist/src/execution/session-inbox/resume.js +1 -1
  72. package/dist/src/execution/session-inbox/runnable-owner.d.ts +13 -0
  73. package/dist/src/execution/session-inbox/runnable-owner.js +1 -0
  74. package/dist/src/execution/session-inbox/stranded-replay-guard.d.ts +16 -0
  75. package/dist/src/execution/session-inbox/stranded-replay-guard.js +1 -0
  76. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  77. package/dist/src/execution/workflow-runtime.d.ts +5 -1
  78. package/dist/src/execution/workflow-runtime.js +1 -1
  79. package/dist/src/harness/compaction/step.js +1 -1
  80. package/dist/src/harness/input-request-resolution.js +1 -1
  81. package/dist/src/harness/model-call/call.d.ts +1 -1
  82. package/dist/src/harness/model-call/call.js +1 -1
  83. package/dist/src/harness/model-call/errors.d.ts +5 -6
  84. package/dist/src/harness/model-call/errors.js +1 -1
  85. package/dist/src/harness/model-call/recovery.d.ts +4 -3
  86. package/dist/src/harness/model-call/request.d.ts +2 -1
  87. package/dist/src/harness/model-call/request.js +1 -1
  88. package/dist/src/harness/model-call/tools.js +1 -1
  89. package/dist/src/harness/model-profile.d.ts +10 -3
  90. package/dist/src/harness/model-profile.js +1 -1
  91. package/dist/src/harness/prompt-cache.d.ts +6 -3
  92. package/dist/src/harness/prompt-cache.js +1 -1
  93. package/dist/src/harness/provider-safety.d.ts +2 -1
  94. package/dist/src/harness/provider-safety.js +1 -1
  95. package/dist/src/harness/provider-tools.d.ts +1 -1
  96. package/dist/src/harness/provider-tools.js +1 -1
  97. package/dist/src/harness/session-machine/transitions.d.ts +3 -1
  98. package/dist/src/harness/session-machine/transitions.js +1 -1
  99. package/dist/src/harness/step/context.js +1 -1
  100. package/dist/src/harness/step-hooks.js +1 -1
  101. package/dist/src/internal/application/compiled-artifacts.js +4 -4
  102. package/dist/src/internal/application/package.d.ts +1 -1
  103. package/dist/src/internal/application/package.js +1 -1
  104. package/dist/src/internal/application/stamped-package-version.d.ts +5 -0
  105. package/dist/src/internal/application/stamped-package-version.js +1 -0
  106. package/dist/src/internal/authored-definition/core.d.ts +3 -0
  107. package/dist/src/internal/authored-definition/core.js +1 -1
  108. package/dist/src/internal/model-auth/available-models.js +1 -1
  109. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
  110. package/dist/src/internal/workflow/development-run-unavailable-error.d.ts +15 -0
  111. package/dist/src/internal/workflow/development-run-unavailable-error.js +1 -0
  112. package/dist/src/internal/workflow/development-world-server.js +1 -1
  113. package/dist/src/internal/workflow-bundle/workflow-builders.js +1 -1
  114. package/dist/src/protocol/message.d.ts +14 -0
  115. package/dist/src/protocol/message.js +1 -1
  116. package/dist/src/public/channels/index.d.ts +1 -1
  117. package/dist/src/public/channels/index.js +1 -1
  118. package/dist/src/public/context/index.d.ts +1 -1
  119. package/dist/src/public/definitions/callback-context.d.ts +1 -1
  120. package/dist/src/public/definitions/channel.d.ts +1 -0
  121. package/dist/src/public/definitions/channel.js +1 -1
  122. package/dist/src/public/models/openai/chatgpt/model.js +1 -1
  123. package/dist/src/public/models/openai/chatgpt/transport.js +1 -1
  124. package/dist/src/public/tanstack/index.d.ts +40 -0
  125. package/dist/src/public/tanstack/index.js +1 -0
  126. package/dist/src/runtime/agent/resolve-model.js +1 -1
  127. package/dist/src/runtime/resolve-agent.js +1 -1
  128. package/dist/src/setup/flows/model-login.js +1 -1
  129. package/dist/src/setup/integrations/web/framework.d.ts +8 -0
  130. package/dist/src/setup/integrations/web/framework.js +1 -0
  131. package/dist/src/setup/integrations/web/hosting.d.ts +43 -0
  132. package/dist/src/setup/integrations/web/hosting.js +15 -0
  133. package/dist/src/setup/integrations/web/setup.d.ts +6 -10
  134. package/dist/src/setup/integrations/web/setup.js +23 -17
  135. package/dist/src/setup/scaffold/create/project.js +1 -1
  136. package/dist/src/setup/scaffold/create/web-template.d.ts +6 -2
  137. package/dist/src/setup/scaffold/create/web-template.js +39 -2
  138. package/dist/src/setup/scaffold/update/channels.js +1 -1
  139. package/dist/src/setup/vercel-project-framework.js +1 -1
  140. package/dist/src/shared/agent-definition.d.ts +25 -0
  141. package/dist/src/shared/agent-definition.js +1 -1
  142. package/dist/src/shared/chatgpt-model.d.ts +8 -0
  143. package/dist/src/shared/chatgpt-model.js +1 -1
  144. package/dist/src/subagents/callback-route.js +1 -1
  145. package/dist/src/svelte/index.js +1 -1
  146. package/dist/src/svelte/use-eve-agent.js +1 -1
  147. package/dist/src/tools/provided/ask-question-workflow.js +1 -1
  148. package/dist/src/tools/provided/sleep-workflow.js +1 -1
  149. package/dist/src/vue/index.js +1 -1
  150. package/dist/src/vue/use-eve-agent.js +1 -1
  151. package/docs/agent-config.md +32 -1
  152. package/docs/channels/custom.mdx +26 -0
  153. package/docs/channels/eve.mdx +8 -5
  154. package/docs/concepts/built-in-tools.md +17 -0
  155. package/docs/concepts/execution-model-and-durability.mdx +100 -0
  156. package/docs/concepts/project-structure.mdx +2 -2
  157. package/docs/concepts/sessions-runs-and-streaming.md +36 -34
  158. package/docs/guides/client/messages.mdx +10 -1
  159. package/docs/guides/deployment/overview.md +2 -2
  160. package/docs/guides/dev-tui.md +2 -0
  161. package/docs/guides/frontend/meta.json +9 -1
  162. package/docs/guides/frontend/nextjs.mdx +1 -1
  163. package/docs/guides/frontend/overview.mdx +9 -8
  164. package/docs/guides/frontend/tanstack.mdx +201 -0
  165. package/docs/guides/session-context.md +1 -0
  166. package/docs/install-integrations.mdx +1 -1
  167. package/docs/instructions.mdx +1 -1
  168. package/docs/reference/typescript-api.md +1 -1
  169. package/docs/tools/human-in-the-loop.md +2 -0
  170. package/docs/tutorial/ship-it.mdx +1 -1
  171. package/package.json +7 -1
@@ -67,6 +67,14 @@ Gateway, share that ID so their Gateway generations can be found together.
67
67
  Direct-provider calls do not receive the option. An authored `gateway.sessionId`
68
68
  takes precedence; AI Gateway hashes IDs longer than 256 characters.
69
69
 
70
+ For direct OpenAI and `chatgpt()` model calls, eve sets
71
+ `providerOptions.openai.promptCacheKey` to a SHA-256 hash of the session ID,
72
+ which helps OpenAI route a session's calls to the cache that holds its prompt
73
+ prefix. Each subagent session gets its own key. An authored
74
+ `openai.promptCacheKey` takes precedence. `chatgpt()` calls also send the key
75
+ in the `session-id` header, which the Codex backend uses for cache routing.
76
+ Gateway-routed calls do not receive the option.
77
+
70
78
  ### Choose the model dynamically
71
79
 
72
80
  To select a model from the incoming prompt with an AI SDK decision model, use
@@ -144,6 +152,29 @@ the agent-level setting for that selection. Omitting it inherits the agent setti
144
152
 
145
153
  Run `eve set model --reasoning high` to update this field from the command line.
146
154
 
155
+ ## Prompt caching
156
+
157
+ eve caches prompts automatically for AI Gateway models and for Anthropic models
158
+ it recognizes by provider or model id. Set `modelOptions.promptCache` on a
159
+ directly called model to mark an unrecognized model as Anthropic (such as a
160
+ Bedrock application inference profile) or to use a 1-hour cache:
161
+
162
+ ```ts title="agent/agent.ts"
163
+ import { bedrock } from "@ai-sdk/amazon-bedrock";
164
+ import { defineAgent } from "eve";
165
+
166
+ export default defineAgent({
167
+ model: bedrock(process.env.BEDROCK_INFERENCE_PROFILE_ARN!),
168
+ modelContextWindowTokens: 200_000,
169
+ modelOptions: {
170
+ promptCache: { anthropic: { ttl: "1h" } }, // or { anthropic: {} } for the default 5m
171
+ },
172
+ });
173
+ ```
174
+
175
+ Not every Claude model supports a 1-hour cache. eve rejects `promptCache` on
176
+ Gateway models; use `providerOptions.gateway.caching` instead.
177
+
147
178
  ## Compaction
148
179
 
149
180
  Compaction summarizes older turns as you approach the context window. It's on by default, so you only tune when it kicks in. eve adds the estimated fixed checkpoint-prompt envelope to the trigger count, so compaction starts sooner than the conversation-only estimate. Lower `thresholdPercent` to compact sooner:
@@ -361,7 +392,7 @@ it falls back to the World's default retention period.
361
392
  | Field | Type | Default | Description |
362
393
  | -------------- | ------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
363
394
  | `reasoning` | `AgentReasoningDefinition` | provider default | Provider-agnostic reasoning effort forwarded to the agent's turn model calls. |
364
- | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
395
+ | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call, and [`promptCache`](#prompt-caching) settings. |
365
396
  | `limits` | `AgentLimitsDefinition` | field-specific | Framework-owned runtime limits. Sessions complete after 30 days by default; usage-limit defaults and inheritance are described above. Set a limit to `false` to disable it. |
366
397
  | `experimental` | `AgentExperimentalDefinition` | unset | Unstable opt-ins. `workflow.world` selects the Workflow world package on the root agent; `workflow.modelCallsPerStep` batches sequential model calls into a wider replay unit; `workflow.retention` controls how long the durable runtime keeps run data. |
367
398
  | `build` | `{ externalDependencies?: string[] }` | none | Hosted-build packaging controls. `externalDependencies` keeps listed packages external while eve compiles authored modules such as tools and channels, and traces those packages into the hosted output. |
@@ -116,6 +116,15 @@ await source.reset({ reason: "Start over" });
116
116
  const currentSession = await resolveSession(threadId);
117
117
  ```
118
118
 
119
+ When the session at an address is
120
+ [stranded](../concepts/execution-model-and-durability#stranded-sessions) because another eve version
121
+ built it, `send()` ends that session and starts a fresh one. The message becomes the fresh
122
+ session's first turn, and the fresh session can read recent text from the earlier conversation;
123
+ see [What a replacement session receives](../concepts/execution-model-and-durability#what-a-replacement-session-receives). Concurrent sends to the same stranded address still
124
+ produce one fresh session. A `respond()` call, or a `deliver()` that includes input responses,
125
+ carries answers a fresh session has no request for, so it throws `SessionStrandedError` and leaves
126
+ the stranded session in place.
127
+
119
128
  `Session` is fixed: every call targets exactly one durable ID. It never creates,
120
129
  follows, or resolves a replacement.
121
130
 
@@ -131,6 +140,23 @@ await session.reset({ reason: "Retire this session" });
131
140
  await session.getEventStream({ startIndex: 12 });
132
141
  ```
133
142
 
143
+ For a stranded session, `Session.send()`, `Session.respond()`, and a following
144
+ `Session.getEventStream()` throw `SessionStrandedError` from `eve/channels` and deliver nothing.
145
+ `getEventStream({ follow: false })` still reads the recorded history up to the current tail without
146
+ changing the session. `error.owner.eveVersion` names the eve release that built the session when it recorded one. Call
147
+ `reset()` to end the stranded session, then start a new one:
148
+
149
+ ```ts
150
+ import { SessionStrandedError } from "eve/channels";
151
+
152
+ try {
153
+ await attachSession(sessionId).send(text, { auth });
154
+ } catch (error) {
155
+ if (!SessionStrandedError.is(error)) throw error;
156
+ await attachSession(sessionId).reset({ reason: "Session was stranded by an upgrade" });
157
+ }
158
+ ```
159
+
134
160
  `respond()` accepts exact response literals directly. If responses have already
135
161
  been widened to `InputResponse[]`—for example, after decoding a platform
136
162
  payload—validate them with eve's strict schema before delivery. The validated
@@ -21,7 +21,8 @@ export default eveChannel({
21
21
  ## Web Chat with Sign in with Vercel
22
22
 
23
23
  Run `eve add channel/web`, or select **Web Chat** from the terminal UI's channel
24
- picker. Choose a hosting layout, then **Sign in with Vercel**. Setup uses a
24
+ picker. Choose **Next.js**, a hosting layout, then **Sign in with Vercel**. Sign
25
+ in with Vercel is available only for the Next.js Web Chat app. Setup uses a
25
26
  linked Vercel project and creates a Vercel App that only members of that
26
27
  project's team can sign in to.
27
28
 
@@ -159,13 +160,15 @@ Set `turnPolicy: "queue"` on `eveChannel(...)` when follow-ups should wait for a
159
160
  Sending a message to an unknown or terminal session ID returns `409` with
160
161
  `{"code":"session_not_active","error":"The session is no longer active.","ok":false}`.
161
162
  An existing workflow whose inbox is not yet available returns `409 session_not_ready` instead.
162
- TypeScript clients expose the stable code as `ClientError.code`. The route never
163
- creates or follows a replacement session.
163
+ A session that another eve version built returns `409 session_stranded` with the `eveVersion`
164
+ that built it.
164
165
 
165
166
  ### Stream events
166
167
 
167
168
  Stream a session as newline-delimited JSON from `GET /eve/v1/session/:sessionId/stream`. The [session protocol](../concepts/sessions-runs-and-streaming#stream-a-session) defines the event set, envelopes, cursors, and reconnection behavior.
168
169
 
170
+ Following a stranded session's stream returns `409 session_stranded`, just like a send. The TypeScript client reports `ClientSessionStrandedError` without retrying. Pass `follow=false` to read the recorded history instead: the response ends at the durable tail, carries the `x-eve-stream-tail-index` header, and never inspects or changes the session, so it works before a reset. A reset attempts to append `session.failed` with code `session_stranded` and close the stream. Existing readers finish when they receive that terminal event.
171
+
169
172
  ### Cancel a turn
170
173
 
171
174
  Post to `/eve/v1/session/:sessionId/cancel` to request cancellation of the active turn. You can include the observed `turnId` to keep a late request from cancelling a newer turn. Cancellation is asynchronous; confirm the turn boundary on the stream as `turn.cancelled` followed by `session.waiting`.
@@ -174,7 +177,7 @@ See [Cancel the in-flight turn](../concepts/sessions-runs-and-streaming#cancel-t
174
177
 
175
178
  ### Clear context
176
179
 
177
- Post to `/eve/v1/session/:sessionId/clear` to remove model-message history while preserving the session ID, system prompt, tools, skills, durable state, limits, and sandbox. See [Compact, clear, and reset](../concepts/sessions-runs-and-streaming#compact-clear-and-reset).
180
+ Post to `/eve/v1/session/:sessionId/clear` to remove model-message history while preserving the session ID, system prompt, tools, skills, durable state, limits, and sandbox. A stranded session cannot run the clear, so the route returns `409 session_stranded` and changes nothing; post to the `reset` route to end it instead. See [Compact, clear, and reset](../concepts/sessions-runs-and-streaming#compact-clear-and-reset).
178
181
 
179
182
  ### Compact context
180
183
 
@@ -182,7 +185,7 @@ Post to `/eve/v1/session/:sessionId/compact` to summarize context without sendin
182
185
 
183
186
  ### Reset a session
184
187
 
185
- Post to `/eve/v1/session/:sessionId/reset` to terminally retire that session. Reset never replaces the ID automatically; create a new session explicitly for a fresh conversation. See [Compact, clear, and reset](../concepts/sessions-runs-and-streaming#compact-clear-and-reset).
188
+ Post to `/eve/v1/session/:sessionId/reset` to terminally retire that session. Reset never replaces the ID automatically; create a new session explicitly for a fresh conversation. Reset also works on a stranded session: eve cancels the stranded workflow run directly instead of delivering the command to it. See [Compact, clear, and reset](../concepts/sessions-runs-and-streaming#compact-clear-and-reset).
186
189
 
187
190
  ## Replace or disable the defaults
188
191
 
@@ -240,6 +240,23 @@ export default webSearch({ provider: "native", fallback: "exa" });
240
240
 
241
241
  The `provider` setting applies only to AI Gateway models. Direct OpenAI, Anthropic, and Gemini 3 and later models always use their native search; other direct models omit `web_search`.
242
242
 
243
+ eve identifies a direct model's vendor by its AI SDK provider name, so a `createOpenAI()` model pointed at an OpenAI-compatible endpoint, such as Amazon Bedrock's, gets OpenAI web search. When the endpoint rejects it, eve retries the step without `web_search`, so each step makes one rejected call first. To skip it, give the provider its own name:
244
+
245
+ ```ts title="agent/agent.ts"
246
+ import { createOpenAI } from "@ai-sdk/openai";
247
+ import { defineAgent } from "eve";
248
+
249
+ const mantle = createOpenAI({
250
+ name: "mantle",
251
+ baseURL: "https://bedrock-mantle.us-east-1.api.aws/openai/v1",
252
+ apiKey: process.env.BEDROCK_API_KEY,
253
+ });
254
+
255
+ export default defineAgent({ model: mantle("openai.gpt-5.6-sol") });
256
+ ```
257
+
258
+ The agent then gets no `web_search`, the same as other direct models without native search.
259
+
243
260
  Replace provider-managed search with an authored implementation:
244
261
 
245
262
  ```ts title="agent/tools/web_search.ts"
@@ -37,6 +37,106 @@ Every workflow run records each step it executes, so a session that ran on one r
37
37
 
38
38
  The move happens once the turn settles and nothing else is waiting: no queued messages, running tasks or subagents, or pending human input. The new run receives the compacted history and settled state, then waits for the next message. If a single message reaches an otherwise idle session before the move completes, eve moves the session with that message, and the message's turn runs on the new run. If work is still pending, eve tries again after a later turn settles. The session keeps its ID, event stream, continuation addresses, and deadline; compaction never extends [`sessionTimeoutMs`](../agent-config#runtime-limits). If the new run cannot take ownership, the current run keeps the session, writes a `session handoff failed` warning to its runtime logs, and tries again later.
39
39
 
40
+ ### Stranded sessions
41
+
42
+ A session is stranded when another eve version built the workflow run that owns it. A run can only be replayed by the eve version that started it, so the conversation is still stored, but nothing on the current deployment can process a new message. On worlds without per-deployment routing, eve checks the owner before delivering channel messages and input responses, rather than committing them to a session that cannot answer.
43
+
44
+ eve treats every world other than Vercel Workflow as running one eve version at a time, so a stranded session can never run again:
45
+
46
+ | World | When a session is stranded |
47
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
48
+ | Vercel Workflow | eve does not check. Each run keeps executing on the deployment that started it, and handoff moves idle sessions to newer deployments. |
49
+ | Local world with `eve start` | After any upgrade that changes the eve version. Sessions created before eve recorded its version on the run are treated the same way. |
50
+ | Postgres and other worlds | Same as the local world. Serve one world from one eve version at a time: a deployment running another version cannot replay these sessions, and eve does not route runs between deployments. |
51
+ | `eve dev` | After an eve version change. A session from a previous dev server invocation built by the same eve version is not stranded: it stays dormant until you restart `eve dev` with [`--resume`](../reference/cli#local-workflow-recovery). |
52
+
53
+ eve ends a stranded session without running any of its code. When that happens depends on the kind of run:
54
+
55
+ - **A root session**, one that a channel or client started, stays where it is. It keeps its address and executes nothing until a new channel message replaces it, a reset ends it, or its session timeout elapses, whichever comes first.
56
+ - **A subagent session or workflow tool run**, including a task, is cancelled the next time the world delivers work to it, for example when the local world or Postgres recovers active runs at startup. No later message could resume its interrupted work, so eve does not wait.
57
+
58
+ See [How a stranded session ends](#how-a-stranded-session-ends) for the details.
59
+
60
+ For a root session, what happens next depends on how the message is addressed:
61
+
62
+ - **A channel address**, such as a Slack thread or any `from(address).send(...)`, resets the stranded session and starts a fresh session at the same address. The message becomes the fresh session's first turn. Concurrent messages to the address produce one fresh session. The fresh session names the session it replaced, whose recorded conversation your instructions can read; see [What a replacement session receives](#what-a-replacement-session-receives). A delivery that carries input responses, with or without a message, throws `SessionStrandedError` and leaves the stranded session in place, because a fresh session has no request for them to answer. See [custom channels](../channels/custom#channel-operations-and-session-handles).
63
+ - **A session ID** is never reset implicitly. The eve HTTP channel returns `409` with `code: "session_stranded"` and the `eveVersion` that built the session when it recorded one. `Session.send()` and `Session.respond()` throw `SessionStrandedError` from `eve/channels`, and the TypeScript client's `ClientSession.send()` and `respond()` throw `ClientSessionStrandedError` from `eve/client`. Nothing is delivered, and the stranded run keeps its status and event log. See [Start and continue a session](../channels/eve#start-and-continue-a-session).
64
+ - **Following a stream** of a stranded session returns the same `409 session_stranded`. The TypeScript client stops reconnecting and reports `ClientSessionStrandedError`; the TUI explains how to recover. An existing connection learns this when it reconnects. Messages accepted before the upgrade are not redelivered.
65
+ - **Reading recorded history** still works. A bounded read (`stream({ follow: false })`, or `follow=false` on the stream route) returns the events recorded so far without inspecting or changing the session, so you can read a stranded conversation before deciding to reset it.
66
+ - **Reset** works on a stranded session through a channel address or a session ID. **Clear** would keep a session that cannot run, so it is refused: `Session.clear()` throws `SessionStrandedError`, the eve HTTP channel returns `409 session_stranded`, and nothing changes. Reset the session instead.
67
+ - **Session timeouts** end a stranded session the same way a reset does; see [How a stranded session ends](#how-a-stranded-session-ends).
68
+
69
+ A reset ends the stranded session without running any of its code. eve cancels the session's owner run and, after a handoff, the original run that anchors its event stream, then waits until the world releases the session's addresses. If another run has taken over the session since eve inspected it, the reset ends nothing and fails so the request can be retried. The stranded session's `session.completed` and `session.failed` hooks do not run; the fresh session runs `session.started` as usual. Before cancelling, eve attempts to append a `session.failed` event with code `session_stranded` and close the old stream, without invoking authored hooks. Stream publication is best effort and waits at most 5 seconds: missing payload keys, unavailable storage, or a stalled write do not prevent reset. Each step tolerates an earlier attempt having completed it, so a reset that fails part way can be retried.
70
+
71
+ Each reset logs a `warn` record, `Reset stranded session`, with the previous session ID, both eve versions, and the `trigger`: `message`, `reset`, or `timeout`.
72
+
73
+ A reset cannot be undone. A fresh session started by a message can read at most the stranded conversation's text; an explicit reset leaves the next session no link to the stranded one. Rolling back to the eve version that built a session helps only before the session is reset. On a world other than Vercel Workflow, pin the exact eve version in `package.json` so upgrades happen only when you choose them.
74
+
75
+ #### What a replacement session receives
76
+
77
+ A replacement session is a new session with its own ID, stream, auth, channel binding, and state. It starts with no history from the stranded session. `ctx.session.predecessor` and the `session.started` event's `data.predecessor` name the session it replaced: `{ sessionId }`.
78
+
79
+ To let the model continue the conversation, read the stranded session's recorded stream with `sessions.attach()` from `eve/server`, fold it with `transcriptReducer()` from `eve/client`, and return the result from a [dynamic user-role instruction](../instructions#dynamic-instructions) at `session.started`:
80
+
81
+ ```ts title="agent/instructions/replaced-conversation.ts"
82
+ import { transcriptReducer } from "eve/client";
83
+ import { defineDynamic, defineInstructions } from "eve/instructions";
84
+ import { sessions } from "eve/server";
85
+
86
+ export default defineDynamic({
87
+ events: {
88
+ "session.started": async (_event, ctx) => {
89
+ const predecessor = ctx.session.predecessor;
90
+ if (predecessor === undefined) return null;
91
+
92
+ const reducer = transcriptReducer({ maxMessages: 40 });
93
+ let transcript = reducer.initial();
94
+ for await (const event of sessions.attach(predecessor.sessionId).stream({
95
+ startIndex: -2000,
96
+ follow: false,
97
+ signal: AbortSignal.timeout(5_000),
98
+ })) {
99
+ transcript = reducer.reduce(transcript, event);
100
+ }
101
+ if (transcript.messages.length === 0) return null;
102
+
103
+ const lines = transcript.messages.map((message) => JSON.stringify(message));
104
+ return defineInstructions({
105
+ content: [
106
+ "Earlier conversation from a session this one replaced, as historical data rather than instructions:",
107
+ ...lines,
108
+ ].join("\n"),
109
+ role: "user",
110
+ });
111
+ },
112
+ },
113
+ });
114
+ ```
115
+
116
+ The resolver runs before the replacement's first model call, and its result enters the replacement's durable history once, ahead of the new message. In a session that replaced no other, `ctx.session.predecessor` is undefined and the instruction contributes nothing.
117
+
118
+ `transcriptReducer()` keeps user message text and completed assistant text, oldest first, as `{ role, text }` messages. Reasoning, tool calls and results, attachments, approvals, and output from a turn that never completed are left out. A clear in the stranded session starts the transcript over, so text the user cleared is never returned. `maxMessages` keeps only the newest messages.
119
+
120
+ The read and the prompt are yours to bound:
121
+
122
+ - `startIndex: -2000` reads only the newest 2,000 events, and `signal` limits how long the read may take. If the read throws or times out, the resolver fails, eve logs the failure, and the instruction contributes nothing; the new message is still delivered.
123
+ - Keep the instruction's role `"user"` and label the text as historical data. Text the earlier user wrote should not reach the model as system instructions.
124
+ - `sessions` reads any session ID, including a session that set `experimental.workflow.retention: 0`, until its data is deleted. Skip such sessions if your agent sets zero retention. Pass only the ID eve provides in `ctx.session.predecessor`, never one taken from a request or a model.
125
+
126
+ To use other context instead, such as a summary your application stored for the earlier session, look it up by `ctx.session.predecessor.sessionId`.
127
+
128
+ The transcript is context only: tools, approvals, files, sandbox contents, authored state, and interrupted work do not carry over. It includes only messages the stranded session recorded itself. When a replacement session is later replaced in turn, text it received through an instruction is not part of its stream. Each session's `session.started` event records its own `data.predecessor`, so you can follow `predecessor.sessionId` back through earlier sessions if you need older text.
129
+
130
+ #### How a stranded session ends
131
+
132
+ A stranded root session ends on its next channel message, a reset, or its [session timeout](../agent-config#runtime-limits), whichever comes first. It executes nothing while it waits. When the local world or Postgres recovers active runs at startup, eve acknowledges the root session's delivery without replaying it and logs a warning naming the run and both eve versions. The run keeps its `pending` or `running` status and its addresses. A later restart skips it the same way.
133
+
134
+ The timeout is the session's own durable timer, armed for the deadline `sessionTimeoutMs` sets from the session's creation or latest deployment handoff (30 days by default; compaction handoffs keep the deadline). The timer is a separate workflow run that keeps working after the upgrade, and when it fires on a stranded session it ends the session the same way a reset does, without starting a fresh one. The next message to the address then starts an ordinary session. A session with `sessionTimeoutMs: false` has no timer, so a stranded one stays until a message or a reset, just as a healthy one would stay open.
135
+
136
+ Ending a root session does not find or cancel its subagent sessions and workflow tool runs. Each one is cancelled when a delivery for it reaches the current deployment, with the cancellation reason `Run retired: its eve deployment is no longer available`, and eve logs a `Retired stranded descendant run` warning. A run that never receives a delivery, such as one parked in a dormant `eve dev` generation, can stay active; cancel it in your Workflow world if you need to. Cancellation does not run authored cleanup or lifecycle hooks, undo side effects, or stop independent external jobs, and results and approvals do not carry over to a replacement session.
137
+
138
+ Runs that recorded no eve version, including every run from a release before eve recorded versions, are treated as stranded.
139
+
40
140
  ### Upgrading sessions from the former execution model
41
141
 
42
142
  You can upgrade directly from a release that used a session driver and separate
@@ -32,7 +32,7 @@ project/
32
32
 
33
33
  Create the agent with [Getting Started](/docs/getting-started). The [Agent Files reference](/docs/reference/agent-files) lists supported paths. Keep agent-only helpers in `agent/lib/`; share reusable capabilities through [extensions](/docs/extensions).
34
34
 
35
- For a generated Next.js chat UI, run `eve add channel/web` from the project root. For an existing browser app, follow your [framework integration guide](/docs/guides/frontend/overview#per-framework-integration).
35
+ For a generated chat UI, run `eve add channel/web` from the project root and choose Next.js or TanStack Start. For an existing browser app, follow your [framework integration guide](/docs/guides/frontend/overview#per-framework-integration).
36
36
 
37
37
  ## Several root agents
38
38
 
@@ -79,7 +79,7 @@ Choose how the frontend and agents run together independently of how many agents
79
79
 
80
80
  `apps/web/` is an organizational convention, not an eve filesystem slot. Deployment configuration owns peer services; eve discovers only the agents. Both Vercel integrations deploy the services together, not as independent releases.
81
81
 
82
- Run `eve add channel/web` from a standalone agent or select a workspace agent with `--agent <name>`. The installer creates a Next.js frontend under `apps/web/`. Choose **Vercel** to configure separate Vercel services and run them with `pnpm dev:all`; you don't need to link a Vercel project to run them locally. Choose **Other** to let Next.js host the agents and run the application with `pnpm dev:web`. Use `pnpm dev` or `pnpm dev:eve` when you only want one agent and the eve terminal UI.
82
+ Run `eve add channel/web` from a standalone agent or select a workspace agent with `--agent <name>`. The installer asks which framework to use and creates a Next.js or TanStack Start frontend under `apps/web/`. Choose **Vercel services** to configure separate Vercel services and run them with `pnpm dev:all`; you don't need to link a Vercel project to run them locally. Choose **Next.js** or **TanStack Start** to let the frontend host the agent and run the application with `pnpm dev:web`. Use `pnpm dev` or `pnpm dev:eve` when you only want one agent and the eve terminal UI.
83
83
 
84
84
  ## Add a second root agent
85
85
 
@@ -67,39 +67,39 @@ curl http://127.0.0.1:2000/eve/v1/session/<sessionId>/stream
67
67
 
68
68
  The stream is newline-delimited JSON (NDJSON), one event per line:
69
69
 
70
- | Event | Meaning |
71
- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
72
- | `session.started` | A durable session was created; carries `trace` when the runtime is traced. |
73
- | `turn.started` | A new turn began; carries the active `trace` when the runtime is traced. |
74
- | `message.received` | Input reached a turn; carries flattened text plus structured text/file parts. |
75
- | `step.started` | A model step began. |
76
- | `action.input.appended` | A raw tool-input text delta and its tool-call identity. |
77
- | `actions.requested` | The model requested one or more actions, including tool calls; calls stream before execution. |
78
- | `action.partial` | A locally executed tool generator yielded a preliminary output snapshot. |
79
- | `action.result` | A tool call returned. |
80
- | `input.requested` | The run paused for human input ([HITL](/docs/human-in-the-loop) approval or a `ctx.ask()` question); carries `requests`. |
81
- | `input.resolved` | The server accepted terminal human-input outcomes; carries `resolutions` with responses when provided. |
82
- | `agent.started` | A workflow run opened a session with `ctx.agent`, such as an agent tool's; carries `taskId` when the run is a task, `sessionId`, and the `streamPath` that `session.agent(started).stream()` follows. |
83
- | `task.started` | A tool call started a [task](#task-events); carries `taskId`, `callId`, `turnId`, the tool `name`, and `kind`: `"agent"` for an agent tool, `"tool"` otherwise. |
84
- | `task.settled` | A task's call settled; carries `taskId`, `callId`, `turnId`, the tool `name`, `kind`, `status`, and `output` or `error.message`; a cancelled call carries `cancel.reason` instead. |
85
- | `reasoning.appended` | A reasoning text delta. |
86
- | `reasoning.completed` | The finalized reasoning block. |
87
- | `message.appended` | An assistant text delta. |
88
- | `message.completed` | A finalized assistant text block. |
89
- | `result.completed` | The finalized structured result for a turn that requested an output schema; carries `result`. |
90
- | `compaction.requested` | Context-window compaction began; carries `modelId`, `sessionId`, `turnId`, `stepIndex`, and `usageInputTokens`. |
91
- | `compaction.completed` | A compaction checkpoint was written to durable history; carries the same model, session, turn, and step identity. |
92
- | `authorization.required` | A connection needs OAuth; carries `name`, `description`, and an `authorization` challenge. |
93
- | `authorization.completed` | A connection's authorization resolved; carries `outcome`. |
94
- | `step.completed` | A model step finished; carries `finishReason` and usage. |
95
- | `step.failed` | A model step failed; carries `{ code, message, details? }`. |
96
- | `turn.waiting` | The open turn parked; carries `turnId` and `on`: `"input"` while a person must act on a sign-in, approval, or question, `"tasks"` while work it started runs. The turn has not ended. |
97
- | `turn.completed` | The turn finished. |
98
- | `turn.failed` | The turn failed; carries `{ code, message, details? }`. |
99
- | `turn.cancelled` | The turn was cancelled before finishing; always followed by `session.waiting`. |
100
- | `session.waiting` | The session parked and is ready for the next message. |
101
- | `session.failed` | The session failed. |
102
- | `session.completed` | The session reached a terminal end. |
70
+ | Event | Meaning |
71
+ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
72
+ | `session.started` | A durable session was created; carries `trace` when the runtime is traced, and `predecessor` when the session [replaced a stranded one](./execution-model-and-durability#what-a-replacement-session-receives). |
73
+ | `turn.started` | A new turn began; carries the active `trace` when the runtime is traced. |
74
+ | `message.received` | Input reached a turn; carries flattened text plus structured text/file parts. |
75
+ | `step.started` | A model step began. |
76
+ | `action.input.appended` | A raw tool-input text delta and its tool-call identity. |
77
+ | `actions.requested` | The model requested one or more actions, including tool calls; calls stream before execution. |
78
+ | `action.partial` | A locally executed tool generator yielded a preliminary output snapshot. |
79
+ | `action.result` | A tool call returned. |
80
+ | `input.requested` | The run paused for human input ([HITL](/docs/human-in-the-loop) approval or a `ctx.ask()` question); carries `requests`. |
81
+ | `input.resolved` | The server accepted terminal human-input outcomes; carries `resolutions` with responses when provided. |
82
+ | `agent.started` | A workflow run opened a session with `ctx.agent`, such as an agent tool's; carries `taskId` when the run is a task, `sessionId`, and the `streamPath` that `session.agent(started).stream()` follows. |
83
+ | `task.started` | A tool call started a [task](#task-events); carries `taskId`, `callId`, `turnId`, the tool `name`, and `kind`: `"agent"` for an agent tool, `"tool"` otherwise. |
84
+ | `task.settled` | A task's call settled; carries `taskId`, `callId`, `turnId`, the tool `name`, `kind`, `status`, and `output` or `error.message`; a cancelled call carries `cancel.reason` instead. |
85
+ | `reasoning.appended` | A reasoning text delta. |
86
+ | `reasoning.completed` | The finalized reasoning block. |
87
+ | `message.appended` | An assistant text delta. |
88
+ | `message.completed` | A finalized assistant text block. |
89
+ | `result.completed` | The finalized structured result for a turn that requested an output schema; carries `result`. |
90
+ | `compaction.requested` | Context-window compaction began; carries `modelId`, `sessionId`, `turnId`, `stepIndex`, and `usageInputTokens`. |
91
+ | `compaction.completed` | A compaction checkpoint was written to durable history; carries the same model, session, turn, and step identity. |
92
+ | `authorization.required` | A connection needs OAuth; carries `name`, `description`, and an `authorization` challenge. |
93
+ | `authorization.completed` | A connection's authorization resolved; carries `outcome`. |
94
+ | `step.completed` | A model step finished; carries `finishReason` and usage. |
95
+ | `step.failed` | A model step failed; carries `{ code, message, details? }`. |
96
+ | `turn.waiting` | The open turn parked; carries `turnId` and `on`: `"input"` while a person must act on a sign-in, approval, or question, `"tasks"` while work it started runs. The turn has not ended. |
97
+ | `turn.completed` | The turn finished. |
98
+ | `turn.failed` | The turn failed; carries `{ code, message, details? }`. |
99
+ | `turn.cancelled` | The turn was cancelled before finishing; always followed by `session.waiting`. |
100
+ | `session.waiting` | The session parked and is ready for the next message. |
101
+ | `session.failed` | The session failed. |
102
+ | `session.completed` | The session reached a terminal end. |
103
103
 
104
104
  The optional `data.trace` on session and turn starts contains eve-owned W3C trace coordinates: `traceId`, `spanId`, and `traceFlags`. Use it to correlate stream consumers such as eval reporters with an observability backend. An uninstrumented target omits it.
105
105
 
@@ -265,6 +265,8 @@ Clear removes model-message history in place, including static and dynamic user-
265
265
 
266
266
  Reset terminally retires the exact session ID. A reset ID never becomes a new session; create another session explicitly for a fresh conversation. Compact, clear, and reset return `"no_active_session"` when the target is already inactive.
267
267
 
268
+ A [stranded session](./execution-model-and-durability#stranded-sessions) cannot run commands. Reset still works: eve cancels the stranded workflow run and returns `"reset"`. Clear is refused with `409 session_stranded` (`SessionStrandedError` from a `Session` handle), because it would keep a session that cannot run. Compact returns `"no_active_session"` and cancel returns `"no_active_turn"`.
269
+
268
270
  ## Reconnect and rewind
269
271
 
270
272
  The stream is durable. Every event is recorded before a step completes, so consumers can reconnect from their cursor when an HTTP connection ends. A nonnegative `startIndex` is an absolute event count: use it to pick up where you dropped off or pass `0` to rewind to the start.
@@ -294,7 +296,7 @@ curl -i "http://127.0.0.1:2000/eve/v1/session/<sessionId>/stream?startIndex=<cou
294
296
  # x-eve-stream-tail-index: <tail>
295
297
  ```
296
298
 
297
- The lookup is opt-in; requests without the parameter get no header. The TypeScript client wraps this into `stream({ follow: false })`.
299
+ The lookup is opt-in; requests without the parameter get no header. Passing `follow=false` instead bounds the response at the durable tail on the server and implies the header. A bounded read only reads recorded history, so it also works for a [stranded session](./execution-model-and-durability#stranded-sessions), whose live stream returns `409 session_stranded`. The TypeScript client wraps this into `stream({ follow: false })`.
298
300
 
299
301
  ## Use the client from TypeScript
300
302
 
@@ -40,7 +40,16 @@ When the stream includes `session.failed`, the turn returns `status: "failed"` r
40
40
  seconds when a durable run has been accepted but its command inbox is still starting. The caller's
41
41
  abort signal cancels the wait. Other errors, `session.respond()`, control methods, and raw HTTP
42
42
  requests do not use this retry. An unknown
43
- or terminal session throws `ClientError` immediately with `session_not_active`. The client never creates a replacement session.
43
+ or terminal session throws `ClientError` immediately with `session_not_active`. A
44
+ [stranded](../../concepts/execution-model-and-durability#stranded-sessions) session throws
45
+ `ClientSessionStrandedError`, a `ClientError` with `code: "session_stranded"` and the
46
+ `eveVersion` that built the session when it recorded one. It is never retried automatically. Call
47
+ `session.reset()` and create a new session.
48
+ The client never creates a replacement session. Following or reconnecting to a stranded session's
49
+ stream reports the same typed error without retrying, including when you were waiting for a
50
+ response to a message accepted before the upgrade. That accepted message is not redelivered.
51
+ `session.clear()` throws the same error, because clearing would keep a session that cannot run.
52
+ `session.stream({ follow: false })` and `session.snapshot()` still read the recorded history.
44
53
 
45
54
  ## Send a full turn payload
46
55
 
@@ -14,7 +14,7 @@ Choose where the eve runtime will run:
14
14
  | [Vercel](./vercel) | `.vercel/output` | Vercel Workflow | Vercel Sandbox | You want Vercel to operate the runtime services |
15
15
  | [Self-hosting](./self-hosting) | `.output/` Node server | Local or custom Workflow world | Docker, microsandbox, or custom | You operate your own Node or container infrastructure |
16
16
 
17
- eve is frontend agnostic and can be deployed within Next.js, Nuxt, or SvelteKit applications. See [Frontend integrations](../frontend/overview) for more details.
17
+ eve is frontend agnostic and can be deployed within Next.js, Nuxt, SvelteKit, or TanStack Start applications. See [Frontend integrations](../frontend/overview) for more details.
18
18
 
19
19
  ## Prepare for production
20
20
 
@@ -58,4 +58,4 @@ Follow the guide for your deployment platform or application topology:
58
58
 
59
59
  - [Deploy to Vercel](./vercel): use Vercel Build Output, Workflow, Sandbox, Cron, and observability
60
60
  - [Self-host eve](./self-hosting): run the Nitro Node server with infrastructure you manage
61
- - [Frontend integrations](../frontend/overview): mount eve alongside Next.js, Nuxt, or SvelteKit
61
+ - [Frontend integrations](../frontend/overview): mount eve alongside Next.js, Nuxt, SvelteKit, or TanStack Start
@@ -35,6 +35,8 @@ Before your first message, the empty composer may suggest asking your local agen
35
35
  | `/help` | List available commands. |
36
36
  | `/exit` | Quit the UI. |
37
37
 
38
+ When a message or stream reconnection reaches a [stranded session](../concepts/execution-model-and-durability#stranded-sessions), the TUI says why and which eve version built it, when known. New messages and approvals are not delivered. Run `/new` to end the stranded session; your next message starts a fresh one. `/clear` is refused, because it would keep the stranded session. A session from a previous `eve dev` invocation that the same eve version built is not stranded; see [local workflow recovery](../reference/cli#local-workflow-recovery). A message accepted before the upgrade is not redelivered.
39
+
38
40
  `/login`, `/model`, `/add`, `/deploy`, `/info`, and `/traces` are available when `eve dev` runs locally. They are unavailable when the UI connects through `eve remote connect`.
39
41
 
40
42
  ## Set up a new agent
@@ -1,5 +1,13 @@
1
1
  {
2
2
  "title": "Frontend Frameworks",
3
3
  "defaultOpen": false,
4
- "pages": ["overview", "nextjs", "nuxt", "sveltekit", "use-eve-agent-vue", "use-eve-agent-svelte"]
4
+ "pages": [
5
+ "overview",
6
+ "nextjs",
7
+ "nuxt",
8
+ "sveltekit",
9
+ "tanstack",
10
+ "use-eve-agent-vue",
11
+ "use-eve-agent-svelte"
12
+ ]
5
13
  }
@@ -15,7 +15,7 @@ Use this integration when Next.js owns the application's development and deploym
15
15
 
16
16
  ## Add the generated Web Chat app
17
17
 
18
- Install the generated Next.js Web Chat app from a standalone agent project:
18
+ Install the generated Web Chat app from a standalone agent project, and choose **Next.js** when the installer asks which framework to use:
19
19
 
20
20
  ```bash
21
21
  eve add channel/web
@@ -9,10 +9,10 @@ Use this guide when you already have a browser application and need to connect i
9
9
 
10
10
  A browser UI is a client of the agent's HTTP routes (the [eve channel](../../channels/overview)). Two layers wire it up:
11
11
 
12
- - **Routing** makes the agent reachable from the browser. A framework integration can mount eve on your app's origin: [Next.js](./nextjs) (`withEve`), [Nuxt](./nuxt) (the `eve/nuxt` module), or [SvelteKit](./sveltekit) (the `eveSvelteKit` Vite plugin). If the frontend and agents are peer services, use your deployment platform or reverse proxy instead. The hook works with either setup; pass `host` for a different origin or a named path mount.
12
+ - **Routing** makes the agent reachable from the browser. A framework integration can mount eve on your app's origin: [Next.js](./nextjs) (`withEve`), [Nuxt](./nuxt) (the `eve/nuxt` module), [SvelteKit](./sveltekit) (the `eveSvelteKit` Vite plugin), or [TanStack Start](./tanstack) (the `eveTanStack` Vite plugin). If the frontend and agents are peer services, use your deployment platform or reverse proxy instead. The hook works with either setup; pass `host` for a different origin or a named path mount.
13
13
  - **The hook** (`useEveAgent`) holds the session state, streaming, errors, and composer status. It defaults to same-origin eve routes such as `/eve/v1/session`.
14
14
 
15
- The framework pages walk through the wiring step by step: [Next.js](./nextjs), [Nuxt](./nuxt), and [SvelteKit](./sveltekit). See [Project Structure](../../concepts/project-structure#configure-a-web-deployment) to choose between a framework integration and peer services.
15
+ The framework pages walk through the wiring step by step: [Next.js](./nextjs), [Nuxt](./nuxt), [SvelteKit](./sveltekit), and [TanStack Start](./tanstack). See [Project Structure](../../concepts/project-structure#configure-a-web-deployment) to choose between a framework integration and peer services.
16
16
 
17
17
  For scripts, server-to-server calls, evals, tests, or custom clients that do not need framework UI state, use the [Client SDK](../client/overview) directly.
18
18
 
@@ -437,12 +437,13 @@ const support = useEveAgent({ agent: "support" });
437
437
 
438
438
  ## Per-framework integration
439
439
 
440
- | Framework | Integration | Hook |
441
- | --------- | ------------------------------------ | ------------------------------------------------ |
442
- | Next.js | [`withEve`](./nextjs) | [`useEveAgent` (React)](#basic-chat-react) |
443
- | Nuxt | [`eve/nuxt` module](./nuxt) | [`useEveAgent` (Vue)](./use-eve-agent-vue) |
444
- | SvelteKit | [`eveSvelteKit` plugin](./sveltekit) | [`useEveAgent` (Svelte)](./use-eve-agent-svelte) |
445
- | Any React | same-origin or `host` | [`useEveAgent` (React)](#basic-chat-react) |
440
+ | Framework | Integration | Hook |
441
+ | -------------- | ------------------------------------ | ------------------------------------------------ |
442
+ | Next.js | [`withEve`](./nextjs) | [`useEveAgent` (React)](#basic-chat-react) |
443
+ | Nuxt | [`eve/nuxt` module](./nuxt) | [`useEveAgent` (Vue)](./use-eve-agent-vue) |
444
+ | SvelteKit | [`eveSvelteKit` plugin](./sveltekit) | [`useEveAgent` (Svelte)](./use-eve-agent-svelte) |
445
+ | TanStack Start | [`eveTanStack` plugin](./tanstack) | [`useEveAgent` (React)](#basic-chat-react) |
446
+ | Any React | same-origin or `host` | [`useEveAgent` (React)](#basic-chat-react) |
446
447
 
447
448
  ## What to read next
448
449