eve 0.27.0 → 0.27.2

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 (177) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/src/channel/resolve-active-session.d.ts +4 -0
  3. package/dist/src/channel/resolve-active-session.js +1 -0
  4. package/dist/src/channel/routes.d.ts +11 -0
  5. package/dist/src/chunks/{use-eve-agent-PMY2WkAt.js → use-eve-agent-CbF0l_Fp.js} +66 -27
  6. package/dist/src/chunks/{use-eve-agent-AeQLLhu9.js → use-eve-agent-CgxB9WQv.js} +66 -27
  7. package/dist/src/cli/dev/tui/vercel-trusted-sources-policy.d.ts +1 -1
  8. package/dist/src/cli/dev/tui/vercel-trusted-sources.js +1 -1
  9. package/dist/src/client/index.d.ts +1 -1
  10. package/dist/src/client/open-stream.d.ts +15 -2
  11. package/dist/src/client/open-stream.js +1 -1
  12. package/dist/src/client/session.d.ts +3 -2
  13. package/dist/src/client/session.js +1 -1
  14. package/dist/src/client/types.d.ts +32 -0
  15. package/dist/src/compiled/.vendor-stamp.json +16 -16
  16. package/dist/src/compiled/@ai-sdk/anthropic/index.js +2 -2
  17. package/dist/src/compiled/@ai-sdk/google/index.d.ts +9 -2
  18. package/dist/src/compiled/@ai-sdk/google/index.js +6 -6
  19. package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +18 -3
  20. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  21. package/dist/src/compiled/@ai-sdk/openai/index.d.ts +285 -24
  22. package/dist/src/compiled/@ai-sdk/openai/index.js +2 -2
  23. package/dist/src/compiled/@ai-sdk/otel/index.js +1 -1
  24. package/dist/src/compiled/@ai-sdk/provider-utils/index.d.ts +7 -3
  25. package/dist/src/compiled/@vercel/oidc/index.js +28 -2
  26. package/dist/src/compiled/@vercel/sandbox/api-client/api-client.d.ts +11 -0
  27. package/dist/src/compiled/@vercel/sandbox/api-client/validators.d.ts +7 -0
  28. package/dist/src/compiled/@vercel/sandbox/command.d.ts +8 -0
  29. package/dist/src/compiled/@vercel/sandbox/execution-context.d.ts +116 -0
  30. package/dist/src/compiled/@vercel/sandbox/index.d.ts +3 -1
  31. package/dist/src/compiled/@vercel/sandbox/index.js +20 -19
  32. package/dist/src/compiled/@vercel/sandbox/network-policy.d.ts +13 -5
  33. package/dist/src/compiled/@vercel/sandbox/sandbox-user.d.ts +212 -0
  34. package/dist/src/compiled/@vercel/sandbox/sandbox.d.ts +133 -2
  35. package/dist/src/compiled/@vercel/sandbox/session.d.ts +2 -1
  36. package/dist/src/compiled/@vercel/sandbox/snapshot.d.ts +1 -1
  37. package/dist/src/compiled/@workflow/core/create-hook.d.ts +41 -22
  38. package/dist/src/compiled/@workflow/core/flushable-stream.d.ts +29 -0
  39. package/dist/src/compiled/@workflow/core/global.d.ts +2 -0
  40. package/dist/src/compiled/@workflow/core/index.js +2 -2
  41. package/dist/src/compiled/@workflow/core/private.d.ts +6 -17
  42. package/dist/src/compiled/@workflow/core/private.js +1 -1
  43. package/dist/src/compiled/@workflow/core/replay-payload-cache.d.ts +56 -0
  44. package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +11 -0
  45. package/dist/src/compiled/@workflow/core/runtime/helpers.d.ts +6 -4
  46. package/dist/src/compiled/@workflow/core/runtime/resume-hook.d.ts +4 -1
  47. package/dist/src/compiled/@workflow/core/runtime/step-executor.d.ts +39 -0
  48. package/dist/src/compiled/@workflow/core/runtime.js +3 -3
  49. package/dist/src/compiled/@workflow/core/serialization.d.ts +44 -4
  50. package/dist/src/compiled/@workflow/core/symbols.d.ts +13 -0
  51. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  52. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  53. package/dist/src/compiled/@workflow/errors/error-codes.d.ts +2 -0
  54. package/dist/src/compiled/@workflow/errors/index.d.ts +14 -0
  55. package/dist/src/compiled/@workflow/errors/index.js +1 -1
  56. package/dist/src/compiled/@workflow/world/events.d.ts +15 -0
  57. package/dist/src/compiled/@workflow/world/hooks.d.ts +2 -1
  58. package/dist/src/compiled/@workflow/world/interfaces.d.ts +77 -4
  59. package/dist/src/compiled/@workflow/world-local/index.js +4 -4
  60. package/dist/src/compiled/@workflow/world-vercel/index.js +16 -16
  61. package/dist/src/compiled/_chunks/node/{auth-BuQkq4Gr.js → auth-l7F2PF2b.js} +2 -2
  62. package/dist/src/compiled/_chunks/node/{version-DD-FX9rK.js → version-CjHTLx2a.js} +1 -1
  63. package/dist/src/compiled/_chunks/node/virtual-loader-Cwgujv7l.js +1 -0
  64. package/dist/src/compiled/_chunks/workflow/attribute-changes-Dk1fP6vt.js +20 -0
  65. package/dist/src/compiled/_chunks/workflow/{chunk-BHKSVoKr.js → chunk-BTyA9uPd.js} +1 -1
  66. package/dist/src/compiled/_chunks/workflow/dist-B3qkUnLJ.js +3 -0
  67. package/dist/src/compiled/_chunks/workflow/{dist-CSfKNxQW.js → dist-BX517Nmz.js} +2 -2
  68. package/dist/src/compiled/_chunks/workflow/{dist-zLevmMyU.js → dist-C_VDTghO.js} +10 -10
  69. package/dist/src/compiled/_chunks/workflow/{dist-CttsoN4D.js → dist-CrxV2rsR.js} +1 -1
  70. package/dist/src/compiled/_chunks/workflow/{functions-PT9ri9Hu.js → functions-Dv8dc_-I.js} +1 -1
  71. package/dist/src/compiled/_chunks/workflow/resume-hook-czpG5xnP.js +1 -0
  72. package/dist/src/compiled/_chunks/workflow/run-6zaMjFY6.js +1 -0
  73. package/dist/src/compiled/_chunks/workflow/sleep-B2u06FWc.js +1 -0
  74. package/dist/src/compiled/_chunks/workflow/{src-CQuMexnO.js → src-D1EltLDA.js} +1 -1
  75. package/dist/src/compiled/_chunks/workflow/{token-CaY0nA5J.js → token-Cz223z3C.js} +1 -1
  76. package/dist/src/compiled/_chunks/workflow/token-DWqA5-DA.js +1 -0
  77. package/dist/src/compiled/_chunks/workflow/token-util-B6qBs3-0.js +27 -0
  78. package/dist/src/compiled/_chunks/workflow/{undici-fb54XL8i.js → undici-C2ZhYnV9.js} +1 -1
  79. package/dist/src/compiled/_chunks/workflow/wrapper-Cc2otuUR.js +6 -0
  80. package/dist/src/compiled/env-runner/index.js +1 -1
  81. package/dist/src/compiled/env-runner/node-worker.js +1 -1
  82. package/dist/src/compiled/eventsource-parser/stream/index.d.ts +16 -3
  83. package/dist/src/compiled/jose/index.js +1 -1
  84. package/dist/src/compiler/compile-from-memory.d.ts +5 -0
  85. package/dist/src/compiler/compile-from-memory.js +1 -1
  86. package/dist/src/compiler/extension-compatibility.d.ts +10 -10
  87. package/dist/src/compiler/extension-compatibility.js +1 -1
  88. package/dist/src/execution/deliver-payloads.d.ts +1 -1
  89. package/dist/src/execution/deliver-payloads.js +1 -1
  90. package/dist/src/execution/workflow-entry.js +1 -1
  91. package/dist/src/execution/workflow-steps.js +1 -1
  92. package/dist/src/harness/session-limit-continuation.d.ts +8 -0
  93. package/dist/src/harness/session-limit-continuation.js +1 -1
  94. package/dist/src/harness/session-limit-enforcement.d.ts +9 -6
  95. package/dist/src/harness/session-limit-enforcement.js +1 -1
  96. package/dist/src/harness/stale-input-responses.d.ts +21 -3
  97. package/dist/src/harness/stale-input-responses.js +2 -2
  98. package/dist/src/harness/tool-loop.js +2 -2
  99. package/dist/src/harness/turn-cancellation.d.ts +13 -0
  100. package/dist/src/harness/turn-cancellation.js +1 -1
  101. package/dist/src/internal/application/package.js +1 -1
  102. package/dist/src/internal/authored-module-evaluation-error.d.ts +2 -0
  103. package/dist/src/internal/authored-module-evaluation-error.js +4 -0
  104. package/dist/src/internal/authored-module-loader.js +2 -2
  105. package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
  106. package/dist/src/internal/workflow-bundle/vercel-workflow-output.js +1 -1
  107. package/dist/src/node_modules/.pnpm/@vercel_sdk@1.28.8/node_modules/@vercel/sdk/esm/models/updateprojectblock.js +1 -0
  108. package/dist/src/node_modules/.pnpm/@vercel_sdk@1.28.8/node_modules/@vercel/sdk/esm/models/updateprojectprojectsbranchmatcher.js +1 -0
  109. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  110. package/dist/src/public/channels/discord/defaults.js +1 -1
  111. package/dist/src/public/channels/eve.js +2 -1
  112. package/dist/src/public/channels/linear/api.d.ts +2 -0
  113. package/dist/src/public/channels/linear/api.js +3 -1
  114. package/dist/src/public/channels/linear/defaults.js +2 -2
  115. package/dist/src/public/channels/linear/hitl.d.ts +8 -13
  116. package/dist/src/public/channels/linear/hitl.js +3 -3
  117. package/dist/src/public/channels/linear/index.d.ts +1 -1
  118. package/dist/src/public/channels/linear/index.js +1 -1
  119. package/dist/src/public/channels/linear/linearChannel.js +1 -1
  120. package/dist/src/public/channels/slack/api.d.ts +46 -2
  121. package/dist/src/public/channels/slack/api.js +1 -1
  122. package/dist/src/public/channels/slack/attachments.d.ts +6 -5
  123. package/dist/src/public/channels/slack/defaults.js +1 -1
  124. package/dist/src/public/channels/slack/inbound.d.ts +31 -0
  125. package/dist/src/public/channels/slack/inbound.js +1 -1
  126. package/dist/src/public/channels/slack/index.d.ts +2 -2
  127. package/dist/src/public/channels/slack/interactions.d.ts +2 -1
  128. package/dist/src/public/channels/slack/interactions.js +1 -1
  129. package/dist/src/public/channels/slack/slackChannel.d.ts +118 -13
  130. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  131. package/dist/src/public/channels/slack/thread.d.ts +4 -3
  132. package/dist/src/public/channels/slack/thread.js +1 -1
  133. package/dist/src/public/channels/teams/defaults.js +2 -2
  134. package/dist/src/public/channels/teams/index.d.ts +1 -1
  135. package/dist/src/public/channels/teams/teamsChannel.d.ts +7 -2
  136. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  137. package/dist/src/public/channels/telegram/defaults.js +1 -1
  138. package/dist/src/public/definitions/channel.d.ts +1 -1
  139. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +2 -1
  140. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  141. package/dist/src/setup/scaffold/create/project.js +2 -2
  142. package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
  143. package/dist/src/setup/scaffold/create/web-template.js +90 -5
  144. package/dist/src/setup/scaffold/update/channels.js +2 -2
  145. package/dist/src/setup/scaffold/update/connections.js +1 -1
  146. package/dist/src/svelte/index.js +1 -1
  147. package/dist/src/svelte/use-eve-agent.js +1 -1
  148. package/dist/src/vue/index.js +1 -1
  149. package/dist/src/vue/use-eve-agent.js +1 -1
  150. package/docs/agent-config.md +10 -5
  151. package/docs/channels/slack.mdx +115 -12
  152. package/docs/channels/teams.mdx +10 -3
  153. package/docs/guides/client/streaming.mdx +15 -0
  154. package/docs/guides/deployment/vercel.mdx +1 -1
  155. package/docs/guides/frontend/overview.mdx +2 -0
  156. package/package.json +22 -22
  157. package/dist/src/compiled/@workflow/core/step-hydration-cache.d.ts +0 -148
  158. package/dist/src/compiled/_chunks/node/virtual-loader-B8K8w9cr.js +0 -1
  159. package/dist/src/compiled/_chunks/workflow/attribute-changes-CXiYB8h9.js +0 -20
  160. package/dist/src/compiled/_chunks/workflow/dist-BYo8Gno2.js +0 -3
  161. package/dist/src/compiled/_chunks/workflow/resume-hook-CHXbXuRZ.js +0 -1
  162. package/dist/src/compiled/_chunks/workflow/run-BjtAGa3_.js +0 -1
  163. package/dist/src/compiled/_chunks/workflow/sleep-BJAv0MFP.js +0 -1
  164. package/dist/src/compiled/_chunks/workflow/token-D4VIwGlD.js +0 -1
  165. package/dist/src/compiled/_chunks/workflow/wrapper-ulTvoTdW.js +0 -6
  166. package/dist/src/node_modules/.pnpm/@vercel_sdk@1.28.1/node_modules/@vercel/sdk/esm/models/updateprojectprojectsaction.js +0 -1
  167. package/dist/src/node_modules/.pnpm/@vercel_sdk@1.28.1/node_modules/@vercel/sdk/esm/models/updateprojectprojectsoptionsallowlist.js +0 -1
  168. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/lib/primitives.js +0 -0
  169. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/lib/schemas.js +0 -0
  170. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/models/aclaction.js +0 -0
  171. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/models/sdkvalidationerror.js +0 -0
  172. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/defaultToZeroValue.js +0 -0
  173. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/fp.js +0 -0
  174. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/primitives.js +0 -0
  175. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/rfcdate.js +0 -0
  176. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/smartUnion.js +0 -0
  177. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/unrecognized.js +0 -0
@@ -16,8 +16,10 @@ export default eveChannel({
16
16
  `,"app/_components/agent-chat.tsx":`"use client";
17
17
 
18
18
  import type { UserContent } from "ai";
19
+ import { Client, type HandleMessageStreamEvent } from "eve/client";
19
20
  import { useEveAgent } from "eve/react";
20
21
  import { AlertCircleIcon } from "lucide-react";
22
+ import { useCallback, useRef, useState } from "react";
21
23
  import {
22
24
  Conversation,
23
25
  ConversationContent,
@@ -35,16 +37,92 @@ import { AgentMessage } from "./agent-message";
35
37
  const AGENT_NAME = "__EVE_INIT_APP_NAME__";
36
38
 
37
39
  type AgentStatus = ReturnType<typeof useEveAgent>["status"];
40
+ type CancellationState = "idle" | "requested" | "cancelling";
41
+
42
+ type Cancellation = {
43
+ requested: boolean;
44
+ sentTurnId?: string;
45
+ turnId?: string;
46
+ };
38
47
 
39
48
  export function AgentChat() {
40
- const agent = useEveAgent();
49
+ const [session] = useState(() =>
50
+ new Client({ host: "", preserveCompletedSessions: true }).session(),
51
+ );
52
+ const cancellationRef = useRef<Cancellation>({ requested: false });
53
+ const [cancellationError, setCancellationError] = useState<string>();
54
+ const [cancellationState, setCancellationState] = useState<CancellationState>("idle");
55
+
56
+ const cancelTurn = useCallback(
57
+ (turnId: string) => {
58
+ const cancellation = cancellationRef.current;
59
+ if (!cancellation.requested || cancellation.sentTurnId === turnId) {
60
+ return;
61
+ }
62
+
63
+ cancellation.sentTurnId = turnId;
64
+ setCancellationState("cancelling");
65
+
66
+ void session.cancel({ turnId }).catch((error: unknown) => {
67
+ if (cancellationRef.current !== cancellation) {
68
+ return;
69
+ }
70
+
71
+ cancellation.requested = false;
72
+ cancellation.sentTurnId = undefined;
73
+ setCancellationError(toErrorMessage(error));
74
+ setCancellationState("idle");
75
+ });
76
+ },
77
+ [session],
78
+ );
79
+
80
+ const handleEvent = useCallback(
81
+ (event: HandleMessageStreamEvent) => {
82
+ if (event.type !== "turn.started") {
83
+ return;
84
+ }
85
+
86
+ const cancellation = cancellationRef.current;
87
+ cancellation.turnId = event.data.turnId;
88
+ cancelTurn(event.data.turnId);
89
+ },
90
+ [cancelTurn],
91
+ );
92
+
93
+ const agent = useEveAgent({ onEvent: handleEvent, session });
41
94
  const isBusy = agent.status === "submitted" || agent.status === "streaming";
42
95
  const isEmpty = agent.data.messages.length === 0;
96
+ const errorMessage = cancellationError ?? agent.error?.message;
97
+ const submitStatus = isBusy && cancellationState !== "idle" ? "submitted" : agent.status;
98
+
99
+ const prepareTurn = () => {
100
+ cancellationRef.current = { requested: false };
101
+ setCancellationError(undefined);
102
+ setCancellationState("idle");
103
+ };
104
+
105
+ const requestCancellation = () => {
106
+ if (!isBusy || cancellationState !== "idle") {
107
+ return;
108
+ }
109
+
110
+ const cancellation = cancellationRef.current;
111
+ cancellation.requested = true;
112
+ setCancellationError(undefined);
113
+ setCancellationState("requested");
114
+
115
+ if (cancellation.turnId !== undefined) {
116
+ cancelTurn(cancellation.turnId);
117
+ }
118
+ };
43
119
 
44
120
  const handleSubmit = async (message: PromptInputMessage) => {
45
121
  const text = message.text.trim();
46
122
  if ((text.length === 0 && message.files.length === 0) || isBusy) return;
47
123
 
124
+ prepareTurn();
125
+
48
126
  if (message.files.length === 0) {
49
127
  await agent.send({ message: text });
50
128
  return;
@@ -69,7 +147,7 @@ export function AgentChat() {
69
147
  const composer = (
70
148
  <PromptInput onSubmit={handleSubmit}>
71
149
  <PromptInputTextarea placeholder="Send a message…" />
72
- <PromptInputSubmit onStop={agent.stop} status={agent.status} />
150
+ <PromptInputSubmit onStop={requestCancellation} status={submitStatus} />
73
151
  </PromptInput>
74
152
  );
75
153
 
@@ -84,13 +162,13 @@ export function AgentChat() {
84
162
  </header>
85
163
  )}
86
164
 
87
- {agent.error ? (
165
+ {errorMessage ? (
88
166
  <div className="mx-auto w-full max-w-3xl shrink-0 px-4 pt-2 sm:px-6">
89
167
  <div className="flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm">
90
168
  <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />
91
169
  <div>
92
170
  <p className="font-medium">Request failed</p>
93
- <p className="mt-0.5 text-muted-foreground">{agent.error.message}</p>
171
+ <p className="mt-0.5 text-muted-foreground">{errorMessage}</p>
94
172
  </div>
95
173
  </div>
96
174
  </div>
@@ -107,7 +185,10 @@ export function AgentChat() {
107
185
  }
108
186
  key={message.id}
109
187
  message={message}
110
- onInputResponses={(inputResponses) => agent.send({ inputResponses })}
188
+ onInputResponses={(inputResponses) => {
189
+ prepareTurn();
190
+ return agent.send({ inputResponses });
191
+ }}
111
192
  />
112
193
  ))}
113
194
  </ConversationContent>
@@ -134,6 +215,10 @@ export function AgentChat() {
134
215
  );
135
216
  }
136
217
 
218
+ function toErrorMessage(error: unknown): string {
219
+ return error instanceof Error ? error.message : "Unable to cancel the response.";
220
+ }
221
+
137
222
  function StatusDot({ status }: { readonly status: AgentStatus }) {
138
223
  const isLive = status === "submitted" || status === "streaming";
139
224
  const tone =
@@ -1,7 +1,7 @@
1
- import{pinnedNodeEngineMajor}from"../../node-engine.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"../create/web-template.js";import{DEFAULT_EVE_PACKAGE_CONTRACT,resolveEvePackageContract}from"../create/project.js";import{basename,join,resolve}from"node:path";import{readFile,readdir}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/eve/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,NEXT_PACKAGE_NAME=`next`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`],WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH);function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readPackageJsonObject(e){if(!await pathExists(e))return;let t=JSON.parse(await readFile(e,`utf8`));return isJsonObject(t)?t:void 0}async function readDependencyVersion(e,t){let n=await readPackageJsonObject(e);if(n===void 0||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){let n=await readPackageJsonObject(e);return n!==void 0&&packageJsonHasDependency(n,t)}async function isNextJsProject(e){return hasPackageDependency(join(e,`package.json`),NEXT_PACKAGE_NAME)}const VERCEL_HOST_FRAMEWORK_PRESETS={"@sveltejs/kit":`sveltekit`,[NEXT_PACKAGE_NAME]:`nextjs`,nuxt:`nuxtjs`,nuxt3:`nuxtjs`,"nuxt-edge":`nuxtjs`,"nuxt-nightly":`nuxtjs`};async function resolveVercelHostFrameworkPreset(e){let t=await readPackageJsonObject(join(e,`package.json`));if(t!==void 0){for(let[e,n]of Object.entries(VERCEL_HOST_FRAMEWORK_PRESETS))if(packageJsonHasDependency(t,e))return n}}async function hasVercelHostFramework(e){return await resolveVercelHostFrameworkPreset(e)!==void 0}async function ensurePackageDependency(e,t,n){return!await pathExists(e)||await readDependencyVersion(e,t)===n?[]:(await patchPackageJson(e,{dependencies:{[t]:n}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{evePackage:e?.evePackage??DEFAULT_EVE_PACKAGE_CONTRACT,aiPackageVersion:e?.aiPackageVersion??`^7.0.26`,nextPackageVersion:e?.nextPackageVersion??`16.3.0-preview.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(t,n,i,s,u){let f=join(t,`package.json`);if(!await pathExists(f))return{mutations:[]};let m=resolveEvePackageContract(s.evePackage),h=pinnedNodeEngineMajor(m.nodeEngine),g={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:resolveVersionToken(`aiPackageVersion`,s.aiPackageVersion),eve:formatEveDependencySpecifier(m.version),next:resolveVersionToken(`nextPackageVersion`,s.nextPackageVersion),react:resolveVersionToken(`reactPackageVersion`,s.reactPackageVersion),"react-dom":resolveVersionToken(`reactDomPackageVersion`,s.reactDomPackageVersion),streamdown:resolveVersionToken(`streamdownPackageVersion`,s.streamdownPackageVersion),zod:resolveVersionToken(`zodPackageVersion`,s.zodPackageVersion)},_={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":h,"@types/react":resolveVersionToken(`typesReactPackageVersion`,s.typesReactPackageVersion),"@types/react-dom":resolveVersionToken(`typesReactDomPackageVersion`,s.typesReactDomPackageVersion),typescript:`6.0.3`},v=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts,y=isPackageManagerWorkspaceMember(n,i),b={dependencies:g,devDependencies:_,scripts:v};y||(b.nodeEngineRequirement=m.nodeEngine);let x=await patchPackageJson(f,b),S=(await patchWorkspaceRootPackageJson(n,i,{aiPackageVersion:g.ai,nodeEngineRequirement:m.nodeEngine,onWorkspaceRootMutation:u})).nodeEngineOverride??x.nodeEngineOverride;return{mutations:[{path:f,dependencies:Object.keys(g),devDependencies:Object.keys(_),scripts:Object.keys(v)}],nodeEngineOverride:S}}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){if(!e.startsWith(`slack/`)||e.length===6)throw Error(`Invalid Slack connector UID "${e}".`);return`import { connectSlackCredentials } from "@vercel/connect/eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"../create/web-template.js";import{DEFAULT_EVE_PACKAGE_CONTRACT,resolveEvePackageContract}from"../create/project.js";import{basename,join,resolve}from"node:path";import{readFile,readdir}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/eve/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,NEXT_PACKAGE_NAME=`next`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`],WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH);function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readPackageJsonObject(e){if(!await pathExists(e))return;let t=JSON.parse(await readFile(e,`utf8`));return isJsonObject(t)?t:void 0}async function readDependencyVersion(e,t){let n=await readPackageJsonObject(e);if(n===void 0||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){let n=await readPackageJsonObject(e);return n!==void 0&&packageJsonHasDependency(n,t)}async function isNextJsProject(e){return hasPackageDependency(join(e,`package.json`),NEXT_PACKAGE_NAME)}const VERCEL_HOST_FRAMEWORK_PRESETS={"@sveltejs/kit":`sveltekit`,[NEXT_PACKAGE_NAME]:`nextjs`,nuxt:`nuxtjs`,nuxt3:`nuxtjs`,"nuxt-edge":`nuxtjs`,"nuxt-nightly":`nuxtjs`};async function resolveVercelHostFrameworkPreset(e){let t=await readPackageJsonObject(join(e,`package.json`));if(t!==void 0){for(let[e,n]of Object.entries(VERCEL_HOST_FRAMEWORK_PRESETS))if(packageJsonHasDependency(t,e))return n}}async function hasVercelHostFramework(e){return await resolveVercelHostFrameworkPreset(e)!==void 0}async function ensurePackageDependency(e,t,n){return!await pathExists(e)||await readDependencyVersion(e,t)===n?[]:(await patchPackageJson(e,{dependencies:{[t]:n}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{evePackage:e?.evePackage??DEFAULT_EVE_PACKAGE_CONTRACT,aiPackageVersion:e?.aiPackageVersion??`^7.0.34`,nextPackageVersion:e?.nextPackageVersion??`16.3.0-preview.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(t,n,i,s,u){let f=join(t,`package.json`);if(!await pathExists(f))return{mutations:[]};let m=resolveEvePackageContract(s.evePackage),h=pinnedNodeEngineMajor(m.nodeEngine),g={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:resolveVersionToken(`aiPackageVersion`,s.aiPackageVersion),eve:formatEveDependencySpecifier(m.version),next:resolveVersionToken(`nextPackageVersion`,s.nextPackageVersion),react:resolveVersionToken(`reactPackageVersion`,s.reactPackageVersion),"react-dom":resolveVersionToken(`reactDomPackageVersion`,s.reactDomPackageVersion),streamdown:resolveVersionToken(`streamdownPackageVersion`,s.streamdownPackageVersion),zod:resolveVersionToken(`zodPackageVersion`,s.zodPackageVersion)},_={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":h,"@types/react":resolveVersionToken(`typesReactPackageVersion`,s.typesReactPackageVersion),"@types/react-dom":resolveVersionToken(`typesReactDomPackageVersion`,s.typesReactDomPackageVersion),typescript:`6.0.3`},v=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts,y=isPackageManagerWorkspaceMember(n,i),b={dependencies:g,devDependencies:_,scripts:v};y||(b.nodeEngineRequirement=m.nodeEngine);let x=await patchPackageJson(f,b),S=(await patchWorkspaceRootPackageJson(n,i,{aiPackageVersion:g.ai,nodeEngineRequirement:m.nodeEngine,onWorkspaceRootMutation:u})).nodeEngineOverride??x.nodeEngineOverride;return{mutations:[{path:f,dependencies:Object.keys(g),devDependencies:Object.keys(_),scripts:Object.keys(v)}],nodeEngineOverride:S}}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){if(!e.startsWith(`slack/`)||e.length===6)throw Error(`Invalid Slack connector UID "${e}".`);return`import { connectSlackCredentials } from "@vercel/connect/eve";
2
2
  import { slackChannel } from "eve/channels/slack";
3
3
 
4
4
  export default slackChannel({
5
5
  credentials: connectSlackCredentials(${JSON.stringify(e)}),
6
6
  });
7
- `}function renderWebAppTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,``)}async function ensureWebVercelJson(e){return await pathExists(e)?`skipped`:(await writeTextFile(e,`${JSON.stringify({$schema:`https://openapi.vercel.sh/vercel.json`},null,2)}\n`,{force:!0}),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let n of WEB_COMPETING_NEXT_CONFIG_PATHS){let i=join(e,n);await pathExists(i)&&t.push(i)}return t}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),n=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await isNextJsProject(e.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let a=resolveWebPackageVersions(e.webPackageVersions),o=e.packageManager??`pnpm`,c=resolve(e.workspaceProbeDirectory??e.projectRoot),l=await patchWebPackageJson(e.projectRoot,o,c,a,e.onWorkspaceRootMutation),d=[],f=[],p=[],g=[],_=basename(resolve(e.projectRoot));if(e.configureVercelServices??!0){let t=join(e.projectRoot,`vercel.json`);await ensureWebVercelJson(t)===`written`?d.push(t):g.push(t)}let v=await applyPackageManagerWorkspaceConfiguration({packageManager:o,projectRoot:e.projectRoot,workspaceProbeRoot:c,onWorkspaceRootMutation:e.onWorkspaceRootMutation});d.push(...v.filesWritten),g.push(...v.filesSkipped);for(let[t,n]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(e.projectRoot,t);if(t===`agent/channels/eve.ts`&&!e.force&&await pathExists(a)){g.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(n,_),{force:!0}),d.push(a),o&&f.push(a)}p.push(...await findCompetingNextConfigFiles(e.projectRoot));let y=[...new Set(d)],b=[...new Set(g)].filter(e=>!y.includes(e)),x={kind:`web`,action:n?`overwritten`:`created`,filesWritten:y,filesSkipped:b,packageJsonUpdated:l.mutations};return f.length>0&&(x.filesOverwritten=f),p.length>0&&(x.competingNextConfigFiles=p),l.nodeEngineOverride!==void 0&&(x.nodeEngineOverride=l.nodeEngineOverride),x}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),n=await pathExists(t);if(!e.force&&n)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let o=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`0.4.0`),s=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,o),c=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(t,buildSlackTemplate(e.slackConnectorUid??`slack/${c}`),{force:e.force});let l={kind:`slack`,action:n?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:s,slackConnectorSlug:c};return n&&(l.filesOverwritten=[t]),l}async function listAuthoredChannels(e){let r=join(e,`agent/channels`),i;try{i=await readdir(r,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let a=[];for(let e of i){if(e.isFile()){let n=getSupportedModuleBaseName(e.name);n!==null&&a.push(n);continue}if(e.isDirectory())try{(await readdir(join(r,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&a.push(e.name)}catch{}}return a.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,hasVercelHostFramework,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug,resolveVercelHostFrameworkPreset};
7
+ `}function renderWebAppTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,``)}async function ensureWebVercelJson(e){return await pathExists(e)?`skipped`:(await writeTextFile(e,`${JSON.stringify({$schema:`https://openapi.vercel.sh/vercel.json`},null,2)}\n`,{force:!0}),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let n of WEB_COMPETING_NEXT_CONFIG_PATHS){let i=join(e,n);await pathExists(i)&&t.push(i)}return t}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),n=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await isNextJsProject(e.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let a=resolveWebPackageVersions(e.webPackageVersions),o=e.packageManager??`pnpm`,c=resolve(e.workspaceProbeDirectory??e.projectRoot),l=await patchWebPackageJson(e.projectRoot,o,c,a,e.onWorkspaceRootMutation),d=[],f=[],p=[],g=[],_=basename(resolve(e.projectRoot));if(e.configureVercelServices??!0){let t=join(e.projectRoot,`vercel.json`);await ensureWebVercelJson(t)===`written`?d.push(t):g.push(t)}let v=await applyPackageManagerWorkspaceConfiguration({packageManager:o,projectRoot:e.projectRoot,workspaceProbeRoot:c,onWorkspaceRootMutation:e.onWorkspaceRootMutation});d.push(...v.filesWritten),g.push(...v.filesSkipped);for(let[t,n]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(e.projectRoot,t);if(t===`agent/channels/eve.ts`&&!e.force&&await pathExists(a)){g.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(n,_),{force:!0}),d.push(a),o&&f.push(a)}p.push(...await findCompetingNextConfigFiles(e.projectRoot));let y=[...new Set(d)],b=[...new Set(g)].filter(e=>!y.includes(e)),x={kind:`web`,action:n?`overwritten`:`created`,filesWritten:y,filesSkipped:b,packageJsonUpdated:l.mutations};return f.length>0&&(x.filesOverwritten=f),p.length>0&&(x.competingNextConfigFiles=p),l.nodeEngineOverride!==void 0&&(x.nodeEngineOverride=l.nodeEngineOverride),x}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),n=await pathExists(t);if(!e.force&&n)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let o=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`0.4.2`),s=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,o),c=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(t,buildSlackTemplate(e.slackConnectorUid??`slack/${c}`),{force:e.force});let l={kind:`slack`,action:n?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:s,slackConnectorSlug:c};return n&&(l.filesOverwritten=[t]),l}async function listAuthoredChannels(e){let r=join(e,`agent/channels`),i;try{i=await readdir(r,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let a=[];for(let e of i){if(e.isFile()){let n=getSupportedModuleBaseName(e.name);n!==null&&a.push(n);continue}if(e.isDirectory())try{(await readdir(join(r,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&a.push(e.name)}catch{}}return a.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,hasVercelHostFramework,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug,resolveVercelHostFrameworkPreset};
@@ -1,4 +1,4 @@
1
- import{endpointForProtocol}from"../connections/catalog.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.0`,CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(`
1
+ import{endpointForProtocol}from"../connections/catalog.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.2`,CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(`
2
2
  `)}\n }),\n`;case`none`:return``}}function renderMcpTemplate(e,t,n){return`${n.kind===`connect`?`import { connect } from "@vercel/connect/eve";
3
3
  import { defineMcpClientConnection } from "eve/connections";
4
4
  `:`import { defineMcpClientConnection } from "eve/connections";
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-AeQLLhu9.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-CgxB9WQv.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-AeQLLhu9.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-CgxB9WQv.js";
2
2
 
3
3
  export { useEveAgent };
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-PMY2WkAt.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-CbF0l_Fp.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-PMY2WkAt.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-CbF0l_Fp.js";
2
2
 
3
3
  export { useEveAgent };
@@ -139,11 +139,16 @@ Input and output budgets are checked independently. The model call that crosses
139
139
  either limit is allowed to finish because providers only report exact token
140
140
  usage after a call completes. Before the next model call, eve pauses the
141
141
  session and sends a deterministic continuation prompt with two options:
142
- **Continue** grants a fresh budget window of the configured size (both input
143
- and output windows reset together), and **Stop** ends the session gracefully
144
- (`session.completed`) declining is a user decision, not an error. A reply
145
- that answers neither option re-raises the prompt; the reply stays in history
146
- and is processed once the budget is granted.
142
+ **Approve** grants a fresh budget window of the configured size (both input
143
+ and output windows reset together), and **Stop** cancels the in-flight turn
144
+ through the standard cancellation path (`turn.cancelled` `session.waiting`)
145
+ a user decision, not an error. The session stays resumable; because it is
146
+ still over budget, the next message re-raises the prompt. Declining a
147
+ delegated child's prompt cancels the root turn, which cascades to the whole
148
+ delegation tree — the delegating parent never receives an error result it
149
+ could retry against a fresh quota share. A reply that answers neither option
150
+ re-raises the prompt; the reply stays in history and is processed once the
151
+ budget is granted.
147
152
 
148
153
  Sessions that cannot reach a human — task-mode runs such as schedules and
149
154
  subagents without input proxying — skip the prompt and fail the next model
@@ -36,7 +36,11 @@ export default slackChannel({
36
36
  });
37
37
  ```
38
38
 
39
- `connectSlackCredentials` returns `{ botToken, webhookVerifier }`, keeping token rotation, multi-workspace tenancy, and request verification inside Connect rather than your code. Deploy once the trigger destination and channel file are ready:
39
+ `connectSlackCredentials` returns `{ botToken, webhookVerifier }`, keeping token rotation, multi-workspace tenancy, and request verification inside Connect rather than your code.
40
+
41
+ ### Deploy
42
+
43
+ Deploy once the trigger destination and channel file are ready:
40
44
 
41
45
  ```bash
42
46
  VERCEL_USE_EXPERIMENTAL_FRAMEWORKS=1 vercel deploy --prod
@@ -44,17 +48,116 @@ VERCEL_USE_EXPERIMENTAL_FRAMEWORKS=1 vercel deploy --prod
44
48
 
45
49
  `VERCEL_USE_EXPERIMENTAL_FRAMEWORKS=1` lets the Vercel CLI recognize eve as a framework during the build. eve's own setup commands set the same flag.
46
50
 
47
- ## How the channel handles messages
51
+ ## How the channel handles inbound events
52
+
53
+ ### Message hooks
54
+
55
+ Message hooks return `{ auth }` to dispatch, `null` to drop, or `{ auth, context }` to inject background into history.
56
+
57
+ - `onMessage(ctx, message)` handles Slack `message` events. eve drops messages authored by the installed app before this hook runs, preventing self-reply loops. Messages from other bots remain visible; check `message.author?.isBot` when those should also be ignored. `ctx.isBotMentioned()` and `ctx.isSubscribed()` support mention and active-thread policies.
58
+ - `onAppMention(ctx, message)` handles only `app_mention` and takes precedence over `onMessage`. Its default derives workspace-scoped auth and posts `Thinking…`.
59
+ - `onDirectMessage(ctx, message)` handles only DMs and takes precedence over `onMessage`. Bot-authored messages and edits are filtered first; Slack requires `message.im` and `im:history`.
60
+
61
+ | Incoming event | Handler order |
62
+ | ---------------------- | ------------------------------------------------------------------- |
63
+ | App mention | `onAppMention` → `onMessage` → `onEvent` → built-in mention default |
64
+ | Direct message | `onDirectMessage` → `onMessage` → `onEvent` → built-in DM default |
65
+ | Other Slack message | `onMessage` → `onEvent` → ignore |
66
+ | Other Events API event | `onEvent` → ignore |
67
+
68
+ Only the first available handler runs. A message hook returning `null` drops the message; it does not continue down the table.
69
+
70
+ `onInteraction(action, ctx)` separately handles `block_actions` callbacks not consumed by HITL. eve attaches the triggering Slack user id to the same model message as its text, preserving speaker attribution without profile lookups.
71
+
72
+ #### Continue conversations without repeated mentions
73
+
74
+ Use `onMessage` to handle explicit mentions and continue replies in threads with an active eve session:
75
+
76
+ ```ts title="agent/channels/slack.ts"
77
+ export default slackChannel({
78
+ credentials: connectSlackCredentials("slack/my-agent"),
79
+ async onMessage(ctx, message) {
80
+ if (message.author?.isBot) return null;
81
+ const isDirectMessage = message.raw.channel_type === "im";
82
+ return isDirectMessage || ctx.isBotMentioned() || (await ctx.isSubscribed())
83
+ ? { auth: null }
84
+ : null;
85
+ },
86
+ });
87
+ ```
88
+
89
+ `isBotMentioned()` identifies an explicit mention. `isSubscribed()` checks whether the message belongs to a thread with an active eve session. For Vercel Connect, open **Advanced** when creating the connector and add `message.channels` under **Trigger Event Types** and `channels:history` under **Bot Scopes**. Private channels additionally need `message.groups` and `groups:history`.
90
+
91
+ Use `ctx.thread.listParticipants()` when routing depends on who has joined the thread. It fetches the current thread and returns unique human Slack user ids in first-appearance order, so the first id is the starting author for a human-started thread. Bot and system messages are excluded:
92
+
93
+ ```ts
94
+ async onMessage(ctx, message) {
95
+ if (!message.author || message.author.isBot) return null;
96
+
97
+ const participants = await ctx.thread.listParticipants();
98
+ const isGroupFollowUpFromStarter =
99
+ participants.length > 1 && participants[0] === message.author.userId;
100
+
101
+ return isGroupFollowUpFromStarter ? { auth: null } : null;
102
+ }
103
+ ```
104
+
105
+ Like `threadContext`, this helper calls `conversations.replies` and requires the matching Slack history scope. It observes at most the first 50 messages of the thread, and a failed fetch is logged and swallowed, so the returned list may be empty or stale — treat an unexpected empty list as "don't route" rather than "no participants".
106
+
107
+ #### Cancel and replace in-flight work
108
+
109
+ Message hooks (`onMessage`, `onAppMention`, and `onDirectMessage`) receive a thread-bound `ctx.cancel({ turnId? })` helper. Call it before returning `{ auth }` to stop the current turn and queue the new Slack message as replacement input, producing a debounce-like experience:
110
+
111
+ ```ts title="agent/channels/slack.ts"
112
+ export default slackChannel({
113
+ credentials: connectSlackCredentials("slack/my-agent"),
114
+ async onMessage(ctx, message) {
115
+ const shouldHandle = ctx.isBotMentioned() || (await ctx.isSubscribed());
116
+ if (!shouldHandle || message.author?.isBot) return null;
117
+
118
+ await ctx.cancel();
119
+ return { auth: null };
120
+ },
121
+ });
122
+ ```
123
+
124
+ `"accepted"` means the cancellation request was consumed; `"no_active_turn"` means the thread had no cancellable turn. Both are successful outcomes, so the hook can still return `{ auth }` to deliver the replacement message. Cancellation never sends input itself. A custom `onInteraction` handler receives the same bound helper, which is useful for a Stop button that should cancel without replacement input.
125
+
126
+ ### Other Events API callbacks
127
+
128
+ Use `onEvent` for subscribed events such as `reaction_added`, `team_join`, or `channel_created`. It receives the raw, open-ended Slack event and owns control flow. `ctx.receive(options)` is the current Slack channel's pre-bound form of the schedule API's `receive(slack, options)`: call it zero, one, or many times to start turns. Each call accepts `message`, `target`, and `auth`, and returns the resulting session.
129
+
130
+ ```ts title="agent/channels/slack.ts"
131
+ import { connectSlackCredentials } from "@vercel/connect/eve";
132
+ import { slackChannel } from "eve/channels/slack";
133
+
134
+ const onboardingChannels = ["C0123ABC", "C0456DEF"];
135
+
136
+ export default slackChannel({
137
+ credentials: connectSlackCredentials("slack/my-agent"),
138
+ async onEvent({ receive }, event) {
139
+ if (event.type !== "team_join") return;
140
+
141
+ await Promise.all(
142
+ onboardingChannels.map((channelId) =>
143
+ receive({
144
+ message: `A user joined the Slack workspace. Onboard them from this event:\n${JSON.stringify(event)}`,
145
+ target: { channelId },
146
+ auth: null,
147
+ }),
148
+ ),
149
+ );
150
+ },
151
+ });
152
+ ```
48
153
 
49
- ### Dispatch
154
+ The webhook invocation already keeps an awaited `onEvent` handler alive. Use `ctx.waitUntil(promise)` for deliberately detached work, matching a handler-form schedule. `ctx.slack.request(operation, body)` provides workspace-scoped Slack Web API access, while `ctx.envelope` carries delivery metadata such as `team_id`, `event_id`, and `event_time`. Calls to `ctx.receive` automatically seed the callback's team id into Slack session state.
50
155
 
51
- Inbound hooks decide whether to dispatch a turn and with what `auth`. Return `{ auth }` to dispatch, `null` to drop, or `{ auth, context }` to inject background into history.
156
+ Because a generic event is not necessarily tied to one thread, its cancellation helper takes the target explicitly: `ctx.cancel({ channelId, threadTs, turnId? })`. Pair it with `ctx.receive(...)` when an event should replace in-flight work, or call it alone to stop the turn.
52
157
 
53
- - `onAppMention(ctx, message)` handles `app_mention` events. The default derives workspace-scoped auth and posts a `Thinking…` indicator.
54
- - `onDirectMessage(ctx, message)` handles `message.im` events (needs `im:history` scope). Bot-authored messages and edits are filtered out first.
55
- - `onInteraction(action, ctx)` handles `block_actions` callbacks not consumed by HITL.
158
+ `onEvent` is the raw fallback after the message hooks. If an event is not claimed by `onAppMention`, `onDirectMessage`, or `onMessage`, an authored `onEvent` receives it; otherwise eve applies the built-in mention/DM default or ignores it.
56
159
 
57
- eve attaches the triggering Slack user id to the same model message as the message text. This keeps speaker attribution intact when several people use one thread without making profile lookup requests.
160
+ `onEvent` covers JSON `event_callback` deliveries only. URL verification, slash commands, and interactive payloads do not reach it. Add every desired event and required OAuth scope to the Slack app's Event Subscriptions configuration; eve can only handle events Slack sends.
58
161
 
59
162
  You get the triggering mention by default, but not the earlier replies in the thread. Enable `threadContext` to fetch and inject them with every message attributed by stable Slack user id. Use `since: "last-agent-reply"` so repeated mentions inject only what is new:
60
163
 
@@ -71,14 +174,14 @@ export default slackChannel({
71
174
  `since` sets the boundary for what each mention injects and accepts three values:
72
175
 
73
176
  - `"thread-root"` (the default): every prior message in the thread, on every mention. `threadContext: {}` behaves the same.
74
- - `"last-agent-reply"`: only messages after the agent's last reply, keeping repeated mentions incremental.
177
+ - `"last-agent-reply"`: only messages after this installed agent's last reply, keeping repeated mentions incremental. Replies from other Slack bots do not move the boundary.
75
178
  - A predicate `(message: SlackThreadMessage) => boolean`: only messages after the last one it matches, such as "since the last message that mentioned a particular user".
76
179
 
77
- `threadContext` performs one `conversations.replies` request for each triggering thread reply and requires the matching Slack history scope. Omit it when the agent should see only direct mentions. `loadThreadContextMessages` remains available when you need custom filtering or non-model processing of the raw thread messages.
180
+ `threadContext` requires the matching Slack history scope. Thread helpers reuse messages already loaded within the same inbound handler, and overlapping refreshes share one `conversations.replies` request. Omit it when the agent should see only direct mentions. `loadThreadContextMessages` remains available when you need custom filtering or non-model processing of the raw thread messages.
78
181
 
79
182
  ### Slack API calls outside a handler
80
183
 
81
- Inside webhook-side handlers (`onAppMention`, `onInteraction`, `events`),
184
+ Inside webhook-side handlers (`onAppMention`, `onEvent`, `onInteraction`, `events`),
82
185
  `ctx.slack.request(operation, body)` is the raw-API escape hatch. Outside
83
186
  those contexts there is no handle — a schedule resolving reactions on old
84
187
  messages, for example, has no inbound Slack request. For that, call the
@@ -105,7 +208,7 @@ when you need the bearer token itself.
105
208
 
106
209
  ### Delivery
107
210
 
108
- The default handlers reply in-thread and show progress. Typing indicators post automatically: `Thinking…` on inbound, `Working…` on `turn.started`, a truncated reasoning snippet on `reasoning.appended`, and an action label on `actions.requested` — the tool name plus its most telling argument (`grep useEveAgent`, `read_file agent/agent.ts`), the subagent or remote-agent name for dispatched calls, and `+N more` when the model requests several actions at once. The model's own pre-tool narration, when present, takes precedence over the derived label. Reasoning snippets build progressively: extensions of at least four characters appear immediately, while smaller streamed deltas use the five-second refresh interval to avoid one Slack request per token. Override `events["reasoning.appended"]` if you prefer generic wording. Override `onAppMention` or the `events` handlers to customize.
211
+ The default handlers reply in-thread and show progress. Typing indicators post automatically: `Thinking…` on inbound, `Working…` on `turn.started`, a truncated reasoning snippet on `reasoning.appended`, and an action label on `actions.requested` — the tool name plus its most telling argument (`grep useEveAgent`, `read_file agent/agent.ts`), the subagent or remote-agent name for dispatched calls, and `+N more` when the model requests several actions at once. The model's own pre-tool narration, when present, takes precedence over the derived label. Reasoning snippets build progressively: extensions of at least four characters appear immediately, while smaller streamed deltas use the five-second refresh interval to avoid one Slack request per token. Override `events["reasoning.appended"]` if you prefer generic wording. Override an inbound handler or the `events` handlers to customize.
109
212
 
110
213
  Outbound text preserves bare `@` tokens as literal text. To mention a user, embed Slack's `<@USER_ID>` syntax directly or use `channel.thread.mentionUser(userId)`.
111
214
 
@@ -28,17 +28,24 @@ By default the channel mounts at `POST /eve/v1/teams`. Point your Azure Bot or T
28
28
 
29
29
  The default `onMessage` handles two cases: personal-chat messages, and channel or group-chat messages that mention the bot directly. Ambient resource-specific-consent messages are dropped unless you override it. Before dispatch, eve strips the mention, adds a `<teams_context>` block, and scopes channel and group threads by root activity id (`replyToId ?? id`).
30
30
 
31
+ For example, use the contextual `isSubscribed()` helper to continue active threads without requiring another mention:
32
+
31
33
  ```ts
32
34
  import { defaultTeamsAuth, teamsChannel } from "eve/channels/teams";
33
35
 
34
36
  export default teamsChannel({
35
- onMessage(ctx, message) {
36
- if (message.scope !== "personal" && !message.isBotMentioned) return null;
37
- return { auth: defaultTeamsAuth(message) };
37
+ async onMessage(ctx, message) {
38
+ if (message.from.role === "bot" || message.from.id === message.recipient.id) return null;
39
+ const isDirectMessage = message.scope === "personal";
40
+ return isDirectMessage || message.isBotMentioned || (await ctx.isSubscribed())
41
+ ? { auth: defaultTeamsAuth(message) }
42
+ : null;
38
43
  },
39
44
  });
40
45
  ```
41
46
 
47
+ `isSubscribed()` checks whether the conversation has previously involved the agent.
48
+
42
49
  ### Delivery
43
50
 
44
51
  Replies post as Markdown (`textFormat: "markdown"`), with oversized text split across messages and a typing indicator sent on turn start and action requests.
@@ -105,6 +105,21 @@ If you support refresh while an authorization prompt is pending, keep the sessio
105
105
 
106
106
  HTTP connections can end before a run does. The client reconnects from the number of events already consumed, so long turns continue without replaying events. It stops at a turn boundary, when aborted, or when the stream can no longer make progress.
107
107
 
108
+ Set `streamReconnectPolicy: { reconnect: false }` when a relay or proxy owns the cursor and reconnection policy. This makes a single stream GET attempt and returns when that connection ends; it does not stop the server-side turn:
109
+
110
+ ```ts
111
+ const response = await session.send({
112
+ message: "Run the long operation.",
113
+ streamReconnectPolicy: { reconnect: false },
114
+ });
115
+
116
+ for await (const event of response) {
117
+ console.log(event.type);
118
+ }
119
+ ```
120
+
121
+ The same option is available on manual attachments as `session.stream({ streamReconnectPolicy: { reconnect: false } })`.
122
+
108
123
  ## Open a stream manually
109
124
 
110
125
  Use `session.stream()` when you already have a session cursor and only need to attach to the existing stream:
@@ -60,7 +60,7 @@ eve deploy
60
60
  Vercel uses the generated output to configure these services:
61
61
 
62
62
  - **Web runtime**: serves health, session, stream, channel, callback, and schedule routes
63
- - **Vercel Workflow**: persists and resumes durable runs
63
+ - **Vercel Workflow**: persists and resumes durable runs, with optimistic replay preconditions enabled so stale event-log snapshots reload before they can commit
64
64
  - **Vercel Cron**: invokes authored schedules
65
65
  - **Vercel Sandbox**: runs sandbox sessions selected by `defaultBackend()`
66
66
 
@@ -100,6 +100,8 @@ Assistant text, reasoning, tool calls, and tool results stream into `data` as th
100
100
 
101
101
  `stop()` is local: turns are resumable across disconnects, so detaching the stream never cancels the work — the turn keeps running and billing on the server. To stop the turn itself, POST the session's [cancel route](../../channels/eve#routes); the turn then settles on the stream as `turn.cancelled` followed by `session.waiting`.
102
102
 
103
+ A chat UI with a Stop button should own the `ClientSession` passed to `useEveAgent`, create it with `preserveCompletedSessions: true`, and call `session.cancel({ turnId })` after observing that turn's `turn.started` event. The `turnId` guard makes a late click a harmless no-op instead of cancelling a newer turn. Keep the stream open until the cancellation boundary arrives; do not use `stop()` for this interaction.
104
+
103
105
  After eve confirms an attachment turn with `message.received`, the default reducer projects each
104
106
  received attachment as a `file` part on the user message. The part includes `mediaType`, optional
105
107
  `filename` and `size`, and a `url` only when the original attachment was browser-resolvable.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.27.0",
3
+ "version": "0.27.2",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [
@@ -282,13 +282,13 @@
282
282
  "nitro": "3.0.260610-beta"
283
283
  },
284
284
  "devDependencies": {
285
- "@ai-sdk/anthropic": "^4.0.14",
286
- "@ai-sdk/google": "^4.0.14",
287
- "@ai-sdk/mcp": "^2.0.12",
288
- "@ai-sdk/openai": "^4.0.13",
289
- "@ai-sdk/otel": "^1.0.26",
285
+ "@ai-sdk/anthropic": "^4.0.18",
286
+ "@ai-sdk/google": "^4.0.21",
287
+ "@ai-sdk/mcp": "^2.0.16",
288
+ "@ai-sdk/openai": "^4.0.17",
289
+ "@ai-sdk/otel": "^1.0.34",
290
290
  "@ai-sdk/provider": "^4.0.3",
291
- "@ai-sdk/provider-utils": "^5.0.9",
291
+ "@ai-sdk/provider-utils": "^5.0.12",
292
292
  "@chat-adapter/slack": "4.34.0",
293
293
  "@chat-adapter/state-memory": "4.34.0",
294
294
  "@chat-adapter/twilio": "4.34.0",
@@ -300,29 +300,29 @@
300
300
  "@types/react": "19.2.15",
301
301
  "@types/react-test-renderer": "19.1.0",
302
302
  "@vercel/detect-agent": "1.2.3",
303
- "@vercel/oidc": "3.5.0",
304
- "@vercel/sandbox": "2.3.0",
305
- "@vercel/sdk": "1.28.1",
306
- "@workflow/core": "5.0.0-beta.35",
307
- "@workflow/errors": "5.0.0-beta.11",
303
+ "@vercel/oidc": "3.8.0",
304
+ "@vercel/sandbox": "2.8.0",
305
+ "@vercel/sdk": "1.28.8",
306
+ "@workflow/core": "5.0.0-beta.36",
307
+ "@workflow/errors": "5.0.0-beta.12",
308
308
  "@workflow/serde": "5.0.0-beta.2",
309
309
  "@workflow/utils": "5.0.0-beta.6",
310
- "@workflow/world": "5.0.0-beta.21",
311
- "@workflow/world-local": "5.0.0-beta.29",
312
- "@workflow/world-vercel": "5.0.0-beta.31",
313
- "ai": "^7.0.26",
310
+ "@workflow/world": "5.0.0-beta.22",
311
+ "@workflow/world-local": "5.0.0-beta.30",
312
+ "@workflow/world-vercel": "5.0.0-beta.32",
313
+ "ai": "^7.0.34",
314
314
  "autoevals": "0.0.132",
315
315
  "chat": "4.34.0",
316
316
  "chokidar": "5.0.0",
317
317
  "commander": "14.0.3",
318
318
  "emulate": "0.6.0",
319
- "env-runner": "0.1.12",
320
- "eventsource-parser": "3.0.8",
319
+ "env-runner": "0.1.16",
320
+ "eventsource-parser": "3.1.0",
321
321
  "experimental-ai-sdk-code-mode": "1.0.16",
322
322
  "gray-matter": "4.0.3",
323
323
  "jose": "6.2.3",
324
324
  "jsonc-parser": "3.3.1",
325
- "just-bash": "3.0.1",
325
+ "just-bash": "3.1.0",
326
326
  "marked": "17.0.6",
327
327
  "microsandbox": "0.5.5",
328
328
  "next": "16.3.0-preview.6",
@@ -332,8 +332,8 @@
332
332
  "semver": "7.8.4",
333
333
  "svelte": "^5.0.0",
334
334
  "turndown": "7.2.4",
335
- "vite": "^8.0.0",
336
- "vitest": "4.1.7",
335
+ "vite": "^8.1.5",
336
+ "vitest": "4.1.10",
337
337
  "vue": "^3.5.0",
338
338
  "zod": "4.4.3",
339
339
  "zod-validation-error": "5.0.0",
@@ -341,7 +341,7 @@
341
341
  },
342
342
  "peerDependencies": {
343
343
  "@opentelemetry/api": "^1.0.0",
344
- "ai": "^7.0.26",
344
+ "ai": "^7.0.34",
345
345
  "braintrust": "^3.0.0",
346
346
  "just-bash": "^3.0.0",
347
347
  "microsandbox": "^0.5.0"