eve 0.80.1 → 0.81.1
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.
- package/CHANGELOG.md +32 -0
- package/dist/src/channel/channel-address.d.ts +1 -0
- package/dist/src/channel/channel-address.js +1 -1
- package/dist/src/channel/inactive-prompt.d.ts +46 -0
- package/dist/src/channel/inactive-prompt.js +1 -0
- package/dist/src/channel/principal-identity.d.ts +2 -0
- package/dist/src/channel/principal-identity.js +1 -1
- package/dist/src/channel/session-not-active-error.d.ts +16 -0
- package/dist/src/channel/session-not-active-error.js +1 -0
- package/dist/src/chunks/{use-eve-agent-Vsl-S0yI.js → use-eve-agent-Bp6nbhfV.js} +6 -0
- package/dist/src/chunks/{use-eve-agent-Ch-Q-s12.js → use-eve-agent-DLkjJIm3.js} +6 -0
- package/dist/src/compiled/.vendor-stamp.json +2 -2
- package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
- package/dist/src/compiled/@eve/catalog/index.js +1 -1
- package/dist/src/compiler/extension-compatibility.d.ts +18 -18
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/context/provider.d.ts +5 -0
- package/dist/src/context/providers/connection.js +1 -1
- package/dist/src/context/run-step.js +1 -1
- package/dist/src/eve-channel/create-request.d.ts +2 -1
- package/dist/src/eve-channel/index.js +1 -1
- package/dist/src/eve-channel/request.d.ts +2 -1
- package/dist/src/eve-channel/request.js +1 -2
- package/dist/src/eve-channel/support.d.ts +9 -1
- package/dist/src/eve-channel/support.js +2 -1
- package/dist/src/eve-channel/types.d.ts +18 -0
- package/dist/src/execution/agent-sessions/steps.js +1 -1
- package/dist/src/execution/dynamic-workflow/tool.js +1 -1
- package/dist/src/execution/hook-ownership.d.ts +8 -0
- package/dist/src/execution/hook-ownership.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel-lookup.d.ts +6 -0
- package/dist/src/execution/sandbox/bindings/vercel-lookup.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +2 -2
- package/dist/src/execution/session/entry-input.d.ts +19 -5
- package/dist/src/execution/session/entry-input.js +1 -1
- package/dist/src/execution/session/entry.js +1 -1
- package/dist/src/execution/session/handoff-steps.d.ts +75 -13
- package/dist/src/execution/session/handoff-steps.js +1 -1
- package/dist/src/execution/session/handoff.d.ts +69 -27
- package/dist/src/execution/session/handoff.js +1 -1
- package/dist/src/execution/session/program.js +1 -1
- package/dist/src/execution/session/run-watch.d.ts +22 -0
- package/dist/src/execution/session/run-watch.js +1 -0
- package/dist/src/execution/session-inbox/address.d.ts +4 -1
- package/dist/src/execution/session-inbox/end-stranded-session.js +1 -1
- package/dist/src/execution/session-inbox/hook-release.d.ts +9 -2
- package/dist/src/execution/session-inbox/hook-release.js +1 -1
- package/dist/src/execution/session-inbox/inbox.d.ts +66 -6
- package/dist/src/execution/session-inbox/inbox.js +1 -1
- package/dist/src/execution/session-inbox/resume.d.ts +4 -3
- package/dist/src/execution/tools/agent-router-workflow.js +1 -1
- package/dist/src/execution/workflow-runtime.d.ts +6 -11
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/extensions/code/extension/extension.d.ts +2 -0
- package/dist/src/extensions/code/extension/extension.js +1 -1
- package/dist/src/extensions/git/extension/extension.d.ts +3 -0
- package/dist/src/extensions/git/extension/extension.js +1 -1
- package/dist/src/extensions/git/extension/lib/attribution.d.ts +22 -0
- package/dist/src/extensions/git/extension/lib/attribution.js +4 -0
- package/dist/src/extensions/git/extension/lib/gh-tool.js +1 -1
- package/dist/src/extensions/git/extension/lib/github-shell.d.ts +13 -1
- package/dist/src/extensions/git/extension/lib/github-shell.js +1 -1
- package/dist/src/extensions/git/extension/lib/sandbox.d.ts +1 -1
- package/dist/src/harness/authorization-event-fields.d.ts +3 -0
- package/dist/src/harness/authorization-event-fields.js +1 -1
- package/dist/src/harness/hitl/approvals.d.ts +9 -9
- package/dist/src/harness/hitl/approvals.js +1 -1
- package/dist/src/harness/hitl/approved-calls.d.ts +4 -4
- package/dist/src/harness/hitl/approved-calls.js +1 -1
- package/dist/src/harness/hitl/candidates.d.ts +7 -1
- package/dist/src/harness/hitl/candidates.js +1 -1
- package/dist/src/harness/hitl/coordinator.d.ts +2 -2
- package/dist/src/harness/hitl/coordinator.js +1 -1
- package/dist/src/harness/hitl/index.d.ts +2 -2
- package/dist/src/harness/hitl/index.js +1 -1
- package/dist/src/harness/hitl/intake.js +1 -1
- package/dist/src/harness/hitl/sign-ins.d.ts +10 -0
- package/dist/src/harness/hitl/sign-ins.js +1 -1
- package/dist/src/harness/inline-tool-authorization.d.ts +10 -11
- package/dist/src/harness/inline-tool-authorization.js +1 -1
- package/dist/src/harness/model-call/call.js +1 -1
- package/dist/src/harness/session-machine/current.d.ts +5 -0
- package/dist/src/harness/session-machine/current.js +1 -1
- package/dist/src/harness/session-machine/state.d.ts +15 -3
- package/dist/src/harness/session-machine/transitions.d.ts +23 -5
- package/dist/src/harness/session-machine/transitions.js +1 -1
- package/dist/src/harness/step/after-model.js +1 -1
- package/dist/src/instrumentation/content.d.ts +1 -1
- package/dist/src/instrumentation/content.js +1 -1
- package/dist/src/instrumentation/lifecycle.d.ts +4 -2
- package/dist/src/internal/application/stamped-package-version.js +1 -1
- package/dist/src/internal/authored-definition/connection.js +1 -1
- package/dist/src/internal/authored-definition/source-identity.js +1 -1
- package/dist/src/internal/mcp/skills.js +1 -1
- package/dist/src/internal/workflow/development-world-client.js +1 -1
- package/dist/src/protocol/message.d.ts +30 -0
- package/dist/src/protocol/message.js +1 -1
- package/dist/src/protocol/session-projection.d.ts +8 -1
- package/dist/src/protocol/session-projection.js +1 -1
- package/dist/src/public/channels/auth.js +1 -1
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +1 -1
- package/dist/src/public/channels/chat-sdk/input-actions.d.ts +5 -0
- package/dist/src/public/channels/chat-sdk/input-actions.js +2 -2
- package/dist/src/public/channels/chat-sdk/messageToUserContent.d.ts +2 -1
- package/dist/src/public/channels/chat-sdk/messageToUserContent.js +1 -1
- package/dist/src/public/channels/discord/defaults.d.ts +6 -1
- package/dist/src/public/channels/discord/defaults.js +2 -2
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/photon/attachments.d.ts +5 -2
- package/dist/src/public/channels/photon/attachments.js +1 -1
- package/dist/src/public/channels/photon/photonIMessageChannel.js +1 -1
- package/dist/src/public/channels/slack/api.d.ts +8 -1
- package/dist/src/public/channels/slack/api.js +1 -1
- package/dist/src/public/channels/slack/approval-cards.js +2 -2
- package/dist/src/public/channels/slack/interaction-cards.d.ts +19 -6
- package/dist/src/public/channels/slack/interaction-cards.js +1 -1
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/hitl.d.ts +7 -0
- package/dist/src/public/channels/teams/hitl.js +1 -1
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/telegram/hitl.d.ts +2 -0
- package/dist/src/public/channels/telegram/hitl.js +2 -2
- package/dist/src/public/channels/telegram/inbound.d.ts +2 -0
- package/dist/src/public/channels/telegram/inbound.js +1 -1
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/definitions/channel.js +1 -1
- package/dist/src/runtime/connections/mcp-client.d.ts +1 -1
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/registry-types.d.ts +4 -1
- package/dist/src/runtime/connections/registry.d.ts +3 -2
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/shared/gateway-routing.d.ts +20 -0
- package/dist/src/shared/gateway-routing.js +1 -0
- package/dist/src/shared/skill-package.js +1 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tools/provided/ask-question-workflow.js +1 -1
- package/dist/src/tools/provided/sleep-workflow.js +1 -1
- package/dist/src/tracing/eve/agent-otel-provider.js +1 -1
- package/dist/src/tracing/eve/gateway.d.ts +5 -0
- package/dist/src/tracing/eve/gateway.js +1 -1
- package/dist/src/tracing/eve/operation-input.d.ts +0 -7
- package/dist/src/tracing/eve/operation-input.js +1 -1
- package/dist/src/tracing/eve/policy.d.ts +1 -1
- package/dist/src/tracing/eve/policy.js +1 -1
- package/dist/src/tracing/eve/session.d.ts +2 -0
- package/dist/src/tracing/eve/session.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/README.md +1 -0
- package/docs/agent-config.md +22 -0
- package/docs/channels/chat-sdk.mdx +4 -2
- package/docs/channels/eve.mdx +27 -0
- package/docs/channels/photon.mdx +4 -2
- package/docs/channels/slack.mdx +4 -0
- package/docs/channels/teams.mdx +1 -1
- package/docs/channels/telegram.mdx +1 -1
- package/docs/code-extension.mdx +20 -6
- package/docs/concepts/execution-model-and-durability.mdx +16 -7
- package/docs/connections/mcp.mdx +4 -0
- package/docs/connections/overview.mdx +2 -2
- package/docs/evals/assertions.mdx +21 -0
- package/docs/guides/instrumentation/otel.mdx +20 -0
- package/docs/patterns/meta.json +1 -0
- package/docs/patterns/voice-notes.md +67 -0
- package/docs/sandbox/index.mdx +2 -0
- package/package.json +1 -1
package/docs/agent-config.md
CHANGED
|
@@ -67,6 +67,28 @@ 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
|
+
AI Gateway also uses this ID for
|
|
71
|
+
[session affinity](https://vercel.com/docs/ai-gateway/models-and-providers/session-affinity),
|
|
72
|
+
which tries the provider that served the session's previous call first, among
|
|
73
|
+
the providers your routing settings allow, so that provider can read the prompt
|
|
74
|
+
from its cache. The conversation
|
|
75
|
+
appears as one session in the AI Gateway logs Sessions view. Affinity can move
|
|
76
|
+
the pinned provider ahead of your `order`. To keep `order` exactly, set
|
|
77
|
+
`sessionAffinity: false`:
|
|
78
|
+
|
|
79
|
+
```ts title="agent/agent.ts"
|
|
80
|
+
import { defineAgent } from "eve";
|
|
81
|
+
|
|
82
|
+
export default defineAgent({
|
|
83
|
+
model: "anthropic/claude-opus-5.5",
|
|
84
|
+
modelOptions: {
|
|
85
|
+
providerOptions: {
|
|
86
|
+
gateway: { order: ["bedrock", "anthropic"], sessionAffinity: false },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
70
92
|
For direct OpenAI and `chatgpt()` model calls, eve sets
|
|
71
93
|
`providerOptions.openai.promptCacheKey` to a SHA-256 hash of the session ID,
|
|
72
94
|
which helps OpenAI route a session's calls to the cache that holds its prompt
|
|
@@ -140,7 +140,7 @@ This policy controls overlapping eve turns. Chat SDK's separate `concurrency` op
|
|
|
140
140
|
|
|
141
141
|
The default handlers post the agent's reply back to the thread — no `events` override required. Completed assistant messages are posted as markdown (`{ markdown: … }`) so adapters render rich text and email HTML rather than a raw string.
|
|
142
142
|
|
|
143
|
-
Streaming is on by default: the channel posts an initial message and edits it as tokens arrive (`message.appended`), throttled by `streamingEditIntervalMs` (default `1000`). Set `streaming: false` for surfaces that deliver one message per turn — email, for example — so the reply posts once on completion instead of editing:
|
|
143
|
+
Streaming is on by default: the channel posts an initial message and edits it as tokens arrive (`message.appended`), throttled by `streamingEditIntervalMs` (default `1000`). Set `streaming: false` for surfaces that deliver one message per turn or can't edit sent messages — email and WhatsApp, for example — so the reply posts once on completion instead of editing:
|
|
144
144
|
|
|
145
145
|
```ts
|
|
146
146
|
chatSdkChannel({
|
|
@@ -245,7 +245,9 @@ bot.onNewMention(async (thread, message) => {
|
|
|
245
245
|
});
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
When the adapter can download an attachment (it sets `fetchData`, as the Slack, Google Chat, WhatsApp, and Twilio adapters do), eve downloads it with the adapter's credentials when the turn runs, not in the webhook, and passes the bytes. Platform file URLs usually need those credentials, so the model provider couldn't open them. eve rebuilds the download with the adapter's `rehydrateAttachment`; an adapter without one can't download after the webhook returns, so its files reach the agent as a short note. A failed download, or a file over 25 MB, also becomes a note. An attachment with only a URL is downloaded by eve when the URL is public `https:`; otherwise it becomes a note.
|
|
248
|
+
When the adapter can download an attachment (it sets `fetchData`, as the Slack, Google Chat, WhatsApp, and Twilio adapters do), eve downloads it with the adapter's credentials when the turn runs, not in the webhook, and passes the bytes. Platform file URLs usually need those credentials, so the model provider couldn't open them. eve rebuilds the download with the adapter's `rehydrateAttachment`; an adapter without one can't download after the webhook returns, so its files reach the agent as a short note. A failed download, or a file over 25 MB, also becomes a note. An attachment with only a URL is downloaded by eve when the URL is public `https:`; otherwise it becomes a note. An attachment with neither a download nor a URL also becomes a note, so the agent still knows a file was sent.
|
|
249
|
+
|
|
250
|
+
Audio attachments, such as voice notes, reach the model as a file label, not as sound. To answer them, give the agent a transcription tool; see [Voice notes](../patterns/voice-notes).
|
|
249
251
|
|
|
250
252
|
See [File uploads](./custom#file-uploads) for how eve stages remote file URLs before the model call.
|
|
251
253
|
|
package/docs/channels/eve.mdx
CHANGED
|
@@ -341,6 +341,33 @@ export default eveChannel({
|
|
|
341
341
|
|
|
342
342
|
`onMessage` must return an auth result. Return `title` alongside `auth` to set the title when the dispatch creates a session or sends the first message to a prewarmed session. A successful canonical eve HTTP message always dispatches and therefore always produces or continues a session.
|
|
343
343
|
|
|
344
|
+
### Turn context
|
|
345
|
+
|
|
346
|
+
`context` strings become user messages in durable session history. For context that applies to one turn only, return `clientContext` instead. It replaces the request's [`clientContext`](../guides/client/messages) for that turn: it reaches every model call in the turn and is not kept in session history.
|
|
347
|
+
|
|
348
|
+
`ctx.eve.clientContext` holds the request's `clientContext` exactly as the client sent it, or `undefined` when it sent none. Use it to check or correct client-supplied state before the model sees it:
|
|
349
|
+
|
|
350
|
+
```ts title="agent/channels/eve.ts"
|
|
351
|
+
import { eveChannel, defaultEveAuth } from "eve/channels/eve";
|
|
352
|
+
import { vercelOidc } from "eve/channels/auth";
|
|
353
|
+
|
|
354
|
+
import { getVerifiedPlan } from "../lib/billing";
|
|
355
|
+
|
|
356
|
+
export default eveChannel({
|
|
357
|
+
auth: [vercelOidc()],
|
|
358
|
+
async onMessage(ctx) {
|
|
359
|
+
const auth = defaultEveAuth(ctx);
|
|
360
|
+
const plan = await getVerifiedPlan(auth);
|
|
361
|
+
return { auth, clientContext: [`The user's plan is ${plan}.`] };
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
- Omit `clientContext` to keep the request's context. Return `[]` to drop it.
|
|
367
|
+
- The returned value takes the same shape as request `clientContext` (`ClientContextValue`, exported from `eve/channels/eve`): a string, an array of strings, or a JSON object. eve turns it into model messages the same way.
|
|
368
|
+
- eve labels client-supplied entries `Client context:` for the model. Returned context is sent without the label.
|
|
369
|
+
- `onMessage` runs only for requests with a message. An input response keeps the context its request sent.
|
|
370
|
+
|
|
344
371
|
## Clients
|
|
345
372
|
|
|
346
373
|
The browser side of this API lives in the [Frontend](../guides/frontend/overview) docs, where `useEveAgent` drives the eve channel from React UI.
|
package/docs/channels/photon.mdx
CHANGED
|
@@ -34,8 +34,10 @@ Set `turnPolicy: "queue"` when every response should finish before Photon starts
|
|
|
34
34
|
|
|
35
35
|
Images and other files people send reach the agent as file parts. Photon's
|
|
36
36
|
webhook lists each file without its bytes, so eve downloads it from Photon by
|
|
37
|
-
its attachment id with the project's credentials.
|
|
38
|
-
|
|
37
|
+
its attachment id with the project's credentials. The webhook omits the id
|
|
38
|
+
for voice notes, so eve refetches those messages from Photon to get it. A
|
|
39
|
+
failed download, a file over 25 MB, or a file eve can't find an id for reaches
|
|
40
|
+
the agent as a short note.
|
|
39
41
|
|
|
40
42
|
Customize inbound dispatch with `onMessage`. Return `null` to ignore a message, or return `title` alongside `auth` to set the title when the dispatch starts a run:
|
|
41
43
|
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -645,6 +645,8 @@ The callback receives the `InputRequest` and current `SessionContext`, so it can
|
|
|
645
645
|
|
|
646
646
|
When a tool approval resolves, eve retires the messages it posted for it. The card loses its buttons and shows the answer and who gave it, the separate tool-input post is deleted, and a direct-message request's `Waiting on approval` note in the thread changes to the outcome. This also happens when the approval ends without a click, for example when the person replies in the thread instead, in which case the card reads `No longer needed`.
|
|
647
647
|
|
|
648
|
+
A click on any request whose session is no longer active, for example because its run was cancelled outside eve, can never be answered. eve removes the card's buttons, marks it `No longer active`, and tells the person who clicked, in an ephemeral message, to send a new message.
|
|
649
|
+
|
|
648
650
|
A renderer's `input.requested` handler can extend delivery and then hand the requests on to eve with `next()`. For example, an application can add non-sensitive context to the Slack thread before eve renders the actionable controls. The configured `approvalChannel` still sends sensitive reviews to a DM and keeps ordinary requests in the thread:
|
|
649
651
|
|
|
650
652
|
```ts title="agent/channels/slack.ts"
|
|
@@ -748,6 +750,8 @@ The agent receives the files attached to the message that reaches it. When a men
|
|
|
748
750
|
|
|
749
751
|
Images, PDFs, audio, video, and other files all reach the agent. See [Inbound attachments](../sandbox#inbound-attachments) for which files the model reads directly and which it opens with tools. Files shared through an integration such as Google Drive or Dropbox are links to the other service, not Slack files, so eve skips them; their link stays in the message text. A download that fails or takes longer than 30 seconds reaches the model as a note.
|
|
750
752
|
|
|
753
|
+
Slack voice clips reach the model as an audio file label, not as sound. To answer them, give the agent a transcription tool; see [Voice notes](../patterns/voice-notes).
|
|
754
|
+
|
|
751
755
|
Private file downloads resolve credentials from the session's app installation workspace and require the Slack bot token's `files:read` scope. Add the scope and reinstall the Slack app before sending attachments.
|
|
752
756
|
|
|
753
757
|
Downloads cover Slack's own file hosts plus URLs under a configured [`api.fileBaseUrl`](#point-the-channel-at-a-different-slack-api-host), and travel on `api.fetch` when one is configured.
|
package/docs/channels/teams.mdx
CHANGED
|
@@ -85,7 +85,7 @@ A human-in-the-loop (HITL) `input.requested` event renders as an Adaptive Card.
|
|
|
85
85
|
|
|
86
86
|
By default, submissions use the Teams identity of the user who clicked the card. If you customize `onMessage` for an allowlist, configure the same policy in `onInputResponse`; eve otherwise rejects HITL submissions rather than bypassing the message gate.
|
|
87
87
|
|
|
88
|
-
When a tool approval settles, eve replaces its Adaptive Card with the outcome and responder name. A response rejected by an approval policy leaves the shared card unchanged.
|
|
88
|
+
When a tool approval settles, eve replaces its Adaptive Card with the outcome and responder name. A response rejected by an approval policy leaves the shared card unchanged. A submission to a card whose session is no longer active, for example because its run was cancelled outside eve, replaces the card with its prompt and `No longer active`, and the invoke response tells the person who submitted to send a new message.
|
|
89
89
|
|
|
90
90
|
### Connection sign-ins
|
|
91
91
|
|
|
@@ -49,7 +49,7 @@ The default `message.completed` handler sends plain text via `sendMessage`. It p
|
|
|
49
49
|
|
|
50
50
|
### Human-in-the-loop (HITL)
|
|
51
51
|
|
|
52
|
-
Human-in-the-loop (HITL) turns option requests into inline-keyboard buttons and freeform requests into `ForceReply`. Telegram caps `callback_data` at 64 bytes, so eve keeps compact callback ids in channel state instead. It acknowledges its own callbacks with `answerCallbackQuery`; anything it doesn't recognize goes to `onCallbackQuery`.
|
|
52
|
+
Human-in-the-loop (HITL) turns option requests into inline-keyboard buttons and freeform requests into `ForceReply`. Telegram caps `callback_data` at 64 bytes, so eve keeps compact callback ids in channel state instead. It acknowledges its own callbacks with `answerCallbackQuery`; anything it doesn't recognize goes to `onCallbackQuery`. A press on a prompt whose session is no longer active replaces the keyboard with `No longer active`, and the acknowledgement tells the person to send a new message.
|
|
53
53
|
|
|
54
54
|
A text reply to a bot message is sent as a normal message, with the replied-to message id in `state.replyToBotMessageId`. When that id matches a pending `ForceReply` prompt, the channel's `deliver` hook turns the reply into the answer. Otherwise the reply reaches the agent as a message, and starts a new session when none owns the thread.
|
|
55
55
|
|
package/docs/code-extension.mdx
CHANGED
|
@@ -98,12 +98,13 @@ export default git({});
|
|
|
98
98
|
|
|
99
99
|
Configure `github` to broker GitHub access through Connect instead. The extension then adds the authenticated `git__gh` tool, tells the agent to use it for every authenticated GitHub operation, and switches `git__pr` to `gh-signed-commit` for repositories that require verified signatures.
|
|
100
100
|
|
|
101
|
-
| Option
|
|
102
|
-
|
|
|
103
|
-
| `github.connector`
|
|
104
|
-
| `github.org`
|
|
105
|
-
| `github.broker`
|
|
106
|
-
| `github.authorize`
|
|
101
|
+
| Option | Required | Effect |
|
|
102
|
+
| -------------------- | ------------- | ------------------------------------------------------------------------------------------------- |
|
|
103
|
+
| `github.connector` | With `github` | Connector that mints a GitHub token |
|
|
104
|
+
| `github.org` | With `github` | Organization whose repositories the `gh` tool can access |
|
|
105
|
+
| `github.broker` | With `github` | `broker(sandbox, rules)` callback that installs, then removes, the GitHub credential rules |
|
|
106
|
+
| `github.authorize` | No | `authorize({ auth, repository, token })` callback that allows or denies each `gh` tool invocation |
|
|
107
|
+
| `github.attribution` | No | `attribution(ctx)` callback that returns the `{ name, email }` co-author for this turn |
|
|
107
108
|
|
|
108
109
|
The `gh` tool requests a token scoped to exactly one repository in `github.org` for each invocation. The sandbox process receives a placeholder `GH_TOKEN`, and the firewall exchanges it on matching GitHub requests. Your `github.broker` callback receives the header-transform rules for the command, then `null` to remove the lease. The callback must preserve your other network rules. GitHub authentication has no command-delivery option.
|
|
109
110
|
|
|
@@ -134,6 +135,19 @@ export default git({
|
|
|
134
135
|
});
|
|
135
136
|
```
|
|
136
137
|
|
|
138
|
+
Commits made by the `gh` tool are authored by the GitHub App. To credit the person a turn works for, return them from `github.attribution`. The callback receives the tool's context, so it can read the verified caller from `ctx.session.auth.current` or resolve their linked accounts with `ctx.getToken`. The `gh` tool then ends every `gh-signed-commit` message, and every `gh pr create` body passed with `--body` or `--body-file`, with `Co-authored-by: <name> <email>`. Return `null` to publish without a co-author. `Co-authored-by` lines the agent writes itself are kept, and the trailer joins their block so they still count. Pull requests created without a body flag, such as with `--fill` or `--template`, and other paths, such as `gh api`, are not attributed.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
git({
|
|
142
|
+
github: {
|
|
143
|
+
connector: "github",
|
|
144
|
+
org: "acme",
|
|
145
|
+
broker,
|
|
146
|
+
attribution: (ctx) => coAuthorFor(ctx.session.auth.current),
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
137
151
|
### Migrate from `code({ github })`
|
|
138
152
|
|
|
139
153
|
`code({ github })` is deprecated. While it is set, the code extension still contributes `code__gh`, its GitHub instructions, and the signed-commit `code__pr` skill. Without it, the code extension contributes no GitHub tool, instructions, or pull request skill. Move the `github` option to `git({ github })`, and import `gh` and the GitHub sandbox helpers from `eve/extensions/git/tools` and `eve/extensions/git/sandbox`.
|
|
@@ -27,7 +27,11 @@ When a Vercel production deployment changes, a new delivery to an idle session h
|
|
|
27
27
|
|
|
28
28
|
Before taking ownership, the target deployment upgrades the checkpoint when an older eve release wrote it, then checks that no pending work remains. A deployment accepts checkpoints from eve 0.66.0 and later, so upgrading eve does not leave long-lived sessions, such as a Slack thread, on their old deployment. An older release cannot read a newer checkpoint: after a rollback, a session that already moved to the newer deployment stays there. Extension state saved before eve 0.69 moves to its mount when exactly one mount of that extension package defines that state. State that the target deployment no longer defines, such as a removed `defineState` key, is dropped, and the target deployment's runtime logs show a `dropping unknown context key during deserialization` warning. When two or more mounts of one extension package define that state, eve cannot choose an owner, and the session stays on its current deployment. Idle subagent sessions that eve 0.66–0.68 kept for later reuse are stopped after the handoff. Authored session state remains opaque to this validation.
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Messages that arrive while a handoff is in progress are accepted right away and are not lost. The new owner processes them after the triggering delivery, in the order they arrived, whichever run accepted them.
|
|
31
|
+
|
|
32
|
+
The previous owner forwards the messages it accepted as soon as the new owner has taken over the session. Until they arrive, the new owner holds back everything it receives itself, including replies to tool runs and subagents that its first turn started, and processes it afterwards. A cancel still stops the new owner's running turn as soon as it arrives. If either run ends before the handoff settles, the other stops waiting for it.
|
|
33
|
+
|
|
34
|
+
If the target deployment rejects the session, the previous owner keeps it and processes the triggering delivery, followed by any messages that arrived during the attempt. When the target deployment cannot read the checkpoint, its Vercel runtime logs show a `session handoff refused` warning with the checkpoint version and the reason. Owners on this release or later also write a `session handoff failed` warning to their own runtime logs, with the target deployment and the reason.
|
|
31
35
|
|
|
32
36
|
eve does not move live work between deployments. A session with a running turn, pending human input or authorization, queued messages, or active tasks and subagents stays on its current deployment. After that work settles, a later delivery can move the idle session; the earlier deployment signal is not remembered.
|
|
33
37
|
|
|
@@ -35,7 +39,9 @@ eve does not move live work between deployments. A session with a running turn,
|
|
|
35
39
|
|
|
36
40
|
Every workflow run records each step it executes, so a session that ran on one run for weeks would carry its whole history in that run. To bound this, eve moves a session to a fresh run on its current deployment after [compaction](./default-harness#compaction) rewrites the conversation history. This applies to automatic compaction during a turn and to a client or channel compaction request.
|
|
37
41
|
|
|
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.
|
|
42
|
+
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. A message that is already waiting when the turn settles runs on the current run first, and the session moves at the next point where nothing is waiting. A message that arrives after the move starts is accepted right away and runs once, 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.
|
|
43
|
+
|
|
44
|
+
Both kinds of handoff need a Workflow world that can move a session's addresses from one run to another without releasing them, and can keep a released address reserved for a while (the `hookForceClaim` and `hookRetention` capabilities). Vercel Workflow, the local world, and `@workflow/world-postgres` 5.0.4 and later provide both. On a world without them, sessions never move between runs: deployment and compaction handoffs are skipped, and a session's run history grows for as long as the session lives. The first skipped attempt writes a `session handoff unavailable` warning to the runtime logs.
|
|
39
45
|
|
|
40
46
|
### Stranded sessions
|
|
41
47
|
|
|
@@ -253,11 +259,14 @@ the returned handle. That identity lookup does not block workflow resumption.
|
|
|
253
259
|
ID-addressed delivery already knows the session ID and needs no hook metadata
|
|
254
260
|
lookup. An ambiguous delivery failure does not trigger session creation.
|
|
255
261
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
delivery
|
|
259
|
-
|
|
260
|
-
|
|
262
|
+
A handoff never leaves the session's addresses without an owner. The previous
|
|
263
|
+
owner keeps every address until the new run takes all of them over, so a
|
|
264
|
+
delivery during a handoff is accepted immediately and is never reported as an
|
|
265
|
+
inactive session. An owner still running an earlier eve release gives up its
|
|
266
|
+
addresses briefly during a handoff and leaves a short-lived marker for each
|
|
267
|
+
one; a delivery that lands in that interval retries until the successor
|
|
268
|
+
claims the address. Either way, a channel never starts a replacement session
|
|
269
|
+
for an address that is changing owners.
|
|
261
270
|
|
|
262
271
|
When a session is waiting, a delivery through its ID or any claimed channel address wakes it and starts the next turn. Message sends default to `turnPolicy: "steer"`. Before assistant output begins, the session owner signals pending model generation to stop, then applies the correction within the same turn and with the same turn ID. This includes model requests that perform provider-managed search before answering; interrupted search work may run again. Reasoning and search progress do not count as assistant output.
|
|
263
272
|
|
package/docs/connections/mcp.mdx
CHANGED
|
@@ -53,6 +53,10 @@ eve asks the user to sign in only when the MCP server asks for it. When a connec
|
|
|
53
53
|
|
|
54
54
|
After the user signs in, the turn resumes and the model makes the call again. eve reconnects with the token and reloads the tool list, which may now include tools the server hides from anonymous callers. A connection that already has a token sends it on every request, as before.
|
|
55
55
|
|
|
56
|
+
eve resolves credentials before each MCP HTTP request, including requests on an existing client. When the cached token's `expiresAt` has passed, eve calls the token provider again. If the server rejects a bearer with HTTP `401`, eve evicts the cached token, asks the provider for another token, and retries the request once before asking for sign-in.
|
|
57
|
+
|
|
58
|
+
Eviction clears eve's cached access token and calls the provider's optional `evict` hook. The provider controls whether it retains a refresh grant for silent renewal. Missing grants and rejected replacement tokens still use the normal authorization flow; HTTP `403` permission failures do not trigger token refresh or retries.
|
|
59
|
+
|
|
56
60
|
A server that leaves protected tools out of its anonymous `tools/list` never triggers sign-in, because the model cannot find those tools. Servers that support anonymous use should list every tool and refuse the protected calls.
|
|
57
61
|
|
|
58
62
|
## Static tokens and headers
|
|
@@ -99,7 +99,7 @@ export default defineMcpClientConnection({
|
|
|
99
99
|
});
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
If the token has a known TTL, set `expiresAt` (milliseconds since epoch)
|
|
102
|
+
If the token has a known TTL, set `expiresAt` (milliseconds since epoch). Once it expires, eve calls `getToken` again before the next request, including requests on an existing MCP client.
|
|
103
103
|
|
|
104
104
|
When `getToken` is the only auth, `credentialOwner` defaults to `"app"`: one shared credential keyed across all sessions. Set `credentialOwner: "user"` when each end-user carries their own token.
|
|
105
105
|
|
|
@@ -332,7 +332,7 @@ channels fall back to title-casing the connection name when neither is set.
|
|
|
332
332
|
Two error classes drive the consent flow. Throw them from `getToken` or `completeAuthorization`; both are exported from `eve/connections`.
|
|
333
333
|
|
|
334
334
|
- `ConnectionAuthorizationRequiredError(connectionName)`: the user must authorize. Throw it from `getToken` to emit `authorization.required` and kick off the flow.
|
|
335
|
-
- `ConnectionAuthorizationFailedError(connectionName, { reason?, retryable? })`: authorization failed. `reason` is a stable machine-readable code (e.g. `"access_denied"`) that shows up on the `authorization.completed` event and the failed tool result. `retryable` defaults to `true`; set it to `false` for terminal cases like user denial
|
|
335
|
+
- `ConnectionAuthorizationFailedError(connectionName, { reason?, retryable? })`: authorization failed. `reason` is a stable machine-readable code (e.g. `"access_denied"`) that shows up on the `authorization.completed` event and the failed tool result. `retryable` defaults to `true`; set it to `false` for terminal cases like user denial. The `authorization.completed` event then carries `retryable: false`, and a later tool call in the same turn that needs the same grant for the same principal fails with that failure instead of asking the user to sign in again. The next turn asks again.
|
|
336
336
|
|
|
337
337
|
```ts
|
|
338
338
|
import { ConnectionAuthorizationFailedError } from "eve/connections";
|
|
@@ -187,6 +187,27 @@ t.eventsSatisfy("assistant reply includes the marker", (events) =>
|
|
|
187
187
|
);
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
+
### Assert and score reasoning
|
|
191
|
+
|
|
192
|
+
When the provider exposes reasoning, eve captures each completed block as a `reasoning.completed` event. Assert on its text with `turn.event(...)`, or join the blocks and pass them to a judge:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
turn.event("reasoning.completed", {
|
|
196
|
+
data: { reasoning: /checks? the units/i },
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const reasoning = turn.events
|
|
200
|
+
.filter((event) => event.type === "reasoning.completed")
|
|
201
|
+
.map((event) => event.data.reasoning)
|
|
202
|
+
.join("\n\n");
|
|
203
|
+
|
|
204
|
+
t.judge("The reasoning checks its assumptions before concluding.", {
|
|
205
|
+
on: reasoning,
|
|
206
|
+
}).atLeast(0.8);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
A provider that does not expose reasoning emits no `reasoning.completed` event, so the `turn.event` gate fails and the judge grades an empty string. Reasoning the provider keeps hidden cannot be graded. The default judge input and `session.transcript` exclude reasoning, which is why the example passes it with `on`.
|
|
210
|
+
|
|
190
211
|
## Required preconditions
|
|
191
212
|
|
|
192
213
|
Recorded assertions never throw and are not awaitable. When later control flow depends on a value, use `await t.require(value, assertion)`. It records a gate, returns the original value when it passes, and stops the test body without adding a duplicate execution error when it fails:
|
|
@@ -188,6 +188,26 @@ Gateway-routed model calls propagate the configured OpenTelemetry context at
|
|
|
188
188
|
each provider request. Eve-owned baggage is removed before forwarding. Gateway
|
|
189
189
|
controls whether its spans are sampled and which destinations receive them.
|
|
190
190
|
|
|
191
|
+
When AI Gateway reports
|
|
192
|
+
[session affinity](https://vercel.com/docs/ai-gateway/models-and-providers/session-affinity)
|
|
193
|
+
for a model call, the `agent.step` span records it. These attributes appear even
|
|
194
|
+
when the trace content policy omits outputs.
|
|
195
|
+
|
|
196
|
+
`vercel.ai_gateway.client_session_id` is the session ID AI Gateway used for
|
|
197
|
+
affinity. It is not the eve session ID that `vercel.session_id` records. It
|
|
198
|
+
equals `gen_ai.conversation.id` unless you set `gateway.sessionId` yourself or
|
|
199
|
+
the ID is longer than 256 characters, in which case AI Gateway hashes it. An
|
|
200
|
+
authored `gateway.sessionId` is recorded verbatim, so don't put personal data in
|
|
201
|
+
it.
|
|
202
|
+
|
|
203
|
+
| Attribute | Value |
|
|
204
|
+
| ----------------------------------------------- | --------------------------------------------------------------- |
|
|
205
|
+
| `vercel.ai_gateway.client_session_id` | Session ID AI Gateway used |
|
|
206
|
+
| `vercel.ai_gateway.client_session_id_source` | `explicit`, `agent`, or `fingerprint` |
|
|
207
|
+
| `vercel.ai_gateway.affinity.outcome` | What affinity did, such as `promoted`, `confirmed`, or `no_pin` |
|
|
208
|
+
| `vercel.ai_gateway.affinity.pinned_provider` | Provider the session's pin named |
|
|
209
|
+
| `vercel.ai_gateway.affinity.displaced_provider` | Provider that would have gone first, when the pin moved it |
|
|
210
|
+
|
|
191
211
|
## Local traces
|
|
192
212
|
|
|
193
213
|
`eve dev` records local traces by default. Omitting
|
package/docs/patterns/meta.json
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Voice Notes"
|
|
3
|
+
description: "Transcribe voice notes, voice messages, and other audio attachments with an AI Gateway transcription model so an eve agent can answer them."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Voice notes and other audio files reach your agent, but by default the model does not transcribe them. To give your agent the ability to answer a voice note, you will need to add a tool that transcribes the staged file.
|
|
7
|
+
|
|
8
|
+
This works for any channel that delivers audio as a file, such as iMessage voice notes through [Photon](../channels/photon), [Slack](../channels/slack#attachments) voice clips, and files sent through the [eve](../channels/eve) and [Chat SDK](../channels/chat-sdk#attachments) channels. See [Inbound attachments](../sandbox#inbound-attachments) for how eve stages files.
|
|
9
|
+
|
|
10
|
+
## Add a transcription tool
|
|
11
|
+
|
|
12
|
+
```ts title="agent/tools/transcribe_audio.ts"
|
|
13
|
+
import { posix } from "node:path";
|
|
14
|
+
import { transcribe } from "ai";
|
|
15
|
+
import { defineTool } from "eve/tools";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
const TRANSCRIPTION_MODEL = "openai/whisper-1";
|
|
19
|
+
const ATTACHMENTS_DIR = "/workspace/.eve/attachments/";
|
|
20
|
+
|
|
21
|
+
export default defineTool({
|
|
22
|
+
description:
|
|
23
|
+
"Transcribe an audio attachment, such as a voice note, to text. Pass the path from its `Attached file` label.",
|
|
24
|
+
inputSchema: z.object({
|
|
25
|
+
path: z.string().describe(`Attachment path under ${ATTACHMENTS_DIR}`),
|
|
26
|
+
}),
|
|
27
|
+
outputSchema: z.object({ transcript: z.string() }),
|
|
28
|
+
async execute({ path }, ctx) {
|
|
29
|
+
const file = posix.normalize(path);
|
|
30
|
+
if (!file.startsWith(ATTACHMENTS_DIR)) {
|
|
31
|
+
throw new Error(`Only attachments under ${ATTACHMENTS_DIR} can be transcribed.`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const sandbox = await ctx.getSandbox();
|
|
35
|
+
const audio = await sandbox.readBinaryFile({ path: file, abortSignal: ctx.abortSignal });
|
|
36
|
+
if (!audio) {
|
|
37
|
+
throw new Error(`Attachment not found: ${file}. Ask the user to send the voice note again.`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const { text } = await transcribe({
|
|
42
|
+
model: TRANSCRIPTION_MODEL,
|
|
43
|
+
audio,
|
|
44
|
+
abortSignal: ctx.abortSignal,
|
|
45
|
+
});
|
|
46
|
+
return { transcript: text.trim() };
|
|
47
|
+
} catch (error) {
|
|
48
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Transcription failed (${reason}). Tell the user you couldn't understand the voice note and ask them to type it.`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`transcribe()` uses the same credentials as your eve agent.
|
|
58
|
+
|
|
59
|
+
## Tell the agent when to transcribe
|
|
60
|
+
|
|
61
|
+
```md title="agent/instructions.md"
|
|
62
|
+
When a message has an audio attachment (`Attached file ... (audio/...)`), such as a voice note, call `transcribe_audio` with its path first and treat the transcript as what the user said. Do not mention the transcription step unless it fails.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## When transcription fails
|
|
66
|
+
|
|
67
|
+
When `execute` throws, eve gives the error to the model as a tool error, which it can relay to the user. See [When a tool throws](../tools#when-a-tool-throws). A file over the channel's upload limit, 25 MB by default, or a file eve can't download reaches the agent as a short note instead of a file.
|
package/docs/sandbox/index.mdx
CHANGED
|
@@ -162,6 +162,8 @@ Before every model call, eve reads the referenced files back from the sandbox. E
|
|
|
162
162
|
|
|
163
163
|
Other files, such as CSV, HEIC, audio, and video, reach the model as the label alone. eve checks image and PDF media types against the file's bytes when it stages the file. A file whose bytes don't match its declared image or PDF type is staged as `application/octet-stream`, so a provider never receives bytes it would reject.
|
|
164
164
|
|
|
165
|
+
To let the agent answer voice notes and other audio, add a tool that transcribes the staged file. See [Voice notes](/docs/patterns/voice-notes).
|
|
166
|
+
|
|
165
167
|
If a referenced file is missing, or its bytes no longer match what eve staged because something in the sandbox overwrote it, the model receives a `FileNotFound` note in its place.
|
|
166
168
|
|
|
167
169
|
A file that eve can't stage never enters session history. When a channel's `fetchFile` fails, or a file part's `data` is not bytes, base64, a `data:` URL, or a URL, the model receives a note that names the file instead.
|