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.
Files changed (211) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +5 -6
  3. package/dist/src/acp/adapter.d.ts +10 -4
  4. package/dist/src/acp/adapter.js +1 -1
  5. package/dist/src/channel/adapter-context.d.ts +1 -1
  6. package/dist/src/channel/adapter.d.ts +2 -4
  7. package/dist/src/channel/adapter.js +1 -1
  8. package/dist/src/channel/channel-address.d.ts +58 -0
  9. package/dist/src/channel/channel-address.js +1 -0
  10. package/dist/src/channel/channel-operations.d.ts +72 -0
  11. package/dist/src/channel/channel-operations.js +1 -0
  12. package/dist/src/channel/compiled-channel.d.ts +10 -6
  13. package/dist/src/channel/cross-channel-receive.d.ts +13 -25
  14. package/dist/src/channel/cross-channel-receive.js +2 -2
  15. package/dist/src/channel/receive-target.d.ts +1 -2
  16. package/dist/src/channel/routes.d.ts +21 -149
  17. package/dist/src/channel/schedule.d.ts +1 -1
  18. package/dist/src/channel/schedule.js +1 -1
  19. package/dist/src/channel/send-input.d.ts +6 -0
  20. package/dist/src/channel/send-input.js +1 -0
  21. package/dist/src/channel/session.d.ts +44 -30
  22. package/dist/src/channel/session.js +1 -1
  23. package/dist/src/channel/types.d.ts +8 -35
  24. package/dist/src/chunks/{use-eve-agent-DDoDDmJ2.js → use-eve-agent-CF5D6jSV.js} +339 -367
  25. package/dist/src/chunks/{use-eve-agent-Bx-ONuyb.js → use-eve-agent-DbYZLG0h.js} +342 -368
  26. package/dist/src/cli/commands/agent-prompt/build-and-verify.md +1 -2
  27. package/dist/src/cli/commands/channel-add-conflicts.js +1 -1
  28. package/dist/src/cli/commands/info.d.ts +1 -1
  29. package/dist/src/cli/commands/info.js +1 -1
  30. package/dist/src/cli/dev/tui/runner.d.ts +1 -1
  31. package/dist/src/cli/dev/tui/runner.js +1 -1
  32. package/dist/src/cli/dev/tui/subagent-pump.js +1 -1
  33. package/dist/src/cli/dev/tui/tui.js +1 -1
  34. package/dist/src/cli/invoke/invoke.d.ts +1 -1
  35. package/dist/src/cli/invoke/invoke.js +1 -1
  36. package/dist/src/cli/invoke/result.d.ts +0 -11
  37. package/dist/src/cli/invoke/result.js +1 -1
  38. package/dist/src/client/client-error.d.ts +2 -0
  39. package/dist/src/client/client-error.js +1 -1
  40. package/dist/src/client/client.d.ts +7 -14
  41. package/dist/src/client/client.js +1 -1
  42. package/dist/src/client/eve-agent-store.d.ts +4 -4
  43. package/dist/src/client/eve-agent-store.js +1 -1
  44. package/dist/src/client/index.d.ts +2 -1
  45. package/dist/src/client/index.js +1 -1
  46. package/dist/src/client/message-response.d.ts +2 -7
  47. package/dist/src/client/message-response.js +1 -1
  48. package/dist/src/client/open-stream.js +1 -1
  49. package/dist/src/client/session-controls.d.ts +29 -0
  50. package/dist/src/client/session-controls.js +1 -0
  51. package/dist/src/client/session-utils.d.ts +0 -20
  52. package/dist/src/client/session-utils.js +1 -1
  53. package/dist/src/client/session.d.ts +26 -90
  54. package/dist/src/client/session.js +1 -1
  55. package/dist/src/client/sessions.d.ts +20 -0
  56. package/dist/src/client/sessions.js +1 -0
  57. package/dist/src/client/types.d.ts +26 -47
  58. package/dist/src/compiler/normalize-subagent.js +1 -1
  59. package/dist/src/evals/cli/eval-client.js +1 -1
  60. package/dist/src/evals/context.js +1 -1
  61. package/dist/src/evals/session.d.ts +7 -6
  62. package/dist/src/evals/session.js +1 -1
  63. package/dist/src/evals/types.d.ts +9 -9
  64. package/dist/src/execution/agent-handle-dispatch.js +1 -1
  65. package/dist/src/execution/cancel-descendant-turns-step.js +1 -1
  66. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  67. package/dist/src/execution/remote-agent-dispatch.d.ts +1 -3
  68. package/dist/src/execution/remote-agent-dispatch.js +1 -1
  69. package/dist/src/execution/runtime-context.js +1 -1
  70. package/dist/src/execution/runtime-errors.d.ts +1 -1
  71. package/dist/src/execution/session-command-token.d.ts +2 -0
  72. package/dist/src/execution/session-command-token.js +1 -1
  73. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  74. package/dist/src/execution/subagent-hitl-proxy.js +1 -1
  75. package/dist/src/execution/turn-control-receiver.d.ts +2 -2
  76. package/dist/src/execution/turn-workflow.js +1 -1
  77. package/dist/src/execution/workflow-entry.js +1 -1
  78. package/dist/src/execution/workflow-runtime.js +1 -1
  79. package/dist/src/harness/cancelled-turn-emission.d.ts +1 -1
  80. package/dist/src/harness/cancelled-turn-emission.js +1 -1
  81. package/dist/src/harness/emission.d.ts +1 -1
  82. package/dist/src/harness/emission.js +1 -1
  83. package/dist/src/harness/handles/prompt.d.ts +22 -3
  84. package/dist/src/harness/handles/prompt.js +1 -1
  85. package/dist/src/harness/handles/store.d.ts +0 -5
  86. package/dist/src/harness/handles/store.js +1 -1
  87. package/dist/src/harness/runtime-actions.js +1 -1
  88. package/dist/src/harness/session-limit-enforcement.js +1 -1
  89. package/dist/src/harness/tool-loop.js +1 -1
  90. package/dist/src/internal/application/package.js +1 -1
  91. package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
  92. package/dist/src/internal/nitro/dev-runtime-source-snapshot-copy.js +1 -1
  93. package/dist/src/internal/nitro/dev-runtime-source-snapshot-local-roots.d.ts +15 -0
  94. package/dist/src/internal/nitro/dev-runtime-source-snapshot-local-roots.js +1 -0
  95. package/dist/src/internal/nitro/dev-runtime-source-snapshot.d.ts +14 -2
  96. package/dist/src/internal/nitro/dev-runtime-source-snapshot.js +1 -1
  97. package/dist/src/internal/nitro/host/dev-server-http.js +1 -1
  98. package/dist/src/internal/nitro/routes/channel-dispatch.d.ts +2 -5
  99. package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
  100. package/dist/src/internal/nitro/routes/channel-route-context.d.ts +4 -3
  101. package/dist/src/internal/nitro/routes/channel-route-context.js +1 -1
  102. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  103. package/dist/src/protocol/cancel-turn.d.ts +9 -4
  104. package/dist/src/protocol/cancel-turn.js +1 -1
  105. package/dist/src/protocol/message.d.ts +4 -14
  106. package/dist/src/protocol/message.js +2 -2
  107. package/dist/src/protocol/reset-session.d.ts +1 -1
  108. package/dist/src/protocol/routes.d.ts +27 -47
  109. package/dist/src/protocol/routes.js +1 -1
  110. package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +19 -21
  111. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
  112. package/dist/src/public/channels/discord/discordChannel.d.ts +4 -4
  113. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  114. package/dist/src/public/channels/eve.d.ts +11 -10
  115. package/dist/src/public/channels/eve.js +1 -1
  116. package/dist/src/public/channels/github/dispatch.d.ts +8 -8
  117. package/dist/src/public/channels/github/dispatch.js +1 -1
  118. package/dist/src/public/channels/github/githubChannel.d.ts +3 -3
  119. package/dist/src/public/channels/github/githubChannel.js +1 -1
  120. package/dist/src/public/channels/github/pr-context.d.ts +1 -1
  121. package/dist/src/public/channels/index.d.ts +1 -1
  122. package/dist/src/public/channels/linear/linearChannel.d.ts +3 -3
  123. package/dist/src/public/channels/linear/linearChannel.js +1 -1
  124. package/dist/src/public/channels/slack/index.d.ts +1 -1
  125. package/dist/src/public/channels/slack/interactions.d.ts +3 -4
  126. package/dist/src/public/channels/slack/interactions.js +1 -1
  127. package/dist/src/public/channels/slack/session-operations.d.ts +37 -0
  128. package/dist/src/public/channels/slack/session-operations.js +1 -0
  129. package/dist/src/public/channels/slack/slackChannel.d.ts +55 -74
  130. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  131. package/dist/src/public/channels/teams/teamsChannel.d.ts +3 -3
  132. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  133. package/dist/src/public/channels/telegram/telegramChannel.d.ts +4 -4
  134. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  135. package/dist/src/public/channels/twilio/twilioChannel.d.ts +3 -3
  136. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  137. package/dist/src/public/definitions/callback-context.d.ts +1 -2
  138. package/dist/src/public/definitions/channel.d.ts +21 -86
  139. package/dist/src/public/definitions/channel.js +1 -1
  140. package/dist/src/public/definitions/remote-agent.js +1 -1
  141. package/dist/src/public/definitions/schedule.d.ts +16 -16
  142. package/dist/src/public/schedules/index.d.ts +1 -1
  143. package/dist/src/react/use-eve-agent.d.ts +7 -4
  144. package/dist/src/react/use-eve-agent.js +1 -1
  145. package/dist/src/runtime/agent/bootstrap-model-utils.d.ts +11 -0
  146. package/dist/src/runtime/agent/bootstrap-model-utils.js +1 -1
  147. package/dist/src/runtime/agent/mock-model-adapter.js +1 -1
  148. package/dist/src/runtime/agent/mock-model-fixtures.js +2 -2
  149. package/dist/src/runtime/prompt/compose.js +1 -1
  150. package/dist/src/runtime/subagents/dynamic-remote-agent-config.js +1 -1
  151. package/dist/src/runtime/types.d.ts +2 -2
  152. package/dist/src/services/inspect-application.d.ts +1 -1
  153. package/dist/src/services/inspect-application.js +1 -1
  154. package/dist/src/setup/channel-add-conflicts.js +1 -1
  155. package/dist/src/setup/scaffold/create/project.js +1 -1
  156. package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
  157. package/dist/src/setup/scaffold/create/web-template.js +29 -17
  158. package/dist/src/shared/channel-definition.d.ts +7 -7
  159. package/dist/src/shared/schedule-definition.d.ts +2 -2
  160. package/dist/src/svelte/index.js +1 -1
  161. package/dist/src/svelte/use-eve-agent.d.ts +8 -5
  162. package/dist/src/svelte/use-eve-agent.js +1 -1
  163. package/dist/src/vue/index.js +1 -1
  164. package/dist/src/vue/use-eve-agent.d.ts +8 -5
  165. package/dist/src/vue/use-eve-agent.js +1 -1
  166. package/docs/README.md +6 -6
  167. package/docs/channels/chat-sdk.mdx +5 -6
  168. package/docs/channels/custom.mdx +116 -144
  169. package/docs/channels/discord.mdx +1 -1
  170. package/docs/channels/eve.mdx +76 -30
  171. package/docs/channels/github.mdx +1 -1
  172. package/docs/channels/linear.mdx +6 -8
  173. package/docs/channels/overview.mdx +2 -2
  174. package/docs/channels/slack.mdx +46 -15
  175. package/docs/channels/telegram.mdx +1 -1
  176. package/docs/channels/twilio.mdx +1 -1
  177. package/docs/concepts/default-harness.md +11 -3
  178. package/docs/concepts/execution-model-and-durability.mdx +17 -5
  179. package/docs/concepts/sessions-runs-and-streaming.md +42 -16
  180. package/docs/evals/cases.mdx +3 -3
  181. package/docs/evals/targets.mdx +1 -1
  182. package/docs/guides/auth-and-route-protection.md +2 -1
  183. package/docs/guides/client/continuations.mdx +50 -77
  184. package/docs/guides/client/messages.mdx +29 -33
  185. package/docs/guides/client/output-schema.mdx +7 -14
  186. package/docs/guides/client/overview.mdx +40 -11
  187. package/docs/guides/client/streaming.mdx +17 -23
  188. package/docs/guides/frontend/nuxt.mdx +1 -1
  189. package/docs/guides/frontend/overview.mdx +40 -36
  190. package/docs/guides/frontend/sveltekit.mdx +1 -1
  191. package/docs/guides/frontend/use-eve-agent-svelte.mdx +19 -23
  192. package/docs/guides/frontend/use-eve-agent-vue.mdx +21 -25
  193. package/docs/guides/instrumentation.md +1 -1
  194. package/docs/patterns/dynamic-scheduling.md +16 -15
  195. package/docs/schedules.mdx +10 -11
  196. package/docs/subagents.mdx +1 -1
  197. package/docs/tools/human-in-the-loop.md +1 -1
  198. package/docs/tutorial/ship-it.mdx +1 -1
  199. package/package.json +1 -1
  200. package/dist/src/channel/cancel.d.ts +0 -6
  201. package/dist/src/channel/cancel.js +0 -1
  202. package/dist/src/channel/clear-session.d.ts +0 -4
  203. package/dist/src/channel/clear-session.js +0 -1
  204. package/dist/src/channel/compact-session.d.ts +0 -4
  205. package/dist/src/channel/compact-session.js +0 -1
  206. package/dist/src/channel/reset-session.d.ts +0 -6
  207. package/dist/src/channel/reset-session.js +0 -1
  208. package/dist/src/channel/resolve-active-session.d.ts +0 -4
  209. package/dist/src/channel/resolve-active-session.js +0 -1
  210. package/dist/src/channel/send.d.ts +0 -6
  211. package/dist/src/channel/send.js +0 -1
@@ -3,7 +3,7 @@ title: "Messages"
3
3
  description: "Send text, full turn payloads, client context, attachments, and HITL responses with eve/client."
4
4
  ---
5
5
 
6
- `ClientSession` sends one turn at a time. A fresh session starts on the first send; later sends continue the same conversation as long as the previous turn left the session waiting.
6
+ Create a session with its first turn, then use the returned `ClientSession` for follow-ups. Each handle targets one durable session ID.
7
7
 
8
8
  ## Send text
9
9
 
@@ -13,12 +13,12 @@ Pass a string to `send()` for plain text:
13
13
  import { Client } from "eve/client";
14
14
 
15
15
  const client = new Client({ host: "http://127.0.0.1:2000" });
16
- const session = client.session();
17
-
18
- const response = await session.send("What is the weather in Brooklyn?");
16
+ const { session, response } = await client.sessions.create({
17
+ message: "What is the weather in Brooklyn?",
18
+ });
19
19
 
20
20
  // Metadata is available as soon as the POST succeeds.
21
- console.log(response.sessionId, response.continuationToken);
21
+ console.log(response.sessionId);
22
22
 
23
23
  const result = await response.result();
24
24
  console.log(result.status, result.message);
@@ -38,10 +38,10 @@ When the stream includes `session.failed`, the turn returns `status: "failed"` r
38
38
 
39
39
  ## Send a full turn payload
40
40
 
41
- Use `send()` when you need more than plain text:
41
+ Pass the full payload to `create()` for the first turn or `send()` for a follow-up:
42
42
 
43
43
  ```ts
44
- const response = await session.send({
44
+ const { session, response } = await client.sessions.create({
45
45
  message: "What should I do on this screen?",
46
46
  clientContext: {
47
47
  route: "/billing",
@@ -60,17 +60,15 @@ await response.result();
60
60
  `send()` accepts AI SDK `UserContent`, so a message can mix text and file parts:
61
61
 
62
62
  ```ts
63
- const response = await session.send({
64
- message: [
65
- { type: "text", text: "Summarize this report." },
66
- {
67
- type: "file",
68
- data: reportDataUrl,
69
- mediaType: "application/pdf",
70
- filename: "report.pdf",
71
- },
72
- ],
73
- });
63
+ const response = await session.send([
64
+ { type: "text", text: "Summarize this report." },
65
+ {
66
+ type: "file",
67
+ data: reportDataUrl,
68
+ mediaType: "application/pdf",
69
+ filename: "report.pdf",
70
+ },
71
+ ]);
74
72
 
75
73
  await response.result();
76
74
  ```
@@ -83,17 +81,15 @@ import { readFile } from "node:fs/promises";
83
81
  const bytes = await readFile("report.pdf");
84
82
  const reportDataUrl = `data:application/pdf;base64,${bytes.toString("base64")}`;
85
83
 
86
- const response = await session.send({
87
- message: [
88
- { type: "text", text: "Summarize this report." },
89
- {
90
- type: "file",
91
- data: reportDataUrl,
92
- mediaType: "application/pdf",
93
- filename: "report.pdf",
94
- },
95
- ],
96
- });
84
+ const response = await session.send([
85
+ { type: "text", text: "Summarize this report." },
86
+ {
87
+ type: "file",
88
+ data: reportDataUrl,
89
+ mediaType: "application/pdf",
90
+ filename: "report.pdf",
91
+ },
92
+ ]);
97
93
 
98
94
  await response.result();
99
95
  ```
@@ -119,17 +115,17 @@ for await (const event of response) {
119
115
  }
120
116
  }
121
117
 
122
- const resumed = await session.send({
123
- inputResponses: pendingRequests.map((request) => ({
118
+ const resumed = await session.respond(
119
+ pendingRequests.map((request) => ({
124
120
  requestId: request.requestId,
125
121
  optionId: "approve",
126
122
  })),
127
- });
123
+ );
128
124
 
129
125
  await resumed.result();
130
126
  ```
131
127
 
132
- You can send `message`, `inputResponses`, and `clientContext` together when the resumed turn needs both a human answer and follow-up text.
128
+ `send(message, options)` and `respond(inputResponses, options)` are separate operations. Put `clientContext`, `outputSchema`, headers, or stream options in the second argument to either method.
133
129
 
134
130
  ## Single-use responses
135
131
 
@@ -27,9 +27,7 @@ const outputSchema = {
27
27
  } as const;
28
28
 
29
29
  const client = new Client({ host: "http://127.0.0.1:2000" });
30
- const session = client.session();
31
-
32
- const response = await session.send<Summary>({
30
+ const { session, response } = await client.sessions.create<Summary>({
33
31
  message: "Summarize this turn.",
34
32
  outputSchema,
35
33
  });
@@ -56,8 +54,7 @@ const summarySchema = z.object({
56
54
 
57
55
  type Summary = z.infer<typeof summarySchema>;
58
56
 
59
- const response = await session.send<Summary>({
60
- message: "Summarize this turn.",
57
+ const response = await session.send<Summary>("Summarize this turn.", {
61
58
  outputSchema: summarySchema,
62
59
  });
63
60
 
@@ -71,8 +68,7 @@ The server is authoritative for validation. The client types `MessageResult.data
71
68
  If you consume events manually, read `result.completed`:
72
69
 
73
70
  ```ts
74
- const response = await session.send<Summary>({
75
- message: "Summarize this turn.",
71
+ const response = await session.send<Summary>("Summarize this turn.", {
76
72
  outputSchema,
77
73
  });
78
74
 
@@ -88,11 +84,10 @@ If more than one `result.completed` appears in the consumed event list, `result(
88
84
 
89
85
  ## Send payloads with output schema
90
86
 
91
- `outputSchema` works with string shorthand and object-form sends. Use object form when you need schema, headers, signal, context, attachments, or HITL responses:
87
+ Pass `outputSchema` in the second argument to `send()` or `respond()` alongside headers, signal, or client context:
92
88
 
93
89
  ```ts
94
- const response = await session.send<Summary>({
95
- message: "Summarize this PDF.",
90
+ const response = await session.send<Summary>("Summarize this PDF.", {
96
91
  clientContext: { reportId: "rpt_123" },
97
92
  outputSchema,
98
93
  });
@@ -103,9 +98,7 @@ const result = await response.result();
103
98
  It also works on follow-up turns and HITL response turns:
104
99
 
105
100
  ```ts
106
- const response = await session.send({
107
- inputResponses: [{ requestId, optionId: "approve" }],
108
- message: "Return the approved action as structured output.",
101
+ const response = await session.respond([{ requestId, optionId: "approve" }], {
109
102
  outputSchema,
110
103
  });
111
104
 
@@ -117,7 +110,7 @@ const result = await response.result();
117
110
  Client `outputSchema` is scoped to the turn that sends it. It doesn't become a permanent setting for the conversation:
118
111
 
119
112
  ```ts
120
- const response = await session.send({ message: "Return a structured summary.", outputSchema });
113
+ const response = await session.send("Return a structured summary.", { outputSchema });
121
114
  await response.result();
122
115
 
123
116
  const followUpResponse = await session.send("Now answer normally.");
@@ -100,8 +100,7 @@ Set `redirect` to `"manual"` or `"error"` on credential-bearing clients so fetch
100
100
  Per-request headers can be attached to an individual turn:
101
101
 
102
102
  ```ts
103
- const response = await session.send({
104
- message: "Run the check.",
103
+ const response = await session.send("Run the check.", {
105
104
  headers: { "x-request-id": requestId },
106
105
  });
107
106
 
@@ -112,23 +111,53 @@ Per-request headers override client-level values with the same name. For example
112
111
 
113
112
  ## Sessions
114
113
 
115
- Create a `ClientSession` for each conversation:
114
+ For an ID-addressed session, create it explicitly with the first message:
116
115
 
117
116
  ```ts
118
- const session = client.session();
117
+ const { session, response } = await client.sessions.create({ message: "Summarize account A." });
118
+ await response.result();
119
+
120
+ await (await session.send("Now list the risks.")).result();
121
+ await session.compact();
122
+ await session.clear();
123
+ ```
124
+
125
+ If you already know the durable ID, attach a fixed handle without performing I/O:
126
+
127
+ ```ts
128
+ const session = client.sessions.attach("wrun_A");
119
129
  ```
120
130
 
121
- A client can own many sessions at once. Each session tracks its own `sessionId`, `continuationToken`, and stream cursor:
131
+ The fixed client handle exposes the full lifecycle around that ID. The calls below show the available shapes independently:
122
132
 
123
133
  ```ts
124
- const alice = client.session();
125
- const bob = client.session();
134
+ const response = await session.send("Continue the analysis.");
135
+ await response.result();
136
+
137
+ await session.cancel({ turnId: "turn_123" });
138
+ await session.compact();
139
+ await session.clear();
126
140
 
127
- const aliceResponse = await alice.send("Summarize account A.");
128
- await aliceResponse.result();
141
+ for await (const event of session.stream({ follow: false })) {
142
+ console.log(event.type);
143
+ }
144
+
145
+ await session.reset({ reason: "User requested a fresh session" });
146
+ ```
147
+
148
+ `client.sessions` stores only the session ID and stream cursor. Every method calls an ID-addressed `/eve/v1/session/:sessionId/...` route. Sending through a handle for an unknown or terminal ID fails instead of creating a replacement, and reset leaves the handle pinned to the retired ID.
149
+
150
+ A client can own many independent fixed sessions at once:
151
+
152
+ ```ts
153
+ const { session: alice, response: aliceResponse } = await client.sessions.create({
154
+ message: "Summarize account A.",
155
+ });
156
+ const { session: bob, response: bobResponse } = await client.sessions.create({
157
+ message: "Summarize account B.",
158
+ });
129
159
 
130
- const bobResponse = await bob.send("Summarize account B.");
131
- await bobResponse.result();
160
+ await Promise.all([aliceResponse.result(), bobResponse.result()]);
132
161
  ```
133
162
 
134
163
  The next pages cover the session lifecycle:
@@ -5,18 +5,22 @@ description: "Consume eve client stream events live, reconnect by event index, a
5
5
 
6
6
  Every `ClientSession.send()` call posts the turn, then reads the session's NDJSON (newline-delimited JSON) event stream. `MessageResponse` gives you two ways to consume that stream, aggregating it with `result()` or iterating it live.
7
7
 
8
- Once `send()` is accepted, the session exposes its assigned `sessionId` and can request cooperative cancellation with `session.cancel()`, even while the response stream is still running. The result status is `accepted` when the active turn accepted the signal or `no_active_turn` when it already settled:
8
+ Once `send()` is accepted, the session exposes its assigned `sessionId` and can request cooperative cancellation with `session.cancel()`, even while the response stream is still running. The result status is `accepted` when the live session durably queued the command, including when a parked session consumes it as a no-op. An unknown or terminal session returns `no_active_turn`:
9
9
 
10
10
  ```ts
11
- const session = client.session();
12
- const response = await session.send("Run the long operation.");
11
+ const { session, response } = await client.sessions.create({ message: "Run the long operation." });
13
12
 
14
13
  const cancellation = await session.cancel();
15
- console.log(cancellation.status);
14
+ if (cancellation.status === "accepted") {
15
+ console.log(cancellation.sessionId);
16
+ }
16
17
 
17
18
  const result = await response.result();
18
19
  ```
19
20
 
21
+ The cancellation result is discriminated by `status`: only `accepted` includes
22
+ `sessionId`; `no_active_turn` has no session identity field.
23
+
20
24
  Cancellation does not replace stream consumption. Continue reading the response to observe its terminal `turn.cancelled` and `session.waiting` boundary and to advance the client session cursor normally.
21
25
 
22
26
  Between turns, `session.compact()` queues context compaction without sending model input. An accepted request reports the session id; consume the durable stream through the following `session.waiting` boundary before sending the next turn. `compaction.completed` confirms that summarization succeeded; without it, eve preserves the previous history. A never-started session returns `no_active_session` as a successful no-op.
@@ -103,7 +107,7 @@ The most common UI events are:
103
107
  | `action.result` | Show tool call results. |
104
108
  | `input.requested` | Pause the UI for approval or a question answer. |
105
109
  | `result.completed` | Read structured output from an [output schema](./output-schema). |
106
- | `session.waiting` | Enable the composer and read the current `data.continuationToken`. |
110
+ | `session.waiting` | Enable the composer; the same fixed session handle accepts the next message. |
107
111
  | `session.completed` | Mark the conversation terminal. |
108
112
  | `session.failed` | Mark the conversation failed. |
109
113
 
@@ -129,8 +133,7 @@ If your consumer persists events, key on `event.meta.id`. It is stable across re
129
133
  Set `streamReconnectPolicy: { reconnect: false }` when a relay or proxy owns the cursor and reconnection policy. This makes a single stream GET attempt and returns when that connection ends; it does not stop the server-side turn:
130
134
 
131
135
  ```ts
132
- const response = await session.send({
133
- message: "Run the long operation.",
136
+ const response = await session.send("Run the long operation.", {
134
137
  streamReconnectPolicy: { reconnect: false },
135
138
  });
136
139
 
@@ -146,9 +149,7 @@ The same option is available on manual attachments as `session.stream({ streamRe
146
149
  Use `session.stream()` when you already have a session cursor and only need to attach to the existing stream:
147
150
 
148
151
  ```ts
149
- const session = client.session({
150
- continuationToken: "eve:6c8b1f2e-3d4a-4b9c-8e21-9f0a1b2c3d4e",
151
- sessionId: "wrun_01ARYZ6S41TSV4RRFFQ69G5FAV",
152
+ const session = client.sessions.attach("wrun_01ARYZ6S41TSV4RRFFQ69G5FAV", {
152
153
  streamIndex: 10,
153
154
  });
154
155
 
@@ -169,9 +170,7 @@ Nonnegative values are absolute event indexes. Negative values read relative to
169
170
 
170
171
  ```ts
171
172
  for await (const event of session.stream({ startIndex: -1 })) {
172
- if (event.type === "session.waiting") {
173
- console.log(event.data.continuationToken);
174
- }
173
+ console.log(event.type);
175
174
  break;
176
175
  }
177
176
  ```
@@ -193,15 +192,13 @@ The first connection pins the bound to the tail the server reports at open time;
193
192
 
194
193
  Because a tail-relative cursor cannot be bounded, `follow: false` throws when combined with a negative `startIndex`. It also fails if the server does not report the durable tail (an agent running an older eve version).
195
194
 
196
- `stream()` throws if the session has no `sessionId`, because there's no stream to attach to before the first send.
197
-
198
195
  ## Snapshot a session
199
196
 
200
197
  Use `snapshot()` when you need the complete event prefix and its matching cursor
201
198
  as one value, such as when hydrating a server-rendered chat:
202
199
 
203
200
  ```ts
204
- const session = client.session({ sessionId, streamIndex: 0 });
201
+ const session = client.sessions.attach(sessionId);
205
202
  const snapshot = await session.snapshot();
206
203
 
207
204
  // snapshot.events contains event indexes 0 through snapshot.session.streamIndex - 1.
@@ -213,11 +210,9 @@ The read pins the durable tail when it opens, just like
213
210
  continue. `snapshot()` always reads from index `0`; use a bounded stream directly
214
211
  when you only need the unread suffix from an existing cursor.
215
212
 
216
- Unlike `stream()`, `snapshot()` does not advance or reset the originating
217
- `ClientSession`. Its returned session includes a continuation token only when
218
- the captured prefix ends in `session.waiting`, or when completed sessions are
219
- explicitly preserved by the client. Active and terminal snapshots remain
220
- inspectable at their returned stream cursor but cannot accept another turn.
213
+ Unlike `stream()`, `snapshot()` does not advance the originating
214
+ `ClientSession`. Its returned cursor contains the fixed session ID and the exact
215
+ stream index after the captured prefix.
221
216
 
222
217
  ## Abort a request
223
218
 
@@ -229,8 +224,7 @@ Arm the timeout before awaiting `send()` so it covers the POST as well as the st
229
224
  const controller = new AbortController();
230
225
  const timeout = setTimeout(() => controller.abort(), 10_000);
231
226
 
232
- const response = await session.send({
233
- message: "Run a long analysis.",
227
+ const response = await session.send("Run a long analysis.", {
234
228
  signal: controller.signal,
235
229
  });
236
230
 
@@ -48,7 +48,7 @@ async function handleSubmit() {
48
48
  const text = message.value.trim();
49
49
  if (!text || isBusy.value) return;
50
50
  message.value = "";
51
- await send({ message: text });
51
+ await send(text);
52
52
  }
53
53
  </script>
54
54
 
@@ -36,7 +36,7 @@ export function Chat() {
36
36
  const form = new FormData(event.currentTarget);
37
37
  const message = String(form.get("message") ?? "").trim();
38
38
  if (message.length > 0) {
39
- void agent.send({ message });
39
+ void agent.send(message);
40
40
  }
41
41
  }}
42
42
  >
@@ -61,16 +61,17 @@ export function Chat() {
61
61
 
62
62
  `useEveAgent()` returns the current UI state plus commands:
63
63
 
64
- | Field | What it is |
65
- | --------- | ------------------------------------------------------------------------------ |
66
- | `data` | Projected UI state from the reducer. Defaults to `{ messages }`. |
67
- | `status` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"`. Drives the composer. |
68
- | `error` | The last `Error` thrown, if any. |
69
- | `events` | Raw eve stream events for this session. |
70
- | `session` | Serializable session cursor (`sessionId`, `continuationToken`, `streamIndex`). |
71
- | `send` | Send text or the full turn payload (multi-part messages, HITL responses). |
72
- | `stop` | Abort the client's in-flight stream. The server-side turn keeps running. |
73
- | `reset` | Clear local events, data, errors, and the local session cursor. |
64
+ | Field | What it is |
65
+ | --------- | --------------------------------------------------------------------------- |
66
+ | `data` | Projected UI state from the reducer. Defaults to `{ messages }`. |
67
+ | `status` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"`. Drives the composer. |
68
+ | `error` | The last `Error` thrown, if any. |
69
+ | `events` | Raw eve stream events for this session. |
70
+ | `session` | Serializable fixed session cursor (`sessionId`, `streamIndex`). |
71
+ | `send` | Send text or a multi-part message, with per-turn options. |
72
+ | `respond` | Answer pending HITL input requests, with per-turn options. |
73
+ | `stop` | Abort the client's in-flight stream. The server-side turn keeps running. |
74
+ | `reset` | Clear local events, data, errors, and the local session cursor. |
74
75
 
75
76
  Most chat UIs only need `data.messages` and `status`. Drop down to `events` when you need the authoritative wire events directly, for example to persist an audit log or build a custom projection.
76
77
 
@@ -78,29 +79,31 @@ Most chat UIs only need `data.messages` and `status`. Drop down to `events` when
78
79
 
79
80
  ## Sending and streaming
80
81
 
81
- Pass an object to `send()` for text, multi-part messages, attachments, HITL responses, and per-turn context:
82
+ Pass the message first and optional per-turn settings second. Use `respond()` for HITL answers:
82
83
 
83
84
  ```tsx
84
- await agent.send({ message: "Summarize this session." });
85
-
86
- await agent.send({
87
- message: [
88
- { type: "text", text: "What is in this file?" },
89
- {
90
- type: "file",
91
- data: fileDataUrl, // base64 data URL
92
- mediaType: "application/pdf",
93
- filename: "report.pdf",
94
- },
95
- ],
96
- });
85
+ await agent.send("Summarize this session.");
86
+
87
+ await agent.send([
88
+ { type: "text", text: "What is in this file?" },
89
+ {
90
+ type: "file",
91
+ data: fileDataUrl, // base64 data URL
92
+ mediaType: "application/pdf",
93
+ filename: "report.pdf",
94
+ },
95
+ ]);
97
96
  ```
98
97
 
99
98
  Assistant text, reasoning, tool calls, and tool results stream into `data` as they arrive, and `status` moves from `ready` to `submitted` to `streaming` and back. Call `stop()` to abort the client's in-flight stream, and `reset()` to clear local state so the next send starts a fresh durable session.
100
99
 
101
100
  `stop()` is local: turns are resumable across disconnects, so detaching the stream never cancels the work — the turn keeps running and billing on the server. To stop the turn itself, POST the session's [cancel route](../../channels/eve#routes); the turn then settles on the stream as `turn.cancelled` followed by `session.waiting`.
102
101
 
103
- A chat UI with a Stop button should own the `ClientSession` passed to `useEveAgent`, create it with `preserveCompletedSessions: true`, and call `session.cancel({ turnId })` after observing that turn's `turn.started` event. The `turnId` guard makes a late click a harmless no-op instead of cancelling a newer turn. Keep the stream open until the cancellation boundary arrives; do not use `stop()` for this interaction.
102
+ A chat UI with a server-side Cancel button can use the `sessionId` exposed by the
103
+ hook, attach a fixed `ClientSession`, and call `session.cancel({ turnId })` after
104
+ observing that turn's `turn.started` event. The `turnId` guard makes a late click
105
+ a harmless no-op instead of cancelling a newer turn. Keep the stream open until
106
+ the cancellation boundary arrives; do not use `stop()` for this interaction.
104
107
 
105
108
  After eve confirms an attachment turn with `message.received`, the default reducer projects each
106
109
  received attachment as a `file` part on the user message. The part includes `mediaType`, optional
@@ -108,7 +111,7 @@ received attachment as a `file` part on the user message. The part includes `med
108
111
 
109
112
  ## Human-in-the-loop prompts
110
113
 
111
- Tools opt into approval with `approval`, and the model can also ask a question with `ask_question` — see [Human-in-the-loop](/docs/human-in-the-loop) for the server-side model. Either way the stream emits an `input.requested` event, and the pending request rides on a `dynamic-tool` part of the latest message at `part.toolMetadata?.eve?.inputRequest`. Read it, then answer through the same session with `send()`:
114
+ Tools opt into approval with `approval`, and the model can also ask a question with `ask_question` — see [Human-in-the-loop](/docs/human-in-the-loop) for the server-side model. Either way the stream emits an `input.requested` event, and the pending request rides on a `dynamic-tool` part of the latest message at `part.toolMetadata?.eve?.inputRequest`. Read it, then answer through the same session with `respond()`:
112
115
 
113
116
  ```tsx
114
117
  const request = agent.data.messages
@@ -117,9 +120,7 @@ const request = agent.data.messages
117
120
  ?.toolMetadata?.eve?.inputRequest;
118
121
 
119
122
  if (request) {
120
- await agent.send({
121
- inputResponses: [{ requestId: request.requestId, optionId: "approve" }],
122
- });
123
+ await agent.respond([{ requestId: request.requestId, optionId: "approve" }]);
123
124
  }
124
125
  ```
125
126
 
@@ -159,11 +160,10 @@ For fully custom state machines, `authorization.required` and `authorization.com
159
160
 
160
161
  ## Attach page context per turn
161
162
 
162
- `clientContext` adds ephemeral context for the next model call only. Strings (or an array of strings) become user-role context messages; an object is JSON-serialized into one. It rides along with a message or HITL response, so it never dispatches a turn on its own and never lands in durable session history. Pass it directly to `send()`:
163
+ `clientContext` adds ephemeral context for the next model call only. Strings (or an array of strings) become user-role context messages; an object is JSON-serialized into one. It rides along with a message or HITL response, so it never dispatches a turn on its own and never lands in durable session history. Pass it in the second argument to `send()` or `respond()`:
163
164
 
164
165
  ```tsx
165
- await agent.send({
166
- message: "What should I do on this screen?",
166
+ await agent.send("What should I do on this screen?", {
167
167
  clientContext: { route: "/billing", plan: "pro", seatsUsed: 4 },
168
168
  });
169
169
  ```
@@ -226,11 +226,11 @@ const agent = useEveAgent({ reducer: toolCounter });
226
226
  The browser conversation lives durably on the server. Persist both the rendered event log and the `session` cursor to pick it back up after a reload:
227
227
 
228
228
  ```tsx
229
- import type { SessionState, MessageStreamEvent } from "eve/client";
229
+ import type { ClientSessionState, MessageStreamEvent } from "eve/client";
230
230
 
231
231
  type SavedEveChat = {
232
232
  events?: readonly MessageStreamEvent[];
233
- session?: SessionState;
233
+ session?: ClientSessionState;
234
234
  };
235
235
 
236
236
  const [saved] = useState<SavedEveChat>(() => {
@@ -253,7 +253,11 @@ const agent = useEveAgent({
253
253
  });
254
254
  ```
255
255
 
256
- Store the full `session` object (`sessionId`, `continuationToken`, `streamIndex`), not a single field. The session cursor lets eve continue the durable conversation; the event log lets your UI render historical messages without replaying the whole stream. A database-backed chat app should usually persist stream events as they arrive with `onEvent` and then save a final snapshot in `onFinish`.
256
+ Store the full `session` object (`sessionId`, `streamIndex`). The session cursor
257
+ lets eve continue the exact durable conversation; the event log lets your UI
258
+ render historical messages without replaying the whole stream. A database-backed
259
+ chat app should usually persist stream events as they arrive with `onEvent` and
260
+ then save a final snapshot in `onFinish`.
257
261
 
258
262
  `initialEvents` must be an ordered prefix of the same session's stream, but its endpoint does not have to line up exactly with where the stream resumes. Every event carries a stable [`meta.id`](/docs/concepts/sessions-runs-and-streaming#the-event-envelope), and the store drops any event whose id it has already applied — so a saved log that overlaps the replayed prefix renders once, and `onEvent` only fires for events your UI has not seen.
259
263
 
@@ -56,7 +56,7 @@ With the plugin in `vite.config.ts`, components call [`useEveAgent`](./use-eve-a
56
56
  const text = message.trim();
57
57
  if (!text || isBusy) return;
58
58
  message = "";
59
- await agent.send({ message: text });
59
+ await agent.send(text);
60
60
  }
61
61
  </script>
62
62
 
@@ -23,16 +23,17 @@ Import from `eve/svelte` and read the reactive getters directly. No `$` prefix:
23
23
 
24
24
  ## What it returns
25
25
 
26
- | Property | Type | Description |
27
- | --------- | ------------------------------------------- | ------------------------------------------------------------------------- |
28
- | `data` | `TData` | Projected state. With the default reducer, `EveMessageData` (`messages`). |
29
- | `status` | `UseEveAgentStatus` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"`. |
30
- | `error` | `Error \| undefined` | Last transport-level error. |
31
- | `events` | `readonly MessageStreamEvent[]` | Raw server events for this session. |
32
- | `session` | `SessionState` | Snapshot of session state. |
33
- | `send` | `(input: SendTurnPayload) => Promise<void>` | Send text or a full turn (multi-part, attachments, HITL responses). |
34
- | `stop` | `() => void` | Abort the client's in-flight stream. |
35
- | `reset` | `() => void` | Clear state and start a new session. |
26
+ | Property | Type | Description |
27
+ | --------- | --------------------------------------------- | ------------------------------------------------------------------------- |
28
+ | `data` | `TData` | Projected state. With the default reducer, `EveMessageData` (`messages`). |
29
+ | `status` | `UseEveAgentStatus` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"`. |
30
+ | `error` | `Error \| undefined` | Last transport-level error. |
31
+ | `events` | `readonly MessageStreamEvent[]` | Raw server events for this session. |
32
+ | `session` | `ClientSessionState \| undefined` | Fixed session ID and stream cursor. |
33
+ | `send` | `(message, options?) => Promise<void>` | Send text or a multi-part message. |
34
+ | `respond` | `(inputResponses, options?) => Promise<void>` | Answer pending HITL input requests. |
35
+ | `stop` | `() => void` | Abort the client's in-flight stream. |
36
+ | `reset` | `() => void` | Clear state and start a new session. |
36
37
 
37
38
  These state fields are reactive getters, so read them straight from templates, `$derived`, or `$effect`. They are not stores, so don't prefix them with `$`.
38
39
 
@@ -49,7 +50,7 @@ These state fields are reactive getters, so read them straight from templates, `
49
50
  const text = message.trim();
50
51
  if (!text) return;
51
52
  message = "";
52
- await agent.send({ message: text });
53
+ await agent.send(text);
53
54
  }
54
55
  </script>
55
56
 
@@ -68,17 +69,15 @@ When a turn is more than plain text, reach for `send()`. Attachments follow the
68
69
  const bytes = new Uint8Array(await file.arrayBuffer());
69
70
  const base64 = btoa(String.fromCodePoint(...bytes));
70
71
 
71
- await agent.send({
72
- message: [
73
- { type: "text", text: "Describe this image." },
74
- { type: "file", data: `data:${file.type};base64,${base64}`, mediaType: file.type },
75
- ],
76
- });
72
+ await agent.send([
73
+ { type: "text", text: "Describe this image." },
74
+ { type: "file", data: `data:${file.type};base64,${base64}`, mediaType: file.type },
75
+ ]);
77
76
  ```
78
77
 
79
78
  ## Human-in-the-loop prompts
80
79
 
81
- A tool opts into approval with `approval` ([Tools](../../tools)). When one fires, the pending request rides along on a `dynamic-tool` part of the latest message at `part.toolMetadata?.eve?.inputRequest`. Read it, then answer through the same session with `agent.send({ inputResponses })`:
80
+ A tool opts into approval with `approval` ([Tools](../../tools)). When one fires, the pending request rides along on a `dynamic-tool` part of the latest message at `part.toolMetadata?.eve?.inputRequest`. Read it, then answer through the same session with `agent.respond(inputResponses)`:
82
81
 
83
82
  ```ts
84
83
  import type { EveDynamicToolPart, EveMessagePart } from "eve/svelte";
@@ -93,9 +92,7 @@ const request = agent.data.messages
93
92
  .find((value) => value !== undefined);
94
93
 
95
94
  if (request) {
96
- await agent.send({
97
- inputResponses: [{ requestId: request.requestId, optionId: "approve" }],
98
- });
95
+ await agent.respond([{ requestId: request.requestId, optionId: "approve" }]);
99
96
  }
100
97
  ```
101
98
 
@@ -133,8 +130,7 @@ const agent = useEveAgent({
133
130
  `clientContext` adds ephemeral context for the next model call and nothing more. Strings (or an array of strings) become user-role context messages; an object is JSON-serialized into one. It rides along with a message or HITL response, never dispatches a turn on its own, and never lands in durable session history. Pass it to `send()`:
134
131
 
135
132
  ```ts
136
- await agent.send({
137
- message: "What should I do on this screen?",
133
+ await agent.send("What should I do on this screen?", {
138
134
  clientContext: { route: "/billing", plan: "pro", seatsUsed: 4 },
139
135
  });
140
136
  ```