eve 0.20.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 (136) hide show
  1. package/CHANGELOG.md +16 -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/client/index.d.ts +1 -1
  6. package/dist/src/client/message-reducer-types.d.ts +11 -3
  7. package/dist/src/client/message-reducer.js +1 -1
  8. package/dist/src/compiler/manifest.js +1 -1
  9. package/dist/src/compiler/normalize-agent-config.js +1 -1
  10. package/dist/src/compiler/workspace-resources.js +1 -1
  11. package/dist/src/context/dynamic-skill-lifecycle.js +1 -1
  12. package/dist/src/context/keys.d.ts +0 -1
  13. package/dist/src/context/keys.js +1 -1
  14. package/dist/src/discover/sandbox.d.ts +0 -6
  15. package/dist/src/discover/sandbox.js +1 -1
  16. package/dist/src/execution/create-session-step.d.ts +2 -1
  17. package/dist/src/execution/create-session-step.js +1 -1
  18. package/dist/src/execution/delegated-parent-notification.d.ts +6 -0
  19. package/dist/src/execution/delegated-parent-notification.js +1 -1
  20. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  21. package/dist/src/execution/dispatch-workflow-runtime-actions-step.js +1 -1
  22. package/dist/src/execution/durable-session-store.d.ts +1 -0
  23. package/dist/src/execution/next-driver-action.d.ts +3 -0
  24. package/dist/src/execution/node-step.d.ts +6 -0
  25. package/dist/src/execution/node-step.js +1 -1
  26. package/dist/src/execution/run-session-limits.d.ts +21 -0
  27. package/dist/src/execution/run-session-limits.js +1 -0
  28. package/dist/src/execution/runtime-context.js +1 -1
  29. package/dist/src/execution/sandbox/bindings/local-backend-utils.d.ts +1 -1
  30. package/dist/src/execution/sandbox/bindings/local-backend-utils.js +1 -1
  31. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  32. package/dist/src/execution/sandbox/prewarm.js +1 -1
  33. package/dist/src/execution/session-callback-step.d.ts +6 -0
  34. package/dist/src/execution/session-callback-step.js +1 -1
  35. package/dist/src/execution/session.d.ts +14 -3
  36. package/dist/src/execution/session.js +2 -4
  37. package/dist/src/execution/skills/instructions.d.ts +8 -4
  38. package/dist/src/execution/skills/instructions.js +2 -2
  39. package/dist/src/execution/subagent-tool.d.ts +7 -0
  40. package/dist/src/execution/subagent-tool.js +1 -1
  41. package/dist/src/execution/subagent-usage-span.d.ts +9 -0
  42. package/dist/src/execution/subagent-usage-span.js +1 -0
  43. package/dist/src/execution/turn-execution-cursor.d.ts +2 -0
  44. package/dist/src/execution/turn-workflow.js +1 -1
  45. package/dist/src/execution/workflow-entry.d.ts +1 -0
  46. package/dist/src/execution/workflow-entry.js +1 -1
  47. package/dist/src/execution/workflow-runtime.js +1 -1
  48. package/dist/src/execution/workflow-skill-root.d.ts +6 -0
  49. package/dist/src/execution/workflow-skill-root.js +1 -0
  50. package/dist/src/execution/workflow-steps.d.ts +3 -0
  51. package/dist/src/execution/workflow-steps.js +1 -1
  52. package/dist/src/harness/advertised-tools.d.ts +1 -0
  53. package/dist/src/harness/advertised-tools.js +1 -1
  54. package/dist/src/harness/input-requests.d.ts +8 -0
  55. package/dist/src/harness/input-requests.js +1 -1
  56. package/dist/src/harness/runtime-actions.js +1 -1
  57. package/dist/src/harness/session-limit-continuation.d.ts +55 -0
  58. package/dist/src/harness/session-limit-continuation.js +1 -0
  59. package/dist/src/harness/session-limit-enforcement.d.ts +55 -0
  60. package/dist/src/harness/session-limit-enforcement.js +1 -0
  61. package/dist/src/harness/subagent-depth.d.ts +1 -4
  62. package/dist/src/harness/subagent-depth.js +1 -1
  63. package/dist/src/harness/subagent-token-budget.d.ts +14 -0
  64. package/dist/src/harness/subagent-token-budget.js +1 -0
  65. package/dist/src/harness/tool-loop.js +1 -1
  66. package/dist/src/harness/turn-tag-state.d.ts +31 -0
  67. package/dist/src/harness/turn-tag-state.js +1 -1
  68. package/dist/src/harness/types.d.ts +21 -4
  69. package/dist/src/harness/workflow-sandbox.d.ts +1 -0
  70. package/dist/src/harness/workflow-sandbox.js +1 -1
  71. package/dist/src/harness/workflow-subagent-limit.d.ts +38 -0
  72. package/dist/src/harness/workflow-subagent-limit.js +1 -0
  73. package/dist/src/harness/workflow-tool-description.d.ts +6 -1
  74. package/dist/src/harness/workflow-tool-description.js +5 -3
  75. package/dist/src/internal/application/package.js +1 -1
  76. package/dist/src/internal/authored-definition/core.js +1 -1
  77. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  78. package/dist/src/protocol/message.d.ts +19 -2
  79. package/dist/src/protocol/message.js +2 -2
  80. package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +34 -3
  81. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
  82. package/dist/src/public/channels/chat-sdk/index.d.ts +2 -0
  83. package/dist/src/public/channels/chat-sdk/index.js +1 -1
  84. package/dist/src/public/channels/chat-sdk/messageToUserContent.d.ts +13 -0
  85. package/dist/src/public/channels/chat-sdk/messageToUserContent.js +1 -0
  86. package/dist/src/public/channels/chat-sdk/notImplemented.d.ts +7 -0
  87. package/dist/src/public/channels/chat-sdk/notImplemented.js +1 -0
  88. package/dist/src/public/channels/slack/api.d.ts +14 -12
  89. package/dist/src/public/channels/slack/api.js +1 -1
  90. package/dist/src/public/definitions/tool.d.ts +37 -0
  91. package/dist/src/public/definitions/tool.js +1 -1
  92. package/dist/src/public/tools/index.d.ts +1 -1
  93. package/dist/src/public/tools/index.js +1 -1
  94. package/dist/src/runtime/actions/types.d.ts +9 -0
  95. package/dist/src/runtime/actions/types.js +1 -1
  96. package/dist/src/runtime/agent/bootstrap.d.ts +2 -0
  97. package/dist/src/runtime/agent/bootstrap.js +1 -1
  98. package/dist/src/runtime/resolve-agent.js +1 -1
  99. package/dist/src/runtime/sandbox/keys.js +1 -1
  100. package/dist/src/runtime/sandbox/template-plan.js +1 -1
  101. package/dist/src/runtime/session-callback-route.js +1 -1
  102. package/dist/src/runtime/skills/sandbox-access.d.ts +1 -1
  103. package/dist/src/runtime/skills/sandbox-access.js +1 -1
  104. package/dist/src/runtime/workspace/seed-files.js +1 -1
  105. package/dist/src/setup/scaffold/create/project.js +1 -1
  106. package/dist/src/setup/scaffold/create/web-template.d.ts +2 -2
  107. package/dist/src/setup/scaffold/create/web-template.js +77 -3
  108. package/dist/src/shared/agent-definition.d.ts +31 -3
  109. package/dist/src/shared/skill-package.d.ts +2 -2
  110. package/dist/src/shared/skill-package.js +1 -1
  111. package/dist/src/shared/skill-paths.d.ts +28 -0
  112. package/dist/src/shared/skill-paths.js +2 -0
  113. package/dist/src/shared/token-usage.d.ts +22 -0
  114. package/dist/src/shared/token-usage.js +1 -0
  115. package/dist/src/svelte/index.js +1 -1
  116. package/dist/src/svelte/use-eve-agent.js +1 -1
  117. package/dist/src/vue/index.js +1 -1
  118. package/dist/src/vue/use-eve-agent.js +1 -1
  119. package/docs/agent-config.md +32 -15
  120. package/docs/channels/chat-sdk.mdx +209 -0
  121. package/docs/channels/meta.json +1 -0
  122. package/docs/concepts/context-control.md +2 -2
  123. package/docs/concepts/default-harness.md +10 -0
  124. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  125. package/docs/guides/client/messages.mdx +4 -0
  126. package/docs/guides/client/streaming.mdx +17 -12
  127. package/docs/guides/dynamic-workflows.md +87 -0
  128. package/docs/guides/frontend/overview.mdx +5 -1
  129. package/docs/guides/meta.json +1 -0
  130. package/docs/guides/remote-agents.md +1 -0
  131. package/docs/reference/project-layout.md +5 -4
  132. package/docs/reference/typescript-api.md +26 -26
  133. package/docs/sandbox.mdx +1 -1
  134. package/docs/skills.mdx +3 -1
  135. package/docs/subagents.mdx +2 -1
  136. package/package.json +1 -1
@@ -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
  }
@@ -52,13 +52,13 @@ When the task is novel or ambiguous, gather evidence first, then answer with the
52
52
  remaining uncertainty.
53
53
  ```
54
54
 
55
- Packaged skills are useful when you also want sibling files such as `references/`, `assets/`, or `scripts/` under the same skill directory. Those paths show up under the runtime workspace root, so the model can inspect them with the normal file or shell tools instead of pasting their content into the prompt.
55
+ Packaged skills are useful when you also want sibling files such as `references/`, `assets/`, or `scripts/` under the same skill directory. Those files are available under the runtime skill root, normally `$HOME/.agents/skills/<skill>/`. Relative references inside a `SKILL.md` resolve from that specific skill directory, so `references/checklist.md` means `$HOME/.agents/skills/<skill>/references/checklist.md` unless eve has fallen back to `/workspace/skills/<skill>/`.
56
56
 
57
57
  See [Skills](../skills) for the full authoring model and install notes.
58
58
 
59
59
  ## Put runtime files in the workspace, not the prompt
60
60
 
61
- eve does not inline the entire authored surface into the prompt. Instead, it gives the model a shallow workspace hint and runtime tools to inspect deeper when needed. Skill files are available under the active workspace root, and the model inspects them with the shared `bash` tool, which keeps prompts smaller and makes file and command work explicit.
61
+ eve does not inline the entire authored surface into the prompt. Instead, it gives the model a shallow workspace hint, a separate skill-root hint, and runtime tools to inspect deeper when needed. Skill package files are outside `/workspace` in the normal case, and the model inspects them with the shared `bash` tool, which keeps prompts smaller and makes file and command work explicit.
62
62
 
63
63
  See [Sandbox](../sandbox) for the workspace and sandbox model.
64
64
 
@@ -87,6 +87,16 @@ Three moves shape the harness. The right one depends on whether the model should
87
87
  - **Disable** when the model should not have the capability at all. A `disableTool()` sentinel removes the built-in, and the model never sees it. Reach for this to lock down `bash` or `web_fetch` in an agent that should not run shell commands or fetch arbitrary URLs.
88
88
  - **Author a new tool** when you want a capability the harness does not ship. Give it a fresh slug under `agent/tools/` and it joins the built-ins instead of replacing one. See [Tools](../tools) for the authoring model.
89
89
 
90
+ ## The opt-in `Workflow` tool
91
+
92
+ An experimental `Workflow` tool ships but stays off by default. To turn it on, re-export the opt-in marker from `agent/tools/workflow.ts`:
93
+
94
+ ```ts
95
+ export { ExperimentalWorkflow as default } from "eve/tools";
96
+ ```
97
+
98
+ With it on, the model can orchestrate the agent's own subagents from model-authored JavaScript, all as one durable step. The tool is root-only — delegated subagent sessions never see it — and one program may dispatch at most `limits.maxSubagents` subagent calls (default 100). See [Dynamic workflows](../guides/dynamic-workflows).
99
+
90
100
  ## What to read next
91
101
 
92
102
  - [Tools](../tools): define your own tools, gate them on approval, and shape their output with `toModelOutput`
@@ -38,7 +38,7 @@ The stream is newline-delimited JSON (NDJSON), one event per line:
38
38
  | ------------------------- | ---------------------------------------------------------------------------------------------------------------- |
39
39
  | `session.started` | A durable session was created. |
40
40
  | `turn.started` | A new turn began. |
41
- | `message.received` | An inbound user message was accepted. |
41
+ | `message.received` | An inbound user message was accepted; carries flattened text plus structured text/file parts. |
42
42
  | `step.started` | A model step began. |
43
43
  | `actions.requested` | The model requested one or more actions, including tool calls; calls stream before execution. |
44
44
  | `action.result` | A tool call returned. |
@@ -98,6 +98,10 @@ const response = await session.send({
98
98
  await response.result();
99
99
  ```
100
100
 
101
+ The stream confirms the turn with `message.received`. Its `data.message` remains the flattened
102
+ summary for compatibility, and `data.parts` contains structured text and file metadata for clients
103
+ that render attachments. File parts never include raw bytes or internal sandbox paths.
104
+
101
105
  ## Answer human input requests
102
106
 
103
107
  Tools can pause for approval or ask the user a question. The stream emits `input.requested` with one or more requests. Reply through the same session with `inputResponses`:
@@ -61,21 +61,26 @@ function handleEvent(event: HandleMessageStreamEvent) {
61
61
 
62
62
  The most common UI events are:
63
63
 
64
- | Event | Use |
65
- | -------------------- | ---------------------------------------------------------------- |
66
- | `message.received` | Confirm the user message landed. |
67
- | `reasoning.appended` | Render reasoning deltas when the model provides them. |
68
- | `message.appended` | Render assistant text deltas. |
69
- | `actions.requested` | Show tool calls as the model requests them, before execution. |
70
- | `action.result` | Show tool call results. |
71
- | `input.requested` | Pause the UI for approval or a question answer. |
72
- | `result.completed` | Read structured output from an [output schema](./output-schema). |
73
- | `session.waiting` | Enable the composer for the next turn. |
74
- | `session.completed` | Mark the conversation terminal. |
75
- | `session.failed` | Mark the conversation failed. |
64
+ | Event | Use |
65
+ | -------------------- | ------------------------------------------------------------------------------ |
66
+ | `message.received` | Confirm the user message landed; `data.parts` includes text and file metadata. |
67
+ | `reasoning.appended` | Render reasoning deltas when the model provides them. |
68
+ | `message.appended` | Render assistant text deltas. |
69
+ | `actions.requested` | Show tool calls as the model requests them, before execution. |
70
+ | `action.result` | Show tool call results. |
71
+ | `input.requested` | Pause the UI for approval or a question answer. |
72
+ | `result.completed` | Read structured output from an [output schema](./output-schema). |
73
+ | `session.waiting` | Enable the composer for the next turn. |
74
+ | `session.completed` | Mark the conversation terminal. |
75
+ | `session.failed` | Mark the conversation failed. |
76
76
 
77
77
  For the complete event table, see [Sessions, runs & streaming](../../concepts/sessions-runs-and-streaming).
78
78
 
79
+ When a submitted message includes attachments, `message.received.data.message` stays the
80
+ flattened compatibility summary, while `message.received.data.parts` carries renderable text and
81
+ file metadata. File parts never include raw bytes or internal sandbox paths; `url` appears only for
82
+ client-resolvable `http(s)` and `data:` URLs.
83
+
79
84
  ## Authorization pauses
80
85
 
81
86
  `authorization.required` is different from the normal `session.waiting` boundary. It means a connection needs OAuth or another authorization challenge before the parked turn can continue. Chat UIs should render the authorization prompt, disable ordinary text input for that session, and persist the event with the rest of the chat history.
@@ -0,0 +1,87 @@
1
+ ---
2
+ title: "Dynamic Workflows"
3
+ description: "The experimental Workflow tool: let the model orchestrate its own subagents from model-authored JavaScript as one durable step."
4
+ ---
5
+
6
+ The experimental `Workflow` tool lets the model write JavaScript that coordinates the agent's own subagents as a single durable step. The program can run them in sequence, feed one result into the next, fan out over a list, and combine the results. You enable the capability and the model decides and runs the orchestration.
7
+
8
+ A single turn can already call several subagents, and parallel tool calls dispatch concurrently. What a workflow adds is _programmatic_ coordination. The program decides how many subagents to run based on an earlier result, which output feeds which call, and how to combine everything. That is logic the model cannot express as a few one-off calls.
9
+
10
+ ## Enable the Workflow tool
11
+
12
+ Re-export the opt-in marker as the default export of `agent/tools/workflow.ts`. The marker name carries the "experimental" warning, but the tool the model actually sees is named `Workflow`.
13
+
14
+ ```ts title="agent/tools/workflow.ts"
15
+ export { ExperimentalWorkflow as default } from "eve/tools";
16
+ ```
17
+
18
+ Without that file, the `Workflow` tool stays off. It earns its keep only when the agent has subagents (or the built-in `agent`) worth coordinating:
19
+
20
+ ```ts title="agent/subagents/analyst/agent.ts"
21
+ import { defineAgent } from "eve";
22
+
23
+ export default defineAgent({
24
+ description: "Analyzes one metric: queries, computes, writes a short finding.",
25
+ model: "anthropic/claude-opus-4.8",
26
+ });
27
+ ```
28
+
29
+ When asked for a weekly business review, the model picks the metrics, runs one `analyst` per metric in parallel, and combines the findings. The program below is the kind of JavaScript the model authors. It fans `analyst` out over a runtime-decided list of metrics and merges the results:
30
+
31
+ ```js
32
+ const metrics = ["revenue", "signups", "churn"];
33
+ const findings = await Promise.all(
34
+ metrics.map((metric) => tools.analyst({ message: `Summarize last week's ${metric}.` })),
35
+ );
36
+ return findings.join("\n\n");
37
+ ```
38
+
39
+ Each `tools.analyst(...)` call dispatches a child subagent, so the parent stream records one `subagent.called` per metric and one `subagent.completed` as each finishes:
40
+
41
+ ```json
42
+ { "type": "subagent.called", "data": { "name": "analyst", "toolName": "analyst", "callId": "call_1", "childSessionId": "ses_a1", "sequence": 0 } }
43
+ { "type": "subagent.called", "data": { "name": "analyst", "toolName": "analyst", "callId": "call_2", "childSessionId": "ses_a2", "sequence": 1 } }
44
+ { "type": "subagent.called", "data": { "name": "analyst", "toolName": "analyst", "callId": "call_3", "childSessionId": "ses_a3", "sequence": 2 } }
45
+ { "type": "subagent.completed", "data": { "subagentName": "analyst", "callId": "call_1", "output": "..." } }
46
+ { "type": "subagent.completed", "data": { "subagentName": "analyst", "callId": "call_2", "output": "..." } }
47
+ { "type": "subagent.completed", "data": { "subagentName": "analyst", "callId": "call_3", "output": "..." } }
48
+ ```
49
+
50
+ ## What a workflow can orchestrate
51
+
52
+ A workflow reaches only this agent's own agents: the built-in `agent` (a copy of itself), declared [subagents](../subagents), and [remote agents](./remote-agents). That is the whole list. No files, network, shell, skills, or connections. A workflow is a coordination layer over subagents, not a place to do other work. Each call can still request structured output via `outputSchema`, exactly like a direct subagent delegation.
53
+
54
+ ## Caps on workflow-spawned subagents
55
+
56
+ Workflow orchestration is capped in two independent ways.
57
+
58
+ **Per-program call budget.** One Workflow program may dispatch at most `limits.maxSubagents` subagent calls in total, counted across the whole program — sequential and parallel calls alike. The default is 100. Calls beyond the budget do not start a child session; they resolve inside the program with a `WORKFLOW_SUBAGENT_LIMIT_REACHED` error result, and the budget is stated in the tool's description so the model sizes its fan-out to fit.
59
+
60
+ ```ts title="agent/agent.ts"
61
+ export default defineAgent({
62
+ model: "anthropic/claude-sonnet-5",
63
+ limits: {
64
+ maxSubagents: 4,
65
+ },
66
+ });
67
+ ```
68
+
69
+ **Root-only, one level of orchestration.** Only the root session ever sees the `Workflow` tool. The subagents a workflow spawns are ordinary delegated child sessions: they never receive the `Workflow` tool themselves, so a workflow cannot recursively spawn more workflows. Direct (non-workflow) delegation by those children stays subject to the usual `limits.maxSubagentDepth` cap (see [Subagents](../subagents)).
70
+
71
+ ## Where the JavaScript runs
72
+
73
+ The orchestration code never touches the agent's process. The runtime hands the program text to a small isolated JavaScript engine (a QuickJS sandbox) and runs it there. Nothing from the host realm crosses in, so there is no `process`, no `globalThis` from the agent, and no `import`/`require`. The program can reach exactly two things, the agent functions bridged in as `tools.<name>` and the ordinary language built-ins.
74
+
75
+ That is an allowlist, not a denylist. The sandbox cannot read files, open a socket, or see an environment variable because those are not present, not because each one is blocked in turn. When the program calls an agent function, that call bridges back out to the runtime, which dispatches it exactly like a direct delegation. The orchestration glue stays inside the sandbox.
76
+
77
+ ## Durability, approvals, and observability
78
+
79
+ - **Durable.** The whole orchestration counts as one step. Subagents dispatched together run concurrently, and if a run parks (suspends durably without holding compute; see [Execution model & durability](../concepts/execution-model-and-durability)) on a long-running or human-gated child, it resumes where it left off after a restart.
80
+ - **Approval-safe.** A subagent that needs human approval (HITL, human-in-the-loop) mid-run surfaces its request to the user, and the workflow picks back up once that is answered, same as direct delegation.
81
+ - **Observable.** Every orchestrated subagent emits the usual `subagent.called` / `subagent.completed` events on the parent stream and gets its own child session and stream. The telemetry matches direct delegation, so existing dashboards and cost attribution keep working.
82
+
83
+ ## What to read next
84
+
85
+ - Declare the subagents a workflow orchestrates → [Subagents](../subagents)
86
+ - Call another deployment as one of those agents → [Remote agents](./remote-agents)
87
+ - The `agent/tools/` opt-in mechanism → [Default harness](../concepts/default-harness)
@@ -74,7 +74,7 @@ export function Chat() {
74
74
 
75
75
  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
76
 
77
- `data.messages` are `EveMessage[]` following the AI SDK `UIMessage` convention, so they drop straight into any AI SDK UI primitive that accepts a `UIMessage[]`. Parts include user text, assistant text, reasoning, tool calls, tool results, input requests, and connection authorization prompts.
77
+ `data.messages` are `EveMessage[]` following the AI SDK `UIMessage` convention, so they drop straight into any AI SDK UI primitive that accepts a `UIMessage[]`. Parts include user text, user file attachments, assistant text, reasoning, tool calls, tool results, input requests, and connection authorization prompts.
78
78
 
79
79
  ## Sending and streaming
80
80
 
@@ -98,6 +98,10 @@ await agent.send({
98
98
 
99
99
  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 active request, and `reset()` to clear local state so the next send starts a fresh durable session.
100
100
 
101
+ After eve confirms an attachment turn with `message.received`, the default reducer projects each
102
+ received attachment as a `file` part on the user message. The part includes `mediaType`, optional
103
+ `filename` and `size`, and a `url` only when the original attachment was browser-resolvable.
104
+
101
105
  ## Human-in-the-loop prompts
102
106
 
103
107
  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()`:
@@ -8,6 +8,7 @@
8
8
  "session-context",
9
9
  "state",
10
10
  "dynamic-capabilities",
11
+ "dynamic-workflows",
11
12
  "remote-agents",
12
13
  "dev-tui",
13
14
  "frontend",
@@ -60,4 +60,5 @@ Both failure paths surface to the parent as a failed tool result, so the caller
60
60
  ## What to read next
61
61
 
62
62
  - Local delegation and the isolation boundary → [Subagents](../subagents)
63
+ - Have the model orchestrate remote agents programmatically → [Dynamic workflows](./dynamic-workflows)
63
64
  - Securing the receiving deployment → [Auth & route protection](./auth-and-route-protection)
@@ -54,7 +54,7 @@ The Subagents column states whether a local subagent (`subagents/<id>/`) can aut
54
54
  | `channels/` | HTTP / messaging entrypoints | No | Root-only. |
55
55
  | `connections/` | External service connections (MCP, OpenAPI) | Yes | One connection per file; name derived from filename. |
56
56
  | `hooks/` | Lifecycle and stream-event subscribers | Yes | Module-backed only. Recursive directories supported. |
57
- | `skills/` | On-demand procedures and capability packs | Yes | Flat markdown, module-backed skills, or packaged skills. Seeded into `/workspace/skills/...`. |
57
+ | `skills/` | On-demand procedures and capability packs | Yes | Flat markdown, module-backed skills, or packaged skills. Seeded into `$HOME/.agents/skills/...`, with `/workspace/skills/...` as a fallback if `$HOME` is unavailable. |
58
58
  | `lib/` | Shared authored helper code | Yes | Import-only; not mounted into the workspace. |
59
59
  | `sandbox.ts` or `sandbox/sandbox.ts` | The agent's single sandbox | Yes | Use top-level `sandbox.ts` for a definition-only override; use `sandbox/sandbox.ts` + `sandbox/workspace/**` to also seed files. Framework default applies when neither is authored. |
60
60
  | `sandbox/workspace/**` | Files seeded into the sandbox | Yes | Mirrored into `/workspace/...` at session bootstrap. |
@@ -62,13 +62,14 @@ The Subagents column states whether a local subagent (`subagents/<id>/`) can aut
62
62
  | `schedules/` | Recurring jobs | No | Each schedule is `<name>.ts` (default-exported `defineSchedule`) or `<name>.md` (frontmatter `cron:` + prompt body). Recursive nesting supported. Root-only. |
63
63
  | `subagents/` | Specialist child agents | Yes | Each child is its own local package under `subagents/<id>/`. Nested subagents are supported. |
64
64
 
65
- ## What reaches the runtime workspace
65
+ ## What reaches the runtime sandbox
66
66
 
67
- eve does not mount the whole tree. Only two sources land in the sandbox workspace:
67
+ eve does not mount the whole tree. Authored workspace files land in the sandbox workspace:
68
68
 
69
- - `skills/` files → `/workspace/skills/...`
70
69
  - `agent/sandbox/workspace/**` → `/workspace/...` at session bootstrap
71
70
 
71
+ Skill files land outside the workspace, under `$HOME/.agents/skills/...`. If `$HOME` is unavailable, eve falls back to `/workspace/skills/...`. Packaged skill references such as `references/checklist.md` resolve relative to the directory containing that skill's `SKILL.md`.
72
+
72
73
  Everything in `lib/` stays import-only source code and never reaches the workspace.
73
74
 
74
75
  ## Local subagent layout
@@ -52,7 +52,7 @@ export default defineTool({
52
52
  | `mockModel` | `eve/evals` | Deterministic fixture agent models | [Evals](../evals/overview) |
53
53
  | `useEveAgent` | `eve/react`, `eve/vue`, `eve/svelte` | frontend | [Frontend](../guides/frontend/overview) |
54
54
 
55
- A few non-`define*` helpers round out the set: `disableTool` from `eve/tools` (see [Default harness](../concepts/default-harness)), the route verbs `GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`WS` from `eve/channels`, the approval policies `always`/`once`/`never` from `eve/tools/approval`, and the channel auth helpers `localDev`/`vercelOidc`/`placeholderAuth` from `eve/channels/auth`. To wrap a built-in tool, import its default value from `eve/tools/defaults` (`bash`, `readFile`, `writeFile`, `glob`, `grep`, `webFetch`, `webSearch`, `todo`, `loadSkill`). `AgentReasoningDefinition` is exported from `eve` for the top-level `defineAgent({ reasoning })` setting. `AgentLimitsDefinition` is exported for `defineAgent({ limits })`. `AgentWorkflowDefinition` and `AgentWorkflowWorldDefinition` are exported from `eve` for the `defineAgent({ experimental: { workflow } })` config shape.
55
+ A few non-`define*` helpers round out the set: `disableTool` and `ExperimentalWorkflow` from `eve/tools` (see [Default harness](../concepts/default-harness)), the route verbs `GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`WS` from `eve/channels`, the approval policies `always`/`once`/`never` from `eve/tools/approval`, and the channel auth helpers `localDev`/`vercelOidc`/`placeholderAuth` from `eve/channels/auth`. To wrap a built-in tool, import its default value from `eve/tools/defaults` (`bash`, `readFile`, `writeFile`, `glob`, `grep`, `webFetch`, `webSearch`, `todo`, `loadSkill`). `AgentReasoningDefinition` is exported from `eve` for the top-level `defineAgent({ reasoning })` setting. `AgentLimitsDefinition` is exported for `defineAgent({ limits })`. `AgentWorkflowDefinition` and `AgentWorkflowWorldDefinition` are exported from `eve` for the `defineAgent({ experimental: { workflow } })` config shape.
56
56
 
57
57
  ## Runtime context (`ctx`)
58
58
 
@@ -68,31 +68,31 @@ A few non-`define*` helpers round out the set: `disableTool` from `eve/tools` (s
68
68
 
69
69
  ## Imports at a glance
70
70
 
71
- | Import | Holds |
72
- | ----------------------------------------------------------- | --------------------------------------------------------- |
73
- | `eve` | `defineAgent`, `defineRemoteAgent`, agent config types |
74
- | `eve/tools` | `defineTool`, `defineDynamic`, `disableTool` |
75
- | `eve/tools/defaults` | the built-in tools as plain values |
76
- | `eve/tools/approval` | `always`, `once`, `never` |
77
- | `eve/connections` | `defineMcpClientConnection`, `defineOpenAPIConnection` |
78
- | `eve/channels` | `defineChannel`, route verbs |
79
- | `eve/channels/eve` | `eveChannel` |
80
- | `eve/channels/auth` | `localDev`, `vercelOidc`, `placeholderAuth` |
81
- | `eve/channels/{slack,discord,teams,telegram,twilio,github}` | platform channel factories |
82
- | `eve/hooks` | `defineHook` |
83
- | `eve/schedules` | `defineSchedule` |
84
- | `eve/skills` | `defineSkill`, `defineDynamic` |
85
- | `eve/instructions` | `defineInstructions`, `defineDynamic` |
86
- | `eve/context` | `defineState`, session and state types |
87
- | `eve/sandbox` | `defineSandbox`, backends |
88
- | `eve/instrumentation` | `defineInstrumentation`, `isChannel` |
89
- | `eve/evals` | `defineEval`, `defineEvalConfig`, `mockModel`, eval types |
90
- | `eve/evals/expect` | `includes`, `equals`, `matches`, `similarity` |
91
- | `eve/evals/reporters` | `Braintrust`, `JUnit`, `EvalReporter` |
92
- | `eve/evals/loaders` | `loadJson`, `loadYaml` |
93
- | `eve/react`, `eve/vue`, `eve/svelte` | `useEveAgent` |
94
- | `eve/next`, `eve/nuxt`, `eve/sveltekit` | framework bundler plugins |
95
- | [`eve/client`](../guides/client/overview) | `Client`, `ClientSession` |
71
+ | Import | Holds |
72
+ | ----------------------------------------------------------- | -------------------------------------------------------------------- |
73
+ | `eve` | `defineAgent`, `defineRemoteAgent`, agent config types |
74
+ | `eve/tools` | `defineTool`, `defineDynamic`, `disableTool`, `ExperimentalWorkflow` |
75
+ | `eve/tools/defaults` | the built-in tools as plain values |
76
+ | `eve/tools/approval` | `always`, `once`, `never` |
77
+ | `eve/connections` | `defineMcpClientConnection`, `defineOpenAPIConnection` |
78
+ | `eve/channels` | `defineChannel`, route verbs |
79
+ | `eve/channels/eve` | `eveChannel` |
80
+ | `eve/channels/auth` | `localDev`, `vercelOidc`, `placeholderAuth` |
81
+ | `eve/channels/{slack,discord,teams,telegram,twilio,github}` | platform channel factories |
82
+ | `eve/hooks` | `defineHook` |
83
+ | `eve/schedules` | `defineSchedule` |
84
+ | `eve/skills` | `defineSkill`, `defineDynamic` |
85
+ | `eve/instructions` | `defineInstructions`, `defineDynamic` |
86
+ | `eve/context` | `defineState`, session and state types |
87
+ | `eve/sandbox` | `defineSandbox`, backends |
88
+ | `eve/instrumentation` | `defineInstrumentation`, `isChannel` |
89
+ | `eve/evals` | `defineEval`, `defineEvalConfig`, `mockModel`, eval types |
90
+ | `eve/evals/expect` | `includes`, `equals`, `matches`, `similarity` |
91
+ | `eve/evals/reporters` | `Braintrust`, `JUnit`, `EvalReporter` |
92
+ | `eve/evals/loaders` | `loadJson`, `loadYaml` |
93
+ | `eve/react`, `eve/vue`, `eve/svelte` | `useEveAgent` |
94
+ | `eve/next`, `eve/nuxt`, `eve/sveltekit` | framework bundler plugins |
95
+ | [`eve/client`](../guides/client/overview) | `Client`, `ClientSession` |
96
96
 
97
97
  Exported types ship from the same entrypoint as the helper they describe (for example `ToolDefinition` and `ToolContext` from `eve/tools`). For the exhaustive list, read `packages/eve/src/public/index.ts`.
98
98
 
package/docs/sandbox.mdx CHANGED
@@ -82,7 +82,7 @@ agent/sandbox/
82
82
  scripts/run.sh ← lands at /workspace/scripts/run.sh
83
83
  ```
84
84
 
85
- Every file under `workspace/` mirrors into the sandbox cwd with its structure intact, and eve lists the top-level entries to the model in the prompt automatically. One subtree is off limits. Skill discovery already seeds skill files under `/workspace/skills/`, so authoring `agent/sandbox/workspace/skills/...` is rejected; put those under `agent/skills/` instead.
85
+ Every file under `workspace/` mirrors into the sandbox cwd with its structure intact, and eve lists the top-level entries to the model in the prompt automatically. `agent/skills/` files are materialized separately under `$HOME/.agents/skills/`, so `agent/sandbox/workspace/skills/...` is an ordinary workspace subtree when you choose to author one.
86
86
 
87
87
  ## Overriding the sandbox
88
88
 
package/docs/skills.mdx CHANGED
@@ -9,6 +9,8 @@ A skill is a model-loadable procedure that follows the `SKILL.md` convention. It
9
9
 
10
10
  eve scans the files under `agent/skills/` and exposes each one's description to the model alongside a framework-owned `load_skill` tool. When a request matches a skill's description (or you name the skill outright), the model calls `load_skill`, and eve appends that skill's markdown to the active turn's context.
11
11
 
12
+ At runtime, skill package files are placed under `$HOME/.agents/skills/<skill>/`. If `$HOME` is unavailable in a sandbox, eve falls back to `/workspace/skills/<skill>/`. `load_skill` reads `SKILL.md` automatically; any sibling references inside that file, such as `references/checklist.md`, are relative to the directory containing that specific `SKILL.md`.
13
+
12
14
  The description is a routing hint, not a label. Write it as the task that should trigger activation:
13
15
 
14
16
  ```md
@@ -68,7 +70,7 @@ const research = ctx.getSkill("research");
68
70
  const checklist = await research.file("references/checklist.md").text();
69
71
  ```
70
72
 
71
- The handle exposes the skill's `name` and `file(relativePath)`; file content is read lazily from the active sandbox.
73
+ The handle exposes the skill's `name` and `file(relativePath)`; file content is read lazily from the active sandbox, relative to that skill package directory.
72
74
 
73
75
  ## Dynamic skills
74
76
 
@@ -94,7 +94,7 @@ at depths 1 through 4; a session already at depth 4 cannot delegate again.
94
94
 
95
95
  At the configured depth, eve stops advertising subagent tools, including declared local subagents, remote-agent tools, the built-in `agent` tool, and the `Workflow` orchestration tool when it would only expose subagents. If a stale or forced subagent call still reaches execution, eve blocks it and returns an error tool result instead of starting another child session.
96
96
 
97
- `Workflow` is root-only. Delegated subagent sessions can still call their own visible subagent tools until the depth cap, but they do not receive the `Workflow` orchestration wrapper.
97
+ `Workflow` is root-only. Delegated subagent sessions can still call their own visible subagent tools until the depth cap, but they do not receive the `Workflow` orchestration wrapper. The number of subagent calls one Workflow program may dispatch is capped separately by `limits.maxSubagents` (default 100); see [Dynamic workflows](./guides/dynamic-workflows).
98
98
 
99
99
  A declared subagent's tool name is the bare path-derived name, with no prefix. `agent/subagents/researcher/` registers as the tool `researcher`. Unlike connection tools (`<connection>__<tool>`), it carries no namespace, so the model, approvals, logs, and evals all reference it by that name. Its input schema is:
100
100
 
@@ -118,3 +118,4 @@ Split out a subagent when the task needs a different prompt or specialist role,
118
118
  ## What to read next
119
119
 
120
120
  - [Remote agents](./guides/remote-agents): call another eve deployment as a subagent.
121
+ - [Dynamic workflows](./guides/dynamic-workflows): have the model orchestrate its subagents programmatically (fan-out, map-reduce).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [