eve 0.44.3 → 0.44.4

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 (68) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/src/channel/adapter.d.ts +2 -2
  3. package/dist/src/chunks/{use-eve-agent-DpUEwS23.js → use-eve-agent-BJwaMe5k.js} +6 -2
  4. package/dist/src/chunks/{use-eve-agent-BsLJiIbc.js → use-eve-agent-BtsOXv3s.js} +6 -2
  5. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  6. package/dist/src/cli/dev/tui/prompt-commands.d.ts +1 -1
  7. package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
  8. package/dist/src/cli/dev/tui/setup-commands.d.ts +2 -0
  9. package/dist/src/cli/dev/tui/setup-commands.js +1 -1
  10. package/dist/src/client/eve-agent-store.js +1 -1
  11. package/dist/src/compiler/extension-compatibility.d.ts +2 -2
  12. package/dist/src/compiler/extension-compatibility.js +1 -1
  13. package/dist/src/execution/sandbox/bindings/just-bash-runtime.d.ts +1 -0
  14. package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
  15. package/dist/src/execution/sandbox/bindings/just-bash.js +1 -1
  16. package/dist/src/execution/web-fetch/request.js +1 -1
  17. package/dist/src/execution/web-fetch/tool.js +1 -1
  18. package/dist/src/execution/workflow-runtime.js +1 -1
  19. package/dist/src/harness/attachment-staging.js +1 -1
  20. package/dist/src/internal/application/package.js +1 -1
  21. package/dist/src/public/channels/chat-sdk/authorization.d.ts +3 -0
  22. package/dist/src/public/channels/chat-sdk/authorization.js +3 -0
  23. package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +2 -0
  24. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
  25. package/dist/src/public/channels/linq/index.d.ts +1 -1
  26. package/dist/src/public/channels/linq/index.js +1 -1
  27. package/dist/src/public/channels/linq/linqChannel.d.ts +3 -1
  28. package/dist/src/public/channels/linq/linqChannel.js +1 -1
  29. package/dist/src/public/channels/photon/index.d.ts +1 -1
  30. package/dist/src/public/channels/photon/index.js +1 -1
  31. package/dist/src/public/channels/photon/photonIMessageChannel.d.ts +3 -1
  32. package/dist/src/public/channels/photon/photonIMessageChannel.js +1 -1
  33. package/dist/src/public/channels/slack/api.d.ts +15 -5
  34. package/dist/src/public/channels/slack/api.js +1 -1
  35. package/dist/src/public/channels/slack/attachments.d.ts +2 -2
  36. package/dist/src/public/channels/slack/attachments.js +1 -1
  37. package/dist/src/public/channels/slack/hitl.d.ts +2 -0
  38. package/dist/src/public/channels/slack/inbound-content.d.ts +4 -0
  39. package/dist/src/public/channels/slack/inbound-content.js +4 -0
  40. package/dist/src/public/channels/slack/inbound.d.ts +8 -1
  41. package/dist/src/public/channels/slack/inbound.js +1 -1
  42. package/dist/src/public/channels/slack/index.d.ts +1 -1
  43. package/dist/src/public/channels/slack/input-response.d.ts +1 -0
  44. package/dist/src/public/channels/slack/input-response.js +1 -1
  45. package/dist/src/public/channels/slack/interaction-cards.d.ts +10 -0
  46. package/dist/src/public/channels/slack/interaction-cards.js +1 -0
  47. package/dist/src/public/channels/slack/interactions.d.ts +1 -0
  48. package/dist/src/public/channels/slack/interactions.js +1 -1
  49. package/dist/src/public/channels/slack/slackChannel.d.ts +6 -1
  50. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  51. package/dist/src/public/sandbox/just-bash-sandbox.d.ts +12 -1
  52. package/dist/src/setup/flows/registry.d.ts +7 -1
  53. package/dist/src/setup/flows/registry.js +1 -1
  54. package/dist/src/setup/scaffold/create/project.js +39 -2
  55. package/dist/src/shared/channel-definition.d.ts +5 -1
  56. package/dist/src/svelte/index.js +1 -1
  57. package/dist/src/svelte/use-eve-agent.js +1 -1
  58. package/dist/src/vue/index.js +1 -1
  59. package/dist/src/vue/use-eve-agent.js +1 -1
  60. package/docs/channels/linq.mdx +1 -1
  61. package/docs/channels/photon.mdx +4 -4
  62. package/docs/channels/slack.mdx +18 -3
  63. package/docs/concepts/built-in-tools.md +1 -0
  64. package/docs/getting-started.mdx +1 -0
  65. package/docs/guides/dev-tui.md +26 -15
  66. package/docs/meta.json +1 -1
  67. package/docs/sandbox.mdx +20 -0
  68. package/package.json +2 -2
@@ -14,14 +14,17 @@
14
14
  * handlers (`defaults.ts`) can all share the same low-level helpers
15
15
  * without depending on each other.
16
16
  */
17
- import { type SlackApiResponse as SlackPrimitiveApiResponse, type SlackBotToken as SlackPrimitiveBotToken } from "#compiled/@chat-adapter/slack/api.js";
17
+ import { type SlackApiResponse as SlackPrimitiveApiResponse } from "#compiled/@chat-adapter/slack/api.js";
18
18
  import { type CardElement, type FileUpload } from "#compiled/chat/index.js";
19
+ /** Slack app installation workspace available when eve resolves a bot token. */
20
+ export interface SlackBotTokenContext {
21
+ readonly teamId?: string;
22
+ }
19
23
  /**
20
24
  * Slack bot token, materialized either as a literal `xoxb-...` string or
21
- * as a (possibly async) function that returns one. The function form
22
- * supports secret-manager lookups and credential rotation.
25
+ * as a (possibly async) function that receives the app installation workspace.
23
26
  */
24
- export type SlackBotToken = SlackPrimitiveBotToken;
27
+ export type SlackBotToken = string | ((context: SlackBotTokenContext) => string | Promise<string>);
25
28
  /**
26
29
  * Builds the channel-local continuation token (`<channelId>:<threadTs>`).
27
30
  * The runtime's `send()` later namespaces it with the channel's
@@ -34,7 +37,7 @@ export declare function slackContinuationToken(channelId: string, threadTs: stri
34
37
  * Materializes a {@link SlackBotToken} to a string, falling back to
35
38
  * `process.env.SLACK_BOT_TOKEN`. Throws when neither is set.
36
39
  */
37
- export declare function resolveSlackBotToken(token?: SlackBotToken): Promise<string>;
40
+ export declare function resolveSlackBotToken(token?: SlackBotToken, context?: SlackBotTokenContext): Promise<string>;
38
41
  /**
39
42
  * Slack Web API JSON response envelope. `ok` signals success, `error`
40
43
  * carries Slack's error code on failure, and method-specific fields pass
@@ -49,6 +52,7 @@ export type SlackApiResponse = SlackPrimitiveApiResponse;
49
52
  */
50
53
  export declare function callSlackApi(input: {
51
54
  readonly botToken: SlackBotToken | undefined;
55
+ readonly context?: SlackBotTokenContext;
52
56
  readonly operation: string;
53
57
  readonly body: unknown;
54
58
  }): Promise<SlackApiResponse>;
@@ -275,6 +279,9 @@ export interface SlackWorkspaceHandle {
275
279
  /** Builds the workspace-scoped API handle used by generic event callbacks. */
276
280
  export declare function buildSlackWorkspaceHandle(input: {
277
281
  readonly botToken: SlackBotToken | undefined;
282
+ /** Workspace whose app installation supplies the bot token. */
283
+ readonly installationTeamId?: string;
284
+ /** Actor/content workspace exposed on the public handle. */
278
285
  readonly teamId: string | undefined;
279
286
  }): SlackWorkspaceHandle;
280
287
  /**
@@ -303,6 +310,9 @@ export declare function buildSlackBinding(input: {
303
310
  readonly botUserId?: string;
304
311
  readonly channelId: string;
305
312
  readonly threadTs: string;
313
+ /** Workspace whose app installation supplies the bot token. */
314
+ readonly installationTeamId?: string;
315
+ /** Actor/content workspace exposed on the public handle. */
306
316
  readonly teamId: string | undefined;
307
317
  readonly onThreadTsChanged?: (ts: string) => void;
308
318
  }): SlackBinding;
@@ -1 +1 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{isCardElement}from"#compiled/chat/index.js";import{callSlackApi as callSlackApi$1,fetchSlackThreadReplies,postSlackEphemeral,postSlackMessage,resolveSlackBotToken as resolveSlackBotToken$1,uploadSlackFiles}from"#compiled/@chat-adapter/slack/api.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{truncateTypingStatus}from"#public/channels/slack/limits.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";const log=createLogger(`slack.api`);function slackContinuationToken(e,t){return`${e}:${t}`}async function resolveSlackBotToken(e){let t=e??process.env.SLACK_BOT_TOKEN;if(!t)throw Error(`SLACK_BOT_TOKEN is required.`);return resolveSlackBotToken$1(t)}async function callSlackApi(e){return callSlackApi$1(e.operation,normalizeSlackApiBody(e.body),createSlackApiOptions(e.botToken))}function createSlackRequester(e){return(t,n)=>callSlackApi({botToken:e,operation:t,body:n})}function buildSlackWorkspaceHandle(e){return{teamId:e.teamId,request:createSlackRequester(e.botToken)}}function buildSlackBinding(e){let n=createSlackRequester(e.botToken),r=[],s=e.threadTs,l;function handleMessageTs(t){s||t===s||(s=t,e.onThreadTsChanged?.(t))}async function uploadFiles(t,n){let r=n?.channelId??e.channelId,i=n?.threadTs??s;return uploadSlackFiles(t.map(toSlackFileUpload),{...createSlackApiOptions(e.botToken),channelId:r||void 0,initialComment:n?.initialComment,threadTs:i||void 0})}function refreshMessages(){if(l!==void 0)return l;async function fetchAndReplace(){if(!e.channelId||!s){r=[];return}try{r=(await fetchSlackThreadReplies({...createSlackApiOptions(e.botToken),channel:e.channelId,limit:50,ts:s})).messages.map(t=>parseThreadMessage(t,s,{appId:e.appId,botUserId:e.botUserId}))}catch(n){logError(log,`refresh threw — swallowed`,n,{channelId:e.channelId})}}let n=fetchAndReplace().finally(()=>{l===n&&(l=void 0)});return l=n,n}let u={get recentMessages(){return r},async listParticipants(){await u.refresh();let e=new Set;for(let t of r)t.user!==void 0&&t.botId===void 0&&e.add(t.user);return[...e]},async post(t){let n=normalizePostInput(t),r=n.files??[],i=`blocks`in n||`card`in n||`markdown`in n;if(r.length>0&&!i){let e=await uploadFiles(r,{initialComment:`text`in n?n.text:void 0});return{id:Array.isArray(e.raw.files)&&e.raw.files.length>0?String(e.raw.files[0].id??``):``,raw:e.raw}}let a=await postSlackMessage(buildPostMessageOptions(n,e.channelId,s,e.botToken)),c=a.id;if(handleMessageTs(c),r.length>0&&i)try{await uploadFiles(r)}catch(e){log.warn(`file upload after message post failed`,{error:e})}return{id:c,raw:a.raw}},async postEphemeral(t,n){let r=await postSlackEphemeral({...buildPostMessageOptions(normalizePostInput(n),e.channelId,s,e.botToken),user:t});return{id:r.id,raw:r.raw}},async postDirectMessage(t,r){let i=await n(`conversations.open`,{users:t}),a=i.ok===!0?i.channel?.id:void 0;if(typeof a!=`string`||a.length===0)throw Error(`Slack conversations.open failed: ${i.error??`unknown_error`}`);let s=await postSlackMessage(buildPostMessageOptions(normalizePostInput(r),a,``,e.botToken));return{id:s.id,raw:s.raw}},async startTyping(r){if(!(!e.channelId||!s))try{let t=r===void 0?``:truncateTypingStatus(r),i={channel_id:e.channelId,thread_ts:s,status:t};t.length>0&&(i.loading_messages=[t]);let a=await n(`assistant.threads.setStatus`,i);a.ok!==!0&&log.warn(`assistant.threads.setStatus returned not-ok`,{error:a.error})}catch(n){logError(log,`startTyping threw — swallowed`,n,{channelId:e.channelId})}},refresh(){return refreshMessages()},mentionUser(e){return`<@${e}>`}};return{thread:u,slack:{channelId:e.channelId,get threadTs(){return s},teamId:e.teamId,request:n,uploadFiles}}}function normalizePostInput(e){return typeof e==`string`?{markdown:e}:isCardElement(e)?{card:e}:e}function buildPostMessageOptions(e,t,n,r){let i={...createSlackApiOptions(r),channel:t,threadTs:n||void 0,unfurlLinks:!1,unfurlMedia:!1};return`card`in e?(i.blocks=cardToBlocks(e.card),i.text=e.fallbackText??cardToFallbackText(e.card),i):`blocks`in e?(i.blocks=[...e.blocks],e.text!==void 0&&(i.text=e.text),i):`markdown`in e?(i.markdownText=e.markdown,i):(i.text=e.text,i)}function createSlackApiOptions(e){return{token:()=>resolveSlackBotToken(e)}}function normalizeSlackApiBody(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:{}}function toSlackFileUpload(e){return{data:normalizeFileData(e.data),filename:e.filename}}function normalizeFileData(e){if(e instanceof ArrayBuffer||typeof Blob<`u`&&e instanceof Blob)return e;if(ArrayBuffer.isView(e)){let t=e;return new Uint8Array(t.buffer,t.byteOffset,t.byteLength)}return e}function parseThreadMessage(e,t,n){let r=typeof e.text==`string`?e.text:``,i=typeof e.ts==`string`?e.ts:``,a=typeof e.thread_ts==`string`?e.thread_ts:t,o=typeof e.user==`string`?e.user:void 0,s=typeof e.bot_id==`string`?e.bot_id:void 0,c=typeof e.app_id==`string`?e.app_id:void 0,l=n.botUserId!==void 0&&o===n.botUserId||n.appId!==void 0&&c===n.appId;return{text:r,markdown:slackMrkdwnToGfm(r),user:o,botId:s,ts:i,threadTs:a,isMe:l,raw:e}}export{buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,resolveSlackBotToken,slackContinuationToken};
1
+ import{createLogger,logError}from"#internal/logging.js";import{isCardElement}from"#compiled/chat/index.js";import{callSlackApi as callSlackApi$1,fetchSlackThreadReplies,postSlackEphemeral,postSlackMessage,resolveSlackBotToken as resolveSlackBotToken$1,uploadSlackFiles}from"#compiled/@chat-adapter/slack/api.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{resolveSlackInboundMrkdwn}from"#public/channels/slack/inbound-content.js";import{truncateTypingStatus}from"#public/channels/slack/limits.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";const log=createLogger(`slack.api`);function slackContinuationToken(e,t){return`${e}:${t}`}async function resolveSlackBotToken(e,t={}){let n=e??process.env.SLACK_BOT_TOKEN;if(!n)throw Error(`SLACK_BOT_TOKEN is required.`);return typeof n==`function`?n(t):resolveSlackBotToken$1(n)}async function callSlackApi(e){return callSlackApi$1(e.operation,normalizeSlackApiBody(e.body),createSlackApiOptions(e.botToken,e.context))}function createSlackRequester(e,t){return(n,r)=>callSlackApi({botToken:e,context:t,operation:n,body:r})}function buildSlackWorkspaceHandle(e){return{teamId:e.teamId,request:createSlackRequester(e.botToken,{teamId:e.installationTeamId})}}function buildSlackBinding(e){let n={teamId:e.installationTeamId},r=createSlackRequester(e.botToken,n),s=[],l=e.threadTs,u;function handleMessageTs(t){l||t===l||(l=t,e.onThreadTsChanged?.(t))}async function uploadFiles(t,r){let i=r?.channelId??e.channelId,a=r?.threadTs??l;return uploadSlackFiles(t.map(toSlackFileUpload),{...createSlackApiOptions(e.botToken,n),channelId:i||void 0,initialComment:r?.initialComment,threadTs:a||void 0})}function refreshMessages(){if(u!==void 0)return u;async function fetchAndReplace(){if(!e.channelId||!l){s=[];return}try{s=(await fetchSlackThreadReplies({...createSlackApiOptions(e.botToken,n),channel:e.channelId,limit:50,ts:l})).messages.map(t=>parseThreadMessage(t,l,{appId:e.appId,botUserId:e.botUserId}))}catch(n){logError(log,`refresh threw — swallowed`,n,{channelId:e.channelId})}}let r=fetchAndReplace().finally(()=>{u===r&&(u=void 0)});return u=r,r}let d={get recentMessages(){return s},async listParticipants(){await d.refresh();let e=new Set;for(let t of s)t.user!==void 0&&t.botId===void 0&&e.add(t.user);return[...e]},async post(t){let r=normalizePostInput(t),i=r.files??[],a=`blocks`in r||`card`in r||`markdown`in r;if(i.length>0&&!a){let e=await uploadFiles(i,{initialComment:`text`in r?r.text:void 0});return{id:Array.isArray(e.raw.files)&&e.raw.files.length>0?String(e.raw.files[0].id??``):``,raw:e.raw}}let s=await postSlackMessage(buildPostMessageOptions(r,e.channelId,l,e.botToken,n)),c=s.id;if(handleMessageTs(c),i.length>0&&a)try{await uploadFiles(i)}catch(e){log.warn(`file upload after message post failed`,{error:e})}return{id:c,raw:s.raw}},async postEphemeral(t,r){let i=await postSlackEphemeral({...buildPostMessageOptions(normalizePostInput(r),e.channelId,l,e.botToken,n),user:t});return{id:i.id,raw:i.raw}},async postDirectMessage(t,i){let a=await r(`conversations.open`,{users:t}),s=a.ok===!0?a.channel?.id:void 0;if(typeof s!=`string`||s.length===0)throw Error(`Slack conversations.open failed: ${a.error??`unknown_error`}`);let c=await postSlackMessage(buildPostMessageOptions(normalizePostInput(i),s,``,e.botToken,n));return{id:c.id,raw:c.raw}},async startTyping(n){if(!(!e.channelId||!l))try{let t=n===void 0?``:truncateTypingStatus(n),i={channel_id:e.channelId,thread_ts:l,status:t};t.length>0&&(i.loading_messages=[t]);let a=await r(`assistant.threads.setStatus`,i);a.ok!==!0&&log.warn(`assistant.threads.setStatus returned not-ok`,{error:a.error})}catch(n){logError(log,`startTyping threw — swallowed`,n,{channelId:e.channelId})}},refresh(){return refreshMessages()},mentionUser(e){return`<@${e}>`}};return{thread:d,slack:{channelId:e.channelId,get threadTs(){return l},teamId:e.teamId,request:r,uploadFiles}}}function normalizePostInput(e){return typeof e==`string`?{markdown:e}:isCardElement(e)?{card:e}:e}function buildPostMessageOptions(e,t,n,r,i){let a={...createSlackApiOptions(r,i),channel:t,threadTs:n||void 0,unfurlLinks:!1,unfurlMedia:!1};return`card`in e?(a.blocks=cardToBlocks(e.card),a.text=e.fallbackText??cardToFallbackText(e.card),a):`blocks`in e?(a.blocks=[...e.blocks],e.text!==void 0&&(a.text=e.text),a):`markdown`in e?(a.markdownText=e.markdown,a):(a.text=e.text,a)}function createSlackApiOptions(e,t={}){return{token:()=>resolveSlackBotToken(e,t)}}function normalizeSlackApiBody(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:{}}function toSlackFileUpload(e){return{data:normalizeFileData(e.data),filename:e.filename}}function normalizeFileData(e){if(e instanceof ArrayBuffer||typeof Blob<`u`&&e instanceof Blob)return e;if(ArrayBuffer.isView(e)){let t=e;return new Uint8Array(t.buffer,t.byteOffset,t.byteLength)}return e}function parseThreadMessage(e,t,n){let r=resolveSlackInboundMrkdwn(typeof e.text==`string`?e.text:``,e),i=typeof e.ts==`string`?e.ts:``,a=typeof e.thread_ts==`string`?e.thread_ts:t,o=typeof e.user==`string`?e.user:void 0,s=typeof e.bot_id==`string`?e.bot_id:void 0,c=typeof e.app_id==`string`?e.app_id:void 0,l=n.botUserId!==void 0&&o===n.botUserId||n.appId!==void 0&&c===n.appId;return{text:r,markdown:slackMrkdwnToGfm(r),user:o,botId:s,ts:i,threadTs:a,isMe:l,raw:e}}export{buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,resolveSlackBotToken,slackContinuationToken};
@@ -1,5 +1,5 @@
1
1
  import type { FilePart, UserContent } from "ai";
2
- import type { FetchFileResult } from "#channel/adapter.js";
2
+ import type { FetchFileContext, FetchFileResult } from "#channel/adapter.js";
3
3
  import { type SlackBotToken, type SlackThread } from "#public/channels/slack/api.js";
4
4
  import type { SlackAttachment, SlackMessage } from "#public/channels/slack/inbound.js";
5
5
  import type { UploadPolicy } from "#public/channels/upload-policy.js";
@@ -49,4 +49,4 @@ export declare function buildSlackTurnMessage(text: string, fileParts: readonly
49
49
  */
50
50
  export declare function createSlackFetchFile(input: {
51
51
  readonly botToken?: SlackBotToken;
52
- }): (url: string) => Promise<FetchFileResult | null>;
52
+ }): (url: string, context?: FetchFileContext) => Promise<FetchFileResult | null>;
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{evaluateFilePart,formatUploadPolicyViolation,isUploadsDisabled}from"#public/channels/upload-policy.js";import{resolveSlackBotToken}from"#public/channels/slack/api.js";const log=createLogger(`slack.attachments`);function collectSlackFileParts(e,r){let i=[];for(let o of e??[]){let e=toSlackFilePart(o,i.length);if(e===null)continue;let s=evaluateFilePart(e,r);if(s!==null){log.warn(`dropped attachment — ${formatUploadPolicyViolation(s)}`,{name:o.name});continue}i.push(e)}return i}function toSlackFilePart(e,t){return e.type===`audio`||e.type===`video`?null:e.url?{type:`file`,mediaType:e.mimeType??`application/octet-stream`,filename:e.name??`attachment-${t}`,data:new URL(e.url)}:(log.warn(`dropped attachment — no url available`,{name:e.name}),null)}async function collectInboundFileParts(e){let t=collectSlackFileParts(e.mention.attachments,e.policy);if(t.length>0)return t;if(isUploadsDisabled(e.policy))return[];if(e.thread.recentMessages.length===0)try{await e.thread.refresh()}catch(e){return log.warn(`slack thread refresh failed for attachment collection`,{error:e}),[]}let n=e.thread.recentMessages;for(let t=n.length-1;t>=0;--t){let r=n[t];if(!r||r.isMe)continue;let i=r.raw,a=collectSlackFileParts(extractAttachmentsFromRaw(i?.files),e.policy);return a.length>0?a:[]}return[]}function extractAttachmentsFromRaw(e){return Array.isArray(e)?e.map(e=>{let t=typeof e.mimetype==`string`?e.mimetype:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:typeof e.url_private==`string`?e.url_private:void 0,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}):[]}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function buildSlackTurnMessage(e,t){return t.length===0?e:e.trim().length===0?[...t]:[{type:`text`,text:e},...t]}function createSlackFetchFile(e){return async t=>{if(!isSlackFileUrl(t))return null;let n=await resolveSlackBotToken(e.botToken),r=await fetch(t,{headers:{authorization:`Bearer ${n}`}});if(!r.ok)throw Error(`Slack file fetch returned HTTP ${r.status} for ${t}.`);let a=r.headers.get(`content-type`)??void 0;if(a?.split(`;`,1)[0]?.trim().toLowerCase()===`text/html`)throw Error(`Slack file fetch returned an HTML sign-in page instead of file bytes for ${t}. The bot token may be missing the files:read scope. Add the scope, reinstall the Slack app, and retry.`);return{bytes:Buffer.from(await r.arrayBuffer()),mediaType:a}}}function isSlackFileUrl(e){let t=URL.parse(e);return t?.protocol===`https:`?t.hostname===`files.slack.com`||(t.hostname===`enterprise.slack.com`||t.hostname.endsWith(`.enterprise.slack.com`))&&t.pathname.startsWith(`/files/`):!1}export{buildSlackTurnMessage,collectInboundFileParts,collectSlackFileParts,createSlackFetchFile};
1
+ import{createLogger}from"#internal/logging.js";import{evaluateFilePart,formatUploadPolicyViolation,isUploadsDisabled}from"#public/channels/upload-policy.js";import{resolveSlackBotToken}from"#public/channels/slack/api.js";const log=createLogger(`slack.attachments`);function collectSlackFileParts(e,r){let i=[];for(let o of e??[]){let e=toSlackFilePart(o,i.length);if(e===null)continue;let s=evaluateFilePart(e,r);if(s!==null){log.warn(`dropped attachment — ${formatUploadPolicyViolation(s)}`,{name:o.name});continue}i.push(e)}return i}function toSlackFilePart(e,t){return e.type===`audio`||e.type===`video`?null:e.url?{type:`file`,mediaType:e.mimeType??`application/octet-stream`,filename:e.name??`attachment-${t}`,data:new URL(e.url)}:(log.warn(`dropped attachment — no url available`,{name:e.name}),null)}async function collectInboundFileParts(e){let t=collectSlackFileParts(e.mention.attachments,e.policy);if(t.length>0)return t;if(isUploadsDisabled(e.policy))return[];if(e.thread.recentMessages.length===0)try{await e.thread.refresh()}catch(e){return log.warn(`slack thread refresh failed for attachment collection`,{error:e}),[]}let n=e.thread.recentMessages;for(let t=n.length-1;t>=0;--t){let r=n[t];if(!r||r.isMe)continue;let i=r.raw,a=collectSlackFileParts(extractAttachmentsFromRaw(i?.files),e.policy);return a.length>0?a:[]}return[]}function extractAttachmentsFromRaw(e){return Array.isArray(e)?e.map(e=>{let t=typeof e.mimetype==`string`?e.mimetype:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:typeof e.url_private==`string`?e.url_private:void 0,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}):[]}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function buildSlackTurnMessage(e,t){return t.length===0?e:e.trim().length===0?[...t]:[{type:`text`,text:e},...t]}function createSlackFetchFile(e){return async(t,n)=>{if(!isSlackFileUrl(t))return null;let r=n?.state.installationTeamId,a=await resolveSlackBotToken(e.botToken,{teamId:typeof r==`string`?r:void 0}),o=await fetch(t,{headers:{authorization:`Bearer ${a}`}});if(!o.ok)throw Error(`Slack file fetch returned HTTP ${o.status} for ${t}.`);let s=o.headers.get(`content-type`)??void 0;if(s?.split(`;`,1)[0]?.trim().toLowerCase()===`text/html`)throw Error(`Slack file fetch returned an HTML sign-in page instead of file bytes for ${t}. The bot token may be missing the files:read scope. Add the scope, reinstall the Slack app, and retry.`);return{bytes:Buffer.from(await o.arrayBuffer()),mediaType:s}}}function isSlackFileUrl(e){let t=URL.parse(e);return t?.protocol===`https:`?t.hostname===`files.slack.com`||(t.hostname===`enterprise.slack.com`||t.hostname.endsWith(`.enterprise.slack.com`))&&t.pathname.startsWith(`/files/`):!1}export{buildSlackTurnMessage,collectInboundFileParts,collectSlackFileParts,createSlackFetchFile};
@@ -119,6 +119,8 @@ export declare function formatInputRequestFallbackText(request: InputRequest): s
119
119
  export interface HitlFreeformModalMetadata {
120
120
  readonly continuationToken: string;
121
121
  readonly channelId: string;
122
+ /** Workspace whose app installation supplies credentials for modal follow-up API calls. */
123
+ readonly installationTeamId?: string;
122
124
  readonly threadTs: string;
123
125
  readonly messageTs: string;
124
126
  readonly requestId: string;
@@ -0,0 +1,4 @@
1
+ /** Derives inbound mrkdwn from top-level text plus Block Kit and legacy attachments. */
2
+ export declare function resolveSlackInboundMrkdwn(text: string, raw: Record<string, unknown>): string;
3
+ /** Reads the `text` string from a Slack composition text object (`{ text: "..." }`). */
4
+ export declare function readSlackTextObject(textObject: unknown): string;
@@ -0,0 +1,4 @@
1
+ import{isObject}from"#shared/guards.js";import{formatSlackLink}from"#compiled/@chat-adapter/slack/format.js";function resolveSlackInboundMrkdwn(e,t){try{return resolveSlackInboundMrkdwnUnsafe(e,t)}catch{return e}}function resolveSlackInboundMrkdwnUnsafe(e,t){let n=extractSlackStructuredMrkdwn(t.blocks,t.attachments),r=e.trim();if(!r)return n;if(!n||normalizeComparableText(n)===normalizeComparableText(r))return e;let i=normalizeComparableText(n),a=normalizeComparableText(r);return n.length>r.length&&i.includes(a)?n:n.length>=r.length*2?Array.isArray(t.attachments)&&t.attachments.length>0&&!i.includes(a)?`${e}\n${n}`:n:e}function extractSlackStructuredMrkdwn(e,t){return[...extractBlockKitLines(e),...extractLegacyAttachmentLines(t)].join(`
2
+ `).trim()}function extractBlockKitLines(t){if(!Array.isArray(t))return[];let n=[];for(let r of t)if(isObject(r))switch(r.type){case`section`:appendTextObjectLine(n,r.text),appendFieldLines(n,r.fields),appendSectionAccessoryLine(n,r.accessory);break;case`header`:appendTextObjectLine(n,r.text);break;case`video`:appendTextObjectLine(n,r.title),appendTextObjectLine(n,r.description);break;case`markdown`:typeof r.text==`string`&&r.text.length>0&&n.push(r.text);break;case`table`:case`data_table`:appendTableLines(n,r);break;case`card`:appendCardLines(n,r);break;case`carousel`:if(Array.isArray(r.elements))for(let t of r.elements)isObject(t)&&appendCardLines(n,t);break;case`container`:appendTextObjectLine(n,r.title),appendTextObjectLine(n,r.subtitle),n.push(...extractBlockKitLines(r.child_blocks));break;case`context`:appendElementLines(n,r.elements);break;case`actions`:appendActionsLine(n,r.elements);break;case`rich_text`:appendRichTextLine(n,r.elements);break;case`image`:typeof r.alt_text==`string`&&r.alt_text.length>0&&n.push(r.alt_text),typeof r.title==`object`&&r.title!==null&&appendTextObjectLine(n,r.title);break;default:break}return n}function extractLegacyAttachmentLines(t){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!isObject(r))continue;let t=n.length;typeof r.pretext==`string`&&r.pretext.length>0&&n.push(r.pretext),typeof r.title==`string`&&r.title.length>0&&n.push(r.title),typeof r.text==`string`&&r.text.length>0&&n.push(r.text),appendLegacyFieldLines(n,r.fields),typeof r.footer==`string`&&r.footer.length>0&&n.push(r.footer),n.push(...extractBlockKitLines(r.blocks)),n.length===t&&typeof r.fallback==`string`&&r.fallback.length>0&&n.push(r.fallback)}return n}function appendTextObjectLine(e,t){let n=readSlackTextObject(t);n&&e.push(n)}function appendFieldLines(e,t){if(Array.isArray(t))for(let n of t)appendTextObjectLine(e,n)}function appendElementLines(t,n){if(Array.isArray(n)){for(let r of n)if(isObject(r)){if(r.type===`image`){typeof r.alt_text==`string`&&r.alt_text.length>0&&t.push(r.alt_text);continue}appendTextObjectLine(t,r)}}}function appendSectionAccessoryLine(t,n){isObject(n)&&n.type===`button`&&appendActionsLine(t,[n])}function appendCardLines(e,t){appendTextObjectLine(e,t.title),appendTextObjectLine(e,t.subtitle),appendTextObjectLine(e,t.body),appendTextObjectLine(e,t.subtext),appendActionsLine(e,t.actions)}function appendTableLines(e,t){if(appendTextObjectLine(e,t.caption),Array.isArray(t.rows))for(let n of t.rows){if(!Array.isArray(n))continue;let t=n.map(tableCellToPlain);t.some(e=>e.length>0)&&e.push(t.join(` | `))}}function tableCellToPlain(t){return isObject(t)?t.type===`rich_text`?richTextPartsToPlain(t.elements):typeof t.text==`string`?t.text:typeof t.value==`number`||typeof t.value==`string`?String(t.value):``:``}function appendActionsLine(t,n){if(!Array.isArray(n))return;let r=n.map(t=>isObject(t)?readSlackTextObject(t.text):``).filter(e=>e.length>0);r.length>0&&t.push(r.map(e=>`[${e}]`).join(` `))}function appendLegacyFieldLines(t,n){if(Array.isArray(n))for(let r of n){if(!isObject(r))continue;let n=typeof r.title==`string`?r.title.trim():``,i=typeof r.value==`string`?r.value.trim():``;n&&i?t.push(`${n}: ${i}`):i?t.push(i):n&&t.push(n)}}function appendRichTextLine(e,t){let n=richTextPartsToPlain(t);n&&e.push(n)}function richTextPartsToPlain(t){return Array.isArray(t)?t.map(t=>isObject(t)?richTextPartToPlain(t):``).filter(e=>e.length>0).join(`
3
+ `):``}function richTextPartToPlain(e){switch(e.type){case`rich_text_section`:case`rich_text_preformatted`:case`rich_text_quote`:return richTextInlineToPlain(e.elements);case`rich_text_list`:return richTextListToPlain(e);default:return``}}function richTextInlineToPlain(t){if(!Array.isArray(t))return``;let n=[];for(let r of t)if(isObject(r))switch(r.type){case`text`:typeof r.text==`string`&&n.push(r.text);break;case`link`:typeof r.url==`string`&&typeof r.text==`string`?n.push(formatInboundRichTextLink(r.url,r.text)):typeof r.text==`string`?n.push(r.text):typeof r.url==`string`&&n.push(r.url);break;case`emoji`:typeof r.name==`string`&&n.push(`:${r.name}:`);break;case`user`:typeof r.user_id==`string`&&n.push(`<@${r.user_id}>`);break;case`channel`:typeof r.channel_id==`string`&&n.push(`<#${r.channel_id}>`);break;case`usergroup`:typeof r.usergroup_id==`string`&&n.push(`<!subteam^${r.usergroup_id}>`);break;case`broadcast`:typeof r.range==`string`&&n.push(`<!${r.range}>`);break;case`date`:typeof r.fallback==`string`&&n.push(r.fallback);break;default:break}return n.join(``)}function richTextListToPlain(t){return Array.isArray(t.elements)?t.elements.map(t=>isObject(t)?richTextInlineToPlain(t.elements):``).filter(e=>e.length>0).join(`
4
+ `):``}function readSlackTextObject(t){return isObject(t)&&typeof t.text==`string`?t.text:``}function normalizeComparableText(e){return e.replace(/\s+/gu,` `).trim()}function formatInboundRichTextLink(e,n){try{return formatSlackLink(e,n)}catch(t){if(t instanceof TypeError)return`${n} (${e})`;throw t}}export{readSlackTextObject,resolveSlackInboundMrkdwn};
@@ -45,7 +45,11 @@ export interface SlackAttachment {
45
45
  * `onAppMention(ctx, message)`).
46
46
  */
47
47
  export interface SlackMessage {
48
- /** The original Slack text (mrkdwn). */
48
+ /**
49
+ * The message body (mrkdwn). Usually the top-level Slack `text`; when
50
+ * that is empty or a short fallback, derived from Block Kit blocks and
51
+ * legacy attachments on the raw event.
52
+ */
49
53
  readonly text: string;
50
54
  /** {@link text} re-rendered as GFM markdown for the agent. */
51
55
  readonly markdown: string;
@@ -83,6 +87,7 @@ export interface SlackEventCallback {
83
87
  readonly team_id?: string;
84
88
  readonly authorizations?: readonly {
85
89
  readonly is_bot?: boolean;
90
+ readonly team_id?: string;
86
91
  readonly user_id?: string;
87
92
  }[];
88
93
  readonly event?: {
@@ -128,6 +133,8 @@ export declare function parseAppMentionEvent(envelope: SlackEventCallback): Slac
128
133
  */
129
134
  /** Returns the bot user id attached to a Slack event authorization. */
130
135
  export declare function slackEventBotUserId(envelope: SlackEventCallback): string | undefined;
136
+ /** Returns the workspace whose app installation authorized this event. */
137
+ export declare function slackEventInstallationTeamId(envelope: SlackEventCallback): string | undefined;
131
138
  /** Parses a Slack message event without applying bot or subtype policy. */
132
139
  export declare function parseMessageEvent(envelope: SlackEventCallback): SlackMessage | null;
133
140
  export declare function parseDirectMessageEvent(envelope: SlackEventCallback): SlackMessage | null;
@@ -1 +1 @@
1
- import{isObject}from"#shared/guards.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";function parseSlackEventEnvelope(t){let n=JSON.parse(t);if(!isObject(n)||n.type!==`event_callback`)return null;let r=n.event;return!isObject(r)||typeof r.type!=`string`||r.type.length===0?null:n}function parseAppMentionEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`app_mention`?null:buildSlackMessage(t,e.team_id)}function slackEventBotUserId(e){return e.authorizations?.find(e=>e.is_bot===!0&&typeof e.user_id==`string`)?.user_id}function parseMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`message`?null:buildSlackMessage(t,e.team_id)}function parseDirectMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;if(!t||t.type!==`message`)return null;let n=t;return n.channel_type!==`im`||!isHumanMessage(n)?null:buildSlackMessage(n,e.team_id)}function isHumanMessage(e){return typeof e.subtype==`string`&&e.subtype.length>0&&e.subtype!==`file_share`?!1:!(typeof e.bot_id==`string`&&e.bot_id.length>0)}function slackMessageFromWebhookPayload(e){return e.kind===`direct_message`&&(typeof e.subtype==`string`&&e.subtype.length>0&&e.subtype!==`file_share`||typeof e.botId==`string`&&e.botId.length>0)||!e.channelId||!e.ts?null:{text:e.text,markdown:slackMrkdwnToGfm(e.text),ts:e.ts,threadTs:e.threadTs,channelId:e.channelId,teamId:e.teamId,author:parsePayloadAuthor(e),attachments:parsePayloadAttachments(e.files),raw:e.raw}}function buildSlackMessage(e,n){let r=typeof e.channel==`string`?e.channel:``,i=typeof e.ts==`string`?e.ts:``;if(!r||!i)return null;let a=typeof e.text==`string`?e.text:``,o=typeof e.thread_ts==`string`?e.thread_ts:i,s=typeof n==`string`?n:void 0;return{text:a,markdown:slackMrkdwnToGfm(a),ts:i,threadTs:o,channelId:r,teamId:s,author:parseAuthor(e),attachments:parseAttachments(e.files),raw:e}}function parseAuthor(e){let t=typeof e.user==`string`?e.user:``;if(t)return{userId:t,userName:typeof e.username==`string`?e.username:void 0,fullName:void 0,isBot:typeof e.bot_id==`string`&&e.bot_id.length>0,isMe:!1}}function parseAttachments(e){return Array.isArray(e)?e.map(toAttachment):[]}function toAttachment(e){let t=typeof e.mimetype==`string`?e.mimetype:void 0,n=typeof e.url_private==`string`?e.url_private:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:n,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}function toPayloadAttachment(e){return{id:e.id,type:e.type,url:e.url,name:e.name,mimeType:e.mimeType,size:e.size}}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function parsePayloadAuthor(e){if(!e.userId)return;let t=e.raw;return{userId:e.userId,userName:typeof t.username==`string`?t.username:void 0,fullName:void 0,isBot:typeof t.bot_id==`string`&&t.bot_id.length>0,isMe:!1}}function parsePayloadAttachments(e){return Array.isArray(e)?e.map(toPayloadAttachment):[]}export{parseAppMentionEvent,parseDirectMessageEvent,parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackMessageFromWebhookPayload};
1
+ import{isObject}from"#shared/guards.js";import{resolveSlackInboundMrkdwn}from"#public/channels/slack/inbound-content.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";function parseSlackEventEnvelope(t){let n=JSON.parse(t);if(!isObject(n)||n.type!==`event_callback`)return null;let r=n.event;return!isObject(r)||typeof r.type!=`string`||r.type.length===0?null:n}function parseAppMentionEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`app_mention`?null:buildSlackMessage(t,e.team_id)}function slackEventBotUserId(e){return e.authorizations?.find(e=>e.is_bot===!0&&typeof e.user_id==`string`)?.user_id}function slackEventInstallationTeamId(e){let t=e.authorizations??[],n=t.find(e=>e.is_bot===!0&&typeof e.team_id==`string`);return n?.team_id===void 0?t.find(e=>typeof e.team_id==`string`)?.team_id:n.team_id}function parseMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`message`?null:buildSlackMessage(t,e.team_id)}function parseDirectMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;if(!t||t.type!==`message`)return null;let n=t;return n.channel_type!==`im`||!isHumanMessage(n)?null:buildSlackMessage(n,e.team_id)}function isHumanMessage(e){return typeof e.subtype==`string`&&e.subtype.length>0&&e.subtype!==`file_share`?!1:!(typeof e.bot_id==`string`&&e.bot_id.length>0)}function slackMessageFromWebhookPayload(e){if(e.kind===`direct_message`&&(typeof e.subtype==`string`&&e.subtype.length>0&&e.subtype!==`file_share`||typeof e.botId==`string`&&e.botId.length>0)||!e.channelId||!e.ts)return null;let r=resolveSlackInboundMrkdwn(e.text,e.raw);return{text:r,markdown:slackMrkdwnToGfm(r),ts:e.ts,threadTs:e.threadTs,channelId:e.channelId,teamId:e.teamId,author:parsePayloadAuthor(e),attachments:parsePayloadAttachments(e.files),raw:e.raw}}function buildSlackMessage(e,r){let i=typeof e.channel==`string`?e.channel:``,a=typeof e.ts==`string`?e.ts:``;if(!i||!a)return null;let o=resolveSlackInboundMrkdwn(typeof e.text==`string`?e.text:``,e),s=typeof e.thread_ts==`string`?e.thread_ts:a,c=typeof r==`string`?r:void 0;return{text:o,markdown:slackMrkdwnToGfm(o),ts:a,threadTs:s,channelId:i,teamId:c,author:parseAuthor(e),attachments:parseAttachments(e.files),raw:e}}function parseAuthor(e){let t=typeof e.user==`string`?e.user:``;if(t)return{userId:t,userName:typeof e.username==`string`?e.username:void 0,fullName:void 0,isBot:typeof e.bot_id==`string`&&e.bot_id.length>0,isMe:!1}}function parseAttachments(e){return Array.isArray(e)?e.map(toAttachment):[]}function toAttachment(e){let t=typeof e.mimetype==`string`?e.mimetype:void 0,n=typeof e.url_private==`string`?e.url_private:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:n,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}function toPayloadAttachment(e){return{id:e.id,type:e.type,url:e.url,name:e.name,mimeType:e.mimeType,size:e.size}}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function parsePayloadAuthor(e){if(!e.userId)return;let t=e.raw;return{userId:e.userId,userName:typeof t.username==`string`?t.username:void 0,fullName:void 0,isBot:typeof t.bot_id==`string`&&t.bot_id.length>0,isMe:!1}}function parsePayloadAttachments(e){return Array.isArray(e)?e.map(toPayloadAttachment):[]}export{parseAppMentionEvent,parseDirectMessageEvent,parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackMessageFromWebhookPayload};
@@ -7,7 +7,7 @@
7
7
  export type { ModelMessage } from "ai";
8
8
  export { slackChannel, type SlackApiResponse, type SlackAuthorizationEventContext, type SlackAuthorizationRequiredHandler, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackEventRespondFn, type SlackEventRespondOptions, type SlackEventSendFn, type SlackEventSendOptions, type SlackHandle, type SlackInboundEventContext, type SlackInboundMessageContext, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInitialMessage, type SlackInputResponseContext, type SlackInputResponseResult, type SlackInputResponseSubmission, type SlackInstrumentationMetadata, type SlackInteractionAction, type SlackInteractionContext, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveTarget, type SlackSessionTarget, type SlackRespondOptions, type SlackSendOptions, type SlackSessionOperations, type SlackThread, type SlackWebhookVerifier, type SlackWorkspaceHandle, } from "#public/channels/slack/slackChannel.js";
9
9
  export type { SlackAttachment, SlackAuthor, SlackEvent, SlackEventEnvelope, SlackInboundContext, SlackMessage, } from "#public/channels/slack/inbound.js";
10
- export { callSlackApi, resolveSlackBotToken, slackContinuationToken, type SlackPostInput, type SlackPostedMessage, type SlackThreadMessage, type SlackUploadFilesOptions, type SlackUploadFilesResult, } from "#public/channels/slack/api.js";
10
+ export { callSlackApi, resolveSlackBotToken, slackContinuationToken, type SlackPostInput, type SlackPostedMessage, type SlackBotTokenContext, type SlackThreadMessage, type SlackUploadFilesOptions, type SlackUploadFilesResult, } from "#public/channels/slack/api.js";
11
11
  export { defaultSlackAuth } from "#public/channels/slack/defaults.js";
12
12
  export { describeActionRequest, describeActionRequests, } from "#public/channels/slack/action-status.js";
13
13
  export { loadThreadContextMessages, type LoadThreadContextMessagesOptions, type ThreadContextSince, } from "#public/channels/slack/thread.js";
@@ -9,6 +9,7 @@ export declare function authorizeInputResponse(input: {
9
9
  readonly config: SlackChannelConfig;
10
10
  readonly onInputResponse: NonNullable<SlackChannelConfig["onInputResponse"]>;
11
11
  };
12
+ readonly installationTeamId: string | null | undefined;
12
13
  readonly submission: SlackInputResponseSubmission;
13
14
  readonly teamId: string | null | undefined;
14
15
  readonly threadTs: string;
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{buildSlackBinding}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{deriveHitlResponse}from"#public/channels/slack/hitl.js";const log=createLogger(`slack.interactions`);function approvalResponderStatePatch(e,t){if(!(t?.principalId===void 0||!e.actions.some(e=>deriveHitlResponse(e)?.kind===`tool-approval`)))return{approvalResponderUsers:{[t.principalId]:e.user.id}}}async function authorizeInputResponse(e){let n=buildSlackAuthContext({channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,userId:e.submission.user.id,userName:e.submission.user.username??e.submission.user.name}),{thread:r,slack:i}=buildSlackBinding({botToken:e.deps.config.credentials?.botToken,channelId:e.channelId,threadTs:e.threadTs,teamId:e.teamId??void 0}),a={defaultAuth:n,slack:i,thread:r};try{return await e.deps.onInputResponse(a,e.submission)}catch(e){return log.error(`HITL input response authorization failed`,{error:e}),null}}export{approvalResponderStatePatch,authorizeInputResponse};
1
+ import{createLogger}from"#internal/logging.js";import{buildSlackBinding}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{deriveHitlResponse}from"#public/channels/slack/hitl.js";const log=createLogger(`slack.interactions`);function approvalResponderStatePatch(e,t){if(!(t?.principalId===void 0||!e.actions.some(e=>deriveHitlResponse(e)?.kind===`tool-approval`)))return{approvalResponderUsers:{[t.principalId]:e.user.id}}}async function authorizeInputResponse(e){let n=buildSlackAuthContext({channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,userId:e.submission.user.id,userName:e.submission.user.username??e.submission.user.name}),{thread:r,slack:i}=buildSlackBinding({botToken:e.deps.config.credentials?.botToken,channelId:e.channelId,threadTs:e.threadTs,installationTeamId:e.installationTeamId??void 0,teamId:e.teamId??void 0}),a={defaultAuth:n,slack:i,thread:r};try{return await e.deps.onInputResponse(a,e.submission)}catch(e){return log.error(`HITL input response authorization failed`,{error:e}),null}}export{approvalResponderStatePatch,authorizeInputResponse};
@@ -0,0 +1,10 @@
1
+ import type { InteractionHandlerDeps, ParsedBlockActionsPayload } from "#public/channels/slack/interactions.js";
2
+ export declare function updateAnsweredHitlCard(interaction: ParsedBlockActionsPayload, deps: InteractionHandlerDeps): Promise<void>;
3
+ export declare function updateAnsweredFreeformCard(input: {
4
+ readonly channelId: string;
5
+ readonly messageTs: string;
6
+ readonly answerLabel: string;
7
+ readonly userId?: string;
8
+ readonly installationTeamId?: string;
9
+ readonly deps: InteractionHandlerDeps;
10
+ }): Promise<void>;
@@ -0,0 +1 @@
1
+ import{SLACK_CARD_SUBTEXT_MAX_LENGTH,truncateCardSubtext}from"#public/channels/slack/limits.js";import{resolveSlackBotToken}from"#public/channels/slack/api.js";import{buildAnsweredBlocks,isHitlAction}from"#public/channels/slack/hitl.js";function buildAnsweredHitlMessageBlocks(e){let t=e.messageBlocks.findIndex(t=>blockContainsActionId(t,e.actionId));if(t===-1)return buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:e.answerLabel,userId:e.userId});let n=e.messageBlocks[t],i=answeredBlocksFromActionBlock({answerLabel:e.answerLabel,block:n,userId:e.userId})??buildAnsweredBlocks({promptBlocks:promptBlocksFromActionBlock(n),answerLabel:e.answerLabel,userId:e.userId});return[...e.messageBlocks.slice(0,t),...i,...e.messageBlocks.slice(t+1)]}function findPromptBlocks(e){let t=[];for(let n of e){if(!isObjectRecord(n))continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function blockContainsActionId(e,t){return isObjectRecord(e)?actionsContainActionId(e.elements,t)||actionsContainActionId(e.actions,t):!1}function actionsContainActionId(e,t){return Array.isArray(e)?e.some(e=>isObjectRecord(e)&&e.action_id===t):!1}function answeredBlocksFromActionBlock(e){if(!isObjectRecord(e.block)||e.block.type!==`card`)return;let{actions:t,subtext:n,...r}=e.block,i={...r,subtext:{type:`mrkdwn`,text:formatAnsweredCardSubtext(e),verbatim:!1}};return hasCardContent(i)?[i]:void 0}function formatAnsweredCardSubtext(n){let r=n.userId.length>0?` by <@${n.userId}>`:``,i=SLACK_CARD_SUBTEXT_MAX_LENGTH-20-1-r.length;return truncateCardSubtext(`:white_check_mark: *${truncateWithEllipsis(n.answerLabel,i)}*${r}`)}function truncateWithEllipsis(e,t){if(t<=0)return``;if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function promptBlocksFromActionBlock(e){if(!isObjectRecord(e)||e.type!==`card`)return[];let{actions:t,...n}=e;return hasCardContent(n)?[n]:[]}function hasCardContent(e){return e.body!==void 0||e.title!==void 0||e.hero_image!==void 0}function isObjectRecord(e){return typeof e==`object`&&!!e}async function updateAnsweredHitlCard(e,t){let n=e.actions.find(e=>isHitlAction(e.actionId));if(!n?.messageTs)return;let r=n.label??n.selectedOptionValue??n.value;r&&await updateAnsweredCard({answerLabel:r,blocks:buildAnsweredHitlMessageBlocks({actionId:n.actionId,answerLabel:r,messageBlocks:e.messageBlocks,userId:n.user.id}),channelId:e.channelId,deps:t,installationTeamId:e.installationTeamId,messageTs:n.messageTs})}async function updateAnsweredFreeformCard(e){await updateAnsweredCard({...e,blocks:buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId})})}async function updateAnsweredCard(e){let t=await resolveSlackBotToken(e.deps.config.credentials?.botToken,{teamId:e.installationTeamId}),r=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${t}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:e.messageTs,blocks:e.blocks,text:`Answered: ${e.answerLabel}`})});if(!r.ok)throw Error(`Slack chat.update returned HTTP ${r.status}`)}export{updateAnsweredFreeformCard,updateAnsweredHitlCard};
@@ -13,6 +13,7 @@ import type { ChannelFrom, ChannelResolveSession } from "#channel/channel-operat
13
13
  interface ParsedBlockActionsPayload {
14
14
  readonly actions: SlackInteractionAction[];
15
15
  readonly channelId: string;
16
+ readonly installationTeamId: string | undefined;
16
17
  readonly threadTs: string;
17
18
  readonly teamId: string | undefined;
18
19
  /**
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{parseInputResponse}from"#runtime/input/types.js";import{SLACK_CARD_SUBTEXT_MAX_LENGTH,truncateCardSubtext}from"#public/channels/slack/limits.js";import{buildSlackBinding,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,freeformRequestIdFromActionId,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{approvalResponderStatePatch,authorizeInputResponse}from"#public/channels/slack/input-response.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=o?.id??s.team_id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,threadTs:e.threadTs,teamId:e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlock(e){return findPromptBlocks(e)[0]}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlock(e)?.text?.text;return typeof t==`string`&&t.length>0?t:void 0}function buildAnsweredHitlMessageBlocks(e){let t=findActionBlockIndex(e.messageBlocks,e.actionId);if(t===-1)return buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:e.answerLabel,userId:e.userId});let n=e.messageBlocks[t],r=answeredBlocksFromActionBlock({answerLabel:e.answerLabel,block:n,userId:e.userId})??buildAnsweredBlocks({promptBlocks:promptBlocksFromActionBlock(n),answerLabel:e.answerLabel,userId:e.userId});return[...e.messageBlocks.slice(0,t),...r,...e.messageBlocks.slice(t+1)]}function findActionBlockIndex(e,t){return e.findIndex(e=>blockContainsActionId(e,t))}function blockContainsActionId(e,t){return isObjectRecord(e)?actionsContainActionId(e.elements,t)||actionsContainActionId(e.actions,t):!1}function actionsContainActionId(e,t){return Array.isArray(e)?e.some(e=>isObjectRecord(e)&&e.action_id===t):!1}function isObjectRecord(e){return typeof e==`object`&&!!e}function answeredBlocksFromActionBlock(e){if(!isObjectRecord(e.block)||e.block.type!==`card`)return;let{actions:t,subtext:n,...r}=e.block,i={...r,subtext:{type:`mrkdwn`,text:formatAnsweredCardSubtext(e),verbatim:!1}};return hasCardContent(i)?[i]:void 0}function formatAnsweredCardSubtext(e){let t=e.userId.length>0?` by <@${e.userId}>`:``,i=SLACK_CARD_SUBTEXT_MAX_LENGTH-20-1-t.length;return truncateCardSubtext(`:white_check_mark: *${truncateWithEllipsis(e.answerLabel,i)}*${t}`)}function truncateWithEllipsis(e,t){if(t<=0)return``;if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function promptBlocksFromActionBlock(e){if(!isObjectRecord(e)||e.type!==`card`)return[];let{actions:t,...n}=e;return hasCardContent(n)?[n]:[]}function hasCardContent(e){return e.body!==void 0||e.title!==void 0||e.hero_image!==void 0}async function handleInteractionPost(e,t,n){let r=new Response(`ok`,{status:200}),a;try{a=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),r}if(a.kind===`view_submission`)return handleViewSubmission(a,t,n);if(a.kind!==`block_actions`)return r;let c=parseBlockActionsPayload(a);if(!c)return r;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:a.raw,interaction:c,freeformAction:l,deps:n}),r;let u=slackContinuationToken(c.channelId,c.threadTs),d=c.actions.flatMap(e=>{let t=deriveHitlResponse(e);return t===null?[]:[{action:e,derived:t}]});if(d.length>0){let e=d[0].action.user;t.waitUntil(dispatchBlockInputResponses({ctx:t,deps:n,interaction:c,submission:{type:`block_actions`,actions:d.map(({action:e})=>e),inputResponses:d.map(({derived:e})=>e.response),messageTs:d[0].action.messageTs,user:e}}))}let f=n.config.onInteraction;if(f){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let r=e[0].user,{thread:a,slack:o}=buildSlackBinding({botToken:n.config.credentials?.botToken,channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId}),l={...bindSlackSessionOperations({address:u,defaultAuth:buildSlackAuthContext({channelId:c.channelId,teamId:c.teamId,threadTs:c.threadTs,userId:r.id,userName:r.username??r.name}),from:t.from,resolveSession:t.resolveSession,state:{channelId:c.channelId,teamId:c.teamId??null,threadTs:c.threadTs,triggeringUserId:r.id}}),thread:a,slack:o};for(let n of e)t.waitUntil(Promise.resolve(f(n,l)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return r}async function dispatchBlockInputResponses(e){let t=await authorizeInputResponse({channelId:e.interaction.channelId,deps:e.deps,submission:e.submission,teamId:e.interaction.teamId,threadTs:e.interaction.threadTs});if(t!==null){try{await e.ctx.from(slackContinuationToken(e.interaction.channelId,e.interaction.threadTs)).respond(e.submission.inputResponses,{auth:t.auth,state:approvalResponderStatePatch(e.submission,t.auth)})}catch(e){log.error(`HITL interaction delivery failed`,{error:e});return}if(!e.submission.actions.some(e=>deriveHitlResponse(e)?.kind===`tool-approval`))try{await updateAnsweredHitlCard(e.interaction,e.deps)}catch(e){log.error(`HITL answered-card update failed`,{error:e})}}}async function openFreeformModal(e){let t=e.payload.trigger_id;if(typeof t!=`string`||t.length===0){log.warn(`freeform button click missing trigger_id — cannot open modal`);return}let n=freeformRequestIdFromActionId(e.freeformAction.actionId)??e.freeformAction.value;if(!n){log.warn(`freeform button click missing requestId`);return}let r=e.freeformAction.messageTs;if(!r){log.warn(`freeform button click missing messageTs`);return}let i=buildFreeformModalView({metadata:{continuationToken:slackContinuationToken(e.interaction.channelId,e.interaction.threadTs),channelId:e.interaction.channelId,threadTs:e.interaction.threadTs,messageTs:r,requestId:n},prompt:readPromptTextFromBlocks(e.interaction.messageBlocks)}),s=await resolveSlackBotToken(e.deps.config.credentials?.botToken),c=await fetch(`https://slack.com/api/views.open`,{method:`POST`,headers:{authorization:`Bearer ${s}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({trigger_id:t,view:i})});c.ok||log.error(`Slack views.open returned non-2xx`,{status:c.status})}async function handleViewSubmission(e,n,r){let i=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return i;let a;try{a=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),i}if(!a.continuationToken||!a.requestId||!a.messageTs||!a.channelId||!a.threadTs)return i;let o=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(o.length===0)return i;let s=e.user,d=e.userId,f=s?.teamId??e.teamId??null,p={type:`view_submission`,inputResponses:[parseInputResponse({requestId:a.requestId,text:o})],messageTs:a.messageTs,user:{id:d,username:s?.username,name:s?.name}};return n.waitUntil(dispatchViewInputResponse({ctx:n,deps:r,metadata:a,submission:p,teamId:f,text:o})),i}async function dispatchViewInputResponse(e){let t=await authorizeInputResponse({channelId:e.metadata.channelId,deps:e.deps,submission:e.submission,teamId:e.teamId,threadTs:e.metadata.threadTs});if(t!==null){try{await e.ctx.from(e.metadata.continuationToken).respond(e.submission.inputResponses,{auth:t.auth})}catch(e){log.error(`freeform answer delivery failed`,{error:e});return}try{await updateAnsweredFreeformCard({channelId:e.metadata.channelId,messageTs:e.metadata.messageTs,answerLabel:e.text,userId:e.submission.user.id,deps:e.deps})}catch(e){log.error(`freeform answered-card update failed`,{error:e})}}}async function updateAnsweredHitlCard(e,t){let n=e.actions.find(e=>isHitlAction(e.actionId));if(!n||!n.messageTs)return;let r=n.label??n.selectedOptionValue??n.value;if(!r)return;let i=buildAnsweredHitlMessageBlocks({actionId:n.actionId,answerLabel:r,messageBlocks:e.messageBlocks,userId:n.user.id}),o=await resolveSlackBotToken(t.config.credentials?.botToken),s=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${o}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:n.messageTs,blocks:i,text:`Answered: ${r}`})});if(!s.ok)throw Error(`Slack chat.update returned HTTP ${s.status}`)}async function updateAnsweredFreeformCard(e){let t=buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId}),n=await resolveSlackBotToken(e.deps.config.credentials?.botToken),r=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${n}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:e.messageTs,blocks:t,text:`Answered: ${e.answerLabel}`})});if(!r.ok)throw Error(`Slack chat.update returned HTTP ${r.status}`)}export{handleInteractionPost,parseBlockActionsPayload};
1
+ import{createLogger}from"#internal/logging.js";import{parseInputResponse}from"#runtime/input/types.js";import{readSlackTextObject}from"#public/channels/slack/inbound-content.js";import{buildSlackBinding,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildFreeformModalView,deriveHitlResponse,freeformRequestIdFromActionId,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{updateAnsweredFreeformCard,updateAnsweredHitlCard}from"#public/channels/slack/interaction-cards.js";import{approvalResponderStatePatch,authorizeInputResponse}from"#public/channels/slack/input-response.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=s.team_id??o?.id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,installationTeamId:readInstallationTeamId(t),threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,installationTeamId:readInstallationTeamId(e.raw),threadTs:e.threadTs,teamId:e.user?.teamId??e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlock(e){return findPromptBlocks(e)[0]}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=readSlackTextObject(findPromptBlock(e)?.text);return t.length>0?t:void 0}function isObjectRecord(e){return typeof e==`object`&&!!e}function readInstallationTeamId(e){if(!isObjectRecord(e))return;let t=isObjectRecord(e.view)?e.view:void 0,n=isObjectRecord(e.team)?e.team:void 0,r=isObjectRecord(e.user)?e.user:void 0;return[t?.app_installed_team_id,e.app_installed_team_id,n?.id,r?.team_id,t?.team_id].find(e=>typeof e==`string`&&e.length>0)}async function handleInteractionPost(e,t,n){let i=new Response(`ok`,{status:200}),s;try{s=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),i}if(s.kind===`view_submission`)return handleViewSubmission(s,t,n);if(s.kind!==`block_actions`)return i;let c=parseBlockActionsPayload(s);if(!c)return i;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:s.raw,interaction:c,freeformAction:l,deps:n}),i;let u=slackContinuationToken(c.channelId,c.threadTs),f=c.actions.flatMap(e=>{let t=deriveHitlResponse(e);return t===null?[]:[{action:e,derived:t}]});if(f.length>0){let e=f[0].action.user;t.waitUntil(dispatchBlockInputResponses({ctx:t,deps:n,interaction:c,submission:{type:`block_actions`,actions:f.map(({action:e})=>e),inputResponses:f.map(({derived:e})=>e.response),messageTs:f[0].action.messageTs,user:e}}))}let h=n.config.onInteraction;if(h){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let i=e[0].user,{thread:a,slack:s}=buildSlackBinding({botToken:n.config.credentials?.botToken,channelId:c.channelId,threadTs:c.threadTs,installationTeamId:c.installationTeamId,teamId:c.teamId}),l={...bindSlackSessionOperations({address:u,defaultAuth:buildSlackAuthContext({channelId:c.channelId,teamId:c.teamId,threadTs:c.threadTs,userId:i.id,userName:i.username??i.name}),from:t.from,resolveSession:t.resolveSession,state:{channelId:c.channelId,installationTeamId:c.installationTeamId??null,teamId:c.teamId??null,threadTs:c.threadTs,triggeringUserId:i.id}}),thread:a,slack:s};for(let n of e)t.waitUntil(Promise.resolve(h(n,l)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return i}async function dispatchBlockInputResponses(e){let t=await authorizeInputResponse({channelId:e.interaction.channelId,deps:e.deps,installationTeamId:e.interaction.installationTeamId,submission:e.submission,teamId:e.interaction.teamId,threadTs:e.interaction.threadTs});if(t!==null){try{await e.ctx.from(slackContinuationToken(e.interaction.channelId,e.interaction.threadTs)).respond(e.submission.inputResponses,{auth:t.auth,state:approvalResponderStatePatch(e.submission,t.auth)})}catch(e){log.error(`HITL interaction delivery failed`,{error:e});return}if(!e.submission.actions.some(e=>deriveHitlResponse(e)?.kind===`tool-approval`))try{await updateAnsweredHitlCard(e.interaction,e.deps)}catch(e){log.error(`HITL answered-card update failed`,{error:e})}}}async function openFreeformModal(e){let t=e.payload.trigger_id;if(typeof t!=`string`||t.length===0){log.warn(`freeform button click missing trigger_id — cannot open modal`);return}let n=freeformRequestIdFromActionId(e.freeformAction.actionId)??e.freeformAction.value;if(!n){log.warn(`freeform button click missing requestId`);return}let r=e.freeformAction.messageTs;if(!r){log.warn(`freeform button click missing messageTs`);return}let o=buildFreeformModalView({metadata:{continuationToken:slackContinuationToken(e.interaction.channelId,e.interaction.threadTs),channelId:e.interaction.channelId,installationTeamId:e.interaction.installationTeamId,threadTs:e.interaction.threadTs,messageTs:r,requestId:n},prompt:readPromptTextFromBlocks(e.interaction.messageBlocks)}),s=await resolveSlackBotToken(e.deps.config.credentials?.botToken,{teamId:e.interaction.installationTeamId}),c=await fetch(`https://slack.com/api/views.open`,{method:`POST`,headers:{authorization:`Bearer ${s}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({trigger_id:t,view:o})});c.ok||log.error(`Slack views.open returned non-2xx`,{status:c.status})}async function handleViewSubmission(e,n,r){let i=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return i;let a;try{a=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),i}if(!a.continuationToken||!a.requestId||!a.messageTs||!a.channelId||!a.threadTs)return i;let o=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(o.length===0)return i;let u=e.user,d=e.userId,f=u?.teamId??e.teamId??null,p=a.installationTeamId??readInstallationTeamId(e.raw),m={type:`view_submission`,inputResponses:[parseInputResponse({requestId:a.requestId,text:o})],messageTs:a.messageTs,user:{id:d,username:u?.username,name:u?.name}};return n.waitUntil(dispatchViewInputResponse({ctx:n,deps:r,installationTeamId:p,metadata:a,submission:m,teamId:f,text:o})),i}async function dispatchViewInputResponse(e){let t=await authorizeInputResponse({channelId:e.metadata.channelId,deps:e.deps,installationTeamId:e.installationTeamId,submission:e.submission,teamId:e.teamId,threadTs:e.metadata.threadTs});if(t!==null){try{await e.ctx.from(e.metadata.continuationToken).respond(e.submission.inputResponses,{auth:t.auth})}catch(e){log.error(`freeform answer delivery failed`,{error:e});return}try{await updateAnsweredFreeformCard({channelId:e.metadata.channelId,messageTs:e.metadata.messageTs,answerLabel:e.text,userId:e.submission.user.id,installationTeamId:e.installationTeamId,deps:e.deps})}catch(e){log.error(`freeform answered-card update failed`,{error:e})}}}export{handleInteractionPost,parseBlockActionsPayload};
@@ -105,6 +105,8 @@ export interface SlackChannelState {
105
105
  threadTs: string | null;
106
106
  /** Slack team id, when the inbound event carried one. */
107
107
  teamId: string | null;
108
+ /** Slack workspace whose app installation supplies this session's credentials. */
109
+ installationTeamId?: string | null;
108
110
  /**
109
111
  * Slack user id of the actor that triggered the current session/turn.
110
112
  * Captured on every inbound mention so default handlers (e.g.
@@ -160,7 +162,8 @@ export interface SlackInstrumentationMetadata extends Record<string, unknown> {
160
162
  */
161
163
  export interface SlackChannelCredentials {
162
164
  /**
163
- * Bot token for all outbound Slack Web API calls. Falls back to
165
+ * Bot token for all outbound Slack Web API calls. Function providers receive
166
+ * the app installation workspace id when Slack supplies one. Falls back to
164
167
  * `process.env.SLACK_BOT_TOKEN` when omitted.
165
168
  */
166
169
  readonly botToken?: SlackBotToken;
@@ -181,6 +184,8 @@ export interface SlackChannelCredentials {
181
184
  export interface SlackReceiveTarget {
182
185
  readonly channelId: string;
183
186
  readonly threadTs?: string;
187
+ /** Slack workspace whose app installation supplies credentials for this send. */
188
+ readonly installationTeamId?: string;
184
189
  /**
185
190
  * Optional message posted into the Slack channel before the agent runs.
186
191
  * The post becomes the thread root and the first turn is threaded under
@@ -1 +1 @@
1
- import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{defaultDeliverResult}from"#channel/adapter.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{buildSlackAuthContext,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{bindSlackSessionOperations}from"#public/channels/slack/session-operations.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{isPrivateSlackConversation}from"#public/channels/slack/privacy.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.continuation?.rekey(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}),s=e.onInputResponse??defaultOnInputResponse,c=e.events?.[`authorization.required`],l=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],u=e.events?.[`turn.started`]??defaultEvents[`turn.started`],m={...defaultEvents,...e.events,async"approval.candidate"(e,t,n){let r=t.state.approvalResponderUsers?.[e.responderPrincipalId];if(e.outcome===`pending`&&r!==void 0&&(t.state.pendingApprovalCandidateUsers={...t.state.pendingApprovalCandidateUsers,[e.candidateId]:r}),await l(e,t,n),e.outcome!==`pending`){let n={...t.state.pendingApprovalCandidateUsers};delete n[e.candidateId],t.state.pendingApprovalCandidateUsers=n}},async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await u(e,t,n)},"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":c===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(c)},h=new Set;return defineChannel({kindHint:`slack`,turnPolicy:e.turnPolicy,state:{audience:`unknown`,channelId:null,threadTs:null,teamId:null,triggeringUserId:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{},pendingApprovalCards:{},pendingApprovalCandidateUsers:{},approvalResponderUsers:{}},fetchFile:n,metadata(e){return{audience:e.audience??`unknown`,channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},deliver(e,t){let n=e.pendingApprovalCards;typeof n==`object`&&n&&(t.state.pendingApprovalCards={...t.state.pendingApprovalCards,...n});let i=e.state?.approvalResponderUsers;return typeof i==`object`&&i&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...i}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,r)=>{let{waitUntil:i}=r,{from:a,resolveSession:o}=r,c=await verifyInbound(n,e.credentials);return c===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(c,{from:a,resolveSession:o,waitUntil:i},{config:e,onInputResponse:s}):handleEventPost({body:c,from:a,resolveSession:o,waitUntil:i,config:e,uploadPolicy:t,handledEvents:h,headers:n.headers})})],receive(t,{from:n}){return receiveOnSlack(t,{from:n,credentials:e.credentials})},events:m})}function defaultOnInputResponse(e){return{auth:e.defaultAuth}}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 o=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),o=(await e.post(n)).id}let c=o||crypto.randomUUID();return t.from(slackContinuationToken(r,c)).send(e.message,{auth:e.auth,state:{channelId:r,threadTs:o||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null},title:e.title})}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,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,kind:e,message:i,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,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,kind:e,message:i,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,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:n.onMessage,kind:`channel_message`,message:e,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}))}let c=n.onEvent;if(dispatch===null&&c!==void 0&&(dispatch=()=>dispatchSlackEvent({from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,envelope:i,handler:c})),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||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=e.message.author,o={audience:`unknown`,channelId:e.message.channelId,teamId:e.message.teamId??null,threadTs:e.message.threadTs,triggeringUserId:a?.userId??null},c=bindSlackSessionOperations({address:t,defaultAuth:a===void 0?null:buildSlackAuthContext({channelId:e.message.channelId,fullName:a.fullName,isBot:a.isBot,teamId:e.message.teamId,threadTs:e.message.threadTs,userId:a.userId,userName:a.userName}),from:e.from,resolveSession:e.resolveSession,state:o}),d,isDMOrPrivateChannel=()=>d??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:i.request}),f={...c,isBotMentioned:()=>e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),isDMOrPrivateChannel,isSubscribed:async()=>await c.resolveSession()!==void 0,slack:i,thread:r},p;try{p=await e.handler(f,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(p==null)return;let m=await isDMOrPrivateChannel();o.audience=e.kind===`direct_message`||m?`private`:`public`,await deliverSlackMessage({credentials:e.credentials,kind:e.kind,isPrivateConversation:m,message:e.message,result:p,sessionOperations:c,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=[],sourceFor=t=>e.from(slackContinuationToken(t.channelId,t.threadTs)),a={cancel:({target:e,turnId:t})=>sourceFor(e).cancel({turnId:t}),clear:({target:e})=>sourceFor(e).clear(),compact:({target:e})=>sourceFor(e).compact(),envelope:e.envelope,reset:({reason:e,target:t})=>sourceFor(t).reset({reason:e}),resolveSession:({target:t})=>e.resolveSession(slackContinuationToken(t.channelId,t.threadTs)),respond:(e,{auth:t,target:n})=>sourceFor(n).respond(e,{auth:t}),send:(t,{auth:n,target:i,title:a})=>receiveOnSlack({auth:n,message:t,target:i,title:a},{from:e.from,credentials:e.credentials,teamId:r,...typeof e.envelope.event.user==`string`?{triggeringUserId:e.envelope.event.user}:{}}),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=formatSlackInboundMessage({channelId:t.channelId,fullName:t.author?.fullName,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},t),o=buildSlackTurnMessage(n===void 0?a:`${n}\n\n${a}`,i),s=e.result.context??[],c=e.isPrivateConversation?`Private message`:e.result.title??t.markdown,l=s.length===0?{auth:e.result.auth,title:c}:{auth:e.result.auth,context:s,title:c};await e.sessionOperations.send(o,l)}catch(r){logError(log,`${e.kind} delivery failed`,r,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
1
+ import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{defaultDeliverResult}from"#channel/adapter.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{buildSlackAuthContext,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{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{isPrivateSlackConversation}from"#public/channels/slack/privacy.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??``,installationTeamId:e.installationTeamId??void 0,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.continuation?.rekey(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}),s=e.onInputResponse??defaultOnInputResponse,c=e.events?.[`authorization.required`],l=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],u=e.events?.[`turn.started`]??defaultEvents[`turn.started`],m={...defaultEvents,...e.events,async"approval.candidate"(e,t,n){let r=t.state.approvalResponderUsers?.[e.responderPrincipalId];if(e.outcome===`pending`&&r!==void 0&&(t.state.pendingApprovalCandidateUsers={...t.state.pendingApprovalCandidateUsers,[e.candidateId]:r}),await l(e,t,n),e.outcome!==`pending`){let n={...t.state.pendingApprovalCandidateUsers};delete n[e.candidateId],t.state.pendingApprovalCandidateUsers=n}},async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await u(e,t,n)},"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":c===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(c)},h=new Set;return defineChannel({kindHint:`slack`,turnPolicy:e.turnPolicy,state:{audience:`unknown`,channelId:null,threadTs:null,teamId:null,installationTeamId:null,triggeringUserId:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{},pendingApprovalCards:{},pendingApprovalCandidateUsers:{},approvalResponderUsers:{}},fetchFile:n,metadata(e){return{audience:e.audience??`unknown`,channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},deliver(e,t){let n=e.pendingApprovalCards;typeof n==`object`&&n&&(t.state.pendingApprovalCards={...t.state.pendingApprovalCards,...n});let i=e.state?.approvalResponderUsers;return typeof i==`object`&&i&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...i}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,r)=>{let{waitUntil:i}=r,{from:a,resolveSession:o}=r,c=await verifyInbound(n,e.credentials);return c===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(c,{from:a,resolveSession:o,waitUntil:i},{config:e,onInputResponse:s}):handleEventPost({body:c,from:a,resolveSession:o,waitUntil:i,config:e,uploadPolicy:t,handledEvents:h,headers:n.headers})})],receive(t,{from:n}){return receiveOnSlack(t,{from:n,credentials:e.credentials})},events:m})}function defaultOnInputResponse(e){return{auth:e.defaultAuth}}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=typeof n.installationTeamId==`string`?n.installationTeamId:t.installationTeamId,o=n.initialMessage;if(o&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let c=i;if(o){let{thread:e}=buildSlackBinding({botToken:t.credentials?.botToken,channelId:r,threadTs:``,installationTeamId:a,teamId:t.teamId}),n={card:o.card};o.fallbackText!==void 0&&(n.fallbackText=o.fallbackText),c=(await e.post(n)).id}let u=c||crypto.randomUUID();return t.from(slackContinuationToken(r,u)).send(e.message,{auth:e.auth,state:{channelId:r,installationTeamId:a??null,threadTs:c||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null},title:e.title})}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),s=slackEventInstallationTeamId(i),dispatch=null,c=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,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,installationTeamId:s,kind:e,message:i,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}),r=(e===`app_mention`?n.onAppMention:n.onDirectMessage)??n.onMessage;r===void 0?c=dispatchMessageWith(e===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,installationTeamId:s,kind:e,message:i,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,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:n.onMessage,installationTeamId:s,kind:`channel_message`,message:e,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}))}let l=n.onEvent;if(dispatch===null&&l!==void 0&&(dispatch=()=>dispatchSlackEvent({from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,envelope:i,handler:l,installationTeamId:s})),dispatch??=c,dispatch===null)return new Response(`ok`);let u=i.event_id;if(u){if(t.handledEvents.has(u))return log.warn(`received a duplicate event`,{event_id:u,event_time:i.event_time,retry_num:r.retry?.num??`(null)`,retry_reason:r.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(u,t.handledEvents)}return t.waitUntil(dispatch()),new Response(`ok`)}function isSelfAuthoredSlackMessage(e,t){return e.botUserId!==void 0&&t.author?.userId===e.botUserId||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,installationTeamId:e.installationTeamId,teamId:e.message.teamId}),a=e.message.author,o={audience:`unknown`,channelId:e.message.channelId,installationTeamId:e.installationTeamId??null,teamId:e.message.teamId??null,threadTs:e.message.threadTs,triggeringUserId:a?.userId??null},c=bindSlackSessionOperations({address:t,defaultAuth:a===void 0?null:buildSlackAuthContext({channelId:e.message.channelId,fullName:a.fullName,isBot:a.isBot,teamId:e.message.teamId,threadTs:e.message.threadTs,userId:a.userId,userName:a.userName}),from:e.from,resolveSession:e.resolveSession,state:o}),d,isDMOrPrivateChannel=()=>d??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:i.request}),f={...c,isBotMentioned:()=>e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),isDMOrPrivateChannel,isSubscribed:async()=>await c.resolveSession()!==void 0,slack:i,thread:r},p;try{p=await e.handler(f,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(p==null)return;let m=await isDMOrPrivateChannel();o.audience=e.kind===`direct_message`||m?`private`:`public`,await deliverSlackMessage({credentials:e.credentials,kind:e.kind,isPrivateConversation:m,message:e.message,result:p,sessionOperations:c,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=[],sourceFor=t=>e.from(slackContinuationToken(t.channelId,t.threadTs)),a={cancel:({target:e,turnId:t})=>sourceFor(e).cancel({turnId:t}),clear:({target:e})=>sourceFor(e).clear(),compact:({target:e})=>sourceFor(e).compact(),envelope:e.envelope,reset:({reason:e,target:t})=>sourceFor(t).reset({reason:e}),resolveSession:({target:t})=>e.resolveSession(slackContinuationToken(t.channelId,t.threadTs)),respond:(e,{auth:t,target:n})=>sourceFor(n).respond(e,{auth:t}),send:(t,{auth:n,target:i,title:a})=>receiveOnSlack({auth:n,message:t,target:i,title:a},{from:e.from,credentials:e.credentials,installationTeamId:e.installationTeamId,teamId:r,...typeof e.envelope.event.user==`string`?{triggeringUserId:e.envelope.event.user}:{}}),slack:buildSlackWorkspaceHandle({botToken:e.credentials?.botToken,installationTeamId:e.installationTeamId,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=formatSlackInboundMessage({channelId:t.channelId,fullName:t.author?.fullName,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},t),o=buildSlackTurnMessage(n===void 0?a:`${n}\n\n${a}`,i),s=e.result.context??[],c=e.isPrivateConversation?`Private message`:e.result.title??t.markdown,l=s.length===0?{auth:e.result.auth,title:c}:{auth:e.result.auth,context:s,title:c};await e.sessionOperations.send(o,l)}catch(r){logError(log,`${e.kind} delivery failed`,r,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
@@ -1,4 +1,4 @@
1
- import type { IFileSystem } from "just-bash";
1
+ import type { CustomCommand, IFileSystem } from "just-bash";
2
2
  /**
3
3
  * Context passed to a custom just-bash filesystem factory for each live handle.
4
4
  */
@@ -26,6 +26,17 @@ export interface JustBashSandboxCreateOptions {
26
26
  * actionable install error instead. Defaults to `true`.
27
27
  */
28
28
  readonly autoInstall?: boolean;
29
+ /**
30
+ * Registers trusted host application code as commands in each live
31
+ * just-bash interpreter. Custom commands can participate in normal shell
32
+ * composition, including pipelines and redirections.
33
+ *
34
+ * Custom commands are not available during template prewarming. They run in
35
+ * eve's host process, outside the virtual filesystem security boundary, and
36
+ * are responsible for validating their own inputs and cleaning up host
37
+ * resources.
38
+ */
39
+ readonly customCommands?: ReadonlyArray<CustomCommand>;
29
40
  /**
30
41
  * Composes the filesystem used by each live sandbox handle. The factory is
31
42
  * called when a handle opens with eve's durable default filesystem; return a
@@ -27,10 +27,16 @@ export declare class RegistryFlowFailedError extends Error {
27
27
  kind: "done";
28
28
  }>);
29
29
  }
30
- /** Runs the categorized interactive registry catalog used by the dev TUI's `/add`. */
30
+ /**
31
+ * Runs the categorized interactive registry catalog used by the dev TUI's
32
+ * `/add`. `initialAddress` — `/add channel/slack` — skips the category and
33
+ * search screens and opens that item's confirmation directly; backing out of it
34
+ * lands on the same category hub the browser starts from.
35
+ */
31
36
  export declare function runRegistryFlow(input: {
32
37
  appRoot: string;
33
38
  prompter: Prompter;
34
39
  signal?: AbortSignal;
40
+ initialAddress?: string;
35
41
  deps?: Partial<RegistryFlowDeps>;
36
42
  }): Promise<RegistryFlowResult>;
@@ -1 +1 @@
1
- import{createRegistrySession}from"./registry-session.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{WizardCancelledError}from"#setup/step.js";import{detectDeployment}from"#setup/project-resolution.js";import{withSpinner}from"#setup/with-spinner.js";const ADDRESS_PREFIX=`address:`,BACK=`action:back`,DONE=`action:done`,ALL=`category:all`,REGISTRY_CATEGORIES=[{value:`category:channel`,prefix:`channel/`,label:`Channels`,hint:`Where people talk to your agent — Web, Slack, Discord, Teams`,browseLabel:`Browse channels`},{value:`category:connection`,prefix:`connection/`,label:`MCP connections`,hint:`Connect services like Linear, Notion, GitHub, and Vercel`,browseLabel:`Browse MCP connections`},{value:`category:extension`,prefix:`extension/`,label:`Extensions`,hint:`Add browser automation, memory, and developer tools`,browseLabel:`Browse extensions`},{value:`category:instrumentation`,prefix:`instrumentation/`,label:`Observability`,hint:`Trace, evaluate, and monitor your agent`,browseLabel:`Browse observability integrations`}];var RegistryFlowFailedError=class extends Error{completed;constructor(e,t){super(e instanceof Error?e.message:String(e),{cause:e}),this.name=`RegistryFlowFailedError`,this.completed=t}};function itemLabel(e){return e.title===void 0?(e.name.split(`/`).at(-1)??e.name).split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `):e.title}function itemRows(e){return e.map((e,t)=>({value:`item:${t}`,label:itemLabel(e),hint:e.description??e.type}))}function categoryRows(){return[...REGISTRY_CATEGORIES.map(e=>({value:e.value,label:e.label,hint:e.hint})),{value:ALL,label:`Browse all`,hint:`Search every integration or enter an item address`},{value:DONE,label:`Return to chat`,trailingAction:!0}]}function categoryFor(e){return REGISTRY_CATEGORIES.find(t=>t.value===e)}function itemsForCategory(e,t){if(t===ALL)return e;let n=categoryFor(t);return n===void 0?e:e.filter(e=>e.address.startsWith(n.prefix)||e.name.startsWith(n.prefix))}function stringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function itemSource(e){if(e.startsWith(`@`))return e.split(`/`)[0]??e;if(/^https?:\/\//u.test(e))try{return new URL(e).host}catch{return e}return`Vercel`}function manifestRecord(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:{}}function summarizeDetails(e,t=3){let n=e.slice(0,t),r=e.length-n.length;return r>0?`${n.join(`, `)} … (+${r} more)`:n.join(`, `)}function itemDetails(e,t){let n=[{label:`Source`,value:e.source}],r=[...stringArray(t.dependencies),...stringArray(t.devDependencies),...stringArray(t.registryDependencies)];r.length>0&&n.push({label:`Packages`,value:summarizeDetails(r)});let i=t.envVars;if(typeof i==`object`&&i&&!Array.isArray(i)){let e=Object.keys(i);e.length>0&&n.push({label:`Environment`,value:summarizeDetails(e)})}let a=(Array.isArray(t.files)?t.files:[]).flatMap(e=>{if(typeof e!=`object`||!e||Array.isArray(e))return[];let t=e.target;return typeof t==`string`?[t]:[]});return a.length>0&&n.push({label:`Files`,value:summarizeDetails(a)}),n}async function inspectItem(e,t,n,r,a,o){let s=a??manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r.address)));for(;;){let i={message:typeof s.title==`string`?s.title:r.name,metadata:itemDetails(r,s),options:[{value:`add`,label:`Add to project`},{value:`back`,label:`Back`}]},a=typeof s.description==`string`?s.description:r.description;if(a!==void 0&&(i.description=a),await e.select(i)===`back`)return{kind:`back`};let c=e.log.spinner?.(`Installing ${itemLabel(r)} and dependencies…`);try{c?.stop();let install=()=>t.installRegistryItem(n,r.address,{silent:!0,prompter:e,signal:o});return{kind:`added`,...await(e.withExclusiveTerminal?.(install)??install())}}finally{c?.stop()}}}async function resolveAddressItem(e,t,n,r){let a=manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r))),o={address:r,name:typeof a.name==`string`?a.name:r,source:itemSource(r)};return typeof a.type==`string`&&(o.type=a.type),typeof a.description==`string`&&(o.description=a.description),{item:o,manifest:a}}async function runRegistryFlow(c){let l;(c.deps?.browseRegistryCatalog===void 0||c.deps.getRegistryItemManifest===void 0||c.deps.installRegistryItem===void 0)&&(l=await import(`#cli/commands/registry.js`));let u={browseRegistryCatalog:c.deps?.browseRegistryCatalog??l.browseRegistryCatalog,detectDeployment:c.deps?.detectDeployment??detectDeployment,getRegistryItemManifest:c.deps?.getRegistryItemManifest??l.getRegistryItemManifest,installRegistryItem:c.deps?.installRegistryItem??l.installRegistryItem,runDeployFlow:c.deps?.runDeployFlow??runDeployFlow},d=[],f=createRegistrySession(u);try{for(;;){c.signal?.throwIfAborted();let e=await withSpinner(c.prompter,`Loading registry…`,()=>u.browseRegistryCatalog(c.appRoot));d=[...d,...e.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`}))];let t=await c.prompter.select({message:`Add an integration`,options:categoryRows(),hintLayout:`inline`,notices:d});if(d=[],t===DONE)return f.result();let n=itemsForCategory(e.items,t),r=itemRows(n);r.push({value:BACK,label:`Back`,trailingAction:!0});let l=await c.prompter.select({message:categoryFor(t)?.browseLabel??`Browse integrations`,options:r,search:!0,placeholder:`Search integrations or enter an item address`,searchAction:{label:e=>`Add “${e}”`,value:e=>`${ADDRESS_PREFIX}${e.trim()}`},hintLayout:`inline`});if(l===BACK)continue;let p=l.startsWith(ADDRESS_PREFIX)?await resolveAddressItem(c.prompter,u,c.appRoot,l.slice(8)):{item:n[Number(l.slice(5))]};if(p.item===void 0)throw Error(`The selected registry item is no longer available.`);let m=await inspectItem(c.prompter,u,c.appRoot,p.item,`manifest`in p?p.manifest:void 0,c.signal);if(m.kind!==`added`)continue;f.add(p.item.address,itemLabel(p.item),m.output,m.setup);let h=await f.continueAfterInstall({appRoot:c.appRoot,prompter:c.prompter,signal:c.signal});if(h!==`add-more`)return h}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};let t=f.result();throw t.addedItems.length>0?new RegistryFlowFailedError(e,t):e}}export{RegistryFlowFailedError,runRegistryFlow};
1
+ import{createRegistrySession}from"./registry-session.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{WizardCancelledError}from"#setup/step.js";import{detectDeployment}from"#setup/project-resolution.js";import{withSpinner}from"#setup/with-spinner.js";const ADDRESS_PREFIX=`address:`,BACK=`action:back`,DONE=`action:done`,ALL=`category:all`,REGISTRY_CATEGORIES=[{value:`category:channel`,prefix:`channel/`,label:`Channels`,hint:`Where people talk to your agent — Web, Slack, Discord, Teams`,browseLabel:`Browse channels`},{value:`category:connection`,prefix:`connection/`,label:`MCP connections`,hint:`Connect services like Linear, Notion, GitHub, and Vercel`,browseLabel:`Browse MCP connections`},{value:`category:extension`,prefix:`extension/`,label:`Extensions`,hint:`Add browser automation, memory, and developer tools`,browseLabel:`Browse extensions`},{value:`category:instrumentation`,prefix:`instrumentation/`,label:`Observability`,hint:`Trace, evaluate, and monitor your agent`,browseLabel:`Browse observability integrations`}];var RegistryFlowFailedError=class extends Error{completed;constructor(e,t){super(e instanceof Error?e.message:String(e),{cause:e}),this.name=`RegistryFlowFailedError`,this.completed=t}};function itemLabel(e){return e.title===void 0?(e.name.split(`/`).at(-1)??e.name).split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `):e.title}function itemRows(e){return e.map((e,t)=>({value:`item:${t}`,label:itemLabel(e),hint:e.description??e.type}))}function categoryRows(){return[...REGISTRY_CATEGORIES.map(e=>({value:e.value,label:e.label,hint:e.hint})),{value:ALL,label:`Browse all`,hint:`Search every integration or enter an item address`},{value:DONE,label:`Return to chat`,trailingAction:!0}]}function categoryFor(e){return REGISTRY_CATEGORIES.find(t=>t.value===e)}function itemsForCategory(e,t){if(t===ALL)return e;let n=categoryFor(t);return n===void 0?e:e.filter(e=>e.address.startsWith(n.prefix)||e.name.startsWith(n.prefix))}function stringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function itemSource(e){if(e.startsWith(`@`))return e.split(`/`)[0]??e;if(/^https?:\/\//u.test(e))try{return new URL(e).host}catch{return e}return`Vercel`}function manifestRecord(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:{}}function summarizeDetails(e,t=3){let n=e.slice(0,t),r=e.length-n.length;return r>0?`${n.join(`, `)} … (+${r} more)`:n.join(`, `)}function itemDetails(e,t){let n=[{label:`Source`,value:e.source}],r=[...stringArray(t.dependencies),...stringArray(t.devDependencies),...stringArray(t.registryDependencies)];r.length>0&&n.push({label:`Packages`,value:summarizeDetails(r)});let i=t.envVars;if(typeof i==`object`&&i&&!Array.isArray(i)){let e=Object.keys(i);e.length>0&&n.push({label:`Environment`,value:summarizeDetails(e)})}let a=(Array.isArray(t.files)?t.files:[]).flatMap(e=>{if(typeof e!=`object`||!e||Array.isArray(e))return[];let t=e.target;return typeof t==`string`?[t]:[]});return a.length>0&&n.push({label:`Files`,value:summarizeDetails(a)}),n}async function inspectItem(e,t,n,r,a,o){let s=a??manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r.address)));for(;;){let i={message:typeof s.title==`string`?s.title:r.name,metadata:itemDetails(r,s),options:[{value:`add`,label:`Add to project`},{value:`back`,label:`Back`}]},a=typeof s.description==`string`?s.description:r.description;if(a!==void 0&&(i.description=a),await e.select(i)===`back`)return{kind:`back`};let c=e.log.spinner?.(`Installing ${itemLabel(r)} and dependencies…`);try{c?.stop();let install=()=>t.installRegistryItem(n,r.address,{silent:!0,prompter:e,signal:o});return{kind:`added`,...await(e.withExclusiveTerminal?.(install)??install())}}finally{c?.stop()}}}async function resolveAddressItem(e,t,n,r){let a=manifestRecord(await withSpinner(e,`Loading registry item…`,()=>t.getRegistryItemManifest(n,r))),o={address:r,name:typeof a.name==`string`?a.name:r,source:itemSource(r)};return typeof a.type==`string`&&(o.type=a.type),typeof a.description==`string`&&(o.description=a.description),{item:o,manifest:a}}async function offerItem(e,t,n,r,i){let a=await inspectItem(e.prompter,t,e.appRoot,r,i,e.signal);if(a.kind!==`added`)return;n.add(r.address,itemLabel(r),a.output,a.setup);let o=await n.continueAfterInstall({appRoot:e.appRoot,prompter:e.prompter,signal:e.signal});return o===`add-more`?void 0:o}async function runRegistryFlow(c){let l;(c.deps?.browseRegistryCatalog===void 0||c.deps.getRegistryItemManifest===void 0||c.deps.installRegistryItem===void 0)&&(l=await import(`#cli/commands/registry.js`));let u={browseRegistryCatalog:c.deps?.browseRegistryCatalog??l.browseRegistryCatalog,detectDeployment:c.deps?.detectDeployment??detectDeployment,getRegistryItemManifest:c.deps?.getRegistryItemManifest??l.getRegistryItemManifest,installRegistryItem:c.deps?.installRegistryItem??l.installRegistryItem,runDeployFlow:c.deps?.runDeployFlow??runDeployFlow},d=[],f=createRegistrySession(u),p=c.initialAddress?.trim(),m=p===``?void 0:p;try{for(;;){if(c.signal?.throwIfAborted(),m!==void 0){let e=m;m=void 0;let t=await resolveAddressItem(c.prompter,u,c.appRoot,e),n=await offerItem(c,u,f,t.item,t.manifest);if(n!==void 0)return n;continue}let e=await withSpinner(c.prompter,`Loading registry…`,()=>u.browseRegistryCatalog(c.appRoot));d=[...d,...e.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`}))];let t=await c.prompter.select({message:`Add an integration`,options:categoryRows(),hintLayout:`inline`,notices:d});if(d=[],t===DONE)return f.result();let n=itemsForCategory(e.items,t),r=itemRows(n);r.push({value:BACK,label:`Back`,trailingAction:!0});let l=await c.prompter.select({message:categoryFor(t)?.browseLabel??`Browse integrations`,options:r,search:!0,placeholder:`Search integrations or enter an item address`,searchAction:{label:e=>`Add “${e}”`,value:e=>`${ADDRESS_PREFIX}${e.trim()}`},hintLayout:`inline`});if(l===BACK)continue;let p=l.startsWith(ADDRESS_PREFIX)?await resolveAddressItem(c.prompter,u,c.appRoot,l.slice(8)):{item:n[Number(l.slice(5))]};if(p.item===void 0)throw Error(`The selected registry item is no longer available.`);let h=await offerItem(c,u,f,p.item,`manifest`in p?p.manifest:void 0);if(h!==void 0)return h}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};let t=f.result();throw t.addedItems.length>0?new RegistryFlowFailedError(e,t):e}}export{RegistryFlowFailedError,runRegistryFlow};
@@ -1,4 +1,4 @@
1
- import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.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{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.58`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.3`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.44.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,t){return{"agent/agent.ts":renderAgentTemplate(e,t),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,n){let r=parseChatGptModelSelection(e);return r===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(r)}),\n${reasoningTemplateLine(n)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,n){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(n.model)!==void 0?renderAgentTemplate(n.model,n.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,n.appName).replaceAll(`__EVE_INIT_MODEL__`,n.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(n.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(n.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(n.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,n.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,n.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,n.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,n.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,n.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,n.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.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{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.58`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.3`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.44.4`,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,t){return{"agent/agent.ts":renderAgentTemplate(e,t),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,n){let r=parseChatGptModelSelection(e);return r===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(r)}),\n${reasoningTemplateLine(n)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,n){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(n.model)!==void 0?renderAgentTemplate(n.model,n.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,n.appName).replaceAll(`__EVE_INIT_MODEL__`,n.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(n.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(n.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(n.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,n.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,n.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,n.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,n.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,n.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,n.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__",
@@ -13,7 +13,9 @@ __EVE_INIT_REASONING__});
13
13
  },
14
14
  "scripts": {
15
15
  "build": "eve build",
16
+ "deploy": "eve deploy",
16
17
  "dev": "eve dev",
18
+ "eval": "eve eval",
17
19
  "start": "eve start",
18
20
  "typecheck": "tsc"
19
21
  },
@@ -35,7 +37,42 @@ __EVE_INIT_REASONING__});
35
37
  `,AGENT_INSTRUCTIONS_TEMPLATE=`# Identity
36
38
 
37
39
  You are a helpful assistant.
38
- `,SHARED_TEMPLATE_FILES={"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE,"tsconfig.json":`{
40
+ `,SHARED_TEMPLATE_FILES={"README.md":`# __EVE_INIT_APP_NAME__
41
+
42
+ This is an [eve](https://eve.dev) agent bootstrapped with [\`eve init\`](https://eve.dev/docs/reference/cli#eve-init).
43
+
44
+ ## Getting started
45
+
46
+ First, run the development server:
47
+
48
+ \`\`\`bash
49
+ eve dev
50
+ \`\`\`
51
+
52
+ The development TUI opens an interactive session where you can send messages to your agent.
53
+
54
+ Start by editing \`agent/instructions.md\` to define the agent's identity, purpose, tone, and response guidelines. Configure its model and runtime behavior in \`agent/agent.ts\`.
55
+
56
+ Add capabilities under \`agent/\`, including tools, connections, channels, skills, subagents, and schedules. eve reloads your changes as you work.
57
+
58
+ ## Learn more
59
+
60
+ To learn more about eve, explore these resources:
61
+
62
+ - [eve documentation](https://eve.dev/docs) — learn about eve's features and authoring APIs.
63
+ - [Build an Agent tutorial](https://eve.dev/docs/tutorial/first-agent) — build and deploy an agent step by step.
64
+ - [eve on GitHub](https://github.com/vercel/eve) — view the source and contribute.
65
+
66
+ ## Deploy on Vercel
67
+
68
+ Deploy your agent to [Vercel](https://vercel.com) from the project root:
69
+
70
+ \`\`\`bash
71
+ eve deploy
72
+ \`\`\`
73
+
74
+ \`eve deploy\` links a Vercel project if needed and deploys the agent to production. See the [eve deployment documentation](https://eve.dev/docs/guides/deployment/vercel) for authentication, environment variables, and deployment options.
75
+ `,"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE,"tsconfig.json":`{
39
76
  "compilerOptions": {
40
77
  "target": "ES2022",
41
78
  "module": "esnext",
@@ -20,7 +20,11 @@ export interface FetchFileResult {
20
20
  readonly mediaType?: string;
21
21
  readonly filename?: string;
22
22
  }
23
- export type FetchFileFunction = (url: string) => Promise<Buffer | FetchFileResult | null>;
23
+ /** Runtime context supplied while resolving a channel-owned file URL. */
24
+ export interface FetchFileContext {
25
+ readonly state: Readonly<Record<string, unknown>>;
26
+ }
27
+ export type FetchFileFunction = (url: string, context?: FetchFileContext) => Promise<Buffer | FetchFileResult | null>;
24
28
  /**
25
29
  * Input passed to a channel's `receive` callback when another channel or
26
30
  * schedule proactively routes a message to it.
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BsLJiIbc.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BtsOXv3s.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-BsLJiIbc.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-BtsOXv3s.js";
2
2
 
3
3
  export { useEveAgent };
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-DpUEwS23.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BJwaMe5k.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-DpUEwS23.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-BJwaMe5k.js";
2
2
 
3
3
  export { useEveAgent };