agents 0.25.0 → 0.27.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/README.md +8 -9
- package/dist/{agent-routing-0DR0Tk2v.d.ts → agent-routing-Bhdbvf7s.d.ts} +13 -8
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-YrATZJu2.d.ts → agent-tools-bJZwBMfq.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/ai-sdk-OoGkFy3s.js +2444 -0
- package/dist/ai-sdk-OoGkFy3s.js.map +1 -0
- package/dist/apply-chunks-1FheRb9W.js +99 -0
- package/dist/apply-chunks-1FheRb9W.js.map +1 -0
- package/dist/broadcast-state-CEyI4uAK.js +69 -0
- package/dist/broadcast-state-CEyI4uAK.js.map +1 -0
- package/dist/browser/ai-sdk.d.ts +1 -1
- package/dist/browser/ai-sdk.js +2 -2
- package/dist/browser/ai-sdk.js.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/tanstack-ai.d.ts +1 -1
- package/dist/browser/tanstack-ai.js +1 -1
- package/dist/{browser-DE3d-RLk.d.ts → browser-tmkcAAI3.d.ts} +2 -2
- package/dist/{browser-tool-DuNdlwZ9.d.ts → browser-tool-Bn-nzJQI.d.ts} +3 -3
- package/dist/{browser-tool-CQ10e6Kj.js → browser-tool-CBqs6hqa.js} +58 -18
- package/dist/browser-tool-CBqs6hqa.js.map +1 -0
- package/dist/capability-B4WbF81e.js.map +1 -1
- package/dist/{capability-runner-DBAnGr6L.d.ts → capability-runner-O5VbQzqg.d.ts} +9 -1
- package/dist/channel-Bak_6zDP.d.ts +333 -0
- package/dist/chat/index.d.ts +11 -9
- package/dist/chat/index.js +5 -194
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +2 -4
- package/dist/chat/react.js +3 -1
- package/dist/chat/react.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/chat-sdk/index.js +1 -1
- package/dist/cli.js +144 -0
- package/dist/{client-fyQrLGkD.js → client-1tEQ1Oqz.js} +27 -2
- package/dist/{client-fyQrLGkD.js.map → client-1tEQ1Oqz.js.map} +1 -1
- package/dist/{client-je3koD9Z.d.ts → client-DS3Dz9l0.d.ts} +2 -2
- package/dist/client-tools-aIBO0Fk7.d.ts +53 -0
- package/dist/client.d.ts +1 -1
- package/dist/conversations-DH0ut_8n.d.ts +192 -0
- package/dist/{current-agent-vAk_5JMt.d.ts → current-agent-C07C5o5Y.d.ts} +3 -3
- package/dist/daemon-core-Buubj-y1.js +481 -0
- package/dist/daemon-core-Buubj-y1.js.map +1 -0
- package/dist/daemon-core-CbohVPAu.d.ts +132 -0
- package/dist/{do-oauth-client-provider-Tmf1vgKz.d.ts → do-oauth-client-provider-DfsUcpc3.d.ts} +7 -1
- package/dist/{durable-object-lifecycle-HhgOMWq9.d.ts → durable-object-lifecycle-DRqp475_.d.ts} +2 -2
- package/dist/{channels/email.d.ts → experimental/channels/email/index.d.ts} +25 -12
- package/dist/{channels/email.js → experimental/channels/email/index.js} +13 -35
- package/dist/experimental/channels/email/index.js.map +1 -0
- package/dist/experimental/channels/index.d.ts +270 -0
- package/dist/experimental/channels/index.js +1186 -0
- package/dist/experimental/channels/index.js.map +1 -0
- package/dist/{channels/slack.d.ts → experimental/channels/slack/index.d.ts} +19 -9
- package/dist/experimental/channels/slack/index.js +267 -0
- package/dist/experimental/channels/slack/index.js.map +1 -0
- package/dist/{channels/telegram.d.ts → experimental/channels/telegram/index.d.ts} +19 -9
- package/dist/experimental/channels/telegram/index.js +198 -0
- package/dist/experimental/channels/telegram/index.js.map +1 -0
- package/dist/experimental/channels/web/ai-sdk.d.ts +55 -0
- package/dist/experimental/channels/web/ai-sdk.js +266 -0
- package/dist/experimental/channels/web/ai-sdk.js.map +1 -0
- package/dist/experimental/channels/web/client.d.ts +97 -0
- package/dist/experimental/channels/web/client.js +349 -0
- package/dist/experimental/channels/web/client.js.map +1 -0
- package/dist/experimental/channels/web/index.d.ts +189 -0
- package/dist/experimental/channels/web/index.js +332 -0
- package/dist/experimental/channels/web/index.js.map +1 -0
- package/dist/gateway-BDC07gYO.d.ts +100 -0
- package/dist/handle-zAzh0UW2.d.ts +460 -0
- package/dist/harness/ai-sdk/index.d.ts +175 -0
- package/dist/harness/ai-sdk/index.js +605 -0
- package/dist/harness/ai-sdk/index.js.map +1 -0
- package/dist/harness/container/daemon/index.d.ts +76 -0
- package/dist/harness/container/daemon/index.js +3 -0
- package/dist/harness/container/index.d.ts +740 -0
- package/dist/harness/container/index.js +2152 -0
- package/dist/harness/container/index.js.map +1 -0
- package/dist/harness/container/runtime/index.d.ts +144 -0
- package/dist/harness/container/runtime/index.js +1082 -0
- package/dist/harness/container/runtime/index.js.map +1 -0
- package/dist/harness/opencode/index.d.ts +369 -0
- package/dist/harness/opencode/index.js +1072 -0
- package/dist/harness/opencode/index.js.map +1 -0
- package/dist/{harnesses → harness}/pi/index.d.ts +85 -88
- package/dist/{harnesses → harness}/pi/index.js +196 -77
- package/dist/harness/pi/index.js.map +1 -0
- package/dist/harness/store/index.d.ts +24 -0
- package/dist/harness/store/index.js +2 -0
- package/dist/harness/think/index.d.ts +821 -0
- package/dist/harness/think/index.js +2529 -0
- package/dist/harness/think/index.js.map +1 -0
- package/dist/harness-B6B9S3rb.d.ts +203 -0
- package/dist/{images-iiTDSyRu.js → images-Ce9SCu3X.js} +2 -248
- package/dist/images-Ce9SCu3X.js.map +1 -0
- package/dist/{index-BreL0YrQ.d.ts → index-D0A5tbWu.d.ts} +2 -2
- package/dist/index-Ib_4dwOo.d.ts +200 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -2
- package/dist/ingress-EFbrS547.js +9 -0
- package/dist/ingress-EFbrS547.js.map +1 -0
- package/dist/input-schema-DNrpTu49.js +23 -0
- package/dist/input-schema-DNrpTu49.js.map +1 -0
- package/dist/internal-kmw70xwR.js +46 -0
- package/dist/internal-kmw70xwR.js.map +1 -0
- package/dist/internal_context.d.ts +1 -1
- package/dist/lifecycle/index.d.ts +3 -3
- package/dist/mcp/client/do-oauth-client-provider.d.ts +1 -1
- package/dist/mcp/client/do-oauth-client-provider.js +9 -0
- package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
- package/dist/mcp/client/index.d.ts +1 -1
- package/dist/mcp/client/index.js +1 -1
- package/dist/mcp/client/x402.d.ts +35 -4
- package/dist/mcp/client/x402.js +24 -3
- package/dist/mcp/client/x402.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +2 -2
- package/dist/models/ai-sdk/index.d.ts +16 -14
- package/dist/models/ai-sdk/index.js +1 -2442
- package/dist/models/opencode/index.d.ts +65 -0
- package/dist/models/opencode/index.js +285 -0
- package/dist/models/opencode/index.js.map +1 -0
- package/dist/models/pi-ai/index.d.ts +23 -21
- package/dist/models/pi-ai/index.js +7 -6
- package/dist/models/pi-ai/index.js.map +1 -1
- package/dist/options-pwTNq9z6.d.ts +134 -0
- package/dist/protocol-BEgrJYC5.d.ts +331 -0
- package/dist/protocol-CukuI73M.d.ts +386 -0
- package/dist/protocol-cOklVBBD.js +227 -0
- package/dist/protocol-cOklVBBD.js.map +1 -0
- package/dist/{protocol-kHsr-f0X.js → protocol-eXeJxhgu.js} +2 -67
- package/dist/protocol-eXeJxhgu.js.map +1 -0
- package/dist/protocol-y2sSY584.js +148 -0
- package/dist/protocol-y2sSY584.js.map +1 -0
- package/dist/queue/index.d.ts +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/routing/index.d.ts +2 -2
- package/dist/{scheduler-RdACNXDP.d.ts → scheduler-CrFCnv_J.d.ts} +2 -2
- package/dist/schedules/index.d.ts +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +11 -434
- package/dist/sessions/index.js +1 -2271
- package/dist/sessions-B9pNuxdT.js +2272 -0
- package/dist/sessions-B9pNuxdT.js.map +1 -0
- package/dist/settings-C45hDliX.js +253 -0
- package/dist/settings-C45hDliX.js.map +1 -0
- package/dist/{transport-PfFnt-Ld.d.ts → settings-NgGwF7Qt.d.ts} +14 -208
- package/dist/{src-BZeu-SCD.js → src-DgCbZtIR.js} +4 -4
- package/dist/{src-BZeu-SCD.js.map → src-DgCbZtIR.js.map} +1 -1
- package/dist/stall-watchdog-BpRF2uwG.js +195 -0
- package/dist/stall-watchdog-BpRF2uwG.js.map +1 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/store-DLeqKuj3.d.ts +310 -0
- package/dist/store-vgcPYo8K.js +432 -0
- package/dist/store-vgcPYo8K.js.map +1 -0
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.js +1 -1
- package/dist/{streams-DW-UCOUc.js → streams-CITClVer.js} +16 -3
- package/dist/streams-CITClVer.js.map +1 -0
- package/dist/{streams-Cmkj9QfW.d.ts → streams-fbG61qM8.d.ts} +2 -2
- package/dist/sub-routing.d.ts +1 -1
- package/dist/tasks/index.d.ts +1 -1
- package/dist/tasks/index.js +1 -1
- package/dist/{tasks-68DESxf0.js → tasks-CkTZNaV1.js} +2 -1
- package/dist/tasks-CkTZNaV1.js.map +1 -0
- package/dist/{tasks-9YxYtByh.d.ts → tasks-PtlB4Uyn.d.ts} +2 -2
- package/dist/tool-3PA8xjB_.js +421 -0
- package/dist/tool-3PA8xjB_.js.map +1 -0
- package/dist/tool-BPJEbwaX.d.ts +278 -0
- package/dist/transport-k2mFrtL8.d.ts +200 -0
- package/dist/turns-DCV3ycDl.js +317 -0
- package/dist/turns-DCV3ycDl.js.map +1 -0
- package/dist/voice/client.js +1 -1
- package/dist/voice/index.d.ts +2 -2
- package/dist/voice/index.js +1 -1
- package/dist/websearch/index.d.ts +58 -0
- package/dist/websearch/index.js +2 -0
- package/dist/websearch/tools/ai-sdk.d.ts +77 -0
- package/dist/websearch/tools/ai-sdk.js +46 -0
- package/dist/websearch/tools/ai-sdk.js.map +1 -0
- package/dist/websearch/tools/pi.d.ts +82 -0
- package/dist/websearch/tools/pi.js +89 -0
- package/dist/websearch/tools/pi.js.map +1 -0
- package/dist/websearch/tools/tanstack-ai.d.ts +71 -0
- package/dist/websearch/tools/tanstack-ai.js +46 -0
- package/dist/websearch/tools/tanstack-ai.js.map +1 -0
- package/dist/websockets/index.d.ts +12 -296
- package/dist/websockets/index.js +1 -1
- package/dist/{websockets-Ci-LZFHH.js → websockets-CmENGb-Z.js} +18 -4
- package/dist/websockets-CmENGb-Z.js.map +1 -0
- package/dist/{wire-types-CrjKvDyi.d.ts → wire-types-Cv0iwjk5.d.ts} +3 -51
- package/dist/workflow-types.d.ts +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js +1 -1
- package/docs/browse-the-web.md +3 -0
- package/docs/harnesses/pi.md +114 -32
- package/docs/harnesses/think.md +215 -0
- package/docs/index.md +4 -3
- package/docs/lifecycle.md +40 -0
- package/docs/models-pi-ai.md +5 -5
- package/docs/search-the-web.md +133 -0
- package/package.json +126 -47
- package/dist/browser-tool-CQ10e6Kj.js.map +0 -1
- package/dist/channel-Bnm4S7T2.d.ts +0 -491
- package/dist/channels/ai-sdk.d.ts +0 -46
- package/dist/channels/ai-sdk.js +0 -120
- package/dist/channels/ai-sdk.js.map +0 -1
- package/dist/channels/email.js.map +0 -1
- package/dist/channels/index.d.ts +0 -233
- package/dist/channels/index.js +0 -608
- package/dist/channels/index.js.map +0 -1
- package/dist/channels/slack.js +0 -614
- package/dist/channels/slack.js.map +0 -1
- package/dist/channels/tanstack-ai.d.ts +0 -39
- package/dist/channels/tanstack-ai.js +0 -17
- package/dist/channels/tanstack-ai.js.map +0 -1
- package/dist/channels/telegram.js +0 -427
- package/dist/channels/telegram.js.map +0 -1
- package/dist/channels/voice.d.ts +0 -45
- package/dist/channels/voice.js +0 -122
- package/dist/channels/voice.js.map +0 -1
- package/dist/harnesses/pi/index.js.map +0 -1
- package/dist/images-iiTDSyRu.js.map +0 -1
- package/dist/index-BB0kqhIz.d.ts +0 -101
- package/dist/ingress-BfetZbMO.js +0 -83
- package/dist/ingress-BfetZbMO.js.map +0 -1
- package/dist/internal-CYlgHl1l.js +0 -59
- package/dist/internal-CYlgHl1l.js.map +0 -1
- package/dist/models/ai-sdk/index.js.map +0 -1
- package/dist/protocol-kHsr-f0X.js.map +0 -1
- package/dist/sessions/index.js.map +0 -1
- package/dist/streams-DW-UCOUc.js.map +0 -1
- package/dist/surface-bZZJqBka.js +0 -17
- package/dist/surface-bZZJqBka.js.map +0 -1
- package/dist/tasks-68DESxf0.js.map +0 -1
- package/dist/tool-schema-CBjGPrsQ.js +0 -31
- package/dist/tool-schema-CBjGPrsQ.js.map +0 -1
- package/dist/websockets-Ci-LZFHH.js.map +0 -1
- package/docs/channels.md +0 -323
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"telegram.js","names":[],"sources":["../../src/channels/adapters/telegram.ts"],"sourcesContent":["import type {\n Channel,\n ChannelApprovalRequest,\n ChannelChunk,\n ChannelMessage,\n ChannelRoute,\n ChannelStreamOptions,\n DeliveryResult\n} from \"../channel\";\nimport { consumeChunks, createPacer } from \"../stream\";\nimport type { ChannelIdentity } from \"../identity\";\nimport {\n isChannelMessageSurface,\n type ChannelMessageSurface\n} from \"../surface\";\nimport {\n matchesPath,\n type ChannelApprovalResponseInput,\n type ChannelEventContextInput,\n type ChannelInboundMessageInput,\n type ChannelIngress,\n type ChannelIngressEventInput\n} from \"../ingress\";\nimport {\n defaultText,\n emptyIngressResponse,\n encodeUtf8,\n isRecord,\n renderInput,\n uncertain\n} from \"../internal\";\n\nexport type TelegramUser = {\n id: number;\n is_bot: boolean;\n first_name: string;\n last_name?: string;\n username?: string;\n [key: string]: unknown;\n};\n\nexport type TelegramChat = {\n id: number;\n type: \"private\" | \"group\" | \"supergroup\" | \"channel\";\n title?: string;\n username?: string;\n first_name?: string;\n last_name?: string;\n [key: string]: unknown;\n};\n\nexport type TelegramMessage = {\n message_id: number;\n date: number;\n chat: TelegramChat;\n message_thread_id?: number;\n from?: TelegramUser;\n sender_chat?: TelegramChat;\n text?: string;\n edit_date?: number;\n reply_to_message?: TelegramMessage;\n [key: string]: unknown;\n};\n\n/** Authenticated JSON body delivered by a Telegram webhook. */\nexport type TelegramUpdate = {\n update_id: number;\n message?: TelegramMessage;\n edited_message?: TelegramMessage;\n [key: string]: unknown;\n};\n\nexport type TelegramMessageSurface = ChannelMessageSurface<\n string,\n {\n chatId: string;\n botUserId?: number;\n messageThreadId?: number;\n replyToMessageId?: number;\n }\n>;\n\n/** Configuration for a Telegram Channel. */\nexport type TelegramChannelOptions = {\n /** Bot token issued by BotFather. */\n botToken: string;\n /** Telegram Bot API origin. @default \"https://api.telegram.org\" */\n apiBaseUrl?: string;\n /** Maximum text length accepted by this route. @default 4096 */\n maxLength?: number;\n /** Project the canonical message into Telegram text. */\n toText?: (message: ChannelMessage) => string;\n /** Parse mode for caller-formatted delivery text. */\n parseMode?: \"HTML\" | \"MarkdownV2\";\n /**\n * Smallest gap between `sendMessageDraft` previews. Snapshots produced\n * inside one interval are replaced rather than sent. @default 500\n */\n streamIntervalMs?: number;\n /** Select an application route from the event, raw update, and Host context. */\n route?: ChannelRoute<TelegramUpdate>;\n /** Add secret-verified Telegram webhook ingress to the returned Channel. */\n webhook?: {\n secretToken: string;\n /** Exact webhook pathname accepted by this ingress. @default \"/webhooks/telegram\" */\n path?: string;\n };\n /** Override fetch for testing or custom network routing. */\n fetch?: typeof globalThis.fetch;\n};\n\ntype TelegramApiResponse = {\n ok?: unknown;\n result?: unknown;\n error_code?: unknown;\n description?: unknown;\n};\n\nconst TELEGRAM_MESSAGE_LIMIT = 4096;\nconst DEFAULT_STREAM_INTERVAL_MS = 500;\nconst DEFAULT_TELEGRAM_WEBHOOK_PATH = \"/webhooks/telegram\";\nconst APPROVAL_INSTRUCTIONS = \"Reply YES to approve or NO to reject.\";\nconst INTERACTION_FOOTER =\n /\\n\\nReply YES to approve or NO to reject\\.\\n\\n\\[channel-interaction:v1:([A-Za-z0-9_-]+)\\]$/;\n\nfunction channelIdentity(chatId: number): string {\n return `telegram:chat:${chatId}`;\n}\n\nfunction userIdentity(userId: number): string {\n return `telegram:user:${userId}`;\n}\n\nfunction messageIdentity(chatId: number, messageId: number): string {\n return `${channelIdentity(chatId)}:message:${messageId}`;\n}\n\nfunction updateIdentity(chatId: number, updateId: number): string {\n return `${channelIdentity(chatId)}:update:${updateId}`;\n}\n\nfunction encodeInteractionId(interactionId: string): string {\n const bytes = encodeUtf8(interactionId);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction decodeInteractionId(value: string): string | undefined {\n try {\n const base64 = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padding = \"=\".repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(base64 + padding);\n const bytes = Uint8Array.from(binary, (character) =>\n character.charCodeAt(0)\n );\n const interactionId = new TextDecoder(\"utf-8\", { fatal: true }).decode(\n bytes\n );\n return interactionId || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction approvalText(\n interactionId: string,\n request: ChannelApprovalRequest\n): string {\n return [\n ...(request.title ? [request.title] : []),\n request.summary,\n `Input:\\n${renderInput(request.input)}`,\n APPROVAL_INSTRUCTIONS,\n `[channel-interaction:v1:${encodeInteractionId(interactionId)}]`\n ].join(\"\\n\\n\");\n}\n\nfunction asApiResponse(value: unknown): TelegramApiResponse | undefined {\n return value !== null && typeof value === \"object\"\n ? (value as TelegramApiResponse)\n : undefined;\n}\n\nfunction classifyResponse(\n response: Response,\n payload: TelegramApiResponse\n): DeliveryResult {\n if (payload.ok === true) {\n const result = asApiResponse(payload.result) as\n | { message_id?: unknown }\n | undefined;\n if (typeof result?.message_id === \"number\") {\n return { status: \"delivered\", reference: String(result.message_id) };\n }\n return uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram returned an invalid delivery response\"\n );\n }\n\n if (payload.ok === false) {\n const errorCode =\n typeof payload.error_code === \"number\"\n ? payload.error_code\n : response.status;\n const message =\n typeof payload.description === \"string\"\n ? payload.description\n : \"Telegram rejected the message\";\n const error = { code: `TELEGRAM_API_ERROR_${errorCode}`, message };\n if (errorCode >= 500) return { status: \"uncertain\", error };\n return {\n status: \"failed\",\n retryable: errorCode === 429,\n error\n };\n }\n\n return uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram returned an invalid delivery response\"\n );\n}\n\nexport type TelegramWebhookOptions = {\n /** Telegram webhook secret configured through setWebhook. */\n secretToken: string;\n /** Exact webhook pathname accepted by this ingress. @default \"/webhooks/telegram\" */\n path?: string;\n /**\n * Numeric user id of this bot, used to prove that an approval reply answers\n * a message this bot sent. Without it, marker replies stay ordinary\n * messages because their provenance cannot be established.\n */\n botUserId?: number;\n};\n\nfunction asTelegramMessage(value: unknown): TelegramMessage | undefined {\n return value !== null && typeof value === \"object\"\n ? (value as TelegramMessage)\n : undefined;\n}\n\nfunction telegramActor(message: TelegramMessage) {\n const sender = message.from;\n if (sender) {\n const fullName = [sender.first_name, sender.last_name]\n .filter(Boolean)\n .join(\" \");\n return {\n id: userIdentity(sender.id),\n identity: {\n subject: `user:${sender.id}`\n },\n ...(sender.username && { username: sender.username }),\n ...(fullName && { fullName }),\n isBot: sender.is_bot\n };\n }\n\n const senderChat = message.sender_chat;\n if (!senderChat) return undefined;\n const fullName =\n senderChat.title ??\n [senderChat.first_name, senderChat.last_name].filter(Boolean).join(\" \");\n return {\n id: channelIdentity(senderChat.id),\n identity: {\n subject: `chat:${senderChat.id}`\n },\n ...(senderChat.username && { username: senderChat.username }),\n ...(fullName && { fullName }),\n isBot: \"unknown\" as const\n };\n}\n\nfunction chatLabel(chat: TelegramChat): string {\n const name =\n chat.title ??\n (chat.username ? `@${chat.username}` : undefined) ??\n [chat.first_name, chat.last_name].filter(Boolean).join(\" \") ??\n String(chat.id);\n return `Telegram · ${name || chat.id}`;\n}\n\nfunction eventContext(\n message: TelegramMessage,\n eventId: string,\n botUserId: number | undefined\n): ChannelEventContextInput {\n const actor = telegramActor(message);\n const channelId = channelIdentity(message.chat.id);\n const threadId =\n typeof message.message_thread_id === \"number\"\n ? `${channelId}:topic:${message.message_thread_id}`\n : channelId;\n const isDirectMessage =\n message.chat.type === \"private\"\n ? true\n : message.chat.type === \"group\" ||\n message.chat.type === \"supergroup\" ||\n message.chat.type === \"channel\"\n ? false\n : (\"unknown\" as const);\n return {\n eventId,\n thread: { id: threadId, isDirectMessage },\n replySurface: {\n version: 1,\n address: {\n chatId: String(message.chat.id),\n ...(botUserId && { botUserId }),\n ...(typeof message.message_thread_id === \"number\" && {\n messageThreadId: message.message_thread_id\n }),\n replyToMessageId: message.message_id\n },\n label: chatLabel(message.chat)\n },\n ...(actor && { actor })\n };\n}\n\nfunction telegramTimestamp(value: unknown): string | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return undefined;\n const date = new Date(value * 1000);\n return Number.isFinite(date.getTime()) ? date.toISOString() : undefined;\n}\n\nfunction normalizedMessage(\n message: TelegramMessage,\n eventId: string,\n edited: boolean,\n botUserId: number | undefined\n): ChannelInboundMessageInput {\n const reply = asTelegramMessage(message.reply_to_message);\n const sentAt = telegramTimestamp(message.date);\n const editedAt = telegramTimestamp(message.edit_date);\n\n return {\n type: \"message\",\n ...eventContext(message, eventId, botUserId),\n message: {\n id: messageIdentity(message.chat.id, message.message_id),\n text: message.text as string,\n ...(typeof reply?.message_id === \"number\" && {\n reply: {\n id: messageIdentity(message.chat.id, reply.message_id),\n ...(typeof reply.text === \"string\" && { text: reply.text })\n }\n }),\n ...((sentAt || edited || editedAt) && {\n metadata: {\n ...(sentAt && { sentAt }),\n ...(edited && { edited: true }),\n ...(editedAt && { editedAt })\n }\n })\n }\n };\n}\n\nfunction approvalResponse(\n message: TelegramMessage,\n eventId: string,\n botUserId: number | undefined\n): ChannelApprovalResponseInput | undefined {\n if (botUserId === undefined) return undefined;\n if (message.text !== \"YES\" && message.text !== \"NO\") return undefined;\n\n const reply = asTelegramMessage(message.reply_to_message);\n if (typeof reply?.message_id !== \"number\") return undefined;\n\n // An interaction marker only authorizes a decision when this bot authored\n // the replied-to message; otherwise any chat member could forge one.\n const author = reply.from;\n if (author?.is_bot !== true || author.id !== botUserId) return undefined;\n\n const encodedInteractionId =\n typeof reply.text === \"string\"\n ? reply.text.match(INTERACTION_FOOTER)?.[1]\n : undefined;\n const interactionId = encodedInteractionId\n ? decodeInteractionId(encodedInteractionId)\n : undefined;\n if (!interactionId) return undefined;\n\n return {\n type: \"approval-response\",\n ...eventContext(message, eventId, botUserId),\n interactionId,\n decision: message.text === \"YES\" ? \"approve\" : \"reject\",\n reference: messageIdentity(message.chat.id, message.message_id)\n };\n}\n\nfunction inboundEvent(\n update: TelegramUpdate,\n botUserId: number | undefined\n): ChannelIngressEventInput | undefined {\n const edited = update.edited_message !== undefined;\n const message = asTelegramMessage(\n edited ? update.edited_message : update.message\n );\n if (\n typeof update.update_id !== \"number\" ||\n typeof message?.message_id !== \"number\" ||\n typeof message.text !== \"string\" ||\n typeof message.chat?.id !== \"number\"\n ) {\n return undefined;\n }\n\n const eventId = updateIdentity(message.chat.id, update.update_id);\n return (\n approvalResponse(message, eventId, botUserId) ??\n normalizedMessage(message, eventId, edited, botUserId)\n );\n}\n\n/** Recover this bot's numeric user id from its BotFather token prefix. */\nfunction telegramBotUserId(botToken: string): number | undefined {\n const prefix = botToken.match(/^(\\d+):/)?.[1];\n if (!prefix) return undefined;\n const botUserId = Number(prefix);\n return Number.isSafeInteger(botUserId) && botUserId > 0\n ? botUserId\n : undefined;\n}\n\n/** Create dependency-free Telegram webhook ingress for a destination chat. */\nexport function telegramWebhook(\n options: TelegramWebhookOptions\n): ChannelIngress<TelegramUpdate> {\n if (!options.secretToken.trim()) {\n throw new Error(\n \"secretToken is required to create Telegram webhook ingress\"\n );\n }\n if (\n options.botUserId !== undefined &&\n (!Number.isSafeInteger(options.botUserId) || options.botUserId <= 0)\n ) {\n throw new Error(\"botUserId must be a positive Telegram user id\");\n }\n\n const botUserId = options.botUserId;\n const path = options.path ?? DEFAULT_TELEGRAM_WEBHOOK_PATH;\n return {\n async receive(request) {\n if (!matchesPath(request, path)) return null;\n if (request.method !== \"POST\") return emptyIngressResponse(405);\n if (\n request.headers.get(\"x-telegram-bot-api-secret-token\") !==\n options.secretToken\n ) {\n return emptyIngressResponse(401);\n }\n\n let update: unknown;\n try {\n update = await request.json();\n } catch {\n return emptyIngressResponse(400);\n }\n if (update === null || typeof update !== \"object\") {\n return emptyIngressResponse(400);\n }\n\n const raw = update as TelegramUpdate;\n const event = inboundEvent(raw, botUserId);\n return {\n events: event ? [{ event, raw }] : [],\n response: new Response(null, { status: 200 })\n };\n }\n };\n}\n\nfunction telegramSurface(\n surface: ChannelMessageSurface\n): TelegramMessageSurface | undefined {\n if (\n !isChannelMessageSurface(surface) ||\n surface.version !== 1 ||\n !isRecord(surface.address) ||\n typeof surface.address.chatId !== \"string\" ||\n surface.address.chatId.length === 0 ||\n (surface.address.botUserId !== undefined &&\n (!Number.isSafeInteger(surface.address.botUserId) ||\n Number(surface.address.botUserId) <= 0))\n ) {\n return undefined;\n }\n if (\n (surface.address.messageThreadId !== undefined &&\n !Number.isSafeInteger(surface.address.messageThreadId)) ||\n (surface.address.replyToMessageId !== undefined &&\n !Number.isSafeInteger(surface.address.replyToMessageId))\n ) {\n return undefined;\n }\n return surface as TelegramMessageSurface;\n}\n\n/**\n * Whether a chat can show an animated draft.\n *\n * `sendMessageDraft` is private chats only, and Telegram gives private chats\n * positive ids. Anywhere else the answer is simply sent once at the end.\n */\nfunction supportsDraft(chatId: string): boolean {\n const numeric = Number(chatId);\n return Number.isSafeInteger(numeric) && numeric > 0;\n}\n\n/** A non-zero draft identifier; reusing one animates between snapshots. */\nfunction newDraftId(): number {\n const [random] = crypto.getRandomValues(new Uint32Array(1));\n return ((random ?? 1) % 2_147_483_647) + 1;\n}\n\nfunction splitText(text: string, limit: number): string[] {\n const pieces: string[] = [];\n let piece = \"\";\n let length = 0;\n for (const character of text) {\n if (length === limit) {\n pieces.push(piece);\n piece = \"\";\n length = 0;\n }\n piece += character;\n length += 1;\n }\n if (piece.length > 0) pieces.push(piece);\n return pieces;\n}\n\nfunction textLength(text: string): number {\n return [...text].length;\n}\n\n/** Create a configured Telegram Bot API Channel. */\nexport function telegram(\n options: TelegramChannelOptions\n): Channel<TelegramUpdate> {\n if (!options.botToken.trim()) {\n throw new Error(\"botToken is required to create a Telegram channel\");\n }\n const fetch = options.fetch ?? globalThis.fetch;\n const apiBaseUrl = (options.apiBaseUrl ?? \"https://api.telegram.org\").replace(\n /\\/$/,\n \"\"\n );\n const maxLength = options.maxLength ?? TELEGRAM_MESSAGE_LIMIT;\n if (\n !Number.isInteger(maxLength) ||\n maxLength < 1 ||\n maxLength > TELEGRAM_MESSAGE_LIMIT\n ) {\n throw new Error(\n `maxLength must be an integer between 1 and ${TELEGRAM_MESSAGE_LIMIT}`\n );\n }\n const toText = options.toText ?? defaultText;\n const streamIntervalMs =\n options.streamIntervalMs ?? DEFAULT_STREAM_INTERVAL_MS;\n if (!Number.isInteger(streamIntervalMs) || streamIntervalMs < 0) {\n throw new Error(\"streamIntervalMs must be a non-negative integer\");\n }\n const botUserId = telegramBotUserId(options.botToken);\n const ingress = options.webhook\n ? telegramWebhook({ ...options.webhook, botUserId })\n : undefined;\n\n async function send(\n destinationValue: ChannelMessageSurface,\n text: string,\n parseMode?: TelegramChannelOptions[\"parseMode\"]\n ): Promise<DeliveryResult> {\n const destination = telegramSurface(destinationValue);\n if (\n !destination ||\n (botUserId !== undefined &&\n destination.address.botUserId !== undefined &&\n destination.address.botUserId !== botUserId)\n ) {\n return {\n status: \"failed\",\n retryable: false,\n error: {\n code: \"TELEGRAM_SURFACE_INVALID\",\n message: `Telegram cannot parse the address for Channel \"${destinationValue.channelKey}\"`\n }\n };\n }\n if (textLength(text) > maxLength) {\n return {\n status: \"failed\",\n retryable: false,\n error: {\n code: \"TELEGRAM_MESSAGE_TOO_LONG\",\n message: `Telegram message exceeds the configured ${maxLength}-character limit`\n }\n };\n }\n\n let response: Response;\n try {\n response = await fetch(\n `${apiBaseUrl}/bot${options.botToken}/sendMessage`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n chat_id: destination.address.chatId,\n text,\n ...(destination.address.messageThreadId !== undefined && {\n message_thread_id: destination.address.messageThreadId\n }),\n ...(destination.address.replyToMessageId !== undefined && {\n reply_parameters: {\n message_id: destination.address.replyToMessageId\n }\n }),\n ...(parseMode && { parse_mode: parseMode })\n })\n }\n );\n } catch {\n return uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram delivery failed with an unknown outcome\"\n );\n }\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n return uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram returned an invalid delivery response\"\n );\n }\n\n const apiResponse = asApiResponse(payload);\n return apiResponse\n ? classifyResponse(response, apiResponse)\n : uncertain(\n \"TELEGRAM_DELIVERY_ERROR\",\n \"Telegram returned an invalid delivery response\"\n );\n }\n\n /**\n * Show one ephemeral preview. Failures are swallowed on purpose: a draft is\n * a 30-second animation, and losing one must never cost the real message.\n *\n * The configured parse mode is deliberately not applied. A partial answer\n * routinely holds unbalanced markup, which Telegram rejects outright.\n */\n async function sendDraft(\n destination: TelegramMessageSurface,\n draftId: number,\n text: string\n ): Promise<boolean> {\n try {\n const response = await fetch(\n `${apiBaseUrl}/bot${options.botToken}/sendMessageDraft`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n chat_id: Number(destination.address.chatId),\n draft_id: draftId,\n text,\n ...(destination.address.messageThreadId !== undefined && {\n message_thread_id: destination.address.messageThreadId\n })\n })\n }\n );\n return asApiResponse(await response.json())?.ok === true;\n } catch {\n return false;\n }\n }\n\n /** Persist the answer, splitting it when it outgrows one Telegram message. */\n async function sendComplete(\n destination: ChannelMessageSurface,\n text: string\n ): Promise<DeliveryResult> {\n const [head, ...tail] = splitText(text, maxLength);\n // Raw HTML and Markdown cannot be partitioned safely without parsing it.\n // Preserve all content literally rather than send malformed fragments.\n const parseMode = tail.length === 0 ? options.parseMode : undefined;\n const first = await send(destination, head!, parseMode);\n if (first.status !== \"delivered\") return first;\n\n for (const piece of tail) {\n const result = await send(destination, piece, parseMode);\n // A later piece failing still leaves earlier ones in the chat.\n if (result.status !== \"delivered\") {\n return uncertain(\n \"TELEGRAM_STREAM_PARTIAL\",\n \"Telegram accepted only part of a split answer\",\n first.reference\n );\n }\n }\n return first;\n }\n\n async function streamMessage(\n destinationValue: ChannelMessageSurface,\n chunks: ReadableStream<ChannelChunk>,\n streamOptions: ChannelStreamOptions\n ): Promise<DeliveryResult> {\n const destination = telegramSurface(destinationValue);\n if (\n !destination ||\n (botUserId !== undefined &&\n destination.address.botUserId !== undefined &&\n destination.address.botUserId !== botUserId)\n ) {\n await chunks.cancel().catch(() => {});\n return {\n status: \"failed\",\n retryable: false,\n error: {\n code: \"TELEGRAM_SURFACE_INVALID\",\n message: `Telegram cannot parse the address for Channel \"${destinationValue.channelKey}\"`\n }\n };\n }\n\n const draftId = supportsDraft(destination.address.chatId)\n ? newDraftId()\n : undefined;\n const prefix = streamOptions.title ? `${streamOptions.title}\\n\\n` : \"\";\n const shouldPreview = createPacer(streamIntervalMs);\n let answer = \"\";\n let draftsStopped = draftId === undefined;\n\n return consumeChunks(chunks, {\n async onChunk(chunk) {\n if (chunk.type !== \"text\" || chunk.text.length === 0) return;\n answer += chunk.text;\n if (draftsStopped || !shouldPreview()) return;\n const preview = splitText(`${prefix}${answer}`, maxLength)[0] ?? \"\";\n const shown = await sendDraft(destination, draftId!, preview);\n if (!shown) draftsStopped = true;\n },\n async onFinish(outcome) {\n // The draft is not the message. Without this send the reader's screen\n // goes blank in thirty seconds and nothing is kept.\n if (answer.length === 0) {\n return {\n status: \"failed\",\n retryable: false,\n error: outcome.interrupted\n ? {\n code: \"TELEGRAM_STREAM_INTERRUPTED\",\n message: \"The answer ended before producing any text to send\"\n }\n : {\n code: \"TELEGRAM_STREAM_EMPTY\",\n message: \"The stream carried no text to send\"\n }\n };\n }\n\n const result = await sendComplete(destination, `${prefix}${answer}`);\n if (!outcome.interrupted || result.status !== \"delivered\") {\n return result;\n }\n return uncertain(\n \"TELEGRAM_STREAM_INTERRUPTED\",\n \"An incomplete answer was sent because the stream ended early\",\n result.reference\n );\n }\n });\n }\n\n return {\n ...(options.route && { route: options.route }),\n ...(ingress && { ingress }),\n contactSurface(identity: ChannelIdentity) {\n if ((identity.scope ?? \"default\") !== \"default\") return null;\n const match = identity.subject.match(/^(user|chat):(-?\\d+)$/);\n if (!match) return null;\n return {\n version: 1,\n address: {\n chatId: match[2],\n ...(botUserId && { botUserId })\n },\n label: `Telegram · ${match[1]} ${match[2]}`\n };\n },\n deliver(destination, message) {\n return send(destination, toText(message), options.parseMode);\n },\n stream(destination, chunks, streamOptions) {\n return streamMessage(destination, chunks, streamOptions);\n },\n requestApproval(destination, { interactionId, request }) {\n if (interactionId.length === 0) {\n return Promise.resolve({\n status: \"failed\",\n retryable: false,\n error: {\n code: \"TELEGRAM_INTERACTION_ID_REQUIRED\",\n message:\n \"Telegram approval requests require a non-empty interaction id\"\n }\n });\n }\n return send(destination, approvalText(interactionId, request));\n }\n };\n}\n"],"mappings":"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"}
|
package/dist/channels/voice.d.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
F as ChannelMessageSurface,
|
|
3
|
-
n as Channel,
|
|
4
|
-
u as ChannelMessage
|
|
5
|
-
} from "../channel-Bnm4S7T2.js";
|
|
6
|
-
import { c as VoiceAudioFormat, n as TTSProvider } from "../types-_Faxb570.js";
|
|
7
|
-
|
|
8
|
-
//#region src/channels/adapters/voice.d.ts
|
|
9
|
-
type BrowserVoiceSurface = ChannelMessageSurface<
|
|
10
|
-
string,
|
|
11
|
-
{
|
|
12
|
-
connection: "current";
|
|
13
|
-
}
|
|
14
|
-
>;
|
|
15
|
-
/** The subset of an Agent WebSocket connection needed for audio delivery. */
|
|
16
|
-
type BrowserVoiceConnection = {
|
|
17
|
-
id: string;
|
|
18
|
-
send(message: string | ArrayBuffer | ArrayBufferView): void;
|
|
19
|
-
};
|
|
20
|
-
/** Configuration for an output-only browser voice channel. */
|
|
21
|
-
type BrowserVoiceChannelOptions = {
|
|
22
|
-
/** Text-to-speech provider used to synthesize each outbound message. */ tts: TTSProvider /** Return the browser voice surface to use, or undefined when unavailable. */;
|
|
23
|
-
getConnection: () =>
|
|
24
|
-
| BrowserVoiceConnection
|
|
25
|
-
| undefined /** Encoded audio format returned by the TTS provider. @default "mp3" */;
|
|
26
|
-
audioFormat?: VoiceAudioFormat /** Sample rate for raw PCM audio. Ignored by encoded formats. @default 16000 */;
|
|
27
|
-
sampleRate?: number /** Project the canonical Markdown message into text suitable for speech. */;
|
|
28
|
-
toSpeechText?: (message: ChannelMessage) => string;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Create an output-only channel that synthesizes one message and sends its
|
|
32
|
-
* encoded audio to one browser using the Cloudflare Voice wire protocol.
|
|
33
|
-
*
|
|
34
|
-
* The browser should connect with `VoiceClient` or `useVoiceAgent()`. No
|
|
35
|
-
* `withVoice()` mixin, microphone, or speech-to-text provider is required.
|
|
36
|
-
*/
|
|
37
|
-
declare function browserVoice(options: BrowserVoiceChannelOptions): Channel;
|
|
38
|
-
//#endregion
|
|
39
|
-
export {
|
|
40
|
-
BrowserVoiceChannelOptions,
|
|
41
|
-
BrowserVoiceConnection,
|
|
42
|
-
BrowserVoiceSurface,
|
|
43
|
-
browserVoice
|
|
44
|
-
};
|
|
45
|
-
//# sourceMappingURL=voice.d.ts.map
|
package/dist/channels/voice.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { t as isChannelMessageSurface } from "../surface-bZZJqBka.js";
|
|
2
|
-
import "../voice/types.js";
|
|
3
|
-
//#region src/channels/adapters/voice.ts
|
|
4
|
-
const DEFAULT_SAMPLE_RATE = 16e3;
|
|
5
|
-
function validSurface(surface) {
|
|
6
|
-
if (!isChannelMessageSurface(surface) || surface.version !== 1 || surface.address === null || typeof surface.address !== "object" || Array.isArray(surface.address)) return false;
|
|
7
|
-
return surface.address.connection === "current";
|
|
8
|
-
}
|
|
9
|
-
function unavailable() {
|
|
10
|
-
return {
|
|
11
|
-
status: "failed",
|
|
12
|
-
retryable: true,
|
|
13
|
-
error: {
|
|
14
|
-
code: "BROWSER_VOICE_UNAVAILABLE",
|
|
15
|
-
message: "No browser voice surface is connected"
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
function synthesisFailure(error) {
|
|
20
|
-
return {
|
|
21
|
-
status: "failed",
|
|
22
|
-
retryable: true,
|
|
23
|
-
error: {
|
|
24
|
-
code: "BROWSER_VOICE_TTS_FAILED",
|
|
25
|
-
message: error instanceof Error ? error.message : "The text-to-speech provider did not return audio"
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
function deliveryFailure(error, contentDeliveryStarted) {
|
|
30
|
-
if (!contentDeliveryStarted) {
|
|
31
|
-
const connectionClosed = error instanceof TypeError && error.message.includes("WebSocket send() after close");
|
|
32
|
-
return {
|
|
33
|
-
status: "failed",
|
|
34
|
-
retryable: true,
|
|
35
|
-
error: {
|
|
36
|
-
code: connectionClosed ? "BROWSER_VOICE_CONNECTION_CLOSED" : "BROWSER_VOICE_DELIVERY_FAILED",
|
|
37
|
-
message: connectionClosed ? "The browser voice surface disconnected before delivery" : error instanceof Error ? error.message : "Browser voice delivery failed before sending content"
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
status: "uncertain",
|
|
43
|
-
error: {
|
|
44
|
-
code: "BROWSER_VOICE_DELIVERY_ERROR",
|
|
45
|
-
message: error instanceof Error ? error.message : "Browser voice delivery failed with an unknown outcome"
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function sendJSON(connection, message) {
|
|
50
|
-
connection.send(JSON.stringify(message));
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Create an output-only channel that synthesizes one message and sends its
|
|
54
|
-
* encoded audio to one browser using the Cloudflare Voice wire protocol.
|
|
55
|
-
*
|
|
56
|
-
* The browser should connect with `VoiceClient` or `useVoiceAgent()`. No
|
|
57
|
-
* `withVoice()` mixin, microphone, or speech-to-text provider is required.
|
|
58
|
-
*/
|
|
59
|
-
function browserVoice(options) {
|
|
60
|
-
const audioFormat = options.audioFormat ?? "mp3";
|
|
61
|
-
const sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;
|
|
62
|
-
const toSpeechText = options.toSpeechText ?? ((message) => message.markdown);
|
|
63
|
-
return {
|
|
64
|
-
isAvailable() {
|
|
65
|
-
return options.getConnection() !== void 0;
|
|
66
|
-
},
|
|
67
|
-
async deliver(surface, message) {
|
|
68
|
-
if (!validSurface(surface)) return {
|
|
69
|
-
status: "failed",
|
|
70
|
-
retryable: false,
|
|
71
|
-
error: {
|
|
72
|
-
code: "BROWSER_VOICE_SURFACE_INVALID",
|
|
73
|
-
message: `Browser voice cannot parse the address for Channel "${surface.channelKey}"`
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
const connection = options.getConnection();
|
|
77
|
-
if (!connection) return unavailable();
|
|
78
|
-
let audio;
|
|
79
|
-
const speechText = toSpeechText(message);
|
|
80
|
-
try {
|
|
81
|
-
audio = await options.tts.synthesize(speechText);
|
|
82
|
-
} catch (error) {
|
|
83
|
-
return synthesisFailure(error);
|
|
84
|
-
}
|
|
85
|
-
if (!audio) return synthesisFailure();
|
|
86
|
-
let contentDeliveryStarted = false;
|
|
87
|
-
try {
|
|
88
|
-
sendJSON(connection, {
|
|
89
|
-
type: "welcome",
|
|
90
|
-
protocol_version: 1
|
|
91
|
-
});
|
|
92
|
-
sendJSON(connection, {
|
|
93
|
-
type: "audio_config",
|
|
94
|
-
format: audioFormat,
|
|
95
|
-
sampleRate
|
|
96
|
-
});
|
|
97
|
-
sendJSON(connection, {
|
|
98
|
-
type: "status",
|
|
99
|
-
status: "speaking"
|
|
100
|
-
});
|
|
101
|
-
sendJSON(connection, {
|
|
102
|
-
type: "transcript",
|
|
103
|
-
role: "assistant",
|
|
104
|
-
text: speechText
|
|
105
|
-
});
|
|
106
|
-
contentDeliveryStarted = true;
|
|
107
|
-
connection.send(audio);
|
|
108
|
-
sendJSON(connection, {
|
|
109
|
-
type: "status",
|
|
110
|
-
status: "idle"
|
|
111
|
-
});
|
|
112
|
-
return { status: "delivered" };
|
|
113
|
-
} catch (error) {
|
|
114
|
-
return deliveryFailure(error, contentDeliveryStarted);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
//#endregion
|
|
120
|
-
export { browserVoice };
|
|
121
|
-
|
|
122
|
-
//# sourceMappingURL=voice.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"voice.js","names":[],"sources":["../../src/channels/adapters/voice.ts"],"sourcesContent":["import {\n VOICE_PROTOCOL_VERSION,\n type TTSProvider,\n type VoiceAudioFormat,\n type VoiceServerMessage\n} from \"../../voice/types\";\nimport type {\n Channel,\n ChannelMessage,\n DeliveryFailure,\n DeliveryResult\n} from \"../channel\";\nimport {\n isChannelMessageSurface,\n type ChannelMessageSurface\n} from \"../surface\";\n\nexport type BrowserVoiceSurface = ChannelMessageSurface<\n string,\n { connection: \"current\" }\n>;\n\n/** The subset of an Agent WebSocket connection needed for audio delivery. */\nexport type BrowserVoiceConnection = {\n id: string;\n send(message: string | ArrayBuffer | ArrayBufferView): void;\n};\n\n/** Configuration for an output-only browser voice channel. */\nexport type BrowserVoiceChannelOptions = {\n /** Text-to-speech provider used to synthesize each outbound message. */\n tts: TTSProvider;\n /** Return the browser voice surface to use, or undefined when unavailable. */\n getConnection: () => BrowserVoiceConnection | undefined;\n /** Encoded audio format returned by the TTS provider. @default \"mp3\" */\n audioFormat?: VoiceAudioFormat;\n /** Sample rate for raw PCM audio. Ignored by encoded formats. @default 16000 */\n sampleRate?: number;\n /** Project the canonical Markdown message into text suitable for speech. */\n toSpeechText?: (message: ChannelMessage) => string;\n};\n\nconst DEFAULT_SAMPLE_RATE = 16_000;\n\nfunction validSurface(surface: ChannelMessageSurface): boolean {\n if (\n !isChannelMessageSurface(surface) ||\n surface.version !== 1 ||\n surface.address === null ||\n typeof surface.address !== \"object\" ||\n Array.isArray(surface.address)\n ) {\n return false;\n }\n return (surface.address as Record<string, unknown>).connection === \"current\";\n}\n\nfunction unavailable(): DeliveryResult {\n return {\n status: \"failed\",\n retryable: true,\n error: {\n code: \"BROWSER_VOICE_UNAVAILABLE\",\n message: \"No browser voice surface is connected\"\n }\n };\n}\n\nfunction synthesisFailure(error?: unknown): DeliveryResult {\n const failure: DeliveryFailure = {\n code: \"BROWSER_VOICE_TTS_FAILED\",\n message:\n error instanceof Error\n ? error.message\n : \"The text-to-speech provider did not return audio\"\n };\n return { status: \"failed\", retryable: true, error: failure };\n}\n\nfunction deliveryFailure(\n error: unknown,\n contentDeliveryStarted: boolean\n): DeliveryResult {\n if (!contentDeliveryStarted) {\n const connectionClosed =\n error instanceof TypeError &&\n error.message.includes(\"WebSocket send() after close\");\n return {\n status: \"failed\",\n retryable: true,\n error: {\n code: connectionClosed\n ? \"BROWSER_VOICE_CONNECTION_CLOSED\"\n : \"BROWSER_VOICE_DELIVERY_FAILED\",\n message: connectionClosed\n ? \"The browser voice surface disconnected before delivery\"\n : error instanceof Error\n ? error.message\n : \"Browser voice delivery failed before sending content\"\n }\n };\n }\n\n return {\n status: \"uncertain\",\n error: {\n code: \"BROWSER_VOICE_DELIVERY_ERROR\",\n message:\n error instanceof Error\n ? error.message\n : \"Browser voice delivery failed with an unknown outcome\"\n }\n };\n}\n\nfunction sendJSON(\n connection: BrowserVoiceConnection,\n message: VoiceServerMessage\n): void {\n connection.send(JSON.stringify(message));\n}\n\n/**\n * Create an output-only channel that synthesizes one message and sends its\n * encoded audio to one browser using the Cloudflare Voice wire protocol.\n *\n * The browser should connect with `VoiceClient` or `useVoiceAgent()`. No\n * `withVoice()` mixin, microphone, or speech-to-text provider is required.\n */\nexport function browserVoice(options: BrowserVoiceChannelOptions): Channel {\n const audioFormat = options.audioFormat ?? \"mp3\";\n const sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n const toSpeechText = options.toSpeechText ?? ((message) => message.markdown);\n\n return {\n isAvailable() {\n return options.getConnection() !== undefined;\n },\n\n async deliver(surface, message) {\n if (!validSurface(surface)) {\n return {\n status: \"failed\",\n retryable: false,\n error: {\n code: \"BROWSER_VOICE_SURFACE_INVALID\",\n message: `Browser voice cannot parse the address for Channel \"${surface.channelKey}\"`\n }\n };\n }\n const connection = options.getConnection();\n if (!connection) return unavailable();\n\n let audio: ArrayBuffer | null;\n const speechText = toSpeechText(message);\n try {\n audio = await options.tts.synthesize(speechText);\n } catch (error) {\n return synthesisFailure(error);\n }\n if (!audio) return synthesisFailure();\n\n let contentDeliveryStarted = false;\n try {\n sendJSON(connection, {\n type: \"welcome\",\n protocol_version: VOICE_PROTOCOL_VERSION\n });\n sendJSON(connection, {\n type: \"audio_config\",\n format: audioFormat,\n sampleRate\n });\n sendJSON(connection, { type: \"status\", status: \"speaking\" });\n sendJSON(connection, {\n type: \"transcript\",\n role: \"assistant\",\n text: speechText\n });\n contentDeliveryStarted = true;\n connection.send(audio);\n sendJSON(connection, { type: \"status\", status: \"idle\" });\n return { status: \"delivered\" };\n } catch (error) {\n return deliveryFailure(error, contentDeliveryStarted);\n }\n }\n };\n}\n"],"mappings":";;;AA0CA,MAAM,sBAAsB;AAE5B,SAAS,aAAa,SAAyC;CAC7D,IACE,CAAC,wBAAwB,OAAO,KAChC,QAAQ,YAAY,KACpB,QAAQ,YAAY,QACpB,OAAO,QAAQ,YAAY,YAC3B,MAAM,QAAQ,QAAQ,OAAO,GAE7B,OAAO;CAET,OAAQ,QAAQ,QAAoC,eAAe;AACrE;AAEA,SAAS,cAA8B;CACrC,OAAO;EACL,QAAQ;EACR,WAAW;EACX,OAAO;GACL,MAAM;GACN,SAAS;EACX;CACF;AACF;AAEA,SAAS,iBAAiB,OAAiC;CAQzD,OAAO;EAAE,QAAQ;EAAU,WAAW;EAAM,OAAO;GANjD,MAAM;GACN,SACE,iBAAiB,QACb,MAAM,UACN;EAEiD;CAAE;AAC7D;AAEA,SAAS,gBACP,OACA,wBACgB;CAChB,IAAI,CAAC,wBAAwB;EAC3B,MAAM,mBACJ,iBAAiB,aACjB,MAAM,QAAQ,SAAS,8BAA8B;EACvD,OAAO;GACL,QAAQ;GACR,WAAW;GACX,OAAO;IACL,MAAM,mBACF,oCACA;IACJ,SAAS,mBACL,2DACA,iBAAiB,QACf,MAAM,UACN;GACR;EACF;CACF;CAEA,OAAO;EACL,QAAQ;EACR,OAAO;GACL,MAAM;GACN,SACE,iBAAiB,QACb,MAAM,UACN;EACR;CACF;AACF;AAEA,SAAS,SACP,YACA,SACM;CACN,WAAW,KAAK,KAAK,UAAU,OAAO,CAAC;AACzC;;;;;;;;AASA,SAAgB,aAAa,SAA8C;CACzE,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,eAAe,QAAQ,kBAAkB,YAAY,QAAQ;CAEnE,OAAO;EACL,cAAc;GACZ,OAAO,QAAQ,cAAc,MAAM,KAAA;EACrC;EAEA,MAAM,QAAQ,SAAS,SAAS;GAC9B,IAAI,CAAC,aAAa,OAAO,GACvB,OAAO;IACL,QAAQ;IACR,WAAW;IACX,OAAO;KACL,MAAM;KACN,SAAS,uDAAuD,QAAQ,WAAW;IACrF;GACF;GAEF,MAAM,aAAa,QAAQ,cAAc;GACzC,IAAI,CAAC,YAAY,OAAO,YAAY;GAEpC,IAAI;GACJ,MAAM,aAAa,aAAa,OAAO;GACvC,IAAI;IACF,QAAQ,MAAM,QAAQ,IAAI,WAAW,UAAU;GACjD,SAAS,OAAO;IACd,OAAO,iBAAiB,KAAK;GAC/B;GACA,IAAI,CAAC,OAAO,OAAO,iBAAiB;GAEpC,IAAI,yBAAyB;GAC7B,IAAI;IACF,SAAS,YAAY;KACnB,MAAM;KACN,kBAAA;IACF,CAAC;IACD,SAAS,YAAY;KACnB,MAAM;KACN,QAAQ;KACR;IACF,CAAC;IACD,SAAS,YAAY;KAAE,MAAM;KAAU,QAAQ;IAAW,CAAC;IAC3D,SAAS,YAAY;KACnB,MAAM;KACN,MAAM;KACN,MAAM;IACR,CAAC;IACD,yBAAyB;IACzB,WAAW,KAAK,KAAK;IACrB,SAAS,YAAY;KAAE,MAAM;KAAU,QAAQ;IAAO,CAAC;IACvD,OAAO,EAAE,QAAQ,YAAY;GAC/B,SAAS,OAAO;IACd,OAAO,gBAAgB,OAAO,sBAAsB;GACtD;EACF;CACF;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/harnesses/pi/context.ts","../../../src/harnesses/pi/session-store.ts","../../../src/harnesses/pi/harness.ts","../../../src/harnesses/pi/skills.ts"],"sourcesContent":["import type { Harness } from \"@earendil-works/pi-durable\";\n\n/**\n * pi's invocation context, as `@earendil-works/chord` defines it.\n *\n * pi-durable takes a chord `Context` on every call but does not re-export\n * the type or its constructors. The harness needs only a background context\n * and a way to add an abort signal, so both are inlined here instead of\n * depending on chord directly. A chord `Context` is a plain structural\n * interface (an abort signal, keyed values, and a name), so these interoperate\n * with chord's own contexts inside pi.\n *\n * One limit: chord's own derived contexts read the abort signal through a\n * private key rather than `abortSignal`, so a chord `withContextValue`\n * wrapped around one of these would not see its signal. pi 0.99.2 never\n * wraps a caller's context that way; `withAbortSignal` and `withCancel`\n * read `abortSignal` and work.\n *\n * If pi-durable starts re-exporting chord's context helpers, replace this\n * file with those.\n */\nexport type Context = Parameters<Harness[\"close\"]>[0];\n\n/** chord's `BACKGROUND_CONTEXT`: no values, never cancelled. */\nexport const BACKGROUND_CONTEXT: Context = Object.freeze({\n abortSignal: undefined,\n value: () => undefined,\n toString: () => \"[Context pi-harness background]\"\n});\n\n/**\n * chord's `withAbortSignal`: a context cancelled by either the parent's\n * signal or `signal`, carrying the parent's values.\n */\nexport function withAbortSignal(signal: AbortSignal, parent: Context): Context {\n const abortSignal =\n parent.abortSignal === undefined\n ? signal\n : AbortSignal.any([parent.abortSignal, signal]);\n return Object.freeze({\n abortSignal,\n value: parent.value.bind(parent),\n toString: () => `${parent}.WithAbortSignal`\n });\n}\n","import {\n SQLITE_MIGRATIONS,\n SqliteStorage,\n type SqliteDatabase,\n type SqliteStatement,\n type SqliteValue\n} from \"@earendil-works/pi-durable/storage/sqlite\";\n\n/**\n * pi's session store on a Durable Object's SQLite database.\n *\n * pi owns its sessions: conversations, entries, tasks, submissions, and\n * documents live in pi's own schema, created and migrated by pi's portable\n * `SqliteStorage`. This file only supplies the synchronous database facade\n * pi asks for, over `ctx.storage.sql`, and moves pi's tables under a prefix\n * so they cannot collide with the SDK's or the host's tables in the same\n * object.\n *\n * It is the Durable Object counterpart of `agents/sessions` for a harness\n * that brings its own session model, and has nothing pi-harness specific in\n * it: any harness built on pi-durable can open its storage with it.\n */\nexport type PiSessionStoreOptions = {\n /**\n * Prefix for every table and index pi creates. Must not start with `_cf_`,\n * which Durable Objects reserve. Default `pi_`.\n */\n readonly prefix?: string;\n};\n\nconst DEFAULT_PREFIX = \"pi_\";\n\n/** Open pi-durable's storage over this object's SQLite database. */\nexport function openPiSessionStore(\n storage: DurableObjectStorage,\n options: PiSessionStoreOptions = {}\n): Promise<SqliteStorage> {\n return SqliteStorage.open(new DurableObjectSqliteDatabase(storage, options));\n}\n\n/** Names pi's migrations create: every table and index, in any version. */\nfunction schemaNames(): readonly string[] {\n const names = new Set<string>([\"durable_schema\"]);\n const pattern =\n /\\bCREATE\\s+(?:UNIQUE\\s+)?(?:TABLE|INDEX)\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?([A-Za-z_][A-Za-z0-9_]*)/gi;\n for (const migration of SQLITE_MIGRATIONS) {\n for (const statement of migration.statements) {\n for (const match of statement.matchAll(pattern)) names.add(match[1]);\n }\n }\n return [...names];\n}\n\n/**\n * Rewrites pi's schema identifiers outside string literals. Identifiers are\n * matched on word boundaries, so column names such as `record_type` or\n * `document_id` are left alone.\n */\nclass Prefixer {\n readonly #pattern: RegExp;\n readonly #prefix: string;\n readonly #cache = new Map<string, string>();\n\n constructor(prefix: string) {\n if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(prefix)) {\n throw new Error(\n `Invalid pi session store prefix ${JSON.stringify(prefix)}`\n );\n }\n if (prefix.startsWith(\"_cf_\")) {\n throw new Error(\"The pi session store prefix must not start with _cf_\");\n }\n this.#prefix = prefix;\n this.#pattern = new RegExp(`\\\\b(${schemaNames().join(\"|\")})\\\\b`, \"g\");\n }\n\n rewrite(sql: string): string {\n const cached = this.#cache.get(sql);\n if (cached !== undefined) return cached;\n // Split on single-quoted literals ('' escapes stay inside a literal).\n const rewritten = sql\n .split(/('(?:[^']|'')*')/)\n .map((part, index) =>\n index % 2 === 1\n ? part\n : part.replace(this.#pattern, (name) => `${this.#prefix}${name}`)\n )\n .join(\"\");\n this.#cache.set(sql, rewritten);\n return rewritten;\n }\n}\n\n/** Durable Objects bind strings, numbers, null, and ArrayBuffers. */\nfunction binding(value: SqliteValue): SqlStorageValue {\n if (typeof value === \"bigint\") {\n if (\n value > BigInt(Number.MAX_SAFE_INTEGER) ||\n value < BigInt(Number.MIN_SAFE_INTEGER)\n ) {\n throw new RangeError(`SQLite integer ${value} is outside the safe range`);\n }\n return Number(value);\n }\n if (value instanceof Uint8Array) {\n return value.buffer.slice(\n value.byteOffset,\n value.byteOffset + value.byteLength\n ) as ArrayBuffer;\n }\n return value;\n}\n\n/** Blobs come back as ArrayBuffers; pi's contract reads Uint8Arrays. */\nfunction row<T extends object>(raw: Record<string, SqlStorageValue>): T {\n for (const key of Object.keys(raw)) {\n const value = raw[key];\n if (value instanceof ArrayBuffer) {\n (raw as Record<string, unknown>)[key] = new Uint8Array(value);\n }\n }\n return raw as T;\n}\n\nclass DurableObjectSqliteStatement implements SqliteStatement {\n readonly #sql: SqlStorage;\n readonly #query: string;\n\n constructor(sql: SqlStorage, query: string) {\n this.#sql = sql;\n this.#query = query;\n }\n\n run(...params: SqliteValue[]): void {\n this.#sql.exec(this.#query, ...params.map(binding));\n }\n\n get<T extends object>(...params: SqliteValue[]): T | undefined {\n const cursor = this.#sql.exec(this.#query, ...params.map(binding));\n const first = cursor.next();\n return first.done ? undefined : row<T>(first.value);\n }\n\n all<T extends object>(...params: SqliteValue[]): T[] {\n return this.#sql\n .exec(this.#query, ...params.map(binding))\n .toArray()\n .map((raw) => row<T>(raw));\n }\n}\n\n/**\n * pi's `SqliteDatabase` facade over `DurableObjectStorage`.\n *\n * Durable Objects reject `BEGIN`, so transactions go through\n * `transactionSync`, which rolls back when the callback throws and rethrows\n * the same error, as pi's contract requires. Durable Objects have no\n * prepared statements either; a statement is the query text, executed on\n * each call.\n */\nexport class DurableObjectSqliteDatabase implements SqliteDatabase {\n readonly #storage: DurableObjectStorage;\n readonly #prefixer: Prefixer;\n\n constructor(\n storage: DurableObjectStorage,\n options: PiSessionStoreOptions = {}\n ) {\n this.#storage = storage;\n this.#prefixer = new Prefixer(options.prefix ?? DEFAULT_PREFIX);\n }\n\n exec(sql: string): void {\n this.#storage.sql.exec(this.#prefixer.rewrite(sql));\n }\n\n prepare(sql: string): SqliteStatement {\n return new DurableObjectSqliteStatement(\n this.#storage.sql,\n this.#prefixer.rewrite(sql)\n );\n }\n\n transaction<T>(callback: () => T): T {\n return this.#storage.transactionSync(callback);\n }\n\n /** The object owns the database; there is nothing to close. */\n close(): void {}\n}\n","import type { ModelThinkingLevel } from \"@earendil-works/pi-ai\";\nimport {\n ConversationConfig,\n LiveDoc,\n ROOT_CONVERSATION_ID,\n watchEvents,\n type AgentEventStream,\n type Conversation,\n type Harness,\n type ConversationId,\n type ConversationRetryPolicy,\n type EntryRecord,\n type ModelRef,\n type SettledSubmissionRecord,\n type Tx,\n type UserInput\n} from \"@earendil-works/pi-durable\";\nimport type { SqliteStorage } from \"@earendil-works/pi-durable/storage/sqlite\";\nimport {\n LifecycleCapability,\n type CapabilityStartContext,\n type LifecycleJobContext,\n type LifecycleJobOutcome\n} from \"../../lifecycle\";\nimport { BACKGROUND_CONTEXT, withAbortSignal, type Context } from \"./context\";\nimport {\n openPiSessionStore,\n type PiSessionStoreOptions\n} from \"./session-store\";\nimport type {\n PiOperationResult,\n PiPendingOperation,\n PiPromptResponse,\n PiReceipt,\n PiSessionId,\n PiSessionInfo,\n PiSessionOptions,\n PiSubmitOptions\n} from \"./types\";\n\nconst BG = BACKGROUND_CONTEXT;\n\n/** The root session's id. */\nexport const ROOT_SESSION: PiSessionId = String(ROOT_CONVERSATION_ID);\n\n/**\n * Defaults for the three wake timings; a host overrides them with\n * `timing`, which is what the tests do to avoid the real heartbeat.\n *\n * A pi long wait further away than this is handed to the alarm, so the\n * alarm, not pi's in-memory timer, is what wakes the object.\n */\nconst SLEEP_THRESHOLD_MS = 60_000;\n\n/**\n * Longest one wake waits on pi. It waits inside an alarm invocation, which\n * has a 15 minute wall-time limit, so a longer run is waited on across\n * several alarms.\n */\nconst WAIT_BUDGET_MS = 10 * 60_000;\n\n/**\n * The wake job's heartbeat while it waits, and how often it re-checks work\n * it cannot wait on, such as background tasks. If the object is evicted, the\n * job is still due and its alarm restarts the object.\n */\nconst HEARTBEAT_MS = 30_000;\n\nconst WAKE_FN = \"wake\";\n\nfunction wakeJobId(session: PiSessionId): string {\n return `pi-wake:${session}`;\n}\n\nfunction sessionOfJob(payload: unknown): PiSessionId | undefined {\n return typeof payload === \"object\" &&\n payload !== null &&\n \"session\" in payload &&\n typeof payload.session === \"string\"\n ? payload.session\n : undefined;\n}\n\n/**\n * What the harness factory is handed. The store is already open and pi's\n * migrations have run, so a factory only has to decide how to call\n * `Harness.open` — or call something else that satisfies the same contract.\n */\nexport type PiHarnessContext = {\n /** pi's storage over this object's SQLite, tables under the store prefix. */\n readonly storage: SqliteStorage;\n /** Background context, for the open itself. */\n readonly context: Context;\n};\n\n/**\n * Builds pi's `Harness`. Return value is adopted as-is: the caller owns the\n * registry (tools, system prompt, hooks, skills), `models`, `env`,\n * `onReport` and anything else `HarnessOptions` grows.\n *\n * ```ts\n * harness: ({ storage, context }) =>\n * Harness.open(storage, { models, registry }, context)\n * ```\n */\nexport type PiHarnessFactory = (\n context: PiHarnessContext\n) => Harness | Promise<Harness>;\n\n/**\n * Applied to a session the first time it is created. Without a model, a\n * session's generation fails as unanswered until `session.setModel` sets one.\n */\nexport type PiSessionDefaults = {\n /** Model for new sessions. Change one session's with `session.setModel`. */\n readonly model?: ModelRef;\n readonly thinkingLevel?: ModelThinkingLevel;\n /** pi's generation retries for new sessions. */\n readonly retry?: ConversationRetryPolicy;\n};\n\n/**\n * How long the wake waits and when it hands a wait to the alarm. Defaults\n * suit a real deployment; a test shortens them so a suite does not sit on\n * the real heartbeat.\n */\nexport type PiWakeTiming = {\n /** A pi wait further away than this goes to the alarm. Default 60_000. */\n readonly sleepThresholdMs?: number;\n /** Longest one wake waits inside an alarm. Default 600_000. */\n readonly waitBudgetMs?: number;\n /** Heartbeat while waiting, and the re-check for background work. Default 30_000. */\n readonly heartbeatMs?: number;\n};\n\n/** `PiHarness`'s options. Only `harness`, which opens pi, is required. */\nexport type PiHarnessOptions = {\n /** Opens pi's `Harness` over the store this object prepared. */\n readonly harness: PiHarnessFactory;\n /** Applied to new sessions. */\n readonly defaults?: PiSessionDefaults;\n readonly store?: PiSessionStoreOptions;\n readonly timing?: PiWakeTiming;\n};\n\ntype Opened = {\n readonly pi: Harness;\n readonly storage: SqliteStorage;\n};\n\nfunction conversationId(session: PiSessionId): ConversationId {\n const id = Number(session);\n if (!Number.isSafeInteger(id) || id < 1) {\n throw new Error(`Invalid pi session ${JSON.stringify(session)}`);\n }\n return id as ConversationId;\n}\n\n/** The text of an assistant entry, for an operation's result. */\nfunction assistantText(entry: EntryRecord | undefined): string {\n const message = entry?.model?.[0];\n if (message?.role !== \"assistant\") return \"\";\n return message.content\n .map((part) => (part.type === \"text\" ? part.text : \"\"))\n .join(\"\");\n}\n\nfunction signalContext(signal: AbortSignal | undefined): Context {\n return signal ? withAbortSignal(signal, BG) : BG;\n}\n\n/**\n * pi-durable hosted in a Durable Object, behind a small harness interface:\n * `harness.prompt()`, `harness.sessions`, `harness.session(id)`. How a session reaches a\n * client (sockets, SSE, RPC) is the host's glue, built on `session.events()`.\n *\n * pi owns everything about a run: the transcript, the inbox of steers and\n * follow-ups, generation and tool tasks, retries, crash recovery, and the\n * live view. It keeps all of it in its own tables in this object's SQLite\n * database (see `session-store.ts`).\n *\n * What pi cannot do on a Durable Object is wake itself: its scheduler runs\n * in memory, and an evicted object has no memory. The harness is that wake,\n * with one Lifecycle job per session. Input goes to pi once, in `submit()`,\n * after the session's job is scheduled. The job waits while pi has live\n * tasks in the session, rescheduling itself as a heartbeat, and completes\n * when there are none. An eviction mid-run leaves the job due, so its alarm\n * restarts the object, pi reopens and resumes its own tasks, and the job\n * waits again.\n *\n * @experimental The API may change between releases.\n */\nexport class PiHarness extends LifecycleCapability {\n readonly sessions: PiSessions;\n readonly #options: PiHarnessOptions;\n /** In-memory waits on pi, per session, each inside an alarm's work. */\n readonly #waits = new Map<PiSessionId, Promise<void>>();\n /** Submissions between their wake and pi's admission, per session. */\n readonly #admitting = new Map<PiSessionId, number>();\n readonly #sleepThresholdMs: number;\n readonly #waitBudgetMs: number;\n readonly #heartbeatMs: number;\n #opening: Promise<Opened> | undefined;\n\n constructor(options: PiHarnessOptions) {\n super(\"pi-harness\");\n this.#options = options;\n this.#sleepThresholdMs =\n options.timing?.sleepThresholdMs ?? SLEEP_THRESHOLD_MS;\n this.#waitBudgetMs = options.timing?.waitBudgetMs ?? WAIT_BUDGET_MS;\n this.#heartbeatMs = options.timing?.heartbeatMs ?? HEARTBEAT_MS;\n for (const [name, value] of [\n [\"sleepThresholdMs\", this.#sleepThresholdMs],\n [\"waitBudgetMs\", this.#waitBudgetMs],\n [\"heartbeatMs\", this.#heartbeatMs]\n ] as const) {\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(`PiHarness timing.${name} must be a positive number`);\n }\n }\n this.sessions = new PiSessions(this);\n }\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n override async onStart(_context: CapabilityStartContext): Promise<void> {\n const { pi } = await this.#open();\n // Every session with live work gets a wake, including ones whose wake\n // failed out or that a subagent created without going through submit().\n const inspection = await pi.inspect(BG);\n const sessions = new Set(\n inspection.tasks.map((task) => String(task.record.conversationId))\n );\n for (const session of sessions) await this.#wake(session);\n }\n\n async onJob(\n context: LifecycleJobContext\n ): Promise<LifecycleJobOutcome | void> {\n if (context.job.fn !== WAKE_FN) return;\n const session = sessionOfJob(context.job.payload);\n if (session !== undefined) return this.#wakeStep(session);\n }\n\n /** Close pi's in-memory resources. Durable state is untouched. */\n async dispose(): Promise<void> {\n const opening = this.#opening;\n this.#opening = undefined;\n const opened = await opening?.catch(() => undefined);\n await opened?.pi.close(BG);\n }\n\n // ── The harness interface ────────────────────────────────────────────────\n\n /** A handle on one session. No I/O until you call it. */\n session(id: PiSessionId = ROOT_SESSION): PiSession {\n return new PiSession(this, id);\n }\n\n /** Submit a prompt and wait for its answer. */\n prompt(\n input: UserInput,\n options: PiSubmitOptions = {}\n ): Promise<PiPromptResponse> {\n return this.session(options.session).prompt(input, options);\n }\n\n /** Durably submit a prompt. Resolves before the model runs. */\n submit(input: UserInput, options: PiSubmitOptions = {}): Promise<PiReceipt> {\n return this.session(options.session).submit(input, options);\n }\n\n /** Stop one operation, or everything running in a session. */\n abort(\n options: PiSessionOptions & { readonly operationId?: string } = {}\n ): Promise<boolean> {\n return this.session(options.session).abort(options.operationId);\n }\n\n wait(\n operationId: string,\n options: PiSessionOptions & { readonly signal?: AbortSignal } = {}\n ): Promise<PiOperationResult> {\n return this.session(options.session).wait(operationId, options.signal);\n }\n\n /** The session's active transcript, as pi's entries. */\n messages(options: PiSessionOptions = {}): Promise<EntryRecord[]> {\n return this.session(options.session).messages();\n }\n\n /** Submissions pi has not settled yet, oldest first. */\n async pending(options: PiSessionOptions = {}): Promise<PiPendingOperation[]> {\n const { pi } = await this.#open();\n const id =\n options.session === undefined\n ? undefined\n : conversationId(options.session);\n return (await pi.inspect(BG)).submissions\n .filter(\n (record) =>\n record.requestId !== undefined &&\n (id === undefined || record.conversationId === id)\n )\n .map((record) => ({\n operationId: record.requestId as string,\n session: String(record.conversationId),\n status: record.status === \"queued\" ? \"queued\" : \"running\"\n }));\n }\n\n /** The opened pi Harness, for anything the interface does not cover. */\n async pi(): Promise<Harness> {\n return (await this.#open()).pi;\n }\n\n // ── Used by PiSession and PiSessions ─────────────────────────────────────\n\n /** @internal */\n async conversation(session: PiSessionId): Promise<Conversation> {\n const { pi } = await this.#open();\n const conversation = await pi.conversation(conversationId(session), BG);\n if (!conversation) throw new Error(`Unknown pi session ${session}`);\n return conversation;\n }\n\n /** @internal */\n async storage(): Promise<SqliteStorage> {\n return (await this.#open()).storage;\n }\n\n /** @internal Configure a new session like the root. */\n initSession(tx: Tx, id: ConversationId): Promise<void> {\n return this.#init(tx, id);\n }\n\n /** @internal */\n async enqueue(\n session: PiSessionId,\n input: UserInput,\n options: PiSubmitOptions\n ): Promise<PiReceipt> {\n const operationId = options.operationId ?? crypto.randomUUID();\n const { storage } = await this.#open();\n const conversation = await this.conversation(session);\n this.#admitting.set(session, (this.#admitting.get(session) ?? 0) + 1);\n let accepted: boolean;\n try {\n // 1. The wake first, so a job is scheduled before pi has the work.\n // If the object dies after pi admits the input, that job restarts\n // it. While this admission is in flight the step will not park.\n await this.#wake(session);\n // 2. The one admission. pi deduplicates by request id.\n accepted =\n (await storage.submissionByRequest(\n conversation.id,\n operationId,\n BG\n )) === undefined;\n await conversation.submit(\n {\n type: \"input\",\n content: input,\n whenBusy: options.whenBusy ?? \"followUp\",\n requestId: operationId\n },\n BG\n );\n } finally {\n const left = (this.#admitting.get(session) ?? 1) - 1;\n if (left === 0) this.#admitting.delete(session);\n else this.#admitting.set(session, left);\n }\n // 3. Step now, so the wake sees the work even if it just parked.\n await this.#wake(session);\n return { operationId, session, accepted };\n }\n\n /** @internal Withdraw a queued input, or abort the run it joined. */\n async withdraw(session: PiSessionId, operationId: string): Promise<boolean> {\n const { pi, storage } = await this.#open();\n const id = conversationId(session);\n const record = await storage.submissionByRequest(id, operationId, BG);\n if (!record || record.status === \"done\" || record.status === \"unanswered\") {\n return false;\n }\n const withdrawn = await pi.abortSubmission(record.id, BG, id);\n if (withdrawn === \"already_placed\") {\n await (await pi.conversation(id, BG))?.abort(BG);\n }\n return withdrawn !== \"settled\" && withdrawn !== \"not_found\";\n }\n\n /** @internal Wait for pi to settle an operation, by its request id. */\n async settled(\n session: PiSessionId,\n operationId: string,\n signal?: AbortSignal\n ): Promise<PiOperationResult> {\n const conversation = await this.conversation(session);\n const context = signalContext(signal);\n const submission = await this.#findSubmission(\n conversation.id,\n operationId,\n context\n );\n if (!submission) {\n return {\n operationId,\n session,\n status: \"unanswered\",\n reason: \"not_found\"\n };\n }\n return this.#result(\n session,\n operationId,\n await submission.wait(context),\n context\n );\n }\n\n // ── The wake ─────────────────────────────────────────────────────────────\n\n /** Schedule the session's wake job now, or pull it forward. */\n #wake(session: PiSessionId, time = Date.now()): Promise<unknown> {\n // A push made while the job is dispatching supersedes that dispatch's\n // outcome, so a submit is never lost to a wake that is completing.\n return this.lifecycle.jobs.push({\n id: wakeJobId(session),\n fn: WAKE_FN,\n time,\n payload: { session },\n singleflight: true,\n recoveryLoop: true\n });\n }\n\n /**\n * One run of a session's wake job. It never admits or replays anything:\n * it waits while pi has live tasks in the session and completes when it\n * has none. pi does all the work in between.\n */\n async #wakeStep(session: PiSessionId): Promise<LifecycleJobOutcome> {\n const heartbeat = { rescheduleAt: Date.now() + this.#heartbeatMs };\n if (this.#waits.has(session)) return heartbeat;\n const { pi } = await this.#open();\n const conversation = await pi.conversation(conversationId(session), BG);\n if (!conversation) return undefined;\n\n const tasks = (await pi.inspect(BG)).tasks.filter(\n (task) => task.record.conversationId === conversation.id\n );\n if (tasks.length === 0) {\n // A submit between its wake and pi's admission: check again later.\n return this.#admitting.has(session) ? heartbeat : undefined;\n }\n const wakeAt = await this.#longWait(pi, conversation.id, BG);\n if (wakeAt !== undefined) return { rescheduleAt: wakeAt };\n // Background tasks are outside the conversation's idle wait.\n if (tasks.every((task) => task.record.background)) return heartbeat;\n\n const wait = this.#waitForIdle(conversation).finally(() => {\n this.#waits.delete(session);\n // Re-check now: pi may have started more work, such as a follow-up.\n void this.#wake(session);\n });\n this.#waits.set(session, wait);\n // The wait runs past this dispatch, inside the alarm's work, so the\n // object stays alive for it. The heartbeat covers an eviction.\n this.lifecycle.trackAlarmWork(wait);\n return heartbeat;\n }\n\n async #waitForIdle(conversation: Conversation): Promise<void> {\n const budget = new AbortController();\n const timer = setTimeout(() => budget.abort(), this.#waitBudgetMs);\n try {\n // Cancelling the wait never cancels pi's work.\n await conversation.waitForIdle(withAbortSignal(budget.signal, BG));\n } catch (error) {\n if (!budget.signal.aborted) {\n this.lifecycle.events.emit(\"pi:wake_error\", {\n session: String(conversation.id),\n error: error instanceof Error ? error.message : String(error)\n });\n }\n } finally {\n clearTimeout(timer);\n }\n }\n\n // ── pi ───────────────────────────────────────────────────────────────────\n\n #open(): Promise<Opened> {\n this.#opening ??= this.#doOpen().catch((error: unknown) => {\n this.#opening = undefined;\n throw error;\n });\n return this.#opening;\n }\n\n async #doOpen(): Promise<Opened> {\n const storage = await openPiSessionStore(\n this.lifecycle.storage,\n this.#options.store\n );\n const pi = await this.#options.harness({ storage, context: BG });\n await pi.root(BG, { init: (tx, id) => this.#init(tx, id) });\n // Continue whatever the last isolate left: pi reconciles tasks that were\n // running to pending and schedules them again.\n pi.resume();\n return { pi, storage };\n }\n\n async #init(tx: Tx, id: ConversationId): Promise<void> {\n const config = await tx.doc(ConversationConfig, id);\n const defaults = this.#options.defaults;\n if (defaults?.model) config.model = { ...defaults.model };\n if (defaults?.thinkingLevel) config.thinkingLevel = defaults.thinkingLevel;\n if (defaults?.retry) config.retry = { ...defaults.retry };\n }\n\n async #findSubmission(\n id: ConversationId,\n operationId: string,\n context: Context\n ) {\n const { pi, storage } = await this.#open();\n // Read from pi's storage: the Harness only reacquires submissions by id.\n const record = await storage.submissionByRequest(id, operationId, context);\n return record ? pi.submission(record.id, context) : undefined;\n }\n\n /** When pi's current wait is far enough away to hand to the alarm. */\n async #longWait(\n pi: Harness,\n id: ConversationId,\n context: Context\n ): Promise<number | undefined> {\n const live = await pi.snapshot(LiveDoc, id, context);\n const at =\n live?.generation?.deferred?.pollAt ?? live?.generation?.retry?.at;\n return at !== undefined && at - Date.now() > this.#sleepThresholdMs\n ? at\n : undefined;\n }\n\n async #result(\n session: PiSessionId,\n operationId: string,\n settled: SettledSubmissionRecord,\n context: Context\n ): Promise<PiOperationResult> {\n if (settled.status === \"unanswered\") {\n return {\n operationId,\n session,\n status: \"unanswered\",\n reason: settled.reason\n };\n }\n if (settled.type !== \"input\")\n return { operationId, session, status: \"done\" };\n const conversation = await this.conversation(session);\n const page = await conversation.entries(\n { minEntryId: settled.answer, maxEntryId: settled.answer },\n 1,\n undefined,\n context\n );\n return {\n operationId,\n session,\n status: \"done\",\n text: assistantText(page.items[0])\n };\n }\n}\n\n/** One pi conversation, addressed through the harness. */\nexport class PiSession {\n readonly #harness: PiHarness;\n readonly id: PiSessionId;\n\n constructor(harness: PiHarness, id: PiSessionId) {\n this.#harness = harness;\n this.id = id;\n }\n\n /** Durably submit a prompt. Resolves before the model runs. */\n submit(input: UserInput, options: PiSubmitOptions = {}): Promise<PiReceipt> {\n return this.#harness.enqueue(this.id, input, options);\n }\n\n /** Submit and wait for the answer and the updated transcript. */\n async prompt(\n input: UserInput,\n options: PiSubmitOptions = {}\n ): Promise<PiPromptResponse> {\n const receipt = await this.submit(input, options);\n const result = await this.wait(receipt.operationId);\n return { ...result, messages: await this.messages() };\n }\n\n /** Join the running work after its current tool round. */\n steer(input: UserInput, options: Omit<PiSubmitOptions, \"whenBusy\"> = {}) {\n return this.submit(input, { ...options, whenBusy: \"steer\" });\n }\n\n /** Wait for an operation to settle. Aborting `signal` stops only the wait. */\n wait(operationId: string, signal?: AbortSignal): Promise<PiOperationResult> {\n return this.#harness.settled(this.id, operationId, signal);\n }\n\n /**\n * Withdraw one queued operation (or abort the run it joined), or, with no\n * id, abort the session: pi withdraws queued inputs and aborts the\n * running work.\n */\n async abort(operationId?: string): Promise<boolean> {\n if (operationId !== undefined) {\n return this.#harness.withdraw(this.id, operationId);\n }\n const conversation = await this.#harness.conversation(this.id);\n await conversation.abort(BG);\n return true;\n }\n\n /** Start a new context, optionally from a handoff note. */\n async reset(handoff?: string): Promise<void> {\n await (await this.#harness.conversation(this.id)).reset(handoff, BG);\n }\n\n async setModel(model: ModelRef): Promise<void> {\n await (await this.#harness.conversation(this.id)).setModel(model, BG);\n }\n\n /** The active transcript, as pi's entries since the newest reset. */\n async messages(): Promise<EntryRecord[]> {\n const view = await (await this.#harness.conversation(this.id)).context(BG);\n return [...view.entries];\n }\n\n /** pi's agent events for this session: a snapshot, then one batch per commit. */\n async events(context: Context = BG): Promise<AgentEventStream> {\n const pi = await this.#harness.pi();\n return watchEvents(pi, conversationId(this.id), context);\n }\n\n async busy(): Promise<boolean> {\n const pi = await this.#harness.pi();\n const live = await pi.snapshot(LiveDoc, conversationId(this.id), BG);\n return live?.run !== undefined;\n }\n}\n\n/** Every pi conversation in this object. */\nexport class PiSessions {\n readonly #harness: PiHarness;\n\n constructor(harness: PiHarness) {\n this.#harness = harness;\n }\n\n get(id: PiSessionId): PiSession {\n return this.#harness.session(id);\n }\n\n /** A new top-level session, configured like the root. */\n async create(): Promise<PiSession> {\n const pi = await this.#harness.pi();\n const conversation = await pi.createConversation(\n {\n ownership: { kind: \"ownerless\" },\n init: (tx, id) => this.#harness.initSession(tx, id)\n },\n BG\n );\n return this.#harness.session(String(conversation.id));\n }\n\n /** A new session that sees `from`'s history up to its newest entry. */\n async fork(from: PiSessionId): Promise<PiSession> {\n const conversation = await this.#harness.conversation(from);\n const newest = await conversation.entries({}, 1, undefined, BG);\n const at = newest.items[0];\n if (!at) throw new Error(`Session ${from} has no entries to fork from`);\n const fork = await conversation.fork(\n at.id,\n { ownership: { kind: \"ownerless\" } },\n BG\n );\n return this.#harness.session(String(fork.id));\n }\n\n /**\n * Every conversation, including ones subagent tools own. Read from pi's\n * storage directly: the Harness has no conversation listing.\n */\n async list(): Promise<PiSessionInfo[]> {\n const storage = await this.#harness.storage();\n const pi = await this.#harness.pi();\n const sessions: PiSessionInfo[] = [];\n let cursor: Parameters<SqliteStorage[\"scanConversations\"]>[2];\n for (;;) {\n const page = await storage.scanConversations({}, 100, cursor, BG);\n for (const record of page.items) {\n const live = await pi.snapshot(LiveDoc, record.id, BG);\n sessions.push({\n id: String(record.id),\n ...(record.parent\n ? { parent: String(record.parent.conversationId) }\n : {}),\n busy: live?.run !== undefined\n });\n }\n if (page.next === undefined) return sessions;\n cursor = page.next;\n }\n }\n}\n","import type { JsonValue } from \"@earendil-works/pi-ai\";\nimport { Type } from \"@earendil-works/pi-ai\";\nimport type {\n Registry,\n ToolExecutionResult,\n ToolRegistration\n} from \"@earendil-works/pi-durable\";\nimport type {\n SkillContent,\n SkillDescriptor,\n SkillResourceDescriptor,\n SkillSource\n} from \"../../skills\";\n\n/** Skills resolved from `agents/skills` sources for one process lifetime. */\nexport type ResolvedSkills = {\n /** Changes when any source's fingerprint changes. */\n readonly fingerprint: string;\n /** Model-facing activation tools; empty when there are no skills. */\n readonly tools: readonly ToolRegistration[];\n /** System-prompt catalog, or null when there are no skills. */\n readonly catalog: string | null;\n readonly warnings: readonly string[];\n};\n\ntype ResolvedSkill = {\n readonly descriptor: SkillDescriptor;\n readonly content: SkillContent;\n readonly source: SkillSource;\n};\n\nexport function skillsFingerprint(sources: readonly SkillSource[]): string {\n return sources\n .map((source) => `${source.id}:${source.fingerprint}`)\n .join(\"|\");\n}\n\nfunction textResult(text: string): ToolExecutionResult {\n return { content: [{ type: \"text\", text }] };\n}\n\n/** pi validates arguments against the schema before `execute` runs. */\nfunction argsOf<T>(args: JsonValue): T {\n return args as T;\n}\n\nfunction attributes(\n pairs: ReadonlyArray<readonly [string, string | undefined]>\n) {\n return pairs\n .filter((pair): pair is readonly [string, string] => pair[1] !== undefined)\n .map(([key, value]) => ` ${key}=\"${value.replace(/\"/g, \""\")}\"`)\n .join(\"\");\n}\n\nfunction describeResource(resource: SkillResourceDescriptor): string {\n const details = [\n resource.kind,\n resource.encoding ?? \"text\",\n resource.mimeType,\n resource.size === undefined ? undefined : `${resource.size} bytes`\n ].filter((value): value is string => value !== undefined);\n return `- ${resource.path} (${details.join(\", \")})`;\n}\n\nfunction renderSkillContent(skill: ResolvedSkill): string {\n const resources = skill.content.resources ?? [];\n const lines = [\n `<skill_content${attributes([\n [\"name\", skill.descriptor.name],\n [\"version\", skill.descriptor.version]\n ])}>`,\n skill.content.body.trim()\n ];\n if (resources.length > 0) {\n lines.push(\"<skill_resources>\");\n for (const resource of resources) {\n lines.push(\n ` <file${attributes([\n [\"kind\", resource.kind],\n [\"encoding\", resource.encoding ?? \"text\"],\n [\n \"size\",\n resource.size === undefined ? undefined : String(resource.size)\n ]\n ])}>${resource.path}</file>`\n );\n }\n lines.push(\"</skill_resources>\");\n }\n lines.push(\"</skill_content>\", \"\");\n lines.push(\n resources.length === 0\n ? \"No bundled resources.\"\n : [\"Bundled resources:\", ...resources.map(describeResource)].join(\"\\n\")\n );\n return lines.join(\"\\n\");\n}\n\n/**\n * Resolve `agents/skills` sources into a system-prompt catalog and two\n * tools.\n *\n * The first source to list a name wins, as in `SkillRegistry`. pi-durable\n * has no skill concept of its own, so the model activates skills through\n * `activate_skill` and reads bundled files through `read_skill_resource` —\n * the same tools `@cloudflare/think` offers, so skills authored for one\n * framework work in the other.\n */\nexport async function resolveSkillSources(\n sources: readonly SkillSource[]\n): Promise<ResolvedSkills> {\n const fingerprint = skillsFingerprint(sources);\n const warnings: string[] = [];\n const resolved = new Map<string, ResolvedSkill>();\n for (const source of sources) {\n let descriptors: SkillDescriptor[];\n try {\n descriptors = await source.list();\n } catch (error) {\n warnings.push(\n `Skill source \"${source.id}\" failed to list skills: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n continue;\n }\n for (const descriptor of descriptors) {\n if (resolved.has(descriptor.name)) {\n warnings.push(\n `Skill \"${descriptor.name}\" from source \"${source.id}\" is shadowed by an earlier source`\n );\n continue;\n }\n let content: SkillContent | null;\n try {\n content = await source.load(descriptor.name);\n } catch (error) {\n warnings.push(\n `Skill \"${descriptor.name}\" from source \"${source.id}\" failed to load: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n continue;\n }\n if (!content) continue;\n resolved.set(descriptor.name, { descriptor, content, source });\n }\n }\n\n const visible = [...resolved.values()].filter(\n (skill) => skill.descriptor.metadata?.[\"disable-model-invocation\"] !== true\n );\n if (visible.length === 0) {\n return { fingerprint, tools: [], catalog: null, warnings };\n }\n\n const names = visible.map((skill) => skill.descriptor.name);\n const nameSchema = Type.Union(names.map((name) => Type.Literal(name)));\n const byName = new Map(\n visible.map((skill) => [skill.descriptor.name, skill])\n );\n\n const activateParameters = Type.Object({ name: nameSchema });\n const activateSkill: ToolRegistration = {\n name: \"activate_skill\",\n description:\n \"Activate a skill by name. Use this when the user's task matches one of the available skills; the response contains the skill's full instructions.\",\n parameters: activateParameters,\n replay: \"safe\",\n async execute(args) {\n const input = argsOf<{ name: string }>(args);\n const skill = byName.get(input.name);\n return textResult(\n skill ? renderSkillContent(skill) : `Skill not found: ${input.name}`\n );\n }\n };\n\n const resourceParameters = Type.Object({\n name: Type.Optional(nameSchema),\n path: Type.String({ minLength: 1 })\n });\n const readResource: ToolRegistration = {\n name: \"read_skill_resource\",\n description:\n \"Read a file bundled with a skill, such as a reference document or template. Provide the skill name and the file's path from the skill's resource list.\",\n parameters: resourceParameters,\n replay: \"safe\",\n async execute(args) {\n const input = argsOf<{ name?: string; path: string }>(args);\n const target = resolveResourceTarget(byName, input.name, input.path);\n if (!target) {\n return textResult(\n `Skill resource not found: ${input.name ? `${input.name}/` : \"\"}${input.path}`\n );\n }\n const resource = await target.skill.source\n .readResource?.(target.skill.descriptor.name, target.path)\n .catch(() => null);\n if (!resource) {\n return textResult(\n `Skill resource not found: ${target.skill.descriptor.name}/${target.path}`\n );\n }\n return textResult(\n [\n `<skill_resource${attributes([\n [\"name\", target.skill.descriptor.name],\n [\"path\", resource.path],\n [\"kind\", resource.kind],\n [\"encoding\", resource.encoding ?? \"text\"],\n [\"mimeType\", resource.mimeType]\n ])}>`,\n resource.content,\n \"</skill_resource>\"\n ].join(\"\\n\")\n );\n }\n };\n\n const catalog = [\n \"Available skills. When a task matches a skill, use activate_skill with its name before proceeding.\",\n \"\",\n ...visible.map(\n (skill) => `- ${skill.descriptor.name}: ${skill.descriptor.description}`\n )\n ].join(\"\\n\");\n\n return {\n fingerprint,\n tools: [activateSkill, readResource],\n catalog,\n warnings\n };\n}\n\nfunction resolveResourceTarget(\n byName: ReadonlyMap<string, ResolvedSkill>,\n name: string | undefined,\n path: string\n): { readonly skill: ResolvedSkill; readonly path: string } | undefined {\n if (path.split(\"/\").some((segment) => segment === \"..\")) return undefined;\n if (name !== undefined) {\n const skill = byName.get(name);\n return skill && hasResource(skill, path) ? { skill, path } : undefined;\n }\n // A qualified path names the skill in its first segment.\n const [head, ...rest] = path.split(\"/\");\n const skill = head === undefined ? undefined : byName.get(head);\n const relative = rest.join(\"/\");\n return skill && relative && hasResource(skill, relative)\n ? { skill, path: relative }\n : undefined;\n}\n\nfunction hasResource(skill: ResolvedSkill, path: string): boolean {\n return (skill.content.resources ?? []).some(\n (resource) => resource.path === path\n );\n}\n\n/**\n * Register `agents/skills` sources on a pi registry: the activation tools\n * and a \"skills\" system prompt section. Resolution reads the sources, so a\n * host awaits this in its `harness` factory, before `Harness.open`.\n */\nexport async function addSkills(\n registry: Registry,\n sources: readonly SkillSource[]\n): Promise<ResolvedSkills> {\n const resolved = await resolveSkillSources(sources);\n for (const warning of resolved.warnings) {\n console.warn(`pi skills: ${warning}`);\n }\n const catalog = resolved.catalog;\n registry.batch(() => {\n for (const tool of resolved.tools) registry.tools.add(tool);\n if (catalog) registry.systemPrompt.section(\"skills\", () => catalog);\n });\n return resolved;\n}\n"],"mappings":"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"}
|