eve 0.84.0 → 0.85.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 (248) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/src/channel/channel-operations.d.ts +2 -18
  3. package/dist/src/channel/compiled-channel.d.ts +3 -7
  4. package/dist/src/channel/cross-channel-receive.d.ts +13 -11
  5. package/dist/src/channel/cross-channel-receive.js +1 -1
  6. package/dist/src/channel/delivery.js +1 -1
  7. package/dist/src/channel/schedule.js +1 -1
  8. package/dist/src/channel/session.d.ts +26 -4
  9. package/dist/src/channel/session.js +1 -1
  10. package/dist/src/channel/types.d.ts +2 -0
  11. package/dist/src/chunks/{use-eve-agent-BbbguIdB.js → use-eve-agent-B2jCfcnG.js} +29 -12
  12. package/dist/src/chunks/{use-eve-agent-DWjyzplb.js → use-eve-agent-DCLFi26P.js} +29 -12
  13. package/dist/src/cli/commands/init.js +1 -1
  14. package/dist/src/client/client.js +1 -1
  15. package/dist/src/client/delivery.d.ts +2 -0
  16. package/dist/src/client/delivery.js +1 -1
  17. package/dist/src/client/open-stream.js +1 -1
  18. package/dist/src/client/optimistic-message-submissions.js +1 -1
  19. package/dist/src/client/session-store.js +1 -1
  20. package/dist/src/client/session.js +1 -1
  21. package/dist/src/client/sessions.d.ts +1 -0
  22. package/dist/src/client/sessions.js +1 -1
  23. package/dist/src/client/types.d.ts +7 -4
  24. package/dist/src/compiled/.vendor-stamp.json +2 -2
  25. package/dist/src/compiled/@chat-adapter/slack/index.js +13 -13
  26. package/dist/src/compiled/@vercel/sandbox/api-client/api-client.d.ts +75 -0
  27. package/dist/src/compiled/@vercel/sandbox/api-client/validators.d.ts +4 -0
  28. package/dist/src/compiled/@vercel/sandbox/command.d.ts +78 -0
  29. package/dist/src/compiled/@vercel/sandbox/drive.d.ts +41 -1
  30. package/dist/src/compiled/@vercel/sandbox/index.js +13 -13
  31. package/dist/src/compiled/@vercel/sandbox/network-policy.d.ts +35 -4
  32. package/dist/src/compiled/@vercel/sandbox/sandbox.d.ts +18 -4
  33. package/dist/src/compiled/@vercel/sandbox/session.d.ts +29 -3
  34. package/dist/src/compiled/@vercel/sandbox/utils/pipe-stdin.d.ts +19 -0
  35. package/dist/src/compiled/_chunks/client/{core-CspsxXR-.js → core-BsvaaMUJ.js} +1 -1
  36. package/dist/src/compiled/_chunks/node/{auth-Dztlx9pE.js → auth-v5pS3mLo.js} +1 -1
  37. package/dist/src/compiled/_chunks/node/is-stream-DsuQX_bd.js +1 -0
  38. package/dist/src/compiled/_chunks/node/token-DHGH-jVc.js +1 -0
  39. package/dist/src/compiled/_chunks/node/token-util-BH2pw9Qm.js +25 -0
  40. package/dist/src/compiled/_chunks/node/token-util-CcvxNv76.js +1 -0
  41. package/dist/src/compiled/_chunks/node/version-C2PfNyEv.js +1 -0
  42. package/dist/src/compiled/zod/index.js +1 -1
  43. package/dist/src/compiled/zod/v4/core.js +1 -1
  44. package/dist/src/compiled/zod-validation-error/index.js +1 -1
  45. package/dist/src/compiler/artifacts.d.ts +1 -0
  46. package/dist/src/compiler/artifacts.js +1 -1
  47. package/dist/src/compiler/manifest.d.ts +2 -2
  48. package/dist/src/compiler/manifest.js +1 -1
  49. package/dist/src/compiler/model-catalog.d.ts +8 -18
  50. package/dist/src/compiler/model-catalog.js +1 -1
  51. package/dist/src/compiler/normalize-agent-config.js +1 -1
  52. package/dist/src/compiler/normalize-manifest-types.d.ts +3 -0
  53. package/dist/src/compiler/normalize-manifest.js +1 -1
  54. package/dist/src/context/keys.d.ts +7 -8
  55. package/dist/src/context/keys.js +1 -1
  56. package/dist/src/eve-channel/index.js +1 -1
  57. package/dist/src/eve-channel/request.d.ts +1 -0
  58. package/dist/src/eve-channel/request.js +1 -1
  59. package/dist/src/eve-channel/support.js +1 -1
  60. package/dist/src/execution/agent-sessions/context.d.ts +5 -1
  61. package/dist/src/execution/agent-sessions/context.js +1 -1
  62. package/dist/src/execution/agent-sessions/steps.js +1 -1
  63. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  64. package/dist/src/execution/legacy-session/snapshot.d.ts +1 -2
  65. package/dist/src/execution/legacy-session/snapshot.js +1 -1
  66. package/dist/src/execution/proxied-deliver-step.js +1 -1
  67. package/dist/src/execution/public-destination.d.ts +3 -0
  68. package/dist/src/execution/public-destination.js +1 -1
  69. package/dist/src/execution/publish-session-events.js +1 -1
  70. package/dist/src/execution/sandbox/bindings/docker-cli.d.ts +2 -0
  71. package/dist/src/execution/sandbox/bindings/docker-cli.js +1 -1
  72. package/dist/src/execution/sandbox/bindings/docker-connection.js +1 -1
  73. package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
  74. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  75. package/dist/src/execution/sandbox/multiplexed-command.d.ts +11 -1
  76. package/dist/src/execution/sandbox/multiplexed-command.js +1 -1
  77. package/dist/src/execution/sandbox/session.js +1 -1
  78. package/dist/src/execution/session/checkpoint-migrations.d.ts +16 -5
  79. package/dist/src/execution/session/checkpoint-migrations.js +1 -1
  80. package/dist/src/execution/session/checkpoint-refusal.d.ts +4 -0
  81. package/dist/src/execution/session/checkpoint-refusal.js +1 -0
  82. package/dist/src/execution/session/entry.js +1 -1
  83. package/dist/src/execution/session/handoff-steps.d.ts +7 -1
  84. package/dist/src/execution/session/handoff-steps.js +1 -1
  85. package/dist/src/execution/session/imported-conversation.d.ts +2 -0
  86. package/dist/src/execution/session/imported-conversation.js +1 -0
  87. package/dist/src/execution/session/interrupted-step.js +1 -1
  88. package/dist/src/execution/session/pre-v27-checkpoint.d.ts +36 -0
  89. package/dist/src/execution/session/pre-v27-checkpoint.js +1 -0
  90. package/dist/src/execution/session/program.d.ts +2 -0
  91. package/dist/src/execution/session/program.js +1 -1
  92. package/dist/src/execution/session/turn-step.js +1 -1
  93. package/dist/src/execution/session-callback-request.js +1 -1
  94. package/dist/src/execution/session-inbox/address.d.ts +1 -0
  95. package/dist/src/execution/session-inbox/handle.d.ts +7 -0
  96. package/dist/src/execution/session-inbox/handle.js +1 -0
  97. package/dist/src/execution/session-inbox/owner.d.ts +4 -4
  98. package/dist/src/execution/session-inbox/owner.js +1 -1
  99. package/dist/src/execution/session-inbox/resume.d.ts +1 -0
  100. package/dist/src/execution/session-inbox/resume.js +1 -1
  101. package/dist/src/execution/tool-auth.js +1 -1
  102. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  103. package/dist/src/execution/tools/channel-to.d.ts +12 -0
  104. package/dist/src/execution/tools/channel-to.js +1 -0
  105. package/dist/src/execution/tools/workflow/body.d.ts +3 -0
  106. package/dist/src/execution/tools/workflow/body.js +1 -1
  107. package/dist/src/execution/tools/workflow/step-context.d.ts +5 -0
  108. package/dist/src/execution/tools/workflow/step-execution.js +1 -1
  109. package/dist/src/execution/tools/workflow/step.js +1 -1
  110. package/dist/src/execution/workflow-callback-url.d.ts +2 -3
  111. package/dist/src/execution/workflow-callback-url.js +1 -1
  112. package/dist/src/execution/workflow-runtime.js +1 -1
  113. package/dist/src/harness/compaction/step.js +1 -1
  114. package/dist/src/harness/model-call/call.d.ts +3 -1
  115. package/dist/src/harness/model-call/call.js +1 -1
  116. package/dist/src/harness/model-call/run.js +1 -1
  117. package/dist/src/harness/semantic-errors/rules/gateway.js +1 -1
  118. package/dist/src/harness/session-machine/transitions.js +1 -1
  119. package/dist/src/internal/application/compiled-artifacts.d.ts +2 -0
  120. package/dist/src/internal/application/compiled-artifacts.js +2 -2
  121. package/dist/src/internal/application/optional-package-install.js +1 -1
  122. package/dist/src/internal/application/stamped-package-version.js +1 -1
  123. package/dist/src/internal/attachments/limited-read.d.ts +2 -0
  124. package/dist/src/internal/attachments/limited-read.js +1 -1
  125. package/dist/src/internal/attachments/url-refs.js +1 -1
  126. package/dist/src/internal/capability-error.d.ts +3 -0
  127. package/dist/src/internal/capability-error.js +1 -0
  128. package/dist/src/internal/model-catalog.d.ts +39 -16
  129. package/dist/src/internal/model-catalog.js +1 -1
  130. package/dist/src/internal/nitro/host/dev-build-host.js +1 -1
  131. package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
  132. package/dist/src/protocol/delivery.d.ts +1 -0
  133. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +1 -1
  134. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  135. package/dist/src/public/channels/github/githubChannel.js +1 -1
  136. package/dist/src/public/channels/linear/linearChannel.js +1 -1
  137. package/dist/src/public/channels/linq/inboundContent.js +1 -1
  138. package/dist/src/public/channels/slack/answers.d.ts +2 -3
  139. package/dist/src/public/channels/slack/answers.js +1 -1
  140. package/dist/src/public/channels/slack/api.d.ts +23 -3
  141. package/dist/src/public/channels/slack/api.js +1 -1
  142. package/dist/src/public/channels/slack/app-manifest.d.ts +2 -0
  143. package/dist/src/public/channels/slack/app-manifest.js +1 -1
  144. package/dist/src/public/channels/slack/auth.d.ts +2 -1
  145. package/dist/src/public/channels/slack/auth.js +1 -1
  146. package/dist/src/public/channels/slack/code-channel.d.ts +42 -0
  147. package/dist/src/public/channels/slack/code-channel.js +1 -0
  148. package/dist/src/public/channels/slack/defaults.d.ts +7 -3
  149. package/dist/src/public/channels/slack/defaults.js +3 -3
  150. package/dist/src/public/channels/slack/hitl.d.ts +7 -17
  151. package/dist/src/public/channels/slack/hitl.js +1 -1
  152. package/dist/src/public/channels/slack/index.d.ts +4 -3
  153. package/dist/src/public/channels/slack/index.js +1 -1
  154. package/dist/src/public/channels/slack/interaction-cards.d.ts +0 -2
  155. package/dist/src/public/channels/slack/interaction-cards.js +1 -1
  156. package/dist/src/public/channels/slack/interactions.js +1 -1
  157. package/dist/src/public/channels/slack/open-code-channel.d.ts +30 -0
  158. package/dist/src/public/channels/slack/open-code-channel.js +1 -0
  159. package/dist/src/public/channels/slack/private-approval-delivery.d.ts +16 -4
  160. package/dist/src/public/channels/slack/private-approval-delivery.js +1 -1
  161. package/dist/src/public/channels/slack/renderers.d.ts +58 -6
  162. package/dist/src/public/channels/slack/renderers.js +1 -1
  163. package/dist/src/public/channels/slack/reply-delivery.d.ts +4 -0
  164. package/dist/src/public/channels/slack/reply-delivery.js +1 -1
  165. package/dist/src/public/channels/slack/request-card-render.d.ts +45 -0
  166. package/dist/src/public/channels/slack/request-card-render.js +1 -0
  167. package/dist/src/public/channels/slack/request-cards.d.ts +35 -0
  168. package/dist/src/public/channels/slack/request-cards.js +3 -0
  169. package/dist/src/public/channels/slack/sign-in-cancel.d.ts +3 -5
  170. package/dist/src/public/channels/slack/sign-in-cancel.js +1 -1
  171. package/dist/src/public/channels/slack/slackChannel.d.ts +109 -22
  172. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  173. package/dist/src/public/channels/slack/task-card.d.ts +3 -3
  174. package/dist/src/public/channels/slack/task-card.js +1 -1
  175. package/dist/src/public/channels/slack/thread-status.js +1 -1
  176. package/dist/src/public/channels/teams/api.js +1 -1
  177. package/dist/src/public/channels/teams/defaults.js +1 -1
  178. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  179. package/dist/src/public/channels/teams/verify.d.ts +9 -5
  180. package/dist/src/public/channels/teams/verify.js +1 -1
  181. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  182. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  183. package/dist/src/public/nuxt/module.js +1 -1
  184. package/dist/src/public/schedules/subscription.d.ts +10 -1
  185. package/dist/src/reactions/kinds/model.js +1 -1
  186. package/dist/src/reactions/kinds/subagent.js +1 -1
  187. package/dist/src/runtime/agent/resolve-model.d.ts +3 -4
  188. package/dist/src/runtime/agent/resolve-model.js +1 -1
  189. package/dist/src/runtime/loaders/artifact-paths.d.ts +1 -0
  190. package/dist/src/runtime/loaders/artifact-paths.js +1 -1
  191. package/dist/src/runtime/loaders/bundled-artifacts.d.ts +2 -0
  192. package/dist/src/runtime/loaders/bundled-artifacts.js +1 -1
  193. package/dist/src/runtime/loaders/model-metadata.d.ts +4 -0
  194. package/dist/src/runtime/loaders/model-metadata.js +1 -0
  195. package/dist/src/runtime/resolve-agent.js +1 -1
  196. package/dist/src/runtime/sessions/compiled-agent-cache.d.ts +3 -0
  197. package/dist/src/runtime/sessions/compiled-agent-cache.js +1 -1
  198. package/dist/src/runtime/subagents/dynamic-agent-config.d.ts +2 -2
  199. package/dist/src/runtime/subagents/dynamic-agent-config.js +1 -1
  200. package/dist/src/runtime/subagents/workflow.js +1 -1
  201. package/dist/src/sandbox/provider.d.ts +2 -0
  202. package/dist/src/setup/flows/model-source-change.js +1 -1
  203. package/dist/src/setup/index.d.ts +1 -1
  204. package/dist/src/setup/primitives/index.d.ts +1 -1
  205. package/dist/src/setup/primitives/pm/run.d.ts +0 -2
  206. package/dist/src/setup/scaffold/create/project.js +1 -1
  207. package/dist/src/shared/agent-definition.d.ts +0 -1
  208. package/dist/src/shared/channel-definition.d.ts +4 -4
  209. package/dist/src/shared/sandbox-session.d.ts +4 -0
  210. package/dist/src/subagents/adapter.js +1 -1
  211. package/dist/src/subagents/parent-notification.js +1 -1
  212. package/dist/src/svelte/index.js +1 -1
  213. package/dist/src/svelte/use-eve-agent.js +1 -1
  214. package/dist/src/tools/definition.d.ts +21 -0
  215. package/dist/src/tools/provided/ask-question-workflow.js +1 -1
  216. package/dist/src/tools/provided/sleep-workflow.js +1 -1
  217. package/dist/src/tools/workflow-definition.d.ts +4 -4
  218. package/dist/src/vue/index.js +1 -1
  219. package/dist/src/vue/use-eve-agent.js +1 -1
  220. package/docs/agent-config.md +5 -4
  221. package/docs/channels/custom.mdx +16 -3
  222. package/docs/channels/eve.mdx +2 -0
  223. package/docs/channels/linq.mdx +1 -1
  224. package/docs/channels/slack.mdx +185 -18
  225. package/docs/channels/teams.mdx +6 -2
  226. package/docs/concepts/default-harness.md +6 -0
  227. package/docs/concepts/upgrade-to-v27-events.md +2 -2
  228. package/docs/evals/judge.mdx +13 -0
  229. package/docs/experimental/dynamic-schedules.mdx +2 -2
  230. package/docs/guides/client/overview.mdx +24 -2
  231. package/docs/guides/remote-agents.md +8 -0
  232. package/docs/patterns/fork-conversations-into-new-threads.md +159 -0
  233. package/docs/patterns/meta.json +1 -0
  234. package/docs/reference/cli.md +1 -1
  235. package/docs/sandbox/custom-providers.mdx +1 -1
  236. package/docs/sandbox/index.mdx +1 -1
  237. package/docs/schedules.mdx +1 -1
  238. package/docs/tools/overview.mdx +34 -0
  239. package/docs/tools/workflows.mdx +51 -5
  240. package/package.json +2 -2
  241. package/dist/src/compiled/_chunks/node/token-Bedbhv5z.js +0 -1
  242. package/dist/src/compiled/_chunks/node/token-util-DpJNusVS.js +0 -1
  243. package/dist/src/compiled/_chunks/node/token-util-DwHQ2a3c.js +0 -1
  244. package/dist/src/compiled/_chunks/node/version-C__MqbLM.js +0 -1
  245. package/dist/src/public/channels/slack/approval-cards.d.ts +0 -25
  246. package/dist/src/public/channels/slack/approval-cards.js +0 -2
  247. package/dist/src/runtime/agent/model-catalog.d.ts +0 -10
  248. package/dist/src/runtime/agent/model-catalog.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # eve
2
2
 
3
+ ## 0.85.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 24c6b9f: Client requests now reject redirects by default, including session writes and event streams. Update redirecting base URLs to their final destination, use `redirect: "manual"` to stop at redirects, or explicitly opt into `"follow"` only when every redirect destination is trusted.
8
+ - c8328f8: Protected cross-project callbacks now require reciprocal Vercel Trusted Sources and an explicit `EVE_CALLBACK_TRUSTED_ORIGINS` allowlist on the remote deployment. Callback URLs no longer include the parent's deployment-wide bypass secret; listed origins instead receive the remote's short-lived OIDC credential.
9
+ - 5cfec0e: Slack renderers can restyle approval, question, and budget prompt cards with a new `interactionCard(card, next)` hook, while eve keeps routing, batching, action IDs, and card updates. Cards now update from the session for every request kind, so a question answered by a typed reply or withdrawn, or a settled budget prompt, no longer keeps live buttons.
10
+
11
+ Breaking changes:
12
+
13
+ - `SlackChannelState.pendingApprovalCards` is replaced by `interactionCards`. Cards posted before the upgrade keep their buttons.
14
+ - `SlackTaskCard` is renamed to `SlackCardMessage`, which both `taskCard` and `interactionCard` return.
15
+ - Clicking a question no longer rewrites its card from the webhook. The buttons stay until the session admits the answer, and apps that post eve's controls themselves no longer get that rewrite.
16
+ - An approval's buttons disappear once its answer is admitted, so a later click can't change it. A denied approval reads **Deny** instead of **Cancel**, and a direct-message request's thread note reports the same outcome as its card.
17
+
18
+ ### Patch Changes
19
+
20
+ - f20a7a1: Bind native Teams activities to the verified Bot Connector token's service URL before dispatch. Connector requests require HTTPS and reject redirects, keeping bearer credentials on the validated destination.
21
+ - 4f290a7: Sessions whose deployment runs eve 0.69 through 0.83 now move to a v27 deployment the next time they receive a message while idle, or earlier when a channel prepares them for the next message, instead of staying on their original deployment until they time out. eve compacts the earlier conversation as the session moves, so the model continues from a summary; the stored transcript stays intact, and new turns continue after it. Remembered tool approvals and usage limits start over.
22
+ - 0553a67: Keep continuation capabilities out of session lookup and child-interaction failure logs, including capability values embedded in error messages and causes. Failure diagnostics retain correlation fields and non-secret error details.
23
+ - e54f078: Render Teams mention display names and fallback IDs as literal text, preserving names that contain ampersands or angle brackets without changing mention identity.
24
+ - ba52da4: Remove the deprecated internal setup type alias `RunPnpmOptions`. Setup tooling should use `RunPackageManagerOptions`; package-manager command behavior is unchanged.
25
+ - 5b8a34b: `to(channel, target).send()`, Slack's `onEvent` `ctx.send()`, dynamic schedules' `to().send()`, and channel `receive` hooks now accept the same options as `from(address).send()`, including `history`, `idempotencyKey`, and `title`. Slack derives a threadless target's session from `idempotencyKey`, and a dynamic schedule's keyed send reaches the session its earlier attempt started, so a retried send no longer starts a second thread or session.
26
+ - 6ea94da: Set `codeChannels: true` on `slackChannel()` to serve Slack code channels as one session per channel. The agent answers un-mentioned messages, replies top-level or in the thread a person wrote in, and follows Slack's session status and stop. The option also adds the code channel scopes and events to the app manifest. A code channel session has no thread: `threadTs` is empty or `null`, and `ctx.slack.codeChannel` marks it. Address its session with `{ channelId, codeChannel: true }` in `onEvent` targets, or with `slackCodeChannelContinuationToken(channelId)`.
27
+
28
+ Two changes also reach agents that don't enable code channels: a cancelled turn now clears the Slack thread status by default, and `SlackSessionTarget` is now a union, so code that reads `target.threadTs` must handle the `{ channelId, codeChannel: true }` variant. `SlackHandle` also gains a required `codeChannel` field, which affects code that builds a handle by hand.
29
+ - 2764e1e: Add `openCodeChannel(ctx, slack, message, { name })` to `eve/channels/slack`, which a tool calls to move the Slack conversation it was called from into a new Slack code channel and continue the work there with the conversation so far. Wrap it in your own tool with your own description and inputs, and pass your Slack channel. It finds the conversation from `ctx.channel`, so it works when message hooks return their own auth. To write your own variant, Slack receive targets accept `codeChannel`. Message hooks can open a code channel without the model through `ctx.openCodeChannel()`.
30
+ - cb14c32: Reject framework-owned adapter attachment references in HTTP messages and restrict Linq attachment downloads to bounded public HTTPS destinations. Thanks to HackerOne researcher ryota for reporting the attachment-reference boundary issue.
31
+ - 412b077: Tools can start or reach sessions on the agent's channels with `ctx.to(channel, target).send(message, options)`, the same call route handlers and schedules use, such as forking a conversation into a new Slack thread. Ordinary tools call it directly, and workflow tools call it in a `"use step"` helper. Pass `` idempotencyKey: `${ctx.session.id}:${ctx.callId}` `` so a retried call reaches the same session once. `ctx.to()` throws in a subagent, a root copy, or a remote agent call. Tools also get `ctx.channel`, the calling session's channel `kind`, `continuationToken`, and `metadata`, as hooks and dynamic resolvers read it.
32
+
33
+ ## 0.84.1
34
+
35
+ ### Patch Changes
36
+
37
+ - 41916a5: Dynamic models, dynamic subagent configs, and `ctx.agent(name, { model })` overrides now read context windows from the AI Gateway model catalog recorded at build time instead of downloading it at runtime; a model that catalog doesn't list fails until you return `modelContextWindowTokens` or rebuild. Builds no longer carry built-in limits for `anthropic/claude-opus-4.7`, `openai/gpt-5.4`, and `openai/gpt-5.4-mini`, so a static model needs the catalog, a cached copy, or `modelContextWindowTokens` to compile offline.
38
+ - b322096: A compaction that fails during a turn, for example on a provider 429 that outlasts its retries, now fails that turn with `MODEL_CALL_FAILED` and leaves the session waiting with its history uncompacted. Previously the error escaped the turn step, so eve replayed the step and then ended the session. Thanks @gshost1 for the report and reproduction in #5350.
39
+ - 4276727: Messages a route forwards with `ctx.to()` now carry the deployment and request that accepted them, so an idle session reached that way hands off to the new deployment like one reached with `from()`. Thanks @haydenbleasel for reporting this in #5408.
40
+ - bea10fd: Model calls that AI Gateway rejects with a 402 because it temporarily throttled the credit balance check now retry with backoff. They previously reported "AI Gateway credit balance exhausted", told users to add credits, and parked the session without retrying.
41
+ - 105bf60: `eve init apps/my-agent` now creates missing parent directories such as `apps/` instead of failing with `ENOENT` from `mkdtemp`. Thanks @otakusboy for reporting this in #5422.
42
+ - 5bb6d43: Nuxt apps that call the auto-imported `useEveAgent()` now hydrate in `nuxt dev`. The `eve/nuxt` module pre-bundles `eve/vue` with Vite, which previously served its CommonJS dependencies to the browser unbundled and failed with `does not provide an export named 'getContext'`.
43
+ - cb1db12: Sending a message from a web client served over plain HTTP on a host other than `localhost` no longer fails with `crypto.randomUUID is not a function`. Thanks @KingAmo for reporting this in #5415.
44
+ - 5458bc4: Reuse signed inbox routing information between client turns when the Workflow SDK and adapter support guarded resumes and `EVE_INBOX_HANDLE_SECRET` is configured. Refresh stale routing after a confirmed refusal without retrying uncertain deliveries.
45
+ - a5328b4: `sandbox.spawn({ stdin: true })` returns a process with a writable `stdin` stream; closing it sends EOF. The Vercel and Docker sandboxes support it, and just-bash and custom providers without stdin support reject the call.
46
+ - 66103be: `eve dev` on Windows no longer opens a console window for each Docker command, dev build process, or optional package install. Thanks @satish860 for reporting this in #5291.
47
+ - 040519a: The browser client no longer triggers a Content Security Policy `eval` violation on pages whose `script-src` omits `'unsafe-eval'`. eve's bundled Zod now skips its `Function("")` probe and always uses its interpreted parser. Your app's own Zod and `z.config()` are not affected. Thanks @wasimxyz for reporting this in #5365.
48
+
3
49
  ## 0.84.0
4
50
 
5
51
  ### Minor Changes
@@ -4,29 +4,13 @@ import type { ChannelAdapter } from "#channel/adapter.js";
4
4
  import type { ChannelDeliverySource } from "#channel/delivery-metadata.js";
5
5
  import { type ChannelCreateResult } from "#channel/channel-address.js";
6
6
  export type { ChannelCreateResult } from "#channel/channel-address.js";
7
- import type { Session } from "#channel/session.js";
7
+ import type { SessionSendOptions, Session } from "#channel/session.js";
8
8
  import type { Runtime, SessionAuthContext, TurnPolicy, SessionControlOptions } from "#channel/types.js";
9
9
  import { type Answer, type StrictAnswers } from "#shared/input.js";
10
10
  import type { JsonObject } from "#shared/json.js";
11
11
  import type { SessionHistoryMessage } from "#shared/session-history.js";
12
- interface BaseChannelSendOptions {
13
- /** Stable delivery identity, deduplicated within this session's recent-key window. */
14
- readonly idempotencyKey?: string;
15
- /**
16
- * Prior conversation added as user and assistant turns before this
17
- * message and its `context`. Seeds a session this send creates, or
18
- * extends the history of the session that owns the address.
19
- */
20
- readonly history?: readonly SessionHistoryMessage[];
21
- readonly auth: SessionAuthContext | null;
22
- readonly context?: readonly string[];
23
- /** JSON Schema for a structured final answer, as on `Session.send()`. */
24
- readonly outputSchema?: JsonObject;
25
- readonly title?: string;
26
- readonly turnPolicy?: TurnPolicy;
27
- }
28
12
  /** Options for sending a message from a channel-local continuation address. */
29
- export type ChannelSendOptions<TState = undefined> = BaseChannelSendOptions & {
13
+ export type ChannelSendOptions<TState = undefined> = SessionSendOptions & {
30
14
  readonly state?: Partial<TState>;
31
15
  };
32
16
  interface BaseChannelRespondOptions<TState = undefined> {
@@ -1,11 +1,11 @@
1
1
  import type { ChannelAdapter } from "#channel/adapter.js";
2
- import type { MessageContent } from "#shared/message-content.js";
3
2
  import type { ChannelReceiveContext } from "#channel/channel-operations.js";
4
3
  import type { NormalizedChannelCorsOptions } from "#channel/cors.js";
5
4
  import type { TypedReceiveTarget } from "#channel/receive-target.js";
6
5
  import type { RouteDefinition } from "#channel/routes.js";
7
6
  import type { Delivery } from "#shared/delivery.js";
8
- import type { SessionAuthContext, TurnPolicy } from "#channel/types.js";
7
+ import type { TurnPolicy } from "#channel/types.js";
8
+ import type { GenericReceiveInput } from "#shared/channel-definition.js";
9
9
  import type { JsonObject } from "#shared/json.js";
10
10
  export declare const CHANNEL_SENTINEL: "eve:channel";
11
11
  interface ChannelBuildMetadata {
@@ -22,11 +22,7 @@ export interface CompiledChannel<TState = undefined, TReceiveTarget = Record<str
22
22
  readonly adapter: ChannelAdapter<any>;
23
23
  readonly cors?: NormalizedChannelCorsOptions;
24
24
  readonly __metadata?: TMetadata;
25
- readonly receive?: (input: {
26
- readonly message: MessageContent;
27
- readonly target: Readonly<TReceiveTarget>;
28
- readonly auth: SessionAuthContext | null;
29
- }, ctx: ChannelReceiveContext<TState>) => Promise<Delivery>;
25
+ readonly receive?: (input: GenericReceiveInput<TReceiveTarget>, ctx: ChannelReceiveContext<TState>) => Promise<Delivery>;
30
26
  readonly turnPolicy?: TurnPolicy;
31
27
  }
32
28
  export declare function isCompiledChannel(value: unknown): value is CompiledChannel;
@@ -1,20 +1,19 @@
1
1
  import type { MessageContent } from "#shared/message-content.js";
2
2
  import type { ChannelAdapter } from "#channel/adapter.js";
3
3
  import { type ChannelReference, type CompiledChannel } from "#channel/compiled-channel.js";
4
+ import type { ChannelDeliverySource } from "#channel/delivery-metadata.js";
4
5
  import type { InferReceiveTarget } from "#channel/receive-target.js";
6
+ import { type SessionSendOptions } from "#channel/session.js";
5
7
  import type { Delivery } from "#shared/delivery.js";
6
- import type { Runtime, SessionAuthContext, TurnPolicy } from "#channel/types.js";
8
+ import type { Runtime } from "#channel/types.js";
7
9
  import type { ResolvedChannelDefinition } from "#runtime/types.js";
8
- /**
9
- * Options for sending a message to a channel selected with `ctx.to(...)`.
10
- */
11
- interface CrossChannelSendOptions {
12
- readonly auth: SessionAuthContext | null;
13
- readonly turnPolicy?: TurnPolicy;
14
- }
15
10
  /** Message delivery bound to one channel-specific proactive target. */
16
11
  export interface CrossChannelTargetHandle {
17
- send(message: MessageContent, options: CrossChannelSendOptions): Promise<Delivery>;
12
+ /**
13
+ * Starts a session at the target, or continues the one that owns it. The
14
+ * channel's `receive()` hook gets `options` with the message and target.
15
+ */
16
+ send(message: MessageContent, options: SessionSendOptions): Promise<Delivery>;
18
17
  }
19
18
  /** Selects another authored channel and one of its proactive targets. */
20
19
  export type CrossChannelToFn = <TChannel extends ChannelReference<unknown>>(channel: TChannel, target: InferReceiveTarget<TChannel>) => CrossChannelTargetHandle;
@@ -30,6 +29,8 @@ export interface CrossChannelTarget {
30
29
  readonly adapter?: ChannelAdapter;
31
30
  readonly turnPolicy?: CompiledChannel["turnPolicy"];
32
31
  }
32
+ /** What a forwarded delivery inherits from the caller; the target channel supplies its own name. */
33
+ type CrossChannelDeliverySource = Omit<ChannelDeliverySource, "channelKind" | "channelName">;
33
34
  /**
34
35
  * Projects an agent's resolved channels into the receiver-input shape.
35
36
  *
@@ -39,7 +40,8 @@ export interface CrossChannelTarget {
39
40
  */
40
41
  export declare function toCrossChannelTargets(channels: readonly ResolvedChannelDefinition[]): readonly CrossChannelTarget[];
41
42
  /**
42
- * Builds the `ctx.to(channel, target)` closure used by route and schedule handlers.
43
+ * Builds the `ctx.to(channel, target)` closure used by route and schedule
44
+ * handlers and tools.
43
45
  */
44
- export declare function createCrossChannelToFn(runtime: Runtime, channels: readonly CrossChannelTarget[]): CrossChannelToFn;
46
+ export declare function createCrossChannelToFn(runtime: Runtime, channels: readonly CrossChannelTarget[], source?: CrossChannelDeliverySource): CrossChannelToFn;
45
47
  export {};
@@ -1,2 +1,2 @@
1
- import{createChannelOperations}from"#channel/channel-operations.js";import{isCompiledChannel}from"#channel/compiled-channel.js";function toCrossChannelTargets(e){return e.flatMap(e=>e.definition===void 0?[]:[{name:e.name,definition:e.definition,receive:e.receive,adapter:e.adapter,turnPolicy:e.turnPolicy}])}function createCrossChannelToFn(e,t){return(n,r)=>{let i=resolveTargetByReference(n,t);return{async send(t,n){return await invokeChannelReceive({runtime:e,target:i,input:{message:t,target:r,auth:n.auth},turnPolicy:n.turnPolicy,describeMissingReceive:()=>`ctx.to(): channel "${i.name}" does not implement receive(). Declare a receive hook on the channel to accept cross-channel sessions.`,describeMissingAdapter:()=>`ctx.to(): channel "${i.name}" has no adapter — cannot build ctx.from().`})}}}}async function invokeChannelReceive(e){if(!e.target.receive)throw Error(e.describeMissingReceive());if(!e.target.adapter)throw Error(e.describeMissingAdapter());let t=createChannelOperations({adapter:e.target.adapter,channelName:e.target.name,runtime:e.runtime,turnPolicy:e.turnPolicy??e.target.turnPolicy});return await e.target.receive(e.input,t)}function resolveTargetByReference(e,t){for(let n of t)if(n.definition===e)return n;let n=resolveTargetByRouteFingerprint(e,t);if(n!==null)return n;throw Error(`ctx.to(): the channel passed as the first argument is not registered in this agent's channels/. Import the channel module's default export from agent/channels/<name>.ts and pass that value.`)}function resolveTargetByRouteFingerprint(e,t){if(!isCompiledChannel(e))return null;let n=createRouteFingerprint(e);if(n===null)return null;let r=new Map;for(let e of t)createRouteFingerprint(e.definition)===n&&r.set(e.name,e);if(r.size===1)return[...r.values()][0];if(r.size>1)throw Error(`ctx.to(): the channel passed as the first argument matches multiple registered channels by route shape. Import a channel with a unique route set from agent/channels/<name>.ts before passing it to ctx.to().`);return null}function createRouteFingerprint(e){return e.routes.length===0?null:e.routes.map(e=>`${e.method.toUpperCase()} ${e.path}`).sort().join(`
1
+ import{toSessionSendOptions}from"#channel/session.js";import{createChannelOperations}from"#channel/channel-operations.js";import{isCompiledChannel}from"#channel/compiled-channel.js";function toCrossChannelTargets(e){return e.flatMap(e=>e.definition===void 0?[]:[{name:e.name,definition:e.definition,receive:e.receive,adapter:e.adapter,turnPolicy:e.turnPolicy}])}function createCrossChannelToFn(e,t,n={}){return(r,i)=>{let a=resolveTargetByReference(r,t);return{async send(t,r){return await invokeChannelReceive({runtime:e,source:n,target:a,input:{...toSessionSendOptions(r),message:t,target:i},turnPolicy:r.turnPolicy})}}}}async function invokeChannelReceive(e){let{name:t}=e.target;if(!e.target.receive)throw Error(`ctx.to(): channel "${t}" does not implement receive(). Declare a receive hook on the channel to accept cross-channel sessions.`);if(!e.target.adapter)throw Error(`ctx.to(): channel "${t}" has no adapter — cannot build ctx.from().`);let n=createChannelOperations({adapter:e.target.adapter,channelName:t,metadata:{...e.source,channelKind:`channel:${t}`,channelName:t},runtime:e.runtime,turnPolicy:e.turnPolicy??e.target.turnPolicy});return await e.target.receive(e.input,n)}function resolveTargetByReference(e,t){for(let n of t)if(n.definition===e)return n;let n=resolveTargetByRouteFingerprint(e,t);if(n!==null)return n;throw Error(`ctx.to(): the channel passed as the first argument is not registered in this agent's channels/. Import the channel module's default export from agent/channels/<name>.ts and pass that value.`)}function resolveTargetByRouteFingerprint(e,t){if(!isCompiledChannel(e))return null;let n=createRouteFingerprint(e);if(n===null)return null;let r=new Map;for(let e of t)createRouteFingerprint(e.definition)===n&&r.set(e.name,e);if(r.size===1)return[...r.values()][0];if(r.size>1)throw Error(`ctx.to(): the channel passed as the first argument matches multiple registered channels by route shape. Import a channel with a unique route set from agent/channels/<name>.ts before passing it to ctx.to().`);return null}function createRouteFingerprint(e){return e.routes.length===0?null:e.routes.map(e=>`${e.method.toUpperCase()} ${e.path}`).sort().join(`
2
2
  `)}export{createCrossChannelToFn,toCrossChannelTargets};
@@ -1 +1 @@
1
- import{SessionNotActiveError}from"#channel/session-not-active-error.js";import{StreamDelivery}from"#client/delivery.js";import{deliveryEvents}from"#client/delivery-events.js";function commandDelivery(e,t,n,r={}){if(t.status!==`accepted`&&t.status!==`reset`)throw new SessionNotActiveError({retryable:`retryable`in t&&t.retryable===!0});let i=t.status===`reset`?t.previousSessionId:t.sessionId;return new StreamDelivery({sessionId:i,deliveryId:n,createStream:async function*(t){yield*deliveryEvents(await e.getEventStream(i,{...r,signal:t?.signal}),n,t?.signal)}})}export{commandDelivery};
1
+ import{SessionNotActiveError}from"#channel/session-not-active-error.js";import{StreamDelivery}from"#client/delivery.js";import{deliveryEvents}from"#client/delivery-events.js";function commandDelivery(e,t,n,r={}){if(t.status!==`accepted`&&t.status!==`reset`)throw new SessionNotActiveError({retryable:`retryable`in t&&t.retryable===!0});let i=t.status===`reset`?t.previousSessionId:t.sessionId;return new StreamDelivery({inbox:`inbox`in t?t.inbox:void 0,sessionId:i,deliveryId:n,createStream:async function*(t){yield*deliveryEvents(await e.getEventStream(i,{...r,signal:t?.signal}),n,t?.signal)}})}export{commandDelivery};
@@ -1 +1 @@
1
- import{randomUUID}from"node:crypto";import{commandDelivery}from"#channel/delivery.js";import{createChannelDeliveryMetadata}from"#channel/delivery-metadata.js";import{scheduleDisplayName}from"#runtime/schedules/record.js";import{SCHEDULE_APP_AUTH,SCHEDULE_APP_AUTH as SCHEDULE_APP_AUTH$1}from"#channel/schedule-auth.js";import{createCrossChannelToFn,toCrossChannelTargets}from"#channel/cross-channel-receive.js";import{ContextContainer,contextStorage}from"#context/container.js";import{AuthKey,OccurrenceIdKey,ScheduleIdKey,ScheduleInstanceKey}from"#context/keys.js";import{expectFunction}from"#internal/authored-module.js";import{parseSchedulePayload,validateSchedulePayload}from"#runtime/schedules/payload.js";const SCHEDULE_ADAPTER_KIND=`schedule`,SCHEDULE_ADAPTER={kind:SCHEDULE_ADAPTER_KIND};var ScheduleDispatcher=class{runtime;channels;constructor(e){this.runtime=e.runtime,this.channels=e.channels}async trigger(e){let t=new ContextContainer;return t.set(ScheduleIdKey,e.scheduleId),await contextStorage.run(t,()=>this.triggerInScope(e))}async triggerCollection(e){let{envelope:t}=parseSchedulePayload(e.payload,{application:e.payload.eve.application,collection:e.collectionId}),{definition:n}=e,r={...e.occurrence,displayName:scheduleDisplayName(e.occurrence.name)},i=n.preparePayload===void 0?await validateSchedulePayload(n.inputSchema,t.payload):t.payload;await e.verifyDelivery?.();let a=await n.auth({principal:t.principal,payload:i,occurrence:r});if(a===null)throw Error(`Scheduled execution is no longer authorized.`);if(a.principalId!==t.principal.principalId||a.principalType!==t.principal.type||a.authenticator!==t.principal.authenticator||a.issuer!==t.principal.issuer||a.subject!==t.principal.subject)throw Error(`Scheduled execution auth must resolve the schedule creator.`);let o=a,s=new ContextContainer;return s.set(ScheduleIdKey,e.collectionId),s.set(AuthKey,o),s.set(OccurrenceIdKey,r.executionId),s.set(ScheduleInstanceKey,r.name),await contextStorage.run(s,async()=>{let e={...this.runtime,resolveContinuation:async()=>void 0,dispatchContinuation:async e=>{if(e.command.kind!==`send`)throw Error(`Scheduled channel receive can only start a fresh session.`);return{status:`session_not_active`}},createSession:async e=>await this.runtime.createSession({...e,auth:o,initiatorAuth:o,capabilities:{requestInput:!1},continuationToken:`eve-scheduled-send:${randomUUID()}`,continuationConflictCommand:void 0})},{args:t,sessions:a,waitUntilTasks:s}=this.createHandlerContext(e);return await n.run({payload:i,occurrence:r,auth:o,to:(e,n)=>({send:r=>t.to(e,n).send(r,{auth:o})}),waitUntil:t.waitUntil}),await Promise.all(s),{sessions:a,waitUntilTasks:s}})}async triggerInScope(e){let{args:t,sessions:n,waitUntilTasks:r}=this.createHandlerContext();if(e.run)await e.run(t);else if(e.markdown!==void 0)n.push(await this.runMarkdown(e.markdown));else throw Error(`Schedule "${e.scheduleId}" has neither "run" nor "markdown" — at least one must be set.`);return{sessions:n,waitUntilTasks:r}}createHandlerContext(e=this.runtime){let t=[],n=[],r=createCrossChannelToFn(e,toCrossChannelTargets(this.channels));return{args:{appAuth:SCHEDULE_APP_AUTH$1,to(e,n){let i=r(e,n);return{async send(e,n){let r=await i.send(e,n);return t.push(r),r}}},waitUntil(e){n.push(e)}},sessions:t,waitUntilTasks:n}}async runMarkdown(e){let t=createChannelDeliveryMetadata({channelKind:`schedule`,channelName:`schedule`}),n=await this.runtime.createSession({delivery:t,adapter:SCHEDULE_ADAPTER,auth:SCHEDULE_APP_AUTH$1,input:{message:e}});return commandDelivery(this.runtime,{status:`accepted`,sessionId:n.sessionId},t.deliveryId)}};function expectScheduleRun(e,t,n){let r=e;if(typeof r!=`object`||!r)throw Error(`Schedule export "${n??`default`}" from "${t}" must be an object.`);return expectFunction(r.run,`Expected the schedule export "${n??`default`}" from "${t}" to export a \`run\` handler function.`)}export{SCHEDULE_ADAPTER,SCHEDULE_ADAPTER_KIND,SCHEDULE_APP_AUTH,ScheduleDispatcher,expectScheduleRun};
1
+ import{createHash,randomUUID}from"node:crypto";import{commandDelivery}from"#channel/delivery.js";import{createChannelDeliveryMetadata}from"#channel/delivery-metadata.js";import{scheduleDisplayName}from"#runtime/schedules/record.js";import{SCHEDULE_APP_AUTH,SCHEDULE_APP_AUTH as SCHEDULE_APP_AUTH$1}from"#channel/schedule-auth.js";import{createCrossChannelToFn,toCrossChannelTargets}from"#channel/cross-channel-receive.js";import{ContextContainer,contextStorage}from"#context/container.js";import{AuthKey,OccurrenceIdKey,ScheduleIdKey,ScheduleInstanceKey}from"#context/keys.js";import{expectFunction}from"#internal/authored-module.js";import{parseSchedulePayload,validateSchedulePayload}from"#runtime/schedules/payload.js";const SCHEDULE_ADAPTER_KIND=`schedule`,SCHEDULE_ADAPTER={kind:SCHEDULE_ADAPTER_KIND};var ScheduleDispatcher=class{runtime;channels;constructor(e){this.runtime=e.runtime,this.channels=e.channels}async trigger(e){let t=new ContextContainer;return t.set(ScheduleIdKey,e.scheduleId),await contextStorage.run(t,()=>this.triggerInScope(e))}async triggerCollection(e){let{envelope:t}=parseSchedulePayload(e.payload,{application:e.payload.eve.application,collection:e.collectionId}),{definition:n}=e,r={...e.occurrence,displayName:scheduleDisplayName(e.occurrence.name)},i=n.preparePayload===void 0?await validateSchedulePayload(n.inputSchema,t.payload):t.payload;await e.verifyDelivery?.();let a=await n.auth({principal:t.principal,payload:i,occurrence:r});if(a===null)throw Error(`Scheduled execution is no longer authorized.`);if(a.principalId!==t.principal.principalId||a.principalType!==t.principal.type||a.authenticator!==t.principal.authenticator||a.issuer!==t.principal.issuer||a.subject!==t.principal.subject)throw Error(`Scheduled execution auth must resolve the schedule creator.`);let o=a,s=new ContextContainer;s.set(ScheduleIdKey,e.collectionId),s.set(AuthKey,o),s.set(OccurrenceIdKey,r.executionId),s.set(ScheduleInstanceKey,r.name);let scheduledToken=(e,t)=>t===void 0?`eve-scheduled-send:${randomUUID()}`:`eve-scheduled-send:${createHash(`sha256`).update(JSON.stringify([r.scheduleId,e,t])).digest(`hex`)}`,freshRun=e=>({...e,auth:o,initiatorAuth:o,capabilities:{requestInput:!1},continuationToken:scheduledToken(e.continuationToken,e.idempotencyKey),continuationConflictCommand:e.idempotencyKey===void 0?void 0:e.continuationConflictCommand});return await contextStorage.run(s,async()=>{let e={...this.runtime,resolveContinuation:async()=>void 0,dispatchContinuation:async e=>{let{command:t}=e;if(t.kind!==`send`)throw Error(`Scheduled channel receive can only start a fresh session.`);return t.idempotencyKey===void 0?{status:`session_not_active`}:await this.runtime.dispatchContinuation({command:t,continuationToken:scheduledToken(e.continuationToken,t.idempotencyKey),successor:e.successor&&freshRun(e.successor)})},createSession:async e=>await this.runtime.createSession(freshRun(e))},{args:t,sessions:a,waitUntilTasks:s}=this.createHandlerContext(e);return await n.run({payload:i,occurrence:r,auth:o,to:(e,n)=>({send:(r,i)=>t.to(e,n).send(r,{...i,auth:o})}),waitUntil:t.waitUntil}),await Promise.all(s),{sessions:a,waitUntilTasks:s}})}async triggerInScope(e){let{args:t,sessions:n,waitUntilTasks:r}=this.createHandlerContext();if(e.run)await e.run(t);else if(e.markdown!==void 0)n.push(await this.runMarkdown(e.markdown));else throw Error(`Schedule "${e.scheduleId}" has neither "run" nor "markdown" — at least one must be set.`);return{sessions:n,waitUntilTasks:r}}createHandlerContext(e=this.runtime){let t=[],n=[],r=createCrossChannelToFn(e,toCrossChannelTargets(this.channels));return{args:{appAuth:SCHEDULE_APP_AUTH$1,to(e,n){let i=r(e,n);return{async send(e,n){let r=await i.send(e,n);return t.push(r),r}}},waitUntil(e){n.push(e)}},sessions:t,waitUntilTasks:n}}async runMarkdown(e){let t=createChannelDeliveryMetadata({channelKind:`schedule`,channelName:`schedule`}),n=await this.runtime.createSession({delivery:t,adapter:SCHEDULE_ADAPTER,auth:SCHEDULE_APP_AUTH$1,input:{message:e}});return commandDelivery(this.runtime,{status:`accepted`,sessionId:n.sessionId},t.deliveryId)}};function expectScheduleRun(e,t,n){let r=e;if(typeof r!=`object`||!r)throw Error(`Schedule export "${n??`default`}" from "${t}" must be an object.`);return expectFunction(r.run,`Expected the schedule export "${n??`default`}" from "${t}" to export a \`run\` handler function.`)}export{SCHEDULE_ADAPTER,SCHEDULE_ADAPTER_KIND,SCHEDULE_APP_AUTH,ScheduleDispatcher,expectScheduleRun};
@@ -48,14 +48,36 @@ interface SessionDeliveryOptions {
48
48
  readonly context?: readonly string[];
49
49
  readonly outputSchema?: JsonObject;
50
50
  }
51
- /** Options for sending a message through a fixed session handle. */
52
- export type SessionSendOptions = SessionDeliveryOptions & {
51
+ /**
52
+ * Options every server-side message send accepts: `Session.send()`,
53
+ * `from(address).send()`, `to(channel, target).send()`, and the input a
54
+ * channel's `receive()` hook gets.
55
+ */
56
+ export interface SessionSendOptions {
57
+ /** Who the session runs as. */
58
+ readonly auth: SessionAuthContext | null;
59
+ /** Strings added as user messages before the message, kept in session history. */
60
+ readonly context?: readonly string[];
53
61
  /** Prior conversation added as user and assistant turns before this message and its `context`. */
54
62
  readonly history?: readonly SessionHistoryMessage[];
55
- /** Initial workflow title for a prewarmed session. */
63
+ /**
64
+ * Stable delivery identity. Deduplicated within the receiving session's recent-key window, which
65
+ * every sender to that session shares, so make it unique across senders, for example by
66
+ * prefixing the source. A threadless Slack target also derives its session from it.
67
+ */
68
+ readonly idempotencyKey?: string;
69
+ /** JSON Schema for a structured final answer. */
70
+ readonly outputSchema?: JsonObject;
71
+ /** Workflow run title for a session this send starts. Defaults to one derived from the message. */
56
72
  readonly title?: string;
73
+ /** How the message treats a running turn. */
57
74
  readonly turnPolicy?: TurnPolicy;
58
- };
75
+ }
76
+ /**
77
+ * Copies only the {@link SessionSendOptions} fields and validates the key. Callers can pass wider
78
+ * objects, which must not carry internal send fields such as `callback` across a channel boundary.
79
+ */
80
+ export declare function toSessionSendOptions(options: SessionSendOptions): SessionSendOptions;
59
81
  /** Options for answering pending input requests through a fixed session handle. */
60
82
  export type SessionRespondOptions = SessionDeliveryOptions;
61
83
  export declare function createSession(id: string, runtime: Runtime, metadata?: Partial<ChannelDeliverySource> & {
@@ -1 +1 @@
1
- import{attachClientContext,readClientContext}from"#internal/client-context.js";import{commandDelivery}from"#channel/delivery.js";import{validateDeliveryIdempotencyKey}from"#channel/delivery-idempotency.js";import{createChannelDeliveryMetadata}from"#channel/delivery-metadata.js";import{serializeFilePartsInMessage}from"#channel/send-input.js";import{DEFAULT_TURN_POLICY,controlCommand}from"#channel/types.js";import{validateSessionHistory}from"#shared/session-history.js";import{parseAnswers}from"#shared/input.js";import{AuthKey,ContinuationHookTokensKey,ContinuationTokenKey,InitiatorAuthKey,SessionIdKey}from"#context/keys.js";import{toChannelLocalContinuationToken}from"#shared/continuation-token.js";function internalSession(e){return e}function createSession(e,t,n={}){return{id:e,async send(r,i){validateDeliveryIdempotencyKey(i.idempotencyKey);let a=createDelivery(n,i.deliveryId),o=sessionCallbackToTurnCaller(i.callback),s=validateSessionHistory(i.history),c=attachClientContext({message:serializeFilePartsInMessage(r)},readClientContext(i));i.context!==void 0&&(c.context=i.context),s!==void 0&&(c.history=s),i.outputSchema!==void 0&&(c.outputSchema=i.outputSchema);let l={idempotencyKey:i.idempotencyKey,auth:i.auth,delivery:a,kind:`send`,payload:c,requestId:n.requestId,turnPolicy:i.turnPolicy??n.turnPolicy??DEFAULT_TURN_POLICY,title:i.title},u=await t.dispatchSession({command:o===void 0?l:{...l,caller:o},sessionId:e});return commandDelivery(t,u,a.deliveryId)},async respond(r,i){if(validateDeliveryIdempotencyKey(i.idempotencyKey),r.length===0)throw Error(`respond() requires at least one answer.`);let a=parseAnswers(r),o=sessionCallbackToTurnCaller(i.callback),s=createDelivery(n,i.deliveryId),c=attachClientContext({answers:a},readClientContext(i));i.context!==void 0&&(c.context=i.context),i.outputSchema!==void 0&&(c.outputSchema=i.outputSchema);let l={idempotencyKey:i.idempotencyKey,auth:i.auth,delivery:s,kind:`send`,payload:c,requestId:n.requestId},u=await t.dispatchSession({command:o===void 0?l:{...l,caller:o},sessionId:e});return commandDelivery(t,u,s.deliveryId)},async cancel(n){let r=controlCommand({kind:`cancel`,turnId:n?.turnId},n),i=await t.dispatchSession({command:r,sessionId:e});return commandDelivery(t,i,r.deliveryId)},async prewarm(){return await t.dispatchSession({command:{kind:`prewarm`,targetDeploymentId:n.acceptedDeploymentId},sessionId:e})},async compact(n){let r=controlCommand({kind:`compact`},n),i=await t.dispatchSession({command:r,sessionId:e});return commandDelivery(t,i,r.deliveryId)},async clear(n){let r=controlCommand({kind:`clear`},n),i=await t.dispatchSession({command:r,sessionId:e});return commandDelivery(t,i,r.deliveryId)},async reset(n){let r=controlCommand({kind:`reset`,reason:n?.reason},n),i=await t.dispatchSession({command:r,sessionId:e});return commandDelivery(t,i,r.deliveryId,{follow:!1})},async*stream(n){yield*await t.getEventStream(e,n)},async getLineStream(n){return t.getLineStream(e,n)},async getStreamTailIndex(){return t.getStreamTailIndex(e)},async hasRunnableOwner(){return await t.hasRunnableOwner?.(e)??!0},async getChildBinding(n){return await t.readChildBinding?.(e,n)}}}function createAttachSessionFn(e,t={}){return n=>createSession(n,e,t)}function createDelivery(e,t){let n=createChannelDeliveryMetadata({channelKind:`http`,channelName:`http`,...e});return t===void 0?n:{...n,deliveryId:t}}function buildChannelContext(e){return{session:{get id(){return e.get(SessionIdKey)??``},get auth(){return{current:e.get(AuthKey)??null,initiator:e.get(InitiatorAuthKey)??null}}},get continuation(){let t=e.get(ContinuationTokenKey);if(t!==void 0&&t.length!==0)return{token:toChannelLocalContinuationToken(t),alias(n){if(n.length===0)throw Error(`A session alias requires a nonempty token.`);let r=namespaceContinuationToken(t,n);t!==r&&(e.set(ContinuationHookTokensKey,e=>{let n=e??[t];return n.includes(r)?n:[...n,r]}),e.set(ContinuationTokenKey,r))}}}}}function namespaceContinuationToken(e,t){let n=e.indexOf(`:`);if(n<=0)throw Error(`Cannot set session continuation token without an existing namespaced continuation token. Start the session with a placeholder continuationToken.`);return`${e.slice(0,n+1)}${t}`}function sessionCallbackToTurnCaller(e){return e===void 0?void 0:{callId:e.callId,replyTo:{kind:`callback`,token:e.token,url:e.url},subagentName:e.subagentName}}export{buildChannelContext,createAttachSessionFn,createSession,internalSession,sessionCallbackToTurnCaller};
1
+ import{attachClientContext,readClientContext}from"#internal/client-context.js";import{commandDelivery}from"#channel/delivery.js";import{validateDeliveryIdempotencyKey}from"#channel/delivery-idempotency.js";import{createChannelDeliveryMetadata}from"#channel/delivery-metadata.js";import{serializeFilePartsInMessage}from"#channel/send-input.js";import{DEFAULT_TURN_POLICY,controlCommand}from"#channel/types.js";import{validateSessionHistory}from"#shared/session-history.js";import{parseAnswers}from"#shared/input.js";import{AuthKey,ContinuationHookTokensKey,ContinuationTokenKey,InitiatorAuthKey,SessionIdKey}from"#context/keys.js";import{toChannelLocalContinuationToken}from"#shared/continuation-token.js";function internalSession(e){return e}const SESSION_SEND_OPTION_KEYS=Object.keys({auth:!0,context:!0,history:!0,idempotencyKey:!0,outputSchema:!0,title:!0,turnPolicy:!0});function toSessionSendOptions(e){validateDeliveryIdempotencyKey(e.idempotencyKey);let t={};for(let n of SESSION_SEND_OPTION_KEYS)e[n]!==void 0&&(t[n]=e[n]);return t}function createSession(e,t,n={}){return{id:e,async send(r,i){validateDeliveryIdempotencyKey(i.idempotencyKey);let a=createDelivery(n,i.deliveryId),o=sessionCallbackToTurnCaller(i.callback),s=validateSessionHistory(i.history),c=attachClientContext({message:serializeFilePartsInMessage(r)},readClientContext(i));i.context!==void 0&&(c.context=i.context),s!==void 0&&(c.history=s),i.outputSchema!==void 0&&(c.outputSchema=i.outputSchema);let l={idempotencyKey:i.idempotencyKey,auth:i.auth,delivery:a,kind:`send`,payload:c,requestId:n.requestId,turnPolicy:i.turnPolicy??n.turnPolicy??DEFAULT_TURN_POLICY,title:i.title},u=await t.dispatchSession({command:o===void 0?l:{...l,caller:o},sessionId:e,inbox:i.inbox});return commandDelivery(t,u,a.deliveryId)},async respond(r,i){if(validateDeliveryIdempotencyKey(i.idempotencyKey),r.length===0)throw Error(`respond() requires at least one answer.`);let a=parseAnswers(r),o=sessionCallbackToTurnCaller(i.callback),s=createDelivery(n,i.deliveryId),c=attachClientContext({answers:a},readClientContext(i));i.context!==void 0&&(c.context=i.context),i.outputSchema!==void 0&&(c.outputSchema=i.outputSchema);let l={idempotencyKey:i.idempotencyKey,auth:i.auth,delivery:s,kind:`send`,payload:c,requestId:n.requestId},u=await t.dispatchSession({command:o===void 0?l:{...l,caller:o},sessionId:e,inbox:i.inbox});return commandDelivery(t,u,s.deliveryId)},async cancel(n){let r=controlCommand({kind:`cancel`,turnId:n?.turnId},n),i=await t.dispatchSession({command:r,sessionId:e});return commandDelivery(t,i,r.deliveryId)},async prewarm(){return await t.dispatchSession({command:{kind:`prewarm`,targetDeploymentId:n.acceptedDeploymentId},sessionId:e})},async compact(n){let r=controlCommand({kind:`compact`},n),i=await t.dispatchSession({command:r,sessionId:e});return commandDelivery(t,i,r.deliveryId)},async clear(n){let r=controlCommand({kind:`clear`},n),i=await t.dispatchSession({command:r,sessionId:e});return commandDelivery(t,i,r.deliveryId)},async reset(n){let r=controlCommand({kind:`reset`,reason:n?.reason},n),i=await t.dispatchSession({command:r,sessionId:e});return commandDelivery(t,i,r.deliveryId,{follow:!1})},async*stream(n){yield*await t.getEventStream(e,n)},async getLineStream(n){return t.getLineStream(e,n)},async getStreamTailIndex(){return t.getStreamTailIndex(e)},async hasRunnableOwner(){return await t.hasRunnableOwner?.(e)??!0},async getChildBinding(n){return await t.readChildBinding?.(e,n)}}}function createAttachSessionFn(e,t={}){return n=>createSession(n,e,t)}function createDelivery(e,t){let n=createChannelDeliveryMetadata({channelKind:`http`,channelName:`http`,...e});return t===void 0?n:{...n,deliveryId:t}}function buildChannelContext(e){return{session:{get id(){return e.get(SessionIdKey)??``},get auth(){return{current:e.get(AuthKey)??null,initiator:e.get(InitiatorAuthKey)??null}}},get continuation(){let t=e.get(ContinuationTokenKey);if(t!==void 0&&t.length!==0)return{token:toChannelLocalContinuationToken(t),alias(n){if(n.length===0)throw Error(`A session alias requires a nonempty token.`);let r=namespaceContinuationToken(t,n);t!==r&&(e.set(ContinuationHookTokensKey,e=>{let n=e??[t];return n.includes(r)?n:[...n,r]}),e.set(ContinuationTokenKey,r))}}}}}function namespaceContinuationToken(e,t){let n=e.indexOf(`:`);if(n<=0)throw Error(`Cannot set session continuation token without an existing namespaced continuation token. Start the session with a placeholder continuationToken.`);return`${e.slice(0,n+1)}${t}`}function sessionCallbackToTurnCaller(e){return e===void 0?void 0:{callId:e.callId,replyTo:{kind:`callback`,token:e.token,url:e.url},subagentName:e.subagentName}}export{buildChannelContext,createAttachSessionFn,createSession,internalSession,sessionCallbackToTurnCaller,toSessionSendOptions};
@@ -199,6 +199,7 @@ export type SessionSendCommandResult = {
199
199
  readonly status: "accepted";
200
200
  readonly sessionId: string;
201
201
  readonly deliveryId?: string;
202
+ readonly inbox?: string;
202
203
  } | {
203
204
  readonly status: "session_not_active";
204
205
  /** The workflow exists but its inbox is not yet available; no delivery was accepted. */
@@ -232,6 +233,7 @@ export interface DispatchContinuationInput<TCommand extends SessionCommand = Ses
232
233
  readonly successor?: RunInput;
233
234
  }
234
235
  export interface DispatchSessionInput<TCommand extends SessionCommand = SessionCommand> {
236
+ readonly inbox?: string;
235
237
  readonly command: TCommand;
236
238
  readonly sessionId: string;
237
239
  }
@@ -2299,6 +2299,7 @@ async function openStreamBody(input) {
2299
2299
  signal
2300
2300
  });
2301
2301
  } catch (error) {
2302
+ if (input.redirect === "error" && error instanceof Error && error.cause instanceof Error && error.cause.message === "unexpected redirect") throw new Error("The session stream refused a redirect. Use the final agent URL instead.", { cause: error });
2302
2303
  if (input.signal?.aborted || !isStreamDisconnectError(error) || attempt === openRetryPolicy.maxAttempts - 1) throw error;
2303
2304
  await sleep(retryDelayMs, input.signal);
2304
2305
  retryDelayMs = Math.min(retryDelayMs * 2, openRetryPolicy.maxDelayMs);
@@ -2447,10 +2448,12 @@ const sendReceipts = /* @__PURE__ */ new WeakMap();
2447
2448
  var StreamDelivery = class {
2448
2449
  sessionId;
2449
2450
  deliveryId;
2451
+ inbox;
2450
2452
  #createStream;
2451
2453
  #result;
2452
2454
  constructor(input) {
2453
2455
  this.sessionId = input.sessionId;
2456
+ this.inbox = input.inbox;
2454
2457
  this.deliveryId = input.deliveryId;
2455
2458
  if (input.receipt !== void 0) sendReceipts.set(this, input.receipt);
2456
2459
  this.#createStream = input.createStream;
@@ -3010,9 +3013,13 @@ var ClientSession = class ClientSession {
3010
3013
  async #send(input, retrySessionNotReady) {
3011
3014
  const initialStreamIndex = this.#state.streamIndex;
3012
3015
  const path = createEveSessionRoutePath(this.#state.sessionId);
3013
- const { response, timing } = await postSessionSend(this.#context, path, input, retrySessionNotReady);
3014
- const { sessionId: responseSessionId, deliveryId } = await readAcceptedMessage(response, this.#state.sessionId);
3016
+ const { response, timing } = await postSessionSend(this.#context, path, input, retrySessionNotReady, this.#state.inbox);
3017
+ const { sessionId: responseSessionId, deliveryId, inbox } = await readAcceptedMessage(response, this.#state.sessionId);
3015
3018
  if (responseSessionId !== this.#state.sessionId) throw new Error("Message route returned a different session id.");
3019
+ this.#state = {
3020
+ ...this.#state,
3021
+ inbox
3022
+ };
3016
3023
  if (deliveryId === void 0) throw new Error("Message route did not return a delivery id. Update the server before sending with this client.");
3017
3024
  return this.#messageResponse(response, input, initialStreamIndex, deliveryId, {
3018
3025
  ...timing,
@@ -3100,7 +3107,7 @@ var ClientSession = class ClientSession {
3100
3107
  }
3101
3108
  #advanceStreamIndex(streamIndex) {
3102
3109
  this.#state = {
3103
- sessionId: this.#state.sessionId,
3110
+ ...this.#state,
3104
3111
  streamIndex: Math.max(this.#state.streamIndex, streamIndex)
3105
3112
  };
3106
3113
  }
@@ -3122,7 +3129,7 @@ var ClientSession = class ClientSession {
3122
3129
  function followClientSession(session, options) {
3123
3130
  return session[followSession](options);
3124
3131
  }
3125
- async function postSessionSend(context, path, input, retrySessionNotReady) {
3132
+ async function postSessionSend(context, path, input, retrySessionNotReady, inbox) {
3126
3133
  const startedAt = Date.now();
3127
3134
  const readyDeadline = startedAt + SESSION_SEND_READY_TIMEOUT_MS;
3128
3135
  let retryDelayMs = SESSION_SEND_RETRY_BASE_DELAY_MS;
@@ -3130,7 +3137,7 @@ async function postSessionSend(context, path, input, retrySessionNotReady) {
3130
3137
  const attemptAt = Date.now();
3131
3138
  try {
3132
3139
  return {
3133
- response: await postTurn(context, path, input, false),
3140
+ response: await postTurn(context, path, input, false, void 0, inbox),
3134
3141
  timing: {
3135
3142
  startedAt,
3136
3143
  notReadyMs: attemptAt - startedAt,
@@ -3167,10 +3174,11 @@ async function postCreateSession(context, options) {
3167
3174
  function isSessionNotReady(error) {
3168
3175
  return SessionNotActiveError.is(error) && error.retryable;
3169
3176
  }
3170
- async function postTurn(context, path, input, requireMessage, stubs) {
3177
+ async function postTurn(context, path, input, requireMessage, stubs, inbox) {
3171
3178
  const body = createMessageBody(input, requireMessage);
3172
3179
  if (body === null) throw new Error(requireMessage ? "Creating a session requires a non-empty message." : "A session turn requires a non-empty message or answers.");
3173
3180
  if (stubs !== void 0) body.stubs = stubs;
3181
+ if (inbox !== void 0) body.inbox = inbox;
3174
3182
  const headers = await context.resolveHeaders(input.headers);
3175
3183
  headers.set("content-type", "application/json");
3176
3184
  const response = await fetch(createClientUrl(context.baseUrl, path), {
@@ -3192,6 +3200,7 @@ async function readAcceptedMessage(response, expected) {
3192
3200
  if (!sessionId) throw new Error("Message route did not return a session id.");
3193
3201
  return {
3194
3202
  sessionId,
3203
+ inbox: typeof payload.inbox === "string" ? payload.inbox : void 0,
3195
3204
  deliveryId: typeof payload.deliveryId === "string" && payload.deliveryId.length > 0 ? payload.deliveryId : void 0
3196
3205
  };
3197
3206
  }
@@ -3223,6 +3232,7 @@ var ClientSessions = class {
3223
3232
  if (sessionId.length === 0) throw new Error("sessionId must be a non-empty string.");
3224
3233
  return new ClientSession(this.#context, {
3225
3234
  sessionId,
3235
+ inbox: options?.inbox,
3226
3236
  streamIndex: options?.streamIndex ?? 0
3227
3237
  });
3228
3238
  }
@@ -3244,7 +3254,7 @@ var Client = class {
3244
3254
  this.#baseUrl = options.baseUrl;
3245
3255
  this.#auth = options.auth;
3246
3256
  this.#headers = options.headers;
3247
- this.#redirect = options.redirect;
3257
+ this.#redirect = options.redirect ?? "error";
3248
3258
  this.sessions = new ClientSessions({
3249
3259
  baseUrl: this.#baseUrl,
3250
3260
  redirect: this.#redirect,
@@ -3341,7 +3351,7 @@ function headersInitToRecord(headers) {
3341
3351
  return Object.fromEntries(new Headers(headers).entries());
3342
3352
  }
3343
3353
  function withRedirectPolicy(init, redirect) {
3344
- return redirect === void 0 ? init : {
3354
+ return {
3345
3355
  ...init,
3346
3356
  redirect
3347
3357
  };
@@ -3443,6 +3453,7 @@ function isAbortError(error) {
3443
3453
 
3444
3454
  //#endregion
3445
3455
  //#region src/client/optimistic-message-submissions.ts
3456
+ let submissionSequence = 0;
3446
3457
  var OptimisticMessageSubmissions = class {
3447
3458
  #optimistic;
3448
3459
  #show;
@@ -3457,7 +3468,7 @@ var OptimisticMessageSubmissions = class {
3457
3468
  }
3458
3469
  submit(input, turnId) {
3459
3470
  if (input.message === void 0) return void 0;
3460
- const id = crypto.randomUUID();
3471
+ const id = crypto.randomUUID?.() ?? `submission_${(submissionSequence += 1).toString()}`;
3461
3472
  this.#submissions.set(id, {
3462
3473
  id,
3463
3474
  message: summarizeMessage(input.message),
@@ -4494,7 +4505,10 @@ var SessionStore = class {
4494
4505
  this.#conversationClient = new ConversationClient(() => this.#publish());
4495
4506
  this.#events = (init.initialEvents ?? []).filter((event) => this.#conversationClient.hydrate(event));
4496
4507
  this.#messageSubmissions = new OptimisticMessageSubmissions((submissions) => this.#conversationClient.showSubmissions(submissions), init.optimistic ?? true);
4497
- this.#session = init.session ?? (init.initialSession === void 0 ? void 0 : this.#client?.sessions.attach(init.initialSession.sessionId, { streamIndex: init.initialSession.streamIndex }));
4508
+ this.#session = init.session ?? (init.initialSession === void 0 ? void 0 : this.#client?.sessions.attach(init.initialSession.sessionId, {
4509
+ streamIndex: init.initialSession.streamIndex,
4510
+ inbox: init.initialSession.inbox
4511
+ }));
4498
4512
  this.#error = this.#recordedFailure();
4499
4513
  this.#snapshot = this.#createSnapshot();
4500
4514
  }
@@ -4569,6 +4583,7 @@ var SessionStore = class {
4569
4583
  this.#messageSubmissions.correlate(submissionId, deliveryId);
4570
4584
  }
4571
4585
  this.#acceptReceipt(deliveryId);
4586
+ this.#callbacks.onSessionChange?.(this.#sessionState);
4572
4587
  }
4573
4588
  accepted.resolve(deliveryId);
4574
4589
  return new StreamDelivery({
@@ -4901,9 +4916,11 @@ var SessionStore = class {
4901
4916
  if (this.#session === void 0) return this.#observedSession = void 0;
4902
4917
  const sessionId = this.#session.id;
4903
4918
  const streamIndex = this.#conversationClient.view.position;
4904
- if (this.#observedSession?.sessionId !== sessionId || this.#observedSession.streamIndex !== streamIndex) this.#observedSession = {
4919
+ const { inbox } = this.#session.state;
4920
+ if (this.#observedSession?.sessionId !== sessionId || this.#observedSession.streamIndex !== streamIndex || this.#observedSession.inbox !== inbox) this.#observedSession = {
4905
4921
  sessionId,
4906
- streamIndex
4922
+ streamIndex,
4923
+ inbox
4907
4924
  };
4908
4925
  return this.#observedSession;
4909
4926
  }
@@ -2300,6 +2300,7 @@ async function openStreamBody(input) {
2300
2300
  signal
2301
2301
  });
2302
2302
  } catch (error) {
2303
+ if (input.redirect === "error" && error instanceof Error && error.cause instanceof Error && error.cause.message === "unexpected redirect") throw new Error("The session stream refused a redirect. Use the final agent URL instead.", { cause: error });
2303
2304
  if (input.signal?.aborted || !isStreamDisconnectError(error) || attempt === openRetryPolicy.maxAttempts - 1) throw error;
2304
2305
  await sleep(retryDelayMs, input.signal);
2305
2306
  retryDelayMs = Math.min(retryDelayMs * 2, openRetryPolicy.maxDelayMs);
@@ -2448,10 +2449,12 @@ const sendReceipts = /* @__PURE__ */ new WeakMap();
2448
2449
  var StreamDelivery = class {
2449
2450
  sessionId;
2450
2451
  deliveryId;
2452
+ inbox;
2451
2453
  #createStream;
2452
2454
  #result;
2453
2455
  constructor(input) {
2454
2456
  this.sessionId = input.sessionId;
2457
+ this.inbox = input.inbox;
2455
2458
  this.deliveryId = input.deliveryId;
2456
2459
  if (input.receipt !== void 0) sendReceipts.set(this, input.receipt);
2457
2460
  this.#createStream = input.createStream;
@@ -3011,9 +3014,13 @@ var ClientSession = class ClientSession {
3011
3014
  async #send(input, retrySessionNotReady) {
3012
3015
  const initialStreamIndex = this.#state.streamIndex;
3013
3016
  const path = createEveSessionRoutePath(this.#state.sessionId);
3014
- const { response, timing } = await postSessionSend(this.#context, path, input, retrySessionNotReady);
3015
- const { sessionId: responseSessionId, deliveryId } = await readAcceptedMessage(response, this.#state.sessionId);
3017
+ const { response, timing } = await postSessionSend(this.#context, path, input, retrySessionNotReady, this.#state.inbox);
3018
+ const { sessionId: responseSessionId, deliveryId, inbox } = await readAcceptedMessage(response, this.#state.sessionId);
3016
3019
  if (responseSessionId !== this.#state.sessionId) throw new Error("Message route returned a different session id.");
3020
+ this.#state = {
3021
+ ...this.#state,
3022
+ inbox
3023
+ };
3017
3024
  if (deliveryId === void 0) throw new Error("Message route did not return a delivery id. Update the server before sending with this client.");
3018
3025
  return this.#messageResponse(response, input, initialStreamIndex, deliveryId, {
3019
3026
  ...timing,
@@ -3101,7 +3108,7 @@ var ClientSession = class ClientSession {
3101
3108
  }
3102
3109
  #advanceStreamIndex(streamIndex) {
3103
3110
  this.#state = {
3104
- sessionId: this.#state.sessionId,
3111
+ ...this.#state,
3105
3112
  streamIndex: Math.max(this.#state.streamIndex, streamIndex)
3106
3113
  };
3107
3114
  }
@@ -3123,7 +3130,7 @@ var ClientSession = class ClientSession {
3123
3130
  function followClientSession(session, options) {
3124
3131
  return session[followSession](options);
3125
3132
  }
3126
- async function postSessionSend(context, path, input, retrySessionNotReady) {
3133
+ async function postSessionSend(context, path, input, retrySessionNotReady, inbox) {
3127
3134
  const startedAt = Date.now();
3128
3135
  const readyDeadline = startedAt + SESSION_SEND_READY_TIMEOUT_MS;
3129
3136
  let retryDelayMs = SESSION_SEND_RETRY_BASE_DELAY_MS;
@@ -3131,7 +3138,7 @@ async function postSessionSend(context, path, input, retrySessionNotReady) {
3131
3138
  const attemptAt = Date.now();
3132
3139
  try {
3133
3140
  return {
3134
- response: await postTurn(context, path, input, false),
3141
+ response: await postTurn(context, path, input, false, void 0, inbox),
3135
3142
  timing: {
3136
3143
  startedAt,
3137
3144
  notReadyMs: attemptAt - startedAt,
@@ -3168,10 +3175,11 @@ async function postCreateSession(context, options) {
3168
3175
  function isSessionNotReady(error) {
3169
3176
  return SessionNotActiveError.is(error) && error.retryable;
3170
3177
  }
3171
- async function postTurn(context, path, input, requireMessage, stubs) {
3178
+ async function postTurn(context, path, input, requireMessage, stubs, inbox) {
3172
3179
  const body = createMessageBody(input, requireMessage);
3173
3180
  if (body === null) throw new Error(requireMessage ? "Creating a session requires a non-empty message." : "A session turn requires a non-empty message or answers.");
3174
3181
  if (stubs !== void 0) body.stubs = stubs;
3182
+ if (inbox !== void 0) body.inbox = inbox;
3175
3183
  const headers = await context.resolveHeaders(input.headers);
3176
3184
  headers.set("content-type", "application/json");
3177
3185
  const response = await fetch(createClientUrl(context.baseUrl, path), {
@@ -3193,6 +3201,7 @@ async function readAcceptedMessage(response, expected) {
3193
3201
  if (!sessionId) throw new Error("Message route did not return a session id.");
3194
3202
  return {
3195
3203
  sessionId,
3204
+ inbox: typeof payload.inbox === "string" ? payload.inbox : void 0,
3196
3205
  deliveryId: typeof payload.deliveryId === "string" && payload.deliveryId.length > 0 ? payload.deliveryId : void 0
3197
3206
  };
3198
3207
  }
@@ -3224,6 +3233,7 @@ var ClientSessions = class {
3224
3233
  if (sessionId.length === 0) throw new Error("sessionId must be a non-empty string.");
3225
3234
  return new ClientSession(this.#context, {
3226
3235
  sessionId,
3236
+ inbox: options?.inbox,
3227
3237
  streamIndex: options?.streamIndex ?? 0
3228
3238
  });
3229
3239
  }
@@ -3245,7 +3255,7 @@ var Client = class {
3245
3255
  this.#baseUrl = options.baseUrl;
3246
3256
  this.#auth = options.auth;
3247
3257
  this.#headers = options.headers;
3248
- this.#redirect = options.redirect;
3258
+ this.#redirect = options.redirect ?? "error";
3249
3259
  this.sessions = new ClientSessions({
3250
3260
  baseUrl: this.#baseUrl,
3251
3261
  redirect: this.#redirect,
@@ -3342,7 +3352,7 @@ function headersInitToRecord(headers) {
3342
3352
  return Object.fromEntries(new Headers(headers).entries());
3343
3353
  }
3344
3354
  function withRedirectPolicy(init, redirect) {
3345
- return redirect === void 0 ? init : {
3355
+ return {
3346
3356
  ...init,
3347
3357
  redirect
3348
3358
  };
@@ -3444,6 +3454,7 @@ function isAbortError(error) {
3444
3454
 
3445
3455
  //#endregion
3446
3456
  //#region src/client/optimistic-message-submissions.ts
3457
+ let submissionSequence = 0;
3447
3458
  var OptimisticMessageSubmissions = class {
3448
3459
  #optimistic;
3449
3460
  #show;
@@ -3458,7 +3469,7 @@ var OptimisticMessageSubmissions = class {
3458
3469
  }
3459
3470
  submit(input, turnId) {
3460
3471
  if (input.message === void 0) return void 0;
3461
- const id = crypto.randomUUID();
3472
+ const id = crypto.randomUUID?.() ?? `submission_${(submissionSequence += 1).toString()}`;
3462
3473
  this.#submissions.set(id, {
3463
3474
  id,
3464
3475
  message: summarizeMessage(input.message),
@@ -4495,7 +4506,10 @@ var SessionStore = class {
4495
4506
  this.#conversationClient = new ConversationClient(() => this.#publish());
4496
4507
  this.#events = (init.initialEvents ?? []).filter((event) => this.#conversationClient.hydrate(event));
4497
4508
  this.#messageSubmissions = new OptimisticMessageSubmissions((submissions) => this.#conversationClient.showSubmissions(submissions), init.optimistic ?? true);
4498
- this.#session = init.session ?? (init.initialSession === void 0 ? void 0 : this.#client?.sessions.attach(init.initialSession.sessionId, { streamIndex: init.initialSession.streamIndex }));
4509
+ this.#session = init.session ?? (init.initialSession === void 0 ? void 0 : this.#client?.sessions.attach(init.initialSession.sessionId, {
4510
+ streamIndex: init.initialSession.streamIndex,
4511
+ inbox: init.initialSession.inbox
4512
+ }));
4499
4513
  this.#error = this.#recordedFailure();
4500
4514
  this.#snapshot = this.#createSnapshot();
4501
4515
  }
@@ -4570,6 +4584,7 @@ var SessionStore = class {
4570
4584
  this.#messageSubmissions.correlate(submissionId, deliveryId);
4571
4585
  }
4572
4586
  this.#acceptReceipt(deliveryId);
4587
+ this.#callbacks.onSessionChange?.(this.#sessionState);
4573
4588
  }
4574
4589
  accepted.resolve(deliveryId);
4575
4590
  return new StreamDelivery({
@@ -4902,9 +4917,11 @@ var SessionStore = class {
4902
4917
  if (this.#session === void 0) return this.#observedSession = void 0;
4903
4918
  const sessionId = this.#session.id;
4904
4919
  const streamIndex = this.#conversationClient.view.position;
4905
- if (this.#observedSession?.sessionId !== sessionId || this.#observedSession.streamIndex !== streamIndex) this.#observedSession = {
4920
+ const { inbox } = this.#session.state;
4921
+ if (this.#observedSession?.sessionId !== sessionId || this.#observedSession.streamIndex !== streamIndex || this.#observedSession.inbox !== inbox) this.#observedSession = {
4906
4922
  sessionId,
4907
- streamIndex
4923
+ streamIndex,
4924
+ inbox
4908
4925
  };
4909
4926
  return this.#observedSession;
4910
4927
  }