eve 0.30.8 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +5 -6
- package/dist/src/acp/adapter.d.ts +10 -4
- package/dist/src/acp/adapter.js +1 -1
- package/dist/src/channel/adapter-context.d.ts +1 -1
- package/dist/src/channel/adapter.d.ts +2 -4
- package/dist/src/channel/adapter.js +1 -1
- package/dist/src/channel/channel-address.d.ts +58 -0
- package/dist/src/channel/channel-address.js +1 -0
- package/dist/src/channel/channel-operations.d.ts +72 -0
- package/dist/src/channel/channel-operations.js +1 -0
- package/dist/src/channel/compiled-channel.d.ts +10 -6
- package/dist/src/channel/cross-channel-receive.d.ts +13 -25
- package/dist/src/channel/cross-channel-receive.js +2 -2
- package/dist/src/channel/receive-target.d.ts +1 -2
- package/dist/src/channel/routes.d.ts +21 -149
- package/dist/src/channel/schedule.d.ts +1 -1
- package/dist/src/channel/schedule.js +1 -1
- package/dist/src/channel/send-input.d.ts +6 -0
- package/dist/src/channel/send-input.js +1 -0
- package/dist/src/channel/session.d.ts +44 -30
- package/dist/src/channel/session.js +1 -1
- package/dist/src/channel/types.d.ts +8 -35
- package/dist/src/chunks/{use-eve-agent-DDoDDmJ2.js → use-eve-agent-CF5D6jSV.js} +339 -367
- package/dist/src/chunks/{use-eve-agent-Bx-ONuyb.js → use-eve-agent-DbYZLG0h.js} +342 -368
- package/dist/src/cli/commands/agent-prompt/build-and-verify.md +1 -2
- package/dist/src/cli/commands/channel-add-conflicts.js +1 -1
- package/dist/src/cli/commands/info.d.ts +1 -1
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/dev/tui/runner.d.ts +1 -1
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/subagent-pump.js +1 -1
- package/dist/src/cli/dev/tui/tui.js +1 -1
- package/dist/src/cli/invoke/invoke.d.ts +1 -1
- package/dist/src/cli/invoke/invoke.js +1 -1
- package/dist/src/cli/invoke/result.d.ts +0 -11
- package/dist/src/cli/invoke/result.js +1 -1
- package/dist/src/client/client-error.d.ts +2 -0
- package/dist/src/client/client-error.js +1 -1
- package/dist/src/client/client.d.ts +7 -14
- package/dist/src/client/client.js +1 -1
- package/dist/src/client/eve-agent-store.d.ts +4 -4
- package/dist/src/client/eve-agent-store.js +1 -1
- package/dist/src/client/index.d.ts +2 -1
- package/dist/src/client/index.js +1 -1
- package/dist/src/client/message-response.d.ts +2 -7
- package/dist/src/client/message-response.js +1 -1
- package/dist/src/client/open-stream.js +1 -1
- package/dist/src/client/session-controls.d.ts +29 -0
- package/dist/src/client/session-controls.js +1 -0
- package/dist/src/client/session-utils.d.ts +0 -20
- package/dist/src/client/session-utils.js +1 -1
- package/dist/src/client/session.d.ts +26 -90
- package/dist/src/client/session.js +1 -1
- package/dist/src/client/sessions.d.ts +20 -0
- package/dist/src/client/sessions.js +1 -0
- package/dist/src/client/types.d.ts +26 -47
- package/dist/src/compiler/normalize-subagent.js +1 -1
- package/dist/src/evals/cli/eval-client.js +1 -1
- package/dist/src/evals/context.js +1 -1
- package/dist/src/evals/session.d.ts +7 -6
- package/dist/src/evals/session.js +1 -1
- package/dist/src/evals/types.d.ts +9 -9
- package/dist/src/execution/agent-handle-dispatch.js +1 -1
- package/dist/src/execution/cancel-descendant-turns-step.js +1 -1
- package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
- package/dist/src/execution/remote-agent-dispatch.d.ts +1 -3
- package/dist/src/execution/remote-agent-dispatch.js +1 -1
- package/dist/src/execution/runtime-context.js +1 -1
- package/dist/src/execution/runtime-errors.d.ts +1 -1
- package/dist/src/execution/session-command-token.d.ts +2 -0
- package/dist/src/execution/session-command-token.js +1 -1
- package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
- package/dist/src/execution/subagent-hitl-proxy.js +1 -1
- package/dist/src/execution/turn-control-receiver.d.ts +2 -2
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/harness/cancelled-turn-emission.d.ts +1 -1
- package/dist/src/harness/cancelled-turn-emission.js +1 -1
- package/dist/src/harness/emission.d.ts +1 -1
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/handles/prompt.d.ts +22 -3
- package/dist/src/harness/handles/prompt.js +1 -1
- package/dist/src/harness/handles/store.d.ts +0 -5
- package/dist/src/harness/handles/store.js +1 -1
- package/dist/src/harness/runtime-actions.js +1 -1
- package/dist/src/harness/session-limit-enforcement.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
- package/dist/src/internal/nitro/dev-runtime-source-snapshot-copy.js +1 -1
- package/dist/src/internal/nitro/dev-runtime-source-snapshot-local-roots.d.ts +15 -0
- package/dist/src/internal/nitro/dev-runtime-source-snapshot-local-roots.js +1 -0
- package/dist/src/internal/nitro/dev-runtime-source-snapshot.d.ts +14 -2
- package/dist/src/internal/nitro/dev-runtime-source-snapshot.js +1 -1
- package/dist/src/internal/nitro/host/dev-server-http.js +1 -1
- package/dist/src/internal/nitro/routes/channel-dispatch.d.ts +2 -5
- package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
- package/dist/src/internal/nitro/routes/channel-route-context.d.ts +4 -3
- package/dist/src/internal/nitro/routes/channel-route-context.js +1 -1
- package/dist/src/packages/eve-catalog/src/index.js +1 -1
- package/dist/src/protocol/cancel-turn.d.ts +9 -4
- package/dist/src/protocol/cancel-turn.js +1 -1
- package/dist/src/protocol/message.d.ts +4 -14
- package/dist/src/protocol/message.js +2 -2
- package/dist/src/protocol/reset-session.d.ts +1 -1
- package/dist/src/protocol/routes.d.ts +27 -47
- package/dist/src/protocol/routes.js +1 -1
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +19 -21
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
- package/dist/src/public/channels/discord/discordChannel.d.ts +4 -4
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/eve.d.ts +11 -10
- package/dist/src/public/channels/eve.js +1 -1
- package/dist/src/public/channels/github/dispatch.d.ts +8 -8
- package/dist/src/public/channels/github/dispatch.js +1 -1
- package/dist/src/public/channels/github/githubChannel.d.ts +3 -3
- package/dist/src/public/channels/github/githubChannel.js +1 -1
- package/dist/src/public/channels/github/pr-context.d.ts +1 -1
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/channels/linear/linearChannel.d.ts +3 -3
- package/dist/src/public/channels/linear/linearChannel.js +1 -1
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/interactions.d.ts +3 -4
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/public/channels/slack/session-operations.d.ts +37 -0
- package/dist/src/public/channels/slack/session-operations.js +1 -0
- package/dist/src/public/channels/slack/slackChannel.d.ts +55 -74
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/teamsChannel.d.ts +3 -3
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/telegram/telegramChannel.d.ts +4 -4
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +3 -3
- package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
- package/dist/src/public/definitions/callback-context.d.ts +1 -2
- package/dist/src/public/definitions/channel.d.ts +21 -86
- package/dist/src/public/definitions/channel.js +1 -1
- package/dist/src/public/definitions/remote-agent.js +1 -1
- package/dist/src/public/definitions/schedule.d.ts +16 -16
- package/dist/src/public/schedules/index.d.ts +1 -1
- package/dist/src/react/use-eve-agent.d.ts +7 -4
- package/dist/src/react/use-eve-agent.js +1 -1
- package/dist/src/runtime/agent/bootstrap-model-utils.d.ts +11 -0
- package/dist/src/runtime/agent/bootstrap-model-utils.js +1 -1
- package/dist/src/runtime/agent/mock-model-adapter.js +1 -1
- package/dist/src/runtime/agent/mock-model-fixtures.js +2 -2
- package/dist/src/runtime/prompt/compose.js +1 -1
- package/dist/src/runtime/subagents/dynamic-remote-agent-config.js +1 -1
- package/dist/src/runtime/types.d.ts +2 -2
- package/dist/src/services/inspect-application.d.ts +1 -1
- package/dist/src/services/inspect-application.js +1 -1
- package/dist/src/setup/channel-add-conflicts.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
- package/dist/src/setup/scaffold/create/web-template.js +29 -17
- package/dist/src/shared/channel-definition.d.ts +7 -7
- package/dist/src/shared/schedule-definition.d.ts +2 -2
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.d.ts +8 -5
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.d.ts +8 -5
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/README.md +6 -6
- package/docs/channels/chat-sdk.mdx +5 -6
- package/docs/channels/custom.mdx +116 -144
- package/docs/channels/discord.mdx +1 -1
- package/docs/channels/eve.mdx +76 -30
- package/docs/channels/github.mdx +1 -1
- package/docs/channels/linear.mdx +6 -8
- package/docs/channels/overview.mdx +2 -2
- package/docs/channels/slack.mdx +46 -15
- package/docs/channels/telegram.mdx +1 -1
- package/docs/channels/twilio.mdx +1 -1
- package/docs/concepts/default-harness.md +11 -3
- package/docs/concepts/execution-model-and-durability.mdx +17 -5
- package/docs/concepts/sessions-runs-and-streaming.md +42 -16
- package/docs/evals/cases.mdx +3 -3
- package/docs/evals/targets.mdx +1 -1
- package/docs/guides/auth-and-route-protection.md +2 -1
- package/docs/guides/client/continuations.mdx +50 -77
- package/docs/guides/client/messages.mdx +29 -33
- package/docs/guides/client/output-schema.mdx +7 -14
- package/docs/guides/client/overview.mdx +40 -11
- package/docs/guides/client/streaming.mdx +17 -23
- package/docs/guides/frontend/nuxt.mdx +1 -1
- package/docs/guides/frontend/overview.mdx +40 -36
- package/docs/guides/frontend/sveltekit.mdx +1 -1
- package/docs/guides/frontend/use-eve-agent-svelte.mdx +19 -23
- package/docs/guides/frontend/use-eve-agent-vue.mdx +21 -25
- package/docs/guides/instrumentation.md +1 -1
- package/docs/patterns/dynamic-scheduling.md +16 -15
- package/docs/schedules.mdx +10 -11
- package/docs/subagents.mdx +1 -1
- package/docs/tools/human-in-the-loop.md +1 -1
- package/docs/tutorial/ship-it.mdx +1 -1
- package/package.json +1 -1
- package/dist/src/channel/cancel.d.ts +0 -6
- package/dist/src/channel/cancel.js +0 -1
- package/dist/src/channel/clear-session.d.ts +0 -4
- package/dist/src/channel/clear-session.js +0 -1
- package/dist/src/channel/compact-session.d.ts +0 -4
- package/dist/src/channel/compact-session.js +0 -1
- package/dist/src/channel/reset-session.d.ts +0 -6
- package/dist/src/channel/reset-session.js +0 -1
- package/dist/src/channel/resolve-active-session.d.ts +0 -4
- package/dist/src/channel/resolve-active-session.js +0 -1
- package/dist/src/channel/send.d.ts +0 -6
- package/dist/src/channel/send.js +0 -1
|
@@ -6,7 +6,7 @@ description: "How users reach your agent: the channel contract, the base eve HTT
|
|
|
6
6
|
A channel is the edge adapter between a platform and your agent. It does three things:
|
|
7
7
|
|
|
8
8
|
- Normalizes platform input into a user message.
|
|
9
|
-
- Owns the
|
|
9
|
+
- Owns the channel-local address that maps a platform conversation to its current durable session.
|
|
10
10
|
- Decides delivery, meaning how, where, and whether a response goes back.
|
|
11
11
|
|
|
12
12
|
eve ships a base HTTP channel plus first-class platform channels, and you can author your own. Browse the full set in the [Integrations](/integrations) gallery and choose the Channels filter.
|
|
@@ -36,7 +36,7 @@ The eve channel is the framework's default HTTP session API, the routes the term
|
|
|
36
36
|
|
|
37
37
|
## Custom channels
|
|
38
38
|
|
|
39
|
-
When eve doesn't ship a channel for your surface, build one with `defineChannel` from `eve/channels`. A custom channel declares route handlers (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `WS`), an `events` map, and
|
|
39
|
+
When eve doesn't ship a channel for your surface, build one with `defineChannel` from `eve/channels`. A custom channel declares route handlers (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `WS`), an `events` map, and uses `send(address, input)` to start or resume a session. See [Custom channels](./custom) for the full walkthrough, including WebSocket routes, cross-channel hand-off, channel metadata, address tokens, and file uploads.
|
|
40
40
|
|
|
41
41
|
## Relationship to the Chat SDK
|
|
42
42
|
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -108,7 +108,7 @@ Like `threadContext`, this helper calls `conversations.replies` and requires the
|
|
|
108
108
|
|
|
109
109
|
#### Cancel an active turn
|
|
110
110
|
|
|
111
|
-
Message hooks (`onMessage`, `onAppMention`, and `onDirectMessage`) receive
|
|
111
|
+
Message hooks (`onMessage`, `onAppMention`, and `onDirectMessage`) receive thread-bound session operations directly on `ctx`. Call `ctx.cancel({ turnId? })` before returning `{ auth }` to stop the current turn and queue the new Slack message as replacement input, producing a debounce-like experience:
|
|
112
112
|
|
|
113
113
|
```ts title="agent/channels/slack.ts"
|
|
114
114
|
export default slackChannel({
|
|
@@ -123,11 +123,28 @@ export default slackChannel({
|
|
|
123
123
|
});
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
`"accepted"` means the cancellation request
|
|
126
|
+
`"accepted"` means the live thread session durably queued the cancellation request. A parked session consumes it as a no-op; `"no_active_turn"` means the thread has no active session owner. Both are successful outcomes, so the hook can still return `{ auth }` to deliver the replacement message. Cancellation never sends input itself. A custom `onInteraction` handler receives the same flat operations, which is useful for a Stop button that should cancel without replacement input.
|
|
127
|
+
|
|
128
|
+
Message and interaction hooks expose every current-owner operation directly on `ctx`:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
async onAppMention(ctx) {
|
|
132
|
+
await ctx.send("Run a separate turn.");
|
|
133
|
+
await ctx.respond(inputResponses);
|
|
134
|
+
await ctx.cancel();
|
|
135
|
+
await ctx.compact();
|
|
136
|
+
await ctx.clear();
|
|
137
|
+
await ctx.reset({ reason: "Start over" });
|
|
138
|
+
const session = await ctx.resolveSession();
|
|
139
|
+
return { auth: null };
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
These calls are independent examples; a handler normally chooses one. `ctx.send()` is the only method that can create a session when the thread is unowned. It derives Slack auth from the inbound user unless `auth` is supplied explicitly. Use `await ctx.resolveSession()` only when later work must stay pinned to the currently owning durable session ID.
|
|
127
144
|
|
|
128
145
|
#### Reset a conversation
|
|
129
146
|
|
|
130
|
-
Use the thread-bound `ctx.reset({ reason? })`
|
|
147
|
+
Use the thread-bound `ctx.reset({ reason? })` method when the conversation should start over instead. Reset terminally retires the session that currently owns the thread, including any in-flight turn. The next delivered message starts a new session with fresh history, state, and a new session-scoped sandbox:
|
|
131
148
|
|
|
132
149
|
```ts title="agent/channels/slack.ts"
|
|
133
150
|
export default slackChannel({
|
|
@@ -142,11 +159,11 @@ export default slackChannel({
|
|
|
142
159
|
});
|
|
143
160
|
```
|
|
144
161
|
|
|
145
|
-
Reset returns `{ status: "reset", previousSessionId }` when the thread had a session, or `{ status: "no_active_session" }` when it was already free. Both outcomes are successful. Returning `null` after reset consumes the triggering message as a command; return `{ auth }` instead to deliver that message as the first turn of the fresh session. Custom `onInteraction` handlers receive the same
|
|
162
|
+
Reset returns `{ status: "reset", previousSessionId }` when the thread had a session, or `{ status: "no_active_session" }` when it was already free. Both outcomes are successful. Returning `null` after reset consumes the triggering message as a command; return `{ auth }` instead to deliver that message as the first turn of the fresh session. Custom `onInteraction` handlers receive the same flat operations, which is useful for a New conversation button.
|
|
146
163
|
|
|
147
164
|
### Other Events API callbacks
|
|
148
165
|
|
|
149
|
-
Use `onEvent` for subscribed events such as `reaction_added`, `team_join`, or `channel_created`. It receives the raw, open-ended Slack event and owns control flow. `ctx.
|
|
166
|
+
Use `onEvent` for subscribed events such as `reaction_added`, `team_join`, or `channel_created`. It receives the raw, open-ended Slack event and owns control flow. Call `ctx.send(message, { target, auth })` zero, one, or many times to start turns. Each call returns the resulting session.
|
|
150
167
|
|
|
151
168
|
```ts title="agent/channels/slack.ts"
|
|
152
169
|
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
@@ -156,25 +173,39 @@ const onboardingChannels = ["C0123ABC", "C0456DEF"];
|
|
|
156
173
|
|
|
157
174
|
export default slackChannel({
|
|
158
175
|
credentials: connectSlackCredentials("slack/my-agent"),
|
|
159
|
-
async onEvent(
|
|
176
|
+
async onEvent(ctx, event) {
|
|
160
177
|
if (event.type !== "team_join") return;
|
|
161
178
|
|
|
162
179
|
await Promise.all(
|
|
163
180
|
onboardingChannels.map((channelId) =>
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
181
|
+
ctx.send(
|
|
182
|
+
`A user joined the Slack workspace. Onboard them from this event:\n${JSON.stringify(event)}`,
|
|
183
|
+
{
|
|
184
|
+
target: { channelId },
|
|
185
|
+
auth: null,
|
|
186
|
+
},
|
|
187
|
+
),
|
|
169
188
|
),
|
|
170
189
|
);
|
|
171
190
|
},
|
|
172
191
|
});
|
|
173
192
|
```
|
|
174
193
|
|
|
175
|
-
The webhook invocation already keeps an awaited `onEvent` handler alive. Use `ctx.waitUntil(promise)` for deliberately detached work, matching a handler-form schedule. `ctx.slack.request(operation, body)` provides workspace-scoped Slack Web API access, while `ctx.envelope` carries delivery metadata such as `team_id`, `event_id`, and `event_time`. Calls to `ctx.
|
|
194
|
+
The webhook invocation already keeps an awaited `onEvent` handler alive. Use `ctx.waitUntil(promise)` for deliberately detached work, matching a handler-form schedule. `ctx.slack.request(operation, body)` provides workspace-scoped Slack Web API access, while `ctx.envelope` carries delivery metadata such as `team_id`, `event_id`, and `event_time`. Calls to `ctx.send` automatically seed the callback's team id into Slack session state.
|
|
195
|
+
|
|
196
|
+
Because a generic event is not necessarily tied to one thread, put the target in each operation's input:
|
|
176
197
|
|
|
177
|
-
|
|
198
|
+
```ts
|
|
199
|
+
const target = { channelId, threadTs };
|
|
200
|
+
|
|
201
|
+
await ctx.send("Follow up", { auth: null, target });
|
|
202
|
+
await ctx.respond(inputResponses, { auth: null, target });
|
|
203
|
+
await ctx.cancel({ target, turnId });
|
|
204
|
+
await ctx.compact({ target });
|
|
205
|
+
await ctx.clear({ target });
|
|
206
|
+
await ctx.reset({ reason: "Start over", target });
|
|
207
|
+
const session = await ctx.resolveSession({ target });
|
|
208
|
+
```
|
|
178
209
|
|
|
179
210
|
`onEvent` is the raw fallback after the message hooks. If an event is not claimed by `onAppMention`, `onDirectMessage`, or `onMessage`, an authored `onEvent` receives it; otherwise eve applies the built-in mention/DM default or ignores it.
|
|
180
211
|
|
|
@@ -233,7 +264,7 @@ The default handlers reply in-thread and show progress. Typing indicators post a
|
|
|
233
264
|
|
|
234
265
|
Outbound text preserves bare `@` tokens as literal text. To mention a user, embed Slack's `<@USER_ID>` syntax directly or use `channel.thread.mentionUser(userId)`.
|
|
235
266
|
|
|
236
|
-
When a session starts without a `threadTs` (say, from a schedule
|
|
267
|
+
When a session starts without a `threadTs` (say, from a schedule's `to(slack, target).send(...)`), eve gives it a unique temporary continuation token. The first agent post anchors the session to the Slack message timestamp, and later posts and mentions resume that same session. Pass `initialMessage` with a `Card` to land a structured anchor first instead. `threadTs` and `initialMessage` are mutually exclusive.
|
|
237
268
|
|
|
238
269
|
The example below overrides `onAppMention` to gate on an authored message and posts the completed reply to the thread. Event handlers receive `(eventData, channel, ctx)`, with Slack platform handles on `channel.thread` and `channel.slack`:
|
|
239
270
|
|
|
@@ -272,7 +303,7 @@ events: {
|
|
|
272
303
|
|
|
273
304
|
### Proactive sessions
|
|
274
305
|
|
|
275
|
-
Start a session without an inbound message through `
|
|
306
|
+
Start a session without an inbound message through `ctx.to(slack, target).send(message, { auth })` from another channel, or `to(slack, target).send(message, { auth })` from a schedule `run` handler. The proactive target shape is `{ channelId, threadTs?, initialMessage? }`.
|
|
276
307
|
|
|
277
308
|
### Attachments
|
|
278
309
|
|
|
@@ -51,7 +51,7 @@ Human-in-the-loop (HITL) turns option requests into inline-keyboard buttons and
|
|
|
51
51
|
|
|
52
52
|
### Proactive sessions
|
|
53
53
|
|
|
54
|
-
Start a session without an inbound message through `
|
|
54
|
+
Start a session without an inbound message through `to(telegram, target).send(message, { auth })` from a schedule `run` handler, or `ctx.to(telegram, target).send(message, { auth })` from another channel. `target.chatId` is required. Add `messageThreadId` to land in a specific forum topic.
|
|
55
55
|
|
|
56
56
|
Private proactive chats stay keyed to the chat, or to the chat plus `messageThreadId` when you target a topic. Group and supergroup proactive sends anchor to the bot message id returned by Telegram, so replies to different bot messages can resume different sessions in the same chat. If Telegram does not return a recognized chat type for an outbound send, eve keeps the session unanchored instead of guessing.
|
|
57
57
|
|
package/docs/channels/twilio.mdx
CHANGED
|
@@ -66,7 +66,7 @@ SMS and voice have no native button or card affordance, so HITL prompts do not r
|
|
|
66
66
|
|
|
67
67
|
### Proactive sessions
|
|
68
68
|
|
|
69
|
-
Start a session without an inbound message through `
|
|
69
|
+
Start a session without an inbound message through `to(twilio, target).send(message, { auth })` from a schedule `run` handler, or `ctx.to(twilio, target).send(message, { auth })` from another channel. `target.phoneNumber` is required, and the channel needs `messaging.from` or `messaging.messagingServiceSid` for the outbound sender.
|
|
70
70
|
|
|
71
71
|
### Attachments
|
|
72
72
|
|
|
@@ -20,9 +20,17 @@ export default defineAgent({
|
|
|
20
20
|
|
|
21
21
|
Compaction also preserves the framework's own tool state automatically. It resets read-before-write tracking (so a write afterward re-reads the file whose read evidence was summarized away) and re-injects the active todo list, so the model keeps its task list across the summary. There is no per-tool hook to configure.
|
|
22
22
|
|
|
23
|
-
Clients and channels can also request compaction between turns. Call
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
Clients and channels can also request compaction between turns. Call
|
|
24
|
+
`ClientSession.compact()`, a channel route's `compact(address)`, or
|
|
25
|
+
`attachSession(sessionId).compact()`. The request does not append a user message;
|
|
26
|
+
if a turn is running, eve queues it until that turn settles. A successful manual
|
|
27
|
+
compaction emits the same `compaction.requested` and `compaction.completed`
|
|
28
|
+
events as automatic compaction, followed by `session.waiting`.
|
|
29
|
+
|
|
30
|
+
To discard model-message history instead of summarizing it, call the corresponding
|
|
31
|
+
`clear()` method on any of those handles. Clearing preserves the session identity,
|
|
32
|
+
system prompt, configured tools and skills, durable state, limits, and sandbox.
|
|
33
|
+
Its stream boundary is `context.cleared` followed by `session.waiting`.
|
|
26
34
|
|
|
27
35
|
## Built-in tools
|
|
28
36
|
|
|
@@ -68,11 +68,23 @@ Some work has to wait, including a human approving a [tool](../tools), an intera
|
|
|
68
68
|
|
|
69
69
|
## Message delivery and queueing
|
|
70
70
|
|
|
71
|
-
eve does not maintain a durable FIFO queue of user messages for a session.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
eve does not maintain a durable FIFO queue of user messages for a session. An
|
|
72
|
+
ID-addressed HTTP delivery and a channel-address delivery both target the
|
|
73
|
+
session's current command inbox; neither is a general message queue.
|
|
74
|
+
|
|
75
|
+
Only one active session can own a channel continuation token. A channel-created
|
|
76
|
+
session commits both its stable session-ID alias and its channel alias before
|
|
77
|
+
processing the first turn, and fails creation if another run already owns the
|
|
78
|
+
channel alias. Rekeying replaces only that channel alias. An HTTP-created
|
|
79
|
+
session has the stable ID alias and no channel continuation token. Competing
|
|
80
|
+
channel input is not forwarded to the owner.
|
|
81
|
+
|
|
82
|
+
When a session is waiting, a delivery through its ID or current channel address
|
|
83
|
+
wakes the session and starts the next turn. When a turn is already active, the
|
|
84
|
+
inbox may accept additional deliveries, but the runtime only drains them at
|
|
85
|
+
specific workflow boundaries. If more than one delivery is ready when the driver
|
|
86
|
+
checks, eve may fold them into the next turn; that drain is best-effort and
|
|
87
|
+
depends on workflow and transport timing.
|
|
76
88
|
|
|
77
89
|
So don't rely on concurrent sends to the same session behaving like a typical ordered chat queue. For deterministic behavior, send one user turn at a time and wait for `session.waiting` before sending the next message to the same session. If your channel can receive bursts while the agent is working, keep your own per-session queue in the channel or app layer, then deliver the next message after the session parks again. Separate sessions still run independently.
|
|
78
90
|
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Sessions, Runs & Streaming"
|
|
3
|
-
description: "The session
|
|
3
|
+
description: "The ID-addressed session contract: messages, controls, the NDJSON event stream, and reconnecting."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Every eve app speaks the same stable HTTP API to a [durable session](./execution-model-and-durability). This page is the contract you hold: the handles you get back, the events you stream, and how to reconnect.
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Identity by surface
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
The HTTP API and TypeScript client use one durable `sessionId` for messages,
|
|
11
|
+
controls, and streams. Every operation targets that exact session; none follows
|
|
12
|
+
or creates a replacement implicitly.
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
Authored channels also have channel-local continuation tokens. A token addresses
|
|
15
|
+
whichever session currently owns a platform conversation, such as a Slack thread.
|
|
16
|
+
That identity stays behind the channel boundary and is never accepted or returned
|
|
17
|
+
by the eve HTTP session API. See [Custom channels](../channels/custom#address-and-session-handles).
|
|
16
18
|
|
|
17
19
|
Sessions last 30 days by default; configure `limits.sessionTimeoutMs` in
|
|
18
20
|
`agent.ts`, or set it to `false` to disable the deadline. At expiration, eve
|
|
@@ -30,7 +32,8 @@ curl -X POST http://127.0.0.1:2000/eve/v1/session \
|
|
|
30
32
|
-d '{"message":"Summarize the latest forecast."}'
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
eve responds right away
|
|
35
|
+
eve responds right away with the durable `sessionId` in the JSON body and
|
|
36
|
+
`x-eve-session-id` header.
|
|
34
37
|
|
|
35
38
|
## Stream a session
|
|
36
39
|
|
|
@@ -66,7 +69,7 @@ The stream is newline-delimited JSON (NDJSON), one event per line:
|
|
|
66
69
|
| `turn.completed` | The turn finished. |
|
|
67
70
|
| `turn.failed` | The turn failed; carries `{ code, message, details? }`. |
|
|
68
71
|
| `turn.cancelled` | The turn was cancelled before finishing; always followed by `session.waiting`. |
|
|
69
|
-
| `session.waiting` | The session parked
|
|
72
|
+
| `session.waiting` | The session parked and is ready for the next message. |
|
|
70
73
|
| `session.failed` | The session failed. |
|
|
71
74
|
| `session.completed` | The session reached a terminal end. |
|
|
72
75
|
|
|
@@ -137,12 +140,12 @@ Authored [hooks](../guides/hooks) receive the same envelope, but observe each ev
|
|
|
137
140
|
|
|
138
141
|
## Send a follow-up message
|
|
139
142
|
|
|
140
|
-
Once the session is waiting (you'll see `session.waiting`), POST your follow-up to
|
|
143
|
+
Once the session is waiting (you'll see `session.waiting`), POST your follow-up to its ID-addressed messages endpoint:
|
|
141
144
|
|
|
142
145
|
```bash
|
|
143
146
|
curl -X POST http://127.0.0.1:2000/eve/v1/session/<sessionId> \
|
|
144
147
|
-H 'content-type: application/json' \
|
|
145
|
-
-d '{"
|
|
148
|
+
-d '{"message":"Now send the short version."}'
|
|
146
149
|
```
|
|
147
150
|
|
|
148
151
|
The follow-up reuses the same durable session: same history, same state.
|
|
@@ -166,9 +169,30 @@ curl -X POST http://127.0.0.1:2000/eve/v1/session/<sessionId>/cancel
|
|
|
166
169
|
# {"ok":true,"sessionId":"<sessionId>","status":"accepted"}
|
|
167
170
|
```
|
|
168
171
|
|
|
169
|
-
`"accepted"` means
|
|
172
|
+
`"accepted"` means the live session durably queued the request. Confirm an actual cancellation on the stream as `turn.cancelled` followed by `session.waiting`; the session then accepts the next message normally. If the turn is waiting on active local or remote subagents, eve also requests cancellation of every adopted child, recursively, before settling the parent. Each child reports its own cancellation boundary on its child-session stream; the parent does not emit `subagent.completed` for cancelled work. A live but already-parked session also returns `"accepted"` and consumes the command as a no-op. `"no_active_turn"` means the session or channel address is unknown or terminal. Both statuses are success, so clients can fire and forget. See the [eve channel](../channels/eve) for the full route contract.
|
|
173
|
+
|
|
174
|
+
The HTTP route returns `202` for `"accepted"` and `200` for
|
|
175
|
+
`"no_active_turn"`. Only the accepted result includes `sessionId`.
|
|
176
|
+
|
|
177
|
+
Custom channel routes request the same cancellation through
|
|
178
|
+
`from(address).cancel()` or `attachSession(sessionId).cancel()`. See
|
|
179
|
+
[custom channels](../channels/custom#channel-operations-and-session-handles).
|
|
180
|
+
|
|
181
|
+
## Compact, clear, and reset
|
|
182
|
+
|
|
183
|
+
All session controls are ID-addressed and accept no continuation token:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
curl -X POST http://127.0.0.1:2000/eve/v1/session/<sessionId>/compact
|
|
187
|
+
curl -X POST http://127.0.0.1:2000/eve/v1/session/<sessionId>/clear
|
|
188
|
+
curl -X POST http://127.0.0.1:2000/eve/v1/session/<sessionId>/reset \
|
|
189
|
+
-H 'content-type: application/json' \
|
|
190
|
+
-d '{"reason":"Start over"}'
|
|
191
|
+
```
|
|
170
192
|
|
|
171
|
-
|
|
193
|
+
Compaction summarizes context, clear removes model-message history in place,
|
|
194
|
+
and reset terminally retires the session. A reset ID never becomes a new session;
|
|
195
|
+
create another session explicitly for a fresh conversation.
|
|
172
196
|
|
|
173
197
|
## Reconnect and rewind
|
|
174
198
|
|
|
@@ -186,7 +210,9 @@ A negative `startIndex` reads relative to the stream's current tail. For example
|
|
|
186
210
|
curl "http://127.0.0.1:2000/eve/v1/session/<sessionId>/stream?startIndex=-1"
|
|
187
211
|
```
|
|
188
212
|
|
|
189
|
-
|
|
213
|
+
Because a tail-relative position does not resolve to an absolute consumed-event
|
|
214
|
+
count, client tail reads do not automatically reconnect or advance the stored
|
|
215
|
+
cursor.
|
|
190
216
|
|
|
191
217
|
For a catch-up read that stops instead of following the live stream, pass `includeTailIndex=1`. The response then carries the `x-eve-stream-tail-index` header: the zero-based index of the last durably recorded event, or `-1` before the first. Read from your cursor until it passes that tail, then disconnect — reconnecting from the updated cursor if the connection drops first:
|
|
192
218
|
|
|
@@ -201,7 +227,7 @@ The lookup is opt-in; requests without the parameter get no header. The TypeScri
|
|
|
201
227
|
|
|
202
228
|
For scripts, server-to-server calls, tests, evals, and custom UIs, `eve/client` wraps these routes in a typed client so you don't hand-roll the POST and NDJSON stream loop.
|
|
203
229
|
|
|
204
|
-
Start with the [TypeScript SDK](../guides/client/overview) guide. It covers basic usage, sending messages,
|
|
230
|
+
Start with the [TypeScript SDK](../guides/client/overview) guide. It covers basic usage, sending messages, session state, streaming, and per-turn `outputSchema` results.
|
|
205
231
|
|
|
206
232
|
## Inspect the agent over HTTP
|
|
207
233
|
|
|
@@ -227,6 +253,6 @@ The order is structural, not incidental. By the time a resolver or hook reads ch
|
|
|
227
253
|
## What to read next
|
|
228
254
|
|
|
229
255
|
- [Execution model & durability](./execution-model-and-durability): what makes a session durable and how parked work resumes.
|
|
230
|
-
- [Channels](../channels/overview):
|
|
256
|
+
- [Channels](../channels/overview): how platform addresses map to durable sessions.
|
|
231
257
|
- [TypeScript SDK](../guides/client/overview): call these routes from scripts and server-side code.
|
|
232
258
|
- [Frontend](../guides/frontend/overview): `useEveAgent` instead of raw routes.
|
package/docs/evals/cases.mdx
CHANGED
|
@@ -101,12 +101,12 @@ export default defineEval({
|
|
|
101
101
|
|
|
102
102
|
`t` drives the primary session; `t.newSession()` returns an independent `EveEvalSession` against the same target, whose events feed the same run-level assertions.
|
|
103
103
|
|
|
104
|
-
- `t.send(
|
|
105
|
-
- `t.start(
|
|
104
|
+
- `t.send(message, options?)` sends a turn and waits for it to settle. It matches `ClientSession.send()` and resolves to a turn carrying `.message` and `.expectOk()`.
|
|
105
|
+
- `t.start(message)` starts a text turn but returns as soon as the server accepts it. The returned live turn exposes `.sessionId`, `.waitForEvent(...)`, `.cancel()`, and `.result()` for coordinating with work that is still running.
|
|
106
106
|
- `t.cancel()` requests cooperative cancellation of the primary session's active turn. Both `accepted` and `no_active_turn` are successful outcomes.
|
|
107
107
|
- `t.sendFile(text, path, mediaType?)` attaches a local file as a data URL.
|
|
108
108
|
- `t.requireInputRequest(filter?)` records a gate, requires exactly one pending request, and returns it. Filters match tool name, action input, prompt, display, and option ids.
|
|
109
|
-
- `t.respond(
|
|
109
|
+
- `t.respond(responses, options?)` answers specific pending input requests and sends them as the next turn.
|
|
110
110
|
- `t.respondAll(optionId)` answers every pending input request with the same option and sends the responses as the next turn.
|
|
111
111
|
- `t.reply` is the last assistant message (or `null`); `t.sessionId` is the current session id; `t.events` is the full typed event stream captured so far.
|
|
112
112
|
|
package/docs/evals/targets.mdx
CHANGED
|
@@ -24,7 +24,7 @@ export default defineEval({
|
|
|
24
24
|
|
|
25
25
|
- `t.target.fetch(path, init)` performs an authenticated fetch against the target, useful for channel and webhook ingress. See [Authentication](#authentication) for how the runner authenticates.
|
|
26
26
|
- `t.target.dispatchSchedule(id)` triggers a [schedule](../schedules) through the dev-only schedule route and returns the session ids it created. It works only against a target with dev routes enabled (the local `eve eval` dev server, or a deployment running in development mode), and throws otherwise.
|
|
27
|
-
- `t.target.attachSession(sessionId, { startIndex? })` consumes one turn from a session created outside the eval, by a channel or a schedule, so its events feed the run-level assertions. `startIndex` skips events before that position, so a session already partway through its stream resumes from where you left off rather than replaying from the start.
|
|
27
|
+
- `t.target.attachSession(sessionId, { startIndex? })` consumes one turn from a session created outside the eval, by a channel or a schedule, so its events feed the run-level assertions. `startIndex` skips events before that position, so a session already partway through its stream resumes from where you left off rather than replaying from the start. The attached session stays bound to that exact ID, so `session.send(...)` and `session.respond(...)` continue it after the turn parks (`session.waiting`).
|
|
28
28
|
- `t.target.watchTurn(sessionId, { startIndex? })` starts consuming an externally-created turn immediately and returns a live-turn handle. Use `waitForEvent(...)` to coordinate with mid-turn work, `cancel()` to request cancellation, and `result()` to consume through the boundary. The live turn's `.session` is the attached `EveEvalSession` for follow-up sends after settlement.
|
|
29
29
|
|
|
30
30
|
Sessions attached this way are full `EveEvalSession`s: you can keep driving them and assert directly on that session (`session.succeeded()`, `session.calledTool(...)`). Aggregate assertions on `t` continue to read the whole run, including every attached session.
|
|
@@ -16,6 +16,7 @@ The route-auth policy lives on the HTTP channel factory (`agent/channels/eve.ts`
|
|
|
16
16
|
|
|
17
17
|
- `POST /eve/v1/session`
|
|
18
18
|
- `POST /eve/v1/session/:sessionId`
|
|
19
|
+
- `POST /eve/v1/session/:sessionId/{cancel,compact,clear,reset}`
|
|
19
20
|
- `GET /eve/v1/session/:sessionId/stream`
|
|
20
21
|
|
|
21
22
|
These routes are protected by the channel's auth policy. eve fails closed by default: production traffic is rejected unless you configure an authenticator that accepts it, and anonymous access requires an explicit `none()`.
|
|
@@ -181,7 +182,7 @@ import { createUnauthorizedResponse } from "eve/channels/auth";
|
|
|
181
182
|
|
|
182
183
|
export default defineChannel({
|
|
183
184
|
routes: [
|
|
184
|
-
POST("/message", async (req
|
|
185
|
+
POST("/message", async (req) => {
|
|
185
186
|
if (!isAllowed(req)) {
|
|
186
187
|
return createUnauthorizedResponse({
|
|
187
188
|
status: 403, // defaults to 401; code defaults to "forbidden" / "unauthorized"
|
|
@@ -1,137 +1,110 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
3
|
-
description: "Persist and resume eve client sessions with
|
|
2
|
+
title: "Session State"
|
|
3
|
+
description: "Persist and resume eve client sessions with durable session IDs and stream cursors."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- `sessionId`: the stream-and-inspect handle. Use it to attach to event history.
|
|
10
|
-
|
|
11
|
-
`ClientSession` also tracks `streamIndex`, the count of events already consumed. Together these three fields make a `SessionState`.
|
|
6
|
+
The TypeScript client uses one durable session ID for messages, controls, and
|
|
7
|
+
streams. A `ClientSession` adds a local `streamIndex` cursor so reconnects do not
|
|
8
|
+
replay events already consumed by that handle.
|
|
12
9
|
|
|
13
10
|
## Read and persist state
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
Create the session with its first turn, then persist `session.state` after
|
|
13
|
+
consuming the response:
|
|
16
14
|
|
|
17
15
|
```ts
|
|
18
|
-
const session = client.
|
|
16
|
+
const { session, response } = await client.sessions.create({
|
|
17
|
+
message: "Create a launch checklist.",
|
|
18
|
+
});
|
|
19
19
|
|
|
20
|
-
const response = await session.send("Create a launch checklist.");
|
|
21
20
|
await response.result();
|
|
22
|
-
|
|
23
21
|
await saveSessionState(session.state);
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Store the full state object:
|
|
24
|
+
The state is deliberately small:
|
|
29
25
|
|
|
30
26
|
```ts
|
|
31
|
-
interface
|
|
32
|
-
|
|
33
|
-
sessionId?: string;
|
|
27
|
+
interface ClientSessionState {
|
|
28
|
+
sessionId: string;
|
|
34
29
|
streamIndex: number;
|
|
35
30
|
}
|
|
36
31
|
```
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
`SessionState` is a cursor, not a chat transcript. If your app shows historical messages, persist the stream events separately and store them under your own chat or thread ID. On reload, pass the saved `SessionState` back to `client.session(savedState)` or `useEveAgent({ initialSession })`, and pass the saved events to your renderer or `useEveAgent({ initialEvents })`.
|
|
33
|
+
It is a remote stream cursor, not a transcript. Persist events separately when
|
|
34
|
+
your application renders chat history.
|
|
41
35
|
|
|
42
36
|
## Resume a saved session
|
|
43
37
|
|
|
44
|
-
|
|
38
|
+
Attach a fixed handle to the saved ID and restore its cursor:
|
|
45
39
|
|
|
46
40
|
```ts
|
|
47
|
-
import type {
|
|
41
|
+
import type { ClientSessionState } from "eve/client";
|
|
48
42
|
|
|
49
|
-
const saved = (await loadSessionState()) as
|
|
50
|
-
const session = client.
|
|
43
|
+
const saved = (await loadSessionState()) as ClientSessionState;
|
|
44
|
+
const session = client.sessions.attach(saved.sessionId, {
|
|
45
|
+
streamIndex: saved.streamIndex,
|
|
46
|
+
});
|
|
51
47
|
|
|
52
48
|
const response = await session.send("Now shorten it.");
|
|
53
|
-
|
|
54
|
-
console.log(result.message);
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
If all you have is a continuation token, pass it as shorthand:
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
const session = client.session(continuationToken);
|
|
61
|
-
const response = await session.send("Continue where we left off.");
|
|
62
|
-
await response.result();
|
|
49
|
+
console.log((await response.result()).message);
|
|
63
50
|
```
|
|
64
51
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
## Waiting, completed, and failed sessions
|
|
52
|
+
`attach()` performs no request. Every later operation targets exactly
|
|
53
|
+
`saved.sessionId`; it never follows or creates a replacement.
|
|
68
54
|
|
|
69
|
-
|
|
55
|
+
## Waiting, completed, and reset sessions
|
|
70
56
|
|
|
71
|
-
|
|
57
|
+
A session that emits `session.waiting` accepts another message through the same
|
|
58
|
+
handle. A terminal or reset session does not. Start a fresh conversation
|
|
59
|
+
explicitly with another `client.sessions.create(firstTurn)` call.
|
|
72
60
|
|
|
73
61
|
```ts
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
if (result.status === "completed") {
|
|
78
|
-
// session.state is now a fresh cursor: { streamIndex: 0 }
|
|
79
|
-
}
|
|
62
|
+
const reset = await session.reset({ reason: "Start over" });
|
|
63
|
+
const { session: fresh, response } = await client.sessions.create({ message: "Begin again." });
|
|
80
64
|
```
|
|
81
65
|
|
|
82
|
-
|
|
66
|
+
The old `session` remains pinned to its retired ID; `fresh` owns a different ID.
|
|
83
67
|
|
|
84
68
|
## Multiple sessions
|
|
85
69
|
|
|
86
|
-
Create
|
|
70
|
+
Create one handle per conversation:
|
|
87
71
|
|
|
88
72
|
```ts
|
|
89
|
-
const research = client.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
await supportResponse.result();
|
|
97
|
-
|
|
73
|
+
const { session: research, response: researchResponse } = await client.sessions.create({
|
|
74
|
+
message: "Research competitors.",
|
|
75
|
+
});
|
|
76
|
+
const { session: support, response: supportResponse } = await client.sessions.create({
|
|
77
|
+
message: "Draft a support reply.",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await Promise.all([researchResponse.result(), supportResponse.result()]);
|
|
98
81
|
await save("research", research.state);
|
|
99
82
|
await save("support", support.state);
|
|
100
83
|
```
|
|
101
84
|
|
|
102
|
-
The shared `Client`
|
|
103
|
-
|
|
104
|
-
For a multi-chat UI, this usually means one application row per chat with two eve fields: the latest `SessionState` and the ordered event log for rendering. Do not reuse one `ClientSession` across sidebar conversations, and do not treat `streamIndex` as your database row index; it is only the remote stream cursor for that eve session.
|
|
85
|
+
The shared `Client` owns host, auth, headers, and redirect policy. Each
|
|
86
|
+
`ClientSession` owns its fixed ID and stream cursor.
|
|
105
87
|
|
|
106
88
|
## Reconnect an existing stream
|
|
107
89
|
|
|
108
|
-
|
|
90
|
+
`stream()` starts from the handle's saved cursor and advances it as events are
|
|
91
|
+
read:
|
|
109
92
|
|
|
110
93
|
```ts
|
|
111
|
-
const session = client.
|
|
94
|
+
const session = client.sessions.attach(saved.sessionId, {
|
|
95
|
+
streamIndex: saved.streamIndex,
|
|
96
|
+
});
|
|
112
97
|
|
|
113
98
|
for await (const event of session.stream()) {
|
|
114
99
|
console.log(event.type);
|
|
115
100
|
}
|
|
116
101
|
```
|
|
117
102
|
|
|
118
|
-
`
|
|
119
|
-
|
|
120
|
-
If you only persisted `sessionId`, read the latest durable event to recover the current token from a waiting session:
|
|
121
|
-
|
|
122
|
-
```ts
|
|
123
|
-
const session = client.session({ sessionId, streamIndex: 0 });
|
|
124
|
-
|
|
125
|
-
for await (const event of session.stream({ startIndex: -1 })) {
|
|
126
|
-
if (event.type === "session.waiting") {
|
|
127
|
-
await saveContinuationToken(event.data.continuationToken);
|
|
128
|
-
}
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
```
|
|
103
|
+
Use `send()` for new input. For explicit cursors, tail-relative reads, and
|
|
104
|
+
bounded catch-up, see [Streaming](./streaming#open-a-stream-manually).
|
|
132
105
|
|
|
133
106
|
## What to read next
|
|
134
107
|
|
|
135
108
|
- [Streaming](./streaming): stream events and reconnect by index
|
|
136
109
|
- [Sessions, runs & streaming](../../concepts/sessions-runs-and-streaming): the raw HTTP contract
|
|
137
|
-
- [eve channel](../../channels/eve):
|
|
110
|
+
- [eve channel](../../channels/eve): the ID-addressed routes
|