eve 0.54.5 → 0.56.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 +41 -0
- package/dist/src/channel/adapter.d.ts +3 -2
- package/dist/src/channel/audience.d.ts +16 -0
- package/dist/src/channel/audience.js +1 -0
- package/dist/src/channel/conversation-context.d.ts +5 -0
- package/dist/src/channel/conversation-context.js +1 -0
- package/dist/src/channel/instrumentation.js +1 -1
- package/dist/src/channel/types.d.ts +9 -0
- package/dist/src/chunks/{use-eve-agent-CxPlg0KL.js → use-eve-agent-CMdMlS7Y.js} +0 -10
- package/dist/src/chunks/{use-eve-agent-D-KQzzyW.js → use-eve-agent-RUGSwq8b.js} +0 -10
- package/dist/src/cli/commands/agent-prompt/scaffold.md +5 -4
- package/dist/src/cli/commands/init-target.js +2 -2
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/registry-metadata.d.ts +0 -18
- package/dist/src/cli/commands/registry-metadata.js +1 -1
- package/dist/src/cli/commands/registry-pnpm-build-policy-flow.js +1 -1
- package/dist/src/cli/commands/registry.js +1 -1
- package/dist/src/cli/dev/environment.js +1 -1
- package/dist/src/cli/dev/tui/blocks.d.ts +2 -0
- package/dist/src/cli/dev/tui/blocks.js +3 -3
- package/dist/src/cli/dev/tui/markdown.d.ts +8 -1
- package/dist/src/cli/dev/tui/markdown.js +7 -7
- package/dist/src/cli/dev/tui/runner.d.ts +4 -1
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/terminal-renderer.d.ts +5 -1
- package/dist/src/cli/dev/tui/terminal-renderer.js +12 -12
- package/dist/src/cli/dev/tui/tui.d.ts +4 -1
- package/dist/src/cli/ui/terminal-text.js +1 -1
- package/dist/src/client/agent-info-schema.d.ts +0 -57
- package/dist/src/client/agent-info-schema.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiler/extension-compatibility.d.ts +26 -16
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/compiler/manifest.d.ts +12 -11
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/normalize-manifest.js +1 -1
- package/dist/src/compiler/normalize-tool.d.ts +0 -3
- package/dist/src/compiler/normalize-tool.js +1 -1
- package/dist/src/compiler/validate-artifact.js +1 -1
- package/dist/src/context/dynamic-resolve-context.js +1 -1
- package/dist/src/context/keys.d.ts +2 -0
- package/dist/src/context/keys.js +1 -1
- package/dist/src/discover/filesystem.d.ts +3 -1
- package/dist/src/discover/filesystem.js +1 -1
- package/dist/src/discover/skills.js +1 -1
- package/dist/src/dynamic/definition.d.ts +6 -0
- package/dist/src/eve-channel/audience.d.ts +3 -0
- package/dist/src/eve-channel/audience.js +1 -0
- package/dist/src/eve-channel/index.js +1 -1
- package/dist/src/eve-channel/types.d.ts +14 -1
- package/dist/src/execution/activity-work.d.ts +2 -0
- package/dist/src/execution/activity-work.js +1 -1
- package/dist/src/execution/channel-context.js +1 -1
- package/dist/src/execution/coordination-dispatch-shared.d.ts +1 -0
- package/dist/src/execution/coordination-dispatch-shared.js +1 -1
- package/dist/src/execution/coordination-dispatch-step.d.ts +2 -2
- package/dist/src/execution/coordination-dispatch-step.js +1 -1
- package/dist/src/execution/create-session-step.js +1 -1
- package/dist/src/execution/durable-session-store.d.ts +0 -1
- package/dist/src/execution/dynamic-workflow/program-step.d.ts +21 -0
- package/dist/src/execution/dynamic-workflow/program-step.js +1 -0
- package/dist/src/execution/dynamic-workflow/schema.d.ts +36 -0
- package/dist/src/execution/dynamic-workflow/schema.js +1 -0
- package/dist/src/execution/dynamic-workflow/security-step.d.ts +3 -0
- package/dist/src/execution/dynamic-workflow/security-step.js +1 -0
- package/dist/src/execution/dynamic-workflow/tool.d.ts +8 -0
- package/dist/src/execution/dynamic-workflow/tool.js +1 -0
- package/dist/src/execution/dynamic-workflow/workflow.d.ts +10 -0
- package/dist/src/execution/dynamic-workflow/workflow.js +1 -0
- package/dist/src/execution/eve-workflow-attributes.d.ts +2 -1
- package/dist/src/execution/eve-workflow-attributes.js +1 -1
- package/dist/src/execution/inline-turn.js +1 -1
- package/dist/src/execution/model-call-batching.js +1 -1
- package/dist/src/execution/next-driver-action.d.ts +0 -8
- package/dist/src/execution/node-step.d.ts +0 -5
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/runtime-context.js +1 -1
- package/dist/src/execution/scheduled-launch-delivery.d.ts +7 -0
- package/dist/src/execution/scheduled-launch-delivery.js +1 -0
- package/dist/src/execution/session-activity.js +1 -1
- package/dist/src/execution/session.d.ts +0 -1
- package/dist/src/execution/session.js +2 -2
- package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
- package/dist/src/execution/tasks/child/steps.js +1 -1
- package/dist/src/execution/tasks/parent/delegate.d.ts +9 -0
- package/dist/src/execution/tasks/parent/delegate.js +1 -1
- package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
- package/dist/src/execution/tools/sleep-workflow.js +1 -1
- package/dist/src/execution/tools/subagent/invoke-step.d.ts +4 -0
- package/dist/src/execution/tools/subagent/invoke-step.js +1 -1
- package/dist/src/execution/tools/subagent/start.d.ts +5 -0
- package/dist/src/execution/tools/subagent/start.js +1 -1
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/wire/session-inbox-contract.d.ts +2 -2
- package/dist/src/execution/wire/session-inbox-contract.js +1 -1
- package/dist/src/execution/wire/session-inbox-encoder.d.ts +6 -2
- package/dist/src/execution/wire/session-inbox-encoder.js +1 -1
- package/dist/src/execution/wire/session-inbox-resume.js +1 -1
- package/dist/src/execution/wire/session-inbox-wire.js +1 -1
- package/dist/src/execution/wire/session-inbox-wire.v7.d.ts +342 -0
- package/dist/src/execution/wire/session-inbox-wire.v7.js +1 -0
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/action-presentation.d.ts +5 -0
- package/dist/src/harness/action-presentation.js +1 -1
- package/dist/src/harness/advertised-tools.d.ts +0 -3
- package/dist/src/harness/advertised-tools.js +1 -1
- package/dist/src/harness/background-tools.d.ts +3 -1
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/step-hooks.js +1 -1
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/harness/tools.js +1 -1
- package/dist/src/harness/types.d.ts +0 -21
- package/dist/src/instrumentation/channel-delivery.js +1 -1
- package/dist/src/instrumentation/content-policy.d.ts +2 -2
- package/dist/src/instrumentation/content.js +1 -1
- package/dist/src/instrumentation/lifecycle.d.ts +5 -7
- package/dist/src/instrumentation/native-events.d.ts +2 -0
- package/dist/src/instrumentation/native-events.js +1 -1
- package/dist/src/instrumentation/principal-summary.d.ts +3 -3
- package/dist/src/instrumentation/runtime-global.d.ts +4 -0
- package/dist/src/instrumentation/runtime-global.js +1 -0
- package/dist/src/instrumentation/runtime.d.ts +5 -9
- package/dist/src/instrumentation/runtime.js +1 -1
- package/dist/src/instrumentation/session-context.d.ts +3 -0
- package/dist/src/instrumentation/session-context.js +1 -1
- package/dist/src/instrumentation/session-init.d.ts +5 -0
- package/dist/src/instrumentation/session-init.js +1 -0
- package/dist/src/instrumentation/setup-context.js +1 -1
- package/dist/src/internal/application/dev-environment.d.ts +3 -0
- package/dist/src/internal/application/dev-environment.js +1 -1
- package/dist/src/internal/application/output-publication-journal.js +1 -1
- package/dist/src/internal/application/output-publication-lock.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/connection.js +1 -1
- package/dist/src/internal/authored-definition/schema-backed.d.ts +3 -4
- package/dist/src/internal/authored-definition/schema-backed.js +1 -1
- package/dist/src/internal/instrumentation.d.ts +1 -12
- package/dist/src/internal/instrumentation.js +1 -1
- package/dist/src/internal/logging.js +1 -1
- package/dist/src/internal/nitro/host/build-application.js +1 -1
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
- package/dist/src/internal/nitro/host/dev-host-fingerprint.js +1 -1
- package/dist/src/internal/nitro/host/workflow-sandbox-runtime-plugin.js +1 -1
- package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.js +1 -1
- package/dist/src/protocol/activity.d.ts +1 -0
- package/dist/src/protocol/activity.js +1 -1
- package/dist/src/public/channels/chat-sdk/audience.d.ts +8 -0
- package/dist/src/public/channels/chat-sdk/audience.js +1 -1
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +0 -2
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +1 -1
- package/dist/src/public/channels/discord/audience.js +1 -1
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/discord/index.d.ts +0 -1
- package/dist/src/public/channels/index.d.ts +2 -3
- package/dist/src/public/channels/slack/activity-plan.js +1 -1
- package/dist/src/public/channels/slack/defaults.d.ts +2 -2
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +11 -3
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/hitl.js +1 -1
- package/dist/src/public/channels/teams/index.d.ts +0 -1
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/teams/verify.js +1 -1
- package/dist/src/public/channels/telegram/audience.d.ts +7 -0
- package/dist/src/public/channels/telegram/audience.js +1 -1
- package/dist/src/public/channels/telegram/index.d.ts +0 -1
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +0 -2
- package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
- package/dist/src/public/connections/index.d.ts +1 -1
- package/dist/src/public/definitions/channel.d.ts +2 -1
- package/dist/src/public/definitions/channel.js +1 -1
- package/dist/src/public/definitions/connections/mcp.d.ts +7 -1
- package/dist/src/public/definitions/connections/openapi.d.ts +1 -1
- package/dist/src/public/definitions/workspace-agent.js +1 -1
- package/dist/src/public/models/openai/chatgpt/auth.js +1 -1
- package/dist/src/public/models/openai/chatgpt/codex-app-server.js +1 -1
- package/dist/src/public/models/openai/chatgpt/credential-store.js +1 -1
- package/dist/src/public/next/index.d.ts +6 -4
- package/dist/src/public/next/index.js +1 -1
- package/dist/src/public/next/server.js +1 -1
- package/dist/src/public/next/vercel-output-config.d.ts +1 -0
- package/dist/src/public/tools/workflow.d.ts +1 -4
- package/dist/src/public/tools/workflow.js +1 -1
- package/dist/src/runtime/agent/mock-model-adapter.js +1 -1
- package/dist/src/runtime/connections/authorization-tokens.d.ts +2 -4
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/principal.d.ts +6 -6
- package/dist/src/runtime/connections/principal.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-connection.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/runtime/sessions/runtime-context-keys.js +1 -1
- package/dist/src/runtime/types.d.ts +4 -9
- package/dist/src/self-modification/agent.d.ts +7 -1
- package/dist/src/self-modification/agent.js +1 -1
- package/dist/src/self-modification/extension/classify-registry-item.d.ts +3 -3
- package/dist/src/self-modification/extension/classify-registry-item.js +1 -1
- package/dist/src/self-modification/extension/tools/search_registry.d.ts +1 -7
- package/dist/src/self-modification/extension/tools/search_registry.js +1 -1
- package/dist/src/setup/flows/chatgpt-auth.js +1 -1
- package/dist/src/setup/flows/registry.js +1 -1
- package/dist/src/setup/integrations/self-modification/setup.js +1 -1
- package/dist/src/setup/provider-settings.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +6 -2
- package/dist/src/shared/channel-audience.d.ts +0 -3
- package/dist/src/shared/channel-definition.d.ts +13 -4
- package/dist/src/shared/connection-types.d.ts +20 -7
- package/dist/src/shared/conversation-context.d.ts +62 -0
- package/dist/src/shared/conversation-context.js +1 -0
- package/dist/src/shared/forwarded-trace-policy.d.ts +2 -1
- package/dist/src/shared/forwarded-trace-policy.js +1 -1
- package/dist/src/shared/instrumentation-channel-kind.d.ts +3 -0
- package/dist/src/shared/instrumentation-channel-kind.js +1 -0
- package/dist/src/shared/instrumentation-content.d.ts +9 -4
- package/dist/src/shared/instrumentation-content.js +1 -1
- package/dist/src/shared/trace-policy.d.ts +7 -6
- package/dist/src/shared/trace-policy.js +1 -1
- package/dist/src/shared/validate-authorization.js +1 -1
- package/dist/src/shared/workflow-sandbox.d.ts +18 -12
- package/dist/src/shared/workflow-sandbox.js +1 -1
- package/dist/src/subagents/handle-dispatch.d.ts +2 -1
- package/dist/src/subagents/handle-dispatch.js +1 -1
- package/dist/src/subagents/remote-dispatch.js +1 -1
- package/dist/src/subagents/start-local.d.ts +1 -0
- package/dist/src/subagents/start-local.js +1 -1
- package/dist/src/subagents/start-remote.d.ts +2 -0
- package/dist/src/subagents/start-remote.js +1 -1
- package/dist/src/subagents/tool.d.ts +3 -0
- package/dist/src/subagents/tool.js +1 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tasks/session-index.d.ts +13 -0
- package/dist/src/tasks/session-index.js +1 -1
- package/dist/src/tools/auth.d.ts +5 -6
- package/dist/src/tools/provided/workflow.d.ts +12 -0
- package/dist/src/tools/provided/workflow.js +1 -0
- package/dist/src/tools/workflow-program-input.d.ts +6 -0
- package/dist/src/tools/workflow-program-input.js +1 -0
- package/dist/src/tracing/agent-invocation-coordinator.d.ts +3 -2
- package/dist/src/tracing/agent-invocation-coordinator.js +1 -1
- package/dist/src/tracing/agent-memory-instrumentation.d.ts +3 -1
- package/dist/src/tracing/agent-memory-instrumentation.js +1 -1
- package/dist/src/tracing/agent-otel-provider.d.ts +2 -0
- package/dist/src/tracing/agent-otel-provider.js +2 -2
- package/dist/src/tracing/agent-otel-runtime-context.d.ts +2 -1
- package/dist/src/tracing/agent-otel-runtime-context.js +1 -1
- package/dist/src/tracing/agent-otel-session-context.d.ts +2 -0
- package/dist/src/tracing/agent-otel-session-context.js +1 -1
- package/dist/src/tracing/agent-trace-context-codec.js +1 -1
- package/dist/src/tracing/agent-trace-context-store.js +1 -1
- package/dist/src/tracing/agent-trace-state.d.ts +2 -0
- package/dist/src/tracing/content-attributes.js +1 -1
- package/dist/src/tracing/install-instrumentation-runtime.js +1 -1
- package/dist/src/tracing/local-instrumentation-runtime.d.ts +1 -1
- package/dist/src/tracing/local-instrumentation-runtime.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/agent-config.md +1 -1
- package/docs/channels/chat-sdk.mdx +7 -0
- package/docs/channels/custom.mdx +32 -2
- package/docs/channels/eve.mdx +39 -0
- package/docs/channels/linear.mdx +7 -3
- package/docs/channels/overview.mdx +13 -0
- package/docs/channels/slack.mdx +41 -1
- package/docs/connections/mcp.mdx +18 -1
- package/docs/connections/openapi.mdx +8 -2
- package/docs/connections/overview.mdx +16 -13
- package/docs/getting-started.mdx +1 -1
- package/docs/guides/deployment/self-hosting.md +1 -1
- package/docs/guides/deployment/vercel.mdx +2 -2
- package/docs/guides/dev-tui.md +4 -4
- package/docs/guides/dynamic-capabilities.md +4 -4
- package/docs/guides/frontend/nextjs.mdx +12 -10
- package/docs/guides/hooks.md +1 -1
- package/docs/guides/instrumentation/meta.json +4 -0
- package/docs/guides/instrumentation/otel.mdx +149 -0
- package/docs/guides/instrumentation/overview.md +199 -0
- package/docs/guides/instrumentation/providers.mdx +169 -0
- package/docs/guides/meta.json +0 -1
- package/docs/guides/remote-agents.md +19 -21
- package/docs/install-integrations.mdx +3 -5
- package/docs/patterns/multi-tenant-auth.md +3 -3
- package/docs/reference/cli.md +22 -21
- package/docs/reference/typescript-api.md +9 -9
- package/docs/subagents/index.mdx +5 -3
- package/docs/tools/workflows.mdx +56 -1
- package/package.json +1 -1
- package/dist/src/cli/commands/registry-package.d.ts +0 -52
- package/dist/src/cli/commands/registry-package.js +0 -1
- package/dist/src/harness/workflow-continuation-security.d.ts +0 -5
- package/dist/src/harness/workflow-continuation-security.js +0 -1
- package/dist/src/harness/workflow-interrupt-state.d.ts +0 -20
- package/dist/src/harness/workflow-interrupt-state.js +0 -1
- package/dist/src/harness/workflow-lifecycle.d.ts +0 -32
- package/dist/src/harness/workflow-lifecycle.js +0 -1
- package/dist/src/harness/workflow-sandbox.d.ts +0 -28
- package/dist/src/harness/workflow-sandbox.js +0 -2
- package/dist/src/harness/workflow-subagent-limit.d.ts +0 -28
- package/dist/src/harness/workflow-subagent-limit.js +0 -1
- package/dist/src/harness/workflow-task-state.d.ts +0 -9
- package/dist/src/harness/workflow-task-state.js +0 -1
- package/dist/src/harness/workflow-tool-description.d.ts +0 -22
- package/dist/src/harness/workflow-tool-description.js +0 -37
- package/dist/src/tools/workflow.d.ts +0 -37
- package/dist/src/tools/workflow.js +0 -1
- package/docs/guides/instrumentation-providers.md +0 -247
- package/docs/guides/instrumentation.md +0 -453
|
@@ -1 +1 @@
|
|
|
1
|
-
import{callSlackApi}from"#public/channels/slack/api.js";const SLACK_ACTIVITY_PLAN_RENDERER_ID=`slack.experimental.plan.v1`;function createSlackPlanRenderer(e){return{id:SLACK_ACTIVITY_PLAN_RENDERER_ID,async dispose(){},async render({destination:t,snapshot:n,state:r}){let i=t.channelId,a=t.threadTs,o=t.teamId,s=t.triggeringUserId,c=t.installationTeamId;if(typeof i!=`string`||typeof a!=`string`||!a||typeof o!=`string`||typeof s!=`string`)return r;let l=isState(r)?r.streams:{},u={...l};for(let t of new Set(Object.values(n.work).map(e=>e.rootTurnId))){let r=project(n,t),d=l[t];if(!d){let t=await api(`chat.startStream`,{channel:i,thread_ts:a,recipient_team_id:o,recipient_user_id:s,task_display_mode:`plan`,chunks:[{type:`plan_update`,title:`Agent activity`},...r.parents.map(taskChunk)]},e,c);if(!t.ok||typeof t.ts!=`string`)throw Error(`Slack activity plan failed: ${t.error??`missing ts`}`);d={ts:t.ts,seen:Object.fromEntries(r.parents.map(e=>[e.id,e.phase])),stopped:!1}}if(d.stopped){u[t]=d;continue}let f=detailUpdates(r,d.seen);f.length&&await checked(`chat.appendStream`,{channel:i,ts:d.ts,chunks:f},e,c);let p=Object.fromEntries([...r.parents.map(e=>[e.id,e.phase]),...r.entities.map(e=>[e.id,entityVersion(e)])]);if(r.settled){await checked(`chat.stopStream`,{channel:i,ts:d.ts},e,c);let n=[{type:`plan`,title:`Agent activity`,tasks:r.parents.map(e=>({type:`task_card`,task_id:safeId(e.id),title:e.name,status:status(e.phase)}))}];await checked(`chat.update`,{channel:i,ts:d.ts,text:r.parents.map(e=>e.name).join(`, `),blocks:n},e,c),u[t]={ts:d.ts,seen:p,stopped:!0}}else u[t]={ts:d.ts,seen:p,stopped:!1}}return{streams:u}}}}function project(e,t){let n=Object.values(e.work).filter(e=>e.rootTurnId===t),r=n.filter(e=>e.kind===`root-turn`),i=new Set(r.map(e=>e.id)),a=n.filter(e=>e.parentId&&i.has(e.parentId));a.length||(a=r);let owner=e=>{let t=n.find(t=>t.id===e);for(;t?.parentId&&!a.some(e=>e.id===t.id);)t=n.find(e=>e.id===t.parentId);return t&&a.find(e=>e.id===t.id)},o=[];for(let e of n)if(!a.some(t=>t.id===e.id)&&!i.has(e.id)){let t=owner(e.id);t&&o.push({id:e.id,name:e.name??`Agent work`,phase:e.phase,parent:t.id})}for(let n of Object.values(e.actions).filter(e=>e.rootTurnId===t)){let e=owner(n.parentWorkId);e&&o.push({id:n.id,name:n.label??n.name,phase:n.phase,parent:e.id})}for(let n of Object.values(e.blockers).filter(e=>e.rootTurnId===t)){let e=owner(n.parentWorkId);e&&o.push({id:n.id,name:n.label??`Waiting`,phase:n.phase,parent:e.id})}let s=[...n,...Object.values(e.actions).filter(e=>e.rootTurnId===t),...Object.values(e.blockers).filter(e=>e.rootTurnId===t)].every(e=>e.phase!==`running`&&e.phase!==`blocked`);return{parents:a,entities:o,settled:s}}function detailUpdates(e,t){let n=e.parents.filter(e=>t[e.id]!==e.phase).map(taskChunk),r=e.entities.filter(e=>t[e.id]!==entityVersion(e)).map(t=>({type:`task_update`,id:safeId(t.parent),title:e.parents.find(e=>e.id===t.parent)?.name??`Agent work`,status:status(e.parents.find(e=>e.id===t.parent)?.phase??`running`),details:`${icon(t.phase)} ${t.name}\n`}));return[...n,...r]}function entityVersion(e){return`${e.phase}:${e.name}`}function taskChunk(e){return{type:`task_update`,id:safeId(e.id),title:e.kind===`root-turn`?`Agent turn`:e.name??`Agent work`,status:status(e.phase)}}function status(e){return e===`running`||e===`blocked`?`in_progress`:e===`completed`?`complete`:`error`}function icon(e){return e===`running`||e===`blocked`?`•`:e===`completed`?`✓`:e===`cancelled`?`–`:`✗`}function safeId(e){return e.replace(/[^a-zA-Z0-9_-]/g,`_`).slice(-200)}async function api(e,t,n,r){return callSlackApi({operation:e,body:t,botToken:n,context:{teamId:typeof r==`string`?r:void 0}})}async function checked(e,t,n,r){let i=await api(e,t,n,r);if(!i.ok)throw Error(`Slack ${e} failed: ${i.error??`unknown_error`}`)}function isState(e){return typeof e==`object`&&!!e&&typeof Reflect.get(e,`streams`)==`object`}export{SLACK_ACTIVITY_PLAN_RENDERER_ID,createSlackPlanRenderer};
|
|
1
|
+
import{callSlackApi}from"#public/channels/slack/api.js";const SLACK_ACTIVITY_PLAN_RENDERER_ID=`slack.experimental.plan.v1`;function createSlackPlanRenderer(e){return{id:SLACK_ACTIVITY_PLAN_RENDERER_ID,async dispose(){},async render({destination:t,snapshot:n,state:r}){let i=t.channelId,a=t.threadTs,o=t.teamId,s=t.triggeringUserId,c=t.installationTeamId;if(typeof i!=`string`||typeof a!=`string`||!a||typeof o!=`string`||typeof s!=`string`)return r;let l=isState(r)?r.streams:{},u={...l};for(let t of new Set(Object.values(n.work).map(e=>e.rootTurnId))){let r=project(n,t),d=l[t];if(!d){let t=await api(`chat.startStream`,{channel:i,thread_ts:a,recipient_team_id:o,recipient_user_id:s,task_display_mode:`plan`,chunks:[{type:`plan_update`,title:`Agent activity`},...r.parents.map(taskChunk)]},e,c);if(!t.ok||typeof t.ts!=`string`)throw Error(`Slack activity plan failed: ${t.error??`missing ts`}`);d={ts:t.ts,seen:Object.fromEntries(r.parents.map(e=>[e.id,e.phase])),stopped:!1}}if(d.stopped){u[t]=d;continue}let f=detailUpdates(r,d.seen);f.length&&await checked(`chat.appendStream`,{channel:i,ts:d.ts,chunks:f},e,c);let p=Object.fromEntries([...r.parents.map(e=>[e.id,e.phase]),...r.entities.map(e=>[e.id,entityVersion(e)])]);if(r.settled){await checked(`chat.stopStream`,{channel:i,ts:d.ts},e,c);let n=[{type:`plan`,title:`Agent activity`,tasks:r.parents.map(e=>({type:`task_card`,task_id:safeId(e.id),title:e.name,status:status(e.phase)}))}];await checked(`chat.update`,{channel:i,ts:d.ts,text:r.parents.map(e=>e.name).join(`, `),blocks:n},e,c),u[t]={ts:d.ts,seen:p,stopped:!0}}else u[t]={ts:d.ts,seen:p,stopped:!1}}return{streams:u}}}}function project(e,t){let n=Object.values(e.work).filter(e=>e.rootTurnId===t),r=n.filter(e=>e.kind===`root-turn`),i=new Set(r.map(e=>e.id)),a=n.filter(e=>e.parentId&&i.has(e.parentId));a.length||(a=r);let owner=e=>{let t=n.find(t=>t.id===e);for(;t?.parentId&&!a.some(e=>e.id===t.id);)t=n.find(e=>e.id===t.parentId);return t&&a.find(e=>e.id===t.id)},o=[];for(let e of n)if(!a.some(t=>t.id===e.id)&&!i.has(e.id)){let t=owner(e.id);t&&o.push({id:e.id,name:e.name??`Agent work`,phase:e.phase,parent:t.id})}for(let n of Object.values(e.actions).filter(e=>e.rootTurnId===t)){let e=owner(n.parentWorkId);e&&o.push({id:n.id,name:n.label??n.name,phase:n.phase,parent:e.id})}for(let n of Object.values(e.blockers).filter(e=>e.rootTurnId===t)){let e=owner(n.parentWorkId);e&&o.push({id:n.id,name:n.label??`Waiting`,phase:n.phase,parent:e.id})}let s=[...n,...Object.values(e.actions).filter(e=>e.rootTurnId===t),...Object.values(e.blockers).filter(e=>e.rootTurnId===t)].every(e=>e.phase!==`running`&&e.phase!==`blocked`);return{parents:a,entities:o,settled:s}}function detailUpdates(e,t){let n=e.parents.filter(e=>t[e.id]!==e.phase).map(taskChunk),r=e.entities.filter(e=>t[e.id]!==entityVersion(e)).map(t=>({type:`task_update`,id:safeId(t.parent),title:e.parents.find(e=>e.id===t.parent)?.name??`Agent work`,status:status(e.parents.find(e=>e.id===t.parent)?.phase??`running`),details:`${icon(t.phase)} ${t.name}\n`}));return[...n,...r]}function entityVersion(e){return`${e.phase}:${e.name}`}function taskChunk(e){return{type:`task_update`,id:safeId(e.id),title:e.kind===`root-turn`?`Agent turn`:e.label??e.name??`Agent work`,status:status(e.phase)}}function status(e){return e===`running`||e===`blocked`?`in_progress`:e===`completed`?`complete`:`error`}function icon(e){return e===`running`||e===`blocked`?`•`:e===`completed`?`✓`:e===`cancelled`?`–`:`✗`}function safeId(e){return e.replace(/[^a-zA-Z0-9_-]/g,`_`).slice(-200)}async function api(e,t,n,r){return callSlackApi({operation:e,body:t,botToken:n,context:{teamId:typeof r==`string`?r:void 0}})}async function checked(e,t,n,r){let i=await api(e,t,n,r);if(!i.ok)throw Error(`Slack ${e} failed: ${i.error??`unknown_error`}`)}function isState(e){return typeof e==`object`&&!!e&&typeof Reflect.get(e,`streams`)==`object`}export{SLACK_ACTIVITY_PLAN_RENDERER_ID,createSlackPlanRenderer};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SessionAuthContext } from "#channel/types.js";
|
|
2
2
|
import type { SlackMessage } from "#public/channels/slack/inbound.js";
|
|
3
|
-
import type { SlackApprovalChannelResolver,
|
|
3
|
+
import type { SlackApprovalChannelResolver, SlackChannelInternalEvents, SlackContext, SlackMentionResult } from "#public/channels/slack/slackChannel.js";
|
|
4
4
|
/**
|
|
5
5
|
* Workspace-scoped projection of the Slack actor that produced
|
|
6
6
|
* `message`, derived into a {@link SessionAuthContext}. Used by both
|
|
@@ -29,7 +29,7 @@ export declare function defaultOnDirectMessage(ctx: SlackContext, message: Slack
|
|
|
29
29
|
* Slack's 50-block message cap. Override by declaring
|
|
30
30
|
* `events["input.requested"]`.
|
|
31
31
|
*/
|
|
32
|
-
export declare function defaultInputRequestedHandler(approvalChannel?: SlackApprovalChannelResolver): NonNullable<
|
|
32
|
+
export declare function defaultInputRequestedHandler(approvalChannel?: SlackApprovalChannelResolver): NonNullable<SlackChannelInternalEvents["input.requested"]>;
|
|
33
33
|
/**
|
|
34
34
|
* Delivers a completed default Slack reply without sending content that
|
|
35
35
|
* exceeds Slack's native Markdown limit. Long replies stay intact as one
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* without taking a direct `ai` dependency.
|
|
6
6
|
*/
|
|
7
7
|
export type { ModelMessage } from "ai";
|
|
8
|
-
export { slackChannel, type SlackApiResponse, type SlackAuthorizationEventContext, type SlackAuthorizationRequiredHandler, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackEventRespondFn, type SlackEventRespondOptions, type SlackEventSendFn, type SlackEventSendOptions, type SlackHandle, type SlackInboundEventContext, type SlackInboundMessageContext, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInitialMessage, type SlackInputResponseContext, type SlackInputResponseResult, type SlackInputResponseSubmission, type SlackInstrumentationMetadata, type SlackInteractionAction, type SlackInteractionContext, type SlackSlashCommand, type SlackSlashCommandContext, type SlackShortcut, type SlackShortcutContext, type SlackShortcutMessage, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveTarget, type SlackSessionTarget, type SlackRespondOptions, type SlackSendOptions, type SlackSessionOperations, type SlackThread, type SlackWebhookVerifier, type SlackWorkspaceHandle, } from "#public/channels/slack/slackChannel.js";
|
|
8
|
+
export { slackChannel, type SlackApiResponse, type SlackAuthorizationEventContext, type SlackAuthorizationRequiredHandler, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackEventRespondFn, type SlackEventRespondOptions, type SlackEventSendFn, type SlackEventSendOptions, type SlackHandle, type SlackInboundEventContext, type SlackInboundMessageContext, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInitialMessage, type SlackInputRequestedDefaultDeliver, type SlackInputRequestedEvent, type SlackInputRequestedHandler, type SlackInputResponseContext, type SlackInputResponseResult, type SlackInputResponseSubmission, type SlackInstrumentationMetadata, type SlackInteractionAction, type SlackInteractionContext, type SlackSlashCommand, type SlackSlashCommandContext, type SlackShortcut, type SlackShortcutContext, type SlackShortcutMessage, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveTarget, type SlackSessionTarget, type SlackRespondOptions, type SlackSendOptions, type SlackSessionOperations, type SlackThread, type SlackWebhookVerifier, type SlackWorkspaceHandle, } from "#public/channels/slack/slackChannel.js";
|
|
9
9
|
export type { SlackAttachment, SlackAuthor, SlackEvent, SlackEventEnvelope, SlackInboundContext, SlackMessage, } from "#public/channels/slack/inbound.js";
|
|
10
10
|
export { callSlackApi, resolveSlackBotToken, slackContinuationToken, type SlackPostInput, type SlackPostedMessage, type SlackBotTokenContext, type SlackThreadMessage, type SlackUploadFilesOptions, type SlackUploadFilesResult, } from "#public/channels/slack/api.js";
|
|
11
11
|
export { defaultSlackAuth } from "#public/channels/slack/defaults.js";
|
|
@@ -53,6 +53,10 @@ export interface SlackEventContext extends SlackChannelContext, ChannelContinuat
|
|
|
53
53
|
export type { SlackApiResponse, SlackBotToken, SlackHandle, SlackThread, SlackWorkspaceHandle, } from "#public/channels/slack/api.js";
|
|
54
54
|
export type { SlackWebhookVerifier } from "#public/channels/slack/verify.js";
|
|
55
55
|
type SlackEventHandler<T extends UnstampedMessageStreamEvent["type"]> = (data: EventData<T>, channel: SlackEventContext, ctx: SessionContext) => void | Promise<void>;
|
|
56
|
+
export type SlackInputRequestedEvent = EventData<"input.requested">;
|
|
57
|
+
/** Bound access to eve's standard Slack input delivery. */
|
|
58
|
+
export type SlackInputRequestedDefaultDeliver = (data: SlackInputRequestedEvent) => Promise<void>;
|
|
59
|
+
export type SlackInputRequestedHandler = (data: SlackInputRequestedEvent, channel: SlackEventContext, ctx: SessionContext, defaultDeliver: SlackInputRequestedDefaultDeliver) => void | Promise<void>;
|
|
56
60
|
/**
|
|
57
61
|
* Delivery surface handed to `authorization.required` overrides. The
|
|
58
62
|
* connection challenge is a credential: anyone who completes the sign-in
|
|
@@ -154,7 +158,6 @@ export interface SlackChannelState {
|
|
|
154
158
|
* can attach extra span attributes.
|
|
155
159
|
*/
|
|
156
160
|
export interface SlackInstrumentationMetadata extends Record<string, unknown> {
|
|
157
|
-
readonly audience: ChannelAudience;
|
|
158
161
|
readonly channelId: string | null;
|
|
159
162
|
readonly teamId: string | null;
|
|
160
163
|
readonly threadTs: string | null;
|
|
@@ -447,7 +450,11 @@ export interface SlackChannelEvents {
|
|
|
447
450
|
readonly "message.appended"?: SlackEventHandler<"message.appended">;
|
|
448
451
|
readonly "reasoning.appended"?: SlackEventHandler<"reasoning.appended">;
|
|
449
452
|
readonly "reasoning.completed"?: SlackEventHandler<"reasoning.completed">;
|
|
450
|
-
|
|
453
|
+
/**
|
|
454
|
+
* An override may pass selected requests to `defaultDeliver()` while
|
|
455
|
+
* rendering the rest itself. The default delivery honors `approvalChannel`.
|
|
456
|
+
*/
|
|
457
|
+
readonly "input.requested"?: SlackInputRequestedHandler;
|
|
451
458
|
readonly "turn.failed"?: SlackEventHandler<"turn.failed">;
|
|
452
459
|
readonly "turn.completed"?: SlackEventHandler<"turn.completed">;
|
|
453
460
|
readonly "turn.cancelled"?: SlackEventHandler<"turn.cancelled">;
|
|
@@ -471,8 +478,9 @@ export interface SlackChannelEvents {
|
|
|
471
478
|
* factory adapts user overrides into this shape with
|
|
472
479
|
* {@link constrainAuthorizationRequired}.
|
|
473
480
|
*/
|
|
474
|
-
export interface SlackChannelInternalEvents extends Omit<SlackChannelEvents, "authorization.required"> {
|
|
481
|
+
export interface SlackChannelInternalEvents extends Omit<SlackChannelEvents, "authorization.required" | "input.requested"> {
|
|
475
482
|
readonly "authorization.required"?: SlackEventHandler<"authorization.required">;
|
|
483
|
+
readonly "input.requested"?: SlackEventHandler<"input.requested">;
|
|
476
484
|
}
|
|
477
485
|
export type SlackApprovalChannel = "direct-message" | "thread";
|
|
478
486
|
/** Input request passed to a {@link SlackApprovalChannelResolver}. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{setChannelActivityRenderers}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{normalizeChannelAudience}from"#shared/channel-audience.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,buildSlackWorkspaceHandle,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage,postCompletedSlackReply}from"#public/channels/slack/defaults.js";import{buildSlackActivityRenderers,hasSlackActivityStatus}from"#public/channels/slack/activity.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackEventReceivingBotUserId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{isPrivateSlackConversation}from"#public/channels/slack/privacy.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`),ignoreSlackActivityEvent=async()=>void 0,activityOwnedMessageCompleted=async(e,t)=>{t.state.pendingToolCallMessage=null,e.finishReason!==`tool-calls`&&e.message&&await postCompletedSlackReply(t,e.message)},clearSlackTurnState=async(e,t)=>{t.state.pendingToolCallMessage=null,t.state.lastReasoningTypingAtMs=null,t.state.lastReasoningTypingStatus=null};function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,installationTeamId:e.installationTeamId??void 0,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.continuation?.rekey(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),r=buildSlackActivityRenderers({botToken:e.credentials?.botToken,renderers:e.activity?.renderers??[]}),i=e.onInputResponse??defaultOnInputResponse,a=e.events?.[`authorization.required`],o=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],s=hasSlackActivityStatus(e.activity?.renderers),c=e.events?.[`turn.started`]??(s?clearSlackTurnState:defaultEvents[`turn.started`]),l=e.events?.[`reasoning.appended`]??(s?ignoreSlackActivityEvent:defaultEvents[`reasoning.appended`]),u=e.events?.[`actions.requested`]??(s?ignoreSlackActivityEvent:defaultEvents[`actions.requested`]),d=e.events?.[`message.completed`]??(s?activityOwnedMessageCompleted:defaultEvents[`message.completed`]),f={...defaultEvents,...e.events,async"approval.candidate"(e,t,n){let r=t.state.approvalResponderUsers?.[e.responderPrincipalId];if(e.outcome===`pending`&&r!==void 0&&(t.state.pendingApprovalCandidateUsers={...t.state.pendingApprovalCandidateUsers,[e.candidateId]:r}),await o(e,t,n),e.outcome!==`pending`){let n={...t.state.pendingApprovalCandidateUsers};delete n[e.candidateId],t.state.pendingApprovalCandidateUsers=n}},async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await c(e,t,n)},"reasoning.appended":l,"actions.requested":u,"message.completed":d,"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler(e.approvalChannel),"authorization.required":a===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(a)},p=new Set,m=defineChannel({kindHint:`slack`,turnPolicy:e.turnPolicy,state:{audience:`unknown`,channelId:null,threadTs:null,teamId:null,installationTeamId:null,triggeringUserId:null,triggeringMessageTs:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{},pendingApprovalCards:{},pendingApprovalCandidateUsers:{},approvalResponderUsers:{}},fetchFile:n,metadata(e){return{audience:e.audience??`unknown`,channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},deliver(e,t){let n=e.pendingApprovalCards;typeof n==`object`&&n&&(t.state.pendingApprovalCards={...t.state.pendingApprovalCards,...n});let r=e.state?.approvalResponderUsers;return typeof r==`object`&&r&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...r}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,r)=>{let{waitUntil:a}=r,{from:o,resolveSession:s}=r,c=await verifyInbound(n,e.credentials);return c===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(c,{from:o,resolveSession:s,waitUntil:a},{config:e,onInputResponse:i}):handleEventPost({body:c,from:o,resolveSession:s,waitUntil:a,config:e,uploadPolicy:t,handledEvents:p,headers:n.headers})})],receive(t,{from:n}){return receiveOnSlack(t,{from:n,credentials:e.credentials})},events:f});return setChannelActivityRenderers(m,{destination(e){let t=e;return{channelId:t?.channelId??null,installationTeamId:t?.installationTeamId??null,teamId:t?.teamId??null,threadTs:t?.threadTs??null,triggeringUserId:t?.triggeringUserId??null}},renderers:r}),m}function defaultOnInputResponse(e){return{auth:e.defaultAuth}}async function receiveOnSlack(e,t){let n=e.target,r=n.channelId;if(!r||typeof r!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let i=typeof n.threadTs==`string`?n.threadTs:``,a=typeof n.installationTeamId==`string`?n.installationTeamId:t.installationTeamId,o=n.initialMessage;if(o&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let s=i;if(o){let{thread:e}=buildSlackBinding({botToken:t.credentials?.botToken,channelId:r,threadTs:``,installationTeamId:a,teamId:t.teamId}),n={card:o.card};o.fallbackText!==void 0&&(n.fallbackText=o.fallbackText),s=(await e.post(n)).id}let c=s||crypto.randomUUID(),l=n.audience===void 0?void 0:normalizeChannelAudience(n.audience),u={channelId:r,installationTeamId:a??null,threadTs:s||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null};return l!==void 0&&(u.audience=l),t.from(slackContinuationToken(r,c)).send(e.message,{auth:e.auth,state:u,title:e.title})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(e){let{config:t}=e,n,r;try{n=parseSlackWebhookBody(e.body,{headers:e.headers}),r=parseSlackEventEnvelope(e.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(n.kind===`url_verification`)return new Response(n.challenge,{status:200,headers:{"content-type":`text/plain`}});if(r===null)return new Response(`ok`);let i=typeof r.api_app_id==`string`?r.api_app_id:void 0,a=slackEventBotUserId(r),o=slackEventReceivingBotUserId(r),s=slackEventInstallationTeamId(r),dispatch=null,c=null;if(n.kind===`app_mention`||n.kind===`direct_message`){let r=n.kind,l=slackMessageFromWebhookPayload(n);if(l!==null&&!isSelfAuthoredSlackMessage({appId:i,botUserId:a},l)){let dispatchMessageWith=n=>()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:n,installationTeamId:s,kind:r,message:l,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy}),n=(r===`app_mention`?t.onAppMention:t.onDirectMessage)??t.onMessage;n===void 0?c=dispatchMessageWith(r===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:n,installationTeamId:s,kind:r,message:l,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy})}}if(dispatch===null&&t.onMessage!==void 0){let n=parseMessageEvent(r);n!==null&&!isSelfAuthoredSlackMessage({appId:i,botUserId:a},n)&&(a===void 0||!n.text.includes(`<@${a}`))&&(dispatch=()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:t.onMessage,installationTeamId:s,kind:`channel_message`,message:n,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy}))}let l=t.onEvent;if(dispatch===null&&l!==void 0&&(dispatch=()=>dispatchSlackEvent({from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,envelope:r,handler:l,installationTeamId:s})),dispatch??=c,dispatch===null)return new Response(`ok`);let u=r.event_id;if(u){if(e.handledEvents.has(u))return log.warn(`received a duplicate event`,{event_id:u,event_time:r.event_time,retry_num:n.retry?.num??`(null)`,retry_reason:n.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(u,e.handledEvents)}return e.waitUntil(dispatch()),new Response(`ok`)}function isSelfAuthoredSlackMessage(e,t){return e.botUserId!==void 0&&t.author?.userId===e.botUserId||e.appId!==void 0&&t.raw.app_id===e.appId}async function dispatchSlackMessage(e){let t=slackContinuationToken(e.message.channelId,e.message.threadTs),{thread:n,slack:r}=buildSlackBinding({appId:e.appId,botToken:e.credentials?.botToken,botUserId:e.botUserId,channelId:e.message.channelId,threadTs:e.message.threadTs,installationTeamId:e.installationTeamId,teamId:e.message.teamId}),i=e.message.author,a={audience:`unknown`,channelId:e.message.channelId,installationTeamId:e.installationTeamId??null,teamId:e.message.teamId??null,threadTs:e.message.threadTs,triggeringMessageTs:e.message.ts,triggeringUserId:i?.userId??null},o=bindSlackSessionOperations({address:t,defaultAuth:i===void 0?null:buildSlackAuthContext({channelId:e.message.channelId,fullName:i.fullName,isBot:i.isBot,teamId:e.message.teamId,threadTs:e.message.threadTs,userId:i.userId,userName:i.userName}),from:e.from,resolveSession:e.resolveSession,state:a}),s,isDMOrPrivateChannel=()=>s??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:r.request}),c=e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),l={...o,isBotMentioned:()=>c,isDMOrPrivateChannel,isSubscribed:async()=>await o.resolveSession()!==void 0,slack:r,thread:n},u;try{u=await e.handler(l,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(u==null)return;let d=await isDMOrPrivateChannel();a.audience=e.kind===`direct_message`||d?`private`:`public`,await deliverSlackMessage({botUserId:e.receivingBotUserId,credentials:e.credentials,kind:e.kind,isPrivateConversation:d,isMentioned:c,message:e.message,result:u,sessionOperations:o,thread:n,threadContext:e.threadContext,uploadPolicy:e.uploadPolicy})}async function dispatchSlackEvent(e){let t=e.envelope.event.team_id,n=typeof t==`string`?t:typeof e.envelope.team_id==`string`?e.envelope.team_id:void 0,r=[],sourceFor=t=>e.from(slackContinuationToken(t.channelId,t.threadTs)),i={cancel:({target:e,turnId:t})=>sourceFor(e).cancel({turnId:t}),clear:({target:e})=>sourceFor(e).clear(),compact:({target:e})=>sourceFor(e).compact(),envelope:e.envelope,reset:({reason:e,target:t})=>sourceFor(t).reset({reason:e}),resolveSession:({target:t})=>e.resolveSession(slackContinuationToken(t.channelId,t.threadTs)),respond:(e,{auth:t,target:n})=>sourceFor(n).respond(e,{auth:t}),send:(t,{auth:r,target:i,title:a})=>receiveOnSlack({auth:r,message:t,target:i,title:a},{from:e.from,credentials:e.credentials,installationTeamId:e.installationTeamId,teamId:n,...typeof e.envelope.event.user==`string`?{triggeringUserId:e.envelope.event.user}:{}}),slack:buildSlackWorkspaceHandle({botToken:e.credentials?.botToken,installationTeamId:e.installationTeamId,teamId:n}),waitUntil(e){r.push(e)}};try{await e.handler(i,e.envelope.event)}catch(t){logError(log,`event handler failed`,t,{eventId:e.envelope.event_id,eventType:e.envelope.event.type})}await Promise.allSettled(r)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function deliverSlackMessage(e){let{message:t,thread:n}=e;try{let r=e.threadContext===void 0?[]:await loadThreadContextMessages(n,t,e.threadContext),i=formatSlackThreadContext(r),a=await collectInboundFileParts({mention:t,thread:n,policy:e.uploadPolicy}),o={botUserId:e.botUserId,channelId:t.channelId,fullName:t.author?.fullName,isMentioned:e.isMentioned,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},s=formatSlackInboundMessage(o,t),c=buildSlackTurnMessage(i===void 0?s:`${i}\n\n${s}`,a),l=e.result.context??[],u=e.isPrivateConversation?`Private message`:e.result.title??t.markdown,d=l.length===0?{auth:e.result.auth,title:u}:{auth:e.result.auth,context:l,title:u};await e.sessionOperations.send(c,d)}catch(n){logError(log,`${e.kind} delivery failed`,n,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
|
|
1
|
+
import{markEventHandled}from"./utils.js";import{createLogger,logError}from"#internal/logging.js";import{normalizeChannelAudience}from"#shared/channel-audience.js";import{setChannelActivityRenderers}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{buildSlackBinding,buildSlackWorkspaceHandle,slackContinuationToken}from"#public/channels/slack/api.js";import{buildSlackAuthContext,slackUserIdFromAuthContext}from"#public/channels/slack/auth.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage,postCompletedSlackReply}from"#public/channels/slack/defaults.js";import{buildSlackActivityRenderers,hasSlackActivityStatus}from"#public/channels/slack/activity.js";import{loadThreadContextMessages}from"#public/channels/slack/thread.js";import{parseSlackWebhookBody}from"#compiled/@chat-adapter/slack/webhook.js";import{bindSlackSessionOperations}from"#public/channels/slack/session-operations.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseMessageEvent,parseSlackEventEnvelope,slackEventBotUserId,slackEventInstallationTeamId,slackEventReceivingBotUserId,slackMessageFromWebhookPayload}from"#public/channels/slack/inbound.js";import{formatSlackInboundMessage,formatSlackThreadContext}from"#public/channels/slack/model-context.js";import{isPrivateSlackConversation}from"#public/channels/slack/privacy.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest as verifySlackRequest$1}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`),ignoreSlackActivityEvent=async()=>void 0,activityOwnedMessageCompleted=async(e,t)=>{t.state.pendingToolCallMessage=null,e.finishReason!==`tool-calls`&&e.message&&await postCompletedSlackReply(t,e.message)},clearSlackTurnState=async(e,t)=>{t.state.pendingToolCallMessage=null,t.state.lastReasoningTypingAtMs=null,t.state.lastReasoningTypingStatus=null};function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,installationTeamId:e.installationTeamId??void 0,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.continuation?.rekey(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=createSlackFetchFile({botToken:e.credentials?.botToken}),r=buildSlackActivityRenderers({botToken:e.credentials?.botToken,renderers:e.activity?.renderers??[]}),i=e.onInputResponse??defaultOnInputResponse,a=e.events?.[`authorization.required`],o=e.events?.[`approval.candidate`]??defaultEvents[`approval.candidate`],s=hasSlackActivityStatus(e.activity?.renderers),c=e.events?.[`turn.started`]??(s?clearSlackTurnState:defaultEvents[`turn.started`]),l=e.events?.[`reasoning.appended`]??(s?ignoreSlackActivityEvent:defaultEvents[`reasoning.appended`]),u=e.events?.[`actions.requested`]??(s?ignoreSlackActivityEvent:defaultEvents[`actions.requested`]),d=e.events?.[`message.completed`]??(s?activityOwnedMessageCompleted:defaultEvents[`message.completed`]),f=defaultInputRequestedHandler(e.approvalChannel),p=e.events?.[`input.requested`],m=p===void 0?f:async(e,t,n)=>p(e,t,n,async e=>{await f(e,t,n)}),h={...defaultEvents,...e.events,async"approval.candidate"(e,t,n){let r=t.state.approvalResponderUsers?.[e.responderPrincipalId];if(e.outcome===`pending`&&r!==void 0&&(t.state.pendingApprovalCandidateUsers={...t.state.pendingApprovalCandidateUsers,[e.candidateId]:r}),await o(e,t,n),e.outcome!==`pending`){let n={...t.state.pendingApprovalCandidateUsers};delete n[e.candidateId],t.state.pendingApprovalCandidateUsers=n}},async"turn.started"(e,t,n){let r=slackUserIdFromAuthContext(n.session.auth.current);r!==void 0&&(t.state.triggeringUserId=r),await c(e,t,n)},"reasoning.appended":l,"actions.requested":u,"message.completed":d,"input.requested":m,"authorization.required":a===void 0?defaultEvents[`authorization.required`]:constrainAuthorizationRequired(a)},g=new Set,_=defineChannel({kindHint:`slack`,turnPolicy:e.turnPolicy,state:{channelId:null,threadTs:null,teamId:null,installationTeamId:null,triggeringUserId:null,triggeringMessageTs:null,pendingToolCallMessage:null,lastReasoningTypingAtMs:null,lastReasoningTypingStatus:null,pendingAuthMessageTs:{},pendingApprovalCards:{},pendingApprovalCandidateUsers:{},approvalResponderUsers:{}},fetchFile:n,metadata(e){return{channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},audience:({state:e})=>e.audience??`unknown`,context(t,n){return rebuildSlackContext(t,n,e.credentials)},deliver(e,t){let n=e.pendingApprovalCards;typeof n==`object`&&n&&(t.state.pendingApprovalCards={...t.state.pendingApprovalCards,...n});let r=e.state?.approvalResponderUsers;return typeof r==`object`&&r&&(t.state.approvalResponderUsers={...t.state.approvalResponderUsers,...r}),defaultDeliverResult(e)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,r)=>{let{waitUntil:a}=r,{from:o,resolveSession:s}=r,c=await verifyInbound(n,e.credentials);return c===null?new Response(`unauthorized`,{status:401}):shouldDropSlackHttpTimeoutRetry(n.headers)?new Response(`ok`):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(c,{from:o,resolveSession:s,waitUntil:a},{config:e,onInputResponse:i}):handleEventPost({body:c,from:o,resolveSession:s,waitUntil:a,config:e,uploadPolicy:t,handledEvents:g,headers:n.headers})})],receive(t,{from:n}){return receiveOnSlack(t,{from:n,credentials:e.credentials})},events:h});return setChannelActivityRenderers(_,{destination(e){let t=e;return{channelId:t?.channelId??null,installationTeamId:t?.installationTeamId??null,teamId:t?.teamId??null,threadTs:t?.threadTs??null,triggeringUserId:t?.triggeringUserId??null}},renderers:r}),_}function defaultOnInputResponse(e){return{auth:e.defaultAuth}}async function receiveOnSlack(e,t){let n=e.target,r=n.channelId;if(!r||typeof r!=`string`)throw Error(`slackChannel().receive requires target.channelId.`);let i=typeof n.threadTs==`string`?n.threadTs:``,a=typeof n.installationTeamId==`string`?n.installationTeamId:t.installationTeamId,o=n.initialMessage;if(o&&i.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let s=i;if(o){let{thread:e}=buildSlackBinding({botToken:t.credentials?.botToken,channelId:r,threadTs:``,installationTeamId:a,teamId:t.teamId}),n={card:o.card};o.fallbackText!==void 0&&(n.fallbackText=o.fallbackText),s=(await e.post(n)).id}let c=s||crypto.randomUUID(),l=n.audience===void 0?void 0:normalizeChannelAudience(n.audience),u={channelId:r,installationTeamId:a??null,threadTs:s||null,teamId:t.teamId??null,triggeringUserId:t.triggeringUserId??null};return l!==void 0&&(u.audience=l),t.from(slackContinuationToken(r,c)).send(e.message,{auth:e.auth,state:u,title:e.title})}function constrainAuthorizationRequired(e){return(t,n,r)=>e(t,{postEphemeral:(e,t)=>n.thread.postEphemeral(e,t),postDirectMessage:(e,t)=>n.thread.postDirectMessage(e,t),state:n.state},r)}function shouldDropSlackHttpTimeoutRetry(e){return Number(e.get(`x-slack-retry-num`)??`0`)>=1&&e.get(`x-slack-retry-reason`)===`http_timeout`}async function handleEventPost(e){let{config:t}=e,n,r;try{n=parseSlackWebhookBody(e.body,{headers:e.headers}),r=parseSlackEventEnvelope(e.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(n.kind===`url_verification`)return new Response(n.challenge,{status:200,headers:{"content-type":`text/plain`}});if(r===null)return new Response(`ok`);let i=typeof r.api_app_id==`string`?r.api_app_id:void 0,a=slackEventBotUserId(r),o=slackEventReceivingBotUserId(r),s=slackEventInstallationTeamId(r),dispatch=null,c=null;if(n.kind===`app_mention`||n.kind===`direct_message`){let r=n.kind,l=slackMessageFromWebhookPayload(n);if(l!==null&&!isSelfAuthoredSlackMessage({appId:i,botUserId:a},l)){let dispatchMessageWith=n=>()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:n,installationTeamId:s,kind:r,message:l,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy}),n=(r===`app_mention`?t.onAppMention:t.onDirectMessage)??t.onMessage;n===void 0?c=dispatchMessageWith(r===`app_mention`?defaultOnAppMention:defaultOnDirectMessage):dispatch=()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:n,installationTeamId:s,kind:r,message:l,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy})}}if(dispatch===null&&t.onMessage!==void 0){let n=parseMessageEvent(r);n!==null&&!isSelfAuthoredSlackMessage({appId:i,botUserId:a},n)&&(a===void 0||!n.text.includes(`<@${a}`))&&(dispatch=()=>dispatchSlackMessage({appId:i,botUserId:a,receivingBotUserId:o,from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,handler:t.onMessage,installationTeamId:s,kind:`channel_message`,message:n,threadContext:t.threadContext,uploadPolicy:e.uploadPolicy}))}let l=t.onEvent;if(dispatch===null&&l!==void 0&&(dispatch=()=>dispatchSlackEvent({from:e.from,resolveSession:e.resolveSession,credentials:t.credentials,envelope:r,handler:l,installationTeamId:s})),dispatch??=c,dispatch===null)return new Response(`ok`);let u=r.event_id;if(u){if(e.handledEvents.has(u))return log.warn(`received a duplicate event`,{event_id:u,event_time:r.event_time,retry_num:n.retry?.num??`(null)`,retry_reason:n.retry?.reason??`(null)`}),new Response(`ok`);markEventHandled(u,e.handledEvents)}return e.waitUntil(dispatch()),new Response(`ok`)}function isSelfAuthoredSlackMessage(e,t){return e.botUserId!==void 0&&t.author?.userId===e.botUserId||e.appId!==void 0&&t.raw.app_id===e.appId}async function dispatchSlackMessage(e){let t=slackContinuationToken(e.message.channelId,e.message.threadTs),{thread:n,slack:r}=buildSlackBinding({appId:e.appId,botToken:e.credentials?.botToken,botUserId:e.botUserId,channelId:e.message.channelId,threadTs:e.message.threadTs,installationTeamId:e.installationTeamId,teamId:e.message.teamId}),i=e.message.author,a={channelId:e.message.channelId,installationTeamId:e.installationTeamId??null,teamId:e.message.teamId??null,threadTs:e.message.threadTs,triggeringMessageTs:e.message.ts,triggeringUserId:i?.userId??null},o=bindSlackSessionOperations({address:t,defaultAuth:i===void 0?null:buildSlackAuthContext({channelId:e.message.channelId,fullName:i.fullName,isBot:i.isBot,teamId:e.message.teamId,threadTs:e.message.threadTs,userId:i.userId,userName:i.userName}),from:e.from,resolveSession:e.resolveSession,state:a}),s,isDMOrPrivateChannel=()=>s??=isPrivateSlackConversation({channelId:e.message.channelId,raw:e.message.raw,request:r.request}),c=e.kind===`app_mention`||e.botUserId!==void 0&&e.message.text.includes(`<@${e.botUserId}`),l={...o,isBotMentioned:()=>c,isDMOrPrivateChannel,isSubscribed:async()=>await o.resolveSession()!==void 0,slack:r,thread:n},u;try{u=await e.handler(l,e.message)}catch(t){logError(log,`${e.kind} handler failed`,t,{channelId:e.message.channelId});return}if(u==null)return;let d=await isDMOrPrivateChannel();a.audience=e.kind===`direct_message`||d?`private`:`public`,await deliverSlackMessage({botUserId:e.receivingBotUserId,credentials:e.credentials,kind:e.kind,isPrivateConversation:d,isMentioned:c,message:e.message,result:u,sessionOperations:o,thread:n,threadContext:e.threadContext,uploadPolicy:e.uploadPolicy})}async function dispatchSlackEvent(e){let t=e.envelope.event.team_id,n=typeof t==`string`?t:typeof e.envelope.team_id==`string`?e.envelope.team_id:void 0,r=[],sourceFor=t=>e.from(slackContinuationToken(t.channelId,t.threadTs)),i={cancel:({target:e,turnId:t})=>sourceFor(e).cancel({turnId:t}),clear:({target:e})=>sourceFor(e).clear(),compact:({target:e})=>sourceFor(e).compact(),envelope:e.envelope,reset:({reason:e,target:t})=>sourceFor(t).reset({reason:e}),resolveSession:({target:t})=>e.resolveSession(slackContinuationToken(t.channelId,t.threadTs)),respond:(e,{auth:t,target:n})=>sourceFor(n).respond(e,{auth:t}),send:(t,{auth:r,target:i,title:a})=>receiveOnSlack({auth:r,message:t,target:i,title:a},{from:e.from,credentials:e.credentials,installationTeamId:e.installationTeamId,teamId:n,...typeof e.envelope.event.user==`string`?{triggeringUserId:e.envelope.event.user}:{}}),slack:buildSlackWorkspaceHandle({botToken:e.credentials?.botToken,installationTeamId:e.installationTeamId,teamId:n}),waitUntil(e){r.push(e)}};try{await e.handler(i,e.envelope.event)}catch(t){logError(log,`event handler failed`,t,{eventId:e.envelope.event_id,eventType:e.envelope.event.type})}await Promise.allSettled(r)}async function verifyInbound(e,t){try{return await verifySlackRequest$1(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function deliverSlackMessage(e){let{message:t,thread:n}=e;try{let r=e.threadContext===void 0?[]:await loadThreadContextMessages(n,t,e.threadContext),i=formatSlackThreadContext(r),a=await collectInboundFileParts({mention:t,thread:n,policy:e.uploadPolicy}),o={botUserId:e.botUserId,channelId:t.channelId,fullName:t.author?.fullName,isMentioned:e.isMentioned,teamId:t.teamId,threadTs:t.threadTs,userId:t.author?.userId??``,userName:t.author?.userName},s=formatSlackInboundMessage(o,t),c=buildSlackTurnMessage(i===void 0?s:`${i}\n\n${s}`,a),l=e.result.context??[],u=e.isPrivateConversation?`Private message`:e.result.title??t.markdown,d=l.length===0?{auth:e.result.auth,title:u}:{auth:e.result.auth,context:l,title:u};await e.sessionOperations.send(c,d)}catch(n){logError(log,`${e.kind} delivery failed`,n,{channelId:t.channelId})}}export{constrainAuthorizationRequired,slackChannel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{isObject}from"#shared/guards.js";import{parseInputResponse}from"#shared/input.js";import{parseJsonObject}from"#shared/json.js";import{TEAMS_ADAPTIVE_CARD_ACTION_LIMIT,TEAMS_ADAPTIVE_CARD_ACTION_TITLE_MAX_LENGTH,TEAMS_ADAPTIVE_CARD_CHOICE_TITLE_MAX_LENGTH,TEAMS_ADAPTIVE_CARD_TEXT_MAX_LENGTH}from"#public/channels/teams/limits.js";const TEAMS_ADAPTIVE_CARD_CONTENT_TYPE=`application/vnd.microsoft.card.adaptive`,TEAMS_HITL_DATA_KEY=`eve_input`,TEAMS_HITL_CHOICE_INPUT_ID=`eve_option`,TEAMS_HITL_FREEFORM_INPUT_ID=`eve_freeform_text`,TEAMS_HITL_REQUEST_KIND_KEY=`kind`,TEAMS_HITL_PROMPT_KEY=`prompt`;function renderInputRequestMessage(e,t={}){let n=formatApprovalInput(e),r=n?truncate(n,TEAMS_ADAPTIVE_CARD_TEXT_MAX_LENGTH):null;return{attachments:[renderInputRequestAttachment(e,t)],text:r?`${e.prompt}\n\nTool input:\n${r}`:e.prompt}}function renderInputRequestAttachment(e,t={}){let n=formatApprovalInput(e),r={$schema:`http://adaptivecards.io/schemas/adaptive-card.json`,actions:renderActions(e,t.replyToActivityId),body:[{text:truncate(e.prompt,TEAMS_ADAPTIVE_CARD_TEXT_MAX_LENGTH),type:`TextBlock`,wrap:!0},...n?[{fontType:`Monospace`,text:truncate(n,TEAMS_ADAPTIVE_CARD_TEXT_MAX_LENGTH),type:`TextBlock`,wrap:!0}]:[],...renderInputs(e)],type:`AdaptiveCard`,version:t.adaptiveCardVersion??`1.5`};return{content:parseJsonObject(r),contentType:TEAMS_ADAPTIVE_CARD_CONTENT_TYPE}}function renderAnsweredInputRequestMessage(e){let t=e.label?`Answered: ${e.label}`:`Answered`,n={attachments:[{content:parseJsonObject({$schema:`http://adaptivecards.io/schemas/adaptive-card.json`,body:[{text:truncate(e.prompt,TEAMS_ADAPTIVE_CARD_TEXT_MAX_LENGTH),type:`TextBlock`,wrap:!0},{color:`good`,text:t,type:`TextBlock`,wrap:!0}],type:`AdaptiveCard`,version:`1.5`}),contentType:TEAMS_ADAPTIVE_CARD_CONTENT_TYPE}]};return e.includeText===!1?n:{...n,text:t}}function isTeamsInputResponseActivity(e){return deriveTeamsInputResponses(e).length>0}function isTeamsToolApprovalResponseActivity(e){let t=readActivityValue(e);return(t&&readHitlPayload(t))?.[TEAMS_HITL_REQUEST_KIND_KEY]===`tool-approval`}function readTeamsToolApprovalPrompt(e){let t=readActivityValue(e),n=(t&&readHitlPayload(t))?.[TEAMS_HITL_PROMPT_KEY];return typeof n==`string`&&n.length>0?n:void 0}function deriveTeamsInputResponses(e){let t=readActivityValue(e);if(!t)return[];let n=readHitlPayload(t);if(!n)return[];let r=typeof n.requestId==`string`?n.requestId:``;if(!r)return[];let i=typeof n.optionId==`string`?n.optionId:typeof t.eve_option==`string`?t[TEAMS_HITL_CHOICE_INPUT_ID]:void 0,a=typeof t.eve_freeform_text==`string`?t[TEAMS_HITL_FREEFORM_INPUT_ID]:void 0;return i===void 0?a===void 0?[parseInputResponse({requestId:r})]:[parseInputResponse({requestId:r,text:a})]:[parseInputResponse({optionId:i,requestId:r})]}function readTeamsInputReplyToActivityId(e){let t=readActivityValue(e);if(!t)return null;let n=readHitlPayload(t);return n&&typeof n.replyToActivityId==`string`?n.replyToActivityId:null}function teamsInvokeResponse(e={}){return{statusCode:e.statusCode??200,type:`application/vnd.microsoft.activity.message`,value:e.message??`Answer received.`}}function renderInputs(e){return e.display===`select`&&e.options&&e.options.length>0?[{choices:e.options.map(e=>({title:truncate(e.label,TEAMS_ADAPTIVE_CARD_CHOICE_TITLE_MAX_LENGTH),value:e.id})),id:TEAMS_HITL_CHOICE_INPUT_ID,isMultiSelect:!1,style:`compact`,type:`Input.ChoiceSet`}]:e.allowFreeform===!0||!e.options||e.options.length===0?[{id:TEAMS_HITL_FREEFORM_INPUT_ID,isMultiline:!0,placeholder:`Type your answer`,type:`Input.Text`}]:[]}function renderActions(e,t){let payload=n=>{let r={requestId:e.requestId};return e.kind===`tool-approval`&&(r[TEAMS_HITL_REQUEST_KIND_KEY]=`tool-approval`,r[TEAMS_HITL_PROMPT_KEY]=e.prompt),t!==void 0&&(r.replyToActivityId=t),n!==void 0&&(r.optionId=n),r},n=e.options;return n&&n.length>0&&e.display!==`select`?n.slice(0,TEAMS_ADAPTIVE_CARD_ACTION_LIMIT).map(e=>({data:{[TEAMS_HITL_DATA_KEY]:payload(e.id)},title:truncate(e.label,TEAMS_ADAPTIVE_CARD_ACTION_TITLE_MAX_LENGTH),type:`Action.Submit`})):[{data:{[TEAMS_HITL_DATA_KEY]:payload()},title:`Submit`,type:`Action.Submit`}]}function readActivityValue(e){return e.type===`message`?readMessageValue(e):e.type===`invoke`?readInvokeValue(e):null}function readMessageValue(e){return e.value??null}function readInvokeValue(e){let t=e.value;if(!t)return null;let n=isObject(t.action)?t.action:null;return(n&&isObject(n.data)?n.data:null)??t}function readHitlPayload(e){let t=e[TEAMS_HITL_DATA_KEY];if(isObject(t))return t;let n=isObject(e.action)?e.action:null,r=(n&&isObject(n.data)?n.data:null)?.[TEAMS_HITL_DATA_KEY];return isObject(r)?r:null}function formatApprovalInput(e){if(e.kind!==`tool-approval`)return null;let t=JSON.stringify(e.action.input,null,2);return t===`{}`?null:t}function truncate(e,t){if(e.length<=t)return e;let n=Math.max(0,t-3);return`${e.slice(0,n).trimEnd()}...`}export{TEAMS_ADAPTIVE_CARD_CONTENT_TYPE,TEAMS_HITL_CHOICE_INPUT_ID,TEAMS_HITL_DATA_KEY,TEAMS_HITL_FREEFORM_INPUT_ID,TEAMS_HITL_PROMPT_KEY,TEAMS_HITL_REQUEST_KIND_KEY,deriveTeamsInputResponses,isTeamsInputResponseActivity,isTeamsToolApprovalResponseActivity,readTeamsInputReplyToActivityId,readTeamsToolApprovalPrompt,renderAnsweredInputRequestMessage,renderInputRequestAttachment,renderInputRequestMessage,teamsInvokeResponse};
|
|
@@ -6,7 +6,6 @@ export type { ModelMessage } from "ai";
|
|
|
6
6
|
* null when the inbound activity did not include it.
|
|
7
7
|
*/
|
|
8
8
|
export interface TeamsInstrumentationMetadata extends Record<string, unknown> {
|
|
9
|
-
readonly audience: import("#shared/channel-audience.js").ChannelAudience;
|
|
10
9
|
readonly channelId: string | null;
|
|
11
10
|
readonly conversationType: string | null;
|
|
12
11
|
readonly teamId: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger,logError}from"#internal/logging.js";import{defaultDeliverResult}from"#channel/adapter.js";import{readNonEmptyString}from"#shared/guards.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{callTeamsConnectorApi,normalizeTeamsContinuationAddress,normalizeTeamsPostInput,replyToTeamsActivity,sendTeamsActivity,teamsContinuationToken,triggerTeamsTypingIndicator,updateTeamsActivity}from"#public/channels/teams/api.js";import{deriveTeamsInputResponses,isTeamsInputResponseActivity,isTeamsToolApprovalResponseActivity,readTeamsInputReplyToActivityId,readTeamsToolApprovalPrompt,teamsInvokeResponse}from"#public/channels/teams/hitl.js";import{formatTeamsContextBlock,parseTeamsActivity,teamsThreadRootActivityId}from"#public/channels/teams/inbound.js";import{buildTeamsTurnMessage,collectTeamsFileParts,createTeamsFetchFile,normalizeTeamsFilesPolicy}from"#public/channels/teams/attachments.js";import{defaultEvents,defaultOnMessage,defaultTeamsAuth,teamsMentionUser}from"#public/channels/teams/defaults.js";import{verifyTeamsRequest}from"#public/channels/teams/verify.js";const log=createLogger(`teams.channel`);function teamsChannel(e={}){let t=normalizeTeamsFilesPolicy(e.files),n=e.onMessage??defaultOnMessage,r=e.onInputResponse??(e.onMessage===void 0?defaultOnInputResponse:rejectInput),i={...defaultEvents,...e.events};return defineChannel({kindHint:`teams`,turnPolicy:e.turnPolicy,state:initialTeamsState(),fetchFile:createTeamsFetchFile(t,{...e.api,credentials:e.credentials}),metadata:e=>({audience:teamsAudience(e.conversationType),channelId:e.channelId,conversationType:e.conversationType,teamId:e.teamId}),context(t,n){return rebuildTeamsContext(t,n,e)},deliver(e,t){let n=e.state,r=n?.pendingApprovalCards;r!==void 0&&(t.state.pendingApprovalCards={...r,...t.state.pendingApprovalCards});let i=n?.approvalResponderAccounts;return i!==void 0&&(t.state.approvalResponderAccounts={...t.state.approvalResponderAccounts,...i}),defaultDeliverResult(e)},routes:[POST(e.route??`/eve/v1/teams`,async(i,{from:a,resolveSession:o,waitUntil:s})=>{let c=await verifyInbound(i,e.credentials);if(c===null)return new Response(`unauthorized`,{status:401});let l;try{l=parseJsonObject(JSON.parse(c))}catch(e){return log.warn(`inbound Teams body is not valid JSON`,{error:e}),teamsOk()}let u=parseTeamsActivity(l);return u===null?teamsOk():u.type===`message`?(s(isTeamsInputResponseActivity(u)?dispatchInputResponses({activity:u,config:e,from:a,onInputResponse:r}):dispatchMessage({activity:u,config:e,filesPolicy:t,onMessage:n,from:a,resolveSession:o})),teamsOk()):u.type===`invoke`?handleInvoke({activity:u,config:e,onInputResponse:r,waitUntil:s,from:a}):teamsOk()})],async receive(t,{from:n}){let r=t.target,i=readNonEmptyString(r.serviceUrl),a=readNonEmptyString(r.conversationId);if(!i||!a)throw Error(`teamsChannel().receive requires target.serviceUrl and target.conversationId.`);let o=readNonEmptyString(r.conversationType)??null,s=readNonEmptyString(r.replyToActivityId)??null,c=r.initialMessage;if(c!==void 0&&s!==null)throw Error("teamsChannel().receive: `replyToActivityId` and `initialMessage` are mutually exclusive.");let l={...initialTeamsState(),channelId:readNonEmptyString(r.channelId)??null,conversationId:a,conversationType:o,replyToActivityId:s,serviceUrl:i,teamId:readNonEmptyString(r.teamId)??null,tenantId:readNonEmptyString(r.tenantId)??null};if(c!==void 0){let t=await buildTeamsBinding({config:e,state:l}).thread.post(c);o!==`personal`&&t.id&&(s=t.id,l.replyToActivityId=t.id)}return n(teamsContinuationToken({conversationId:a,replyToActivityId:s,tenantId:l.tenantId})).send(t.message,{auth:t.auth,state:l})},events:i})}function teamsAudience(e){return e===`personal`||e===`groupChat`?`private`:`unknown`}function rebuildTeamsContext(e,t,n){return{...buildTeamsBinding({config:n,session:t,state:e}),adaptiveCardVersion:n.adaptiveCardVersion??`1.5`,state:e}}function buildTeamsBinding(e){let t=buildTeamsHandle(e);return{teams:t,thread:{mentionUser:teamsMentionUser,post(e){return t.sendActivity(e)},async startTyping(){try{await t.startTyping()}catch(e){logError(log,`Teams typing indicator failed — swallowed`,e)}},update(e,n){return t.updateActivity(e,n)}}}}function buildTeamsHandle(e){let t=e.state,n=e.config.api,r=e.config.credentials;function requireAddress(){let e=t.conversationId??``,n=t.serviceUrl??``;if(!e||!n)throw Error(`teamsChannel: missing serviceUrl or conversationId for outbound message.`);return{conversationId:e,serviceUrl:n}}function anchor(n){if(!n.id||t.replyToActivityId||t.conversationType===`personal`)return;t.replyToActivityId=n.id;let r=t.conversationId;r&&e.session?.continuation?.rekey(teamsContinuationToken({conversationId:r,replyToActivityId:n.id,tenantId:t.tenantId}))}async function send(e){let i=requireAddress(),a=buildOutboundActivity(t,e),o=t.replyToActivityId===null?await sendTeamsActivity({...n,body:a,credentials:r,conversationId:i.conversationId,serviceUrl:i.serviceUrl}):await replyToTeamsActivity({...n,body:a,credentials:r,activityId:t.replyToActivityId,conversationId:i.conversationId,serviceUrl:i.serviceUrl});return anchor(o),o}return{channelId:t.channelId??void 0,conversationId:t.conversationId??``,conversationType:t.conversationType??void 0,replyToActivityId:t.replyToActivityId??void 0,serviceUrl:t.serviceUrl??``,teamId:t.teamId??void 0,tenantId:t.tenantId??void 0,request(e,t,i){let a=requireAddress();return callTeamsConnectorApi({...n,body:t,credentials:r,method:i?.method,path:e,serviceUrl:a.serviceUrl})},sendActivity:send,replyToActivity(e){let i=requireAddress(),a=t.replyToActivityId??``;if(!a)throw Error(`teamsChannel: missing reply activity id.`);return replyToTeamsActivity({...n,body:buildOutboundActivity(t,e),credentials:r,activityId:a,conversationId:i.conversationId,serviceUrl:i.serviceUrl})},updateActivity(e,i){let a=requireAddress();return updateTeamsActivity({...n,body:buildUpdateActivity(t,i),credentials:r,activityId:e,conversationId:normalizeTeamsContinuationAddress({conversationId:a.conversationId}).conversationId,serviceUrl:a.serviceUrl})},async startTyping(){let e=requireAddress();await triggerTeamsTypingIndicator({...n,credentials:r,conversationId:e.conversationId,serviceUrl:e.serviceUrl})}}}async function verifyInbound(e,t){try{return await verifyTeamsRequest(e,{appId:t?.webhookVerifier?void 0:t?.appId,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`teams inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){let t=stateFromActivity(e.activity),n=buildTeamsBinding({config:e.config,state:t}),r=stateToken(t),i={...n,isSubscribed:async()=>await e.resolveSession(r)!==void 0},a;try{a=await e.onMessage(i,e.activity)}catch(e){log.error(`Teams message handler failed`,{error:e});return}if(a==null)return;let o=collectTeamsFileParts(e.activity.attachments,e.filesPolicy),s=buildTeamsTurnMessage(e.activity.text,o),c={activityId:e.activity.id,botId:e.activity.recipient.id,channelId:e.activity.teamsChannelId,conversationId:e.activity.conversation.id,conversationType:e.activity.conversationType,isMentioned:e.activity.isBotMentioned,scope:e.activity.scope,teamId:e.activity.teamId,tenantId:e.activity.tenantId,userId:e.activity.from.id,userName:e.activity.from.name},l=a.context??[];try{await e.from(r).send(s,{auth:a.auth,context:[formatTeamsContextBlock(c),...l],state:t,title:a.title})}catch(e){log.error(`Teams message delivery failed`,{error:e})}}async function handleInvoke(e){if(isTeamsInputResponseActivity(e.activity))return e.waitUntil(dispatchInputResponses({activity:e.activity,config:e.config,onInputResponse:e.onInputResponse,from:e.from})),Response.json(teamsInvokeResponse());if(e.config.onInvoke===void 0)return teamsOk();let t=buildTeamsBinding({config:e.config,state:stateFromActivity(e.activity)}),n=await e.config.onInvoke(t,e.activity);return n instanceof Response?n:n&&typeof n==`object`?Response.json(n):teamsOk()}async function dispatchInputResponses(e){let t=deriveTeamsInputResponses(e.activity);if(t.length===0)return;let n=stateFromActivity(e.activity),r=buildTeamsBinding({config:e.config,state:n}),i;try{i=await e.onInputResponse(r,e.activity)}catch(e){log.error(`Teams input response authorization failed`,{error:e});return}if(i!==null)try{await e.from(resolveInputContinuationToken(e.activity,n)).respond(t,{auth:i.auth,state:approvalResponseStatePatch(e.activity,i.auth)})}catch(e){log.error(`Teams input response delivery failed`,{error:e})}}function stateFromActivity(e){let t=normalizeTeamsContinuationAddress({conversationId:e.conversation.id,replyToActivityId:teamsThreadRootActivityId(e)});return{bot:e.recipient,channelId:e.teamsChannelId??null,conversationId:e.conversation.id,conversationType:e.conversationType??e.scope,pendingAuthActivityId:null,replyToActivityId:t.replyToActivityId,serviceUrl:e.serviceUrl,teamId:e.teamId??null,tenantId:e.tenantId??null,triggeringUser:e.from}}function resolveInputContinuationToken(e,t){let n=readTeamsInputReplyToActivityId(e);return n===null?stateToken(t):teamsContinuationToken({conversationId:e.conversation.id,replyToActivityId:n,tenantId:e.tenantId})}function approvalResponseStatePatch(e,t){if(t?.principalId===void 0||!isTeamsToolApprovalResponseActivity(e))return;let n=deriveTeamsInputResponses(e)[0]?.requestId,r=e.replyToId;return n===void 0||r===void 0?{approvalResponderAccounts:{[t.principalId]:e.from}}:{approvalResponderAccounts:{[t.principalId]:e.from},pendingApprovalCards:{[n]:{activityId:r,prompt:readTeamsToolApprovalPrompt(e)??`Tool approval`}}}}function defaultOnInputResponse(e,t){return{auth:defaultTeamsAuth(t)}}function rejectInput(){return null}function initialTeamsState(){return{bot:null,channelId:null,conversationId:null,conversationType:null,approvalResponderAccounts:{},pendingApprovalCards:{},pendingAuthActivityId:null,replyToActivityId:null,serviceUrl:null,teamId:null,tenantId:null,triggeringUser:null}}function stateToken(e){let t=e.conversationId??``;if(!t)throw Error(`teamsChannel: missing conversation id.`);return teamsContinuationToken({conversationId:t,replyToActivityId:e.replyToActivityId,tenantId:e.tenantId})}function buildOutboundActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)return t;let n=normalizeTeamsPostInput(t),r=mergeChannelData(e,n.channelData);return{...n,channelData:r,conversation:e.conversationId?{id:e.conversationId}:void 0,from:e.bot??void 0,replyToId:e.replyToActivityId??void 0,type:`message`}}function buildUpdateActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)throw Error(`teamsChannel: cannot replace an activity with a typing indicator.`);let{channelData:n,conversation:r,from:i,recipient:a,replyToId:o,type:s,...c}=normalizeTeamsPostInput(t);return{...c,type:`message`}}function mergeChannelData(e,t){let n={...t};return e.tenantId&&(n.tenant={id:e.tenantId}),e.teamId&&(n.team={id:e.teamId}),e.channelId&&(n.channel={id:e.channelId}),Object.keys(n).length>0?parseJsonObject(n):void 0}function teamsOk(){return new Response(`ok`,{status:200})}export{teamsChannel};
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{readNonEmptyString}from"#shared/guards.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{callTeamsConnectorApi,normalizeTeamsContinuationAddress,normalizeTeamsPostInput,replyToTeamsActivity,sendTeamsActivity,teamsContinuationToken,triggerTeamsTypingIndicator,updateTeamsActivity}from"#public/channels/teams/api.js";import{deriveTeamsInputResponses,isTeamsInputResponseActivity,isTeamsToolApprovalResponseActivity,readTeamsInputReplyToActivityId,readTeamsToolApprovalPrompt,teamsInvokeResponse}from"#public/channels/teams/hitl.js";import{formatTeamsContextBlock,parseTeamsActivity,teamsThreadRootActivityId}from"#public/channels/teams/inbound.js";import{buildTeamsTurnMessage,collectTeamsFileParts,createTeamsFetchFile,normalizeTeamsFilesPolicy}from"#public/channels/teams/attachments.js";import{defaultEvents,defaultOnMessage,defaultTeamsAuth,teamsMentionUser}from"#public/channels/teams/defaults.js";import{verifyTeamsRequest}from"#public/channels/teams/verify.js";const log=createLogger(`teams.channel`);function teamsChannel(e={}){let t=normalizeTeamsFilesPolicy(e.files),n=e.onMessage??defaultOnMessage,r=e.onInputResponse??(e.onMessage===void 0?defaultOnInputResponse:rejectInput),i={...defaultEvents,...e.events};return defineChannel({kindHint:`teams`,turnPolicy:e.turnPolicy,state:initialTeamsState(),fetchFile:createTeamsFetchFile(t,{...e.api,credentials:e.credentials}),metadata:e=>({channelId:e.channelId,conversationType:e.conversationType,teamId:e.teamId}),audience:({state:e})=>teamsAudience(e.conversationType),context(t,n){return rebuildTeamsContext(t,n,e)},deliver(e,t){let n=e.state,r=n?.pendingApprovalCards;r!==void 0&&(t.state.pendingApprovalCards={...r,...t.state.pendingApprovalCards});let i=n?.approvalResponderAccounts;return i!==void 0&&(t.state.approvalResponderAccounts={...t.state.approvalResponderAccounts,...i}),defaultDeliverResult(e)},routes:[POST(e.route??`/eve/v1/teams`,async(i,{from:a,resolveSession:o,waitUntil:s})=>{let c=await verifyInbound(i,e.credentials);if(c===null)return new Response(`unauthorized`,{status:401});let l;try{l=parseJsonObject(JSON.parse(c))}catch(e){return log.warn(`inbound Teams body is not valid JSON`,{error:e}),teamsOk()}let u=parseTeamsActivity(l);return u===null?teamsOk():u.type===`message`?(s(isTeamsInputResponseActivity(u)?dispatchInputResponses({activity:u,config:e,from:a,onInputResponse:r}):dispatchMessage({activity:u,config:e,filesPolicy:t,onMessage:n,from:a,resolveSession:o})),teamsOk()):u.type===`invoke`?handleInvoke({activity:u,config:e,onInputResponse:r,waitUntil:s,from:a}):teamsOk()})],async receive(t,{from:n}){let r=t.target,i=readNonEmptyString(r.serviceUrl),a=readNonEmptyString(r.conversationId);if(!i||!a)throw Error(`teamsChannel().receive requires target.serviceUrl and target.conversationId.`);let o=readNonEmptyString(r.conversationType)??null,s=readNonEmptyString(r.replyToActivityId)??null,c=r.initialMessage;if(c!==void 0&&s!==null)throw Error("teamsChannel().receive: `replyToActivityId` and `initialMessage` are mutually exclusive.");let l={...initialTeamsState(),channelId:readNonEmptyString(r.channelId)??null,conversationId:a,conversationType:o,replyToActivityId:s,serviceUrl:i,teamId:readNonEmptyString(r.teamId)??null,tenantId:readNonEmptyString(r.tenantId)??null};if(c!==void 0){let t=await buildTeamsBinding({config:e,state:l}).thread.post(c);o!==`personal`&&t.id&&(s=t.id,l.replyToActivityId=t.id)}return n(teamsContinuationToken({conversationId:a,replyToActivityId:s,tenantId:l.tenantId})).send(t.message,{auth:t.auth,state:l})},events:i})}function teamsAudience(e){return e===`personal`||e===`groupChat`?`private`:`unknown`}function rebuildTeamsContext(e,t,n){return{...buildTeamsBinding({config:n,session:t,state:e}),adaptiveCardVersion:n.adaptiveCardVersion??`1.5`,state:e}}function buildTeamsBinding(e){let t=buildTeamsHandle(e);return{teams:t,thread:{mentionUser:teamsMentionUser,post(e){return t.sendActivity(e)},async startTyping(){try{await t.startTyping()}catch(e){logError(log,`Teams typing indicator failed — swallowed`,e)}},update(e,n){return t.updateActivity(e,n)}}}}function buildTeamsHandle(e){let t=e.state,n=e.config.api,r=e.config.credentials;function requireAddress(){let e=t.conversationId??``,n=t.serviceUrl??``;if(!e||!n)throw Error(`teamsChannel: missing serviceUrl or conversationId for outbound message.`);return{conversationId:e,serviceUrl:n}}function anchor(n){if(!n.id||t.replyToActivityId||t.conversationType===`personal`)return;t.replyToActivityId=n.id;let r=t.conversationId;r&&e.session?.continuation?.rekey(teamsContinuationToken({conversationId:r,replyToActivityId:n.id,tenantId:t.tenantId}))}async function send(e){let i=requireAddress(),a=buildOutboundActivity(t,e),o=t.replyToActivityId===null?await sendTeamsActivity({...n,body:a,credentials:r,conversationId:i.conversationId,serviceUrl:i.serviceUrl}):await replyToTeamsActivity({...n,body:a,credentials:r,activityId:t.replyToActivityId,conversationId:i.conversationId,serviceUrl:i.serviceUrl});return anchor(o),o}return{channelId:t.channelId??void 0,conversationId:t.conversationId??``,conversationType:t.conversationType??void 0,replyToActivityId:t.replyToActivityId??void 0,serviceUrl:t.serviceUrl??``,teamId:t.teamId??void 0,tenantId:t.tenantId??void 0,request(e,t,i){let a=requireAddress();return callTeamsConnectorApi({...n,body:t,credentials:r,method:i?.method,path:e,serviceUrl:a.serviceUrl})},sendActivity:send,replyToActivity(e){let i=requireAddress(),a=t.replyToActivityId??``;if(!a)throw Error(`teamsChannel: missing reply activity id.`);return replyToTeamsActivity({...n,body:buildOutboundActivity(t,e),credentials:r,activityId:a,conversationId:i.conversationId,serviceUrl:i.serviceUrl})},updateActivity(e,i){let a=requireAddress();return updateTeamsActivity({...n,body:buildUpdateActivity(t,i),credentials:r,activityId:e,conversationId:normalizeTeamsContinuationAddress({conversationId:a.conversationId}).conversationId,serviceUrl:a.serviceUrl})},async startTyping(){let e=requireAddress();await triggerTeamsTypingIndicator({...n,credentials:r,conversationId:e.conversationId,serviceUrl:e.serviceUrl})}}}async function verifyInbound(e,t){try{return await verifyTeamsRequest(e,{appId:t?.webhookVerifier?void 0:t?.appId,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`teams inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){let t=stateFromActivity(e.activity),n=buildTeamsBinding({config:e.config,state:t}),r=stateToken(t),i={...n,isSubscribed:async()=>await e.resolveSession(r)!==void 0},a;try{a=await e.onMessage(i,e.activity)}catch(e){log.error(`Teams message handler failed`,{error:e});return}if(a==null)return;let o=collectTeamsFileParts(e.activity.attachments,e.filesPolicy),s=buildTeamsTurnMessage(e.activity.text,o),c={activityId:e.activity.id,botId:e.activity.recipient.id,channelId:e.activity.teamsChannelId,conversationId:e.activity.conversation.id,conversationType:e.activity.conversationType,isMentioned:e.activity.isBotMentioned,scope:e.activity.scope,teamId:e.activity.teamId,tenantId:e.activity.tenantId,userId:e.activity.from.id,userName:e.activity.from.name},l=a.context??[];try{await e.from(r).send(s,{auth:a.auth,context:[formatTeamsContextBlock(c),...l],state:t,title:a.title})}catch(e){log.error(`Teams message delivery failed`,{error:e})}}async function handleInvoke(e){if(isTeamsInputResponseActivity(e.activity))return e.waitUntil(dispatchInputResponses({activity:e.activity,config:e.config,onInputResponse:e.onInputResponse,from:e.from})),Response.json(teamsInvokeResponse());if(e.config.onInvoke===void 0)return teamsOk();let t=buildTeamsBinding({config:e.config,state:stateFromActivity(e.activity)}),n=await e.config.onInvoke(t,e.activity);return n instanceof Response?n:n&&typeof n==`object`?Response.json(n):teamsOk()}async function dispatchInputResponses(e){let t=deriveTeamsInputResponses(e.activity);if(t.length===0)return;let n=stateFromActivity(e.activity),r=buildTeamsBinding({config:e.config,state:n}),i;try{i=await e.onInputResponse(r,e.activity)}catch(e){log.error(`Teams input response authorization failed`,{error:e});return}if(i!==null)try{await e.from(resolveInputContinuationToken(e.activity,n)).respond(t,{auth:i.auth,state:approvalResponseStatePatch(e.activity,i.auth)})}catch(e){log.error(`Teams input response delivery failed`,{error:e})}}function stateFromActivity(e){let t=normalizeTeamsContinuationAddress({conversationId:e.conversation.id,replyToActivityId:teamsThreadRootActivityId(e)});return{bot:e.recipient,channelId:e.teamsChannelId??null,conversationId:e.conversation.id,conversationType:e.conversationType??e.scope,pendingAuthActivityId:null,replyToActivityId:t.replyToActivityId,serviceUrl:e.serviceUrl,teamId:e.teamId??null,tenantId:e.tenantId??null,triggeringUser:e.from}}function resolveInputContinuationToken(e,t){let n=readTeamsInputReplyToActivityId(e);return n===null?stateToken(t):teamsContinuationToken({conversationId:e.conversation.id,replyToActivityId:n,tenantId:e.tenantId})}function approvalResponseStatePatch(e,t){if(t?.principalId===void 0||!isTeamsToolApprovalResponseActivity(e))return;let n=deriveTeamsInputResponses(e)[0]?.requestId,r=e.replyToId;return n===void 0||r===void 0?{approvalResponderAccounts:{[t.principalId]:e.from}}:{approvalResponderAccounts:{[t.principalId]:e.from},pendingApprovalCards:{[n]:{activityId:r,prompt:readTeamsToolApprovalPrompt(e)??`Tool approval`}}}}function defaultOnInputResponse(e,t){return{auth:defaultTeamsAuth(t)}}function rejectInput(){return null}function initialTeamsState(){return{bot:null,channelId:null,conversationId:null,conversationType:null,approvalResponderAccounts:{},pendingApprovalCards:{},pendingAuthActivityId:null,replyToActivityId:null,serviceUrl:null,teamId:null,tenantId:null,triggeringUser:null}}function stateToken(e){let t=e.conversationId??``;if(!t)throw Error(`teamsChannel: missing conversation id.`);return teamsContinuationToken({conversationId:t,replyToActivityId:e.replyToActivityId,tenantId:e.tenantId})}function buildOutboundActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)return t;let n=normalizeTeamsPostInput(t),r=mergeChannelData(e,n.channelData);return{...n,channelData:r,conversation:e.conversationId?{id:e.conversationId}:void 0,from:e.bot??void 0,replyToId:e.replyToActivityId??void 0,type:`message`}}function buildUpdateActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)throw Error(`teamsChannel: cannot replace an activity with a typing indicator.`);let{channelData:n,conversation:r,from:i,recipient:a,replyToId:o,type:s,...c}=normalizeTeamsPostInput(t);return{...c,type:`message`}}function mergeChannelData(e,t){let n={...t};return e.tenantId&&(n.tenant={id:e.tenantId}),e.teamId&&(n.team={id:e.teamId}),e.channelId&&(n.channel={id:e.channelId}),Object.keys(n).length>0?parseJsonObject(n):void 0}function teamsOk(){return new Response(`ok`,{status:200})}export{teamsChannel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{isObject}from"#shared/guards.js";import{importJWK,jwtVerify}from"#compiled/jose/index.js";import{resolveTeamsAppId}from"#public/channels/teams/api.js";const log=createLogger(`teams.verify`);async function verifyTeamsRequest(e,t){let n=await e.text();if(t.webhookVerifier!==void 0){let r=await t.webhookVerifier(e,n);if(!r)throw Error(`teamsChannel: inbound webhook verifier rejected the request.`);return typeof r==`string`?r:n}let r=readBearerToken(e.headers.get(`authorization`)??``);if(!r)throw Error(`teamsChannel: inbound request missing bearer token.`);return await verifyTeamsJwt(r,t),n}async function verifyTeamsJwt(e,t){let n=await resolveTeamsAppId(t.appId),r=await loadJwks(t);return(await jwtVerify(e,async e=>{let t=selectJwk(r,e),n=typeof e.alg==`string`?e.alg:void 0;return importJWK(t,n)},{audience:n,clockTolerance:t.maxSkewSeconds??300,issuer:`https://api.botframework.com`})).payload}function readBearerToken(e){let[t,n]=e.split(/\s+/,2);return t?.toLowerCase()!==`bearer`||!n?null:n}async function loadJwks(e){let t=e.fetch??fetch,n=await t(e.jwksUrl??await loadJwksUrl(e,t),{headers:{accept:`application/json`}});if(!n.ok)throw Error(`Teams JWKS route returned HTTP ${n.status}.`);let r=await n.json();if(!isObject(r)||!Array.isArray(r.keys))throw Error(`Teams JWKS response is malformed.`);return r.keys.filter(isObject)}async function loadJwksUrl(e,t){let n=await t(e.openIdMetadataUrl??`https://login.botframework.com/v1/.well-known/openidconfiguration`,{headers:{accept:`application/json`}});if(!n.ok)throw Error(`Teams OpenID metadata route returned HTTP ${n.status}.`);let r=await n.json();if(!isObject(r)||typeof r.jwks_uri!=`string`)throw Error(`Teams OpenID metadata response is missing jwks_uri.`);return r.jwks_uri}function selectJwk(e,t){let n=typeof t.kid==`string`?t.kid:void 0,r=typeof t.x5t==`string`?t.x5t:void 0,i=e.find(e=>n!==void 0&&e.kid===n||r!==void 0&&e.x5t===r)??(e.length===1?e[0]:void 0);if(i===void 0)throw log.debug(`Teams JWT key not found`,{kid:n,x5t:r}),Error(`teamsChannel: inbound JWT key was not found in JWKS.`);return i}export{verifyTeamsJwt,verifyTeamsRequest};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import type { ChannelAudience } from "#shared/channel-audience.js";
|
|
1
2
|
import type { TelegramInstrumentationMetadata } from "#public/channels/telegram/index.js";
|
|
2
3
|
import type { TelegramChannelState } from "#public/channels/telegram/telegramChannel.js";
|
|
4
|
+
import type { AudienceContext } from "#shared/conversation-context.js";
|
|
3
5
|
export declare function telegramInstrumentationMetadata(state: TelegramChannelState): TelegramInstrumentationMetadata;
|
|
6
|
+
export declare function telegramAudience(state: TelegramChannelState): ChannelAudience;
|
|
7
|
+
export declare const telegramInstrumentation: {
|
|
8
|
+
readonly audience: (input: AudienceContext<TelegramChannelState>) => "private" | "public" | "unknown";
|
|
9
|
+
readonly metadata: typeof telegramInstrumentationMetadata;
|
|
10
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function telegramInstrumentationMetadata(e){return{
|
|
1
|
+
function telegramInstrumentationMetadata(e){return{chatId:e.chatId,chatType:e.chatType,triggeringUserId:e.triggeringUserId??null}}function telegramAudience(e){return telegramAudienceForChatType(e.chatType)}const telegramInstrumentation={audience:e=>telegramAudience(e.state),metadata:telegramInstrumentationMetadata};function telegramAudienceForChatType(e){return e===`private`||e===`group`||e===`supergroup`||e===`channel`?`private`:`unknown`}export{telegramAudience,telegramInstrumentation,telegramInstrumentationMetadata};
|
|
@@ -6,7 +6,6 @@ export type { ModelMessage } from "ai";
|
|
|
6
6
|
* inbound update populates it.
|
|
7
7
|
*/
|
|
8
8
|
export interface TelegramInstrumentationMetadata extends Record<string, unknown> {
|
|
9
|
-
readonly audience: import("#shared/channel-audience.js").ChannelAudience;
|
|
10
9
|
readonly chatId: string | null;
|
|
11
10
|
readonly chatType: import("#public/channels/telegram/inbound.js").TelegramChatType | null;
|
|
12
11
|
readonly triggeringUserId: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger,logError}from"#internal/logging.js";import{INTERNAL_CHANNEL_DELIVER}from"#channel/channel-operations.js";import{isCompiledChannel}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{formatTelegramContextBlock,parseTelegramUpdate}from"#public/channels/telegram/inbound.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,editTelegramMessageText,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX}from"#public/channels/telegram/authorization.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponse}from"#public/channels/telegram/hitl.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage,isTelegramBotMentioned}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";import{dispatchTelegramAuthorizationCallback}from"#public/channels/telegram/authorization-callback.js";import{initialTelegramState,stateFromTelegramCallbackQuery,stateFromTelegramMessage,telegramContinuationTokenFromState}from"#public/channels/telegram/state.js";import{
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{INTERNAL_CHANNEL_DELIVER}from"#channel/channel-operations.js";import{isCompiledChannel}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{formatTelegramContextBlock,parseTelegramUpdate}from"#public/channels/telegram/inbound.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,editTelegramMessageText,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX}from"#public/channels/telegram/authorization.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponse}from"#public/channels/telegram/hitl.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage,isTelegramBotMentioned}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";import{dispatchTelegramAuthorizationCallback}from"#public/channels/telegram/authorization-callback.js";import{initialTelegramState,stateFromTelegramCallbackQuery,stateFromTelegramMessage,telegramContinuationTokenFromState}from"#public/channels/telegram/state.js";import{telegramInstrumentation}from"#public/channels/telegram/audience.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,r={...defaultEvents,...e.events},i=defineChannel({kindHint:`telegram`,turnPolicy:e.turnPolicy,state:initialTelegramState(e.botUsername),...telegramInstrumentation,fetchFile:createTelegramFetchFile({api:e.api,credentials:e.credentials,policy:t}),context(t,n){return rebuildTelegramContext(t,n,e)},routes:[POST(e.route??`/eve/v1/telegram`,async(r,{from:i,resolveSession:a,waitUntil:o})=>{let s=await verifyInbound(r,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Telegram body is not valid JSON`,{error:e}),new Response(`ok`)}let l=parseTelegramUpdate(c);return l===null?new Response(`ok`):l.kind===`message`?(o(dispatchMessage({config:e,message:l.message,onMessage:n,uploadPolicy:t,from:i})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:l.callbackQuery,from:i,resolveSession:a})),new Response(`ok`))})],async receive(t,{from:n}){let r=t.target,i=readChatId(r.chatId);if(i===void 0)throw Error(`telegramChannel().receive requires target.chatId.`);let a=typeof r.messageThreadId==`number`?r.messageThreadId:void 0,o=readChatId(r.conversationId),s=r.initialMessage;if(s!==void 0&&o!==void 0)throw Error("telegramChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c={...initialTelegramState(e.botUsername),chatId:i,conversationId:o??null,messageThreadId:a??null};return s!==void 0&&await buildTelegramHandle({config:e,state:c}).sendMessage(s),n(telegramContinuationTokenFromState(c)).send(t.message,{auth:t.auth,state:c})},events:r});return attachTelegramDeliver(i),i}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let t=e.config.api,n=e.state,r=e.config.credentials;function anchor(t){let r=n.chatType??t.chatType??null;n.chatType===null&&t.chatType!==void 0&&(n.chatType=t.chatType),t.id&&shouldAnchorTelegramConversation(r)&&(n.conversationId=t.id,n.chatId&&e.session?.continuation?.rekey(telegramContinuationToken({chatId:n.chatId,conversationId:t.id,messageThreadId:n.messageThreadId??void 0})))}async function sendOne(e){let t=await sendMessage(e);return anchor(t),t}async function sendMessage(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for outbound message.`);return sendTelegramMessage({apiBaseUrl:t?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??n.messageThreadId??void 0},credentials:r,fetch:t?.fetch,fileBaseUrl:t?.fileBaseUrl,chatId:i})}return{botUsername:n.botUsername??e.config.botUsername,chatId:n.chatId??``,chatType:n.chatType??void 0,conversationId:n.conversationId??void 0,messageThreadId:n.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:t?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:r,fetch:t?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},editMessageText(e){let i=n.chatId??``;if(!i)throw Error(`telegramChannel: missing chat id for text edit.`);return editTelegramMessageText({apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup,text:e.text})},post(e){return postTelegramMessage(e,sendOne)},postEphemeral(e,t,n){return postTelegramMessage({...typeof t==`string`?{text:t}:t,ephemeral_message_parameters:{...n?.callbackQueryId===void 0?{}:{callback_query_id:n.callbackQueryId},receiver_user_id:e}},sendMessage)},request(e,n){return callTelegramApi({apiBaseUrl:t?.apiBaseUrl,body:n,botToken:r?.botToken,fetch:t?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let i=n.chatId??``;if(i)try{await sendTelegramChatAction({action:e,apiBaseUrl:t?.apiBaseUrl,chatId:i,credentials:r,fetch:t?.fetch,messageThreadId:n.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:i})}}}}function shouldAnchorTelegramConversation(e){return e===`group`||e===`supergroup`}async function postTelegramMessage(e,t){let n=typeof e==`string`?{text:e}:e,r=splitTelegramMessageText(n.text),i;for(let[e,a]of r.entries()){let r=await t(e===0?{...n,text:a}:{...n.ephemeral_message_parameters===void 0?{}:{ephemeral_message_parameters:n.ephemeral_message_parameters},text:a});i===void 0&&(i=r)}return i??{id:``,raw:null}}async function verifyInbound(e,t){try{return await verifyTelegramRequest(e,{secretToken:t?.webhookVerifier?void 0:t?.webhookSecretToken,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`telegram inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){if(e.message.from?.isBot===!0)return;let t=stateFromTelegramMessage(e.message,e.config.botUsername),n={telegram:buildTelegramHandle({config:e.config,state:t})},r;try{r=await e.onMessage(n,e.message)}catch(e){log.error(`message handler failed`,{error:e});return}if(r==null)return;let i=collectTelegramFileParts(e.message.attachments,e.uploadPolicy),a=buildTelegramTurnMessage(e.message,i),o=formatTelegramContextBlock({botUsername:e.config.botUsername,chatId:e.message.chat.id,chatTitle:e.message.chat.title,chatType:e.message.chat.type,isMentioned:isTelegramBotMentioned(e.message,e.config.botUsername),messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),s=r.context??[],c=e.message.text||e.message.caption,l=e.message.replyToMessage?.from?.isBot===!0&&c.trim().length>0?[telegramReplyInputResponse({messageId:e.message.replyToMessage.messageId,text:c})]:void 0;try{await e.from(telegramContinuationTokenFromState(t))[INTERNAL_CHANNEL_DELIVER]({context:[o,...s],inputResponses:l,message:a},{auth:r.auth,state:t,title:r.title})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromTelegramCallbackQuery(e.query,e.config.botUsername),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX)===!0){await dispatchTelegramAuthorizationCallback({continuationToken:telegramContinuationTokenFromState(t),query:e.query,resolveSession:e.resolveSession,state:t,telegram:n});return}if(e.query.data?.startsWith(TELEGRAM_HITL_CALLBACK_PREFIX)===!0){try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Answer received.`})}catch(e){log.warn(`Telegram callback-query acknowledgement failed`,{error:e})}if(!e.query.message||!t.chatId)return;try{await e.from(telegramContinuationTokenFromState(t)).respond([telegramCallbackInputResponse(e.query.data)],{auth:null})}catch(e){log.error(`callback query delivery failed`,{error:e})}return}if(e.config.onCallbackQuery!==void 0){try{await e.config.onCallbackQuery(n,e.query)}catch(e){log.error(`custom callback-query handler failed`,{error:e})}return}try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Unsupported action.`})}catch(e){log.warn(`Telegram unsupported callback-query acknowledgement failed`,{error:e})}}function attachTelegramDeliver(e){if(!isCompiledChannel(e))return;let t=e.adapter;t.deliver=(e,t)=>{let n=e.inputResponses??[];if(n.some(isTelegramSyntheticResponse)){let r=resolveTelegramInputResponses(t.state,n);return r.length>0?{inputResponses:r,context:e.context}:e.message===void 0?void 0:{message:e.message,context:e.context}}return defaultDeliverResult(e)}}function readChatId(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}export{telegramChannel};
|
|
@@ -6,7 +6,6 @@ import { type TwilioApiOptions, type TwilioApiResponse, type TwilioCredentials }
|
|
|
6
6
|
import { type TwilioTextMessage, type TwilioVoiceCall, type TwilioVoiceTranscription } from "#public/channels/twilio/inbound.js";
|
|
7
7
|
import { type TwilioAuthToken, type TwilioWebhookUrl } from "#public/channels/twilio/verify.js";
|
|
8
8
|
import { type Channel } from "#public/definitions/channel.js";
|
|
9
|
-
import type { ChannelAudience } from "#shared/channel-audience.js";
|
|
10
9
|
type EventData<T extends UnstampedMessageStreamEvent["type"]> = Extract<UnstampedMessageStreamEvent, {
|
|
11
10
|
type: T;
|
|
12
11
|
}> extends {
|
|
@@ -36,7 +35,6 @@ export interface TwilioChannelState {
|
|
|
36
35
|
}
|
|
37
36
|
/** Per-session instrumentation snapshot for Twilio runtime telemetry. Reports the active phone-number pair and the most recent message and call SIDs. */
|
|
38
37
|
export interface TwilioInstrumentationMetadata extends Record<string, unknown> {
|
|
39
|
-
readonly audience: ChannelAudience;
|
|
40
38
|
readonly from: string | null;
|
|
41
39
|
readonly lastCallSid: string | null;
|
|
42
40
|
readonly lastMessageSid: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{readNonEmptyString}from"#shared/guards.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription}from"#public/channels/twilio/inbound.js";import{buildTwilioActionUrl,buildTwilioRoutes,verifyTwilioInbound}from"#public/channels/twilio/routing.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let t=buildTwilioRoutes(e.route??`/eve/v1/twilio`),n=e.onText??defaultOnText,r=e.onVoice??defaultOnVoice,i=e.onVoiceTranscription??defaultOnVoiceTranscription,a={...defaultEvents,...e.events},o=handleTwilioMessages({config:e,onText:n}),s=handleTwilioVoice({config:e,onVoice:r,routes:t}),c=handleTwilioTranscription({config:e,onVoiceTranscription:i,routes:t});return defineChannel({kindHint:`twilio`,turnPolicy:e.turnPolicy,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{readNonEmptyString}from"#shared/guards.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription}from"#public/channels/twilio/inbound.js";import{buildTwilioActionUrl,buildTwilioRoutes,verifyTwilioInbound}from"#public/channels/twilio/routing.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let t=buildTwilioRoutes(e.route??`/eve/v1/twilio`),n=e.onText??defaultOnText,r=e.onVoice??defaultOnVoice,i=e.onVoiceTranscription??defaultOnVoiceTranscription,a={...defaultEvents,...e.events},o=handleTwilioMessages({config:e,onText:n}),s=handleTwilioVoice({config:e,onVoice:r,routes:t}),c=handleTwilioTranscription({config:e,onVoiceTranscription:i,routes:t});return defineChannel({kindHint:`twilio`,turnPolicy:e.turnPolicy,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}},audience:()=>`private`,context(t,n){return rebuildTwilioContext(t,n,e)},routes:[GET(t.messages,o),POST(t.messages,o),GET(t.voice,s),POST(t.voice,s),GET(t.transcription,c),POST(t.transcription,c)],async receive(t,{from:n}){let r=readNonEmptyString(t.target.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires target.phoneNumber.`);let i=readNonEmptyString(t.target.from)??e.messaging?.from??null;return n(twilioContinuationToken(r,i??void 0)).send(t.message,{auth:t.auth,state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:a})}function handleTwilioMessages(e){return async(t,{from:n,waitUntil:r})=>{let i=await verifyTwilioInbound(t,e.config);if(i===null)return new Response(`unauthorized`,{status:401});let a=parseTwilioTextMessage(i.params);return a?await isAllowed(a.from,e.config.allowFrom)?(r(dispatchText({config:e.config,message:a,onText:e.onText,from:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}}function handleTwilioVoice(e){return async t=>{let n=await verifyTwilioInbound(t,e.config);if(n===null)return new Response(`unauthorized`,{status:401});let r=parseTwilioVoiceCall(n.params);if(!r)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(r.from,e.config.allowFrom))return new Response(`forbidden`,{status:403});let i=await acceptVoiceCall({call:r,config:e.config,onVoice:e.onVoice});if(i===null)return new Response(`forbidden`,{status:403});let a=i??{};return gatherSpeechTwilioResponse({actionUrl:await buildTwilioActionUrl(t,e.config,e.routes.transcription),hints:a.hints??e.config.voice?.hints,language:a.language??e.config.voice?.language,profanityFilter:a.profanityFilter??e.config.voice?.profanityFilter,prompt:a.prompt??e.config.voice?.prompt??`Please say your message after the tone.`,speechModel:a.speechModel??e.config.voice?.speechModel,speechTimeout:a.speechTimeout??e.config.voice?.speechTimeout??`auto`,timeoutSeconds:a.timeoutSeconds??e.config.voice?.timeoutSeconds,voice:a.voice??e.config.voice?.voice})}}function handleTwilioTranscription(e){return async(t,{from:n,waitUntil:r})=>{let i=await verifyTwilioInbound(t,e.config);if(i===null)return new Response(`unauthorized`,{status:401});let a=parseTwilioVoiceTranscription(i.params);return a?await isAllowed(a.from,e.config.allowFrom)?(r(dispatchVoiceTranscription({config:e.config,onVoiceTranscription:e.onVoiceTranscription,from:n,transcription:a})),sayTwilioResponse(e.config.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildTwilioActionUrl(t,e.config,e.routes.transcription),language:e.config.voice?.language,prompt:e.config.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.config.voice?.speechTimeout??`auto`,timeoutSeconds:e.config.voice?.timeoutSeconds})}}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,i=e.config.messaging?.messagingServiceSid,a=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(o,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:o,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??i,statusCallbackUrl:s?.statusCallbackUrl??a,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.from(twilioContinuationToken(t.from,t.to)).send(t.body,{auth:r.auth,context:[i],state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null},title:r.title})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.from(twilioContinuationToken(t.from,t.to)).send(t.text,{auth:r.auth,context:[i],state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null},title:r.title})}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)}export{twilioChannel};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Connection authoring helpers for `agent/connections/*.ts` files.
|
|
3
3
|
*/
|
|
4
|
-
export type { AuthorizationCallback, AuthorizationDefinition, ConnectionAuthDefinition, ConnectionAuthProvider, ConnectionAuthResolver, ConnectionPrincipal, HeadersDefinition, InteractiveAuthorizationDefinition, NonInteractiveAuthorizationDefinition, TokenResult, ToolFilterDefinition, } from "#shared/connection-types.js";
|
|
4
|
+
export type { AuthorizationCallback, AuthorizationDefinition, CredentialOwner, ConnectionAuthDefinition, ConnectionAuthProvider, ConnectionAuthResolver, ConnectionPrincipal, HeadersDefinition, InteractiveAuthorizationDefinition, NonInteractiveAuthorizationDefinition, TokenResult, ToolFilterDefinition, } from "#shared/connection-types.js";
|
|
5
5
|
export { defineInteractiveAuthorization } from "#shared/connection-types.js";
|
|
6
6
|
export type { JsonValue } from "#public/types/json.js";
|
|
7
7
|
export { defineDynamic, type DynamicConnectionDefinition, type DynamicConnectionEvents, type DynamicConnectionResolveContext, type DynamicConnectionResult, type DynamicConnectionSet, } from "#public/definitions/connections/dynamic.js";
|
|
@@ -10,7 +10,8 @@ import type { GenericChannelDefinition, GenericReceiveInput } from "#shared/chan
|
|
|
10
10
|
declare const CHANNEL_METADATA_TYPE: unique symbol;
|
|
11
11
|
export type { CancelTurnResult, ClearSessionResult, CompactSessionResult, GetEventStreamOptions, ResetSessionResult, SessionCallback, TurnPolicy, } from "#channel/types.js";
|
|
12
12
|
export type { Session, SessionHandle } from "#channel/session.js";
|
|
13
|
-
export type { ChannelAudience
|
|
13
|
+
export type { ChannelAudience } from "#shared/channel-audience.js";
|
|
14
|
+
export type { AudienceCaller, AudienceContext, AudienceInput, AudiencePrincipal, ConversationEnvironment, } from "#shared/conversation-context.js";
|
|
14
15
|
export type { SessionRespondOptions, SessionSendOptions } from "#channel/session.js";
|
|
15
16
|
export type { ChannelFrom, ChannelReceiveContext, ChannelResolveSession, ChannelRespondOptions, ChannelSendOptions, ChannelSource, };
|
|
16
17
|
export type { ChannelCors, ChannelCorsOptions } from "#channel/cors.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{normalizeChannelCors}from"#channel/cors.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,WS}from"#channel/routes.js";const DISABLED_ROUTE_SENTINEL_KIND=`eve:disabled-channel`;function disableRoute(){return{kind:DISABLED_ROUTE_SENTINEL_KIND}}function isDisabledRouteSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_ROUTE_SENTINEL_KIND}function defineChannel(e){let t=buildAdapter(e),n=normalizeChannelCors(e.cors);return{__kind:CHANNEL_SENTINEL,routes:e.routes,adapter:t,cors:n,receive:e.receive,turnPolicy:e.turnPolicy}}const eventTypes=Object.keys({"approval.candidate":null,"approval.settled":null,"context.cleared":null,"compaction.requested":null,"compaction.completed":null,"turn.started":null,"actions.requested":null,"action.partial":null,"action.result":null,"message.completed":null,"message.appended":null,"reasoning.appended":null,"reasoning.completed":null,"input.requested":null,"turn.failed":null,"turn.completed":null,"turn.cancelled":null,"session.failed":null,"session.completed":null,"session.waiting":null,"authorization.required":null,"authorization.completed":null});function buildAdapter(e){let t=e.state!=null,n=e.context!=null,r=e.fetchFile!==void 0,i=e.metadata,a=
|
|
1
|
+
import{createMetadataAudienceProjector}from"#channel/audience.js";import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{normalizeChannelCors}from"#channel/cors.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,WS}from"#channel/routes.js";const DISABLED_ROUTE_SENTINEL_KIND=`eve:disabled-channel`;function disableRoute(){return{kind:DISABLED_ROUTE_SENTINEL_KIND}}function isDisabledRouteSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_ROUTE_SENTINEL_KIND}function defineChannel(e){let t=buildAdapter(e),n=normalizeChannelCors(e.cors);return{__kind:CHANNEL_SENTINEL,routes:e.routes,adapter:t,cors:n,receive:e.receive,turnPolicy:e.turnPolicy}}const eventTypes=Object.keys({"approval.candidate":null,"approval.settled":null,"context.cleared":null,"compaction.requested":null,"compaction.completed":null,"turn.started":null,"actions.requested":null,"action.partial":null,"action.result":null,"message.completed":null,"message.appended":null,"reasoning.appended":null,"reasoning.completed":null,"input.requested":null,"turn.failed":null,"turn.completed":null,"turn.cancelled":null,"session.failed":null,"session.completed":null,"session.waiting":null,"authorization.required":null,"authorization.completed":null});function buildAdapter(e){let t=e.state!=null,n=e.context!=null,r=e.fetchFile!==void 0,i=e.metadata,a=i!==void 0,o=e.audience,s=t||n||a,c={},l=!1,u={kind:e.kindHint??`defineChannel`},d=e.events;for(let e of eventTypes){let t=d?.[e];t&&(l=!0,c[e]=(n,r)=>{let{session:i,...a}=r,o={...a,continuation:i?.continuation===void 0?void 0:{token:i.continuation.token,rekey:e=>i.continuation?.rekey(e)}};if(e===`session.failed`)return t(n,o);let s=buildCallbackContext();return t(n,o,s)})}return!s&&!l&&!r?{kind:e.kindHint??HTTP_ADAPTER_KIND,...o===void 0?void 0:{instrumentation:{audience:o}}}:{kind:e.kindHint??`defineChannel`,state:t?{...e.state}:{},fetchFile:e.fetchFile,instrumentation:i===void 0&&o===void 0?void 0:{...i===void 0?void 0:{metadata(e){let{audience:t,...n}=i(e);return n}},...o===void 0?i===void 0?void 0:{audience:createMetadataAudienceProjector(u,i)}:{audience:o}},createAdapterContext(t){let r=t.state,i=t.session;return{...n?e.context(r,i):{},state:r,ctx:t.ctx,session:i}},deliver(t,n){return e.deliver===void 0?defaultDeliverResult(t):e.deliver(t,n)},...c}}export{DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,WS,defineChannel,disableRoute,isDisabledRouteSentinel};
|
|
@@ -20,6 +20,12 @@ export interface McpClientConnectionDefinition {
|
|
|
20
20
|
* Must support Streamable HTTP or SSE transport.
|
|
21
21
|
*/
|
|
22
22
|
readonly url: string;
|
|
23
|
+
/**
|
|
24
|
+
* Whether to discover the server's protocol before initialization.
|
|
25
|
+
* Defaults to enabled. Set to false for servers that require the older
|
|
26
|
+
* initialize handshake; that handshake still negotiates a supported version.
|
|
27
|
+
*/
|
|
28
|
+
readonly protocolVersionDiscovery?: boolean;
|
|
23
29
|
/**
|
|
24
30
|
* Human-readable summary of the connection and its tools.
|
|
25
31
|
*
|
|
@@ -34,7 +40,7 @@ export interface McpClientConnectionDefinition {
|
|
|
34
40
|
*
|
|
35
41
|
* - `getToken`-only: covers static API keys, pre-provisioned
|
|
36
42
|
* JWTs, and out-of-band OAuth. Defaults to
|
|
37
|
-
* `
|
|
43
|
+
* `credentialOwner: "app"` when omitted.
|
|
38
44
|
* - Three-method form: provide `startAuthorization` and
|
|
39
45
|
* `completeAuthorization` together to opt into
|
|
40
46
|
* interactive OAuth authorization.
|
|
@@ -56,7 +56,7 @@ export interface OpenAPIConnectionDefinition {
|
|
|
56
56
|
* `Authorization: Bearer <token>`.
|
|
57
57
|
*
|
|
58
58
|
* - `getToken`-only: covers static API keys, pre-provisioned tokens,
|
|
59
|
-
* and out-of-band OAuth. Defaults to `
|
|
59
|
+
* and out-of-band OAuth. Defaults to `credentialOwner: "app"` when
|
|
60
60
|
* omitted.
|
|
61
61
|
* - Three-method form: provide `startAuthorization` and
|
|
62
62
|
* `completeAuthorization` together to opt into interactive OAuth.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{vercelOidc}from"#public/agents/auth.js";import{defineRemoteAgent}from"#public/definitions/remote-agent.js";const WORKSPACE_AGENT_NAME=Symbol.for(`eve.workspace-agent.name`);function defineWorkspaceAgent(e){let t=e.transport??defaultWorkspaceAgentTransport(e.name),n=defineRemoteAgent({auth:t.auth,description:e.description??``,forwardPrincipal:e.forwardPrincipal,headers:t.headers,outputSchema:e.outputSchema,url:t.url});return Object.defineProperty(n,WORKSPACE_AGENT_NAME,{value:e.name}),n}function workspaceSubagentName(e){return isBrandedWorkspaceSubagent(e)?e[WORKSPACE_AGENT_NAME]:void 0}function isBrandedWorkspaceSubagent(e){return typeof e==`object`&&!!e&&Reflect.has(e,WORKSPACE_AGENT_NAME)}function defaultWorkspaceAgentTransport(e){let t=vercelOidc();return{auth:async()=>(requireVercelWorkspaceEnvironment(),t()),url:()=>{requireVercelWorkspaceEnvironment();let t=process.env.VERCEL_ENV===`production`?process.env.VERCEL_PROJECT_PRODUCTION_URL:process.env.VERCEL_URL;if(t===void 0||t.length===0)throw Error(`The default workspace-agent transport requires VERCEL_URL, or VERCEL_PROJECT_PRODUCTION_URL in production.`);return`https://${t}/${e}`}}}function requireVercelWorkspaceEnvironment(){if(!process.env.VERCEL)throw Error(`No default workspace-agent transport is available in this environment. Provide transport to defineWorkspaceAgent().`)}export{defineWorkspaceAgent,workspaceSubagentName};
|
|
1
|
+
import{normalizePublicRoutePrefix}from"#shared/public-route-prefix.js";import{vercelOidc}from"#public/agents/auth.js";import{defineRemoteAgent}from"#public/definitions/remote-agent.js";const WORKSPACE_AGENT_NAME=Symbol.for(`eve.workspace-agent.name`);function defineWorkspaceAgent(e){let t=e.transport??defaultWorkspaceAgentTransport(e.name),n=defineRemoteAgent({auth:t.auth,description:e.description??``,forwardPrincipal:e.forwardPrincipal,headers:t.headers,outputSchema:e.outputSchema,url:t.url});return Object.defineProperty(n,WORKSPACE_AGENT_NAME,{value:e.name}),n}function workspaceSubagentName(e){return isBrandedWorkspaceSubagent(e)?e[WORKSPACE_AGENT_NAME]:void 0}function isBrandedWorkspaceSubagent(e){return typeof e==`object`&&!!e&&Reflect.has(e,WORKSPACE_AGENT_NAME)}function defaultWorkspaceAgentTransport(e){let t=vercelOidc();return{auth:async()=>(requireVercelWorkspaceEnvironment(),t()),url:()=>{requireVercelWorkspaceEnvironment();let t=process.env.VERCEL_ENV===`production`?process.env.VERCEL_PROJECT_PRODUCTION_URL:process.env.VERCEL_URL;if(t===void 0||t.length===0)throw Error(`The default workspace-agent transport requires VERCEL_URL, or VERCEL_PROJECT_PRODUCTION_URL in production.`);return`https://${t}${normalizePublicRoutePrefix(process.env.EVE_PUBLIC_ROUTE_PREFIX)?.startsWith(`/eve/agents/`)===!0?`/eve/agents/${e}`:`/${e}`}`}}}function requireVercelWorkspaceEnvironment(){if(!process.env.VERCEL)throw Error(`No default workspace-agent transport is available in this environment. Provide transport to defineWorkspaceAgent().`)}export{defineWorkspaceAgent,workspaceSubagentName};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{isObject}from"#shared/guards.js";import{decodeJwt}from"#compiled/jose/index.js";function readCodexJwtExpirationMs(e){let t=parseCodexJwtClaims(e);if(t!==void 0&&typeof t.exp==`number`)return t.exp*1e3}function extractCodexAccountIdFromToken(e){let t=parseCodexJwtClaims(e);if(t===void 0)return;let n=t[`https://api.openai.com/auth`],r=t.organizations;return readNonEmptyString$1(t.chatgpt_account_id)??readNonEmptyString$1(isObject(n)?n.chatgpt_account_id:void 0)??readNonEmptyString$1(Array.isArray(r)&&isObject(r[0])?r[0].id:void 0)}function extractCodexAccountLabelFromToken(e){let t=parseCodexJwtClaims(e);if(t===void 0)return;let n=t[`https://api.openai.com/auth`],r=readEmail(t.email)??readEmail(isObject(n)?n.email:void 0);if(r!==void 0)return r;let i=readNonEmptyString$1(t.sub);return i===void 0?void 0:readEmail(i.split(`|`).at(-1))}function readEmail(e){let t=readNonEmptyString$1(e);return t!==void 0&&/^[^@\s]+@[^@\s]+\.[^@\s]+$/u.test(t)?t:void 0}function readNonEmptyString$1(e){return typeof e==`string`&&e.trim()!==``?e:void 0}function parseCodexJwtClaims(e){if(e!==void 0)try{return decodeJwt(e)}catch{return}}export{extractCodexAccountIdFromToken,extractCodexAccountLabelFromToken,readCodexJwtExpirationMs};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{extractCodexAccountIdFromToken,extractCodexAccountLabelFromToken,readCodexJwtExpirationMs}from"./auth.js";import{ChatGptSignInRequiredError}from"./oauth.js";import{ChatGptSignedOutError,isChatGptTokenFresh}from"./token.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{spawn}from"node:child_process";import{
|
|
1
|
+
import{extractCodexAccountIdFromToken,extractCodexAccountLabelFromToken,readCodexJwtExpirationMs}from"./auth.js";import{ChatGptSignInRequiredError}from"./oauth.js";import{ChatGptSignedOutError,isChatGptTokenFresh}from"./token.js";import{isErrnoCode,isObject}from"#shared/guards.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{spawn}from"node:child_process";import{createInterface}from"node:readline";var CodexBinaryNotFoundError=class extends Error{constructor(e){super("The `codex` CLI was not found.",{cause:e}),this.name=`CodexBinaryNotFoundError`}},CodexAppServerClient=class{#e;#t;#n;#r;constructor(e={}){this.#e=e.command??`codex`,this.#t=e.env,this.#n=e.spawnProcess??spawnCodexAppServer}async resolveToken(e){let t=await this.#i(e.forceRefresh);if(t.authMethod!==`chatgpt`||t.authToken===void 0)throw e.forceRefresh?new ChatGptSignInRequiredError:new ChatGptSignedOutError("ChatGPT subscription is not signed in to Codex. Run `codex login` or sign in from /model.");let n=tokenFromCodex(t.authToken);if(!e.forceRefresh&&!isChatGptTokenFresh(n,e.now())){if(t=await this.#i(!0),t.authMethod!==`chatgpt`||t.authToken===void 0)throw new ChatGptSignInRequiredError;n=tokenFromCodex(t.authToken)}return n}async#i(e){let t=await this.#a();try{return await t.getAuthStatus({refreshToken:e})}catch(e){throw t.exited&&(this.#r=void 0),e}}restart(){let e=this.#r;this.#r=void 0,e?.then(e=>e.close()).catch(()=>void 0)}#a(){return this.#r===void 0&&(this.#r=CodexAppServerConnection.start({command:this.#e,env:this.#t,spawnProcess:this.#n}).catch(e=>{throw this.#r=void 0,e})),this.#r}},CodexAppServerConnection=class CodexAppServerConnection{#e;#t;#n=new Map;#r=1;#i=!1;#a;constructor(e){this.#e=e,e.unref(),unrefStream(e.stdin),unrefStream(e.stdout),unrefStream(e.stderr),e.stderr.resume(),this.#t=createInterface({input:e.stdout}),this.#t.on(`line`,e=>this.#l(e)),e.once(`error`,e=>this.#u(e)),e.once(`exit`,(e,t)=>{let n=t===null?`code ${e??`unknown`}`:`signal ${t}`;this.#u(Error(`Codex app-server exited with ${n}.`))})}get exited(){return this.#i}close(){this.#e.kill(),this.#u(Error(`Codex app-server connection was restarted.`))}static async start(e){let t;try{t=e.spawnProcess(e.command,[`app-server`,`--stdio`],{env:e.env,stdio:[`pipe`,`pipe`,`pipe`]})}catch(e){throw appServerError(e)}let n=new CodexAppServerConnection(t);try{return await n.#o(`initialize`,{capabilities:null,clientInfo:{name:`eve`,title:`eve`,version:resolveInstalledPackageInfo().version}}),n.#s(`initialized`,{}),n}catch(e){throw t.kill(),e}}async getAuthStatus(e){let t=await this.#o(`getAuthStatus`,{includeToken:!0,refreshToken:e.refreshToken});if(!isObject(t))throw Error(`Codex app-server returned an invalid authentication response.`);return{...typeof t.authMethod==`string`&&{authMethod:t.authMethod},...typeof t.authToken==`string`&&{authToken:t.authToken},...typeof t.requiresOpenaiAuth==`boolean`&&{requiresOpenaiAuth:t.requiresOpenaiAuth}}}#o(e,t){if(this.#i)return Promise.reject(this.#a??Error(`Codex app-server is not running.`));let n=this.#r++,r=new Promise((e,t)=>{this.#n.set(n,{reject:t,resolve:e})});return this.#c({id:n,method:e,params:t}),r}#s(e,t){this.#c({method:e,params:t})}#c(e){this.#e.stdin.write(`${JSON.stringify(e)}\n`)}#l(e){let t;try{t=JSON.parse(e)}catch{return}if(typeof t.id!=`number`)return;let n=this.#n.get(t.id);if(n!==void 0){if(this.#n.delete(t.id),t.error!==void 0){n.reject(Error(t.error.message??`Codex app-server request failed (${t.error.code}).`));return}n.resolve(t.result)}}#u(e){if(!this.#i){this.#i=!0,this.#a=appServerError(e),this.#t.close();for(let e of this.#n.values())e.reject(this.#a);this.#n.clear()}}};function spawnCodexAppServer(e,t,n){return spawn(e,[...t],n)}function unrefStream(e){`unref`in e&&typeof e.unref==`function`&&e.unref()}function tokenFromCodex(e){let t=extractCodexAccountIdFromToken(e),n=extractCodexAccountLabelFromToken(e),r=readCodexJwtExpirationMs(e);return{token:e,...t!==void 0&&{accountId:t},...n!==void 0&&{accountLabel:n},...r!==void 0&&{expiresAt:r}}}function appServerError(e){if(isErrnoCode(e,`ENOENT`))return new CodexBinaryNotFoundError(e);let t=e instanceof Error?e.message:String(e);return Error(`Codex app-server is unavailable: ${t}`,{cause:e})}export{CodexAppServerClient,CodexBinaryNotFoundError};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CHATGPT_LOGIN_HINT,ChatGptSignInRequiredError,requestChatGptTokens}from"./oauth.js";import{ChatGptSignedOutError,isChatGptTokenFresh}from"./token.js";import{randomUUID}from"node:crypto";import{mkdir,rm,stat}from"node:fs/promises";import{
|
|
1
|
+
import{CHATGPT_LOGIN_HINT,ChatGptSignInRequiredError,requestChatGptTokens}from"./oauth.js";import{ChatGptSignedOutError,isChatGptTokenFresh}from"./token.js";import{randomUUID}from"node:crypto";import{mkdir,rm,stat}from"node:fs/promises";import{isErrnoCode,isObject}from"#shared/guards.js";import{dirname,join}from"node:path";import{homedir}from"node:os";import{setTimeout}from"node:timers/promises";import{secrets}from"#compiled/just-secrets/index.js";const SECRET_ID={service:`eve`,name:`chatgpt`},MAX_SECRET_BYTES=2560;var ChatGptInvalidStoredSessionError=class extends Error{constructor(){super(`The ChatGPT session in the OS secret store is invalid. Sign in again from /model to replace it.`)}};function createChatGptCredentialStore(e=join(homedir(),`.eve`,`auth`,`chatgpt.json`),t=secrets){let n;async function removeLegacyFile(){try{await rm(e,{force:!0})}catch{throw Error(`ChatGPT credentials are stored securely, but the old plaintext session could not be removed. Stop older eve processes, remove ~/.eve/auth/chatgpt.json, and retry.`)}}async function load(){let e;try{e=await t.get(SECRET_ID)}catch{throw secretStoreError()}if(e===null){n=void 0;return}let r;try{if(Buffer.byteLength(e)>MAX_SECRET_BYTES)throw Error();r=JSON.parse(e)}catch{throw new ChatGptInvalidStoredSessionError}if(!isObject(r)||typeof r.sessionId!=`string`||!r.sessionId||typeof r.refreshToken!=`string`||!r.refreshToken)throw new ChatGptInvalidStoredSessionError;let i={refreshToken:r.refreshToken,...typeof r.accountId==`string`&&{accountId:r.accountId},...typeof r.accountLabel==`string`&&{accountLabel:r.accountLabel}},a=n?.sessionId===r.sessionId?{...i,accessToken:n.credentials.accessToken,expiresAt:n.credentials.expiresAt}:i;return await removeLegacyFile(),{sessionId:r.sessionId,credentials:a}}let r={async read(){return(await load())?.credentials},async resolveToken(e){let t=await r.read();if(!t)throw e.forceRefresh?new ChatGptSignInRequiredError:new ChatGptSignedOutError(`ChatGPT subscription is not signed in. ${CHATGPT_LOGIN_HINT}`);let n=tokenFromCredentials(t)?.token;(e.forceRefresh||!isChatGptTokenFresh(tokenFromCredentials(t),e.now()))&&(t=await r.update(async t=>{if(!t)throw new ChatGptSignInRequiredError;return`accessToken`in t&&isChatGptTokenFresh(tokenFromCredentials(t),e.now())&&(!e.forceRefresh||t.accessToken!==n)?t:requestChatGptTokens({grant_type:`refresh_token`,refresh_token:t.refreshToken},{fetch:e.fetch,previous:t,now:e.now})}));let i=tokenFromCredentials(t);if(!i)throw Error(`ChatGPT access token is unavailable. ${CHATGPT_LOGIN_HINT}`);return i},async update(r,i){await mkdir(dirname(e),{recursive:!0,mode:448});let a=`${e}.lock`,o=Date.now()+35e3;for(;;)try{await mkdir(a,{mode:448});break}catch(e){if(!isErrnoCode(e,`EEXIST`))throw e;let t=await stat(a).catch(e=>{if(!isErrnoCode(e,`ENOENT`))throw e});if(t&&Date.now()-t.mtimeMs>6e5){await rm(a,{recursive:!0,force:!0});continue}if(Date.now()>=o)throw Error(`Another eve process is updating the ChatGPT session. Retry shortly.`);await setTimeout(100)}try{let e=i?.replace?void 0:await load(),a=await r(e?.credentials),o=e?.sessionId??randomUUID(),s=JSON.stringify({sessionId:o,refreshToken:a.refreshToken,...a.accountId&&{accountId:a.accountId},...a.accountLabel&&{accountLabel:a.accountLabel}});if(Buffer.byteLength(s)>MAX_SECRET_BYTES)throw Error(`The ChatGPT refresh session exceeds the OS secret store's 2,560-byte limit. Use an API-key model instead.`);try{await t.set({...SECRET_ID,value:s})}catch{throw secretStoreError()}return n={sessionId:o,credentials:a},await removeLegacyFile(),a}finally{await rm(a,{recursive:!0,force:!0})}}};return r}let defaultStore;function getDefaultChatGptCredentialStore(){return defaultStore??=createChatGptCredentialStore()}function tokenFromCredentials(e){if(`accessToken`in e)return{token:e.accessToken,expiresAt:e.expiresAt,...e.accountId&&{accountId:e.accountId},...e.accountLabel&&{accountLabel:e.accountLabel}}}function secretStoreError(){let e=process.platform===`linux`?`Install libsecret-tools and start or unlock a Secret Service keyring with a session D-Bus. Headless sessions may need an API-key model.`:process.platform===`win32`?`Allow Windows PowerShell and Credential Manager access in your user session.`:`Unlock your login keychain and allow credential access.`;return Error(`Could not access ChatGPT credentials in the OS secret store. ${e} Retry from /model.`)}export{ChatGptInvalidStoredSessionError,createChatGptCredentialStore,getDefaultChatGptCredentialStore};
|
|
@@ -77,15 +77,17 @@ export interface WithEveOptions {
|
|
|
77
77
|
readonly devServerTimeoutMs?: number;
|
|
78
78
|
/**
|
|
79
79
|
* Path to the eve application root, relative to `process.cwd()` unless
|
|
80
|
-
* absolute. Defaults to the Next.js app root.
|
|
80
|
+
* absolute. Defaults to the Next.js app root when it is not an eve workspace.
|
|
81
81
|
*/
|
|
82
82
|
readonly eveRoot?: string;
|
|
83
83
|
/**
|
|
84
84
|
* Named eve agents to mount under `/eve/agents/<name>/eve/v1/*`.
|
|
85
85
|
*
|
|
86
|
-
* Use this when one Next.js app needs to talk to multiple eve agents
|
|
87
|
-
*
|
|
88
|
-
*
|
|
86
|
+
* Use this when one Next.js app needs to talk to multiple eve agents outside
|
|
87
|
+
* a project-level `agents/` workspace. When unset, withEve discovers that
|
|
88
|
+
* workspace's members automatically. Do not combine with {@link eveRoot};
|
|
89
|
+
* the single-agent form remains the shorthand for one unnamed agent mounted
|
|
90
|
+
* at `/eve/v1/*`.
|
|
89
91
|
*/
|
|
90
92
|
readonly agents?: WithEveAgentsConfig;
|
|
91
93
|
/**
|