eve 0.27.0 → 0.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/src/channel/resolve-active-session.d.ts +4 -0
  3. package/dist/src/channel/resolve-active-session.js +1 -0
  4. package/dist/src/channel/routes.d.ts +11 -0
  5. package/dist/src/chunks/{use-eve-agent-PMY2WkAt.js → use-eve-agent-CbF0l_Fp.js} +66 -27
  6. package/dist/src/chunks/{use-eve-agent-AeQLLhu9.js → use-eve-agent-CgxB9WQv.js} +66 -27
  7. package/dist/src/cli/dev/tui/vercel-trusted-sources-policy.d.ts +1 -1
  8. package/dist/src/cli/dev/tui/vercel-trusted-sources.js +1 -1
  9. package/dist/src/client/index.d.ts +1 -1
  10. package/dist/src/client/open-stream.d.ts +15 -2
  11. package/dist/src/client/open-stream.js +1 -1
  12. package/dist/src/client/session.d.ts +3 -2
  13. package/dist/src/client/session.js +1 -1
  14. package/dist/src/client/types.d.ts +32 -0
  15. package/dist/src/compiled/.vendor-stamp.json +16 -16
  16. package/dist/src/compiled/@ai-sdk/anthropic/index.js +2 -2
  17. package/dist/src/compiled/@ai-sdk/google/index.d.ts +9 -2
  18. package/dist/src/compiled/@ai-sdk/google/index.js +6 -6
  19. package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +18 -3
  20. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  21. package/dist/src/compiled/@ai-sdk/openai/index.d.ts +285 -24
  22. package/dist/src/compiled/@ai-sdk/openai/index.js +2 -2
  23. package/dist/src/compiled/@ai-sdk/otel/index.js +1 -1
  24. package/dist/src/compiled/@ai-sdk/provider-utils/index.d.ts +7 -3
  25. package/dist/src/compiled/@vercel/oidc/index.js +28 -2
  26. package/dist/src/compiled/@vercel/sandbox/api-client/api-client.d.ts +11 -0
  27. package/dist/src/compiled/@vercel/sandbox/api-client/validators.d.ts +7 -0
  28. package/dist/src/compiled/@vercel/sandbox/command.d.ts +8 -0
  29. package/dist/src/compiled/@vercel/sandbox/execution-context.d.ts +116 -0
  30. package/dist/src/compiled/@vercel/sandbox/index.d.ts +3 -1
  31. package/dist/src/compiled/@vercel/sandbox/index.js +20 -19
  32. package/dist/src/compiled/@vercel/sandbox/network-policy.d.ts +13 -5
  33. package/dist/src/compiled/@vercel/sandbox/sandbox-user.d.ts +212 -0
  34. package/dist/src/compiled/@vercel/sandbox/sandbox.d.ts +133 -2
  35. package/dist/src/compiled/@vercel/sandbox/session.d.ts +2 -1
  36. package/dist/src/compiled/@vercel/sandbox/snapshot.d.ts +1 -1
  37. package/dist/src/compiled/@workflow/core/create-hook.d.ts +41 -22
  38. package/dist/src/compiled/@workflow/core/flushable-stream.d.ts +29 -0
  39. package/dist/src/compiled/@workflow/core/global.d.ts +2 -0
  40. package/dist/src/compiled/@workflow/core/index.js +2 -2
  41. package/dist/src/compiled/@workflow/core/private.d.ts +6 -17
  42. package/dist/src/compiled/@workflow/core/private.js +1 -1
  43. package/dist/src/compiled/@workflow/core/replay-payload-cache.d.ts +56 -0
  44. package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +11 -0
  45. package/dist/src/compiled/@workflow/core/runtime/helpers.d.ts +6 -4
  46. package/dist/src/compiled/@workflow/core/runtime/resume-hook.d.ts +4 -1
  47. package/dist/src/compiled/@workflow/core/runtime/step-executor.d.ts +39 -0
  48. package/dist/src/compiled/@workflow/core/runtime.js +3 -3
  49. package/dist/src/compiled/@workflow/core/serialization.d.ts +44 -4
  50. package/dist/src/compiled/@workflow/core/symbols.d.ts +13 -0
  51. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  52. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  53. package/dist/src/compiled/@workflow/errors/error-codes.d.ts +2 -0
  54. package/dist/src/compiled/@workflow/errors/index.d.ts +14 -0
  55. package/dist/src/compiled/@workflow/errors/index.js +1 -1
  56. package/dist/src/compiled/@workflow/world/events.d.ts +15 -0
  57. package/dist/src/compiled/@workflow/world/hooks.d.ts +2 -1
  58. package/dist/src/compiled/@workflow/world/interfaces.d.ts +77 -4
  59. package/dist/src/compiled/@workflow/world-local/index.js +4 -4
  60. package/dist/src/compiled/@workflow/world-vercel/index.js +16 -16
  61. package/dist/src/compiled/_chunks/node/{auth-BuQkq4Gr.js → auth-l7F2PF2b.js} +2 -2
  62. package/dist/src/compiled/_chunks/node/{version-DD-FX9rK.js → version-CjHTLx2a.js} +1 -1
  63. package/dist/src/compiled/_chunks/node/virtual-loader-Cwgujv7l.js +1 -0
  64. package/dist/src/compiled/_chunks/workflow/attribute-changes-Dk1fP6vt.js +20 -0
  65. package/dist/src/compiled/_chunks/workflow/{chunk-BHKSVoKr.js → chunk-BTyA9uPd.js} +1 -1
  66. package/dist/src/compiled/_chunks/workflow/dist-B3qkUnLJ.js +3 -0
  67. package/dist/src/compiled/_chunks/workflow/{dist-CSfKNxQW.js → dist-BX517Nmz.js} +2 -2
  68. package/dist/src/compiled/_chunks/workflow/{dist-zLevmMyU.js → dist-C_VDTghO.js} +10 -10
  69. package/dist/src/compiled/_chunks/workflow/{dist-CttsoN4D.js → dist-CrxV2rsR.js} +1 -1
  70. package/dist/src/compiled/_chunks/workflow/{functions-PT9ri9Hu.js → functions-Dv8dc_-I.js} +1 -1
  71. package/dist/src/compiled/_chunks/workflow/resume-hook-czpG5xnP.js +1 -0
  72. package/dist/src/compiled/_chunks/workflow/run-6zaMjFY6.js +1 -0
  73. package/dist/src/compiled/_chunks/workflow/sleep-B2u06FWc.js +1 -0
  74. package/dist/src/compiled/_chunks/workflow/{src-CQuMexnO.js → src-D1EltLDA.js} +1 -1
  75. package/dist/src/compiled/_chunks/workflow/{token-CaY0nA5J.js → token-Cz223z3C.js} +1 -1
  76. package/dist/src/compiled/_chunks/workflow/token-DWqA5-DA.js +1 -0
  77. package/dist/src/compiled/_chunks/workflow/token-util-B6qBs3-0.js +27 -0
  78. package/dist/src/compiled/_chunks/workflow/{undici-fb54XL8i.js → undici-C2ZhYnV9.js} +1 -1
  79. package/dist/src/compiled/_chunks/workflow/wrapper-Cc2otuUR.js +6 -0
  80. package/dist/src/compiled/env-runner/index.js +1 -1
  81. package/dist/src/compiled/env-runner/node-worker.js +1 -1
  82. package/dist/src/compiled/eventsource-parser/stream/index.d.ts +16 -3
  83. package/dist/src/compiled/jose/index.js +1 -1
  84. package/dist/src/compiler/compile-from-memory.d.ts +5 -0
  85. package/dist/src/compiler/compile-from-memory.js +1 -1
  86. package/dist/src/compiler/extension-compatibility.d.ts +10 -10
  87. package/dist/src/compiler/extension-compatibility.js +1 -1
  88. package/dist/src/execution/deliver-payloads.d.ts +1 -1
  89. package/dist/src/execution/deliver-payloads.js +1 -1
  90. package/dist/src/execution/workflow-entry.js +1 -1
  91. package/dist/src/execution/workflow-steps.js +1 -1
  92. package/dist/src/harness/session-limit-continuation.d.ts +8 -0
  93. package/dist/src/harness/session-limit-continuation.js +1 -1
  94. package/dist/src/harness/session-limit-enforcement.d.ts +9 -6
  95. package/dist/src/harness/session-limit-enforcement.js +1 -1
  96. package/dist/src/harness/stale-input-responses.d.ts +21 -3
  97. package/dist/src/harness/stale-input-responses.js +2 -2
  98. package/dist/src/harness/tool-loop.js +2 -2
  99. package/dist/src/harness/turn-cancellation.d.ts +13 -0
  100. package/dist/src/harness/turn-cancellation.js +1 -1
  101. package/dist/src/internal/application/package.js +1 -1
  102. package/dist/src/internal/authored-module-evaluation-error.d.ts +2 -0
  103. package/dist/src/internal/authored-module-evaluation-error.js +4 -0
  104. package/dist/src/internal/authored-module-loader.js +2 -2
  105. package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
  106. package/dist/src/internal/workflow-bundle/vercel-workflow-output.js +1 -1
  107. package/dist/src/node_modules/.pnpm/@vercel_sdk@1.28.8/node_modules/@vercel/sdk/esm/models/updateprojectblock.js +1 -0
  108. package/dist/src/node_modules/.pnpm/@vercel_sdk@1.28.8/node_modules/@vercel/sdk/esm/models/updateprojectprojectsbranchmatcher.js +1 -0
  109. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  110. package/dist/src/public/channels/discord/defaults.js +1 -1
  111. package/dist/src/public/channels/eve.js +2 -1
  112. package/dist/src/public/channels/linear/api.d.ts +2 -0
  113. package/dist/src/public/channels/linear/api.js +3 -1
  114. package/dist/src/public/channels/linear/defaults.js +2 -2
  115. package/dist/src/public/channels/linear/hitl.d.ts +8 -13
  116. package/dist/src/public/channels/linear/hitl.js +3 -3
  117. package/dist/src/public/channels/linear/index.d.ts +1 -1
  118. package/dist/src/public/channels/linear/index.js +1 -1
  119. package/dist/src/public/channels/linear/linearChannel.js +1 -1
  120. package/dist/src/public/channels/slack/api.d.ts +46 -2
  121. package/dist/src/public/channels/slack/api.js +1 -1
  122. package/dist/src/public/channels/slack/attachments.d.ts +6 -5
  123. package/dist/src/public/channels/slack/defaults.js +1 -1
  124. package/dist/src/public/channels/slack/inbound.d.ts +31 -0
  125. package/dist/src/public/channels/slack/inbound.js +1 -1
  126. package/dist/src/public/channels/slack/index.d.ts +2 -2
  127. package/dist/src/public/channels/slack/interactions.d.ts +2 -1
  128. package/dist/src/public/channels/slack/interactions.js +1 -1
  129. package/dist/src/public/channels/slack/slackChannel.d.ts +118 -13
  130. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  131. package/dist/src/public/channels/slack/thread.d.ts +4 -3
  132. package/dist/src/public/channels/slack/thread.js +1 -1
  133. package/dist/src/public/channels/teams/defaults.js +2 -2
  134. package/dist/src/public/channels/teams/index.d.ts +1 -1
  135. package/dist/src/public/channels/teams/teamsChannel.d.ts +7 -2
  136. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  137. package/dist/src/public/channels/telegram/defaults.js +1 -1
  138. package/dist/src/public/definitions/channel.d.ts +1 -1
  139. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +2 -1
  140. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  141. package/dist/src/setup/scaffold/create/project.js +2 -2
  142. package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
  143. package/dist/src/setup/scaffold/create/web-template.js +90 -5
  144. package/dist/src/setup/scaffold/update/channels.js +2 -2
  145. package/dist/src/setup/scaffold/update/connections.js +1 -1
  146. package/dist/src/svelte/index.js +1 -1
  147. package/dist/src/svelte/use-eve-agent.js +1 -1
  148. package/dist/src/vue/index.js +1 -1
  149. package/dist/src/vue/use-eve-agent.js +1 -1
  150. package/docs/agent-config.md +10 -5
  151. package/docs/channels/slack.mdx +115 -12
  152. package/docs/channels/teams.mdx +10 -3
  153. package/docs/guides/client/streaming.mdx +15 -0
  154. package/docs/guides/deployment/vercel.mdx +1 -1
  155. package/docs/guides/frontend/overview.mdx +2 -0
  156. package/package.json +22 -22
  157. package/dist/src/compiled/@workflow/core/step-hydration-cache.d.ts +0 -148
  158. package/dist/src/compiled/_chunks/node/virtual-loader-B8K8w9cr.js +0 -1
  159. package/dist/src/compiled/_chunks/workflow/attribute-changes-CXiYB8h9.js +0 -20
  160. package/dist/src/compiled/_chunks/workflow/dist-BYo8Gno2.js +0 -3
  161. package/dist/src/compiled/_chunks/workflow/resume-hook-CHXbXuRZ.js +0 -1
  162. package/dist/src/compiled/_chunks/workflow/run-BjtAGa3_.js +0 -1
  163. package/dist/src/compiled/_chunks/workflow/sleep-BJAv0MFP.js +0 -1
  164. package/dist/src/compiled/_chunks/workflow/token-D4VIwGlD.js +0 -1
  165. package/dist/src/compiled/_chunks/workflow/wrapper-ulTvoTdW.js +0 -6
  166. package/dist/src/node_modules/.pnpm/@vercel_sdk@1.28.1/node_modules/@vercel/sdk/esm/models/updateprojectprojectsaction.js +0 -1
  167. package/dist/src/node_modules/.pnpm/@vercel_sdk@1.28.1/node_modules/@vercel/sdk/esm/models/updateprojectprojectsoptionsallowlist.js +0 -1
  168. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/lib/primitives.js +0 -0
  169. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/lib/schemas.js +0 -0
  170. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/models/aclaction.js +0 -0
  171. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/models/sdkvalidationerror.js +0 -0
  172. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/defaultToZeroValue.js +0 -0
  173. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/fp.js +0 -0
  174. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/primitives.js +0 -0
  175. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/rfcdate.js +0 -0
  176. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/smartUnion.js +0 -0
  177. /package/dist/src/node_modules/.pnpm/{@vercel_sdk@1.28.1 → @vercel_sdk@1.28.8}/node_modules/@vercel/sdk/esm/types/unrecognized.js +0 -0
@@ -1 +1 @@
1
- function connectionProtocols(e){return[e.mcp?`mcp`:null,e.openapi?`openapi`:null].filter(e=>e!==null)}const INTEGRATIONS=[{slug:`slack`,name:`Slack`,kind:`channel`,tagline:`Mention your agent in channels and DMs, with Connect-managed auth.`,surfaces:{scaffoldable:!0,gallery:!0}},{slug:`discord`,name:`Discord`,kind:`channel`,tagline:`Run your agent as a Discord bot across servers and threads.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`teams`,name:`Microsoft Teams`,kind:`channel`,tagline:`Bring your agent into Teams chats and channels.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`telegram`,name:`Telegram`,kind:`channel`,tagline:`Connect your agent to a Telegram bot for 1:1 and group chats.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`twilio`,name:`Twilio`,kind:`channel`,tagline:`Put your agent on a phone number: SMS and speech-transcribed calls.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`github`,name:`GitHub`,kind:`channel`,tagline:`Drive your agent from issues, pull requests, and comments.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`linear-agent`,name:`Linear Agent`,kind:`channel`,tagline:`Delegate Linear issues and comments to your agent through Linear's Agent Sessions.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`eve`,name:`Web Chat`,kind:`channel`,tagline:`Embed a first-party web chat UI backed by your agent.`,surfaces:{scaffoldable:!0,gallery:!0}},{slug:`chat-sdk-gchat`,name:`Google Chat`,kind:`channel`,tagline:`Google Chat spaces and DMs via the Chat SDK.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-whatsapp`,name:`WhatsApp`,kind:`channel`,tagline:`Customer messaging through WhatsApp Business Cloud via the Chat SDK.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-x`,name:`X`,kind:`channel`,tagline:`Public mentions and DMs on X via the Chat SDK.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-messenger`,name:`Messenger`,kind:`channel`,tagline:`Facebook Messenger bots with templates, buttons, and reactions via the Chat SDK.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`agent-browser`,name:`agent-browser`,kind:`extension`,tagline:`Add browser automation tools backed by agent-browser to an eve agent.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`kernel`,name:`Kernel`,kind:`connection`,tagline:`Launch cloud browsers and automate web interactions through Kernel's MCP server.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Kernel: launch and automate cloud browsers, run Playwright, and inspect replays.`,mcp:{url:`https://mcp.onkernel.com/mcp`}}},{slug:`browser-use`,name:`Browser Use`,kind:`connection`,tagline:`Run managed browser automation tasks through Browser Use's MCP server.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Browser Use: run browser automation tasks, inspect sessions, and manage browser profiles.`,mcp:{url:`https://api.browser-use.com/v3/mcp`}}},{slug:`linear`,name:`Linear`,kind:`connection`,tagline:`Issues, projects, cycles, and comments via Linear's MCP server.`,surfaces:{scaffoldable:!0,gallery:!0},connection:{description:`Linear workspace: issues, projects, cycles, and comments.`,mcp:{url:`https://mcp.linear.app/mcp`}}},{slug:`notion`,name:`Notion`,kind:`connection`,tagline:`Search and edit Notion pages and databases over MCP or OpenAPI.`,surfaces:{scaffoldable:!0,gallery:!0},connection:{description:`Notion workspace: search and edit pages and databases.`,mcp:{url:`https://mcp.notion.com/mcp`},openapi:{spec:`https://developers.notion.com/openapi.json`,baseUrl:`https://api.notion.com`,headers:{"Notion-Version":`2022-06-28`}}}},{slug:`datadog`,name:`Datadog`,kind:`connection`,tagline:`Query metrics, monitors, and logs through Datadog's MCP server.`,surfaces:{scaffoldable:!0,gallery:!0},connection:{description:`Datadog: query metrics, monitors, logs, and incidents.`,mcp:{url:`https://mcp.datadoghq.com/api/mcp`}}},{slug:`honeycomb`,name:`Honeycomb`,kind:`connection`,tagline:`Explore traces and run queries through Honeycomb's MCP server.`,surfaces:{scaffoldable:!0,gallery:!0},connection:{description:`Honeycomb: explore traces, run queries, and inspect datasets.`,mcp:{url:`https://mcp.honeycomb.io/mcp`}}},{slug:`airtable`,name:`Airtable`,kind:`connection`,tagline:`Bases, tables, and records through Airtable's MCP server.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Airtable: bases, tables, and records.`,mcp:{url:`https://mcp.airtable.com/mcp`}}},{slug:`bitly`,name:`Bitly`,kind:`connection`,tagline:`Shorten links, generate QR Codes, and track performance.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Bitly: shorten links, generate QR Codes, and track link performance.`,mcp:{url:`https://api-ssl.bitly.com/v4/mcp`}}},{slug:`brex`,name:`Brex`,kind:`connection`,tagline:`Expenses, cards, and cash through Brex's finance automation.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Brex: expenses, cards, budgets, and cash.`,mcp:{url:`https://api.brex.com/mcp`}}},{slug:`candid`,name:`Candid`,kind:`connection`,tagline:`Research nonprofits and funders using Candid's data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Candid: research nonprofits, funders, and grants.`,mcp:{url:`https://mcp.candid.org/mcp`}}},{slug:`clickhouse`,name:`ClickHouse`,kind:`connection`,tagline:`Query and explore your ClickHouse Cloud data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`ClickHouse Cloud: query and explore databases and tables.`,mcp:{url:`https://mcp.clickhouse.cloud/mcp`}}},{slug:`cloudinary`,name:`Cloudinary`,kind:`connection`,tagline:`Manage, transform, and deliver your images and videos.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Cloudinary: manage, transform, and deliver image and video assets.`,mcp:{url:`https://asset-management.mcp.cloudinary.com/sse`}}},{slug:`coda`,name:`Coda`,kind:`connection`,tagline:`Create, search, and update docs and tables.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Coda: create, search, and update docs and tables.`,mcp:{url:`https://coda.io/apis/mcp`}}},{slug:`egnyte`,name:`Egnyte`,kind:`connection`,tagline:`Securely access and analyze Egnyte content.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Egnyte: search, access, and analyze governed content.`,mcp:{url:`https://mcp-server.egnyte.com/mcp`}}},{slug:`embat`,name:`Embat`,kind:`connection`,tagline:`Ask Embat about cash, debt, payments, and accounting.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Embat: cash, debt, payments, and accounting.`,mcp:{url:`https://tellme.embat.io/mcp`}}},{slug:`hugging-face`,name:`Hugging Face`,kind:`connection`,tagline:`Access the Hugging Face Hub and thousands of Gradio apps.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Hugging Face: models, datasets, Spaces, and Gradio apps on the Hub.`,mcp:{url:`https://huggingface.co/mcp?login&gradio=none`}}},{slug:`local-falcon`,name:`Local Falcon`,kind:`connection`,tagline:`AI visibility and local search intelligence.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Local Falcon: local search rankings and AI visibility reports.`,mcp:{url:`https://mcp.localfalcon.com`}}},{slug:`make`,name:`Make`,kind:`connection`,tagline:`Run Make scenarios and manage your Make account.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Make: run scenarios and manage automations.`,mcp:{url:`https://mcp.make.com`}}},{slug:`manufact`,name:`Manufact`,kind:`connection`,tagline:`Deploy and monitor MCP servers with Manufact.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Manufact: deploy and monitor MCP servers.`,mcp:{url:`https://mcp.manufact.com/mcp`}}},{slug:`mem0`,name:`Mem0`,kind:`connection`,tagline:`Persistent memory for AI agents and assistants.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Mem0: store and retrieve persistent agent memory.`,mcp:{url:`https://mcp.mem0.ai/mcp`}}},{slug:`miro`,name:`Miro`,kind:`connection`,tagline:`Access and create content on Miro boards.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Miro: read and create content on boards.`,mcp:{url:`https://mcp.miro.com/`}}},{slug:`mixpanel`,name:`Mixpanel`,kind:`connection`,tagline:`Analyze, query, and manage your Mixpanel data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Mixpanel: analyze, query, and manage analytics data.`,mcp:{url:`https://mcp.mixpanel.com/mcp`}}},{slug:`netlify`,name:`Netlify`,kind:`connection`,tagline:`Create, deploy, manage, and secure websites on Netlify.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Netlify: create, deploy, manage, and secure sites.`,mcp:{url:`https://netlify-mcp.netlify.app/mcp`}}},{slug:`oreilly`,name:`O'Reilly`,kind:`connection`,tagline:`Discover O'Reilly's expert learning content.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`O'Reilly: search books, courses, and learning content.`,mcp:{url:`https://api.oreilly.com/api/content-discovery/v1/mcp/`}}},{slug:`planetscale`,name:`PlanetScale`,kind:`connection`,tagline:`Authenticated access to your PlanetScale Postgres and MySQL databases.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`PlanetScale: query Postgres and MySQL databases.`,mcp:{url:`https://mcp.pscale.dev/mcp/planetscale`}}},{slug:`posthog`,name:`PostHog`,kind:`connection`,tagline:`Query, analyze, and manage your PostHog insights.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`PostHog: insights, events, and feature flags.`,mcp:{url:`https://mcp.posthog.com/mcp`}}},{slug:`postman`,name:`Postman`,kind:`connection`,tagline:`Give API context to your coding agents with Postman.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Postman: APIs, collections, and workspaces.`,mcp:{url:`https://mcp.postman.com/minimal`}}},{slug:`razorpay`,name:`Razorpay`,kind:`connection`,tagline:`Razorpay payments, settlements, and dashboard data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Razorpay: payments, settlements, and dashboard data.`,mcp:{url:`https://mcp.razorpay.com/mcp`}}},{slug:`sentry`,name:`Sentry`,kind:`connection`,tagline:`Search, query, and debug errors intelligently.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Sentry: search, query, and debug errors and issues.`,mcp:{url:`https://mcp.sentry.dev/mcp`}}},{slug:`similarweb`,name:`Similarweb`,kind:`connection`,tagline:`Real-time web, mobile app, and market data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Similarweb: web traffic, app, and market intelligence data.`,mcp:{url:`https://mcp.similarweb.com`}}},{slug:`stripe`,name:`Stripe`,kind:`connection`,tagline:`Payment processing and financial infrastructure tools.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Stripe: payments, customers, billing, and financial infrastructure.`,mcp:{url:`https://mcp.stripe.com`}}},{slug:`supabase`,name:`Supabase`,kind:`connection`,tagline:`Manage databases, authentication, and storage.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Supabase: databases, authentication, and storage.`,mcp:{url:`https://mcp.supabase.com/mcp`}}},{slug:`ticket-tailor`,name:`Ticket Tailor`,kind:`connection`,tagline:`Manage tickets, orders, and events with Ticket Tailor.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Ticket Tailor: events, tickets, and orders.`,mcp:{url:`https://mcp.tickettailor.ai/mcp`}}},{slug:`ticktick`,name:`TickTick`,kind:`connection`,tagline:`Search, create, and manage your tasks and habits in TickTick.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`TickTick: tasks, habits, and lists.`,mcp:{url:`https://mcp.ticktick.com`}}},{slug:`todoist`,name:`Todoist`,kind:`connection`,tagline:`Search, complete, and manage your tasks in Todoist.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Todoist: search, complete, and manage tasks.`,mcp:{url:`https://ai.todoist.net/mcp`}}},{slug:`webflow`,name:`Webflow`,kind:`connection`,tagline:`Manage Webflow CMS, pages, assets, and sites.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Webflow: CMS items, pages, assets, and sites.`,mcp:{url:`https://mcp.webflow.com/mcp`}}},{slug:`wix`,name:`Wix`,kind:`connection`,tagline:`Manage and build sites and apps on Wix.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Wix: manage and build sites and apps.`,mcp:{url:`https://mcp.wix.com/mcp`}}},{slug:`zapier`,name:`Zapier`,kind:`connection`,tagline:`Automate workflows across thousands of apps.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Zapier: run and manage automations across apps.`,mcp:{url:`https://mcp.zapier.com/api/v1/connect`}}},{slug:`zomato`,name:`Zomato`,kind:`connection`,tagline:`Online food ordering and delivery through Zomato.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Zomato: food ordering and delivery.`,mcp:{url:`https://mcp-server.zomato.com/mcp`}}}];new Map(INTEGRATIONS.map(e=>[e.slug,e]));function integrationsByKind(t){return INTEGRATIONS.filter(e=>e.kind===t)}function connectionEntries(){return integrationsByKind(`connection`)}function channelEntries(){return integrationsByKind(`channel`)}export{channelEntries,connectionEntries,connectionProtocols};
1
+ function connectionProtocols(e){return[e.mcp?`mcp`:null,e.openapi?`openapi`:null].filter(e=>e!==null)}const INTEGRATIONS=[{slug:`slack`,name:`Slack`,kind:`channel`,tagline:`Mention your agent in channels and DMs, with Connect-managed auth.`,surfaces:{scaffoldable:!0,gallery:!0}},{slug:`discord`,name:`Discord`,kind:`channel`,tagline:`Run your agent as a Discord bot across servers and threads.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`teams`,name:`Microsoft Teams`,kind:`channel`,tagline:`Bring your agent into Teams chats and channels.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`telegram`,name:`Telegram`,kind:`channel`,tagline:`Connect your agent to a Telegram bot for 1:1 and group chats.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`twilio`,name:`Twilio`,kind:`channel`,tagline:`Put your agent on a phone number: SMS and speech-transcribed calls.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`github`,name:`GitHub`,kind:`channel`,tagline:`Drive your agent from issues, pull requests, and comments.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`linear-agent`,name:`Linear Agent`,kind:`channel`,tagline:`Delegate Linear issues and comments to your agent through Linear's Agent Sessions.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`eve`,name:`Web Chat`,kind:`channel`,tagline:`Embed a first-party web chat UI backed by your agent.`,surfaces:{scaffoldable:!0,gallery:!0}},{slug:`chat-sdk-gchat`,name:`Google Chat`,kind:`channel`,tagline:`Google Chat spaces and DMs via the Chat SDK.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-whatsapp`,name:`WhatsApp`,kind:`channel`,tagline:`Customer messaging through WhatsApp Business Cloud via the Chat SDK.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-x`,name:`X`,kind:`channel`,tagline:`Public mentions and DMs on X via the Chat SDK.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-messenger`,name:`Messenger`,kind:`channel`,tagline:`Facebook Messenger bots with templates, buttons, and reactions via the Chat SDK.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-zernio`,name:`Zernio`,kind:`channel`,tagline:`Reach seven social and messaging platforms through one Zernio integration.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-velt`,name:`Velt`,kind:`channel`,tagline:`Add agents to anchored comments across documents, canvases, PDFs, and video.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-sendblue`,name:`Sendblue`,kind:`channel`,tagline:`Send and receive iMessage, SMS, and RCS through Sendblue.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-novu`,name:`Novu`,kind:`channel`,tagline:`Reach Slack, Teams, WhatsApp, Telegram, and email through Novu.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-liveblocks`,name:`Liveblocks`,kind:`channel`,tagline:`Bring your agent into Liveblocks comment threads, mentions, and reactions.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-linq`,name:`Linq`,kind:`channel`,tagline:`iMessage and SMS conversations, media, and tapbacks through Linq.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`chat-sdk-kapso`,name:`Kapso`,kind:`channel`,tagline:`Managed WhatsApp conversations, media, buttons, and history through Kapso.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`agent-browser`,name:`agent-browser`,kind:`extension`,tagline:`Add browser automation tools backed by agent-browser to an eve agent.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`browserbase`,name:`Browserbase`,kind:`extension`,tagline:`Search, fetch, and automate the web with Browserbase and Stagehand.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`github-tools`,name:`GitHub Tools`,kind:`extension`,tagline:`Add scoped GitHub tools with Vercel Connect authentication and approval rules.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`jetty`,name:`Jetty`,kind:`extension`,tagline:`Grade agent turns, compare experiments, and store durable evaluation trajectories.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`kernel`,name:`Kernel`,kind:`extension`,tagline:`Add a Kernel cloud browser and browser automation skills to an eve agent.`,surfaces:{scaffoldable:!1,gallery:!0}},{slug:`browser-use`,name:`Browser Use`,kind:`connection`,tagline:`Run managed browser automation tasks through Browser Use's MCP server.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Browser Use: run browser automation tasks, inspect sessions, and manage browser profiles.`,mcp:{url:`https://api.browser-use.com/v3/mcp`}}},{slug:`vercel`,name:`Vercel`,kind:`connection`,tagline:`Manage Vercel projects, deployments, and logs through Vercel's MCP server.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Vercel: manage projects and deployments, inspect logs, and search documentation.`,mcp:{url:`https://mcp.vercel.com`}}},{slug:`linear`,name:`Linear`,kind:`connection`,tagline:`Issues, projects, cycles, and comments via Linear's MCP server.`,surfaces:{scaffoldable:!0,gallery:!0},connection:{description:`Linear workspace: issues, projects, cycles, and comments.`,mcp:{url:`https://mcp.linear.app/mcp`}}},{slug:`notion`,name:`Notion`,kind:`connection`,tagline:`Search and edit Notion pages and databases over MCP or OpenAPI.`,surfaces:{scaffoldable:!0,gallery:!0},connection:{description:`Notion workspace: search and edit pages and databases.`,mcp:{url:`https://mcp.notion.com/mcp`},openapi:{spec:`https://developers.notion.com/openapi.json`,baseUrl:`https://api.notion.com`,headers:{"Notion-Version":`2022-06-28`}}}},{slug:`datadog`,name:`Datadog`,kind:`connection`,tagline:`Query metrics, monitors, and logs through Datadog's MCP server.`,surfaces:{scaffoldable:!0,gallery:!0},connection:{description:`Datadog: query metrics, monitors, logs, and incidents.`,mcp:{url:`https://mcp.datadoghq.com/api/mcp`}}},{slug:`honeycomb`,name:`Honeycomb`,kind:`connection`,tagline:`Explore traces and run queries through Honeycomb's MCP server.`,surfaces:{scaffoldable:!0,gallery:!0},connection:{description:`Honeycomb: explore traces, run queries, and inspect datasets.`,mcp:{url:`https://mcp.honeycomb.io/mcp`}}},{slug:`airtable`,name:`Airtable`,kind:`connection`,tagline:`Bases, tables, and records through Airtable's MCP server.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Airtable: bases, tables, and records.`,mcp:{url:`https://mcp.airtable.com/mcp`}}},{slug:`bitly`,name:`Bitly`,kind:`connection`,tagline:`Shorten links, generate QR Codes, and track performance.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Bitly: shorten links, generate QR Codes, and track link performance.`,mcp:{url:`https://api-ssl.bitly.com/v4/mcp`}}},{slug:`brex`,name:`Brex`,kind:`connection`,tagline:`Expenses, cards, and cash through Brex's finance automation.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Brex: expenses, cards, budgets, and cash.`,mcp:{url:`https://api.brex.com/mcp`}}},{slug:`candid`,name:`Candid`,kind:`connection`,tagline:`Research nonprofits and funders using Candid's data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Candid: research nonprofits, funders, and grants.`,mcp:{url:`https://mcp.candid.org/mcp`}}},{slug:`clickhouse`,name:`ClickHouse`,kind:`connection`,tagline:`Query and explore your ClickHouse Cloud data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`ClickHouse Cloud: query and explore databases and tables.`,mcp:{url:`https://mcp.clickhouse.cloud/mcp`}}},{slug:`cloudinary`,name:`Cloudinary`,kind:`connection`,tagline:`Manage, transform, and deliver your images and videos.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Cloudinary: manage, transform, and deliver image and video assets.`,mcp:{url:`https://asset-management.mcp.cloudinary.com/sse`}}},{slug:`coda`,name:`Coda`,kind:`connection`,tagline:`Create, search, and update docs and tables.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Coda: create, search, and update docs and tables.`,mcp:{url:`https://coda.io/apis/mcp`}}},{slug:`egnyte`,name:`Egnyte`,kind:`connection`,tagline:`Securely access and analyze Egnyte content.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Egnyte: search, access, and analyze governed content.`,mcp:{url:`https://mcp-server.egnyte.com/mcp`}}},{slug:`embat`,name:`Embat`,kind:`connection`,tagline:`Ask Embat about cash, debt, payments, and accounting.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Embat: cash, debt, payments, and accounting.`,mcp:{url:`https://tellme.embat.io/mcp`}}},{slug:`hugging-face`,name:`Hugging Face`,kind:`connection`,tagline:`Access the Hugging Face Hub and thousands of Gradio apps.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Hugging Face: models, datasets, Spaces, and Gradio apps on the Hub.`,mcp:{url:`https://huggingface.co/mcp?login&gradio=none`}}},{slug:`local-falcon`,name:`Local Falcon`,kind:`connection`,tagline:`AI visibility and local search intelligence.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Local Falcon: local search rankings and AI visibility reports.`,mcp:{url:`https://mcp.localfalcon.com`}}},{slug:`make`,name:`Make`,kind:`connection`,tagline:`Run Make scenarios and manage your Make account.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Make: run scenarios and manage automations.`,mcp:{url:`https://mcp.make.com`}}},{slug:`manufact`,name:`Manufact`,kind:`connection`,tagline:`Deploy and monitor MCP servers with Manufact.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Manufact: deploy and monitor MCP servers.`,mcp:{url:`https://mcp.manufact.com/mcp`}}},{slug:`mem0`,name:`Mem0`,kind:`connection`,tagline:`Persistent memory for AI agents and assistants.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Mem0: store and retrieve persistent agent memory.`,mcp:{url:`https://mcp.mem0.ai/mcp`}}},{slug:`miro`,name:`Miro`,kind:`connection`,tagline:`Access and create content on Miro boards.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Miro: read and create content on boards.`,mcp:{url:`https://mcp.miro.com/`}}},{slug:`mixpanel`,name:`Mixpanel`,kind:`connection`,tagline:`Analyze, query, and manage your Mixpanel data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Mixpanel: analyze, query, and manage analytics data.`,mcp:{url:`https://mcp.mixpanel.com/mcp`}}},{slug:`netlify`,name:`Netlify`,kind:`connection`,tagline:`Create, deploy, manage, and secure websites on Netlify.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Netlify: create, deploy, manage, and secure sites.`,mcp:{url:`https://netlify-mcp.netlify.app/mcp`}}},{slug:`oreilly`,name:`O'Reilly`,kind:`connection`,tagline:`Discover O'Reilly's expert learning content.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`O'Reilly: search books, courses, and learning content.`,mcp:{url:`https://api.oreilly.com/api/content-discovery/v1/mcp/`}}},{slug:`planetscale`,name:`PlanetScale`,kind:`connection`,tagline:`Authenticated access to your PlanetScale Postgres and MySQL databases.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`PlanetScale: query Postgres and MySQL databases.`,mcp:{url:`https://mcp.pscale.dev/mcp/planetscale`}}},{slug:`posthog`,name:`PostHog`,kind:`connection`,tagline:`Query, analyze, and manage your PostHog insights.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`PostHog: insights, events, and feature flags.`,mcp:{url:`https://mcp.posthog.com/mcp`}}},{slug:`postman`,name:`Postman`,kind:`connection`,tagline:`Give API context to your coding agents with Postman.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Postman: APIs, collections, and workspaces.`,mcp:{url:`https://mcp.postman.com/minimal`}}},{slug:`razorpay`,name:`Razorpay`,kind:`connection`,tagline:`Razorpay payments, settlements, and dashboard data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Razorpay: payments, settlements, and dashboard data.`,mcp:{url:`https://mcp.razorpay.com/mcp`}}},{slug:`sentry`,name:`Sentry`,kind:`connection`,tagline:`Search, query, and debug errors intelligently.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Sentry: search, query, and debug errors and issues.`,mcp:{url:`https://mcp.sentry.dev/mcp`}}},{slug:`similarweb`,name:`Similarweb`,kind:`connection`,tagline:`Real-time web, mobile app, and market data.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Similarweb: web traffic, app, and market intelligence data.`,mcp:{url:`https://mcp.similarweb.com`}}},{slug:`stripe`,name:`Stripe`,kind:`connection`,tagline:`Payment processing and financial infrastructure tools.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Stripe: payments, customers, billing, and financial infrastructure.`,mcp:{url:`https://mcp.stripe.com`}}},{slug:`supabase`,name:`Supabase`,kind:`connection`,tagline:`Manage databases, authentication, and storage.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Supabase: databases, authentication, and storage.`,mcp:{url:`https://mcp.supabase.com/mcp`}}},{slug:`ticket-tailor`,name:`Ticket Tailor`,kind:`connection`,tagline:`Manage tickets, orders, and events with Ticket Tailor.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Ticket Tailor: events, tickets, and orders.`,mcp:{url:`https://mcp.tickettailor.ai/mcp`}}},{slug:`ticktick`,name:`TickTick`,kind:`connection`,tagline:`Search, create, and manage your tasks and habits in TickTick.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`TickTick: tasks, habits, and lists.`,mcp:{url:`https://mcp.ticktick.com`}}},{slug:`todoist`,name:`Todoist`,kind:`connection`,tagline:`Search, complete, and manage your tasks in Todoist.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Todoist: search, complete, and manage tasks.`,mcp:{url:`https://ai.todoist.net/mcp`}}},{slug:`webflow`,name:`Webflow`,kind:`connection`,tagline:`Manage Webflow CMS, pages, assets, and sites.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Webflow: CMS items, pages, assets, and sites.`,mcp:{url:`https://mcp.webflow.com/mcp`}}},{slug:`wix`,name:`Wix`,kind:`connection`,tagline:`Manage and build sites and apps on Wix.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Wix: manage and build sites and apps.`,mcp:{url:`https://mcp.wix.com/mcp`}}},{slug:`zapier`,name:`Zapier`,kind:`connection`,tagline:`Automate workflows across thousands of apps.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Zapier: run and manage automations across apps.`,mcp:{url:`https://mcp.zapier.com/api/v1/connect`}}},{slug:`zomato`,name:`Zomato`,kind:`connection`,tagline:`Online food ordering and delivery through Zomato.`,surfaces:{scaffoldable:!1,gallery:!0},connection:{description:`Zomato: food ordering and delivery.`,mcp:{url:`https://mcp-server.zomato.com/mcp`}}}];new Map(INTEGRATIONS.map(e=>[e.slug,e]));function integrationsByKind(t){return INTEGRATIONS.filter(e=>e.kind===t)}function connectionEntries(){return integrationsByKind(`connection`)}function channelEntries(){return integrationsByKind(`channel`)}export{channelEntries,connectionEntries,connectionProtocols};
@@ -1,3 +1,3 @@
1
- import{extractErrorId,formatErrorHint}from"#internal/logging.js";import{splitDiscordMessageContent}from"#public/channels/discord/api.js";import{renderInputRequestComponents}from"#public/channels/discord/hitl.js";function defaultDiscordAuth(e){let t={channel_id:e.channelId,interaction_id:e.id,user_id:e.user.id,username:e.user.username};return e.guildId!==void 0&&(t.guild_id=e.guildId),e.member?.nick!==void 0&&(t.member_nick=e.member.nick),{attributes:t,authenticator:`discord-interaction`,issuer:e.guildId?`discord:${e.guildId}`:`discord`,principalId:e.guildId?`discord:${e.guildId}:${e.user.id}`:`discord:${e.user.id}`,principalType:e.user.isBot?`service`:`user`}}function defaultOnCommand(e,t){return{auth:defaultDiscordAuth(t)}}const defaultEvents={async"turn.started"(e,t,n){await t.discord.startTyping()},async"actions.requested"(e,t,n){await t.discord.startTyping()},async"input.requested"(e,t,i){for(let i of e.requests){let e=splitDiscordMessageContent(i.prompt)[0]??i.prompt;await t.discord.post({components:renderInputRequestComponents(i),content:e})}},async"message.completed"(e,t,n){e.finishReason===`tool-calls`||!e.message||await t.discord.post(e.message)},async"session.failed"(n,r){let i=formatErrorHint(n),a=extractErrorId(n.details);await r.discord.post([`This session could not recover from an error${i}.`,``,`Start a new command to continue.`,...a?[``,`Error id: ${a}`]:[]].join(`
1
+ import{extractErrorId,formatErrorHint}from"#internal/logging.js";import{splitDiscordMessageContent}from"#public/channels/discord/api.js";import{renderInputRequestComponents}from"#public/channels/discord/hitl.js";function defaultDiscordAuth(e){let t={channel_id:e.channelId,interaction_id:e.id,user_id:e.user.id,username:e.user.username};return e.guildId!==void 0&&(t.guild_id=e.guildId),e.member?.nick!==void 0&&(t.member_nick=e.member.nick),{attributes:t,authenticator:`discord-interaction`,issuer:e.guildId?`discord:${e.guildId}`:`discord`,principalId:e.guildId?`discord:${e.guildId}:${e.user.id}`:`discord:${e.user.id}`,principalType:e.user.isBot?`service`:`user`}}function defaultOnCommand(e,t){return{auth:defaultDiscordAuth(t)}}const defaultEvents={async"turn.started"(e,t,n){await t.discord.startTyping()},async"authorization.completed"(e,t,n){e.outcome===`authorized`&&await t.discord.startTyping()},async"actions.requested"(e,t,n){await t.discord.startTyping()},async"input.requested"(e,t,i){for(let i of e.requests){let e=splitDiscordMessageContent(i.prompt)[0]??i.prompt;await t.discord.post({components:renderInputRequestComponents(i),content:e})}},async"message.completed"(e,t,n){e.finishReason===`tool-calls`||!e.message||await t.discord.post(e.message)},async"session.failed"(n,r){let i=formatErrorHint(n),a=extractErrorId(n.details);await r.discord.post([`This session could not recover from an error${i}.`,``,`Start a new command to continue.`,...a?[``,`Error id: ${a}`]:[]].join(`
2
2
  `))},async"turn.failed"(n,r,i){let a=formatErrorHint(n),o=extractErrorId(n.details);await r.discord.post([`I hit an error while handling your request${a}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...o?[``,`Error id: ${o}`]:[]].join(`
3
3
  `))}};export{defaultDiscordAuth,defaultEvents,defaultOnCommand};
@@ -1,2 +1,3 @@
1
1
  import{createLogger,logError}from"#internal/logging.js";import{hasInternalRefScheme}from"#internal/attachments/url-refs.js";import{EVE_CANCEL_TURN_ROUTE_PATTERN,EVE_INFO_ROUTE_PATH}from"#protocol/routes.js";import{EVE_MESSAGE_STREAM_CONTENT_TYPE,EVE_MESSAGE_STREAM_FORMAT,EVE_MESSAGE_STREAM_VERSION,EVE_SESSION_ID_HEADER,EVE_STREAM_FORMAT_HEADER,EVE_STREAM_VERSION_HEADER}from"#protocol/message.js";import{isInputResponse}from"#runtime/input/types.js";import{parseJsonObject}from"#shared/json.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import"ai";import{parseSessionCallback}from"#channel/session-callback.js";import{readAgentInfoRouteResponse,readRouteAgent}from"#internal/nitro/routes/channel-route-context.js";import{routeAuth}from"#public/channels/auth.js";import{collectUploadPolicyViolations,formatUploadPolicyViolation,mergeUploadPolicy}from"#public/channels/upload-policy.js";const log=createLogger(`eve.channel`);function defaultEveAuth(e){return e.eve.caller}function eveChannel(e){let n=mergeUploadPolicy(e.uploadPolicy);return defineChannel({cors:normalizeEveCors(e.cors),routes:[GET(EVE_INFO_ROUTE_PATH,async(t,n)=>{let r=await routeAuth(t,e.auth);if(r instanceof Response)return r;let i=readAgentInfoRouteResponse(n);return i===void 0?Response.json({error:`Agent info route requires internal channel dispatch context.`,ok:!1},{status:500}):await i()}),POST(`/eve/v1/session`,async(t,{send:r})=>{let i=await routeAuth(t,e.auth);if(i instanceof Response)return i;let a=i,o;try{o=await t.json()}catch{return Response.json({error:`Invalid JSON body.`,ok:!1},{status:400})}if(typeof o!=`object`||!o)return Response.json({error:`Expected a JSON object.`,ok:!1},{status:400});let s=parseCreateBody(o);if(s instanceof Response)return s;let c=checkUploadPolicy(s,n);if(c!==null)return c;let l=await resolveOnMessage({auth:a,config:e,message:s.message,request:t});if(l instanceof Response)return l;if(!l.dispatch)return droppedMessageResponse();let u=`eve:${crypto.randomUUID()}`,d=await r(createSendPayload(s,mergeContext(s.context,l.context)),{auth:l.auth,callback:s.callback,continuationToken:u,mode:s.mode});return Response.json({continuationToken:d.continuationToken,ok:!0,sessionId:d.id},{headers:{"cache-control":`no-store`,[EVE_SESSION_ID_HEADER]:d.id},status:202})}),POST(`/eve/v1/session/:sessionId`,async(t,{send:r,getSession:i,params:a})=>{let o=await routeAuth(t,e.auth);if(o instanceof Response)return o;let s=o,c=a.sessionId;if(!c)return Response.json({error:`Missing session id.`,ok:!1},{status:400});try{i(c)}catch{return Response.json({error:`Session not found.`,ok:!1},{status:404})}let l;try{l=await t.json()}catch{return Response.json({error:`Invalid JSON body.`,ok:!1},{status:400})}if(typeof l!=`object`||!l)return Response.json({error:`Expected a JSON object.`,ok:!1},{status:400});let u=parseContinueBody(l);if(u instanceof Response)return u;let d=checkUploadPolicy(u,n);if(d!==null)return d;let f=u.context,p=s;if(u.message!==void 0){let n=await resolveOnMessage({auth:s,config:e,message:u.message,request:t,sessionId:c});if(n instanceof Response)return n;if(!n.dispatch)return droppedMessageResponse();f=mergeContext(u.context,n.context),p=n.auth}let m=await r({inputResponses:u.inputResponses,message:u.message,context:f,outputSchema:u.outputSchema},{auth:p,continuationToken:u.continuationToken});return Response.json({ok:!0,sessionId:m.id},{headers:{"cache-control":`no-store`,[EVE_SESSION_ID_HEADER]:m.id},status:200})}),POST(EVE_CANCEL_TURN_ROUTE_PATTERN,async(n,r)=>{let i=await routeAuth(n,e.auth);if(i instanceof Response)return i;let a=r.params.sessionId;if(!a)return Response.json({error:`Missing session id.`,ok:!1},{status:400});let o=await parseCancelTurnBody(n);if(o instanceof Response)return o;let s;try{let e=readRouteAgent(r);if(e===void 0)throw Error(`Missing route agent.`);s=await e.cancelTurn({sessionId:a,turnId:o.turnId})}catch(e){let n=logError(log,`cancel-turn request failed`,e,{sessionId:a});return Response.json({error:`Failed to cancel the turn.`,errorId:n,ok:!1},{status:500})}return Response.json({ok:!0,sessionId:a,status:s.status},{headers:{"cache-control":`no-store`,[EVE_SESSION_ID_HEADER]:a},status:202})}),GET(`/eve/v1/session/:sessionId/stream`,async(t,{getSession:n,params:r})=>{let i=await routeAuth(t,e.auth);if(i instanceof Response)return i;let u=r.sessionId;if(!u)return Response.json({error:`Missing session id.`,ok:!1},{status:400});let d=parseStartIndex(t);if(d instanceof Response)return d;try{let e=serializeAsNdjson(await n(u).getEventStream({startIndex:d}));return new Response(e,{headers:{"cache-control":`no-store, no-transform`,"content-type":EVE_MESSAGE_STREAM_CONTENT_TYPE,"x-accel-buffering":`no`,[EVE_SESSION_ID_HEADER]:u,[EVE_STREAM_FORMAT_HEADER]:EVE_MESSAGE_STREAM_FORMAT,[EVE_STREAM_VERSION_HEADER]:EVE_MESSAGE_STREAM_VERSION}})}catch{return Response.json({error:`Session not found.`,ok:!1},{status:404})}})],events:e.events})}function normalizeEveCors(e){if(e===void 0||e===!1)return!1;if(e===!0)return!0;let t={};return e.origin!==void 0&&(t.origin=normalizeEveCorsOrigin(e.origin)),e.methods!==void 0&&(t.methods=e.methods),e.allowedHeaders!==void 0&&(t.allowHeaders=e.allowedHeaders),e.exposedHeaders!==void 0&&(t.exposeHeaders=e.exposedHeaders),e.credentials!==void 0&&(t.credentials=e.credentials),e.maxAge!==void 0&&(t.maxAge=e.maxAge),e.preflightStatus!==void 0&&(t.preflight={statusCode:e.preflightStatus}),t}function normalizeEveCorsOrigin(e){return e===`*`||e===`null`?e:typeof e==`string`?[e]:e}async function resolveOnMessage(e){let n=e.config.onMessage??defaultOnMessage,r;try{r=await n({eve:e.sessionId===void 0?{caller:e.auth,request:e.request}:{caller:e.auth,request:e.request,sessionId:e.sessionId}},e.message)}catch(n){let r=logError(log,`onMessage handler failed`,n,{sessionId:e.sessionId});return Response.json({error:`onMessage handler failed.`,errorId:r,ok:!1},{status:500})}return r==null?{dispatch:!1}:r.context===void 0?{auth:r.auth,dispatch:!0}:{auth:r.auth,context:r.context,dispatch:!0}}function defaultOnMessage(e){return{auth:defaultEveAuth(e)}}function droppedMessageResponse(){return new Response(null,{headers:{"cache-control":`no-store`},status:204})}function parseCreateBody(e){let t=parseMessageField(e.message);if(t instanceof Response)return t;let n=parseClientContextField(e.clientContext);if(n instanceof Response)return n;let r=parseCallbackField(e.callback);if(r instanceof Response)return r;let i=parseModeField(e.mode);if(i instanceof Response)return i;let a=parseOutputSchemaField(e.outputSchema);return a instanceof Response?a:t===void 0?Response.json({error:`Missing or empty 'message' field.`,ok:!1},{status:400}):{callback:r,message:t,mode:i,context:n,outputSchema:a}}function parseContinueBody(e){let t=typeof e.continuationToken==`string`&&e.continuationToken.length>0?e.continuationToken:void 0;if(t===void 0)return Response.json({error:`Missing or empty 'continuationToken' field.`,ok:!1},{status:400});let n=parseMessageField(e.message);if(n instanceof Response)return n;let r=parseInputResponses(e.inputResponses);if(r instanceof Response)return r;let i=parseClientContextField(e.clientContext);if(i instanceof Response)return i;let a=parseOutputSchemaField(e.outputSchema);return a instanceof Response?a:n===void 0&&r===void 0?Response.json({error:`Expected a non-empty 'message', a non-empty 'inputResponses' array, or both.`,ok:!1},{status:400}):{message:n,continuationToken:t,inputResponses:r,context:i,outputSchema:a}}async function parseCancelTurnBody(e){let t;try{t=await e.text()}catch{return Response.json({error:`Unreadable request body.`,ok:!1},{status:400})}if(t.trim().length===0)return{};let n;try{n=JSON.parse(t)}catch{return Response.json({error:`Invalid JSON body.`,ok:!1},{status:400})}if(typeof n!=`object`||!n||Array.isArray(n))return Response.json({error:`Expected a JSON object.`,ok:!1},{status:400});let r=n.turnId;return r===void 0?{}:typeof r!=`string`||r.length===0?Response.json({error:`Expected 'turnId' to be a non-empty string.`,ok:!1},{status:400}):{turnId:r}}function createSendPayload(e,t=e.context){if(t===void 0&&e.outputSchema===void 0)return e.message;let n={message:e.message};return t!==void 0&&(n.context=t),e.outputSchema!==void 0&&(n.outputSchema=e.outputSchema),n}function parseOutputSchemaField(e){if(e!==void 0)try{return parseJsonObject(e)}catch{return Response.json({error:`Expected 'outputSchema' to be a JSON-serializable object.`,ok:!1},{status:400})}}function parseCallbackField(e){if(e===void 0)return;let t=parseSessionCallback(e);return t.ok?t.callback:Response.json({error:t.message,ok:!1},{status:400})}function parseModeField(e){if(e!==void 0)return e===`conversation`||e===`task`?e:Response.json({error:`Expected 'mode' to be either 'conversation' or 'task'.`,ok:!1},{status:400})}function parseMessageField(e){if(e===void 0)return;if(typeof e==`string`)return e.length>0?e:void 0;if(!Array.isArray(e))return Response.json({error:`Expected 'message' to be a string or an array of text/file parts.`,ok:!1},{status:400});if(e.length===0)return;let t=[];for(let n of e){let e=parseMessagePart(n);if(e instanceof Response)return e;t.push(e)}return t}function parseMessagePart(e){if(typeof e!=`object`||!e)return Response.json({error:`Expected each message part to be an object.`,ok:!1},{status:400});let t=e;if(t.type===`text`)return typeof t.text!=`string`||t.text.length===0?Response.json({error:`Text parts require a non-empty 'text' string.`,ok:!1},{status:400}):{type:`text`,text:t.text};if(t.type===`file`){if(typeof t.mediaType!=`string`||t.mediaType.length===0)return Response.json({error:`File parts require a non-empty 'mediaType' string.`,ok:!1},{status:400});if(typeof t.data!=`string`)return Response.json({error:`File parts require a 'data' string (base64, data URL, or URL).`,ok:!1},{status:400});if(hasInternalRefScheme(t.data))return Response.json({error:`File part 'data' must not use a framework-internal ref scheme.`,ok:!1},{status:400});let e={type:`file`,mediaType:t.mediaType,data:t.data};return typeof t.filename==`string`&&t.filename.length>0&&(e.filename=t.filename),e}return Response.json({error:`Unsupported message part type "${String(t.type)}". Use 'text' or 'file'.`,ok:!1},{status:400})}function checkUploadPolicy(e,t){if(!e.message)return null;let n=collectUploadPolicyViolations(e.message,t);if(n.length===0)return null;let[r]=n;if(!r)return null;let i=r.kind===`too-large`?413:415;return Response.json({error:formatUploadPolicyViolation(r),ok:!1,violations:n.map(e=>e.kind===`too-large`?{byteLength:e.byteLength,filename:e.filename,kind:e.kind,limit:e.limit,mediaType:e.mediaType}:{allowedMediaTypes:e.allowedMediaTypes,filename:e.filename,kind:e.kind,mediaType:e.mediaType})},{status:i})}function parseInputResponses(e){if(e===void 0)return;if(!Array.isArray(e)||e.length===0)return Response.json({error:`Expected 'inputResponses' to be a non-empty array.`,ok:!1},{status:400});let t=e.filter(isInputResponse);return t.length===e.length?t:Response.json({error:`Expected every 'inputResponses' entry to match the HITL response schema.`,ok:!1},{status:400})}function mergeContext(e,t){return e===void 0?t:t===void 0?e:[...e,...t]}function parseClientContextField(e){if(e!==void 0){if(typeof e==`string`)return e.length>0?[toClientContextMessage(e)]:void 0;if(Array.isArray(e))return e.length===0?void 0:e.every(e=>typeof e==`string`&&e.length>0)?e.map(e=>toClientContextMessage(e)):Response.json({error:`Expected 'clientContext' array entries to be non-empty strings.`,ok:!1},{status:400});if(typeof e!=`object`||!e)return Response.json({error:`Expected 'clientContext' to be a string, string array, or JSON object.`,ok:!1},{status:400});try{let t=parseJsonObject(e);return[toClientContextMessage(JSON.stringify(t))]}catch{return Response.json({error:`Expected 'clientContext' to be a JSON-serializable object.`,ok:!1},{status:400})}}}function toClientContextMessage(e){return`Client context:
2
- ${e}`}function parseStartIndex(e){let t=new URL(e.url).searchParams.get(`startIndex`);if(t===null)return;let n=Number(t);return!/^-?\d+$/.test(t)||!Number.isSafeInteger(n)?Response.json({error:`Expected startIndex to be an integer.`,ok:!1},{status:400}):n}function serializeAsNdjson(e){let t=new TextEncoder;return e.pipeThrough(new TransformStream({transform(e,n){n.enqueue(t.encode(`${JSON.stringify(e)}\n`))}}))}export{defaultEveAuth,eveChannel};
2
+ ${e}`}function parseStartIndex(e){let t=new URL(e.url).searchParams.get(`startIndex`);if(t===null)return;let n=Number(t);return!/^-?\d+$/.test(t)||!Number.isSafeInteger(n)?Response.json({error:`Expected startIndex to be an integer.`,ok:!1},{status:400}):n}function serializeAsNdjson(e){let t=new TextEncoder;return e.pipeThrough(new TransformStream({start(e){e.enqueue(t.encode(`
3
+ `))},transform(e,n){n.enqueue(t.encode(`${JSON.stringify(e)}\n`))}}))}export{defaultEveAuth,eveChannel};
@@ -59,6 +59,8 @@ export interface LinearAgentActivityRecord {
59
59
  __typename?: string;
60
60
  };
61
61
  id: string;
62
+ signal?: string | null;
63
+ signalMetadata?: JsonObject | null;
62
64
  updatedAt?: string;
63
65
  }
64
66
  export declare class LinearApiError extends Error {
@@ -48,6 +48,8 @@ import{isObject}from"#shared/guards.js";import{parseJsonObject}from"#shared/json
48
48
  activities(last: $last) {
49
49
  nodes {
50
50
  id
51
+ signal
52
+ signalMetadata
51
53
  updatedAt
52
54
  content {
53
55
  __typename
@@ -61,4 +63,4 @@ import{isObject}from"#shared/guards.js";import{parseJsonObject}from"#shared/json
61
63
  }
62
64
  }
63
65
  }
64
- `,queryName:`AgentSessionActivities`,variables:{id:e.agentSessionId,last:e.last??20}})).agentSession?.activities?.nodes;return Array.isArray(t)?t.map(normalizeAgentActivityRecord).filter(notNull):[]}function normalizeAgentSessionRecord(t){if(!isObject(t)||typeof t.id!=`string`)throw Error(`linearChannel: Linear Agent Session response was malformed.`);let n=normalizeIssue(t.issue),r={id:t.id},i=relationId(t.appUser);typeof i==`string`&&(r.appUserId=i);let a=relationId(t.comment);a!==void 0&&(r.commentId=a);let o=relationId(t.creator);o!==void 0&&(r.creatorId=o),n!==void 0&&(r.issue=n);let s=relationId(t.issue);s!==void 0&&(r.issueId=s);let c=relationId(t.sourceComment);return c!==void 0&&(r.sourceCommentId=c),typeof t.status==`string`&&(r.status=t.status),(typeof t.url==`string`||t.url===null)&&(r.url=t.url),r}function relationId(t){if(t===null)return null;if(isObject(t)&&typeof t.id==`string`)return t.id}function normalizeAgentActivityRecord(t){if(!isObject(t)||typeof t.id!=`string`||!isObject(t.content))return null;let n={};typeof t.content.body==`string`&&(n.body=t.content.body),typeof t.content.type==`string`&&(n.type=t.content.type),typeof t.content.__typename==`string`&&(n.__typename=t.content.__typename);let r={content:n,id:t.id};return typeof t.updatedAt==`string`&&(r.updatedAt=t.updatedAt),r}function normalizeIssue(t){if(t===null)return null;if(!isObject(t)||typeof t.id!=`string`)return;let n={id:t.id};return typeof t.identifier==`string`&&(n.identifier=t.identifier),typeof t.title==`string`&&(n.title=t.title),typeof t.url==`string`&&(n.url=t.url),n}async function parseResponseBody(e){let t=await e.text();if(!t)return null;try{return JSON.parse(t)}catch{return t}}function hasGraphQLErrors(t){return isObject(t)&&Array.isArray(t.errors)&&t.errors.length>0}function notNull(e){return e!==null}export{LinearApiError,callLinearGraphQL,createLinearAgentActivity,createLinearAgentSessionOnComment,createLinearAgentSessionOnIssue,listLinearAgentSessionActivities,updateLinearAgentSession};
66
+ `,queryName:`AgentSessionActivities`,variables:{id:e.agentSessionId,last:e.last??20}})).agentSession?.activities?.nodes;return Array.isArray(t)?t.map(normalizeAgentActivityRecord).filter(notNull):[]}function normalizeAgentSessionRecord(t){if(!isObject(t)||typeof t.id!=`string`)throw Error(`linearChannel: Linear Agent Session response was malformed.`);let n=normalizeIssue(t.issue),r={id:t.id},i=relationId(t.appUser);typeof i==`string`&&(r.appUserId=i);let a=relationId(t.comment);a!==void 0&&(r.commentId=a);let o=relationId(t.creator);o!==void 0&&(r.creatorId=o),n!==void 0&&(r.issue=n);let s=relationId(t.issue);s!==void 0&&(r.issueId=s);let c=relationId(t.sourceComment);return c!==void 0&&(r.sourceCommentId=c),typeof t.status==`string`&&(r.status=t.status),(typeof t.url==`string`||t.url===null)&&(r.url=t.url),r}function relationId(t){if(t===null)return null;if(isObject(t)&&typeof t.id==`string`)return t.id}function normalizeAgentActivityRecord(n){if(!isObject(n)||typeof n.id!=`string`||!isObject(n.content))return null;let r={};typeof n.content.body==`string`&&(r.body=n.content.body),typeof n.content.type==`string`&&(r.type=n.content.type),typeof n.content.__typename==`string`&&(r.__typename=n.content.__typename);let i={content:r,id:n.id};return(typeof n.signal==`string`||n.signal===null)&&(i.signal=n.signal),isObject(n.signalMetadata)&&(i.signalMetadata=parseJsonObject(n.signalMetadata)),n.signalMetadata===null&&(i.signalMetadata=null),typeof n.updatedAt==`string`&&(i.updatedAt=n.updatedAt),i}function normalizeIssue(t){if(t===null)return null;if(!isObject(t)||typeof t.id!=`string`)return;let n={id:t.id};return typeof t.identifier==`string`&&(n.identifier=t.identifier),typeof t.title==`string`&&(n.title=t.title),typeof t.url==`string`&&(n.url=t.url),n}async function parseResponseBody(e){let t=await e.text();if(!t)return null;try{return JSON.parse(t)}catch{return t}}function hasGraphQLErrors(t){return isObject(t)&&Array.isArray(t.errors)&&t.errors.length>0}function notNull(e){return e!==null}export{LinearApiError,callLinearGraphQL,createLinearAgentActivity,createLinearAgentSessionOnComment,createLinearAgentSessionOnIssue,listLinearAgentSessionActivities,updateLinearAgentSession};
@@ -1,3 +1,3 @@
1
- import{extractErrorId,formatErrorHint}from"#internal/logging.js";import{createLinearAgentActivity}from"#public/channels/linear/api.js";import{renderLinearInputRequests}from"#public/channels/linear/hitl.js";function defaultLinearAuth(e){let t=e.agentActivity?.user??e.agentSession.creator,n=e.agentActivity?.userId??e.agentSession.creatorId??t?.id??`unknown`,r={action:e.action,agent_session_id:e.agentSession.id,organization_id:e.organizationId??e.agentSession.organizationId??``};if(e.delivery.id!==void 0&&(r.delivery_id=e.delivery.id),e.agentSession.issueId!==void 0&&e.agentSession.issueId!==null&&(r.issue_id=e.agentSession.issueId),e.agentSession.issue?.identifier!==void 0&&(r.issue_identifier=e.agentSession.issue.identifier),t!==void 0){let e=linearUserLabel(t);e!==void 0&&(r.user=e)}return{attributes:r,authenticator:`linear-agent-webhook`,issuer:e.organizationId?`linear:${e.organizationId}`:`linear`,principalId:`linear:${n}`,principalType:`user`,subject:n}}function defaultOnAgentSession(e,t){return t.action!==`created`&&t.action!==`prompted`?null:{auth:defaultLinearAuth(t)}}function createDefaultEvents(n={}){return{async"turn.started"(e,t,r){t.state.pendingToolCallMessage=null,await postActivity(t,n,{body:`Working on this.`,type:`thought`},{ephemeral:!0})},async"actions.requested"(e,t,r){let i=t.state.pendingToolCallMessage;if(t.state.pendingToolCallMessage=null,i){await postActivity(t,n,{body:i,type:`thought`},{ephemeral:!0});return}if(e.actions.length!==0){if(e.actions.length>1){await postActivity(t,n,{action:`Running`,parameter:e.actions.map(actionLabel).join(`, `),type:`action`},{ephemeral:!0});return}for(let r of e.actions)await postActivity(t,n,{action:actionLabel(r),parameter:actionParameter(r),type:`action`},{ephemeral:!0})}},async"input.requested"(e,t,i){await postActivity(t,n,{body:renderLinearInputRequests(e.requests),type:`elicitation`})},async"message.completed"(e,t,r){if(e.finishReason===`tool-calls`){t.state.pendingToolCallMessage=e.message?firstNonEmptyLine(e.message)??null:null;return}t.state.pendingToolCallMessage=null,e.message&&await postActivity(t,n,{body:e.message,type:`response`})},async"session.failed"(r,i){let a=formatErrorHint(r),o=extractErrorId(r.details);await postActivity(i,n,{body:[`This session could not recover from an error${a}.`,``,`Start a new Linear agent session to continue.`,...o?[``,`Error id: ${o}`]:[]].join(`
1
+ import{extractErrorId,formatErrorHint}from"#internal/logging.js";import{createLinearAgentActivity}from"#public/channels/linear/api.js";import{linearInputRequestSignal,renderLinearInputRequests}from"#public/channels/linear/hitl.js";function defaultLinearAuth(e){let t=e.agentActivity?.user??e.agentSession.creator,n=e.agentActivity?.userId??e.agentSession.creatorId??t?.id??`unknown`,r={action:e.action,agent_session_id:e.agentSession.id,organization_id:e.organizationId??e.agentSession.organizationId??``};if(e.delivery.id!==void 0&&(r.delivery_id=e.delivery.id),e.agentSession.issueId!==void 0&&e.agentSession.issueId!==null&&(r.issue_id=e.agentSession.issueId),e.agentSession.issue?.identifier!==void 0&&(r.issue_identifier=e.agentSession.issue.identifier),t!==void 0){let e=linearUserLabel(t);e!==void 0&&(r.user=e)}return{attributes:r,authenticator:`linear-agent-webhook`,issuer:e.organizationId?`linear:${e.organizationId}`:`linear`,principalId:`linear:${n}`,principalType:`user`,subject:n}}function defaultOnAgentSession(e,t){return t.action!==`created`&&t.action!==`prompted`?null:{auth:defaultLinearAuth(t)}}function createDefaultEvents(n={}){return{async"turn.started"(e,t,r){t.state.pendingToolCallMessage=null,await postActivity(t,n,{body:`Working on this.`,type:`thought`},{ephemeral:!0})},async"actions.requested"(e,t,r){let i=t.state.pendingToolCallMessage;if(t.state.pendingToolCallMessage=null,i){await postActivity(t,n,{body:i,type:`thought`},{ephemeral:!0});return}if(e.actions.length!==0){if(e.actions.length>1){await postActivity(t,n,{action:`Running`,parameter:e.actions.map(actionLabel).join(`, `),type:`action`},{ephemeral:!0});return}for(let r of e.actions)await postActivity(t,n,{action:actionLabel(r),parameter:actionParameter(r),type:`action`},{ephemeral:!0})}},async"input.requested"(e,t,a){let o=linearInputRequestSignal(e.requests);await postActivity(t,n,{body:renderLinearInputRequests(e.requests),type:`elicitation`},o)},async"message.completed"(e,t,r){if(e.finishReason===`tool-calls`){t.state.pendingToolCallMessage=e.message?firstNonEmptyLine(e.message)??null:null;return}t.state.pendingToolCallMessage=null,e.message&&await postActivity(t,n,{body:e.message,type:`response`})},async"session.failed"(r,i){let a=formatErrorHint(r),o=extractErrorId(r.details);await postActivity(i,n,{body:[`This session could not recover from an error${a}.`,``,`Start a new Linear agent session to continue.`,...o?[``,`Error id: ${o}`]:[]].join(`
2
2
  `),type:`error`})},async"turn.failed"(r,i,a){let o=formatErrorHint(r),s=extractErrorId(r.details);await postActivity(i,n,{body:[`I hit an error while handling your request${o}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...s?[``,`Error id: ${s}`]:[]].join(`
3
- `),type:`error`})}}}function postActivity(e,t,r,i={}){return createLinearAgentActivity({api:t.api,credentials:t.credentials,activity:{agentSessionId:requireAgentSessionId(e.state.agentSessionId),content:r,ephemeral:i.ephemeral}})}function requireAgentSessionId(e){if(e===null)throw Error(`linearChannel: cannot post Agent Activity without an Agent Session id.`);return e}function linearUserLabel(e){return e.displayName??e.name??e.email}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function actionLabel(e){return e.kind===`tool-call`&&e.toolName?e.toolName:e.kind}function actionParameter(e){if(e.description)return e.description;if(e.name)return e.name;if(e.input!==void 0)try{return JSON.stringify(e.input)}catch{return``}return``}export{createDefaultEvents,defaultLinearAuth,defaultOnAgentSession};
3
+ `),type:`error`})}}}function postActivity(e,t,r,i={}){return createLinearAgentActivity({api:t.api,credentials:t.credentials,activity:{agentSessionId:requireAgentSessionId(e.state.agentSessionId),content:r,ephemeral:i.ephemeral,signal:i.signal,signalMetadata:i.signalMetadata}})}function requireAgentSessionId(e){if(e===null)throw Error(`linearChannel: cannot post Agent Activity without an Agent Session id.`);return e}function linearUserLabel(e){return e.displayName??e.name??e.email}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function actionLabel(e){return e.kind===`tool-call`&&e.toolName?e.toolName:e.kind}function actionParameter(e){if(e.description)return e.description;if(e.name)return e.name;if(e.input!==void 0)try{return JSON.stringify(e.input)}catch{return``}return``}export{createDefaultEvents,defaultLinearAuth,defaultOnAgentSession};
@@ -1,14 +1,9 @@
1
- import type { LinearAgentActivityRecord } from "#public/channels/linear/api.js";
2
- import type { InputRequest, InputResponse } from "#runtime/input/types.js";
3
- /** Hidden marker embedded in eve-created Linear elicitation bodies. */
4
- export declare const LINEAR_HITL_MARKER_PREFIX = "<!-- eve-input:";
5
- export declare const LINEAR_HITL_MARKER_SUFFIX = " -->";
6
- /** Renders eve input requests as one Linear elicitation body. */
1
+ import type { InputRequest } from "#runtime/input/types.js";
2
+ import type { JsonObject } from "#shared/json.js";
3
+ /** Renders eve input requests as one user-visible Linear elicitation body. */
7
4
  export declare function renderLinearInputRequests(requests: readonly InputRequest[]): string;
8
- /** Resolves a Linear user prompt against the latest eve-created elicitation marker. */
9
- export declare function resolveLinearPromptInputResponses(input: {
10
- readonly activities: readonly LinearAgentActivityRecord[];
11
- readonly body: string;
12
- }): readonly InputResponse[];
13
- /** Strips eve's hidden HITL marker from a body before user-facing assertions/logging. */
14
- export declare function stripLinearHitlMarker(body: string): string;
5
+ /** Builds native Linear select metadata for a single input request with options. */
6
+ export declare function linearInputRequestSignal(requests: readonly InputRequest[]): {
7
+ readonly signal?: "select";
8
+ readonly signalMetadata?: JsonObject;
9
+ };
@@ -1,4 +1,4 @@
1
- import{resolveTextToResponses}from"#channel/resolve-text.js";const LINEAR_HITL_MARKER_PREFIX=`<!-- eve-input:`,LINEAR_HITL_MARKER_SUFFIX=` -->`;function renderLinearInputRequests(e){let t=encodeLinearHitlMarker(e.map(storableRequest));return`${e.map(renderLinearInputRequest).join(`
1
+ function renderLinearInputRequests(e){return e.map(renderLinearInputRequest).join(`
2
2
 
3
- `)}\n\n${t}`}function resolveLinearPromptInputResponses(t){let n=findLatestLinearHitlMarker(t.activities);return n===null?[]:resolveTextToResponses(t.body,n.requests.map(toInputRequest))}function stripLinearHitlMarker(e){let n=e.indexOf(LINEAR_HITL_MARKER_PREFIX);return n===-1?e:e.slice(0,n).trimEnd()}function renderLinearInputRequest(e){let t=[e.prompt];return e.options!==void 0&&e.options.length>0&&t.push(``,...e.options.map((e,t)=>{let n=e.description?` - ${e.description}`:``;return`${t+1}. ${e.label}${n}`})),e.allowFreeform===!0&&t.push(``,`You can also reply with a custom answer.`),t.join(`
4
- `)}function storableRequest(e){let t={prompt:e.prompt,requestId:e.requestId};return e.allowFreeform!==void 0&&(t.allowFreeform=e.allowFreeform),e.display!==void 0&&(t.display=e.display),e.options!==void 0&&(t.options=e.options),t}function toInputRequest(e){let t={action:{callId:`linear-input:${e.requestId}`,input:{},kind:`tool-call`,toolName:`linear_input`},prompt:e.prompt,requestId:e.requestId};return e.allowFreeform!==void 0&&(t.allowFreeform=e.allowFreeform),e.display!==void 0&&(t.display=e.display),e.options!==void 0&&(t.options=[...e.options]),t}function encodeLinearHitlMarker(e){return`${LINEAR_HITL_MARKER_PREFIX}${Buffer.from(JSON.stringify({requests:e}),`utf8`).toString(`base64url`)}${LINEAR_HITL_MARKER_SUFFIX}`}function findLatestLinearHitlMarker(e){for(let t=e.length-1;t>=0;--t){let n=e[t]?.content.body;if(n===void 0)continue;let r=decodeLinearHitlMarker(n);if(r!==null)return r}return null}function decodeLinearHitlMarker(e){let r=e.lastIndexOf(LINEAR_HITL_MARKER_PREFIX);if(r===-1)return null;let i=r+15,a=e.indexOf(LINEAR_HITL_MARKER_SUFFIX,i);if(a===-1)return null;try{let t=Buffer.from(e.slice(i,a),`base64url`).toString(`utf8`),n=JSON.parse(t);return isMarkerPayload(n)?n:null}catch{return null}}function isMarkerPayload(e){if(!e||typeof e!=`object`||!(`requests`in e))return!1;let t=e.requests;return Array.isArray(t)&&t.every(isStoredRequest)}function isStoredRequest(e){if(!e||typeof e!=`object`)return!1;let t=e;if(typeof t.prompt!=`string`||typeof t.requestId!=`string`||t.allowFreeform!==void 0&&typeof t.allowFreeform!=`boolean`||t.display!==void 0&&t.display!==`confirmation`&&t.display!==`select`&&t.display!==`text`)return!1;let n=t.options;return n===void 0||Array.isArray(n)&&n.every(isStoredOption)}function isStoredOption(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id!=`string`||typeof t.label!=`string`||t.description!==void 0&&typeof t.description!=`string`?!1:t.style===void 0||t.style===`primary`||t.style===`danger`||t.style==="default"}export{LINEAR_HITL_MARKER_PREFIX,LINEAR_HITL_MARKER_SUFFIX,renderLinearInputRequests,resolveLinearPromptInputResponses,stripLinearHitlMarker};
3
+ `)}function linearInputRequestSignal(e){let t=e.length===1?e[0]:void 0;return t?.options===void 0||t.options.length===0?{}:{signal:`select`,signalMetadata:{options:t.options.map(e=>({label:e.label,value:e.id}))}}}function renderLinearInputRequest(e){let t=[e.prompt];return e.options!==void 0&&e.options.length>0&&t.push(``,...e.options.map((e,t)=>{let n=e.description?` - ${e.description}`:``;return`${t+1}. ${e.label}${n}`})),e.allowFreeform===!0&&t.push(``,`You can also reply with a custom answer.`),t.join(`
4
+ `)}export{linearInputRequestSignal,renderLinearInputRequests};
@@ -2,7 +2,7 @@ export { LinearApiError, callLinearGraphQL, createLinearAgentActivity, createLin
2
2
  export { type LinearAccessToken, type LinearChannelCredentials, type LinearWebhookSecret, } from "#public/channels/linear/auth.js";
3
3
  export { LINEAR_CHANNEL_DEFAULT_ROUTE } from "#public/channels/linear/constants.js";
4
4
  export { defaultLinearAuth, defaultOnAgentSession } from "#public/channels/linear/defaults.js";
5
- export { LINEAR_HITL_MARKER_PREFIX, renderLinearInputRequests, resolveLinearPromptInputResponses, stripLinearHitlMarker, } from "#public/channels/linear/hitl.js";
5
+ export { linearInputRequestSignal, renderLinearInputRequests, } from "#public/channels/linear/hitl.js";
6
6
  export { formatLinearContextBlock, linearContinuationToken, messageFromLinearAgentSessionEvent, parseLinearWebhookEvent, type LinearAgentActivityRef, type LinearAgentSessionAction, type LinearAgentSessionEvent, type LinearAgentSessionRef, type LinearDataWebhookEvent, type LinearDelivery, type LinearInboundEvent, type LinearIssueRef, type LinearUser, } from "#public/channels/linear/inbound.js";
7
7
  export { linearChannel, type LinearChannel, type LinearChannelConfig, type LinearChannelContext, type LinearChannelEvents, type LinearChannelState, type LinearEventContext, type LinearHandle, type LinearInboundResult, type LinearInboundResultOrPromise, type LinearInstrumentationMetadata, type LinearReceiveTarget, type LinearSessionContext, } from "#public/channels/linear/linearChannel.js";
8
8
  export { signLinearWebhookBody, type LinearVerifyOptions, type LinearWebhookVerifier, } from "#public/channels/linear/verify.js";
@@ -1 +1 @@
1
- import{LinearApiError,callLinearGraphQL,createLinearAgentActivity,createLinearAgentSessionOnComment,createLinearAgentSessionOnIssue,listLinearAgentSessionActivities,updateLinearAgentSession}from"#public/channels/linear/api.js";import{LINEAR_HITL_MARKER_PREFIX,renderLinearInputRequests,resolveLinearPromptInputResponses,stripLinearHitlMarker}from"#public/channels/linear/hitl.js";import{LINEAR_CHANNEL_DEFAULT_ROUTE}from"#public/channels/linear/constants.js";import{defaultLinearAuth,defaultOnAgentSession}from"#public/channels/linear/defaults.js";import{formatLinearContextBlock,linearContinuationToken,messageFromLinearAgentSessionEvent,parseLinearWebhookEvent}from"#public/channels/linear/inbound.js";import{linearChannel}from"#public/channels/linear/linearChannel.js";import{signLinearWebhookBody}from"#public/channels/linear/verify.js";export{LINEAR_CHANNEL_DEFAULT_ROUTE,LINEAR_HITL_MARKER_PREFIX,LinearApiError,callLinearGraphQL,createLinearAgentActivity,createLinearAgentSessionOnComment,createLinearAgentSessionOnIssue,defaultLinearAuth,defaultOnAgentSession,formatLinearContextBlock,linearChannel,linearContinuationToken,listLinearAgentSessionActivities,messageFromLinearAgentSessionEvent,parseLinearWebhookEvent,renderLinearInputRequests,resolveLinearPromptInputResponses,signLinearWebhookBody,stripLinearHitlMarker,updateLinearAgentSession};
1
+ import{LinearApiError,callLinearGraphQL,createLinearAgentActivity,createLinearAgentSessionOnComment,createLinearAgentSessionOnIssue,listLinearAgentSessionActivities,updateLinearAgentSession}from"#public/channels/linear/api.js";import{linearInputRequestSignal,renderLinearInputRequests}from"#public/channels/linear/hitl.js";import{LINEAR_CHANNEL_DEFAULT_ROUTE}from"#public/channels/linear/constants.js";import{defaultLinearAuth,defaultOnAgentSession}from"#public/channels/linear/defaults.js";import{formatLinearContextBlock,linearContinuationToken,messageFromLinearAgentSessionEvent,parseLinearWebhookEvent}from"#public/channels/linear/inbound.js";import{linearChannel}from"#public/channels/linear/linearChannel.js";import{signLinearWebhookBody}from"#public/channels/linear/verify.js";export{LINEAR_CHANNEL_DEFAULT_ROUTE,LinearApiError,callLinearGraphQL,createLinearAgentActivity,createLinearAgentSessionOnComment,createLinearAgentSessionOnIssue,defaultLinearAuth,defaultOnAgentSession,formatLinearContextBlock,linearChannel,linearContinuationToken,linearInputRequestSignal,listLinearAgentSessionActivities,messageFromLinearAgentSessionEvent,parseLinearWebhookEvent,renderLinearInputRequests,signLinearWebhookBody,updateLinearAgentSession};
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{isObject}from"#shared/guards.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{createLinearAgentActivity,createLinearAgentSessionOnComment,createLinearAgentSessionOnIssue,listLinearAgentSessionActivities,updateLinearAgentSession}from"#public/channels/linear/api.js";import{resolveLinearPromptInputResponses}from"#public/channels/linear/hitl.js";import{LINEAR_CHANNEL_DEFAULT_ROUTE}from"#public/channels/linear/constants.js";import{createDefaultEvents,defaultOnAgentSession}from"#public/channels/linear/defaults.js";import{formatLinearContextBlock,linearContinuationToken,messageFromLinearAgentSessionEvent,parseLinearWebhookEvent}from"#public/channels/linear/inbound.js";import{verifyLinearRequest}from"#public/channels/linear/verify.js";const log=createLogger(`linear.channel`);function linearChannel(e={}){let t=e.onAgentSession??defaultOnAgentSession,a={...createDefaultEvents({api:e.api,credentials:e.credentials}),...e.events};return defineChannel({kindHint:`linear`,state:initialLinearState(),metadata(e){return{agentSessionId:e.agentSessionId,commentId:e.commentId??null,issueId:e.issueId??null,issueIdentifier:e.issueIdentifier??null,organizationId:e.organizationId??null}},context(t,n){return rebuildLinearContext(t,n,e)},routes:[POST(e.route??LINEAR_CHANNEL_DEFAULT_ROUTE,async(n,{send:r,waitUntil:i})=>{let a=await verifyInbound(n,e.credentials);if(a===null)return new Response(`unauthorized`,{status:401});let o;try{o=parseLinearWebhookEvent({body:a,headers:n.headers})}catch(e){return log.warn(`inbound Linear body is not valid JSON`,{error:e}),jsonOk({ignored:!0,ok:!0})}return o===null?jsonOk({ignored:!0,ok:!0}):o.kind===`agent_session`?(i(dispatchAgentSession({config:e,event:o,onAgentSession:t,send:r})),jsonOk({ok:!0})):e.onDataWebhook===void 0?jsonOk({ignored:!0,ok:!0}):(i(Promise.resolve(e.onDataWebhook(o))),jsonOk({ok:!0}))})],async receive(t,{send:n}){let r=t.target,a=await resolveReceiveSession(r,e),o=readString(r.initialActivity);return o!==void 0&&await createLinearAgentActivity({api:e.api,credentials:e.credentials,activity:{agentSessionId:a.id,content:{body:o,type:`thought`}}}),n(t.message,{auth:t.auth,continuationToken:linearContinuationToken(a.id),state:stateFromAgentSession(a)})},events:a})}function rebuildLinearContext(e,t,n){return{linear:buildLinearHandle({agentSessionId:e.agentSessionId??``,config:n}),state:e}}function buildLinearHandle(e){return{agentSessionId:e.agentSessionId,createActivity(t,n){return createLinearAgentActivity({api:e.config.api,credentials:e.config.credentials,activity:{agentSessionId:e.agentSessionId,content:t,ephemeral:n?.ephemeral,signal:n?.signal,signalMetadata:n?.signalMetadata}})},listActivities(t){return listLinearAgentSessionActivities({api:e.config.api,credentials:e.config.credentials,agentSessionId:e.agentSessionId,last:t?.last})},updateSession(t){return updateLinearAgentSession({api:e.config.api,credentials:e.config.credentials,id:e.agentSessionId,update:t})}}}async function dispatchAgentSession(e){let{event:t}=e,n={delivery:t.delivery,linear:buildLinearHandle({agentSessionId:t.agentSession.id,config:e.config}),session:t.agentSession},r=await e.onAgentSession(n,t);if(r===null)return;let i=t.agentActivity?.body,a=t.action===`prompted`&&i!==void 0?await resolvePromptResponses({body:i,config:e.config,event:t}):[];await e.send({context:[formatLinearContextBlock(t),...t.previousComments,...r.context??[]],inputResponses:a,message:messageFromLinearAgentSessionEvent(t)},{auth:r.auth,continuationToken:linearContinuationToken(t.agentSession.id),state:stateFromAgentSession(t.agentSession)})}async function resolvePromptResponses(e){try{return resolveLinearPromptInputResponses({activities:await listLinearAgentSessionActivities({api:e.config.api,credentials:e.config.credentials,agentSessionId:e.event.agentSession.id,last:20}),body:e.body})}catch(e){return log.warn(`linear HITL activity lookup failed — treating prompt as a message`,{error:e}),[]}}async function resolveReceiveSession(e,t){if(hasString(e,`agentSessionId`))return{id:e.agentSessionId};if(hasString(e,`issueId`))return createLinearAgentSessionOnIssue({api:t.api,credentials:t.credentials,externalLink:readString(e.externalLink),externalUrls:readExternalUrls(e.externalUrls),issueId:e.issueId});if(hasString(e,`commentId`))return createLinearAgentSessionOnComment({api:t.api,credentials:t.credentials,commentId:e.commentId,externalLink:readString(e.externalLink),externalUrls:readExternalUrls(e.externalUrls)});throw Error(`linearChannel().receive requires target.agentSessionId, issueId, or commentId.`)}function stateFromAgentSession(e){return{agentSessionId:e.id,agentSessionUrl:e.url??null,commentId:e.commentId??null,issueId:e.issueId??e.issue?.id??null,issueIdentifier:e.issue?.identifier??null,issueTitle:e.issue?.title??null,issueUrl:e.issue?.url??null,organizationId:`organizationId`in e?e.organizationId??null:null,pendingToolCallMessage:null,sourceCommentId:e.sourceCommentId??null}}function initialLinearState(){return{agentSessionId:null,agentSessionUrl:null,commentId:null,issueId:null,issueIdentifier:null,issueTitle:null,issueUrl:null,organizationId:null,pendingToolCallMessage:null,sourceCommentId:null}}async function verifyInbound(e,t){try{return await verifyLinearRequest(e,{webhookSecret:t?.webhookSecret,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`linear inbound verification failed`,{error:e}),null}}function hasString(e,t){return typeof e[t]==`string`&&e[t].length>0}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}function readExternalUrls(e){if(!Array.isArray(e))return;let n=e.filter(e=>isObject(e)&&typeof e.label==`string`&&typeof e.url==`string`);return n.length>0?n:void 0}function jsonOk(e){return new Response(JSON.stringify(e),{headers:{"content-type":`application/json; charset=utf-8`},status:200})}export{linearChannel};
1
+ import{createLogger}from"#internal/logging.js";import{isObject}from"#shared/guards.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{createLinearAgentActivity,createLinearAgentSessionOnComment,createLinearAgentSessionOnIssue,listLinearAgentSessionActivities,updateLinearAgentSession}from"#public/channels/linear/api.js";import{LINEAR_CHANNEL_DEFAULT_ROUTE}from"#public/channels/linear/constants.js";import{createDefaultEvents,defaultOnAgentSession}from"#public/channels/linear/defaults.js";import{formatLinearContextBlock,linearContinuationToken,messageFromLinearAgentSessionEvent,parseLinearWebhookEvent}from"#public/channels/linear/inbound.js";import{verifyLinearRequest}from"#public/channels/linear/verify.js";const log=createLogger(`linear.channel`);function linearChannel(e={}){let t=e.onAgentSession??defaultOnAgentSession,a={...createDefaultEvents({api:e.api,credentials:e.credentials}),...e.events};return defineChannel({kindHint:`linear`,state:initialLinearState(),metadata(e){return{agentSessionId:e.agentSessionId,commentId:e.commentId??null,issueId:e.issueId??null,issueIdentifier:e.issueIdentifier??null,organizationId:e.organizationId??null}},context(t,n){return rebuildLinearContext(t,n,e)},routes:[POST(e.route??LINEAR_CHANNEL_DEFAULT_ROUTE,async(n,{send:r,waitUntil:i})=>{let a=await verifyInbound(n,e.credentials);if(a===null)return new Response(`unauthorized`,{status:401});let o;try{o=parseLinearWebhookEvent({body:a,headers:n.headers})}catch(e){return log.warn(`inbound Linear body is not valid JSON`,{error:e}),jsonOk({ignored:!0,ok:!0})}return o===null?jsonOk({ignored:!0,ok:!0}):o.kind===`agent_session`?(i(dispatchAgentSession({config:e,event:o,onAgentSession:t,send:r})),jsonOk({ok:!0})):e.onDataWebhook===void 0?jsonOk({ignored:!0,ok:!0}):(i(Promise.resolve(e.onDataWebhook(o))),jsonOk({ok:!0}))})],async receive(t,{send:n}){let r=t.target,a=await resolveReceiveSession(r,e),o=readString(r.initialActivity);return o!==void 0&&await createLinearAgentActivity({api:e.api,credentials:e.credentials,activity:{agentSessionId:a.id,content:{body:o,type:`thought`}}}),n(t.message,{auth:t.auth,continuationToken:linearContinuationToken(a.id),state:stateFromAgentSession(a)})},events:a})}function rebuildLinearContext(e,t,n){return{linear:buildLinearHandle({agentSessionId:e.agentSessionId??``,config:n}),state:e}}function buildLinearHandle(e){return{agentSessionId:e.agentSessionId,createActivity(t,n){return createLinearAgentActivity({api:e.config.api,credentials:e.config.credentials,activity:{agentSessionId:e.agentSessionId,content:t,ephemeral:n?.ephemeral,signal:n?.signal,signalMetadata:n?.signalMetadata}})},listActivities(t){return listLinearAgentSessionActivities({api:e.config.api,credentials:e.config.credentials,agentSessionId:e.agentSessionId,last:t?.last})},updateSession(t){return updateLinearAgentSession({api:e.config.api,credentials:e.config.credentials,id:e.agentSessionId,update:t})}}}async function dispatchAgentSession(e){let{event:t}=e,n={delivery:t.delivery,linear:buildLinearHandle({agentSessionId:t.agentSession.id,config:e.config}),session:t.agentSession},r=await e.onAgentSession(n,t);r!==null&&await e.send({context:[formatLinearContextBlock(t),...t.previousComments,...r.context??[]],message:messageFromLinearAgentSessionEvent(t)},{auth:r.auth,continuationToken:linearContinuationToken(t.agentSession.id),state:stateFromAgentSession(t.agentSession)})}async function resolveReceiveSession(e,t){if(hasString(e,`agentSessionId`))return{id:e.agentSessionId};if(hasString(e,`issueId`))return createLinearAgentSessionOnIssue({api:t.api,credentials:t.credentials,externalLink:readString(e.externalLink),externalUrls:readExternalUrls(e.externalUrls),issueId:e.issueId});if(hasString(e,`commentId`))return createLinearAgentSessionOnComment({api:t.api,credentials:t.credentials,commentId:e.commentId,externalLink:readString(e.externalLink),externalUrls:readExternalUrls(e.externalUrls)});throw Error(`linearChannel().receive requires target.agentSessionId, issueId, or commentId.`)}function stateFromAgentSession(e){return{agentSessionId:e.id,agentSessionUrl:e.url??null,commentId:e.commentId??null,issueId:e.issueId??e.issue?.id??null,issueIdentifier:e.issue?.identifier??null,issueTitle:e.issue?.title??null,issueUrl:e.issue?.url??null,organizationId:`organizationId`in e?e.organizationId??null:null,pendingToolCallMessage:null,sourceCommentId:e.sourceCommentId??null}}function initialLinearState(){return{agentSessionId:null,agentSessionUrl:null,commentId:null,issueId:null,issueIdentifier:null,issueTitle:null,issueUrl:null,organizationId:null,pendingToolCallMessage:null,sourceCommentId:null}}async function verifyInbound(e,t){try{return await verifyLinearRequest(e,{webhookSecret:t?.webhookSecret,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`linear inbound verification failed`,{error:e}),null}}function hasString(e,t){return typeof e[t]==`string`&&e[t].length>0}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}function readExternalUrls(e){if(!Array.isArray(e))return;let n=e.filter(e=>isObject(e)&&typeof e.label==`string`&&typeof e.url==`string`);return n.length>0?n:void 0}function jsonOk(e){return new Response(JSON.stringify(e),{headers:{"content-type":`application/json; charset=utf-8`},status:200})}export{linearChannel};
@@ -145,6 +145,13 @@ export interface SlackThreadMessage {
145
145
  readonly botId: string | undefined;
146
146
  readonly ts: string;
147
147
  readonly threadTs: string;
148
+ /**
149
+ * Whether this message was authored by the Slack app bound to the thread.
150
+ * Classification requires the binding to carry the app identity (bot user
151
+ * id or app id). Inbound event bindings do; bindings rebuilt from session
152
+ * state or interaction payloads currently do not and mark every message
153
+ * `false`.
154
+ */
148
155
  readonly isMe: boolean;
149
156
  readonly raw: Record<string, unknown>;
150
157
  }
@@ -157,6 +164,18 @@ export interface SlackThreadMessage {
157
164
  export interface SlackThread {
158
165
  /** Recently fetched thread messages. Populated by {@link refresh}. */
159
166
  readonly recentMessages: readonly SlackThreadMessage[];
167
+ /**
168
+ * Fetch the latest replies via {@link refresh} and return the unique
169
+ * human Slack user ids participating in this thread, ordered by first
170
+ * appearance. For a human-started thread, the first entry is the
171
+ * starting author. Bot messages and user-less system messages are
172
+ * excluded.
173
+ *
174
+ * Shares {@link refresh} semantics: it observes at most the first 50
175
+ * messages of the thread, and refresh failures are logged and swallowed,
176
+ * so the returned list may be empty or stale.
177
+ */
178
+ listParticipants(): Promise<readonly string[]>;
160
179
  /**
161
180
  * Post a reply to this thread.
162
181
  *
@@ -195,8 +214,9 @@ export interface SlackThread {
195
214
  startTyping(status?: string): Promise<void>;
196
215
  /**
197
216
  * Fetch the latest replies in this thread into {@link recentMessages}
198
- * via `conversations.replies` (50-message cap). Failures are logged and
199
- * swallowed, leaving `recentMessages` empty.
217
+ * via `conversations.replies` (50-message cap). Overlapping calls share
218
+ * one request. Failures are logged and swallowed without discarding the
219
+ * most recently loaded messages.
200
220
  */
201
221
  refresh(): Promise<void>;
202
222
  /**
@@ -237,6 +257,26 @@ export interface SlackHandle {
237
257
  */
238
258
  uploadFiles(files: readonly FileUpload[], options?: SlackUploadFilesOptions): Promise<SlackUploadFilesResult>;
239
259
  }
260
+ /**
261
+ * Workspace-scoped Slack API handle exposed to generic inbound event
262
+ * handlers. Events such as `team_join` and `reaction_added` are not always
263
+ * bound to one message thread, so this surface deliberately exposes only
264
+ * workspace identity and the raw Web API escape hatch.
265
+ */
266
+ export interface SlackWorkspaceHandle {
267
+ /** Slack team id carried by the Events API envelope, when present. */
268
+ readonly teamId: string | undefined;
269
+ /**
270
+ * POST to a Slack Web API method. Returns Slack's raw JSON response.
271
+ * Callers must check `response.ok` themselves.
272
+ */
273
+ request(operation: string, body: unknown): Promise<SlackApiResponse>;
274
+ }
275
+ /** Builds the workspace-scoped API handle used by generic event callbacks. */
276
+ export declare function buildSlackWorkspaceHandle(input: {
277
+ readonly botToken: SlackBotToken | undefined;
278
+ readonly teamId: string | undefined;
279
+ }): SlackWorkspaceHandle;
240
280
  /**
241
281
  * The `{ thread, slack }` pair exposed through `ctx` to every mention
242
282
  * handler, interaction handler, and event handler. Returned by
@@ -256,7 +296,11 @@ interface SlackBinding {
256
296
  * anchor.
257
297
  */
258
298
  export declare function buildSlackBinding(input: {
299
+ /** Slack app id used to identify this app's fetched thread replies. */
300
+ readonly appId?: string;
259
301
  readonly botToken: SlackBotToken | undefined;
302
+ /** Slack bot user id used to identify this app's fetched thread replies. */
303
+ readonly botUserId?: string;
260
304
  readonly channelId: string;
261
305
  readonly threadTs: string;
262
306
  readonly teamId: string | undefined;
@@ -1 +1 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{isCardElement}from"#compiled/chat/index.js";import{callSlackApi as callSlackApi$1,fetchSlackThreadReplies,postSlackEphemeral,postSlackMessage,resolveSlackBotToken as resolveSlackBotToken$1,uploadSlackFiles}from"#compiled/@chat-adapter/slack/api.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{truncateTypingStatus}from"#public/channels/slack/limits.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";const log=createLogger(`slack.api`);function slackContinuationToken(e,t){return`${e}:${t}`}async function resolveSlackBotToken(e){let t=e??process.env.SLACK_BOT_TOKEN;if(!t)throw Error(`SLACK_BOT_TOKEN is required.`);return resolveSlackBotToken$1(t)}async function callSlackApi(e){return callSlackApi$1(e.operation,normalizeSlackApiBody(e.body),createSlackApiOptions(e.botToken))}function createSlackRequester(e){return(t,n)=>callSlackApi({botToken:e,operation:t,body:n})}function buildSlackBinding(e){let n=createSlackRequester(e.botToken),r=[],s=e.threadTs;function handleMessageTs(t){s||t===s||(s=t,e.onThreadTsChanged?.(t))}async function uploadFiles(t,n){let r=n?.channelId??e.channelId,i=n?.threadTs??s;return uploadSlackFiles(t.map(toSlackFileUpload),{...createSlackApiOptions(e.botToken),channelId:r||void 0,initialComment:n?.initialComment,threadTs:i||void 0})}return{thread:{recentMessages:r,async post(t){let n=normalizePostInput(t),r=n.files??[],i=`blocks`in n||`card`in n||`markdown`in n;if(r.length>0&&!i){let e=await uploadFiles(r,{initialComment:`text`in n?n.text:void 0});return{id:Array.isArray(e.raw.files)&&e.raw.files.length>0?String(e.raw.files[0].id??``):``,raw:e.raw}}let a=await postSlackMessage(buildPostMessageOptions(n,e.channelId,s,e.botToken)),c=a.id;if(handleMessageTs(c),r.length>0&&i)try{await uploadFiles(r)}catch(e){log.warn(`file upload after message post failed`,{error:e})}return{id:c,raw:a.raw}},async postEphemeral(t,n){let r=await postSlackEphemeral({...buildPostMessageOptions(normalizePostInput(n),e.channelId,s,e.botToken),user:t});return{id:r.id,raw:r.raw}},async postDirectMessage(t,r){let i=await n(`conversations.open`,{users:t}),a=i.ok===!0?i.channel?.id:void 0;if(typeof a!=`string`||a.length===0)throw Error(`Slack conversations.open failed: ${i.error??`unknown_error`}`);let s=await postSlackMessage(buildPostMessageOptions(normalizePostInput(r),a,``,e.botToken));return{id:s.id,raw:s.raw}},async startTyping(r){if(!(!e.channelId||!s))try{let t=r===void 0?``:truncateTypingStatus(r),i={channel_id:e.channelId,thread_ts:s,status:t};t.length>0&&(i.loading_messages=[t]);let a=await n(`assistant.threads.setStatus`,i);a.ok!==!0&&log.warn(`assistant.threads.setStatus returned not-ok`,{error:a.error})}catch(n){logError(log,`startTyping threw — swallowed`,n,{channelId:e.channelId})}},async refresh(){if(r.length=0,!(!e.channelId||!s))try{let t=await fetchSlackThreadReplies({...createSlackApiOptions(e.botToken),channel:e.channelId,limit:50,ts:s});for(let e of t.messages)r.push(parseThreadMessage(e,s))}catch(n){logError(log,`refresh threw — swallowed`,n,{channelId:e.channelId})}},mentionUser(e){return`<@${e}>`}},slack:{channelId:e.channelId,get threadTs(){return s},teamId:e.teamId,request:n,uploadFiles}}}function normalizePostInput(e){return typeof e==`string`?{markdown:e}:isCardElement(e)?{card:e}:e}function buildPostMessageOptions(e,t,n,r){let i={...createSlackApiOptions(r),channel:t,threadTs:n||void 0,unfurlLinks:!1,unfurlMedia:!1};return`card`in e?(i.blocks=cardToBlocks(e.card),i.text=e.fallbackText??cardToFallbackText(e.card),i):`blocks`in e?(i.blocks=[...e.blocks],e.text!==void 0&&(i.text=e.text),i):`markdown`in e?(i.markdownText=e.markdown,i):(i.text=e.text,i)}function createSlackApiOptions(e){return{token:()=>resolveSlackBotToken(e)}}function normalizeSlackApiBody(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:{}}function toSlackFileUpload(e){return{data:normalizeFileData(e.data),filename:e.filename}}function normalizeFileData(e){if(e instanceof ArrayBuffer||typeof Blob<`u`&&e instanceof Blob)return e;if(ArrayBuffer.isView(e)){let t=e;return new Uint8Array(t.buffer,t.byteOffset,t.byteLength)}return e}function parseThreadMessage(e,t){let n=typeof e.text==`string`?e.text:``,r=typeof e.ts==`string`?e.ts:``,i=typeof e.thread_ts==`string`?e.thread_ts:t,a=typeof e.user==`string`?e.user:void 0,o=typeof e.bot_id==`string`?e.bot_id:void 0;return{text:n,markdown:slackMrkdwnToGfm(n),user:a,botId:o,ts:r,threadTs:i,isMe:o!==void 0,raw:e}}export{buildSlackBinding,callSlackApi,resolveSlackBotToken,slackContinuationToken};
1
+ import{createLogger,logError}from"#internal/logging.js";import{isCardElement}from"#compiled/chat/index.js";import{callSlackApi as callSlackApi$1,fetchSlackThreadReplies,postSlackEphemeral,postSlackMessage,resolveSlackBotToken as resolveSlackBotToken$1,uploadSlackFiles}from"#compiled/@chat-adapter/slack/api.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{truncateTypingStatus}from"#public/channels/slack/limits.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";const log=createLogger(`slack.api`);function slackContinuationToken(e,t){return`${e}:${t}`}async function resolveSlackBotToken(e){let t=e??process.env.SLACK_BOT_TOKEN;if(!t)throw Error(`SLACK_BOT_TOKEN is required.`);return resolveSlackBotToken$1(t)}async function callSlackApi(e){return callSlackApi$1(e.operation,normalizeSlackApiBody(e.body),createSlackApiOptions(e.botToken))}function createSlackRequester(e){return(t,n)=>callSlackApi({botToken:e,operation:t,body:n})}function buildSlackWorkspaceHandle(e){return{teamId:e.teamId,request:createSlackRequester(e.botToken)}}function buildSlackBinding(e){let n=createSlackRequester(e.botToken),r=[],s=e.threadTs,l;function handleMessageTs(t){s||t===s||(s=t,e.onThreadTsChanged?.(t))}async function uploadFiles(t,n){let r=n?.channelId??e.channelId,i=n?.threadTs??s;return uploadSlackFiles(t.map(toSlackFileUpload),{...createSlackApiOptions(e.botToken),channelId:r||void 0,initialComment:n?.initialComment,threadTs:i||void 0})}function refreshMessages(){if(l!==void 0)return l;async function fetchAndReplace(){if(!e.channelId||!s){r=[];return}try{r=(await fetchSlackThreadReplies({...createSlackApiOptions(e.botToken),channel:e.channelId,limit:50,ts:s})).messages.map(t=>parseThreadMessage(t,s,{appId:e.appId,botUserId:e.botUserId}))}catch(n){logError(log,`refresh threw — swallowed`,n,{channelId:e.channelId})}}let n=fetchAndReplace().finally(()=>{l===n&&(l=void 0)});return l=n,n}let u={get recentMessages(){return r},async listParticipants(){await u.refresh();let e=new Set;for(let t of r)t.user!==void 0&&t.botId===void 0&&e.add(t.user);return[...e]},async post(t){let n=normalizePostInput(t),r=n.files??[],i=`blocks`in n||`card`in n||`markdown`in n;if(r.length>0&&!i){let e=await uploadFiles(r,{initialComment:`text`in n?n.text:void 0});return{id:Array.isArray(e.raw.files)&&e.raw.files.length>0?String(e.raw.files[0].id??``):``,raw:e.raw}}let a=await postSlackMessage(buildPostMessageOptions(n,e.channelId,s,e.botToken)),c=a.id;if(handleMessageTs(c),r.length>0&&i)try{await uploadFiles(r)}catch(e){log.warn(`file upload after message post failed`,{error:e})}return{id:c,raw:a.raw}},async postEphemeral(t,n){let r=await postSlackEphemeral({...buildPostMessageOptions(normalizePostInput(n),e.channelId,s,e.botToken),user:t});return{id:r.id,raw:r.raw}},async postDirectMessage(t,r){let i=await n(`conversations.open`,{users:t}),a=i.ok===!0?i.channel?.id:void 0;if(typeof a!=`string`||a.length===0)throw Error(`Slack conversations.open failed: ${i.error??`unknown_error`}`);let s=await postSlackMessage(buildPostMessageOptions(normalizePostInput(r),a,``,e.botToken));return{id:s.id,raw:s.raw}},async startTyping(r){if(!(!e.channelId||!s))try{let t=r===void 0?``:truncateTypingStatus(r),i={channel_id:e.channelId,thread_ts:s,status:t};t.length>0&&(i.loading_messages=[t]);let a=await n(`assistant.threads.setStatus`,i);a.ok!==!0&&log.warn(`assistant.threads.setStatus returned not-ok`,{error:a.error})}catch(n){logError(log,`startTyping threw — swallowed`,n,{channelId:e.channelId})}},refresh(){return refreshMessages()},mentionUser(e){return`<@${e}>`}};return{thread:u,slack:{channelId:e.channelId,get threadTs(){return s},teamId:e.teamId,request:n,uploadFiles}}}function normalizePostInput(e){return typeof e==`string`?{markdown:e}:isCardElement(e)?{card:e}:e}function buildPostMessageOptions(e,t,n,r){let i={...createSlackApiOptions(r),channel:t,threadTs:n||void 0,unfurlLinks:!1,unfurlMedia:!1};return`card`in e?(i.blocks=cardToBlocks(e.card),i.text=e.fallbackText??cardToFallbackText(e.card),i):`blocks`in e?(i.blocks=[...e.blocks],e.text!==void 0&&(i.text=e.text),i):`markdown`in e?(i.markdownText=e.markdown,i):(i.text=e.text,i)}function createSlackApiOptions(e){return{token:()=>resolveSlackBotToken(e)}}function normalizeSlackApiBody(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:{}}function toSlackFileUpload(e){return{data:normalizeFileData(e.data),filename:e.filename}}function normalizeFileData(e){if(e instanceof ArrayBuffer||typeof Blob<`u`&&e instanceof Blob)return e;if(ArrayBuffer.isView(e)){let t=e;return new Uint8Array(t.buffer,t.byteOffset,t.byteLength)}return e}function parseThreadMessage(e,t,n){let r=typeof e.text==`string`?e.text:``,i=typeof e.ts==`string`?e.ts:``,a=typeof e.thread_ts==`string`?e.thread_ts:t,o=typeof e.user==`string`?e.user:void 0,s=typeof e.bot_id==`string`?e.bot_id:void 0,c=typeof e.app_id==`string`?e.app_id:void 0,l=n.botUserId!==void 0&&o===n.botUserId||n.appId!==void 0&&c===n.appId;return{text:r,markdown:slackMrkdwnToGfm(r),user:o,botId:s,ts:i,threadTs:a,isMe:l,raw:e}}export{buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,resolveSlackBotToken,slackContinuationToken};
@@ -19,11 +19,12 @@ export declare function collectSlackFileParts(attachments: readonly SlackAttachm
19
19
  * Prefers attachments on the triggering mention (the common case: user
20
20
  * uploads a file and mentions the bot in the same message). When the
21
21
  * mention has none, refreshes the thread via {@link SlackThread.refresh}
22
- * and picks the latest non-bot message's attachments covering the
23
- * case where a user dropped a file in the thread first, then mentioned
24
- * the bot in a follow-up. Any error during refresh is logged and treated
25
- * as "no attachments" so the text portion of the mention still gets
26
- * delivered.
22
+ * and picks the attachments of the latest message this app did not
23
+ * author (the thread message `isMe` classification) covering the case
24
+ * where a user or another bot dropped a file in the thread first, then a
25
+ * user mentioned the bot in a follow-up. Any error during refresh is logged
26
+ * and treated as "no attachments" so the text portion of the mention
27
+ * still gets delivered.
27
28
  *
28
29
  * Skips the thread-history lookback when the policy disables uploads,
29
30
  * since the refresh can't surface anything we'd deliver.
@@ -1,4 +1,4 @@
1
1
  import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{SLACK_MAX_BLOCKS_PER_MESSAGE,truncateMessageText,truncateTypingStatus}from"#public/channels/slack/limits.js";import{describeActionRequests}from"#public/channels/slack/action-status.js";import{buildSlackAuthContext,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{buildAuthCompletedText,buildAuthEphemeralBlocks,buildAuthRequiredPublicText,formatConnectionDisplayName}from"#public/channels/slack/connections.js";import{formatInputRequestFallbackText,renderInputRequestBlocks}from"#public/channels/slack/hitl.js";const log=createLogger(`slack.defaults`);function defaultSlackAuth(e,t){let n=e.author;return n?buildSlackAuthContext({channelId:t.slack.channelId,fullName:n.fullName,isBot:n.isBot,teamId:e.teamId,threadTs:t.slack.threadTs,userId:n.userId,userName:n.userName}):null}async function defaultOnAppMention(e,t){return await e.thread.startTyping(`Thinking...`),{auth:defaultSlackAuth(t,e)}}async function defaultOnDirectMessage(e,t){return await e.thread.startTyping(`Thinking...`),{auth:defaultSlackAuth(t,e)}}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function defaultInputRequestedHandler(){return async(e,t,n)=>{for(let n of buildInputRequestPosts(e.requests))await t.thread.post(n)}}function buildInputRequestPosts(e){let t=[];for(let n of e){let e=renderInputRequestBlocks(n),i=t.at(-1);i&&i.blocks.length+e.length<=SLACK_MAX_BLOCKS_PER_MESSAGE?(i.blocks.push(...e),i.fallbacks.push(formatInputRequestFallbackText(n))):t.push({blocks:e,fallbacks:[formatInputRequestFallbackText(n)]})}return t.map(e=>({blocks:e.blocks,text:truncateMessageText(e.fallbacks.join(`
2
2
  `))}))}const defaultEvents={async"turn.started"(e,t,n){t.state.pendingToolCallMessage=null,t.state.lastReasoningTypingAtMs=null,t.state.lastReasoningTypingStatus=null,await t.thread.startTyping(`Working...`)},async"reasoning.appended"(e,t,n){let r=firstNonEmptyLine(e.reasoningSoFar);if(r===void 0)return;let i=truncateTypingStatus(r),o=t.state.lastReasoningTypingStatus,s=o!=null&&i.startsWith(o)&&i.length>=o.length+4,c=Date.now(),l=t.state.lastReasoningTypingAtMs;if(!s&&l!=null){let e=c-l;if(e>=0&&e<5e3)return}await t.thread.startTyping(i),t.state.lastReasoningTypingAtMs=c,t.state.lastReasoningTypingStatus=i},async"actions.requested"(e,t,n){let r=t.state.pendingToolCallMessage;if(t.state.pendingToolCallMessage=null,r){await t.thread.startTyping(truncateTypingStatus(r));return}await t.thread.startTyping(truncateTypingStatus(describeActionRequests(e.actions)))},async"message.completed"(e,t,n){if(e.finishReason===`tool-calls`){t.state.pendingToolCallMessage=e.message?firstNonEmptyLine(e.message)??null:null;return}if(t.state.pendingToolCallMessage=null,!e.message){await t.thread.startTyping();return}await t.thread.post(e.message)},async"turn.failed"(e,r,i){let a=formatErrorHint(e),o=extractErrorId(e.details);await r.thread.post([`I hit an error while handling your request${a}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...o?[``,`_Error id: \`${o}\`_`]:[]].join(`
3
3
  `))},async"session.failed"(e,r){let i=formatErrorHint(e),a=extractErrorId(e.details);await r.thread.post([`This session couldn't recover from an error${i}.`,``,`Start a new thread to continue — I can't pick this one back up.`,...a?[``,`_Error id: \`${a}\`_`]:[]].join(`
4
- `))},async"authorization.required"(e,t,n){let r=e.authorization?.displayName??formatConnectionDisplayName(e.name),i=slackUserIdFromAuthContext(n.session.auth.current)??t.state.triggeringUserId??null,a=e.authorization?.url,o=t.state.pendingAuthMessageTs??{};if(o[e.name]===void 0){let n=buildAuthRequiredPublicText({displayName:r,hasUser:i!==null});try{let r=await t.thread.post(n);r.id&&(t.state.pendingAuthMessageTs={...o,[e.name]:r.id})}catch(t){log.error(`Slack auth public message delivery failed`,{name:e.name,error:t})}}if(i&&a){let n=e.authorization?.userCode;try{await t.thread.postEphemeral(i,{blocks:buildAuthEphemeralBlocks({displayName:r,url:a,userCode:n}),text:n?`Sign in with ${r}: ${a} (code: ${n})`:`Sign in with ${r}: ${a}`})}catch(t){log.error(`Slack auth ephemeral delivery failed`,{name:e.name,error:t})}}},async"authorization.completed"(e,t,n){let r=t.state.pendingAuthMessageTs??{},i=r[e.name];if(i===void 0)return;let a=buildAuthCompletedText({displayName:e.authorization?.displayName??formatConnectionDisplayName(e.name),outcome:e.outcome,reason:e.reason});try{await t.slack.request(`chat.update`,{channel:t.slack.channelId,ts:i,text:a})}catch(t){log.error(`Slack auth status edit failed`,{name:e.name,error:t})}let o={...r};delete o[e.name],t.state.pendingAuthMessageTs=o}};export{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage,defaultSlackAuth};
4
+ `))},async"authorization.required"(e,t,n){let r=e.authorization?.displayName??formatConnectionDisplayName(e.name),i=slackUserIdFromAuthContext(n.session.auth.current)??t.state.triggeringUserId??null,a=e.authorization?.url,o=t.state.pendingAuthMessageTs??{};if(o[e.name]===void 0){let n=buildAuthRequiredPublicText({displayName:r,hasUser:i!==null});try{let r=await t.thread.post(n);r.id&&(t.state.pendingAuthMessageTs={...o,[e.name]:r.id})}catch(t){log.error(`Slack auth public message delivery failed`,{name:e.name,error:t})}}if(i&&a){let n=e.authorization?.userCode;try{await t.thread.postEphemeral(i,{blocks:buildAuthEphemeralBlocks({displayName:r,url:a,userCode:n}),text:n?`Sign in with ${r}: ${a} (code: ${n})`:`Sign in with ${r}: ${a}`})}catch(t){log.error(`Slack auth ephemeral delivery failed`,{name:e.name,error:t})}}},async"authorization.completed"(e,t,n){let r=e.authorization?.displayName??formatConnectionDisplayName(e.name);e.outcome===`authorized`&&await t.thread.startTyping(`Connected to ${r}. Resuming...`);let i=t.state.pendingAuthMessageTs??{},a=i[e.name];if(a===void 0)return;let o=buildAuthCompletedText({displayName:r,outcome:e.outcome,reason:e.reason});try{await t.slack.request(`chat.update`,{channel:t.slack.channelId,ts:a,text:o})}catch(t){log.error(`Slack auth status edit failed`,{name:e.name,error:t})}let s={...i};delete s[e.name],t.state.pendingAuthMessageTs=s}};export{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage,defaultSlackAuth};
@@ -64,6 +64,15 @@ export interface SlackMessage {
64
64
  /** Raw inbound event payload from Slack. */
65
65
  readonly raw: Record<string, unknown>;
66
66
  }
67
+ /**
68
+ * Open-ended Slack Events API payload handed to `slackChannel({ onEvent })`.
69
+ * `type` is the Slack event discriminator; all event-specific fields pass
70
+ * through unchanged from the signed webhook body.
71
+ */
72
+ export interface SlackEvent {
73
+ readonly type: string;
74
+ readonly [key: string]: unknown;
75
+ }
67
76
  /**
68
77
  * Slack webhook envelope for an `event_callback`. The channel cares
69
78
  * only about `team_id` (for ergonomic auth derivation) and the inner
@@ -72,6 +81,10 @@ export interface SlackMessage {
72
81
  export interface SlackEventCallback {
73
82
  readonly type: "event_callback";
74
83
  readonly team_id?: string;
84
+ readonly authorizations?: readonly {
85
+ readonly is_bot?: boolean;
86
+ readonly user_id?: string;
87
+ }[];
75
88
  readonly event?: {
76
89
  readonly type?: string;
77
90
  } & Record<string, unknown>;
@@ -79,6 +92,20 @@ export interface SlackEventCallback {
79
92
  readonly event_time?: number;
80
93
  readonly [key: string]: unknown;
81
94
  }
95
+ /**
96
+ * Validated Slack Events API callback envelope handed to `onEvent` through
97
+ * {@link SlackInboundEventContext}. Unlike the permissive parser input type,
98
+ * this shape always carries an event with a non-empty `type` discriminator.
99
+ */
100
+ export interface SlackEventEnvelope extends SlackEventCallback {
101
+ readonly event: SlackEvent;
102
+ }
103
+ /**
104
+ * Parses a raw JSON webhook body into an open-ended Slack Events API envelope.
105
+ * Returns `null` for URL verification, interactivity, and malformed callback
106
+ * payloads. Invalid JSON is allowed to throw so the route can log it once.
107
+ */
108
+ export declare function parseSlackEventEnvelope(body: string): SlackEventEnvelope | null;
82
109
  /**
83
110
  * Parses a Slack `app_mention` event into a {@link SlackMessage}.
84
111
  *
@@ -99,6 +126,10 @@ export declare function parseAppMentionEvent(envelope: SlackEventCallback): Slac
99
126
  * - the message was posted by a bot (`bot_id` set) — this prevents the
100
127
  * bot's own DM replies from re-triggering the handler.
101
128
  */
129
+ /** Returns the bot user id attached to a Slack event authorization. */
130
+ export declare function slackEventBotUserId(envelope: SlackEventCallback): string | undefined;
131
+ /** Parses a Slack message event without applying bot or subtype policy. */
132
+ export declare function parseMessageEvent(envelope: SlackEventCallback): SlackMessage | null;
102
133
  export declare function parseDirectMessageEvent(envelope: SlackEventCallback): SlackMessage | null;
103
134
  export declare function slackMessageFromWebhookPayload(payload: SlackAppMentionPayload | SlackDirectMessagePayload): SlackMessage | null;
104
135
  /**
@@ -1 +1 @@
1
- import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";function parseAppMentionEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`app_mention`?null:buildSlackMessage(t,e.team_id)}function parseDirectMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;if(!t||t.type!==`message`)return null;let n=t;return n.channel_type!==`im`||typeof n.subtype==`string`&&n.subtype.length>0&&n.subtype!==`file_share`||typeof n.bot_id==`string`&&n.bot_id.length>0?null:buildSlackMessage(n,e.team_id)}function slackMessageFromWebhookPayload(t){return t.kind===`direct_message`&&(typeof t.subtype==`string`&&t.subtype.length>0&&t.subtype!==`file_share`||typeof t.botId==`string`&&t.botId.length>0)||!t.channelId||!t.ts?null:{text:t.text,markdown:slackMrkdwnToGfm(t.text),ts:t.ts,threadTs:t.threadTs,channelId:t.channelId,teamId:t.teamId,author:parsePayloadAuthor(t),attachments:parsePayloadAttachments(t.files),raw:t.raw}}function buildSlackMessage(t,n){let r=typeof t.channel==`string`?t.channel:``,i=typeof t.ts==`string`?t.ts:``;if(!r||!i)return null;let a=typeof t.text==`string`?t.text:``,o=typeof t.thread_ts==`string`?t.thread_ts:i,s=typeof n==`string`?n:void 0;return{text:a,markdown:slackMrkdwnToGfm(a),ts:i,threadTs:o,channelId:r,teamId:s,author:parseAuthor(t),attachments:parseAttachments(t.files),raw:t}}function parseAuthor(e){let t=typeof e.user==`string`?e.user:``;if(t)return{userId:t,userName:typeof e.username==`string`?e.username:void 0,fullName:void 0,isBot:typeof e.bot_id==`string`&&e.bot_id.length>0,isMe:!1}}function parseAttachments(e){return Array.isArray(e)?e.map(toAttachment):[]}function toAttachment(e){let t=typeof e.mimetype==`string`?e.mimetype:void 0,n=typeof e.url_private==`string`?e.url_private:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:n,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}function toPayloadAttachment(e){return{id:e.id,type:e.type,url:e.url,name:e.name,mimeType:e.mimeType,size:e.size}}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function parsePayloadAuthor(e){if(!e.userId)return;let t=e.raw;return{userId:e.userId,userName:typeof t.username==`string`?t.username:void 0,fullName:void 0,isBot:typeof t.bot_id==`string`&&t.bot_id.length>0,isMe:!1}}function parsePayloadAttachments(e){return Array.isArray(e)?e.map(toPayloadAttachment):[]}export{parseAppMentionEvent,parseDirectMessageEvent,slackMessageFromWebhookPayload};
1
+ import{isObject}from"#shared/guards.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";function parseSlackEventEnvelope(t){let n=JSON.parse(t);if(!isObject(n)||n.type!==`event_callback`)return null;let r=n.event;return!isObject(r)||typeof r.type!=`string`||r.type.length===0?null:n}function parseAppMentionEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`app_mention`?null:buildSlackMessage(t,e.team_id)}function slackEventBotUserId(e){return e.authorizations?.find(e=>e.is_bot===!0&&typeof e.user_id==`string`)?.user_id}function parseMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`message`?null:buildSlackMessage(t,e.team_id)}function parseDirectMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;if(!t||t.type!==`message`)return null;let n=t;return n.channel_type!==`im`||!isHumanMessage(n)?null:buildSlackMessage(n,e.team_id)}function isHumanMessage(e){return typeof e.subtype==`string`&&e.subtype.length>0&&e.subtype!==`file_share`?!1:!(typeof e.bot_id==`string`&&e.bot_id.length>0)}function slackMessageFromWebhookPayload(e){return e.kind===`direct_message`&&(typeof e.subtype==`string`&&e.subtype.length>0&&e.subtype!==`file_share`||typeof e.botId==`string`&&e.botId.length>0)||!e.channelId||!e.ts?null:{text:e.text,markdown:slackMrkdwnToGfm(e.text),ts:e.ts,threadTs:e.threadTs,channelId:e.channelId,teamId:e.teamId,author:parsePayloadAuthor(e),attachments:parsePayloadAttachments(e.files),raw:e.raw}}function buildSlackMessage(e,n){let r=typeof e.channel==`string`?e.channel:``,i=typeof e.ts==`string`?e.ts:``;if(!r||!i)return null;let a=typeof e.text==`string`?e.text:``,o=typeof e.thread_ts==`string`?e.thread_ts:i,s=typeof n==`string`?n:void 0;return{text:a,markdown:slackMrkdwnToGfm(a),ts:i,threadTs:o,channelId:r,teamId:s,author:parseAuthor(e),attachments:parseAttachments(e.files),raw:e}}function parseAuthor(e){let t=typeof e.user==`string`?e.user:``;if(t)return{userId:t,userName:typeof e.username==`string`?e.username:void 0,fullName:void 0,isBot:typeof e.bot_id==`string`&&e.bot_id.length>0,isMe:!1}}function parseAttachments(e){return Array.isArray(e)?e.map(toAttachment):[]}function toAttachment(e){let t=typeof e.mimetype==`string`?e.mimetype:void 0,n=typeof e.url_private==`string`?e.url_private:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:n,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}function toPayloadAttachment(e){return{id:e.id,type:e.type,url:e.url,name:e.name,mimeType:e.mimeType,size:e.size}}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function parsePayloadAuthor(e){if(!e.userId)return;let t=e.raw;return{userId:e.userId,userName:typeof t.username==`string`?t.username:void 0,fullName:void 0,isBot:typeof t.bot_id==`string`&&t.bot_id.length>0,isMe:!1}}function parsePayloadAttachments(e){return Array.isArray(e)?e.map(toPayloadAttachment):[]}export{parseAppMentionEvent,parseDirectMessageEvent,parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackMessageFromWebhookPayload};
@@ -5,8 +5,8 @@
5
5
  * without taking a direct `ai` dependency.
6
6
  */
7
7
  export type { ModelMessage } from "ai";
8
- export { slackChannel, type SlackApiResponse, type SlackAuthorizationEventContext, type SlackAuthorizationRequiredHandler, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackHandle, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInstrumentationMetadata, type SlackInitialMessage, type SlackInteractionAction, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveTarget, type SlackThread, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
9
- export type { SlackAttachment, SlackAuthor, SlackInboundContext, SlackMessage, } from "#public/channels/slack/inbound.js";
8
+ export { slackChannel, type SlackApiResponse, type SlackAuthorizationEventContext, type SlackAuthorizationRequiredHandler, type SlackBotToken, type SlackCancelOptions, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventCancelOptions, type SlackEventContext, type SlackEventReceiveFn, type SlackEventReceiveOptions, type SlackHandle, type SlackInboundEventContext, type SlackInboundMessageContext, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInitialMessage, type SlackInstrumentationMetadata, type SlackInteractionAction, type SlackInteractionContext, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveTarget, type SlackSessionTarget, type SlackThread, type SlackWebhookVerifier, type SlackWorkspaceHandle, } from "#public/channels/slack/slackChannel.js";
9
+ export type { SlackAttachment, SlackAuthor, SlackEvent, SlackEventEnvelope, SlackInboundContext, SlackMessage, } from "#public/channels/slack/inbound.js";
10
10
  export { callSlackApi, resolveSlackBotToken, slackContinuationToken, type SlackPostInput, type SlackPostedMessage, type SlackThreadMessage, type SlackUploadFilesOptions, type SlackUploadFilesResult, } from "#public/channels/slack/api.js";
11
11
  export { defaultSlackAuth } from "#public/channels/slack/defaults.js";
12
12
  export { describeActionRequest, describeActionRequests, } from "#public/channels/slack/action-status.js";
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { type SlackBlockActionsPayload } from "#compiled/@chat-adapter/slack/webhook.js";
20
20
  import type { SlackChannelConfig, SlackChannelState, SlackInteractionAction } from "#public/channels/slack/slackChannel.js";
21
- import type { SendFn } from "#public/definitions/channel.js";
21
+ import type { CancelFn, SendFn } from "#public/definitions/channel.js";
22
22
  /**
23
23
  * Decoded view of a Slack `block_actions` payload. Returned by
24
24
  * {@link parseBlockActionsPayload} and read by the handler.
@@ -59,6 +59,7 @@ export interface InteractionHandlerDeps {
59
59
  * anything else to `config.onInteraction`.
60
60
  */
61
61
  export declare function handleInteractionPost(rawBody: string, ctx: {
62
+ cancel: CancelFn;
62
63
  send: SendFn<SlackChannelState>;
63
64
  waitUntil: (task: Promise<unknown>) => void;
64
65
  }, deps: InteractionHandlerDeps): Promise<Response>;
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{SLACK_CARD_SUBTEXT_MAX_LENGTH,truncateCardSubtext}from"#public/channels/slack/limits.js";import{buildSlackBinding,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,freeformRequestIdFromActionId,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=o?.id??s.team_id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,threadTs:e.threadTs,teamId:e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlock(e){return findPromptBlocks(e)[0]}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlock(e)?.text?.text;return typeof t==`string`&&t.length>0?t:void 0}function buildAnsweredHitlMessageBlocks(e){let t=findActionBlockIndex(e.messageBlocks,e.actionId);if(t===-1)return buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:e.answerLabel,userId:e.userId});let n=e.messageBlocks[t],r=answeredBlocksFromActionBlock({answerLabel:e.answerLabel,block:n,userId:e.userId})??buildAnsweredBlocks({promptBlocks:promptBlocksFromActionBlock(n),answerLabel:e.answerLabel,userId:e.userId});return[...e.messageBlocks.slice(0,t),...r,...e.messageBlocks.slice(t+1)]}function findActionBlockIndex(e,t){return e.findIndex(e=>blockContainsActionId(e,t))}function blockContainsActionId(e,t){return isObjectRecord(e)?actionsContainActionId(e.elements,t)||actionsContainActionId(e.actions,t):!1}function actionsContainActionId(e,t){return Array.isArray(e)?e.some(e=>isObjectRecord(e)&&e.action_id===t):!1}function isObjectRecord(e){return typeof e==`object`&&!!e}function answeredBlocksFromActionBlock(e){if(!isObjectRecord(e.block)||e.block.type!==`card`)return;let{actions:t,subtext:n,...r}=e.block,i={...r,subtext:{type:`mrkdwn`,text:formatAnsweredCardSubtext(e),verbatim:!1}};return hasCardContent(i)?[i]:void 0}function formatAnsweredCardSubtext(e){let r=e.userId.length>0?` by <@${e.userId}>`:``,i=SLACK_CARD_SUBTEXT_MAX_LENGTH-20-1-r.length;return truncateCardSubtext(`:white_check_mark: *${truncateWithEllipsis(e.answerLabel,i)}*${r}`)}function truncateWithEllipsis(e,t){if(t<=0)return``;if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function promptBlocksFromActionBlock(e){if(!isObjectRecord(e)||e.type!==`card`)return[];let{actions:t,...n}=e;return hasCardContent(n)?[n]:[]}function hasCardContent(e){return e.body!==void 0||e.title!==void 0||e.hero_image!==void 0}async function handleInteractionPost(e,t,n){let i=new Response(`ok`,{status:200}),s;try{s=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),i}if(s.kind===`view_submission`)return handleViewSubmission(s,t,n);if(s.kind!==`block_actions`)return i;let c=parseBlockActionsPayload(s);if(!c)return i;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:s.raw,interaction:c,freeformAction:l,deps:n}),i;let u=slackContinuationToken(c.channelId,c.threadTs),d=c.actions.map(deriveHitlResponse).filter(e=>e!==null);if(d.length>0){let e=c.actions[0]?.user;if(!e)return i;t.waitUntil(t.send({inputResponses:d},{auth:buildSlackAuthContext({channelId:c.channelId,teamId:c.teamId,threadTs:c.threadTs,userId:e.id,userName:e.username??e.name}),continuationToken:u,state:{channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId??null,triggeringUserId:e.id}}).catch(e=>{log.error(`HITL interaction delivery failed`,{error:e})})),t.waitUntil(updateAnsweredHitlCard(c,n).catch(e=>{log.error(`HITL answered-card update failed`,{error:e})}))}let p=n.config.onInteraction;if(p){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let{thread:i,slack:a}=buildSlackBinding({botToken:n.config.credentials?.botToken,channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId}),o={thread:i,slack:a};for(let n of e)t.waitUntil(Promise.resolve(p(n,o)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return i}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,threadTs:e.interaction.threadTs,messageTs:r,requestId:n},prompt:readPromptTextFromBlocks(e.interaction.messageBlocks)}),s=await resolveSlackBotToken(e.deps.config.credentials?.botToken),c=await fetch(`https://slack.com/api/views.open`,{method:`POST`,headers:{authorization:`Bearer ${s}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({trigger_id:t,view:o})});c.ok||log.error(`Slack views.open returned non-2xx`,{status:c.status})}async function handleViewSubmission(e,t,n){let r=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return r;let i;try{i=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),r}if(!i.continuationToken||!i.requestId||!i.messageTs||!i.channelId||!i.threadTs)return r;let a=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(a.length===0)return r;let u=e.user,d=e.userId,f=u?.teamId??e.teamId??null;return t.waitUntil(t.send({inputResponses:[{requestId:i.requestId,text:a}]},{auth:buildSlackAuthContext({channelId:i.channelId,teamId:f,threadTs:i.threadTs,userId:d,userName:u?.username??u?.name}),continuationToken:i.continuationToken,state:{channelId:i.channelId,threadTs:i.threadTs,teamId:f,triggeringUserId:d}}).catch(e=>{log.error(`freeform answer delivery failed`,{error:e})})),t.waitUntil(updateAnsweredFreeformCard({channelId:i.channelId,messageTs:i.messageTs,answerLabel:a,userId:d??void 0,deps:n}).catch(e=>{log.error(`freeform answered-card update failed`,{error:e})})),r}async function updateAnsweredHitlCard(e,t){let n=e.actions.find(e=>isHitlAction(e.actionId));if(!n||!n.messageTs)return;let r=n.label??n.selectedOptionValue??n.value;if(!r)return;let a=buildAnsweredHitlMessageBlocks({actionId:n.actionId,answerLabel:r,messageBlocks:e.messageBlocks,userId:n.user.id}),o=await resolveSlackBotToken(t.config.credentials?.botToken),s=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${o}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:n.messageTs,blocks:a,text:`Answered: ${r}`})});if(!s.ok)throw Error(`Slack chat.update returned HTTP ${s.status}`)}async function updateAnsweredFreeformCard(e){let t=buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId}),n=await resolveSlackBotToken(e.deps.config.credentials?.botToken),r=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${n}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:e.messageTs,blocks:t,text:`Answered: ${e.answerLabel}`})});if(!r.ok)throw Error(`Slack chat.update returned HTTP ${r.status}`)}export{handleInteractionPost,parseBlockActionsPayload};
1
+ import{createLogger}from"#internal/logging.js";import{SLACK_CARD_SUBTEXT_MAX_LENGTH,truncateCardSubtext}from"#public/channels/slack/limits.js";import{buildSlackBinding,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,freeformRequestIdFromActionId,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=o?.id??s.team_id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,threadTs:e.threadTs,teamId:e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlock(e){return findPromptBlocks(e)[0]}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlock(e)?.text?.text;return typeof t==`string`&&t.length>0?t:void 0}function buildAnsweredHitlMessageBlocks(e){let t=findActionBlockIndex(e.messageBlocks,e.actionId);if(t===-1)return buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:e.answerLabel,userId:e.userId});let n=e.messageBlocks[t],r=answeredBlocksFromActionBlock({answerLabel:e.answerLabel,block:n,userId:e.userId})??buildAnsweredBlocks({promptBlocks:promptBlocksFromActionBlock(n),answerLabel:e.answerLabel,userId:e.userId});return[...e.messageBlocks.slice(0,t),...r,...e.messageBlocks.slice(t+1)]}function findActionBlockIndex(e,t){return e.findIndex(e=>blockContainsActionId(e,t))}function blockContainsActionId(e,t){return isObjectRecord(e)?actionsContainActionId(e.elements,t)||actionsContainActionId(e.actions,t):!1}function actionsContainActionId(e,t){return Array.isArray(e)?e.some(e=>isObjectRecord(e)&&e.action_id===t):!1}function isObjectRecord(e){return typeof e==`object`&&!!e}function answeredBlocksFromActionBlock(e){if(!isObjectRecord(e.block)||e.block.type!==`card`)return;let{actions:t,subtext:n,...r}=e.block,i={...r,subtext:{type:`mrkdwn`,text:formatAnsweredCardSubtext(e),verbatim:!1}};return hasCardContent(i)?[i]:void 0}function formatAnsweredCardSubtext(e){let r=e.userId.length>0?` by <@${e.userId}>`:``,i=SLACK_CARD_SUBTEXT_MAX_LENGTH-20-1-r.length;return truncateCardSubtext(`:white_check_mark: *${truncateWithEllipsis(e.answerLabel,i)}*${r}`)}function truncateWithEllipsis(e,t){if(t<=0)return``;if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function promptBlocksFromActionBlock(e){if(!isObjectRecord(e)||e.type!==`card`)return[];let{actions:t,...n}=e;return hasCardContent(n)?[n]:[]}function hasCardContent(e){return e.body!==void 0||e.title!==void 0||e.hero_image!==void 0}async function handleInteractionPost(e,t,n){let i=new Response(`ok`,{status:200}),s;try{s=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),i}if(s.kind===`view_submission`)return handleViewSubmission(s,t,n);if(s.kind!==`block_actions`)return i;let c=parseBlockActionsPayload(s);if(!c)return i;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:s.raw,interaction:c,freeformAction:l,deps:n}),i;let u=slackContinuationToken(c.channelId,c.threadTs),d=c.actions.map(deriveHitlResponse).filter(e=>e!==null);if(d.length>0){let e=c.actions[0]?.user;if(!e)return i;t.waitUntil(t.send({inputResponses:d},{auth:buildSlackAuthContext({channelId:c.channelId,teamId:c.teamId,threadTs:c.threadTs,userId:e.id,userName:e.username??e.name}),continuationToken:u,state:{channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId??null,triggeringUserId:e.id}}).catch(e=>{log.error(`HITL interaction delivery failed`,{error:e})})),t.waitUntil(updateAnsweredHitlCard(c,n).catch(e=>{log.error(`HITL answered-card update failed`,{error:e})}))}let p=n.config.onInteraction;if(p){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let{thread:i,slack:a}=buildSlackBinding({botToken:n.config.credentials?.botToken,channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId}),o={cancel:(e={})=>t.cancel({continuationToken:u,turnId:e.turnId}),thread:i,slack:a};for(let n of e)t.waitUntil(Promise.resolve(p(n,o)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return i}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,threadTs:e.interaction.threadTs,messageTs:r,requestId:n},prompt:readPromptTextFromBlocks(e.interaction.messageBlocks)}),s=await resolveSlackBotToken(e.deps.config.credentials?.botToken),c=await fetch(`https://slack.com/api/views.open`,{method:`POST`,headers:{authorization:`Bearer ${s}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({trigger_id:t,view:o})});c.ok||log.error(`Slack views.open returned non-2xx`,{status:c.status})}async function handleViewSubmission(e,t,n){let r=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return r;let i;try{i=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),r}if(!i.continuationToken||!i.requestId||!i.messageTs||!i.channelId||!i.threadTs)return r;let a=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(a.length===0)return r;let u=e.user,d=e.userId,f=u?.teamId??e.teamId??null;return t.waitUntil(t.send({inputResponses:[{requestId:i.requestId,text:a}]},{auth:buildSlackAuthContext({channelId:i.channelId,teamId:f,threadTs:i.threadTs,userId:d,userName:u?.username??u?.name}),continuationToken:i.continuationToken,state:{channelId:i.channelId,threadTs:i.threadTs,teamId:f,triggeringUserId:d}}).catch(e=>{log.error(`freeform answer delivery failed`,{error:e})})),t.waitUntil(updateAnsweredFreeformCard({channelId:i.channelId,messageTs:i.messageTs,answerLabel:a,userId:d??void 0,deps:n}).catch(e=>{log.error(`freeform answered-card update failed`,{error:e})})),r}async function updateAnsweredHitlCard(e,t){let n=e.actions.find(e=>isHitlAction(e.actionId));if(!n||!n.messageTs)return;let r=n.label??n.selectedOptionValue??n.value;if(!r)return;let a=buildAnsweredHitlMessageBlocks({actionId:n.actionId,answerLabel:r,messageBlocks:e.messageBlocks,userId:n.user.id}),o=await resolveSlackBotToken(t.config.credentials?.botToken),s=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${o}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:n.messageTs,blocks:a,text:`Answered: ${r}`})});if(!s.ok)throw Error(`Slack chat.update returned HTTP ${s.status}`)}async function updateAnsweredFreeformCard(e){let t=buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId}),n=await resolveSlackBotToken(e.deps.config.credentials?.botToken),r=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${n}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:e.messageTs,blocks:t,text:`Answered: ${e.answerLabel}`})});if(!r.ok)throw Error(`Slack chat.update returned HTTP ${r.status}`)}export{handleInteractionPost,parseBlockActionsPayload};