eve 0.27.1 → 0.27.3
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.
- package/CHANGELOG.md +29 -0
- package/dist/src/channel/session-callback.js +1 -1
- package/dist/src/chunks/{use-eve-agent-PMY2WkAt.js → use-eve-agent-CbF0l_Fp.js} +66 -27
- package/dist/src/chunks/{use-eve-agent-AeQLLhu9.js → use-eve-agent-CgxB9WQv.js} +66 -27
- package/dist/src/cli/commands/build.js +1 -1
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.js +1 -1
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/open-stream.d.ts +15 -2
- package/dist/src/client/open-stream.js +1 -1
- package/dist/src/client/session.d.ts +3 -2
- package/dist/src/client/session.js +1 -1
- package/dist/src/client/types.d.ts +32 -0
- package/dist/src/context/dynamic-tool-lifecycle.d.ts +13 -1
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +5 -0
- package/dist/src/context/keys.js +1 -1
- package/dist/src/execution/node-step.d.ts +6 -0
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/sandbox/bindings/docker.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/terminal-session-failure-step.js +1 -1
- package/dist/src/execution/workflow-callback-url.d.ts +12 -2
- package/dist/src/execution/workflow-callback-url.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-module-evaluation-error.d.ts +2 -0
- package/dist/src/internal/authored-module-evaluation-error.js +4 -0
- package/dist/src/internal/authored-module-loader.js +3 -3
- package/dist/src/internal/authored-package-tsconfig-paths.js +1 -1
- package/dist/src/internal/authored-relative-extension-resolver.d.ts +6 -0
- package/dist/src/internal/authored-relative-extension-resolver.js +1 -0
- package/dist/src/internal/nitro/host/build-application.js +1 -1
- package/dist/src/internal/nitro/host/types.d.ts +9 -0
- package/dist/src/internal/node-esm-compat-banner.js +1 -1
- package/dist/src/internal/workflow-bundle/builder-support.d.ts +5 -0
- package/dist/src/internal/workflow-bundle/builder.js +2 -2
- package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.d.ts +39 -0
- package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.js +1 -0
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +4 -4
- package/dist/src/internal/workflow-bundle/vercel-workflow-output.d.ts +10 -1
- package/dist/src/internal/workflow-bundle/vercel-workflow-output.js +1 -1
- package/dist/src/packages/eve-catalog/src/index.js +1 -1
- package/dist/src/public/channels/eve.js +2 -1
- package/dist/src/public/channels/linear/inbound-images.d.ts +18 -0
- package/dist/src/public/channels/linear/inbound-images.js +1 -0
- package/dist/src/public/channels/linear/linearChannel.js +1 -1
- package/dist/src/public/channels/slack/api.d.ts +26 -2
- package/dist/src/public/channels/slack/api.js +1 -1
- package/dist/src/public/channels/slack/attachments.d.ts +6 -5
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/interactions.d.ts +2 -1
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/public/channels/slack/limits.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +51 -14
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/slack/thread.d.ts +4 -3
- package/dist/src/public/channels/slack/thread.js +1 -1
- package/dist/src/public/next/vercel-output-config.js +1 -1
- package/dist/src/runtime/sessions/compiled-agent-cache.js +1 -1
- package/dist/src/setup/flows/install-vercel-cli.d.ts +8 -2
- package/dist/src/setup/flows/install-vercel-cli.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/vercel-project-api.js +1 -1
- package/dist/src/shared/public-route-prefix.d.ts +23 -0
- package/dist/src/shared/public-route-prefix.js +1 -0
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/channels/linear.mdx +1 -1
- package/docs/channels/slack.mdx +40 -3
- package/docs/extensions.md +2 -2
- package/docs/guides/client/streaming.mdx +15 -0
- package/docs/guides/frontend/nextjs.mdx +2 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const SLACK_TYPING_STATUS_MAX_LENGTH=50,SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH=75,SLACK_SECTION_TEXT_MAX_LENGTH=3e3,SLACK_CARD_BODY_TEXT_MAX_LENGTH=200,SLACK_CARD_SUBTEXT_MAX_LENGTH=200,SLACK_MESSAGE_TEXT_MAX_LENGTH=4e4,SLACK_MAX_BLOCKS_PER_MESSAGE=50,SLACK_MODAL_TITLE_MAX_LENGTH=24;function truncateTypingStatus(e){return truncateWithEllipsis(stripTypingStatusMarkdown(e).trim().replace(/\s+/gu,` `),50)}function truncatePlainText(e){if(e!==void 0)return truncateWithEllipsis(e,75)}function truncateSectionText(e){return truncateWithEllipsis(e,SLACK_SECTION_TEXT_MAX_LENGTH)}function truncateCardBodyText(e){return truncateWithEllipsis(e,200)}function truncateCardSubtext(e){return truncateWithEllipsis(e,200)}function truncateMessageText(e){return truncateWithEllipsis(e,SLACK_MESSAGE_TEXT_MAX_LENGTH)}function truncateModalTitle(e){return truncateWithEllipsis(e,24)}function truncateWithEllipsis(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function stripTypingStatusMarkdown(e){return e.replace(/\[([^\]]+)\]\([^)]+\)/gu,`$1`).replace(/`([^`]+)`/gu,`$1`).replace(/~~([^~]+)~~/gu,`$1`).replace(/(\*\*|__)([^*_]+)\
|
|
1
|
+
const SLACK_TYPING_STATUS_MAX_LENGTH=50,SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH=75,SLACK_SECTION_TEXT_MAX_LENGTH=3e3,SLACK_CARD_BODY_TEXT_MAX_LENGTH=200,SLACK_CARD_SUBTEXT_MAX_LENGTH=200,SLACK_MESSAGE_TEXT_MAX_LENGTH=4e4,SLACK_MAX_BLOCKS_PER_MESSAGE=50,SLACK_MODAL_TITLE_MAX_LENGTH=24;function truncateTypingStatus(e){return truncateWithEllipsis(stripTypingStatusMarkdown(e).trim().replace(/\s+/gu,` `),50)}function truncatePlainText(e){if(e!==void 0)return truncateWithEllipsis(e,75)}function truncateSectionText(e){return truncateWithEllipsis(e,SLACK_SECTION_TEXT_MAX_LENGTH)}function truncateCardBodyText(e){return truncateWithEllipsis(e,200)}function truncateCardSubtext(e){return truncateWithEllipsis(e,200)}function truncateMessageText(e){return truncateWithEllipsis(e,SLACK_MESSAGE_TEXT_MAX_LENGTH)}function truncateModalTitle(e){return truncateWithEllipsis(e,24)}function truncateWithEllipsis(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function stripTypingStatusMarkdown(e){return e.replace(/\[([^\]]+)\]\([^)]+\)/gu,`$1`).replace(/`([^`]+)`/gu,`$1`).replace(/~~([^~]+)~~/gu,`$1`).replace(/(^|[^\p{L}\p{N}])(\*\*|__)([^*_]+)\2(?=$|[^\p{L}\p{N}])/gu,`$1$3`).replace(/(^|[^\p{L}\p{N}*])\*([^*_]+)\*(?=$|[^\p{L}\p{N}*])/gu,`$1$2`).replace(/(^|[^\p{L}\p{N}_])_([^*_]+)_(?=$|[^\p{L}\p{N}_])/gu,`$1$2`)}export{SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH,SLACK_CARD_BODY_TEXT_MAX_LENGTH,SLACK_CARD_SUBTEXT_MAX_LENGTH,SLACK_MAX_BLOCKS_PER_MESSAGE,SLACK_MESSAGE_TEXT_MAX_LENGTH,SLACK_MODAL_TITLE_MAX_LENGTH,SLACK_SECTION_TEXT_MAX_LENGTH,SLACK_TYPING_STATUS_MAX_LENGTH,truncateCardBodyText,truncateCardSubtext,truncateMessageText,truncateModalTitle,truncatePlainText,truncateSectionText,truncateTypingStatus};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CrossChannelReceiveOptions } from "#channel/cross-channel-receive.js";
|
|
2
2
|
import type { Session } from "#channel/session.js";
|
|
3
|
-
import type { SessionAuthContext } from "#channel/types.js";
|
|
3
|
+
import type { CancelTurnResult, SessionAuthContext } from "#channel/types.js";
|
|
4
4
|
import type { CardElement } from "#compiled/chat/index.js";
|
|
5
5
|
import type { SessionContext } from "#public/definitions/callback-context.js";
|
|
6
6
|
import type { ChannelSessionOps } from "#public/definitions/channel.js";
|
|
@@ -17,13 +17,12 @@ type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessa
|
|
|
17
17
|
data: infer D;
|
|
18
18
|
} ? D : undefined;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
* `onInteraction`. These hooks run on the inbound webhook side before the
|
|
20
|
+
* Base Slack context for inbound webhook handlers. These hooks run before the
|
|
22
21
|
* runtime hydrates session state, so `state` is absent here.
|
|
23
22
|
* {@link thread} owns thread-scoped operations (`post`, `postEphemeral`,
|
|
24
|
-
* `startTyping`, `refresh`, `
|
|
25
|
-
* owns Slack identity (`channelId`, `threadTs`,
|
|
26
|
-
* escape hatch (`request`, `uploadFiles`).
|
|
23
|
+
* `startTyping`, `refresh`, `listParticipants`, `recentMessages`,
|
|
24
|
+
* `mentionUser`); {@link slack} owns Slack identity (`channelId`, `threadTs`,
|
|
25
|
+
* `teamId`) plus the raw-API escape hatch (`request`, `uploadFiles`).
|
|
27
26
|
*/
|
|
28
27
|
export interface SlackContext {
|
|
29
28
|
readonly thread: SlackThread;
|
|
@@ -195,6 +194,26 @@ export type SlackEventReceiveOptions = CrossChannelReceiveOptions<SlackReceiveTa
|
|
|
195
194
|
* one, or many times; each invocation returns the resulting session.
|
|
196
195
|
*/
|
|
197
196
|
export type SlackEventReceiveFn = (options: SlackEventReceiveOptions) => Promise<Session>;
|
|
197
|
+
/**
|
|
198
|
+
* Slack thread identity used by workspace-scoped inbound helpers.
|
|
199
|
+
*/
|
|
200
|
+
export interface SlackSessionTarget {
|
|
201
|
+
readonly channelId: string;
|
|
202
|
+
readonly threadTs: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Options for cancelling the turn bound to a message or interaction context.
|
|
206
|
+
* `turnId` guards against a stale request cancelling a newer turn.
|
|
207
|
+
*/
|
|
208
|
+
export interface SlackCancelOptions {
|
|
209
|
+
readonly turnId?: string;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Target and optional stale-turn guard accepted by `onEvent`'s cancellation
|
|
213
|
+
* helper.
|
|
214
|
+
*/
|
|
215
|
+
export interface SlackEventCancelOptions extends SlackSessionTarget, SlackCancelOptions {
|
|
216
|
+
}
|
|
198
217
|
/**
|
|
199
218
|
* Imperative surface handed to `slackChannel({ onEvent })`. Generic Events API
|
|
200
219
|
* payloads are not necessarily tied to one thread, so the context exposes a
|
|
@@ -202,15 +221,17 @@ export type SlackEventReceiveFn = (options: SlackEventReceiveOptions) => Promise
|
|
|
202
221
|
* thread-scoped {@link SlackContext} used by message handlers.
|
|
203
222
|
*/
|
|
204
223
|
export interface SlackInboundEventContext {
|
|
224
|
+
/**
|
|
225
|
+
* Cancels the active turn for a Slack thread. Both `"accepted"` and
|
|
226
|
+
* `"no_active_turn"` are successful outcomes.
|
|
227
|
+
*/
|
|
228
|
+
readonly cancel: (options: SlackEventCancelOptions) => Promise<CancelTurnResult>;
|
|
205
229
|
/** The complete signed Events API callback envelope. */
|
|
206
230
|
readonly envelope: SlackEventEnvelope;
|
|
207
231
|
/** Starts a turn on this Slack channel using the proactive receive contract. */
|
|
208
232
|
readonly receive: SlackEventReceiveFn;
|
|
209
233
|
/** Resolves the active eve session for a Slack channel thread. */
|
|
210
|
-
readonly resolveActiveSession: (target: {
|
|
211
|
-
readonly channelId: string;
|
|
212
|
-
readonly threadTs: string;
|
|
213
|
-
}) => Promise<{
|
|
234
|
+
readonly resolveActiveSession: (target: SlackSessionTarget) => Promise<{
|
|
214
235
|
readonly sessionId: string;
|
|
215
236
|
} | undefined>;
|
|
216
237
|
/** Workspace-scoped Slack identity and raw Web API escape hatch. */
|
|
@@ -218,13 +239,29 @@ export interface SlackInboundEventContext {
|
|
|
218
239
|
/** Keeps detached handler work alive after the Slack webhook is acknowledged. */
|
|
219
240
|
readonly waitUntil: (task: Promise<unknown>) => void;
|
|
220
241
|
}
|
|
221
|
-
/**
|
|
242
|
+
/**
|
|
243
|
+
* Message-scoped context handed to `onMessage`, `onAppMention`, and
|
|
244
|
+
* `onDirectMessage`.
|
|
245
|
+
*/
|
|
222
246
|
export interface SlackInboundMessageContext extends SlackContext {
|
|
247
|
+
/**
|
|
248
|
+
* Cancels the active turn in this message's thread. Both `"accepted"` and
|
|
249
|
+
* `"no_active_turn"` are successful outcomes.
|
|
250
|
+
*/
|
|
251
|
+
cancel(options?: SlackCancelOptions): Promise<CancelTurnResult>;
|
|
223
252
|
/** Returns whether this message belongs to a thread with an active eve session. */
|
|
224
253
|
isSubscribed(): Promise<boolean>;
|
|
225
254
|
/** Returns whether the inbound event explicitly mentions this bot. */
|
|
226
255
|
isBotMentioned(): boolean;
|
|
227
256
|
}
|
|
257
|
+
/** Interaction-scoped context handed to `slackChannel({ onInteraction })`. */
|
|
258
|
+
export interface SlackInteractionContext extends SlackContext {
|
|
259
|
+
/**
|
|
260
|
+
* Cancels the active turn in the interaction's thread. Both `"accepted"` and
|
|
261
|
+
* `"no_active_turn"` are successful outcomes.
|
|
262
|
+
*/
|
|
263
|
+
cancel(options?: SlackCancelOptions): Promise<CancelTurnResult>;
|
|
264
|
+
}
|
|
228
265
|
export interface SlackInteractionAction {
|
|
229
266
|
readonly actionId: string;
|
|
230
267
|
readonly value?: string;
|
|
@@ -363,7 +400,7 @@ export interface SlackChannelConfig {
|
|
|
363
400
|
* non-fatal. Defaults to a workspace-scoped auth derivation that posts a
|
|
364
401
|
* `"Thinking..."` typing indicator; replacing this replaces both.
|
|
365
402
|
*/
|
|
366
|
-
onAppMention?(ctx:
|
|
403
|
+
onAppMention?(ctx: SlackInboundMessageContext, message: SlackMessage): SlackMentionResultOrPromise;
|
|
367
404
|
/**
|
|
368
405
|
* Invoked on a direct message: a Slack `message` event with
|
|
369
406
|
* `channel_type: "im"`. Subtype messages (edits, deletes, joins, etc.)
|
|
@@ -381,7 +418,7 @@ export interface SlackChannelConfig {
|
|
|
381
418
|
* Requires the bot's Slack app to subscribe to `message.im` with the
|
|
382
419
|
* `im:history` scope.
|
|
383
420
|
*/
|
|
384
|
-
onDirectMessage?(ctx:
|
|
421
|
+
onDirectMessage?(ctx: SlackInboundMessageContext, message: SlackMessage): SlackInboundResultOrPromise;
|
|
385
422
|
/**
|
|
386
423
|
* Fallback handler for signed Slack Events API callbacks. An authored
|
|
387
424
|
* `onAppMention` or `onDirectMessage` takes precedence for events accepted
|
|
@@ -417,7 +454,7 @@ export interface SlackChannelConfig {
|
|
|
417
454
|
* used by event handlers. Use `ctx.slack.request(...)` for arbitrary
|
|
418
455
|
* Slack Web API calls and `action.messageTs` to target `chat.update`.
|
|
419
456
|
*/
|
|
420
|
-
onInteraction?(action: SlackInteractionAction, ctx:
|
|
457
|
+
onInteraction?(action: SlackInteractionAction, ctx: SlackInteractionContext): void | Promise<void>;
|
|
421
458
|
readonly events?: SlackChannelEvents;
|
|
422
459
|
}
|
|
423
460
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,buildSlackWorkspaceHandle,slackContinuationToken}from"#public/channels/slack/api.js";import{slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage}from"#public/channels/slack/defaults.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`);function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.setContinuationToken(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),o=e.events?.[`authorization.required`],s=e.events?.[`turn.started`]??defaultEvents[`turn.started`],c={...defaultEvents,...e.events,async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await s(e,t,n)},"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":o===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(o)},u=new Set;return defineChannel({kindHint:`slack`,state:{channelId:null,threadTs:null,teamId:null,triggeringUserId:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{}},fetchFile:n,metadata(e){return{channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,{
|
|
1
|
+
import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,buildSlackWorkspaceHandle,slackContinuationToken}from"#public/channels/slack/api.js";import{slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage}from"#public/channels/slack/defaults.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`);function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.setContinuationToken(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),o=e.events?.[`authorization.required`],s=e.events?.[`turn.started`]??defaultEvents[`turn.started`],c={...defaultEvents,...e.events,async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await s(e,t,n)},"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":o===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(o)},u=new Set;return defineChannel({kindHint:`slack`,state:{channelId:null,threadTs:null,teamId:null,triggeringUserId:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{}},fetchFile:n,metadata(e){return{channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,{cancel:r,resolveActiveSession:i,send:a,waitUntil:o})=>{let s=await verifyInbound(n,e.credentials);return s===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(s,{cancel:r,send:a,waitUntil:o},{config:e}):handleEventPost({body:s,cancel:r,send:a,resolveActiveSession:i,waitUntil:o,config:e,uploadPolicy:t,handledEvents:u,headers:n.headers})})],receive(t,{send:n}){return receiveOnSlack(t,{credentials:e.credentials,send:n})},events:c})}async function receiveOnSlack(e,t){let n=e.target,r=n.channelId;if(!r||typeof r!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let i=typeof n.threadTs==`string`?n.threadTs:``,a=n.initialMessage;if(a&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let s=i;if(a){let{thread:e}=buildSlackBinding({botToken:t.credentials?.botToken,channelId:r,threadTs:``,teamId:t.teamId}),n={card:a.card};a.fallbackText!==void 0&&(n.fallbackText=a.fallbackText),s=(await e.post(n)).id}let l=s||crypto.randomUUID();return t.send(e.message,{auth:e.auth,continuationToken:slackContinuationToken(r,l),state:{channelId:r,threadTs:s||null,teamId:t.teamId??null,triggeringUserId:null}})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(t){let{config:n}=t,r,i;try{r=parseSlackWebhookBody(t.body,{headers:t.headers}),i=parseSlackEventEnvelope(t.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(r.kind===`url_verification`)return new Response(r.challenge,{status:200,headers:{"content-type":`text/plain`}});if(i===null)return new Response(`ok`);let a=typeof i.api_app_id==`string`?i.api_app_id:void 0,o=slackEventBotUserId(i),dispatch=null,s=null;if(r.kind===`app_mention`||r.kind===`direct_message`){let e=r.kind,i=slackMessageFromWebhookPayload(r);if(i!==null&&!isSelfAuthoredSlackMessage({appId:a,botUserId:o},i)){let dispatchMessageWith=r=>()=>dispatchSlackMessage({appId:a,botUserId:o,cancel:t.cancel,credentials:n.credentials,handler:r,kind:e,message:i,resolveActiveSession:t.resolveActiveSession,send:t.send,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}),r=(e===`app_mention`?n.onAppMention:n.onDirectMessage)??n.onMessage;r===void 0?s=dispatchMessageWith(e===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,cancel:t.cancel,credentials:n.credentials,handler:r,kind:e,message:i,resolveActiveSession:t.resolveActiveSession,send:t.send,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy})}}if(dispatch===null&&n.onMessage!==void 0){let e=parseMessageEvent(i);e!==null&&!isSelfAuthoredSlackMessage({appId:a,botUserId:o},e)&&(o===void 0||!e.text.includes(`<@${o}`))&&(dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,cancel:t.cancel,credentials:n.credentials,handler:n.onMessage,kind:`channel_message`,message:e,resolveActiveSession:t.resolveActiveSession,send:t.send,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}))}let c=n.onEvent;if(dispatch===null&&c!==void 0&&(dispatch=()=>dispatchSlackEvent({cancel:t.cancel,credentials:n.credentials,envelope:i,handler:c,resolveActiveSession:t.resolveActiveSession,send:t.send})),dispatch??=s,dispatch===null)return new Response(`ok`);let l=i.event_id;if(l){if(t.handledEvents.has(l))return log.warn(`received a duplicate event`,{event_id:l,event_time:i.event_time,retry_num:r.retry?.num??`(null)`,retry_reason:r.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(l,t.handledEvents)}return t.waitUntil(dispatch()),new Response(`ok`)}function isSelfAuthoredSlackMessage(e,t){return e.botUserId!==void 0&&t.author?.userId===e.botUserId?!0:e.appId!==void 0&&t.raw.app_id===e.appId}async function dispatchSlackMessage(e){let t=slackContinuationToken(e.message.channelId,e.message.threadTs),{thread:r,slack:i}=buildSlackBinding({appId:e.appId,botToken:e.credentials?.botToken,botUserId:e.botUserId,channelId:e.message.channelId,threadTs:e.message.threadTs,teamId:e.message.teamId}),a={cancel:(n={})=>e.cancel({continuationToken:t,turnId:n.turnId}),isBotMentioned:()=>e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),isSubscribed:async()=>await e.resolveActiveSession({continuationToken:t})!==void 0,slack:i,thread:r},s;try{s=await e.handler(a,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}s!=null&&await deliverSlackMessage({credentials:e.credentials,kind:e.kind,message:e.message,result:s,send:e.send,thread:r,threadContext:e.threadContext,uploadPolicy:e.uploadPolicy})}async function dispatchSlackEvent(e){let t=e.envelope.event.team_id,r=typeof t==`string`?t:typeof e.envelope.team_id==`string`?e.envelope.team_id:void 0,i=[],a={cancel:({channelId:t,threadTs:n,turnId:r})=>e.cancel({continuationToken:slackContinuationToken(t,n),turnId:r}),envelope:e.envelope,receive:t=>receiveOnSlack(t,{credentials:e.credentials,send:e.send,teamId:r}),resolveActiveSession:({channelId:t,threadTs:n})=>e.resolveActiveSession({continuationToken:slackContinuationToken(t,n)}),slack:buildSlackWorkspaceHandle({botToken:e.credentials?.botToken,teamId:r}),waitUntil(e){i.push(e)}};try{await e.handler(a,e.envelope.event)}catch(t){logError(log,`event handler failed`,t,{eventId:e.envelope.event_id,eventType:e.envelope.event.type})}await Promise.allSettled(i)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function deliverSlackMessage(e){let{message:t,thread:r}=e;try{let n=formatSlackThreadContext(e.threadContext===void 0?[]:await loadThreadContextMessages(r,t,e.threadContext)),i=await collectInboundFileParts({mention:t,thread:r,policy:e.uploadPolicy}),a={channelId:t.channelId,fullName:t.author?.fullName,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},o=formatSlackInboundMessage(a,t),s=buildSlackTurnMessage(n===void 0?o:`${n}\n\n${o}`,i),l=e.result.context??[];await e.send(l.length===0?{message:s}:{message:s,context:l},{auth:e.result.auth,continuationToken:slackContinuationToken(t.channelId,t.threadTs),state:{channelId:t.channelId,threadTs:t.threadTs,teamId:t.teamId??null,triggeringUserId:a.userId||null},title:t.markdown})}catch(r){logError(log,`${e.kind} delivery failed`,r,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
|
|
@@ -23,9 +23,10 @@ export interface LoadThreadContextMessagesOptions {
|
|
|
23
23
|
* Slack thread turn.
|
|
24
24
|
*
|
|
25
25
|
* Returns an empty array when `message` is the thread root. For thread
|
|
26
|
-
* replies,
|
|
27
|
-
* messages before the triggering message,
|
|
28
|
-
* Formatting and model-message role choice
|
|
26
|
+
* replies, reuses already loaded thread messages or refreshes the bound
|
|
27
|
+
* Slack thread, then returns messages before the triggering message,
|
|
28
|
+
* filtered by {@link options}. Formatting and model-message role choice
|
|
29
|
+
* stay with the caller.
|
|
29
30
|
*/
|
|
30
31
|
export declare function loadThreadContextMessages(thread: Pick<SlackThread, "recentMessages" | "refresh">, message: {
|
|
31
32
|
readonly threadTs: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
async function loadThreadContextMessages(e,t,n={}){if(isThreadRootMessage(t))return[];await e.refresh();let r=e.recentMessages.findIndex(e=>e.ts===t.ts);return applySinceBoundary((r===-1?e.recentMessages:e.recentMessages.slice(0,r)).filter(e=>e.threadTs===t.threadTs&&e.ts!==t.ts),n.since)}function isThreadRootMessage(e){return e.threadTs===e.ts}function findLastIndex(e,t){for(let n=e.length-1;n>=0;--n)if(t(e[n]))return n;return-1}function applySinceBoundary(e,t){let n=t??`thread-root`;if(typeof n==`function`){let t=findLastIndex(e,n);return e.slice(t+1)}switch(n){case`thread-root`:return[...e];case`last-agent-reply`:{let t=findLastIndex(e,e=>e.isMe);return e.slice(t+1)}}}export{loadThreadContextMessages};
|
|
1
|
+
async function loadThreadContextMessages(e,t,n={}){if(isThreadRootMessage(t))return[];e.recentMessages.length===0&&await e.refresh();let r=e.recentMessages.findIndex(e=>e.ts===t.ts);return applySinceBoundary((r===-1?e.recentMessages:e.recentMessages.slice(0,r)).filter(e=>e.threadTs===t.threadTs&&e.ts!==t.ts),n.since)}function isThreadRootMessage(e){return e.threadTs===e.ts}function findLastIndex(e,t){for(let n=e.length-1;n>=0;--n)if(t(e[n]))return n;return-1}function applySinceBoundary(e,t){let n=t??`thread-root`;if(typeof n==`function`){let t=findLastIndex(e,n);return e.slice(t+1)}switch(n){case`thread-root`:return[...e];case`last-agent-reply`:{let t=findLastIndex(e,e=>e.isMe);return e.slice(t+1)}}}export{loadThreadContextMessages};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dirname,join,relative}from"node:path";import{mkdir,readFile,writeFile}from"node:fs/promises";import{EVE_INTERNAL_BUILD_OUTPUT_DIRECTORY_ENV,EVE_INTERNAL_HOST_BUILD_OUTPUT_DIRECTORY_ENV}from"#internal/application/paths.js";import{findClosestLinkedVercelDirectory,findClosestVercelOutputDirectory}from"#shared/vercel-output-directory.js";const VERCEL_JSON_FILE_NAME=`vercel.json`,VERCEL_OUTPUT_CONFIG_FILE_NAME=`.vercel/output/config.json`;function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasServices(e){return e!==void 0&&Object.keys(createServiceConfigRecord(e)).length>0}function isNamedServiceConfigArray(e){return Array.isArray(e)}function createServiceConfigRecord(e){if(e===void 0)return{};if(isNamedServiceConfigArray(e)){let t={};for(let n of e)if(typeof n.name==`string`&&n.name.trim().length>0){let{name:e,...r}=n;t[e]=r}return t}return e}function resolveRelativeEntrypoint(e,t){let r=relative(e,t);return r.length===0?`.`:r.replaceAll(`\\`,`/`)}function quoteShellArgument(e){return`'${e.replaceAll(`'`,`'\\''`)}'`}function createGeneratedServiceBuild(e){let n=join(e.nextRoot,`.eve/vercel-services`,e.serviceName),
|
|
1
|
+
import{dirname,join,relative}from"node:path";import{EVE_PUBLIC_ROUTE_PREFIX_ENV,normalizePublicRoutePrefix}from"#shared/public-route-prefix.js";import{mkdir,readFile,writeFile}from"node:fs/promises";import{EVE_INTERNAL_BUILD_OUTPUT_DIRECTORY_ENV,EVE_INTERNAL_HOST_BUILD_OUTPUT_DIRECTORY_ENV}from"#internal/application/paths.js";import{findClosestLinkedVercelDirectory,findClosestVercelOutputDirectory}from"#shared/vercel-output-directory.js";const VERCEL_JSON_FILE_NAME=`vercel.json`,VERCEL_OUTPUT_CONFIG_FILE_NAME=`.vercel/output/config.json`;function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasServices(e){return e!==void 0&&Object.keys(createServiceConfigRecord(e)).length>0}function isNamedServiceConfigArray(e){return Array.isArray(e)}function createServiceConfigRecord(e){if(e===void 0)return{};if(isNamedServiceConfigArray(e)){let t={};for(let n of e)if(typeof n.name==`string`&&n.name.trim().length>0){let{name:e,...r}=n;t[e]=r}return t}return e}function resolveRelativeEntrypoint(e,t){let r=relative(e,t);return r.length===0?`.`:r.replaceAll(`\\`,`/`)}function quoteShellArgument(e){return`'${e.replaceAll(`'`,`'\\''`)}'`}function createGeneratedServiceBuild(e){let n=join(e.nextRoot,`.eve/vercel-services`,e.serviceName),a=join(n,`.vercel`,`output`),o=resolveRelativeEntrypoint(n,e.agent.appRoot),s=resolveRelativeEntrypoint(e.agent.appRoot,a),u=resolveRelativeEntrypoint(e.agent.appRoot,e.hostOutputDirectory),d=normalizePublicRoutePrefix(e.agent.publicRoutePrefix),f=d===void 0?``:` && export ${EVE_PUBLIC_ROUTE_PREFIX_ENV}=${quoteShellArgument(d)}`;return{buildCommand:`cd ${quoteShellArgument(o)} && export ${EVE_INTERNAL_BUILD_OUTPUT_DIRECTORY_ENV}=${quoteShellArgument(s)} && export ${EVE_INTERNAL_HOST_BUILD_OUTPUT_DIRECTORY_ENV}=${quoteShellArgument(u)}${f} && ${e.agent.buildCommand}`,root:resolveRelativeEntrypoint(e.nextRoot,n),rootDirectory:n}}async function resolveVercelOutputConfigLocation(n){let r=await findClosestLinkedVercelDirectory(n),i=r===void 0?n:dirname(r),a=await findClosestVercelOutputDirectory(n);return a===void 0?r===void 0?{canWriteGeneratedOutput:!!process.env.VERCEL,outputConfigPath:join(n,VERCEL_OUTPUT_CONFIG_FILE_NAME),projectRoot:i}:{canWriteGeneratedOutput:!0,outputConfigPath:join(r,`output`,`config.json`),projectRoot:i}:{canWriteGeneratedOutput:!0,outputConfigPath:join(a,`config.json`),projectRoot:i}}function normalizeVercelServicesConfig(e,t){if(!isRecord(e))throw Error(`${t} must contain a JSON object.`);let n=e.services;if(n!==void 0&&!isRecord(n)&&!(Array.isArray(n)&&n.every(e=>isRecord(e)&&typeof e.name==`string`&&e.name.trim().length>0)))throw Error(`${t} services must be a JSON object or named service array.`);let r=e.routes;if(r!==void 0&&!Array.isArray(r))throw Error(`${t} routes must be an array.`);return e}async function readVercelServicesConfig(e,t){try{return normalizeVercelServicesConfig(JSON.parse(await readFile(e,`utf8`)),t)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return{};throw e}}function findServiceEntryByFramework(e,t){return Object.entries(e).map(([e,t])=>({name:e,service:t})).find(e=>e.service.framework===t)}function findServiceEntryByName(e,t){let n=e[t];return n===void 0?void 0:{name:t,service:n}}function resolveServicePrefix(e){if(e!==void 0){if(typeof e.routePrefix==`string`&&e.routePrefix.trim().length>0)return e.routePrefix.trim();if(typeof e.mount==`string`&&e.mount.trim().length>0)return e.mount.trim();if(isRecord(e.mount)&&typeof e.mount.path==`string`&&e.mount.path.trim().length>0)return e.mount.path.trim()}}function resolveConfiguredServicePrefix(e){let t=findConfiguredEveServiceEntry(e.services,e.agent)?.service;return resolveServicePrefix(t)??e.agent.servicePrefix}function findConfiguredEveServiceEntry(e,t){if(t.name!==void 0){let n=findServiceEntryByName(e,createEveServiceName(t.name));if(n?.service.framework===`eve`)return n}return t.name===void 0?findServiceEntryByFramework(e,`eve`):void 0}function assertRootServicesIncludeEve(e){let t=[];for(let n of e.agents){let r=findConfiguredEveServiceEntry(e.services,n)?.service;if(r===void 0)throw Error(`${VERCEL_JSON_FILE_NAME} already defines services, so withEve cannot add generated eve services through ${VERCEL_OUTPUT_CONFIG_FILE_NAME}. Add the eve service for ${n.name??`the default agent`} to ${VERCEL_JSON_FILE_NAME}, or remove services from ${VERCEL_JSON_FILE_NAME}.`);t.push({name:n.name,servicePrefix:resolveServicePrefix(r)??n.servicePrefix})}return t}function createEveServiceRouteSrc(e){return e.length===0?`^/eve/v1/(.*)$`:`^${escapeRegExp(e.startsWith(`/`)?e:`/${e}`)}/eve/v1/(.*)$`}function escapeRegExp(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function createEveServiceName(e){return e===void 0?`eve`:`eve-${e}`}function isEveServiceRoute(e,t,n){let r=e.destination;return e.src===n&&isRecord(r)&&r.type===`service`&&r.service===t}function createEveServiceRoute(e,t){return{destination:{service:e,type:`service`},src:t}}function isEveServiceRequestPathRoute(e,t){return e.src===t}function createEveServiceRequestPathRoute(e){return{src:e,transforms:[{args:`/eve/v1/$1`,op:`set`,type:`request.path`}]}}function insertEveServiceRequestPathRoute(e,t){let n=(e??[]).filter(e=>!isEveServiceRequestPathRoute(e,t));return[createEveServiceRequestPathRoute(t),...n]}function insertEveServiceRoutes(e,t){let n=e.filter(e=>!t.some(t=>isEveServiceRoute(e,t.serviceName,t.routeSrc))),r=n.findIndex(e=>e.handle===`filesystem`),i=t.map(e=>createEveServiceRoute(e.serviceName,e.routeSrc));return r===-1?[...i,...n]:[...n.slice(0,r),...i,...n.slice(r)]}async function ensureEveVercelOutputConfig(n){let{canWriteGeneratedOutput:r,outputConfigPath:i,projectRoot:o}=await resolveVercelOutputConfigLocation(n.nextRoot),c=(await readVercelServicesConfig(join(o,VERCEL_JSON_FILE_NAME),VERCEL_JSON_FILE_NAME)).services;if(hasServices(c))return{agents:assertRootServicesIncludeEve({agents:n.agents,services:createServiceConfigRecord(c)})};let l=await readVercelServicesConfig(i,VERCEL_OUTPUT_CONFIG_FILE_NAME),u=createServiceConfigRecord(l.services),d=n.agents.map(e=>({name:e.name,servicePrefix:resolveConfiguredServicePrefix({agent:e,services:u})}));if(!r)return{agents:d};let p={...u},m=[];for(let t of n.agents){let r=findConfiguredEveServiceEntry(u,t),o=r?.name??createEveServiceName(t.name),s=createEveServiceRouteSrc(t.publicRoutePrefix);if(r===void 0){let r=createGeneratedServiceBuild({agent:t,hostOutputDirectory:dirname(i),nextRoot:n.nextRoot,serviceName:o});await mkdir(r.rootDirectory,{recursive:!0});let c={buildCommand:r.buildCommand,framework:`eve`,routes:insertEveServiceRequestPathRoute(void 0,s),root:r.root};t.publicRoutePrefix.length>0&&(c.routePrefix=t.publicRoutePrefix),p[o]=c}else p[o]={...r.service,routes:insertEveServiceRequestPathRoute(r.service.routes,s)};m.push({routeSrc:s,serviceName:o})}let{services:h,...g}=l,_={...g,routes:insertEveServiceRoutes(l.routes??[],m),services:p,version:3};return JSON.stringify(l)!==JSON.stringify(_)&&(await mkdir(dirname(i),{recursive:!0}),await writeFile(i,`${JSON.stringify(_,null,2)}\n`)),{agents:d}}export{ensureEveVercelOutputConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolvePackageSourceFilePath}from"#internal/application/package.js";import{getResolvedRuntimeAgentNode}from"#runtime/graph.js";import{getRuntimeCompiledArtifactsCacheKey}from"#runtime/compiled-artifacts-source.js";import{loadCompiledManifest}from"#runtime/loaders/manifest.js";import{resolveRuntimeAgentGraph}from"#runtime/resolve-agent-graph.js";import{pathToFileURL}from"node:url";import{loadCompiledModuleMap}from"#runtime/loaders/module-map.js";import{getActiveRuntimeSession}from"#runtime/sessions/runtime-session.js";import{
|
|
1
|
+
import{resolvePackageSourceFilePath}from"#internal/application/package.js";import{getResolvedRuntimeAgentNode}from"#runtime/graph.js";import{getRuntimeCompiledArtifactsCacheKey}from"#runtime/compiled-artifacts-source.js";import{loadCompiledManifest}from"#runtime/loaders/manifest.js";import{resolveRuntimeAgentGraph}from"#runtime/resolve-agent-graph.js";import{resolveRuntimeCompiledArtifactsVersionedCacheKey}from"#runtime/cache-key.js";import{pathToFileURL}from"node:url";import{loadCompiledModuleMap}from"#runtime/loaders/module-map.js";import{getActiveRuntimeSession}from"#runtime/sessions/runtime-session.js";import{createRuntimeAdapterRegistry}from"#runtime/channels/registry.js";const isCacheDisabled=process.env.EVE_DISABLE_AGENT_CACHE===`1`;function isDevelopmentRuntimeSnapshotRoot(e){return e.replaceAll(`\\`,`/`).includes(`/.eve/dev-runtime/snapshots/`)}function normalizeCompiledArtifactsSource(t){return t.kind!==`disk`||t.moduleMapLoaderPath!==void 0||!isDevelopmentRuntimeSnapshotRoot(t.appRoot)?t:{...t,moduleMapLoaderPath:resolvePackageSourceFilePath(`src/internal/authored-module-map-loader.ts`)}}async function loadFullBundle(e){let t=normalizeCompiledArtifactsSource(e),[n,a]=await Promise.all([loadCompiledManifest({compiledArtifactsSource:t}),loadRuntimeCompiledModuleMap(t)]),o=await resolveRuntimeAgentGraph({manifest:n,moduleMap:a}),s=o.root;return{adapterRegistry:createRuntimeAdapterRegistry({channels:collectResolvedChannels(o)}),compiledArtifactsSource:t,graph:o,hookRegistry:s.hookRegistry,moduleMap:a,resolvedAgent:s.agent,subagentRegistry:s.subagentRegistry,toolRegistry:s.toolRegistry,turnAgent:s.turnAgent}}async function loadRuntimeCompiledModuleMap(e){return e.kind===`disk`&&e.moduleMapLoaderPath!==void 0?await loadAuthoredSourceCompiledModuleMap(e):await loadCompiledModuleMap({compiledArtifactsSource:e})}async function loadAuthoredSourceCompiledModuleMap(e){if(e.moduleMapLoaderPath===void 0)throw Error(`Authored-source module map loading requires "moduleMapLoaderPath" in the compiled artifacts source.`);return await(await import(pathToFileURL(e.moduleMapLoaderPath).href)).loadCompiledModuleMapFromAuthoredSource({compiledArtifactsSource:e})}async function getOrLoadFullBundle(e){let t=normalizeCompiledArtifactsSource(e);if(isCacheDisabled)return loadFullBundle(t);let r=getActiveRuntimeSession(),i=getRuntimeCompiledArtifactsCacheKey(t),o=await resolveRuntimeCompiledArtifactsVersionedCacheKey(t),s=r.bundleCacheKeyBySourceKey.get(i);s!==void 0&&s!==o&&r.bundleCache.delete(s),r.bundleCacheKeyBySourceKey.set(i,o);let c=r.bundleCache.get(o);if(c!==void 0)return c;let l=loadFullBundle(t).catch(e=>{throw r.bundleCache.delete(o),r.bundleCacheKeyBySourceKey.get(i)===o&&r.bundleCacheKeyBySourceKey.delete(i),e});return r.bundleCache.set(o,l),l}async function getCompiledRuntimeAgentBundle(e){let n=await getOrLoadFullBundle(e.compiledArtifactsSource);if(e.nodeId===void 0)return n;let r=getResolvedRuntimeAgentNode(n.graph,e.nodeId);return{adapterRegistry:n.adapterRegistry,compiledArtifactsSource:n.compiledArtifactsSource,graph:{nodesByNodeId:n.graph.nodesByNodeId,root:r},hookRegistry:r.hookRegistry,moduleMap:n.moduleMap,nodeId:e.nodeId,resolvedAgent:r.agent,subagentRegistry:r.subagentRegistry,toolRegistry:r.toolRegistry,turnAgent:r.turnAgent}}function clearCompiledRuntimeAgentBundleCache(){let e=getActiveRuntimeSession();e.bundleCache.clear(),e.bundleCacheKeyBySourceKey.clear()}function collectResolvedChannels(e){let t=new Map;for(let n of e.nodesByNodeId.values())for(let e of n.channels)t.set(`${e.sourceId}:${e.name}`,e);return[...t.values()]}export{clearCompiledRuntimeAgentBundleCache,getCompiledRuntimeAgentBundle};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { detectPackageManager } from "#setup/package-manager.js";
|
|
2
|
-
import { spawnPackageManager } from "#setup/primitives/index.js";
|
|
2
|
+
import { runVercel, spawnPackageManager } from "#setup/primitives/index.js";
|
|
3
3
|
import { getVercelAuthStatus } from "#setup/vercel-project.js";
|
|
4
4
|
import type { Prompter } from "../prompter.js";
|
|
5
5
|
export type InstallVercelCliResult =
|
|
@@ -14,6 +14,7 @@ export type InstallVercelCliResult =
|
|
|
14
14
|
/** The install exited non-zero, or the CLI still isn't on PATH afterward. */
|
|
15
15
|
| {
|
|
16
16
|
kind: "failed";
|
|
17
|
+
reason?: string;
|
|
17
18
|
}
|
|
18
19
|
/** The user interrupted (Ctrl-C / Esc) before the install finished. */
|
|
19
20
|
| {
|
|
@@ -23,6 +24,7 @@ export type InstallVercelCliResult =
|
|
|
23
24
|
export interface InstallVercelCliDeps {
|
|
24
25
|
getVercelAuthStatus: typeof getVercelAuthStatus;
|
|
25
26
|
detectPackageManager: typeof detectPackageManager;
|
|
27
|
+
runVercel: typeof runVercel;
|
|
26
28
|
spawnPackageManager: typeof spawnPackageManager;
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
@@ -32,11 +34,15 @@ export interface InstallVercelCliDeps {
|
|
|
32
34
|
* global install with the project's package manager, streaming output to the
|
|
33
35
|
* rail, then re-probes. A global install can exit clean yet leave the binary
|
|
34
36
|
* off PATH (pnpm/yarn global bins commonly aren't), so success is confirmed by
|
|
35
|
-
* the re-probe, not the exit code alone.
|
|
37
|
+
* the re-probe, not the exit code alone. `upgrade` invokes the active Vercel
|
|
38
|
+
* CLI's native upgrader so it can update the installation that owns that
|
|
39
|
+
* executable, independent of the project's package manager.
|
|
36
40
|
*/
|
|
37
41
|
export declare function runInstallVercelCliFlow(input: {
|
|
38
42
|
appRoot: string;
|
|
39
43
|
prompter: Prompter;
|
|
44
|
+
/** Reinstall the latest CLI even when an existing binary resolves. */
|
|
45
|
+
upgrade?: boolean;
|
|
40
46
|
signal?: AbortSignal;
|
|
41
47
|
deps?: Partial<InstallVercelCliDeps>;
|
|
42
48
|
}): Promise<InstallVercelCliResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{withSpinner}from"../with-spinner.js";import{detectPackageManager}from"#setup/package-manager.js";import{spawnPackageManager}from"#setup/primitives/index.js";import{getVercelAuthStatus}from"#setup/vercel-project.js";import{createPromptCommandOutput}from"#setup/cli/index.js";const defaultDeps={getVercelAuthStatus,detectPackageManager,spawnPackageManager};function globalInstallArguments(e){switch(e){case`npm`:return[`install`,`-g`,`vercel@latest`];case`yarn`:return[`global`,`add`,`vercel@latest`];case`pnpm`:case`bun`:return[`add`,`-g`,`vercel@latest`]}}async function runInstallVercelCliFlow(t){let{appRoot:n,prompter:r,signal:i}=t,a={...defaultDeps,...t.deps},o=createPromptCommandOutput(r.log),probe=async()=>await a.getVercelAuthStatus(n,{signal:i})!==`cli-missing`;if(await withSpinner(r,`Checking for the Vercel CLI…`,probe))return i?.throwIfAborted(),{kind:`already`};i?.throwIfAborted();let s=await a.
|
|
1
|
+
import{withSpinner}from"../with-spinner.js";import{detectPackageManager}from"#setup/package-manager.js";import{runVercel,spawnPackageManager}from"#setup/primitives/index.js";import{getVercelAuthStatus}from"#setup/vercel-project.js";import{createPromptCommandOutput}from"#setup/cli/index.js";const defaultDeps={getVercelAuthStatus,detectPackageManager,runVercel,spawnPackageManager};function globalInstallArguments(e){switch(e){case`npm`:return[`install`,`-g`,`vercel@latest`];case`yarn`:return[`global`,`add`,`vercel@latest`];case`pnpm`:case`bun`:return[`add`,`-g`,`vercel@latest`]}}function summarizeUpgradeFailure(e){let t=e.map(e=>e.trim().replace(/\s+/gu,` `)).filter(e=>e!==``&&e!==`}`&&!e.startsWith(`at `)&&!/^vercel upgrade exited with code \d+\.$/u.test(e)),n=t.find(e=>/^(?:error\b|err_|.*\b(?:cannot|failed|could not)\b)/iu.test(e))??t.at(-1);if(n!==void 0)return n.length<=240?n:`${n.slice(0,239)}…`}async function runInstallVercelCliFlow(t){let{appRoot:n,prompter:r,signal:i}=t,a={...defaultDeps,...t.deps},o=createPromptCommandOutput(r.log),probe=async()=>await a.getVercelAuthStatus(n,{signal:i})!==`cli-missing`;if(!t.upgrade&&await withSpinner(r,`Checking for the Vercel CLI…`,probe))return i?.throwIfAborted(),{kind:`already`};i?.throwIfAborted();let s,c;if(t.upgrade){let t=[];s=await withSpinner(r,`Upgrading the Vercel CLI…`,async()=>{let e=await a.runVercel([`upgrade`],{cwd:n,onOutput:e=>{o(e),e.stream===`stderr`&&t.push(e.text)},signal:i,nonInteractive:!0});return e||(c=summarizeUpgradeFailure(t)),e})}else{let t=await a.detectPackageManager(n);s=await withSpinner(r,`Installing the Vercel CLI with ${t.kind}…`,()=>a.spawnPackageManager(t.kind,n,globalInstallArguments(t.kind),{onOutput:o,signal:i,nonInteractive:!0}))}if(i?.aborted===!0)return{kind:`cancelled`};if(!s)return c===void 0?{kind:`failed`}:{kind:`failed`,reason:c};let l=await withSpinner(r,`Verifying the Vercel CLI…`,probe);return i?.throwIfAborted(),l?{kind:`installed`}:t.upgrade?{kind:`failed`,reason:`The Vercel CLI could not be found after the upgrade completed.`}:{kind:`failed`}}export{runInstallVercelCliFlow};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.34`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.27.
|
|
1
|
+
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.34`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.27.3`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{array,boolean,number,object,string}from"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/schemas.js";import"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js";import{isForbiddenApiFailure}from"./vercel-api-failure.js";import{captureVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";const VERCEL_PROJECT_REQUEST_TIMEOUT_MS=15e3,VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelPaginationSchema=object({next:number().int().nonnegative().nullable().optional()}),VercelTeamPageSchema=object({teams:array(VercelTeamListEntrySchema),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.teams,next:e.pagination?.next??void 0})),VercelProjectPageSchema=object({projects:array(VercelProjectListEntrySchema),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.projects,next:e.pagination?.next??void 0}));function parseVercelJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}async function drainPages(e,t,n){let r=new Map,i=new Set,a;for(;;){let o=await n(a);for(let e of o.items)r.set(
|
|
1
|
+
import{array,boolean,number,object,string}from"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/schemas.js";import"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js";import{isForbiddenApiFailure}from"./vercel-api-failure.js";import{captureVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";const VERCEL_PROJECT_REQUEST_TIMEOUT_MS=15e3,VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelPaginationSchema=object({next:number().int().nonnegative().nullable().optional()}),VercelTeamPageSchema=object({teams:array(VercelTeamListEntrySchema),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.teams,next:e.pagination?.next??void 0})),VercelApiTeamPageSchema=object({teams:array(VercelTeamListEntrySchema.omit({current:!0})),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.teams.map(e=>({...e,current:!1})),next:e.pagination?.next??void 0})),VercelProjectPageSchema=object({projects:array(VercelProjectListEntrySchema),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.projects,next:e.pagination?.next??void 0}));function parseVercelJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}async function drainPages(e,t,n){let r=new Map,i=new Set,a;for(;;){let o=await n(a);for(let e of o.items){let n=t(e);r.has(n)||r.set(n,e)}if(o.next===void 0)return[...r.values()];if(i.has(o.next))throw Error(`Vercel returned a repeated pagination cursor for ${e}.`);i.add(o.next),a=o.next}}function requireVercelTeamAccess(e){let t=e.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-forbidden`,command:`vercel login`,reason:`Vercel denied access to this scope.${t?` ${t}`:``} Re-authenticate (for example to complete a team's SSO) or switch to a team you can access.`})}function isUnsupportedTeamListFailure(e){let t=`${e.stderr}\n${e.stdout}`;return/(?:unknown|unexpected|invalid).*(?:--format|--limit)/iu.test(t)}function requireVercelCliUpgrade(e){throw new HumanActionRequiredError({kind:`vercel-cli-upgrade`,command:`vercel upgrade`,reason:`The installed Vercel CLI does not support the team-list options eve needs. ${e.message} Upgrade it and retry.`})}async function captureTeamPage(e,t,n){let r=await captureVercel(n,{cwd:e,signal:t.signal});if(t.signal?.throwIfAborted(),!r.ok)throw isForbiddenApiFailure(r.failure)&&requireVercelTeamAccess(r.failure),isUnsupportedTeamListFailure(r.failure)&&requireVercelCliUpgrade(r.failure),Error(`Could not list Vercel teams. ${r.failure.message}`);return r.stdout}async function fetchTeamPage(e,t,n){if(n===void 0){let n=await captureTeamPage(e,t,[`teams`,`ls`,`--format`,`json`,`--limit`,`100`]),r=VercelTeamPageSchema.safeParse(parseVercelJson(n,`teams`));if(!r.success)throw Error(`Could not read teams from Vercel CLI JSON output.`);return r.data}let r=await captureTeamPage(e,t,[`api`,`/v2/teams?limit=100&until=${n}`]),i=VercelApiTeamPageSchema.safeParse(parseVercelJson(r,`teams`));if(!i.success)throw Error(`Could not read teams from Vercel API JSON output.`);return i.data}async function listTeams(e,t={}){return drainPages(`Vercel teams`,e=>e.slug,n=>fetchTeamPage(e,t,n))}async function fetchProjectPage(e,t,n){let r=[`project`,`ls`,`--format`,`json`,`--scope`,t];n.search!==void 0&&r.push(`--filter`,n.search),n.next!==void 0&&r.push(`--next`,String(n.next));let i=await captureVercel(r,{cwd:e,signal:n.signal,timeoutMs:VERCEL_PROJECT_REQUEST_TIMEOUT_MS});if(n.signal?.throwIfAborted(),!i.ok)throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not list Vercel projects in ${t}. ${i.failure.message}`);let s=VercelProjectPageSchema.safeParse(parseVercelJson(i.stdout,`projects`));if(!s.success)throw Error(`Could not read projects from Vercel CLI JSON output.`);return s.data}async function listRecentProjects(e,t,n={}){return(await fetchProjectPage(e,t,n)).items}function projectSearchRank(e,t){let n=e.name.toLowerCase(),r=t.toLowerCase();return n===r?0:n.startsWith(r)?1:2}function rankProjectSearchResults(e,t){let n=t.trim();return[...e].sort((e,t)=>projectSearchRank(e,n)-projectSearchRank(t,n))}async function searchProjects(e,t,n,r={}){let i=n.trim();if(i.length===0)throw Error(`Project search query cannot be empty.`);let a=await fetchProjectPage(e,t,{...r,search:i}),o=rankProjectSearchResults(a.items,i);return a.next===void 0?{projects:o}:{projects:o,next:a.next}}export{VERCEL_PROJECT_REQUEST_TIMEOUT_MS,listRecentProjects,listTeams,parseVercelJson,rankProjectSearchResults,requireVercelTeamAccess,searchProjects};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variable naming the public route prefix an eve agent's
|
|
3
|
+
* `/eve/v1/*` transport surface is mounted under on its callback origin.
|
|
4
|
+
*
|
|
5
|
+
* Multi-agent host integrations mount each named agent under
|
|
6
|
+
* `/eve/agents/<name>/eve/v1/*` and strip that prefix before requests reach
|
|
7
|
+
* the eve service, so a running agent cannot recover its own public mount
|
|
8
|
+
* from an inbound request. The integration exports this variable in the
|
|
9
|
+
* generated service build command, the eve CLI resolves it once into the
|
|
10
|
+
* build input, the build bakes it into every emitted Vercel workflow function
|
|
11
|
+
* environment, and callback-URL minting prepends it so framework-owned
|
|
12
|
+
* callbacks resolve to a routable public path.
|
|
13
|
+
*
|
|
14
|
+
* Self-hosted deployments that proxy an agent behind a path prefix can set
|
|
15
|
+
* it directly on the runtime environment.
|
|
16
|
+
*/
|
|
17
|
+
export declare const EVE_PUBLIC_ROUTE_PREFIX_ENV = "EVE_PUBLIC_ROUTE_PREFIX";
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes a public route prefix to `/segment(/segment)*` form: adds the
|
|
20
|
+
* leading slash, strips trailing slashes, and returns `undefined` for
|
|
21
|
+
* values that resolve to the root route (empty, blank, or `/`).
|
|
22
|
+
*/
|
|
23
|
+
export declare function normalizePublicRoutePrefix(value: string | undefined): string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const EVE_PUBLIC_ROUTE_PREFIX_ENV=`EVE_PUBLIC_ROUTE_PREFIX`;function normalizePublicRoutePrefix(e){let t=e?.trim();if(t===void 0||t.length===0)return;let n=(t.startsWith(`/`)?t:`/${t}`).replace(/\/+$/,``);return n.length===0?void 0:n}export{EVE_PUBLIC_ROUTE_PREFIX_ENV,normalizePublicRoutePrefix};
|
package/dist/src/svelte/index.js
CHANGED
package/dist/src/vue/index.js
CHANGED
package/docs/channels/linear.mdx
CHANGED
|
@@ -88,7 +88,7 @@ Start a session without an inbound webhook with `receive(linear, { target })`. S
|
|
|
88
88
|
|
|
89
89
|
### Attachments
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
Markdown images hosted at `https://uploads.linear.app` in Agent Session prompts are fetched with the resolved Linear access token and included as image file parts. eve sends the bearer token only to that exact HTTPS origin; images from other hosts remain markdown text. If a Linear upload fails or returns non-image content, eve preserves its markdown reference and continues the text turn. Other inbound file attachments are not supported on this channel today.
|
|
92
92
|
|
|
93
93
|
### API handle
|
|
94
94
|
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -54,7 +54,7 @@ VERCEL_USE_EXPERIMENTAL_FRAMEWORKS=1 vercel deploy --prod
|
|
|
54
54
|
|
|
55
55
|
Message hooks return `{ auth }` to dispatch, `null` to drop, or `{ auth, context }` to inject background into history.
|
|
56
56
|
|
|
57
|
-
- `onMessage(ctx, message)` handles Slack `message` events. `ctx.isBotMentioned()` and `ctx.isSubscribed()` support mention and active-thread policies
|
|
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
58
|
- `onAppMention(ctx, message)` handles only `app_mention` and takes precedence over `onMessage`. Its default derives workspace-scoped auth and posts `Thinking…`.
|
|
59
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
60
|
|
|
@@ -88,6 +88,41 @@ export default slackChannel({
|
|
|
88
88
|
|
|
89
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
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
|
+
|
|
91
126
|
### Other Events API callbacks
|
|
92
127
|
|
|
93
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.
|
|
@@ -118,6 +153,8 @@ export default slackChannel({
|
|
|
118
153
|
|
|
119
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.
|
|
120
155
|
|
|
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.
|
|
157
|
+
|
|
121
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.
|
|
122
159
|
|
|
123
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.
|
|
@@ -137,10 +174,10 @@ export default slackChannel({
|
|
|
137
174
|
`since` sets the boundary for what each mention injects and accepts three values:
|
|
138
175
|
|
|
139
176
|
- `"thread-root"` (the default): every prior message in the thread, on every mention. `threadContext: {}` behaves the same.
|
|
140
|
-
- `"last-agent-reply"`: only messages after
|
|
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.
|
|
141
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".
|
|
142
179
|
|
|
143
|
-
`threadContext`
|
|
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.
|
|
144
181
|
|
|
145
182
|
### Slack API calls outside a handler
|
|
146
183
|
|
package/docs/extensions.md
CHANGED
|
@@ -148,10 +148,10 @@ A mount gives the publisher's contributions a namespace. Updating the package up
|
|
|
148
148
|
|
|
149
149
|
### Install the package
|
|
150
150
|
|
|
151
|
-
Install the extension
|
|
151
|
+
Install the extension with the package manager already used by the consumer's agent project. Fresh eve projects use pnpm:
|
|
152
152
|
|
|
153
153
|
```bash
|
|
154
|
-
|
|
154
|
+
pnpm add @acme/crm
|
|
155
155
|
```
|
|
156
156
|
|
|
157
157
|
### Mount it
|
|
@@ -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:
|
|
@@ -54,6 +54,8 @@ const billing = useEveAgent({ agent: "billing" });
|
|
|
54
54
|
|
|
55
55
|
Use either `eveRoot` or `agents`, not both. `eveRoot` remains the shorthand for a single unnamed agent mounted at `/eve/v1/*`.
|
|
56
56
|
|
|
57
|
+
Generated agent services build with `EVE_PUBLIC_ROUTE_PREFIX` set to the agent's public mount (for example `/eve/agents/support`) so framework-minted callback URLs — OAuth connection callbacks and remote-subagent session callbacks — resolve to the public per-agent path. If you configure eve services manually in `vercel.json` instead, export that variable in each named agent's build command.
|
|
58
|
+
|
|
57
59
|
### `withEve` options
|
|
58
60
|
|
|
59
61
|
All fields are optional.
|