eve 0.52.2 → 0.52.3

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 (195) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/src/channel/types.d.ts +1 -0
  3. package/dist/src/chunks/{use-eve-agent-1NAM6ftx.js → use-eve-agent-BR8EpIOX.js} +27 -10
  4. package/dist/src/chunks/{use-eve-agent-BEjdhhhK.js → use-eve-agent-XnfjQt1Z.js} +27 -10
  5. package/dist/src/cli/commands/deploy.js +1 -1
  6. package/dist/src/cli/commands/extension-init.d.ts +2 -1
  7. package/dist/src/cli/commands/extension-init.js +1 -1
  8. package/dist/src/cli/commands/init.d.ts +2 -13
  9. package/dist/src/cli/commands/init.js +1 -1
  10. package/dist/src/cli/dev/run-interactive-ui.d.ts +3 -0
  11. package/dist/src/cli/dev/run-interactive-ui.js +1 -1
  12. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  13. package/dist/src/cli/dev/tui/runner.d.ts +6 -0
  14. package/dist/src/cli/dev/tui/runner.js +1 -1
  15. package/dist/src/cli/dev/tui/setup-commands.d.ts +5 -3
  16. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  17. package/dist/src/cli/dev/tui/status-line.js +1 -1
  18. package/dist/src/cli/dev/tui/tui.d.ts +4 -0
  19. package/dist/src/cli/dev/tui/tui.js +1 -1
  20. package/dist/src/cli/run.d.ts +1 -1
  21. package/dist/src/cli/run.js +2 -2
  22. package/dist/src/cli/telemetry/index.d.ts +15 -0
  23. package/dist/src/cli/telemetry/index.js +2 -2
  24. package/dist/src/client/session.js +1 -1
  25. package/dist/src/compiled/.vendor-stamp.json +1 -1
  26. package/dist/src/compiler/compile-from-memory.d.ts +1 -0
  27. package/dist/src/compiler/compile-from-memory.js +1 -1
  28. package/dist/src/compiler/extension-compatibility.d.ts +18 -18
  29. package/dist/src/compiler/extension-compatibility.js +1 -1
  30. package/dist/src/context/build-dynamic-tools.d.ts +2 -1
  31. package/dist/src/context/build-dynamic-tools.js +1 -1
  32. package/dist/src/context/dynamic-tool-lifecycle.d.ts +3 -3
  33. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  34. package/dist/src/context/dynamic-tool-metadata.d.ts +1 -1
  35. package/dist/src/context/hook-lifecycle.js +1 -1
  36. package/dist/src/context/keys.d.ts +2 -0
  37. package/dist/src/context/keys.js +1 -1
  38. package/dist/src/eve-channel/index.js +1 -1
  39. package/dist/src/execution/activity-events.d.ts +1 -0
  40. package/dist/src/execution/activity-events.js +1 -1
  41. package/dist/src/execution/node-step.js +1 -1
  42. package/dist/src/execution/sandbox/prewarm.js +1 -1
  43. package/dist/src/execution/session-activity-projection.js +1 -1
  44. package/dist/src/execution/session-activity.js +1 -1
  45. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  46. package/dist/src/execution/tasks/parent/dispatch.js +1 -1
  47. package/dist/src/execution/tasks/parent/task-cancel.d.ts +2 -2
  48. package/dist/src/execution/tasks/parent/task-cancel.js +1 -1
  49. package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
  50. package/dist/src/execution/tools/sleep-workflow.js +1 -1
  51. package/dist/src/execution/workflow-runtime.js +1 -1
  52. package/dist/src/execution/workflow-steps.js +1 -1
  53. package/dist/src/harness/action-presentation.d.ts +13 -0
  54. package/dist/src/harness/action-presentation.js +1 -0
  55. package/dist/src/harness/compaction.js +1 -1
  56. package/dist/src/harness/coordination.js +1 -1
  57. package/dist/src/harness/emission.d.ts +1 -6
  58. package/dist/src/harness/emission.js +1 -1
  59. package/dist/src/harness/execute-tool.d.ts +2 -1
  60. package/dist/src/harness/finish-reason.d.ts +3 -0
  61. package/dist/src/harness/finish-reason.js +1 -0
  62. package/dist/src/harness/provider-tool-schemas.d.ts +1 -0
  63. package/dist/src/harness/provider-tool-schemas.js +1 -1
  64. package/dist/src/harness/step-hooks.js +1 -1
  65. package/dist/src/harness/stream-actions.d.ts +2 -2
  66. package/dist/src/harness/stream-actions.js +1 -1
  67. package/dist/src/harness/tool-call-input-errors.d.ts +4 -3
  68. package/dist/src/harness/tool-call-input-errors.js +1 -1
  69. package/dist/src/harness/tool-loop.js +2 -2
  70. package/dist/src/harness/tool-presentation.d.ts +5 -0
  71. package/dist/src/harness/tool-presentation.js +1 -0
  72. package/dist/src/harness/turn-client-context.d.ts +1 -0
  73. package/dist/src/harness/workflow-lifecycle.d.ts +2 -3
  74. package/dist/src/harness/workflow-lifecycle.js +1 -1
  75. package/dist/src/internal/application/compiled-artifacts.d.ts +3 -0
  76. package/dist/src/internal/application/compiled-artifacts.js +1 -1
  77. package/dist/src/internal/application/package.js +1 -1
  78. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  79. package/dist/src/internal/authored-module-conditions.d.ts +2 -0
  80. package/dist/src/internal/authored-module-conditions.js +1 -0
  81. package/dist/src/internal/authored-module-loader.d.ts +3 -0
  82. package/dist/src/internal/authored-module-loader.js +3 -3
  83. package/dist/src/internal/authored-module-map-loader.d.ts +3 -1
  84. package/dist/src/internal/authored-module-map-loader.js +1 -1
  85. package/dist/src/internal/authored-runtime-modules.d.ts +2 -0
  86. package/dist/src/internal/authored-runtime-modules.js +1 -1
  87. package/dist/src/internal/bundler/nitro-rolldown.d.ts +1 -0
  88. package/dist/src/internal/bundler/nitro-rolldown.js +1 -1
  89. package/dist/src/internal/nitro/development-generation.d.ts +2 -0
  90. package/dist/src/internal/nitro/development-generation.js +1 -1
  91. package/dist/src/internal/nitro/host/configure-nitro-routes.js +1 -1
  92. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
  93. package/dist/src/internal/nitro/host/prepare-application-host.js +1 -1
  94. package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
  95. package/dist/src/internal/workflow-bundle/builder-support.d.ts +5 -0
  96. package/dist/src/internal/workflow-bundle/builder.js +2 -2
  97. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +1 -1
  98. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +1 -1
  99. package/dist/src/protocol/activity.d.ts +6 -0
  100. package/dist/src/protocol/activity.js +1 -1
  101. package/dist/src/protocol/message.d.ts +13 -1
  102. package/dist/src/protocol/message.js +1 -1
  103. package/dist/src/public/channels/slack/activity-plan.js +1 -1
  104. package/dist/src/public/channels/slack/activity.js +2 -2
  105. package/dist/src/public/models/openai/chatgpt/credential-store.d.ts +6 -0
  106. package/dist/src/public/models/openai/chatgpt/credential-store.js +1 -0
  107. package/dist/src/public/models/openai/chatgpt/model.d.ts +1 -1
  108. package/dist/src/public/models/openai/chatgpt/oauth.d.ts +20 -0
  109. package/dist/src/public/models/openai/chatgpt/oauth.js +1 -0
  110. package/dist/src/public/models/openai/chatgpt/token-broker.d.ts +4 -3
  111. package/dist/src/public/models/openai/chatgpt/token-broker.js +1 -1
  112. package/dist/src/public/models/openai/chatgpt/transport.d.ts +1 -2
  113. package/dist/src/public/models/openai/index.d.ts +3 -4
  114. package/dist/src/public/tools/index.d.ts +1 -1
  115. package/dist/src/runtime/prompt/compose.js +1 -1
  116. package/dist/src/runtime/resolve-tool.js +1 -1
  117. package/dist/src/runtime/types.d.ts +2 -0
  118. package/dist/src/self-modification/agent.js +1 -1
  119. package/dist/src/self-modification/extension/instructions.d.ts +1 -3
  120. package/dist/src/self-modification/extension/instructions.js +4 -2
  121. package/dist/src/self-modification/filesystem.js +1 -1
  122. package/dist/src/setup/flows/chatgpt-auth.d.ts +14 -2
  123. package/dist/src/setup/flows/chatgpt-auth.js +1 -1
  124. package/dist/src/setup/flows/model.d.ts +3 -2
  125. package/dist/src/setup/flows/model.js +1 -1
  126. package/dist/src/setup/flows/provider.js +1 -1
  127. package/dist/src/setup/flows/registry.d.ts +4 -0
  128. package/dist/src/setup/flows/registry.js +1 -1
  129. package/dist/src/setup/scaffold/create/project.js +1 -1
  130. package/dist/src/shared/boundary-hook-error.d.ts +4 -0
  131. package/dist/src/shared/boundary-hook-error.js +1 -0
  132. package/dist/src/{execution/activity-text.d.ts → shared/presentation-text.d.ts} +1 -1
  133. package/dist/src/shared/presentation-text.js +1 -0
  134. package/dist/src/svelte/index.js +1 -1
  135. package/dist/src/svelte/use-eve-agent.js +1 -1
  136. package/dist/src/tools/definition.d.ts +21 -0
  137. package/dist/src/tools/definition.js +1 -1
  138. package/dist/src/tools/durable-callbacks.d.ts +37 -9
  139. package/dist/src/tools/durable-callbacks.js +1 -1
  140. package/dist/src/tools/dynamic.d.ts +2 -1
  141. package/dist/src/tools/framework/agent-contract.js +1 -1
  142. package/dist/src/tools/provided/bash.js +1 -1
  143. package/dist/src/tools/provided/glob.js +1 -1
  144. package/dist/src/tools/provided/grep.js +1 -1
  145. package/dist/src/tools/provided/load-skill.js +1 -1
  146. package/dist/src/tools/provided/read-file.js +1 -1
  147. package/dist/src/tools/provided/web-fetch.js +1 -1
  148. package/dist/src/tools/provided/write-file.js +1 -1
  149. package/dist/src/tools/tool-label.d.ts +1 -0
  150. package/dist/src/tools/tool-label.js +1 -0
  151. package/dist/src/tracing/agent-action-instrumentation.js +1 -1
  152. package/dist/src/tracing/agent-approval-instrumentation.js +1 -1
  153. package/dist/src/tracing/agent-channel-delivery-instrumentation.js +1 -1
  154. package/dist/src/tracing/agent-otel-provider.js +2 -2
  155. package/dist/src/tracing/agent-otel-session-context.js +1 -1
  156. package/dist/src/tracing/agent-otel-usage.d.ts +10 -0
  157. package/dist/src/tracing/agent-otel-usage.js +1 -1
  158. package/dist/src/tracing/agent-span-naming.d.ts +1 -0
  159. package/dist/src/tracing/agent-span-naming.js +1 -0
  160. package/dist/src/tracing/agent-tool-instrumentation.js +1 -1
  161. package/dist/src/vue/index.js +1 -1
  162. package/dist/src/vue/use-eve-agent.js +1 -1
  163. package/docs/concepts/default-harness.md +6 -0
  164. package/docs/concepts/sessions-runs-and-streaming.md +4 -0
  165. package/docs/connections/overview.mdx +1 -1
  166. package/docs/guides/client/continuations.mdx +11 -0
  167. package/docs/guides/dynamic-capabilities.md +4 -4
  168. package/docs/guides/hooks.md +1 -1
  169. package/docs/guides/instrumentation.md +40 -1
  170. package/docs/guides/remote-agents.md +2 -0
  171. package/docs/instructions.mdx +1 -1
  172. package/docs/memory/overview.mdx +45 -13
  173. package/docs/reference/cli.md +6 -0
  174. package/docs/reference/telemetry.md +2 -2
  175. package/docs/reference/typescript-api.md +13 -9
  176. package/docs/sandbox.mdx +14 -2
  177. package/docs/subagents/index.mdx +8 -4
  178. package/docs/tools/overview.mdx +44 -2
  179. package/docs/tools/workflows.mdx +6 -0
  180. package/docs/tutorial/connect-a-warehouse.mdx +49 -26
  181. package/docs/tutorial/first-agent.mdx +20 -19
  182. package/docs/tutorial/guard-the-spend.mdx +4 -4
  183. package/docs/tutorial/how-it-runs.mdx +2 -3
  184. package/docs/tutorial/meta.json +2 -2
  185. package/docs/tutorial/query-sample-data.mdx +30 -19
  186. package/docs/tutorial/remember-definitions.mdx +5 -5
  187. package/docs/tutorial/run-analysis.mdx +155 -18
  188. package/docs/tutorial/ship-it.mdx +71 -81
  189. package/docs/tutorial/team-playbooks.mdx +10 -10
  190. package/package.json +1 -1
  191. package/dist/src/execution/activity-text.js +0 -1
  192. package/dist/src/internal/workflow-bundle/authored-workflow-modules.d.ts +0 -9
  193. package/dist/src/internal/workflow-bundle/authored-workflow-modules.js +0 -1
  194. package/dist/src/public/models/openai/chatgpt/codex-app-server.d.ts +0 -40
  195. package/dist/src/public/models/openai/chatgpt/codex-app-server.js +0 -1
@@ -257,11 +257,11 @@ Call expressions such as `execute: makeExecutor()` are not transformed. Put the
257
257
 
258
258
  ### Identity and redeploys
259
259
 
260
- A parked call binds to its callback by **tool name and phase** — the same identity a static tool uses — never by source position. This gives dynamic tools static-tool semantics across deploys:
260
+ A parked call binds to its callback within its session, lifecycle scope, and resolver entry. Another session or scope can expose the same tool name without replacing that binding. Callback identity does not depend on source position:
261
261
 
262
- - Editing a callback body (or anything else that does not change tool names) is safe: replaying a parked call runs the latest deployed code with the closure values snapshotted when the call was made.
263
- - If a persisted callback has no registered implementation (fresh process after a crash, or after a redeploy), eve re-runs `session.started` resolvers once to rebind it, then replays.
264
- - If the tool no longer exists under that name, replay fails closed with an explicit error instead of invoking something else. Ordinary turn-scoped and step-scoped tools are not rebound; a parked call to a missing one errors. Framework-provided resolvers such as memory provider-tool wrappers opt into the same generic missing-callback rebind while preserving their locked scope.
262
+ - Editing a callback body while keeping its resolver entry and tool names is safe: replaying a parked call runs the latest deployed code with the closure values snapshotted when the call was made.
263
+ - If a persisted session-scoped callback has no registered implementation (a fresh process, a redeploy, or an expired in-process binding), eve re-runs `session.started` resolvers once to rebind it, then replays.
264
+ - If the owning resolver no longer returns that tool, replay fails closed with an explicit error instead of invoking something else. Ordinary turn-scoped and step-scoped tools are not rebound; a parked call to a missing one errors. Framework-provided resolvers such as memory provider-tool wrappers opt into the same generic missing-callback rebind while preserving their locked scope.
265
265
 
266
266
  ### Naming
267
267
 
@@ -176,7 +176,7 @@ Hooks always run after the event is durably recorded, so if a hook throws, the s
176
176
 
177
177
  ## What happens when a hook throws
178
178
 
179
- A thrown handler propagates through the emit composer and surfaces as `turn.failed`. If a hook subscribed to a failure-cascade event also throws, it escalates to `session.failed`. For belt-and-suspenders semantics inside a hook, wrap the body in `try`/`catch`. eve treats a thrown hook as a real failure.
179
+ A thrown handler propagates through the emit composer and surfaces as `turn.failed`. In a conversation session, this includes handlers for `turn.started` and the first `step.started` of a model call: the failed turn ends with `session.waiting`, and the next message can start another turn. Task-mode boundary failures remain terminal. If a hook subscribed to a failure-cascade event also throws, it escalates to `session.failed`. For belt-and-suspenders semantics inside a hook, wrap the body in `try`/`catch`. eve treats a thrown hook as a real failure.
180
180
 
181
181
  ## Subagent isolation
182
182
 
@@ -97,6 +97,45 @@ retry behavior. Best-effort activity delivery keeps its single
97
97
  `[eve:execution.activity-submit] activity sink request failed` warning and does
98
98
  not mark the active span as failed.
99
99
 
100
+ ## Exported span names and outcomes
101
+
102
+ The built-in OpenTelemetry provider preserves each span's OTel name and adds
103
+ `operation.name` and `resource.name` for Datadog's operation/resource mapping:
104
+
105
+ | OTel span name | `operation.name` | `resource.name` |
106
+ | ------------------------------------------------------------------------------------------------------------------ | ----------------- | ---------------------- |
107
+ | `invoke_agent weather` | `invoke_agent` | `invoke_agent weather` |
108
+ | `execute_tool search` | `execute_tool` | `execute_tool search` |
109
+ | `chat <model>` | `chat` | `chat <model>` |
110
+ | `agent.session`, `agent.step`, `agent.action`, `agent.approval`, `agent.channel.delivery`, `agent.channel.request` | Same as span name | Same as span name |
111
+
112
+ These attributes apply to eve-owned spans in local tracing and the
113
+ [instrumentation provider layout](./instrumentation-providers). They do not
114
+ rename Workflow, AI SDK, or other third-party spans, or change trace IDs,
115
+ parenting, sampling, or session grouping. Legacy `instrumentation.ts` setup
116
+ still owns its exporter configuration and [authored trace
117
+ hierarchy](#authored-trace-hierarchy).
118
+
119
+ In Datadog APM, `operation_name:invoke_agent resource_name:"invoke_agent weather"`
120
+ selects named agent invocations. Operation-based dashboards and monitors may
121
+ need to replace inferred names such as `otel.span` with these explicit names.
122
+ Keep session IDs, turn IDs, and message content in attributes, not operation or
123
+ resource names.
124
+
125
+ If an eve-owned span still appears as `otel.span`, inspect its exported OTel
126
+ name, `operation.name`, and `resource.name` before and after any drain or
127
+ Collector transforms. Datadog's [operation-name mapping
128
+ guide](https://docs.datadoghq.com/opentelemetry/migrate/migrate_operation_names/)
129
+ describes the explicit override. Exported fields must survive the ingestion
130
+ path; a local export does not prove that a hosted backend retained them.
131
+
132
+ Agent invocation spans carry `agent.turn.outcome=completed|failed|cancelled`
133
+ when the turn has a terminal outcome. A failed invocation also has OTel error
134
+ status; cancellation is not an error. The scalar outcome survives backends
135
+ that discard span events, including Sentry's [direct OTLP
136
+ intake](https://docs.sentry.io/concepts/otlp/direct/traces/). These attributes
137
+ remain available when model and tool content is redacted.
138
+
100
139
  ## Runtime context
101
140
 
102
141
  _Runtime context_ is an [AI SDK concept](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text): a user-defined object that flows through a generation lifecycle. eve exposes it through `events["step.started"]`, a callback that runs once eve has assembled the model input for an attempt and returns `{ runtimeContext }`. Because eve registers the AI SDK's OpenTelemetry integration with runtime context enabled, those returned values ride onto the model-call span and its children. The field is named `runtimeContext`, not `metadata`, because AI SDK v7 carries per-call attributes on runtime context rather than a dedicated metadata field.
@@ -156,7 +195,7 @@ ai.eve.turn {eve.session.id}
156
195
 
157
196
  eve creates the `ai.eve.turn` parent span per turn and passes enriched telemetry to the AI SDK so model calls and tool executions are traced automatically. The AI SDK's OpenTelemetry integration names these spans after the [OpenTelemetry GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/), so backends that understand `gen_ai.operation.name` can classify them without extra configuration. The `invoke_agent` span is named after the model; the agent name is on its `gen_ai.agent.name` attribute.
158
197
 
159
- This hierarchy applies when eve passes telemetry to the AI SDK. When the `otel()` provider layout is declared and eve owns the agent spans, eve names the span `invoke_agent <agent>` and emits no step spans. Session, turn, step, and channel context is injected as the framework half of the runtime context (`eve.version`, `eve.session.id`, `eve.environment`, `eve.turn.id`, `eve.turn.sequence`, `eve.step.index`, `eve.channel.kind`) and rides onto the spans alongside any values your `events["step.started"]` callback returns under `runtimeContext`.
198
+ This hierarchy applies when eve passes telemetry to the AI SDK. When the `otel()` provider layout is declared and eve owns the agent spans, eve names its invocation span `invoke_agent <agent>` and its model-attempt spans `agent.step`. Session, turn, step, and channel context is injected as the framework half of the runtime context (`eve.version`, `eve.session.id`, `eve.environment`, `eve.turn.id`, `eve.turn.sequence`, `eve.step.index`, `eve.channel.kind`) and rides onto the spans alongside any values your `events["step.started"]` callback returns under `runtimeContext`.
160
199
 
161
200
  Set `traceChannelRequests: true` on `defineInstrumentation` to also wrap each inbound channel HTTP request in a single OpenTelemetry `SERVER` span named for the registered route, which parents the turn tree above (and any `hook.resume` and outgoing HTTP spans):
162
201
 
@@ -180,6 +180,8 @@ The parent stream carries the same `subagent.called`, `action.result`, and `suba
180
180
 
181
181
  An admitted task survives cancellation of the turn that started it; background work that has not yet been admitted is rejected with the cancelled step. Use `task_cancel` to stop an admitted task. eve resolves the remote's `headers` and `auth` again for every cancellation attempt, so rotating credentials work the same way as they do for session creation. Cancellation always uses the standard eve cancel path on `url`, even when `path` customizes only the create-session endpoint. The remote child reports `turn.cancelled` → `session.waiting` on its own stream; an older or unreachable remote is logged but cannot turn the parent's cancellation into a failure.
182
182
 
183
+ You can also steer a running remote background child by calling its subagent tool with the same `agentId` and an updated `message`. eve cancels the old task and requests cancellation of the remote turn before continuing the same remote session under a new task ID. The remote must support the standard eve cancellation and session-message routes. See [Agent messaging](../subagents#agent-messaging) for the shared steering contract.
184
+
183
185
  When the parent session ends, eve sends an authenticated `POST /eve/v1/session/:childSessionId/reset` for each remote child. Reset retires the parked remote session and recursively cleans up its descendants. The request uses freshly resolved `headers` and `auth`; failures are logged so an unreachable remote cannot block parent finalization.
184
186
 
185
187
  A failed _start_ rejects admission before a task receipt is returned. After a remote starts, a terminal failure callback fails the task and notifies the parent with the remote's error (or `REMOTE_AGENT_FAILED` when none is supplied). Terminal callback delivery runs as a durable step on the underlying workflow engine (see [Execution model & durability](../concepts/execution-model-and-durability)). A failed callback POST is rethrown rather than marking the task complete, so the engine retries it.
@@ -86,7 +86,7 @@ See [Dynamic capabilities](./guides/dynamic-capabilities) for examples and failu
86
86
 
87
87
  [Compaction](./concepts/sessions-runs-and-streaming#compact-clear-and-reset) treats user-role instructions like ordinary conversation history, so a summary may replace their original text. Clear removes them with the rest of model-message history and does not rerun static or dynamic instructions. System-role instructions remain because they are outside history.
88
88
 
89
- Keep system-role content stable and place it before frequently changing context when practical. That gives providers the best opportunity to reuse a prompt prefix, but cache behavior and billing remain provider-specific. User-role instructions preserve the normal append-only message prefix; eve does not promise a cache hit.
89
+ Keep system-role content stable and place it before frequently changing context when practical. That gives providers the best opportunity to reuse a prompt prefix, but cache behavior and billing remain provider-specific. User-role instructions preserve the normal append-only message prefix. Framework context such as dynamic skill announcements also keeps its position before the current request across tool steps, including when a durable step resumes. eve does not promise a cache hit.
90
90
 
91
91
  ## Disclaimer
92
92
 
@@ -70,12 +70,23 @@ Every slot needs a provider. The provider decides how memory is stored, how
70
70
  relevant context is retrieved, and whether the agent captures conversation
71
71
  automatically or only through explicit tool calls.
72
72
 
73
- | Provider | Ships as | Recall | Capture |
74
- | ------------------------------------- | ----------------------- | ------------------------------------------------------ | ---------------------------------------------------- |
75
- | [Supermemory](#supermemory) | `@supermemory/eve` | Semantic search over stored memories | Automatic after each turn, plus tools |
76
- | [Upstash AgentKit](#upstash-agentkit) | `@upstash/agentkit-eve` | Ranked Redis Search recall or a Redis document backend | Automatic user-message capture or model-driven tools |
77
- | [File memory](#file-memory) | Built into eve | One bounded document per scope | Model-driven `save_memory` / `remove_memory` |
78
- | [Your own provider](#build-your-own) | Your code | Whatever your store returns | Whatever rules you implement |
73
+ | Provider | Ships as | Recall | Capture |
74
+ | --------------------------------------- | ----------------------- | ------------------------------------------------------ | ---------------------------------------------------- |
75
+ | [File memory](#file-memory) | Built into eve | One bounded document per scope | Model-driven `save_memory` / `remove_memory` |
76
+ | [Supermemory](#supermemory) | `@supermemory/eve` | Semantic search over stored memories | Automatic after each turn, plus tools |
77
+ | [Upstash AgentKit](#upstash-agentkit) | `@upstash/agentkit-eve` | Ranked Redis Search recall or a Redis document backend | Automatic user-message capture or model-driven tools |
78
+ | [Kybernesis Arcana](#kybernesis-arcana) | `@kybernesis/arcana` | Semantic search and brain notes | Model-driven tools or optional automatic capture |
79
+ | [Your own provider](#build-your-own) | Your code | Whatever your store returns | Whatever rules you implement |
80
+
81
+ ### File memory
82
+
83
+ `fileMemory()` from `eve/memory/file` keeps one bounded document per scope and
84
+ gives the model `save_memory` and `remove_memory` tools. It does not extract
85
+ facts automatically; the model decides what to save. It needs no external
86
+ service in `eve dev` and stores to Vercel Blob when deployed to Vercel, which
87
+ makes it the shortest path to a working slot.
88
+
89
+ Read [File memory](/docs/memory/file) for size limits, storage backends, and options.
79
90
 
80
91
  ### Supermemory
81
92
 
@@ -137,15 +148,36 @@ export default defineMemory({
137
148
  See the [Upstash AgentKit integration page](/integrations/upstash-agentkit) for
138
149
  options.
139
150
 
140
- ### File memory
151
+ ### Kybernesis Arcana
141
152
 
142
- `fileMemory()` from `eve/memory/file` keeps one bounded document per scope and
143
- gives the model `save_memory` and `remove_memory` tools. It does not extract
144
- facts automatically; the model decides what to save. It needs no external
145
- service in `eve dev` and stores to Vercel Blob when deployed to Vercel, which
146
- makes it the shortest path to a working slot.
153
+ [Kybernesis Arcana](https://github.com/KybernesisAI/platform/tree/master/packages/arcana#readme)
154
+ backs a memory slot with semantic search and brain notes. It gives the model
155
+ tools to remember, recall, and search memory; capture is disabled by default.
147
156
 
148
- Read [File memory](/docs/memory/file) for size limits, storage backends, and options.
157
+ ```bash
158
+ eve add memory/arcana
159
+ ```
160
+
161
+ The command installs `@kybernesis/arcana` and writes an `arcana` slot:
162
+
163
+ ```ts title="agent/memory/arcana.ts"
164
+ import { arcanaMemory } from "@kybernesis/arcana/memory";
165
+ import { defineMemory } from "eve/memory";
166
+ import { byPrincipal } from "eve/memory/scope";
167
+
168
+ export default defineMemory({
169
+ description: "Recall and manage durable context for the current user.",
170
+ provider: arcanaMemory({
171
+ apiKey: process.env.ARCANA_API_KEY!,
172
+ workspace: process.env.ARCANA_WORKSPACE!,
173
+ }),
174
+ scope: byPrincipal,
175
+ });
176
+ ```
177
+
178
+ Set `capture: { enabled: true }` when you want Arcana to capture completed
179
+ turns automatically. See the [Arcana package documentation](https://github.com/KybernesisAI/platform/tree/master/packages/arcana#readme)
180
+ for provider options.
149
181
 
150
182
  ### Build your own
151
183
 
@@ -161,6 +161,10 @@ eve build [--profile <path>] [--skip-sandbox-prewarm]
161
161
 
162
162
  Compiles and bundles in an invocation-owned directory under `.eve/builds/`, then publishes the completed host output and prints its path. Scratch workspaces are removed after success or failure.
163
163
 
164
+ Authored bundles preserve custom Node.js resolution conditions supplied through `--conditions`,
165
+ `-C`, or `NODE_OPTIONS`. For example, `NODE_OPTIONS="--conditions=react-server" eve build`
166
+ keeps a channel's `server-only` imports on the same export used during compilation.
167
+
164
168
  | Flag | Type | Default | Description |
165
169
  | ------------------------ | ------ | ------- | --------------------------------------------------------------------------------------------- |
166
170
  | `--profile <path>` | string | off | Best-effort versioned JSON report with build-phase timings and final output-size measurements |
@@ -199,6 +203,8 @@ eve start [--host <host>] [--port <port>]
199
203
 
200
204
  Serves the previously built output. Prints the listening URL.
201
205
 
206
+ For self-hosted deployments, copy the app source, `.output/`, and installed dependencies together. The deployment directory can differ from the build directory. Preserve the relative layout of any workspace packages used by the app; startup resolves sandbox prewarm modules from the deployed source.
207
+
202
208
  ## `eve dev`
203
209
 
204
210
  ```bash
@@ -12,9 +12,9 @@ eve collects usage data from its CLI to help improve its commands and developmen
12
12
  eve sends the following information to Vercel:
13
13
 
14
14
  - The eve version, operating system, CPU architecture, and whether stdin is a terminal.
15
- - The command you ran and whether it succeeded, had a usage error, or failed.
15
+ - The command you ran, its outcome, and setup or onboarding steps when applicable.
16
16
  - For `eve dev`, whether you connected to a local or remote agent and whether the UI was interactive or headless.
17
- - Random identifiers for the CLI session, your eve installation, and the project.
17
+ - Random identifiers for the CLI session, installation, and project, plus whether the installation and project identifiers are ephemeral or persistent.
18
18
 
19
19
  The project identifier lets eve group usage from the same project without sending its name or location. eve derives it from the Git remote when available, otherwise `REPOSITORY_URL` or the working directory, and transforms that value before sending it.
20
20
 
@@ -54,7 +54,7 @@ export default defineTool({
54
54
  | `mockModel` | `eve/evals` | Deterministic fixture agent models | [Evals](../evals/overview) |
55
55
  | `useEveAgent` | `eve/react`, `eve/vue`, `eve/svelte` | frontend | [Frontend](../guides/frontend/overview) |
56
56
 
57
- Tool-wide authoring helpers such as `defineTool`, `defineWorkflowTool`, `defineDynamic`, and `disableTool` come from `eve/tools`. Capability-specific definitions and helpers use their own subpaths (see [Built-in tools](../concepts/built-in-tools)): reusable definitions such as `bash` and `glob` come from `eve/tools/<name>`, `webSearch` comes from `eve/tools/web_search`, `experimental_workflow` comes from `eve/tools/workflow`, `sleep` comes from `eve/tools/sleep`, and approval policies and types come from `eve/tools/approval`. The route verbs `GET`/`HEAD`/`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`/`WS` plus `disableRoute` come from `eve/channels`, and the channel auth helpers `localDev`/`vercelOidc`/`placeholderAuth` come from `eve/channels/auth`.
57
+ Tool-wide authoring helpers and types such as `defineTool`, `defineWorkflowTool`, `defineDynamic`, `disableTool`, and `ToolLabelDefinition` come from `eve/tools`. Capability-specific definitions and helpers use their own subpaths (see [Built-in tools](../concepts/built-in-tools)): reusable definitions such as `bash` and `glob` come from `eve/tools/<name>`, `webSearch` comes from `eve/tools/web_search`, `experimental_workflow` comes from `eve/tools/workflow`, `sleep` comes from `eve/tools/sleep`, and approval policies and types come from `eve/tools/approval`. The route verbs `GET`/`HEAD`/`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`/`WS` plus `disableRoute` come from `eve/channels`, and the channel auth helpers `localDev`/`vercelOidc`/`placeholderAuth` come from `eve/channels/auth`.
58
58
 
59
59
  `AgentReasoningDefinition` is exported from `eve` for the top-level `defineAgent({ reasoning })` setting. `AgentLimitsDefinition` is exported for `defineAgent({ limits })`. `AgentWorkflowDefinition` and `AgentWorkflowWorldDefinition` are exported from `eve` for the `defineAgent({ experimental: { workflow } })` config shape. `ExperimentalWorkflowToolInput` is exported from `eve/tools/workflow`; `WebSearchToolInput` and `WebSearchProvider` are exported from `eve/tools/web_search`.
60
60
 
@@ -170,7 +170,7 @@ Run source mutations inside `withSuspendedSource()`. It acquires a unique watche
170
170
 
171
171
  ## ChatGPT subscription models
172
172
 
173
- `chatgpt()` from `eve/models/openai` serves an OpenAI model through the local Codex login and bills the ChatGPT subscription. With no argument, it selects `gpt-5.6-sol`:
173
+ `chatgpt()` from `eve/models/openai` serves an OpenAI model through your local ChatGPT login and bills the ChatGPT subscription. With no argument, it selects `gpt-5.6-sol`:
174
174
 
175
175
  ```ts title="agent/agent.ts"
176
176
  import { defineAgent } from "eve";
@@ -185,20 +185,24 @@ Pass another bare OpenAI model slug to override the default. `experimental_chatg
185
185
 
186
186
  `chatgpt()` uses stateless requests (`store: false`). eve retains reasoning summaries and encrypted reasoning in session history and replays them after tool calls and on later turns. You do not need to configure `reasoning.encrypted_content` explicitly.
187
187
 
188
- Authentication is delegated entirely to the Codex CLI:
188
+ Sign in directly from eve; the Codex CLI is not required:
189
189
 
190
- 1. Install or upgrade `codex` and run `codex login`.
191
- 2. `eve dev` asks `codex app-server` for a usable access token. Codex owns refresh and credential persistence; eve does not read or write Codex login files.
192
- 3. Normal token expiry is refreshed automatically. If the login is revoked, the status line shows `codex login`; completing login inside or outside eve repairs the running dev session without restarting it.
190
+ 1. Run `eve dev`, open `/model`, and select **Provider** → **ChatGPT subscription**.
191
+ 2. Complete sign-in in the browser. If the browser does not open, use the URL printed in the terminal.
192
+ 3. Return to eve after the terminal confirms that your subscription is connected. Normal token expiry is refreshed automatically.
193
+
194
+ eve stores this session in `~/.eve/auth/chatgpt.json` with owner-only file permissions on Unix. It is separate from any Codex login and is never written to your project. Keep this file private. To remove the local eve login, stop your eve processes and delete the file. Existing Codex users must sign in once through eve after upgrading.
195
+
196
+ Over SSH, or if localhost port 1455 is occupied, eve shows a device code instead. Open the displayed link in a browser and enter the code. Device sign-in requires enabling device code authorization in **ChatGPT Settings → Security**, or having a workspace admin enable it in workspace permissions. Sign-in times out after five minutes; press **Ctrl+C** to cancel sooner.
193
197
 
194
198
  ChatGPT subscription credentials are local user credentials. `eve deploy` blocks agents whose active model is `chatgpt()` because those credentials are not uploaded to a deployment. Use an environment branch with a deployable model, or switch to an AI Gateway model before deploying.
195
199
 
196
200
  Troubleshooting:
197
201
 
198
- - **`chatgpt-sub login`**: run `codex login`.
199
- - **`chatgpt-sub unavailable`**: ensure `codex` is installed, current, and available on `PATH`; then restart the command.
202
+ - **`chatgpt-sub login`**: open `/model` and select **Provider** → **ChatGPT subscription** to sign in again. The running dev session picks up the new login.
203
+ - **`chatgpt-sub unavailable`**: check your network connection and retry from `/model`. If eve reports an invalid credential file, remove `~/.eve/auth/chatgpt.json` and sign in again.
200
204
  - **Model rejected by the backend**: model availability depends on the signed-in ChatGPT account. Pick another supported OpenAI model.
201
- - **SSH/headless login**: run `codex login --device-auth` in another terminal, then return to the still-running `eve dev` session.
205
+ - **Device sign-in unavailable**: enable device code authorization in ChatGPT security settings, or sign in from a local terminal with port 1455 available.
202
206
 
203
207
  ## What to read next
204
208
 
package/docs/sandbox.mdx CHANGED
@@ -31,6 +31,11 @@ export default defineTool({
31
31
  const sandbox = await ctx.getSandbox();
32
32
  await sandbox.writeTextFile({ path: "analysis/run.py", content: script });
33
33
  const result = await sandbox.run({ command: "python analysis/run.py" });
34
+ if (result.exitCode !== 0) {
35
+ throw new Error(
36
+ `Analysis failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`,
37
+ );
38
+ }
34
39
  return { stdout: result.stdout };
35
40
  },
36
41
  });
@@ -38,13 +43,15 @@ export default defineTool({
38
43
 
39
44
  `ctx.getSandbox()` takes no arguments, is async, and only works inside authored runtime execution.
40
45
 
46
+ `sandbox.run()` resolves with `{ exitCode, stdout, stderr }` when the process exits, including a nonzero exit. Check `exitCode` before reporting success or using generated files. Throw from a tool to report a failed operation to the model, and throw from `bootstrap` to prevent eve from caching an incomplete setup. The built-in `bash` tool returns all three fields so the model can interpret command results itself.
47
+
41
48
  `/workspace` is one namespace across every backend, so `/workspace/foo` points at the same file whether the backend is local or Vercel. When you need to interpolate a path into a generated command, `sandbox.resolvePath("repo/build.py")` anchors a relative path to its absolute `/workspace/repo/build.py` form.
42
49
 
43
50
  The handle does more than `run` and `writeTextFile`. In every method, relative paths resolve from `/workspace` and absolute paths pass through untouched:
44
51
 
45
52
  | Method | Does |
46
53
  | ---------------------------------------- | ----------------------------------------------------------------------------------------------- |
47
- | `run({ command })` | run one command, block until it exits, return `{ stdout, stderr, ... }` |
54
+ | `run({ command })` | run one command, block until it exits, return `{ exitCode, stdout, stderr }` |
48
55
  | `spawn(options)` | launch a long-running process (server, watcher) and return a `SandboxProcess` handle |
49
56
  | `readTextFile` / `writeTextFile` | read/write a UTF-8 (or specified encoding) file; `readTextFile` supports 1-based line ranges |
50
57
  | `readBinaryFile` / `writeBinaryFile` | read/write raw bytes (images, archives, anything non-text) |
@@ -106,7 +113,12 @@ export default defineSandbox({
106
113
  revalidationKey: () => "repo-bootstrap-v1",
107
114
  async bootstrap({ use }) {
108
115
  const sandbox = await use();
109
- await sandbox.run({ command: "sudo apt-get install -y jq" });
116
+ const result = await sandbox.run({ command: "sudo apt-get install -y jq" });
117
+ if (result.exitCode !== 0) {
118
+ throw new Error(
119
+ `Sandbox setup failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`,
120
+ );
121
+ }
110
122
  },
111
123
  async onSession({ use }) {
112
124
  await use({ networkPolicy: "deny-all" });
@@ -12,7 +12,7 @@ The root session receives `agent` by default. The model calls it to delegate a t
12
12
  ```ts
13
13
  {
14
14
  message: string; // everything the child needs; it does not see the parent's history
15
- agentId?: string; // continue an existing child
15
+ agentId?: string; // continue or steer an existing child
16
16
  outputSchema?: object; // require structured output for this turn
17
17
  }
18
18
  ```
@@ -160,7 +160,7 @@ A directly declared subagent's tool name is the bare path-derived name, with no
160
160
  ```ts
161
161
  {
162
162
  message: string; // all context the child needs; it never sees the parent's history
163
- agentId?: string; // continue an existing child
163
+ agentId?: string; // continue or steer an existing child
164
164
  outputSchema?: object; // require structured output for this turn
165
165
  }
166
166
  ```
@@ -171,13 +171,17 @@ Do not rely on subagent delegation by itself as an approval boundary. Put sensit
171
171
 
172
172
  Each delegated subagent spins up its own child session and stream. The parent stream carries the control-plane events `subagent.called` and `subagent.completed`, plus interactive `input.requested`, `authorization.required`, and `authorization.completed` events proxied from descendants so the root channel can prompt the user. To follow the child's other progress, read `subagent.called.data.childSessionId` and subscribe at `GET /eve/v1/session/:childSessionId/stream`.
173
173
 
174
- A background task that was already admitted survives cancellation of the turn that started it; background work that has not yet been admitted is rejected with the cancelled step. Use `task_cancel` to stop an admitted task. Parent-session finalization cancels remaining live tasks.
174
+ A background task that was already admitted survives cancellation of the turn that started it; background work that has not yet been admitted is rejected with the cancelled step. Use `task_cancel` to stop an admitted task. Cancellation delivers the task's final notification to an active parent even if the task must be stopped forcibly. Parent-session finalization cancels remaining live tasks.
175
175
 
176
176
  Subagent model calls automatically retry classified transient provider failures, including overload errors delivered after a stream starts. eve makes at most three fresh model-call attempts, repeating only the current uncommitted call so completed earlier steps, tool results, and sandbox work remain available to the child. Other recoverable task errors fall back to Workflow's durable step retry from the last committed session snapshot. Exhausting the transient model-call attempts or the dedicated empty-response reissue returns one failed task result instead of stacking both retry budgets; terminal errors fail immediately.
177
177
 
178
178
  ## Agent messaging
179
179
 
180
- A child parks after answering instead of terminating, keeping its session and conversation history alive. A failed child turn can also leave the child parked — its latest status shows the error and the parent may message it again. Pass a parked child's `agentId` to the same subagent tool with a new `message` to continue that session. Omitting `agentId` (or passing an empty string or `null`) always starts a new child, and an `agentId` that matches no known agent falls back to starting a new child rather than failing. Passing a known `agentId` through a different subagent tool fails with `AGENT_MISMATCH`, and messaging a child that is still starting or working on its previous request fails with `AGENT_BUSY` — wait for its result before continuing it.
180
+ A child parks after answering instead of terminating, keeping its session and conversation history alive. A failed child turn can also leave the child parked — its latest status shows the error and the parent may message it again. Pass a parked child's `agentId` to the same subagent tool with a new `message` to continue that session. Omitting `agentId` (or passing an empty string or `null`) always starts a new child, and an `agentId` that matches no known agent falls back to starting a new child rather than failing. Passing a known `agentId` through a different subagent tool fails with `AGENT_MISMATCH`.
181
+
182
+ To steer a running background child, call the same subagent tool with its `agentId` and the updated `message`. eve cancels the previous task before starting a new task in the same child session. The child retains its conversation history, and the receipt contains the same `agentId` with a new `taskId`. The cancelled task cannot publish a later successful result. Steering does not undo tool side effects that have already occurred.
183
+
184
+ A child that is still starting, or is owned by a blocking workflow invocation rather than an admitted background task, continues to return `AGENT_BUSY`. Cancellation or delivery failures are reported to the caller; eve does not start a replacement child session to hide them.
181
185
 
182
186
  Whenever the set of parked (resumable) children changes, eve appends a framework-injected note to the conversation — labeled `[Agents]` and carrying an `<agents>` block — listing each child's `agentId`, name, and latest status. The static system prompt tells the model the note is injected by eve, not written by the user. The note is appended only when the listing changes (an append-only design that preserves the provider prompt cache), the most recent note is authoritative, and children that are starting or running do not appear until they park again.
183
187
 
@@ -32,6 +32,30 @@ A tool definition needs:
32
32
 
33
33
  When a tool returns structured data, add an optional `outputSchema`. With Zod or Standard Schema it also types the `execute` return.
34
34
 
35
+ ### Label tool activity
36
+
37
+ Use `label.start(input)` to describe a call in user-facing activity. The callback receives the validated tool input, so it can select the useful fields without exposing the full argument object:
38
+
39
+ ```ts title="agent/tools/deploy.ts"
40
+ export default defineTool({
41
+ description: "Deploy a project to an environment.",
42
+ inputSchema: z.object({
43
+ project: z.string(),
44
+ environment: z.enum(["preview", "production"]),
45
+ }),
46
+ label: {
47
+ start: ({ project, environment }) => `Deploy ${project} to ${environment}`,
48
+ },
49
+ async execute(input) {
50
+ return deploy(input);
51
+ },
52
+ });
53
+ ```
54
+
55
+ The label is presentation only. It does not replace the path-derived tool name, enter model input, or change execution. eve records the bounded label on the corresponding public action event so activity renderers and other authorized stream consumers can use it without interpreting raw tool data. Built-in channel activity can show `Deploy storefront to production` instead of `deploy`; if the callback throws or returns an empty label, activity falls back to the tool name.
56
+
57
+ Provided tools define labels for `bash`, `glob`, `grep`, `load_skill`, `read_file`, `web_fetch`, `web_search`, and `write_file`.
58
+
35
59
  ### Stream preliminary tool results
36
60
 
37
61
  For a `defineTool` executor with the default execution mode, an async generator streams complete
@@ -42,10 +66,15 @@ yield is the normal tool result the model receives:
42
66
  export default defineTool({
43
67
  description: "Build a project report.",
44
68
  inputSchema: z.object({ project: z.string() }),
69
+ label: {
70
+ start: ({ project }) => `Build report for ${project}`,
71
+ delta: (_input, partial) => partial.phase,
72
+ complete: (_input, output) => `Report ready with ${output.report.sections.length} sections`,
73
+ },
45
74
  async *execute({ project }) {
46
- yield { phase: "collecting", report: null };
75
+ yield { phase: "Collecting sources", report: null };
47
76
  const report = await buildReport(project);
48
- yield { phase: "complete", report };
77
+ yield { phase: "Complete", report };
49
78
  },
50
79
  });
51
80
  ```
@@ -58,6 +87,19 @@ can retry a step and replay overlapping snapshots. A generator `return` value is
58
87
  the result in this mode; yield the final output. Workflow and background generators have
59
88
  different result rules, described under [yield and return](#yield-and-return).
60
89
 
90
+ Use `label.delta(input, partial)` to project preliminary snapshots into
91
+ user-facing activity, and `label.complete(input, output)` to describe successful
92
+ settlement differently. Both callbacks receive the validated input first and the typed value yielded by
93
+ `execute` second. Each non-empty update replaces the previous activity label; the
94
+ result projection replaces the latest update immediately before eve marks the
95
+ action complete.
96
+
97
+ eve normalizes and bounds projected text before rendering it. If either
98
+ callback throws or returns an empty string, eve keeps the existing activity
99
+ label. `label.complete` does not run for failed or rejected tool calls. The
100
+ full values remain available in `action.partial` and `action.result`; renderers
101
+ receive only the projected text.
102
+
61
103
  ### Background execution
62
104
 
63
105
  Background execution controls how a tool delivers its result to the parent agent. Durable
@@ -93,6 +93,12 @@ tool object, a channel handler, or a schedule handler fails the build. To start
93
93
  channel or schedule, use the [channel operations](/docs/channels/custom#channel-operations-and-session-handles)
94
94
  or [schedule handler](/docs/schedules#handler-form-run) APIs.
95
95
 
96
+ Workflow imports resolve `paths` aliases from your application's `tsconfig.json` or
97
+ `jsconfig.json`, including when the application is a workspace package. eve includes only
98
+ workflow and step modules reachable from the agent's runtime modules. Unrelated workflows
99
+ in the host application stay outside the agent bundle. An unresolved workflow import fails
100
+ the build with the missing import in the error.
101
+
96
102
  ### Migrate an existing workflow tool
97
103
 
98
104
  Replace `defineTool` with `defineWorkflowTool`, keep the executor's `"use workflow"` directive,
@@ -1,58 +1,81 @@
1
1
  ---
2
- title: "Connect a Warehouse"
3
- description: "Part 4 of the Build an Agent tutorial. Let each user connect their own warehouse over an OAuth MCP via Vercel Connect."
2
+ title: "Connect a Warehouse (Optional)"
3
+ description: "Optional follow-up to the Build an Agent tutorial. Let each user connect their own warehouse over an OAuth MCP via Vercel Connect."
4
4
  ---
5
5
 
6
- The sample dataset got the analytics assistant running, but it's a stand-in. Now point the agent at a real warehouse and let each user connect their own by signing in through their browser. That's what a connection is for. It's an MCP server the model reaches through tools, with auth that eve drives for you.
6
+ Complete [the tutorial with sample data](./first-agent) first. When you have a warehouse with an MCP server, you can connect it to the analytics assistant and let each user sign in through their browser. That's what a connection is for. It's an MCP server the model reaches through tools, with auth that eve drives for you.
7
7
 
8
- This step depends on Vercel Connect, which is in private beta. No Connect access? Keep the Step 3 sample dataset and read this step for the connection model. Steps 5 through 9 work against the sample dataset, so you can complete the tutorial without a warehouse.
8
+ [Vercel Connect is generally available](https://vercel.com/changelog/vercel-connect-secure-access-to-external-services-for-your-agents). You can complete the tutorial with the [sample dataset](./query-sample-data); Connect is only needed for the OAuth integration shown on this page.
9
9
 
10
- The filename sets the runtime name. Put the file at `agent/connections/warehouse.ts` and it registers as `"warehouse"`, with its tools surfaced as `warehouse__<tool>`.
10
+ ## Before you start
11
+
12
+ This integration needs:
13
+
14
+ - A warehouse account and a working MCP server for that warehouse, with its full endpoint URL. Connect manages credentials; it does not create a warehouse or turn a SQL database into an MCP server.
15
+ - Permission to authorize that server, with read-only access to the data you want the agent to query.
16
+ - A Vercel account and a linked project for Connect.
17
+ - An authenticated user on the eve session. Complete the route-auth setup in [Ship it](./ship-it#replace-placeholderauth) before trying per-user OAuth from a web app.
18
+
19
+ If you are working through the tutorial for the first time, keep `run_sql` and [continue to Run analysis](./run-analysis). You can add this integration later.
20
+
21
+ ## Register the connector
22
+
23
+ Run these commands from `analytics-assistant/`. Replace the example URL with your MCP server's actual endpoint, including its path:
24
+
25
+ ```bash
26
+ npm install @vercel/connect
27
+ npx vercel@latest link
28
+ npx vercel@latest connect create "https://your-warehouse.example/mcp" --name warehouse
29
+ ```
30
+
31
+ Follow the provider-specific registration prompts. Copy the **connector UID returned by the CLI**. The display name `warehouse` is not the connector UID. Attach the returned connector to this project, then pull the local environment:
32
+
33
+ ```bash
34
+ npx vercel@latest connect attach "<returned-connector-uid>" --yes
35
+ npx vercel@latest env pull
36
+ ```
37
+
38
+ `vercel env pull` provides `VERCEL_OIDC_TOKEN` for local requests to Connect. That token identifies your Vercel project; it does not sign an end user into the app. The steps below use the authenticated deployment from [Ship it](./ship-it#replace-placeholderauth). The [Connect reference](https://vercel.com/docs/connect) covers service-specific setup and project attachments.
11
39
 
12
40
  ## Declare the connection
13
41
 
14
- The warehouse exposes a generic SQL MCP behind OAuth. Pass `connect()` from `@vercel/connect/eve` as the auth, and Vercel Connect handles the OAuth flow, stores the tokens, and refreshes them for you:
42
+ Create `agent/connections/warehouse.ts`. Replace both the endpoint URL and connector UID below with the values you just configured:
15
43
 
16
44
  ```ts title="agent/connections/warehouse.ts"
17
45
  import { connect } from "@vercel/connect/eve";
18
46
  import { defineMcpClientConnection } from "eve/connections";
19
47
 
20
48
  export default defineMcpClientConnection({
21
- url: "https://mcp.your-warehouse.example/sse",
49
+ url: "https://your-warehouse.example/mcp",
22
50
  description: "The team's data warehouse: run read-only SQL and list tables and columns.",
23
- auth: connect("warehouse"),
51
+ auth: connect("<returned-connector-uid>"),
24
52
  });
25
53
  ```
26
54
 
27
- `"warehouse"` is the UID you chose when registering the Connect client. By default this OAuth is user-scoped. Each end-user authorizes in their own browser, and eve resolves that user's token before every tool call.
55
+ The filename registers the eve connection as `"warehouse"`, with tools named `warehouse__<tool>`. This eve name is independent of the Connect connector UID. The remote server determines which tools exist; check its tool inventory and read-only permissions before using it.
28
56
 
29
- Before testing the warehouse from a web app, make sure the eve channel route auth maps your signed-in app user to `principalType: "user"`. A Connect-backed connection can only start per-user OAuth when the active session already has an authenticated user principal. If route auth still only accepts `localDev()`, a runtime token, or a placeholder guard, the first warehouse tool call fails with `reason: "principal_required"` instead of showing the sign-in challenge.
57
+ `connect("...")` is user-scoped by default. Each end-user authorizes in their own browser, and eve resolves that user's token before a tool call. The eve channel's route auth must map the signed-in app user to `principalType: "user"`. `localDev()`, a runtime token, or a placeholder guard cannot supply that identity; those sessions fail with `reason: "principal_required"` before OAuth starts. For a shared app credential, see [app vs. user auth](../connections#choose-app-vs-user-auth).
30
58
 
31
- Once Connect is enabled on your account, wire it up:
59
+ ## Deploy and try it
32
60
 
33
- 1. Install the package: `npm install @vercel/connect`.
34
- 2. Create the Connect client: `vercel connect create <type> --name warehouse`.
35
- 3. Link the client to your project.
36
- 4. Run `vercel link` and `vercel env pull` so `VERCEL_OIDC_TOKEN` is available locally.
61
+ Deploy the updated app so the authenticated web app includes the new connection:
37
62
 
38
- For the full reference, see [MCP connections](../connections/mcp).
39
-
40
- ## What the user sees
63
+ ```bash
64
+ npx vercel@latest deploy
65
+ ```
41
66
 
42
- Ask a question that needs the warehouse:
67
+ Open the new HTTPS preview URL, sign in with the credentials configured in [Ship it](./ship-it#replace-placeholderauth), and create a new session. The tutorial's development server bypasses the browser login and uses `localDev()`; restarting `npm run dev` alone does not provide the user identity this connection requires.
43
68
 
44
- ```text
45
- How many enterprise customers signed up last month?
46
- ```
69
+ From your authenticated web app, ask the agent to inspect the warehouse's available tables and then query a table you have access to. Use the server's schema rather than assuming it has the tutorial's `orders` and `customers` tables.
47
70
 
48
- The first time, the model picks a warehouse tool but there's no token yet, so the turn parks and the channel shows a "Sign in" affordance. You authorize in the browser, and once the OAuth callback completes, the turn resumes from exactly that step (the durable parking from [Step 2](./how-it-runs)) and the query runs. Later calls in the session reuse the cached per-user token, so there's no prompt.
71
+ If the user has not authorized the connector, the turn parks and the channel shows a sign-in link. Complete authorization in the browser; after the callback succeeds, the turn resumes and retries the tool call. Later calls use the existing grant while it remains valid. See [MCP connection troubleshooting](../connections/mcp#troubleshooting) if registration succeeds but the tool cannot connect.
49
72
 
50
73
  ## The token never reaches the model
51
74
 
52
- Right before each request to the MCP server, eve resolves the bearer and sends it as `Authorization: Bearer <token>`. The model only ever sees tool names, descriptions, and results. The credential stays out of its reach.
75
+ Right before each request to the MCP server, eve resolves the bearer and sends it as `Authorization: Bearer <token>`. The model sees tool names, descriptions, and results. The credential stays in the app runtime.
53
76
 
54
- If you want more control, gate the connection behind approval (`approval: once()`) or narrow which tools the model sees (`tools.allow`). See [MCP connections](../connections/mcp).
77
+ Gate the connection behind approval (`approval: once()`) or narrow which tools the model sees (`tools.allow`) when needed. See [MCP connections](../connections/mcp).
55
78
 
56
- → Next: [Run analysis](./run-analysis)
79
+ → Return to the tutorial: [Run analysis](./run-analysis)
57
80
 
58
81
  Learn more: [MCP connections](../connections/mcp) · [Authentication](../guides/auth-and-route-protection)