eve 0.76.2 → 0.77.0

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 (132) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/src/channel/adapter.d.ts +13 -4
  3. package/dist/src/channel/channel-address.js +1 -1
  4. package/dist/src/channel/send-input.d.ts +6 -2
  5. package/dist/src/channel/send-input.js +1 -1
  6. package/dist/src/channel/session.js +1 -1
  7. package/dist/src/chunks/{use-eve-agent-BtKWTEpm.js → use-eve-agent-B8brxCk-.js} +46 -28
  8. package/dist/src/chunks/{use-eve-agent-jI6EsYAu.js → use-eve-agent-BSYe_Re7.js} +46 -28
  9. package/dist/src/cli/commands/vercel-non-interactive.d.ts +5 -3
  10. package/dist/src/cli/commands/vercel-non-interactive.js +1 -1
  11. package/dist/src/client/eve-agent-store.d.ts +2 -2
  12. package/dist/src/client/message-response.d.ts +2 -0
  13. package/dist/src/client/message-response.js +1 -1
  14. package/dist/src/client/session.js +1 -1
  15. package/dist/src/compiled/.vendor-stamp.json +9 -9
  16. package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +6 -1
  17. package/dist/src/compiled/@ai-sdk/anthropic/index.js +1 -1
  18. package/dist/src/compiled/@ai-sdk/google/index.d.ts +16 -3
  19. package/dist/src/compiled/@ai-sdk/google/index.js +6 -6
  20. package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +681 -348
  21. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  22. package/dist/src/compiled/@ai-sdk/openai/index.d.ts +56 -42
  23. package/dist/src/compiled/@ai-sdk/openai/index.js +5 -3
  24. package/dist/src/compiled/@ai-sdk/otel/index.js +3 -3
  25. package/dist/src/compiled/@ai-sdk/provider/index.d.ts +49 -15
  26. package/dist/src/compiled/@ai-sdk/provider/index.js +1 -1
  27. package/dist/src/compiled/@ai-sdk/provider-utils/index.d.ts +4 -1
  28. package/dist/src/compiled/_chunks/workflow/{dist-DXLdlucM.js → dist-C8k3N3BQ.js} +2 -4
  29. package/dist/src/compiled/_chunks/workflow/dist-DsNhwdzf.js +1 -0
  30. package/dist/src/compiled/_chunks/workflow/experimental-decision-DlvnsBD-.js +3 -0
  31. package/dist/src/compiler/extension-compatibility.d.ts +18 -18
  32. package/dist/src/compiler/extension-compatibility.js +1 -1
  33. package/dist/src/compiler/normalize-agent-config.js +1 -1
  34. package/dist/src/context/dynamic-model-lifecycle.js +1 -1
  35. package/dist/src/evals/judge.js +1 -1
  36. package/dist/src/eve-channel/index.js +1 -1
  37. package/dist/src/eve-channel/types.d.ts +14 -1
  38. package/dist/src/execution/cancelled-model-call-batch.js +1 -1
  39. package/dist/src/execution/catalog/connection-entry.js +1 -1
  40. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  41. package/dist/src/execution/publish-session-events.js +1 -1
  42. package/dist/src/execution/sandbox/bindings/just-bash.d.ts +1 -1
  43. package/dist/src/execution/sandbox/bindings/just-bash.js +1 -1
  44. package/dist/src/execution/sandbox/read-file.d.ts +5 -5
  45. package/dist/src/execution/sandbox/read-file.js +2 -2
  46. package/dist/src/execution/session/end-sandbox-step.js +1 -1
  47. package/dist/src/execution/session/turn-step.js +1 -1
  48. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  49. package/dist/src/execution/web-fetch/request.js +1 -1
  50. package/dist/src/harness/attachment-staging.d.ts +13 -12
  51. package/dist/src/harness/attachment-staging.js +1 -1
  52. package/dist/src/harness/compaction/step.js +1 -1
  53. package/dist/src/harness/emission.js +1 -1
  54. package/dist/src/harness/model-call/call.js +1 -1
  55. package/dist/src/harness/model-call/errors.d.ts +10 -0
  56. package/dist/src/harness/model-call/errors.js +1 -1
  57. package/dist/src/harness/session-machine/transitions.js +1 -1
  58. package/dist/src/harness/token-estimate.js +1 -1
  59. package/dist/src/internal/application/package.js +1 -1
  60. package/dist/src/internal/application/stamped-package-version.js +1 -1
  61. package/dist/src/internal/attachments/data.d.ts +29 -10
  62. package/dist/src/internal/attachments/data.js +1 -1
  63. package/dist/src/internal/attachments/errors.d.ts +2 -2
  64. package/dist/src/internal/attachments/media-metadata.d.ts +15 -0
  65. package/dist/src/internal/attachments/media-metadata.js +1 -1
  66. package/dist/src/internal/attachments/public-link.d.ts +5 -0
  67. package/dist/src/internal/attachments/public-link.js +1 -0
  68. package/dist/src/internal/attachments/sandbox-refs.d.ts +7 -6
  69. package/dist/src/internal/attachments/sandbox-refs.js +1 -1
  70. package/dist/src/internal/attachments/url-refs.js +1 -1
  71. package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.d.ts +0 -4
  72. package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.js +1 -1
  73. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +2 -2
  74. package/dist/src/protocol/message.d.ts +7 -0
  75. package/dist/src/protocol/message.js +2 -2
  76. package/dist/src/public/channels/attachment-error.d.ts +14 -0
  77. package/dist/src/public/channels/attachment-error.js +1 -0
  78. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +1 -1
  79. package/dist/src/public/channels/discord/defaults.js +1 -1
  80. package/dist/src/public/channels/github/defaults.js +1 -1
  81. package/dist/src/public/channels/index.d.ts +2 -0
  82. package/dist/src/public/channels/index.js +1 -1
  83. package/dist/src/public/channels/linear/defaults.js +1 -1
  84. package/dist/src/public/channels/slack/api.d.ts +10 -0
  85. package/dist/src/public/channels/slack/api.js +1 -1
  86. package/dist/src/public/channels/slack/approval-cards.js +1 -1
  87. package/dist/src/public/channels/slack/attachments.d.ts +29 -19
  88. package/dist/src/public/channels/slack/attachments.js +1 -1
  89. package/dist/src/public/channels/slack/defaults.js +1 -1
  90. package/dist/src/public/channels/slack/inbound.d.ts +6 -0
  91. package/dist/src/public/channels/slack/inbound.js +1 -1
  92. package/dist/src/public/channels/slack/interaction-cards.d.ts +6 -0
  93. package/dist/src/public/channels/slack/interaction-cards.js +1 -1
  94. package/dist/src/public/channels/slack/interactions.js +1 -1
  95. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  96. package/dist/src/public/channels/teams/defaults.js +1 -1
  97. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  98. package/dist/src/public/channels/telegram/defaults.js +1 -1
  99. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  100. package/dist/src/public/channels/twilio/defaults.js +1 -1
  101. package/dist/src/public/definitions/channel.js +1 -1
  102. package/dist/src/runtime/channels/registry.js +1 -1
  103. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  104. package/dist/src/setup/scaffold/create/project.js +2 -2
  105. package/dist/src/setup/scaffold/update/web-options.js +1 -1
  106. package/dist/src/shared/agent-definition.d.ts +2 -1
  107. package/dist/src/shared/channel-definition.d.ts +16 -4
  108. package/dist/src/svelte/index.d.ts +0 -1
  109. package/dist/src/svelte/index.js +1 -1
  110. package/dist/src/svelte/use-eve-agent.js +1 -1
  111. package/dist/src/tools/provided/ask-question-workflow.js +1 -1
  112. package/dist/src/tools/provided/read-file.d.ts +3 -2
  113. package/dist/src/tools/provided/read-file.js +2 -2
  114. package/dist/src/tools/provided/sleep-workflow.js +1 -1
  115. package/dist/src/vue/index.d.ts +0 -1
  116. package/dist/src/vue/index.js +1 -1
  117. package/dist/src/vue/use-eve-agent.js +1 -1
  118. package/docs/channels/chat-sdk.mdx +1 -1
  119. package/docs/channels/custom.mdx +30 -3
  120. package/docs/channels/eve.mdx +34 -0
  121. package/docs/channels/slack.mdx +5 -1
  122. package/docs/concepts/built-in-tools.md +1 -1
  123. package/docs/concepts/sessions-runs-and-streaming.md +2 -2
  124. package/docs/evals/overview.mdx +1 -1
  125. package/docs/guides/client/streaming.mdx +1 -1
  126. package/docs/memory/custom-provider.md +3 -1
  127. package/docs/sandbox/index.mdx +8 -4
  128. package/docs/tools/overview.mdx +1 -1
  129. package/package.json +11 -11
  130. package/dist/src/compiled/_chunks/workflow/experimental-decision-BrfWf6-n.js +0 -3
  131. package/dist/src/internal/attachments/refs.d.ts +0 -55
  132. package/dist/src/internal/attachments/refs.js +0 -1
@@ -1,2 +1,2 @@
1
- import{deserializeUrlFilePart,hasInternalRefScheme,isSerializedUrlFilePart}from"#internal/attachments/url-refs.js";import{createEveSessionStreamRoutePath,createEveSubagentStreamRoutePath}from"#protocol/routes.js";import{decodeSandboxRef,isSandboxRefUrl}from"#internal/attachments/sandbox-refs.js";import{createEventId}from"#protocol/event-id.js";const EVE_SESSION_ID_HEADER=`x-eve-session-id`,EVE_STREAM_FORMAT_HEADER=`x-eve-stream-format`,EVE_STREAM_TAIL_INDEX_HEADER=`x-eve-stream-tail-index`,EVE_STREAM_VERSION_HEADER=`x-eve-stream-version`,EVE_MESSAGE_STREAM_CONTENT_TYPE=`application/x-ndjson; charset=utf-8`,EVE_MESSAGE_STREAM_FORMAT=`ndjson`,EVE_MESSAGE_STREAM_VERSION=`26`,EVE_STREAM_CONTROL_VERSION=`1`,EVE_STREAM_CONTROL_VERSION_QUERY=`streamControlVersion`,EVE_STREAM_LEASE_ENDED_CONTROL={$eve:`stream.lease-ended`,version:1},textEncoder=new TextEncoder;function isCurrentTurnBoundaryEvent(e){return e.type===`session.completed`||e.type===`session.failed`||e.type===`session.waiting`}function isTurnFailureEvent(e){return e.type===`session.failed`||e.type===`step.failed`||e.type===`turn.failed`}function createSessionStartedEvent(e){let t={};return e?.invocation!==void 0&&(t.invocation=e.invocation),e?.predecessor!==void 0&&(t.predecessor=e.predecessor),e?.runtime!==void 0&&(t.runtime=e.runtime),e?.trace!==void 0&&(t.trace=e.trace),{data:t,type:`session.started`}}function createTurnStartedEvent(e){let t={sequence:e.sequence,turnId:e.turnId};return e.trace!==void 0&&(t.trace=e.trace),{data:t,type:`turn.started`}}function createMessageReceivedEvent(e){return{data:{message:summarizeUserContent(e.message),parts:projectUserContentParts(e.message),sequence:e.sequence,turnId:e.turnId},type:`message.received`}}function summarizeUserContent(e){if(typeof e==`string`)return e;let t=[];for(let n of e)if(n.type===`text`)t.push(n.text);else if(n.type===`file`){let e=n.filename??n.mediaType;t.push(`[file: ${e} (${n.mediaType})]`)}else n.type===`image`&&t.push(`[image: ${n.mediaType??`image`}]`);return t.join(`
2
- `)}function projectUserContentParts(e){if(typeof e==`string`)return[{text:e,type:`text`}];let t=[];for(let n of e)n.type===`text`?t.push({text:n.text,type:`text`}):n.type===`file`?t.push(projectFileLikePart(n.data,n.mediaType,n.filename)):n.type===`image`&&t.push(projectFileLikePart(n.image,n.mediaType??`application/octet-stream`,void 0));return t}function projectFileLikePart(e,t,n){if(isSandboxRefUrl(e)){let t=decodeSandboxRef(e);return createProjectedFilePart({filename:basenameOf(n??t.path),mediaType:t.mediaType,size:t.size})}let r=projectTaggedFileData(e,t,n);if(r!==void 0)return r;let i=byteLengthOf(e);return createProjectedFilePart(i===void 0?{filename:n,mediaType:t,...clientUrlFragment(e)}:{filename:n,mediaType:t,size:i})}function projectTaggedFileData(e,t,n){if(isTaggedFileData(e))switch(e.type){case`data`:{let r=byteLengthOf(e.data);return createProjectedFilePart(r===void 0?{filename:n,mediaType:t}:{filename:n,mediaType:t,size:r})}case`reference`:case`text`:return createProjectedFilePart({filename:n,mediaType:t});case`url`:return createProjectedFilePart({filename:n,mediaType:t,...clientUrlFragment(e.url)})}}function createProjectedFilePart(e){let t={mediaType:e.mediaType,type:`file`};return e.filename!==void 0&&(t.filename=e.filename),e.size!==void 0&&(t.size=e.size),e.url!==void 0&&(t.url=e.url),t}function isTaggedFileData(e){if(typeof e!=`object`||!e)return!1;let t=e.type;return t===`data`||t===`reference`||t===`text`||t===`url`}function byteLengthOf(e){if(e instanceof Uint8Array||e instanceof ArrayBuffer)return e.byteLength}function clientUrlFragment(e){if(isSerializedUrlFilePart(e))try{let t=deserializeUrlFilePart(e);return isClientResolvableUrl(t)?{url:t.href}:{}}catch{return{}}if(e instanceof URL)return isClientResolvableUrl(e)?{url:e.href}:{};if(typeof e!=`string`||hasInternalRefScheme(e))return{};if(e.startsWith(`data:`))return{url:e};try{let t=new URL(e);return isClientResolvableUrl(t)?{url:t.href}:{}}catch{return{}}}function isClientResolvableUrl(e){return e.protocol===`http:`||e.protocol===`https:`||e.protocol===`data:`}function basenameOf(e){let t=e.replaceAll(`\\`,`/`),n=t.slice(t.lastIndexOf(`/`)+1);return n.length>0?n:e}function createActionsRequestedEvent(e){return{data:{actions:e.actions,...optionalPresentation(e.presentation),sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`actions.requested`}}function optionalPresentation(e){return e===void 0?{}:{presentation:e}}function createActionInputAppendedEvent(e){return{data:{callId:e.callId,inputTextDelta:e.inputTextDelta,sequence:e.sequence,stepIndex:e.stepIndex,toolName:e.toolName,turnId:e.turnId},type:`action.input.appended`}}function createAuthorizationRequiredEvent(e){let t={description:e.description,name:e.name,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.attemptId!==void 0&&(t.attemptId=e.attemptId),e.authorization!==void 0&&(t.authorization=e.authorization),e.candidateId!==void 0&&(t.candidateId=e.candidateId),e.principalId!==void 0&&(t.principalId=e.principalId),e.webhookUrl!==void 0&&(t.webhookUrl=e.webhookUrl),e.taskId!==void 0&&(t.taskId=e.taskId),{data:t,type:`authorization.required`}}function createAuthorizationCompletedEvent(e){let t={name:e.name,outcome:e.outcome,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.attemptId!==void 0&&(t.attemptId=e.attemptId),e.authorization!==void 0&&(t.authorization=e.authorization),e.candidateId!==void 0&&(t.candidateId=e.candidateId),e.principalId!==void 0&&(t.principalId=e.principalId),e.reason!==void 0&&(t.reason=e.reason),e.taskId!==void 0&&(t.taskId=e.taskId),{data:t,type:`authorization.completed`}}function createApprovalCandidateEvent(e){return{data:e,type:`approval.candidate`}}function createApprovalSettledEvent(e){return{data:e,type:`approval.settled`}}function createInputRequestedEvent(e){let t={requests:e.requests,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.callId!==void 0&&(t.callId=e.callId),e.taskId!==void 0&&(t.taskId=e.taskId),{data:t,type:`input.requested`}}function createInputResolvedEvent(e){return{data:{resolutions:e.resolutions,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`input.resolved`}}function createActionResultEvent(e){let t=e.rejected===!0?{error:buildActionResultError(e.result),status:`rejected`}:normalizeActionResultOutcome(e.result);return{data:{error:t.error,...optionalPresentation(e.presentation),result:e.result,sequence:e.sequence,status:t.status,stepIndex:e.stepIndex,turnId:e.turnId},type:`action.result`}}function createActionPartialEvent(e){return{data:{...optionalPresentation(e.presentation),result:e.result,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`action.partial`}}function createTaskStartedEvent(e){return{data:{callId:e.callId,kind:e.kind,name:e.name,taskId:e.taskId,turnId:e.turnId},type:`task.started`}}function createTaskSettledEvent(e){let t={callId:e.callId,...e.kind!==void 0&&{kind:e.kind},...e.name!==void 0&&{name:e.name},status:e.status,taskId:e.taskId,turnId:e.turnId};return e.output!==void 0&&(t.output=e.output),e.error!==void 0&&(t.error=e.error),e.cancel!==void 0&&(t.cancel=e.cancel),{data:t,type:`task.settled`}}function createAgentStartedEvent(e){let t={callId:e.callId,turnId:e.turnId,name:e.name,sessionId:e.sessionId,streamPath:createEveSessionStreamRoutePath(e.sessionId)};return e.taskId!==void 0&&(t.taskId=e.taskId),e.remote!==void 0&&(t.remote=e.remote,t.streamPath=createEveSubagentStreamRoutePath({callId:e.callId,childSessionId:e.sessionId,parentSessionId:e.parentSessionId})),{data:t,type:`agent.started`}}function createMessageAppendedEvent(e){return{data:{messageDelta:e.messageDelta,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`message.appended`}}function createReasoningAppendedEvent(e){return{data:{reasoningDelta:e.reasoningDelta,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`reasoning.appended`}}function createMessageCompletedEvent(e){return{data:{finishReason:e.finishReason??`stop`,message:e.message,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`message.completed`}}function createReasoningCompletedEvent(e){return{data:{reasoning:e.reasoning,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`reasoning.completed`}}function createResultCompletedEvent(e){return{data:{result:e.result,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`result.completed`}}function createStepStartedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`step.started`}}function createStepCompletedEvent(e){let t={finishReason:e.finishReason,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.usage!==void 0&&(t.usage=e.usage),e.providerMetadata!==void 0&&(t.providerMetadata=e.providerMetadata),{data:t,type:`step.completed`}}function createStepFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`step.failed`}}function createTurnCompletedEvent(e){return{data:{sequence:e.sequence,turnId:e.turnId},type:`turn.completed`}}function createTurnWaitingEvent(e){return{data:{on:e.on,sequence:e.sequence,turnId:e.turnId,...e.usage!==void 0&&{usage:e.usage}},type:`turn.waiting`}}function createTurnFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sequence:e.sequence,turnId:e.turnId},type:`turn.failed`}}function createTurnCancelledEvent(e){return{data:{sequence:e.sequence,turnId:e.turnId},type:`turn.cancelled`}}function createContextClearedEvent(e){return{data:{sequence:e.sequence,sessionId:e.sessionId,turnId:e.turnId},type:`context.cleared`}}function createCompactionRequestedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,sessionId:e.sessionId,stepIndex:e.stepIndex,turnId:e.turnId,usageInputTokens:e.usageInputTokens??null},type:`compaction.requested`}}function createCompactionCompletedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,sessionId:e.sessionId,stepIndex:e.stepIndex,turnId:e.turnId},type:`compaction.completed`}}function createSessionWaitingEvent(e){return{data:{continuationToken:``,...e!==void 0&&{usage:e},wait:`next-user-message`},type:`session.waiting`}}function createSessionFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sessionId:e.sessionId,...e.usage!==void 0&&{usage:e.usage}},type:`session.failed`}}function createSessionCompletedEvent(e){return e===void 0?{type:`session.completed`}:{data:{usage:e},type:`session.completed`}}function stampMessageStreamEvent(e,t){let n={at:new Date().toISOString(),id:createEventId()};return t!==void 0&&t.length>0&&(n.deliveryIds=t),{...e,meta:n}}function encodeMessageStreamEvent(e){return textEncoder.encode(`${JSON.stringify(e)}\n`)}function normalizeActionResultOutcome(e){let t=readActionResultOutputError(e.output);return e.isError===!0?{error:buildActionResultError(e),status:`failed`}:t===void 0?{status:`completed`}:{error:t,status:`failed`}}function buildActionResultError(e){let t=readActionResultOutputError(e.output);return t===void 0?{code:`ACTION_RESULT_FAILED`,message:formatActionResultOutput(e.output)}:t}function readActionResultOutputError(e){let t=parseActionResultOutputRecord(e);if(t===void 0)return;let n=typeof t.code==`string`&&t.code.length>0?t.code:void 0,r=typeof t.message==`string`&&t.message.length>0?t.message:void 0;if(n!==void 0&&r!==void 0)return{code:n,message:r}}function parseActionResultOutputRecord(e){if(typeof e==`object`&&e)return e;if(typeof e!=`string`)return;let t=e.trim();if(t.length!==0)try{let e=JSON.parse(t);if(typeof e==`object`&&e)return e}catch{return}}function formatActionResultOutput(e){if(typeof e==`string`)return e;let t=JSON.stringify(e);return typeof t==`string`&&t.length>0?t:`Action failed.`}export{EVE_MESSAGE_STREAM_CONTENT_TYPE,EVE_MESSAGE_STREAM_FORMAT,EVE_MESSAGE_STREAM_VERSION,EVE_SESSION_ID_HEADER,EVE_STREAM_CONTROL_VERSION,EVE_STREAM_CONTROL_VERSION_QUERY,EVE_STREAM_FORMAT_HEADER,EVE_STREAM_LEASE_ENDED_CONTROL,EVE_STREAM_TAIL_INDEX_HEADER,EVE_STREAM_VERSION_HEADER,createActionInputAppendedEvent,createActionPartialEvent,createActionResultEvent,createActionsRequestedEvent,createAgentStartedEvent,createApprovalCandidateEvent,createApprovalSettledEvent,createAuthorizationCompletedEvent,createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createContextClearedEvent,createInputRequestedEvent,createInputResolvedEvent,createMessageAppendedEvent,createMessageCompletedEvent,createMessageReceivedEvent,createReasoningAppendedEvent,createReasoningCompletedEvent,createResultCompletedEvent,createSessionCompletedEvent,createSessionFailedEvent,createSessionStartedEvent,createSessionWaitingEvent,createStepCompletedEvent,createStepFailedEvent,createStepStartedEvent,createTaskSettledEvent,createTaskStartedEvent,createTurnCancelledEvent,createTurnCompletedEvent,createTurnFailedEvent,createTurnStartedEvent,createTurnWaitingEvent,encodeMessageStreamEvent,isCurrentTurnBoundaryEvent,isTurnFailureEvent,stampMessageStreamEvent};
1
+ import{deserializeUrlFilePart,hasInternalRefScheme,isSerializedUrlFilePart}from"#internal/attachments/url-refs.js";import{createEveSessionStreamRoutePath,createEveSubagentStreamRoutePath}from"#protocol/routes.js";import{decodeSandboxRef,isSandboxRefUrl}from"#internal/attachments/sandbox-refs.js";import{getKnownByteLength}from"#internal/attachments/data.js";import{createEventId}from"#protocol/event-id.js";const EVE_SESSION_ID_HEADER=`x-eve-session-id`,EVE_STREAM_FORMAT_HEADER=`x-eve-stream-format`,EVE_STREAM_TAIL_INDEX_HEADER=`x-eve-stream-tail-index`,EVE_STREAM_VERSION_HEADER=`x-eve-stream-version`,EVE_MESSAGE_STREAM_CONTENT_TYPE=`application/x-ndjson; charset=utf-8`,EVE_MESSAGE_STREAM_FORMAT=`ndjson`,EVE_MESSAGE_STREAM_VERSION=`26`,EVE_STREAM_CONTROL_VERSION=`1`,EVE_STREAM_CONTROL_VERSION_QUERY=`streamControlVersion`,EVE_STREAM_LEASE_ENDED_CONTROL={$eve:`stream.lease-ended`,version:1},textEncoder=new TextEncoder;function isCurrentTurnBoundaryEvent(e){return e.type===`session.completed`||e.type===`session.failed`||e.type===`session.waiting`}function isTurnFailureEvent(e){return e.type===`session.failed`||e.type===`step.failed`||e.type===`turn.failed`}function createSessionStartedEvent(e){let t={};return e?.invocation!==void 0&&(t.invocation=e.invocation),e?.predecessor!==void 0&&(t.predecessor=e.predecessor),e?.runtime!==void 0&&(t.runtime=e.runtime),e?.trace!==void 0&&(t.trace=e.trace),{data:t,type:`session.started`}}function createTurnStartedEvent(e){let t={sequence:e.sequence,turnId:e.turnId};return e.trace!==void 0&&(t.trace=e.trace),{data:t,type:`turn.started`}}function createMessageReceivedEvent(e){return{data:{message:summarizeUserContent(e.message),parts:projectUserContentParts(e.message),sequence:e.sequence,turnId:e.turnId},type:`message.received`}}function summarizeUserContent(e){if(typeof e==`string`)return e;let t=[];for(let n of e)if(n.type===`text`)t.push(n.text);else if(n.type===`file`){let e=n.filename??n.mediaType;t.push(`[file: ${e} (${n.mediaType})]`)}else n.type===`image`&&t.push(`[image: ${n.mediaType??`image`}]`);return t.join(`
2
+ `)}function projectUserContentParts(e){if(typeof e==`string`)return[{text:e,type:`text`}];let t=[];for(let n of e)n.type===`text`?t.push({text:n.text,type:`text`}):n.type===`file`?t.push(projectFileLikePart(n.data,n.mediaType,n.filename)):n.type===`image`&&t.push(projectFileLikePart(n.image,n.mediaType??`application/octet-stream`,void 0));return t}function projectFileLikePart(e,t,n){if(isSandboxRefUrl(e)){let t=decodeSandboxRef(e);return createProjectedFilePart({filename:basenameOf(n??t.path),mediaType:t.mediaType,size:t.size})}let r=projectTaggedFileData(e,t,n);if(r!==void 0)return r;let i=clientUrlFragment(e);if(i.url!==void 0)return createProjectedFilePart({filename:n,mediaType:t,...i});let a=getKnownByteLength(e);return createProjectedFilePart(a===null?{filename:n,mediaType:t}:{filename:n,mediaType:t,size:a})}function projectTaggedFileData(e,t,n){if(isTaggedFileData(e))switch(e.type){case`data`:{let r=byteLengthOf(e.data);return createProjectedFilePart(r===void 0?{filename:n,mediaType:t}:{filename:n,mediaType:t,size:r})}case`reference`:case`text`:return createProjectedFilePart({filename:n,mediaType:t});case`url`:return createProjectedFilePart({filename:n,mediaType:t,...clientUrlFragment(e.url)})}}function createProjectedFilePart(e){let t={mediaType:e.mediaType,type:`file`};return e.filename!==void 0&&(t.filename=e.filename),e.size!==void 0&&(t.size=e.size),e.url!==void 0&&(t.url=e.url),t}function isTaggedFileData(e){if(typeof e!=`object`||!e)return!1;let t=e.type;return t===`data`||t===`reference`||t===`text`||t===`url`}function byteLengthOf(e){if(e instanceof Uint8Array||e instanceof ArrayBuffer)return e.byteLength}function clientUrlFragment(e){if(isSerializedUrlFilePart(e))try{let t=deserializeUrlFilePart(e);return isClientResolvableUrl(t)?{url:t.href}:{}}catch{return{}}if(e instanceof URL)return isClientResolvableUrl(e)?{url:e.href}:{};if(typeof e!=`string`||hasInternalRefScheme(e))return{};try{let t=new URL(e);return isClientResolvableUrl(t)?{url:t.href}:{}}catch{return{}}}function isClientResolvableUrl(e){return e.protocol===`http:`||e.protocol===`https:`}function basenameOf(e){let t=e.replaceAll(`\\`,`/`),n=t.slice(t.lastIndexOf(`/`)+1);return n.length>0?n:e}function createActionsRequestedEvent(e){return{data:{actions:e.actions,...optionalPresentation(e.presentation),sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`actions.requested`}}function optionalPresentation(e){return e===void 0?{}:{presentation:e}}function createActionInputAppendedEvent(e){return{data:{callId:e.callId,inputTextDelta:e.inputTextDelta,sequence:e.sequence,stepIndex:e.stepIndex,toolName:e.toolName,turnId:e.turnId},type:`action.input.appended`}}function createAuthorizationRequiredEvent(e){let t={description:e.description,name:e.name,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.attemptId!==void 0&&(t.attemptId=e.attemptId),e.authorization!==void 0&&(t.authorization=e.authorization),e.candidateId!==void 0&&(t.candidateId=e.candidateId),e.principalId!==void 0&&(t.principalId=e.principalId),e.webhookUrl!==void 0&&(t.webhookUrl=e.webhookUrl),e.taskId!==void 0&&(t.taskId=e.taskId),{data:t,type:`authorization.required`}}function createAuthorizationCompletedEvent(e){let t={name:e.name,outcome:e.outcome,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.attemptId!==void 0&&(t.attemptId=e.attemptId),e.authorization!==void 0&&(t.authorization=e.authorization),e.candidateId!==void 0&&(t.candidateId=e.candidateId),e.principalId!==void 0&&(t.principalId=e.principalId),e.reason!==void 0&&(t.reason=e.reason),e.taskId!==void 0&&(t.taskId=e.taskId),{data:t,type:`authorization.completed`}}function createApprovalCandidateEvent(e){return{data:e,type:`approval.candidate`}}function createApprovalSettledEvent(e){return{data:e,type:`approval.settled`}}function createInputRequestedEvent(e){let t={requests:e.requests,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.callId!==void 0&&(t.callId=e.callId),e.taskId!==void 0&&(t.taskId=e.taskId),{data:t,type:`input.requested`}}function createInputResolvedEvent(e){return{data:{resolutions:e.resolutions,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`input.resolved`}}function createActionResultEvent(e){let t=e.rejected===!0?{error:buildActionResultError(e.result),status:`rejected`}:normalizeActionResultOutcome(e.result);return{data:{error:t.error,...optionalPresentation(e.presentation),result:e.result,sequence:e.sequence,status:t.status,stepIndex:e.stepIndex,turnId:e.turnId},type:`action.result`}}function createActionPartialEvent(e){return{data:{...optionalPresentation(e.presentation),result:e.result,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`action.partial`}}function createTaskStartedEvent(e){return{data:{callId:e.callId,kind:e.kind,name:e.name,taskId:e.taskId,turnId:e.turnId},type:`task.started`}}function createTaskSettledEvent(e){let t={callId:e.callId,...e.kind!==void 0&&{kind:e.kind},...e.name!==void 0&&{name:e.name},status:e.status,taskId:e.taskId,turnId:e.turnId};return e.output!==void 0&&(t.output=e.output),e.error!==void 0&&(t.error=e.error),e.cancel!==void 0&&(t.cancel=e.cancel),{data:t,type:`task.settled`}}function createAgentStartedEvent(e){let t={callId:e.callId,turnId:e.turnId,name:e.name,sessionId:e.sessionId,streamPath:createEveSessionStreamRoutePath(e.sessionId)};return e.taskId!==void 0&&(t.taskId=e.taskId),e.remote!==void 0&&(t.remote=e.remote,t.streamPath=createEveSubagentStreamRoutePath({callId:e.callId,childSessionId:e.sessionId,parentSessionId:e.parentSessionId})),{data:t,type:`agent.started`}}function createMessageAppendedEvent(e){return{data:{messageDelta:e.messageDelta,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`message.appended`}}function createReasoningAppendedEvent(e){return{data:{reasoningDelta:e.reasoningDelta,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`reasoning.appended`}}function createMessageCompletedEvent(e){return{data:{finishReason:e.finishReason??`stop`,message:e.message,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`message.completed`}}function createReasoningCompletedEvent(e){return{data:{reasoning:e.reasoning,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`reasoning.completed`}}function createResultCompletedEvent(e){return{data:{result:e.result,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`result.completed`}}function createStepStartedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`step.started`}}function createStepCompletedEvent(e){let t={finishReason:e.finishReason,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.usage!==void 0&&(t.usage=e.usage),e.providerMetadata!==void 0&&(t.providerMetadata=e.providerMetadata),{data:t,type:`step.completed`}}function createStepFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`step.failed`}}function createTurnCompletedEvent(e){return{data:{sequence:e.sequence,turnId:e.turnId},type:`turn.completed`}}function createTurnWaitingEvent(e){return{data:{on:e.on,sequence:e.sequence,turnId:e.turnId,...e.usage!==void 0&&{usage:e.usage}},type:`turn.waiting`}}function createTurnFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sequence:e.sequence,terminal:e.terminal,turnId:e.turnId},type:`turn.failed`}}function createTurnCancelledEvent(e){return{data:{sequence:e.sequence,turnId:e.turnId},type:`turn.cancelled`}}function createContextClearedEvent(e){return{data:{sequence:e.sequence,sessionId:e.sessionId,turnId:e.turnId},type:`context.cleared`}}function createCompactionRequestedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,sessionId:e.sessionId,stepIndex:e.stepIndex,turnId:e.turnId,usageInputTokens:e.usageInputTokens??null},type:`compaction.requested`}}function createCompactionCompletedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,sessionId:e.sessionId,stepIndex:e.stepIndex,turnId:e.turnId},type:`compaction.completed`}}function createSessionWaitingEvent(e){return{data:{continuationToken:``,...e!==void 0&&{usage:e},wait:`next-user-message`},type:`session.waiting`}}function createSessionFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sessionId:e.sessionId,...e.usage!==void 0&&{usage:e.usage}},type:`session.failed`}}function createSessionCompletedEvent(e){return e===void 0?{type:`session.completed`}:{data:{usage:e},type:`session.completed`}}function stampMessageStreamEvent(e,t){let n={at:new Date().toISOString(),id:createEventId()};return t!==void 0&&t.length>0&&(n.deliveryIds=t),{...e,meta:n}}function encodeMessageStreamEvent(e){return textEncoder.encode(`${JSON.stringify(e)}\n`)}function normalizeActionResultOutcome(e){let t=readActionResultOutputError(e.output);return e.isError===!0?{error:buildActionResultError(e),status:`failed`}:t===void 0?{status:`completed`}:{error:t,status:`failed`}}function buildActionResultError(e){let t=readActionResultOutputError(e.output);return t===void 0?{code:`ACTION_RESULT_FAILED`,message:formatActionResultOutput(e.output)}:t}function readActionResultOutputError(e){let t=parseActionResultOutputRecord(e);if(t===void 0)return;let n=typeof t.code==`string`&&t.code.length>0?t.code:void 0,r=typeof t.message==`string`&&t.message.length>0?t.message:void 0;if(n!==void 0&&r!==void 0)return{code:n,message:r}}function parseActionResultOutputRecord(e){if(typeof e==`object`&&e)return e;if(typeof e!=`string`)return;let t=e.trim();if(t.length!==0)try{let e=JSON.parse(t);if(typeof e==`object`&&e)return e}catch{return}}function formatActionResultOutput(e){if(typeof e==`string`)return e;let t=JSON.stringify(e);return typeof t==`string`&&t.length>0?t:`Action failed.`}export{EVE_MESSAGE_STREAM_CONTENT_TYPE,EVE_MESSAGE_STREAM_FORMAT,EVE_MESSAGE_STREAM_VERSION,EVE_SESSION_ID_HEADER,EVE_STREAM_CONTROL_VERSION,EVE_STREAM_CONTROL_VERSION_QUERY,EVE_STREAM_FORMAT_HEADER,EVE_STREAM_LEASE_ENDED_CONTROL,EVE_STREAM_TAIL_INDEX_HEADER,EVE_STREAM_VERSION_HEADER,createActionInputAppendedEvent,createActionPartialEvent,createActionResultEvent,createActionsRequestedEvent,createAgentStartedEvent,createApprovalCandidateEvent,createApprovalSettledEvent,createAuthorizationCompletedEvent,createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createContextClearedEvent,createInputRequestedEvent,createInputResolvedEvent,createMessageAppendedEvent,createMessageCompletedEvent,createMessageReceivedEvent,createReasoningAppendedEvent,createReasoningCompletedEvent,createResultCompletedEvent,createSessionCompletedEvent,createSessionFailedEvent,createSessionStartedEvent,createSessionWaitingEvent,createStepCompletedEvent,createStepFailedEvent,createStepStartedEvent,createTaskSettledEvent,createTaskStartedEvent,createTurnCancelledEvent,createTurnCompletedEvent,createTurnFailedEvent,createTurnStartedEvent,createTurnWaitingEvent,encodeMessageStreamEvent,isCurrentTurnBoundaryEvent,isTurnFailureEvent,stampMessageStreamEvent};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Builds the error a channel's `fetchFile` throws to tell the model why one
3
+ * attachment didn't arrive. eve replaces the attachment with
4
+ * `Attachment <name> could not be retrieved: <message>`, continues the turn,
5
+ * and keeps `cause` in operator logs.
6
+ *
7
+ * The model reads `message` as written. Name what the person can act on, such
8
+ * as a size limit or a missing permission, and never include URLs, tokens, or
9
+ * upstream response text. Any other error from `fetchFile` reaches the model as
10
+ * a generic note.
11
+ */
12
+ export declare function attachmentError(message: string, options?: {
13
+ readonly cause?: unknown;
14
+ }): Error;
@@ -0,0 +1 @@
1
+ import{EveAttachmentError}from"#internal/attachments/errors.js";function attachmentError(e,t){return new EveAttachmentError({cause:t?.cause,kind:`resolver-threw`,message:e})}export{attachmentError};
@@ -1,3 +1,3 @@
1
- import{actionLabel,visibleActions}from"#shared/action-label.js";import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{parseInputResponses}from"#shared/input.js";import{defaultDeliverResult}from"#channel/adapter.js";import{ContextContainer,contextStorage}from"#context/container.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import{ContextKey}from"#context/key.js";import{DEFAULT_UPLOAD_POLICY}from"#public/channels/upload-policy.js";import{EveAttachmentError}from"#internal/attachments/errors.js";import{isNotImplemented}from"#public/channels/chat-sdk/notImplemented.js";import{promptQueueEvents}from"#channel/prompt-queue.js";import{assertWithinLimit}from"#internal/attachments/limited-read.js";import{Chat,Message,ThreadImpl}from"#compiled/chat/index.js";import{defaultAuthorizationEvents}from"#public/channels/chat-sdk/authorization.js";import{parseChatSdkFileRef}from"#public/channels/chat-sdk/attachment-refs.js";import{decodeInputAction,renderInputRequests}from"#public/channels/chat-sdk/input-actions.js";import{chatSdkInstrumentation}from"#public/channels/chat-sdk/audience.js";const log=createLogger(`chat-sdk.channel`),streamTextByState=new WeakMap,ActiveWebhookKey=new ContextKey(`chat-sdk.active-webhook`);function chatSdkChannel(e){let t=new Chat(e),n=e.inputActionPrefix??`eve_input:`,r=e.streaming??!0,i=e.streamingEditIntervalMs??1e3,a={...defaultEvents(n),...e.events};return t.onAction(async r=>{let i=decodeInputAction(r.actionId,n,r.value);if(i){if(!r.thread)throw Error(`chatSdkChannel input actions require a thread on the Chat SDK action event.`);await bridgeRespond(t,[i],{auth:e.resolveInputAuth?await e.resolveInputAuth(r):null,thread:r.thread.toJSON()})}}),{bot:t,channel:defineChannel({kindHint:`chat-sdk`,turnPolicy:e.turnPolicy,fetchFile:e=>fetchAttachment(t,e),state:initialState(),...chatSdkInstrumentation,context(e){return{bot:t,state:e,streaming:r,streamingEditIntervalMs:i,thread:threadFromState(t,e)}},routes:adapterNames(e.adapters).flatMap(n=>{let r=routeForAdapter(n,e),handler=async(r,i)=>{let a=t.webhooks[n],o=new ContextContainer;return o.setVirtualContext(ActiveWebhookKey,{from:i.from}),contextStorage.run(o,()=>a(r,{...e.webhook,waitUntil(e){i.waitUntil(e)}}))};return[GET(r,handler),POST(r,handler)]}),async receive(e,{from:n}){let r=serializeReceiveTarget(t,e.target);return n(r.id).send(e.message,{auth:e.auth,state:{thread:r}})},deliver(e,t){return recordPrincipalUser(t.state,t.session.auth.current,e),defaultDeliverResult(e)},events:a}),send(e,n){return bridgeSend(t,e,n)},respond(e,n){return bridgeRespond(t,e,n)}}}function defaultEvents(e){async function showPrompt(t,n){if(!t.thread)return!1;let r=await t.thread.post(renderInputRequests([n],e));r.id&&t.state.editSupported!==!1&&(t.state.pendingInputCards={...t.state.pendingInputCards,[r.id]:{requests:[n],resolved:{}}})}async function clearAnsweredCards(t,n){let r=n.thread;if(r)for(let[i,a]of Object.entries(n.state.pendingInputCards??{})){let o=t.resolutions.filter(e=>a.requests.some(t=>t.requestId===e.requestId));if(o.length===0)continue;let s={...a.resolved,...Object.fromEntries(o.map(e=>[e.requestId,e]))},{[i]:c,...l}=n.state.pendingInputCards??{};n.state.pendingInputCards=a.requests.every(e=>s[e.requestId]!==void 0)?l:{...l,[i]:{requests:a.requests,resolved:s}};try{await r.adapter.editMessage(r.id,i,renderInputRequests(a.requests,e,s))}catch(e){if(!isNotImplemented(e)){log.warn(`answered input card edit failed`,{error:e,messageId:i});continue}n.state.editSupported=!1,n.state.pendingInputCards={};return}}}let t=promptQueueEvents(showPrompt);return{...defaultAuthorizationEvents(),async"turn.started"(e,t,n){t.state.pendingToolCallMessage=null,clearStream(t.state),await safeStartTyping(t.thread,`Working...`)},async"actions.requested"(e,t,n){let r=t.state.pendingToolCallMessage;if(t.state.pendingToolCallMessage=null,r){await safeStartTyping(t.thread,truncate(r));return}let i=visibleActions(e.actions).map(t=>actionLabel(t,e.presentation));i.length!==0&&await safeStartTyping(t.thread,truncate(`${i.join(`, `)}...`))},async"message.appended"(e,t,n){if(!t.thread||!canStream(t))return;let r=((t.state.streamStepIndex===e.stepIndex?streamTextByState.get(t.state):void 0)??``)+e.messageDelta;if(!r)return;streamTextByState.set(t.state,r);let i=t.state.anchorMessageId;if(!i||t.state.streamStepIndex!==e.stepIndex){let n=await t.thread.post({markdown:r});t.state.anchorMessageId=n.id,t.state.streamStepIndex=e.stepIndex,t.state.lastEditAtMs=Date.now();return}let a=t.state.lastEditAtMs??0;if(!(Date.now()-a<t.streamingEditIntervalMs))try{await editMessage(t.thread,i,r),t.state.lastEditAtMs=Date.now()}catch(e){if(!isNotImplemented(e))throw e;t.state.editSupported=!1,clearStream(t.state)}},...t,async"input.resolved"(e,n,r){await clearAnsweredCards(e,n),await t[`input.resolved`](e,n)},async"message.completed"(e,t,n){if(e.finishReason===`tool-calls`){t.state.pendingToolCallMessage=e.message?firstNonEmptyLine(e.message)??null:null,e.message?await finalizeStreamedMessage(t,e.message,!1):clearStream(t.state);return}if(t.state.pendingToolCallMessage=null,!e.message){clearStream(t.state);return}await finalizeStreamedMessage(t,e.message,!0)},async"turn.failed"(e,t,n){await postFailure(t.thread,`I hit an error while handling your request`,e)},async"session.failed"(e,t){await postFailure(t.thread,`This session could not recover from an error`,e)}}}async function safeStartTyping(e,t){if(e)try{await e.startTyping(t)}catch(e){if(!isNotImplemented(e))throw e}}async function editMessage(e,t,n){await e.adapter.editMessage(e.id,t,{markdown:n})}async function finalizeStreamedMessage(e,t,n){let r=e.thread;if(!r){clearStream(e.state);return}let i=e.state.anchorMessageId;if(canStream(e)&&i)try{await editMessage(r,i,t)}catch(i){if(!isNotImplemented(i))throw i;e.state.editSupported=!1,n&&await r.post({markdown:t})}else n&&await r.post({markdown:t});clearStream(e.state)}function canStream(e){return e.streaming&&e.state.editSupported!==!1}function clearStream(e){e.anchorMessageId=null,e.lastEditAtMs=null,e.streamStepIndex=null,streamTextByState.delete(e)}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function truncate(e,t=80){return e.length<=t?e:`${e.slice(0,t-1)}\u2026`}async function postFailure(e,t,n){if(!e)return;let r=formatErrorHint(n),i=extractErrorId(n.details);await e.post([`${t}${r}.`,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[`Error id: ${i}`]:[]].join(`
1
+ import{actionLabel,visibleActions}from"#shared/action-label.js";import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{parseInputResponses}from"#shared/input.js";import{defaultDeliverResult}from"#channel/adapter.js";import{ContextContainer,contextStorage}from"#context/container.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import{ContextKey}from"#context/key.js";import{DEFAULT_UPLOAD_POLICY}from"#public/channels/upload-policy.js";import{EveAttachmentError}from"#internal/attachments/errors.js";import{assertWithinLimit}from"#internal/attachments/limited-read.js";import{isNotImplemented}from"#public/channels/chat-sdk/notImplemented.js";import{promptQueueEvents}from"#channel/prompt-queue.js";import{Chat,Message,ThreadImpl}from"#compiled/chat/index.js";import{defaultAuthorizationEvents}from"#public/channels/chat-sdk/authorization.js";import{parseChatSdkFileRef}from"#public/channels/chat-sdk/attachment-refs.js";import{decodeInputAction,renderInputRequests}from"#public/channels/chat-sdk/input-actions.js";import{chatSdkInstrumentation}from"#public/channels/chat-sdk/audience.js";const log=createLogger(`chat-sdk.channel`),streamTextByState=new WeakMap,ActiveWebhookKey=new ContextKey(`chat-sdk.active-webhook`);function chatSdkChannel(e){let t=new Chat(e),n=e.inputActionPrefix??`eve_input:`,r=e.streaming??!0,i=e.streamingEditIntervalMs??1e3,a={...defaultEvents(n),...e.events};return t.onAction(async r=>{let i=decodeInputAction(r.actionId,n,r.value);if(i){if(!r.thread)throw Error(`chatSdkChannel input actions require a thread on the Chat SDK action event.`);await bridgeRespond(t,[i],{auth:e.resolveInputAuth?await e.resolveInputAuth(r):null,thread:r.thread.toJSON()})}}),{bot:t,channel:defineChannel({kindHint:`chat-sdk`,turnPolicy:e.turnPolicy,fetchFile:e=>fetchAttachment(t,e),state:initialState(),...chatSdkInstrumentation,context(e){return{bot:t,state:e,streaming:r,streamingEditIntervalMs:i,thread:threadFromState(t,e)}},routes:adapterNames(e.adapters).flatMap(n=>{let r=routeForAdapter(n,e),handler=async(r,i)=>{let a=t.webhooks[n],o=new ContextContainer;return o.setVirtualContext(ActiveWebhookKey,{from:i.from}),contextStorage.run(o,()=>a(r,{...e.webhook,waitUntil(e){i.waitUntil(e)}}))};return[GET(r,handler),POST(r,handler)]}),async receive(e,{from:n}){let r=serializeReceiveTarget(t,e.target);return n(r.id).send(e.message,{auth:e.auth,state:{thread:r}})},deliver(e,t){return recordPrincipalUser(t.state,t.session.auth.current,e),defaultDeliverResult(e)},events:a}),send(e,n){return bridgeSend(t,e,n)},respond(e,n){return bridgeRespond(t,e,n)}}}function defaultEvents(e){async function showPrompt(t,n){if(!t.thread)return!1;let r=await t.thread.post(renderInputRequests([n],e));r.id&&t.state.editSupported!==!1&&(t.state.pendingInputCards={...t.state.pendingInputCards,[r.id]:{requests:[n],resolved:{}}})}async function clearAnsweredCards(t,n){let r=n.thread;if(r)for(let[i,a]of Object.entries(n.state.pendingInputCards??{})){let o=t.resolutions.filter(e=>a.requests.some(t=>t.requestId===e.requestId));if(o.length===0)continue;let s={...a.resolved,...Object.fromEntries(o.map(e=>[e.requestId,e]))},{[i]:c,...l}=n.state.pendingInputCards??{};n.state.pendingInputCards=a.requests.every(e=>s[e.requestId]!==void 0)?l:{...l,[i]:{requests:a.requests,resolved:s}};try{await r.adapter.editMessage(r.id,i,renderInputRequests(a.requests,e,s))}catch(e){if(!isNotImplemented(e)){log.warn(`answered input card edit failed`,{error:e,messageId:i});continue}n.state.editSupported=!1,n.state.pendingInputCards={};return}}}let t=promptQueueEvents(showPrompt);return{...defaultAuthorizationEvents(),async"turn.started"(e,t,n){t.state.pendingToolCallMessage=null,clearStream(t.state),await safeStartTyping(t.thread,`Working...`)},async"actions.requested"(e,t,n){let r=t.state.pendingToolCallMessage;if(t.state.pendingToolCallMessage=null,r){await safeStartTyping(t.thread,truncate(r));return}let i=visibleActions(e.actions).map(t=>actionLabel(t,e.presentation));i.length!==0&&await safeStartTyping(t.thread,truncate(`${i.join(`, `)}...`))},async"message.appended"(e,t,n){if(!t.thread||!canStream(t))return;let r=((t.state.streamStepIndex===e.stepIndex?streamTextByState.get(t.state):void 0)??``)+e.messageDelta;if(!r)return;streamTextByState.set(t.state,r);let i=t.state.anchorMessageId;if(!i||t.state.streamStepIndex!==e.stepIndex){let n=await t.thread.post({markdown:r});t.state.anchorMessageId=n.id,t.state.streamStepIndex=e.stepIndex,t.state.lastEditAtMs=Date.now();return}let a=t.state.lastEditAtMs??0;if(!(Date.now()-a<t.streamingEditIntervalMs))try{await editMessage(t.thread,i,r),t.state.lastEditAtMs=Date.now()}catch(e){if(!isNotImplemented(e))throw e;t.state.editSupported=!1,clearStream(t.state)}},...t,async"input.resolved"(e,n,r){await clearAnsweredCards(e,n),await t[`input.resolved`](e,n)},async"message.completed"(e,t,n){if(e.finishReason===`tool-calls`){t.state.pendingToolCallMessage=e.message?firstNonEmptyLine(e.message)??null:null,e.message?await finalizeStreamedMessage(t,e.message,!1):clearStream(t.state);return}if(t.state.pendingToolCallMessage=null,!e.message){clearStream(t.state);return}await finalizeStreamedMessage(t,e.message,!0)},async"turn.failed"(e,t,n){e.terminal||await postFailure(t.thread,`I hit an error while handling your request`,`Please try again, rephrase, or reach out if it keeps failing.`,e)},async"session.failed"(e,t){await postFailure(t.thread,`This session could not recover from an error`,`Start a new conversation to continue.`,e)}}}async function safeStartTyping(e,t){if(e)try{await e.startTyping(t)}catch(e){if(!isNotImplemented(e))throw e}}async function editMessage(e,t,n){await e.adapter.editMessage(e.id,t,{markdown:n})}async function finalizeStreamedMessage(e,t,n){let r=e.thread;if(!r){clearStream(e.state);return}let i=e.state.anchorMessageId;if(canStream(e)&&i)try{await editMessage(r,i,t)}catch(i){if(!isNotImplemented(i))throw i;e.state.editSupported=!1,n&&await r.post({markdown:t})}else n&&await r.post({markdown:t});clearStream(e.state)}function canStream(e){return e.streaming&&e.state.editSupported!==!1}function clearStream(e){e.anchorMessageId=null,e.lastEditAtMs=null,e.streamStepIndex=null,streamTextByState.delete(e)}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function truncate(e,t=80){return e.length<=t?e:`${e.slice(0,t-1)}\u2026`}async function postFailure(e,t,n,r){if(!e)return;let i=formatErrorHint(r),a=extractErrorId(r.details);await e.post([`${t}${i}.`,n,...a?[`Error id: ${a}`]:[]].join(`
2
2
 
3
3
  `))}async function bridgeSend(e,t,n){let r=activeFrom(`send`),{adapterName:i,auth:a,thread:o,...s}=n,c=serializeThread(e,o,i);return r(c.id).send(t,{...s,auth:a??null,state:{thread:c}})}async function bridgeRespond(e,t,n){let r=activeFrom(`respond`),{adapterName:i,auth:a,thread:o,...s}=n;return r(serializeThread(e,o,i).id).respond(parseInputResponses(t),{...s,auth:a??null})}function recordPrincipalUser(e,t,n){if(t===null)return;let r=(n.state?.thread)?.currentMessage?.author;if(r===void 0||r.isBot===!0||r.isMe)return;let i=e.usersByPrincipal?.[t.principalId];i!==r.userId&&i!==null&&(e.usersByPrincipal={...e.usersByPrincipal,[t.principalId]:i===void 0?r.userId:null})}function activeFrom(e){let t=contextStorage.getStore()?.get(ActiveWebhookKey);if(!t)throw Error(`chatSdkChannel().${e} can only run during a Chat SDK webhook handler for this bridge.`);return t.from}function initialState(){return{pendingAuthMessageIds:{},thread:null}}async function fetchAttachment(e,t){let n=parseChatSdkFileRef(new URL(t));if(n===null)return null;await e.initialize();let r=e.getAdapter(n.adapter).rehydrateAttachment?.(n.attachment);if(r?.fetchData===void 0)throw new EveAttachmentError({adapterKind:`chat-sdk`,kind:`resolver-threw`,message:`the ${n.adapter} adapter can't download it after the webhook returns.`});let i;try{i=await r.fetchData()}catch(e){throw e instanceof EveAttachmentError?e:new EveAttachmentError({adapterKind:`chat-sdk`,cause:e,kind:`resolver-threw`,message:`the ${n.adapter} download failed.`})}let a=Buffer.isBuffer(i)?i:Buffer.from(i);return assertWithinLimit(a,DEFAULT_UPLOAD_POLICY.maxBytes,`chat-sdk`),{bytes:a,mediaType:n.attachment.mimeType}}function threadFromState(e,t){if(!t.thread)return null;try{let n=t.thread;return new ThreadImpl({adapter:e.getAdapter(n.adapterName),channelId:n.channelId,channelVisibility:n.channelVisibility,currentMessage:n.currentMessage?Message.fromJSON(n.currentMessage):void 0,id:n.id,isDM:n.isDM,stateAdapter:e.getState()})}catch(e){return log.warn(`failed to rebuild Chat SDK thread from channel state`,{error:e}),null}}function serializeReceiveTarget(e,t){if(t.thread)return t.thread;if(!t.threadId)throw Error(`chatSdkChannel().receive requires target.thread or target.threadId.`);return serializeThread(e,t.threadId,t.adapterName)}function serializeThread(e,t,n){if(typeof t==`string`){let r=n??inferAdapterName(t),i=e.getAdapter(r);return{_type:`chat:Thread`,adapterName:r,channelId:i.channelIdFromThreadId(t),channelVisibility:i.getChannelVisibility?.(t),id:t,isDM:!1}}return isSerializedThread(t)?t:t.toJSON()}function isSerializedThread(e){return`_type`in e&&e._type===`chat:Thread`}function inferAdapterName(e){let t=e.indexOf(`:`);if(t<=0)throw Error(`chatSdkChannel string thread references require options.adapterName.`);return e.slice(0,t)}function adapterNames(e){return Object.keys(e)}function routeForAdapter(e,t){return t.routes?.[e]||`${(t.route??`/eve/v1`).replace(/\/$/u,``)}/${e}`}export{chatSdkChannel};
@@ -1,3 +1,3 @@
1
1
  import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{resolvedPromptLabel}from"#channel/resolved-prompt.js";import{DISCORD_MESSAGE_CONTENT_MAX_LENGTH,splitDiscordMessageContent}from"#public/channels/discord/api.js";import{renderInputRequestComponents}from"#public/channels/discord/hitl.js";const log=createLogger(`discord.defaults`);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,n){for(let n of e.requests){let e=splitDiscordMessageContent(n.prompt)[0]??n.prompt,r=renderInputRequestComponents(n),i=await t.discord.post({components:r,content:e});r.length!==0&&i.id&&(t.state.hitlPrompts={...t.state.hitlPrompts,[n.requestId]:{content:e,messageId:i.id,options:(n.options??[]).map(({id:e,label:t})=>({id:e,label:t}))}})}},async"input.resolved"(e,t,n){for(let n of e.resolutions){let e=t.state.hitlPrompts?.[n.requestId];if(e===void 0)continue;let{[n.requestId]:r,...i}=t.state.hitlPrompts??{};t.state.hitlPrompts=i;let a=resolvedPromptLabel(n,e.options),o=`${e.content}\n\n${a}`.slice(0,DISCORD_MESSAGE_CONTENT_MAX_LENGTH);try{let r=await t.discord.request(`/channels/${encodeURIComponent(t.discord.channelId)}/messages/${encodeURIComponent(e.messageId)}`,{components:[],content:o},{botAuth:!0,method:`PATCH`});r.ok||log.warn(`Discord answered prompt edit failed`,{requestId:n.requestId,status:r.status})}catch(e){log.warn(`Discord answered prompt edit failed`,{error:e,requestId:n.requestId})}}},async"message.completed"(e,t,n){e.finishReason!==`tool-calls`&&e.message&&await t.discord.post(e.message)},async"session.failed"(e,t){let n=formatErrorHint(e),r=extractErrorId(e.details);await t.discord.post([`This session could not recover from an error${n}.`,``,`Start a new command to continue.`,...r?[``,`Error id: ${r}`]:[]].join(`
2
- `))},async"turn.failed"(e,t,n){let r=formatErrorHint(e),i=extractErrorId(e.details);await t.discord.post([`I hit an error while handling your request${r}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[``,`Error id: ${i}`]:[]].join(`
2
+ `))},async"turn.failed"(e,t,n){if(e.terminal)return;let r=formatErrorHint(e),i=extractErrorId(e.details);await t.discord.post([`I hit an error while handling your request${r}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[``,`Error id: ${i}`]:[]].join(`
3
3
  `))}};export{defaultDiscordAuth,defaultEvents,defaultOnCommand};
@@ -1,5 +1,5 @@
1
1
  import{createLogger,extractErrorId,formatErrorHint,logError}from"#internal/logging.js";import{renderTextInputRequest}from"#channel/resolve-text.js";import{promptQueueEvents}from"#channel/prompt-queue.js";import{checkoutGitHubRepository}from"#public/channels/github/checkout.js";import{shouldDispatchGitHubComment}from"#public/channels/github/inbound.js";import{splitGitHubCommentBody}from"#public/channels/github/limits.js";const log=createLogger(`github.defaults`);function defaultGitHubAuth(e){let{sender:t}=e;return{attributes:{conversation_kind:e.conversation.kind,delivery_id:e.delivery.id,installation_id:String(e.github.installationId??``),issue_number:String(e.conversation.issueNumber??``),pull_request_number:String(e.conversation.pullRequestNumber??``),repository:e.repository.fullName,repository_id:String(e.repository.id),user_login:t.login,user_type:t.type},authenticator:`github-webhook`,issuer:`github:${e.repository.owner}`,principalId:`github:${t.id}`,principalType:t.type===`Bot`?`service`:`user`,subject:t.login}}async function defaultOnComment(e,t,n){return shouldDispatchGitHubComment({author:t.author,body:t.body,botName:await n.botName?.()})?{auth:defaultGitHubAuth(e)}:null}function createDefaultEvents(e={}){async function showPrompt(t,n){let r=[renderInputRequest(n)],i=renderReplyInstruction(n,await e.botName?.());i!==void 0&&r.push(i),await postCommentChunks(t,r.join(`
2
2
 
3
3
  `))}return{async"turn.started"(t,n,r){if(e.progress?.reactions!==!1)try{await n.thread.react(`eyes`)}catch(e){logError(log,`GitHub reaction failed — swallowed`,e)}await checkoutRepositoryForTurn(n,r,e)},async"message.completed"(e,t,n){e.finishReason!==`tool-calls`&&e.message&&await postCommentChunks(t,e.message)},...promptQueueEvents(showPrompt),async"session.failed"(e,t){let n=formatErrorHint(e),r=extractErrorId(e.details);await postFailure(t,[`This session could not recover from an error${n}.`,``,`Start a new comment to continue.`,...r?[``,`Error id: ${r}`]:[]].join(`
4
- `))},async"turn.failed"(e,t,n){let r=formatErrorHint(e),i=extractErrorId(e.details);await postFailure(t,[`I hit an error while handling your request${r}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[``,`Error id: ${i}`]:[]].join(`
4
+ `))},async"turn.failed"(e,t,n){if(e.terminal)return;let r=formatErrorHint(e),i=extractErrorId(e.details);await postFailure(t,[`I hit an error while handling your request${r}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[``,`Error id: ${i}`]:[]].join(`
5
5
  `))}}}function renderInputRequest(e){let t=renderTextInputRequest(e);return e.allowFreeform===!0?`${t}\n\nYou can also reply with a custom answer.`:t}function renderReplyInstruction(e,t){let n=t?.trim();return n?`Answer by mentioning me in a reply, e.g. \`@${n} ${e.options?.[0]?.label??`<your answer>`}\`.`:void 0}async function checkoutRepositoryForTurn(e,t,n){let{state:r}=e;try{let e=await t.getSandbox();if(!(`setNetworkPolicy`in e))throw Error(`GitHub checkout requires a sandbox provider with mutable network policy.`);let i=await checkoutGitHubRepository(e,{api:n.api,baseRef:r.baseRef,baseSha:r.baseSha,credentials:n.credentials,defaultBranch:r.defaultBranch,headRef:r.headRef,headSha:r.headSha,includeBase:r.pullRequestNumber!==null,installationId:r.installationId,owner:r.owner,pullRequestNumber:r.pullRequestNumber,repo:r.repo});r.checkoutPath=i.path,r.headSha=i.sha,r.baseRef=i.baseRef}catch(e){logError(log,`GitHub checkout failed — swallowed`,e)}}async function postCommentChunks(e,t){for(let n of splitGitHubCommentBody(t))await e.thread.post(n)}async function postFailure(e,t){await postCommentChunks(e,t)}export{createDefaultEvents,defaultGitHubAuth,defaultOnComment};
@@ -1,4 +1,6 @@
1
1
  export { defineChannel, disableRoute, GET, HEAD, OPTIONS, POST, PUT, PATCH, DELETE, WS, type AttachSessionFn, type CancelTurnResult, type ClearSessionResult, type CompactSessionResult, type ResetSessionResult, SessionStrandedError, type Channel, type ChannelAudience, type AudienceCaller, type AudienceContext, type AudienceInput, type AudiencePrincipal, type ConversationEnvironment, type ChannelFrom, type ChannelReceiveContext, type ChannelResolveSession, type ChannelRespondOptions, type ChannelSendOptions, type ChannelSource, type ChannelCors, type ChannelCorsOptions, type ChannelDefinition, type DisabledRouteSentinel, type ChannelContinuationOps, type ChannelEvents, type InferChannelMetadata, type Session, type SessionCallback, type SessionHandle, type SessionRespondOptions, type SessionSendOptions, type TurnPolicy, type RouteDefinition, type RouteHandlerArgs, type AgentDescription, type AgentSkillDescription, type AgentSkillFileDescription, type AgentToolDescription, type InvokeToolFn, type InvokeToolOptions, type InvokeToolResult, type HttpRouteDefinition, type WebSocketMessage, type WebSocketPeer, type WebSocketRouteDefinition, type WebSocketRouteHandler, type WebSocketRouteHooks, type WebSocketUpgradeRequest, type WebSocketUpgradeResult, isDisabledRouteSentinel, } from "#public/definitions/channel.js";
2
+ export { attachmentError } from "#public/channels/attachment-error.js";
3
+ export type { FetchFileContext, FetchFileFunction, FetchFileResult, } from "#shared/channel-definition.js";
2
4
  export { createWebSocketUpgradeServer, type WebSocketUpgradeServerBridge, } from "#channel/websocket-upgrade-server.js";
3
5
  import type { Channel, InferChannelMetadata } from "#public/definitions/channel.js";
4
6
  /**
@@ -1 +1 @@
1
- import{getChannelInstrumentationKind}from"#channel/compiled-channel.js";import{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,SessionStrandedError,WS,defineChannel,disableRoute,isDisabledRouteSentinel}from"#public/definitions/channel.js";import{createWebSocketUpgradeServer}from"#channel/websocket-upgrade-server.js";function isChannel(e,t){return e.kind===getChannelInstrumentationKind(t)}export{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,SessionStrandedError,WS,createWebSocketUpgradeServer,defineChannel,disableRoute,isChannel,isDisabledRouteSentinel};
1
+ import{getChannelInstrumentationKind}from"#channel/compiled-channel.js";import{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,SessionStrandedError,WS,defineChannel,disableRoute,isDisabledRouteSentinel}from"#public/definitions/channel.js";import{attachmentError}from"#public/channels/attachment-error.js";import{createWebSocketUpgradeServer}from"#channel/websocket-upgrade-server.js";function isChannel(e,t){return e.kind===getChannelInstrumentationKind(t)}export{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,SessionStrandedError,WS,attachmentError,createWebSocketUpgradeServer,defineChannel,disableRoute,isChannel,isDisabledRouteSentinel};
@@ -1,5 +1,5 @@
1
1
  import{actionLabel,visibleActions}from"#shared/action-label.js";import{extractErrorId,formatErrorHint}from"#internal/logging.js";import{promptQueueEvents}from"#channel/prompt-queue.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(e={}){async function showPrompt(t,n){await postActivity(t,e,{body:renderLinearInputRequests([n]),type:`elicitation`},linearInputRequestSignal([n]))}return{async"turn.started"(t,n,r){n.state.pendingToolCallMessage=null,await postActivity(n,e,{body:`Working on this.`,type:`thought`},{ephemeral:!0})},async"actions.requested"(t,n,r){let i=n.state.pendingToolCallMessage;if(n.state.pendingToolCallMessage=null,i){await postActivity(n,e,{body:i,type:`thought`},{ephemeral:!0});return}let a=visibleActions(t.actions);if(a.length!==0){if(a.length>1){await postActivity(n,e,{action:`Running`,parameter:a.map(e=>actionLabel(e,t.presentation)).join(`, `),type:`action`},{ephemeral:!0});return}for(let r of a)await postActivity(n,e,{action:actionLabel(r,t.presentation),parameter:actionParameter(r),type:`action`},{ephemeral:!0})}},...promptQueueEvents(showPrompt),async"authorization.required"(t,n,r){let i=authorizationDisplayName(t.name,t.authorization?.displayName),a=t.authorization?.url,o=linearUserId(r),s;if(a!==void 0){let e={providerName:i,url:a};o!==void 0&&(e.userId=o),s={signal:`auth`,signalMetadata:e}}await postActivity(n,e,{body:authorizationRequiredBody({displayName:i,instructions:t.authorization?.instructions,userCode:t.authorization?.userCode}),type:`elicitation`},s)},async"authorization.completed"(t,n,r){let i=authorizationDisplayName(t.name,t.authorization?.displayName);if(t.outcome===`authorized`){await postActivity(n,e,{body:`Connected to ${i}. Resuming.`,type:`thought`},{ephemeral:!0});return}let a=t.reason===void 0?``:` (${t.reason})`;await postActivity(n,e,{body:`${i} authorization ${formatAuthorizationOutcome(t.outcome)}${a}.`,type:`thought`})},async"message.completed"(t,n,r){if(t.finishReason===`tool-calls`){n.state.pendingToolCallMessage=t.message?firstNonEmptyLine(t.message)??null:null;return}n.state.pendingToolCallMessage=null,t.message&&await postActivity(n,e,{body:t.message,type:`response`})},async"session.failed"(t,n){let r=formatErrorHint(t),i=extractErrorId(t.details);await postActivity(n,e,{body:[`This session could not recover from an error${r}.`,``,`Start a new Linear agent session to continue.`,...i?[``,`Error id: ${i}`]:[]].join(`
2
- `),type:`error`})},async"turn.failed"(t,n,r){let i=formatErrorHint(t),a=extractErrorId(t.details);await postActivity(n,e,{body:[`I hit an error while handling your request${i}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...a?[``,`Error id: ${a}`]:[]].join(`
2
+ `),type:`error`})},async"turn.failed"(t,n,r){if(t.terminal)return;let i=formatErrorHint(t),a=extractErrorId(t.details);await postActivity(n,e,{body:[`I hit an error while handling your request${i}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...a?[``,`Error id: ${a}`]:[]].join(`
3
3
  `),type:`error`})}}}function postActivity(e,t,n,r={}){return createLinearAgentActivity({api:t.api,credentials:t.credentials,activity:{agentSessionId:requireAgentSessionId(e.state.agentSessionId),content:n,ephemeral:r.ephemeral,signal:r.signal,signalMetadata:r.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 authorizationDisplayName(e,t){return t===void 0?e.length===0?e:e.charAt(0).toUpperCase()+e.slice(1):t}function authorizationRequiredBody(e){return[`Authorization required for ${e.displayName}.`,e.instructions,e.userCode===void 0?void 0:`Code: ${e.userCode}`].filter(e=>e!==void 0&&e.length>0).join(`
4
4
 
5
5
  `)}function linearUserId(e){let t=e.session.auth.current;return t?.authenticator===`linear-agent-webhook`?t.subject:void 0}function formatAuthorizationOutcome(e){return e===`timed-out`?`timed out`:e}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}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};
@@ -301,6 +301,16 @@ interface SlackBinding {
301
301
  readonly thread: SlackThread;
302
302
  readonly slack: SlackHandle;
303
303
  }
304
+ /** Replies {@link SlackThread.refresh} loads: the thread's first page. */
305
+ export declare const THREAD_REFRESH_LIMIT = 50;
306
+ /**
307
+ * Fetches the last `size` messages of a thread up to and including the one at
308
+ * `latest`, oldest first, without touching {@link SlackThread.recentMessages}.
309
+ * Slack returns replies oldest first, so this pages forward to `latest`.
310
+ * Returns `undefined` when `latest` sits more than 1000 replies in, or for a
311
+ * thread {@link buildSlackBinding} didn't build.
312
+ */
313
+ export declare function fetchSlackThreadWindow(thread: SlackThread, latest: string, size: number): Promise<readonly SlackThreadMessage[] | undefined>;
304
314
  /**
305
315
  * Constructs the `{ thread, slack }` pair.
306
316
  *
@@ -1 +1 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{isCardElement}from"#compiled/chat/index.js";import{fetchSlackThreadReplies,postSlackEphemeral,postSlackMessage,resolveSlackBotToken as resolveSlackBotToken$1,uploadSlackFiles}from"#compiled/@chat-adapter/slack/api.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{resolveSlackInboundMrkdwn}from"#public/channels/slack/inbound-content.js";import{truncateTypingStatus}from"#public/channels/slack/limits.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";import{callSlackApiTrackingResponse,slackApiOptions}from"#public/channels/slack/transport.js";const log=createLogger(`slack.api`);function slackContinuationToken(e,t){return`${e}:${t}`}async function resolveSlackBotToken(e,t={}){let n=e??process.env.SLACK_BOT_TOKEN;if(!n)throw Error(`SLACK_BOT_TOKEN is required.`);return typeof n==`function`?n(t):resolveSlackBotToken$1(n)}async function callSlackApi(e){return callSlackApiTrackingResponse(e.operation,normalizeSlackApiBody(e.body),slackApiOptions(e.api,()=>resolveSlackBotToken(e.botToken,e.context)))}function createSlackRequester(e,t,n){return(r,i)=>callSlackApi({api:e,botToken:t,context:n,operation:r,body:i})}function buildSlackWorkspaceHandle(e){return{teamId:e.teamId,request:createSlackRequester(e.api,e.botToken,{teamId:e.installationTeamId})}}function buildSlackBinding(e){let t={teamId:e.installationTeamId},n=slackApiOptions(e.api,()=>resolveSlackBotToken(e.botToken,t)),r=createSlackRequester(e.api,e.botToken,t),i=[],a=e.threadTs,o;function handleMessageTs(t){a||t===a||(a=t,e.onThreadTsChanged?.(t))}async function uploadFiles(t,r){let i=r?.channelId??e.channelId,o=r?.threadTs??a,s=t.map(e=>toSlackFileUpload(e,r?.snippetType));return uploadSlackFiles(s,{...n,channelId:i||void 0,initialComment:r?.initialComment,threadTs:o||void 0})}function refreshMessages(){if(o!==void 0)return o;async function fetchAndReplace(){if(!e.channelId||!a){i=[];return}try{i=(await fetchSlackThreadReplies({...n,channel:e.channelId,limit:50,ts:a})).messages.map(t=>parseThreadMessage(t,a,{appId:e.appId,botUserId:e.botUserId}))}catch(t){logError(log,`refresh threw — swallowed`,t,{channelId:e.channelId})}}let t=fetchAndReplace().finally(()=>{o===t&&(o=void 0)});return o=t,t}let s={get recentMessages(){return i},async listParticipants(){await s.refresh();let e=new Set;for(let t of i)t.user!==void 0&&t.botId===void 0&&e.add(t.user);return[...e]},async post(t){let r=normalizePostInput(t),i=r.files??[],o=`blocks`in r||`card`in r||`markdown`in r;if(i.length>0&&!o){let e=await uploadFiles(i,{initialComment:`text`in r?r.text:void 0});return{id:Array.isArray(e.raw.files)&&e.raw.files.length>0?String(e.raw.files[0].id??``):``,raw:e.raw}}let s=await postSlackMessage(buildPostMessageOptions(r,e.channelId,a,n)),c=s.id;if(handleMessageTs(c),i.length>0&&o)try{await uploadFiles(i)}catch(e){log.warn(`file upload after message post failed`,{error:e})}return{id:c,raw:s.raw}},async postEphemeral(t,r){let i=normalizePostInput(r),o=await postSlackEphemeral({...buildPostMessageOptions(i,e.channelId,a,n),user:t});return{id:o.id,raw:o.raw}},async postDirectMessage(e,t){let i=await r(`conversations.open`,{users:e}),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 o=normalizePostInput(t),s=await postSlackMessage(buildPostMessageOptions(o,a,``,n));return{id:s.id,raw:s.raw}},async startTyping(t){if(e.channelId&&a)try{let n=t===void 0?``:truncateTypingStatus(t),i={channel_id:e.channelId,thread_ts:a,status:n};n.length>0&&(i.loading_messages=[n]);let o=await r(`assistant.threads.setStatus`,i);o.ok!==!0&&log.warn(`assistant.threads.setStatus returned not-ok`,{error:o.error})}catch(t){logError(log,`startTyping threw — swallowed`,t,{channelId:e.channelId})}},refresh(){return refreshMessages()},mentionUser(e){return`<@${e}>`}};return{thread:s,slack:{channelId:e.channelId,get threadTs(){return a},teamId:e.teamId,request:r,uploadFiles}}}function normalizePostInput(e){return typeof e==`string`?{markdown:e}:isCardElement(e)?{card:e}:e}function buildPostMessageOptions(e,t,n,r){let i={...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 normalizeSlackApiBody(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:{}}function toSlackFileUpload(e,t){return{data:normalizeFileData(e.data),filename:e.filename,snippetType:t}}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=resolveSlackInboundMrkdwn(r,e),a=typeof e.ts==`string`?e.ts:``,o=typeof e.thread_ts==`string`?e.thread_ts:t,s=typeof e.user==`string`?e.user:void 0,c=typeof e.bot_id==`string`?e.bot_id:void 0,l=typeof e.app_id==`string`?e.app_id:void 0,u=n.botUserId!==void 0&&s===n.botUserId||n.appId!==void 0&&l===n.appId;return{text:i,markdown:slackMrkdwnToGfm(i),user:s,botId:c,ts:a,threadTs:o,isMe:u,raw:e}}export{buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,resolveSlackBotToken,slackContinuationToken};
1
+ import{createLogger,logError}from"#internal/logging.js";import{isCardElement}from"#compiled/chat/index.js";import{fetchSlackThreadReplies,postSlackEphemeral,postSlackMessage,resolveSlackBotToken as resolveSlackBotToken$1,uploadSlackFiles}from"#compiled/@chat-adapter/slack/api.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{resolveSlackInboundMrkdwn}from"#public/channels/slack/inbound-content.js";import{truncateTypingStatus}from"#public/channels/slack/limits.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";import{callSlackApiTrackingResponse,slackApiOptions}from"#public/channels/slack/transport.js";const log=createLogger(`slack.api`);function slackContinuationToken(e,t){return`${e}:${t}`}async function resolveSlackBotToken(e,t={}){let n=e??process.env.SLACK_BOT_TOKEN;if(!n)throw Error(`SLACK_BOT_TOKEN is required.`);return typeof n==`function`?n(t):resolveSlackBotToken$1(n)}async function callSlackApi(e){return callSlackApiTrackingResponse(e.operation,normalizeSlackApiBody(e.body),slackApiOptions(e.api,()=>resolveSlackBotToken(e.botToken,e.context)))}function createSlackRequester(e,t,n){return(r,i)=>callSlackApi({api:e,botToken:t,context:n,operation:r,body:i})}function buildSlackWorkspaceHandle(e){return{teamId:e.teamId,request:createSlackRequester(e.api,e.botToken,{teamId:e.installationTeamId})}}const THREAD_REFRESH_LIMIT=50,threadWindowFetchers=new WeakMap;async function fetchSlackThreadWindow(e,t,n){return await threadWindowFetchers.get(e)?.(t,n)}function buildSlackBinding(e){let t={teamId:e.installationTeamId},n=slackApiOptions(e.api,()=>resolveSlackBotToken(e.botToken,t)),r=createSlackRequester(e.api,e.botToken,t),i=[],a=e.threadTs,o;function handleMessageTs(t){a||t===a||(a=t,e.onThreadTsChanged?.(t))}async function uploadFiles(t,r){let i=r?.channelId??e.channelId,o=r?.threadTs??a,s=t.map(e=>toSlackFileUpload(e,r?.snippetType));return uploadSlackFiles(s,{...n,channelId:i||void 0,initialComment:r?.initialComment,threadTs:o||void 0})}function refreshMessages(){if(o!==void 0)return o;async function fetchAndReplace(){if(!e.channelId||!a){i=[];return}try{i=(await fetchSlackThreadReplies({...n,channel:e.channelId,limit:50,ts:a})).messages.map(t=>parseThreadMessage(t,a,{appId:e.appId,botUserId:e.botUserId}))}catch(t){logError(log,`refresh threw — swallowed`,t,{channelId:e.channelId})}}let t=fetchAndReplace().finally(()=>{o===t&&(o=void 0)});return o=t,t}async function fetchWindow(t,r){if(!e.channelId||!a)return;let i=[],o;for(let s=0;s<5;s+=1){let s=await fetchSlackThreadReplies({...n,channel:e.channelId,cursor:o,inclusive:!0,latest:t,limit:200,ts:a});if(i=[...i,...s.messages].slice(-r),o=s.nextCursor,o===void 0||o===``)return i.map(t=>parseThreadMessage(t,a,{appId:e.appId,botUserId:e.botUserId}))}}let s={get recentMessages(){return i},async listParticipants(){await s.refresh();let e=new Set;for(let t of i)t.user!==void 0&&t.botId===void 0&&e.add(t.user);return[...e]},async post(t){let r=normalizePostInput(t),i=r.files??[],o=`blocks`in r||`card`in r||`markdown`in r;if(i.length>0&&!o){let e=await uploadFiles(i,{initialComment:`text`in r?r.text:void 0});return{id:Array.isArray(e.raw.files)&&e.raw.files.length>0?String(e.raw.files[0].id??``):``,raw:e.raw}}let s=await postSlackMessage(buildPostMessageOptions(r,e.channelId,a,n)),c=s.id;if(handleMessageTs(c),i.length>0&&o)try{await uploadFiles(i)}catch(e){log.warn(`file upload after message post failed`,{error:e})}return{id:c,raw:s.raw}},async postEphemeral(t,r){let i=normalizePostInput(r),o=await postSlackEphemeral({...buildPostMessageOptions(i,e.channelId,a,n),user:t});return{id:o.id,raw:o.raw}},async postDirectMessage(e,t){let i=await r(`conversations.open`,{users:e}),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 o=normalizePostInput(t),s=await postSlackMessage(buildPostMessageOptions(o,a,``,n));return{id:s.id,raw:s.raw}},async startTyping(t){if(e.channelId&&a)try{let n=t===void 0?``:truncateTypingStatus(t),i={channel_id:e.channelId,thread_ts:a,status:n};n.length>0&&(i.loading_messages=[n]);let o=await r(`assistant.threads.setStatus`,i);o.ok!==!0&&log.warn(`assistant.threads.setStatus returned not-ok`,{error:o.error})}catch(t){logError(log,`startTyping threw — swallowed`,t,{channelId:e.channelId})}},refresh(){return refreshMessages()},mentionUser(e){return`<@${e}>`}},c={channelId:e.channelId,get threadTs(){return a},teamId:e.teamId,request:r,uploadFiles};return threadWindowFetchers.set(s,fetchWindow),{thread:s,slack:c}}function normalizePostInput(e){return typeof e==`string`?{markdown:e}:isCardElement(e)?{card:e}:e}function buildPostMessageOptions(e,t,n,r){let i={...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 normalizeSlackApiBody(e){return e&&typeof e==`object`&&!Array.isArray(e)?e:{}}function toSlackFileUpload(e,t){return{data:normalizeFileData(e.data),filename:e.filename,snippetType:t}}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=resolveSlackInboundMrkdwn(r,e),a=typeof e.ts==`string`?e.ts:``,o=typeof e.thread_ts==`string`?e.thread_ts:t,s=typeof e.user==`string`?e.user:void 0,c=typeof e.bot_id==`string`?e.bot_id:void 0,l=typeof e.app_id==`string`?e.app_id:void 0,u=n.botUserId!==void 0&&s===n.botUserId||n.appId!==void 0&&l===n.appId;return{text:i,markdown:slackMrkdwnToGfm(i),user:s,botId:c,ts:a,threadTs:o,isMe:u,raw:e}}export{THREAD_REFRESH_LIMIT,buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,fetchSlackThreadWindow,resolveSlackBotToken,slackContinuationToken};
@@ -1,2 +1,2 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{SLACK_MAX_BLOCKS_PER_MESSAGE,truncateMessageText}from"#public/channels/slack/limits.js";import{slackUserIdForPrincipal,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{buildAnsweredBlocks,decodeHitlActionId,renderInputRequestPostParts}from"#public/channels/slack/hitl.js";import{deliverPrivateInputRequest}from"#public/channels/slack/private-approval-delivery.js";const log=createLogger(`slack.defaults`);function defaultInputRequestedHandler(e){return async(t,n,r)=>{let i=[],a=[];for(let n of t.requests)((e===void 0?`thread`:await e(n,r))===`direct-message`?i:a).push(n);await postPublicInputRequests(a,n);for(let e of i){let t=slackUserIdFromAuthContext(r.session.auth.current)??n.state.triggeringUserId;if(!t){log.warn(`direct-message input request not delivered because no reviewer was resolved`,{requestId:e.requestId,sessionId:r.session.id});continue}let i=await deliverPrivateInputRequest({previewMessageTs:n.state.triggeringMessageTs??n.slack.threadTs,request:e,reviewer:t,slack:n.slack});recordApprovalCards(n.state,[e],i);try{let r=await n.thread.post(`Waiting on ${e.kind===`tool-approval`?`approval`:`a response`} from <@${t}>…`);r.id&&recordApprovalCards(n.state,[e],{...i,announcementTs:r.id})}catch(t){logError(log,`failed to announce private input request`,t,{requestId:e.requestId,sessionId:r.session.id})}}}}async function postPublicInputRequests(e,t){let n=new Map;for(let r of buildInputRequestPosts(e)){let e=await t.thread.post({blocks:r.blocks,text:r.text});if(r.kind===`details`){if(e.id)for(let t of r.requests)n.set(t.requestId,e.id);continue}for(let i of r.requests){let a=n.get(i.requestId);recordApprovalCards(t.state,[i],{detailsMessageTs:a,messageBlocks:r.blocks,messageTs:e.id})}}}function recordApprovalCards(e,t,n){if(!n.messageTs)return;let r={...e.pendingApprovalCards};for(let e of t)e.kind===`tool-approval`&&(r[e.requestId]=n);e.pendingApprovalCards=r}async function settleApprovalCard(e,t,n){let r=e.state.pendingApprovalCards??{},i=r[t];if(i===void 0)return;let a={...r};delete a[t],e.state.pendingApprovalCards=a;let o=i.messageChannelId??e.state.channelId;if(o===null)return;let s=i.messageBlocks.flatMap(e=>{if(!blockContainsRequestAction(e,t))return[e];if(typeof e!=`object`||!e)return[];let r=e;if(r.type!==`card`)return buildAnsweredBlocks({answerLabel:n.label,promptBlocks:[],userId:n.userId});let{actions:i,subtext:a,...o}=r;return buildAnsweredBlocks({answerLabel:n.label,promptBlocks:[o],userId:n.userId})});for(let[e,t]of Object.entries(a))t.messageTs===i.messageTs&&(a[e]={...t,messageBlocks:s});await settleRequest(e,`chat.update`,t,{blocks:s,channel:o,text:`Answered: ${n.label}`,ts:i.messageTs});let c=i.detailsMessageTs;c!==void 0&&!Object.values(a).some(e=>e.detailsMessageTs===c)&&await settleRequest(e,`chat.delete`,t,{channel:o,ts:c}),i.announcementTs!==void 0&&e.state.channelId!==null&&await settleRequest(e,`chat.update`,t,{channel:e.state.channelId,text:n.announcement,ts:i.announcementTs})}async function settleRequest(e,t,n,r){try{let i=await e.slack.request(t,r);i.ok!==!0&&log.warn(`failed to retire approval message`,{error:i.error,operation:t,requestId:n})}catch(e){logError(log,`failed to retire approval message`,e,{operation:t,requestId:n})}}function buildInputRequestPosts(e){let t=[],n=[];for(let r of e){let e=renderInputRequestPostParts(r);e.details&&t.push({...e.details,request:r}),n.push({...e.controls,request:r})}return[...groupInputRequestPostParts(t).map(e=>({...e,kind:`details`})),...groupInputRequestPostParts(n).map(e=>({...e,kind:`controls`}))]}function groupInputRequestPostParts(e){let t=[];for(let n of e){let e=t.at(-1);e&&e.blocks.length+n.blocks.length<=SLACK_MAX_BLOCKS_PER_MESSAGE?(e.blocks.push(...n.blocks),e.fallbacks.push(n.text),e.requests.push(n.request)):t.push({blocks:[...n.blocks],fallbacks:[n.text],requests:[n.request]})}return t.map(e=>({blocks:e.blocks,requests:e.requests,text:truncateMessageText(e.fallbacks.join(`
1
+ import{createLogger,logError}from"#internal/logging.js";import{SLACK_MAX_BLOCKS_PER_MESSAGE,truncateMessageText}from"#public/channels/slack/limits.js";import{slackUserIdForPrincipal,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{buildAnsweredBlocks,decodeHitlActionId,renderInputRequestPostParts}from"#public/channels/slack/hitl.js";import{answeredBlocksFromActionBlock}from"#public/channels/slack/interaction-cards.js";import{deliverPrivateInputRequest}from"#public/channels/slack/private-approval-delivery.js";const log=createLogger(`slack.defaults`);function defaultInputRequestedHandler(e){return async(t,n,r)=>{let i=[],a=[];for(let n of t.requests)((e===void 0?`thread`:await e(n,r))===`direct-message`?i:a).push(n);await postPublicInputRequests(a,n);for(let e of i){let t=slackUserIdFromAuthContext(r.session.auth.current)??n.state.triggeringUserId;if(!t){log.warn(`direct-message input request not delivered because no reviewer was resolved`,{requestId:e.requestId,sessionId:r.session.id});continue}let i=await deliverPrivateInputRequest({previewMessageTs:n.state.triggeringMessageTs??n.slack.threadTs,request:e,reviewer:t,slack:n.slack});recordApprovalCards(n.state,[e],i);try{let r=await n.thread.post(`Waiting on ${e.kind===`tool-approval`?`approval`:`a response`} from <@${t}>…`);r.id&&recordApprovalCards(n.state,[e],{...i,announcementTs:r.id})}catch(t){logError(log,`failed to announce private input request`,t,{requestId:e.requestId,sessionId:r.session.id})}}}}async function postPublicInputRequests(e,t){let n=new Map;for(let r of buildInputRequestPosts(e)){let e=await t.thread.post({blocks:r.blocks,text:r.text});if(r.kind===`details`){if(e.id)for(let t of r.requests)n.set(t.requestId,e.id);continue}for(let i of r.requests){let a=n.get(i.requestId);recordApprovalCards(t.state,[i],{detailsMessageTs:a,messageBlocks:r.blocks,messageTs:e.id})}}}function recordApprovalCards(e,t,n){if(!n.messageTs)return;let r={...e.pendingApprovalCards};for(let e of t)e.kind===`tool-approval`&&(r[e.requestId]=n);e.pendingApprovalCards=r}async function settleApprovalCard(e,t,n){let r=e.state.pendingApprovalCards??{},i=r[t];if(i===void 0)return;let a={...r};delete a[t],e.state.pendingApprovalCards=a;let o=i.messageChannelId??e.state.channelId;if(o===null)return;let s=i.messageBlocks.flatMap(e=>blockContainsRequestAction(e,t)?answeredBlocksFromActionBlock({answerLabel:n.label,block:e,userId:n.userId??``})??buildAnsweredBlocks({answerLabel:n.label,promptBlocks:[]}):[e]);for(let[e,t]of Object.entries(a))t.messageTs===i.messageTs&&(a[e]={...t,messageBlocks:s});await settleRequest(e,`chat.update`,t,{blocks:s,channel:o,text:`Answered: ${n.label}`,ts:i.messageTs});let c=i.detailsMessageTs;c!==void 0&&!Object.values(a).some(e=>e.detailsMessageTs===c)&&await settleRequest(e,`chat.delete`,t,{channel:o,ts:c}),i.announcementTs!==void 0&&e.state.channelId!==null&&await settleRequest(e,`chat.update`,t,{channel:e.state.channelId,text:n.announcement,ts:i.announcementTs})}async function settleRequest(e,t,n,r){try{let i=await e.slack.request(t,r);i.ok!==!0&&log.warn(`failed to retire approval message`,{error:i.error,operation:t,requestId:n})}catch(e){logError(log,`failed to retire approval message`,e,{operation:t,requestId:n})}}function buildInputRequestPosts(e){let t=[],n=[];for(let r of e){let e=renderInputRequestPostParts(r);e.details&&t.push({...e.details,request:r}),n.push({...e.controls,request:r})}return[...groupInputRequestPostParts(t).map(e=>({...e,kind:`details`})),...groupInputRequestPostParts(n).map(e=>({...e,kind:`controls`}))]}function groupInputRequestPostParts(e){let t=[];for(let n of e){let e=t.at(-1);e&&e.blocks.length+n.blocks.length<=SLACK_MAX_BLOCKS_PER_MESSAGE?(e.blocks.push(...n.blocks),e.fallbacks.push(n.text),e.requests.push(n.request)):t.push({blocks:[...n.blocks],fallbacks:[n.text],requests:[n.request]})}return t.map(e=>({blocks:e.blocks,requests:e.requests,text:truncateMessageText(e.fallbacks.join(`
2
2
  `))}))}function blockContainsRequestAction(e,t){if(typeof e!=`object`||!e)return!1;let n=e;return[n.actions,n.elements].some(e=>Array.isArray(e)&&e.some(e=>{if(typeof e!=`object`||!e)return!1;let n=e.action_id;return typeof n==`string`&&decodeHitlActionId(n)?.requestId===t}))}const approvalEvents={async"approval.candidate"(e,t,n){if(e.outcome!==`rejected`&&e.outcome!==`failed`)return;let r=slackUserIdForPrincipal(t.state,e.responderPrincipalId);r!==void 0&&await t.thread.postEphemeral(r,e.reason??`We couldn’t verify your response. Please try again.`)},async"approval.settled"(e,t,n){let r=e.outcome===`approved`,i=slackUserIdForPrincipal(t.state,e.responderPrincipalId),a=i===void 0?``:` by <@${i}>`;await settleApprovalCard(t,e.requestId,{announcement:`${r?`Approved`:`Cancelled`}${a}.`,label:r?`Approve`:`Cancel`,userId:i})},async"input.resolved"(e,t,n){for(let n of e.resolutions){if(n.kind!==`tool-approval`)continue;let e=n.outcome===`approved`?`Approve`:n.outcome===`denied`?`Cancel`:`No longer needed`;await settleApprovalCard(t,n.requestId,{announcement:e===`No longer needed`?`This approval is no longer needed.`:`${e===`Approve`?`Approved`:`Cancelled`}.`,label:e})}}};export{approvalEvents,defaultInputRequestedHandler};
@@ -1,39 +1,49 @@
1
1
  import type { FilePart, UserContent } from "ai";
2
2
  import type { FetchFileContext, FetchFileResult } from "#channel/adapter.js";
3
- import { type SlackBotToken, type SlackThread } from "#public/channels/slack/api.js";
4
- import type { SlackAttachment, SlackMessage } from "#public/channels/slack/inbound.js";
3
+ import { type SlackBotToken, type SlackThread, type SlackThreadMessage } from "#public/channels/slack/api.js";
4
+ import { type SlackAttachment, type SlackMessage } from "#public/channels/slack/inbound.js";
5
5
  import { type SlackTransportOptions } from "#public/channels/slack/transport.js";
6
6
  import { type UploadPolicy } from "#public/channels/upload-policy.js";
7
7
  /**
8
- * Emits one {@link FilePart} per supported attachment in the inbound
9
- * message, with `data` set to a `URL` object pointing at the Slack
10
- * file. Audio, video, URL-less, and policy-violating attachments are
11
- * dropped so a single bad upload never blocks the text portion.
8
+ * Emits one {@link FilePart} per attachment in the inbound message, with
9
+ * `data` set to a `URL` object pointing at the Slack file. URL-less and
10
+ * policy-violating attachments are dropped so a single bad upload never
11
+ * blocks the text portion.
12
12
  *
13
13
  * The `URL` object in `data` is resolved by the channel's `fetchFile`
14
14
  * function at staging time inside the workflow step.
15
15
  */
16
16
  export declare function collectSlackFileParts(attachments: readonly SlackAttachment[] | undefined, policy: UploadPolicy): FilePart[];
17
17
  /**
18
- * Collects file parts for an inbound mention.
18
+ * Collects file parts for an inbound message.
19
19
  *
20
- * Prefers attachments on the triggering mention (the common case: user
21
- * uploads a file and mentions the bot in the same message). When the
22
- * mention has none, refreshes the thread via {@link SlackThread.refresh}
23
- * and picks the attachments of the latest message this app did not
24
- * author (the thread message `isMe` classification) — covering the case
25
- * where a user or another bot dropped a file in the thread first, then a
26
- * user mentioned the bot in a follow-up. Any error during refresh is logged
27
- * and treated as "no attachments" so the text portion of the mention
28
- * still gets delivered.
20
+ * Prefers attachments on the triggering message (the common case: a person
21
+ * uploads a file and mentions the app in the same message). When a mention
22
+ * carries none, refreshes the thread via {@link SlackThread.refresh} and
23
+ * collects, in thread order, the files of the messages between the previous
24
+ * mention of the app and the trigger, up to {@link THREAD_LOOKBACK_MESSAGES}
25
+ * messages back. When the cached replies don't reach the trigger, as in a
26
+ * thread past Slack's first page, the window ending at the trigger is
27
+ * fetched; a trigger neither returns gets no lookback.
28
+ * That earlier mention started its own turn, which collected the files before
29
+ * it. The app's own messages and remote files are skipped. A message that
30
+ * didn't mention the app gets no lookback: the app answers every message in
31
+ * that conversation, so each earlier message brought its own files.
29
32
  *
30
- * Skips the thread-history lookback when the policy disables uploads,
31
- * since the refresh can't surface anything we'd deliver.
33
+ * Any error during refresh is logged and treated as "no attachments" so the
34
+ * text portion of the message still gets delivered. Skips the lookback when
35
+ * the policy disables uploads, since the refresh can't surface anything we'd
36
+ * deliver.
32
37
  */
33
38
  export declare function collectInboundFileParts(input: {
34
- readonly mention: Pick<SlackMessage, "attachments">;
39
+ readonly mention: Pick<SlackMessage, "attachments" | "ts">;
35
40
  readonly thread: SlackThread;
36
41
  readonly policy: UploadPolicy;
42
+ /** Whether the triggering message mentions the app. */
43
+ readonly isMentioned: boolean;
44
+ readonly botUserId: string | undefined;
45
+ /** Fetches the `size` thread messages ending at `latest`, oldest first. */
46
+ readonly fetchThreadWindow?: (latest: string, size: number) => Promise<readonly SlackThreadMessage[] | undefined>;
37
47
  }): Promise<FilePart[]>;
38
48
  /**
39
49
  * Combines text + file parts into the {@link UserContent} shape the
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{DEFAULT_UPLOAD_POLICY,evaluateFilePart,formatUploadPolicyViolation,isUploadsDisabled}from"#public/channels/upload-policy.js";import{EveAttachmentError}from"#internal/attachments/errors.js";import{readLimitedBytes}from"#internal/attachments/limited-read.js";import{isConfiguredSlackFileUrl,resolveSlackTransportOptions}from"#public/channels/slack/transport.js";import{resolveSlackBotToken}from"#public/channels/slack/api.js";const log=createLogger(`slack.attachments`);function collectSlackFileParts(e,t){let n=[];for(let r of e??[]){let e=toSlackFilePart(r,n.length);if(e===null)continue;let i=evaluateFilePart(e,t);if(i!==null){log.warn(`dropped attachment — ${formatUploadPolicyViolation(i)}`,{name:r.name});continue}n.push(e)}return n}function toSlackFilePart(e,t){return e.type===`audio`||e.type===`video`?null:e.url?{type:`file`,mediaType:e.mimeType??`application/octet-stream`,filename:e.name??`attachment-${t}`,data:new URL(e.url)}:(log.warn(`dropped attachment — no url available`,{name:e.name}),null)}async function collectInboundFileParts(e){let t=collectSlackFileParts(e.mention.attachments,e.policy);if(t.length>0)return t;if(isUploadsDisabled(e.policy))return[];if(e.thread.recentMessages.length===0)try{await e.thread.refresh()}catch(e){return log.warn(`slack thread refresh failed for attachment collection`,{error:e}),[]}let n=e.thread.recentMessages;for(let t=n.length-1;t>=0;--t){let r=n[t];if(!r||r.isMe)continue;let i=r.raw,a=collectSlackFileParts(extractAttachmentsFromRaw(i?.files),e.policy);return a.length>0?a:[]}return[]}function extractAttachmentsFromRaw(e){return Array.isArray(e)?e.map(e=>{let t=typeof e.mimetype==`string`?e.mimetype:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:typeof e.url_private==`string`?e.url_private:void 0,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}):[]}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function buildSlackTurnMessage(e,t){return t.length===0?e:e.trim().length===0?[...t]:[{type:`text`,text:e},...t]}function createSlackFetchFile(e){let t=resolveSlackTransportOptions(e.api);return async(n,r)=>{if(!isConfiguredSlackFileUrl(t,n)&&!isSlackFileUrl(n))return null;let i=r?.state.installationTeamId,a=await resolveSlackBotToken(e.botToken,{teamId:typeof i==`string`?i:void 0}),o=await(t?.fetch??fetch)(n,{headers:{authorization:`Bearer ${a}`}});if(!o.ok)throw new EveAttachmentError({adapterKind:`slack`,kind:`resolver-threw`,message:`Slack file fetch returned HTTP ${o.status}.`});let s=o.headers.get(`content-type`)??void 0;if(s?.split(`;`,1)[0]?.trim().toLowerCase()===`text/html`)throw new EveAttachmentError({adapterKind:`slack`,kind:`resolver-threw`,message:`Slack returned an HTML sign-in page instead of file bytes. The bot token may be missing the files:read scope. Add the scope, reinstall the Slack app, and retry.`});return{bytes:await readLimitedBytes(o,e.maxBytes??DEFAULT_UPLOAD_POLICY.maxBytes,`slack`),mediaType:s}}}function isSlackFileUrl(e){let t=URL.parse(e);return t?.protocol===`https:`?t.hostname===`files.slack.com`||(t.hostname===`enterprise.slack.com`||t.hostname.endsWith(`.enterprise.slack.com`))&&t.pathname.startsWith(`/files/`):!1}export{buildSlackTurnMessage,collectInboundFileParts,collectSlackFileParts,createSlackFetchFile};
1
+ import{createLogger}from"#internal/logging.js";import{DEFAULT_UPLOAD_POLICY,evaluateFilePart,formatUploadPolicyViolation,isUploadsDisabled}from"#public/channels/upload-policy.js";import{EveAttachmentError}from"#internal/attachments/errors.js";import{readLimitedBytes}from"#internal/attachments/limited-read.js";import{isConfiguredSlackFileUrl,resolveSlackTransportOptions}from"#public/channels/slack/transport.js";import{THREAD_REFRESH_LIMIT,resolveSlackBotToken}from"#public/channels/slack/api.js";import{parseAttachments}from"#public/channels/slack/inbound.js";const log=createLogger(`slack.attachments`);function collectSlackFileParts(e,t){let n=[];for(let r of e??[]){let e=toSlackFilePart(r,n.length);if(e===null)continue;let i=evaluateFilePart(e,t);if(i!==null){log.warn(`dropped attachment — ${formatUploadPolicyViolation(i)}`,{name:r.name});continue}n.push(e)}return n}function toSlackFilePart(e,t){return e.url?{type:`file`,mediaType:e.mimeType??`application/octet-stream`,filename:e.name??`attachment-${t}`,data:new URL(e.url)}:(log.warn(`dropped attachment — no url available`,{name:e.name}),null)}async function collectInboundFileParts(e){let t=collectSlackFileParts(e.mention.attachments,e.policy);if(t.length>0||!e.isMentioned)return t;if(isUploadsDisabled(e.policy))return[];if(e.thread.recentMessages.length===0)try{await e.thread.refresh()}catch(e){return log.warn(`slack thread refresh failed for attachment collection`,{error:e}),[]}let n=await withTrigger(e),r=n.findIndex(t=>t.ts===e.mention.ts);if(r===-1)return[];let i=n.slice(Math.max(0,r-10),r),a=[],o=new Set;for(let t of i.toReversed()){if(t.isMe)continue;if(mentionsApp(t.text,e.botUserId))break;let n=t.raw,r=parseAttachments(n?.files).filter(e=>e.id===``||!o.has(e.id));for(let e of r)o.add(e.id);a.unshift(...r)}return collectSlackFileParts(a,e.policy)}async function withTrigger(e){let t=e.thread.recentMessages;if(t.some(t=>t.ts===e.mention.ts))return t;if(e.fetchThreadWindow===void 0||t.length<THREAD_REFRESH_LIMIT)return[];try{return await e.fetchThreadWindow(e.mention.ts,11)??[]}catch(e){return log.warn(`slack thread window fetch failed for attachment collection`,{error:e}),[]}}function mentionsApp(e,t){return t!==void 0&&(e.includes(`<@${t}>`)||e.includes(`<@${t}|`))}function buildSlackTurnMessage(e,t){return t.length===0?e:e.trim().length===0?[...t]:[{type:`text`,text:e},...t]}function createSlackFetchFile(e){let t=resolveSlackTransportOptions(e.api);return async(n,r)=>{if(!isConfiguredSlackFileUrl(t,n)&&!isSlackFileUrl(n))return null;let i=r?.state.installationTeamId,a=await resolveSlackBotToken(e.botToken,{teamId:typeof i==`string`?i:void 0}),o=await(t?.fetch??fetch)(n,{headers:{authorization:`Bearer ${a}`},signal:AbortSignal.timeout(3e4)});if(!o.ok)throw new EveAttachmentError({adapterKind:`slack`,kind:`resolver-threw`,message:`Slack file fetch returned HTTP ${o.status}.`});let s=o.headers.get(`content-type`)??void 0;if(s?.split(`;`,1)[0]?.trim().toLowerCase()===`text/html`)throw new EveAttachmentError({adapterKind:`slack`,kind:`resolver-threw`,message:`Slack returned an HTML sign-in page instead of file bytes. The bot token may be missing the files:read scope. Add the scope, reinstall the Slack app, and retry.`});return{bytes:await readLimitedBytes(o,e.maxBytes??DEFAULT_UPLOAD_POLICY.maxBytes,`slack`),mediaType:s}}}function isSlackFileUrl(e){let t=URL.parse(e);return t?.protocol===`https:`?t.hostname===`files.slack.com`||(t.hostname===`enterprise.slack.com`||t.hostname.endsWith(`.enterprise.slack.com`))&&t.pathname.startsWith(`/files/`):!1}export{buildSlackTurnMessage,collectInboundFileParts,collectSlackFileParts,createSlackFetchFile};
@@ -1,6 +1,6 @@
1
1
  import{actionLabel,visibleActions}from"#shared/action-label.js";import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{truncateTypingStatus}from"#public/channels/slack/limits.js";import{buildSlackAuthContext,slackUserIdForPrincipal}from"#public/channels/slack/auth.js";import{workingTaskNames}from"#channel/task-card.js";import{reviewingResults,waitingOnTasks,withMoreCalls}from"#public/channels/slack/action-status.js";import{buildAuthCompletedText,buildAuthEphemeralBlocks,buildAuthEphemeralText,buildAuthRequiredPublicText,formatConnectionDisplayName}from"#public/channels/slack/connections.js";import{approvalEvents}from"#public/channels/slack/approval-cards.js";import{deliverCompletedSlackReply}from"#public/channels/slack/reply-delivery.js";import{clearStatus,hideStatus,reasoningStatus,showStatus}from"#public/channels/slack/thread-status.js";const log=createLogger(`slack.defaults`),streamByState=new WeakMap;function stepStream(e,t,n){let r=streamByState.get(e);if(r?.turnId===t&&r.stepIndex===n)return r;let i={calls:null,reasoning:``,reasoningShownAtMs:null,replyChars:0,stepIndex:n,turnId:t};return streamByState.set(e,i),i}function heldWithin(e,t){return e!=null&&t-e>=0&&t-e<3e3}async function showReasoning(e,t,n){let r=reasoningStatus(t.reasoning,n);if(r===void 0||truncateTypingStatus(r)===e.state.threadStatus?.text)return;let i=Date.now();heldWithin(t.reasoningShownAtMs,i)||(await showStatus(e,r),t.reasoningShownAtMs=i)}function extractSemanticErrorSummary(e){if(typeof e.details!=`object`||e.details===null)return null;let t=e.details;if(typeof t.semanticErrorId!=`string`||t.semanticErrorId.length===0||typeof t.name!=`string`||t.name.length===0)return null;let n=typeof t.message==`string`&&t.message.trim().length>0?t.message.trim():e.message?.trim();if(!n)return null;let r=typeof t.hint==`string`&&t.hint.trim().length>0?t.hint.trim():void 0;return r===void 0?{message:n,name:t.name}:{hint:r,message:n,name:t.name}}function formatSemanticErrorBlock(e,t){return[`### ${e.name}`,``,e.message,...e.hint?[``,`**How to fix**`,e.hint]:[],...t?[``,`**Error id:**`,`\`${t}\``]:[]].flatMap(e=>e.split(`
2
2
  `)).map(e=>e.length>0?`> ${e}`:`> `).join(`
3
3
  `)}function formatSemanticErrorReply(e){return[`${e.introduction}.`,``,formatSemanticErrorBlock(e.summary,e.errorId),...e.summary.hint?[]:[``,e.nextStep],...e.summary.hint&&e.followUp?[``,e.followUp]:[]].join(`
4
- `)}function defaultSlackAuth(e,t){let n=e.author;return n?buildSlackAuthContext({channelId:t.slack.channelId,fullName:n.fullName,installationTeamId:e.installationTeamId,isBot:n.isBot,teamId:e.teamId,threadTs:t.slack.threadTs,userId:n.userId,userName:n.userName}):null}function defaultOnMessage(e,t){return{auth:defaultSlackAuth(t,e)}}async function defaultReceived(e,t){await t.thread.startTyping(`Thinking...`)}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}const defaultEvents={...approvalEvents,async"turn.waiting"(e,t,n){if(e.on===`input`){await hideStatus(t);return}let r=workingTaskNames(t.state.taskCards?.[e.turnId]?.turn);r.length>0&&await showStatus(t,waitingOnTasks(r))},async"task.settled"(e,t,n){if(e.cancel!==void 0)return;let r=t.state.pendingTaskResults,i=r?.turnId===e.turnId?r.names:[],a=e.kind===`agent`&&e.name!==void 0?e.name:null;t.state.pendingTaskResults={names:[...i,a],turnId:e.turnId}},async"step.started"(e,t,n){if(e.stepIndex===0)return;let r=t.state.pendingTaskResults;t.state.pendingTaskResults=null;let i=r?.turnId===e.turnId?reviewingResults(r.names):t.state.threadStatus?.text??`Thinking...`;await showStatus(t,i,{force:!0})},async"turn.started"(e,t,n){t.state.pendingTaskResults=null,t.state.pendingToolCallMessage=null,streamByState.delete(t.state),await showStatus(t,`Thinking...`,{force:!0})},async"turn.completed"(e,t,n){await clearStatus(t)},async"reasoning.appended"(e,t,n){let r=stepStream(t.state,e.turnId,e.stepIndex);r.reasoning===``&&(r.reasoningShownAtMs=null),r.reasoning+=e.reasoningDelta,await showReasoning(t,r)},async"reasoning.completed"(e,t,n){let r=stepStream(t.state,e.turnId,e.stepIndex);r.reasoning=e.reasoning,await showReasoning(t,r,{complete:!0}),r.reasoning=``},async"message.appended"(e,t,n){let r=stepStream(t.state,e.turnId,e.stepIndex),i=r.replyChars;r.replyChars+=e.messageDelta.length,i<280&&r.replyChars>=280&&await showStatus(t,`Writing a reply...`)},async"actions.requested"(e,t,n){let r=t.state.pendingToolCallMessage;t.state.pendingToolCallMessage=null;let i=visibleActions(e.actions);if(!r&&i.length===0)return;let a=stepStream(t.state,e.turnId,e.stepIndex),o=!r&&a.calls?{...a.calls,count:a.calls.count+i.length}:{count:i.length,label:r??actionLabel(i[0],e.presentation),narrated:r!=null};a.calls=o,await showStatus(t,o.narrated?o.label:withMoreCalls(o.label,o.count))},async"action.partial"(e,t,n){let r=e.presentation?.[e.result.callId]?.label;r&&!heldWithin(t.state.threadStatus?.atMs,Date.now())&&await showStatus(t,r)},async"action.result"(e,t,n){let r=e.presentation?.[e.result.callId]?.label;r&&await showStatus(t,r)},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 clearStatus(t);return}t.state.threadStatus=null,await deliverCompletedSlackReply(t,e.message,{turnId:e.turnId})},async"turn.failed"(e,t,n){let r=extractErrorId(e.details),i=extractSemanticErrorSummary(e);if(i!==null){await t.thread.post(formatSemanticErrorReply({errorId:r,introduction:`I hit an error while handling your request`,nextStep:`Please try again, rephrase, or reach out if it keeps failing.`,summary:i}));return}let a=formatErrorHint(e);await t.thread.post([`I hit an error while handling your request${a}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...r?[``,`_Error id: \`${r}\`_`]:[]].join(`
4
+ `)}function defaultSlackAuth(e,t){let n=e.author;return n?buildSlackAuthContext({channelId:t.slack.channelId,fullName:n.fullName,installationTeamId:e.installationTeamId,isBot:n.isBot,teamId:e.teamId,threadTs:t.slack.threadTs,userId:n.userId,userName:n.userName}):null}function defaultOnMessage(e,t){return{auth:defaultSlackAuth(t,e)}}async function defaultReceived(e,t){await t.thread.startTyping(`Thinking...`)}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}const defaultEvents={...approvalEvents,async"turn.waiting"(e,t,n){if(e.on===`input`){await hideStatus(t);return}let r=workingTaskNames(t.state.taskCards?.[e.turnId]?.turn);r.length>0&&await showStatus(t,waitingOnTasks(r))},async"task.settled"(e,t,n){if(e.cancel!==void 0)return;let r=t.state.pendingTaskResults,i=r?.turnId===e.turnId?r.names:[],a=e.kind===`agent`&&e.name!==void 0?e.name:null;t.state.pendingTaskResults={names:[...i,a],turnId:e.turnId}},async"step.started"(e,t,n){if(e.stepIndex===0)return;let r=t.state.pendingTaskResults;t.state.pendingTaskResults=null;let i=r?.turnId===e.turnId?reviewingResults(r.names):t.state.threadStatus?.text??`Thinking...`;await showStatus(t,i,{force:!0})},async"turn.started"(e,t,n){t.state.pendingTaskResults=null,t.state.pendingToolCallMessage=null,streamByState.delete(t.state),await showStatus(t,`Thinking...`,{force:!0})},async"turn.completed"(e,t,n){await clearStatus(t)},async"reasoning.appended"(e,t,n){let r=stepStream(t.state,e.turnId,e.stepIndex);r.reasoning===``&&(r.reasoningShownAtMs=null),r.reasoning+=e.reasoningDelta,await showReasoning(t,r)},async"reasoning.completed"(e,t,n){let r=stepStream(t.state,e.turnId,e.stepIndex);r.reasoning=e.reasoning,await showReasoning(t,r,{complete:!0}),r.reasoning=``},async"message.appended"(e,t,n){let r=stepStream(t.state,e.turnId,e.stepIndex),i=r.replyChars;r.replyChars+=e.messageDelta.length,i<280&&r.replyChars>=280&&await showStatus(t,`Writing a reply...`)},async"actions.requested"(e,t,n){let r=t.state.pendingToolCallMessage;t.state.pendingToolCallMessage=null;let i=visibleActions(e.actions);if(!r&&i.length===0)return;let a=stepStream(t.state,e.turnId,e.stepIndex),o=!r&&a.calls?{...a.calls,count:a.calls.count+i.length}:{count:i.length,label:r??actionLabel(i[0],e.presentation),narrated:r!=null};a.calls=o,await showStatus(t,o.narrated?o.label:withMoreCalls(o.label,o.count))},async"action.partial"(e,t,n){let r=e.presentation?.[e.result.callId]?.label;r&&!heldWithin(t.state.threadStatus?.atMs,Date.now())&&await showStatus(t,r)},async"action.result"(e,t,n){let r=e.presentation?.[e.result.callId]?.label;r&&await showStatus(t,r)},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 clearStatus(t);return}t.state.threadStatus=null,await deliverCompletedSlackReply(t,e.message,{turnId:e.turnId})},async"turn.failed"(e,t,n){if(e.terminal)return;let r=extractErrorId(e.details),i=extractSemanticErrorSummary(e);if(i!==null){await t.thread.post(formatSemanticErrorReply({errorId:r,introduction:`I hit an error while handling your request`,nextStep:`Please try again, rephrase, or reach out if it keeps failing.`,summary:i}));return}let a=formatErrorHint(e);await t.thread.post([`I hit an error while handling your request${a}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...r?[``,`_Error id: \`${r}\`_`]:[]].join(`
5
5
  `))},async"session.failed"(e,t){let n=extractErrorId(e.details),r=extractSemanticErrorSummary(e);if(r!==null){await t.thread.post(formatSemanticErrorReply({errorId:n,followUp:`Start a new thread to continue — I can't pick this one back up.`,introduction:`This session couldn't recover from an error`,nextStep:`Resolve the issue, then start a new thread — I can't pick this one back up.`,summary:r}));return}let i=formatErrorHint(e);await t.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.`,...n?[``,`_Error id: \`${n}\`_`]:[]].join(`
6
6
  `))},async"authorization.required"(e,t,n){let r=e.authorization?.displayName??formatConnectionDisplayName(e.name),i=slackUserIdForPrincipal(t.state,e.principalId)??null,a=e.authorization?.url,o=t.state.pendingAuthMessageTs??{};if(e.candidateId===void 0&&o[e.name]===void 0){let n=buildAuthRequiredPublicText({displayName:r,recipientUserId:i});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})}}let s=e.authorization?.instructions;if(i&&(a||s)){let{channelId:n,threadTs:o}=t.state,c={cancel:e.candidateId===void 0&&n&&o&&e.turnId?{channelId:n,threadTs:o,turnId:e.turnId}:void 0,displayName:r,instructions:s,url:a,userCode:e.authorization?.userCode};try{await t.thread.postEphemeral(i,{blocks:buildAuthEphemeralBlocks(c),text:buildAuthEphemeralText(c)})}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`&&e.candidateId===void 0&&await showStatus(t,`Connected to ${r}. Resuming...`,{force:!0});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,defaultOnMessage,defaultReceived,defaultSlackAuth};
@@ -154,6 +154,12 @@ export declare function slackEventInstallationTeamId(envelope: SlackEventCallbac
154
154
  export declare function parseMessageEvent(envelope: SlackEventCallback): SlackMessage | null;
155
155
  export declare function parseDirectMessageEvent(envelope: SlackEventCallback): SlackMessage | null;
156
156
  export declare function slackMessageFromWebhookPayload(payload: SlackAppMentionPayload | SlackDirectMessagePayload, installationTeamId?: string): SlackMessage | null;
157
+ /**
158
+ * Parses the `files` of a raw Slack message. A remote file, such as a Google
159
+ * Drive link, carries `mode: "external"` and a URL on the third party's host
160
+ * that the bot token can't open; its link stays in the message text.
161
+ */
162
+ export declare function parseAttachments(files: readonly Record<string, unknown>[] | undefined): SlackAttachment[];
157
163
  /**
158
164
  * Verified inbound identity used to attribute a model-visible Slack message.
159
165
  *
@@ -1 +1 @@
1
- import{isObject}from"#shared/guards.js";import{resolveSlackInboundMrkdwn}from"#public/channels/slack/inbound-content.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";function parseSlackEventEnvelope(e){let t=JSON.parse(e);if(!isObject(t)||t.type!==`event_callback`)return null;let n=t.event;return!isObject(n)||typeof n.type!=`string`||n.type.length===0?null:t}function parseAppMentionEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`app_mention`?null:buildSlackMessage(t,e)}function slackEventBotUserId(e){return e.authorizations?.find(e=>e.is_bot===!0&&typeof e.user_id==`string`)?.user_id}function slackEventReceivingBotUserId(e){let t=slackEventBotUserId(e);if(t!==void 0)return t;let n=e.event,r=typeof n?.text==`string`?n.text:void 0;if(n?.type===`app_mention`&&r!==void 0)return e.authorizations?.find(e=>e.is_bot===void 0&&typeof e.user_id==`string`&&r.includes(`<@${e.user_id}>`))?.user_id}function slackEventInstallationTeamId(e){let t=e.authorizations??[],n=t.find(e=>e.is_bot===!0&&typeof e.team_id==`string`);return n?.team_id===void 0?t.find(e=>typeof e.team_id==`string`)?.team_id:n.team_id}function parseMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;if(!t||t.type!==`message`)return null;let n=t;return isPostedMessageSubtype(n.subtype)?buildSlackMessage(n,e):null}const POSTED_MESSAGE_SUBTYPES=new Set([`bot_message`,`file_share`,`me_message`,`thread_broadcast`]);function isPostedMessageSubtype(e){return e===void 0||e===``||POSTED_MESSAGE_SUBTYPES.has(e)}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)}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,t){if(e.kind===`direct_message`&&(typeof e.subtype==`string`&&e.subtype.length>0&&e.subtype!==`file_share`||typeof e.botId==`string`&&e.botId.length>0)||!e.channelId||!e.ts)return null;let n=resolveSlackInboundMrkdwn(e.text,e.raw);return{text:n,markdown:slackMrkdwnToGfm(n),ts:e.ts,threadTs:e.threadTs,channelId:e.channelId,teamId:e.teamId,installationTeamId:t,author:parsePayloadAuthor(e),attachments:parsePayloadAttachments(e.files),raw:e.raw}}function buildSlackMessage(e,t){let n=typeof e.channel==`string`?e.channel:``,r=typeof e.ts==`string`?e.ts:``;if(!n||!r)return null;let i=typeof e.text==`string`?e.text:``,a=resolveSlackInboundMrkdwn(i,e),o=typeof e.thread_ts==`string`?e.thread_ts:r,s=typeof t.team_id==`string`?t.team_id:void 0,c=slackEventInstallationTeamId(t);return{text:a,markdown:slackMrkdwnToGfm(a),ts:r,threadTs:o,channelId:n,teamId:s,installationTeamId:c,author:parseAuthor(e),attachments:parseAttachments(e.files),raw:e}}function parseAuthor(e){let t=typeof e.user==`string`?e.user:``;if(t)return{userId:t,userName:typeof e.username==`string`?e.username:void 0,fullName:void 0,isBot:typeof e.bot_id==`string`&&e.bot_id.length>0,isMe:!1}}function parseAttachments(e){return Array.isArray(e)?e.map(toAttachment):[]}function toAttachment(e){let t=typeof e.mimetype==`string`?e.mimetype:void 0,n=typeof e.url_private==`string`?e.url_private:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:n,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}function toPayloadAttachment(e){return{id:e.id,type:e.type,url:e.url,name:e.name,mimeType:e.mimeType,size:e.size}}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function parsePayloadAuthor(e){if(!e.userId)return;let t=e.raw;return{userId:e.userId,userName:typeof t.username==`string`?t.username:void 0,fullName:void 0,isBot:typeof t.bot_id==`string`&&t.bot_id.length>0,isMe:!1}}function parsePayloadAttachments(e){return Array.isArray(e)?e.map(toPayloadAttachment):[]}export{parseAppMentionEvent,parseDirectMessageEvent,parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackEventReceivingBotUserId,slackMessageFromWebhookPayload};
1
+ import{isObject}from"#shared/guards.js";import{resolveSlackInboundMrkdwn}from"#public/channels/slack/inbound-content.js";import{slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";function parseSlackEventEnvelope(e){let t=JSON.parse(e);if(!isObject(t)||t.type!==`event_callback`)return null;let n=t.event;return!isObject(n)||typeof n.type!=`string`||n.type.length===0?null:t}function parseAppMentionEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;return!t||t.type!==`app_mention`?null:buildSlackMessage(t,e)}function slackEventBotUserId(e){return e.authorizations?.find(e=>e.is_bot===!0&&typeof e.user_id==`string`)?.user_id}function slackEventReceivingBotUserId(e){let t=slackEventBotUserId(e);if(t!==void 0)return t;let n=e.event,r=typeof n?.text==`string`?n.text:void 0;if(n?.type===`app_mention`&&r!==void 0)return e.authorizations?.find(e=>e.is_bot===void 0&&typeof e.user_id==`string`&&r.includes(`<@${e.user_id}>`))?.user_id}function slackEventInstallationTeamId(e){let t=e.authorizations??[],n=t.find(e=>e.is_bot===!0&&typeof e.team_id==`string`);return n?.team_id===void 0?t.find(e=>typeof e.team_id==`string`)?.team_id:n.team_id}function parseMessageEvent(e){if(e.type!==`event_callback`)return null;let t=e.event;if(!t||t.type!==`message`)return null;let n=t;return isPostedMessageSubtype(n.subtype)?buildSlackMessage(n,e):null}const POSTED_MESSAGE_SUBTYPES=new Set([`bot_message`,`file_share`,`me_message`,`thread_broadcast`]);function isPostedMessageSubtype(e){return e===void 0||e===``||POSTED_MESSAGE_SUBTYPES.has(e)}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)}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,t){if(e.kind===`direct_message`&&(typeof e.subtype==`string`&&e.subtype.length>0&&e.subtype!==`file_share`||typeof e.botId==`string`&&e.botId.length>0)||!e.channelId||!e.ts)return null;let n=resolveSlackInboundMrkdwn(e.text,e.raw);return{text:n,markdown:slackMrkdwnToGfm(n),ts:e.ts,threadTs:e.threadTs,channelId:e.channelId,teamId:e.teamId,installationTeamId:t,author:parsePayloadAuthor(e),attachments:parsePayloadAttachments(e.files),raw:e.raw}}function buildSlackMessage(e,t){let n=typeof e.channel==`string`?e.channel:``,r=typeof e.ts==`string`?e.ts:``;if(!n||!r)return null;let i=typeof e.text==`string`?e.text:``,a=resolveSlackInboundMrkdwn(i,e),o=typeof e.thread_ts==`string`?e.thread_ts:r,s=typeof t.team_id==`string`?t.team_id:void 0,c=slackEventInstallationTeamId(t);return{text:a,markdown:slackMrkdwnToGfm(a),ts:r,threadTs:o,channelId:n,teamId:s,installationTeamId:c,author:parseAuthor(e),attachments:parseAttachments(e.files),raw:e}}function parseAuthor(e){let t=typeof e.user==`string`?e.user:``;if(t)return{userId:t,userName:typeof e.username==`string`?e.username:void 0,fullName:void 0,isBot:typeof e.bot_id==`string`&&e.bot_id.length>0,isMe:!1}}function parseAttachments(e){return Array.isArray(e)?e.filter(e=>!isRemoteFile(e)).map(toAttachment):[]}function isRemoteFile(e){return e.mode===`external`||e.is_external===!0}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.filter(e=>!isRemoteFile(e.raw)).map(toPayloadAttachment):[]}export{parseAppMentionEvent,parseAttachments,parseDirectMessageEvent,parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackEventReceivingBotUserId,slackMessageFromWebhookPayload};
@@ -1,4 +1,10 @@
1
1
  import type { InteractionHandlerDeps, ParsedBlockActionsPayload } from "#public/channels/slack/interactions.js";
2
+ /** Retires an answered `card` in place: same single block, buttons replaced by the answer. */
3
+ export declare function answeredBlocksFromActionBlock(input: {
4
+ readonly answerLabel: string;
5
+ readonly block: unknown;
6
+ readonly userId: string;
7
+ }): unknown[] | undefined;
2
8
  export declare function updateAnsweredHitlCard(interaction: ParsedBlockActionsPayload, deps: InteractionHandlerDeps): Promise<void>;
3
9
  export declare function updateAnsweredFreeformCard(input: {
4
10
  readonly channelId: string;
@@ -1 +1 @@
1
- import{SLACK_CARD_SUBTEXT_MAX_LENGTH,truncateCardSubtext}from"#public/channels/slack/limits.js";import{buildAnsweredBlocks,isHitlAction}from"#public/channels/slack/hitl.js";import{callSlackApi}from"#public/channels/slack/api.js";function buildAnsweredHitlMessageBlocks(e){let t=e.messageBlocks.findIndex(t=>blockContainsActionId(t,e.actionId));if(t===-1)return buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:e.answerLabel,userId:e.userId});let n=e.messageBlocks[t],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 findPromptBlocks(e){let t=[];for(let n of e){if(!isObjectRecord(n))continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function blockContainsActionId(e,t){return isObjectRecord(e)?actionsContainActionId(e.elements,t)||actionsContainActionId(e.actions,t):!1}function actionsContainActionId(e,t){return Array.isArray(e)?e.some(e=>isObjectRecord(e)&&e.action_id===t):!1}function answeredBlocksFromActionBlock(e){if(!isObjectRecord(e.block)||e.block.type!==`card`)return;let{actions:t,subtext:n,...r}=e.block,i={...r,subtext:{type:`mrkdwn`,text:formatAnsweredCardSubtext(e),verbatim:!1}};return hasCardContent(i)?[i]:void 0}function formatAnsweredCardSubtext(e){let t=e.userId.length>0?` by <@${e.userId}>`:``,n=SLACK_CARD_SUBTEXT_MAX_LENGTH-20-1-t.length,r=truncateWithEllipsis(e.answerLabel,n);return truncateCardSubtext(`:white_check_mark: *${r}*${t}`)}function truncateWithEllipsis(e,t){if(t<=0)return``;if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function promptBlocksFromActionBlock(e){if(!isObjectRecord(e)||e.type!==`card`)return[];let{actions:t,...n}=e;return hasCardContent(n)?[n]:[]}function hasCardContent(e){return e.body!==void 0||e.title!==void 0||e.hero_image!==void 0}function isObjectRecord(e){return typeof e==`object`&&!!e}async function updateAnsweredHitlCard(e,t){let n=e.actions.find(e=>isHitlAction(e.actionId));if(!n?.messageTs)return;let r=n.label??n.selectedOptionValue??n.value;r&&await updateAnsweredCard({answerLabel:r,blocks:buildAnsweredHitlMessageBlocks({actionId:n.actionId,answerLabel:r,messageBlocks:e.messageBlocks,userId:n.user.id}),channelId:e.channelId,deps:t,installationTeamId:e.installationTeamId,messageTs:n.messageTs})}async function updateAnsweredFreeformCard(e){await updateAnsweredCard({...e,blocks:buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId})})}async function updateAnsweredCard(e){await callSlackApi({api:e.deps.api,botToken:e.deps.config.credentials?.botToken,context:{teamId:e.installationTeamId},operation:`chat.update`,body:{channel:e.channelId,ts:e.messageTs,blocks:e.blocks,text:`Answered: ${e.answerLabel}`}})}export{updateAnsweredFreeformCard,updateAnsweredHitlCard};
1
+ import{SLACK_CARD_SUBTEXT_MAX_LENGTH,truncateCardSubtext}from"#public/channels/slack/limits.js";import{buildAnsweredBlocks,isHitlAction}from"#public/channels/slack/hitl.js";import{callSlackApi}from"#public/channels/slack/api.js";function buildAnsweredHitlMessageBlocks(e){let t=e.messageBlocks.findIndex(t=>blockContainsActionId(t,e.actionId));if(t===-1)return buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:e.answerLabel,userId:e.userId});let n=e.messageBlocks[t],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 findPromptBlocks(e){let t=[];for(let n of e){if(!isObjectRecord(n))continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function blockContainsActionId(e,t){return isObjectRecord(e)?actionsContainActionId(e.elements,t)||actionsContainActionId(e.actions,t):!1}function actionsContainActionId(e,t){return Array.isArray(e)?e.some(e=>isObjectRecord(e)&&e.action_id===t):!1}function answeredBlocksFromActionBlock(e){if(!isObjectRecord(e.block)||e.block.type!==`card`)return;let{actions:t,subtext:n,...r}=e.block,i={...r,subtext:{type:`mrkdwn`,text:formatAnsweredCardSubtext(e),verbatim:!1}};return hasCardContent(i)?[i]:void 0}function formatAnsweredCardSubtext(e){let t=e.userId.length>0?` by <@${e.userId}>`:``,n=SLACK_CARD_SUBTEXT_MAX_LENGTH-20-1-t.length,r=truncateWithEllipsis(e.answerLabel,n);return truncateCardSubtext(`:white_check_mark: *${r}*${t}`)}function truncateWithEllipsis(e,t){if(t<=0)return``;if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function promptBlocksFromActionBlock(e){if(!isObjectRecord(e)||e.type!==`card`)return[];let{actions:t,...n}=e;return hasCardContent(n)?[n]:[]}function hasCardContent(e){return e.body!==void 0||e.title!==void 0||e.hero_image!==void 0}function isObjectRecord(e){return typeof e==`object`&&!!e}async function updateAnsweredHitlCard(e,t){let n=e.actions.find(e=>isHitlAction(e.actionId));if(!n?.messageTs)return;let r=n.label??n.selectedOptionValue??n.value;r&&await updateAnsweredCard({answerLabel:r,blocks:buildAnsweredHitlMessageBlocks({actionId:n.actionId,answerLabel:r,messageBlocks:e.messageBlocks,userId:n.user.id}),channelId:e.channelId,deps:t,installationTeamId:e.installationTeamId,messageTs:n.messageTs})}async function updateAnsweredFreeformCard(e){await updateAnsweredCard({...e,blocks:buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId})})}async function updateAnsweredCard(e){await callSlackApi({api:e.deps.api,botToken:e.deps.config.credentials?.botToken,context:{teamId:e.installationTeamId},operation:`chat.update`,body:{channel:e.channelId,ts:e.messageTs,blocks:e.blocks,text:`Answered: ${e.answerLabel}`}})}export{answeredBlocksFromActionBlock,updateAnsweredFreeformCard,updateAnsweredHitlCard};
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{parseInputResponse}from"#shared/input.js";import{readSlackTextObject}from"#public/channels/slack/inbound-content.js";import{isSlackResponseError}from"#public/channels/slack/transport.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildFreeformModalView,decodeFreeformHitlActionId,decodeHitlResponse,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{dispatchSignInCancel}from"#public/channels/slack/sign-in-cancel.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{updateAnsweredFreeformCard,updateAnsweredHitlCard}from"#public/channels/slack/interaction-cards.js";import{authorizeInputResponse}from"#public/channels/slack/input-response.js";import{bindSlackSessionOperations,withSlackResponder}from"#public/channels/slack/session-operations.js";import{dispatchSlashCommand}from"#public/channels/slack/slash-command.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=s.team_id??o?.id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,triggerId:typeof t.trigger_id==`string`?t.trigger_id:void 0,label:extractActionLabel(e),user:l})),channelId:r,installationTeamId:readInstallationTeamId(t),threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,triggerId:e.triggerId,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,installationTeamId:readInstallationTeamId(e.raw),threadTs:e.threadTs,teamId:e.user?.teamId??e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function 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=findPromptBlocks(e)[0],n=readSlackTextObject(t?.text);return n.length>0?n:void 0}function isObjectRecord(e){return typeof e==`object`&&!!e}function readInstallationTeamId(e){if(!isObjectRecord(e))return;let t=isObjectRecord(e.view)?e.view:void 0,n=isObjectRecord(e.team)?e.team:void 0,r=isObjectRecord(e.user)?e.user:void 0;return[t?.app_installed_team_id,e.app_installed_team_id,n?.id,r?.team_id,t?.team_id].find(e=>typeof e==`string`&&e.length>0)}async function handleInteractionPost(e,t,n){let r=new Response(`ok`,{status:200}),i;try{i=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),r}if(i.kind===`view_submission`)return handleViewSubmission(i,t,n);if(i.kind===`slash_command`)return dispatchSlashCommand(i,t,n),new Response(null,{status:200});if(i.kind===`unsupported`){let e=parseShortcutPayload(i.raw);return e===null?(log.warn(`unsupported Slack interaction payload ignored`,{type:i.type}),r):(dispatchShortcut(e,readInstallationTeamId(i.raw),t,n),new Response(null,{status:200}))}if(i.kind!==`block_actions`)return log.warn(`unsupported Slack interaction payload ignored`,{type:i.kind}),r;if(dispatchSignInCancel(i.raw,t))return r;let a=parseBlockActionsPayload(i);if(!a)return r;let o=a.actions.find(e=>isFreeformAction(e.actionId));if(o)return await openFreeformModal({payload:i.raw,interaction:a,freeformAction:o,deps:n}),r;let s=a.actions.flatMap(e=>{let t=decodeHitlResponse(e);return t===null?[]:[{action:e,derived:t}]});if(s.length>0){let e=s[0].action.user,r=s[0].derived.route;t.waitUntil(dispatchBlockInputResponses({ctx:t,deps:n,interaction:a,route:r,submission:{type:`block_actions`,actions:s.map(({action:e})=>e),inputResponses:s.map(({derived:e})=>e.response),messageTs:s[0].action.messageTs,user:e}}))}let c=n.config.onInteraction;if(c){let e=a.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let r=e[0].user,{thread:i,slack:o}=buildSlackBinding({api:n.api,botToken:n.config.credentials?.botToken,channelId:a.channelId,threadTs:a.threadTs,installationTeamId:a.installationTeamId,teamId:a.teamId}),s={...bindSlackSessionOperations({address:slackContinuationToken(a.channelId,a.threadTs),defaultAuth:buildSlackAuthContext({channelId:a.channelId,installationTeamId:a.installationTeamId,teamId:a.teamId,threadTs:a.threadTs,userId:r.id,userName:r.username??r.name}),from:t.from,resolveSession:t.resolveSession,state:{channelId:a.channelId,installationTeamId:a.installationTeamId??null,teamId:a.teamId??null,threadTs:a.threadTs,triggeringUserId:r.id}}),thread:i,slack:o};for(let n of e)t.waitUntil(Promise.resolve(c(n,s)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return r}function parseShortcutPayload(e){if(!isObjectRecord(e))return null;let t=e.type;if(t!==`message_action`&&t!==`shortcut`)return null;let n=readRequiredString(e.callback_id),r=readRequiredString(e.trigger_id),i=isObjectRecord(e.user)?e.user:void 0,a=readRequiredString(i?.id);if(n===null||r===null||a===null)return null;let o=isObjectRecord(e.team)?e.team:void 0,s=readOptionalString(i?.team_id)??readOptionalString(o?.id),c={id:a,username:readOptionalString(i?.username),name:readOptionalString(i?.name)};if(t===`shortcut`)return{type:t,callbackId:n,triggerId:r,user:c,teamId:s};let l=isObjectRecord(e.channel)?e.channel:void 0,u=isObjectRecord(e.message)?e.message:void 0,d=readRequiredString(l?.id),f=readRequiredString(u?.ts);return d===null||f===null||u===void 0?null:{type:t,callbackId:n,triggerId:r,user:c,teamId:s,channelId:d,message:{text:typeof u.text==`string`?u.text:``,ts:f,threadTs:readOptionalString(u.thread_ts),userId:readOptionalString(u.user)},responseUrl:readOptionalString(e.response_url)}}function readRequiredString(e){return typeof e==`string`&&e.length>0?e:null}function readOptionalString(e){return typeof e==`string`&&e.length>0?e:void 0}function dispatchShortcut(e,t,n,r){let i=r.config.onShortcut;if(i===void 0){log.warn(`Slack shortcut ignored because onShortcut is not configured`,{type:e.type});return}let a={slack:buildSlackWorkspaceHandle({api:r.api,botToken:r.config.credentials?.botToken,installationTeamId:t,teamId:e.teamId})};dispatchInteractionHook(()=>i(e,a),n,`shortcut handler failed`)}function dispatchInteractionHook(e,t,n){t.waitUntil(Promise.resolve().then(e).catch(e=>{log.error(n,{error:e})}))}async function dispatchBlockInputResponses(e){let t=e.route?.channelId??e.interaction.channelId,n=e.route?.threadTs??e.interaction.threadTs,r=await authorizeInputResponse({channelId:t,deps:e.deps,installationTeamId:e.interaction.installationTeamId,submission:e.submission,teamId:e.interaction.teamId,threadTs:n});if(r!==null){try{await e.ctx.from(slackContinuationToken(t,n)).respond(e.submission.inputResponses,{auth:r.auth,state:withSlackResponder(void 0,e.submission.user.id)})}catch(e){log.error(`HITL interaction delivery failed`,{error:e});return}if(!e.submission.actions.some(e=>decodeHitlResponse(e)?.kind===`tool-approval`))try{await updateAnsweredHitlCard(e.interaction,e.deps)}catch(e){log.error(`HITL answered-card update failed`,{error:e})}}}async function openFreeformModal(e){let t=e.payload.trigger_id;if(typeof t!=`string`||t.length===0){log.warn(`freeform button click missing trigger_id — cannot open modal`);return}let n=decodeFreeformHitlActionId(e.freeformAction.actionId);if(n===null){log.warn(`freeform button click carries invalid metadata`);return}let r=n.requestId;if(!r){log.warn(`freeform button click missing requestId`);return}let i=e.freeformAction.messageTs;if(!i){log.warn(`freeform button click missing messageTs`);return}let a=n?.route?.channelId??e.interaction.channelId,o=n?.route?.threadTs??e.interaction.threadTs,s={continuationToken:slackContinuationToken(a,o),channelId:a,installationTeamId:e.interaction.installationTeamId,messageChannelId:e.interaction.channelId,threadTs:o,messageTs:i,requestId:r},c=readPromptTextFromBlocks(e.interaction.messageBlocks),l=buildFreeformModalView({metadata:s,prompt:c}),u=await resolveSlackBotToken(e.deps.config.credentials?.botToken,{teamId:e.interaction.installationTeamId});try{await callSlackApi({api:e.deps.api,botToken:u,operation:`views.open`,body:{trigger_id:t,view:l}})}catch(e){if(!isSlackResponseError(e))throw e;log.error(`Slack views.open failed`,{error:e})}}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 o=e.user,s=e.userId,c=o?.teamId??e.teamId??null,l=i.installationTeamId??readInstallationTeamId(e.raw),u={type:`view_submission`,inputResponses:[parseInputResponse({requestId:i.requestId,text:a})],messageTs:i.messageTs,user:{id:s,username:o?.username,name:o?.name}};return t.waitUntil(dispatchViewInputResponse({ctx:t,deps:n,installationTeamId:l,metadata:i,submission:u,teamId:c,text:a})),r}async function dispatchViewInputResponse(e){let t=await authorizeInputResponse({channelId:e.metadata.channelId,deps:e.deps,installationTeamId:e.installationTeamId,submission:e.submission,teamId:e.teamId,threadTs:e.metadata.threadTs});if(t!==null){try{await e.ctx.from(e.metadata.continuationToken).respond(e.submission.inputResponses,{auth:t.auth,state:withSlackResponder(void 0,e.submission.user.id)})}catch(e){log.error(`freeform answer delivery failed`,{error:e});return}try{await updateAnsweredFreeformCard({channelId:e.metadata.messageChannelId??e.metadata.channelId,messageTs:e.metadata.messageTs,answerLabel:e.text,userId:e.submission.user.id,installationTeamId:e.installationTeamId,deps:e.deps})}catch(e){log.error(`freeform answered-card update failed`,{error:e})}}}export{handleInteractionPost,parseBlockActionsPayload,parseShortcutPayload};
1
+ import{createLogger}from"#internal/logging.js";import{parseInputResponse}from"#shared/input.js";import{readSlackTextObject}from"#public/channels/slack/inbound-content.js";import{isSlackResponseError}from"#public/channels/slack/transport.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildFreeformModalView,decodeFreeformHitlActionId,decodeHitlResponse,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{updateAnsweredFreeformCard,updateAnsweredHitlCard}from"#public/channels/slack/interaction-cards.js";import{buildSlackBinding,buildSlackWorkspaceHandle,callSlackApi,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{dispatchSignInCancel}from"#public/channels/slack/sign-in-cancel.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{authorizeInputResponse}from"#public/channels/slack/input-response.js";import{bindSlackSessionOperations,withSlackResponder}from"#public/channels/slack/session-operations.js";import{dispatchSlashCommand}from"#public/channels/slack/slash-command.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=s.team_id??o?.id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,triggerId:typeof t.trigger_id==`string`?t.trigger_id:void 0,label:extractActionLabel(e),user:l})),channelId:r,installationTeamId:readInstallationTeamId(t),threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,triggerId:e.triggerId,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,installationTeamId:readInstallationTeamId(e.raw),threadTs:e.threadTs,teamId:e.user?.teamId??e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function 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=findPromptBlocks(e)[0],n=readSlackTextObject(t?.text);return n.length>0?n:void 0}function isObjectRecord(e){return typeof e==`object`&&!!e}function readInstallationTeamId(e){if(!isObjectRecord(e))return;let t=isObjectRecord(e.view)?e.view:void 0,n=isObjectRecord(e.team)?e.team:void 0,r=isObjectRecord(e.user)?e.user:void 0;return[t?.app_installed_team_id,e.app_installed_team_id,n?.id,r?.team_id,t?.team_id].find(e=>typeof e==`string`&&e.length>0)}async function handleInteractionPost(e,t,n){let r=new Response(`ok`,{status:200}),i;try{i=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),r}if(i.kind===`view_submission`)return handleViewSubmission(i,t,n);if(i.kind===`slash_command`)return dispatchSlashCommand(i,t,n),new Response(null,{status:200});if(i.kind===`unsupported`){let e=parseShortcutPayload(i.raw);return e===null?(log.warn(`unsupported Slack interaction payload ignored`,{type:i.type}),r):(dispatchShortcut(e,readInstallationTeamId(i.raw),t,n),new Response(null,{status:200}))}if(i.kind!==`block_actions`)return log.warn(`unsupported Slack interaction payload ignored`,{type:i.kind}),r;if(dispatchSignInCancel(i.raw,t))return r;let a=parseBlockActionsPayload(i);if(!a)return r;let o=a.actions.find(e=>isFreeformAction(e.actionId));if(o)return await openFreeformModal({payload:i.raw,interaction:a,freeformAction:o,deps:n}),r;let s=a.actions.flatMap(e=>{let t=decodeHitlResponse(e);return t===null?[]:[{action:e,derived:t}]});if(s.length>0){let e=s[0].action.user,r=s[0].derived.route;t.waitUntil(dispatchBlockInputResponses({ctx:t,deps:n,interaction:a,route:r,submission:{type:`block_actions`,actions:s.map(({action:e})=>e),inputResponses:s.map(({derived:e})=>e.response),messageTs:s[0].action.messageTs,user:e}}))}let c=n.config.onInteraction;if(c){let e=a.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let r=e[0].user,{thread:i,slack:o}=buildSlackBinding({api:n.api,botToken:n.config.credentials?.botToken,channelId:a.channelId,threadTs:a.threadTs,installationTeamId:a.installationTeamId,teamId:a.teamId}),s={...bindSlackSessionOperations({address:slackContinuationToken(a.channelId,a.threadTs),defaultAuth:buildSlackAuthContext({channelId:a.channelId,installationTeamId:a.installationTeamId,teamId:a.teamId,threadTs:a.threadTs,userId:r.id,userName:r.username??r.name}),from:t.from,resolveSession:t.resolveSession,state:{channelId:a.channelId,installationTeamId:a.installationTeamId??null,teamId:a.teamId??null,threadTs:a.threadTs,triggeringUserId:r.id}}),thread:i,slack:o};for(let n of e)t.waitUntil(Promise.resolve(c(n,s)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return r}function parseShortcutPayload(e){if(!isObjectRecord(e))return null;let t=e.type;if(t!==`message_action`&&t!==`shortcut`)return null;let n=readRequiredString(e.callback_id),r=readRequiredString(e.trigger_id),i=isObjectRecord(e.user)?e.user:void 0,a=readRequiredString(i?.id);if(n===null||r===null||a===null)return null;let o=isObjectRecord(e.team)?e.team:void 0,s=readOptionalString(i?.team_id)??readOptionalString(o?.id),c={id:a,username:readOptionalString(i?.username),name:readOptionalString(i?.name)};if(t===`shortcut`)return{type:t,callbackId:n,triggerId:r,user:c,teamId:s};let l=isObjectRecord(e.channel)?e.channel:void 0,u=isObjectRecord(e.message)?e.message:void 0,d=readRequiredString(l?.id),f=readRequiredString(u?.ts);return d===null||f===null||u===void 0?null:{type:t,callbackId:n,triggerId:r,user:c,teamId:s,channelId:d,message:{text:typeof u.text==`string`?u.text:``,ts:f,threadTs:readOptionalString(u.thread_ts),userId:readOptionalString(u.user)},responseUrl:readOptionalString(e.response_url)}}function readRequiredString(e){return typeof e==`string`&&e.length>0?e:null}function readOptionalString(e){return typeof e==`string`&&e.length>0?e:void 0}function dispatchShortcut(e,t,n,r){let i=r.config.onShortcut;if(i===void 0){log.warn(`Slack shortcut ignored because onShortcut is not configured`,{type:e.type});return}let a={slack:buildSlackWorkspaceHandle({api:r.api,botToken:r.config.credentials?.botToken,installationTeamId:t,teamId:e.teamId})};dispatchInteractionHook(()=>i(e,a),n,`shortcut handler failed`)}function dispatchInteractionHook(e,t,n){t.waitUntil(Promise.resolve().then(e).catch(e=>{log.error(n,{error:e})}))}async function dispatchBlockInputResponses(e){let t=e.route?.channelId??e.interaction.channelId,n=e.route?.threadTs??e.interaction.threadTs,r=await authorizeInputResponse({channelId:t,deps:e.deps,installationTeamId:e.interaction.installationTeamId,submission:e.submission,teamId:e.interaction.teamId,threadTs:n});if(r!==null){try{await e.ctx.from(slackContinuationToken(t,n)).respond(e.submission.inputResponses,{auth:r.auth,state:withSlackResponder(void 0,e.submission.user.id)})}catch(e){log.error(`HITL interaction delivery failed`,{error:e});return}if(!e.submission.actions.some(e=>decodeHitlResponse(e)?.kind===`tool-approval`))try{await updateAnsweredHitlCard(e.interaction,e.deps)}catch(e){log.error(`HITL answered-card update failed`,{error:e})}}}async function openFreeformModal(e){let t=e.payload.trigger_id;if(typeof t!=`string`||t.length===0){log.warn(`freeform button click missing trigger_id — cannot open modal`);return}let n=decodeFreeformHitlActionId(e.freeformAction.actionId);if(n===null){log.warn(`freeform button click carries invalid metadata`);return}let r=n.requestId;if(!r){log.warn(`freeform button click missing requestId`);return}let i=e.freeformAction.messageTs;if(!i){log.warn(`freeform button click missing messageTs`);return}let a=n?.route?.channelId??e.interaction.channelId,o=n?.route?.threadTs??e.interaction.threadTs,s={continuationToken:slackContinuationToken(a,o),channelId:a,installationTeamId:e.interaction.installationTeamId,messageChannelId:e.interaction.channelId,threadTs:o,messageTs:i,requestId:r},c=readPromptTextFromBlocks(e.interaction.messageBlocks),l=buildFreeformModalView({metadata:s,prompt:c}),u=await resolveSlackBotToken(e.deps.config.credentials?.botToken,{teamId:e.interaction.installationTeamId});try{await callSlackApi({api:e.deps.api,botToken:u,operation:`views.open`,body:{trigger_id:t,view:l}})}catch(e){if(!isSlackResponseError(e))throw e;log.error(`Slack views.open failed`,{error:e})}}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 o=e.user,s=e.userId,c=o?.teamId??e.teamId??null,l=i.installationTeamId??readInstallationTeamId(e.raw),u={type:`view_submission`,inputResponses:[parseInputResponse({requestId:i.requestId,text:a})],messageTs:i.messageTs,user:{id:s,username:o?.username,name:o?.name}};return t.waitUntil(dispatchViewInputResponse({ctx:t,deps:n,installationTeamId:l,metadata:i,submission:u,teamId:c,text:a})),r}async function dispatchViewInputResponse(e){let t=await authorizeInputResponse({channelId:e.metadata.channelId,deps:e.deps,installationTeamId:e.installationTeamId,submission:e.submission,teamId:e.teamId,threadTs:e.metadata.threadTs});if(t!==null){try{await e.ctx.from(e.metadata.continuationToken).respond(e.submission.inputResponses,{auth:t.auth,state:withSlackResponder(void 0,e.submission.user.id)})}catch(e){log.error(`freeform answer delivery failed`,{error:e});return}try{await updateAnsweredFreeformCard({channelId:e.metadata.messageChannelId??e.metadata.channelId,messageTs:e.metadata.messageTs,answerLabel:e.text,userId:e.submission.user.id,installationTeamId:e.installationTeamId,deps:e.deps})}catch(e){log.error(`freeform answered-card update failed`,{error:e})}}}export{handleInteractionPost,parseBlockActionsPayload,parseShortcutPayload};