eve 0.19.0 → 0.21.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 (206) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/src/channel/types.d.ts +9 -5
  3. package/dist/src/chunks/{use-eve-agent-Dvy_Wjvq.js → use-eve-agent-CM24ZUEI.js} +48 -34
  4. package/dist/src/chunks/{use-eve-agent-Bh1NmXO9.js → use-eve-agent-CxcKypLZ.js} +48 -34
  5. package/dist/src/cli/dev/tui/setup-selection-input.js +1 -1
  6. package/dist/src/cli/dev/tui/stream-format.d.ts +4 -0
  7. package/dist/src/cli/dev/tui/stream-format.js +1 -1
  8. package/dist/src/cli/dev/tui/terminal-renderer.js +1 -1
  9. package/dist/src/cli/dev/tui/test/mock-terminal.d.ts +2 -0
  10. package/dist/src/cli/dev/tui/test/mock-terminal.js +1 -1
  11. package/dist/src/client/index.d.ts +1 -1
  12. package/dist/src/client/message-reducer-types.d.ts +11 -3
  13. package/dist/src/client/message-reducer.js +1 -1
  14. package/dist/src/compiler/manifest.js +1 -1
  15. package/dist/src/compiler/normalize-agent-config.js +1 -1
  16. package/dist/src/compiler/workspace-resources.js +1 -1
  17. package/dist/src/context/build-base-tool-context.d.ts +9 -0
  18. package/dist/src/context/build-base-tool-context.js +1 -0
  19. package/dist/src/context/build-dynamic-tools.js +1 -1
  20. package/dist/src/context/dynamic-skill-lifecycle.js +1 -1
  21. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  22. package/dist/src/context/keys.d.ts +0 -1
  23. package/dist/src/context/keys.js +1 -1
  24. package/dist/src/discover/sandbox.d.ts +0 -6
  25. package/dist/src/discover/sandbox.js +1 -1
  26. package/dist/src/evals/cli/eval.js +4 -1
  27. package/dist/src/evals/runner/discover.d.ts +10 -0
  28. package/dist/src/evals/runner/discover.js +1 -1
  29. package/dist/src/execution/create-session-step.d.ts +2 -1
  30. package/dist/src/execution/create-session-step.js +1 -1
  31. package/dist/src/execution/delegated-parent-notification.d.ts +6 -0
  32. package/dist/src/execution/delegated-parent-notification.js +1 -1
  33. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  34. package/dist/src/execution/dispatch-workflow-runtime-actions-step.js +1 -1
  35. package/dist/src/execution/durable-session-migrations/turn-workflow.d.ts +2 -0
  36. package/dist/src/execution/durable-session-store.d.ts +1 -0
  37. package/dist/src/execution/next-driver-action.d.ts +3 -0
  38. package/dist/src/execution/node-step.d.ts +8 -0
  39. package/dist/src/execution/node-step.js +1 -1
  40. package/dist/src/execution/run-session-limits.d.ts +21 -0
  41. package/dist/src/execution/run-session-limits.js +1 -0
  42. package/dist/src/execution/runtime-context.js +1 -1
  43. package/dist/src/execution/sandbox/abort-bound-session.d.ts +6 -0
  44. package/dist/src/execution/sandbox/abort-bound-session.js +1 -0
  45. package/dist/src/execution/sandbox/active-handles.d.ts +29 -0
  46. package/dist/src/execution/sandbox/active-handles.js +1 -0
  47. package/dist/src/execution/sandbox/bindings/docker.d.ts +2 -2
  48. package/dist/src/execution/sandbox/bindings/docker.js +1 -1
  49. package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
  50. package/dist/src/execution/sandbox/bindings/local-backend-utils.d.ts +1 -1
  51. package/dist/src/execution/sandbox/bindings/local-backend-utils.js +1 -1
  52. package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
  53. package/dist/src/execution/sandbox/bindings/microsandbox-provider-state.d.ts +11 -0
  54. package/dist/src/execution/sandbox/bindings/microsandbox-provider-state.js +1 -0
  55. package/dist/src/execution/sandbox/bindings/microsandbox-runtime.d.ts +7 -4
  56. package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
  57. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  58. package/dist/src/execution/sandbox/ensure.js +1 -1
  59. package/dist/src/execution/sandbox/prewarm.js +1 -1
  60. package/dist/src/execution/sandbox/require-sandbox.d.ts +3 -1
  61. package/dist/src/execution/sandbox/require-sandbox.js +1 -1
  62. package/dist/src/execution/session-callback-step.d.ts +6 -0
  63. package/dist/src/execution/session-callback-step.js +1 -1
  64. package/dist/src/execution/session.d.ts +14 -3
  65. package/dist/src/execution/session.js +2 -4
  66. package/dist/src/execution/skills/instructions.d.ts +8 -4
  67. package/dist/src/execution/skills/instructions.js +2 -2
  68. package/dist/src/execution/subagent-tool.d.ts +7 -0
  69. package/dist/src/execution/subagent-tool.js +1 -1
  70. package/dist/src/execution/subagent-usage-span.d.ts +9 -0
  71. package/dist/src/execution/subagent-usage-span.js +1 -0
  72. package/dist/src/execution/tool-auth.d.ts +2 -1
  73. package/dist/src/execution/tool-auth.js +1 -1
  74. package/dist/src/execution/turn-execution-cursor.d.ts +2 -0
  75. package/dist/src/execution/turn-workflow.js +1 -1
  76. package/dist/src/execution/web-fetch/tool.d.ts +6 -1
  77. package/dist/src/execution/web-fetch/tool.js +1 -1
  78. package/dist/src/execution/workflow-entry.d.ts +1 -0
  79. package/dist/src/execution/workflow-entry.js +1 -1
  80. package/dist/src/execution/workflow-runtime.js +1 -1
  81. package/dist/src/execution/workflow-skill-root.d.ts +6 -0
  82. package/dist/src/execution/workflow-skill-root.js +1 -0
  83. package/dist/src/execution/workflow-steps.d.ts +3 -0
  84. package/dist/src/execution/workflow-steps.js +1 -1
  85. package/dist/src/harness/advertised-tools.d.ts +1 -0
  86. package/dist/src/harness/advertised-tools.js +1 -1
  87. package/dist/src/harness/attachment-staging.js +1 -1
  88. package/dist/src/harness/compaction.d.ts +1 -1
  89. package/dist/src/harness/compaction.js +1 -1
  90. package/dist/src/harness/emission.js +1 -1
  91. package/dist/src/harness/execute-tool.d.ts +2 -1
  92. package/dist/src/harness/input-requests.d.ts +8 -0
  93. package/dist/src/harness/input-requests.js +1 -1
  94. package/dist/src/harness/model-call-error.js +1 -1
  95. package/dist/src/harness/runtime-actions.js +1 -1
  96. package/dist/src/harness/session-limit-continuation.d.ts +55 -0
  97. package/dist/src/harness/session-limit-continuation.js +1 -0
  98. package/dist/src/harness/session-limit-enforcement.d.ts +55 -0
  99. package/dist/src/harness/session-limit-enforcement.js +1 -0
  100. package/dist/src/harness/step-hooks.d.ts +1 -1
  101. package/dist/src/harness/step-hooks.js +1 -1
  102. package/dist/src/harness/subagent-depth.d.ts +1 -4
  103. package/dist/src/harness/subagent-depth.js +1 -1
  104. package/dist/src/harness/subagent-token-budget.d.ts +14 -0
  105. package/dist/src/harness/subagent-token-budget.js +1 -0
  106. package/dist/src/harness/tool-loop.js +1 -1
  107. package/dist/src/harness/tools.d.ts +2 -3
  108. package/dist/src/harness/tools.js +1 -1
  109. package/dist/src/harness/turn-cancellation.d.ts +8 -0
  110. package/dist/src/harness/turn-cancellation.js +1 -0
  111. package/dist/src/harness/turn-tag-state.d.ts +33 -0
  112. package/dist/src/harness/turn-tag-state.js +1 -1
  113. package/dist/src/harness/types.d.ts +23 -4
  114. package/dist/src/harness/workflow-sandbox.d.ts +1 -0
  115. package/dist/src/harness/workflow-sandbox.js +1 -1
  116. package/dist/src/harness/workflow-subagent-limit.d.ts +38 -0
  117. package/dist/src/harness/workflow-subagent-limit.js +1 -0
  118. package/dist/src/harness/workflow-tool-description.d.ts +6 -1
  119. package/dist/src/harness/workflow-tool-description.js +5 -3
  120. package/dist/src/internal/application/package.js +1 -1
  121. package/dist/src/internal/authored-definition/core.js +1 -1
  122. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  123. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
  124. package/dist/src/internal/nitro/host/sandbox-shutdown-plugin.d.ts +40 -0
  125. package/dist/src/internal/nitro/host/sandbox-shutdown-plugin.js +1 -0
  126. package/dist/src/internal/nitro/host/start-production-server.js +1 -1
  127. package/dist/src/protocol/message.d.ts +31 -2
  128. package/dist/src/protocol/message.js +2 -2
  129. package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +34 -3
  130. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
  131. package/dist/src/public/channels/chat-sdk/index.d.ts +2 -0
  132. package/dist/src/public/channels/chat-sdk/index.js +1 -1
  133. package/dist/src/public/channels/chat-sdk/messageToUserContent.d.ts +13 -0
  134. package/dist/src/public/channels/chat-sdk/messageToUserContent.js +1 -0
  135. package/dist/src/public/channels/chat-sdk/notImplemented.d.ts +7 -0
  136. package/dist/src/public/channels/chat-sdk/notImplemented.js +1 -0
  137. package/dist/src/public/channels/slack/api.d.ts +14 -12
  138. package/dist/src/public/channels/slack/api.js +1 -1
  139. package/dist/src/public/definitions/approval.d.ts +4 -1
  140. package/dist/src/public/definitions/tool.d.ts +44 -0
  141. package/dist/src/public/definitions/tool.js +1 -1
  142. package/dist/src/public/tools/index.d.ts +1 -1
  143. package/dist/src/public/tools/index.js +1 -1
  144. package/dist/src/public/tools/internal.d.ts +3 -2
  145. package/dist/src/public/tools/internal.js +1 -1
  146. package/dist/src/runtime/actions/types.d.ts +9 -0
  147. package/dist/src/runtime/actions/types.js +1 -1
  148. package/dist/src/runtime/agent/bootstrap.d.ts +2 -0
  149. package/dist/src/runtime/agent/bootstrap.js +1 -1
  150. package/dist/src/runtime/connections/mcp-client.d.ts +2 -2
  151. package/dist/src/runtime/connections/mcp-client.js +1 -1
  152. package/dist/src/runtime/connections/openapi-client.d.ts +2 -2
  153. package/dist/src/runtime/connections/openapi-client.js +1 -1
  154. package/dist/src/runtime/connections/types.d.ts +6 -1
  155. package/dist/src/runtime/framework-tools/bash.js +1 -1
  156. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
  157. package/dist/src/runtime/framework-tools/glob.js +1 -1
  158. package/dist/src/runtime/framework-tools/grep.js +1 -1
  159. package/dist/src/runtime/framework-tools/read-file.js +1 -1
  160. package/dist/src/runtime/framework-tools/web-fetch.js +1 -1
  161. package/dist/src/runtime/framework-tools/write-file.js +1 -1
  162. package/dist/src/runtime/resolve-agent.js +1 -1
  163. package/dist/src/runtime/sandbox/keys.js +1 -1
  164. package/dist/src/runtime/sandbox/template-plan.js +1 -1
  165. package/dist/src/runtime/session-callback-route.js +1 -1
  166. package/dist/src/runtime/skills/sandbox-access.d.ts +1 -1
  167. package/dist/src/runtime/skills/sandbox-access.js +1 -1
  168. package/dist/src/runtime/workspace/seed-files.js +1 -1
  169. package/dist/src/sandbox/state.d.ts +0 -1
  170. package/dist/src/setup/scaffold/create/project.js +1 -1
  171. package/dist/src/setup/scaffold/create/web-template.d.ts +2 -2
  172. package/dist/src/setup/scaffold/create/web-template.js +77 -3
  173. package/dist/src/shared/agent-definition.d.ts +31 -3
  174. package/dist/src/shared/dynamic-tool-definition.d.ts +4 -1
  175. package/dist/src/shared/sandbox-backend.d.ts +7 -1
  176. package/dist/src/shared/skill-package.d.ts +2 -2
  177. package/dist/src/shared/skill-package.js +1 -1
  178. package/dist/src/shared/skill-paths.d.ts +28 -0
  179. package/dist/src/shared/skill-paths.js +2 -0
  180. package/dist/src/shared/token-usage.d.ts +22 -0
  181. package/dist/src/shared/token-usage.js +1 -0
  182. package/dist/src/shared/tool-definition.d.ts +1 -1
  183. package/dist/src/svelte/index.js +1 -1
  184. package/dist/src/svelte/use-eve-agent.js +1 -1
  185. package/dist/src/vue/index.js +1 -1
  186. package/dist/src/vue/use-eve-agent.js +1 -1
  187. package/docs/agent-config.md +32 -15
  188. package/docs/channels/chat-sdk.mdx +209 -0
  189. package/docs/channels/meta.json +1 -0
  190. package/docs/concepts/context-control.md +2 -2
  191. package/docs/concepts/default-harness.md +10 -0
  192. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  193. package/docs/guides/client/messages.mdx +4 -0
  194. package/docs/guides/client/streaming.mdx +17 -12
  195. package/docs/guides/dynamic-workflows.md +87 -0
  196. package/docs/guides/frontend/overview.mdx +5 -1
  197. package/docs/guides/meta.json +1 -0
  198. package/docs/guides/remote-agents.md +1 -0
  199. package/docs/reference/project-layout.md +5 -4
  200. package/docs/reference/typescript-api.md +26 -26
  201. package/docs/sandbox.mdx +4 -2
  202. package/docs/skills.mdx +3 -1
  203. package/docs/subagents.mdx +2 -1
  204. package/docs/tools/human-in-the-loop.md +1 -1
  205. package/docs/tools/overview.mdx +2 -0
  206. package/package.json +1 -1
@@ -15,6 +15,7 @@ export default eveChannel({
15
15
  });
16
16
  `,"app/_components/agent-chat.tsx":`"use client";
17
17
 
18
+ import type { UserContent } from "ai";
18
19
  import { useEveAgent } from "eve/react";
19
20
  import { AlertCircleIcon } from "lucide-react";
20
21
  import {
@@ -42,9 +43,27 @@ export function AgentChat() {
42
43
 
43
44
  const handleSubmit = async (message: PromptInputMessage) => {
44
45
  const text = message.text.trim();
45
- if (!text || isBusy) return;
46
+ if ((text.length === 0 && message.files.length === 0) || isBusy) return;
46
47
 
47
- await agent.send({ message: text });
48
+ if (message.files.length === 0) {
49
+ await agent.send({ message: text });
50
+ return;
51
+ }
52
+
53
+ const parts: UserContent = [];
54
+ if (text.length > 0) {
55
+ parts.push({ text, type: "text" });
56
+ }
57
+ for (const file of message.files) {
58
+ parts.push({
59
+ data: file.url,
60
+ filename: file.filename,
61
+ mediaType: file.mediaType,
62
+ type: "file",
63
+ });
64
+ }
65
+
66
+ await agent.send({ message: parts });
48
67
  };
49
68
 
50
69
  const composer = (
@@ -148,7 +167,14 @@ import type {
148
167
  EveMessage,
149
168
  EveMessagePart,
150
169
  } from "eve/react";
151
- import { CheckCircleIcon, ExternalLinkIcon, KeyRoundIcon, XCircleIcon } from "lucide-react";
170
+ import {
171
+ CheckCircleIcon,
172
+ ExternalLinkIcon,
173
+ FileIcon,
174
+ ImageIcon,
175
+ KeyRoundIcon,
176
+ XCircleIcon,
177
+ } from "lucide-react";
152
178
  import { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";
153
179
  import { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";
154
180
  import {
@@ -167,6 +193,8 @@ export type AgentInputResponse = {
167
193
  readonly text?: string;
168
194
  };
169
195
 
196
+ type EveFilePart = Extract<EveMessagePart, { type: "file" }>;
197
+
170
198
  export function AgentMessage({
171
199
  canRespond,
172
200
  isStreaming,
@@ -230,6 +258,8 @@ function AgentMessagePart({
230
258
  <ReasoningContent>{part.text}</ReasoningContent>
231
259
  </Reasoning>
232
260
  );
261
+ case "file":
262
+ return <AttachmentPart part={part} />;
233
263
  case "authorization":
234
264
  return <AuthorizationPrompt part={part} />;
235
265
  case "dynamic-tool":
@@ -257,6 +287,37 @@ function AgentMessagePart({
257
287
  }
258
288
  }
259
289
 
290
+ function AttachmentPart({ part }: { readonly part: EveFilePart }) {
291
+ const label = part.filename ?? "Attachment";
292
+ const detail = [part.mediaType, formatBytes(part.size)].filter(Boolean).join(" - ");
293
+ const isImage = part.mediaType.startsWith("image/") && part.url !== undefined;
294
+ const Icon = isImage ? ImageIcon : FileIcon;
295
+ const body = (
296
+ <span className="flex max-w-sm items-center gap-3 rounded-md border bg-background/60 p-2 text-sm">
297
+ {isImage ? (
298
+ <img alt={label} className="size-12 shrink-0 rounded-sm object-cover" src={part.url} />
299
+ ) : (
300
+ <span className="flex size-10 shrink-0 items-center justify-center rounded-sm bg-muted text-muted-foreground">
301
+ <Icon className="size-4" />
302
+ </span>
303
+ )}
304
+ <span className="min-w-0 flex-1">
305
+ <span className="block truncate font-medium">{label}</span>
306
+ {detail ? <span className="block truncate text-muted-foreground">{detail}</span> : null}
307
+ </span>
308
+ {part.url ? <ExternalLinkIcon className="size-4 shrink-0 text-muted-foreground" /> : null}
309
+ </span>
310
+ );
311
+
312
+ return part.url ? (
313
+ <a href={part.url} rel="noreferrer" target="_blank">
314
+ {body}
315
+ </a>
316
+ ) : (
317
+ body
318
+ );
319
+ }
320
+
260
321
  function AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {
261
322
  const isAuthorized = part.state === "completed" && part.outcome === "authorized";
262
323
  const isCompleted = part.state === "completed";
@@ -350,6 +411,19 @@ function formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["o
350
411
  }
351
412
  }
352
413
 
414
+ function formatBytes(size: number | undefined): string | undefined {
415
+ if (size === undefined) {
416
+ return undefined;
417
+ }
418
+ if (size < 1024) {
419
+ return \`\${size} B\`;
420
+ }
421
+ if (size < 1024 * 1024) {
422
+ return \`\${(size / 1024).toFixed(1)} KB\`;
423
+ }
424
+ return \`\${(size / (1024 * 1024)).toFixed(1)} MB\`;
425
+ }
426
+
353
427
  function InputRequestActions({
354
428
  canRespond,
355
429
  onInputResponses,
@@ -103,9 +103,28 @@ export interface AgentLimitsDefinition {
103
103
  * Root sessions are depth 0. A `maxSubagentDepth` of 3 allows child sessions at
104
104
  * depths 1, 2, and 3; sessions already at depth 3 cannot delegate again.
105
105
  *
106
+ * Delegated subagent sessions resolve this against the cap inherited from
107
+ * the delegating parent; the tighter value wins.
108
+ *
106
109
  * @default 3
107
110
  */
108
111
  readonly maxSubagentDepth?: number;
112
+ /**
113
+ * Maximum number of subagent calls one `Workflow` tool invocation may
114
+ * dispatch.
115
+ *
116
+ * Applies to the opt-in `Workflow` orchestration tool: a single
117
+ * model-authored workflow program may spawn at most this many subagent or
118
+ * remote-agent calls, counted across the whole program (sequential and
119
+ * parallel calls alike). Calls beyond the limit fail with an error result
120
+ * instead of starting a child session.
121
+ *
122
+ * Delegated subagent sessions resolve this against the cap inherited from
123
+ * the delegating parent; the tighter value wins.
124
+ *
125
+ * @default 100
126
+ */
127
+ readonly maxSubagents?: number;
109
128
  /**
110
129
  * Maximum provider-reported input tokens accumulated by one durable session.
111
130
  *
@@ -113,17 +132,26 @@ export interface AgentLimitsDefinition {
113
132
  * the limit is allowed to finish because providers only report exact usage
114
133
  * after the call completes; later model calls in the same session are blocked.
115
134
  *
116
- * @default 40_000_000 for root sessions; 5_000_000 for delegated subagent sessions
135
+ * `false` disables the limit: the session is uncapped.
136
+ *
137
+ * Delegated subagent sessions default to the delegating parent's remaining
138
+ * quota at dispatch time, and the parent's remaining quota always caps an
139
+ * authored child limit — a child can never outspend its parent's budget.
140
+ *
141
+ * @default 40_000_000 for root sessions; the parent's remaining quota for delegated subagent sessions
117
142
  */
118
- readonly maxInputTokensPerSession?: number;
143
+ readonly maxInputTokensPerSession?: number | false;
119
144
  /**
120
145
  * Maximum provider-reported output tokens accumulated by one durable session.
121
146
  *
122
147
  * eve checks this before starting each model call. The model call that crosses
123
148
  * the limit is allowed to finish because providers only report exact usage
124
149
  * after the call completes; later model calls in the same session are blocked.
150
+ *
151
+ * `false` disables the limit. Unset by default; delegated subagent sessions
152
+ * inherit the parent's remaining output quota when the parent has one.
125
153
  */
126
- readonly maxOutputTokensPerSession?: number;
154
+ readonly maxOutputTokensPerSession?: number | false;
127
155
  }
128
156
  /**
129
157
  * Experimental, opt-in agent capabilities authored in `agent.ts`.
@@ -4,7 +4,10 @@ import type { SessionContext } from "#public/definitions/callback-context.js";
4
4
  import type { Approval } from "#public/definitions/approval.js";
5
5
  import type { SessionAuth } from "#context/keys.js";
6
6
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
7
- type ToolContext = SessionContext;
7
+ type ToolContext = SessionContext & {
8
+ /** Aborts when the active turn is cancelled. */
9
+ readonly abortSignal: AbortSignal;
10
+ };
8
11
  /**
9
12
  * Stream event types allowed for dynamic tool resolvers. Dispatch
10
13
  * supports any event; this extract restricts the public surface until
@@ -11,7 +11,13 @@ export interface SandboxBackendHandle<SO = Record<string, never>> {
11
11
  readonly session: SandboxSession;
12
12
  readonly useSessionFn: SandboxSessionUseFn<SO>;
13
13
  captureState(): Promise<SandboxBackendSessionState>;
14
- dispose(): Promise<void>;
14
+ /**
15
+ * Stops the underlying compute because the eve server is shutting
16
+ * down; nothing may be left running afterwards. The session must
17
+ * remain reattachable from persisted state on the next server start
18
+ * when the backend supports durable sessions.
19
+ */
20
+ shutdown(): Promise<void>;
15
21
  }
16
22
  /**
17
23
  * Serializable per-sandbox reconnect record stored on the harness session.
@@ -30,7 +30,7 @@ export declare function writeSkillPackageDirectory(input: {
30
30
  }): Promise<void>;
31
31
  /**
32
32
  * Writes a normalized package into the live sandbox under
33
- * `/workspace/skills/<name>/`.
33
+ * the sandbox skill root.
34
34
  */
35
35
  export declare function writeSkillPackageToSandbox(input: {
36
36
  readonly sandbox: SandboxSession;
@@ -45,6 +45,6 @@ export declare function removeSkillPackageFromSandbox(input: {
45
45
  }): Promise<void>;
46
46
  /**
47
47
  * Validates a runtime-contributed skill name before it becomes one path
48
- * segment under `/workspace/skills`.
48
+ * segment under the sandbox skill root.
49
49
  */
50
50
  export declare function assertSafeSkillPackageName(name: string): void;
@@ -1 +1 @@
1
- import{dirname,join}from"node:path";import{mkdir,writeFile}from"node:fs/promises";import{Buffer}from"node:buffer";const WORKSPACE_ROOT=`/workspace`;function normalizeSkillPackage(e){assertSafeSkillPackageName(e.name);let t=[{content:Buffer.from(e.markdown,`utf8`),relativePath:`SKILL.md`}];for(let[n,r]of Object.entries(e.files??{}))assertSafeSkillPackageFilePath(n),t.push({content:contentToBuffer(r),relativePath:n});return t.sort((e,t)=>comparePaths(e.relativePath,t.relativePath)),{description:e.description,files:t,license:e.license,markdown:e.markdown,metadata:e.metadata===void 0?void 0:{...e.metadata},name:e.name}}async function writeSkillPackageDirectory(i){for(let a of i.skill.files){let o=join(i.rootPath,`skills`,i.skill.name,a.relativePath);await mkdir(dirname(o),{recursive:!0}),await writeFile(o,a.content)}}async function writeSkillPackageToSandbox(e){for(let t of e.skill.files)await e.sandbox.writeBinaryFile({content:t.content,path:`${WORKSPACE_ROOT}/skills/${e.skill.name}/${t.relativePath}`})}async function removeSkillPackageFromSandbox(e){assertSafeSkillPackageName(e.name),await e.sandbox.removePath({force:!0,path:`${WORKSPACE_ROOT}/skills/${e.name}`,recursive:!0})}function assertSafeSkillPackageName(e){if(e.length===0||e.startsWith(`.`)||e.includes(`/`)||e.includes(`\\`)||e.includes(`..`)||!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(e)||/^[A-Za-z]:/.test(e))throw Error(`Expected skill name to be a non-empty shell-safe path segment starting with an alphanumeric character and containing only alphanumerics, ".", "_", or "-".`)}function assertSafeSkillPackageFilePath(e){if(e===`SKILL.md`)throw Error(`Skill package files must not include "SKILL.md"; eve generates it.`);if(e.length===0||e.startsWith(`/`)||e.includes(`\\`)||/^[A-Za-z]:/.test(e)||e.split(`/`).some(e=>e.length===0||e===`.`||e===`..`))throw Error(`Expected skill package file paths to be relative POSIX paths.`)}function contentToBuffer(e){return typeof e==`string`?Buffer.from(e,`utf8`):Buffer.from(e)}function comparePaths(e,t){return e<t?-1:+(e>t)}export{assertSafeSkillPackageName,normalizeSkillPackage,removeSkillPackageFromSandbox,writeSkillPackageDirectory,writeSkillPackageToSandbox};
1
+ import{dirname,join}from"node:path";import{mkdir,writeFile}from"node:fs/promises";import{resolveSandboxSkillRoot,resolveSandboxSkillWritePath}from"#shared/skill-paths.js";import{Buffer}from"node:buffer";function normalizeSkillPackage(e){assertSafeSkillPackageName(e.name);let t=[{content:Buffer.from(e.markdown,`utf8`),relativePath:`SKILL.md`}];for(let[n,r]of Object.entries(e.files??{}))assertSafeSkillPackageFilePath(n),t.push({content:contentToBuffer(r),relativePath:n});return t.sort((e,t)=>comparePaths(e.relativePath,t.relativePath)),{description:e.description,files:t,license:e.license,markdown:e.markdown,metadata:e.metadata===void 0?void 0:{...e.metadata},name:e.name}}async function writeSkillPackageDirectory(i){for(let a of i.skill.files){let o=join(i.rootPath,`skills`,i.skill.name,a.relativePath);await mkdir(dirname(o),{recursive:!0}),await writeFile(o,a.content)}}async function writeSkillPackageToSandbox(e){for(let t of e.skill.files)await e.sandbox.writeBinaryFile({content:t.content,path:await resolveSandboxSkillWritePath({name:e.skill.name,relativePath:t.relativePath,sandbox:e.sandbox})})}async function removeSkillPackageFromSandbox(e){assertSafeSkillPackageName(e.name);let t=await resolveSandboxSkillRoot({sandbox:e.sandbox});await e.sandbox.removePath({force:!0,path:`${t}/${e.name}`,recursive:!0})}function assertSafeSkillPackageName(e){if(e.length===0||e.startsWith(`.`)||e.includes(`/`)||e.includes(`\\`)||e.includes(`..`)||!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(e)||/^[A-Za-z]:/.test(e))throw Error(`Expected skill name to be a non-empty shell-safe path segment starting with an alphanumeric character and containing only alphanumerics, ".", "_", or "-".`)}function assertSafeSkillPackageFilePath(e){if(e===`SKILL.md`)throw Error(`Skill package files must not include "SKILL.md"; eve generates it.`);if(e.length===0||e.startsWith(`/`)||e.includes(`\\`)||/^[A-Za-z]:/.test(e)||e.split(`/`).some(e=>e.length===0||e===`.`||e===`..`))throw Error(`Expected skill package file paths to be relative POSIX paths.`)}function contentToBuffer(e){return typeof e==`string`?Buffer.from(e,`utf8`):Buffer.from(e)}function comparePaths(e,t){return e<t?-1:+(e>t)}export{assertSafeSkillPackageName,normalizeSkillPackage,removeSkillPackageFromSandbox,writeSkillPackageDirectory,writeSkillPackageToSandbox};
@@ -0,0 +1,28 @@
1
+ import type { SandboxSession } from "#shared/sandbox-session.js";
2
+ export declare const MODEL_SKILL_ROOT = "$HOME/.agents/skills";
3
+ export declare const FALLBACK_SKILL_ROOT = "/workspace/skills";
4
+ export declare function formatSkillModelPath(input: {
5
+ readonly name: string;
6
+ readonly relativePath: string;
7
+ }): string;
8
+ export declare function formatFallbackSkillPath(input: {
9
+ readonly name: string;
10
+ readonly relativePath: string;
11
+ }): string;
12
+ export declare function resolveSandboxSkillRoot(input: {
13
+ readonly sandbox: SandboxSession;
14
+ }): Promise<string>;
15
+ export declare function resolveSandboxSkillReadPaths(input: {
16
+ readonly name: string;
17
+ readonly relativePath: string;
18
+ readonly sandbox: SandboxSession;
19
+ }): Promise<readonly string[]>;
20
+ export declare function resolveSandboxSkillWritePath(input: {
21
+ readonly name: string;
22
+ readonly relativePath: string;
23
+ readonly sandbox: SandboxSession;
24
+ }): Promise<string>;
25
+ export declare function resolveSandboxSeedFilePath(input: {
26
+ readonly path: string;
27
+ readonly sandbox: SandboxSession;
28
+ }): Promise<string>;
@@ -0,0 +1,2 @@
1
+ const MODEL_SKILL_ROOT=`$HOME/.agents/skills`,FALLBACK_SKILL_ROOT=`/workspace/skills`,skillRootCache=new WeakMap;function formatSkillModelPath(t){return formatSkillPath({name:t.name,relativePath:t.relativePath,root:MODEL_SKILL_ROOT})}function formatFallbackSkillPath(e){return formatSkillPath({name:e.name,relativePath:e.relativePath,root:FALLBACK_SKILL_ROOT})}async function resolveSandboxSkillRoot(e){let t=skillRootCache.get(e.sandbox);if(t!==void 0)return await t;let r=probeSandboxSkillRoot(e.sandbox);return skillRootCache.set(e.sandbox,r),await r}async function resolveSandboxSkillReadPaths(e){return[formatSkillPath({name:e.name,relativePath:e.relativePath,root:await resolveSandboxSkillRoot({sandbox:e.sandbox})})]}async function resolveSandboxSkillWritePath(e){return formatSkillPath({name:e.name,relativePath:e.relativePath,root:await resolveSandboxSkillRoot({sandbox:e.sandbox})})}async function resolveSandboxSeedFilePath(e){return e.path.startsWith(`$HOME/.agents/skills/`)?`${await resolveSandboxSkillRoot({sandbox:e.sandbox})}${e.path.slice(20)}`:e.path}function formatSkillPath(e){return`${e.root}/${e.name}/${e.relativePath}`}async function probeSandboxSkillRoot(e){try{let n=await e.run({command:`printf '%s\\n' "$HOME"`});if(n.exitCode!==0)return FALLBACK_SKILL_ROOT;let r=n.stdout.trim();return isUsableSandboxHome(r)?joinHomeSkillRoot(r):FALLBACK_SKILL_ROOT}catch{return FALLBACK_SKILL_ROOT}}function isUsableSandboxHome(e){return e.length>0&&e.startsWith(`/`)&&!e.includes(`\0`)&&!e.includes(`
2
+ `)&&!e.includes(`\r`)}function joinHomeSkillRoot(e){return`${e===`/`?``:e.replace(/\/+$/,``)}/.agents/skills`}export{FALLBACK_SKILL_ROOT,MODEL_SKILL_ROOT,formatFallbackSkillPath,formatSkillModelPath,resolveSandboxSeedFilePath,resolveSandboxSkillReadPaths,resolveSandboxSkillRoot,resolveSandboxSkillWritePath};
@@ -0,0 +1,22 @@
1
+ import { z } from "#compiled/zod/index.js";
2
+ /**
3
+ * Provider-reported token usage totals.
4
+ *
5
+ * The single usage shape across eve: harness accumulation
6
+ * (`turn-tag-state`), delegated subagent results, remote session
7
+ * callbacks, and usage spans all carry this type.
8
+ */
9
+ export type TokenUsage = z.infer<typeof tokenUsageSchema>;
10
+ /**
11
+ * Zod schema for {@link TokenUsage}.
12
+ *
13
+ * Validates the `usage` field of remote session callbacks, which may come
14
+ * from a callee on a different eve version — unknown keys are stripped
15
+ * rather than rejected so a newer sender never voids the whole payload.
16
+ */
17
+ export declare const tokenUsageSchema: z.ZodObject<{
18
+ cacheReadTokens: z.ZodNumber;
19
+ cacheWriteTokens: z.ZodNumber;
20
+ inputTokens: z.ZodNumber;
21
+ outputTokens: z.ZodNumber;
22
+ }, z.core.$strip>;
@@ -0,0 +1 @@
1
+ import{z}from"#compiled/zod/index.js";const tokenUsageSchema=z.object({cacheReadTokens:z.number().int().nonnegative(),cacheWriteTokens:z.number().int().nonnegative(),inputTokens:z.number().int().nonnegative(),outputTokens:z.number().int().nonnegative()});export{tokenUsageSchema};
@@ -6,7 +6,7 @@ import type { JsonObject } from "#shared/json.js";
6
6
  * function. These are the same options the SDK passes to every tool call.
7
7
  */
8
8
  export type ToolExecuteOptions = Omit<ToolExecutionOptions<unknown>, "context">;
9
- export type ToolExecuteFn<TInput = unknown, TOutput = unknown> = (input: TInput, options?: ToolExecuteOptions) => Promise<TOutput> | TOutput;
9
+ export type ToolExecuteFn<TInput = unknown, TOutput = unknown> = (input: TInput, options: ToolExecuteOptions) => Promise<TOutput> | TOutput;
10
10
  interface ToolDefinitionBase {
11
11
  readonly description: string;
12
12
  }
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-Bh1NmXO9.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-CxcKypLZ.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-Bh1NmXO9.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-CxcKypLZ.js";
2
2
 
3
3
  export { useEveAgent };
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-Dvy_Wjvq.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-CM24ZUEI.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-Dvy_Wjvq.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-CM24ZUEI.js";
2
2
 
3
3
  export { useEveAgent };
@@ -93,13 +93,30 @@ export default defineAgent({
93
93
 
94
94
  Input and output budgets are checked independently. The model call that crosses
95
95
  either limit is allowed to finish because providers only report exact token
96
- usage after a call completes. Follow-up model calls in the same session fail
97
- with `SESSION_TOKEN_LIMIT_REACHED`.
98
-
99
- When `maxInputTokensPerSession` is omitted, eve applies a default input budget:
100
- `40_000_000` provider-reported input tokens for root sessions and `5_000_000`
101
- for delegated subagent sessions. `maxOutputTokensPerSession` is unset unless
102
- configured.
96
+ usage after a call completes. Before the next model call, eve pauses the
97
+ session and sends a deterministic continuation prompt with two options:
98
+ **Continue** grants a fresh budget window of the configured size (both input
99
+ and output windows reset together), and **Stop** ends the session gracefully
100
+ (`session.completed`) declining is a user decision, not an error. A reply
101
+ that answers neither option re-raises the prompt; the reply stays in history
102
+ and is processed once the budget is granted.
103
+
104
+ Sessions that cannot reach a human — task-mode runs such as schedules and
105
+ subagents without input proxying — skip the prompt and fail the next model
106
+ call with `SESSION_TOKEN_LIMIT_REACHED`.
107
+
108
+ When `maxInputTokensPerSession` is omitted, root sessions apply a default
109
+ input budget of `40_000_000` provider-reported input tokens.
110
+ `maxOutputTokensPerSession` is unset unless configured. Setting either limit
111
+ to `false` uncaps that axis — the session never stops on it.
112
+
113
+ Delegated subagent sessions have no fixed default. Each child receives a
114
+ share of the delegating parent's remaining quota at dispatch time — the
115
+ remainder (limit minus accumulated usage) split evenly across the batch's
116
+ local subagent calls — and a completed child's usage counts against the
117
+ parent's quota, so a delegation tree can never outspend the budget configured
118
+ at its root. An authored child limit applies only when it is tighter than the
119
+ parent's grant; an uncapped parent delegates uncapped children.
103
120
 
104
121
  ## Workflow world
105
122
 
@@ -142,14 +159,14 @@ installed package must stay external in hosted output, list it in
142
159
 
143
160
  `defineAgent` takes a few more fields, all optional. For the exported types, see the [TypeScript API](./reference/typescript-api).
144
161
 
145
- | Field | Type | Default | Description |
146
- | -------------- | --------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
147
- | `reasoning` | `AgentReasoningDefinition` | provider default | Provider-agnostic reasoning effort forwarded to the agent's turn model calls. |
148
- | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
149
- | `limits` | `AgentLimitsDefinition` | field-specific | Framework-owned runtime limits. `maxSubagentDepth` defaults to `3`; `maxInputTokensPerSession` defaults to `40_000_000` for root sessions and `5_000_000` for delegated subagent sessions; `maxOutputTokensPerSession` is unset unless configured. |
150
- | `experimental` | `{ workflow?: { world?: string } }` | unset | Opt-in settings that can change or disappear in any release. Treat them as unstable. `workflow.world` selects the Workflow world package backing session state, queues, hooks, and streams on the root agent. |
151
- | `outputSchema` | Standard Schema or a JSON Schema object | none | Structured return type for task-mode runs (a subagent, schedule, or remote job). Interactive conversation turns ignore it unless the client supplies a per-message schema. |
152
- | `build` | `{ externalDependencies?: string[] }` | none | Hosted-build packaging controls. `externalDependencies` keeps listed packages external while eve compiles authored modules such as tools and channels, and traces those packages into the hosted output. |
162
+ | Field | Type | Default | Description |
163
+ | -------------- | --------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
164
+ | `reasoning` | `AgentReasoningDefinition` | provider default | Provider-agnostic reasoning effort forwarded to the agent's turn model calls. |
165
+ | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
166
+ | `limits` | `AgentLimitsDefinition` | field-specific | Framework-owned runtime limits. `maxSubagentDepth` defaults to `3`; `maxInputTokensPerSession` defaults to `40_000_000` for root sessions, and delegated subagent sessions inherit the parent's remaining quota; `maxOutputTokensPerSession` is unset unless configured; `false` uncaps a session token limit. |
167
+ | `experimental` | `{ workflow?: { world?: string } }` | unset | Opt-in settings that can change or disappear in any release. Treat them as unstable. `workflow.world` selects the Workflow world package backing session state, queues, hooks, and streams on the root agent. |
168
+ | `outputSchema` | Standard Schema or a JSON Schema object | none | Structured return type for task-mode runs (a subagent, schedule, or remote job). Interactive conversation turns ignore it unless the client supplies a per-message schema. |
169
+ | `build` | `{ externalDependencies?: string[] }` | none | Hosted-build packaging controls. `externalDependencies` keeps listed packages external while eve compiles authored modules such as tools and channels, and traces those packages into the hosted output. |
153
170
 
154
171
  `externalDependencies` is a packaging control only. It keeps selected packages as runtime dependencies in the hosted output; it does not authorize, configure, or review any third-party service those packages may call.
155
172
 
@@ -0,0 +1,209 @@
1
+ ---
2
+ title: "Chat SDK"
3
+ description: "Bridge any Vercel Chat SDK adapter — Slack, Discord, Telegram, WhatsApp, email, and more — to your agent through one channel, using your own credentials and state store."
4
+ ---
5
+
6
+ The Chat SDK channel connects your agent to any [Vercel Chat SDK](https://chat-sdk.dev) adapter. You pick an adapter (`@chat-adapter/slack`, `@resend/chat-sdk-adapter`, and so on), register handlers for the messages you care about, and call `send` to hand each turn to eve. Use it to reach a surface eve does not ship a first-class channel for, or when you want to manage credentials and state with the Chat SDK's own primitives rather than [Vercel Connect](../guides/auth-and-route-protection). See [Channels](./overview) for the contract this builds on.
7
+
8
+ You supply an adapter and a state store: the adapter owns provider auth, webhook verification, and delivery, while eve owns session dispatch, streaming, typing, and human-in-the-loop. First-class channels such as [Slack](./slack) manage credentials through Vercel Connect instead — reach for whichever fits the surface you are targeting.
9
+
10
+ ## Install
11
+
12
+ Add eve, the Chat SDK core (`chat`), an adapter, and a state adapter. The example below uses the Resend email adapter with the in-memory state store:
13
+
14
+ ```bash
15
+ npm install eve@latest chat @resend/chat-sdk-adapter @chat-adapter/state-memory
16
+ ```
17
+
18
+ Swap in whichever adapter matches your surface — `@chat-adapter/slack`, `@chat-adapter/discord`, `@chat-adapter/telegram`, and so on. Any Chat SDK adapter works.
19
+
20
+ ## Add the channel
21
+
22
+ `chatSdkChannel` returns `{ bot, channel, send }`. Register Chat SDK handlers on `bot`, call `send` from those handlers to start or resume an eve session, and export `channel` as the module default:
23
+
24
+ ```ts title="agent/channels/resend.ts"
25
+ import { createMemoryState } from "@chat-adapter/state-memory";
26
+ import { createResendAdapter } from "@resend/chat-sdk-adapter";
27
+ import type { Message, Thread } from "chat";
28
+ import { chatSdkChannel } from "eve/channels/chat-sdk";
29
+
30
+ export const { bot, channel, send } = chatSdkChannel({
31
+ userName: "Resend Bot",
32
+ adapters: {
33
+ resend: createResendAdapter({
34
+ fromAddress: "hello@example.com",
35
+ fromName: "Resend Bot",
36
+ }),
37
+ },
38
+ state: createMemoryState(),
39
+ streaming: false,
40
+ });
41
+
42
+ bot.onNewMention(async (thread: Thread, message: Message) => {
43
+ await thread.subscribe();
44
+ await send(message.text, { thread });
45
+ });
46
+
47
+ bot.onSubscribedMessage(async (thread: Thread, message: Message) => {
48
+ await send(message.text, { thread });
49
+ });
50
+
51
+ export default channel;
52
+ ```
53
+
54
+ `adapters` is a map of adapter name to adapter instance; each entry mounts its own webhook (see below). `state` takes any Chat SDK state adapter: `createMemoryState()` is fine for local development, but use a durable adapter (Redis, Upstash, etc.) in production so thread subscriptions and inbound deduplication survive restarts. `send` accepts a plain string, an AI SDK `UserContent` array, or a `SendPayload`, and must be called from inside a Chat SDK handler — it dispatches the turn on the webhook that is currently running.
55
+
56
+ Deploy once the channel file is in place:
57
+
58
+ ```bash
59
+ eve deploy
60
+ ```
61
+
62
+ `eve deploy` links the project if needed and deploys to Vercel production.
63
+
64
+ ## Configure the webhook route
65
+
66
+ Each adapter mounts one `POST` route at `/eve/v1/{adapterName}`, so the `resend` adapter above is served at `/eve/v1/resend`. Point your provider's webhook (the Resend inbound address, the Slack Event Subscriptions URL, etc.) at that path.
67
+
68
+ Override the base path for every adapter with `route`, or pin an individual adapter's path with `routes`:
69
+
70
+ ```ts
71
+ export const { bot, channel, send } = chatSdkChannel({
72
+ userName: "Resend Bot",
73
+ adapters: { resend: createResendAdapter({ fromAddress: "hello@example.com" }) },
74
+ state: createMemoryState(),
75
+ route: "/webhooks", // resend now mounts at /webhooks/resend
76
+ routes: { resend: "/webhooks/inbound-email" }, // …or pin it exactly
77
+ });
78
+ ```
79
+
80
+ Use `routes` when a provider requires a fixed URL or when you are migrating an existing endpoint without changing the provider's settings.
81
+
82
+ ## How the channel handles messages
83
+
84
+ ### Dispatch
85
+
86
+ You choose which Chat SDK events start a turn by registering handlers on `bot` and calling `send`:
87
+
88
+ - `bot.onNewMention(thread, message)` fires on a fresh `@mention` (or, for surfaces like email, a new inbound thread). Call `thread.subscribe()` when you want later replies in the same thread to keep reaching the agent.
89
+ - `bot.onSubscribedMessage(thread, message)` fires on subsequent messages in a subscribed thread.
90
+ - `bot.onAction`, `bot.onReaction`, and `bot.onSlashCommand` are available for adapters that emit them.
91
+
92
+ `send(input, options)` starts or resumes the eve session. The `thread` you pass determines the continuation token and the persisted channel state, so replies land back on the originating thread:
93
+
94
+ ```ts
95
+ bot.onNewMention(async (thread, message) => {
96
+ await send(message.text, { thread, title: "Support request" });
97
+ });
98
+ ```
99
+
100
+ `options` accepts `{ thread, auth?, title?, mode?, callback?, adapterName? }`. `title` sets the eve session's display title without changing the model message; `auth` attaches an authenticated principal to the turn.
101
+
102
+ ### Delivery
103
+
104
+ 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.
105
+
106
+ 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:
107
+
108
+ ```ts
109
+ chatSdkChannel({
110
+ userName: "Resend Bot",
111
+ adapters: { resend: createResendAdapter({ fromAddress: "hello@example.com" }) },
112
+ state: createMemoryState(),
113
+ streaming: false,
114
+ });
115
+ ```
116
+
117
+ Typing indicators post automatically where the adapter supports them: `Working…` on `turn.started`, and tool status on `actions.requested`.
118
+
119
+ ### Optional capabilities degrade gracefully
120
+
121
+ Adapters do not all implement every operation. When an adapter's `startTyping` or `editMessage` throws a `NotImplementedError` (or an error with code `NOT_IMPLEMENTED`), the channel swallows it: typing indicators are skipped, and a streaming edit falls back to a single final post for the rest of the session. You never have to guard optional capabilities in your own handlers. The same predicate is exported as `isNotImplemented` if you want it in custom `events`:
122
+
123
+ ```ts
124
+ import { isNotImplemented } from "eve/channels/chat-sdk";
125
+ ```
126
+
127
+ Override any default by passing `events`. Handlers receive `(eventData, channel, ctx)`, with the rebuilt Chat SDK thread on `channel.thread`:
128
+
129
+ ```ts
130
+ chatSdkChannel({
131
+ userName: "Resend Bot",
132
+ adapters: { resend: createResendAdapter({ fromAddress: "hello@example.com" }) },
133
+ state: createMemoryState(),
134
+ events: {
135
+ "message.completed"(eventData, channel) {
136
+ if (eventData.finishReason === "tool-calls" || !eventData.message || !channel.thread) return;
137
+ return channel.thread.post({ markdown: eventData.message });
138
+ },
139
+ },
140
+ });
141
+ ```
142
+
143
+ ### Human-in-the-loop (HITL)
144
+
145
+ HITL prompts render as a Chat SDK `Card` with buttons. Button clicks resume the parked session automatically — the channel wires `bot.onAction` for you. Change the action-id prefix with `inputActionPrefix` (default `eve_input:`) if your app already uses it, and provide `resolveInputAuth` to carry user or tenant auth across the resume:
146
+
147
+ ```ts
148
+ chatSdkChannel({
149
+ userName: "Support Bot",
150
+ adapters: { slack: createSlackAdapter() },
151
+ state: createMemoryState(),
152
+ resolveInputAuth: (event) => ({
153
+ authenticator: "slack",
154
+ principalType: "user",
155
+ principalId: event.user?.userId ?? "unknown",
156
+ attributes: {},
157
+ }),
158
+ });
159
+ ```
160
+
161
+ ### Proactive sessions
162
+
163
+ Start a session without an inbound webhook through `channel.receive({ message, target, auth })` — from a schedule `run` handler, or `args.receive(channel, ...)` from another channel. The target is a serialized Chat SDK thread, or `{ threadId, adapterName }` when you only have a provider-native thread id:
164
+
165
+ ```ts
166
+ await channel.receive({
167
+ message: "Your weekly digest is ready.",
168
+ target: { adapterName: "resend", threadId: "resend:user@example.com" },
169
+ auth: null,
170
+ });
171
+ ```
172
+
173
+ ### Attachments
174
+
175
+ `send` takes plain text or an AI SDK `UserContent` array. To forward a Chat SDK message's attachments, convert it with `messageToUserContent`, which returns `message.text` when there are no attachments and a `UserContent` array (text plus one file part per attachment URL) when there are:
176
+
177
+ ```ts
178
+ import { messageToUserContent } from "eve/channels/chat-sdk";
179
+
180
+ bot.onNewMention(async (thread, message) => {
181
+ await send(messageToUserContent(message), { thread });
182
+ });
183
+ ```
184
+
185
+ See [File uploads](./custom#file-uploads) for how eve stages remote file URLs before the model call.
186
+
187
+ ## Configuration reference
188
+
189
+ | Option | Default | Purpose |
190
+ | ------------------------- | ------------ | ------------------------------------------------------------------------ |
191
+ | `adapters` | — | Map of adapter name to Chat SDK adapter instance. One webhook per entry. |
192
+ | `state` | — | Chat SDK state adapter for subscriptions, locks, and dedupe. |
193
+ | `userName` | — | Display name for the bot (a standard Chat SDK `ChatConfig` field). |
194
+ | `route` | `/eve/v1` | Base path for generated adapter webhooks (`{route}/{adapter}`). |
195
+ | `routes` | — | Per-adapter path overrides for fixed or migrated webhook URLs. |
196
+ | `streaming` | `true` | Post-then-edit streaming. Set `false` for one-message-per-turn surfaces. |
197
+ | `streamingEditIntervalMs` | `1000` | Minimum interval between streaming edits. |
198
+ | `events` | built-in | Per-event handlers. A supplied handler replaces that built-in default. |
199
+ | `inputActionPrefix` | `eve_input:` | Prefix for default HITL button action ids. |
200
+ | `resolveInputAuth` | `null` | Auth resolver applied when a HITL button click resumes a session. |
201
+ | `webhook` | — | Extra Chat SDK webhook options (eve owns `waitUntil`). |
202
+
203
+ Any other Chat SDK `ChatConfig` field (for example `concurrency`) is accepted and passed through.
204
+
205
+ ## What to read next
206
+
207
+ - [Channels overview](./overview): the channel contract and every built-in channel
208
+ - [Custom channels](./custom): build a channel for any surface with `defineChannel`
209
+ - [Auth & route protection](../guides/auth-and-route-protection): authenticating inbound traffic
@@ -10,6 +10,7 @@
10
10
  "twilio",
11
11
  "github",
12
12
  "linear",
13
+ "chat-sdk",
13
14
  "custom"
14
15
  ]
15
16
  }