experimental-ash 0.42.0 → 0.44.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.
- package/CHANGELOG.md +37 -0
- package/bin/ash.js +1 -0
- package/dist/docs/internals/mechanical-invariants.md +16 -0
- package/dist/docs/public/README.md +8 -8
- package/dist/docs/public/advanced/{auth-and-route-protection.md → auth-and-route-protection.mdx} +11 -0
- package/dist/docs/public/advanced/context-control.md +4 -4
- package/dist/docs/public/advanced/{evals.md → evals.mdx} +11 -1
- package/dist/docs/public/advanced/{hooks.md → hooks.mdx} +28 -40
- package/dist/docs/public/advanced/instrumentation.md +142 -3
- package/dist/docs/public/advanced/project-layout.md +5 -5
- package/dist/docs/public/advanced/runs-and-streaming.md +8 -2
- package/dist/docs/public/advanced/session-context.md +1 -1
- package/dist/docs/public/advanced/typescript-api.md +50 -7
- package/dist/docs/public/advanced/vercel-deployment.md +1 -1
- package/dist/docs/public/agent-ts.md +5 -5
- package/dist/docs/public/channels/{discord.md → discord.mdx} +11 -0
- package/dist/docs/public/channels/index.md +10 -10
- package/dist/docs/public/channels/{slack.md → slack.mdx} +11 -0
- package/dist/docs/public/channels/{teams.md → teams.mdx} +12 -0
- package/dist/docs/public/channels/{telegram.md → telegram.mdx} +11 -0
- package/dist/docs/public/channels/{twilio.md → twilio.mdx} +11 -0
- package/dist/docs/public/{connections.md → connections.mdx} +18 -6
- package/dist/docs/public/frontend/README.md +16 -0
- package/dist/docs/public/frontend/meta.json +3 -0
- package/dist/docs/public/frontend/nextjs.md +192 -0
- package/dist/docs/public/frontend/use-ash-agent.md +332 -0
- package/dist/docs/public/{getting-started.md → getting-started.mdx} +12 -1
- package/dist/docs/public/{human-in-the-loop.md → human-in-the-loop.mdx} +12 -1
- package/dist/docs/public/meta.json +1 -0
- package/dist/docs/public/sandbox.md +39 -1
- package/dist/docs/public/{schedules.md → schedules.mdx} +9 -0
- package/dist/docs/public/skills.md +2 -2
- package/dist/docs/public/{subagents.md → subagents.mdx} +10 -0
- package/dist/docs/public/{tools.md → tools.mdx} +41 -26
- package/dist/src/channel/adapter.d.ts +13 -0
- package/dist/src/channel/compiled-channel.d.ts +4 -1
- package/dist/src/channel/compiled-channel.js +1 -1
- package/dist/src/channel/instrumentation.d.ts +10 -0
- package/dist/src/channel/instrumentation.js +1 -0
- package/dist/src/channel/routes.d.ts +8 -10
- package/dist/src/channel/send.js +1 -1
- package/dist/src/channel/types.d.ts +16 -0
- package/dist/src/cli/commands/channels.d.ts +2 -1
- package/dist/src/cli/commands/channels.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@vercel/sandbox/index.d.ts +12 -19
- package/dist/src/compiled/@vercel/sandbox/network-policy.d.ts +161 -0
- package/dist/src/compiled/just-bash/index.d.ts +15 -2
- package/dist/src/compiled/just-bash/network/types.d.ts +155 -0
- package/dist/src/compiler/artifacts.d.ts +1 -0
- package/dist/src/compiler/artifacts.js +1 -1
- package/dist/src/compiler/channel-instrumentation-types.d.ts +8 -0
- package/dist/src/compiler/channel-instrumentation-types.js +2 -0
- package/dist/src/compiler/manifest.d.ts +13 -1
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/module-map.js +1 -1
- package/dist/src/compiler/normalize-manifest.js +1 -1
- package/dist/src/compiler/normalize-skill.d.ts +15 -2
- package/dist/src/compiler/normalize-skill.js +1 -1
- package/dist/src/compiler/normalize-tool.js +1 -1
- package/dist/src/context/dynamic-skill-lifecycle.d.ts +23 -0
- package/dist/src/context/dynamic-skill-lifecycle.js +1 -0
- package/dist/src/context/dynamic-tool-lifecycle.d.ts +2 -0
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/hook-lifecycle.d.ts +4 -6
- package/dist/src/context/hook-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +6 -4
- package/dist/src/context/keys.js +1 -1
- package/dist/src/context/providers/connection.d.ts +9 -0
- package/dist/src/context/providers/connection.js +1 -1
- package/dist/src/context/providers/sandbox.js +1 -1
- package/dist/src/execution/ash-workflow-attributes.d.ts +118 -0
- package/dist/src/execution/ash-workflow-attributes.js +1 -0
- package/dist/src/execution/channel-context.d.ts +5 -0
- package/dist/src/execution/channel-context.js +1 -0
- package/dist/src/execution/create-session-step.d.ts +28 -1
- package/dist/src/execution/create-session-step.js +1 -1
- package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
- package/dist/src/execution/durable-session-store.d.ts +7 -0
- package/dist/src/execution/runtime-context.js +1 -1
- package/dist/src/execution/sandbox/bindings/local.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/sandbox/prewarm.js +1 -1
- package/dist/src/execution/sandbox/session.d.ts +6 -1
- package/dist/src/execution/sandbox/session.js +1 -1
- package/dist/src/execution/session.d.ts +6 -0
- package/dist/src/execution/session.js +2 -2
- package/dist/src/execution/skills/instructions.d.ts +3 -2
- package/dist/src/execution/subagent-tool.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/attachment-staging.js +1 -1
- package/dist/src/harness/code-mode.d.ts +0 -5
- package/dist/src/harness/code-mode.js +1 -1
- package/dist/src/harness/emission.d.ts +1 -1
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/instrumentation-config.d.ts +1 -1
- package/dist/src/harness/instrumentation-metadata.d.ts +23 -0
- package/dist/src/harness/instrumentation-metadata.js +1 -0
- package/dist/src/harness/otel-integration.d.ts +2 -2
- package/dist/src/harness/otel-integration.js +1 -1
- package/dist/src/harness/step-hooks.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/harness/turn-tag-state.d.ts +50 -0
- package/dist/src/harness/turn-tag-state.js +1 -0
- package/dist/src/harness/types.d.ts +11 -2
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/schema-backed.d.ts +0 -1
- package/dist/src/internal/authored-definition/schema-backed.js +1 -1
- package/dist/src/internal/instrumentation.d.ts +39 -0
- package/dist/src/internal/instrumentation.js +1 -0
- package/dist/src/internal/workflow/builtins.d.ts +32 -0
- package/dist/src/internal/workflow/builtins.js +1 -1
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +1 -1
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +1 -1
- package/dist/src/internal/workflow-bundle/workflow-core-shim.d.ts +34 -0
- package/dist/src/internal/workflow-bundle/workflow-core-shim.js +1 -1
- package/dist/src/internal/workflow-bundle/workflow-transformer.js +1 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/packages/ash-scaffold/src/steps/run-add-to-agent.js +2 -2
- package/dist/src/packages/ash-scaffold/src/web-template.js +1 -0
- package/dist/src/public/channels/discord/discordChannel.d.ts +5 -2
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/channels/slack/attachments.js +1 -1
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +12 -8
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/teamsChannel.d.ts +5 -2
- package/dist/src/public/channels/telegram/telegramChannel.d.ts +5 -2
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/channels/twilio/index.d.ts +1 -1
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +12 -3
- package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
- package/dist/src/public/definitions/defineChannel.d.ts +17 -4
- package/dist/src/public/definitions/defineChannel.js +1 -1
- package/dist/src/public/definitions/hook.d.ts +3 -11
- package/dist/src/public/definitions/instrumentation.d.ts +1 -66
- package/dist/src/public/definitions/instrumentation.js +1 -1
- package/dist/src/public/definitions/skill.d.ts +5 -0
- package/dist/src/public/definitions/tool.d.ts +25 -66
- package/dist/src/public/definitions/tool.js +1 -1
- package/dist/src/public/instrumentation/index.d.ts +175 -1
- package/dist/src/public/instrumentation/index.js +1 -1
- package/dist/src/public/sandbox/index.d.ts +1 -0
- package/dist/src/public/skills/index.d.ts +2 -0
- package/dist/src/public/skills/index.js +1 -1
- package/dist/src/public/tools/index.d.ts +2 -2
- package/dist/src/public/tools/index.js +1 -1
- package/dist/src/runtime/agent/mock-model-adapter.js +4 -7
- package/dist/src/runtime/agent/mock-model-skill-selection.d.ts +9 -0
- package/dist/src/runtime/agent/mock-model-skill-selection.js +4 -0
- package/dist/src/runtime/attributes/emit.d.ts +73 -0
- package/dist/src/runtime/attributes/emit.js +1 -0
- package/dist/src/runtime/channels/registry.js +1 -1
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/framework-tools/code-mode-connection-auth.d.ts +2 -0
- package/dist/src/runtime/framework-tools/connection-search-dynamic.d.ts +34 -0
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -0
- package/dist/src/runtime/framework-tools/index.d.ts +7 -5
- package/dist/src/runtime/framework-tools/index.js +1 -1
- package/dist/src/runtime/prompt/connections.js +1 -1
- package/dist/src/runtime/resolve-agent-graph.js +1 -1
- package/dist/src/runtime/resolve-agent.js +1 -1
- package/dist/src/runtime/resolve-channel.js +1 -1
- package/dist/src/runtime/resolve-dynamic-skill.d.ts +8 -0
- package/dist/src/runtime/resolve-dynamic-skill.js +1 -0
- package/dist/src/runtime/resolve-dynamic-tool.js +1 -1
- package/dist/src/runtime/sessions/compiled-agent-cache.js +1 -1
- package/dist/src/runtime/sessions/runtime-context-keys.js +1 -1
- package/dist/src/runtime/types.d.ts +13 -4
- package/dist/src/shared/dynamic-tool-definition.d.ts +51 -76
- package/dist/src/shared/dynamic-tool-definition.js +1 -1
- package/dist/src/shared/guards.d.ts +14 -0
- package/dist/src/shared/guards.js +1 -1
- package/dist/src/shared/sandbox-network-policy.d.ts +23 -0
- package/dist/src/shared/sandbox-network-policy.js +1 -0
- package/dist/src/shared/sandbox-session.d.ts +15 -0
- package/dist/src/shared/skill-definition.d.ts +5 -4
- package/dist/src/shared/tool-definition.d.ts +12 -0
- package/package.json +2 -1
- package/dist/src/runtime/framework-tools/connection-search.d.ts +0 -57
- package/dist/src/runtime/framework-tools/connection-search.js +0 -1
- package/dist/src/runtime/framework-tools/connection-tools.d.ts +0 -55
- package/dist/src/runtime/framework-tools/connection-tools.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger,logError}from"#internal/logging.js";import{isCompiledChannel}from"#channel/compiled-channel.js";import{
|
|
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{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponse}from"#public/channels/telegram/hitl.js";import{parseTelegramUpdate,prependTelegramContext}from"#public/channels/telegram/inbound.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,r={...defaultEvents,...e.events},c=defineChannel({kindHint:`telegram`,state:initialTelegramState(e.botUsername),fetchFile:createTelegramFetchFile({api:e.api,credentials:e.credentials,policy:t}),context(t,n){return rebuildTelegramContext(t,n,e)},routes:[POST(e.route??`/ash/v1/telegram`,async(r,{send:a,waitUntil:o})=>{let s=await verifyInbound(r,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Telegram body is not valid JSON`,{error:e}),new Response(`ok`)}let l=parseTelegramUpdate(c);return l===null?new Response(`ok`):l.kind===`message`?(o(dispatchMessage({config:e,message:l.message,onMessage:n,send:a,uploadPolicy:t})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:l.callbackQuery,send:a})),new Response(`ok`))})],async receive(t,{send:n}){let r=t.args,i=readChatId(r.chatId);if(i===void 0)throw Error(`telegramChannel().receive requires args.chatId.`);let a=typeof r.messageThreadId==`number`?r.messageThreadId:void 0,o=readOptionalString(r.conversationId),s=r.initialMessage;if(s!==void 0&&o!==void 0)throw Error("telegramChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c=o;return s!==void 0&&(c=(await buildTelegramHandle({config:e,state:{...initialTelegramState(e.botUsername),chatId:i,messageThreadId:a??null}}).sendMessage(s)).id||void 0),n(t.message,{auth:t.auth,continuationToken:telegramContinuationToken({chatId:i,conversationId:c,messageThreadId:a}),state:{...initialTelegramState(e.botUsername),chatId:i,conversationId:c??null,messageThreadId:a??null}})},events:r});return attachTelegramDeliver(c),c}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let n=e.config.api,r=e.state,i=e.config.credentials;function anchor(t){!t.id||r.chatType===`private`||(r.conversationId=t.id,r.chatId&&e.session?.setContinuationToken(telegramContinuationToken({chatId:r.chatId,conversationId:t.id,messageThreadId:r.messageThreadId??void 0})))}async function sendOne(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for outbound message.`);let a=await sendTelegramMessage({apiBaseUrl:n?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??r.messageThreadId??void 0},credentials:i,fetch:n?.fetch,fileBaseUrl:n?.fileBaseUrl,chatId:t});return anchor(a),a}return{botUsername:r.botUsername??e.config.botUsername,chatId:r.chatId??``,chatType:r.chatType??void 0,conversationId:r.conversationId??void 0,messageThreadId:r.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:n?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:i,fetch:n?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:n?.apiBaseUrl,chatId:t,credentials:i,fetch:n?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},post(e){return postTelegramMessage(e,sendOne)},request(e,t){return callTelegramApi({apiBaseUrl:n?.apiBaseUrl,body:t,botToken:i?.botToken,fetch:n?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let a=r.chatId??``;if(a)try{await sendTelegramChatAction({action:e,apiBaseUrl:n?.apiBaseUrl,chatId:a,credentials:i,fetch:n?.fetch,messageThreadId:r.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:a})}}}}async function postTelegramMessage(e,t){let n=typeof e==`string`?{text:e}:e,r=splitTelegramMessageText(n.text),i;for(let[e,a]of r.entries()){let r=await t(e===0?{...n,text:a}:{text:a});i===void 0&&(i=r)}return i??{id:``,raw:null}}async function verifyInbound(e,t){try{return await verifyTelegramRequest(e,{secretToken:t?.webhookVerifier?void 0:t?.webhookSecretToken,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`telegram inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){if(e.message.from?.isBot===!0)return;let t=stateFromMessage(e.message,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})},r;try{r=await e.onMessage(n,e.message)}catch(e){log.error(`message handler failed`,{error:e});return}if(r==null)return;let i=collectTelegramFileParts(e.message.attachments,e.uploadPolicy),a=prependTelegramContext(buildTelegramTurnMessage(e.message,i),{botUsername:e.config.botUsername,chatId:e.message.chat.id,chatTitle:e.message.chat.title,chatType:e.message.chat.type,messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),o=e.message.text||e.message.caption,s=e.message.replyToMessage?.from?.isBot===!0&&o.trim().length>0?[telegramReplyInputResponse({messageId:e.message.replyToMessage.messageId,text:o})]:void 0;try{await e.send({inputResponses:s,message:a,modelContext:r.modelContext},{auth:r.auth,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromCallbackQuery(e.query,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_HITL_CALLBACK_PREFIX)===!0){try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Answer received.`})}catch(e){log.warn(`Telegram callback-query acknowledgement failed`,{error:e})}if(!e.query.message||!t.chatId)return;try{await e.send({inputResponses:[telegramCallbackInputResponse(e.query.data)]},{auth:null,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`callback query delivery failed`,{error:e})}return}if(e.config.onCallbackQuery!==void 0){try{await e.config.onCallbackQuery(n,e.query)}catch(e){log.error(`custom callback-query handler failed`,{error:e})}return}try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Unsupported action.`})}catch(e){log.warn(`Telegram unsupported callback-query acknowledgement failed`,{error:e})}}function attachTelegramDeliver(e){if(!isCompiledChannel(e))return;let t=e.adapter;t.deliver=(e,t)=>{let n=e.inputResponses??[];if(n.some(isTelegramSyntheticResponse)){let r=resolveTelegramInputResponses(t.state,n);return r.length>0?{inputResponses:r,modelContext:e.modelContext}:e.message===void 0?void 0:{message:e.message,modelContext:e.modelContext}}return defaultDeliverResult(e)}}function stateFromMessage(e,t){let n=e.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:e.chat.id,chatType:e.chat.type,conversationId:n?null:conversationIdForMessage(e),messageThreadId:e.messageThreadId??null,triggeringUserId:e.from?.id??null}}function stateFromCallbackQuery(e,t){let n=e.message;if(!n)return{...initialTelegramState(t.botUsername),triggeringUserId:e.from.id};let r=n.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:n.chat.id,chatType:n.chat.type,conversationId:r?null:n.messageId,messageThreadId:n.messageThreadId??null,triggeringUserId:e.from.id}}function conversationIdForMessage(e){return e.replyToMessage?.from?.isBot===!0?e.replyToMessage.messageId:e.messageId}function continuationTokenFromState(e){return telegramContinuationToken({chatId:e.chatId??``,conversationId:e.chatType===`private`?void 0:e.conversationId??void 0,messageThreadId:e.messageThreadId??void 0})}function initialTelegramState(e){return{botUsername:e??null,chatId:null,chatType:null,conversationId:null,hitlCallbacks:{},messageThreadId:null,nextHitlCallbackId:0,pendingFreeformReplies:{},triggeringUserId:null}}function readChatId(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}function readOptionalString(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}export{telegramChannel};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { twilioChannel, type TwilioAllowFrom, type TwilioChannel, type TwilioChannelConfig, type TwilioChannelCredentials, type TwilioChannelEvents, type TwilioChannelState, type TwilioContext, type TwilioEventContext, type TwilioHandle, type TwilioInboundResult, type TwilioInboundResultOrPromise, type TwilioMessagingConfig, type TwilioReceiveArgs, type TwilioSendMessageOptions, type TwilioVoiceConfig, type TwilioVoiceResult, type TwilioVoiceResultOrPromise, } from "#public/channels/twilio/twilioChannel.js";
|
|
1
|
+
export { twilioChannel, type TwilioAllowFrom, type TwilioChannel, type TwilioChannelConfig, type TwilioChannelCredentials, type TwilioChannelEvents, type TwilioChannelState, type TwilioContext, type TwilioEventContext, type TwilioHandle, type TwilioInboundResult, type TwilioInboundResultOrPromise, type TwilioInstrumentationMetadata, type TwilioMessagingConfig, type TwilioReceiveArgs, type TwilioSendMessageOptions, type TwilioVoiceConfig, type TwilioVoiceResult, type TwilioVoiceResultOrPromise, } from "#public/channels/twilio/twilioChannel.js";
|
|
2
2
|
export { callTwilioApi, resolveTwilioAccountSid, sendTwilioMessage, twilioContinuationToken, updateTwilioCall, type TwilioAccountSid, type TwilioApiOptions, type TwilioApiResponse, type TwilioCredentials, type TwilioFetch, type TwilioSendMessageInput, type TwilioUpdateCallInput, } from "#public/channels/twilio/api.js";
|
|
3
3
|
export type { TwilioInboundContext, TwilioTextMessage, TwilioVoiceCall, TwilioVoiceTranscription, } from "#public/channels/twilio/inbound.js";
|
|
4
4
|
export { emptyTwilioResponse, escapeXml, gatherSpeechTwilioResponse, sayTwilioResponse, twimlResponse, type TwilioGatherTwimlOptions, } from "#public/channels/twilio/twiml.js";
|
|
@@ -15,10 +15,13 @@ type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessa
|
|
|
15
15
|
export interface TwilioContext {
|
|
16
16
|
readonly twilio: TwilioHandle;
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
19
|
-
export interface
|
|
18
|
+
/** Channel-owned Twilio context returned by `context()`. */
|
|
19
|
+
export interface TwilioChannelContext extends TwilioContext {
|
|
20
20
|
state: TwilioChannelState;
|
|
21
21
|
}
|
|
22
|
+
/** Event-handler Twilio context, including session operations. */
|
|
23
|
+
export interface TwilioEventContext extends TwilioChannelContext, ChannelSessionOps {
|
|
24
|
+
}
|
|
22
25
|
/** JSON-serializable state for the phone-number conversation. */
|
|
23
26
|
export interface TwilioChannelState {
|
|
24
27
|
/** Caller / sender phone number. */
|
|
@@ -30,6 +33,12 @@ export interface TwilioChannelState {
|
|
|
30
33
|
/** Most recent inbound Call SID when this session was started by voice. */
|
|
31
34
|
lastCallSid?: string | null;
|
|
32
35
|
}
|
|
36
|
+
export interface TwilioInstrumentationMetadata extends Record<string, unknown> {
|
|
37
|
+
readonly from: string | null;
|
|
38
|
+
readonly lastCallSid: string | null;
|
|
39
|
+
readonly lastMessageSid: string | null;
|
|
40
|
+
readonly to: string | null;
|
|
41
|
+
}
|
|
33
42
|
/** Twilio channel credentials. `authToken` also verifies inbound webhook signatures. */
|
|
34
43
|
export interface TwilioChannelCredentials extends TwilioCredentials {
|
|
35
44
|
readonly authToken?: TwilioAuthToken;
|
|
@@ -174,7 +183,7 @@ export interface TwilioSendMessageOptions {
|
|
|
174
183
|
readonly statusCallbackUrl?: string;
|
|
175
184
|
}
|
|
176
185
|
/** Concrete return type of {@link twilioChannel}. */
|
|
177
|
-
export interface TwilioChannel extends Channel<TwilioChannelState, TwilioReceiveArgs> {
|
|
186
|
+
export interface TwilioChannel extends Channel<TwilioChannelState, TwilioReceiveArgs, TwilioInstrumentationMetadata> {
|
|
178
187
|
}
|
|
179
188
|
/** Twilio channel factory for SMS and speech-transcribed inbound calls. */
|
|
180
189
|
export declare function twilioChannel(config: TwilioChannelConfig): TwilioChannel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{verifyTwilioRequest}from"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription,prependTwilioContext}from"#public/channels/twilio/inbound.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let r=buildRoutes(e.route??`/ash/v1/twilio`),i=e.onText??defaultOnText,a=e.onVoice??defaultOnVoice,s=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events};return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[POST(r.messages,async(t,{send:n,waitUntil:r})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioTextMessage(a.params);return o?await isAllowed(o.from,e.allowFrom)?(r(dispatchText({config:e,message:o,onText:i,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}),POST(r.voice,async t=>{let n=await verifyInbound(t,e);if(n===null)return new Response(`unauthorized`,{status:401});let i=parseTwilioVoiceCall(n.params);if(!i)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(i.from,e.allowFrom))return new Response(`forbidden`,{status:403});let o=await acceptVoiceCall({call:i,config:e,onVoice:a});if(o===null)return new Response(`forbidden`,{status:403});let s=o??{};return gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),hints:s.hints??e.voice?.hints,language:s.language??e.voice?.language,profanityFilter:s.profanityFilter??e.voice?.profanityFilter,prompt:s.prompt??e.voice?.prompt??`Please say your message after the tone.`,speechModel:s.speechModel??e.voice?.speechModel,speechTimeout:s.speechTimeout??e.voice?.speechTimeout??`auto`,timeoutSeconds:s.timeoutSeconds??e.voice?.timeoutSeconds,voice:s.voice??e.voice?.voice})}),POST(r.transcription,async(t,{send:n,waitUntil:i})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioVoiceTranscription(a.params);return o?await isAllowed(o.from,e.allowFrom)?(i(dispatchVoiceTranscription({config:e,onVoiceTranscription:s,send:n,transcription:o})),sayTwilioResponse(e.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),language:e.voice?.language,prompt:e.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.voice?.speechTimeout??`auto`,timeoutSeconds:e.voice?.timeoutSeconds})})],async receive(t,{send:n}){let r=readString(t.args.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires args.phoneNumber.`);let i=readString(t.args.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function buildRoutes(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return{messages:`${t}/messages`,transcription:`${t}/voice/transcription`,voice:`${t}/voice`}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function verifyInbound(e,t){try{return await verifyTwilioRequest(e,{authToken:t.credentials?.authToken,webhookUrl:t.webhookUrl})}catch(e){return log.warn(`twilio inbound verification failed`,{error:e}),null}}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=prependTwilioContext(t.body,{channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send(i,{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=prependTwilioContext(t.text,{callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send(i,{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}async function buildActionUrl(e,t,n){let r=typeof t.publicBaseUrl==`function`?await t.publicBaseUrl(e):t.publicBaseUrl;if(r)return new URL(n,ensureTrailingSlash(r)).toString();let i=new URL(e.url);return i.pathname=n,i.search=``,i.toString()}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:`${e}/`}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{verifyTwilioRequest}from"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription,prependTwilioContext}from"#public/channels/twilio/inbound.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let r=buildRoutes(e.route??`/ash/v1/twilio`),i=e.onText??defaultOnText,a=e.onVoice??defaultOnVoice,s=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events};return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{from:e.from,lastCallSid:e.lastCallSid??null,lastMessageSid:e.lastMessageSid??null,to:e.to}},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[POST(r.messages,async(t,{send:n,waitUntil:r})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioTextMessage(a.params);return o?await isAllowed(o.from,e.allowFrom)?(r(dispatchText({config:e,message:o,onText:i,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}),POST(r.voice,async t=>{let n=await verifyInbound(t,e);if(n===null)return new Response(`unauthorized`,{status:401});let i=parseTwilioVoiceCall(n.params);if(!i)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(i.from,e.allowFrom))return new Response(`forbidden`,{status:403});let o=await acceptVoiceCall({call:i,config:e,onVoice:a});if(o===null)return new Response(`forbidden`,{status:403});let s=o??{};return gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),hints:s.hints??e.voice?.hints,language:s.language??e.voice?.language,profanityFilter:s.profanityFilter??e.voice?.profanityFilter,prompt:s.prompt??e.voice?.prompt??`Please say your message after the tone.`,speechModel:s.speechModel??e.voice?.speechModel,speechTimeout:s.speechTimeout??e.voice?.speechTimeout??`auto`,timeoutSeconds:s.timeoutSeconds??e.voice?.timeoutSeconds,voice:s.voice??e.voice?.voice})}),POST(r.transcription,async(t,{send:n,waitUntil:i})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioVoiceTranscription(a.params);return o?await isAllowed(o.from,e.allowFrom)?(i(dispatchVoiceTranscription({config:e,onVoiceTranscription:s,send:n,transcription:o})),sayTwilioResponse(e.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),language:e.voice?.language,prompt:e.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.voice?.speechTimeout??`auto`,timeoutSeconds:e.voice?.timeoutSeconds})})],async receive(t,{send:n}){let r=readString(t.args.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires args.phoneNumber.`);let i=readString(t.args.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function buildRoutes(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return{messages:`${t}/messages`,transcription:`${t}/voice/transcription`,voice:`${t}/voice`}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function verifyInbound(e,t){try{return await verifyTwilioRequest(e,{authToken:t.credentials?.authToken,webhookUrl:t.webhookUrl})}catch(e){return log.warn(`twilio inbound verification failed`,{error:e}),null}}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=prependTwilioContext(t.body,{channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send(i,{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=prependTwilioContext(t.text,{callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send(i,{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}async function buildActionUrl(e,t,n){let r=typeof t.publicBaseUrl==`function`?await t.publicBaseUrl(e):t.publicBaseUrl;if(r)return new URL(n,ensureTrailingSlash(r)).toString();let i=new URL(e.url);return i.pathname=n,i.search=``,i.toString()}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:`${e}/`}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};
|
|
@@ -6,6 +6,7 @@ import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
|
6
6
|
import type { SessionContext } from "#public/definitions/callback-context.js";
|
|
7
7
|
import type { RouteDefinition, SendFn } from "#channel/routes.js";
|
|
8
8
|
import type { Session, SessionHandle } from "#channel/session.js";
|
|
9
|
+
declare const CHANNEL_METADATA_TYPE: unique symbol;
|
|
9
10
|
export type { Session, SessionHandle } from "#channel/session.js";
|
|
10
11
|
export { GET, POST, PUT, PATCH, DELETE } from "#channel/routes.js";
|
|
11
12
|
export type { RouteDefinition, RouteHandlerArgs, SendFn, SendOptions, SendPayload, GetSessionFn, } from "#channel/routes.js";
|
|
@@ -53,7 +54,7 @@ export interface ReceiveInput<TReceiveArgs = Record<string, unknown>> {
|
|
|
53
54
|
readonly args: Readonly<TReceiveArgs>;
|
|
54
55
|
readonly auth: SessionAuthContext | null;
|
|
55
56
|
}
|
|
56
|
-
export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs = Record<string, unknown>> {
|
|
57
|
+
export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> {
|
|
57
58
|
readonly state?: TState;
|
|
58
59
|
/**
|
|
59
60
|
* Builds the per-step channel context handed to `events` and
|
|
@@ -68,7 +69,7 @@ export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs
|
|
|
68
69
|
* (with {@link ChannelSessionOps} injected) and passes
|
|
69
70
|
* {@link SessionContext} as a separate `ctx` argument.
|
|
70
71
|
*/
|
|
71
|
-
context?(state: NonNullable<TState>, session: SessionHandle):
|
|
72
|
+
context?(state: NonNullable<TState>, session: SessionHandle): TCtx;
|
|
72
73
|
readonly routes: readonly RouteDefinition<TState>[];
|
|
73
74
|
receive?(input: ReceiveInput<TReceiveArgs>, args: {
|
|
74
75
|
send: SendFn<TState>;
|
|
@@ -84,6 +85,16 @@ export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs
|
|
|
84
85
|
* stage the file to the sandbox.
|
|
85
86
|
*/
|
|
86
87
|
readonly fetchFile?: (url: string) => Promise<Buffer | FetchFileResult | null>;
|
|
88
|
+
/**
|
|
89
|
+
* Channel-owned metadata exposed to instrumentation callbacks.
|
|
90
|
+
*
|
|
91
|
+
* Keep this projection intentionally small. It is the channel's public
|
|
92
|
+
* observability surface, not a dump of durable adapter state. Return an
|
|
93
|
+
* object composed of JSON primitives, arrays, and plain objects. Ash omits
|
|
94
|
+
* `undefined` object properties and drops projections containing values such
|
|
95
|
+
* as `Date` or `Map`.
|
|
96
|
+
*/
|
|
97
|
+
readonly metadata?: (state: NonNullable<TState>) => TMetadata;
|
|
87
98
|
/**
|
|
88
99
|
* Identifier of the adapter family this channel belongs to. Set by
|
|
89
100
|
* higher-level wrappers (e.g. `slackChannel` passes `"slack"`) so
|
|
@@ -97,8 +108,9 @@ export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs
|
|
|
97
108
|
*/
|
|
98
109
|
readonly kindHint?: string;
|
|
99
110
|
}
|
|
100
|
-
export interface Channel<TState = undefined, TReceiveArgs = Record<string, unknown>> extends TypedReceiveRoute<TReceiveArgs> {
|
|
111
|
+
export interface Channel<TState = undefined, TReceiveArgs = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> extends TypedReceiveRoute<TReceiveArgs> {
|
|
101
112
|
readonly __kind: typeof CHANNEL_SENTINEL;
|
|
113
|
+
readonly [CHANNEL_METADATA_TYPE]?: TMetadata;
|
|
102
114
|
readonly routes: readonly {
|
|
103
115
|
method: string;
|
|
104
116
|
path: string;
|
|
@@ -107,4 +119,5 @@ export interface Channel<TState = undefined, TReceiveArgs = Record<string, unkno
|
|
|
107
119
|
send: SendFn<TState>;
|
|
108
120
|
}) => Promise<Session>;
|
|
109
121
|
}
|
|
110
|
-
export
|
|
122
|
+
export type InferChannelMetadata<TChannel> = TChannel extends Channel<any, any, infer TMetadata> ? TMetadata : Record<string, unknown>;
|
|
123
|
+
export declare function defineChannel<TState = undefined, TCtx = void, TReceiveArgs = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>>(definition: ChannelDefinition<TState, TCtx, TReceiveArgs, TMetadata>): Channel<TState, TReceiveArgs, TMetadata>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{
|
|
1
|
+
import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,PATCH,POST,PUT}from"#channel/routes.js";function defineChannel(t){let n=buildAdapter(t);return{__kind:CHANNEL_SENTINEL,routes:t.routes,adapter:n,receive:t.receive}}function buildAdapter(e){let i=e.state!=null,a=e.context!=null,o=e.fetchFile!==void 0,s=e.metadata,c=i||a||s!==void 0,l={},u=!1,d=[`turn.started`,`actions.requested`,`action.result`,`message.completed`,`message.appended`,`input.requested`,`turn.failed`,`turn.completed`,`session.failed`,`session.completed`,`session.waiting`,`authorization.required`,`authorization.completed`],f=e.events;for(let e of d){let t=f?.[e];t&&(u=!0,l[e]=(r,i)=>{let a={...i,continuationToken:i.session?.continuationToken??``,setContinuationToken:e=>i.session?.setContinuationToken(e)};return e===`session.failed`?t(r,a):t(r,a,buildCallbackContext())})}return!c&&!u&&!o?{kind:e.kindHint??HTTP_ADAPTER_KIND}:{kind:e.kindHint??`defineChannel`,state:i?{...e.state}:{},fetchFile:e.fetchFile,instrumentation:s===void 0?void 0:{metadata(e){return s(e)}},createAdapterContext(t){let n=t.state,r=t.session;return{...a?e.context(n,r):{},state:n,ctx:t.ctx,session:r}},deliver(e){return defaultDeliverResult(e)},...l}}export{DELETE,GET,PATCH,POST,PUT,defineChannel};
|
|
@@ -2,7 +2,6 @@ import type { ModelMessage } from "ai";
|
|
|
2
2
|
import type { HandleMessageStreamEvent } from "../../protocol/message.js";
|
|
3
3
|
import type { SessionContext } from "./callback-context.js";
|
|
4
4
|
import type { ExactDefinition } from "./exact.js";
|
|
5
|
-
import type { NamedSkillDefinition } from "./skill.js";
|
|
6
5
|
/**
|
|
7
6
|
* Context passed to every hook handler.
|
|
8
7
|
*
|
|
@@ -24,22 +23,15 @@ export interface HookContext extends SessionContext {
|
|
|
24
23
|
* `lifecycle.turn`).
|
|
25
24
|
*
|
|
26
25
|
* `modelContext` messages are appended to the next model call's message
|
|
27
|
-
* list and never written to durable session history.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* contributions are concatenated session-then-turn before the harness's
|
|
31
|
-
* first model call.
|
|
26
|
+
* list and never written to durable session history. Each lifecycle key
|
|
27
|
+
* runs in registry order; contributions are concatenated session-then-turn
|
|
28
|
+
* before the harness's first model call.
|
|
32
29
|
*
|
|
33
30
|
* Returning `void` (or `undefined`) means "no opinion" — the turn
|
|
34
31
|
* proceeds with no additional model context from this hook.
|
|
35
32
|
*/
|
|
36
33
|
export interface LifecycleHookResult {
|
|
37
34
|
readonly modelContext?: readonly ModelMessage[];
|
|
38
|
-
/**
|
|
39
|
-
* Skill packages to materialize into the live durable sandbox before the
|
|
40
|
-
* next model call. Authored skill names cannot be overwritten.
|
|
41
|
-
*/
|
|
42
|
-
readonly skills?: readonly NamedSkillDefinition[];
|
|
43
35
|
}
|
|
44
36
|
/**
|
|
45
37
|
* Lifecycle hook signature shared by `lifecycle.session` and
|
|
@@ -1,66 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Context passed to the {@link InstrumentationDefinition.setup} callback.
|
|
4
|
-
*/
|
|
5
|
-
export interface InstrumentationSetupContext {
|
|
6
|
-
/**
|
|
7
|
-
* The agent name declared by `defineAgent`.
|
|
8
|
-
*
|
|
9
|
-
* Use this as the `serviceName` for `registerOTel` instead of
|
|
10
|
-
* hard-coding a string — it prevents copy-paste drift across agents.
|
|
11
|
-
*/
|
|
12
|
-
readonly agentName: string;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Authored instrumentation settings accepted by `defineInstrumentation`.
|
|
16
|
-
*
|
|
17
|
-
* The presence of a `defineInstrumentation` export implicitly enables
|
|
18
|
-
* telemetry — there is no separate `isEnabled` toggle.
|
|
19
|
-
*/
|
|
20
|
-
export interface InstrumentationDefinition {
|
|
21
|
-
/**
|
|
22
|
-
* Override the default function identifier attached to telemetry spans.
|
|
23
|
-
*
|
|
24
|
-
* When omitted, Ash derives the identifier from the agent name.
|
|
25
|
-
*/
|
|
26
|
-
readonly functionId?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Additional metadata merged into every AI SDK telemetry span.
|
|
29
|
-
*/
|
|
30
|
-
readonly metadata?: Record<string, string>;
|
|
31
|
-
/**
|
|
32
|
-
* Whether to record full model inputs in telemetry spans.
|
|
33
|
-
*
|
|
34
|
-
* Defaults to `true` when `instrumentation.ts` is present. Set to `false`
|
|
35
|
-
* to disable recording inputs, which may be useful if inputs contain
|
|
36
|
-
* sensitive content or you want to reduce span payload size.
|
|
37
|
-
*/
|
|
38
|
-
readonly recordInputs?: boolean;
|
|
39
|
-
/**
|
|
40
|
-
* Whether to record full model outputs in telemetry spans.
|
|
41
|
-
*
|
|
42
|
-
* Defaults to `true` when `instrumentation.ts` is present. Set to `false`
|
|
43
|
-
* to disable recording outputs.
|
|
44
|
-
*/
|
|
45
|
-
readonly recordOutputs?: boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Optional setup callback invoked at server startup with the resolved
|
|
48
|
-
* agent name.
|
|
49
|
-
*
|
|
50
|
-
* Use this to call `registerOTel` or any other OTel provider setup.
|
|
51
|
-
* The `context.agentName` value comes from `defineAgent`, so you never
|
|
52
|
-
* need to hard-code a service name.
|
|
53
|
-
*/
|
|
54
|
-
readonly setup?: (context: InstrumentationSetupContext) => void;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Defines instrumentation settings for the agent application.
|
|
58
|
-
*
|
|
59
|
-
* Export the result as the default export of `agent/instrumentation.ts`.
|
|
60
|
-
* Ash picks up these settings at server startup and applies them to every
|
|
61
|
-
* AI SDK model call.
|
|
62
|
-
*
|
|
63
|
-
* The `setup` callback is invoked later by the framework with the resolved
|
|
64
|
-
* agent name — it is not called during `defineInstrumentation` itself.
|
|
65
|
-
*/
|
|
66
|
-
export declare function defineInstrumentation<T extends InstrumentationDefinition>(definition: ExactDefinition<T, InstrumentationDefinition>): T;
|
|
1
|
+
export * from "#public/instrumentation/index.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export*from"#public/instrumentation/index.js";export{};
|
|
@@ -12,5 +12,10 @@ export type SkillDefinition = SkillPackageDefinition;
|
|
|
12
12
|
/**
|
|
13
13
|
* Defines a skill in TypeScript using the same shape discovery produces from
|
|
14
14
|
* markdown, with optional package-relative sibling files.
|
|
15
|
+
*
|
|
16
|
+
* When used as the default export of a file in `agent/skills/`, this
|
|
17
|
+
* produces a static skill. When used inside a `defineDynamic` handler,
|
|
18
|
+
* the brand stamp lets the lifecycle code detect single-entry vs
|
|
19
|
+
* map-of-entries return shapes.
|
|
15
20
|
*/
|
|
16
21
|
export declare function defineSkill<TSkill extends SkillDefinition>(definition: ExactDefinition<TSkill, SkillDefinition>): TSkill;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { StandardJSONSchemaV1 } from "#compiled/@standard-schema/spec/index.js";
|
|
2
2
|
import type { ModelMessage } from "ai";
|
|
3
|
-
import type { PublicToolDefinition } from "#shared/tool-definition.js";
|
|
3
|
+
import type { PublicToolDefinition, ToolModelOutput } from "#shared/tool-definition.js";
|
|
4
4
|
import type { SessionContext } from "#public/definitions/callback-context.js";
|
|
5
5
|
import type { JsonObject } from "#shared/json.js";
|
|
6
|
-
import { type
|
|
6
|
+
import { type DynamicEvents, type DynamicSentinel } from "#shared/dynamic-tool-definition.js";
|
|
7
7
|
/**
|
|
8
8
|
* Domain input passed to a tool's {@link ToolDefinition.onCompact} hook.
|
|
9
9
|
*/
|
|
@@ -39,19 +39,7 @@ export interface NeedsApprovalContext<TInput = Record<string, unknown>> {
|
|
|
39
39
|
readonly toolInput?: ApprovalToolInput<TInput>;
|
|
40
40
|
readonly toolName: string;
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
* Ash-owned shape for the model-facing tool result produced by
|
|
44
|
-
* {@link ToolDefinition.toModelOutput}. Structurally compatible with the
|
|
45
|
-
* AI SDK's `ToolResultOutput` so the harness can forward it without
|
|
46
|
-
* conversion.
|
|
47
|
-
*/
|
|
48
|
-
export type ToolModelOutput = {
|
|
49
|
-
readonly type: "text";
|
|
50
|
-
readonly value: string;
|
|
51
|
-
} | {
|
|
52
|
-
readonly type: "json";
|
|
53
|
-
readonly value: unknown;
|
|
54
|
-
};
|
|
42
|
+
export type { ToolModelOutput } from "#shared/tool-definition.js";
|
|
55
43
|
/**
|
|
56
44
|
* Result returned from a {@link ToolRetentionPolicy} function.
|
|
57
45
|
*
|
|
@@ -140,6 +128,13 @@ export type ToolDefinition<TInput = unknown, TOutput = unknown> = PublicToolDefi
|
|
|
140
128
|
};
|
|
141
129
|
/**
|
|
142
130
|
* Defines a tool configuration.
|
|
131
|
+
*
|
|
132
|
+
* Used for both static tools (default export from `agent/tools/*.ts`)
|
|
133
|
+
* and as the entry wrapper inside `defineDynamic` resolvers.
|
|
134
|
+
*
|
|
135
|
+
* For static tools, the runtime tool name is the filename slug.
|
|
136
|
+
* For dynamic entries, `defineTool` stamps a brand that the
|
|
137
|
+
* lifecycle code validates — raw object literals are rejected.
|
|
143
138
|
*/
|
|
144
139
|
export declare function defineTool<TSchema extends StandardJSONSchemaV1<unknown, unknown>, TOutput>(definition: {
|
|
145
140
|
description: ToolDefinition<unknown, unknown>["description"];
|
|
@@ -160,72 +155,37 @@ export declare function defineTool<TOutput>(definition: {
|
|
|
160
155
|
onCompact?: ToolDefinition<unknown, unknown>["onCompact"];
|
|
161
156
|
}): ToolDefinition<Record<string, unknown>, TOutput>;
|
|
162
157
|
export declare function defineTool<TInput = unknown, TOutput = unknown>(definition: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
|
|
163
|
-
export declare function defineTool<TInput = unknown, TOutput = unknown>(definition: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
|
|
164
|
-
export declare function defineTool(definition: {
|
|
165
|
-
readonly events: DynamicSingleToolEvents;
|
|
166
|
-
}): DynamicToolSentinel;
|
|
167
158
|
/**
|
|
168
159
|
* Defines a dynamic tool set resolved at runtime. The file's
|
|
169
|
-
* path-derived slug becomes the tool name
|
|
170
|
-
*
|
|
171
|
-
* entry, the tool takes the file's name directly.
|
|
172
|
-
*
|
|
173
|
-
* Uses the same `events` object shape as {@link defineHook}:
|
|
160
|
+
* path-derived slug becomes the tool name; the handler's return
|
|
161
|
+
* shape determines naming:
|
|
174
162
|
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* "session.started": async (event, ctx) => ({
|
|
179
|
-
* export: { description: "...", inputSchema: {...}, execute: ... },
|
|
180
|
-
* query: { description: "...", inputSchema: {...}, execute: ... },
|
|
181
|
-
* }),
|
|
182
|
-
* },
|
|
183
|
-
* });
|
|
184
|
-
* ```
|
|
185
|
-
*/
|
|
186
|
-
export declare function defineTools(definition: {
|
|
187
|
-
readonly events: DynamicToolEvents;
|
|
188
|
-
}): DynamicToolsSentinel;
|
|
189
|
-
/**
|
|
190
|
-
* Typed wrapper for a single entry inside a `defineTools` resolver.
|
|
191
|
-
* Mirrors the AI SDK's `tool()` pattern — an identity function at
|
|
192
|
-
* runtime that captures `TSchema` at the type level so `execute(input)`
|
|
193
|
-
* is inferred from the schema.
|
|
163
|
+
* - Return a single `defineTool(...)` → tool named after the file slug.
|
|
164
|
+
* - Return a `Record<string, defineTool(...)>` → tools named `slug__key`.
|
|
165
|
+
* - Return `null` → no tools for this event.
|
|
194
166
|
*
|
|
195
167
|
* ```ts
|
|
196
|
-
* import {
|
|
168
|
+
* import { defineDynamic, defineTool } from "experimental-ash/tools";
|
|
197
169
|
* import { z } from "zod";
|
|
198
170
|
*
|
|
199
|
-
* export default
|
|
171
|
+
* export default defineDynamic({
|
|
200
172
|
* events: {
|
|
201
|
-
* "session.started": async () => ({
|
|
202
|
-
*
|
|
203
|
-
* description: "
|
|
204
|
-
* inputSchema: z.object({
|
|
173
|
+
* "session.started": async (event, ctx) => ({
|
|
174
|
+
* export: defineTool({
|
|
175
|
+
* description: "Export data",
|
|
176
|
+
* inputSchema: z.object({ format: z.string() }),
|
|
205
177
|
* async execute(input) {
|
|
206
|
-
*
|
|
207
|
-
* return fetchWeather(input.city);
|
|
178
|
+
* return doExport(input.format);
|
|
208
179
|
* },
|
|
209
180
|
* }),
|
|
210
181
|
* }),
|
|
211
182
|
* },
|
|
212
183
|
* });
|
|
213
184
|
* ```
|
|
214
|
-
*
|
|
215
|
-
* Without `tool()`, `execute(input)` defaults to
|
|
216
|
-
* `Record<string, unknown>`. Use `tool()` when you want schema-
|
|
217
|
-
* inferred types.
|
|
218
185
|
*/
|
|
219
|
-
export declare function
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
execute(input: StandardJSONSchemaV1.InferOutput<TSchema>, ctx: ToolContext): TOutput | Promise<TOutput>;
|
|
223
|
-
}): DynamicToolEntry<StandardJSONSchemaV1.InferOutput<TSchema>, TOutput>;
|
|
224
|
-
export declare function tool<TOutput>(entry: {
|
|
225
|
-
description: string;
|
|
226
|
-
inputSchema: JsonObject;
|
|
227
|
-
execute(input: Record<string, unknown>, ctx: ToolContext): TOutput | Promise<TOutput>;
|
|
228
|
-
}): DynamicToolEntry<Record<string, unknown>, TOutput>;
|
|
186
|
+
export declare function defineDynamic(definition: {
|
|
187
|
+
readonly events: DynamicEvents;
|
|
188
|
+
}): DynamicSentinel;
|
|
229
189
|
/**
|
|
230
190
|
* Marker discriminator written into every {@link DisabledToolSentinel}.
|
|
231
191
|
*/
|
|
@@ -248,4 +208,3 @@ export declare function disableTool(): DisabledToolSentinel;
|
|
|
248
208
|
* produced by {@link disableTool}.
|
|
249
209
|
*/
|
|
250
210
|
export declare function isDisabledToolSentinel(value: unknown): value is DisabledToolSentinel;
|
|
251
|
-
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{DYNAMIC_SENTINEL_KIND,TOOL_BRAND}from"#shared/dynamic-tool-definition.js";import{stampDefinitionKey}from"#public/tool-result-narrowing.js";function defineTool(e){return Object.assign(e,{[TOOL_BRAND]:!0}),stampDefinitionKey(e,`tool:${e.description}`),e}function defineDynamic(t){let n={kind:DYNAMIC_SENTINEL_KIND,events:t.events};return stampDefinitionKey(n,`dynamic:${Object.keys(t.events).join(`,`)}`),n}const DISABLED_TOOL_SENTINEL_KIND=`ash:disabled-tool`;function disableTool(){return{kind:DISABLED_TOOL_SENTINEL_KIND}}function isDisabledToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_TOOL_SENTINEL_KIND}export{defineDynamic,defineTool,disableTool,isDisabledToolSentinel};
|
|
@@ -1,4 +1,178 @@
|
|
|
1
|
+
import type { ExactDefinition } from "#public/definitions/exact.js";
|
|
1
2
|
/**
|
|
2
3
|
* Instrumentation authoring helpers for `agent/instrumentation.ts`.
|
|
3
4
|
*/
|
|
4
|
-
|
|
5
|
+
import type { ModelMessage, SystemModelMessage } from "ai";
|
|
6
|
+
import type { SessionAuthContext, SessionParent } from "#channel/types.js";
|
|
7
|
+
import type { Channel } from "#public/definitions/defineChannel.js";
|
|
8
|
+
/**
|
|
9
|
+
* Context passed to the {@link InstrumentationDefinition.setup} callback.
|
|
10
|
+
*/
|
|
11
|
+
export interface InstrumentationSetupContext {
|
|
12
|
+
/**
|
|
13
|
+
* The agent name declared by `defineAgent`.
|
|
14
|
+
*
|
|
15
|
+
* Use this as the `serviceName` for `registerOTel` instead of
|
|
16
|
+
* hard-coding a string — it prevents copy-paste drift across agents.
|
|
17
|
+
*/
|
|
18
|
+
readonly agentName: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* User-authored metadata attached to AI SDK telemetry spans.
|
|
22
|
+
*
|
|
23
|
+
* Keys beginning with `ash.` are reserved for framework-owned metadata
|
|
24
|
+
* and are ignored when returned from authored instrumentation.
|
|
25
|
+
*/
|
|
26
|
+
export type InstrumentationMetadata = Readonly<Record<string, string>>;
|
|
27
|
+
/**
|
|
28
|
+
* Base channel metadata shape used by framework channel kinds.
|
|
29
|
+
*/
|
|
30
|
+
export type InstrumentationChannelMetadata = Readonly<Record<string, unknown>>;
|
|
31
|
+
/**
|
|
32
|
+
* Channel metadata projections keyed by instrumentation channel kind.
|
|
33
|
+
*
|
|
34
|
+
* Channel packages and user-authored channels declaration-merge additional
|
|
35
|
+
* `channel:<registered-name>` entries into this map so `input.channel.metadata`
|
|
36
|
+
* narrows after checking `input.channel.kind`.
|
|
37
|
+
*/
|
|
38
|
+
export interface ChannelMetadataMap {
|
|
39
|
+
readonly http: InstrumentationChannelMetadata;
|
|
40
|
+
readonly schedule: InstrumentationChannelMetadata;
|
|
41
|
+
readonly subagent: InstrumentationChannelMetadata;
|
|
42
|
+
readonly unknown: InstrumentationChannelMetadata;
|
|
43
|
+
}
|
|
44
|
+
export type InstrumentationChannelKind = keyof ChannelMetadataMap;
|
|
45
|
+
/**
|
|
46
|
+
* Authored channel values keyed by path-derived instrumentation channel kind.
|
|
47
|
+
*
|
|
48
|
+
* Ash generates declaration-merged entries for authored channel files. Use
|
|
49
|
+
* this map through {@link isChannel}; authored code should not merge it by
|
|
50
|
+
* hand except for unusual setups outside the compiler-owned path.
|
|
51
|
+
*/
|
|
52
|
+
export interface ChannelReferenceMap {
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Channel projection exposed to instrumentation callbacks.
|
|
56
|
+
*
|
|
57
|
+
* `kind` is the channel identity Ash has for the current turn (`"http"`,
|
|
58
|
+
* `"schedule"`, `"subagent"`, or `channel:<name>` for authored channels,
|
|
59
|
+
* where `<name>` is the channel's filename under `agent/channels/`).
|
|
60
|
+
*/
|
|
61
|
+
export interface InstrumentationChannelForKind<K extends InstrumentationChannelKind> {
|
|
62
|
+
readonly kind: K;
|
|
63
|
+
readonly metadata: ChannelMetadataMap[K];
|
|
64
|
+
}
|
|
65
|
+
export type InstrumentationChannel = {
|
|
66
|
+
readonly [K in InstrumentationChannelKind]: InstrumentationChannelForKind<K>;
|
|
67
|
+
}[InstrumentationChannelKind];
|
|
68
|
+
type ChannelReferenceKind<TChannel> = {
|
|
69
|
+
readonly [K in keyof ChannelReferenceMap]: [TChannel] extends [ChannelReferenceMap[K]] ? [ChannelReferenceMap[K]] extends [TChannel] ? K : never : never;
|
|
70
|
+
}[keyof ChannelReferenceMap];
|
|
71
|
+
export type InstrumentationChannelForChannel<TChannel> = Extract<InstrumentationChannel, {
|
|
72
|
+
readonly kind: Extract<ChannelReferenceKind<TChannel>, InstrumentationChannelKind>;
|
|
73
|
+
}>;
|
|
74
|
+
export interface InstrumentationSession {
|
|
75
|
+
readonly auth: {
|
|
76
|
+
readonly current: SessionAuthContext | null;
|
|
77
|
+
readonly initiator: SessionAuthContext | null;
|
|
78
|
+
};
|
|
79
|
+
readonly id: string;
|
|
80
|
+
readonly parent?: SessionParent;
|
|
81
|
+
}
|
|
82
|
+
export interface InstrumentationTurn {
|
|
83
|
+
readonly id: string;
|
|
84
|
+
readonly sequence: number;
|
|
85
|
+
}
|
|
86
|
+
export interface InstrumentationStep {
|
|
87
|
+
readonly index: number;
|
|
88
|
+
}
|
|
89
|
+
export interface InstrumentationModelInput {
|
|
90
|
+
readonly instructions: string | readonly SystemModelMessage[] | undefined;
|
|
91
|
+
readonly messages: readonly ModelMessage[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Input passed to `metadata["step.started"]`.
|
|
95
|
+
*
|
|
96
|
+
* The callback runs after Ash has built the final model input for this
|
|
97
|
+
* model-call attempt and before the AI SDK model call is constructed.
|
|
98
|
+
*/
|
|
99
|
+
export interface InstrumentationStepStartedMetadataInput {
|
|
100
|
+
readonly channel: InstrumentationChannel;
|
|
101
|
+
readonly modelInput: InstrumentationModelInput;
|
|
102
|
+
readonly session: InstrumentationSession;
|
|
103
|
+
readonly step: InstrumentationStep;
|
|
104
|
+
readonly turn: InstrumentationTurn;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Metadata hooks accepted by {@link defineInstrumentation}.
|
|
108
|
+
*/
|
|
109
|
+
export interface InstrumentationMetadataConfig {
|
|
110
|
+
/**
|
|
111
|
+
* Per-attempt metadata resolved before the model call so child spans
|
|
112
|
+
* created by the AI SDK inherit the returned values.
|
|
113
|
+
*/
|
|
114
|
+
readonly "step.started"?: (input: InstrumentationStepStartedMetadataInput) => InstrumentationMetadata;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Authored instrumentation settings accepted by `defineInstrumentation`.
|
|
118
|
+
*
|
|
119
|
+
* The presence of a `defineInstrumentation` export implicitly enables
|
|
120
|
+
* telemetry — there is no separate `isEnabled` toggle.
|
|
121
|
+
*/
|
|
122
|
+
export interface InstrumentationDefinition {
|
|
123
|
+
/**
|
|
124
|
+
* Override the default function identifier attached to telemetry spans.
|
|
125
|
+
*
|
|
126
|
+
* When omitted, Ash derives the identifier from the agent name.
|
|
127
|
+
*/
|
|
128
|
+
readonly functionId?: string;
|
|
129
|
+
/**
|
|
130
|
+
* Additional metadata merged into AI SDK telemetry spans.
|
|
131
|
+
*/
|
|
132
|
+
readonly metadata?: InstrumentationMetadataConfig;
|
|
133
|
+
/**
|
|
134
|
+
* Whether to record full model inputs in telemetry spans.
|
|
135
|
+
*
|
|
136
|
+
* Defaults to `true` when `instrumentation.ts` is present. Set to `false`
|
|
137
|
+
* to disable recording inputs, which may be useful if inputs contain
|
|
138
|
+
* sensitive content or you want to reduce span payload size.
|
|
139
|
+
*/
|
|
140
|
+
readonly recordInputs?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Whether to record full model outputs in telemetry spans.
|
|
143
|
+
*
|
|
144
|
+
* Defaults to `true` when `instrumentation.ts` is present. Set to `false`
|
|
145
|
+
* to disable recording outputs.
|
|
146
|
+
*/
|
|
147
|
+
readonly recordOutputs?: boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Optional setup callback invoked at server startup with the resolved
|
|
150
|
+
* agent name.
|
|
151
|
+
*
|
|
152
|
+
* Use this to call `registerOTel` or any other OTel provider setup.
|
|
153
|
+
* The `context.agentName` value comes from `defineAgent`, so you never
|
|
154
|
+
* need to hard-code a service name.
|
|
155
|
+
*/
|
|
156
|
+
readonly setup?: (context: InstrumentationSetupContext) => void;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Defines instrumentation settings for the agent application.
|
|
160
|
+
*
|
|
161
|
+
* Export the result as the default export of `agent/instrumentation.ts`.
|
|
162
|
+
* Ash picks up these settings at server startup and applies them to every
|
|
163
|
+
* AI SDK model call.
|
|
164
|
+
*
|
|
165
|
+
* The `setup` callback is invoked later by the framework with the resolved
|
|
166
|
+
* agent name — it is not called during `defineInstrumentation` itself.
|
|
167
|
+
*/
|
|
168
|
+
export declare function defineInstrumentation<T extends InstrumentationDefinition>(definition: ExactDefinition<T, InstrumentationDefinition>): T;
|
|
169
|
+
/**
|
|
170
|
+
* Narrows an instrumentation channel by comparing it to an app-owned channel
|
|
171
|
+
* value imported from `agent/channels/*`.
|
|
172
|
+
*
|
|
173
|
+
* The comparison uses the compiler's path-derived `channel:<slug>` identity.
|
|
174
|
+
* It does not read durable channel state; metadata remains the explicit
|
|
175
|
+
* projection returned by the channel's `metadata(state)` function.
|
|
176
|
+
*/
|
|
177
|
+
export declare function isChannel<TChannel extends Channel<any, any, any>>(channel: InstrumentationChannel, target: TChannel): channel is InstrumentationChannelForChannel<TChannel>;
|
|
178
|
+
export {};
|