eve 0.21.1 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/src/channel/adapter.d.ts +3 -7
  3. package/dist/src/channel/types.d.ts +18 -1
  4. package/dist/src/compiler/manifest.d.ts +16 -26
  5. package/dist/src/compiler/manifest.js +1 -1
  6. package/dist/src/compiler/normalize-agent-config.js +1 -1
  7. package/dist/src/compiler/normalize-hook.d.ts +2 -2
  8. package/dist/src/compiler/normalize-instructions.d.ts +3 -3
  9. package/dist/src/compiler/normalize-instructions.js +1 -1
  10. package/dist/src/compiler/normalize-skill.js +1 -1
  11. package/dist/src/compiler/normalize-tool.d.ts +2 -2
  12. package/dist/src/context/container.d.ts +2 -0
  13. package/dist/src/context/dynamic-model-lifecycle.d.ts +19 -0
  14. package/dist/src/context/dynamic-model-lifecycle.js +1 -0
  15. package/dist/src/context/keys.d.ts +13 -1
  16. package/dist/src/context/keys.js +1 -1
  17. package/dist/src/discover/manifest.d.ts +8 -0
  18. package/dist/src/evals/cli/eval.js +2 -2
  19. package/dist/src/evals/reporters/index.d.ts +1 -0
  20. package/dist/src/evals/reporters/index.js +1 -1
  21. package/dist/src/evals/runner/reporters/console.d.ts +10 -11
  22. package/dist/src/evals/runner/reporters/console.js +1 -1
  23. package/dist/src/execution/node-step.js +1 -1
  24. package/dist/src/execution/reconcile-session-continuation-token.d.ts +4 -0
  25. package/dist/src/execution/reconcile-session-continuation-token.js +1 -0
  26. package/dist/src/execution/sandbox/bindings/microsandbox-options.d.ts +2 -2
  27. package/dist/src/execution/sandbox/bindings/microsandbox.d.ts +2 -2
  28. package/dist/src/execution/sandbox/bindings/vercel-credentials.js +1 -1
  29. package/dist/src/execution/sandbox/ensure.js +1 -1
  30. package/dist/src/execution/session.js +2 -2
  31. package/dist/src/execution/subagent-adapter.js +1 -1
  32. package/dist/src/execution/subagent-event-proxy-step.d.ts +23 -0
  33. package/dist/src/execution/subagent-event-proxy-step.js +1 -0
  34. package/dist/src/execution/turn-workflow.js +1 -1
  35. package/dist/src/execution/workflow-steps.d.ts +1 -22
  36. package/dist/src/execution/workflow-steps.js +1 -1
  37. package/dist/src/harness/emission.d.ts +1 -0
  38. package/dist/src/harness/emission.js +1 -1
  39. package/dist/src/harness/input-extraction.d.ts +1 -0
  40. package/dist/src/harness/input-extraction.js +1 -1
  41. package/dist/src/harness/input-requests.js +1 -1
  42. package/dist/src/harness/step-hooks.d.ts +4 -1
  43. package/dist/src/harness/step-hooks.js +1 -1
  44. package/dist/src/harness/tool-call-input-errors.d.ts +8 -0
  45. package/dist/src/harness/tool-call-input-errors.js +1 -0
  46. package/dist/src/harness/tool-loop.js +1 -1
  47. package/dist/src/harness/types.d.ts +9 -0
  48. package/dist/src/internal/application/package.js +1 -1
  49. package/dist/src/internal/authored-definition/core.js +1 -1
  50. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  51. package/dist/src/internal/nitro/host/types.d.ts +2 -2
  52. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.d.ts +2 -2
  53. package/dist/src/internal/nitro/routes/agent-info/load-agent-info-data.d.ts +4 -4
  54. package/dist/src/internal/runtime-model.d.ts +1 -1
  55. package/dist/src/protocol/message.d.ts +1 -0
  56. package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +1 -1
  57. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
  58. package/dist/src/public/channels/discord/discordChannel.d.ts +2 -2
  59. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  60. package/dist/src/public/channels/eve.d.ts +1 -1
  61. package/dist/src/public/channels/eve.js +1 -1
  62. package/dist/src/public/channels/github/dispatch.d.ts +1 -1
  63. package/dist/src/public/channels/github/githubChannel.d.ts +2 -2
  64. package/dist/src/public/channels/github/githubChannel.js +1 -1
  65. package/dist/src/public/channels/index.d.ts +2 -2
  66. package/dist/src/public/channels/index.js +1 -1
  67. package/dist/src/public/channels/linear/linearChannel.d.ts +1 -1
  68. package/dist/src/public/channels/linear/linearChannel.js +1 -1
  69. package/dist/src/public/channels/slack/hitl.d.ts +3 -2
  70. package/dist/src/public/channels/slack/hitl.js +3 -3
  71. package/dist/src/public/channels/slack/index.d.ts +1 -1
  72. package/dist/src/public/channels/slack/index.js +1 -1
  73. package/dist/src/public/channels/slack/interactions.d.ts +1 -1
  74. package/dist/src/public/channels/slack/interactions.js +1 -1
  75. package/dist/src/public/channels/slack/limits.d.ts +18 -0
  76. package/dist/src/public/channels/slack/limits.js +1 -1
  77. package/dist/src/public/channels/slack/slackChannel.d.ts +2 -2
  78. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  79. package/dist/src/public/channels/teams/teamsChannel.d.ts +2 -2
  80. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  81. package/dist/src/public/channels/telegram/telegramChannel.d.ts +2 -2
  82. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  83. package/dist/src/public/channels/twilio/twilioChannel.d.ts +2 -2
  84. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  85. package/dist/src/public/definitions/agent.d.ts +1 -1
  86. package/dist/src/public/definitions/channel.d.ts +99 -0
  87. package/dist/src/public/definitions/channel.js +1 -1
  88. package/dist/src/public/definitions/hook.d.ts +3 -10
  89. package/dist/src/public/definitions/instructions.d.ts +2 -18
  90. package/dist/src/public/definitions/schedule.d.ts +4 -17
  91. package/dist/src/public/definitions/tool.d.ts +10 -4
  92. package/dist/src/public/definitions/tool.js +1 -1
  93. package/dist/src/public/hooks/index.d.ts +1 -1
  94. package/dist/src/public/hooks/index.js +1 -1
  95. package/dist/src/public/index.d.ts +2 -0
  96. package/dist/src/public/index.js +1 -1
  97. package/dist/src/public/sandbox/backends/default.d.ts +2 -2
  98. package/dist/src/public/sandbox/backends/microsandbox.d.ts +2 -2
  99. package/dist/src/public/sandbox/microsandbox-sandbox.d.ts +1 -1
  100. package/dist/src/public/sandbox/microsandbox.d.ts +1 -1
  101. package/dist/src/runtime/agent/bootstrap.d.ts +8 -0
  102. package/dist/src/runtime/agent/bootstrap.js +1 -1
  103. package/dist/src/runtime/agent/resolve-model.d.ts +16 -1
  104. package/dist/src/runtime/agent/resolve-model.js +1 -1
  105. package/dist/src/runtime/hooks/registry.d.ts +2 -1
  106. package/dist/src/runtime/prompt/compose.d.ts +2 -2
  107. package/dist/src/runtime/resolve-agent.js +1 -1
  108. package/dist/src/runtime/sandbox/keys.d.ts +4 -0
  109. package/dist/src/runtime/sandbox/keys.js +1 -1
  110. package/dist/src/runtime/sandbox/template-plan.d.ts +0 -2
  111. package/dist/src/runtime/schedules/register.d.ts +2 -2
  112. package/dist/src/runtime/schedules/resolve-schedule.d.ts +4 -4
  113. package/dist/src/runtime/types.d.ts +7 -4
  114. package/dist/src/setup/scaffold/create/project.js +1 -1
  115. package/dist/src/shared/agent-definition.d.ts +30 -7
  116. package/dist/src/shared/agent-definition.js +1 -1
  117. package/dist/src/shared/channel-definition.d.ts +92 -0
  118. package/dist/src/shared/channel-definition.js +1 -0
  119. package/dist/src/shared/dynamic-tool-definition.d.ts +18 -8
  120. package/dist/src/shared/dynamic-tool-definition.js +1 -1
  121. package/dist/src/shared/hook-definition.d.ts +36 -0
  122. package/dist/src/shared/hook-definition.js +1 -0
  123. package/dist/src/shared/instructions-definition.d.ts +26 -0
  124. package/dist/src/shared/instructions-definition.js +1 -0
  125. package/dist/src/shared/schedule-definition.d.ts +34 -0
  126. package/dist/src/shared/schedule-definition.js +1 -0
  127. package/dist/src/shared/vercel-project.d.ts +25 -0
  128. package/dist/src/shared/vercel-project.js +1 -0
  129. package/dist/src/source-change/apply-model-name.js +1 -1
  130. package/docs/agent-config.md +44 -0
  131. package/docs/channels/slack.mdx +27 -0
  132. package/docs/connections/overview.mdx +2 -0
  133. package/docs/guides/dynamic-capabilities.md +18 -2
  134. package/docs/sandbox.mdx +3 -1
  135. package/docs/subagents.mdx +1 -1
  136. package/package.json +1 -1
  137. package/dist/src/public/definitions/defineChannel.d.ts +0 -157
  138. package/dist/src/public/definitions/defineChannel.js +0 -1
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{buildSlackBinding,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,freeformRequestIdFromActionId,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=o?.id??s.team_id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,threadTs:e.threadTs,teamId:e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlock(e){return findPromptBlocks(e)[0]}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlock(e)?.text?.text;return typeof t==`string`&&t.length>0?t:void 0}async function handleInteractionPost(e,n,a){let o=new Response(`ok`,{status:200}),s;try{s=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),o}if(s.kind===`view_submission`)return handleViewSubmission(s,n,a);if(s.kind!==`block_actions`)return o;let c=parseBlockActionsPayload(s);if(!c)return o;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:s.raw,interaction:c,freeformAction:l,deps:a}),o;let d=slackContinuationToken(c.channelId,c.threadTs),p=c.actions.map(deriveHitlResponse).filter(e=>e!==null);if(p.length>0){let e=c.actions[0]?.user;if(!e)return o;n.waitUntil(n.send({inputResponses:p},{auth:buildSlackAuthContext({channelId:c.channelId,teamId:c.teamId,threadTs:c.threadTs,userId:e.id,userName:e.username??e.name}),continuationToken:d,state:{channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId??null,triggeringUserId:e.id}}).catch(e=>{log.error(`HITL interaction delivery failed`,{error:e})})),n.waitUntil(updateAnsweredHitlCard(c,a).catch(e=>{log.error(`HITL answered-card update failed`,{error:e})}))}let m=a.config.onInteraction;if(m){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let{thread:r,slack:i}=buildSlackBinding({botToken:a.config.credentials?.botToken,channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId}),o={thread:r,slack:i};for(let t of e)n.waitUntil(Promise.resolve(m(t,o)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return o}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 i=freeformRequestIdFromActionId(e.freeformAction.actionId)??e.freeformAction.value;if(!i){log.warn(`freeform button click missing requestId`);return}let a=e.freeformAction.messageTs;if(!a){log.warn(`freeform button click missing messageTs`);return}let o=buildFreeformModalView({metadata:{continuationToken:slackContinuationToken(e.interaction.channelId,e.interaction.threadTs),channelId:e.interaction.channelId,threadTs:e.interaction.threadTs,messageTs:a,requestId:i},prompt:readPromptTextFromBlocks(e.interaction.messageBlocks)}),s=await resolveSlackBotToken(e.deps.config.credentials?.botToken),c=await fetch(`https://slack.com/api/views.open`,{method:`POST`,headers:{authorization:`Bearer ${s}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({trigger_id:t,view:o})});c.ok||log.error(`Slack views.open returned non-2xx`,{status:c.status})}async function handleViewSubmission(e,t,n){let r=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return r;let c;try{c=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),r}if(!c.continuationToken||!c.requestId||!c.messageTs||!c.channelId||!c.threadTs)return r;let l=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(l.length===0)return r;let u=e.user,d=e.userId,f=u?.teamId??e.teamId??null;return t.waitUntil(t.send({inputResponses:[{requestId:c.requestId,text:l}]},{auth:buildSlackAuthContext({channelId:c.channelId,teamId:f,threadTs:c.threadTs,userId:d,userName:u?.username??u?.name}),continuationToken:c.continuationToken,state:{channelId:c.channelId,threadTs:c.threadTs,teamId:f,triggeringUserId:d}}).catch(e=>{log.error(`freeform answer delivery failed`,{error:e})})),t.waitUntil(updateAnsweredFreeformCard({channelId:c.channelId,messageTs:c.messageTs,answerLabel:l,userId:d??void 0,deps:n}).catch(e=>{log.error(`freeform answered-card update failed`,{error:e})})),r}async function updateAnsweredHitlCard(e,t){let r=e.actions.find(e=>isHitlAction(e.actionId));if(!r||!r.messageTs)return;let i=r.label??r.selectedOptionValue??r.value;if(!i)return;let a=buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:i,userId:r.user.id}),o=await resolveSlackBotToken(t.config.credentials?.botToken),s=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${o}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:r.messageTs,blocks:a,text:`Answered: ${i}`})});if(!s.ok)throw Error(`Slack chat.update returned HTTP ${s.status}`)}async function updateAnsweredFreeformCard(e){let t=buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId}),r=await resolveSlackBotToken(e.deps.config.credentials?.botToken),i=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${r}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:e.messageTs,blocks:t,text:`Answered: ${e.answerLabel}`})});if(!i.ok)throw Error(`Slack chat.update returned HTTP ${i.status}`)}export{handleInteractionPost,parseBlockActionsPayload};
1
+ import{createLogger}from"#internal/logging.js";import{SLACK_CARD_SUBTEXT_MAX_LENGTH,truncateCardSubtext}from"#public/channels/slack/limits.js";import{buildSlackBinding,resolveSlackBotToken,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext}from"#public/channels/slack/auth.js";import{HITL_FREEFORM_MODAL_ACTION_ID,HITL_FREEFORM_MODAL_BLOCK_ID,HITL_FREEFORM_MODAL_CALLBACK_ID,buildAnsweredBlocks,buildFreeformModalView,deriveHitlResponse,freeformRequestIdFromActionId,isFreeformAction,isHitlAction}from"#public/channels/slack/hitl.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";const log=createLogger(`slack.interactions`);function parseBlockActionsPayload(e){if(isSharedBlockActionsPayload(e))return parseSharedBlockActionsPayload(e);let t=e,n=t.actions;if(!Array.isArray(n))return null;let r=t.channel?.id,i=t.message,a=i?.thread_ts??i?.ts;if(!r||!a)return null;let o=t.team,s=t.user,c=o?.id??s.team_id,l={id:s.id,username:s.username,name:s.name},u=i?.blocks??[];return{actions:n.map(e=>({actionId:String(e.action_id??``),value:e.value==null?void 0:String(e.value),blockId:e.block_id==null?void 0:String(e.block_id),selectedOptionValue:extractSelectedOptionValue(e),messageTs:i?.ts,label:extractActionLabel(e),user:l})),channelId:r,threadTs:a,teamId:c,messageBlocks:u}}function isSharedBlockActionsPayload(e){return e.kind===`block_actions`&&Array.isArray(e.actions)}function parseSharedBlockActionsPayload(e){return!e.channelId||!e.threadTs?null:{actions:e.actions.map(t=>({actionId:t.actionId,value:t.value,blockId:t.blockId,selectedOptionValue:t.selectedOptionValue,messageTs:e.messageTs,label:t.label,user:{id:t.user?.id??e.userId,username:t.user?.username,name:t.user?.name}})),channelId:e.channelId,threadTs:e.threadTs,teamId:e.teamId,messageBlocks:e.messageBlocks??[]}}function extractSelectedOptionValue(e){let t=e.selected_option;return typeof t?.value==`string`?t.value:void 0}function extractActionLabel(e){let t=e.selected_option?.text?.text;if(typeof t==`string`&&t.length>0)return t;let n=e.text?.text;if(typeof n==`string`&&n.length>0)return n}function findPromptBlock(e){return findPromptBlocks(e)[0]}function findPromptBlocks(e){let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n.type;if(e===`actions`)break;(e===`section`||e===`context`||e===`divider`||e===`image`)&&t.push(n)}return t}function readPromptTextFromBlocks(e){let t=findPromptBlock(e)?.text?.text;return typeof t==`string`&&t.length>0?t:void 0}function buildAnsweredHitlMessageBlocks(e){let t=findActionBlockIndex(e.messageBlocks,e.actionId);if(t===-1)return buildAnsweredBlocks({promptBlocks:findPromptBlocks(e.messageBlocks),answerLabel:e.answerLabel,userId:e.userId});let n=e.messageBlocks[t],r=answeredBlocksFromActionBlock({answerLabel:e.answerLabel,block:n,userId:e.userId})??buildAnsweredBlocks({promptBlocks:promptBlocksFromActionBlock(n),answerLabel:e.answerLabel,userId:e.userId});return[...e.messageBlocks.slice(0,t),...r,...e.messageBlocks.slice(t+1)]}function findActionBlockIndex(e,t){return e.findIndex(e=>blockContainsActionId(e,t))}function blockContainsActionId(e,t){return isObjectRecord(e)?actionsContainActionId(e.elements,t)||actionsContainActionId(e.actions,t):!1}function actionsContainActionId(e,t){return Array.isArray(e)?e.some(e=>isObjectRecord(e)&&e.action_id===t):!1}function isObjectRecord(e){return typeof e==`object`&&!!e}function answeredBlocksFromActionBlock(e){if(!isObjectRecord(e.block)||e.block.type!==`card`)return;let{actions:t,subtext:n,...r}=e.block,i={...r,subtext:{type:`mrkdwn`,text:formatAnsweredCardSubtext(e),verbatim:!1}};return hasCardContent(i)?[i]:void 0}function formatAnsweredCardSubtext(e){let r=e.userId.length>0?` by <@${e.userId}>`:``,i=SLACK_CARD_SUBTEXT_MAX_LENGTH-20-1-r.length;return truncateCardSubtext(`:white_check_mark: *${truncateWithEllipsis(e.answerLabel,i)}*${r}`)}function truncateWithEllipsis(e,t){if(t<=0)return``;if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function promptBlocksFromActionBlock(e){if(!isObjectRecord(e)||e.type!==`card`)return[];let{actions:t,...n}=e;return hasCardContent(n)?[n]:[]}function hasCardContent(e){return e.body!==void 0||e.title!==void 0||e.hero_image!==void 0}async function handleInteractionPost(e,t,n){let i=new Response(`ok`,{status:200}),s;try{s=parseSlackWebhookBody(e,{contentType:`application/x-www-form-urlencoded`})}catch{return log.warn(`failed to parse Slack interaction payload`),i}if(s.kind===`view_submission`)return handleViewSubmission(s,t,n);if(s.kind!==`block_actions`)return i;let c=parseBlockActionsPayload(s);if(!c)return i;let l=c.actions.find(e=>isFreeformAction(e.actionId));if(l)return await openFreeformModal({payload:s.raw,interaction:c,freeformAction:l,deps:n}),i;let u=slackContinuationToken(c.channelId,c.threadTs),d=c.actions.map(deriveHitlResponse).filter(e=>e!==null);if(d.length>0){let e=c.actions[0]?.user;if(!e)return i;t.waitUntil(t.send({inputResponses:d},{auth:buildSlackAuthContext({channelId:c.channelId,teamId:c.teamId,threadTs:c.threadTs,userId:e.id,userName:e.username??e.name}),continuationToken:u,state:{channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId??null,triggeringUserId:e.id}}).catch(e=>{log.error(`HITL interaction delivery failed`,{error:e})})),t.waitUntil(updateAnsweredHitlCard(c,n).catch(e=>{log.error(`HITL answered-card update failed`,{error:e})}))}let p=n.config.onInteraction;if(p){let e=c.actions.filter(e=>!isHitlAction(e.actionId));if(e.length>0){let{thread:i,slack:a}=buildSlackBinding({botToken:n.config.credentials?.botToken,channelId:c.channelId,threadTs:c.threadTs,teamId:c.teamId}),o={thread:i,slack:a};for(let n of e)t.waitUntil(Promise.resolve(p(n,o)).catch(e=>{log.error(`custom interaction handler failed`,{error:e})}))}}return i}async function openFreeformModal(e){let t=e.payload.trigger_id;if(typeof t!=`string`||t.length===0){log.warn(`freeform button click missing trigger_id — cannot open modal`);return}let n=freeformRequestIdFromActionId(e.freeformAction.actionId)??e.freeformAction.value;if(!n){log.warn(`freeform button click missing requestId`);return}let r=e.freeformAction.messageTs;if(!r){log.warn(`freeform button click missing messageTs`);return}let o=buildFreeformModalView({metadata:{continuationToken:slackContinuationToken(e.interaction.channelId,e.interaction.threadTs),channelId:e.interaction.channelId,threadTs:e.interaction.threadTs,messageTs:r,requestId:n},prompt:readPromptTextFromBlocks(e.interaction.messageBlocks)}),s=await resolveSlackBotToken(e.deps.config.credentials?.botToken),c=await fetch(`https://slack.com/api/views.open`,{method:`POST`,headers:{authorization:`Bearer ${s}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({trigger_id:t,view:o})});c.ok||log.error(`Slack views.open returned non-2xx`,{status:c.status})}async function handleViewSubmission(e,t,n){let r=new Response(null,{status:200});if(e.callbackId!==HITL_FREEFORM_MODAL_CALLBACK_ID)return r;let i;try{i=JSON.parse(e.privateMetadata??``)}catch{return log.warn(`freeform view_submission carries invalid private_metadata`),r}if(!i.continuationToken||!i.requestId||!i.messageTs||!i.channelId||!i.threadTs)return r;let a=e.values?.find(e=>e.blockId===HITL_FREEFORM_MODAL_BLOCK_ID&&e.actionId===HITL_FREEFORM_MODAL_ACTION_ID)?.value??``;if(a.length===0)return r;let u=e.user,d=e.userId,f=u?.teamId??e.teamId??null;return t.waitUntil(t.send({inputResponses:[{requestId:i.requestId,text:a}]},{auth:buildSlackAuthContext({channelId:i.channelId,teamId:f,threadTs:i.threadTs,userId:d,userName:u?.username??u?.name}),continuationToken:i.continuationToken,state:{channelId:i.channelId,threadTs:i.threadTs,teamId:f,triggeringUserId:d}}).catch(e=>{log.error(`freeform answer delivery failed`,{error:e})})),t.waitUntil(updateAnsweredFreeformCard({channelId:i.channelId,messageTs:i.messageTs,answerLabel:a,userId:d??void 0,deps:n}).catch(e=>{log.error(`freeform answered-card update failed`,{error:e})})),r}async function updateAnsweredHitlCard(e,t){let n=e.actions.find(e=>isHitlAction(e.actionId));if(!n||!n.messageTs)return;let r=n.label??n.selectedOptionValue??n.value;if(!r)return;let a=buildAnsweredHitlMessageBlocks({actionId:n.actionId,answerLabel:r,messageBlocks:e.messageBlocks,userId:n.user.id}),o=await resolveSlackBotToken(t.config.credentials?.botToken),s=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${o}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:n.messageTs,blocks:a,text:`Answered: ${r}`})});if(!s.ok)throw Error(`Slack chat.update returned HTTP ${s.status}`)}async function updateAnsweredFreeformCard(e){let t=buildAnsweredBlocks({promptBlocks:[],answerLabel:e.answerLabel,userId:e.userId}),n=await resolveSlackBotToken(e.deps.config.credentials?.botToken),r=await fetch(`https://slack.com/api/chat.update`,{method:`POST`,headers:{authorization:`Bearer ${n}`,"content-type":`application/json; charset=utf-8`},body:JSON.stringify({channel:e.channelId,ts:e.messageTs,blocks:t,text:`Answered: ${e.answerLabel}`})});if(!r.ok)throw Error(`Slack chat.update returned HTTP ${r.status}`)}export{handleInteractionPost,parseBlockActionsPayload};
@@ -25,6 +25,14 @@ export declare const SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH = 75;
25
25
  * longer fails the whole post with `invalid_blocks`.
26
26
  */
27
27
  export declare const SLACK_SECTION_TEXT_MAX_LENGTH = 3000;
28
+ /**
29
+ * Block Kit `card` blocks cap `body.text` at 200 chars.
30
+ */
31
+ export declare const SLACK_CARD_BODY_TEXT_MAX_LENGTH = 200;
32
+ /**
33
+ * Block Kit `card` blocks cap `subtext.text` at 200 chars.
34
+ */
35
+ export declare const SLACK_CARD_SUBTEXT_MAX_LENGTH = 200;
28
36
  /**
29
37
  * Top-level `text` field on `chat.postMessage` is capped at 40000 chars.
30
38
  */
@@ -58,6 +66,16 @@ export declare function truncatePlainText(value: string | undefined): string | u
58
66
  * trailing ellipsis.
59
67
  */
60
68
  export declare function truncateSectionText(value: string): string;
69
+ /**
70
+ * Caps a card block's `body.text` at the Slack limit with a trailing
71
+ * ellipsis.
72
+ */
73
+ export declare function truncateCardBodyText(value: string): string;
74
+ /**
75
+ * Caps a card block's `subtext.text` at the Slack limit with a trailing
76
+ * ellipsis.
77
+ */
78
+ export declare function truncateCardSubtext(value: string): string;
61
79
  /**
62
80
  * Caps a `chat.postMessage` `text` field at the Slack limit with a
63
81
  * trailing ellipsis.
@@ -1 +1 @@
1
- const SLACK_TYPING_STATUS_MAX_LENGTH=50,SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH=75,SLACK_SECTION_TEXT_MAX_LENGTH=3e3,SLACK_MESSAGE_TEXT_MAX_LENGTH=4e4,SLACK_MAX_BLOCKS_PER_MESSAGE=50,SLACK_MODAL_TITLE_MAX_LENGTH=24;function truncateTypingStatus(e){return truncateWithEllipsis(stripTypingStatusMarkdown(e).trim().replace(/\s+/gu,` `),50)}function truncatePlainText(e){if(e!==void 0)return truncateWithEllipsis(e,75)}function truncateSectionText(e){return truncateWithEllipsis(e,SLACK_SECTION_TEXT_MAX_LENGTH)}function truncateMessageText(e){return truncateWithEllipsis(e,SLACK_MESSAGE_TEXT_MAX_LENGTH)}function truncateModalTitle(e){return truncateWithEllipsis(e,24)}function truncateWithEllipsis(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function stripTypingStatusMarkdown(e){return e.replace(/\[([^\]]+)\]\([^)]+\)/gu,`$1`).replace(/`([^`]+)`/gu,`$1`).replace(/~~([^~]+)~~/gu,`$1`).replace(/(\*\*|__)([^*_]+)\1/gu,`$2`).replace(/(^|[^\p{L}\p{N}])([*_])([^*_]+)\2(?=$|[^\p{L}\p{N}])/gu,`$1$3`)}export{SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH,SLACK_MAX_BLOCKS_PER_MESSAGE,SLACK_MESSAGE_TEXT_MAX_LENGTH,SLACK_MODAL_TITLE_MAX_LENGTH,SLACK_SECTION_TEXT_MAX_LENGTH,SLACK_TYPING_STATUS_MAX_LENGTH,truncateMessageText,truncateModalTitle,truncatePlainText,truncateSectionText,truncateTypingStatus};
1
+ const SLACK_TYPING_STATUS_MAX_LENGTH=50,SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH=75,SLACK_SECTION_TEXT_MAX_LENGTH=3e3,SLACK_CARD_BODY_TEXT_MAX_LENGTH=200,SLACK_CARD_SUBTEXT_MAX_LENGTH=200,SLACK_MESSAGE_TEXT_MAX_LENGTH=4e4,SLACK_MAX_BLOCKS_PER_MESSAGE=50,SLACK_MODAL_TITLE_MAX_LENGTH=24;function truncateTypingStatus(e){return truncateWithEllipsis(stripTypingStatusMarkdown(e).trim().replace(/\s+/gu,` `),50)}function truncatePlainText(e){if(e!==void 0)return truncateWithEllipsis(e,75)}function truncateSectionText(e){return truncateWithEllipsis(e,SLACK_SECTION_TEXT_MAX_LENGTH)}function truncateCardBodyText(e){return truncateWithEllipsis(e,200)}function truncateCardSubtext(e){return truncateWithEllipsis(e,200)}function truncateMessageText(e){return truncateWithEllipsis(e,SLACK_MESSAGE_TEXT_MAX_LENGTH)}function truncateModalTitle(e){return truncateWithEllipsis(e,24)}function truncateWithEllipsis(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}function stripTypingStatusMarkdown(e){return e.replace(/\[([^\]]+)\]\([^)]+\)/gu,`$1`).replace(/`([^`]+)`/gu,`$1`).replace(/~~([^~]+)~~/gu,`$1`).replace(/(\*\*|__)([^*_]+)\1/gu,`$2`).replace(/(^|[^\p{L}\p{N}])([*_])([^*_]+)\2(?=$|[^\p{L}\p{N}])/gu,`$1$3`)}export{SLACK_BLOCK_KIT_PLAIN_TEXT_MAX_LENGTH,SLACK_CARD_BODY_TEXT_MAX_LENGTH,SLACK_CARD_SUBTEXT_MAX_LENGTH,SLACK_MAX_BLOCKS_PER_MESSAGE,SLACK_MESSAGE_TEXT_MAX_LENGTH,SLACK_MODAL_TITLE_MAX_LENGTH,SLACK_SECTION_TEXT_MAX_LENGTH,SLACK_TYPING_STATUS_MAX_LENGTH,truncateCardBodyText,truncateCardSubtext,truncateMessageText,truncateModalTitle,truncatePlainText,truncateSectionText,truncateTypingStatus};
@@ -1,14 +1,14 @@
1
1
  import type { SessionAuthContext } from "#channel/types.js";
2
2
  import type { CardElement } from "#compiled/chat/index.js";
3
3
  import type { SessionContext } from "#public/definitions/callback-context.js";
4
- import type { ChannelSessionOps } from "#public/definitions/defineChannel.js";
4
+ import type { ChannelSessionOps } from "#public/definitions/channel.js";
5
5
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
6
6
  import { type SlackBotToken, type SlackHandle, type SlackThread } from "#public/channels/slack/api.js";
7
7
  import { type SlackMessage } from "#public/channels/slack/inbound.js";
8
8
  import { type LoadThreadContextMessagesOptions } from "#public/channels/slack/thread.js";
9
9
  import { type UploadPolicyInput } from "#public/channels/upload-policy.js";
10
10
  import { type SlackWebhookVerifier } from "#public/channels/slack/verify.js";
11
- import { type Channel } from "#public/definitions/defineChannel.js";
11
+ import { type Channel } from "#public/definitions/channel.js";
12
12
  type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
13
13
  type: T;
14
14
  }> extends {
@@ -1 +1 @@
1
- import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,slackContinuationToken}from"#public/channels/slack/api.js";import{slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage}from"#public/channels/slack/defaults.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`);function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.setContinuationToken(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),u=e.onAppMention??defaultOnAppMention,d=e.onDirectMessage??defaultOnDirectMessage,f=e.events?.[`authorization.required`],p=e.events?.[`turn.started`]??defaultEvents[`turn.started`],m={...defaultEvents,...e.events,async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await p(e,t,n)},"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":f===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(f)},h=new Set;return defineChannel({kindHint:`slack`,state:{channelId:null,threadTs:null,teamId:null,triggeringUserId:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{}},fetchFile:n,metadata(e){return{channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,{send:r,waitUntil:i})=>{let a=await verifyInbound(n,e.credentials);return a===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(a,{send:r,waitUntil:i},{config:e}):handleEventPost({body:a,send:r,waitUntil:i,onAppMention:u,onDirectMessage:d,uploadPolicy:t,threadContext:e.threadContext,handledEvents:h,headers:n.headers,credentials:e.credentials})})],async receive(t,{send:n}){let r=t.target,i=r.channelId;if(!i||typeof i!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let a=typeof r.threadTs==`string`?r.threadTs:``,s=r.initialMessage;if(s&&a.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let c=a;if(s){let{thread:t}=buildSlackBinding({botToken:e.credentials?.botToken,channelId:i,threadTs:``,teamId:void 0}),n={card:s.card};s.fallbackText!==void 0&&(n.fallbackText=s.fallbackText),c=(await t.post(n)).id}let l=c||crypto.randomUUID();return n(t.message,{auth:t.auth,continuationToken:slackContinuationToken(i,l),state:{channelId:i,threadTs:c||null,teamId:null,triggeringUserId:null}})},events:m})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(t){let n;try{n=parseSlackWebhookBody(t.body,{headers:t.headers})}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(n.kind===`url_verification`)return new Response(n.challenge,{status:200,headers:{"content-type":`text/plain`}});if(n.kind===`unsupported`||n.kind!==`app_mention`&&n.kind!==`direct_message`)return new Response(`ok`);if(n.eventId){if(t.handledEvents.has(n.eventId))return log.warn(`received a duplicate event`,{event_id:n.eventId,event_time:n.eventTime,retry_num:n.retry?.num??`(null)`,retry_reason:n.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(n.eventId,t.handledEvents)}let r=slackMessageFromWebhookPayload(n);return r?n.kind===`app_mention`?(t.waitUntil(dispatchInboundMessage({kind:`app_mention`,message:r,handler:t.onAppMention,send:t.send,uploadPolicy:t.uploadPolicy,threadContext:t.threadContext,credentials:t.credentials})),new Response(`ok`)):(n.kind===`direct_message`&&t.waitUntil(dispatchInboundMessage({kind:`direct_message`,message:r,handler:t.onDirectMessage,send:t.send,uploadPolicy:t.uploadPolicy,threadContext:t.threadContext,credentials:t.credentials})),new Response(`ok`)):new Response(`ok`)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function dispatchInboundMessage(e){let{message:t,kind:r}=e,{thread:i,slack:a}=buildSlackBinding({botToken:e.credentials?.botToken,channelId:t.channelId,threadTs:t.threadTs,teamId:t.teamId}),s={thread:i,slack:a},c;try{c=await e.handler(s,t)}catch(e){logError(log,`${r} handler failed`,e,{channelId:t.channelId});return}if(c!=null)try{let n=formatSlackThreadContext(e.threadContext===void 0?[]:await loadThreadContextMessages(i,t,e.threadContext)),r=await collectInboundFileParts({mention:t,thread:i,policy:e.uploadPolicy}),a={channelId:t.channelId,fullName:t.author?.fullName,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},o=formatSlackInboundMessage(a,t),s=buildSlackTurnMessage(n===void 0?o:`${n}\n\n${o}`,r),l=c.context??[];await e.send(l.length===0?{message:s}:{message:s,context:l},{auth:c.auth,continuationToken:slackContinuationToken(t.channelId,t.threadTs),state:{channelId:t.channelId,threadTs:t.threadTs,teamId:t.teamId??null,triggeringUserId:a.userId||null},title:t.markdown})}catch(e){logError(log,`${r} delivery failed`,e,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
1
+ import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,slackContinuationToken}from"#public/channels/slack/api.js";import{slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage}from"#public/channels/slack/defaults.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`);function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.setContinuationToken(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),u=e.onAppMention??defaultOnAppMention,d=e.onDirectMessage??defaultOnDirectMessage,f=e.events?.[`authorization.required`],p=e.events?.[`turn.started`]??defaultEvents[`turn.started`],m={...defaultEvents,...e.events,async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await p(e,t,n)},"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(),"authorization.required":f===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(f)},h=new Set;return defineChannel({kindHint:`slack`,state:{channelId:null,threadTs:null,teamId:null,triggeringUserId:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{}},fetchFile:n,metadata(e){return{channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,{send:r,waitUntil:i})=>{let a=await verifyInbound(n,e.credentials);return a===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(a,{send:r,waitUntil:i},{config:e}):handleEventPost({body:a,send:r,waitUntil:i,onAppMention:u,onDirectMessage:d,uploadPolicy:t,threadContext:e.threadContext,handledEvents:h,headers:n.headers,credentials:e.credentials})})],async receive(t,{send:n}){let r=t.target,i=r.channelId;if(!i||typeof i!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let a=typeof r.threadTs==`string`?r.threadTs:``,s=r.initialMessage;if(s&&a.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let c=a;if(s){let{thread:t}=buildSlackBinding({botToken:e.credentials?.botToken,channelId:i,threadTs:``,teamId:void 0}),n={card:s.card};s.fallbackText!==void 0&&(n.fallbackText=s.fallbackText),c=(await t.post(n)).id}let l=c||crypto.randomUUID();return n(t.message,{auth:t.auth,continuationToken:slackContinuationToken(i,l),state:{channelId:i,threadTs:c||null,teamId:null,triggeringUserId:null}})},events:m})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(t){let n;try{n=parseSlackWebhookBody(t.body,{headers:t.headers})}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(n.kind===`url_verification`)return new Response(n.challenge,{status:200,headers:{"content-type":`text/plain`}});if(n.kind===`unsupported`||n.kind!==`app_mention`&&n.kind!==`direct_message`)return new Response(`ok`);if(n.eventId){if(t.handledEvents.has(n.eventId))return log.warn(`received a duplicate event`,{event_id:n.eventId,event_time:n.eventTime,retry_num:n.retry?.num??`(null)`,retry_reason:n.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(n.eventId,t.handledEvents)}let r=slackMessageFromWebhookPayload(n);return r?n.kind===`app_mention`?(t.waitUntil(dispatchInboundMessage({kind:`app_mention`,message:r,handler:t.onAppMention,send:t.send,uploadPolicy:t.uploadPolicy,threadContext:t.threadContext,credentials:t.credentials})),new Response(`ok`)):(n.kind===`direct_message`&&t.waitUntil(dispatchInboundMessage({kind:`direct_message`,message:r,handler:t.onDirectMessage,send:t.send,uploadPolicy:t.uploadPolicy,threadContext:t.threadContext,credentials:t.credentials})),new Response(`ok`)):new Response(`ok`)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function dispatchInboundMessage(e){let{message:t,kind:r}=e,{thread:i,slack:a}=buildSlackBinding({botToken:e.credentials?.botToken,channelId:t.channelId,threadTs:t.threadTs,teamId:t.teamId}),s={thread:i,slack:a},c;try{c=await e.handler(s,t)}catch(e){logError(log,`${r} handler failed`,e,{channelId:t.channelId});return}if(c!=null)try{let n=formatSlackThreadContext(e.threadContext===void 0?[]:await loadThreadContextMessages(i,t,e.threadContext)),r=await collectInboundFileParts({mention:t,thread:i,policy:e.uploadPolicy}),a={channelId:t.channelId,fullName:t.author?.fullName,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},o=formatSlackInboundMessage(a,t),s=buildSlackTurnMessage(n===void 0?o:`${n}\n\n${o}`,r),l=c.context??[];await e.send(l.length===0?{message:s}:{message:s,context:l},{auth:c.auth,continuationToken:slackContinuationToken(t.channelId,t.threadTs),state:{channelId:t.channelId,threadTs:t.threadTs,teamId:t.teamId??null,triggeringUserId:a.userId||null},title:t.markdown})}catch(e){logError(log,`${r} delivery failed`,e,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
@@ -1,14 +1,14 @@
1
1
  import type { TeamsInstrumentationMetadata } from "#public/channels/teams/index.js";
2
2
  import type { SessionAuthContext } from "#channel/types.js";
3
3
  import type { SessionContext } from "#public/definitions/callback-context.js";
4
- import type { ChannelSessionOps } from "#public/definitions/defineChannel.js";
4
+ import type { ChannelSessionOps } from "#public/definitions/channel.js";
5
5
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
6
6
  import { type TeamsFilesConfig } from "#public/channels/teams/attachments.js";
7
7
  import { type TeamsApiOptions, type TeamsApiResponse, type TeamsChannelAccount, type TeamsCredentials, type TeamsMention, type TeamsMessageBody, type TeamsOutboundActivity, type TeamsPostedActivity } from "#public/channels/teams/api.js";
8
8
  import { type TeamsInvokeActivity, type TeamsMessageActivity } from "#public/channels/teams/inbound.js";
9
9
  import { type TeamsWebhookVerifier } from "#public/channels/teams/verify.js";
10
10
  import { type JsonObject } from "#shared/json.js";
11
- import { type Channel } from "#public/definitions/defineChannel.js";
11
+ import { type Channel } from "#public/definitions/channel.js";
12
12
  type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
13
13
  type: T;
14
14
  }> extends {
@@ -1 +1 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{callTeamsConnectorApi,normalizeTeamsPostInput,replyToTeamsActivity,sendTeamsActivity,teamsContinuationToken,triggerTeamsTypingIndicator,updateTeamsActivity}from"#public/channels/teams/api.js";import{deriveTeamsInputResponses,isTeamsInputResponseActivity,teamsInvokeResponse}from"#public/channels/teams/hitl.js";import{formatTeamsContextBlock,parseTeamsActivity,teamsThreadRootActivityId}from"#public/channels/teams/inbound.js";import{buildTeamsTurnMessage,collectTeamsFileParts,createTeamsFetchFile,normalizeTeamsFilesPolicy}from"#public/channels/teams/attachments.js";import{defaultEvents,defaultOnMessage,teamsMentionUser}from"#public/channels/teams/defaults.js";import{verifyTeamsRequest}from"#public/channels/teams/verify.js";const log=createLogger(`teams.channel`);function teamsChannel(e={}){let t=normalizeTeamsFilesPolicy(e.files),a=e.onMessage??defaultOnMessage,o={...defaultEvents,...e.events};return defineChannel({kindHint:`teams`,state:initialTeamsState(),fetchFile:createTeamsFetchFile(t),metadata:e=>({channelId:e.channelId,conversationType:e.conversationType,teamId:e.teamId}),context(t,n){return rebuildTeamsContext(t,n,e)},routes:[POST(e.route??`/eve/v1/teams`,async(r,{send:i,waitUntil:o})=>{let s=await verifyInbound(r,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Teams body is not valid JSON`,{error:e}),teamsOk()}let l=parseTeamsActivity(c);return l===null?teamsOk():l.type===`message`?(o(dispatchMessage({activity:l,config:e,filesPolicy:t,onMessage:a,send:i})),teamsOk()):l.type===`invoke`?handleInvoke({activity:l,config:e,send:i,waitUntil:o}):teamsOk()})],async receive(t,{send:n}){let r=t.target,i=readString(r.serviceUrl),a=readString(r.conversationId);if(!i||!a)throw Error(`teamsChannel().receive requires target.serviceUrl and target.conversationId.`);let o=readString(r.conversationType)??null,s=readString(r.replyToActivityId)??null,c=r.initialMessage;if(c!==void 0&&s!==null)throw Error("teamsChannel().receive: `replyToActivityId` and `initialMessage` are mutually exclusive.");let u={...initialTeamsState(),channelId:readString(r.channelId)??null,conversationId:a,conversationType:o,replyToActivityId:s,serviceUrl:i,teamId:readString(r.teamId)??null,tenantId:readString(r.tenantId)??null};if(c!==void 0){let t=await buildTeamsBinding({config:e,state:u}).thread.post(c);o!==`personal`&&t.id&&(s=t.id,u.replyToActivityId=t.id)}return n(t.message,{auth:t.auth,continuationToken:teamsContinuationToken({conversationId:a,replyToActivityId:s,tenantId:u.tenantId}),state:u})},events:o})}function rebuildTeamsContext(e,t,n){return{...buildTeamsBinding({config:n,session:t,state:e}),adaptiveCardVersion:n.adaptiveCardVersion??`1.5`,state:e}}function buildTeamsBinding(e){let n=buildTeamsHandle(e);return{teams:n,thread:{mentionUser:teamsMentionUser,post(e){return n.sendActivity(e)},async startTyping(){try{await n.startTyping()}catch(e){logError(log,`Teams typing indicator failed — swallowed`,e)}},update(e,t){return n.updateActivity(e,t)}}}}function buildTeamsHandle(e){let t=e.state,n=e.config.api,r=e.config.credentials;function requireAddress(){let e=t.conversationId??``,n=t.serviceUrl??``;if(!e||!n)throw Error(`teamsChannel: missing serviceUrl or conversationId for outbound message.`);return{conversationId:e,serviceUrl:n}}function anchor(n){if(!n.id||t.replyToActivityId||t.conversationType===`personal`)return;t.replyToActivityId=n.id;let r=t.conversationId;r&&e.session?.setContinuationToken(teamsContinuationToken({conversationId:r,replyToActivityId:n.id,tenantId:t.tenantId}))}async function send(e){let i=requireAddress(),a=buildOutboundActivity(t,e),o=t.replyToActivityId===null?await sendTeamsActivity({...n,body:a,credentials:r,conversationId:i.conversationId,serviceUrl:i.serviceUrl}):await replyToTeamsActivity({...n,body:a,credentials:r,activityId:t.replyToActivityId,conversationId:i.conversationId,serviceUrl:i.serviceUrl});return anchor(o),o}return{channelId:t.channelId??void 0,conversationId:t.conversationId??``,conversationType:t.conversationType??void 0,replyToActivityId:t.replyToActivityId??void 0,serviceUrl:t.serviceUrl??``,teamId:t.teamId??void 0,tenantId:t.tenantId??void 0,request(e,t,i){let o=requireAddress();return callTeamsConnectorApi({...n,body:t,credentials:r,method:i?.method,path:e,serviceUrl:o.serviceUrl})},sendActivity:send,replyToActivity(e){let i=requireAddress(),a=t.replyToActivityId??``;if(!a)throw Error(`teamsChannel: missing reply activity id.`);return replyToTeamsActivity({...n,body:buildOutboundActivity(t,e),credentials:r,activityId:a,conversationId:i.conversationId,serviceUrl:i.serviceUrl})},updateActivity(e,i){let a=requireAddress();return updateTeamsActivity({...n,body:buildOutboundActivity(t,i),credentials:r,activityId:e,conversationId:a.conversationId,serviceUrl:a.serviceUrl})},async startTyping(){let e=requireAddress();await triggerTeamsTypingIndicator({...n,credentials:r,conversationId:e.conversationId,serviceUrl:e.serviceUrl})}}}async function verifyInbound(e,t){try{return await verifyTeamsRequest(e,{appId:t?.webhookVerifier?void 0:t?.appId,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`teams inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){let t=stateFromActivity(e.activity),n=buildTeamsBinding({config:e.config,state:t}),r;try{r=await e.onMessage(n,e.activity)}catch(e){log.error(`Teams message handler failed`,{error:e});return}if(r==null)return;let i=collectTeamsFileParts(e.activity.attachments,e.filesPolicy),a=buildTeamsTurnMessage(e.activity.text,i),o={activityId:e.activity.id,channelId:e.activity.teamsChannelId,conversationId:e.activity.conversation.id,conversationType:e.activity.conversationType,scope:e.activity.scope,teamId:e.activity.teamId,tenantId:e.activity.tenantId,userId:e.activity.from.id,userName:e.activity.from.name},s=r.context??[];try{await e.send({message:a,context:[formatTeamsContextBlock(o),...s]},{auth:r.auth,continuationToken:stateToken(t),state:t})}catch(e){log.error(`Teams message delivery failed`,{error:e})}}async function handleInvoke(e){if(isTeamsInputResponseActivity(e.activity))return e.waitUntil(dispatchInputResponses({activity:e.activity,send:e.send})),Response.json(teamsInvokeResponse());if(e.config.onInvoke===void 0)return teamsOk();let t=buildTeamsBinding({config:e.config,state:stateFromActivity(e.activity)}),n=await e.config.onInvoke(t,e.activity);return n instanceof Response?n:n&&typeof n==`object`?Response.json(n):teamsOk()}async function dispatchInputResponses(e){let t=deriveTeamsInputResponses(e.activity);if(t.length===0)return;let n=stateFromActivity(e.activity);try{await e.send({inputResponses:t},{auth:null,continuationToken:stateToken(n),state:n})}catch(e){log.error(`Teams input response delivery failed`,{error:e})}}function stateFromActivity(e){return{bot:e.recipient,channelId:e.teamsChannelId??null,conversationId:e.conversation.id,conversationType:e.conversationType??e.scope,pendingAuthActivityId:null,replyToActivityId:teamsThreadRootActivityId(e),serviceUrl:e.serviceUrl,teamId:e.teamId??null,tenantId:e.tenantId??null,triggeringUser:e.from}}function initialTeamsState(){return{bot:null,channelId:null,conversationId:null,conversationType:null,pendingAuthActivityId:null,replyToActivityId:null,serviceUrl:null,teamId:null,tenantId:null,triggeringUser:null}}function stateToken(e){let t=e.conversationId??``;if(!t)throw Error(`teamsChannel: missing conversation id.`);return teamsContinuationToken({conversationId:t,replyToActivityId:e.replyToActivityId,tenantId:e.tenantId})}function buildOutboundActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)return t;let n=normalizeTeamsPostInput(t),r=mergeChannelData(e,n.channelData);return{...n,channelData:r,conversation:e.conversationId?{id:e.conversationId}:void 0,from:e.bot??void 0,replyToId:e.replyToActivityId??void 0,type:`message`}}function mergeChannelData(e,t){let r={...t};return e.tenantId&&(r.tenant={id:e.tenantId}),e.teamId&&(r.team={id:e.teamId}),e.channelId&&(r.channel={id:e.channelId}),Object.keys(r).length>0?parseJsonObject(r):void 0}function teamsOk(){return new Response(`ok`,{status:200})}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{teamsChannel};
1
+ import{createLogger,logError}from"#internal/logging.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{callTeamsConnectorApi,normalizeTeamsPostInput,replyToTeamsActivity,sendTeamsActivity,teamsContinuationToken,triggerTeamsTypingIndicator,updateTeamsActivity}from"#public/channels/teams/api.js";import{deriveTeamsInputResponses,isTeamsInputResponseActivity,teamsInvokeResponse}from"#public/channels/teams/hitl.js";import{formatTeamsContextBlock,parseTeamsActivity,teamsThreadRootActivityId}from"#public/channels/teams/inbound.js";import{buildTeamsTurnMessage,collectTeamsFileParts,createTeamsFetchFile,normalizeTeamsFilesPolicy}from"#public/channels/teams/attachments.js";import{defaultEvents,defaultOnMessage,teamsMentionUser}from"#public/channels/teams/defaults.js";import{verifyTeamsRequest}from"#public/channels/teams/verify.js";const log=createLogger(`teams.channel`);function teamsChannel(e={}){let t=normalizeTeamsFilesPolicy(e.files),a=e.onMessage??defaultOnMessage,o={...defaultEvents,...e.events};return defineChannel({kindHint:`teams`,state:initialTeamsState(),fetchFile:createTeamsFetchFile(t),metadata:e=>({channelId:e.channelId,conversationType:e.conversationType,teamId:e.teamId}),context(t,n){return rebuildTeamsContext(t,n,e)},routes:[POST(e.route??`/eve/v1/teams`,async(r,{send:i,waitUntil:o})=>{let s=await verifyInbound(r,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Teams body is not valid JSON`,{error:e}),teamsOk()}let l=parseTeamsActivity(c);return l===null?teamsOk():l.type===`message`?(o(dispatchMessage({activity:l,config:e,filesPolicy:t,onMessage:a,send:i})),teamsOk()):l.type===`invoke`?handleInvoke({activity:l,config:e,send:i,waitUntil:o}):teamsOk()})],async receive(t,{send:n}){let r=t.target,i=readString(r.serviceUrl),a=readString(r.conversationId);if(!i||!a)throw Error(`teamsChannel().receive requires target.serviceUrl and target.conversationId.`);let o=readString(r.conversationType)??null,s=readString(r.replyToActivityId)??null,c=r.initialMessage;if(c!==void 0&&s!==null)throw Error("teamsChannel().receive: `replyToActivityId` and `initialMessage` are mutually exclusive.");let u={...initialTeamsState(),channelId:readString(r.channelId)??null,conversationId:a,conversationType:o,replyToActivityId:s,serviceUrl:i,teamId:readString(r.teamId)??null,tenantId:readString(r.tenantId)??null};if(c!==void 0){let t=await buildTeamsBinding({config:e,state:u}).thread.post(c);o!==`personal`&&t.id&&(s=t.id,u.replyToActivityId=t.id)}return n(t.message,{auth:t.auth,continuationToken:teamsContinuationToken({conversationId:a,replyToActivityId:s,tenantId:u.tenantId}),state:u})},events:o})}function rebuildTeamsContext(e,t,n){return{...buildTeamsBinding({config:n,session:t,state:e}),adaptiveCardVersion:n.adaptiveCardVersion??`1.5`,state:e}}function buildTeamsBinding(e){let n=buildTeamsHandle(e);return{teams:n,thread:{mentionUser:teamsMentionUser,post(e){return n.sendActivity(e)},async startTyping(){try{await n.startTyping()}catch(e){logError(log,`Teams typing indicator failed — swallowed`,e)}},update(e,t){return n.updateActivity(e,t)}}}}function buildTeamsHandle(e){let t=e.state,n=e.config.api,r=e.config.credentials;function requireAddress(){let e=t.conversationId??``,n=t.serviceUrl??``;if(!e||!n)throw Error(`teamsChannel: missing serviceUrl or conversationId for outbound message.`);return{conversationId:e,serviceUrl:n}}function anchor(n){if(!n.id||t.replyToActivityId||t.conversationType===`personal`)return;t.replyToActivityId=n.id;let r=t.conversationId;r&&e.session?.setContinuationToken(teamsContinuationToken({conversationId:r,replyToActivityId:n.id,tenantId:t.tenantId}))}async function send(e){let i=requireAddress(),a=buildOutboundActivity(t,e),o=t.replyToActivityId===null?await sendTeamsActivity({...n,body:a,credentials:r,conversationId:i.conversationId,serviceUrl:i.serviceUrl}):await replyToTeamsActivity({...n,body:a,credentials:r,activityId:t.replyToActivityId,conversationId:i.conversationId,serviceUrl:i.serviceUrl});return anchor(o),o}return{channelId:t.channelId??void 0,conversationId:t.conversationId??``,conversationType:t.conversationType??void 0,replyToActivityId:t.replyToActivityId??void 0,serviceUrl:t.serviceUrl??``,teamId:t.teamId??void 0,tenantId:t.tenantId??void 0,request(e,t,i){let o=requireAddress();return callTeamsConnectorApi({...n,body:t,credentials:r,method:i?.method,path:e,serviceUrl:o.serviceUrl})},sendActivity:send,replyToActivity(e){let i=requireAddress(),a=t.replyToActivityId??``;if(!a)throw Error(`teamsChannel: missing reply activity id.`);return replyToTeamsActivity({...n,body:buildOutboundActivity(t,e),credentials:r,activityId:a,conversationId:i.conversationId,serviceUrl:i.serviceUrl})},updateActivity(e,i){let a=requireAddress();return updateTeamsActivity({...n,body:buildOutboundActivity(t,i),credentials:r,activityId:e,conversationId:a.conversationId,serviceUrl:a.serviceUrl})},async startTyping(){let e=requireAddress();await triggerTeamsTypingIndicator({...n,credentials:r,conversationId:e.conversationId,serviceUrl:e.serviceUrl})}}}async function verifyInbound(e,t){try{return await verifyTeamsRequest(e,{appId:t?.webhookVerifier?void 0:t?.appId,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`teams inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){let t=stateFromActivity(e.activity),n=buildTeamsBinding({config:e.config,state:t}),r;try{r=await e.onMessage(n,e.activity)}catch(e){log.error(`Teams message handler failed`,{error:e});return}if(r==null)return;let i=collectTeamsFileParts(e.activity.attachments,e.filesPolicy),a=buildTeamsTurnMessage(e.activity.text,i),o={activityId:e.activity.id,channelId:e.activity.teamsChannelId,conversationId:e.activity.conversation.id,conversationType:e.activity.conversationType,scope:e.activity.scope,teamId:e.activity.teamId,tenantId:e.activity.tenantId,userId:e.activity.from.id,userName:e.activity.from.name},s=r.context??[];try{await e.send({message:a,context:[formatTeamsContextBlock(o),...s]},{auth:r.auth,continuationToken:stateToken(t),state:t})}catch(e){log.error(`Teams message delivery failed`,{error:e})}}async function handleInvoke(e){if(isTeamsInputResponseActivity(e.activity))return e.waitUntil(dispatchInputResponses({activity:e.activity,send:e.send})),Response.json(teamsInvokeResponse());if(e.config.onInvoke===void 0)return teamsOk();let t=buildTeamsBinding({config:e.config,state:stateFromActivity(e.activity)}),n=await e.config.onInvoke(t,e.activity);return n instanceof Response?n:n&&typeof n==`object`?Response.json(n):teamsOk()}async function dispatchInputResponses(e){let t=deriveTeamsInputResponses(e.activity);if(t.length===0)return;let n=stateFromActivity(e.activity);try{await e.send({inputResponses:t},{auth:null,continuationToken:stateToken(n),state:n})}catch(e){log.error(`Teams input response delivery failed`,{error:e})}}function stateFromActivity(e){return{bot:e.recipient,channelId:e.teamsChannelId??null,conversationId:e.conversation.id,conversationType:e.conversationType??e.scope,pendingAuthActivityId:null,replyToActivityId:teamsThreadRootActivityId(e),serviceUrl:e.serviceUrl,teamId:e.teamId??null,tenantId:e.tenantId??null,triggeringUser:e.from}}function initialTeamsState(){return{bot:null,channelId:null,conversationId:null,conversationType:null,pendingAuthActivityId:null,replyToActivityId:null,serviceUrl:null,teamId:null,tenantId:null,triggeringUser:null}}function stateToken(e){let t=e.conversationId??``;if(!t)throw Error(`teamsChannel: missing conversation id.`);return teamsContinuationToken({conversationId:t,replyToActivityId:e.replyToActivityId,tenantId:e.tenantId})}function buildOutboundActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)return t;let n=normalizeTeamsPostInput(t),r=mergeChannelData(e,n.channelData);return{...n,channelData:r,conversation:e.conversationId?{id:e.conversationId}:void 0,from:e.bot??void 0,replyToId:e.replyToActivityId??void 0,type:`message`}}function mergeChannelData(e,t){let r={...t};return e.tenantId&&(r.tenant={id:e.tenantId}),e.teamId&&(r.team={id:e.teamId}),e.channelId&&(r.channel={id:e.channelId}),Object.keys(r).length>0?parseJsonObject(r):void 0}function teamsOk(){return new Response(`ok`,{status:200})}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{teamsChannel};
@@ -1,14 +1,14 @@
1
1
  import type { TelegramInstrumentationMetadata } from "#public/channels/telegram/index.js";
2
2
  import type { SessionAuthContext } from "#channel/types.js";
3
3
  import type { SessionContext } from "#public/definitions/callback-context.js";
4
- import type { ChannelSessionOps } from "#public/definitions/defineChannel.js";
4
+ import type { ChannelSessionOps } from "#public/definitions/channel.js";
5
5
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
6
6
  import { type TelegramApiOptions, type TelegramApiResponse, type TelegramCredentials, type TelegramMessageBody, type TelegramMessageResult } from "#public/channels/telegram/api.js";
7
7
  import { type TelegramHitlState } from "#public/channels/telegram/hitl.js";
8
8
  import { type TelegramCallbackQuery, type TelegramChatType, type TelegramMessage } from "#public/channels/telegram/inbound.js";
9
9
  import { type UploadPolicyInput } from "#public/channels/upload-policy.js";
10
10
  import { type TelegramWebhookSecretToken, type TelegramWebhookVerifier } from "#public/channels/telegram/verify.js";
11
- import { type Channel } from "#public/definitions/defineChannel.js";
11
+ import { type Channel } from "#public/definitions/channel.js";
12
12
  import { type JsonObject } from "#shared/json.js";
13
13
  type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
14
14
  type: T;
@@ -1 +1 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{isCompiledChannel}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{formatTelegramContextBlock,parseTelegramUpdate}from"#public/channels/telegram/inbound.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponse}from"#public/channels/telegram/hitl.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,r={...defaultEvents,...e.events},c=defineChannel({kindHint:`telegram`,state:initialTelegramState(e.botUsername),metadata:e=>({chatId:e.chatId,chatType:e.chatType,triggeringUserId:e.triggeringUserId??null}),fetchFile:createTelegramFetchFile({api:e.api,credentials:e.credentials,policy:t}),context(t,n){return rebuildTelegramContext(t,n,e)},routes:[POST(e.route??`/eve/v1/telegram`,async(r,{send:a,waitUntil:o})=>{let s=await verifyInbound(r,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Telegram body is not valid JSON`,{error:e}),new Response(`ok`)}let u=parseTelegramUpdate(c);return u===null?new Response(`ok`):u.kind===`message`?(o(dispatchMessage({config:e,message:u.message,onMessage:n,send:a,uploadPolicy:t})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:u.callbackQuery,send:a})),new Response(`ok`))})],async receive(t,{send:n}){let r=t.target,i=readChatId(r.chatId);if(i===void 0)throw Error(`telegramChannel().receive requires target.chatId.`);let a=typeof r.messageThreadId==`number`?r.messageThreadId:void 0,o=readOptionalString(r.conversationId),s=r.initialMessage;if(s!==void 0&&o!==void 0)throw Error("telegramChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c={...initialTelegramState(e.botUsername),chatId:i,conversationId:o??null,messageThreadId:a??null};return s!==void 0&&await buildTelegramHandle({config:e,state:c}).sendMessage(s),n(t.message,{auth:t.auth,continuationToken:continuationTokenFromState(c),state:c})},events:r});return attachTelegramDeliver(c),c}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let n=e.config.api,r=e.state,i=e.config.credentials;function anchor(t){let n=r.chatType??t.chatType??null;r.chatType===null&&t.chatType!==void 0&&(r.chatType=t.chatType),!(!t.id||!shouldAnchorTelegramConversation(n))&&(r.conversationId=t.id,r.chatId&&e.session?.setContinuationToken(telegramContinuationToken({chatId:r.chatId,conversationId:t.id,messageThreadId:r.messageThreadId??void 0})))}async function sendOne(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for outbound message.`);let a=await sendTelegramMessage({apiBaseUrl:n?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??r.messageThreadId??void 0},credentials:i,fetch:n?.fetch,fileBaseUrl:n?.fileBaseUrl,chatId:t});return anchor(a),a}return{botUsername:r.botUsername??e.config.botUsername,chatId:r.chatId??``,chatType:r.chatType??void 0,conversationId:r.conversationId??void 0,messageThreadId:r.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:n?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:i,fetch:n?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:n?.apiBaseUrl,chatId:t,credentials:i,fetch:n?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},post(e){return postTelegramMessage(e,sendOne)},request(e,t){return callTelegramApi({apiBaseUrl:n?.apiBaseUrl,body:t,botToken:i?.botToken,fetch:n?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let a=r.chatId??``;if(a)try{await sendTelegramChatAction({action:e,apiBaseUrl:n?.apiBaseUrl,chatId:a,credentials:i,fetch:n?.fetch,messageThreadId:r.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:a})}}}}function shouldAnchorTelegramConversation(e){return e===`group`||e===`supergroup`}async function postTelegramMessage(e,t){let n=typeof e==`string`?{text:e}:e,r=splitTelegramMessageText(n.text),i;for(let[e,a]of r.entries()){let r=await t(e===0?{...n,text:a}:{text:a});i===void 0&&(i=r)}return i??{id:``,raw:null}}async function verifyInbound(e,t){try{return await verifyTelegramRequest(e,{secretToken:t?.webhookVerifier?void 0:t?.webhookSecretToken,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`telegram inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){if(e.message.from?.isBot===!0)return;let t=stateFromMessage(e.message,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})},r;try{r=await e.onMessage(n,e.message)}catch(e){log.error(`message handler failed`,{error:e});return}if(r==null)return;let i=collectTelegramFileParts(e.message.attachments,e.uploadPolicy),a=buildTelegramTurnMessage(e.message,i),o=formatTelegramContextBlock({botUsername:e.config.botUsername,chatId:e.message.chat.id,chatTitle:e.message.chat.title,chatType:e.message.chat.type,messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),s=r.context??[],l=e.message.text||e.message.caption,u=e.message.replyToMessage?.from?.isBot===!0&&l.trim().length>0?[telegramReplyInputResponse({messageId:e.message.replyToMessage.messageId,text:l})]:void 0;try{await e.send({inputResponses:u,message:a,context:[o,...s]},{auth:r.auth,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromCallbackQuery(e.query,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_HITL_CALLBACK_PREFIX)===!0){try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Answer received.`})}catch(e){log.warn(`Telegram callback-query acknowledgement failed`,{error:e})}if(!e.query.message||!t.chatId)return;try{await e.send({inputResponses:[telegramCallbackInputResponse(e.query.data)]},{auth:null,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`callback query delivery failed`,{error:e})}return}if(e.config.onCallbackQuery!==void 0){try{await e.config.onCallbackQuery(n,e.query)}catch(e){log.error(`custom callback-query handler failed`,{error:e})}return}try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Unsupported action.`})}catch(e){log.warn(`Telegram unsupported callback-query acknowledgement failed`,{error:e})}}function attachTelegramDeliver(e){if(!isCompiledChannel(e))return;let t=e.adapter;t.deliver=(e,t)=>{let n=e.inputResponses??[];if(n.some(isTelegramSyntheticResponse)){let r=resolveTelegramInputResponses(t.state,n);return r.length>0?{inputResponses:r,context:e.context}:e.message===void 0?void 0:{message:e.message,context:e.context}}return defaultDeliverResult(e)}}function stateFromMessage(e,t){let n=e.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:e.chat.id,chatType:e.chat.type,conversationId:n?null:conversationIdForMessage(e),messageThreadId:e.messageThreadId??null,triggeringUserId:e.from?.id??null}}function stateFromCallbackQuery(e,t){let n=e.message;if(!n)return{...initialTelegramState(t.botUsername),triggeringUserId:e.from.id};let r=n.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:n.chat.id,chatType:n.chat.type,conversationId:r?null:n.messageId,messageThreadId:n.messageThreadId??null,triggeringUserId:e.from.id}}function conversationIdForMessage(e){return e.replyToMessage?.from?.isBot===!0?e.replyToMessage.messageId:e.messageId}function continuationTokenFromState(e){return telegramContinuationToken({chatId:e.chatId??``,conversationId:e.chatType===`private`?void 0:e.conversationId??void 0,messageThreadId:e.messageThreadId??void 0})}function initialTelegramState(e){return{botUsername:e??null,chatId:null,chatType:null,conversationId:null,hitlCallbacks:{},messageThreadId:null,nextHitlCallbackId:0,pendingFreeformReplies:{},triggeringUserId:null}}function readChatId(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}function readOptionalString(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}export{telegramChannel};
1
+ import{createLogger,logError}from"#internal/logging.js";import{isCompiledChannel}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{formatTelegramContextBlock,parseTelegramUpdate}from"#public/channels/telegram/inbound.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponse}from"#public/channels/telegram/hitl.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,r={...defaultEvents,...e.events},c=defineChannel({kindHint:`telegram`,state:initialTelegramState(e.botUsername),metadata:e=>({chatId:e.chatId,chatType:e.chatType,triggeringUserId:e.triggeringUserId??null}),fetchFile:createTelegramFetchFile({api:e.api,credentials:e.credentials,policy:t}),context(t,n){return rebuildTelegramContext(t,n,e)},routes:[POST(e.route??`/eve/v1/telegram`,async(r,{send:a,waitUntil:o})=>{let s=await verifyInbound(r,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Telegram body is not valid JSON`,{error:e}),new Response(`ok`)}let u=parseTelegramUpdate(c);return u===null?new Response(`ok`):u.kind===`message`?(o(dispatchMessage({config:e,message:u.message,onMessage:n,send:a,uploadPolicy:t})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:u.callbackQuery,send:a})),new Response(`ok`))})],async receive(t,{send:n}){let r=t.target,i=readChatId(r.chatId);if(i===void 0)throw Error(`telegramChannel().receive requires target.chatId.`);let a=typeof r.messageThreadId==`number`?r.messageThreadId:void 0,o=readOptionalString(r.conversationId),s=r.initialMessage;if(s!==void 0&&o!==void 0)throw Error("telegramChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c={...initialTelegramState(e.botUsername),chatId:i,conversationId:o??null,messageThreadId:a??null};return s!==void 0&&await buildTelegramHandle({config:e,state:c}).sendMessage(s),n(t.message,{auth:t.auth,continuationToken:continuationTokenFromState(c),state:c})},events:r});return attachTelegramDeliver(c),c}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let n=e.config.api,r=e.state,i=e.config.credentials;function anchor(t){let n=r.chatType??t.chatType??null;r.chatType===null&&t.chatType!==void 0&&(r.chatType=t.chatType),!(!t.id||!shouldAnchorTelegramConversation(n))&&(r.conversationId=t.id,r.chatId&&e.session?.setContinuationToken(telegramContinuationToken({chatId:r.chatId,conversationId:t.id,messageThreadId:r.messageThreadId??void 0})))}async function sendOne(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for outbound message.`);let a=await sendTelegramMessage({apiBaseUrl:n?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??r.messageThreadId??void 0},credentials:i,fetch:n?.fetch,fileBaseUrl:n?.fileBaseUrl,chatId:t});return anchor(a),a}return{botUsername:r.botUsername??e.config.botUsername,chatId:r.chatId??``,chatType:r.chatType??void 0,conversationId:r.conversationId??void 0,messageThreadId:r.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:n?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:i,fetch:n?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:n?.apiBaseUrl,chatId:t,credentials:i,fetch:n?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},post(e){return postTelegramMessage(e,sendOne)},request(e,t){return callTelegramApi({apiBaseUrl:n?.apiBaseUrl,body:t,botToken:i?.botToken,fetch:n?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let a=r.chatId??``;if(a)try{await sendTelegramChatAction({action:e,apiBaseUrl:n?.apiBaseUrl,chatId:a,credentials:i,fetch:n?.fetch,messageThreadId:r.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:a})}}}}function shouldAnchorTelegramConversation(e){return e===`group`||e===`supergroup`}async function postTelegramMessage(e,t){let n=typeof e==`string`?{text:e}:e,r=splitTelegramMessageText(n.text),i;for(let[e,a]of r.entries()){let r=await t(e===0?{...n,text:a}:{text:a});i===void 0&&(i=r)}return i??{id:``,raw:null}}async function verifyInbound(e,t){try{return await verifyTelegramRequest(e,{secretToken:t?.webhookVerifier?void 0:t?.webhookSecretToken,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`telegram inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){if(e.message.from?.isBot===!0)return;let t=stateFromMessage(e.message,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})},r;try{r=await e.onMessage(n,e.message)}catch(e){log.error(`message handler failed`,{error:e});return}if(r==null)return;let i=collectTelegramFileParts(e.message.attachments,e.uploadPolicy),a=buildTelegramTurnMessage(e.message,i),o=formatTelegramContextBlock({botUsername:e.config.botUsername,chatId:e.message.chat.id,chatTitle:e.message.chat.title,chatType:e.message.chat.type,messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),s=r.context??[],l=e.message.text||e.message.caption,u=e.message.replyToMessage?.from?.isBot===!0&&l.trim().length>0?[telegramReplyInputResponse({messageId:e.message.replyToMessage.messageId,text:l})]:void 0;try{await e.send({inputResponses:u,message:a,context:[o,...s]},{auth:r.auth,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromCallbackQuery(e.query,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_HITL_CALLBACK_PREFIX)===!0){try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Answer received.`})}catch(e){log.warn(`Telegram callback-query acknowledgement failed`,{error:e})}if(!e.query.message||!t.chatId)return;try{await e.send({inputResponses:[telegramCallbackInputResponse(e.query.data)]},{auth:null,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`callback query delivery failed`,{error:e})}return}if(e.config.onCallbackQuery!==void 0){try{await e.config.onCallbackQuery(n,e.query)}catch(e){log.error(`custom callback-query handler failed`,{error:e})}return}try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Unsupported action.`})}catch(e){log.warn(`Telegram unsupported callback-query acknowledgement failed`,{error:e})}}function attachTelegramDeliver(e){if(!isCompiledChannel(e))return;let t=e.adapter;t.deliver=(e,t)=>{let n=e.inputResponses??[];if(n.some(isTelegramSyntheticResponse)){let r=resolveTelegramInputResponses(t.state,n);return r.length>0?{inputResponses:r,context:e.context}:e.message===void 0?void 0:{message:e.message,context:e.context}}return defaultDeliverResult(e)}}function stateFromMessage(e,t){let n=e.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:e.chat.id,chatType:e.chat.type,conversationId:n?null:conversationIdForMessage(e),messageThreadId:e.messageThreadId??null,triggeringUserId:e.from?.id??null}}function stateFromCallbackQuery(e,t){let n=e.message;if(!n)return{...initialTelegramState(t.botUsername),triggeringUserId:e.from.id};let r=n.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:n.chat.id,chatType:n.chat.type,conversationId:r?null:n.messageId,messageThreadId:n.messageThreadId??null,triggeringUserId:e.from.id}}function conversationIdForMessage(e){return e.replyToMessage?.from?.isBot===!0?e.replyToMessage.messageId:e.messageId}function continuationTokenFromState(e){return telegramContinuationToken({chatId:e.chatId??``,conversationId:e.chatType===`private`?void 0:e.conversationId??void 0,messageThreadId:e.messageThreadId??void 0})}function initialTelegramState(e){return{botUsername:e??null,chatId:null,chatType:null,conversationId:null,hitlCallbacks:{},messageThreadId:null,nextHitlCallbackId:0,pendingFreeformReplies:{},triggeringUserId:null}}function readChatId(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}function readOptionalString(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}export{telegramChannel};
@@ -1,11 +1,11 @@
1
1
  import type { SessionAuthContext } from "#channel/types.js";
2
2
  import type { SessionContext } from "#public/definitions/callback-context.js";
3
- import type { ChannelSessionOps } from "#public/definitions/defineChannel.js";
3
+ import type { ChannelSessionOps } from "#public/definitions/channel.js";
4
4
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
5
5
  import { type TwilioApiOptions, type TwilioApiResponse, type TwilioCredentials } from "#public/channels/twilio/api.js";
6
6
  import { type TwilioTextMessage, type TwilioVoiceCall, type TwilioVoiceTranscription } from "#public/channels/twilio/inbound.js";
7
7
  import { type TwilioAuthToken, type TwilioWebhookUrl } from "#public/channels/twilio/verify.js";
8
- import { type Channel } from "#public/definitions/defineChannel.js";
8
+ import { type Channel } from "#public/definitions/channel.js";
9
9
  type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
10
10
  type: T;
11
11
  }> extends {
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{GET,POST,defineChannel}from"#public/definitions/defineChannel.js";import"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription}from"#public/channels/twilio/inbound.js";import{buildTwilioActionUrl,buildTwilioRoutes,verifyTwilioInbound}from"#public/channels/twilio/routing.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let i=buildTwilioRoutes(e.route??`/eve/v1/twilio`),a=e.onText??defaultOnText,s=e.onVoice??defaultOnVoice,c=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events},u=handleTwilioMessages({config:e,onText:a}),d=handleTwilioVoice({config:e,onVoice:s,routes:i}),f=handleTwilioTranscription({config:e,onVoiceTranscription:c,routes:i});return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{from:e.from,lastCallSid:e.lastCallSid??null,lastMessageSid:e.lastMessageSid??null,to:e.to}},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[GET(i.messages,u),POST(i.messages,u),GET(i.voice,d),POST(i.voice,d),GET(i.transcription,f),POST(i.transcription,f)],async receive(t,{send:n}){let r=readString(t.target.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires target.phoneNumber.`);let i=readString(t.target.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function handleTwilioMessages(e){return async(t,{send:n,waitUntil:r})=>{let i=await verifyTwilioInbound(t,e.config);if(i===null)return new Response(`unauthorized`,{status:401});let a=parseTwilioTextMessage(i.params);return a?await isAllowed(a.from,e.config.allowFrom)?(r(dispatchText({config:e.config,message:a,onText:e.onText,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}}function handleTwilioVoice(e){return async t=>{let n=await verifyTwilioInbound(t,e.config);if(n===null)return new Response(`unauthorized`,{status:401});let r=parseTwilioVoiceCall(n.params);if(!r)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(r.from,e.config.allowFrom))return new Response(`forbidden`,{status:403});let i=await acceptVoiceCall({call:r,config:e.config,onVoice:e.onVoice});if(i===null)return new Response(`forbidden`,{status:403});let a=i??{};return gatherSpeechTwilioResponse({actionUrl:await buildTwilioActionUrl(t,e.config,e.routes.transcription),hints:a.hints??e.config.voice?.hints,language:a.language??e.config.voice?.language,profanityFilter:a.profanityFilter??e.config.voice?.profanityFilter,prompt:a.prompt??e.config.voice?.prompt??`Please say your message after the tone.`,speechModel:a.speechModel??e.config.voice?.speechModel,speechTimeout:a.speechTimeout??e.config.voice?.speechTimeout??`auto`,timeoutSeconds:a.timeoutSeconds??e.config.voice?.timeoutSeconds,voice:a.voice??e.config.voice?.voice})}}function handleTwilioTranscription(e){return async(t,{send:n,waitUntil:r})=>{let i=await verifyTwilioInbound(t,e.config);if(i===null)return new Response(`unauthorized`,{status:401});let a=parseTwilioVoiceTranscription(i.params);return a?await isAllowed(a.from,e.config.allowFrom)?(r(dispatchVoiceTranscription({config:e.config,onVoiceTranscription:e.onVoiceTranscription,send:n,transcription:a})),sayTwilioResponse(e.config.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildTwilioActionUrl(t,e.config,e.routes.transcription),language:e.config.voice?.language,prompt:e.config.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.config.voice?.speechTimeout??`auto`,timeoutSeconds:e.config.voice?.timeoutSeconds})}}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send({message:t.body,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send({message:t.text,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};
1
+ import{createLogger}from"#internal/logging.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription}from"#public/channels/twilio/inbound.js";import{buildTwilioActionUrl,buildTwilioRoutes,verifyTwilioInbound}from"#public/channels/twilio/routing.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let i=buildTwilioRoutes(e.route??`/eve/v1/twilio`),a=e.onText??defaultOnText,s=e.onVoice??defaultOnVoice,c=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events},u=handleTwilioMessages({config:e,onText:a}),d=handleTwilioVoice({config:e,onVoice:s,routes:i}),f=handleTwilioTranscription({config:e,onVoiceTranscription:c,routes:i});return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{from:e.from,lastCallSid:e.lastCallSid??null,lastMessageSid:e.lastMessageSid??null,to:e.to}},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[GET(i.messages,u),POST(i.messages,u),GET(i.voice,d),POST(i.voice,d),GET(i.transcription,f),POST(i.transcription,f)],async receive(t,{send:n}){let r=readString(t.target.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires target.phoneNumber.`);let i=readString(t.target.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function handleTwilioMessages(e){return async(t,{send:n,waitUntil:r})=>{let i=await verifyTwilioInbound(t,e.config);if(i===null)return new Response(`unauthorized`,{status:401});let a=parseTwilioTextMessage(i.params);return a?await isAllowed(a.from,e.config.allowFrom)?(r(dispatchText({config:e.config,message:a,onText:e.onText,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}}function handleTwilioVoice(e){return async t=>{let n=await verifyTwilioInbound(t,e.config);if(n===null)return new Response(`unauthorized`,{status:401});let r=parseTwilioVoiceCall(n.params);if(!r)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(r.from,e.config.allowFrom))return new Response(`forbidden`,{status:403});let i=await acceptVoiceCall({call:r,config:e.config,onVoice:e.onVoice});if(i===null)return new Response(`forbidden`,{status:403});let a=i??{};return gatherSpeechTwilioResponse({actionUrl:await buildTwilioActionUrl(t,e.config,e.routes.transcription),hints:a.hints??e.config.voice?.hints,language:a.language??e.config.voice?.language,profanityFilter:a.profanityFilter??e.config.voice?.profanityFilter,prompt:a.prompt??e.config.voice?.prompt??`Please say your message after the tone.`,speechModel:a.speechModel??e.config.voice?.speechModel,speechTimeout:a.speechTimeout??e.config.voice?.speechTimeout??`auto`,timeoutSeconds:a.timeoutSeconds??e.config.voice?.timeoutSeconds,voice:a.voice??e.config.voice?.voice})}}function handleTwilioTranscription(e){return async(t,{send:n,waitUntil:r})=>{let i=await verifyTwilioInbound(t,e.config);if(i===null)return new Response(`unauthorized`,{status:401});let a=parseTwilioVoiceTranscription(i.params);return a?await isAllowed(a.from,e.config.allowFrom)?(r(dispatchVoiceTranscription({config:e.config,onVoiceTranscription:e.onVoiceTranscription,send:n,transcription:a})),sayTwilioResponse(e.config.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildTwilioActionUrl(t,e.config,e.routes.transcription),language:e.config.voice?.language,prompt:e.config.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.config.voice?.speechTimeout??`auto`,timeoutSeconds:e.config.voice?.timeoutSeconds})}}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send({message:t.body,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send({message:t.text,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};
@@ -1,6 +1,6 @@
1
1
  import type { PublicAgentDefinition } from "#shared/agent-definition.js";
2
2
  import type { ExactDefinition } from "#public/definitions/exact.js";
3
- export type { AgentModelOptionsDefinition, AgentReasoningDefinition, AgentBuildDefinition, AgentExperimentalDefinition, AgentLimitsDefinition, AgentWorkflowDefinition, AgentWorkflowWorldDefinition, PublicAgentModelDefinition as AgentModelDefinition, PublicAgentCompactionDefinition as AgentCompactionDefinition, } from "#shared/agent-definition.js";
3
+ export type { AgentModelResolveContext, AgentModelOptionsDefinition, AgentModelResolver, AgentReasoningDefinition, AgentBuildDefinition, PublicAgentDynamicModelDefinition as AgentDynamicModelDefinition, PublicAgentDynamicModelResult as AgentDynamicModelResult, AgentExperimentalDefinition, AgentLimitsDefinition, PublicAgentModelSelectionDefinition as AgentModelSelectionDefinition, AgentWorkflowDefinition, AgentWorkflowWorldDefinition, PublicAgentModelDefinition as AgentModelDefinition, PublicAgentStaticModelDefinition as AgentStaticModelDefinition, PublicAgentCompactionDefinition as AgentCompactionDefinition, } from "#shared/agent-definition.js";
4
4
  /**
5
5
  * Additive public agent configuration authored in `agent.ts`.
6
6
  *
@@ -1,6 +1,18 @@
1
+ import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
2
+ import { type ChannelCorsOptions } from "#channel/cors.js";
3
+ import type { TypedReceiveTarget } from "#channel/receive-target.js";
4
+ import type { RouteDefinition, SendFn } from "#channel/routes.js";
5
+ import type { Session } from "#channel/session.js";
1
6
  import type { DeliverInput, GetEventStreamOptions, RunHandle, RunInput } from "#channel/types.js";
2
7
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
8
+ import type { SessionContext } from "#public/definitions/callback-context.js";
9
+ import type { GenericChannelDefinition, GenericReceiveInput } from "#shared/channel-definition.js";
10
+ declare const CHANNEL_METADATA_TYPE: unique symbol;
3
11
  export type { GetEventStreamOptions } from "#channel/types.js";
12
+ export type { Session, SessionHandle } from "#channel/session.js";
13
+ export type { ChannelCors, ChannelCorsOptions } from "#channel/cors.js";
14
+ export { GET, POST, PUT, PATCH, DELETE, WS } from "#channel/routes.js";
15
+ export type { HttpRouteDefinition, RouteDefinition, RouteHandlerArgs, SendFn, SendOptions, SendPayload, GetSessionFn, WebSocketMessage, WebSocketPeer, WebSocketRouteDefinition, WebSocketRouteHandler, WebSocketRouteHooks, WebSocketUpgradeRequest, WebSocketUpgradeResult, } from "#channel/routes.js";
4
16
  /**
5
17
  * HTTP method a route handles. Defaults to `"POST"` — almost every route
6
18
  * is a webhook. Override only when authoring a non-webhook route such as a
@@ -124,3 +136,90 @@ export declare function disableRoute(): DisabledRouteSentinel;
124
136
  * produced by {@link disableRoute}.
125
137
  */
126
138
  export declare function isDisabledRouteSentinel(value: unknown): value is DisabledRouteSentinel;
139
+ type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
140
+ type: T;
141
+ }> extends {
142
+ data: infer D;
143
+ } ? D : undefined;
144
+ /**
145
+ * Session operations on the `channel` argument of every channel event handler.
146
+ */
147
+ export interface ChannelSessionOps {
148
+ readonly continuationToken: string;
149
+ setContinuationToken(token: string): void;
150
+ }
151
+ /**
152
+ * Channel context passed to event handlers: `TCtx` intersected with
153
+ * {@link ChannelSessionOps}.
154
+ */
155
+ export type ChannelContext<TCtx> = TCtx & ChannelSessionOps;
156
+ type ChannelEventHandler<T extends HandleMessageStreamEvent["type"], TCtx> = (data: EventData<T>, channel: ChannelContext<TCtx>, ctx: SessionContext) => void | Promise<void>;
157
+ type ChannelSessionFailedHandler<TCtx> = (data: EventData<"session.failed">, channel: ChannelContext<TCtx>) => void | Promise<void>;
158
+ /**
159
+ * Optional handlers keyed by session lifecycle event name. Each handler receives
160
+ * the event `data`, the {@link ChannelContext}, and a {@link SessionContext}
161
+ * `ctx`. The `session.failed` handler is the exception: it receives only `data`
162
+ * and the channel context, with no `ctx`.
163
+ */
164
+ export interface ChannelEvents<TCtx = void> {
165
+ readonly "turn.started"?: ChannelEventHandler<"turn.started", TCtx>;
166
+ readonly "actions.requested"?: ChannelEventHandler<"actions.requested", TCtx>;
167
+ readonly "action.result"?: ChannelEventHandler<"action.result", TCtx>;
168
+ readonly "message.completed"?: ChannelEventHandler<"message.completed", TCtx>;
169
+ readonly "message.appended"?: ChannelEventHandler<"message.appended", TCtx>;
170
+ readonly "reasoning.appended"?: ChannelEventHandler<"reasoning.appended", TCtx>;
171
+ readonly "reasoning.completed"?: ChannelEventHandler<"reasoning.completed", TCtx>;
172
+ readonly "input.requested"?: ChannelEventHandler<"input.requested", TCtx>;
173
+ readonly "turn.failed"?: ChannelEventHandler<"turn.failed", TCtx>;
174
+ readonly "turn.completed"?: ChannelEventHandler<"turn.completed", TCtx>;
175
+ readonly "session.failed"?: ChannelSessionFailedHandler<TCtx>;
176
+ readonly "session.completed"?: ChannelEventHandler<"session.completed", TCtx>;
177
+ readonly "session.waiting"?: ChannelEventHandler<"session.waiting", TCtx>;
178
+ readonly "authorization.required"?: ChannelEventHandler<"authorization.required", TCtx>;
179
+ readonly "authorization.completed"?: ChannelEventHandler<"authorization.completed", TCtx>;
180
+ }
181
+ /**
182
+ * Input passed to a channel's `receive` callback when another channel or
183
+ * schedule proactively routes a message to it.
184
+ */
185
+ export type ReceiveInput<TReceiveTarget = Record<string, unknown>> = GenericReceiveInput<TReceiveTarget>;
186
+ /**
187
+ * The object passed to {@link defineChannel}. `routes` is required; `state`
188
+ * seeds durable adapter state, `context` builds the per-step `channel` argument
189
+ * for `events` and `deliver`, `events` handle session lifecycle, `receive`
190
+ * accepts cross-channel handoffs, `fetchFile` stages remote file URLs, and
191
+ * `metadata` projects observability data.
192
+ *
193
+ * Generics: `TState` (adapter state), `TCtx` (context factory return type),
194
+ * `TReceiveTarget` (cross-channel target shape), `TMetadata` (instrumentation
195
+ * projection).
196
+ */
197
+ export type ChannelDefinition<TState = undefined, TCtx = void, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> = GenericChannelDefinition<ChannelEvents<TCtx>, TState, TCtx, TReceiveTarget, TMetadata>;
198
+ /**
199
+ * Opaque channel value produced by {@link defineChannel} and exported from
200
+ * `agent/channels/<name>.ts`. Exposes the channel's routes, an optional
201
+ * `receive` hook, and (via a phantom property) its metadata shape. Unlike
202
+ * {@link ChannelDefinition} it has no `TCtx` parameter: the context type is
203
+ * internal to the definition.
204
+ */
205
+ export interface Channel<TState = undefined, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> extends TypedReceiveTarget<TReceiveTarget> {
206
+ readonly __kind: typeof CHANNEL_SENTINEL;
207
+ readonly [CHANNEL_METADATA_TYPE]?: TMetadata;
208
+ readonly routes: readonly RouteDefinition<TState>[];
209
+ readonly cors?: ChannelCorsOptions;
210
+ readonly receive?: (input: ReceiveInput<TReceiveTarget>, args: {
211
+ send: SendFn<TState>;
212
+ }) => Promise<Session>;
213
+ }
214
+ /**
215
+ * Extracts the metadata projection type (`TMetadata`) from a {@link Channel}.
216
+ * Resolves to `Record<string, unknown>` when the value is not a Channel.
217
+ */
218
+ export type InferChannelMetadata<TChannel> = TChannel extends Channel<any, any, infer TMetadata> ? TMetadata : Record<string, unknown>;
219
+ /**
220
+ * Builds a {@link Channel} from a {@link ChannelDefinition}. Returns a value
221
+ * placed at `agent/channels/<name>.ts`; the file path supplies the channel name
222
+ * (do not add a `name` field). `TCtx` (the context factory's return type) is
223
+ * internal to the definition and is not part of the returned Channel signature.
224
+ */
225
+ export declare function defineChannel<TState = undefined, TCtx = void, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>>(definition: ChannelDefinition<TState, TCtx, TReceiveTarget, TMetadata>): Channel<TState, TReceiveTarget, TMetadata>;
@@ -1 +1 @@
1
- const DISABLED_ROUTE_SENTINEL_KIND=`eve:disabled-channel`;function disableRoute(){return{kind:DISABLED_ROUTE_SENTINEL_KIND}}function isDisabledRouteSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_ROUTE_SENTINEL_KIND}export{disableRoute,isDisabledRouteSentinel};
1
+ import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{normalizeChannelCors}from"#channel/cors.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,PATCH,POST,PUT,WS}from"#channel/routes.js";const DISABLED_ROUTE_SENTINEL_KIND=`eve:disabled-channel`;function disableRoute(){return{kind:DISABLED_ROUTE_SENTINEL_KIND}}function isDisabledRouteSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_ROUTE_SENTINEL_KIND}function defineChannel(t){let n=buildAdapter(t),i=normalizeChannelCors(t.cors);return{__kind:CHANNEL_SENTINEL,routes:t.routes,adapter:n,cors:i,receive:t.receive}}function buildAdapter(e){let r=e.state!=null,a=e.context!=null,o=e.fetchFile!==void 0,s=e.metadata,c=r||a||s!==void 0,l={},u=!1,d=[`turn.started`,`actions.requested`,`action.result`,`message.completed`,`message.appended`,`reasoning.appended`,`reasoning.completed`,`input.requested`,`turn.failed`,`turn.completed`,`session.failed`,`session.completed`,`session.waiting`,`authorization.required`,`authorization.completed`],f=e.events;for(let e of d){let t=f?.[e];t&&(u=!0,l[e]=(r,i)=>{let a={...i,continuationToken:i.session?.continuationToken??``,setContinuationToken:e=>i.session?.setContinuationToken(e)};return e===`session.failed`?t(r,a):t(r,a,buildCallbackContext())})}return!c&&!u&&!o?{kind:e.kindHint??HTTP_ADAPTER_KIND}:{kind:e.kindHint??`defineChannel`,state:r?{...e.state}:{},fetchFile:e.fetchFile,instrumentation:s===void 0?void 0:{metadata(e){return s(e)}},createAdapterContext(t){let n=t.state,r=t.session;return{...a?e.context(n,r):{},state:n,ctx:t.ctx,session:r}},deliver(e){return defaultDeliverResult(e)},...l}}export{DELETE,GET,PATCH,POST,PUT,WS,defineChannel,disableRoute,isDisabledRouteSentinel};
@@ -1,6 +1,7 @@
1
1
  import type { HandleMessageStreamEvent } from "../../protocol/message.js";
2
2
  import type { SessionContext } from "./callback-context.js";
3
3
  import type { ExactDefinition } from "./exact.js";
4
+ import type { GenericHookDefinition, GenericStreamEventHooks } from "#shared/hook-definition.js";
4
5
  /**
5
6
  * Every hook handler receives this context.
6
7
  *
@@ -31,13 +32,7 @@ export type StreamEventHook<TEvent> = (event: TEvent, ctx: HookContext) => void
31
32
  * `*` matches every accepted runtime stream event and runs after the
32
33
  * typed handler for that event (if any).
33
34
  */
34
- export type StreamEventHooks = {
35
- readonly "*"?: StreamEventHook<HandleMessageStreamEvent>;
36
- } & {
37
- readonly [K in HandleMessageStreamEvent["type"]]?: StreamEventHook<Extract<HandleMessageStreamEvent, {
38
- type: K;
39
- }>>;
40
- };
35
+ export type StreamEventHooks = GenericStreamEventHooks<HandleMessageStreamEvent, HookContext>;
41
36
  /**
42
37
  * Public hook definition authored in `agent/hooks/*.ts`.
43
38
  *
@@ -47,9 +42,7 @@ export type StreamEventHooks = {
47
42
  * contribute runtime model messages, use `defineDynamic` +
48
43
  * `defineInstructions` in `agent/instructions/`.
49
44
  */
50
- export interface HookDefinition {
51
- readonly events?: StreamEventHooks;
52
- }
45
+ export type HookDefinition = GenericHookDefinition<HandleMessageStreamEvent, HookContext>;
53
46
  /**
54
47
  * Identity-with-types helper. Returns the passed definition unchanged
55
48
  * (identity at runtime) while preserving literal inference and rejecting
@@ -1,22 +1,6 @@
1
1
  import type { ExactDefinition } from "#public/definitions/exact.js";
2
- /**
3
- * Public definition for an instructions prompt authored in markdown or
4
- * TypeScript.
5
- *
6
- * Authored at the agent root as either `instructions.md` or
7
- * `instructions.{ts,cts,mts,js,cjs,mjs}`, or inside the
8
- * `agent/instructions/` directory for multi-file setups. Module-backed
9
- * static instructions execute once at build time. The compiler captures
10
- * the resulting markdown into the compiled manifest.
11
- *
12
- * When used inside a `defineDynamic` handler, the runtime lowers the
13
- * returned markdown to `{ role: "system", content: markdown }`.
14
- * Instructions produce system messages only. Use channel `context` for
15
- * user-role messages.
16
- */
17
- export interface InstructionsDefinition {
18
- readonly markdown: string;
19
- }
2
+ import type { PublicInstructionsDefinition } from "#shared/instructions-definition.js";
3
+ export type InstructionsDefinition = Readonly<PublicInstructionsDefinition>;
20
4
  /**
21
5
  * Defines an instructions prompt in TypeScript from a `{ markdown }`
22
6
  * definition.
@@ -1,6 +1,7 @@
1
1
  import type { CrossChannelReceiveFn } from "#channel/cross-channel-receive.js";
2
2
  import type { SessionAuthContext } from "#channel/types.js";
3
3
  import type { ExactDefinition } from "#public/definitions/exact.js";
4
+ import type { GenericScheduleDefinition, GenericScheduleRunHandler, GenericScheduleDefinitionFields } from "#shared/schedule-definition.js";
4
5
  export type { InferReceiveTarget, TypedReceiveTarget } from "#channel/receive-target.js";
5
6
  /**
6
7
  * Arguments passed to a schedule's `run` handler. A tight subset of a route
@@ -31,13 +32,7 @@ export interface ScheduleHandlerArgs {
31
32
  * schedule's cron fires. It receives {@link ScheduleHandlerArgs} (`receive`,
32
33
  * `waitUntil`, `appAuth`) and may return synchronously or as a promise.
33
34
  */
34
- export type ScheduleRunHandler = (args: ScheduleHandlerArgs) => Promise<void> | void;
35
- /** Constraint shape that bounds the authored keys accepted by {@link defineSchedule}. */
36
- interface ScheduleDefinitionFields {
37
- readonly cron: string;
38
- readonly markdown?: string;
39
- readonly run?: ScheduleRunHandler;
40
- }
35
+ export type ScheduleRunHandler = GenericScheduleRunHandler<ScheduleHandlerArgs>;
41
36
  /**
42
37
  * Public definition for a schedule authored in TypeScript. Provide a required
43
38
  * `cron` expression plus exactly one of `markdown` or `run`:
@@ -51,15 +46,7 @@ interface ScheduleDefinitionFields {
51
46
  * Identity is derived from the file path under `agent/schedules/`; authored
52
47
  * definitions do not carry a `name` field.
53
48
  */
54
- export type ScheduleDefinition = {
55
- readonly cron: string;
56
- readonly markdown: string;
57
- readonly run?: never;
58
- } | {
59
- readonly cron: string;
60
- readonly markdown?: never;
61
- readonly run: ScheduleRunHandler;
62
- };
49
+ export type ScheduleDefinition = GenericScheduleDefinition<ScheduleHandlerArgs>;
63
50
  /**
64
51
  * Defines a schedule in TypeScript. Export as the default from
65
52
  * `agent/schedules/<name>.ts`. Pass a `cron` expression plus exactly one of
@@ -93,4 +80,4 @@ export type ScheduleDefinition = {
93
80
  * });
94
81
  * ```
95
82
  */
96
- export declare function defineSchedule<TSchedule extends ScheduleDefinition>(definition: ExactDefinition<TSchedule, ScheduleDefinitionFields>): TSchedule;
83
+ export declare function defineSchedule<TSchedule extends ScheduleDefinition>(definition: ExactDefinition<TSchedule, GenericScheduleDefinitionFields<ScheduleHandlerArgs>>): TSchedule;
@@ -4,8 +4,10 @@ import type { SessionContext } from "#public/definitions/callback-context.js";
4
4
  import type { Approval } from "#public/definitions/approval.js";
5
5
  import type { JsonObject } from "#shared/json.js";
6
6
  import type { AuthorizationDefinition, ConnectionAuthorizationContext, NonInteractiveAuthorizationDefinition, TokenResult } from "#runtime/connections/types.js";
7
- import { type DynamicEvents, type DynamicSentinel } from "#shared/dynamic-tool-definition.js";
7
+ import { type DynamicEvents, type DynamicEventsWithFallback, type DynamicSentinel } from "#shared/dynamic-tool-definition.js";
8
8
  type ApprovalContextInput<TInput> = unknown extends TInput ? Record<string, unknown> : TInput;
9
+ type DynamicEventMapHandler<TEvents extends DynamicEvents> = Extract<NonNullable<TEvents[keyof TEvents]>, (...args: never[]) => unknown>;
10
+ type DynamicEventMapResult<TEvents extends DynamicEvents> = Awaited<ReturnType<DynamicEventMapHandler<TEvents>>>;
9
11
  export type { ToolModelOutput } from "#shared/tool-definition.js";
10
12
  /**
11
13
  * Authorization provider passed to {@link ToolContext.getToken} or
@@ -191,9 +193,13 @@ export declare function defineTool<TInput = unknown, TOutput = unknown>(definiti
191
193
  * whose name matches an authored one overrides it; two dynamic resolvers
192
194
  * emitting the same name is an error.
193
195
  */
194
- export declare function defineDynamic(definition: {
195
- readonly events: DynamicEvents;
196
- }): DynamicSentinel;
196
+ export declare function defineDynamic<const TEvents extends DynamicEvents>(definition: {
197
+ readonly events: TEvents;
198
+ }): DynamicSentinel<DynamicEventMapResult<TEvents>>;
199
+ export declare function defineDynamic<const TEvents extends DynamicEventsWithFallback, TFallback = unknown>(definition: {
200
+ readonly fallback: TFallback;
201
+ readonly events: TEvents;
202
+ }): DynamicSentinel<Exclude<DynamicEventMapResult<TEvents>, undefined>, TFallback>;
197
203
  /**
198
204
  * Marker discriminator written into every {@link DisabledToolSentinel}.
199
205
  */
@@ -1 +1 @@
1
- import{DYNAMIC_SENTINEL_KIND,TOOL_BRAND}from"#shared/dynamic-tool-definition.js";import{stampDefinitionKey}from"#public/tool-result-narrowing.js";function defineTool(e){if(e.auth!==void 0)throw Error(`defineTool: The "auth" field is no longer supported. Pass auth providers inline to ctx.getToken(provider) or ctx.requireAuth(provider).`);return Object.assign(e,{[TOOL_BRAND]:!0}),stampDefinitionKey(e,`tool:${e.description}`),e}function defineDynamic(t){let n={kind:DYNAMIC_SENTINEL_KIND,events:t.events};return stampDefinitionKey(n,`dynamic:${Object.keys(t.events).join(`,`)}`),n}const DISABLED_TOOL_SENTINEL_KIND=`eve:disabled-tool`;function disableTool(){return{kind:DISABLED_TOOL_SENTINEL_KIND}}function isDisabledToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_TOOL_SENTINEL_KIND}const ENABLE_WORKFLOW_TOOL_SENTINEL_KIND=`eve:enable-workflow-tool`,ExperimentalWorkflow=Object.freeze({kind:ENABLE_WORKFLOW_TOOL_SENTINEL_KIND});function isEnableWorkflowToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===ENABLE_WORKFLOW_TOOL_SENTINEL_KIND}export{ExperimentalWorkflow,defineDynamic,defineTool,disableTool,isDisabledToolSentinel,isEnableWorkflowToolSentinel};
1
+ import{DYNAMIC_SENTINEL_KIND,TOOL_BRAND}from"#shared/dynamic-tool-definition.js";import{stampDefinitionKey}from"#public/tool-result-narrowing.js";function defineTool(e){if(e.auth!==void 0)throw Error(`defineTool: The "auth" field is no longer supported. Pass auth providers inline to ctx.getToken(provider) or ctx.requireAuth(provider).`);return Object.assign(e,{[TOOL_BRAND]:!0}),stampDefinitionKey(e,`tool:${e.description}`),e}function defineDynamic(t){let n={kind:DYNAMIC_SENTINEL_KIND,events:t.events,...Object.hasOwn(t,`fallback`)?{fallback:t.fallback}:{}};return stampDefinitionKey(n,`dynamic:${Object.keys(t.events).join(`,`)}`),n}const DISABLED_TOOL_SENTINEL_KIND=`eve:disabled-tool`;function disableTool(){return{kind:DISABLED_TOOL_SENTINEL_KIND}}function isDisabledToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_TOOL_SENTINEL_KIND}const ENABLE_WORKFLOW_TOOL_SENTINEL_KIND=`eve:enable-workflow-tool`,ExperimentalWorkflow=Object.freeze({kind:ENABLE_WORKFLOW_TOOL_SENTINEL_KIND});function isEnableWorkflowToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===ENABLE_WORKFLOW_TOOL_SENTINEL_KIND}export{ExperimentalWorkflow,defineDynamic,defineTool,disableTool,isDisabledToolSentinel,isEnableWorkflowToolSentinel};
@@ -5,4 +5,4 @@
5
5
  * See {@link defineHook} for the authoring shape and
6
6
  * {@link HookContext} for the runtime context every handler receives.
7
7
  */
8
- export { type HookContext, type HookDefinition, type StreamEventHook, type StreamEventHooks, defineHook, } from "../definitions/hook.js";
8
+ export { type HookContext, type HookDefinition, type StreamEventHook, type StreamEventHooks, defineHook, } from "#public/definitions/hook.js";
@@ -1 +1 @@
1
- import{defineHook}from"../definitions/hook.js";export{defineHook};
1
+ import{defineHook}from"#public/definitions/hook.js";export{defineHook};