experimental-ash 0.42.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/docs/public/README.md +8 -8
  3. package/dist/docs/public/advanced/{auth-and-route-protection.md → auth-and-route-protection.mdx} +11 -0
  4. package/dist/docs/public/advanced/context-control.md +4 -4
  5. package/dist/docs/public/advanced/{evals.md → evals.mdx} +11 -1
  6. package/dist/docs/public/advanced/{hooks.md → hooks.mdx} +28 -40
  7. package/dist/docs/public/advanced/instrumentation.md +92 -3
  8. package/dist/docs/public/advanced/project-layout.md +5 -5
  9. package/dist/docs/public/advanced/runs-and-streaming.md +8 -2
  10. package/dist/docs/public/advanced/session-context.md +1 -1
  11. package/dist/docs/public/advanced/typescript-api.md +49 -6
  12. package/dist/docs/public/advanced/vercel-deployment.md +1 -1
  13. package/dist/docs/public/agent-ts.md +5 -5
  14. package/dist/docs/public/channels/{discord.md → discord.mdx} +11 -0
  15. package/dist/docs/public/channels/index.md +10 -10
  16. package/dist/docs/public/channels/{slack.md → slack.mdx} +11 -0
  17. package/dist/docs/public/channels/{teams.md → teams.mdx} +12 -0
  18. package/dist/docs/public/channels/{telegram.md → telegram.mdx} +11 -0
  19. package/dist/docs/public/channels/{twilio.md → twilio.mdx} +11 -0
  20. package/dist/docs/public/{connections.md → connections.mdx} +18 -6
  21. package/dist/docs/public/frontend/README.md +16 -0
  22. package/dist/docs/public/frontend/meta.json +3 -0
  23. package/dist/docs/public/frontend/nextjs.md +192 -0
  24. package/dist/docs/public/frontend/use-ash-agent.md +332 -0
  25. package/dist/docs/public/{getting-started.md → getting-started.mdx} +12 -1
  26. package/dist/docs/public/{human-in-the-loop.md → human-in-the-loop.mdx} +12 -1
  27. package/dist/docs/public/meta.json +1 -0
  28. package/dist/docs/public/sandbox.md +1 -1
  29. package/dist/docs/public/{schedules.md → schedules.mdx} +9 -0
  30. package/dist/docs/public/skills.md +2 -2
  31. package/dist/docs/public/{subagents.md → subagents.mdx} +10 -0
  32. package/dist/docs/public/{tools.md → tools.mdx} +41 -26
  33. package/dist/src/channel/adapter.d.ts +13 -0
  34. package/dist/src/channel/instrumentation.d.ts +10 -0
  35. package/dist/src/channel/instrumentation.js +1 -0
  36. package/dist/src/channel/send.js +1 -1
  37. package/dist/src/channel/types.d.ts +16 -0
  38. package/dist/src/cli/commands/channels.d.ts +2 -1
  39. package/dist/src/cli/commands/channels.js +1 -1
  40. package/dist/src/compiler/manifest.d.ts +13 -1
  41. package/dist/src/compiler/manifest.js +1 -1
  42. package/dist/src/compiler/module-map.js +1 -1
  43. package/dist/src/compiler/normalize-manifest.js +1 -1
  44. package/dist/src/compiler/normalize-skill.d.ts +15 -2
  45. package/dist/src/compiler/normalize-skill.js +1 -1
  46. package/dist/src/compiler/normalize-tool.js +1 -1
  47. package/dist/src/context/dynamic-skill-lifecycle.d.ts +23 -0
  48. package/dist/src/context/dynamic-skill-lifecycle.js +1 -0
  49. package/dist/src/context/dynamic-tool-lifecycle.d.ts +2 -0
  50. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  51. package/dist/src/context/hook-lifecycle.d.ts +4 -6
  52. package/dist/src/context/hook-lifecycle.js +1 -1
  53. package/dist/src/context/keys.d.ts +6 -4
  54. package/dist/src/context/keys.js +1 -1
  55. package/dist/src/context/providers/connection.d.ts +9 -0
  56. package/dist/src/context/providers/connection.js +1 -1
  57. package/dist/src/context/providers/sandbox.js +1 -1
  58. package/dist/src/execution/ash-workflow-attributes.d.ts +118 -0
  59. package/dist/src/execution/ash-workflow-attributes.js +1 -0
  60. package/dist/src/execution/channel-context.d.ts +5 -0
  61. package/dist/src/execution/channel-context.js +1 -0
  62. package/dist/src/execution/create-session-step.d.ts +28 -1
  63. package/dist/src/execution/create-session-step.js +1 -1
  64. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  65. package/dist/src/execution/durable-session-store.d.ts +7 -0
  66. package/dist/src/execution/runtime-context.js +1 -1
  67. package/dist/src/execution/sandbox/prewarm.js +1 -1
  68. package/dist/src/execution/session.d.ts +6 -0
  69. package/dist/src/execution/session.js +2 -2
  70. package/dist/src/execution/skills/instructions.d.ts +3 -2
  71. package/dist/src/execution/subagent-tool.js +1 -1
  72. package/dist/src/execution/workflow-entry.js +1 -1
  73. package/dist/src/execution/workflow-steps.js +1 -1
  74. package/dist/src/harness/attachment-staging.js +1 -1
  75. package/dist/src/harness/code-mode.d.ts +0 -5
  76. package/dist/src/harness/code-mode.js +1 -1
  77. package/dist/src/harness/emission.d.ts +1 -1
  78. package/dist/src/harness/emission.js +1 -1
  79. package/dist/src/harness/instrumentation-config.d.ts +1 -1
  80. package/dist/src/harness/instrumentation-metadata.d.ts +23 -0
  81. package/dist/src/harness/instrumentation-metadata.js +1 -0
  82. package/dist/src/harness/otel-integration.d.ts +2 -2
  83. package/dist/src/harness/otel-integration.js +1 -1
  84. package/dist/src/harness/step-hooks.js +1 -1
  85. package/dist/src/harness/tool-loop.js +1 -1
  86. package/dist/src/harness/turn-tag-state.d.ts +50 -0
  87. package/dist/src/harness/turn-tag-state.js +1 -0
  88. package/dist/src/harness/types.d.ts +11 -2
  89. package/dist/src/internal/application/package.js +1 -1
  90. package/dist/src/internal/authored-definition/schema-backed.d.ts +0 -1
  91. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  92. package/dist/src/internal/instrumentation.d.ts +39 -0
  93. package/dist/src/internal/instrumentation.js +1 -0
  94. package/dist/src/internal/workflow/builtins.d.ts +32 -0
  95. package/dist/src/internal/workflow/builtins.js +1 -1
  96. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +1 -1
  97. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +1 -1
  98. package/dist/src/internal/workflow-bundle/workflow-core-shim.d.ts +34 -0
  99. package/dist/src/internal/workflow-bundle/workflow-core-shim.js +1 -1
  100. package/dist/src/internal/workflow-bundle/workflow-transformer.js +1 -1
  101. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  102. package/dist/src/packages/ash-scaffold/src/steps/run-add-to-agent.js +2 -2
  103. package/dist/src/public/channels/slack/attachments.js +1 -1
  104. package/dist/src/public/channels/slack/index.d.ts +1 -1
  105. package/dist/src/public/channels/slack/slackChannel.d.ts +6 -0
  106. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  107. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  108. package/dist/src/public/channels/twilio/index.d.ts +1 -1
  109. package/dist/src/public/channels/twilio/twilioChannel.d.ts +6 -0
  110. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  111. package/dist/src/public/definitions/defineChannel.d.ts +12 -2
  112. package/dist/src/public/definitions/defineChannel.js +1 -1
  113. package/dist/src/public/definitions/hook.d.ts +3 -11
  114. package/dist/src/public/definitions/instrumentation.d.ts +88 -2
  115. package/dist/src/public/definitions/skill.d.ts +5 -0
  116. package/dist/src/public/definitions/tool.d.ts +25 -66
  117. package/dist/src/public/definitions/tool.js +1 -1
  118. package/dist/src/public/instrumentation/index.d.ts +1 -4
  119. package/dist/src/public/instrumentation/index.js +1 -1
  120. package/dist/src/public/skills/index.d.ts +2 -0
  121. package/dist/src/public/skills/index.js +1 -1
  122. package/dist/src/public/tools/index.d.ts +2 -2
  123. package/dist/src/public/tools/index.js +1 -1
  124. package/dist/src/runtime/agent/mock-model-adapter.js +4 -7
  125. package/dist/src/runtime/agent/mock-model-skill-selection.d.ts +9 -0
  126. package/dist/src/runtime/agent/mock-model-skill-selection.js +4 -0
  127. package/dist/src/runtime/attributes/emit.d.ts +73 -0
  128. package/dist/src/runtime/attributes/emit.js +1 -0
  129. package/dist/src/runtime/channels/registry.js +1 -1
  130. package/dist/src/runtime/connections/mcp-client.js +1 -1
  131. package/dist/src/runtime/framework-tools/code-mode-connection-auth.d.ts +2 -0
  132. package/dist/src/runtime/framework-tools/connection-search-dynamic.d.ts +34 -0
  133. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -0
  134. package/dist/src/runtime/framework-tools/index.d.ts +7 -5
  135. package/dist/src/runtime/framework-tools/index.js +1 -1
  136. package/dist/src/runtime/prompt/connections.js +1 -1
  137. package/dist/src/runtime/resolve-agent-graph.js +1 -1
  138. package/dist/src/runtime/resolve-agent.js +1 -1
  139. package/dist/src/runtime/resolve-dynamic-skill.d.ts +8 -0
  140. package/dist/src/runtime/resolve-dynamic-skill.js +1 -0
  141. package/dist/src/runtime/resolve-dynamic-tool.js +1 -1
  142. package/dist/src/runtime/sessions/compiled-agent-cache.js +1 -1
  143. package/dist/src/runtime/sessions/runtime-context-keys.js +1 -1
  144. package/dist/src/runtime/types.d.ts +13 -4
  145. package/dist/src/shared/dynamic-tool-definition.d.ts +51 -76
  146. package/dist/src/shared/dynamic-tool-definition.js +1 -1
  147. package/dist/src/shared/guards.d.ts +14 -0
  148. package/dist/src/shared/guards.js +1 -1
  149. package/dist/src/shared/skill-definition.d.ts +5 -4
  150. package/dist/src/shared/tool-definition.d.ts +12 -0
  151. package/package.json +2 -1
  152. package/dist/src/runtime/framework-tools/connection-search.d.ts +0 -57
  153. package/dist/src/runtime/framework-tools/connection-search.js +0 -1
  154. package/dist/src/runtime/framework-tools/connection-tools.d.ts +0 -55
  155. package/dist/src/runtime/framework-tools/connection-tools.js +0 -1
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Per-turn rolling token-usage accumulator for `$ash.*` observability
3
+ * tags. Lives on `session.state` so the totals survive workflow step
4
+ * boundaries the way the rest of the harness state does.
5
+ *
6
+ * The harness runs each turn as a sequence of `"use step"` invocations
7
+ * (one per tool-loop iteration). Each step knows its own
8
+ * `result.usage`, but the dashboard cares about totals **per turn**.
9
+ * The workflow runtime's attribute store is "last write wins" per key,
10
+ * so the simplest cumulative pattern is: read the previous total from
11
+ * `session.state`, add the new step's usage, write the running total
12
+ * back. The most recent emit then carries the final per-turn total.
13
+ *
14
+ * `turnId` keys the state so a fresh turn starts at zero without
15
+ * relying on a separate "reset" code path — when the harness moves to
16
+ * a new turn, the stale totals are discarded automatically.
17
+ */
18
+ import type { HarnessSession, SessionStateMap } from "#harness/types.js";
19
+ /**
20
+ * Rolling token usage for the in-flight turn.
21
+ *
22
+ * `turnId` is the in-flight turn's stable id; when the harness step
23
+ * runs in a different turn (or with the empty-string between-turns
24
+ * sentinel), totals are reset.
25
+ */
26
+ export interface TurnUsageState {
27
+ readonly cacheReadTokens: number;
28
+ readonly inputTokens: number;
29
+ readonly outputTokens: number;
30
+ readonly turnId: string;
31
+ }
32
+ /** Reads the stored per-turn token state, or `undefined` when absent. */
33
+ export declare function getTurnUsageState(state: SessionStateMap | undefined): TurnUsageState | undefined;
34
+ /** Writes per-turn token state onto a new copy of the session. */
35
+ export declare function setTurnUsageState(session: HarnessSession, next: TurnUsageState): HarnessSession;
36
+ /**
37
+ * Folds one step's `usage` into the running per-turn totals. When
38
+ * `turnId` differs from the stored state (e.g. a new turn just
39
+ * started), the previous totals are discarded — fresh turns start at
40
+ * zero without an explicit reset path.
41
+ */
42
+ export declare function accumulateTurnUsage(input: {
43
+ readonly previous: TurnUsageState | undefined;
44
+ readonly turnId: string;
45
+ readonly usage: {
46
+ readonly cachedInputTokens?: number;
47
+ readonly inputTokens?: number;
48
+ readonly outputTokens?: number;
49
+ };
50
+ }): TurnUsageState;
@@ -0,0 +1 @@
1
+ const HARNESS_TURN_USAGE_STATE_KEY=`ash.harness.turnUsage`,ZERO_USAGE={cacheReadTokens:0,inputTokens:0,outputTokens:0};function getTurnUsageState(t){return t?.[HARNESS_TURN_USAGE_STATE_KEY]}function setTurnUsageState(t,n){return{...t,state:{...t.state,[HARNESS_TURN_USAGE_STATE_KEY]:n}}}function accumulateTurnUsage(e){let n=e.previous!==void 0&&e.previous.turnId===e.turnId?e.previous:{...ZERO_USAGE,turnId:e.turnId};return{turnId:e.turnId,cacheReadTokens:n.cacheReadTokens+(e.usage.cachedInputTokens??0),inputTokens:n.inputTokens+(e.usage.inputTokens??0),outputTokens:n.outputTokens+(e.usage.outputTokens??0)}}export{accumulateTurnUsage,getTurnUsageState,setTurnUsageState};
@@ -52,6 +52,15 @@ export interface HarnessSession {
52
52
  readonly compaction: CompactionConfig;
53
53
  readonly continuationToken: string;
54
54
  readonly history: ModelMessage[];
55
+ /**
56
+ * Stable identifier of the top user-facing session in the dispatch
57
+ * chain. For a top-level session this field is `undefined` and
58
+ * `sessionId` itself is the root. For any delegated subagent session,
59
+ * `rootSessionId` carries the original root sessionId so descendant
60
+ * dispatch sites (and observability tags) can attribute work back to
61
+ * the user-facing session without walking the chain.
62
+ */
63
+ readonly rootSessionId?: string;
55
64
  readonly sessionId: string;
56
65
  readonly sandboxState?: SandboxState;
57
66
  readonly state?: SessionStateMap;
@@ -138,7 +147,7 @@ export type HarnessToolMap = ReadonlyMap<string, HarnessToolDefinition>;
138
147
  * event handler, then injected into the harness so it can emit lifecycle
139
148
  * events without knowing about writables or handlers.
140
149
  */
141
- export type HarnessEmitFn = (event: HandleMessageStreamEvent) => Promise<void>;
150
+ export type HarnessEmitFn = (event: HandleMessageStreamEvent, messages?: readonly import("ai").ModelMessage[]) => Promise<void>;
142
151
  /**
143
152
  * Unified event handler: emits the event to the stream, then
144
153
  * dispatches to hook subscribers and dynamic tool resolvers.
@@ -147,7 +156,7 @@ export type HarnessEmitFn = (event: HandleMessageStreamEvent) => Promise<void>;
147
156
  * every event goes through channel adapter, stream write, hooks,
148
157
  * and dynamic tool dispatch in one call.
149
158
  */
150
- export type HandleEventFn = (event: HandleMessageStreamEvent) => Promise<void>;
159
+ export type HandleEventFn = (event: HandleMessageStreamEvent, messages?: readonly import("ai").ModelMessage[]) => Promise<void>;
151
160
  /**
152
161
  * Dependencies injected into the tool-loop harness at construction time.
153
162
  */
@@ -1 +1 @@
1
- import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.42.0`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
1
+ import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.43.0`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
@@ -21,7 +21,6 @@ type NormalizedToolEntry = {
21
21
  readonly kind: "disabled";
22
22
  } | {
23
23
  readonly kind: "dynamic-tool";
24
- readonly identity: "single" | "multi";
25
24
  readonly eventNames: readonly DynamicToolEventName[];
26
25
  };
27
26
  /**
@@ -1 +1 @@
1
- import{expectFunction,expectObjectRecord,expectOnlyKnownKeys,expectString}from"#internal/authored-module.js";import{isDynamicToolSentinel,isDynamicToolsSentinel}from"#shared/dynamic-tool-definition.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{isDisabledToolSentinel}from"#public/definitions/tool.js";function normalizeToolDefinition(t,n){if(isDynamicToolsSentinel(t))return{kind:`dynamic-tool`,identity:`multi`,eventNames:Object.keys(t.events)};if(isDynamicToolSentinel(t))return{kind:`dynamic-tool`,identity:`single`,eventNames:Object.keys(t.events)};if(isDisabledToolSentinel(t))return{kind:`disabled`};let r=expectObjectRecord(t,n);expectOnlyKnownKeys(r,[`description`,`execute`,`inputSchema`,`needsApproval`,`onCompact`,`retentionPolicy`,`toModelOutput`],n);let i=r.inputSchema===void 0?null:normalizeJsonSchemaDefinition(r.inputSchema),a={description:expectString(r.description,n),execute:expectFunction(r.execute,n),inputSchema:i};if(r.onCompact!==void 0&&expectFunction(r.onCompact,n),r.needsApproval!==void 0&&expectFunction(r.needsApproval,n),r.retentionPolicy!==void 0){let e=r.retentionPolicy;if(e!==`auto`&&e!==`keep`&&typeof e!=`function`)throw Error(`${n} Expected \`retentionPolicy\` to be "auto", "keep", or a function.`)}return r.toModelOutput!==void 0&&expectFunction(r.toModelOutput,n),{kind:`tool`,definition:a}}export{normalizeToolDefinition};
1
+ import{expectFunction,expectObjectRecord,expectOnlyKnownKeys,expectString}from"#internal/authored-module.js";import{isDynamicSentinel}from"#shared/dynamic-tool-definition.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{isDisabledToolSentinel}from"#public/definitions/tool.js";function normalizeToolDefinition(t,n){if(isDynamicSentinel(t))return{kind:`dynamic-tool`,eventNames:Object.keys(t.events)};if(isDisabledToolSentinel(t))return{kind:`disabled`};let r=expectObjectRecord(t,n);expectOnlyKnownKeys(r,[`description`,`execute`,`inputSchema`,`needsApproval`,`onCompact`,`retentionPolicy`,`toModelOutput`],n);let i=r.inputSchema===void 0?null:normalizeJsonSchemaDefinition(r.inputSchema),a={description:expectString(r.description,n),execute:expectFunction(r.execute,n),inputSchema:i};if(r.onCompact!==void 0&&expectFunction(r.onCompact,n),r.needsApproval!==void 0&&expectFunction(r.needsApproval,n),r.retentionPolicy!==void 0){let e=r.retentionPolicy;if(e!==`auto`&&e!==`keep`&&typeof e!=`function`)throw Error(`${n} Expected \`retentionPolicy\` to be "auto", "keep", or a function.`)}return r.toModelOutput!==void 0&&expectFunction(r.toModelOutput,n),{kind:`tool`,definition:a}}export{normalizeToolDefinition};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Shared instrumentation primitives used by both the channel projection
3
+ * builder (`#channel/instrumentation.ts`) and the harness telemetry
4
+ * builder (`#harness/instrumentation-metadata.ts`).
5
+ *
6
+ * Both layers resolve a user-authored projector callback into a plain
7
+ * record and reason about the same channel-kind vocabulary. Keeping that
8
+ * vocabulary and the defensive resolution shell in one place stops the
9
+ * two sites from drifting (e.g. the framework-kind set growing in one
10
+ * file but not the other).
11
+ */
12
+ import { type Logger } from "#internal/logging.js";
13
+ import type { InstrumentationChannelKind } from "#public/instrumentation/index.js";
14
+ /**
15
+ * Returns `true` when `kind` is a valid instrumentation channel kind: a
16
+ * framework kind (`"http"`, `"schedule"`, `"subagent"`) or a
17
+ * path-derived `channel:<name>` kind.
18
+ */
19
+ export declare function isInstrumentationChannelKind(kind: string): kind is InstrumentationChannelKind;
20
+ /**
21
+ * Narrows a raw kind string to the public {@link InstrumentationChannelKind}
22
+ * union, falling back to `"unknown"` for anything unrecognized or absent.
23
+ */
24
+ export declare function normalizeInstrumentationChannelKind(rawKind: string | undefined): InstrumentationChannelKind;
25
+ /**
26
+ * Invokes a user-authored instrumentation projector defensively.
27
+ *
28
+ * Returns the JSON object the projector produced, or `undefined` when
29
+ * it threw, returned a `Promise`, returned a non-record, or included
30
+ * values outside Ash's JSON contract. Every rejection path is
31
+ * warning-only so instrumentation can never break the turn. Per-value
32
+ * shaping (for example reserved-key filtering) is left to the caller,
33
+ * since the channel and harness expose different value shapes.
34
+ */
35
+ export declare function resolveInstrumentationProjection(input: {
36
+ readonly invoke: () => unknown;
37
+ readonly log: Logger;
38
+ readonly source: string;
39
+ }): Record<string, unknown> | undefined;
@@ -0,0 +1 @@
1
+ import{formatError}from"#internal/logging.js";import{isPlainRecord,isThenable}from"#shared/guards.js";import{parseJsonObject}from"#shared/json.js";const FRAMEWORK_CHANNEL_KINDS=new Set([`http`,`schedule`,`subagent`]);function isInstrumentationChannelKind(e){return e.startsWith(`channel:`)||FRAMEWORK_CHANNEL_KINDS.has(e)}function normalizeInstrumentationChannelKind(e){return e!==void 0&&isInstrumentationChannelKind(e)?e:`unknown`}function resolveInstrumentationProjection(n){let{invoke:r,log:i,source:a}=n,o;try{o=r()}catch(t){i.warn(`ignoring instrumentation metadata after projector failure`,{error:formatError(t),source:a});return}if(isThenable(o)){i.warn(`ignoring instrumentation metadata because it returned a Promise`,{source:a}),Promise.resolve(o).catch(t=>{i.warn(`ignored instrumentation metadata Promise rejected`,{error:formatError(t),source:a})});return}if(!isPlainRecord(o)){i.warn(`ignoring instrumentation metadata because it is not a record`,{source:a});return}try{return parseJsonObject(o)}catch(t){i.warn(`ignoring instrumentation metadata because it is outside the JSON contract`,{error:formatError(t),source:a});return}}export{isInstrumentationChannelKind,normalizeInstrumentationChannelKind,resolveInstrumentationProjection};
@@ -7,3 +7,35 @@
7
7
  export declare function __builtin_response_array_buffer(this: Request | Response): Promise<ArrayBuffer>;
8
8
  export declare function __builtin_response_json(this: Request | Response): Promise<unknown>;
9
9
  export declare function __builtin_response_text(this: Request | Response): Promise<string>;
10
+ /**
11
+ * Step bridge for `experimental_setAttributes`.
12
+ *
13
+ * Mirrors the `__builtin_set_attributes` step from
14
+ * `@workflow/workflow/internal/builtins`. The workflow-body shim's
15
+ * `experimental_setAttributes` (in `internal/workflow-bundle/workflow-core-shim.ts`)
16
+ * dispatches into the workflow runtime with the step id
17
+ * `"__builtin_set_attributes"`; the runtime walks the deployment's step
18
+ * registry to resolve it, so the step has to live inside an
19
+ * Ash-vendored builtins module that the registry visits. The Ash bundler
20
+ * already pulls this file in via `resolveWorkflowModulePath("workflow/internal/builtins")`,
21
+ * so adding the function here is sufficient to register it.
22
+ *
23
+ * Implementation notes — kept intentionally close to the upstream:
24
+ * - Reads world and run id directly from the runtime's `globalThis`
25
+ * symbols rather than importing `@workflow/core`. Importing the
26
+ * compiled core from a step file would re-introduce the bundling
27
+ * chain we want to keep out of step bodies.
28
+ * - Treats missing world support as a silent best-effort no-op with a
29
+ * single process-wide warning, matching upstream behaviour and the
30
+ * contract on `setAshAttributes`.
31
+ * - On any other error, retries up to `ASH_INTERNAL_ATTRIBUTES_MAX_ATTEMPTS - 1`
32
+ * times via the runtime's normal step retry path, then degrades to a
33
+ * `console.error` so failed attribute writes never escalate into a
34
+ * `FatalError` and tear down the user's agent run.
35
+ */
36
+ export declare function __builtin_set_attributes(changes: Array<{
37
+ key: string;
38
+ value: string | null;
39
+ }>, options?: {
40
+ allowReservedAttributes?: boolean;
41
+ }): Promise<void>;
@@ -1 +1 @@
1
- async function __builtin_response_array_buffer(){"use step";return await this.arrayBuffer()}async function __builtin_response_json(){"use step";return await this.json()}async function __builtin_response_text(){"use step";return await this.text()}export{__builtin_response_array_buffer,__builtin_response_json,__builtin_response_text};
1
+ async function __builtin_response_array_buffer(){"use step";return await this.arrayBuffer()}async function __builtin_response_json(){"use step";return await this.json()}async function __builtin_response_text(){"use step";return await this.text()}const ASH_UNSUPPORTED_WORLD_WARNED=Symbol.for(`@workflow/setAttributes//unsupportedWorldWarned`);function formatUnknownError(e){return e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e)}async function __builtin_set_attributes(t,n){"use step";if(t.length===0)return;let r=globalThis,i=r[Symbol.for(`WORKFLOW_STEP_CONTEXT_STORAGE`)]?.getStore?.(),a=typeof i?.stepMetadata?.attempt==`number`?i.stepMetadata.attempt:3,o=r[Symbol.for(`@workflow/world//cache`)];if(typeof o?.runs?.experimentalSetAttributes!=`function`){if(r[ASH_UNSUPPORTED_WORLD_WARNED]!==!0){r[ASH_UNSUPPORTED_WORLD_WARNED]=!0;let t=o?.name===void 0?``:` (${o.name})`;console.warn(`[ash] setAttributes: the current world implementation${t} does not implement experimentalSetAttributes; this call (and any subsequent setAttributes calls in this process) is a no-op. Attributes will become available once the world adapter adds support.`)}return}try{let e=i?.workflowMetadata?.workflowRunId;if(e===void 0)throw Error(`__builtin_set_attributes: no workflow run id available in step context`);await o.runs.experimentalSetAttributes(e,t,n)}catch(e){if(a<3)throw e;console.error(`[ash] setAttributes: failed to post tags after 3 attempts; dropping the internal attribute write. ${formatUnknownError(e)}`)}}__builtin_set_attributes.maxRetries=2;export{__builtin_response_array_buffer,__builtin_response_json,__builtin_response_text,__builtin_set_attributes};
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Compiler transform for dynamic tool files.
3
3
  *
4
- * Hoists inline `execute` functions from `defineTools`/`defineTool`
4
+ * Hoists inline `execute` functions from `defineDynamic`
5
5
  * event handler return values to module-scope named functions
6
6
  * registered in the global step registry. The workflow SDK then
7
7
  * handles serialization and replay.
@@ -1,4 +1,4 @@
1
- import{loadNitroRolldownParseAst}from"#internal/bundler/nitro-rolldown.js";let transformCounter=0;async function transformDynamicToolExecute(e,t){if(!t.includes(`defineTools`)&&!t.includes(`defineTool`)||!t.includes(`events`)||!t.includes(`execute`))return null;let n=findDynamicToolHandlers(t,await parseSource(e,t));return n.every(e=>e.executes.length===0)?null:applyTransform(t,n)}async function parseSource(t,n){let{parseAst:r}=await loadNitroRolldownParseAst();return r(n,{astType:`ts`,lang:inferLang(t),range:!0,sourceType:`module`},t)}function inferLang(e){return e.endsWith(`.tsx`)?`tsx`:e.endsWith(`.jsx`)?`jsx`:e.endsWith(`.js`)||e.endsWith(`.mjs`)||e.endsWith(`.cjs`)?`js`:`ts`}function findDynamicToolHandlers(e,t){let n=[];return walkNode(t,t=>{if(t.type===`CallExpression`&&t.callee?.type===`Identifier`&&(t.callee.name===`defineTools`||t.callee.name===`defineTool`)&&t.arguments?.length===1){let r=t.arguments[0];if(r.type===`ObjectExpression`){let t=findProperty(r,`events`);t?.value&&t.value.type===`ObjectExpression`&&collectHandlers(e,t.value,n)}return!1}return!0}),n}function collectHandlers(e,t,n){for(let r of t.properties??[]){if(r.type!==`Property`)continue;let t=r.value;if(!t||t.type!==`ArrowFunctionExpression`&&t.type!==`FunctionExpression`)continue;let i=t.body;if(!i)continue;let a=findBlockBodyStart(i);if(a===null)continue;let o=extractParamNames(t),s=collectScopeVarDeclarations(i),c=findExecuteFunctions(e,i);c.length>0&&n.push({handlerNode:t,bodyStart:a,scopeVars:s,paramNames:o,executes:c})}}function findBlockBodyStart(e){return e.type===`BlockStatement`&&e.start!==void 0?e.start:typeof e.body==`object`&&!Array.isArray(e.body)&&e.body?.type===`BlockStatement`&&e.body.start!==void 0?e.body.start:null}function extractParamNames(e){let t=[];for(let n of e.params??[])n.type===`Identifier`&&n.name&&t.push(n.name);return t}function collectScopeVarDeclarations(e){let t=[];return collectVarsRecursive(e,t),t}function collectVarsRecursive(e,t){if(!e||e.type===`FunctionExpression`||e.type===`ArrowFunctionExpression`||e.type===`FunctionDeclaration`)return;if(e.type===`VariableDeclaration`)for(let n of e.declarations??[])collectDeclaredNames(n,t);e.type===`ForStatement`&&e.init&&collectVarsRecursive(e.init,t);let n=e;if((e.type===`ForInStatement`||e.type===`ForOfStatement`)&&e.left&&collectVarsRecursive(e.left,t),Array.isArray(e.body))for(let n of e.body)collectVarsRecursive(n,t);else e.body&&typeof e.body==`object`&&`type`in e.body&&collectVarsRecursive(e.body,t);if(e.declarations)for(let n of e.declarations)collectVarsRecursive(n,t);if(e.expression&&collectVarsRecursive(e.expression,t),Array.isArray(n.consequent))for(let e of n.consequent)collectVarsRecursive(e,t);else n.consequent&&collectVarsRecursive(n.consequent,t);if(n.alternate&&collectVarsRecursive(n.alternate,t),n.block&&collectVarsRecursive(n.block,t),n.handler&&collectVarsRecursive(n.handler,t),n.finalizer&&collectVarsRecursive(n.finalizer,t),n.cases&&Array.isArray(n.cases))for(let e of n.cases)collectVarsRecursive(e,t)}function collectDeclaredNames(e,t){e.type===`VariableDeclarator`&&collectPatternNames(e.id,t)}function collectPatternNames(e,t){if(e){if(e.type===`Identifier`&&e.name){t.push(e.name);return}if(e.type===`ObjectPattern`)for(let n of e.properties??[])n.type===`Property`?collectPatternNames(n.value,t):n.type===`RestElement`&&collectPatternNames(n.argument,t);if(e.type===`ArrayPattern`)for(let n of e.elements??[])n&&collectPatternNames(n,t)}}function findExecuteFunctions(e,t){let n=[];return walkForExecuteProps(e,t,n,[]),n}function walkForExecuteProps(e,n,r,i){if(!n)return;if(n.type===`FunctionExpression`||n.type===`ArrowFunctionExpression`||n.type===`FunctionDeclaration`){let t=extractParamNames(n),a=n.body;if(!a)return;let o=collectScopeVarDeclarations(a),s=[...i,{params:t,vars:o}];a.type,walkForExecuteProps(e,a,r,s);return}if(n.type===`CallExpression`&&n.callee?.type===`Identifier`&&n.callee.name===`tool`&&n.arguments?.length===1&&n.arguments[0].type===`ObjectExpression`){let a=n.arguments[0];for(let n of a.properties??[])if(n.type===`Property`&&!n.computed&&n.key?.type===`Identifier`&&n.key.name===`execute`&&n.start!==void 0&&n.end!==void 0){let a=n.value;if(!a||a.start===void 0||a.end===void 0)continue;let o=a.type===`FunctionExpression`||a.type===`ArrowFunctionExpression`,s=n.method===!0;if(o||s){let o=extractFnParams(e,a),s=extractFnBody(e,a),c=a.async===!0;r.push({propStart:n.start,propEnd:n.end,fnSource:e.slice(a.start,a.end),isAsync:c,params:o,body:s,hoistedName:`__ash_dynamic_exec_${transformCounter++}`,nestedScopes:i})}}return}let walk=t=>walkForExecuteProps(e,t,r,i);if(Array.isArray(n.body))for(let e of n.body)walk(e);else n.body&&typeof n.body==`object`&&`type`in n.body&&walk(n.body);if(n.properties)for(let e of n.properties)e.value&&typeof e.value==`object`&&`type`in e.value&&walk(e.value);if(n.callee&&walk(n.callee),n.arguments)for(let e of n.arguments)walk(e);if(n.expression&&walk(n.expression),n.argument&&walk(n.argument),n.init&&walk(n.init),n.left&&walk(n.left),n.right&&walk(n.right),n.declarations)for(let e of n.declarations)walk(e);let a=n;if(Array.isArray(a.consequent))for(let e of a.consequent)walk(e);else a.consequent&&walk(a.consequent);if(a.alternate&&walk(a.alternate),a.block&&walk(a.block),a.handler&&walk(a.handler),a.finalizer&&walk(a.finalizer),a.cases&&Array.isArray(a.cases))for(let e of a.cases)walk(e)}function extractFnParams(e,t){if(!t.params||t.params.length===0)return``;let n=t.params[0],r=t.params[t.params.length-1];return n.start===void 0||r.end===void 0?``:e.slice(n.start,r.end)}function extractFnBody(e,t){let n=t.body;if(!n||n.start===void 0||n.end===void 0)return`{}`;let r=e.slice(n.start,n.end);return t.type===`ArrowFunctionExpression`&&n.type!==`BlockStatement`?`{ return ${r}; }`:r}function applyTransform(e,t){let n=[],r=[],i=[],a=[];for(let e of t)for(let t of e.executes){let o=[...e.paramNames,...e.scopeVars,...t.nestedScopes.flatMap(e=>[...e.params,...e.vars])],s=new Set,c=[];for(let e=o.length-1;e>=0;e--)s.has(o[e])||(s.add(o[e]),c.unshift(o[e]));let l=t.body,u=extractExecuteParamNames(t.params),d=c.filter(e=>!u.has(e)&&RegExp(`\\b${escapeForRegex(e)}\\b`).test(l)),f=d.length>0?`{ ${d.join(`, `)} }`:`{}`,p=t.isAsync?`async `:``,m=d.length>0?`const ${f} = __vars;\n `:``,h=t.params,g=h?`__vars, ${h}`:`__vars`,_=t.body.slice(1,-1).trim(),v=`ash:dynamic-tool//${t.hoistedName}`;r.push(`${p}function ${t.hoistedName}(${g}) {\n ${m}${_}\n}`),i.push(`${t.hoistedName}.stepId = ${JSON.stringify(v)};`),i.push(`__ashStepRegistry.set(${JSON.stringify(v)}, ${t.hoistedName});`),a.push(t.hoistedName);let y=h||``,b=h?splitParamsTopLevel(h).map(e=>extractParamBindingName(e)).join(`, `):``,x=b?`${f}, ${b}`:f,S=t.isAsync?`async `:``,C=t.isAsync?`await `:``;n.push({start:t.propStart,end:t.propEnd,text:[`execute: ${S}(${y}) => ${C}${t.hoistedName}(${x})`,`__executeStepFn: ${t.hoistedName}`,`__closureVars: ${f}`].join(`,
1
+ import{loadNitroRolldownParseAst}from"#internal/bundler/nitro-rolldown.js";let transformCounter=0;async function transformDynamicToolExecute(e,t){if(!t.includes(`defineDynamic`)||!t.includes(`events`)||!t.includes(`execute`))return null;let n=findDynamicToolHandlers(t,await parseSource(e,t));return n.every(e=>e.executes.length===0)?null:applyTransform(t,n)}async function parseSource(t,n){let{parseAst:r}=await loadNitroRolldownParseAst();return r(n,{astType:`ts`,lang:inferLang(t),range:!0,sourceType:`module`},t)}function inferLang(e){return e.endsWith(`.tsx`)?`tsx`:e.endsWith(`.jsx`)?`jsx`:e.endsWith(`.js`)||e.endsWith(`.mjs`)||e.endsWith(`.cjs`)?`js`:`ts`}function findDynamicToolHandlers(e,t){let n=[];return walkNode(t,t=>{if(t.type===`CallExpression`&&t.callee?.type===`Identifier`&&t.callee.name===`defineDynamic`&&t.arguments?.length===1){let r=t.arguments[0];if(r.type===`ObjectExpression`){let t=findProperty(r,`events`);t?.value&&t.value.type===`ObjectExpression`&&collectHandlers(e,t.value,n)}return!1}return!0}),n}function collectHandlers(e,t,n){for(let r of t.properties??[]){if(r.type!==`Property`)continue;let t=r.value;if(!t||t.type!==`ArrowFunctionExpression`&&t.type!==`FunctionExpression`)continue;let i=t.body;if(!i)continue;let a=findBlockBodyStart(i);if(a===null)continue;let o=extractParamNames(t),s=collectScopeVarDeclarations(i),c=findExecuteFunctions(e,i);c.length>0&&n.push({handlerNode:t,bodyStart:a,scopeVars:s,paramNames:o,executes:c})}}function findBlockBodyStart(e){return e.type===`BlockStatement`&&e.start!==void 0?e.start:typeof e.body==`object`&&!Array.isArray(e.body)&&e.body?.type===`BlockStatement`&&e.body.start!==void 0?e.body.start:null}function extractParamNames(e){let t=[];for(let n of e.params??[])n.type===`Identifier`&&n.name&&t.push(n.name);return t}function collectScopeVarDeclarations(e){let t=[];return collectVarsRecursive(e,t),t}function collectVarsRecursive(e,t){if(!e||e.type===`FunctionExpression`||e.type===`ArrowFunctionExpression`||e.type===`FunctionDeclaration`)return;if(e.type===`VariableDeclaration`)for(let n of e.declarations??[])collectDeclaredNames(n,t);e.type===`ForStatement`&&e.init&&collectVarsRecursive(e.init,t);let n=e;if((e.type===`ForInStatement`||e.type===`ForOfStatement`)&&e.left&&collectVarsRecursive(e.left,t),Array.isArray(e.body))for(let n of e.body)collectVarsRecursive(n,t);else e.body&&typeof e.body==`object`&&`type`in e.body&&collectVarsRecursive(e.body,t);if(e.declarations)for(let n of e.declarations)collectVarsRecursive(n,t);if(e.expression&&collectVarsRecursive(e.expression,t),Array.isArray(n.consequent))for(let e of n.consequent)collectVarsRecursive(e,t);else n.consequent&&collectVarsRecursive(n.consequent,t);if(n.alternate&&collectVarsRecursive(n.alternate,t),n.block&&collectVarsRecursive(n.block,t),n.handler&&collectVarsRecursive(n.handler,t),n.finalizer&&collectVarsRecursive(n.finalizer,t),n.cases&&Array.isArray(n.cases))for(let e of n.cases)collectVarsRecursive(e,t)}function collectDeclaredNames(e,t){e.type===`VariableDeclarator`&&collectPatternNames(e.id,t)}function collectPatternNames(e,t){if(e){if(e.type===`Identifier`&&e.name){t.push(e.name);return}if(e.type===`ObjectPattern`)for(let n of e.properties??[])n.type===`Property`?collectPatternNames(n.value,t):n.type===`RestElement`&&collectPatternNames(n.argument,t);if(e.type===`ArrayPattern`)for(let n of e.elements??[])n&&collectPatternNames(n,t)}}function findExecuteFunctions(e,t){let n=[];return walkForExecuteProps(e,t,n,[]),n}function walkForExecuteProps(e,n,r,i){if(!n)return;if(n.type===`FunctionExpression`||n.type===`ArrowFunctionExpression`||n.type===`FunctionDeclaration`){let t=extractParamNames(n),a=n.body;if(!a)return;let o=collectScopeVarDeclarations(a),s=[...i,{params:t,vars:o}];a.type,walkForExecuteProps(e,a,r,s);return}if(n.type===`CallExpression`&&n.callee?.type===`Identifier`&&n.callee.name===`defineTool`&&n.arguments?.length===1&&n.arguments[0].type===`ObjectExpression`){let a=n.arguments[0];for(let n of a.properties??[])if(n.type===`Property`&&!n.computed&&n.key?.type===`Identifier`&&n.key.name===`execute`&&n.start!==void 0&&n.end!==void 0){let a=n.value;if(!a||a.start===void 0||a.end===void 0)continue;let o=a.type===`FunctionExpression`||a.type===`ArrowFunctionExpression`,s=n.method===!0;if(o||s){let o=extractFnParams(e,a),s=extractFnBody(e,a),c=a.async===!0;r.push({propStart:n.start,propEnd:n.end,fnSource:e.slice(a.start,a.end),isAsync:c,params:o,body:s,hoistedName:`__ash_dynamic_exec_${transformCounter++}`,nestedScopes:i})}}return}let walk=t=>walkForExecuteProps(e,t,r,i);if(Array.isArray(n.body))for(let e of n.body)walk(e);else n.body&&typeof n.body==`object`&&`type`in n.body&&walk(n.body);if(n.properties)for(let e of n.properties)e.value&&typeof e.value==`object`&&`type`in e.value&&walk(e.value);if(n.callee&&walk(n.callee),n.arguments)for(let e of n.arguments)walk(e);if(n.expression&&walk(n.expression),n.argument&&walk(n.argument),n.init&&walk(n.init),n.left&&walk(n.left),n.right&&walk(n.right),n.declarations)for(let e of n.declarations)walk(e);let a=n;if(Array.isArray(a.consequent))for(let e of a.consequent)walk(e);else a.consequent&&walk(a.consequent);if(a.alternate&&walk(a.alternate),a.block&&walk(a.block),a.handler&&walk(a.handler),a.finalizer&&walk(a.finalizer),a.cases&&Array.isArray(a.cases))for(let e of a.cases)walk(e)}function extractFnParams(e,t){if(!t.params||t.params.length===0)return``;let n=t.params[0],r=t.params[t.params.length-1];return n.start===void 0||r.end===void 0?``:e.slice(n.start,r.end)}function extractFnBody(e,t){let n=t.body;if(!n||n.start===void 0||n.end===void 0)return`{}`;let r=e.slice(n.start,n.end);return t.type===`ArrowFunctionExpression`&&n.type!==`BlockStatement`?`{ return ${r}; }`:r}function applyTransform(e,t){let n=[],r=[],i=[],a=[];for(let e of t)for(let t of e.executes){let o=[...e.paramNames,...e.scopeVars,...t.nestedScopes.flatMap(e=>[...e.params,...e.vars])],s=new Set,c=[];for(let e=o.length-1;e>=0;e--)s.has(o[e])||(s.add(o[e]),c.unshift(o[e]));let l=t.body,u=extractExecuteParamNames(t.params),d=c.filter(e=>!u.has(e)&&RegExp(`\\b${escapeForRegex(e)}\\b`).test(l)),f=d.length>0?`{ ${d.join(`, `)} }`:`{}`,p=t.isAsync?`async `:``,m=d.length>0?`const ${f} = __vars;\n `:``,h=t.params,g=h?`__vars, ${h}`:`__vars`,_=t.body.slice(1,-1).trim(),v=`ash:dynamic-tool//${t.hoistedName}`;r.push(`${p}function ${t.hoistedName}(${g}) {\n ${m}${_}\n}`),i.push(`${t.hoistedName}.stepId = ${JSON.stringify(v)};`),i.push(`__ashStepRegistry.set(${JSON.stringify(v)}, ${t.hoistedName});`),a.push(t.hoistedName);let y=h||``,b=h?splitParamsTopLevel(h).map(e=>extractParamBindingName(e)).join(`, `):``,x=b?`${f}, ${b}`:f,S=t.isAsync?`async `:``,C=t.isAsync?`await `:``;n.push({start:t.propStart,end:t.propEnd,text:[`execute: ${S}(${y}) => ${C}${t.hoistedName}(${x})`,`__executeStepFn: ${t.hoistedName}`,`__closureVars: ${f}`].join(`,
2
2
  `)})}let o=[...n].sort((e,t)=>t.start-e.start),s=e;for(let e of o)s=s.slice(0,e.start)+e.text+s.slice(e.end);s=`${[`var __ashStepRegistrySym = Symbol.for("@workflow/core//registeredSteps");`,`if (!globalThis[__ashStepRegistrySym]) globalThis[__ashStepRegistrySym] = new Map();`,`var __ashStepRegistry = globalThis[__ashStepRegistrySym];`].join(`
3
3
  `)}\n${s}`;let c=[...r,...i];return c.length>0&&(s=`${s}\n\n${c.join(`
4
4
  `)}\n`),{code:s}}function walkNode(e,t){if(t(e)){if(Array.isArray(e.body))for(let n of e.body)walkNode(n,t);else e.body&&typeof e.body==`object`&&`type`in e.body&&walkNode(e.body,t);if(e.declarations)for(let n of e.declarations)walkNode(n,t);if(e.init&&walkNode(e.init,t),e.expression&&walkNode(e.expression,t),e.declaration&&walkNode(e.declaration,t),e.argument&&walkNode(e.argument,t),e.arguments)for(let n of e.arguments)walkNode(n,t);if(e.properties)for(let n of e.properties)walkNode(n,t),n.value&&typeof n.value==`object`&&`type`in n.value&&walkNode(n.value,t);e.left&&walkNode(e.left,t),e.right&&walkNode(e.right,t)}}function findProperty(e,t){return e.properties?.find(e=>e.type===`Property`&&!e.computed&&e.key?.type===`Identifier`&&e.key.name===t)}function escapeForRegex(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function splitParamsTopLevel(e){let t=[],n=0,r=0;for(let i=0;i<e.length;i++){let a=e[i];a===`<`||a===`(`||a===`[`||a===`{`?n++:a===`>`||a===`)`||a===`]`||a===`}`?n--:a===`,`&&n===0&&(t.push(e.slice(r,i)),r=i+1)}return t.push(e.slice(r)),t}function extractParamBindingName(e){let t=e.trim(),n=0;for(let e=0;e<t.length;e++){let r=t[e];if(r===`<`||r===`(`||r===`[`||r===`{`)n++;else if(r===`>`||r===`)`||r===`]`||r===`}`)n--;else if(n===0&&(r===`:`||r===`=`))return t.slice(0,e).trim()}return t}function extractExecuteParamNames(e){if(!e)return new Set;let t=new Set;for(let n of splitParamsTopLevel(e)){let e=extractParamBindingName(n);e&&t.add(e)}return t}export{transformDynamicToolExecute as transformDynamicToolAwait,transformDynamicToolExecute};
@@ -46,3 +46,37 @@ export declare function resumeHook(): never;
46
46
  * Step metadata is only available in step functions.
47
47
  */
48
48
  export declare function getStepMetadata(): never;
49
+ /**
50
+ * Options accepted by {@link experimental_setAttributes}.
51
+ *
52
+ * Mirrors `ExperimentalSetAttributesOptions` from `@workflow/core` so the
53
+ * Ash workflow-body bundle does not have to pull the real type in.
54
+ */
55
+ export interface ExperimentalSetAttributesOptions {
56
+ /**
57
+ * Permit attribute keys that start with the reserved `$` prefix. Ash
58
+ * framework code passes `true` so it can write the `$ash.*` namespace;
59
+ * authored agent code never calls this shim directly.
60
+ */
61
+ allowReservedAttributes?: boolean;
62
+ }
63
+ /**
64
+ * Workflow-body implementation of `experimental_setAttributes` for the Ash
65
+ * bundle. Mirrors the dispatch path of `@workflow/core`'s workflow-body
66
+ * export (`dist/workflow/set-attributes.js`):
67
+ *
68
+ * 1. Convert the attribute map into the `AttributeChange[]` shape the
69
+ * runtime expects (`undefined` -> `null` to clear a key).
70
+ * 2. Resolve the workflow runtime's step dispatcher from
71
+ * `globalThis[Symbol.for("WORKFLOW_USE_STEP")]` (the same global symbol
72
+ * Ash already relies on to materialize `"use step"` proxies).
73
+ * 3. Invoke the builtin `__builtin_set_attributes` step with the changes,
74
+ * which the runtime records on the active workflow run.
75
+ *
76
+ * Validation is intentionally skipped here. The only caller, `setAshAttributes`,
77
+ * already normalizes keys/values and is the sole entry point for the
78
+ * `$ash.*` reserved namespace; bouncing through the runtime's full
79
+ * validator would require pulling `@workflow/world` into the workflow body
80
+ * bundle.
81
+ */
82
+ export declare function experimental_setAttributes(attrs: Record<string, string | undefined>, options?: ExperimentalSetAttributesOptions): Promise<void>;
@@ -1 +1 @@
1
- const WORKFLOW_CONTEXT_SYMBOL=Symbol.for(`WORKFLOW_CONTEXT`),WORKFLOW_CREATE_HOOK=Symbol.for(`WORKFLOW_CREATE_HOOK`),WORKFLOW_GET_STREAM_ID=Symbol.for(`WORKFLOW_GET_STREAM_ID`),STREAM_NAME_SYMBOL=Symbol.for(`WORKFLOW_STREAM_NAME`),workflowGlobal=globalThis;var RetryableError=class extends Error{},FatalError=class extends Error{};function createHook(e){let n=workflowGlobal[WORKFLOW_CREATE_HOOK];if(n===void 0)throw Error("`createHook()` can only be called inside a workflow function");return n(e)}function getWorkflowMetadata(){let t=workflowGlobal[WORKFLOW_CONTEXT_SYMBOL];if(t===void 0)throw Error("`getWorkflowMetadata()` can only be called inside a workflow or step function");return t}function getWritable(e={}){let t=workflowGlobal[WORKFLOW_GET_STREAM_ID];if(t===void 0)throw Error("`getWritable()` can only be called inside a workflow function");let r=t(e.namespace);return Object.create(globalThis.WritableStream.prototype,{[STREAM_NAME_SYMBOL]:{value:r,writable:!1}})}function createWebhook(e){let t=createHook(e),n=getWorkflowMetadata();return t.url=`${typeof n.url==`string`?n.url:``}/.well-known/workflow/v1/webhook/${encodeURIComponent(t.token)}`,t}function defineHook(){return{create:createHook,resume(){throw Error("`defineHook().resume()` can only be called from external contexts.")}}}function sleep(){throw Error("`sleep()` is not available in Ash workflow body bundles")}function resumeHook(){throw Error("`resumeHook()` can only be called from outside a workflow function")}function getStepMetadata(){throw Error("`getStepMetadata()` can only be called inside a step function")}export{FatalError,RetryableError,createHook,createWebhook,defineHook,getStepMetadata,getWorkflowMetadata,getWritable,resumeHook,sleep};
1
+ const WORKFLOW_CONTEXT_SYMBOL=Symbol.for(`WORKFLOW_CONTEXT`),WORKFLOW_CREATE_HOOK=Symbol.for(`WORKFLOW_CREATE_HOOK`),WORKFLOW_GET_STREAM_ID=Symbol.for(`WORKFLOW_GET_STREAM_ID`),WORKFLOW_USE_STEP=Symbol.for(`WORKFLOW_USE_STEP`),STREAM_NAME_SYMBOL=Symbol.for(`WORKFLOW_STREAM_NAME`),workflowGlobal=globalThis;var RetryableError=class extends Error{},FatalError=class extends Error{};function createHook(e){let n=workflowGlobal[WORKFLOW_CREATE_HOOK];if(n===void 0)throw Error("`createHook()` can only be called inside a workflow function");return n(e)}function getWorkflowMetadata(){let t=workflowGlobal[WORKFLOW_CONTEXT_SYMBOL];if(t===void 0)throw Error("`getWorkflowMetadata()` can only be called inside a workflow or step function");return t}function getWritable(e={}){let t=workflowGlobal[WORKFLOW_GET_STREAM_ID];if(t===void 0)throw Error("`getWritable()` can only be called inside a workflow function");let r=t(e.namespace);return Object.create(globalThis.WritableStream.prototype,{[STREAM_NAME_SYMBOL]:{value:r,writable:!1}})}function createWebhook(e){let t=createHook(e),n=getWorkflowMetadata();return t.url=`${typeof n.url==`string`?n.url:``}/.well-known/workflow/v1/webhook/${encodeURIComponent(t.token)}`,t}function defineHook(){return{create:createHook,resume(){throw Error("`defineHook().resume()` can only be called from external contexts.")}}}function sleep(){throw Error("`sleep()` is not available in Ash workflow body bundles")}function resumeHook(){throw Error("`resumeHook()` can only be called from outside a workflow function")}function getStepMetadata(){throw Error("`getStepMetadata()` can only be called inside a step function")}async function experimental_setAttributes(e,t={}){let n=Object.entries(e);if(n.length===0)return;let i=workflowGlobal[WORKFLOW_USE_STEP];if(i===void 0)throw Error("`experimental_setAttributes()` can only be called inside a workflow runtime context");let a=n.map(([e,t])=>({key:e,value:t===void 0?null:t})),o=t.allowReservedAttributes===!0?{allowReservedAttributes:!0}:{};await i(`__builtin_set_attributes`)(a,o)}export{FatalError,RetryableError,createHook,createWebhook,defineHook,experimental_setAttributes,getStepMetadata,getWorkflowMetadata,getWritable,resumeHook,sleep};
@@ -1,4 +1,4 @@
1
- import{loadNitroRolldownParseAst}from"#internal/bundler/nitro-rolldown.js";const BUILTIN_STEP_NAMES=new Set([`__builtin_response_array_buffer`,`__builtin_response_json`,`__builtin_response_text`]);async function transformWorkflowDirectives(e){if(e.mode===!1)return{code:e.source,workflowManifest:{}};let t=await parseWorkflowSource(e.filename,e.source),n=findDirectiveFunctions(t);if(n.length===0)return{code:e.source,workflowManifest:{}};let r=e.moduleSpecifier??`./${stripJavaScriptExtension(e.filename)}`,i=e.stableModuleSpecifier??r,a={},o=[],s=[],c=!1;for(let t of n){if(t.directive===`use step`){let n=createStepId(r,t.name);a.steps??={};let i=a.steps[e.filename]??={};if(i[t.name]={stepId:n},e.mode===`workflow`){let e=t.exportPrefix.length>0?`export `:``;o.push({end:t.rangeEnd,start:t.rangeStart,text:`${e}var ${t.name} = globalThis[Symbol.for("WORKFLOW_USE_STEP")](${JSON.stringify(n)});`})}else o.push({end:t.directiveEnd,start:t.directiveStart,text:``}),e.mode===`step`?(c=!0,s.push(`registerStepFunction(${JSON.stringify(n)}, ${t.name});`)):s.push(`${t.name}.stepId = ${JSON.stringify(n)};`);continue}let n=`workflow//${e.stableWorkflowNames?.has(t.name)===!0?i:r}//${t.name}`;a.workflows??={};let l=a.workflows[e.filename]??={};l[t.name]={workflowId:n},e.mode===`workflow`?(o.push({end:t.directiveEnd,start:t.directiveStart,text:``}),s.push(`${t.name}.workflowId = ${JSON.stringify(n)};`),s.push(`globalThis.__private_workflows.set(${JSON.stringify(n)}, ${t.name});`)):(o.push({end:t.directiveEnd,start:t.directiveStart,text:`throw new Error(${JSON.stringify(`You attempted to execute workflow ${t.name} function directly. To start a workflow, use start(${t.name}) from workflow/api`)});`}),s.push(`${t.name}.workflowId = ${JSON.stringify(n)};`))}let l=`/**__internal_workflows${JSON.stringify(a)}*/;`,u=n.some(e=>e.directive===`use workflow`);if(e.mode===`workflow`&&!u)return{code:`${l}\n${createWorkflowStepProxySource(e.source,t,n,r)}`,workflowManifest:a};let d=applySourceReplacements(e.source,o),f=e.mode===`workflow`?await stripUnusedValueImports(e.filename,d):d;return{code:`${c?`import { registerStepFunction } from "workflow/internal/private";\n${l}\n`:`${l}\n`}${f}${s.length>0?`\n${s.join(`
1
+ import{loadNitroRolldownParseAst}from"#internal/bundler/nitro-rolldown.js";const BUILTIN_STEP_NAMES=new Set([`__builtin_response_array_buffer`,`__builtin_response_json`,`__builtin_response_text`,`__builtin_set_attributes`]);async function transformWorkflowDirectives(e){if(e.mode===!1)return{code:e.source,workflowManifest:{}};let t=await parseWorkflowSource(e.filename,e.source),n=findDirectiveFunctions(t);if(n.length===0)return{code:e.source,workflowManifest:{}};let r=e.moduleSpecifier??`./${stripJavaScriptExtension(e.filename)}`,i=e.stableModuleSpecifier??r,a={},o=[],s=[],c=!1;for(let t of n){if(t.directive===`use step`){let n=createStepId(r,t.name);a.steps??={};let i=a.steps[e.filename]??={};if(i[t.name]={stepId:n},e.mode===`workflow`){let e=t.exportPrefix.length>0?`export `:``;o.push({end:t.rangeEnd,start:t.rangeStart,text:`${e}var ${t.name} = globalThis[Symbol.for("WORKFLOW_USE_STEP")](${JSON.stringify(n)});`})}else o.push({end:t.directiveEnd,start:t.directiveStart,text:``}),e.mode===`step`?(c=!0,s.push(`registerStepFunction(${JSON.stringify(n)}, ${t.name});`)):s.push(`${t.name}.stepId = ${JSON.stringify(n)};`);continue}let n=`workflow//${e.stableWorkflowNames?.has(t.name)===!0?i:r}//${t.name}`;a.workflows??={};let l=a.workflows[e.filename]??={};l[t.name]={workflowId:n},e.mode===`workflow`?(o.push({end:t.directiveEnd,start:t.directiveStart,text:``}),s.push(`${t.name}.workflowId = ${JSON.stringify(n)};`),s.push(`globalThis.__private_workflows.set(${JSON.stringify(n)}, ${t.name});`)):(o.push({end:t.directiveEnd,start:t.directiveStart,text:`throw new Error(${JSON.stringify(`You attempted to execute workflow ${t.name} function directly. To start a workflow, use start(${t.name}) from workflow/api`)});`}),s.push(`${t.name}.workflowId = ${JSON.stringify(n)};`))}let l=`/**__internal_workflows${JSON.stringify(a)}*/;`,u=n.some(e=>e.directive===`use workflow`);if(e.mode===`workflow`&&!u)return{code:`${l}\n${createWorkflowStepProxySource(e.source,t,n,r)}`,workflowManifest:a};let d=applySourceReplacements(e.source,o),f=e.mode===`workflow`?await stripUnusedValueImports(e.filename,d):d;return{code:`${c?`import { registerStepFunction } from "workflow/internal/private";\n${l}\n`:`${l}\n`}${f}${s.length>0?`\n${s.join(`
2
2
  `)}\n`:``}`,workflowManifest:a}}async function parseWorkflowSource(t,n){let{parseAst:r}=await loadNitroRolldownParseAst();return r(n,{astType:`ts`,lang:inferParserLanguage(t),range:!0,sourceType:`module`},t)}function createWorkflowStepProxySource(e,t,n,r){let i=findExportedLiteralValueDeclarations(e,t),a=n.filter(e=>e.directive===`use step`).map(e=>{let t=e.exported?`export `:``,n=createStepId(r,e.name);return`${t}var ${e.name} = globalThis[Symbol.for("WORKFLOW_USE_STEP")](${JSON.stringify(n)});`}),o=[...i,...a];return o.length>0?`${o.join(`
3
3
  `)}\n`:``}function findDirectiveFunctions(e){let t=[],n=collectLocalNamesReExportedAtModuleLevel(e);for(let r of e.body??[]){let e=readTopLevelFunctionDeclaration(r);if(e===null)continue;let i=e.fn,a=i.id?.name,o=readBlockStatements(i.body);if(i.async!==!0||a===void 0||o===void 0)continue;let s=readFunctionDirective(o[0]);s!==null&&t.push({directive:s.value,directiveEnd:s.end,directiveStart:s.start,exportPrefix:e.exported?`export `:``,exported:e.exported||n.has(a),name:a,rangeEnd:e.end,rangeStart:e.start})}return t}function collectLocalNamesReExportedAtModuleLevel(e){let t=new Set;for(let n of e.body??[])if(n.type===`ExportNamedDeclaration`&&!(n.source!==void 0&&n.source!==null)&&!(n.declaration!==void 0&&n.declaration!==null))for(let e of n.specifiers??[]){let n=e.local?.name;n!==void 0&&t.add(n)}return t}function readTopLevelFunctionDeclaration(e){return e.type===`FunctionDeclaration`?readFunctionDeclarationRange(e,!1,e):e.type!==`ExportNamedDeclaration`||e.declaration?.type!==`FunctionDeclaration`?null:readFunctionDeclarationRange(e.declaration,!0,e)}function readFunctionDeclarationRange(e,t,n){return e.start===void 0||e.end===void 0||n.start===void 0||n.end===void 0?null:{end:n.end,exported:t,fn:e,start:n.start}}function readBlockStatements(e){return e===void 0||Array.isArray(e)?e:e.body}function readFunctionDirective(e){let t=e?.directive??(e?.type===`ExpressionStatement`&&e.expression?.type===`Literal`?e.expression.value:void 0);return t!==`use workflow`&&t!==`use step`||e?.start===void 0||e.end===void 0?null:{end:e.end,start:e.start,value:t}}function applySourceReplacements(e,t){let n=``,r=0;for(let i of[...t].sort((e,t)=>e.start-t.start))n+=e.slice(r,i.start),n+=i.text,r=i.end;return n+e.slice(r)}async function stripUnusedValueImports(e,t){let n=await parseWorkflowSource(e,t),r=collectReferencedIdentifiers(n),i=[];for(let e of n.body??[]){if(e.type!==`ImportDeclaration`||e.start===void 0||e.end===void 0)continue;let n=readValueImportBindings(e);n.length>0&&n.every(e=>!r.has(e))&&i.push({end:extendRemovalEnd(t,e.end),start:e.start,text:``})}return i.length>0?applySourceReplacements(t,i):t}function findExportedLiteralValueDeclarations(e,t){let n=[],r=collectLocalReExportAliases(t);for(let i of t.body??[]){if(i.type===`ExportNamedDeclaration`&&i.declaration?.type===`VariableDeclaration`&&i.declaration.kind===`const`&&i.start!==void 0&&i.end!==void 0&&(i.declaration.declarations??[]).every(isLiteralValueDeclarator)){n.push(e.slice(i.start,i.end).trim());continue}if(i.type===`VariableDeclaration`&&(i.kind===`const`||i.kind===`var`||i.kind===`let`))for(let t of i.declarations??[]){if(!isLiteralValueDeclarator(t)||t.start===void 0||t.end===void 0)continue;let a=t.id?.name;if(a===void 0)continue;let o=r.get(a);if(o===void 0||o.length===0)continue;let s=e.slice(t.start,t.end).trim(),c=o.map(e=>e===a?a:`${a} as ${e}`);n.push(`${i.kind} ${s};\nexport { ${c.join(`, `)} };`)}}return n}function collectLocalReExportAliases(e){let t=new Map;for(let n of e.body??[])if(n.type===`ExportNamedDeclaration`&&!(n.source!==void 0&&n.source!==null)&&!(n.declaration!==void 0&&n.declaration!==null))for(let e of n.specifiers??[]){let n=e.local?.name,r=e.exported?.name??n;if(n!==void 0&&r!==void 0){let e=t.get(n)??[];e.push(r),t.set(n,e)}}return t}function isLiteralValueDeclarator(e){return isLiteralValueExpression(e.init)}function isLiteralValueExpression(e){return e==null?!1:e.type===`Literal`?e.value===null||typeof e.value==`boolean`||typeof e.value==`number`||typeof e.value==`string`:e.type===`TemplateLiteral`&&(e.expressions??[]).length===0?!0:e.type===`TSAsExpression`||e.type===`TSSatisfiesExpression`||e.type===`TSNonNullExpression`||e.type===`TSTypeAssertion`?isLiteralValueExpression(e.expression):e.type===`UnaryExpression`&&e.argument?.type===`Literal`?typeof e.argument.value==`number`:!1}function readValueImportBindings(e){return e.importKind===`type`?[]:(e.specifiers??[]).filter(e=>e.importKind!==`type`).map(e=>e.local?.name).filter(e=>e!==void 0)}function collectReferencedIdentifiers(e){let t=new Set;return visitAstNode(e,e=>{e.type===`Identifier`&&typeof e.name==`string`&&t.add(e.name)}),t}function visitAstNode(e,t){if(!(e.type===`ImportDeclaration`||e.type?.startsWith(`TS`))){t(e);for(let n of Object.values(e))if(Array.isArray(n))for(let e of n)isAstNode(e)&&visitAstNode(e,t);else isAstNode(n)&&visitAstNode(n,t)}}function isAstNode(e){return typeof e==`object`&&!!e&&typeof e.type==`string`}function extendRemovalEnd(e,t){let n=t;for(;n<e.length&&(e[n]===` `||e[n]===` `);)n+=1;return e[n]===`\r`&&e[n+1]===`
4
4
  `?n+2:e[n]===`
@@ -1,4 +1,4 @@
1
- import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.42.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.159`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,tsgoPackageVersion:e?.tsgoPackageVersion??`7.0.0-dev.20260523.1`,typesNodePackageVersion:e?.typesNodePackageVersion??`25.9.1`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatAshDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t){if(!await pathExists(e))return[];assertStampedVersion(`ashPackageVersion`,t.ashPackageVersion),assertStampedVersion(`aiPackageVersion`,t.aiPackageVersion),assertStampedVersion(`nextPackageVersion`,t.nextPackageVersion),assertStampedVersion(`reactPackageVersion`,t.reactPackageVersion),assertStampedVersion(`reactDomPackageVersion`,t.reactDomPackageVersion),assertStampedVersion(`streamdownPackageVersion`,t.streamdownPackageVersion),assertStampedVersion(`zodPackageVersion`,t.zodPackageVersion),assertStampedVersion(`tsgoPackageVersion`,t.tsgoPackageVersion),assertStampedVersion(`typesNodePackageVersion`,t.typesNodePackageVersion),assertStampedVersion(`typesReactPackageVersion`,t.typesReactPackageVersion),assertStampedVersion(`typesReactDomPackageVersion`,t.typesReactDomPackageVersion);let r={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:t.aiPackageVersion,"experimental-ash":formatAshDependencySpecifier(t.ashPackageVersion),next:t.nextPackageVersion,react:t.reactPackageVersion,"react-dom":t.reactDomPackageVersion,streamdown:t.streamdownPackageVersion,zod:t.zodPackageVersion},i={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":t.typesNodePackageVersion,"@types/react":t.typesReactPackageVersion,"@types/react-dom":t.typesReactDomPackageVersion,"@typescript/native-preview":t.tsgoPackageVersion},a=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:a}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(a)}]}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){return`import { connectSlackCredentials } from "@vercel/connect/ash";
1
+ import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.43.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.159`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,tsgoPackageVersion:e?.tsgoPackageVersion??`7.0.0-dev.20260523.1`,typesNodePackageVersion:e?.typesNodePackageVersion??`25.9.1`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatAshDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t){if(!await pathExists(e))return[];assertStampedVersion(`ashPackageVersion`,t.ashPackageVersion),assertStampedVersion(`aiPackageVersion`,t.aiPackageVersion),assertStampedVersion(`nextPackageVersion`,t.nextPackageVersion),assertStampedVersion(`reactPackageVersion`,t.reactPackageVersion),assertStampedVersion(`reactDomPackageVersion`,t.reactDomPackageVersion),assertStampedVersion(`streamdownPackageVersion`,t.streamdownPackageVersion),assertStampedVersion(`zodPackageVersion`,t.zodPackageVersion),assertStampedVersion(`tsgoPackageVersion`,t.tsgoPackageVersion),assertStampedVersion(`typesNodePackageVersion`,t.typesNodePackageVersion),assertStampedVersion(`typesReactPackageVersion`,t.typesReactPackageVersion),assertStampedVersion(`typesReactDomPackageVersion`,t.typesReactDomPackageVersion);let r={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:t.aiPackageVersion,"experimental-ash":formatAshDependencySpecifier(t.ashPackageVersion),next:t.nextPackageVersion,react:t.reactPackageVersion,"react-dom":t.reactDomPackageVersion,streamdown:t.streamdownPackageVersion,zod:t.zodPackageVersion},i={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":t.typesNodePackageVersion,"@types/react":t.typesReactPackageVersion,"@types/react-dom":t.typesReactDomPackageVersion,"@typescript/native-preview":t.tsgoPackageVersion},a=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:a}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(a)}]}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){return`import { connectSlackCredentials } from "@vercel/connect/ash";
2
2
  import { slackChannel } from "experimental-ash/channels/slack";
3
3
 
4
4
  export default slackChannel({
@@ -1,2 +1,2 @@
1
- import{deriveSlackConnectorSlug,ensureChannel}from"../channels.js";import{createPromptCommandOutput}from"../cli/command-output.js";import{runVercel}from"../primitives/run-vercel.js";import{deployToVercel}from"./deploy-to-vercel.js";import"../cli/channel-setup-prompter.js";import{runPnpmInstall}from"../primitives/run-pnpm.js";import{reconcileSlackUid,setupSlackbot}from"./setup-slackbot.js";const CHANNEL_OPTIONS=[{value:`web`,label:`Web Chat`,hint:`next.js`},{value:`slack`,label:`Slack`,hint:`creates slackbot and deploys to Vercel`}];function selectableChannels(e){return CHANNEL_OPTIONS.map(t=>{let n=e?.[t.value];return n===void 0?t:{...t,disabled:!0,disabledReason:n}})}function createAddToAgentState(e={state:`unlinked`}){return{channels:[],webScaffolded:!1,slackScaffolded:!1,slackDependenciesInstalled:!1,projectLinked:e.state!==`unlinked`,deployed:e.state===`deployed`,slackDeploymentPending:!1,productionUrl:e.productionUrl,slackbotCreated:!1,slackbotAttached:!1,slackConnectorUid:void 0,slackWorkspaceUrl:void 0,slackWorkspaceName:void 0}}function recordScaffoldedChannel(e,t){e.channels.includes(t)||e.channels.push(t)}async function promptCreateSlackbot(e){return await e.select({message:`Do you want to create your slackbot?`,options:[{value:`yes`,label:`Yes`,hint:`connects Slack, then deploys`},{value:`no`,label:`No`}]})===`yes`}async function installSlackDependencies(e,t,r){if(!r.slackDependenciesInstalled){if(e.log.message(`Installing project dependencies before deployment (pnpm install)...`),!await runPnpmInstall(t,{onOutput:createPromptCommandOutput(e.log)}))throw Error(`Dependency installation failed. Deployment did not start.`);r.slackDependenciesInstalled=!0}}async function linkProjectForSlackbot(e){if(e.state.projectLinked)return;let t;if(e.linkProjectForSlackbot===void 0?(e.prompter.log.message(`Linking this directory to a Vercel project...`),t=await runVercel([`link`],{cwd:e.projectPath})):t=await e.linkProjectForSlackbot(),!t)throw Error(`Vercel project linking failed. Slackbot creation did not start.`);e.state.projectLinked=!0}async function scaffoldSlackChannel(e,n){let{prompter:r,projectPath:i,state:a}=e;if(!a.slackScaffolded){r.log.message(`Scaffolding Slack channel files...`);let o=await ensureChannel({projectRoot:i,kind:`slack`,slackConnectorSlug:n,force:e.force});o.action===`created`||o.action===`overwritten`?r.log.success(`Scaffolded channel: slack`):r.log.info(`Channel "slack" already exists. Skipping file creation.`),a.slackScaffolded=!0}recordScaffoldedChannel(a,`slack`)}async function deployAttachedSlackChannel(e,t,n){await installSlackDependencies(e,t,n);let r=await deployToVercel(e,t,{presetDeploy:!0});if(n.deployed=r.deployed,n.productionUrl=r.productionUrl??n.productionUrl,!n.deployed)throw Error(`Deployment failed after Slack attachment.`);n.slackDeploymentPending=!1}async function reconcilePreparedSlackChannel(e,t,n,r){let i=n.slackConnectorUid;if(i===void 0)throw Error(`Slack connector UID was not resolved. Slack deployment did not start.`);if(!await reconcileSlackUid(e,t,{kind:`attached`,created:!0,attached:!0,connectorUid:i},r))throw Error(`Slack connector UID update is required before deployment.`)}function recordAttachedSlackbot(e,t){e.slackbotCreated=!0,e.slackbotAttached=!0,e.slackConnectorUid=t.connectorUid,e.slackWorkspaceUrl=t.workspaceUrl,e.slackWorkspaceName=t.workspaceName}async function runAddToAgentOnce(n,r){let{prompter:i,projectPath:a,projectName:o,state:s}=n;if(r.includes(`web`))if(s.webScaffolded)recordScaffoldedChannel(s,`web`);else{i.log.message(`Scaffolding Web Chat channel files...`);let e=await ensureChannel({projectRoot:a,kind:`web`,force:n.force,webPackageVersions:n.webPackageVersions});e.action===`created`||e.action===`overwritten`?(i.log.success(`Scaffolded channel: web`),s.webScaffolded=!0,recordScaffoldedChannel(s,`web`)):i.log.info(`Next.js project detected. Skipping Web Chat scaffolding.`)}if(!r.includes(`slack`))return;let c=await deriveSlackConnectorSlug(a,o);if(s.slackbotCreated){if(!s.slackbotAttached)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);s.slackDeploymentPending&&(await scaffoldSlackChannel(n,c),await reconcilePreparedSlackChannel(i,a,s,`slack/${c}`),await deployAttachedSlackChannel(i,a,s));return}if(!(n.presetCreateSlackbot??await promptCreateSlackbot(i))){i.log.info(`Slack channel was not added because Slackbot setup was skipped.`);return}await linkProjectForSlackbot(n);let l=await setupSlackbot(i,a,{presetCreate:!0,slackbotName:c});if(!l.created)throw Error(`Slackbot creation failed.`);if(l.kind!==`attached`)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);recordAttachedSlackbot(s,l),s.slackDeploymentPending=!0,await scaffoldSlackChannel(n,c),await reconcilePreparedSlackChannel(i,a,s,`slack/${c}`),await deployAttachedSlackChannel(i,a,s)}async function runAddToAgent(e){for(;;){let t=e.presetChannels??await e.prompter.multiselect({message:`Add to agent`,options:selectableChannels(e.disabledChannelReasons),required:!1});await e.validateSelectedChannels?.(t);try{await runAddToAgentOnce(e,t);return}catch(t){if(t instanceof Error&&(t.name===`WizardCancelledError`||t.name===`ChannelAddCancelledError`))throw t;let n=t instanceof Error?t.message:String(t),r=n.split(`
2
- `)[0]?.trim()??n;if(e.prompter.log.error(r),e.presetChannels!==void 0)throw t}}}export{createAddToAgentState,runAddToAgent};
1
+ import{deriveSlackConnectorSlug,ensureChannel}from"../channels.js";import{createPromptCommandOutput}from"../cli/command-output.js";import{runVercel}from"../primitives/run-vercel.js";import{detectDeployment}from"../primitives/detect-deployment.js";import{deployToVercel}from"./deploy-to-vercel.js";import"../cli/channel-setup-prompter.js";import{runPnpmInstall}from"../primitives/run-pnpm.js";import{reconcileSlackUid,setupSlackbot}from"./setup-slackbot.js";const CHANNEL_OPTIONS=[{value:`web`,label:`Web Chat`},{value:`slack`,label:`Slack`,hint:`creates slackbot and deploys to Vercel`}];function selectableChannels(e){return CHANNEL_OPTIONS.map(t=>{let n=e?.[t.value];return n===void 0?t:{...t,disabled:!0,disabledReason:n}})}function createAddToAgentState(e={state:`unlinked`}){return{channels:[],webScaffolded:!1,slackScaffolded:!1,deploymentDependenciesInstalled:!1,projectLinked:e.state!==`unlinked`,vercelProjectId:e.projectId,deployed:e.state===`deployed`,deploymentPending:!1,productionUrl:e.productionUrl,slackbotCreated:!1,slackbotAttached:!1,slackConnectorUid:void 0,slackWorkspaceUrl:void 0,slackWorkspaceName:void 0}}function recordScaffoldedChannel(e,t){e.channels.includes(t)||(e.channels.push(t),e.deploymentPending=!0)}async function promptCreateSlackbot(e){return await e.select({message:`Do you want to create your slackbot?`,options:[{value:`yes`,label:`Yes`,hint:`connects Slack, then deploys`},{value:`no`,label:`No`}]})===`yes`}async function installDeploymentDependencies(e,t,r){if(!r.deploymentDependenciesInstalled){if(e.log.message(`Installing project dependencies before deployment (pnpm install)...`),!await runPnpmInstall(t,{onOutput:createPromptCommandOutput(e.log)}))throw Error(`Dependency installation failed. Deployment did not start.`);r.deploymentDependenciesInstalled=!0}}async function linkProjectForSlackbot(e){if(e.state.projectLinked)return;let t;if(e.linkProjectForSlackbot===void 0?(e.prompter.log.message(`Linking this directory to a Vercel project...`),t=await runVercel([`link`],{cwd:e.projectPath})):t=await e.linkProjectForSlackbot(),!t)throw Error(`Vercel project linking failed. Slackbot creation did not start.`);e.state.projectLinked=!0;let n=await detectDeployment(e.projectPath);e.state.vercelProjectId=n.projectId??e.state.vercelProjectId,e.state.productionUrl=n.productionUrl??e.state.productionUrl,e.state.deployed=e.state.deployed||n.state===`deployed`}async function scaffoldSlackChannel(e,n){let{prompter:r,projectPath:i,state:a}=e;if(!a.slackScaffolded){r.log.message(`Scaffolding Slack channel files...`);let o=await ensureChannel({projectRoot:i,kind:`slack`,slackConnectorSlug:n,force:e.force});o.action===`created`||o.action===`overwritten`?r.log.success(`Scaffolded channel: slack`):r.log.info(`Channel "slack" already exists. Skipping file creation.`),a.slackScaffolded=!0}recordScaffoldedChannel(a,`slack`)}async function linkProjectForDeployment(e){let{prompter:t,projectPath:i,state:a}=e;if(a.projectLinked)return;let o=e.vercelProjectId??a.vercelProjectId,s=createPromptCommandOutput(t.log);if(o===void 0){if(t.log.message(`Linking this directory to a Vercel project before deployment...`),!await runVercel([`link`],{cwd:i,onOutput:s}))throw Error(`Vercel project linking failed. Deployment did not start.`)}else{if(t.log.message(`Linking this directory to Vercel project "${o}" before deployment...`),!await runVercel([`link`,`--project`,o,`--yes`],{cwd:i,onOutput:s}))throw Error(`Vercel project linking failed. Deployment did not start.`);a.vercelProjectId=o}a.projectLinked=!0}async function deployProject(e){let{prompter:t,projectPath:n,state:r}=e;if(!r.deploymentPending)return;await linkProjectForDeployment(e),await installDeploymentDependencies(t,n,r);let i=await deployToVercel(t,n,{presetDeploy:!0});if(r.deployed=i.deployed,r.productionUrl=i.productionUrl??r.productionUrl,!r.deployed)throw Error(`Deployment failed after channel setup.`);r.deploymentPending=!1}async function reconcilePreparedSlackChannel(e,t,n,r){let i=n.slackConnectorUid;if(i===void 0)throw Error(`Slack connector UID was not resolved. Slack deployment did not start.`);if(!await reconcileSlackUid(e,t,{kind:`attached`,created:!0,attached:!0,connectorUid:i},r))throw Error(`Slack connector UID update is required before deployment.`)}function recordAttachedSlackbot(e,t){e.slackbotCreated=!0,e.slackbotAttached=!0,e.slackConnectorUid=t.connectorUid,e.slackWorkspaceUrl=t.workspaceUrl,e.slackWorkspaceName=t.workspaceName}async function runAddToAgentOnce(n,r){let{prompter:i,projectPath:a,projectName:o,state:s}=n;if(r.includes(`web`))if(s.webScaffolded)recordScaffoldedChannel(s,`web`);else{i.log.message(`Scaffolding Web Chat channel files...`);let e=await ensureChannel({projectRoot:a,kind:`web`,force:n.force,webPackageVersions:n.webPackageVersions});e.action===`created`||e.action===`overwritten`?(i.log.success(`Scaffolded channel: web`),s.webScaffolded=!0,recordScaffoldedChannel(s,`web`)):i.log.info(`Next.js project detected. Skipping Web Chat scaffolding.`)}if(r.includes(`slack`)){let t=await deriveSlackConnectorSlug(a,o);if(s.slackbotCreated){if(!s.slackbotAttached)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);s.deploymentPending&&(await scaffoldSlackChannel(n,t),await reconcilePreparedSlackChannel(i,a,s,`slack/${t}`))}else if(n.presetCreateSlackbot??await promptCreateSlackbot(i)){await linkProjectForSlackbot(n);let e=await setupSlackbot(i,a,{presetCreate:!0,slackbotName:t});if(!e.created)throw Error(`Slackbot creation failed.`);if(e.kind!==`attached`)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);recordAttachedSlackbot(s,e),await scaffoldSlackChannel(n,t),await reconcilePreparedSlackChannel(i,a,s,`slack/${t}`)}else i.log.info(`Slack channel was not added because Slackbot setup was skipped.`)}}async function runAddToAgent(e){for(;;){let t=e.presetChannels??await e.prompter.multiselect({message:`Add to agent`,options:selectableChannels(e.disabledChannelReasons),required:!1});await e.validateSelectedChannels?.(t);try{await runAddToAgentOnce(e,t);return}catch(t){if(t instanceof Error&&(t.name===`WizardCancelledError`||t.name===`ChannelAddCancelledError`))throw t;let n=t instanceof Error?t.message:String(t),r=n.split(`
2
+ `)[0]?.trim()??n;if(e.prompter.log.error(r),e.presetChannels!==void 0)throw t}}}export{createAddToAgentState,deployProject,runAddToAgent};
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{evaluateFilePart,formatUploadPolicyViolation,isUploadsDisabled}from"#public/channels/upload-policy.js";import{resolveSlackBotToken}from"#public/channels/slack/api.js";const log=createLogger(`slack.attachments`);function collectSlackFileParts(e,r){let i=[];for(let o of e??[]){let e=toSlackFilePart(o,i.length);if(e===null)continue;let s=evaluateFilePart(e,r);if(s!==null){log.warn(`dropped attachment — ${formatUploadPolicyViolation(s)}`,{name:o.name});continue}i.push(e)}return i}function toSlackFilePart(e,t){return e.type===`audio`||e.type===`video`?null:e.url?{type:`file`,mediaType:e.mimeType??`application/octet-stream`,filename:e.name??`attachment-${t}`,data:new URL(e.url)}:(log.warn(`dropped attachment — no url available`,{name:e.name}),null)}async function collectInboundFileParts(e){let t=collectSlackFileParts(e.mention.attachments,e.policy);if(t.length>0)return t;if(isUploadsDisabled(e.policy))return[];try{await e.thread.refresh()}catch(e){return log.warn(`slack thread refresh failed for attachment collection`,{error:e}),[]}let n=e.thread.recentMessages;for(let t=n.length-1;t>=0;--t){let r=n[t];if(!r||r.isMe)continue;let i=r.raw,a=collectSlackFileParts(extractAttachmentsFromRaw(i?.files),e.policy);return a.length>0?a:[]}return[]}function extractAttachmentsFromRaw(e){return Array.isArray(e)?e.map(e=>{let t=typeof e.mimetype==`string`?e.mimetype:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:typeof e.url_private==`string`?e.url_private:void 0,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}):[]}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function buildSlackTurnMessage(e,t){return t.length===0?e:e.trim().length===0?[...t]:[{type:`text`,text:e},...t]}function createSlackFetchFile(e){return async t=>{if(!t.startsWith(`https://files.slack.com/`))return null;let n=await resolveSlackBotToken(e.botToken),r=await fetch(t,{headers:{authorization:`Bearer ${n}`}});if(!r.ok)throw Error(`Slack file fetch returned HTTP ${r.status} for ${t}.`);return{bytes:Buffer.from(await r.arrayBuffer()),mediaType:r.headers.get(`content-type`)??void 0}}}export{buildSlackTurnMessage,collectInboundFileParts,collectSlackFileParts,createSlackFetchFile};
1
+ import{createLogger}from"#internal/logging.js";import{evaluateFilePart,formatUploadPolicyViolation,isUploadsDisabled}from"#public/channels/upload-policy.js";import{resolveSlackBotToken}from"#public/channels/slack/api.js";const log=createLogger(`slack.attachments`);function collectSlackFileParts(e,r){let i=[];for(let o of e??[]){let e=toSlackFilePart(o,i.length);if(e===null)continue;let s=evaluateFilePart(e,r);if(s!==null){log.warn(`dropped attachment — ${formatUploadPolicyViolation(s)}`,{name:o.name});continue}i.push(e)}return i}function toSlackFilePart(e,t){return e.type===`audio`||e.type===`video`?null:e.url?{type:`file`,mediaType:e.mimeType??`application/octet-stream`,filename:e.name??`attachment-${t}`,data:new URL(e.url)}:(log.warn(`dropped attachment — no url available`,{name:e.name}),null)}async function collectInboundFileParts(e){let t=collectSlackFileParts(e.mention.attachments,e.policy);if(t.length>0)return t;if(isUploadsDisabled(e.policy))return[];try{await e.thread.refresh()}catch(e){return log.warn(`slack thread refresh failed for attachment collection`,{error:e}),[]}let n=e.thread.recentMessages;for(let t=n.length-1;t>=0;--t){let r=n[t];if(!r||r.isMe)continue;let i=r.raw,a=collectSlackFileParts(extractAttachmentsFromRaw(i?.files),e.policy);return a.length>0?a:[]}return[]}function extractAttachmentsFromRaw(e){return Array.isArray(e)?e.map(e=>{let t=typeof e.mimetype==`string`?e.mimetype:void 0;return{id:typeof e.id==`string`?e.id:``,type:inferAttachmentType(t),url:typeof e.url_private==`string`?e.url_private:void 0,name:typeof e.name==`string`?e.name:void 0,mimeType:t,size:typeof e.size==`number`?e.size:void 0}}):[]}function inferAttachmentType(e){return e===void 0?`file`:e.startsWith(`image/`)?`image`:e.startsWith(`video/`)?`video`:e.startsWith(`audio/`)?`audio`:`file`}function buildSlackTurnMessage(e,t){return t.length===0?e:e.trim().length===0?[...t]:[{type:`text`,text:e},...t]}function createSlackFetchFile(e){return async t=>{if(!isSlackFileUrl(t))return null;let n=await resolveSlackBotToken(e.botToken),r=await fetch(t,{headers:{authorization:`Bearer ${n}`}});if(!r.ok)throw Error(`Slack file fetch returned HTTP ${r.status} for ${t}.`);return{bytes:Buffer.from(await r.arrayBuffer()),mediaType:r.headers.get(`content-type`)??void 0}}}function isSlackFileUrl(e){let t=URL.parse(e);return t?.protocol===`https:`?t.hostname===`files.slack.com`?!0:(t.hostname===`enterprise.slack.com`||t.hostname.endsWith(`.enterprise.slack.com`))&&t.pathname.startsWith(`/files/`):!1}export{buildSlackTurnMessage,collectInboundFileParts,collectSlackFileParts,createSlackFetchFile};
@@ -1,5 +1,5 @@
1
1
  export type { ModelMessage } from "ai";
2
- export { slackChannel, type SlackApiResponse, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackHandle, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInitialMessage, type SlackInteractionAction, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveArgs, type SlackThread, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
2
+ export { slackChannel, type SlackApiResponse, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackHandle, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInstrumentationMetadata, type SlackInitialMessage, type SlackInteractionAction, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveArgs, type SlackThread, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
3
3
  export type { SlackAttachment, SlackAuthor, SlackInboundContext, SlackMessage, } from "#public/channels/slack/inbound.js";
4
4
  export { slackContinuationToken, type SlackPostInput, type SlackPostedMessage, type SlackThreadMessage, type SlackUploadFilesOptions, type SlackUploadFilesResult, } from "#public/channels/slack/api.js";
5
5
  export { defaultSlackAuth } from "#public/channels/slack/defaults.js";
@@ -80,6 +80,12 @@ export interface SlackChannelState {
80
80
  */
81
81
  pendingAuthMessageTs?: Record<string, string>;
82
82
  }
83
+ export interface SlackInstrumentationMetadata extends Record<string, unknown> {
84
+ readonly channelId: string | null;
85
+ readonly teamId: string | null;
86
+ readonly threadTs: string | null;
87
+ readonly triggeringUserId: string | null;
88
+ }
83
89
  export interface SlackChannelCredentials {
84
90
  readonly botToken?: SlackBotToken;
85
91
  /**
@@ -1 +1 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{buildSlackBinding,slackContinuationToken}from"#public/channels/slack/api.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage}from"#public/channels/slack/defaults.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseAppMentionEvent,parseDirectMessageEvent,prependSlackContext}from"#public/channels/slack/inbound.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`);function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.setContinuationToken(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),l=createSlackFetchFile({botToken:e.credentials?.botToken}),u=e.onAppMention??defaultOnAppMention,d=e.onDirectMessage??defaultOnDirectMessage,f={...defaultEvents,...e.events,"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler()};return defineChannel({kindHint:`slack`,state:{channelId:null,threadTs:null,teamId:null,triggeringUserId:null,pendingToolCallMessage:null,pendingAuthMessageTs:{}},fetchFile:l,context(t,n){return rebuildSlackContext(t,n,e.credentials)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,{send:r,waitUntil:i})=>{let a=await verifyInbound(n,e.credentials);return a===null?new Response(`unauthorized`,{status:401}):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(a,{send:r,waitUntil:i},{config:e}):handleEventPost({body:a,send:r,waitUntil:i,onAppMention:u,onDirectMessage:d,uploadPolicy:t,credentials:e.credentials})})],async receive(t,{send:n}){let r=t.args,i=r.channelId;if(!i||typeof i!=`string`)throw Error(`slackChannel().receive requires args.channelId.`);let o=typeof r.threadTs==`string`?r.threadTs:``,s=r.initialMessage;if(s&&o.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let c=o;if(s){let{thread:t}=buildSlackBinding({botToken:e.credentials?.botToken,channelId:i,threadTs:``,teamId:void 0}),n={card:s.card};s.fallbackText!==void 0&&(n.fallbackText=s.fallbackText),c=(await t.post(n)).id}return n(t.message,{auth:t.auth,continuationToken:slackContinuationToken(i,c),state:{channelId:i,threadTs:c||null,teamId:null,triggeringUserId:null}})},events:f})}async function handleEventPost(e){let t;try{t=JSON.parse(e.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(typeof t.challenge==`string`)return new Response(t.challenge,{status:200,headers:{"content-type":`text/plain`}});let n=parseAppMentionEvent(t);if(n)return e.waitUntil(dispatchInboundMessage({kind:`app_mention`,message:n,handler:e.onAppMention,send:e.send,uploadPolicy:e.uploadPolicy,credentials:e.credentials})),new Response(`ok`);let r=parseDirectMessageEvent(t);return r&&e.waitUntil(dispatchInboundMessage({kind:`direct_message`,message:r,handler:e.onDirectMessage,send:e.send,uploadPolicy:e.uploadPolicy,credentials:e.credentials})),new Response(`ok`)}async function verifyInbound(e,t){try{return await verifySlackRequest(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function dispatchInboundMessage(e){let{message:n,kind:r}=e,{thread:i,slack:o}=buildSlackBinding({botToken:e.credentials?.botToken,channelId:n.channelId,threadTs:n.threadTs,teamId:n.teamId}),s={thread:i,slack:o},c;try{c=await e.handler(s,n)}catch(e){logError(log,`${r} handler failed`,e,{channelId:n.channelId});return}if(c!=null)try{let t=await collectInboundFileParts({mention:n,thread:i,policy:e.uploadPolicy}),r=buildSlackTurnMessage(n.markdown,t),a={channelId:n.channelId,fullName:n.author?.fullName,teamId:n.teamId,threadTs:n.threadTs,userId:n.author?.userId??``,userName:n.author?.userName},o=a.userId?prependSlackContext(r,a):r;await e.send({message:o,modelContext:c.modelContext},{auth:c.auth,continuationToken:slackContinuationToken(n.channelId,n.threadTs),state:{channelId:n.channelId,threadTs:n.threadTs,teamId:n.teamId??null,triggeringUserId:a.userId||null}})}catch(e){logError(log,`${r} delivery failed`,e,{channelId:n.channelId})}}export{slackChannel};
1
+ import{createLogger,logError}from"#internal/logging.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{buildSlackBinding,slackContinuationToken}from"#public/channels/slack/api.js";import{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage}from"#public/channels/slack/defaults.js";import{buildSlackTurnMessage,collectInboundFileParts,createSlackFetchFile}from"#public/channels/slack/attachments.js";import{parseAppMentionEvent,parseDirectMessageEvent,prependSlackContext}from"#public/channels/slack/inbound.js";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#public/channels/slack/constants.js";import{handleInteractionPost}from"#public/channels/slack/interactions.js";import{verifySlackRequest}from"#public/channels/slack/verify.js";const log=createLogger(`slack.channel`);function rebuildSlackContext(e,t,n){let{thread:r,slack:i}=buildSlackBinding({botToken:n?.botToken,channelId:e.channelId??``,threadTs:e.threadTs??``,teamId:e.teamId??void 0,onThreadTsChanged(n){e.threadTs=n,e.channelId&&t.setContinuationToken(slackContinuationToken(e.channelId,n))}});return{thread:r,slack:i,state:e}}function slackChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),l=createSlackFetchFile({botToken:e.credentials?.botToken}),u=e.onAppMention??defaultOnAppMention,d=e.onDirectMessage??defaultOnDirectMessage,f={...defaultEvents,...e.events,"input.requested":e.events?.[`input.requested`]??defaultInputRequestedHandler()};return defineChannel({kindHint:`slack`,state:{channelId:null,threadTs:null,teamId:null,triggeringUserId:null,pendingToolCallMessage:null,pendingAuthMessageTs:{}},fetchFile:l,metadata(e){return{channelId:e.channelId,teamId:e.teamId,threadTs:e.threadTs,triggeringUserId:e.triggeringUserId??null}},context(t,n){return rebuildSlackContext(t,n,e.credentials)},routes:[POST(e.route??SLACK_CHANNEL_DEFAULT_ROUTE,async(n,{send:r,waitUntil:i})=>{let a=await verifyInbound(n,e.credentials);return a===null?new Response(`unauthorized`,{status:401}):(n.headers.get(`content-type`)??``).includes(`application/x-www-form-urlencoded`)?handleInteractionPost(a,{send:r,waitUntil:i},{config:e}):handleEventPost({body:a,send:r,waitUntil:i,onAppMention:u,onDirectMessage:d,uploadPolicy:t,credentials:e.credentials})})],async receive(t,{send:n}){let r=t.args,i=r.channelId;if(!i||typeof i!=`string`)throw Error(`slackChannel().receive requires args.channelId.`);let o=typeof r.threadTs==`string`?r.threadTs:``,s=r.initialMessage;if(s&&o.length>0)throw Error("slackChannel().receive: `threadTs` and `initialMessage` are mutually exclusive.");let c=o;if(s){let{thread:t}=buildSlackBinding({botToken:e.credentials?.botToken,channelId:i,threadTs:``,teamId:void 0}),n={card:s.card};s.fallbackText!==void 0&&(n.fallbackText=s.fallbackText),c=(await t.post(n)).id}return n(t.message,{auth:t.auth,continuationToken:slackContinuationToken(i,c),state:{channelId:i,threadTs:c||null,teamId:null,triggeringUserId:null}})},events:f})}async function handleEventPost(e){let t;try{t=JSON.parse(e.body)}catch(e){return log.warn(`inbound webhook body is not valid JSON`,{error:e}),new Response(`ok`)}if(typeof t.challenge==`string`)return new Response(t.challenge,{status:200,headers:{"content-type":`text/plain`}});let n=parseAppMentionEvent(t);if(n)return e.waitUntil(dispatchInboundMessage({kind:`app_mention`,message:n,handler:e.onAppMention,send:e.send,uploadPolicy:e.uploadPolicy,credentials:e.credentials})),new Response(`ok`);let r=parseDirectMessageEvent(t);return r&&e.waitUntil(dispatchInboundMessage({kind:`direct_message`,message:r,handler:e.onDirectMessage,send:e.send,uploadPolicy:e.uploadPolicy,credentials:e.credentials})),new Response(`ok`)}async function verifyInbound(e,t){try{return await verifySlackRequest(e,{signingSecret:t?.signingSecret??(t?.webhookVerifier?void 0:process.env.SLACK_SIGNING_SECRET),webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`slack inbound verification failed`,{error:e}),null}}async function dispatchInboundMessage(e){let{message:n,kind:r}=e,{thread:i,slack:o}=buildSlackBinding({botToken:e.credentials?.botToken,channelId:n.channelId,threadTs:n.threadTs,teamId:n.teamId}),s={thread:i,slack:o},c;try{c=await e.handler(s,n)}catch(e){logError(log,`${r} handler failed`,e,{channelId:n.channelId});return}if(c!=null)try{let t=await collectInboundFileParts({mention:n,thread:i,policy:e.uploadPolicy}),r=buildSlackTurnMessage(n.markdown,t),a={channelId:n.channelId,fullName:n.author?.fullName,teamId:n.teamId,threadTs:n.threadTs,userId:n.author?.userId??``,userName:n.author?.userName},o=a.userId?prependSlackContext(r,a):r;await e.send({message:o,modelContext:c.modelContext},{auth:c.auth,continuationToken:slackContinuationToken(n.channelId,n.threadTs),state:{channelId:n.channelId,threadTs:n.threadTs,teamId:n.teamId??null,triggeringUserId:a.userId||null}})}catch(e){logError(log,`${r} delivery failed`,e,{channelId:n.channelId})}}export{slackChannel};
@@ -1 +1 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{isCompiledChannel}from"#channel/compiled-channel.js";import{parseJsonObject}from"#shared/json.js";import{defaultDeliverResult}from"#channel/adapter.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponse}from"#public/channels/telegram/hitl.js";import{parseTelegramUpdate,prependTelegramContext}from"#public/channels/telegram/inbound.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,i={...defaultEvents,...e.events},c=defineChannel({kindHint:`telegram`,state:initialTelegramState(e.botUsername),fetchFile:createTelegramFetchFile({api:e.api,credentials:e.credentials,policy:t}),context(t,n){return rebuildTelegramContext(t,n,e)},routes:[POST(e.route??`/ash/v1/telegram`,async(i,{send:a,waitUntil:o})=>{let s=await verifyInbound(i,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Telegram body is not valid JSON`,{error:e}),new Response(`ok`)}let l=parseTelegramUpdate(c);return l===null?new Response(`ok`):l.kind===`message`?(o(dispatchMessage({config:e,message:l.message,onMessage:n,send:a,uploadPolicy:t})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:l.callbackQuery,send:a})),new Response(`ok`))})],async receive(t,{send:n}){let r=t.args,i=readChatId(r.chatId);if(i===void 0)throw Error(`telegramChannel().receive requires args.chatId.`);let a=typeof r.messageThreadId==`number`?r.messageThreadId:void 0,o=readOptionalString(r.conversationId),s=r.initialMessage;if(s!==void 0&&o!==void 0)throw Error("telegramChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c=o;return s!==void 0&&(c=(await buildTelegramHandle({config:e,state:{...initialTelegramState(e.botUsername),chatId:i,messageThreadId:a??null}}).sendMessage(s)).id||void 0),n(t.message,{auth:t.auth,continuationToken:telegramContinuationToken({chatId:i,conversationId:c,messageThreadId:a}),state:{...initialTelegramState(e.botUsername),chatId:i,conversationId:c??null,messageThreadId:a??null}})},events:i});return attachTelegramDeliver(c),c}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let n=e.config.api,r=e.state,i=e.config.credentials;function anchor(t){!t.id||r.chatType===`private`||(r.conversationId=t.id,r.chatId&&e.session?.setContinuationToken(telegramContinuationToken({chatId:r.chatId,conversationId:t.id,messageThreadId:r.messageThreadId??void 0})))}async function sendOne(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for outbound message.`);let a=await sendTelegramMessage({apiBaseUrl:n?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??r.messageThreadId??void 0},credentials:i,fetch:n?.fetch,fileBaseUrl:n?.fileBaseUrl,chatId:t});return anchor(a),a}return{botUsername:r.botUsername??e.config.botUsername,chatId:r.chatId??``,chatType:r.chatType??void 0,conversationId:r.conversationId??void 0,messageThreadId:r.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:n?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:i,fetch:n?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:n?.apiBaseUrl,chatId:t,credentials:i,fetch:n?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},post(e){return postTelegramMessage(e,sendOne)},request(e,t){return callTelegramApi({apiBaseUrl:n?.apiBaseUrl,body:t,botToken:i?.botToken,fetch:n?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let a=r.chatId??``;if(a)try{await sendTelegramChatAction({action:e,apiBaseUrl:n?.apiBaseUrl,chatId:a,credentials:i,fetch:n?.fetch,messageThreadId:r.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:a})}}}}async function postTelegramMessage(e,t){let n=typeof e==`string`?{text:e}:e,r=splitTelegramMessageText(n.text),i;for(let[e,a]of r.entries()){let r=await t(e===0?{...n,text:a}:{text:a});i===void 0&&(i=r)}return i??{id:``,raw:null}}async function verifyInbound(e,t){try{return await verifyTelegramRequest(e,{secretToken:t?.webhookVerifier?void 0:t?.webhookSecretToken,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`telegram inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){if(e.message.from?.isBot===!0)return;let t=stateFromMessage(e.message,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})},r;try{r=await e.onMessage(n,e.message)}catch(e){log.error(`message handler failed`,{error:e});return}if(r==null)return;let i=collectTelegramFileParts(e.message.attachments,e.uploadPolicy),a=prependTelegramContext(buildTelegramTurnMessage(e.message,i),{botUsername:e.config.botUsername,chatId:e.message.chat.id,chatTitle:e.message.chat.title,chatType:e.message.chat.type,messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),o=e.message.text||e.message.caption,s=e.message.replyToMessage?.from?.isBot===!0&&o.trim().length>0?[telegramReplyInputResponse({messageId:e.message.replyToMessage.messageId,text:o})]:void 0;try{await e.send({inputResponses:s,message:a,modelContext:r.modelContext},{auth:r.auth,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromCallbackQuery(e.query,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_HITL_CALLBACK_PREFIX)===!0){try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Answer received.`})}catch(e){log.warn(`Telegram callback-query acknowledgement failed`,{error:e})}if(!e.query.message||!t.chatId)return;try{await e.send({inputResponses:[telegramCallbackInputResponse(e.query.data)]},{auth:null,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`callback query delivery failed`,{error:e})}return}if(e.config.onCallbackQuery!==void 0){try{await e.config.onCallbackQuery(n,e.query)}catch(e){log.error(`custom callback-query handler failed`,{error:e})}return}try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Unsupported action.`})}catch(e){log.warn(`Telegram unsupported callback-query acknowledgement failed`,{error:e})}}function attachTelegramDeliver(e){if(!isCompiledChannel(e))return;let t=e.adapter;t.deliver=(e,t)=>{let n=e.inputResponses??[];if(n.some(isTelegramSyntheticResponse)){let r=resolveTelegramInputResponses(t.state,n);return r.length>0?{inputResponses:r,modelContext:e.modelContext}:e.message===void 0?void 0:{message:e.message,modelContext:e.modelContext}}return defaultDeliverResult(e)}}function stateFromMessage(e,t){let n=e.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:e.chat.id,chatType:e.chat.type,conversationId:n?null:conversationIdForMessage(e),messageThreadId:e.messageThreadId??null,triggeringUserId:e.from?.id??null}}function stateFromCallbackQuery(e,t){let n=e.message;if(!n)return{...initialTelegramState(t.botUsername),triggeringUserId:e.from.id};let r=n.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:n.chat.id,chatType:n.chat.type,conversationId:r?null:n.messageId,messageThreadId:n.messageThreadId??null,triggeringUserId:e.from.id}}function conversationIdForMessage(e){return e.replyToMessage?.from?.isBot===!0?e.replyToMessage.messageId:e.messageId}function continuationTokenFromState(e){return telegramContinuationToken({chatId:e.chatId??``,conversationId:e.chatType===`private`?void 0:e.conversationId??void 0,messageThreadId:e.messageThreadId??void 0})}function initialTelegramState(e){return{botUsername:e??null,chatId:null,chatType:null,conversationId:null,hitlCallbacks:{},messageThreadId:null,nextHitlCallbackId:0,pendingFreeformReplies:{},triggeringUserId:null}}function readChatId(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}function readOptionalString(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}export{telegramChannel};
1
+ import{createLogger,logError}from"#internal/logging.js";import{isCompiledChannel}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponse}from"#public/channels/telegram/hitl.js";import{parseTelegramUpdate,prependTelegramContext}from"#public/channels/telegram/inbound.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,r={...defaultEvents,...e.events},c=defineChannel({kindHint:`telegram`,state:initialTelegramState(e.botUsername),fetchFile:createTelegramFetchFile({api:e.api,credentials:e.credentials,policy:t}),context(t,n){return rebuildTelegramContext(t,n,e)},routes:[POST(e.route??`/ash/v1/telegram`,async(r,{send:a,waitUntil:o})=>{let s=await verifyInbound(r,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Telegram body is not valid JSON`,{error:e}),new Response(`ok`)}let l=parseTelegramUpdate(c);return l===null?new Response(`ok`):l.kind===`message`?(o(dispatchMessage({config:e,message:l.message,onMessage:n,send:a,uploadPolicy:t})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:l.callbackQuery,send:a})),new Response(`ok`))})],async receive(t,{send:n}){let r=t.args,i=readChatId(r.chatId);if(i===void 0)throw Error(`telegramChannel().receive requires args.chatId.`);let a=typeof r.messageThreadId==`number`?r.messageThreadId:void 0,o=readOptionalString(r.conversationId),s=r.initialMessage;if(s!==void 0&&o!==void 0)throw Error("telegramChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c=o;return s!==void 0&&(c=(await buildTelegramHandle({config:e,state:{...initialTelegramState(e.botUsername),chatId:i,messageThreadId:a??null}}).sendMessage(s)).id||void 0),n(t.message,{auth:t.auth,continuationToken:telegramContinuationToken({chatId:i,conversationId:c,messageThreadId:a}),state:{...initialTelegramState(e.botUsername),chatId:i,conversationId:c??null,messageThreadId:a??null}})},events:r});return attachTelegramDeliver(c),c}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let n=e.config.api,r=e.state,i=e.config.credentials;function anchor(t){!t.id||r.chatType===`private`||(r.conversationId=t.id,r.chatId&&e.session?.setContinuationToken(telegramContinuationToken({chatId:r.chatId,conversationId:t.id,messageThreadId:r.messageThreadId??void 0})))}async function sendOne(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for outbound message.`);let a=await sendTelegramMessage({apiBaseUrl:n?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??r.messageThreadId??void 0},credentials:i,fetch:n?.fetch,fileBaseUrl:n?.fileBaseUrl,chatId:t});return anchor(a),a}return{botUsername:r.botUsername??e.config.botUsername,chatId:r.chatId??``,chatType:r.chatType??void 0,conversationId:r.conversationId??void 0,messageThreadId:r.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:n?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:i,fetch:n?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:n?.apiBaseUrl,chatId:t,credentials:i,fetch:n?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},post(e){return postTelegramMessage(e,sendOne)},request(e,t){return callTelegramApi({apiBaseUrl:n?.apiBaseUrl,body:t,botToken:i?.botToken,fetch:n?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let a=r.chatId??``;if(a)try{await sendTelegramChatAction({action:e,apiBaseUrl:n?.apiBaseUrl,chatId:a,credentials:i,fetch:n?.fetch,messageThreadId:r.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:a})}}}}async function postTelegramMessage(e,t){let n=typeof e==`string`?{text:e}:e,r=splitTelegramMessageText(n.text),i;for(let[e,a]of r.entries()){let r=await t(e===0?{...n,text:a}:{text:a});i===void 0&&(i=r)}return i??{id:``,raw:null}}async function verifyInbound(e,t){try{return await verifyTelegramRequest(e,{secretToken:t?.webhookVerifier?void 0:t?.webhookSecretToken,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`telegram inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){if(e.message.from?.isBot===!0)return;let t=stateFromMessage(e.message,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})},r;try{r=await e.onMessage(n,e.message)}catch(e){log.error(`message handler failed`,{error:e});return}if(r==null)return;let i=collectTelegramFileParts(e.message.attachments,e.uploadPolicy),a=prependTelegramContext(buildTelegramTurnMessage(e.message,i),{botUsername:e.config.botUsername,chatId:e.message.chat.id,chatTitle:e.message.chat.title,chatType:e.message.chat.type,messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),o=e.message.text||e.message.caption,s=e.message.replyToMessage?.from?.isBot===!0&&o.trim().length>0?[telegramReplyInputResponse({messageId:e.message.replyToMessage.messageId,text:o})]:void 0;try{await e.send({inputResponses:s,message:a,modelContext:r.modelContext},{auth:r.auth,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromCallbackQuery(e.query,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_HITL_CALLBACK_PREFIX)===!0){try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Answer received.`})}catch(e){log.warn(`Telegram callback-query acknowledgement failed`,{error:e})}if(!e.query.message||!t.chatId)return;try{await e.send({inputResponses:[telegramCallbackInputResponse(e.query.data)]},{auth:null,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`callback query delivery failed`,{error:e})}return}if(e.config.onCallbackQuery!==void 0){try{await e.config.onCallbackQuery(n,e.query)}catch(e){log.error(`custom callback-query handler failed`,{error:e})}return}try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Unsupported action.`})}catch(e){log.warn(`Telegram unsupported callback-query acknowledgement failed`,{error:e})}}function attachTelegramDeliver(e){if(!isCompiledChannel(e))return;let t=e.adapter;t.deliver=(e,t)=>{let n=e.inputResponses??[];if(n.some(isTelegramSyntheticResponse)){let r=resolveTelegramInputResponses(t.state,n);return r.length>0?{inputResponses:r,modelContext:e.modelContext}:e.message===void 0?void 0:{message:e.message,modelContext:e.modelContext}}return defaultDeliverResult(e)}}function stateFromMessage(e,t){let n=e.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:e.chat.id,chatType:e.chat.type,conversationId:n?null:conversationIdForMessage(e),messageThreadId:e.messageThreadId??null,triggeringUserId:e.from?.id??null}}function stateFromCallbackQuery(e,t){let n=e.message;if(!n)return{...initialTelegramState(t.botUsername),triggeringUserId:e.from.id};let r=n.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:n.chat.id,chatType:n.chat.type,conversationId:r?null:n.messageId,messageThreadId:n.messageThreadId??null,triggeringUserId:e.from.id}}function conversationIdForMessage(e){return e.replyToMessage?.from?.isBot===!0?e.replyToMessage.messageId:e.messageId}function continuationTokenFromState(e){return telegramContinuationToken({chatId:e.chatId??``,conversationId:e.chatType===`private`?void 0:e.conversationId??void 0,messageThreadId:e.messageThreadId??void 0})}function initialTelegramState(e){return{botUsername:e??null,chatId:null,chatType:null,conversationId:null,hitlCallbacks:{},messageThreadId:null,nextHitlCallbackId:0,pendingFreeformReplies:{},triggeringUserId:null}}function readChatId(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}function readOptionalString(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}export{telegramChannel};
@@ -1,4 +1,4 @@
1
- export { twilioChannel, type TwilioAllowFrom, type TwilioChannel, type TwilioChannelConfig, type TwilioChannelCredentials, type TwilioChannelEvents, type TwilioChannelState, type TwilioContext, type TwilioEventContext, type TwilioHandle, type TwilioInboundResult, type TwilioInboundResultOrPromise, type TwilioMessagingConfig, type TwilioReceiveArgs, type TwilioSendMessageOptions, type TwilioVoiceConfig, type TwilioVoiceResult, type TwilioVoiceResultOrPromise, } from "#public/channels/twilio/twilioChannel.js";
1
+ export { twilioChannel, type TwilioAllowFrom, type TwilioChannel, type TwilioChannelConfig, type TwilioChannelCredentials, type TwilioChannelEvents, type TwilioChannelState, type TwilioContext, type TwilioEventContext, type TwilioHandle, type TwilioInboundResult, type TwilioInboundResultOrPromise, type TwilioInstrumentationMetadata, type TwilioMessagingConfig, type TwilioReceiveArgs, type TwilioSendMessageOptions, type TwilioVoiceConfig, type TwilioVoiceResult, type TwilioVoiceResultOrPromise, } from "#public/channels/twilio/twilioChannel.js";
2
2
  export { callTwilioApi, resolveTwilioAccountSid, sendTwilioMessage, twilioContinuationToken, updateTwilioCall, type TwilioAccountSid, type TwilioApiOptions, type TwilioApiResponse, type TwilioCredentials, type TwilioFetch, type TwilioSendMessageInput, type TwilioUpdateCallInput, } from "#public/channels/twilio/api.js";
3
3
  export type { TwilioInboundContext, TwilioTextMessage, TwilioVoiceCall, TwilioVoiceTranscription, } from "#public/channels/twilio/inbound.js";
4
4
  export { emptyTwilioResponse, escapeXml, gatherSpeechTwilioResponse, sayTwilioResponse, twimlResponse, type TwilioGatherTwimlOptions, } from "#public/channels/twilio/twiml.js";
@@ -30,6 +30,12 @@ export interface TwilioChannelState {
30
30
  /** Most recent inbound Call SID when this session was started by voice. */
31
31
  lastCallSid?: string | null;
32
32
  }
33
+ export interface TwilioInstrumentationMetadata extends Record<string, unknown> {
34
+ readonly from: string | null;
35
+ readonly lastCallSid: string | null;
36
+ readonly lastMessageSid: string | null;
37
+ readonly to: string | null;
38
+ }
33
39
  /** Twilio channel credentials. `authToken` also verifies inbound webhook signatures. */
34
40
  export interface TwilioChannelCredentials extends TwilioCredentials {
35
41
  readonly authToken?: TwilioAuthToken;
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{verifyTwilioRequest}from"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription,prependTwilioContext}from"#public/channels/twilio/inbound.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let r=buildRoutes(e.route??`/ash/v1/twilio`),i=e.onText??defaultOnText,a=e.onVoice??defaultOnVoice,s=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events};return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[POST(r.messages,async(t,{send:n,waitUntil:r})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioTextMessage(a.params);return o?await isAllowed(o.from,e.allowFrom)?(r(dispatchText({config:e,message:o,onText:i,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}),POST(r.voice,async t=>{let n=await verifyInbound(t,e);if(n===null)return new Response(`unauthorized`,{status:401});let i=parseTwilioVoiceCall(n.params);if(!i)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(i.from,e.allowFrom))return new Response(`forbidden`,{status:403});let o=await acceptVoiceCall({call:i,config:e,onVoice:a});if(o===null)return new Response(`forbidden`,{status:403});let s=o??{};return gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),hints:s.hints??e.voice?.hints,language:s.language??e.voice?.language,profanityFilter:s.profanityFilter??e.voice?.profanityFilter,prompt:s.prompt??e.voice?.prompt??`Please say your message after the tone.`,speechModel:s.speechModel??e.voice?.speechModel,speechTimeout:s.speechTimeout??e.voice?.speechTimeout??`auto`,timeoutSeconds:s.timeoutSeconds??e.voice?.timeoutSeconds,voice:s.voice??e.voice?.voice})}),POST(r.transcription,async(t,{send:n,waitUntil:i})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioVoiceTranscription(a.params);return o?await isAllowed(o.from,e.allowFrom)?(i(dispatchVoiceTranscription({config:e,onVoiceTranscription:s,send:n,transcription:o})),sayTwilioResponse(e.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),language:e.voice?.language,prompt:e.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.voice?.speechTimeout??`auto`,timeoutSeconds:e.voice?.timeoutSeconds})})],async receive(t,{send:n}){let r=readString(t.args.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires args.phoneNumber.`);let i=readString(t.args.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function buildRoutes(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return{messages:`${t}/messages`,transcription:`${t}/voice/transcription`,voice:`${t}/voice`}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function verifyInbound(e,t){try{return await verifyTwilioRequest(e,{authToken:t.credentials?.authToken,webhookUrl:t.webhookUrl})}catch(e){return log.warn(`twilio inbound verification failed`,{error:e}),null}}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=prependTwilioContext(t.body,{channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send(i,{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=prependTwilioContext(t.text,{callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send(i,{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}async function buildActionUrl(e,t,n){let r=typeof t.publicBaseUrl==`function`?await t.publicBaseUrl(e):t.publicBaseUrl;if(r)return new URL(n,ensureTrailingSlash(r)).toString();let i=new URL(e.url);return i.pathname=n,i.search=``,i.toString()}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:`${e}/`}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};
1
+ import{createLogger}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{verifyTwilioRequest}from"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription,prependTwilioContext}from"#public/channels/twilio/inbound.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let r=buildRoutes(e.route??`/ash/v1/twilio`),i=e.onText??defaultOnText,a=e.onVoice??defaultOnVoice,s=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events};return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{from:e.from,lastCallSid:e.lastCallSid??null,lastMessageSid:e.lastMessageSid??null,to:e.to}},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[POST(r.messages,async(t,{send:n,waitUntil:r})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioTextMessage(a.params);return o?await isAllowed(o.from,e.allowFrom)?(r(dispatchText({config:e,message:o,onText:i,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}),POST(r.voice,async t=>{let n=await verifyInbound(t,e);if(n===null)return new Response(`unauthorized`,{status:401});let i=parseTwilioVoiceCall(n.params);if(!i)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(i.from,e.allowFrom))return new Response(`forbidden`,{status:403});let o=await acceptVoiceCall({call:i,config:e,onVoice:a});if(o===null)return new Response(`forbidden`,{status:403});let s=o??{};return gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),hints:s.hints??e.voice?.hints,language:s.language??e.voice?.language,profanityFilter:s.profanityFilter??e.voice?.profanityFilter,prompt:s.prompt??e.voice?.prompt??`Please say your message after the tone.`,speechModel:s.speechModel??e.voice?.speechModel,speechTimeout:s.speechTimeout??e.voice?.speechTimeout??`auto`,timeoutSeconds:s.timeoutSeconds??e.voice?.timeoutSeconds,voice:s.voice??e.voice?.voice})}),POST(r.transcription,async(t,{send:n,waitUntil:i})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioVoiceTranscription(a.params);return o?await isAllowed(o.from,e.allowFrom)?(i(dispatchVoiceTranscription({config:e,onVoiceTranscription:s,send:n,transcription:o})),sayTwilioResponse(e.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),language:e.voice?.language,prompt:e.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.voice?.speechTimeout??`auto`,timeoutSeconds:e.voice?.timeoutSeconds})})],async receive(t,{send:n}){let r=readString(t.args.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires args.phoneNumber.`);let i=readString(t.args.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function buildRoutes(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return{messages:`${t}/messages`,transcription:`${t}/voice/transcription`,voice:`${t}/voice`}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function verifyInbound(e,t){try{return await verifyTwilioRequest(e,{authToken:t.credentials?.authToken,webhookUrl:t.webhookUrl})}catch(e){return log.warn(`twilio inbound verification failed`,{error:e}),null}}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=prependTwilioContext(t.body,{channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send(i,{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=prependTwilioContext(t.text,{callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send(i,{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}async function buildActionUrl(e,t,n){let r=typeof t.publicBaseUrl==`function`?await t.publicBaseUrl(e):t.publicBaseUrl;if(r)return new URL(n,ensureTrailingSlash(r)).toString();let i=new URL(e.url);return i.pathname=n,i.search=``,i.toString()}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:`${e}/`}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};