eve 0.48.0 → 0.49.1

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 (222) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/src/channel/channel-address.js +1 -1
  3. package/dist/src/channel/types.d.ts +10 -0
  4. package/dist/src/cli/commands/integration-setup.d.ts +1 -0
  5. package/dist/src/cli/commands/integration-setup.js +1 -1
  6. package/dist/src/cli/commands/register-integration-commands.d.ts +1 -1
  7. package/dist/src/cli/commands/register-integration-commands.js +1 -1
  8. package/dist/src/cli/commands/registry-declared-setups.d.ts +1 -0
  9. package/dist/src/cli/commands/registry-declared-setups.js +1 -1
  10. package/dist/src/cli/commands/registry-metadata.d.ts +30 -0
  11. package/dist/src/cli/commands/registry-metadata.js +1 -1
  12. package/dist/src/cli/commands/registry-package.d.ts +6 -0
  13. package/dist/src/cli/commands/registry-package.js +1 -1
  14. package/dist/src/cli/commands/registry-pnpm-build-policy-flow.d.ts +30 -0
  15. package/dist/src/cli/commands/registry-pnpm-build-policy-flow.js +1 -0
  16. package/dist/src/cli/commands/registry-pnpm-build-policy.d.ts +12 -0
  17. package/dist/src/cli/commands/registry-pnpm-build-policy.js +6 -0
  18. package/dist/src/cli/commands/registry-recovery.d.ts +4 -0
  19. package/dist/src/cli/commands/registry-recovery.js +1 -1
  20. package/dist/src/cli/commands/registry.d.ts +1 -1
  21. package/dist/src/cli/commands/registry.js +1 -1
  22. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  23. package/dist/src/compiled/.vendor-stamp.json +7 -7
  24. package/dist/src/compiled/@workflow/core/attribute-changes.d.ts +2 -4
  25. package/dist/src/compiled/@workflow/core/capabilities.d.ts +10 -9
  26. package/dist/src/compiled/@workflow/core/capture-stack.d.ts +1 -1
  27. package/dist/src/compiled/@workflow/core/class-serialization.d.ts +8 -8
  28. package/dist/src/compiled/@workflow/core/context-violation-error.d.ts +5 -5
  29. package/dist/src/compiled/@workflow/core/create-hook.d.ts +2 -2
  30. package/dist/src/compiled/@workflow/core/describe-error.d.ts +7 -7
  31. package/dist/src/compiled/@workflow/core/encryption.d.ts +5 -5
  32. package/dist/src/compiled/@workflow/core/events-consumer.d.ts +49 -14
  33. package/dist/src/compiled/@workflow/core/flushable-stream.d.ts +4 -4
  34. package/dist/src/compiled/@workflow/core/global.d.ts +4 -2
  35. package/dist/src/compiled/@workflow/core/index.d.ts +1 -1
  36. package/dist/src/compiled/@workflow/core/index.js +2 -2
  37. package/dist/src/compiled/@workflow/core/log-format.d.ts +2 -2
  38. package/dist/src/compiled/@workflow/core/private.d.ts +40 -78
  39. package/dist/src/compiled/@workflow/core/private.js +1 -1
  40. package/dist/src/compiled/@workflow/core/replay-payload-cache.d.ts +13 -17
  41. package/dist/src/compiled/@workflow/core/runtime/compute-instance.d.ts +4 -3
  42. package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +27 -27
  43. package/dist/src/compiled/@workflow/core/runtime/count-step-started-events.d.ts +7 -7
  44. package/dist/src/compiled/@workflow/core/runtime/deployment-guard.d.ts +7 -7
  45. package/dist/src/compiled/@workflow/core/runtime/get-port-lazy.d.ts +2 -2
  46. package/dist/src/compiled/@workflow/core/runtime/get-world-lazy.d.ts +2 -2
  47. package/dist/src/compiled/@workflow/core/runtime/helpers.d.ts +30 -49
  48. package/dist/src/compiled/@workflow/core/runtime/quickjs-entrypoint.d.ts +21 -10
  49. package/dist/src/compiled/@workflow/core/runtime/quickjs-runtime.d.ts +31 -15
  50. package/dist/src/compiled/@workflow/core/runtime/quickjs-serde.d.ts +10 -10
  51. package/dist/src/compiled/@workflow/core/runtime/replay-budget.d.ts +4 -4
  52. package/dist/src/compiled/@workflow/core/runtime/replay-recovery-reporter.d.ts +1 -1
  53. package/dist/src/compiled/@workflow/core/runtime/resume-hook.d.ts +20 -12
  54. package/dist/src/compiled/@workflow/core/runtime/resume-latency.d.ts +37 -30
  55. package/dist/src/compiled/@workflow/core/runtime/run-id-time.d.ts +2 -2
  56. package/dist/src/compiled/@workflow/core/runtime/run.d.ts +22 -5
  57. package/dist/src/compiled/@workflow/core/runtime/runs.d.ts +4 -4
  58. package/dist/src/compiled/@workflow/core/runtime/start.d.ts +3 -3
  59. package/dist/src/compiled/@workflow/core/runtime/step-executor.d.ts +14 -14
  60. package/dist/src/compiled/@workflow/core/runtime/step-latency.d.ts +42 -25
  61. package/dist/src/compiled/@workflow/core/runtime/step-ownership.d.ts +6 -6
  62. package/dist/src/compiled/@workflow/core/runtime/step-single-flight.d.ts +1 -1
  63. package/dist/src/compiled/@workflow/core/runtime/suspension-handler.d.ts +38 -24
  64. package/dist/src/compiled/@workflow/core/runtime/unserializable-step.d.ts +34 -0
  65. package/dist/src/compiled/@workflow/core/runtime/vm-mode.d.ts +2 -2
  66. package/dist/src/compiled/@workflow/core/runtime/wait-continuation.d.ts +31 -9
  67. package/dist/src/compiled/@workflow/core/runtime/wait-until.d.ts +1 -1
  68. package/dist/src/compiled/@workflow/core/runtime/world-compatibility.d.ts +20 -9
  69. package/dist/src/compiled/@workflow/core/runtime/world-init.d.ts +5 -5
  70. package/dist/src/compiled/@workflow/core/runtime.js +3 -3
  71. package/dist/src/compiled/@workflow/core/sealed-box.d.ts +7 -7
  72. package/dist/src/compiled/@workflow/core/serialization/codec-devalue-vm.d.ts +1 -1
  73. package/dist/src/compiled/@workflow/core/serialization/codec-devalue.d.ts +1 -1
  74. package/dist/src/compiled/@workflow/core/serialization/codec.d.ts +4 -4
  75. package/dist/src/compiled/@workflow/core/serialization/compression.d.ts +12 -11
  76. package/dist/src/compiled/@workflow/core/serialization/encryption.d.ts +7 -7
  77. package/dist/src/compiled/@workflow/core/serialization/format.d.ts +6 -6
  78. package/dist/src/compiled/@workflow/core/serialization/hardened.d.ts +23 -22
  79. package/dist/src/compiled/@workflow/core/serialization/index.d.ts +1 -1
  80. package/dist/src/compiled/@workflow/core/serialization/reducers/class-vm.d.ts +1 -1
  81. package/dist/src/compiled/@workflow/core/serialization/reducers/step-function-vm.d.ts +1 -1
  82. package/dist/src/compiled/@workflow/core/serialization/types.d.ts +5 -5
  83. package/dist/src/compiled/@workflow/core/serialization/workflow-vm.d.ts +1 -1
  84. package/dist/src/compiled/@workflow/core/serialization-format.d.ts +11 -11
  85. package/dist/src/compiled/@workflow/core/serialization.d.ts +18 -19
  86. package/dist/src/compiled/@workflow/core/source-map.d.ts +1 -1
  87. package/dist/src/compiled/@workflow/core/step/context-storage.d.ts +5 -5
  88. package/dist/src/compiled/@workflow/core/step/writable-stream.d.ts +3 -3
  89. package/dist/src/compiled/@workflow/core/symbols.d.ts +2 -2
  90. package/dist/src/compiled/@workflow/core/telemetry/semantic-conventions.d.ts +71 -20
  91. package/dist/src/compiled/@workflow/core/telemetry.d.ts +15 -3
  92. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  93. package/dist/src/compiled/@workflow/core/vm/script-cache.d.ts +9 -11
  94. package/dist/src/compiled/@workflow/core/workflow/abort-controller.d.ts +1 -1
  95. package/dist/src/compiled/@workflow/core/workflow/set-attributes.d.ts +1 -1
  96. package/dist/src/compiled/@workflow/core/workflow/world-init-stub.d.ts +1 -1
  97. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  98. package/dist/src/compiled/@workflow/errors/index.d.ts +18 -18
  99. package/dist/src/compiled/@workflow/errors/index.js +1 -1
  100. package/dist/src/compiled/@workflow/world/analytics.d.ts +3 -2
  101. package/dist/src/compiled/@workflow/world/attributes-validation.d.ts +28 -0
  102. package/dist/src/compiled/@workflow/world/attributes.d.ts +8 -107
  103. package/dist/src/compiled/@workflow/world/env-config.d.ts +5 -5
  104. package/dist/src/compiled/@workflow/world/event-metadata.d.ts +132 -0
  105. package/dist/src/compiled/@workflow/world/events.d.ts +42 -96
  106. package/dist/src/compiled/@workflow/world/hooks.d.ts +12 -11
  107. package/dist/src/compiled/@workflow/world/index.d.ts +3 -3
  108. package/dist/src/compiled/@workflow/world/interfaces.d.ts +94 -39
  109. package/dist/src/compiled/@workflow/world/queue.d.ts +29 -22
  110. package/dist/src/compiled/@workflow/world/runs.d.ts +20 -0
  111. package/dist/src/compiled/@workflow/world/slot-identity.d.ts +3 -3
  112. package/dist/src/compiled/@workflow/world/spec-version.d.ts +82 -15
  113. package/dist/src/compiled/@workflow/world/ulid.d.ts +2 -2
  114. package/dist/src/compiled/@workflow/world-local/index.js +5 -5
  115. package/dist/src/compiled/@workflow/world-vercel/index.js +12 -12
  116. package/dist/src/compiled/_chunks/workflow/{attribute-changes-DXbpwBdw.js → attribute-changes-BwAadFfB.js} +3 -3
  117. package/dist/src/compiled/_chunks/workflow/debug-log-CVPstO1D.js +1 -0
  118. package/dist/src/compiled/_chunks/workflow/dist-CgbVZs5l.js +40 -0
  119. package/dist/src/compiled/_chunks/workflow/dist-DjojkP5e.js +3 -0
  120. package/dist/src/compiled/_chunks/workflow/frames-CZ9eHShe.js +3 -0
  121. package/dist/src/compiled/_chunks/workflow/global-singleton-BNinwFrL.js +1 -0
  122. package/dist/src/compiled/_chunks/workflow/private-CZFMABEf.js +1 -0
  123. package/dist/src/compiled/_chunks/workflow/resume-hook-BToMtPbH.js +1 -0
  124. package/dist/src/compiled/_chunks/workflow/run-DzyAoGh4.js +1 -0
  125. package/dist/src/compiled/_chunks/workflow/sleep-3_A5uOp-.js +1 -0
  126. package/dist/src/compiled/_chunks/workflow/undici-DfxNX5og.js +95 -0
  127. package/dist/src/compiled/_chunks/workflow/wait-until-BtySPYD0.js +15 -0
  128. package/dist/src/compiled/_chunks/workflow/ws-transport-PnVVxT0R.js +1 -0
  129. package/dist/src/eve-channel/index.js +1 -1
  130. package/dist/src/execution/agent-handle-dispatch.js +1 -1
  131. package/dist/src/execution/continuation-conflict-step.d.ts +10 -0
  132. package/dist/src/execution/continuation-conflict-step.js +1 -0
  133. package/dist/src/execution/delegated-parent-notification.js +1 -1
  134. package/dist/src/execution/dispatch-runtime-actions-shared.d.ts +7 -29
  135. package/dist/src/execution/dispatch-runtime-actions-shared.js +1 -1
  136. package/dist/src/execution/dispatch-runtime-actions-start.d.ts +33 -0
  137. package/dist/src/execution/dispatch-runtime-actions-start.js +1 -0
  138. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  139. package/dist/src/execution/dispatch-turn-step.js +1 -1
  140. package/dist/src/execution/dynamic-connections.js +1 -1
  141. package/dist/src/execution/inline-turn.js +1 -1
  142. package/dist/src/execution/next-driver-action.d.ts +5 -4
  143. package/dist/src/execution/proxied-deliver-step.js +1 -1
  144. package/dist/src/execution/runtime-errors.d.ts +0 -12
  145. package/dist/src/execution/runtime-errors.js +1 -1
  146. package/dist/src/execution/sandbox/bindings/docker-options.d.ts +1 -1
  147. package/dist/src/execution/sandbox/bindings/docker-options.js +1 -1
  148. package/dist/src/execution/sandbox/bindings/eve-image.d.ts +4 -0
  149. package/dist/src/execution/sandbox/bindings/eve-image.js +1 -0
  150. package/dist/src/execution/sandbox/bindings/microsandbox-options.d.ts +1 -1
  151. package/dist/src/execution/sandbox/bindings/microsandbox-options.js +1 -1
  152. package/dist/src/execution/sandbox/bindings/vercel-create-sdk.js +1 -1
  153. package/dist/src/execution/session-timeout-steps.js +1 -1
  154. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  155. package/dist/src/execution/stable-workflow-names.d.ts +2 -2
  156. package/dist/src/execution/subagent-start-local.js +1 -1
  157. package/dist/src/execution/tasks/parent/dispatch-task-step.js +1 -1
  158. package/dist/src/execution/tasks/parent/run-parent.js +1 -1
  159. package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
  160. package/dist/src/execution/tool-run/start.js +1 -1
  161. package/dist/src/execution/tools/subagent/local.js +1 -1
  162. package/dist/src/execution/turn-workflow.js +1 -1
  163. package/dist/src/execution/workflow-entry.d.ts +5 -1
  164. package/dist/src/execution/workflow-entry.js +1 -1
  165. package/dist/src/execution/workflow-runtime.d.ts +12 -12
  166. package/dist/src/execution/workflow-runtime.js +1 -1
  167. package/dist/src/execution/workflow-steps.js +1 -1
  168. package/dist/src/harness/tool-loop.js +1 -1
  169. package/dist/src/internal/application/package.js +1 -1
  170. package/dist/src/internal/workflow/development-world-client.js +1 -1
  171. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  172. package/dist/src/public/channels/slack/activity.d.ts +18 -0
  173. package/dist/src/public/channels/slack/activity.js +1 -0
  174. package/dist/src/public/channels/slack/api.js +1 -1
  175. package/dist/src/public/channels/slack/index.d.ts +1 -0
  176. package/dist/src/public/channels/slack/index.js +1 -1
  177. package/dist/src/public/channels/slack/interaction-cards.js +1 -1
  178. package/dist/src/public/channels/slack/interactions.js +1 -1
  179. package/dist/src/public/channels/slack/slackChannel.d.ts +12 -1
  180. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  181. package/dist/src/public/memory/file/backends/default.js +1 -1
  182. package/dist/src/public/sandbox/docker-sandbox.d.ts +5 -4
  183. package/dist/src/public/sandbox/microsandbox-sandbox.d.ts +1 -1
  184. package/dist/src/public/sandbox/vercel-sandbox.d.ts +3 -1
  185. package/dist/src/setup/flows/registry.js +1 -1
  186. package/dist/src/setup/integrations/file-memory/setup.d.ts +15 -0
  187. package/dist/src/setup/integrations/file-memory/setup.js +2 -0
  188. package/dist/src/setup/integrations/file-memory/vercel.d.ts +103 -0
  189. package/dist/src/setup/integrations/file-memory/vercel.js +1 -0
  190. package/dist/src/setup/integrations/registry.js +1 -1
  191. package/dist/src/setup/integrations/shared/vercel-project.js +1 -1
  192. package/dist/src/setup/scaffold/connections/catalog.js +1 -1
  193. package/dist/src/setup/scaffold/create/project.js +1 -1
  194. package/docs/channels/custom.mdx +4 -0
  195. package/docs/channels/eve.mdx +12 -8
  196. package/docs/channels/mcp.mdx +4 -1
  197. package/docs/channels/slack.mdx +1 -1
  198. package/docs/guides/instrumentation.md +1 -1
  199. package/docs/install-integrations.mdx +5 -0
  200. package/docs/instructions.mdx +1 -1
  201. package/docs/memory/custom-provider.md +214 -0
  202. package/docs/memory/file.md +168 -0
  203. package/docs/memory/meta.json +5 -0
  204. package/docs/memory/overview.mdx +282 -0
  205. package/docs/patterns/multi-tenant-memory.md +12 -20
  206. package/docs/reference/cli.md +28 -23
  207. package/docs/sandbox.mdx +5 -3
  208. package/package.json +8 -8
  209. package/dist/src/compiled/_chunks/workflow/dist-CqvWKNQv.js +0 -3
  210. package/dist/src/compiled/_chunks/workflow/dist-Dk0aQqFO.js +0 -40
  211. package/dist/src/compiled/_chunks/workflow/dist-DpYyXbJv.js +0 -1
  212. package/dist/src/compiled/_chunks/workflow/env-config-L94XftKz.js +0 -1
  213. package/dist/src/compiled/_chunks/workflow/frames-Dl2Hcma4.js +0 -3
  214. package/dist/src/compiled/_chunks/workflow/private-CuQ4HTFC.js +0 -1
  215. package/dist/src/compiled/_chunks/workflow/resume-hook-E-Nkx8q_.js +0 -1
  216. package/dist/src/compiled/_chunks/workflow/run-cIeex2KF.js +0 -1
  217. package/dist/src/compiled/_chunks/workflow/sleep-CxFsdxB_.js +0 -1
  218. package/dist/src/compiled/_chunks/workflow/spec-version-DBUVfF_R.js +0 -1
  219. package/dist/src/compiled/_chunks/workflow/undici-DznrXKcM.js +0 -95
  220. package/dist/src/compiled/_chunks/workflow/wait-until-BZHmJCAW.js +0 -15
  221. package/dist/src/compiled/_chunks/workflow/ws-transport-Ct6yyRWZ.js +0 -1
  222. package/docs/memory.md +0 -408
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{parseInputResponse}from"#shared/input.js";import{readSlackTextObject}from"#public/channels/slack/inbound-content.js";import{buildSlackBinding,buildSlackWorkspaceHandle,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";import{dispatchSlashCommand}from"#public/channels/slack/slash-command.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}),a;try{a=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),i}if(a.kind===`view_submission`)return handleViewSubmission(a,t,n);if(a.kind===`slash_command`)return dispatchSlashCommand(a,t,n.config),new Response(null,{status:200});if(a.kind===`unsupported`){let e=parseShortcutPayload(a.raw);return e===null?(log.warn(`unsupported Slack interaction payload ignored`,{type:a.type}),i):(dispatchShortcut(e,readInstallationTeamId(a.raw),t,n),new Response(null,{status:200}))}if(a.kind!==`block_actions`)return log.warn(`unsupported Slack interaction payload ignored`,{type:a.kind}),i;let c=parseBlockActionsPayload(a);if(!c)return i;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:a.raw,interaction:c,freeformAction:l,deps:n}),i;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 p=n.config.onInteraction;if(p){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let i=e[0].user,{thread:a,slack:o}=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:o};for(let n of e)t.waitUntil(Promise.resolve(p(n,l)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return i}function parseShortcutPayload(e){if(!isObjectRecord(e))return null;let t=e.type;if(t!==`message_action`&&t!==`shortcut`)return null;let n=readRequiredString(e.callback_id),r=readRequiredString(e.trigger_id),i=isObjectRecord(e.user)?e.user:void 0,a=readRequiredString(i?.id);if(n===null||r===null||a===null)return null;let o=isObjectRecord(e.team)?e.team:void 0,s=readOptionalString(i?.team_id)??readOptionalString(o?.id),c={id:a,username:readOptionalString(i?.username),name:readOptionalString(i?.name)};if(t===`shortcut`)return{type:t,callbackId:n,triggerId:r,user:c,teamId:s};let l=isObjectRecord(e.channel)?e.channel:void 0,u=isObjectRecord(e.message)?e.message:void 0,d=readRequiredString(l?.id),f=readRequiredString(u?.ts);return d===null||f===null||u===void 0?null:{type:t,callbackId:n,triggerId:r,user:c,teamId:s,channelId:d,message:{text:typeof u.text==`string`?u.text:``,ts:f,threadTs:readOptionalString(u.thread_ts),userId:readOptionalString(u.user)},responseUrl:readOptionalString(e.response_url)}}function readRequiredString(e){return typeof e==`string`&&e.length>0?e:null}function readOptionalString(e){return typeof e==`string`&&e.length>0?e:void 0}function dispatchShortcut(e,t,n,r){let i=r.config.onShortcut;if(i===void 0){log.warn(`Slack shortcut ignored because onShortcut is not configured`,{type:e.type});return}let a=buildShortcutContext({config:r.config,installationTeamId:t,teamId:e.teamId});dispatchInteractionHook(()=>i(e,a),n,`shortcut handler failed`)}function buildShortcutContext(e){return{slack:buildSlackWorkspaceHandle({botToken:e.config.credentials?.botToken,installationTeamId:e.installationTeamId,teamId:e.teamId})}}function dispatchInteractionHook(e,t,n){t.waitUntil(Promise.resolve().then(e).catch(e=>{log.error(n,{error:e})}))}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 i=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: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=a.installationTeamId??readInstallationTeamId(e.raw),m={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,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,parseShortcutPayload};
1
+ import{createLogger}from"#internal/logging.js";import{parseInputResponse}from"#shared/input.js";import{buildSlackBinding,buildSlackWorkspaceHandle,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{readSlackTextObject}from"#public/channels/slack/inbound-content.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";import{dispatchSlashCommand}from"#public/channels/slack/slash-command.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,r){let i=new Response(`ok`,{status:200}),o;try{o=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),i}if(o.kind===`view_submission`)return handleViewSubmission(o,t,r);if(o.kind===`slash_command`)return dispatchSlashCommand(o,t,r.config),new Response(null,{status:200});if(o.kind===`unsupported`){let e=parseShortcutPayload(o.raw);return e===null?(log.warn(`unsupported Slack interaction payload ignored`,{type:o.type}),i):(dispatchShortcut(e,readInstallationTeamId(o.raw),t,r),new Response(null,{status:200}))}if(o.kind!==`block_actions`)return log.warn(`unsupported Slack interaction payload ignored`,{type:o.kind}),i;let c=parseBlockActionsPayload(o);if(!c)return i;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:o.raw,interaction:c,freeformAction:l,deps:r}),i;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:r,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 p=r.config.onInteraction;if(p){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let i=e[0].user,{thread:a,slack:o}=buildSlackBinding({botToken:r.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:o};for(let n of e)t.waitUntil(Promise.resolve(p(n,l)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return i}function parseShortcutPayload(e){if(!isObjectRecord(e))return null;let t=e.type;if(t!==`message_action`&&t!==`shortcut`)return null;let n=readRequiredString(e.callback_id),r=readRequiredString(e.trigger_id),i=isObjectRecord(e.user)?e.user:void 0,a=readRequiredString(i?.id);if(n===null||r===null||a===null)return null;let o=isObjectRecord(e.team)?e.team:void 0,s=readOptionalString(i?.team_id)??readOptionalString(o?.id),c={id:a,username:readOptionalString(i?.username),name:readOptionalString(i?.name)};if(t===`shortcut`)return{type:t,callbackId:n,triggerId:r,user:c,teamId:s};let l=isObjectRecord(e.channel)?e.channel:void 0,u=isObjectRecord(e.message)?e.message:void 0,d=readRequiredString(l?.id),f=readRequiredString(u?.ts);return d===null||f===null||u===void 0?null:{type:t,callbackId:n,triggerId:r,user:c,teamId:s,channelId:d,message:{text:typeof u.text==`string`?u.text:``,ts:f,threadTs:readOptionalString(u.thread_ts),userId:readOptionalString(u.user)},responseUrl:readOptionalString(e.response_url)}}function readRequiredString(e){return typeof e==`string`&&e.length>0?e:null}function readOptionalString(e){return typeof e==`string`&&e.length>0?e:void 0}function dispatchShortcut(e,t,n,r){let i=r.config.onShortcut;if(i===void 0){log.warn(`Slack shortcut ignored because onShortcut is not configured`,{type:e.type});return}let a=buildShortcutContext({config:r.config,installationTeamId:t,teamId:e.teamId});dispatchInteractionHook(()=>i(e,a),n,`shortcut handler failed`)}function buildShortcutContext(e){return{slack:buildSlackWorkspaceHandle({botToken:e.config.credentials?.botToken,installationTeamId:e.installationTeamId,teamId:e.teamId})}}function dispatchInteractionHook(e,t,n){t.waitUntil(Promise.resolve().then(e).catch(e=>{log.error(n,{error:e})}))}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 s=e.user,d=e.userId,f=s?.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:s?.username,name:s?.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,parseShortcutPayload};
@@ -14,7 +14,8 @@ import { type SlackSessionOperations } from "#public/channels/slack/session-oper
14
14
  import { type UploadPolicyInput } from "#public/channels/upload-policy.js";
15
15
  import { type SlackWebhookVerifier } from "#public/channels/slack/verify.js";
16
16
  import { type Channel } from "#public/definitions/channel.js";
17
- import type { ChannelAudience } from "#shared/channel-audience.js";
17
+ import { type ChannelAudience } from "#shared/channel-audience.js";
18
+ import { type SlackActivityRenderer } from "#public/channels/slack/activity.js";
18
19
  export type { SlackRespondOptions, SlackSendOptions, SlackSessionOperations, } from "#public/channels/slack/session-operations.js";
19
20
  type EventData<T extends UnstampedMessageStreamEvent["type"]> = Extract<UnstampedMessageStreamEvent, {
20
21
  type: T;
@@ -184,6 +185,12 @@ export interface SlackChannelCredentials {
184
185
  export interface SlackReceiveTarget {
185
186
  readonly channelId: string;
186
187
  readonly threadTs?: string;
188
+ /**
189
+ * Optional audience for proactive receives. Omit to leave `unknown`.
190
+ * Pass when the caller already knows channel visibility, for example a
191
+ * webhook or schedule that classified the Slack destination before handoff.
192
+ */
193
+ readonly audience?: ChannelAudience;
187
194
  /** Slack workspace whose app installation supplies credentials for this send. */
188
195
  readonly installationTeamId?: string;
189
196
  /**
@@ -466,6 +473,10 @@ export interface SlackChannelInternalEvents extends Omit<SlackChannelEvents, "au
466
473
  export interface SlackChannelConfig {
467
474
  readonly credentials?: SlackChannelCredentials;
468
475
  readonly botName?: string;
476
+ /** Optional presentation-only activity rendered without starting parent turns. */
477
+ readonly activity?: {
478
+ readonly renderers: readonly SlackActivityRenderer[];
479
+ };
469
480
  /** Override the default webhook route path (`/eve/v1/slack`). */
470
481
  readonly route?: string;
471
482
  /** Policy for accepted messages that arrive while a turn is active. */
@@ -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,slackEventInstallationTeamId,slackEventReceivingBotUserId,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=slackEventReceivingBotUserId(i),c=slackEventInstallationTeamId(i),dispatch=null,l=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,receivingBotUserId:s,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,installationTeamId:c,kind:e,message:i,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}),r=(e===`app_mention`?n.onAppMention:n.onDirectMessage)??n.onMessage;r===void 0?l=dispatchMessageWith(e===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,receivingBotUserId:s,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,installationTeamId:c,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,receivingBotUserId:s,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:n.onMessage,installationTeamId:c,kind:`channel_message`,message:e,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}))}let u=n.onEvent;if(dispatch===null&&u!==void 0&&(dispatch=()=>dispatchSlackEvent({from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,envelope:i,handler:u,installationTeamId:c})),dispatch??=l,dispatch===null)return new Response(`ok`);let d=i.event_id;if(d){if(t.handledEvents.has(d))return log.warn(`received a duplicate event`,{event_id:d,event_time:i.event_time,retry_num:r.retry?.num??`(null)`,retry_reason:r.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(d,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=e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),p={...c,isBotMentioned:()=>f,isDMOrPrivateChannel,isSubscribed:async()=>await c.resolveSession()!==void 0,slack:i,thread:r},m;try{m=await e.handler(p,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(m==null)return;let h=await isDMOrPrivateChannel();o.audience=e.kind===`direct_message`||h?`private`:`public`,await deliverSlackMessage({botUserId:e.receivingBotUserId,credentials:e.credentials,kind:e.kind,isPrivateConversation:h,isMentioned:f,message:e.message,result:m,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({botUserId:e.botUserId,channelId:t.channelId,fullName:t.author?.fullName,isMentioned:e.isMentioned,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{setChannelActivityRenderers}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{normalizeChannelAudience}from"#shared/channel-audience.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{buildSlackActivityRenderers,hasSlackActivityStatus}from"#public/channels/slack/activity.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,slackEventReceivingBotUserId,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`),ignoreSlackActivityEvent=async()=>void 0,activityOwnedMessageCompleted=async(e,t)=>{t.state.pendingToolCallMessage=null,e.finishReason!==`tool-calls`&&e.message&&await t.thread.post(e.message)},clearSlackTurnState=async(e,t)=>{t.state.pendingToolCallMessage=null,t.state.lastReasoningTypingAtMs=null,t.state.lastReasoningTypingStatus=null};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}),a=buildSlackActivityRenderers({botToken:e.credentials?.botToken,renderers:e.activity?.renderers??[]}),l=e.onInputResponse??defaultOnInputResponse,u=e.events?.[`authorization.required`],d=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],f=hasSlackActivityStatus(e.activity?.renderers),g=e.events?.[`turn.started`]??(f?clearSlackTurnState:defaultEvents[`turn.started`]),_=e.events?.[`reasoning.appended`]??(f?ignoreSlackActivityEvent:defaultEvents[`reasoning.appended`]),v=e.events?.[`actions.requested`]??(f?ignoreSlackActivityEvent:defaultEvents[`actions.requested`]),y=e.events?.[`message.completed`]??(f?activityOwnedMessageCompleted:defaultEvents[`message.completed`]),b={...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 d(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 g(e,t,n)},"reasoning.appended":_,"actions.requested":v,"message.completed":y,"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":u===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(u)},x=new Set,S=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 r=e.state?.approvalResponderUsers;return typeof r==`object`&&r&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...r}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,r)=>{let{waitUntil:i}=r,{from:a,resolveSession:o}=r,s=await verifyInbound(n,e.credentials);return s===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(s,{from:a,resolveSession:o,waitUntil:i},{config:e,onInputResponse:l}):handleEventPost({body:s,from:a,resolveSession:o,waitUntil:i,config:e,uploadPolicy:t,handledEvents:x,headers:n.headers})})],receive(t,{from:n}){return receiveOnSlack(t,{from:n,credentials:e.credentials})},events:b});return setChannelActivityRenderers(S,{destination(e){let t=e;return{channelId:t?.channelId??null,installationTeamId:t?.installationTeamId??null,threadTs:t?.threadTs??null}},renderers:a}),S}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:``,o=typeof n.installationTeamId==`string`?n.installationTeamId:t.installationTeamId,s=n.initialMessage;if(s&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let c=i;if(s){let{thread:e}=buildSlackBinding({botToken:t.credentials?.botToken,channelId:r,threadTs:``,installationTeamId:o,teamId:t.teamId}),n={card:s.card};s.fallbackText!==void 0&&(n.fallbackText=s.fallbackText),c=(await e.post(n)).id}let u=c||crypto.randomUUID(),f=n.audience===void 0?void 0:normalizeChannelAudience(n.audience),p={channelId:r,installationTeamId:o??null,threadTs:c||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null};return f!==void 0&&(p.audience=f),t.from(slackContinuationToken(r,u)).send(e.message,{auth:e.auth,state:p,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=slackEventReceivingBotUserId(i),c=slackEventInstallationTeamId(i),dispatch=null,l=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,receivingBotUserId:s,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,installationTeamId:c,kind:e,message:i,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}),r=(e===`app_mention`?n.onAppMention:n.onDirectMessage)??n.onMessage;r===void 0?l=dispatchMessageWith(e===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:a,botUserId:o,receivingBotUserId:s,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:r,installationTeamId:c,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,receivingBotUserId:s,from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,handler:n.onMessage,installationTeamId:c,kind:`channel_message`,message:e,threadContext:n.threadContext,uploadPolicy:t.uploadPolicy}))}let u=n.onEvent;if(dispatch===null&&u!==void 0&&(dispatch=()=>dispatchSlackEvent({from:t.from,resolveSession:t.resolveSession,credentials:n.credentials,envelope:i,handler:u,installationTeamId:c})),dispatch??=l,dispatch===null)return new Response(`ok`);let d=i.event_id;if(d){if(t.handledEvents.has(d))return log.warn(`received a duplicate event`,{event_id:d,event_time:i.event_time,retry_num:r.retry?.num??`(null)`,retry_reason:r.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(d,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},s=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}),c,isDMOrPrivateChannel=()=>c??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:i.request}),u=e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),p={...s,isBotMentioned:()=>u,isDMOrPrivateChannel,isSubscribed:async()=>await s.resolveSession()!==void 0,slack:i,thread:r},m;try{m=await e.handler(p,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(m==null)return;let h=await isDMOrPrivateChannel();o.audience=e.kind===`direct_message`||h?`private`:`public`,await deliverSlackMessage({botUserId:e.receivingBotUserId,credentials:e.credentials,kind:e.kind,isPrivateConversation:h,isMentioned:u,message:e.message,result:m,sessionOperations:s,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({botUserId:e.botUserId,channelId:t.channelId,fullName:t.author?.fullName,isMentioned:e.isMentioned,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 +1 @@
1
- import{isEveDevEnvironment}from"#internal/application/dev-environment.js";import{inMemory}from"#public/memory/file/backends/in-memory.js";import{lazyBackend}from"#public/memory/file/backends/lazy.js";import{vercelBlob}from"#public/memory/file/backends/vercel-blob.js";const ENVIRONMENT_PROBES={hasVercelBlobStore:()=>hasEnvironmentValue(`BLOB_READ_WRITE_TOKEN`)||hasEnvironmentValue(`BLOB_STORE_ID`)&&hasEnvironmentValue(`VERCEL_OIDC_TOKEN`),isEveDevelopment:isEveDevEnvironment,isDeployedOnVercel:()=>hasEnvironmentValue(`VERCEL`)},DEVELOPMENT_BACKEND=inMemory();function defaultFileMemoryBackend(){return lazyBackend(()=>selectDefaultFileMemoryBackend(ENVIRONMENT_PROBES))}function selectDefaultFileMemoryBackend(e){if(e.isDeployedOnVercel()){if(e.hasVercelBlobStore())return vercelBlob();throw Error(`fileMemory() requires an attached Vercel Blob store on Vercel. Attach a Blob store or pass fileMemory({ backend }).`)}if(e.isEveDevelopment())return DEVELOPMENT_BACKEND;throw Error(`fileMemory() requires an explicit backend outside Vercel and eve dev. Pass fileMemory({ backend }).`)}function hasEnvironmentValue(e){return!!process.env[e]?.trim()}export{defaultFileMemoryBackend};
1
+ import{isEveDevEnvironment}from"#internal/application/dev-environment.js";import{inMemory}from"#public/memory/file/backends/in-memory.js";import{lazyBackend}from"#public/memory/file/backends/lazy.js";import{vercelBlob}from"#public/memory/file/backends/vercel-blob.js";const ENVIRONMENT_PROBES={vercelBlobCredentials:()=>credentialsFromEnvironment(`EVE_MEMORY_BLOB`)??credentialsFromEnvironment(`BLOB`),isEveDevelopment:isEveDevEnvironment,isDeployedOnVercel:()=>hasEnvironmentValue(`VERCEL`)},DEVELOPMENT_BACKEND=inMemory();function defaultFileMemoryBackend(){return lazyBackend(()=>selectDefaultFileMemoryBackend(ENVIRONMENT_PROBES))}function selectDefaultFileMemoryBackend(e){if(e.isDeployedOnVercel()){let t=e.vercelBlobCredentials();if(t!==void 0)return vercelBlob(t);throw Error("fileMemory() requires Vercel Blob storage. Run `/add memory/file` in eve dev or `eve integration setup file-memory`, or pass fileMemory({ backend }).")}if(e.isEveDevelopment())return DEVELOPMENT_BACKEND;throw Error(`fileMemory() requires an explicit backend outside Vercel and eve dev. Pass fileMemory({ backend }).`)}function hasEnvironmentValue(e){return!!process.env[e]?.trim()}function credentialsFromEnvironment(e){let t=environmentValue(`${e}_READ_WRITE_TOKEN`);if(t!==void 0)return{token:t};let n=environmentValue(`${e}_STORE_ID`);if(n!==void 0)return{oidcToken:environmentValue(`VERCEL_OIDC_TOKEN`),storeId:n}}function environmentValue(e){let t=process.env[e]?.trim();return t===``?void 0:t}export{defaultFileMemoryBackend};
@@ -21,10 +21,11 @@ export type DockerSandboxNetworkPolicy = "allow-all" | "deny-all";
21
21
  */
22
22
  export interface DockerSandboxCreateOptions {
23
23
  /**
24
- * Base container image for templates and sessions. Defaults to
25
- * `ghcr.io/vercel/eve:latest` — eve's published sandbox runtime image.
26
- * Framework setup creates `/workspace` and verifies Bash. Install any
27
- * authored runtime tools in sandbox bootstrap or provide them through a
24
+ * Base container image for templates and sessions. Defaults to eve's
25
+ * published `ghcr.io/vercel/eve` image, tagged with the installed eve
26
+ * version or `EVE_SANDBOX_IMAGE_TAG` when set. Framework setup creates
27
+ * `/workspace` and verifies Bash. Install
28
+ * any authored runtime tools in sandbox bootstrap or provide them through a
28
29
  * custom image.
29
30
  */
30
31
  readonly image?: string;
@@ -16,7 +16,7 @@ export interface MicrosandboxSandboxCreateOptions {
16
16
  * Python, or ripgrep in sandbox bootstrap or provide them through a
17
17
  * custom image.
18
18
  *
19
- * @default "ghcr.io/vercel/eve:latest"
19
+ * @default The `ghcr.io/vercel/eve` tag matching the installed eve version, or `EVE_SANDBOX_IMAGE_TAG` when set.
20
20
  */
21
21
  readonly image?: string;
22
22
  /** Number of virtual CPUs assigned to each sandbox. @default 1 */
@@ -22,7 +22,9 @@ type VercelSandboxAuthorCreateOptions<T> = T extends unknown ? Omit<T, "mounts"
22
22
  * author-supplied values.
23
23
  *
24
24
  * `runtime` is excluded as well: eve always boots its sandboxes from the
25
- * published eve image, which is mutually exclusive with a stock runtime.
25
+ * published `vcr.vercel.com/vercel/eve/base` image tagged with the installed
26
+ * eve version or `EVE_SANDBOX_IMAGE_TAG` when set, which is mutually exclusive
27
+ * with a stock runtime.
26
28
  *
27
29
  * `source` is honored only on the template create at prewarm time, so
28
30
  * an author-supplied snapshot, git revision, or tarball becomes the
@@ -1,2 +1,2 @@
1
- import{createRegistrySession}from"./registry-session.js";import{PlannerNavigationError}from"#setup/prompter.js";import{WizardCancelledError}from"#setup/step.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{withSpinner}from"#setup/with-spinner.js";const PLANNER_STEPS=[`Channels`,`Integrations`,`Review`];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}};const SECTIONS={channels:{title:`Where should people reach your agent?`,description:`You can add more later with /add.`,featured:[`channel/web`,`channel/slack`,`channel/github`,`channel/linear-agent`,`channel/photon-imessage`],includes:e=>e.name.startsWith(`channel/`)},integrations:{title:`What should your agent be able to work with?`,featured:[`extension/github-tools`,`connection/linear`,`connection/notion`,`connection/vercel`,`extension/agent-browser`],includes:e=>!e.name.startsWith(`channel/`)&&!e.name.startsWith(`experimental/`)}};function label(e){return e.title??e.name.split(`/`).at(-1)??e.name}function isPlannerItem(e,t){return t.name.includes(`/`)&&SECTIONS[e].includes(t)}function orderedSectionItems(e,t){let n=t.filter(t=>isPlannerItem(e,t)),r=SECTIONS[e].featured.map(e=>n.find(t=>t.name===e)).filter(e=>e!==void 0),i=new Set(r.map(e=>e.address));return[...r,...n.filter(e=>!i.has(e.address))]}function plannerLabel(e){return e.name===`connection/linear`?`Linear MCP`:label(e)}function sectionRows(e,t){return orderedSectionItems(e,t).map(e=>({value:e.address,label:plannerLabel(e),hint:e.description}))}function selectedInSection(e,t,n){return t.filter(t=>isPlannerItem(e,t)&&n.has(t.address)).map(e=>e.address)}function plannerNavigation(e,t){let n=(t.plannerContext?.prefixSteps??[]).map(e=>({label:e.label,complete:e.complete}));return{kind:`planner`,activeStep:n.length+e,firstNavigableStep:n.length,steps:[...n,...PLANNER_STEPS.map((e,n)=>{if(n===2)return{label:e};let r=selectedInSection(n===0?`channels`:`integrations`,t.catalog,t.selected).length;return r===0?{label:e}:{label:e,count:r}})]}}function replaceSectionSelection(e){for(let t of e.catalog)isPlannerItem(e.section,t)&&e.selected.delete(t.address);for(let t of e.addresses)e.selected.add(t)}async function editSection(e){let{section:n,catalog:r,prompter:i,selected:a}=e,s={message:SECTIONS[n].title,multiple:!0,search:!0,placeholder:n===`channels`?`Search channels`:`Search integrations`,navigation:plannerNavigation(n===`channels`?0:1,e),initialValues:selectedInSection(n,r,a),options:sectionRows(n,r)};n===`channels`&&(s.description=SECTIONS.channels.description),e.notices!==void 0&&(s.notices=e.notices);try{return replaceSectionSelection({section:n,catalog:r,selected:a,addresses:await i.select(s)}),`forward`}catch(e){if(!(e instanceof PlannerNavigationError))throw e;return replaceSectionSelection({section:n,catalog:r,selected:a,addresses:e.values.map(e=>{if(typeof e!=`string`)throw Error(`Registry planner returned a non-string item.`);return e})}),e.direction}}async function editPlan(e){let r=`channels`,i=e.notices;for(;;){if(r!==`review`){try{let t=await editSection({...e,section:r,notices:i});i=void 0,t===`back`?r===`integrations`&&(r=`channels`):r=r===`channels`?`integrations`:`review`}catch(e){if(!(e instanceof WizardCancelledError))throw e;return`cancelled`}continue}try{let t=e.selected.size>0,n=[...e.selected].map(t=>{let n=e.itemsByAddress.get(t);if(n===void 0)throw Error(`Registry item "${t}" is no longer available.`);return n}),r=n.filter(e=>e.name.startsWith(`channel/`)),a=n.filter(e=>!e.name.startsWith(`channel/`)),o=[...r.length===0?[]:[{label:`Channels`,value:r.map(label).join(`, `)}],...a.length===0?[]:[{label:`Integrations`,value:a.map(label).join(`, `)}]],s={message:e.plannerContext?.reviewMessage??`Review additions`,metadata:[...e.plannerContext?.reviewMetadata??[],...o],navigation:plannerNavigation(2,e),options:[{value:`install`,label:t?e.plannerContext?.primaryActionLabel??`Install and set up`:e.plannerContext?.emptyActionLabel??`Finish without adding`},{value:`back`,label:`Back`}]};t||(s.description=`No channels or integrations selected.`),i!==void 0&&(s.notices=i);let c=await e.prompter.select(s);if(i=void 0,c===`install`)return`install`}catch(e){if(e instanceof PlannerNavigationError&&e.direction===`back`){r=`integrations`;continue}if(!(e instanceof WizardCancelledError))throw e;return`cancelled`}r=`integrations`}}function hasSettledOutcomes(e){return e!==void 0&&(e.items.length>0||e.failures.length>0||e.outcomes!==void 0)}async function runRegistryFlow(t){let a;try{let o=t.initialAddress?.trim(),s;if(o!==void 0&&o!==``){if(await t.prompter.select({message:`Add ${o}?`,options:[{value:`install`,label:`Install and set up`},{value:`cancel`,label:`Cancel`}]})===`cancel`)return{kind:`cancelled`};s=[{address:o,name:o,source:`Registry`}]}else{let e=t.deps?.browseRegistryCatalog??(await import(`#cli/commands/registry.js`)).browseRegistryCatalog,n=await withSpinner(t.prompter,`Loading registry…`,()=>e(t.appRoot)),r=[...n.items],a=new Set,o=n.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`})),c=new Map(r.map(e=>[e.address,e]));if(await editPlan({prompter:t.prompter,catalog:r,itemsByAddress:c,selected:a,notices:o,plannerContext:t.plannerContext})!==`install`)return{kind:`cancelled`};s=[...a].map(e=>{let t=c.get(e);if(t===void 0)throw Error(`Registry item "${e}" is no longer available.`);return t})}let c=t.deps?.installRegistryItem??(await import(`#cli/commands/registry.js`)).installRegistryItem;a=createRegistrySession({detectDeployment:t.deps?.detectDeployment??(await import(`#setup/project-resolution.js`)).detectDeployment,runDeployFlow:t.deps?.runDeployFlow??(await import(`./deploy.js`)).runDeployFlow});let l=a;for(let[e,i]of s.entries()){t.signal?.throwIfAborted(),t.onItemStart?.(i,e,s.length);try{let install=(e=t.signal)=>c(t.appRoot,i.address,{silent:!0,prompter:t.prompter,signal:e}),run=()=>t.runItem===void 0?install():t.runItem(install),e=await(t.prompter.withExclusiveTerminal?.(run)??run());l.add(label(i),e.output,e.setup)}catch(e){if(t.signal?.throwIfAborted(),e instanceof WizardCancelledError){l.addCancellation(label(i));continue}if(e instanceof HumanActionRequiredError)throw e;let a=(e instanceof Error?e.message:String(e)).trim()||`Installation failed.`,o=a.split(`
1
+ import{createRegistrySession}from"./registry-session.js";import{PlannerNavigationError}from"#setup/prompter.js";import{WizardCancelledError}from"#setup/step.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{withSpinner}from"#setup/with-spinner.js";const PLANNER_STEPS=[`Channels`,`Integrations`,`Review`];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}};const SECTIONS={channels:{title:`Where should people reach your agent?`,description:`You can add more later with /add.`,featured:[`channel/web`,`channel/slack`,`channel/github`,`channel/linear-agent`,`channel/photon-imessage`],includes:e=>e.name.startsWith(`channel/`)},integrations:{title:`What should your agent be able to work with?`,featured:[`extension/github-tools`,`connection/linear`,`connection/notion`,`connection/vercel`,`extension/agent-browser`,`experimental/self-modification`],includes:e=>!e.name.startsWith(`channel/`)}};function label(e){return e.title??e.name.split(`/`).at(-1)??e.name}function isPlannerItem(e,t){return t.name.includes(`/`)&&SECTIONS[e].includes(t)}function orderedSectionItems(e,t){let n=t.filter(t=>isPlannerItem(e,t)),r=SECTIONS[e].featured.map(e=>n.find(t=>t.name===e)).filter(e=>e!==void 0),i=new Set(r.map(e=>e.address));return[...r,...n.filter(e=>!i.has(e.address))]}function plannerLabel(e){return e.name===`connection/linear`?`Linear MCP`:label(e)}function sectionRows(e,t){return orderedSectionItems(e,t).map(e=>({value:e.address,label:plannerLabel(e),hint:e.description}))}function selectedInSection(e,t,n){return t.filter(t=>isPlannerItem(e,t)&&n.has(t.address)).map(e=>e.address)}function plannerNavigation(e,t){let n=(t.plannerContext?.prefixSteps??[]).map(e=>({label:e.label,complete:e.complete}));return{kind:`planner`,activeStep:n.length+e,firstNavigableStep:n.length,steps:[...n,...PLANNER_STEPS.map((e,n)=>{if(n===2)return{label:e};let r=selectedInSection(n===0?`channels`:`integrations`,t.catalog,t.selected).length;return r===0?{label:e}:{label:e,count:r}})]}}function replaceSectionSelection(e){for(let t of e.catalog)isPlannerItem(e.section,t)&&e.selected.delete(t.address);for(let t of e.addresses)e.selected.add(t)}async function editSection(e){let{section:n,catalog:r,prompter:i,selected:a}=e,s={message:SECTIONS[n].title,multiple:!0,search:!0,placeholder:n===`channels`?`Search channels`:`Search integrations`,navigation:plannerNavigation(n===`channels`?0:1,e),initialValues:selectedInSection(n,r,a),options:sectionRows(n,r)};n===`channels`&&(s.description=SECTIONS.channels.description),e.notices!==void 0&&(s.notices=e.notices);try{return replaceSectionSelection({section:n,catalog:r,selected:a,addresses:await i.select(s)}),`forward`}catch(e){if(!(e instanceof PlannerNavigationError))throw e;return replaceSectionSelection({section:n,catalog:r,selected:a,addresses:e.values.map(e=>{if(typeof e!=`string`)throw Error(`Registry planner returned a non-string item.`);return e})}),e.direction}}async function editPlan(e){let r=`channels`,i=e.notices;for(;;){if(r!==`review`){try{let t=await editSection({...e,section:r,notices:i});i=void 0,t===`back`?r===`integrations`&&(r=`channels`):r=r===`channels`?`integrations`:`review`}catch(e){if(!(e instanceof WizardCancelledError))throw e;return`cancelled`}continue}try{let t=e.selected.size>0,n=[...e.selected].map(t=>{let n=e.itemsByAddress.get(t);if(n===void 0)throw Error(`Registry item "${t}" is no longer available.`);return n}),r=n.filter(e=>e.name.startsWith(`channel/`)),a=n.filter(e=>!e.name.startsWith(`channel/`)),o=[...r.length===0?[]:[{label:`Channels`,value:r.map(label).join(`, `)}],...a.length===0?[]:[{label:`Integrations`,value:a.map(label).join(`, `)}]],s={message:e.plannerContext?.reviewMessage??`Review additions`,metadata:[...e.plannerContext?.reviewMetadata??[],...o],navigation:plannerNavigation(2,e),options:[{value:`install`,label:t?e.plannerContext?.primaryActionLabel??`Install and set up`:e.plannerContext?.emptyActionLabel??`Finish without adding`},{value:`back`,label:`Back`}]};t||(s.description=`No channels or integrations selected.`),i!==void 0&&(s.notices=i);let c=await e.prompter.select(s);if(i=void 0,c===`install`)return`install`}catch(e){if(e instanceof PlannerNavigationError&&e.direction===`back`){r=`integrations`;continue}if(!(e instanceof WizardCancelledError))throw e;return`cancelled`}r=`integrations`}}function hasSettledOutcomes(e){return e!==void 0&&(e.items.length>0||e.failures.length>0||e.outcomes!==void 0)}async function runRegistryFlow(t){let a;try{let o=t.initialAddress?.trim(),s;if(o!==void 0&&o!==``){if(await t.prompter.select({message:`Add ${o}?`,options:[{value:`install`,label:`Install and set up`},{value:`cancel`,label:`Cancel`}]})===`cancel`)return{kind:`cancelled`};s=[{address:o,name:o,source:`Registry`}]}else{let e=t.deps?.browseRegistryCatalog??(await import(`#cli/commands/registry.js`)).browseRegistryCatalog,n=await withSpinner(t.prompter,`Loading registry…`,()=>e(t.appRoot)),r=[...n.items],a=new Set,o=n.errors.map(e=>({tone:`warning`,text:`${e.registry}: ${e.message}`})),c=new Map(r.map(e=>[e.address,e]));if(await editPlan({prompter:t.prompter,catalog:r,itemsByAddress:c,selected:a,notices:o,plannerContext:t.plannerContext})!==`install`)return{kind:`cancelled`};s=[...a].map(e=>{let t=c.get(e);if(t===void 0)throw Error(`Registry item "${e}" is no longer available.`);return t})}let c=t.deps?.installRegistryItem??(await import(`#cli/commands/registry.js`)).installRegistryItem;a=createRegistrySession({detectDeployment:t.deps?.detectDeployment??(await import(`#setup/project-resolution.js`)).detectDeployment,runDeployFlow:t.deps?.runDeployFlow??(await import(`./deploy.js`)).runDeployFlow});let l=a;for(let[e,i]of s.entries()){t.signal?.throwIfAborted(),t.onItemStart?.(i,e,s.length);try{let install=(e=t.signal)=>c(t.appRoot,i.address,{silent:!0,prompter:t.prompter,signal:e}),run=()=>t.runItem===void 0?install():t.runItem(install),e=await(t.prompter.withExclusiveTerminal?.(run)??run());l.add(label(i),e.output,e.setup)}catch(e){if(t.signal?.throwIfAborted(),e instanceof WizardCancelledError){l.addCancellation(label(i));continue}if(e instanceof HumanActionRequiredError)throw e;let a=(e instanceof Error?e.message:String(e)).trim()||`Installation failed.`,o=a.split(`
2
2
  `).find(e=>e.trim()!==``);l.addFailure(label(i),a);let s={message:`Couldn't add ${label(i)}`,options:[{value:`skip`,label:`Skip ${label(i)}`},{value:`cancel`,label:`Cancel setup`}]};if(o!==void 0&&(s.description=o),await t.prompter.select(s)===`cancel`)return{kind:`done`,result:{...l.result(),cancelled:!0}}}}return{kind:`done`,result:await l.continueAfterInstall({appRoot:t.appRoot,prompter:t.prompter,signal:t.signal})}}catch(e){if(e instanceof WizardCancelledError){let e=a?.result();return hasSettledOutcomes(e)?{kind:`done`,result:{...e,cancelled:!0}}:{kind:`cancelled`}}let t=a?.result();throw hasSettledOutcomes(t)?new RegistryFlowFailedError(e,t):e}}export{RegistryFlowFailedError,runRegistryFlow};
@@ -0,0 +1,15 @@
1
+ import { type SetupApplyContext, type SetupPrepareContext } from "../types.js";
2
+ import { applyFileMemoryBlob, type FileMemoryBlobPlan, prepareFileMemoryBlob } from "./vercel.js";
3
+ export interface FileMemorySetupDeps {
4
+ readonly applyBlob: typeof applyFileMemoryBlob;
5
+ readonly prepareBlob: typeof prepareFileMemoryBlob;
6
+ }
7
+ export declare function prepareFileMemorySetup(context: SetupPrepareContext, deps?: FileMemorySetupDeps): Promise<FileMemoryBlobPlan>;
8
+ export declare function applyFileMemorySetup(plan: FileMemoryBlobPlan, context: SetupApplyContext, deps?: FileMemorySetupDeps): Promise<{
9
+ deploymentRequired: true;
10
+ facts: {
11
+ label: string;
12
+ value: string;
13
+ }[];
14
+ }>;
15
+ export declare const FILE_MEMORY_SETUP: import("../types.js").SetupIntegration;
@@ -0,0 +1,2 @@
1
+ import{defineSetupIntegration}from"../types.js";import{FILE_MEMORY_BLOB_ENVIRONMENTS,applyFileMemoryBlob,prepareFileMemoryBlob}from"./vercel.js";const defaultDeps={applyBlob:applyFileMemoryBlob,prepareBlob:prepareFileMemoryBlob};async function prepareFileMemorySetup(e,t=defaultDeps){let n=await e.resolveVercelProject(`file memory`);return await t.prepareBlob({appRoot:e.appRoot,project:n,signal:e.signal})}async function applyFileMemorySetup(e,n,r=defaultDeps){n.presenter.note([`Project: ${e.projectName} (${e.project.projectId})`,`Store: ${e.storeName}`,`Region: ${e.region}`,`Environments: ${FILE_MEMORY_BLOB_ENVIRONMENTS.join(`, `)}`,`Setup will create or connect a private Vercel Blob resource. Blob usage may incur charges.`].join(`
2
+ `),`File-memory storage`),e.regionWarning!==void 0&&n.presenter.log.warning(e.regionWarning);let i=await r.applyBlob({appRoot:n.appRoot,log:n.presenter.log,plan:e,signal:n.signal}),a=i.action===`create`?`Created`:i.action===`repair`?`Repaired`:`Reused`;return{deploymentRequired:!0,facts:[{label:`Blob store`,value:i.store.name},{label:`Vercel project`,value:e.projectName},{label:`Region`,value:i.store.region},{label:`Environments`,value:FILE_MEMORY_BLOB_ENVIRONMENTS.join(`, `)},{label:`Setup result`,value:a}]}}const FILE_MEMORY_SETUP=defineSetupIntegration({kind:`file-memory`,label:`File memory`,hint:`Store durable facts in a private Vercel Blob store`,prepare:prepareFileMemorySetup,apply:applyFileMemorySetup});export{FILE_MEMORY_SETUP,applyFileMemorySetup,prepareFileMemorySetup};
@@ -0,0 +1,103 @@
1
+ import { z } from "#compiled/zod/index.js";
2
+ import { type ChannelSetupLog } from "#setup/cli/index.js";
3
+ import type { VercelProjectReference } from "#setup/project-resolution.js";
4
+ import { type RunVercelOptions } from "#setup/primitives/run-vercel.js";
5
+ export declare const FILE_MEMORY_BLOB_PREFIX = "EVE_MEMORY_";
6
+ export declare const FILE_MEMORY_BLOB_ENVIRONMENTS: readonly ["production", "preview", "development"];
7
+ declare const ProjectSchema: z.ZodObject<{
8
+ id: z.ZodString;
9
+ name: z.ZodString;
10
+ resourceConfig: z.ZodOptional<z.ZodObject<{
11
+ functionDefaultRegions: z.ZodOptional<z.ZodArray<z.ZodString>>;
12
+ }, z.core.$strip>>;
13
+ defaultResourceConfig: z.ZodOptional<z.ZodObject<{
14
+ functionDefaultRegions: z.ZodOptional<z.ZodArray<z.ZodString>>;
15
+ }, z.core.$strip>>;
16
+ }, z.core.$loose>;
17
+ declare const StoreReferenceSchema: z.ZodObject<{
18
+ id: z.ZodString;
19
+ name: z.ZodString;
20
+ projects: z.ZodDefault<z.ZodArray<z.ZodObject<{
21
+ id: z.ZodString;
22
+ name: z.ZodString;
23
+ }, z.core.$loose>>>;
24
+ region: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$loose>;
26
+ declare const StoreSchema: z.ZodObject<{
27
+ access: z.ZodEnum<{
28
+ private: "private";
29
+ public: "public";
30
+ }>;
31
+ id: z.ZodString;
32
+ name: z.ZodString;
33
+ region: z.ZodString;
34
+ type: z.ZodOptional<z.ZodString>;
35
+ }, z.core.$loose>;
36
+ declare const StoreConnectionSchema: z.ZodObject<{
37
+ envVarEnvironments: z.ZodDefault<z.ZodArray<z.ZodString>>;
38
+ envVarPrefix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
39
+ id: z.ZodOptional<z.ZodString>;
40
+ projectId: z.ZodString;
41
+ }, z.core.$loose>;
42
+ export type VercelProjectConfiguration = z.infer<typeof ProjectSchema>;
43
+ export type BlobStoreReference = z.infer<typeof StoreReferenceSchema>;
44
+ export type BlobStore = z.infer<typeof StoreSchema>;
45
+ export type BlobStoreConnection = z.infer<typeof StoreConnectionSchema>;
46
+ export interface FileMemoryVercelClient {
47
+ createStore(input: {
48
+ name: string;
49
+ region: string;
50
+ }): Promise<BlobStore>;
51
+ connectStore(input: {
52
+ environments: readonly string[];
53
+ prefix: string;
54
+ projectId: string;
55
+ storeName: string;
56
+ }): Promise<void>;
57
+ getConnections(storeId: string): Promise<readonly BlobStoreConnection[]>;
58
+ getProject(): Promise<VercelProjectConfiguration>;
59
+ getStore(storeId: string): Promise<BlobStore>;
60
+ listStores(): Promise<readonly BlobStoreReference[]>;
61
+ pullEnvironment(): Promise<void>;
62
+ }
63
+ export type FileMemoryBlobAction = "create" | "repair" | "reuse";
64
+ export interface FileMemoryBlobPlan {
65
+ readonly action: FileMemoryBlobAction;
66
+ readonly project: VercelProjectReference;
67
+ readonly projectName: string;
68
+ readonly region: string;
69
+ readonly storeId?: string;
70
+ readonly storeName: string;
71
+ readonly regionWarning?: string;
72
+ }
73
+ export interface FileMemoryBlobResult {
74
+ readonly action: FileMemoryBlobAction;
75
+ readonly store: BlobStore;
76
+ }
77
+ interface FileMemoryVercelClientInput {
78
+ readonly appRoot: string;
79
+ readonly project: VercelProjectReference;
80
+ readonly signal?: AbortSignal;
81
+ readonly onOutput?: RunVercelOptions["onOutput"];
82
+ }
83
+ export declare function createFileMemoryVercelClient(input: FileMemoryVercelClientInput): FileMemoryVercelClient;
84
+ export declare function resolveFileMemoryRegion(input: {
85
+ readonly localRegions?: readonly string[];
86
+ readonly project: Pick<VercelProjectConfiguration, "defaultResourceConfig" | "resourceConfig">;
87
+ }): string;
88
+ export declare function fileMemoryStoreName(projectName: string, projectId: string): string;
89
+ export declare function prepareFileMemoryBlob(input: {
90
+ readonly appRoot: string;
91
+ readonly project: VercelProjectReference;
92
+ readonly client?: FileMemoryVercelClient;
93
+ readonly localRegions?: () => Promise<readonly string[] | undefined>;
94
+ readonly signal?: AbortSignal;
95
+ }): Promise<FileMemoryBlobPlan>;
96
+ export declare function applyFileMemoryBlob(input: {
97
+ readonly client?: FileMemoryVercelClient;
98
+ readonly log: ChannelSetupLog;
99
+ readonly plan: FileMemoryBlobPlan;
100
+ readonly appRoot: string;
101
+ readonly signal?: AbortSignal;
102
+ }): Promise<FileMemoryBlobResult>;
103
+ export {};
@@ -0,0 +1 @@
1
+ import{readFile}from"node:fs/promises";import{z}from"#compiled/zod/index.js";import{join}from"node:path";import{captureVercel}from"#setup/primitives/run-vercel.js";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";const FILE_MEMORY_BLOB_PREFIX=`EVE_MEMORY_`,FILE_MEMORY_BLOB_ENVIRONMENTS=[`production`,`preview`,`development`],ProjectSchema=z.object({id:z.string().min(1),name:z.string().min(1),resourceConfig:z.object({functionDefaultRegions:z.array(z.string().min(1)).optional()}).optional(),defaultResourceConfig:z.object({functionDefaultRegions:z.array(z.string().min(1)).optional()}).optional()}).passthrough(),StoreReferenceSchema=z.object({id:z.string().min(1),name:z.string().min(1),projects:z.array(z.object({id:z.string().min(1),name:z.string().min(1)}).passthrough()).default([]),region:z.string().min(1).optional()}).passthrough(),StoreSchema=z.object({access:z.enum([`private`,`public`]),id:z.string().min(1),name:z.string().min(1),region:z.string().min(1),type:z.string().optional()}).passthrough(),StoreConnectionSchema=z.object({envVarEnvironments:z.array(z.string()).default([]),envVarPrefix:z.string().nullish(),id:z.string().optional(),projectId:z.string().min(1)}).passthrough();function parseJson(e,t,n){try{return t.parse(JSON.parse(e))}catch{throw Error(`Vercel returned invalid ${n} data. Upgrade the Vercel CLI and try again.`)}}function commandFailure(e,t){let n=[t.failure.stderr,t.failure.stdout,t.failure.message].find(e=>e.trim().length>0);return Error(n===void 0?`${e} failed.`:`${e} failed: ${n.trim()}`)}function createFileMemoryVercelClient(e){let n={cwd:e.appRoot,nonInteractive:!0,onOutput:e.onOutput,signal:e.signal},i=[`--scope`,e.project.orgId];async function captureJson(t,i,a,o){let s=await captureVercel(t,{...n,stdin:o});if(e.signal?.throwIfAborted(),!s.ok)throw commandFailure(a,s);return parseJson(s.stdout,i,a.toLowerCase())}async function captureMutation(t,i){let a=await captureVercel(t,n);if(e.signal?.throwIfAborted(),!a.ok)throw commandFailure(i,a)}return{async createStore({name:e,region:n}){return(await captureJson([`api`,`/v1/storage/stores/blob`,`-X`,`POST`,`--input`,`-`,`--raw`,...i],z.object({store:StoreSchema}),`Vercel Blob store creation`,JSON.stringify({access:`private`,name:e,region:n}))).store},async connectStore({environments:e,prefix:t,projectId:n,storeName:r}){await captureMutation([`integration-resource`,`connect`,r,n,`--prefix`,t,...e.flatMap(e=>[`--environment`,e]),`--yes`,`--json`,...i],`Vercel Blob project connection`)},async getConnections(e){return(await captureJson([`api`,`/v1/storage/stores/${encodeURIComponent(e)}/connections`,`--raw`,...i],z.object({connections:z.array(StoreConnectionSchema)}),`Vercel Blob connections lookup`)).connections},async getProject(){return await captureJson([`api`,`/v9/projects/${encodeURIComponent(e.project.projectId)}?teamId=${encodeURIComponent(e.project.orgId)}`,`--raw`,...i],ProjectSchema,`Vercel project lookup`)},async getStore(e){return(await captureJson([`api`,`/v1/storage/stores/${encodeURIComponent(e)}`,`--raw`,...i],z.object({store:StoreSchema}),`Vercel Blob store lookup`)).store},async listStores(){return(await captureJson([`blob`,`list-stores`,`--all`,`--json`,...i],z.object({stores:z.array(StoreReferenceSchema)}),`Vercel Blob stores lookup`)).stores},async pullEnvironment(){await captureMutation([`env`,`pull`,`.env.local`,`--yes`,`--project`,e.project.projectId,...i],`Vercel environment pull`)}}}function resolveFileMemoryRegion(e){return e.localRegions?.[0]??e.project.resourceConfig?.functionDefaultRegions?.[0]??e.project.defaultResourceConfig?.functionDefaultRegions?.[0]??`iad1`}function fileMemoryStoreName(e,t){let n=e.toLowerCase().replace(/[^a-z0-9]+/gu,`-`).replace(/^-+|-+$/gu,``)||`project`,r=t.toLowerCase().replace(/[^a-z0-9]+/gu,``).slice(-8)||`memory`,i=12+r.length;return`eve-memory-${n.slice(0,64-i).replace(/-+$/gu,``)}-${r}`}async function readLocalRegions(r){try{let i=await readFile(join(r,`vercel.json`),`utf8`),a=z.object({regions:z.array(z.string().min(1)).optional()}).safeParse(JSON.parse(i));if(!a.success)throw Error("vercel.json has an invalid `regions` value.");return a.data.regions}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e instanceof SyntaxError?Error(`vercel.json contains invalid JSON.`):e}}function hasProject(e,t){return e.projects.some(e=>e.id===t)}function exactConnection(e,t){return e.projectId===t&&e.envVarPrefix===`EVE_MEMORY_`}function isEveMemoryConnection(e,t){return e.projectId===t&&e.envVarPrefix?.startsWith(`EVE_MEMORY_`)===!0}function assertPrivateBlobStore(e){if(e.type!==void 0&&e.type!==`blob`)throw Error(`Vercel resource "${e.name}" uses the eve file-memory prefix but is not a Blob store. Disconnect that binding before retrying.`);if(e.access!==`private`)throw Error(`Vercel Blob store "${e.name}" is public. Disconnect an eve-prefixed binding or choose a private store before retrying; eve will not replace it automatically.`)}function assertCompleteConnection(e,t){if(FILE_MEMORY_BLOB_ENVIRONMENTS.filter(t=>!e.envVarEnvironments.includes(t)).length>0)throw Error(`Vercel Blob store "${t}" has an incomplete ${FILE_MEMORY_BLOB_PREFIX} connection. Reconnect it for production, preview, and development before retrying; eve will not disconnect or replace it automatically.`)}async function inspectStore(e,t){let[n,r]=await Promise.all([t.getStore(e.id),t.getConnections(e.id)]);return{connections:r,store:n}}async function prepareFileMemoryBlob(e){let t=e.client??createFileMemoryVercelClient({appRoot:e.appRoot,project:e.project,signal:e.signal}),[n,r,i]=await Promise.all([t.getProject(),e.localRegions?.()??readLocalRegions(e.appRoot),t.listStores()]);if(e.signal?.throwIfAborted(),n.id!==e.project.projectId)throw Error(`Vercel returned project "${n.id}" while file-memory setup expected "${e.project.projectId}". Relink the project and retry.`);let a=resolveFileMemoryRegion({localRegions:r,project:n}),s=fileMemoryStoreName(n.name,n.id),c=i.filter(t=>t.name===s||hasProject(t,e.project.projectId)),l=await Promise.all(c.map(async e=>await inspectStore(e,t))),u=l.flatMap(t=>t.connections.filter(t=>exactConnection(t,e.project.projectId)).map(e=>({candidate:t,connection:e})));if(u.length>1)throw Error(`Multiple Vercel Blob connections target project "${n.name}" with prefix ${FILE_MEMORY_BLOB_PREFIX}. Disconnect the extra binding before retrying.`);if(u.length===1){let t=u[0];if(t===void 0)throw Error(`Expected an eve file-memory Blob store.`);let{candidate:r,connection:i}=t;assertPrivateBlobStore(r.store),assertCompleteConnection(i,r.store.name);let o=r.store.region===a?void 0:`The existing file-memory store remains in ${r.store.region}; the project now prefers ${a}. eve preserved the store to avoid memory loss.`;return{action:`reuse`,project:e.project,projectName:n.name,region:r.store.region,regionWarning:o,storeId:r.store.id,storeName:r.store.name}}let d=l.find(({connections:t})=>t.some(t=>isEveMemoryConnection(t,e.project.projectId)));if(d!==void 0)throw Error(`Vercel resource "${d.store.name}" has an incompatible eve file-memory prefix. Disconnect it before retrying; eve will not change an ambiguous binding automatically.`);let f=l.filter(({store:e})=>e.name===s);if(f.length>1)throw Error(`Multiple Vercel Blob stores match the expected file-memory name "${s}". Rename or remove the duplicate before retrying.`);if(f.length===1){let t=f[0];if(t===void 0)throw Error(`Expected a file-memory Blob store candidate.`);if(assertPrivateBlobStore(t.store),t.store.region!==a)throw Error(`Unconnected Vercel Blob store "${s}" is in ${t.store.region}, but project "${n.name}" uses ${a}. Rename that store or connect it manually; eve will not replace it automatically.`);if(t.connections.length>0)throw Error(`Vercel Blob store "${s}" already has a project connection, so eve cannot safely adopt it. Disconnect it or choose a different store before retrying.`);return{action:`repair`,project:e.project,projectName:n.name,region:a,storeId:t.store.id,storeName:s}}return{action:`create`,project:e.project,projectName:n.name,region:a,storeName:s}}async function verifyFileMemoryBlob(e,t,n){let[r,i]=await Promise.all([n.getStore(t),n.getConnections(t)]);if(assertPrivateBlobStore(r),r.name!==e.storeName)throw Error(`Vercel connected an unexpected Blob store named "${r.name}".`);if(r.region!==e.region)throw Error(`Vercel Blob store "${r.name}" is in ${r.region}, but setup expected ${e.region}.`);let a=i.filter(t=>exactConnection(t,e.project.projectId));if(a.length!==1)throw Error(`Vercel Blob store "${r.name}" does not have exactly one ${FILE_MEMORY_BLOB_PREFIX} connection to project "${e.projectName}".`);let s=a[0];if(s===void 0)throw Error(`Expected a verified eve file-memory connection.`);return assertCompleteConnection(s,r.name),r}async function applyFileMemoryBlob(e){let t=createPromptCommandOutput(e.log),n=e.client??createFileMemoryVercelClient({appRoot:e.appRoot,onOutput:t,project:e.plan.project,signal:e.signal}),r=e.plan.storeId;if(e.plan.action===`create`){let t=await withPhase(e.log,`Creating private Vercel Blob store...`,async()=>await n.createStore({name:e.plan.storeName,region:e.plan.region}));if(assertPrivateBlobStore(t),t.name!==e.plan.storeName||t.region!==e.plan.region)throw Error(`Vercel created Blob store "${t.name}" in ${t.region}, but setup expected "${e.plan.storeName}" in ${e.plan.region}.`);r=t.id}if(r===void 0)throw Error(`File-memory Blob setup did not resolve a store ID.`);e.plan.action!==`reuse`&&await withPhase(e.log,`Connecting file memory to the Vercel project...`,async()=>await n.connectStore({environments:FILE_MEMORY_BLOB_ENVIRONMENTS,prefix:FILE_MEMORY_BLOB_PREFIX,projectId:e.plan.project.projectId,storeName:e.plan.storeName})),await withPhase(e.log,`Pulling Vercel environment variables...`,async()=>await n.pullEnvironment());let c=await withPhase(e.log,`Verifying file-memory storage...`,async()=>await verifyFileMemoryBlob(e.plan,r,n));return{action:e.plan.action,store:c}}export{FILE_MEMORY_BLOB_ENVIRONMENTS,FILE_MEMORY_BLOB_PREFIX,applyFileMemoryBlob,createFileMemoryVercelClient,fileMemoryStoreName,prepareFileMemoryBlob,resolveFileMemoryRegion};
@@ -1 +1 @@
1
- import{DISCORD_SETUP}from"./discord/setup.js";import{GITHUB_SETUP}from"./github/setup.js";import{LINEAR_SETUP}from"./linear/setup.js";import{LINQ_SETUP}from"./linq/setup.js";import{PHOTON_SETUP}from"./photon/setup.js";import{SHOPIFY_SETUP}from"./shopify/setup.js";import{SLACK_SETUP}from"./slack/setup.js";import{WEB_SETUP}from"./web/setup.js";import{createSetupContexts,createSetupPresenter}from"./shared/ui.js";const SETUP_INTEGRATIONS=[WEB_SETUP,SLACK_SETUP,DISCORD_SETUP,GITHUB_SETUP,LINEAR_SETUP,LINQ_SETUP,PHOTON_SETUP,SHOPIFY_SETUP];function setupIntegration(e){let t=SETUP_INTEGRATIONS.find(t=>t.kind===e);if(t===void 0)throw Error(`Integration setup "${e}" is not available in this version of eve. Upgrade eve and try again.`);return t}export{SETUP_INTEGRATIONS,createSetupContexts,createSetupPresenter,setupIntegration};
1
+ import{DISCORD_SETUP}from"./discord/setup.js";import{FILE_MEMORY_SETUP}from"./file-memory/setup.js";import{GITHUB_SETUP}from"./github/setup.js";import{LINEAR_SETUP}from"./linear/setup.js";import{LINQ_SETUP}from"./linq/setup.js";import{PHOTON_SETUP}from"./photon/setup.js";import{SHOPIFY_SETUP}from"./shopify/setup.js";import{SLACK_SETUP}from"./slack/setup.js";import{WEB_SETUP}from"./web/setup.js";import{createSetupContexts,createSetupPresenter}from"./shared/ui.js";const SETUP_INTEGRATIONS=[WEB_SETUP,FILE_MEMORY_SETUP,SLACK_SETUP,DISCORD_SETUP,GITHUB_SETUP,LINEAR_SETUP,LINQ_SETUP,PHOTON_SETUP,SHOPIFY_SETUP];function setupIntegration(e){let t=SETUP_INTEGRATIONS.find(t=>t.kind===e);if(t===void 0)throw Error(`Integration setup "${e}" is not available in this version of eve. Upgrade eve and try again.`);return t}export{SETUP_INTEGRATIONS,createSetupContexts,createSetupPresenter,setupIntegration};
@@ -1 +1 @@
1
- import{SetupPrerequisiteRequired}from"./prerequisite.js";import{readProjectLink}from"#setup/project-resolution.js";const defaultDeps={readProjectLink};async function resolveIntegrationVercelProject(e){e.signal?.throwIfAborted();let t=await(e.deps??defaultDeps).readProjectLink(e.appRoot);if(t!==void 0)return t;throw new SetupPrerequisiteRequired({kind:`command`,code:`vercel-project-link`,message:`Vercel Connect setup requires a linked Vercel project. Run \`eve link\`, then retry ${e.integration} setup.`,command:`eve link`})}export{resolveIntegrationVercelProject};
1
+ import{SetupPrerequisiteRequired}from"./prerequisite.js";import{readProjectLink}from"#setup/project-resolution.js";const defaultDeps={readProjectLink};async function resolveIntegrationVercelProject(e){e.signal?.throwIfAborted();let t=await(e.deps??defaultDeps).readProjectLink(e.appRoot);if(t!==void 0)return t;throw new SetupPrerequisiteRequired({kind:`command`,code:`vercel-project-link`,message:`${e.integration} setup requires a linked Vercel project. Run \`eve link\`, then retry.`,command:`eve link`})}export{resolveIntegrationVercelProject};
@@ -1 +1 @@
1
- import{connectionEntries,connectionProtocols}from"../../../packages/eve-catalog/src/index.js";const SUPPORTED_PROTOCOLS=[`mcp`],CUSTOM_CONNECTION_SLUG=`custom`,CONNECTION_AUTH={linear:{kind:`connect`,connector:`linear`,service:`mcp.linear.app`},notion:{kind:`connect`,connector:`notion`,service:`mcp.notion.com`},datadog:{kind:`connect`,connector:`datadog`,service:`mcp.datadoghq.com`},honeycomb:{kind:`connect`,connector:`honeycomb`,service:`mcp.honeycomb.io`}};function buildCatalogEntry(e,n,r){let i=CONNECTION_AUTH[e];if(i===void 0)throw Error(`Connection "${e}" is in the catalog but has no scaffolder auth overlay.`);let a={slug:e,label:n,protocols:connectionProtocols(r),description:r.description,auth:i};return r.mcp&&(a.mcp={url:r.mcp.url}),r.openapi&&(a.openapi={spec:r.openapi.spec,baseUrl:r.openapi.baseUrl}),a}const CONNECTION_CATALOG=connectionEntries().filter(e=>e.surfaces.scaffoldable).map(e=>{if(e.connection===void 0)throw Error(`Catalog connection "${e.slug}" is missing its connection identity.`);return buildCatalogEntry(e.slug,e.name,e.connection)}),CATALOG_BY_SLUG=new Map(CONNECTION_CATALOG.map(e=>[e.slug,e]));function getCatalogEntry(e){return CATALOG_BY_SLUG.get(e)}function catalogSlugs(){return CONNECTION_CATALOG.map(e=>e.slug)}function effectiveProtocols(e){let t=e===void 0||e.length===0?SUPPORTED_PROTOCOLS:e;return SUPPORTED_PROTOCOLS.filter(e=>t.includes(e))}const CONNECTION_SLUG_PATTERN=/^[a-z][a-z0-9-]{0,63}$/;function isValidConnectionSlug(e){return CONNECTION_SLUG_PATTERN.test(e)}function connectorServiceForEntry(e){if(e.auth.kind===`connect`)return e.auth.service?e.auth.service:mcpServiceHost(e.mcp?.url)}function canonicalConnectorNameForEntry(e){if(e.auth?.kind!==`connect`)return;let t=e.auth.connector.trim();return t.length>0?t:void 0}function mcpServiceHost(e){if(e)try{return new URL(e).host||void 0}catch{return}}function endpointForProtocol(e,t){return t===`mcp`?e.mcp??null:e.openapi??null}export{CONNECTION_CATALOG,CUSTOM_CONNECTION_SLUG,SUPPORTED_PROTOCOLS,canonicalConnectorNameForEntry,catalogSlugs,connectorServiceForEntry,effectiveProtocols,endpointForProtocol,getCatalogEntry,isValidConnectionSlug,mcpServiceHost};
1
+ import{connectionEntries,connectionProtocols}from"../../../packages/eve-catalog/src/index.js";const SUPPORTED_PROTOCOLS=[`mcp`],CUSTOM_CONNECTION_SLUG=`custom`,CONNECTION_AUTH={"browser-use":{kind:`header`,headers:[{header:`x-browser-use-api-key`,envVar:`BROWSER_USE_API_KEY`}]},linear:{kind:`connect`,connector:`linear`,service:`mcp.linear.app`},notion:{kind:`connect`,connector:`notion`,service:`mcp.notion.com`},datadog:{kind:`connect`,connector:`datadog`,service:`mcp.datadoghq.com`},honeycomb:{kind:`connect`,connector:`honeycomb`,service:`mcp.honeycomb.io`}};function buildCatalogEntry(e,n,r){let i=CONNECTION_AUTH[e];if(i===void 0)throw Error(`Connection "${e}" is in the catalog but has no scaffolder auth overlay.`);let a={slug:e,label:n,protocols:connectionProtocols(r),description:r.description,auth:i};return r.mcp&&(a.mcp={url:r.mcp.url}),r.openapi&&(a.openapi={spec:r.openapi.spec,baseUrl:r.openapi.baseUrl}),a}const CONNECTION_CATALOG=connectionEntries().filter(e=>e.surfaces.scaffoldable).map(e=>{if(e.connection===void 0)throw Error(`Catalog connection "${e.slug}" is missing its connection identity.`);return buildCatalogEntry(e.slug,e.name,e.connection)}),CATALOG_BY_SLUG=new Map(CONNECTION_CATALOG.map(e=>[e.slug,e]));function getCatalogEntry(e){return CATALOG_BY_SLUG.get(e)}function catalogSlugs(){return CONNECTION_CATALOG.map(e=>e.slug)}function effectiveProtocols(e){let t=e===void 0||e.length===0?SUPPORTED_PROTOCOLS:e;return SUPPORTED_PROTOCOLS.filter(e=>t.includes(e))}const CONNECTION_SLUG_PATTERN=/^[a-z][a-z0-9-]{0,63}$/;function isValidConnectionSlug(e){return CONNECTION_SLUG_PATTERN.test(e)}function connectorServiceForEntry(e){if(e.auth.kind===`connect`)return e.auth.service?e.auth.service:mcpServiceHost(e.mcp?.url)}function canonicalConnectorNameForEntry(e){if(e.auth?.kind!==`connect`)return;let t=e.auth.connector.trim();return t.length>0?t:void 0}function mcpServiceHost(e){if(e)try{return new URL(e).host||void 0}catch{return}}function endpointForProtocol(e,t){return t===`mcp`?e.mcp??null:e.openapi??null}export{CONNECTION_CATALOG,CUSTOM_CONNECTION_SLUG,SUPPORTED_PROTOCOLS,canonicalConnectorNameForEntry,catalogSlugs,connectorServiceForEntry,effectiveProtocols,endpointForProtocol,getCatalogEntry,isValidConnectionSlug,mcpServiceHost};
@@ -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.82`,DEFAULT_CONNECT_PACKAGE_VERSION=`1.0.0`,DEFAULT_ZOD_PACKAGE_VERSION=`4.5.4`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.48.0`,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.82`,DEFAULT_CONNECT_PACKAGE_VERSION=`1.0.0`,DEFAULT_ZOD_PACKAGE_VERSION=`4.5.4`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.49.1`,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__",
@@ -82,6 +82,10 @@ part of the authored channel API.
82
82
 
83
83
  Channel operations are dynamic: every call targets whichever session currently
84
84
  owns the address. Only `send()` can create a session when the address is unowned.
85
+ Cold-start sends return their accepted candidate without waiting for address
86
+ ownership. If simultaneous sends race, eve forwards the losing candidate's
87
+ message to the winner; resolve the address after startup when you need a fixed
88
+ handle to the canonical owner.
85
89
 
86
90
  ```ts
87
91
  const source = from(threadId);
@@ -47,22 +47,26 @@ curl -X POST https://<deployment>/eve/v1/session \
47
47
  # {"ok":true,"sessionId":"wrun_A","status":"accepted"}
48
48
  ```
49
49
 
50
- The `202` response means Workflow accepted the durable run. The command inbox may still be
51
- starting, so an immediate follow-up or control request can report that the session is not active.
52
- The event-stream client retries the brief window before the new run becomes readable.
50
+ The `202` response means Workflow accepted the durable run. The route does not wait for command
51
+ inbox or continuation-token ownership. The inbox may still be starting, so an immediate follow-up
52
+ or control request can report that the session is not active. The event-stream client retries the
53
+ brief window before the new run becomes readable.
53
54
 
54
55
  Authenticated callers that may retry a create request can pass their own `operationId` for
55
- create-once semantics. The same operation under the same authenticated principal returns the
56
- active session it already created instead of dispatching the input again. The first accepted
57
- payload wins; retries with different input still return that first session. Anonymous callers
58
- cannot use `operationId`, and operation ownership expires when the session is no longer resumable.
56
+ create-once semantics. Once the operation owner is active, the same operation under the same
57
+ authenticated principal returns that session instead of dispatching the input again. The create
58
+ route does not wait for a concurrently starting request to publish ownership: simultaneous
59
+ requests can receive different accepted candidate IDs, while only the candidate that claims the
60
+ operation runs its first turn. Retry the operation after startup when you need its canonical
61
+ session ID. Anonymous callers cannot use `operationId`, and operation ownership expires when the
62
+ session is no longer resumable.
59
63
 
60
64
  ```bash
61
65
  curl -X POST https://<deployment>/eve/v1/session \
62
66
  -H "Authorization: Bearer <token>" \
63
67
  -H "Content-Type: application/json" \
64
68
  -d '{"message":"What is the weather in Paris?","operationId":"order-4213-research"}'
65
- # {"ok":true,"sessionId":"wrun_A","status":"accepted"} — retries return the same sessionId
69
+ # {"ok":true,"sessionId":"wrun_A","status":"accepted"}
66
70
  ```
67
71
 
68
72
  The first request requires `message`. A follow-up request accepts exactly one of
@@ -171,7 +171,10 @@ MCP clients receive four tools:
171
171
  | `agent_update` | `{ invocationId, responses }` | Answer the complete pending human-input batch. |
172
172
  | `agent_cancel` | `{ invocationId }` | Request cooperative cancellation of non-terminal work. |
173
173
 
174
- `agent_start` creates one task-mode eve session and returns after durable acceptance. Keep its `invocationId`, then call `agent_get` until the invocation reaches a terminal state. While the status is `working`, wait at least `pollAfterMs` before polling again.
174
+ `agent_start` creates one task-mode eve session and returns after durable acceptance without
175
+ waiting for the session continuation hook to become readable. Keep its `invocationId`, then call
176
+ `agent_get` until the invocation reaches a terminal state. While the status is `working`, wait at
177
+ least `pollAfterMs` before polling again.
175
178
 
176
179
  The invocation response is discriminated by `status`:
177
180
 
@@ -410,7 +410,7 @@ when you need the bearer token itself.
410
410
 
411
411
  ### Delivery
412
412
 
413
- The default handlers reply in-thread and show progress. Typing indicators post automatically: `Thinking…` on inbound, `Working…` on `turn.started`, a truncated reasoning snippet on `reasoning.appended`, and an action label on `actions.requested` — the tool name plus its most telling argument (`grep useEveAgent`, `read_file agent/agent.ts`), the subagent or remote-agent name for dispatched calls, and `+N more` when the model requests several actions at once. The model's own pre-tool narration, when present, takes precedence over the derived label. Reasoning snippets build progressively: extensions of at least four characters appear immediately, while smaller streamed deltas use the five-second refresh interval to avoid one Slack request per token. Override `events["reasoning.appended"]` if you prefer generic wording. Override an inbound handler or the `events` handlers to customize.
413
+ The default handlers reply in-thread and show activity. Typing indicators post automatically: `Thinking…` on inbound, `Working…` on `turn.started`, a truncated reasoning snippet on `reasoning.appended`, and an action label on `actions.requested` — the tool name plus its most telling argument (`grep useEveAgent`, `read_file agent/agent.ts`), the subagent or remote-agent name for dispatched calls, and `+N more` when the model requests several actions at once. The model's own pre-tool narration, when present, takes precedence over the derived label. Reasoning snippets build progressively: extensions of at least four characters appear immediately, while smaller streamed deltas use the five-second refresh interval to avoid one Slack request per token. Override `events["reasoning.appended"]` if you prefer generic wording. Override an inbound handler or the `events` handlers to customize.
414
414
 
415
415
  Outbound text preserves bare `@` tokens as literal text. To mention a user, embed Slack's `<@USER_ID>` syntax directly or use `channel.thread.mentionUser(userId)`.
416
416
 
@@ -58,7 +58,7 @@ You are responsible for ensuring any observability or eval provider is approved
58
58
 
59
59
  The third configurable surface, [runtime context events](#runtime-context), attaches per-model-call values to these spans.
60
60
 
61
- Built-in messaging channels classify their instrumentation metadata with an `audience`: `public`, `private`, or `unknown`. Slack public channels and Chat SDK workspace-visible threads are public; direct and private conversations are private; platform surfaces without enough visibility evidence remain unknown.
61
+ Built-in messaging channels classify their instrumentation metadata with an `audience`: `public`, `private`, or `unknown`. Slack public channels and Chat SDK workspace-visible threads are public; direct and private conversations are private; platform surfaces without enough visibility evidence remain unknown. Proactive Slack `receive` / `ctx.send` handoffs stay `unknown` unless the caller passes `audience` on the target, for example when a webhook or schedule already knows the destination channel is public.
62
62
 
63
63
  ## Channel delivery traces
64
64
 
@@ -14,6 +14,7 @@ Run `eve add` from an eve agent project. This installs the integration's depende
14
14
  eve add extension/agent-browser
15
15
  eve add linear
16
16
  eve add instrumentation/braintrust
17
+ eve add memory/file
17
18
  ```
18
19
 
19
20
  If an item is not found, `eve add` searches the available catalogs and prints close matches without installing anything.
@@ -26,6 +27,10 @@ Some integrations package several independently installable components. For exam
26
27
 
27
28
  When an official item declares an interactive setup flow or flows, eve asks whether to run them after installation and runs multiple flows in declaration order. Run the printed `eve add <item> --skip-install` command to resume a skipped or cancelled setup later; it reruns the selected components' declared flows from the beginning.
28
29
 
30
+ For example, `eve add memory/file` can provision its required storage through the same setup flow. After **Install and set up**, eve creates or reuses a dedicated private Vercel Blob store, connects production, preview, and development with namespaced credentials, pulls the environment, and offers to deploy. The review and setup transcript show the project, store, primary function region, environments, and possible Blob usage charges before provisioning.
31
+
32
+ An official item may identify optional pnpm dependencies that have build scripts. Before installation, eve asks whether to skip those optional packages, allow their build scripts, or abort. Your choice updates `ignoredOptionalDependencies` or `allowBuilds` in the pnpm workspace that owns the project. Allow build scripts only when you trust the packages named in the prompt.
33
+
29
34
  ## Automate setup
30
35
 
31
36
  Use `eve add <item> --non-interactive` when a script or coding agent cannot answer terminal prompts. It prints NDJSON events and exits with a status you can branch on:
@@ -99,4 +99,4 @@ Where an eve agent communicates with people, you may be required to disclose tha
99
99
  - [Tools](./tools): typed actions, the next capability to add
100
100
  - [Context control](./concepts/context-control): all the levers for what the model sees
101
101
  - [Skills](./skills): on-demand procedures, the counterpart to always-on instructions
102
- - [Multi-tenant memory](./patterns/multi-tenant-memory): dynamic instructions that load per-tenant memories each turn
102
+ - [Memory](./memory): provider-backed context that outlives a session, recalled before each turn