eve 0.76.3 → 0.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/src/channel/adapter.d.ts +13 -4
  3. package/dist/src/channel/channel-address.js +1 -1
  4. package/dist/src/channel/send-input.d.ts +6 -2
  5. package/dist/src/channel/send-input.js +1 -1
  6. package/dist/src/channel/session.js +1 -1
  7. package/dist/src/chunks/{use-eve-agent-BtKWTEpm.js → use-eve-agent-B8brxCk-.js} +46 -28
  8. package/dist/src/chunks/{use-eve-agent-jI6EsYAu.js → use-eve-agent-BSYe_Re7.js} +46 -28
  9. package/dist/src/cli/commands/vercel-non-interactive.d.ts +5 -3
  10. package/dist/src/cli/commands/vercel-non-interactive.js +1 -1
  11. package/dist/src/client/eve-agent-store.d.ts +2 -2
  12. package/dist/src/client/message-response.d.ts +2 -0
  13. package/dist/src/client/message-response.js +1 -1
  14. package/dist/src/client/session.js +1 -1
  15. package/dist/src/compiled/.vendor-stamp.json +9 -9
  16. package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +6 -1
  17. package/dist/src/compiled/@ai-sdk/anthropic/index.js +1 -1
  18. package/dist/src/compiled/@ai-sdk/google/index.d.ts +16 -3
  19. package/dist/src/compiled/@ai-sdk/google/index.js +6 -6
  20. package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +681 -348
  21. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  22. package/dist/src/compiled/@ai-sdk/openai/index.d.ts +56 -42
  23. package/dist/src/compiled/@ai-sdk/openai/index.js +5 -3
  24. package/dist/src/compiled/@ai-sdk/otel/index.js +3 -3
  25. package/dist/src/compiled/@ai-sdk/provider/index.d.ts +49 -15
  26. package/dist/src/compiled/@ai-sdk/provider/index.js +1 -1
  27. package/dist/src/compiled/@ai-sdk/provider-utils/index.d.ts +4 -1
  28. package/dist/src/compiled/_chunks/workflow/{dist-DXLdlucM.js → dist-C8k3N3BQ.js} +2 -4
  29. package/dist/src/compiled/_chunks/workflow/dist-DsNhwdzf.js +1 -0
  30. package/dist/src/compiled/_chunks/workflow/experimental-decision-DlvnsBD-.js +3 -0
  31. package/dist/src/compiler/extension-compatibility.d.ts +18 -18
  32. package/dist/src/compiler/extension-compatibility.js +1 -1
  33. package/dist/src/compiler/normalize-agent-config.js +1 -1
  34. package/dist/src/context/dynamic-model-lifecycle.js +1 -1
  35. package/dist/src/context/keys.d.ts +16 -0
  36. package/dist/src/context/keys.js +1 -1
  37. package/dist/src/evals/judge.js +1 -1
  38. package/dist/src/eve-channel/index.js +1 -1
  39. package/dist/src/eve-channel/types.d.ts +14 -1
  40. package/dist/src/execution/agent-sessions/steps.d.ts +2 -0
  41. package/dist/src/execution/agent-sessions/steps.js +1 -1
  42. package/dist/src/execution/cancelled-model-call-batch.js +1 -1
  43. package/dist/src/execution/catalog/connection-entry.js +1 -1
  44. package/dist/src/execution/catalog/search.js +1 -1
  45. package/dist/src/execution/catalog/step-catalog.d.ts +12 -3
  46. package/dist/src/execution/catalog/step-catalog.js +1 -1
  47. package/dist/src/execution/coordination-dispatch-shared.d.ts +1 -2
  48. package/dist/src/execution/coordination-dispatch-shared.js +1 -1
  49. package/dist/src/execution/coordination-dispatch-step.js +1 -1
  50. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  51. package/dist/src/execution/publish-session-events.js +1 -1
  52. package/dist/src/execution/sandbox/bindings/docker-container.js +1 -1
  53. package/dist/src/execution/sandbox/bindings/docker.js +1 -1
  54. package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
  55. package/dist/src/execution/sandbox/bindings/just-bash.d.ts +1 -1
  56. package/dist/src/execution/sandbox/bindings/just-bash.js +1 -1
  57. package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
  58. package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
  59. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  60. package/dist/src/execution/sandbox/read-file.d.ts +5 -5
  61. package/dist/src/execution/sandbox/read-file.js +2 -2
  62. package/dist/src/execution/sandbox/session.d.ts +6 -0
  63. package/dist/src/execution/sandbox/session.js +2 -2
  64. package/dist/src/execution/session/active-turn.d.ts +9 -1
  65. package/dist/src/execution/session/active-turn.js +1 -1
  66. package/dist/src/execution/session/checkpoint-migrations.js +1 -1
  67. package/dist/src/execution/session/end-sandbox-step.js +1 -1
  68. package/dist/src/execution/session/handoff.d.ts +1 -1
  69. package/dist/src/execution/session/handoff.js +1 -1
  70. package/dist/src/execution/session/next-input.js +1 -1
  71. package/dist/src/execution/session/turn-step.js +1 -1
  72. package/dist/src/execution/session/turn.d.ts +11 -4
  73. package/dist/src/execution/session/turn.js +1 -1
  74. package/dist/src/execution/session-inbox/inbox.d.ts +3 -0
  75. package/dist/src/execution/session-inbox/inbox.js +1 -1
  76. package/dist/src/execution/session-workflow-tool-run.d.ts +14 -12
  77. package/dist/src/execution/session-workflow-tool-run.js +1 -1
  78. package/dist/src/execution/tasks/start.d.ts +14 -1
  79. package/dist/src/execution/tasks/start.js +1 -1
  80. package/dist/src/execution/tasks/steps.d.ts +7 -4
  81. package/dist/src/execution/tasks/steps.js +1 -1
  82. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  83. package/dist/src/execution/tools/workflow/apply-run-messages-step.d.ts +20 -0
  84. package/dist/src/execution/tools/workflow/apply-run-messages-step.js +1 -0
  85. package/dist/src/execution/tools/workflow/ask.d.ts +0 -1
  86. package/dist/src/execution/tools/workflow/ask.js +1 -1
  87. package/dist/src/execution/tools/workflow/interrupt.d.ts +5 -2
  88. package/dist/src/execution/tools/workflow/interrupt.js +1 -1
  89. package/dist/src/execution/tools/workflow/messages.d.ts +14 -0
  90. package/dist/src/execution/tools/workflow/messages.js +1 -1
  91. package/dist/src/execution/tools/workflow/owner.d.ts +18 -6
  92. package/dist/src/execution/tools/workflow/owner.js +1 -1
  93. package/dist/src/execution/tools/workflow/start.d.ts +10 -0
  94. package/dist/src/execution/tools/workflow/start.js +1 -1
  95. package/dist/src/execution/tools/workflow/step.js +1 -1
  96. package/dist/src/execution/tools/workflow/workflow-owner-blocking.d.ts +12 -0
  97. package/dist/src/execution/tools/workflow/workflow-owner-blocking.js +1 -1
  98. package/dist/src/execution/tools/workflow/workflow.js +1 -1
  99. package/dist/src/execution/web-fetch/request.js +1 -1
  100. package/dist/src/execution/workflow-runtime.d.ts +18 -3
  101. package/dist/src/execution/workflow-runtime.js +1 -1
  102. package/dist/src/harness/attachment-staging.d.ts +13 -12
  103. package/dist/src/harness/attachment-staging.js +1 -1
  104. package/dist/src/harness/compaction/step.js +1 -1
  105. package/dist/src/harness/emission.js +1 -1
  106. package/dist/src/harness/hitl/approvals.d.ts +4 -7
  107. package/dist/src/harness/hitl/approvals.js +1 -1
  108. package/dist/src/harness/hitl/approved-calls.d.ts +11 -0
  109. package/dist/src/harness/hitl/approved-calls.js +1 -1
  110. package/dist/src/harness/hitl/candidates.d.ts +0 -4
  111. package/dist/src/harness/hitl/candidates.js +1 -1
  112. package/dist/src/harness/hitl/delivery.d.ts +0 -1
  113. package/dist/src/harness/hitl/delivery.js +1 -1
  114. package/dist/src/harness/hitl/index.d.ts +2 -2
  115. package/dist/src/harness/hitl/index.js +1 -1
  116. package/dist/src/harness/hitl/intake.d.ts +2 -2
  117. package/dist/src/harness/hitl/intake.js +1 -1
  118. package/dist/src/harness/model-call/call.js +1 -1
  119. package/dist/src/harness/model-call/errors.d.ts +10 -0
  120. package/dist/src/harness/model-call/errors.js +1 -1
  121. package/dist/src/harness/model-call/run.js +1 -1
  122. package/dist/src/harness/session-machine/state.d.ts +0 -2
  123. package/dist/src/harness/session-machine/transitions.js +1 -1
  124. package/dist/src/harness/session-machine/view.d.ts +0 -3
  125. package/dist/src/harness/session-machine/view.js +1 -1
  126. package/dist/src/harness/step/after-model.js +1 -1
  127. package/dist/src/harness/token-estimate.js +1 -1
  128. package/dist/src/harness/tool-loop.js +1 -1
  129. package/dist/src/harness/tools.js +1 -1
  130. package/dist/src/internal/application/package.js +1 -1
  131. package/dist/src/internal/application/stamped-package-version.js +1 -1
  132. package/dist/src/internal/attachments/data.d.ts +29 -10
  133. package/dist/src/internal/attachments/data.js +1 -1
  134. package/dist/src/internal/attachments/errors.d.ts +2 -2
  135. package/dist/src/internal/attachments/media-metadata.d.ts +15 -0
  136. package/dist/src/internal/attachments/media-metadata.js +1 -1
  137. package/dist/src/internal/attachments/public-link.d.ts +5 -0
  138. package/dist/src/internal/attachments/public-link.js +1 -0
  139. package/dist/src/internal/attachments/sandbox-refs.d.ts +7 -6
  140. package/dist/src/internal/attachments/sandbox-refs.js +1 -1
  141. package/dist/src/internal/attachments/url-refs.js +1 -1
  142. package/dist/src/protocol/message.d.ts +7 -0
  143. package/dist/src/protocol/message.js +2 -2
  144. package/dist/src/public/channels/attachment-error.d.ts +14 -0
  145. package/dist/src/public/channels/attachment-error.js +1 -0
  146. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +1 -1
  147. package/dist/src/public/channels/discord/defaults.js +1 -1
  148. package/dist/src/public/channels/github/defaults.js +1 -1
  149. package/dist/src/public/channels/index.d.ts +2 -0
  150. package/dist/src/public/channels/index.js +1 -1
  151. package/dist/src/public/channels/linear/defaults.js +1 -1
  152. package/dist/src/public/channels/slack/api.d.ts +10 -0
  153. package/dist/src/public/channels/slack/api.js +1 -1
  154. package/dist/src/public/channels/slack/approval-cards.js +1 -1
  155. package/dist/src/public/channels/slack/attachments.d.ts +29 -19
  156. package/dist/src/public/channels/slack/attachments.js +1 -1
  157. package/dist/src/public/channels/slack/defaults.js +1 -1
  158. package/dist/src/public/channels/slack/inbound.d.ts +6 -0
  159. package/dist/src/public/channels/slack/inbound.js +1 -1
  160. package/dist/src/public/channels/slack/interaction-cards.d.ts +6 -0
  161. package/dist/src/public/channels/slack/interaction-cards.js +1 -1
  162. package/dist/src/public/channels/slack/interactions.js +1 -1
  163. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  164. package/dist/src/public/channels/teams/defaults.js +1 -1
  165. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  166. package/dist/src/public/channels/telegram/defaults.js +1 -1
  167. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  168. package/dist/src/public/channels/twilio/defaults.js +1 -1
  169. package/dist/src/public/definitions/channel.js +1 -1
  170. package/dist/src/runtime/channels/registry.js +1 -1
  171. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  172. package/dist/src/setup/scaffold/create/project.js +2 -2
  173. package/dist/src/setup/scaffold/update/web-options.js +1 -1
  174. package/dist/src/shared/agent-definition.d.ts +2 -1
  175. package/dist/src/shared/channel-definition.d.ts +16 -4
  176. package/dist/src/svelte/index.d.ts +0 -1
  177. package/dist/src/svelte/index.js +1 -1
  178. package/dist/src/svelte/use-eve-agent.js +1 -1
  179. package/dist/src/tools/provided/ask-question-workflow.js +1 -1
  180. package/dist/src/tools/provided/read-file.d.ts +3 -2
  181. package/dist/src/tools/provided/read-file.js +2 -2
  182. package/dist/src/tools/provided/sleep-workflow.js +1 -1
  183. package/dist/src/vue/index.d.ts +0 -1
  184. package/dist/src/vue/index.js +1 -1
  185. package/dist/src/vue/use-eve-agent.js +1 -1
  186. package/docs/agent-config.md +12 -4
  187. package/docs/channels/chat-sdk.mdx +1 -1
  188. package/docs/channels/custom.mdx +30 -3
  189. package/docs/channels/eve.mdx +34 -0
  190. package/docs/channels/slack.mdx +5 -1
  191. package/docs/concepts/built-in-tools.md +10 -10
  192. package/docs/concepts/sessions-runs-and-streaming.md +2 -2
  193. package/docs/evals/overview.mdx +1 -1
  194. package/docs/guides/client/streaming.mdx +1 -1
  195. package/docs/guides/dynamic-capabilities.md +6 -2
  196. package/docs/memory/custom-provider.md +3 -1
  197. package/docs/sandbox/index.mdx +10 -4
  198. package/docs/tools/human-in-the-loop.md +1 -1
  199. package/docs/tools/overview.mdx +1 -1
  200. package/package.json +11 -11
  201. package/dist/src/compiled/_chunks/workflow/experimental-decision-BrfWf6-n.js +0 -3
  202. package/dist/src/context/caller-scope.d.ts +0 -7
  203. package/dist/src/context/caller-scope.js +0 -1
  204. package/dist/src/execution/tools/workflow/emit-workflow-tool-run-report-step.d.ts +0 -13
  205. package/dist/src/execution/tools/workflow/emit-workflow-tool-run-report-step.js +0 -1
  206. package/dist/src/harness/hitl/approved-call-callers.d.ts +0 -14
  207. package/dist/src/harness/hitl/approved-call-callers.js +0 -1
  208. package/dist/src/internal/attachments/refs.d.ts +0 -55
  209. package/dist/src/internal/attachments/refs.js +0 -1
@@ -1,3 +1,3 @@
1
- import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{formatTelegramAuthorizationDisplayName,renderTelegramAuthorizationCompleted,renderTelegramAuthorizationPrompt,renderTelegramAuthorizationStatus}from"#public/channels/telegram/authorization.js";import{registerTelegramFreeformPrompt,registerTelegramHitlPrompt,renderTelegramInputRequest,takeTelegramResolvedPrompt}from"#public/channels/telegram/hitl.js";const log=createLogger(`telegram.defaults`);function defaultTelegramAuth(e){let t=e.from;if(!t)return null;let n={chat_id:e.chat.id,chat_type:e.chat.type,message_id:e.messageId,user_id:t.id};e.chat.title!==void 0&&(n.chat_title=e.chat.title),e.messageThreadId!==void 0&&(n.message_thread_id=String(e.messageThreadId)),t.username!==void 0&&(n.username=t.username);let r=e.chat.type===`group`||e.chat.type===`supergroup`,i=r?`telegram:${e.chat.id}:${t.id}`:`telegram:${t.id}`;return{attributes:n,authenticator:`telegram-webhook`,issuer:r?`telegram:${e.chat.id}`:`telegram`,principalId:i,principalType:t.isBot?`service`:`user`}}async function defaultOnMessage(e,t){return shouldDispatchTelegramMessage(t,e.telegram.botUsername)?(await e.telegram.startTyping(),{auth:defaultTelegramAuth(t)}):null}function isTelegramBotMentioned(e,t){if(t===void 0)return!1;let n=e.text||e.caption;if(isTargetedBotCommand(n,t))return!0;let r=t.replace(/[.*+?^${}()|[\]\\]/gu,`\\$&`);return RegExp(`(?:^|[^A-Za-z0-9_])@${r}(?=$|[^A-Za-z0-9_])`,`iu`).test(n)}const defaultEvents={async"turn.started"(e,t,n){await t.telegram.startTyping()},async"authorization.required"(e,t,n){if(e.candidateId!==void 0)return;let r=formatTelegramAuthorizationDisplayName(e.name,e.authorization?.displayName),i=t.telegram.chatType===`private`,a=t.state.pendingAuthMessageIds??{};if(i){try{let n=await t.telegram.post(renderTelegramAuthorizationPrompt(e));n.id&&(t.state.pendingAuthMessageIds={...a,[e.name]:n.id})}catch(t){log.error(`Telegram authorization prompt delivery failed`,{error:t,name:e.name})}return}if(a[e.name]===void 0)try{let n=await t.telegram.post(renderTelegramAuthorizationStatus({displayName:r,requesterUserId:t.state.triggeringUserId}));n.id&&(t.state.pendingAuthMessageIds={...a,[e.name]:n.id})}catch(t){log.error(`Telegram authorization status delivery failed`,{error:t,name:e.name})}},async"authorization.completed"(e,t,n){if(e.outcome===`authorized`&&e.candidateId===void 0&&await t.telegram.startTyping(),e.candidateId!==void 0)return;let r=t.state.pendingAuthMessageIds??{},i=r[e.name];if(i===void 0)return;let a=formatTelegramAuthorizationDisplayName(e.name,e.authorization?.displayName);try{await t.telegram.editMessageText({messageId:i,replyMarkup:{inline_keyboard:[]},text:renderTelegramAuthorizationCompleted({displayName:a,outcome:e.outcome,reason:e.reason})})}catch(t){log.error(`Telegram authorization status edit failed`,{error:t,name:e.name})}let o={...r};delete o[e.name],t.state.pendingAuthMessageIds=o},async"actions.requested"(e,t,n){await t.telegram.startTyping()},async"input.requested"(e,t,n){for(let n of e.requests){let e=renderTelegramInputRequest(n,t.state),r=await t.telegram.post({reply_markup:e.replyMarkup,text:e.text});r.id&&(e.freeformRequestId===void 0?registerTelegramHitlPrompt(t.state,n,{messageId:r.id,text:e.text}):registerTelegramFreeformPrompt(t.state,{messageId:r.id,requestId:e.freeformRequestId}))}},async"input.resolved"(e,t,n){for(let n of e.resolutions){let e=takeTelegramResolvedPrompt(t.state,n);if(e!==void 0)try{await t.telegram.editMessageText({...e,replyMarkup:{inline_keyboard:[]}})}catch(e){log.warn(`Telegram answered prompt edit failed`,{error:e,requestId:n.requestId})}}},async"message.completed"(e,t,n){e.finishReason!==`tool-calls`&&e.message&&await t.telegram.post(e.message)},async"turn.failed"(e,t,n){let r=formatErrorHint(e),i=extractErrorId(e.details);await t.telegram.post([`I hit an error while handling your request${r}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[``,`Error id: ${i}`]:[]].join(`
1
+ import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{formatTelegramAuthorizationDisplayName,renderTelegramAuthorizationCompleted,renderTelegramAuthorizationPrompt,renderTelegramAuthorizationStatus}from"#public/channels/telegram/authorization.js";import{registerTelegramFreeformPrompt,registerTelegramHitlPrompt,renderTelegramInputRequest,takeTelegramResolvedPrompt}from"#public/channels/telegram/hitl.js";const log=createLogger(`telegram.defaults`);function defaultTelegramAuth(e){let t=e.from;if(!t)return null;let n={chat_id:e.chat.id,chat_type:e.chat.type,message_id:e.messageId,user_id:t.id};e.chat.title!==void 0&&(n.chat_title=e.chat.title),e.messageThreadId!==void 0&&(n.message_thread_id=String(e.messageThreadId)),t.username!==void 0&&(n.username=t.username);let r=e.chat.type===`group`||e.chat.type===`supergroup`,i=r?`telegram:${e.chat.id}:${t.id}`:`telegram:${t.id}`;return{attributes:n,authenticator:`telegram-webhook`,issuer:r?`telegram:${e.chat.id}`:`telegram`,principalId:i,principalType:t.isBot?`service`:`user`}}async function defaultOnMessage(e,t){return shouldDispatchTelegramMessage(t,e.telegram.botUsername)?(await e.telegram.startTyping(),{auth:defaultTelegramAuth(t)}):null}function isTelegramBotMentioned(e,t){if(t===void 0)return!1;let n=e.text||e.caption;if(isTargetedBotCommand(n,t))return!0;let r=t.replace(/[.*+?^${}()|[\]\\]/gu,`\\$&`);return RegExp(`(?:^|[^A-Za-z0-9_])@${r}(?=$|[^A-Za-z0-9_])`,`iu`).test(n)}const defaultEvents={async"turn.started"(e,t,n){await t.telegram.startTyping()},async"authorization.required"(e,t,n){if(e.candidateId!==void 0)return;let r=formatTelegramAuthorizationDisplayName(e.name,e.authorization?.displayName),i=t.telegram.chatType===`private`,a=t.state.pendingAuthMessageIds??{};if(i){try{let n=await t.telegram.post(renderTelegramAuthorizationPrompt(e));n.id&&(t.state.pendingAuthMessageIds={...a,[e.name]:n.id})}catch(t){log.error(`Telegram authorization prompt delivery failed`,{error:t,name:e.name})}return}if(a[e.name]===void 0)try{let n=await t.telegram.post(renderTelegramAuthorizationStatus({displayName:r,requesterUserId:t.state.triggeringUserId}));n.id&&(t.state.pendingAuthMessageIds={...a,[e.name]:n.id})}catch(t){log.error(`Telegram authorization status delivery failed`,{error:t,name:e.name})}},async"authorization.completed"(e,t,n){if(e.outcome===`authorized`&&e.candidateId===void 0&&await t.telegram.startTyping(),e.candidateId!==void 0)return;let r=t.state.pendingAuthMessageIds??{},i=r[e.name];if(i===void 0)return;let a=formatTelegramAuthorizationDisplayName(e.name,e.authorization?.displayName);try{await t.telegram.editMessageText({messageId:i,replyMarkup:{inline_keyboard:[]},text:renderTelegramAuthorizationCompleted({displayName:a,outcome:e.outcome,reason:e.reason})})}catch(t){log.error(`Telegram authorization status edit failed`,{error:t,name:e.name})}let o={...r};delete o[e.name],t.state.pendingAuthMessageIds=o},async"actions.requested"(e,t,n){await t.telegram.startTyping()},async"input.requested"(e,t,n){for(let n of e.requests){let e=renderTelegramInputRequest(n,t.state),r=await t.telegram.post({reply_markup:e.replyMarkup,text:e.text});r.id&&(e.freeformRequestId===void 0?registerTelegramHitlPrompt(t.state,n,{messageId:r.id,text:e.text}):registerTelegramFreeformPrompt(t.state,{messageId:r.id,requestId:e.freeformRequestId}))}},async"input.resolved"(e,t,n){for(let n of e.resolutions){let e=takeTelegramResolvedPrompt(t.state,n);if(e!==void 0)try{await t.telegram.editMessageText({...e,replyMarkup:{inline_keyboard:[]}})}catch(e){log.warn(`Telegram answered prompt edit failed`,{error:e,requestId:n.requestId})}}},async"message.completed"(e,t,n){e.finishReason!==`tool-calls`&&e.message&&await t.telegram.post(e.message)},async"turn.failed"(e,t,n){if(e.terminal)return;let r=formatErrorHint(e),i=extractErrorId(e.details);await t.telegram.post([`I hit an error while handling your request${r}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[``,`Error id: ${i}`]:[]].join(`
2
2
  `))},async"session.failed"(e,t){let n=formatErrorHint(e),r=extractErrorId(e.details);await t.telegram.post([`This session could not recover from an error${n}.`,``,`Start a new message to continue.`,...r?[``,`Error id: ${r}`]:[]].join(`
3
3
  `))}};function shouldDispatchTelegramMessage(e,t){if(e.from?.isBot===!0||e.chat.type===`channel`)return!1;let n=e.text||e.caption;return n.trim().length>0||e.attachments.length>0?!!(e.chat.type===`private`||e.replyToMessage?.from?.isBot===!0||isBotCommand(n,t)||t!==void 0&&mentionsBotUsername(n,t)):!1}function isBotCommand(e,t){let n=/^\/(?<command>[A-Za-z0-9_]+)(?:@(?<target>[A-Za-z0-9_]+))?(?:\s|$)/u.exec(e);if(!n)return!1;let r=n.groups?.target;return r===void 0||t!==void 0&&r.toLowerCase()===t.toLowerCase()}function isTargetedBotCommand(e,t){return/^\/[A-Za-z0-9_]+@(?<target>[A-Za-z0-9_]+)(?:\s|$)/u.exec(e)?.groups?.target?.toLowerCase()===t.toLowerCase()}function mentionsBotUsername(e,t){return e.toLowerCase().includes(`@${t.toLowerCase()}`)}export{defaultEvents,defaultOnMessage,defaultTelegramAuth,isTelegramBotMentioned};
@@ -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/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{formatTelegramContextBlock,parseTelegramUpdate}from"#public/channels/telegram/inbound.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,editTelegramMessageText,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX}from"#public/channels/telegram/authorization.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponses}from"#public/channels/telegram/hitl.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage,defaultTelegramAuth,isTelegramBotMentioned}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";import{dispatchTelegramAuthorizationCallback}from"#public/channels/telegram/authorization-callback.js";import{initialTelegramState,stateFromTelegramCallbackQuery,stateFromTelegramMessage,telegramContinuationTokenFromState}from"#public/channels/telegram/state.js";import{telegramInstrumentation}from"#public/channels/telegram/audience.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,r={...defaultEvents,...e.events},i=defineChannel({kindHint:`telegram`,turnPolicy:e.turnPolicy,state:initialTelegramState(e.botUsername),...telegramInstrumentation,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,{from:i,resolveSession: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 l=parseTelegramUpdate(c);return l===null?new Response(`ok`):l.kind===`message`?(o(dispatchMessage({config:e,message:l.message,onMessage:n,uploadPolicy:t,from:i})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:l.callbackQuery,from:i,resolveSession:a})),new Response(`ok`))})],async receive(t,{from: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=readChatId(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(telegramContinuationTokenFromState(c)).send(t.message,{auth:t.auth,state:c})},events:r});return attachTelegramDeliver(i),i}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let t=e.config.api,n=e.state,r=e.config.credentials;function anchor(t){let r=n.chatType??t.chatType??null;n.chatType===null&&t.chatType!==void 0&&(n.chatType=t.chatType),t.id&&shouldAnchorTelegramConversation(r)&&(n.conversationId=t.id,n.chatId&&e.session?.continuation?.alias(telegramContinuationToken({chatId:n.chatId,conversationId:t.id,messageThreadId:n.messageThreadId??void 0})))}async function sendOne(e){let t=await sendMessage(e);return anchor(t),t}async function sendMessage(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for outbound message.`);return sendTelegramMessage({apiBaseUrl:t?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??n.messageThreadId??void 0},credentials:r,fetch:t?.fetch,fileBaseUrl:t?.fileBaseUrl,chatId:i})}return{botUsername:n.botUsername??e.config.botUsername,chatId:n.chatId??``,chatType:n.chatType??void 0,conversationId:n.conversationId??void 0,messageThreadId:n.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:t?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:r,fetch:t?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},editMessageText(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for text edit.`);return editTelegramMessageText({apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup,text:e.text})},post(e){return postTelegramMessage(e,sendOne)},postEphemeral(e,t,n){return postTelegramMessage({...typeof t==`string`?{text:t}:t,ephemeral_message_parameters:{...n?.callbackQueryId===void 0?{}:{callback_query_id:n.callbackQueryId},receiver_user_id:e}},sendMessage)},request(e,n){return callTelegramApi({apiBaseUrl:t?.apiBaseUrl,body:n,botToken:r?.botToken,fetch:t?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let i=n.chatId??``;if(i)try{await sendTelegramChatAction({action:e,apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageThreadId:n.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:i})}}}}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}:{...n.ephemeral_message_parameters===void 0?{}:{ephemeral_message_parameters:n.ephemeral_message_parameters},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=stateFromTelegramMessage(e.message,e.config.botUsername),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,isMentioned:isTelegramBotMentioned(e.message,e.config.botUsername),messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),s=r.context??[];try{await e.from(telegramContinuationTokenFromState(t)).send(a,{auth:r.auth,context:[o,...s],state:t,title:r.title})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromTelegramCallbackQuery(e.query,e.config.botUsername),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX)===!0){await dispatchTelegramAuthorizationCallback({continuationToken:telegramContinuationTokenFromState(t),query:e.query,resolveSession:e.resolveSession,state:t,telegram:n});return}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.from(telegramContinuationTokenFromState(t)).respond([telegramCallbackInputResponse(e.query.data)],{auth:defaultTelegramAuth({...e.query.message,from:e.query.from})})}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.state,r=[...e.inputResponses??[],...telegramReplyInputResponses(n?.replyToBotMessageId,e.message)];if(r.some(isTelegramSyntheticResponse)){let n=resolveTelegramInputResponses(t.state,r);return n.length>0?{inputResponses:n,context:e.context}:e.message===void 0?void 0:{message:e.message,context:e.context}}return defaultDeliverResult(e)}}function readChatId(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,editTelegramMessageText,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX}from"#public/channels/telegram/authorization.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponses}from"#public/channels/telegram/hitl.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage,defaultTelegramAuth,isTelegramBotMentioned}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";import{dispatchTelegramAuthorizationCallback}from"#public/channels/telegram/authorization-callback.js";import{initialTelegramState,stateFromTelegramCallbackQuery,stateFromTelegramMessage,telegramContinuationTokenFromState}from"#public/channels/telegram/state.js";import{telegramInstrumentation}from"#public/channels/telegram/audience.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,r={...defaultEvents,...e.events},i=defineChannel({kindHint:`telegram`,turnPolicy:e.turnPolicy,uploadPolicy:e.uploadPolicy,state:initialTelegramState(e.botUsername),...telegramInstrumentation,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,{from:i,resolveSession: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 l=parseTelegramUpdate(c);return l===null?new Response(`ok`):l.kind===`message`?(o(dispatchMessage({config:e,message:l.message,onMessage:n,uploadPolicy:t,from:i})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:l.callbackQuery,from:i,resolveSession:a})),new Response(`ok`))})],async receive(t,{from: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=readChatId(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(telegramContinuationTokenFromState(c)).send(t.message,{auth:t.auth,state:c})},events:r});return attachTelegramDeliver(i),i}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let t=e.config.api,n=e.state,r=e.config.credentials;function anchor(t){let r=n.chatType??t.chatType??null;n.chatType===null&&t.chatType!==void 0&&(n.chatType=t.chatType),t.id&&shouldAnchorTelegramConversation(r)&&(n.conversationId=t.id,n.chatId&&e.session?.continuation?.alias(telegramContinuationToken({chatId:n.chatId,conversationId:t.id,messageThreadId:n.messageThreadId??void 0})))}async function sendOne(e){let t=await sendMessage(e);return anchor(t),t}async function sendMessage(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for outbound message.`);return sendTelegramMessage({apiBaseUrl:t?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??n.messageThreadId??void 0},credentials:r,fetch:t?.fetch,fileBaseUrl:t?.fileBaseUrl,chatId:i})}return{botUsername:n.botUsername??e.config.botUsername,chatId:n.chatId??``,chatType:n.chatType??void 0,conversationId:n.conversationId??void 0,messageThreadId:n.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:t?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:r,fetch:t?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},editMessageText(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for text edit.`);return editTelegramMessageText({apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup,text:e.text})},post(e){return postTelegramMessage(e,sendOne)},postEphemeral(e,t,n){return postTelegramMessage({...typeof t==`string`?{text:t}:t,ephemeral_message_parameters:{...n?.callbackQueryId===void 0?{}:{callback_query_id:n.callbackQueryId},receiver_user_id:e}},sendMessage)},request(e,n){return callTelegramApi({apiBaseUrl:t?.apiBaseUrl,body:n,botToken:r?.botToken,fetch:t?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let i=n.chatId??``;if(i)try{await sendTelegramChatAction({action:e,apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageThreadId:n.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:i})}}}}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}:{...n.ephemeral_message_parameters===void 0?{}:{ephemeral_message_parameters:n.ephemeral_message_parameters},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=stateFromTelegramMessage(e.message,e.config.botUsername),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,isMentioned:isTelegramBotMentioned(e.message,e.config.botUsername),messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),s=r.context??[];try{await e.from(telegramContinuationTokenFromState(t)).send(a,{auth:r.auth,context:[o,...s],state:t,title:r.title})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromTelegramCallbackQuery(e.query,e.config.botUsername),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX)===!0){await dispatchTelegramAuthorizationCallback({continuationToken:telegramContinuationTokenFromState(t),query:e.query,resolveSession:e.resolveSession,state:t,telegram:n});return}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.from(telegramContinuationTokenFromState(t)).respond([telegramCallbackInputResponse(e.query.data)],{auth:defaultTelegramAuth({...e.query.message,from:e.query.from})})}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.state,r=[...e.inputResponses??[],...telegramReplyInputResponses(n?.replyToBotMessageId,e.message)];if(r.some(isTelegramSyntheticResponse)){let n=resolveTelegramInputResponses(t.state,r);return n.length>0?{inputResponses:n,context:e.context}:e.message===void 0?void 0:{message:e.message,context:e.context}}return defaultDeliverResult(e)}}function readChatId(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}export{telegramChannel};
@@ -1,5 +1,5 @@
1
1
  import{extractErrorId,formatErrorHint}from"#internal/logging.js";import{displayProperName}from"#shared/display-name.js";import{renderTextInputRequest}from"#channel/resolve-text.js";import{promptQueueEvents}from"#channel/prompt-queue.js";function defaultTwilioAuth(e){let t={channel:e.channel,from:e.from};return e.to!==void 0&&(t.to=e.to),{attributes:t,authenticator:`twilio-webhook`,issuer:`twilio`,principalId:`twilio:${e.from}`,principalType:`user`}}function defaultOnText(e,t){return{auth:defaultTwilioAuth({channel:`text`,from:t.from,to:t.to})}}function defaultOnVoice(e,t){return{}}function defaultOnVoiceTranscription(e,t){return{auth:defaultTwilioAuth({channel:`voice`,from:t.from,to:t.to})}}const defaultEvents={async"message.completed"(e,t,n){e.finishReason!==`tool-calls`&&e.message&&await t.twilio.sendMessage(e.message)},...promptQueueEvents(showPrompt),async"authorization.required"(e,t,n){if(e.candidateId!==void 0)return;let r=e.authorization;await t.twilio.sendMessage([`Sign in to ${r?.displayName??displayProperName(e.name)} to continue.`,r?.instructions,r?.userCode?`Code: ${r.userCode}`:void 0,r?.url].filter(e=>e!==void 0&&e.length>0).join(`
2
2
 
3
- `))},async"authorization.completed"(e,t,n){e.candidateId===void 0&&await t.twilio.sendMessage(renderAuthorizationOutcome({displayName:e.authorization?.displayName??displayProperName(e.name),outcome:e.outcome,reason:e.reason}))},async"turn.failed"(e,t,n){let r=formatErrorHint(e),i=extractErrorId(e.details);await t.twilio.sendMessage([`I hit an error while handling your request${r}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[``,`Error id: ${i}`]:[]].join(`
3
+ `))},async"authorization.completed"(e,t,n){e.candidateId===void 0&&await t.twilio.sendMessage(renderAuthorizationOutcome({displayName:e.authorization?.displayName??displayProperName(e.name),outcome:e.outcome,reason:e.reason}))},async"turn.failed"(e,t,n){if(e.terminal)return;let r=formatErrorHint(e),i=extractErrorId(e.details);await t.twilio.sendMessage([`I hit an error while handling your request${r}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...i?[``,`Error id: ${i}`]:[]].join(`
4
4
  `))},async"session.failed"(e,t){let n=formatErrorHint(e),r=extractErrorId(e.details);await t.twilio.sendMessage([`This session could not recover from an error${n}.`,``,`Start a new message to continue.`,...r?[``,`Error id: ${r}`]:[]].join(`
5
5
  `))}};async function showPrompt(e,t){let n=renderTextInputRequest(t);if((t.options??[]).length===0){await e.twilio.sendMessage(n);return}let r=t.allowFreeform===!0?`Reply with a number, or with your own answer.`:`Reply with a number to choose.`;await e.twilio.sendMessage(`${n}\n\n${r}`)}function renderAuthorizationOutcome(e){if(e.outcome===`authorized`)return`${e.displayName} connected.`;if(e.outcome===`declined`)return`${e.displayName} sign-in cancelled.`;let t=e.outcome===`timed-out`?`timed out`:e.outcome,n=e.reason===void 0?``:` (${e.reason})`;return`${e.displayName} sign-in ${t}${n}.`}export{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription};
@@ -1 +1 @@
1
- import{createMetadataAudienceProjector}from"#channel/audience.js";import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{SessionStrandedError}from"#channel/session-stranded-error.js";import{normalizeChannelCors}from"#channel/cors.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,HEAD,OPTIONS,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(e){let t=buildAdapter(e),n=normalizeChannelCors(e.cors);return{__kind:CHANNEL_SENTINEL,routes:e.routes,adapter:t,cors:n,receive:e.receive,turnPolicy:e.turnPolicy}}const eventTypes=Object.keys({"approval.candidate":null,"approval.settled":null,"context.cleared":null,"compaction.requested":null,"compaction.completed":null,"turn.started":null,"actions.requested":null,"action.partial":null,"action.result":null,"message.completed":null,"message.appended":null,"reasoning.appended":null,"reasoning.completed":null,"step.started":null,"step.completed":null,"input.requested":null,"input.resolved":null,"task.started":null,"task.settled":null,"turn.waiting":null,"turn.failed":null,"turn.completed":null,"turn.cancelled":null,"session.failed":null,"session.completed":null,"session.waiting":null,"authorization.required":null,"authorization.completed":null});function buildAdapter(e){let t=e.state!=null,n=e.context!=null,r=e.fetchFile!==void 0,i=e.metadata,a=i!==void 0,o=e.audience,s=t||n||a,c={},l=!1,u={kind:e.kindHint??`defineChannel`},d=e.events;for(let e of eventTypes){let t=d?.[e];t&&(l=!0,c[e]=(n,r)=>{let{session:i,...a}=r,o={...a,continuation:i?.continuation===void 0?void 0:{token:i.continuation.token,alias:e=>i.continuation?.alias(e)}};if(e===`session.failed`)return t(n,o);let s=buildCallbackContext();return t(n,o,s)})}return!s&&!l&&!r?{kind:e.kindHint??HTTP_ADAPTER_KIND,...o===void 0?void 0:{instrumentation:{audience:o}}}:{kind:e.kindHint??`defineChannel`,state:t?{...e.state}:{},fetchFile:e.fetchFile,instrumentation:i===void 0&&o===void 0?void 0:{...i===void 0?void 0:{metadata(e){let{audience:t,...n}=i(e);return n}},...o===void 0?i===void 0?void 0:{audience:createMetadataAudienceProjector(u,i)}:{audience:o}},createAdapterContext(t){let r=t.state,i=t.session;return{...n?e.context(r,i):{},state:r,ctx:t.ctx,session:i}},deliver(t,n){return e.deliver===void 0?defaultDeliverResult(t):e.deliver(t,n)},...c}}export{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,SessionStrandedError,WS,defineChannel,disableRoute,isDisabledRouteSentinel};
1
+ import{createMetadataAudienceProjector}from"#channel/audience.js";import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{SessionStrandedError}from"#channel/session-stranded-error.js";import{normalizeChannelCors}from"#channel/cors.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,HEAD,OPTIONS,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(e){let t=buildAdapter(e),n=normalizeChannelCors(e.cors);return{__kind:CHANNEL_SENTINEL,routes:e.routes,adapter:t,cors:n,receive:e.receive,turnPolicy:e.turnPolicy}}const eventTypes=Object.keys({"approval.candidate":null,"approval.settled":null,"context.cleared":null,"compaction.requested":null,"compaction.completed":null,"turn.started":null,"actions.requested":null,"action.partial":null,"action.result":null,"message.completed":null,"message.appended":null,"reasoning.appended":null,"reasoning.completed":null,"step.started":null,"step.completed":null,"input.requested":null,"input.resolved":null,"task.started":null,"task.settled":null,"turn.waiting":null,"turn.failed":null,"turn.completed":null,"turn.cancelled":null,"session.failed":null,"session.completed":null,"session.waiting":null,"authorization.required":null,"authorization.completed":null});function buildAdapter(e){let t=e.state!=null,n=e.context!=null,r=e.fetchFile!==void 0,i=e.metadata,a=i!==void 0,o=e.audience,s=e.uploadPolicy===void 0?void 0:mergeUploadPolicy(e.uploadPolicy),c=t||n||a||s!==void 0,l={},u=!1,d={kind:e.kindHint??`defineChannel`},f=e.events;for(let e of eventTypes){let t=f?.[e];t&&(u=!0,l[e]=(n,r)=>{let{session:i,...a}=r,o={...a,continuation:i?.continuation===void 0?void 0:{token:i.continuation.token,alias:e=>i.continuation?.alias(e)}};if(e===`session.failed`)return t(n,o);let s=buildCallbackContext();return t(n,o,s)})}return!c&&!u&&!r?{kind:e.kindHint??HTTP_ADAPTER_KIND,...o===void 0?void 0:{instrumentation:{audience:o}}}:{kind:e.kindHint??`defineChannel`,state:t?{...e.state}:{},fetchFile:e.fetchFile,uploadPolicy:s,instrumentation:i===void 0&&o===void 0?void 0:{...i===void 0?void 0:{metadata(e){let{audience:t,...n}=i(e);return n}},...o===void 0?i===void 0?void 0:{audience:createMetadataAudienceProjector(d,i)}:{audience:o}},createAdapterContext(t){let r=t.state,i=t.session;return{...n?e.context(r,i):{},state:r,ctx:t.ctx,session:i}},deliver(t,n){return e.deliver===void 0?defaultDeliverResult(t):e.deliver(t,n)},...l}}export{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,SessionStrandedError,WS,defineChannel,disableRoute,isDisabledRouteSentinel};
@@ -1 +1 @@
1
- import{getAdapterKind}from"#channel/adapter.js";import{SCHEDULE_ADAPTER}from"#channel/schedule.js";import{HTTP_ADAPTER}from"#channel/http.js";import{SUBAGENT_ADAPTER}from"#subagents/adapter.js";import{RuntimeRegistryError}from"#internal/runtime-registry.js";const FRAMEWORK_ADAPTERS=[HTTP_ADAPTER,SUBAGENT_ADAPTER,SCHEDULE_ADAPTER],ADAPTER_NON_EVENT_FIELDS=new Set([`kind`,`state`,`deliver`,`createAdapterContext`,`fetchFile`,`instrumentation`]);function createRuntimeAdapterRegistry(e){let t=new Map,n=new Set;for(let e of FRAMEWORK_ADAPTERS){let r=requireAdapterKind(e);n.add(r),t.set(r,e)}for(let r of e.channels){if(r.adapter===void 0)continue;let e={logicalPath:r.logicalPath,sourceId:r.sourceId},i=r.adapter,a=requireAdapterKind(i,e);if(n.has(a)){if(carriesAdapterBehavior(i))throw new RuntimeRegistryError(`adapter`,`Channel adapter kind "${a}" is reserved by the framework. A route-declared adapter may share a framework kind only as a pass-through with no \`deliver\` hook, event handlers, \`attachments\` resolver, or \`createAdapterContext\` factory. Use a custom \`kind\` to add channel-specific behavior.`,{...e,entryName:a});continue}t.set(a,i)}return{adaptersByKind:t}}function deserializeRuntimeAdapter(e,t){let n=t,r=e.adaptersByKind.get(n.kind);if(r===void 0)throw Error(`Unknown adapter kind: "${n.kind}". Declare the adapter on the route that starts this session so the runtime can rehydrate it.`);return{...r,state:n.state}}function requireAdapterKind(e,t){let n=getAdapterKind(e);if(typeof n!=`string`||n.length===0)throw new RuntimeRegistryError(`adapter`,"Adapters must declare a non-empty `kind` field.",{entryName:`unknown`,logicalPath:t?.logicalPath,sourceId:t?.sourceId});return n}function carriesAdapterBehavior(e){if(e.deliver!==void 0||e.fetchFile!==void 0||e.createAdapterContext!==void 0)return!0;for(let[t,n]of Object.entries(e))if(!ADAPTER_NON_EVENT_FIELDS.has(t)&&typeof n==`function`)return!0;return!1}export{createRuntimeAdapterRegistry,deserializeRuntimeAdapter};
1
+ import{getAdapterKind}from"#channel/adapter.js";import{SCHEDULE_ADAPTER}from"#channel/schedule.js";import{HTTP_ADAPTER}from"#channel/http.js";import{SUBAGENT_ADAPTER}from"#subagents/adapter.js";import{RuntimeRegistryError}from"#internal/runtime-registry.js";const FRAMEWORK_ADAPTERS=[HTTP_ADAPTER,SUBAGENT_ADAPTER,SCHEDULE_ADAPTER],ADAPTER_NON_EVENT_FIELDS=new Set([`kind`,`state`,`deliver`,`createAdapterContext`,`fetchFile`,`instrumentation`,`uploadPolicy`]);function createRuntimeAdapterRegistry(e){let t=new Map,n=new Set;for(let e of FRAMEWORK_ADAPTERS){let r=requireAdapterKind(e);n.add(r),t.set(r,e)}for(let r of e.channels){if(r.adapter===void 0)continue;let e={logicalPath:r.logicalPath,sourceId:r.sourceId},i=r.adapter,a=requireAdapterKind(i,e);if(n.has(a)){if(carriesAdapterBehavior(i))throw new RuntimeRegistryError(`adapter`,`Channel adapter kind "${a}" is reserved by the framework. A route-declared adapter may share a framework kind only as a pass-through with no \`deliver\` hook, event handlers, \`attachments\` resolver, or \`createAdapterContext\` factory. Use a custom \`kind\` to add channel-specific behavior.`,{...e,entryName:a});continue}t.set(a,i)}return{adaptersByKind:t}}function deserializeRuntimeAdapter(e,t){let n=t,r=e.adaptersByKind.get(n.kind);if(r===void 0)throw Error(`Unknown adapter kind: "${n.kind}". Declare the adapter on the route that starts this session so the runtime can rehydrate it.`);return{...r,state:n.state}}function requireAdapterKind(e,t){let n=getAdapterKind(e);if(typeof n!=`string`||n.length===0)throw new RuntimeRegistryError(`adapter`,"Adapters must declare a non-empty `kind` field.",{entryName:`unknown`,logicalPath:t?.logicalPath,sourceId:t?.sourceId});return n}function carriesAdapterBehavior(e){if(e.deliver!==void 0||e.fetchFile!==void 0||e.uploadPolicy!==void 0||e.createAdapterContext!==void 0)return!0;for(let[t,n]of Object.entries(e))if(!ADAPTER_NON_EVENT_FIELDS.has(t)&&typeof n==`function`)return!0;return!1}export{createRuntimeAdapterRegistry,deserializeRuntimeAdapter};
@@ -1 +1 @@
1
- import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"../update/package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{agentTemplateFiles,formatEveDependencySpecifier,resolveEvePackageContract}from"./project.js";import{readFile}from"node:fs/promises";import{join}from"node:path";const DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`];function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasDeclaredDependency(e,t){if(!isJsonObject(e))return!1;for(let n of DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function addAgentToProject(e){let t=e.packageManager??`pnpm`,n=join(e.projectRoot,`package.json`);if(!await pathExists(n))throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it has no package.json. Run \`eve init <name>\` to create a new project instead.`);let r;try{r=JSON.parse(await readFile(n,`utf8`))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Cannot add an eve agent because "${n}" is not valid JSON. No files were changed. Fix the file, then retry eve init. ${t}`)}let i=agentTemplateFiles(e.model,e.reasoning),a=[];for(let t of Object.keys(i))await pathExists(join(e.projectRoot,t))&&a.push(t);if(a.length===0&&await pathExists(join(e.projectRoot,`agent`))&&a.push(`agent/`),a.length>0)throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it already has: ${a.join(`, `)}.`);let o=resolveEvePackageContract(e.evePackage),s=resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.128`),c=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`2.2.0`),l=resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.5.4`),u=[];for(let[t,n]of Object.entries(i)){let r=join(e.projectRoot,t);await writeTextFile(r,n),u.push(r)}let d={"@vercel/connect":c,ai:s,eve:formatEveDependencySpecifier(o.version),zod:l},f={};for(let[e,t]of Object.entries(d))hasDeclaredDependency(r,e)||(f[e]=t);let p={};Object.keys(f).length>0&&(p.dependencies=f),isPackageManagerWorkspaceMember(t,e.projectRoot)||(p.nodeEngineRequirement=o.nodeEngine);let m=await patchPackageJson(n,p),h=await patchWorkspaceRootPackageJson(t,e.projectRoot,{nodeEngineRequirement:o.nodeEngine}),g=h.nodeEngineOverride??m.nodeEngineOverride,_=await applyPackageManagerWorkspaceConfiguration({packageManager:t,projectRoot:e.projectRoot});return{filesWritten:u,dependenciesAdded:Object.keys(f).sort(),nodeEngineOverride:g,configurationFilesChanged:[...m.changed?[n]:[],...h.changed&&h.path!==void 0?[h.path]:[],..._.filesWritten]}}export{addAgentToProject};
1
+ import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"../update/package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{agentTemplateFiles,formatEveDependencySpecifier,resolveEvePackageContract}from"./project.js";import{readFile}from"node:fs/promises";import{join}from"node:path";const DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`];function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasDeclaredDependency(e,t){if(!isJsonObject(e))return!1;for(let n of DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function addAgentToProject(e){let t=e.packageManager??`pnpm`,n=join(e.projectRoot,`package.json`);if(!await pathExists(n))throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it has no package.json. Run \`eve init <name>\` to create a new project instead.`);let r;try{r=JSON.parse(await readFile(n,`utf8`))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Cannot add an eve agent because "${n}" is not valid JSON. No files were changed. Fix the file, then retry eve init. ${t}`)}let i=agentTemplateFiles(e.model,e.reasoning),a=[];for(let t of Object.keys(i))await pathExists(join(e.projectRoot,t))&&a.push(t);if(a.length===0&&await pathExists(join(e.projectRoot,`agent`))&&a.push(`agent/`),a.length>0)throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it already has: ${a.join(`, `)}.`);let o=resolveEvePackageContract(e.evePackage),s=resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.137`),c=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`2.2.0`),l=resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.5.4`),u=[];for(let[t,n]of Object.entries(i)){let r=join(e.projectRoot,t);await writeTextFile(r,n),u.push(r)}let d={"@vercel/connect":c,ai:s,eve:formatEveDependencySpecifier(o.version),zod:l},f={};for(let[e,t]of Object.entries(d))hasDeclaredDependency(r,e)||(f[e]=t);let p={};Object.keys(f).length>0&&(p.dependencies=f),isPackageManagerWorkspaceMember(t,e.projectRoot)||(p.nodeEngineRequirement=o.nodeEngine);let m=await patchPackageJson(n,p),h=await patchWorkspaceRootPackageJson(t,e.projectRoot,{nodeEngineRequirement:o.nodeEngine}),g=h.nodeEngineOverride??m.nodeEngineOverride,_=await applyPackageManagerWorkspaceConfiguration({packageManager:t,projectRoot:e.projectRoot});return{filesWritten:u,dependenciesAdded:Object.keys(f).sort(),nodeEngineOverride:g,configurationFilesChanged:[...m.changed?[n]:[],...h.changed&&h.path!==void 0?[h.path]:[],..._.filesWritten]}}export{addAgentToProject};
@@ -1,4 +1,4 @@
1
- import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{DEFAULT_CONNECT_PACKAGE_VERSION,resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_CHANNEL_TEMPLATES}from"./web-template.js";import{AGENT_INSTRUCTIONS_TEMPLATE}from"./instructions-template.js";import{SCAFFOLDED_AGENT_PATHS}from"./agent-paths.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.128`,DEFAULT_ZOD_PACKAGE_VERSION=`4.5.4`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.76.3`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e,t){return{[SCAFFOLDED_AGENT_PATHS.config]:renderAgentTemplate(e,t),[SCAFFOLDED_AGENT_PATHS.channel]:WEB_CHANNEL_TEMPLATES.default,[SCAFFOLDED_AGENT_PATHS.instructions]:AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,t){let n=parseChatGptModelSelection(e);return n===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(n)}),\n${reasoningTemplateLine(t)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,t){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(t.model)!==void 0?renderAgentTemplate(t.model,t.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{DEFAULT_CONNECT_PACKAGE_VERSION,resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_CHANNEL_TEMPLATES}from"./web-template.js";import{AGENT_INSTRUCTIONS_TEMPLATE}from"./instructions-template.js";import{SCAFFOLDED_AGENT_PATHS}from"./agent-paths.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.137`,DEFAULT_ZOD_PACKAGE_VERSION=`4.5.4`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.78.0`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e,t){return{[SCAFFOLDED_AGENT_PATHS.config]:renderAgentTemplate(e,t),[SCAFFOLDED_AGENT_PATHS.channel]:WEB_CHANNEL_TEMPLATES.default,[SCAFFOLDED_AGENT_PATHS.instructions]:AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,t){let n=parseChatGptModelSelection(e);return n===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(n)}),\n${reasoningTemplateLine(t)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,t){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(t.model)!==void 0?renderAgentTemplate(t.model,t.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
2
2
 
3
3
  export default defineAgent({
4
4
  model: "__EVE_INIT_MODEL__",
@@ -114,4 +114,4 @@ __EVE_INIT_REASONING__ modelOptions: {
114
114
  },
115
115
  },
116
116
  });
117
- `:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(e.includeRootOnlyPackageJsonFields)}}async function assertCanCreateInPlace(e,t){if(!await pathExists(e))return;let n=await readdir(e),r=blockingCreateInPlaceEntries(n);if(r.length>0&&!t){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(e){let t=resolve(e.targetDirectory??process.cwd(),e.projectName),n=e.projectName===`.`,r=e.overwriteExisting??!1,i=e.byokProvider??!1,a=e.packageManager??`pnpm`,o=resolveEvePackageContract(e.evePackage),s=pinnedNodeEngineMajor(o.nodeEngine),c=resolve(e.workspaceProbeDirectory??t),l=isPackageManagerWorkspaceMember(a,c);if(n)await assertCanCreateInPlace(t,r);else if(await pathExists(t))throw Error(`Cannot create project because "${t}" already exists.`);let u={appName:basename(t),model:e.model,reasoning:e.reasoning,eveVersion:o.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.128`),connectPackageVersion:resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`2.2.0`),zodPackageVersion:resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.5.4`),typescriptPackageVersion:resolveVersionToken(`typescriptPackageVersion`,e.typescriptPackageVersion??`7.0.2`),nodeTypesVersion:s,nodeEngine:s};await mkdir(t,{recursive:!0});for(let[a,o]of Object.entries(templateFiles({byokProvider:i,includeRootOnlyPackageJsonFields:!l}))){let i=`${t}/${a}`,s=await pathExists(i);await writeTextFile(i,renderTemplate(o,u),{force:n&&r}),s&&await e.onOverwriteFile?.(i)}return await applyPackageManagerWorkspaceConfiguration({packageManager:a,projectRoot:t,workspaceProbeRoot:c,onWorkspaceRootMutation:e.onWorkspaceRootMutation}),await patchWorkspaceRootPackageJson(a,c,{nodeEngineRequirement:o.nodeEngine,onWorkspaceRootMutation:e.onWorkspaceRootMutation}),t}async function isEveProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{CURRENT_DIRECTORY_PROJECT_NAME,DEFAULT_AI_PACKAGE_VERSION,DEFAULT_CONNECT_PACKAGE_VERSION,DEFAULT_EVE_PACKAGE_CONTRACT,DEFAULT_ZOD_PACKAGE_VERSION,ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX,agentTemplateFiles,byokProviderEnvVar,formatEveDependencySpecifier,isEveProject,modelProviderSlug,resolveEvePackageContract,scaffoldBaseProject};
117
+ `:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(e.includeRootOnlyPackageJsonFields)}}async function assertCanCreateInPlace(e,t){if(!await pathExists(e))return;let n=await readdir(e),r=blockingCreateInPlaceEntries(n);if(r.length>0&&!t){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(e){let t=resolve(e.targetDirectory??process.cwd(),e.projectName),n=e.projectName===`.`,r=e.overwriteExisting??!1,i=e.byokProvider??!1,a=e.packageManager??`pnpm`,o=resolveEvePackageContract(e.evePackage),s=pinnedNodeEngineMajor(o.nodeEngine),c=resolve(e.workspaceProbeDirectory??t),l=isPackageManagerWorkspaceMember(a,c);if(n)await assertCanCreateInPlace(t,r);else if(await pathExists(t))throw Error(`Cannot create project because "${t}" already exists.`);let u={appName:basename(t),model:e.model,reasoning:e.reasoning,eveVersion:o.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.137`),connectPackageVersion:resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`2.2.0`),zodPackageVersion:resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.5.4`),typescriptPackageVersion:resolveVersionToken(`typescriptPackageVersion`,e.typescriptPackageVersion??`7.0.2`),nodeTypesVersion:s,nodeEngine:s};await mkdir(t,{recursive:!0});for(let[a,o]of Object.entries(templateFiles({byokProvider:i,includeRootOnlyPackageJsonFields:!l}))){let i=`${t}/${a}`,s=await pathExists(i);await writeTextFile(i,renderTemplate(o,u),{force:n&&r}),s&&await e.onOverwriteFile?.(i)}return await applyPackageManagerWorkspaceConfiguration({packageManager:a,projectRoot:t,workspaceProbeRoot:c,onWorkspaceRootMutation:e.onWorkspaceRootMutation}),await patchWorkspaceRootPackageJson(a,c,{nodeEngineRequirement:o.nodeEngine,onWorkspaceRootMutation:e.onWorkspaceRootMutation}),t}async function isEveProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{CURRENT_DIRECTORY_PROJECT_NAME,DEFAULT_AI_PACKAGE_VERSION,DEFAULT_CONNECT_PACKAGE_VERSION,DEFAULT_EVE_PACKAGE_CONTRACT,DEFAULT_ZOD_PACKAGE_VERSION,ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX,agentTemplateFiles,byokProviderEnvVar,formatEveDependencySpecifier,isEveProject,modelProviderSlug,resolveEvePackageContract,scaffoldBaseProject};
@@ -1 +1 @@
1
- import{DEFAULT_EVE_PACKAGE_CONTRACT}from"../create/project.js";function resolveWebPackageVersions(e,t){return{evePackage:e?.evePackage??DEFAULT_EVE_PACKAGE_CONTRACT,aiPackageVersion:e?.aiPackageVersion??`^7.0.128`,betterAuthPackageVersion:e?.betterAuthPackageVersion??`1.6.26`,nextPackageVersion:e?.nextPackageVersion??(t===`sign-in-with-vercel`?`16.3.0`:`16.3.4`),reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.5.4`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}export{resolveWebPackageVersions};
1
+ import{DEFAULT_EVE_PACKAGE_CONTRACT}from"../create/project.js";function resolveWebPackageVersions(e,t){return{evePackage:e?.evePackage??DEFAULT_EVE_PACKAGE_CONTRACT,aiPackageVersion:e?.aiPackageVersion??`^7.0.137`,betterAuthPackageVersion:e?.betterAuthPackageVersion??`1.6.26`,nextPackageVersion:e?.nextPackageVersion??(t===`sign-in-with-vercel`?`16.3.0`:`16.3.4`),reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.5.4`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}export{resolveWebPackageVersions};
@@ -35,8 +35,9 @@ export type AnthropicPromptCacheTtl = "5m" | "1h";
35
35
  export declare const ANTHROPIC_PROMPT_CACHE_TTLS: readonly AnthropicPromptCacheTtl[];
36
36
  /**
37
37
  * Provider-agnostic reasoning effort forwarded to the AI SDK model call.
38
+ * The AI SDK's `max` level is not supported yet: eve's manifest and agent info list the levels.
38
39
  */
39
- export type AgentReasoningDefinition = NonNullable<CallSettings["reasoning"]>;
40
+ export type AgentReasoningDefinition = Exclude<NonNullable<CallSettings["reasoning"]>, "max">;
40
41
  /**
41
42
  * How an agent's model is reached at runtime, decided at compile time from the
42
43
  * authored model value.
@@ -5,6 +5,7 @@ import type { RouteDefinition } from "#channel/routes.js";
5
5
  import type { Session, SessionHandle } from "#channel/session.js";
6
6
  import type { DeliverPayload, SessionAuthContext, TurnPolicy } from "#channel/types.js";
7
7
  import type { StepInput } from "#harness/types.js";
8
+ import type { UploadPolicyInput } from "#public/channels/upload-policy.js";
8
9
  import type { AudienceContext } from "#shared/conversation-context.js";
9
10
  import type { ChannelAudience } from "#shared/channel-audience.js";
10
11
  /**
@@ -24,6 +25,8 @@ export interface FetchFileResult {
24
25
  /** Runtime context supplied while resolving a channel-owned file URL. */
25
26
  export interface FetchFileContext {
26
27
  readonly state: Readonly<Record<string, unknown>>;
28
+ /** The session the attachment arrived in, for checking it against the session's caller. */
29
+ readonly session: SessionHandle;
27
30
  }
28
31
  export type FetchFileFunction = (url: string, context?: FetchFileContext) => Promise<Buffer | FetchFileResult | null>;
29
32
  /**
@@ -75,12 +78,21 @@ export interface GenericChannelDefinition<TEvents, TState = undefined, TCtx = vo
75
78
  receive?(input: GenericReceiveInput<TReceiveTarget>, ctx: ChannelReceiveContext<TState>): Promise<Session>;
76
79
  readonly events?: TEvents;
77
80
  /**
78
- * Fetches bytes for a `URL` object encountered on a `FilePart.data` by the
79
- * staging pipeline. Return `null` to pass the URL through to the model
80
- * provider unchanged, or bytes / {@link FetchFileResult} to stage the file to
81
- * the sandbox.
81
+ * Fetches bytes for a link encountered on a `FilePart.data` by the staging
82
+ * pipeline. Return bytes / {@link FetchFileResult} to stage the file to the
83
+ * sandbox, or `null` when the link is not this channel's: eve then downloads
84
+ * a public `https:` link itself, and any other link reaches the model as a
85
+ * note. A provider never fetches an attachment.
82
86
  */
83
87
  readonly fetchFile?: FetchFileFunction;
88
+ /**
89
+ * Size cap and media types for every file eve stages for this channel,
90
+ * checked against the final bytes and the media type they prove, including
91
+ * links eve downloads itself. A file that breaks the policy reaches the
92
+ * model as a note. Omit to check nothing beyond your own `fetchFile`; eve's
93
+ * own downloads then stop at the 25 MB framework default.
94
+ */
95
+ readonly uploadPolicy?: UploadPolicyInput;
84
96
  /**
85
97
  * Channel-owned metadata exposed to instrumentation callbacks. This is the
86
98
  * channel's public observability surface, not a dump of durable adapter state,
@@ -3,6 +3,5 @@ export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespon
3
3
  export { conversationReducer } from "#client/conversation-reducer.js";
4
4
  export { openConversationInputs } from "#client/conversation-state.js";
5
5
  export { toolCallState, type ToolCallState, type ToolCallStatus } from "#client/tool-call-state.js";
6
- export type { SessionAuthorization, SessionCall, SessionCallStatus, SessionProjection, } from "#protocol/session-projection.js";
7
6
  export type { AgentObservation, ConversationAgentSession, ConversationInput, ConversationState, ConversationTask, ConversationTaskCall, ConversationTurn, } from "#client/conversation-state.js";
8
7
  export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
@@ -1,4 +1,4 @@
1
- import { a as conversationProjection, i as openConversationInputs, n as conversationReducer, o as callStatus, r as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-jI6EsYAu.js";
1
+ import { a as conversationProjection, i as openConversationInputs, n as conversationReducer, o as callStatus, r as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BSYe_Re7.js";
2
2
 
3
3
  //#region src/client/tool-call-state.ts
4
4
  function toolCallState(conversation, callId, options = {}) {
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-jI6EsYAu.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-BSYe_Re7.js";
2
2
 
3
3
  export { useEveAgent };
@@ -1 +1 @@
1
- async function executeAskQuestionTool(e,t){"use workflow";return toAskQuestionOutput(await t.ask(toAskQuestionRequest(e)))}function toAskQuestionRequest(e){let t=e.options?.map(e=>({description:e.description,id:e.label,label:e.label}));return{allowFreeform:!0,display:t===void 0?`text`:`select`,prompt:e.question,...t!==void 0&&{options:t}}}function toAskQuestionOutput(e){switch(e.status){case`answered`:return{answer:e.optionId??e.text??``,status:`answered`};case`cancelled`:return{interrupted:!0};case`unavailable`:return{status:`unavailable`}}}executeAskQuestionTool.workflowId=`workflow//eve@0.76.3//executeAskQuestionTool`;export{executeAskQuestionTool,toAskQuestionOutput,toAskQuestionRequest};
1
+ async function executeAskQuestionTool(e,t){"use workflow";return toAskQuestionOutput(await t.ask(toAskQuestionRequest(e)))}function toAskQuestionRequest(e){let t=e.options?.map(e=>({description:e.description,id:e.label,label:e.label}));return{allowFreeform:!0,display:t===void 0?`text`:`select`,prompt:e.question,...t!==void 0&&{options:t}}}function toAskQuestionOutput(e){switch(e.status){case`answered`:return{answer:e.optionId??e.text??``,status:`answered`};case`cancelled`:return{interrupted:!0};case`unavailable`:return{status:`unavailable`}}}executeAskQuestionTool.workflowId=`workflow//eve@0.78.0//executeAskQuestionTool`;export{executeAskQuestionTool,toAskQuestionOutput,toAskQuestionRequest};
@@ -6,10 +6,11 @@ export interface ReadFileToolInput {
6
6
  }
7
7
  export interface ReadFileToolOutput {
8
8
  content: string;
9
- /** Set when the file is a PNG, JPEG, GIF, or WebP image the model sees as an image. */
10
- image?: {
9
+ /** Set when the file is a PNG, JPEG, GIF, or WebP image or a PDF that the model reads natively. */
10
+ file?: {
11
11
  height?: number;
12
12
  mediaType: string;
13
+ pages?: number;
13
14
  size: number;
14
15
  width?: number;
15
16
  };
@@ -1,2 +1,2 @@
1
- import{frameworkTool}from"./framework-tool.js";import{contextStorage}from"#context/container.js";import{SandboxKey}from"#context/keys.js";import{basename}from"node:path";import{defineJsonSchema}from"#tools/schema.js";import{defineTool}from"#tools/definition.js";import{resolveAbsoluteFilePath}from"#execution/sandbox/require-sandbox.js";import{toolLabel}from"#tools/tool-label.js";import{toolOutput,toolOutputPart}from"#tools/model-output.js";import{executeReadFileOnSandbox}from"#execution/sandbox/read-file.js";const READ_FILE_INPUT_SCHEMA=defineJsonSchema({type:`object`,properties:{filePath:{type:`string`,description:`The absolute path to the file to read. A leading $HOME is supported.`},limit:{type:`integer`,minimum:1,description:`Maximum number of lines to return. Defaults to 2000.`},offset:{type:`integer`,minimum:1,description:`1-based line number to start from. Defaults to 1.`}},required:[`filePath`],additionalProperties:!1}),READ_FILE_OUTPUT_SCHEMA=defineJsonSchema({type:`object`,properties:{content:{type:`string`},image:{type:`object`,properties:{height:{type:`integer`,minimum:1},mediaType:{type:`string`},size:{type:`integer`,minimum:0},width:{type:`integer`,minimum:1}},required:[`mediaType`,`size`],additionalProperties:!1},nextOffset:{type:`integer`,minimum:1},path:{type:`string`},totalLines:{type:`integer`,minimum:0},truncated:{type:`boolean`}},required:[`content`,`path`,`totalLines`,`truncated`],additionalProperties:!1}),readFile=frameworkTool(defineTool({label:{start:e=>toolLabel(`Read`,e.filePath)},description:[`Read a file from the local filesystem. If the path does not exist, an error is returned.`,``,`Usage:`,`- The filePath parameter should be an absolute path or begin with $HOME/.`,`- By default, this tool returns up to 2000 lines from the start of the file.`,`- The offset parameter is the line number to start from (1-indexed).`,`- To read later sections, call this tool again with a larger offset.`,'- Contents are returned with each line prefixed by its line number as `<line>: <content>`. For example, if a file has contents "foo\\n", you will receive "1: foo\\n".',`- Any line longer than 2000 characters is truncated.`,"- PNG, JPEG, GIF, and WebP files up to 3 MiB are shown to you as images, including files named in `Attached file <path>` references.",`- Call this tool in parallel when you know there are multiple files you want to read.`,`- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.`].join(`
2
- `),async execute(e,t){return await executeReadFileOnSandbox(await t.getSandbox(),e)},inputSchema:READ_FILE_INPUT_SCHEMA,outputSchema:READ_FILE_OUTPUT_SCHEMA,async toModelOutput(e){if(e.image===void 0)return toolOutput.json(e);let t=await contextStorage.getStore()?.get(SandboxKey)?.get(),n=t==null?null:await t.readBinaryFile({path:await resolveAbsoluteFilePath(t,e.path)});return n===null?toolOutput.text(`${e.content} The image could not be loaded.`):n.byteLength===e.image.size?toolOutput.content([toolOutputPart.text(e.content),toolOutputPart.file(Buffer.from(n).toString(`base64`),{filename:basename(e.path),mediaType:e.image.mediaType})]):toolOutput.text(`${e.content} The image changed after it was read; read it again.`)}}));export{READ_FILE_INPUT_SCHEMA,READ_FILE_OUTPUT_SCHEMA,readFile as default,readFile};
1
+ import{frameworkTool}from"./framework-tool.js";import{contextStorage}from"#context/container.js";import{SandboxKey}from"#context/keys.js";import{basename}from"node:path";import{defineJsonSchema}from"#tools/schema.js";import{defineTool}from"#tools/definition.js";import{resolveAbsoluteFilePath}from"#execution/sandbox/require-sandbox.js";import{toolLabel}from"#tools/tool-label.js";import{toolOutput,toolOutputPart}from"#tools/model-output.js";import{executeReadFileOnSandbox}from"#execution/sandbox/read-file.js";const READ_FILE_INPUT_SCHEMA=defineJsonSchema({type:`object`,properties:{filePath:{type:`string`,description:`The absolute path to the file to read. A leading $HOME is supported.`},limit:{type:`integer`,minimum:1,description:`Maximum number of lines to return. Defaults to 2000.`},offset:{type:`integer`,minimum:1,description:`1-based line number to start from. Defaults to 1.`}},required:[`filePath`],additionalProperties:!1}),READ_FILE_OUTPUT_SCHEMA=defineJsonSchema({type:`object`,properties:{content:{type:`string`},file:{type:`object`,properties:{height:{type:`integer`,minimum:1},mediaType:{type:`string`},pages:{type:`integer`,minimum:1},size:{type:`integer`,minimum:0},width:{type:`integer`,minimum:1}},required:[`mediaType`,`size`],additionalProperties:!1},nextOffset:{type:`integer`,minimum:1},path:{type:`string`},totalLines:{type:`integer`,minimum:0},truncated:{type:`boolean`}},required:[`content`,`path`,`totalLines`,`truncated`],additionalProperties:!1}),readFile=frameworkTool(defineTool({label:{start:e=>toolLabel(`Read`,e.filePath)},description:[`Read a file from the local filesystem. If the path does not exist, an error is returned.`,``,`Usage:`,`- The filePath parameter should be an absolute path or begin with $HOME/.`,`- By default, this tool returns up to 2000 lines from the start of the file.`,`- The offset parameter is the line number to start from (1-indexed).`,`- To read later sections, call this tool again with a larger offset.`,'- Contents are returned with each line prefixed by its line number as `<line>: <content>`. For example, if a file has contents "foo\\n", you will receive "1: foo\\n".',`- Any line longer than 2000 characters is truncated.`,"- PNG, JPEG, GIF, and WebP images up to 3 MiB and PDFs up to 20 MiB are shown to you as files, including files named in `Attached file <path>` references.",`- Call this tool in parallel when you know there are multiple files you want to read.`,`- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.`].join(`
2
+ `),async execute(e,t){return await executeReadFileOnSandbox(await t.getSandbox(),e)},inputSchema:READ_FILE_INPUT_SCHEMA,outputSchema:READ_FILE_OUTPUT_SCHEMA,async toModelOutput(e){if(e.file===void 0)return toolOutput.json(e);let t=await contextStorage.getStore()?.get(SandboxKey)?.get(),n=t==null?null:await t.readBinaryFile({path:await resolveAbsoluteFilePath(t,e.path)});return n===null?toolOutput.text(`${e.content} The file could not be loaded.`):n.byteLength===e.file.size?toolOutput.content([toolOutputPart.text(e.content),toolOutputPart.file(Buffer.from(n).toString(`base64`),{filename:basename(e.path),mediaType:e.file.mediaType})]):toolOutput.text(`${e.content} The file changed after it was read; read it again.`)}}));export{READ_FILE_INPUT_SCHEMA,READ_FILE_OUTPUT_SCHEMA,readFile as default,readFile};
@@ -1 +1 @@
1
- import{sleep}from"#compiled/@workflow/core/index.js";async function executeSleepTool(e,t){"use workflow";let n=new Promise(e=>t.abortSignal.addEventListener(`abort`,()=>e(`interrupted`),{once:!0})),r=sleep(Math.ceil(e.seconds*1e3)).then(()=>`elapsed`);return await Promise.race([r,n])===`elapsed`?{waitedSeconds:e.seconds}:{interrupted:!0}}executeSleepTool.workflowId=`workflow//eve@0.76.3//executeSleepTool`;export{executeSleepTool};
1
+ import{sleep}from"#compiled/@workflow/core/index.js";async function executeSleepTool(e,t){"use workflow";let n=new Promise(e=>t.abortSignal.addEventListener(`abort`,()=>e(`interrupted`),{once:!0})),r=sleep(Math.ceil(e.seconds*1e3)).then(()=>`elapsed`);return await Promise.race([r,n])===`elapsed`?{waitedSeconds:e.seconds}:{interrupted:!0}}executeSleepTool.workflowId=`workflow//eve@0.78.0//executeSleepTool`;export{executeSleepTool};
@@ -3,6 +3,5 @@ export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespon
3
3
  export { conversationReducer } from "#client/conversation-reducer.js";
4
4
  export { openConversationInputs } from "#client/conversation-state.js";
5
5
  export { toolCallState, type ToolCallState, type ToolCallStatus } from "#client/tool-call-state.js";
6
- export type { SessionAuthorization, SessionCall, SessionCallStatus, SessionProjection, } from "#protocol/session-projection.js";
7
6
  export type { AgentObservation, ConversationAgentSession, ConversationInput, ConversationState, ConversationTask, ConversationTaskCall, ConversationTurn, } from "#client/conversation-state.js";
8
7
  export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
@@ -1,4 +1,4 @@
1
- import { a as conversationProjection, i as openConversationInputs, n as conversationReducer, o as callStatus, r as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BtKWTEpm.js";
1
+ import { a as conversationProjection, i as openConversationInputs, n as conversationReducer, o as callStatus, r as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-B8brxCk-.js";
2
2
 
3
3
  //#region src/client/tool-call-state.ts
4
4
  function toolCallState(conversation, callId, options = {}) {
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-BtKWTEpm.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-B8brxCk-.js";
2
2
 
3
3
  export { useEveAgent };
@@ -220,16 +220,24 @@ timeout. Expiration does not delete stored session data.
220
220
  Input tokens, output tokens, and model token cost are checked independently.
221
221
  The model call that crosses a limit is allowed to finish because exact usage
222
222
  arrives after the call completes. Before the next model call, eve pauses the
223
- session and sends a deterministic continuation prompt with two options:
224
- **Approve** grants a fresh window of each configured size, and **Stop**
223
+ turn (`turn.waiting`) and sends a deterministic continuation prompt with two options:
224
+ **Continue** grants a fresh window of each configured size and resumes the same
225
+ turn, and **Stop**
225
226
  cancels the in-flight turn through the standard cancellation path
226
227
  (`turn.cancelled` → `session.waiting`) — a user decision, not an error. The session stays resumable; because it is
227
- still over budget, the next message re-raises the prompt. Declining a
228
+ still over budget after Stop, the next message raises a new prompt. Declining a
228
229
  delegated child's prompt cancels the root turn, which cascades to the whole
229
230
  delegation tree — the delegating parent never receives an error result it
230
231
  could retry against a fresh quota share. A reply that answers neither option
231
232
  is queued while the existing prompt stays pending; eve does not raise another
232
- copy. The reply is processed once the budget is granted.
233
+ copy or start another turn. The reply is received (`message.received`) and
234
+ processed in the held turn once the budget is granted.
235
+
236
+ For sessions already waiting on a continuation prompt from an older deployment,
237
+ the previous turn has completed. A non-answer message still queues behind the
238
+ existing prompt, but emits no event until Continue is chosen. Continue starts a
239
+ new turn and receives the queued message once; it does not reopen the completed
240
+ turn or duplicate the prompt.
233
241
 
234
242
  Sessions that cannot request input from a human, such as markdown schedules and
235
243
  delegated runs without input proxying, skip the prompt and fail the next model
@@ -245,7 +245,7 @@ bot.onNewMention(async (thread, message) => {
245
245
  });
246
246
  ```
247
247
 
248
- When the adapter can download an attachment (it sets `fetchData`, as the Slack, Google Chat, WhatsApp, and Twilio adapters do), eve downloads it with the adapter's credentials when the turn runs, not in the webhook, and passes the bytes. Platform file URLs usually need those credentials, so the model provider couldn't open them. eve rebuilds the download with the adapter's `rehydrateAttachment`; an adapter without one can't download after the webhook returns, so its files reach the agent as a short note. A failed download, or a file over 25 MB, also becomes a note. An attachment with only a URL is passed as that URL.
248
+ When the adapter can download an attachment (it sets `fetchData`, as the Slack, Google Chat, WhatsApp, and Twilio adapters do), eve downloads it with the adapter's credentials when the turn runs, not in the webhook, and passes the bytes. Platform file URLs usually need those credentials, so the model provider couldn't open them. eve rebuilds the download with the adapter's `rehydrateAttachment`; an adapter without one can't download after the webhook returns, so its files reach the agent as a short note. A failed download, or a file over 25 MB, also becomes a note. An attachment with only a URL is downloaded by eve when the URL is public `https:`; otherwise it becomes a note.
249
249
 
250
250
  See [File uploads](./custom#file-uploads) for how eve stages remote file URLs before the model call.
251
251
 
@@ -81,6 +81,13 @@ the exact session, while `channel.continuation` exposes the current address and
81
81
  receives only `(eventData, channel)` because it runs outside session context;
82
82
  its event data contains `sessionId` directly.
83
83
 
84
+ A failed turn emits `turn.failed`, then either `session.waiting` or
85
+ `session.failed`. `turn.failed` carries `terminal: true` when the failure ends
86
+ the session and `session.failed` follows. The built-in channels skip terminal
87
+ `turn.failed` events, so a person sees one notice: the `session.failed` handler
88
+ tells them to start over instead of to try again. Check `eventData.terminal` the
89
+ same way in a custom channel that posts on both events.
90
+
84
91
  A channel that renders questions and approvals from `input.requested` can handle
85
92
  `input.resolved` to learn when each request ends, however it ended: answered from
86
93
  the channel, answered by a plain-text reply, or withdrawn. A request that a
@@ -550,11 +557,31 @@ defineChannel({
550
557
  });
551
558
  ```
552
559
 
553
- If `fetchFile` throws, eve replaces that attachment with a model-visible error note and continues the turn. Built-in channels include safe details such as the upstream HTTP status. Custom channel errors use a generic note, while eve keeps the original error in operator logs.
560
+ If `fetchFile` throws, eve replaces that attachment with a model-visible note and continues the turn. To tell the model why, throw `attachmentError` from `eve/channels`:
561
+
562
+ ```ts
563
+ import { attachmentError } from "eve/channels";
564
+
565
+ async function fetchUpload(url: string) {
566
+ const response = await fetch(url, { headers: { authorization: `Bearer ${token}` } });
567
+ if (response.status === 413) {
568
+ throw attachmentError("The file is over 100 MB. Ask for a smaller file.");
569
+ }
570
+ return Buffer.from(await response.arrayBuffer());
571
+ }
572
+ ```
573
+
574
+ The model then reads `Attachment report.pdf could not be retrieved: The file is over 100 MB. Ask for a smaller file.` The message reaches the model as written, so keep it to details the person can act on, such as a size limit or a missing permission, and never include URLs, tokens, or upstream response text. Pass the original error as `attachmentError(message, { cause })` to keep it in operator logs. Any other error that `fetchFile` throws becomes a generic note, and eve keeps the original error in operator logs.
575
+
576
+ `fetchFile` receives the channel's `state` and the `session` the attachment arrived in as its second argument, so a resolver can check a file against the session's caller in `session.auth`.
577
+
578
+ The `URL` object survives the queue boundary as a string and is reconstituted inside the workflow step. A string with a URL scheme, such as `https://...` or `myapp-file:abc`, is handled the same way. The staging pipeline calls `fetchFile` with the URL serialized as a string (the URL's `href`), which is why the example matches on `url.startsWith(...)`.
579
+
580
+ Return bytes to stage the file to the sandbox. Return `null` when the URL is not one your channel resolves: eve then downloads a public `https:` URL itself, with the 25 MB default size cap and a 30-second timeout, and refuses private or reserved addresses. It downloads at most 10 URLs per message, one at a time. Any other URL, or a download that fails, reaches the model as a note. The model provider never fetches an attachment, so a link that expires or needs credentials can't fail later model calls.
554
581
 
555
- The `URL` object survives the queue boundary as a string and is reconstituted inside the workflow step. The staging pipeline calls `fetchFile` with the URL serialized as a string (the URL's `href`), which is why the example matches on `url.startsWith(...)`. Return bytes to stage the file to the sandbox, or `null` to let the URL pass through to the model provider.
582
+ Set `uploadPolicy` on `defineChannel` to cap what eve stages for the channel, for example `uploadPolicy: { maxBytes: 10 * 1024 * 1024, allowedMediaTypes: ["image/*", "application/pdf"] }`. eve checks every staged file against it, on the final bytes and the media type they prove, including URLs it downloads itself. A file that breaks the policy reaches the model as a note. Without `uploadPolicy`, eve checks only what your `fetchFile` enforces, and its own downloads stop at 25 MB.
556
583
 
557
- The framework handles staging bytes to the sandbox, enforcing upload policy, hydrating files for the model call, and reconstituting `URL` objects after queue serialization. See [Inbound attachments](../sandbox#inbound-attachments) for the shared storage, size, and provider-input behavior.
584
+ The framework handles staging bytes to the sandbox, hydrating files for the model call, and reconstituting `URL` objects after queue serialization. See [Inbound attachments](../sandbox#inbound-attachments) for the shared storage, size, and provider-input behavior.
558
585
 
559
586
  ## What to read next
560
587
 
@@ -163,6 +163,40 @@ An existing workflow whose inbox is not yet available returns `409 session_not_r
163
163
  A session that another eve version built returns `409 session_stranded` with the `eveVersion`
164
164
  that built it.
165
165
 
166
+ ### Send files
167
+
168
+ A message can mix text and file parts. A file part's `data` is base64, a `data:` URL, or a URL:
169
+
170
+ ```bash
171
+ curl -X POST https://<deployment>/eve/v1/session/wrun_A \
172
+ -H "Content-Type: application/json" \
173
+ -d '{"message":[{"type":"text","text":"Summarize this report."},{"type":"file","mediaType":"application/pdf","filename":"report.pdf","data":"data:application/pdf;base64,JVBERi0xLjQK..."}]}'
174
+ ```
175
+
176
+ `uploadPolicy` sets the size cap and allowed media types, 25 MB and every type by default. A request whose inline file breaks the policy returns `413` for size or `415` for type. Inline bytes also count toward the request body limit of the platform that serves the channel. On Vercel Functions that limit is 4.5 MB, so a base64 file stops at about 3.3 MB.
177
+
178
+ For larger files, upload them from the browser to your own storage and send the URL in the file part. Add `fetchFile` to load the URLs you own inside the workflow step, so the bytes never pass through the request or the workflow start payload:
179
+
180
+ ```ts title="agent/channels/eve.ts"
181
+ import { eveChannel } from "eve/channels/eve";
182
+ import { localDev, vercelOidc } from "eve/channels/auth";
183
+
184
+ export default eveChannel({
185
+ auth: [vercelOidc(), localDev()],
186
+ uploadPolicy: { maxBytes: 100 * 1024 * 1024 },
187
+ async fetchFile(url) {
188
+ if (!url.startsWith("https://uploads.example.com/")) return null;
189
+ const response = await fetch(url, {
190
+ headers: { authorization: `Bearer ${process.env.UPLOADS_TOKEN}` },
191
+ });
192
+ if (!response.ok) throw new Error(`Upload fetch returned HTTP ${response.status}`);
193
+ return Buffer.from(await response.arrayBuffer());
194
+ },
195
+ });
196
+ ```
197
+
198
+ `fetchFile` follows the [custom channel contract](./custom#file-uploads): return bytes to stage the file, or `null` for a URL that isn't yours. eve then downloads a public `https:` URL itself, and any other URL reaches the model as a note. `uploadPolicy` covers every file eve stages for the channel, including URLs that `fetchFile` loads and URLs eve downloads itself. eve checks the final bytes and the media type they prove, so a file over the cap, or one whose bytes are a disallowed type, reaches the model as a note instead of failing the request. See [Inbound attachments](../sandbox#inbound-attachments) for how eve stores files and shows them to the model.
199
+
166
200
  ### Stream events
167
201
 
168
202
  Stream a session as newline-delimited JSON from `GET /eve/v1/session/:sessionId/stream`. The [session protocol](../concepts/sessions-runs-and-streaming#stream-a-session) defines the event set, envelopes, cursors, and reconnection behavior.
@@ -399,7 +399,7 @@ slackChannel({
399
399
  });
400
400
  ```
401
401
 
402
- The bases say where a call may go. `apiBaseUrl` is the base Slack Web API method names resolve against, and defaults to `https://slack.com/api/`. `fileBaseUrl` adds a base for attachment downloads and falls back to `apiBaseUrl`: a download URL under it is fetched with the bot token, in addition to `files.slack.com` and Enterprise Grid file URLs. Every other URL passes through to the model provider unchanged. Both bases must be absolute `http:` or `https:` URLs carrying no query string or fragment; `slackChannel()` throws on one that is not, naming the option. A missing trailing slash is added, so `https://sim.example/api` sends `chat.postMessage` to `https://sim.example/api/chat.postMessage`.
402
+ The bases say where a call may go. `apiBaseUrl` is the base Slack Web API method names resolve against, and defaults to `https://slack.com/api/`. `fileBaseUrl` adds a base for attachment downloads and falls back to `apiBaseUrl`: a download URL under it is fetched with the bot token, in addition to `files.slack.com` and Enterprise Grid file URLs. eve downloads every other public `https:` URL without the bot token. Both bases must be absolute `http:` or `https:` URLs carrying no query string or fragment; `slackChannel()` throws on one that is not, naming the option. A missing trailing slash is added, so `https://sim.example/api` sends `chat.postMessage` to `https://sim.example/api/chat.postMessage`.
403
403
 
404
404
  `api.fetch` says how the traffic travels. It replaces the global `fetch` for calls to the configured bases — use it to attach the per-call header a host behind an authenticating proxy requires, as the example does for a simulator behind Vercel Deployment Protection. eve confines it to those bases' origins, so that header reaches the simulator alone: `files.slack.com`, and any other host Slack names in a response or an inbound payload, keep the global `fetch`. A wrapper therefore attaches its credential unconditionally. With no base configured every call already goes to Slack's own hosts, and `api.fetch` carries all of them, which is what observes or stubs the traffic in a test.
405
405
 
@@ -744,6 +744,10 @@ For a Slack post without an agent turn, use `ctx.thread.post(...)` in an inbound
744
744
 
745
745
  Inbound files behind authenticated Slack URLs are staged with `fetchFile`. See [File uploads](./custom#file-uploads) for the `fetchFile` contract.
746
746
 
747
+ The agent receives the files attached to the message that reaches it. When a mention carries no files, eve also collects the files posted in the thread since the previous mention of the app, up to 10 messages back, so a person can share a file and then ask about it in a follow-up. This works for mentions up to 1,000 replies into a thread. The app's own messages are skipped. A DM, or a thread where the app answers without a mention, gets no lookback, because each earlier message brought its own files.
748
+
749
+ Images, PDFs, audio, video, and other files all reach the agent. See [Inbound attachments](../sandbox#inbound-attachments) for which files the model reads directly and which it opens with tools. Files shared through an integration such as Google Drive or Dropbox are links to the other service, not Slack files, so eve skips them; their link stays in the message text. A download that fails or takes longer than 30 seconds reaches the model as a note.
750
+
747
751
  Private file downloads resolve credentials from the session's app installation workspace and require the Slack bot token's `files:read` scope. Add the scope and reinstall the Slack app before sending attachments.
748
752
 
749
753
  Downloads cover Slack's own file hosts plus URLs under a configured [`api.fileBaseUrl`](#point-the-channel-at-a-different-slack-api-host), and travel on `api.fetch` when one is configured.
@@ -24,7 +24,7 @@ export default defineAgent({
24
24
 
25
25
  This turns off the optional defaults described below. Add back only the tools the agent needs with the command in each tool's section. Existing files under `agent/tools/` remain available, including same-name replacements such as `agent/tools/bash.ts`.
26
26
 
27
- `defaultTools` doesn't affect [the catalog tools](#eve__search-eve__tool-and-eve__skill): eve adds them from what the agent declares, as listed in that section.
27
+ `defaultTools` doesn't affect [the catalog tools](#eve__search-eve__tool-and-eve__skill): eve adds them from what the session can reach, as listed in that section.
28
28
 
29
29
  ### `bash`
30
30
 
@@ -81,7 +81,7 @@ export default disableTool();
81
81
 
82
82
  ### `read_file`
83
83
 
84
- `read_file` reads text files from the sandbox with line-numbered output, and shows PNG, JPEG, GIF, and WebP images up to 3 MiB to the model as images. Images are detected from their bytes, so a missing or mismatched filename extension does not matter, and text files are always read as text. It accepts absolute paths and paths beginning with `$HOME/`.
84
+ `read_file` reads text files from the sandbox with line-numbered output, and shows PNG, JPEG, GIF, and WebP images up to 3 MiB and 8000 pixels per side, and PDFs up to 20 MiB, to the model as files. Images and PDFs are detected from their bytes, so a missing or mismatched filename extension does not matter, and other text files are always read as text. It accepts absolute paths and paths beginning with `$HOME/`.
85
85
 
86
86
  ```sh
87
87
  eve add tool/read_file
@@ -301,16 +301,16 @@ export default disableTool();
301
301
 
302
302
  ### `eve__search`, `eve__tool`, and `eve__skill`
303
303
 
304
- These tools let the model reach what isn't in its tool list. `eve__search` finds tools defined with `deferred: true`, agents defined with `tool: "deferred"`, every tool from the agent's [connections](../connections), and deferred [skills](../skills). `eve__tool` calls a deferred tool, agent, or connection tool by name, and `eve__skill` loads a skill, deferred or not. There is no add command; eve adds each one from what the agent declares, even when `defaultTools` is `false`:
304
+ These tools let the model reach what isn't in its tool list. `eve__search` finds tools defined with `deferred: true`, agents defined with `tool: "deferred"`, every tool from the agent's [connections](../connections), and deferred [skills](../skills). `eve__tool` calls a deferred tool, agent, or connection tool by name, and `eve__skill` loads a skill, deferred or not. There is no add command; eve adds each one when the session has something it reaches, even when `defaultTools` is `false`:
305
305
 
306
- - `eve__tool` comes with anything it can call: a deferred tool or agent, a connection, or a dynamic resolver in `agent/tools/`, `agent/subagents/`, or `agent/connections/` that may add one at runtime.
307
- - `eve__skill` comes with any skill, static or from a dynamic skill resolver, so an agent whose only catalog entries are listed skills gets `eve__skill` alone.
308
- - `eve__search` comes with anything it could find: whatever `eve__tool` can call, plus deferred skills and dynamic skill resolvers.
309
- - An agent with none of these gets no catalog tools.
306
+ - `eve__tool` comes with anything it can call: a deferred tool or agent, or a connection.
307
+ - `eve__skill` comes with any skill, so an agent whose only catalog entries are listed skills gets `eve__skill` alone.
308
+ - `eve__search` comes with anything it could find: whatever `eve__tool` can call, plus deferred skills.
309
+ - A session with none of these gets no catalog tools.
310
310
 
311
- The decision depends only on what the agent declares, never on what its resolvers return, so the tool list stays the same for a deployment and never changes within a session.
311
+ Entries the agent declares count from the session's first step. An entry a dynamic resolver returns counts from the step where it first appears, so a resolver in `agent/tools/` that returns only tools without `deferred: true` adds no catalog tools. Once a session's tool list includes a catalog tool, the session keeps it, even after a resolver stops returning the entry that brought it. The catalog tools change only when the session first reaches a new kind of entry, such as its first deferred tool or its first connection.
312
312
 
313
- - `eve__search({ query, limit? })` returns the best matches, up to `limit` (default 10, at most 50). `query` is required and must contain a word. A tool match has its exact `tool` name, its `description`, and a TypeScript `signature` rendered from its schemas. A deferred skill's match has its `skill` name, its `description`, and the `path` of its `SKILL.md` when it has supporting files. A result with `tool` is called with `eve__tool`, and one with `skill` is loaded with `eve__skill`. Tools and skills rank together, in tiers: an entry whose name is exactly the query, then one whose name after its last `__` is the query, such as `linear__create_issue` or `sre__create_issue` for `create_issue`, then the tools of a connection named by the query, then entries whose names, or names after their last `__`, start with the query, then keyword matches in names, parameters, and descriptions. So `eve__search({ query: "linear" })` lists the `linear` connection's tools first. One-letter and filler words such as `a` and `the` don't count toward keyword matches unless the query has nothing else.
313
+ - `eve__search({ query, limit? })` returns the best matches, up to `limit` (default 10, at most 50). `query` is required and must contain a word. A tool match has its exact `tool` name, its `description`, and a TypeScript `signature` rendered from its schemas. A deferred skill's match has its `skill` name, its `description`, and the `path` of its `SKILL.md` when it has supporting files. A result with `tool` is called with `eve__tool`, and one with `skill` is loaded with `eve__skill`. Next to `results`, `searched` counts the entries the query covered, the whole catalog or one namespace, and `remaining` counts the matches past `limit`. A connection behind sign-in counts as its one sign-in result until the user signs in, and an unavailable connection counts as none. Tools and skills rank together, in tiers: an entry whose name is exactly the query, then one whose name after its last `__` is the query, such as `linear__create_issue` or `sre__create_issue` for `create_issue`, then the tools of a connection named by the query, then entries whose names, or names after their last `__`, start with the query, then keyword matches in names, parameters, and descriptions. So `eve__search({ query: "linear" })` lists the `linear` connection's tools first. One-letter and filler words such as `a` and `the` don't count toward keyword matches unless the query has nothing else.
314
314
  - A query whose first word contains `__` searches one namespace: everything before its last `__`, without trailing underscores. The namespace only filters. `eve__search` keeps names under `<namespace>__` and anything named exactly the namespace, such as a connection's sign-in entry, then ranks them by the whole query, so `eve__search({ query: "linear__" })` returns only the `linear` connection's tools, or its sign-in result, and an exact name that contains `__` still ranks first. eve lists only the connections that can own names in that namespace, so other connections make no network call and don't appear in `unavailable`. A namespace that matches nothing fails with the closest connection names. Characters that can't appear in a name, such as a leading `^`, are ignored; `query` isn't a regular expression.
315
315
  - `eve__search` never asks the user to sign in. A connection whose server won't list its tools until the user signs in appears as one result named after the connection, such as `linear`; a connection whose server lists its tools without a token is searched like any other. A connection whose tools fail to load, or don't load within 10 seconds, appears under `unavailable` with its `error`, such as `"crm" did not list its tools within 10s. Try again later.` Matches from everything else are still returned, so a slow server doesn't hold up the search. An `eve__tool` call on that connection's tools fails with the same error.
316
316
  - `eve__tool({ name: "linear" })`, with a connection's own name, asks the user to sign in to that connection when its tools need it, and returns once they can be listed, so the next `eve__search` finds them. On a server that lists its tools without sign-in, it confirms the tools are available without asking.
@@ -319,7 +319,7 @@ The decision depends only on what the agent declares, never on what its resolver
319
319
 
320
320
  After `eve__tool` or `eve__skill` resolves its entry, the call runs exactly like a direct call to that entry. A skill load reports a `load-skill` action and a `load-skill-result`, both with the skill's `name`, and an `execute_tool eve:load-skill` span. Approval policies and `approvedTools`, workflow tools and tasks, agents, `endsTurn`, `toModelOutput`, hooks, and stream events all see the entry's own name and input, such as `linear__list_issues`. Only model history records the call as `eve__tool` or `eve__skill`.
321
321
 
322
- eve tells the model what it can reach in an append-only context message rather than in the system prompt: which kinds of deferred entries exist, up to 20 namespaces (the first `__` segment of deferred names, such as `sre` for `sre__list_alerts`), and up to 20 connections with their descriptions. It never names a deferred entry, so deferred entries stay out of context until `eve__search` finds them. A later step appends the listing again only when that changes; a deferred entry added to a listed namespace, or without one, changes nothing. The definitions of the catalog tools never change within a deployment, so adding a deferred entry, signing in, or resolving a dynamic connection keeps the cached prompt prefix.
322
+ eve tells the model what it can reach in an append-only context message rather than in the system prompt: which kinds of deferred entries exist, up to 20 namespaces (the first `__` segment of deferred names, such as `sre` for `sre__list_alerts`), and up to 20 connections with their descriptions. It never names a deferred entry, so deferred entries stay out of context until `eve__search` finds them. A later step appends the listing again only when that changes; a deferred entry added to a listed namespace, or without one, changes nothing. After the session first reaches each kind of entry, adding or removing entries, signing in, or resolving more dynamic connections doesn't change the catalog tools, so the cached prompt prefix holds.
323
323
 
324
324
  The tools eve adds itself all live in the `eve` namespace: `eve__search`, `eve__tool`, `eve__skill`, `eve__task_wait`, `eve__task_cancel`, and `eve__reply`. Nothing you author or resolve may be named `eve` or start with `eve__`: tools, subagents, skills, connections, and extension mounts, static or dynamic, since a connection or mount named `eve` would own every `eve__` name. The compiler rejects an authored tool, skill, connection, or mount, such as `agent/tools/eve__search.ts`; eve rejects an authored subagent when the agent loads, and a dynamic entry when its resolver returns it. Any other name is free, including `search` and `execute`. A connection's own name is also the entry that signs the user in to it.
325
325