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
@@ -0,0 +1,159 @@
1
+ ---
2
+ title: "Fork Conversations into a New Thread"
3
+ description: "Copy a conversation into a new thread from a tool, so each conversation continues on its own."
4
+ ---
5
+
6
+ Forking copies a conversation into a new session in a new thread, such as a side thread in the same
7
+ channel or a thread in someone's direct messages. The new session starts with the conversation so
8
+ far. After the fork, each conversation continues on its own: a later message in one never reaches
9
+ the other.
10
+
11
+ A fork needs a target that always starts a new session. A Slack target with only a `channelId`
12
+ starts a new thread in any channel the app is in, or in a DM. Many other built-in channels reuse one
13
+ session per chat, so sending the transcript there adds it to the person's existing conversation
14
+ instead of forking.
15
+
16
+ ## How it works
17
+
18
+ Build the fork as a [tool](../tools) so the model decides when to call it. The tool does
19
+ three things:
20
+
21
+ 1. **Identify the destination.** Start a new thread in the current channel, read from
22
+ `ctx.session.auth.current`, open a new direct conversation with the caller, or send a new
23
+ message elsewhere.
24
+ 2. **Decide what context to seed the new session with.** Turn `ctx.messages`, the conversation the
25
+ model saw, into the text messages the new session should have access to.
26
+ 3. **Start the forked session.** Call `ctx.to(channel, target).send(...)` with a first message and
27
+ the selected messages as `history`, the same call a route makes for a
28
+ [cross-channel hand-off](../channels/custom#cross-channel-hand-off). The channel's `receive` hook
29
+ maps the target to a new thread and session, and the message runs the fork's first turn.
30
+
31
+ ## Fork into a new Slack thread
32
+
33
+ This tool lets someone in a Slack thread say "split this off into its own thread":
34
+
35
+ ```ts title="agent/tools/fork_thread.ts"
36
+ import { isChannel } from "eve/channels";
37
+ import { defineTool, type ToolContext } from "eve/tools";
38
+ import { z } from "zod";
39
+ import slack from "../channels/slack";
40
+
41
+ export default defineTool({
42
+ description: "Continue this conversation in a new thread in the current Slack channel.",
43
+ inputSchema: z.object({}),
44
+ async execute(_input, ctx) {
45
+ if (!isChannel(ctx.channel, slack) || ctx.channel.metadata.channelId === null)
46
+ return { forked: false, reason: "This conversation is not in Slack." };
47
+ const { channelId } = ctx.channel.metadata;
48
+ const auth = ctx.session.auth.current;
49
+
50
+ const history = toHistory(ctx.messages);
51
+ const delivery = await ctx.to(slack, { channelId }).send("Continue this conversation here.", {
52
+ auth,
53
+ history,
54
+ idempotencyKey: `${ctx.session.id}:${ctx.callId}`,
55
+ });
56
+ return { forked: true, sessionId: delivery.sessionId };
57
+ },
58
+ });
59
+
60
+ function toHistory(messages: ToolContext["messages"]) {
61
+ return messages.flatMap((message) => {
62
+ if (message.role !== "user" && message.role !== "assistant") return [];
63
+ const text =
64
+ typeof message.content === "string"
65
+ ? message.content
66
+ : message.content.flatMap((part) => (part.type === "text" ? [part.text] : [])).join("");
67
+ return text === "" ? [] : [{ role: message.role, text }];
68
+ });
69
+ }
70
+ ```
71
+
72
+ `ctx.channel` is the channel serving the calling session, and `isChannel` narrows its `metadata`
73
+ to the Slack channel's, which includes the conversation's `channelId`. A call from anywhere else,
74
+ such as a schedule or another channel, gets `forked: false`.
75
+
76
+ A Slack target with only a `channelId` posts the agent's first reply as a new top-level message,
77
+ which becomes the fork's thread. Every call starts a new thread and session. To fork into a
78
+ different channel, pass that channel's id instead; the app must be a member of it.
79
+
80
+ People continue the fork under the channel's usual rules. By default, a reply in a channel thread
81
+ reaches the agent only when it mentions the app. See
82
+ [Continue conversations without repeated mentions](../channels/slack#continue-conversations-without-repeated-mentions)
83
+ to answer every reply in an active thread.
84
+
85
+ ### Fork into direct messages
86
+
87
+ To move the conversation into the caller's DMs instead, open a direct conversation with their
88
+ Slack user id and use its channel id as the target:
89
+
90
+ ```ts title="agent/tools/fork_to_dm.ts"
91
+ import { callSlackApi } from "eve/channels/slack";
92
+
93
+ // In execute(), replace the channelId lookup:
94
+ const auth = ctx.session.auth.current;
95
+ const userId = auth?.attributes.user_id;
96
+ if (!isChannel(ctx.channel, slack) || typeof userId !== "string")
97
+ return { forked: false, reason: "No Slack user to message." };
98
+ const channelId = await openDirectMessage(userId);
99
+
100
+ async function openDirectMessage(userId: string) {
101
+ const response = await callSlackApi({
102
+ botToken: process.env.SLACK_BOT_TOKEN,
103
+ operation: "conversations.open",
104
+ body: { users: userId },
105
+ });
106
+ if (!response.ok) throw new Error(`conversations.open failed: ${String(response.error)}`);
107
+ return (response.channel as { id: string }).id;
108
+ }
109
+ ```
110
+
111
+ The caller's `user_id` comes from `auth.current`, not `ctx.channel.metadata.triggeringUserId`,
112
+ which is whoever started the thread.
113
+
114
+ `conversations.open` needs the `im:write` scope. The example reads `SLACK_BOT_TOKEN`. If your
115
+ channel uses Vercel Connect, pass the connector's `botToken` resolver to `callSlackApi` instead.
116
+
117
+ In the DM, the agent's first reply becomes a thread root, and replies in that thread continue the
118
+ fork. A new top-level DM starts a separate session. The built-in Slack DM handler answers without
119
+ a mention, and needs the `message.im` event and the `im:history` scope; see
120
+ [Message hooks](../channels/slack#message-hooks).
121
+
122
+ ## What the new session sees
123
+
124
+ The fork's first model call sees the copied messages as earlier user and assistant turns, followed
125
+ by the message. `toHistory` keeps the text of user messages and assistant replies, including the
126
+ message that asked for the fork. It leaves out tool calls, files, and reasoning. `ctx.messages`
127
+ also carries eve's context messages, such as request context and recalled memory, and a tool can't
128
+ tell them apart from the conversation yet, so the fork sees them too. The fork's agent can repeat
129
+ anything in the copied history, so only fork into a channel whose members may see that
130
+ conversation, including a DM's. See [Start from prior history](../channels/eve#start-from-prior-history) for how history
131
+ joins a session.
132
+
133
+ After the fork, the original conversation and the fork are separate sessions:
134
+
135
+ - A message in the original thread reaches only its session.
136
+ - A message in the fork's thread reaches only the fork's session.
137
+ - Both keep the conversation from before the fork.
138
+
139
+ The fork runs the root agent with the auth you pass, here the caller's. Only a tool on the root
140
+ agent can fork: `ctx.to()` throws in a subagent, a root copy, or a remote agent call, since the
141
+ fork would outlive the delegation and act for the caller.
142
+
143
+ ## Retries and repeated forks
144
+
145
+ Each call starts a new thread. A tool call that retries runs the tool again with the same
146
+ `ctx.callId`, so Slack maps the retry to the same thread and session, which gets the message once.
147
+ The key also chooses the Slack thread, so it has to be unique across every session that forks
148
+ into this channel, not only across retries. `ctx.callId` is unique only within one session, which
149
+ is why the key includes `ctx.session.id`.
150
+ To stop the model from forking the same conversation twice, say so in the tool's
151
+ `description`, or record each fork in your app and check for one before sending.
152
+
153
+ In a [workflow tool](../tools/workflows), call `ctx.to()` in a `"use step"` helper that receives
154
+ `ctx`, as you would `ctx.getSandbox()`.
155
+
156
+ ## Fork from an earlier point
157
+
158
+ To leave out the latest turns, slice `ctx.messages` before converting it. `ctx.messages` reflects
159
+ history projection and compaction, so a long conversation arrives as the model last saw it.
@@ -4,6 +4,7 @@
4
4
  "pages": [
5
5
  "dynamic-scheduling",
6
6
  "durable-cross-channel-notifications",
7
+ "fork-conversations-into-new-threads",
7
8
  "multi-tenant-auth",
8
9
  "multi-tenant-approvals",
9
10
  "voice-notes",
@@ -47,7 +47,7 @@ Creates a new agent app or adds an agent to an existing app. Always installs dep
47
47
  | `eve init` or `eve init .` in an existing project | Adds `agent/` plus missing `eve`, `ai`, and `zod` dependencies. Requires `package.json` and no existing `agent/` files |
48
48
  | `eve init path/to/app` | Adds an agent to the existing package at `path/to/app` |
49
49
 
50
- Existing packages do not need a target-selection prompt: run `eve init` from the project directory or `eve init path/to/app`. New projects in non-interactive environments need a new directory name, such as `eve init my-agent`.
50
+ Existing packages do not need a target-selection prompt: run `eve init` from the project directory or `eve init path/to/app`. New projects in non-interactive environments need a new directory name, such as `eve init my-agent`. A new project path can be nested, such as `eve init apps/my-agent`; eve creates missing parent directories.
51
51
 
52
52
  After scaffolding in an interactive human terminal, eve opens the TUI directly. Pass `-n` or `--non-interactive` to return after scaffolding instead. It still installs dependencies and follows the normal Git setup behavior. Noninteractive and coding-agent invocations return without starting an interactive session. Fresh projects use the parent workspace's package manager when there is one; otherwise they use the manager that launched `eve init`.
53
53
 
@@ -110,7 +110,7 @@ export default defineSandbox(() => environment.open({ egress: "deny-all" }));
110
110
 
111
111
  A `SandboxConnection` is the byte-level link to one sandbox. Each method has a fixed contract:
112
112
 
113
- - `spawn({ command, workingDirectory, env, signal })` runs `command` as a Bash login-shell script (`bash -lc`) in `workingDirectory`, with the sandbox's environment plus `env`. `env` holds only the per-call variables. Aborting kills the process tree. It resolves to a process with `stdout` and `stderr` byte streams, `wait()`, and `kill()`.
113
+ - `spawn({ command, workingDirectory, env, signal })` runs `command` as a Bash login-shell script (`bash -lc`) in `workingDirectory`, with the sandbox's environment plus `env`. `env` holds only the per-call variables. Aborting kills the process tree. It resolves to a process with `stdout` and `stderr` byte streams, `wait()`, and `kill()`. When the call passes `stdin: true`, the process must also have a writable `stdin` stream; without one, eve kills the process and rejects the call.
114
114
  - `readFile({ path, signal })` resolves to the file's bytes or a byte stream, or to `null` only when the path does not exist. A directory or an unreadable path rejects.
115
115
  - `writeFiles({ files, signal })` writes every file, creating missing parent directories and overwriting existing files. It is the only write primitive.
116
116
  - `removePath({ path, force, recursive, signal })` removes a file or directory. `force` ignores a missing path, and `recursive` removes a non-empty directory.
@@ -26,7 +26,7 @@ export default defineTool({
26
26
  });
27
27
  ```
28
28
 
29
- Relative paths resolve from the workspace root, and `sandbox.resolvePath(".")` returns its absolute path. `run()` returns `{ exitCode, stdout, stderr }`; a nonzero exit code does not throw automatically. Use `spawn()` for a long-running process.
29
+ Relative paths resolve from the workspace root, and `sandbox.resolvePath(".")` returns its absolute path. `run()` returns `{ exitCode, stdout, stderr }`; a nonzero exit code does not throw automatically. Use `spawn()` for a long-running process. Pass `stdin: true` to get a writable `stdin` stream on the process, for example to talk to a stdio server; closing it sends EOF. The Vercel and Docker sandboxes support it.
30
30
 
31
31
  Built-in environments set `AI_AGENT=eve` on every `run()` and `spawn()` call, including the `bash` tool and `prepare`, so agent-aware CLIs such as `vercel` skip interactive prompts. To use a different value, set `AI_AGENT` in the environment's `env` option or pass it in the `env` of one `run()` or `spawn()` call.
32
32
 
@@ -81,7 +81,7 @@ export default defineSchedule({
81
81
 
82
82
  Every run delivers the agent's reply to the channel. To skip a run when there is nothing to report, check that condition in the handler before calling `send`. When only the agent can tell, add the opt-in [`no_reply`](/docs/concepts/built-in-tools#no_reply) tool and ask the agent to call it when there is nothing to report; the run then posts nothing.
83
83
 
84
- - `to(channel, target).send(message, { auth })`: starts a session on another channel. It has the same contract as a route handler's `ctx.to(...)` handle.
84
+ - `to(channel, target).send(message, { auth, ...options })`: starts a session on another channel. It has the same contract and [options](./channels/custom#cross-channel-hand-off) as a route handler's `ctx.to(...)` handle.
85
85
  - `waitUntil(promise)`: extends the cron task's lifetime so the parked session and any in-flight fetches settle before the task ends. Wrap the `send` call in it.
86
86
  - `appAuth`: the app principal (`{ authenticator: "app", principalId: "eve:app", principalType: "runtime" }`). Pass it as `to(...).send(..., { auth: appAuth })` for work the agent does on its own behalf.
87
87
 
@@ -254,6 +254,8 @@ examples.
254
254
  - `ctx.messages`: the model input for the step that requested this call, the same messages a [dynamic capability's](/docs/guides/dynamic-capabilities) `ctx.messages()` returns before that call.
255
255
  - `ctx.abortSignal`: aborts when the active turn is cancelled. Pass it to cancellation-aware work; sandbox sessions from `ctx.getSandbox()` are already bound to it.
256
256
  - `ctx.getSandbox()`: the live [sandbox](/docs/sandbox) handle.
257
+ - `ctx.channel`: the channel serving the session, as [hooks](/docs/guides/hooks) and [dynamic resolvers](/docs/guides/dynamic-capabilities) read it: `kind`, `continuationToken`, and `metadata`, what the channel's `metadata(state)` hook projects, such as a Slack thread's `channelId` and `threadTs`.
258
+ - `ctx.to(channel, target)`: starts or continues a session on one of the agent's channels. See [Start channel sessions](#start-channel-sessions-ctxto).
257
259
 
258
260
  Running in the app runtime is what lets a tool import shared code from `lib/`, read `process.env`, and take part in eve’s durable pause/resume model.
259
261
 
@@ -261,6 +263,38 @@ eve never runs authored tools during discovery. The model sees descriptors first
261
263
 
262
264
  Use operation-specific idempotency keys for external writes such as charges or emails. Approval records consent; it does not deduplicate an operation. See [Crash recovery](/docs/concepts/execution-model-and-durability#resuming-after-a-crash).
263
265
 
266
+ ### Start channel sessions: `ctx.to`
267
+
268
+ `ctx.to(channel, target).send(message, options)` starts a session on one of the agent's channels, or continues the one at that target, through the channel's `receive` hook. It's the same call as a route handler's [cross-channel hand-off](/docs/channels/custom#cross-channel-hand-off), with the same [options](/docs/channels/custom#cross-channel-hand-off), and returns the delivery, whose `sessionId` names the session. Use it when the model decides that work should continue somewhere people can follow it, such as a new Slack thread:
269
+
270
+ ```ts title="agent/tools/escalate.ts"
271
+ import { defineTool } from "eve/tools";
272
+ import { z } from "zod";
273
+ import slack from "../channels/slack";
274
+
275
+ export default defineTool({
276
+ description: "Hand this investigation to the on-call channel in Slack.",
277
+ inputSchema: z.object({ summary: z.string() }),
278
+ async execute({ summary }, ctx) {
279
+ const delivery = await ctx
280
+ .to(slack, { channelId: "C0123ABC" })
281
+ .send("Take over this investigation.", {
282
+ auth: ctx.session.auth.current,
283
+ history: [{ role: "user", text: summary }],
284
+ idempotencyKey: `${ctx.session.id}:${ctx.callId}`,
285
+ });
286
+ return { sessionId: delivery.sessionId };
287
+ },
288
+ });
289
+ ```
290
+
291
+ - The session belongs to the channel. People on that channel continue it, it has no parent, and it keeps running after the tool returns.
292
+ - It runs the root agent with the `auth` you pass. Pass `ctx.session.auth.current` to start it as the caller.
293
+ - A tool call that retries runs the tool again. Pass `` idempotencyKey: `${ctx.session.id}:${ctx.callId}` ``, and the session at the target gets the message once. The destination session dedupes keys from every sender, and `ctx.callId` alone is unique only within the calling session, so include the session id. Built-in channels map a retry to the same session, including a Slack target with only a `channelId`. A platform post made before the session exists, such as an `initialMessage`, repeats.
294
+ - `ctx.to()` throws in a delegated session: a subagent, a root copy, or a remote agent call. A session it started would outlive the delegation and act for the caller, so return the result and send from a tool on the root agent.
295
+
296
+ See [Fork conversations into a new thread](/docs/patterns/fork-conversations-into-new-threads) for a tool that copies the current conversation into a new Slack thread.
297
+
264
298
  ## When a tool throws
265
299
 
266
300
  If an authored tool's `execute` function throws, eve records the call as `call.settled` with `outcome: "failed"` and gives the error to the model as a tool error. The model can respond, choose another action, or call the tool again. eve does not automatically call the tool again based on the exception type, an upstream HTTP status, or a `retryable` property.
@@ -85,14 +85,14 @@ or days later, the run resumes, deploys, and returns. The model sees one tool re
85
85
  `start`, `getRun`, and `resumeHook` from `workflow/api` belong in steps. Your app does not install
86
86
  the SDK; for types, new projects list `eve/workflow-modules` in the tsconfig `types`.
87
87
  - In an `execute` or `task` body, `ctx` is a `WorkflowToolContext` with `session`, `callId`,
88
- `toolName`, `abortSignal`, `agent`, `agents`, and `ask`. A `serve` body gets a
89
- `WorkflowServeContext` with `session`, `toolName`,
88
+ `toolName`, `abortSignal`, `channel`, `agent`, `agents`, and `ask`. A `serve` body gets a
89
+ `WorkflowServeContext` with `session`, `toolName`, `channel`,
90
90
  `agent`, `agents`, `ask`, and `reply`; each call it receives carries its own `callId` and
91
91
  `abortSignal`.
92
92
  A `"use step"` helper that receives `ctx` directly gets a restricted `WorkflowStepToolContext` with
93
- `session`, `callId`, `toolName`, `abortSignal`, `getSandbox`, `getToken`, and `requireAuth`. Read `ctx.agents`, call
94
- `ctx.agent()`, and call `ctx.ask()` in the workflow body; pass only the serializable values the step needs.
95
- Call `getSandbox`, `getToken`, and `requireAuth` in a step: the workflow body throws when it calls them.
93
+ `session`, `callId`, `toolName`, `abortSignal`, `channel`, `getSandbox`, `getToken`, `requireAuth`, and `to`. Read `ctx.agents`, and call
94
+ `ctx.agent()` and `ctx.ask()` in the workflow body; pass only the serializable values the step needs.
95
+ Call `getSandbox`, `getToken`, `requireAuth`, and `to` in a step: the workflow body throws when it calls them.
96
96
  - The tool's input must be a JSON object. Workflow bodies are for static tools under `agent/tools/`,
97
97
  not tools a `defineDynamic` function returns.
98
98
 
@@ -613,6 +613,10 @@ const reviewer = ctx.agent("agent", {
613
613
  });
614
614
  ```
615
615
 
616
+ Without `modelContextWindowTokens`, eve reads the model's context window from the AI Gateway model
617
+ catalog recorded when the agent was built. Set it for a model that catalog doesn't list; otherwise
618
+ the first `send` rejects.
619
+
616
620
  The selection must contain an AI Gateway model id. A live AI SDK `LanguageModel` cannot cross the
617
621
  durable workflow boundary. A remote agent rejects the option because its deployment owns its model
618
622
  configuration.
@@ -778,6 +782,48 @@ When an authored tool accepts a JSON Schema supplied by the model, represent tha
778
782
 
779
783
  This pattern controls specialist selection, not whether the parent model delegates at all. Route before the parent model runs if every incoming request must go through Jev.
780
784
 
785
+ ## Start channel sessions: `ctx.to`
786
+
787
+ A workflow tool reaches the agent's channels with [`ctx.to(channel, target)`](/docs/tools#start-channel-sessions-ctxto), as an ordinary tool does. Call it in a `"use step"` helper that receives `ctx`, like `ctx.getSandbox()`; the workflow body throws when it calls it:
788
+
789
+ ```ts title="agent/tools/open_incident_channel.ts"
790
+ import { defineWorkflowTool, type WorkflowStepToolContext } from "eve/tools";
791
+ import { z } from "zod";
792
+ import slack from "../channels/slack";
793
+ import { createIncidentChannel } from "../lib/incidents";
794
+
795
+ export default defineWorkflowTool({
796
+ description: "Open a Slack channel for an incident and continue the investigation there.",
797
+ inputSchema: z.object({ title: z.string(), summary: z.string() }),
798
+ async task({ title, summary }, ctx) {
799
+ "use workflow";
800
+ const channelId = await createChannel(title);
801
+ return await openIncident(ctx, channelId, summary);
802
+ },
803
+ });
804
+
805
+ async function createChannel(title: string) {
806
+ "use step";
807
+ return await createIncidentChannel(title);
808
+ }
809
+
810
+ async function openIncident(ctx: WorkflowStepToolContext, channelId: string, summary: string) {
811
+ "use step";
812
+ const delivery = await ctx
813
+ .to(slack, { channelId })
814
+ .send("Continue the investigation in this channel.", {
815
+ auth: ctx.session.auth.current,
816
+ history: [{ role: "user", text: summary }],
817
+ idempotencyKey: `${ctx.session.id}:${ctx.callId}:incident`,
818
+ });
819
+ return { channelId, sessionId: delivery.sessionId };
820
+ }
821
+ ```
822
+
823
+ A step that fails retries, so pass an `idempotencyKey` the retry repeats. The destination session dedupes keys from every sender, and `ctx.callId` is unique only within the calling session and is the same for every step of the call, so include `ctx.session.id` and add a label for each send. Create the channel in its own step: a retried send step then reuses the recorded `channelId` instead of creating another channel.
824
+
825
+ `ctx.channel` holds the `kind`, `continuationToken`, and `metadata` of the channel serving the calling session, in the workflow body and in steps, as in [ordinary tools](/docs/tools#the-ctx-parameter).
826
+
781
827
  ## Report progress: `yield`
782
828
 
783
829
  A workflow body may be an async generator whose yields report progress. After processing a yield,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [
@@ -618,7 +618,7 @@
618
618
  "@vercel/oidc": "3.8.0",
619
619
  "@vercel/otel": "2.1.3",
620
620
  "@vercel/queue": "0.5.1",
621
- "@vercel/sandbox": "3.3.0",
621
+ "@vercel/sandbox": "3.9.0",
622
622
  "@vercel/schedules": "0.0.0-alpha.11",
623
623
  "@vercel/sdk": "1.28.8",
624
624
  "@workflow/core": "5.2.3",
@@ -1 +0,0 @@
1
- import{t as e}from"./rolldown-runtime-tZXQYakl.js";import{r as t,t as n}from"./token-util-DpJNusVS.js";var r=e(((e,r)=>{var i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,c=(e,t)=>{for(var n in t)i(e,n,{get:t[n],enumerable:!0})},l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of o(t))!s.call(e,c)&&c!==n&&i(e,c,{get:()=>t[c],enumerable:!(r=a(t,c))||r.enumerable});return e},u=e=>l(i({},`__esModule`,{value:!0}),e),d={};c(d,{refreshToken:()=>m}),r.exports=u(d);var f=t(),p=n();async function m(e){let t=e?.project,n=e?.team;if(!t&&!n){let e=(0,p.findProjectInfo)();t=e.projectId,n=e.teamId}else if(!t||!n){let e=(0,p.findProjectInfo)();t??=e.projectId,n??=e.teamId}if(!t)throw new f.VercelOidcTokenError("Failed to refresh OIDC token: No project specified. Try re-linking your project with `vc link`");let r=(0,p.loadToken)(t);if(!r||(0,p.isExpired)((0,p.getTokenPayload)(r.token),e?.expirationBufferMs)){let i=await(0,p.getVercelToken)({expirationBufferMs:e?.expirationBufferMs});if(r=await(0,p.getVercelOidcToken)(i,t,n),!r)throw new f.VercelOidcTokenError(`Failed to refresh OIDC token`);(0,p.saveToken)(r,t)}process.env.VERCEL_OIDC_TOKEN=r.token}0&&(r.exports={refreshToken:m})}));export default r();export{};
@@ -1 +0,0 @@
1
- import{i as e,t}from"./rolldown-runtime-tZXQYakl.js";var n=t(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{VercelOidcTokenError:()=>u}),t.exports=c(l);var u=class extends Error{constructor(e,t){super(e),this.name=`VercelOidcTokenError`,this.cause=t}toString(){return this.cause?`${this.name}: ${this.message}: ${this.cause}`:`${this.name}: ${this.message}`}};0&&(t.exports={VercelOidcTokenError:u})})),r=t(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{AccessTokenMissingError:()=>u,RefreshAccessTokenFailedError:()=>d}),t.exports=c(l);var u=class extends Error{constructor(){super(`No authentication found. Please log in with the Vercel CLI (vercel login).`),this.name=`AccessTokenMissingError`}},d=class extends Error{constructor(e){super(`Failed to refresh authentication token.`,{cause:e}),this.name=`RefreshAccessTokenFailedError`}};0&&(t.exports={AccessTokenMissingError:u,RefreshAccessTokenFailedError:d})})),i=t(((t,r)=>{var i=Object.create,a=Object.defineProperty,o=Object.getOwnPropertyDescriptor,s=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,u=(e,t)=>{for(var n in t)a(e,n,{get:t[n],enumerable:!0})},d=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let i of s(t))!l.call(e,i)&&i!==n&&a(e,i,{get:()=>t[i],enumerable:!(r=o(t,i))||r.enumerable});return e},f=(e,t,n)=>(n=e==null?{}:i(c(e)),d(t||!e||!e.__esModule?a(n,`default`,{value:e,enumerable:!0}):n,e)),p=e=>d(a({},`__esModule`,{value:!0}),e),m={};u(m,{findRootDir:()=>y,getUserDataDir:()=>b}),r.exports=p(m);var h=f(e(`path`)),g=f(e(`fs`)),_=f(e(`os`)),v=n();function y(){try{let e=process.cwd();for(;e!==h.default.dirname(e);){let t=h.default.join(e,`.vercel`);if(g.default.existsSync(t))return e;e=h.default.dirname(e)}}catch{throw new v.VercelOidcTokenError(`Token refresh only supported in node server environments`)}return null}function b(){if(process.env.XDG_DATA_HOME)return process.env.XDG_DATA_HOME;switch(_.default.platform()){case`darwin`:return h.default.join(_.default.homedir(),`Library/Application Support`);case`linux`:return h.default.join(_.default.homedir(),`.local/share`);case`win32`:return process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:null;default:return null}}0&&(r.exports={findRootDir:y,getUserDataDir:b})})),a=t(((t,n)=>{var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,u=(e,t)=>{for(var n in t)i(e,n,{get:t[n],enumerable:!0})},d=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let s of o(t))!l.call(e,s)&&s!==n&&i(e,s,{get:()=>t[s],enumerable:!(r=a(t,s))||r.enumerable});return e},f=(e,t,n)=>(n=e==null?{}:r(c(e)),d(t||!e||!e.__esModule?i(n,`default`,{value:e,enumerable:!0}):n,e)),p=e=>d(i({},`__esModule`,{value:!0}),e),m={};u(m,{isValidAccessToken:()=>x,readAuthConfig:()=>y,writeAuthConfig:()=>b}),n.exports=p(m);var h=f(e(`fs`)),g=f(e(`path`)),_=s();function v(){let e=(0,_.getVercelDataDir)();if(!e)throw Error(`Unable to find Vercel CLI data directory. Your platform: ${process.platform}. Supported: darwin, linux, win32.`);return g.join(e,`auth.json`)}function y(){try{let e=v();if(!h.existsSync(e))return null;let t=h.readFileSync(e,`utf8`);return t?JSON.parse(t):null}catch{return null}}function b(e){let t=v(),n=g.dirname(t);h.existsSync(n)||h.mkdirSync(n,{mode:504,recursive:!0}),h.writeFileSync(t,JSON.stringify(e,null,2),{mode:384})}function x(e,t=0){if(!e.token)return!1;if(typeof e.expiresAt!=`number`)return!0;let n=Math.floor(Date.now()/1e3),r=t/1e3;return e.expiresAt>=n+r}0&&(n.exports={isValidAccessToken:x,readAuthConfig:y,writeAuthConfig:b})})),o=t(((t,n)=>{var r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,s=(e,t)=>{for(var n in t)r(e,n,{get:t[n],enumerable:!0})},c=(e,t,n,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of a(t))!o.call(e,c)&&c!==n&&r(e,c,{get:()=>t[c],enumerable:!(s=i(t,c))||s.enumerable});return e},l=e=>c(r({},`__esModule`,{value:!0}),e),u={};s(u,{processTokenResponse:()=>g,refreshTokenRequest:()=>h}),n.exports=l(u);var d=e(`os`);let f=`@vercel/oidc node-${process.version} ${(0,d.platform)()} (${(0,d.arch)()}) ${(0,d.hostname)()}`,p=null;async function m(){if(p)return p;let e=await fetch(`https://vercel.com/.well-known/openid-configuration`,{headers:{"user-agent":f}});if(!e.ok)throw Error(`Failed to discover OAuth endpoints`);let t=await e.json();if(!t||typeof t.token_endpoint!=`string`)throw Error(`Invalid OAuth discovery response`);let n=t.token_endpoint;return p=n,n}async function h(e){let t=await m();return await fetch(t,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`,"user-agent":f},body:new URLSearchParams({client_id:`cl_HYyOPBNtFMfHhaUn9L4QPfTZz6TP47bp`,grant_type:`refresh_token`,...e})})}async function g(e){let t=await e.json();if(!e.ok){let e=typeof t==`object`&&t&&`error`in t?String(t.error):`Token refresh failed`;return[Error(e)]}return typeof t!=`object`||!t?[Error(`Invalid token response`)]:typeof t.access_token==`string`?t.token_type===`Bearer`?typeof t.expires_in==`number`?[null,t]:[Error(`Missing expires_in in response`)]:[Error(`Invalid token_type in response`)]:[Error(`Missing access_token in response`)]}0&&(n.exports={processTokenResponse:g,refreshTokenRequest:h})})),s=t(((t,s)=>{var c=Object.create,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyNames,f=Object.getPrototypeOf,p=Object.prototype.hasOwnProperty,m=(e,t)=>{for(var n in t)l(e,n,{get:t[n],enumerable:!0})},h=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let i of d(t))!p.call(e,i)&&i!==n&&l(e,i,{get:()=>t[i],enumerable:!(r=u(t,i))||r.enumerable});return e},g=(e,t,n)=>(n=e==null?{}:c(f(e)),h(t||!e||!e.__esModule?l(n,`default`,{value:e,enumerable:!0}):n,e)),_=e=>h(l({},`__esModule`,{value:!0}),e),v={};m(v,{assertVercelOidcTokenResponse:()=>k,findProjectInfo:()=>A,getTokenPayload:()=>N,getVercelDataDir:()=>E,getVercelOidcToken:()=>O,getVercelToken:()=>D,isExpired:()=>P,loadToken:()=>M,saveToken:()=>j}),s.exports=_(v);var y=g(e(`path`)),b=g(e(`fs`)),x=n(),S=i(),C=a(),w=o(),T=r();function E(){let e=(0,S.getUserDataDir)();return e?y.join(e,`com.vercel.cli`):null}async function D(e){let t=(0,C.readAuthConfig)();if(!t?.token)throw new T.AccessTokenMissingError;if((0,C.isValidAccessToken)(t,e?.expirationBufferMs))return t.token;if(!t.refreshToken)throw(0,C.writeAuthConfig)({}),new T.RefreshAccessTokenFailedError(`No refresh token available`);try{let e=await(0,w.refreshTokenRequest)({refresh_token:t.refreshToken}),[n,r]=await(0,w.processTokenResponse)(e);if(n||!r)throw(0,C.writeAuthConfig)({}),new T.RefreshAccessTokenFailedError(n);let i={token:r.access_token,expiresAt:Math.floor(Date.now()/1e3)+r.expires_in};return r.refresh_token&&(i.refreshToken=r.refresh_token),(0,C.writeAuthConfig)(i),i.token}catch(e){throw(0,C.writeAuthConfig)({}),e instanceof T.AccessTokenMissingError||e instanceof T.RefreshAccessTokenFailedError?e:new T.RefreshAccessTokenFailedError(e)}}async function O(e,t,n){let r=`https://api.vercel.com/v1/projects/${t}/token?source=vercel-oidc-refresh${n?`&teamId=${n}`:``}`,i=await fetch(r,{method:`POST`,headers:{Authorization:`Bearer ${e}`}});if(!i.ok)throw new x.VercelOidcTokenError(`Failed to refresh OIDC token: ${i.statusText}`);let a=await i.json();return k(a),a}function k(e){if(!e||typeof e!=`object`)throw TypeError("Vercel OIDC token is malformed. Expected an object. Please run `vc env pull` and try again");if(!(`token`in e)||typeof e.token!=`string`)throw TypeError("Vercel OIDC token is malformed. Expected a string-valued token property. Please run `vc env pull` and try again")}function A(){let e=(0,S.findRootDir)();if(!e)throw new x.VercelOidcTokenError("Unable to find project root directory. Have you linked your project with `vc link?`");let t=y.join(e,`.vercel`,`project.json`);if(!b.existsSync(t))throw new x.VercelOidcTokenError("project.json not found, have you linked your project with `vc link?`");let n=JSON.parse(b.readFileSync(t,`utf8`));if(typeof n.projectId!=`string`&&typeof n.orgId!=`string`)throw TypeError("Expected a string-valued projectId property. Try running `vc link` to re-link your project.");return{projectId:n.projectId,teamId:n.orgId}}function j(e,t){let n=(0,S.getUserDataDir)();if(!n)throw new x.VercelOidcTokenError(`Unable to find user data directory. Please reach out to Vercel support.`);let r=y.join(n,`com.vercel.token`,`${t}.json`),i=JSON.stringify(e);b.mkdirSync(y.dirname(r),{mode:504,recursive:!0}),b.writeFileSync(r,i),b.chmodSync(r,432)}function M(e){let t=(0,S.getUserDataDir)();if(!t)throw new x.VercelOidcTokenError(`Unable to find user data directory. Please reach out to Vercel support.`);let n=y.join(t,`com.vercel.token`,`${e}.json`);if(!b.existsSync(n))return null;let r=JSON.parse(b.readFileSync(n,`utf8`));return k(r),r}function N(e){let t=e.split(`.`);if(t.length!==3)throw new x.VercelOidcTokenError("Invalid token. Please run `vc env pull` and try again");let n=t[1].replace(/-/g,`+`).replace(/_/g,`/`),r=n.padEnd(n.length+(4-n.length%4)%4,`=`);return JSON.parse(Buffer.from(r,`base64`).toString(`utf8`))}function P(e,t=0){return e.exp*1e3<Date.now()+t}0&&(s.exports={assertVercelOidcTokenResponse:k,findProjectInfo:A,getTokenPayload:N,getVercelDataDir:E,getVercelOidcToken:O,getVercelToken:D,isExpired:P,loadToken:M,saveToken:j})}));export{r as n,n as r,s as t};
@@ -1 +0,0 @@
1
- import{t as e}from"./token-util-DpJNusVS.js";export default e();export{};
@@ -1 +0,0 @@
1
- const e=`3.3.0`;export{e as t};
@@ -1,25 +0,0 @@
1
- /**
2
- * eve's default Slack rendering for human-input requests: posting approval
3
- * and question controls, and retiring an approval's messages once it resolves.
4
- */
5
- import type { RefusedAnswer, RequestBatch, RequestSettlement } from "#channel/interaction-prompts.js";
6
- import type { SlackApprovalChannelResolver, SlackChannelInternalEvents, SlackEventContext } from "#public/channels/slack/slackChannel.js";
7
- /**
8
- * Default `interaction.opened` handler — renders each pending HITL
9
- * request as Slack `block_actions`. Buttons by default; radio for
10
- * ≤6-option select requests; static_select for >6-option select
11
- * requests. Batches split into multiple posts when they would exceed
12
- * Slack's 50-block message cap.
13
- */
14
- export declare function defaultInteractionsHandler(approvalChannel?: SlackApprovalChannelResolver): (data: RequestBatch, ctx: Parameters<NonNullable<SlackChannelInternalEvents["interaction.opened"]>>[1]) => Promise<void>;
15
- /**
16
- * A responder an approval's policy or check refused hears why privately. A pending answer gets
17
- * no notice: an ephemeral cannot be removed once the approval settles, and the card itself
18
- * reports the outcome moments later.
19
- */
20
- export declare function notifyRefusedResponder(refused: RefusedAnswer, channel: SlackEventContext): Promise<void>;
21
- /**
22
- * Retires an approval's card: with the person whose press decided it, or as no longer needed
23
- * when it ended any other way (the user replied instead, or it was withdrawn).
24
- */
25
- export declare function settleApproval(resolution: RequestSettlement, channel: SlackEventContext): Promise<void>;
@@ -1,2 +0,0 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{slackUserIdForPrincipal,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{SLACK_MAX_BLOCKS_PER_MESSAGE,truncateMessageText}from"#public/channels/slack/limits.js";import{buildAnsweredBlocks,decodeHitlActionId,renderInteractionPostParts}from"#public/channels/slack/hitl.js";import{answeredBlocksFromActionBlock}from"#public/channels/slack/interaction-cards.js";import{deliverPrivateInteraction}from"#public/channels/slack/private-approval-delivery.js";const log=createLogger(`slack.defaults`);function defaultInteractionsHandler(e){return async(t,n)=>{let{channel:r}=n,i=[],a=[];for(let r of t.requests)((e===void 0?`thread`:await e(r,n))===`direct-message`?i:a).push(r);await postPublicInteractions(a,r);for(let e of i){let t=slackUserIdFromAuthContext(n.session.auth.current)??r.state.triggeringUserId;if(!t){log.warn(`direct-message input request not delivered because no reviewer was resolved`,{requestId:e.interactionId,sessionId:n.session.id});continue}let i=await deliverPrivateInteraction({previewMessageTs:r.state.triggeringMessageTs??r.slack.threadTs,request:e,reviewer:t,slack:r.slack});recordApprovalCards(r.state,[e],i);try{let n=await r.thread.post(`Waiting on ${e.request.kind===`approval`?`approval`:`a response`} from <@${t}>…`);n.id&&recordApprovalCards(r.state,[e],{...i,announcementTs:n.id})}catch(t){logError(log,`failed to announce private input request`,t,{requestId:e.interactionId,sessionId:n.session.id})}}}}async function postPublicInteractions(e,t){let n=new Map;for(let r of buildInteractionPosts(e)){let e=await t.thread.post({blocks:r.blocks,text:r.text});if(r.kind===`details`){if(e.id)for(let t of r.requests)n.set(t.interactionId,e.id);continue}for(let i of r.requests){let a=n.get(i.interactionId);recordApprovalCards(t.state,[i],{detailsMessageTs:a,messageBlocks:r.blocks,messageTs:e.id})}}}function recordApprovalCards(e,t,n){if(!n.messageTs)return;let r={...e.pendingApprovalCards};for(let e of t)e.request.kind===`approval`&&(r[e.interactionId]=n);e.pendingApprovalCards=r}async function settleApprovalCard(e,t,n){let r=e.state.pendingApprovalCards??{},i=r[t];if(i===void 0)return;let a={...r};delete a[t],e.state.pendingApprovalCards=a;let o=i.messageChannelId??e.state.channelId;if(o===null)return;let s=i.messageBlocks.flatMap(e=>blockContainsRequestAction(e,t)?answeredBlocksFromActionBlock({answerLabel:n.label,block:e,userId:n.userId})??buildAnsweredBlocks({answerLabel:n.label,promptBlocks:[]}):[e]);for(let[e,t]of Object.entries(a))t.messageTs===i.messageTs&&(a[e]={...t,messageBlocks:s});await settleRequest(e,`chat.update`,t,{blocks:s,channel:o,text:n.text,ts:i.messageTs});let c=i.detailsMessageTs;c!==void 0&&!Object.values(a).some(e=>e.detailsMessageTs===c)&&await settleRequest(e,`chat.delete`,t,{channel:o,ts:c}),i.announcementTs!==void 0&&e.state.channelId!==null&&await settleRequest(e,`chat.update`,t,{channel:e.state.channelId,text:n.announcement,ts:i.announcementTs})}async function settleRequest(e,t,n,r){try{let i=await e.slack.request(t,r);i.ok!==!0&&log.warn(`failed to retire approval message`,{error:i.error,operation:t,requestId:n})}catch(e){logError(log,`failed to retire approval message`,e,{operation:t,requestId:n})}}function buildInteractionPosts(e){let t=[],n=[];for(let r of e){let e=renderInteractionPostParts(r);e.details&&t.push({...e.details,request:r}),n.push({...e.controls,request:r})}return[...groupInteractionPostParts(t).map(e=>({...e,kind:`details`})),...groupInteractionPostParts(n).map(e=>({...e,kind:`controls`}))]}function groupInteractionPostParts(e){let t=[];for(let n of e){let e=t.at(-1);e&&e.blocks.length+n.blocks.length<=SLACK_MAX_BLOCKS_PER_MESSAGE?(e.blocks.push(...n.blocks),e.fallbacks.push(n.text),e.requests.push(n.request)):t.push({blocks:[...n.blocks],fallbacks:[n.text],requests:[n.request]})}return t.map(e=>({blocks:e.blocks,requests:e.requests,text:truncateMessageText(e.fallbacks.join(`
2
- `))}))}function blockContainsRequestAction(e,t){if(typeof e!=`object`||!e)return!1;let n=e;return[n.actions,n.elements].some(e=>Array.isArray(e)&&e.some(e=>{if(typeof e!=`object`||!e)return!1;let n=e.action_id;return typeof n==`string`&&decodeHitlActionId(n)?.requestId===t}))}async function notifyRefusedResponder(e,t){let n=slackUserIdForPrincipal(t.state,e.responder?.id);n!==void 0&&await t.thread.postEphemeral(n,e.reason??`We couldn’t verify your response. Please try again.`)}async function settleApproval(e,t){if(e.kind!==`approval`)return;if(e.outcome!==`accepted`&&e.outcome!==`declined`){await settleApprovalCard(t,e.interactionId,{announcement:`This approval is no longer needed.`,label:`No longer needed`,text:`No longer needed`});return}let n=e.outcome===`accepted`,r=slackUserIdForPrincipal(t.state,e.responder?.id),i=r===void 0?``:` by <@${r}>`,a=n?`Approve`:`Cancel`;await settleApprovalCard(t,e.interactionId,{announcement:`${n?`Approved`:`Cancelled`}${i}.`,label:a,text:`Answered: ${a}`,userId:r})}export{defaultInteractionsHandler,notifyRefusedResponder,settleApproval};
@@ -1,10 +0,0 @@
1
- export interface RuntimeModelMetadata {
2
- readonly contextWindowTokens: number;
3
- readonly maxOutputTokens?: number;
4
- readonly resolvedModelId: string;
5
- }
6
- export interface RuntimeModelCatalog {
7
- getByGatewayId(modelId: string): Promise<RuntimeModelMetadata | null>;
8
- getByProviderModelId(provider: string, providerModelId: string): Promise<RuntimeModelMetadata | null>;
9
- }
10
- export declare function createRuntimeModelCatalog(fetchCatalog?: typeof globalThis.fetch): RuntimeModelCatalog;
@@ -1 +0,0 @@
1
- import{AI_GATEWAY_MODELS_CATALOG_URL,vercelGatewayFetch}from"#internal/gateway.js";import{findCatalogModelByProviderModelId,findCatalogModelBySlug,modelCatalogLimitsFromProvider,modelCatalogResponseSchema}from"#internal/model-catalog.js";function createRuntimeModelCatalog(e=vercelGatewayFetch){let t=null,loadCatalog=async()=>(t===null&&(t=e(AI_GATEWAY_MODELS_CATALOG_URL).then(async e=>{if(!e.ok)throw Error(`AI Gateway model catalog request failed with HTTP ${e.status} ${e.statusText}.`);return parseCatalogResponse(await e.json())}).catch(e=>{throw t=null,e})),await t);return{async getByGatewayId(e){let t=await loadCatalog(),n=findCatalogModelBySlug(t.models,e);if(n===void 0)return null;for(let e of n.providers){let t=modelCatalogLimitsFromProvider(e);if(t!==null)return{...t,resolvedModelId:n.slug}}return null},async getByProviderModelId(e,t){let n=await loadCatalog(),r=findCatalogModelByProviderModelId({models:n.models,provider:e,providerAliases:n.providerAliases,providerModelId:t});if(r===null)return null;let i=modelCatalogLimitsFromProvider(r.provider);return i===null?null:{...i,resolvedModelId:r.model.slug}}}}function parseCatalogResponse(e){let t=modelCatalogResponseSchema.safeParse(e);if(!t.success)throw Error(`AI Gateway model catalog response did not match the expected schema.`);return t.data}export{createRuntimeModelCatalog};