experimental-ash 0.34.0 → 0.36.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 (119) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/docs/internals/context.md +6 -5
  3. package/dist/docs/internals/core-beliefs.md +2 -2
  4. package/dist/docs/internals/hooks.md +16 -11
  5. package/dist/docs/internals/mechanical-invariants.md +4 -4
  6. package/dist/docs/internals/testing.md +1 -1
  7. package/dist/docs/public/README.md +1 -1
  8. package/dist/docs/public/auth-and-route-protection.md +12 -3
  9. package/dist/docs/public/channels/{README.md → index.md} +2 -2
  10. package/dist/docs/public/channels/slack.md +3 -3
  11. package/dist/docs/public/cli-build-and-debugging.md +1 -1
  12. package/dist/docs/public/faqs.md +4 -5
  13. package/dist/docs/public/hooks.md +18 -23
  14. package/dist/docs/public/meta.json +4 -4
  15. package/dist/docs/public/sandbox.md +51 -30
  16. package/dist/docs/public/schedules.md +1 -1
  17. package/dist/docs/public/session-context.md +26 -28
  18. package/dist/docs/public/skills.md +3 -4
  19. package/dist/docs/public/subagents.md +1 -1
  20. package/dist/docs/public/tools.md +13 -17
  21. package/dist/docs/public/typescript-api.md +10 -11
  22. package/dist/src/channel/session.d.ts +3 -29
  23. package/dist/src/channel/session.js +1 -1
  24. package/dist/src/compiled/.vendor-stamp.json +2 -2
  25. package/dist/src/compiled/@vercel/sandbox/index.d.ts +11 -2
  26. package/dist/src/compiled/@vercel/sandbox/index.js +3 -3
  27. package/dist/src/compiled/@vercel/sandbox/package.json +1 -1
  28. package/dist/src/compiled/_chunks/node/{auth-ZhCJAHxl.js → auth-CVVvWjaK.js} +1 -1
  29. package/dist/src/compiled/_chunks/node/{version-D4IYmfaS.js → version-nR4RSpFw.js} +1 -1
  30. package/dist/src/context/build-callback-context.d.ts +8 -0
  31. package/dist/src/context/build-callback-context.js +1 -0
  32. package/dist/src/context/hook-lifecycle.js +1 -1
  33. package/dist/src/execution/node-step.js +1 -1
  34. package/dist/src/execution/sandbox/bash-tool.d.ts +2 -1
  35. package/dist/src/execution/sandbox/bash-tool.js +1 -1
  36. package/dist/src/execution/sandbox/bindings/vercel.d.ts +1 -1
  37. package/dist/src/execution/sandbox/glob-tool.d.ts +2 -1
  38. package/dist/src/execution/sandbox/glob-tool.js +3 -3
  39. package/dist/src/execution/sandbox/grep-tool.d.ts +2 -1
  40. package/dist/src/execution/sandbox/grep-tool.js +3 -3
  41. package/dist/src/execution/sandbox/read-file-tool.d.ts +2 -1
  42. package/dist/src/execution/sandbox/read-file-tool.js +1 -1
  43. package/dist/src/execution/sandbox/session.js +1 -1
  44. package/dist/src/execution/sandbox/write-file-tool.d.ts +2 -1
  45. package/dist/src/execution/sandbox/write-file-tool.js +1 -1
  46. package/dist/src/execution/tool-compaction.js +1 -1
  47. package/dist/src/harness/code-mode-approval.js +1 -1
  48. package/dist/src/harness/code-mode.js +1 -1
  49. package/dist/src/harness/tool-loop.js +1 -1
  50. package/dist/src/internal/application/package.js +1 -1
  51. package/dist/src/internal/logging.js +1 -1
  52. package/dist/src/internal/workflow-bundle/builder.js +2 -2
  53. package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/index.js +1 -1
  54. package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/runtime/manager.js +1 -0
  55. package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/runtime/worker-source.js +408 -0
  56. package/dist/src/packages/ash-scaffold/src/channels.js +2 -12
  57. package/dist/src/packages/ash-scaffold/src/pnpm-workspace.js +11 -0
  58. package/dist/src/packages/ash-scaffold/src/project.js +1 -1
  59. package/dist/src/packages/ash-scaffold/src/web-template.js +2 -2
  60. package/dist/src/public/channels/auth.d.ts +22 -11
  61. package/dist/src/public/channels/auth.js +1 -1
  62. package/dist/src/public/context/index.d.ts +3 -2
  63. package/dist/src/public/context/index.js +1 -1
  64. package/dist/src/public/definitions/callback-context.d.ts +22 -0
  65. package/dist/src/public/definitions/callback-context.js +1 -0
  66. package/dist/src/public/definitions/hook.d.ts +9 -49
  67. package/dist/src/public/definitions/sandbox.d.ts +1 -1
  68. package/dist/src/public/definitions/tool.d.ts +14 -15
  69. package/dist/src/public/hooks/index.d.ts +1 -1
  70. package/dist/src/public/sandbox/index.d.ts +1 -2
  71. package/dist/src/public/sandbox/index.js +1 -1
  72. package/dist/src/public/sandbox/vercel-sandbox.d.ts +4 -4
  73. package/dist/src/public/skills/index.d.ts +0 -1
  74. package/dist/src/public/skills/index.js +1 -1
  75. package/dist/src/public/tools/defaults.js +1 -1
  76. package/dist/src/public/tools/define-bash-tool.js +1 -1
  77. package/dist/src/public/tools/define-glob-tool.js +1 -1
  78. package/dist/src/public/tools/define-grep-tool.js +1 -1
  79. package/dist/src/public/tools/define-read-file-tool.js +1 -1
  80. package/dist/src/public/tools/define-write-file-tool.js +1 -1
  81. package/dist/src/public/tools/index.d.ts +2 -1
  82. package/dist/src/public/tools/internal.d.ts +4 -0
  83. package/dist/src/public/tools/internal.js +1 -1
  84. package/dist/src/runtime/framework-tools/bash.js +1 -1
  85. package/dist/src/runtime/framework-tools/connection-search.js +1 -1
  86. package/dist/src/runtime/framework-tools/connection-tools.js +1 -1
  87. package/dist/src/runtime/framework-tools/file-state.d.ts +2 -2
  88. package/dist/src/runtime/framework-tools/file-state.js +1 -1
  89. package/dist/src/runtime/framework-tools/glob.js +1 -1
  90. package/dist/src/runtime/framework-tools/grep.js +1 -1
  91. package/dist/src/runtime/framework-tools/read-file.js +2 -2
  92. package/dist/src/runtime/framework-tools/todo.js +1 -1
  93. package/dist/src/runtime/framework-tools/write-file.js +1 -1
  94. package/dist/src/runtime/governance/auth/oidc.js +1 -1
  95. package/dist/src/runtime/governance/auth/token-claims.d.ts +2 -0
  96. package/dist/src/runtime/governance/auth/token-claims.js +1 -1
  97. package/dist/src/runtime/governance/auth/types.d.ts +6 -0
  98. package/dist/src/runtime/types.d.ts +2 -2
  99. package/dist/src/shared/sandbox-session.d.ts +0 -17
  100. package/package.json +3 -3
  101. package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/runtime/manager.js +0 -1
  102. package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/runtime/worker-source.js +0 -1153
  103. package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/runtime-assets.js +0 -1
  104. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/approval-continuation.js +0 -0
  105. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/approval-response.js +0 -0
  106. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/code-mode-tool.js +0 -0
  107. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/continuation-capability.js +0 -0
  108. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/errors.js +0 -0
  109. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/fetch-policy.js +0 -0
  110. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/host-interrupt.js +0 -0
  111. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/interrupt-continuation.js +0 -0
  112. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/options.js +0 -0
  113. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/run-code-mode.js +0 -0
  114. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/runtime/max-workers.js +0 -0
  115. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/serialization.js +0 -0
  116. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/source-cache.js +0 -0
  117. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/telemetry.js +0 -0
  118. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/tool-invocation.js +0 -0
  119. /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/tool-prompt.js +0 -0
@@ -1,17 +1,7 @@
1
- import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.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_ASH_RELEASE_AGE_EXCLUSION=` - experimental-ash`,WEB_SHARP_BUILD_POLICY=` sharp: false`,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.34.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.154`,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},s=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:s}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(s)}]}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.36.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.154`,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({
5
5
  credentials: connectSlackCredentials("slack/${e}"),
6
6
  });
7
- `}function renderWebAppTemplate(e,t){return e.replaceAll(`__ASH_INIT_APP_NAME__`,t)}function findYamlBlockEnd(e,t){let n=t+1;for(;n<e.length;){let t=e[n]??``;if(t.length>0&&!t.startsWith(` `)&&!t.startsWith(` `))break;n+=1}return n}function withSharpBuildPolicy(e){let t=e.endsWith(`
8
- `)?e:`${e}\n`,n=t.split(`
9
- `),r=n.findIndex(e=>e===`allowBuilds:`);if(r<0)return`${t.trim().length===0?``:`${t}\n`}allowBuilds:\n${WEB_SHARP_BUILD_POLICY}\n`;let i=findYamlBlockEnd(n,r);if(n.slice(r+1,i).some(e=>/^\s+sharp:/.test(e)))return e;let a=i;for(;a>r+1&&n[a-1]===``;)--a;return n.splice(a,0,WEB_SHARP_BUILD_POLICY),n.join(`
10
- `)}function withExperimentalAshReleaseAgeExclusion(e){let t=e.endsWith(`
11
- `)?e:`${e}\n`,n=t.split(`
12
- `),r=n.findIndex(e=>e===`minimumReleaseAgeExclude:`);if(r<0)return`${t.trim().length===0?``:`${t}\n`}minimumReleaseAgeExclude:\n${WEB_ASH_RELEASE_AGE_EXCLUSION}\n`;let i=findYamlBlockEnd(n,r);if(n.slice(r+1,i).some(e=>e.trim()===`- experimental-ash`))return e;let a=i;for(;a>r+1&&n[a-1]===``;)--a;return n.splice(a,0,WEB_ASH_RELEASE_AGE_EXCLUSION),n.join(`
13
- `)}async function ensureWebPnpmWorkspacePolicy(e,t){if(!await pathExists(e))return await writeTextFile(e,t,{force:!0}),`written`;let i=await readFile(e,`utf8`),a=withExperimentalAshReleaseAgeExclusion(withSharpBuildPolicy(i));return a===i?`skipped`:(await writeFile(e,a,`utf8`),`written`)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,ash:n?.ash??WEB_DEFAULT_VERCEL_SERVICES.ash}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}function assertStampedVersion(e,t){if(t.startsWith(`__`))throw Error(`Channel scaffold received unstamped version token (${e}=${t}). Build @vercel/ash-scaffold before using its dist entrypoint.`)}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),a=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await hasPackageDependency(t,`next`))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let o=await patchWebPackageJson(t,resolveWebPackageVersions(e.webPackageVersions)),c=[],l=[],u=basename(resolve(e.projectRoot)),d=join(e.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(d)===`written`?c.push(d):l.push(d);let f=join(e.projectRoot,`pnpm-workspace.yaml`);await ensureWebPnpmWorkspacePolicy(f,`minimumReleaseAgeExclude:
14
- - experimental-ash
15
- allowBuilds:
16
- sharp: false
17
- `)===`written`?c.push(f):l.push(f);for(let[t,a]of Object.entries(WEB_APP_TEMPLATE_FILES)){let i=join(e.projectRoot,t);if(t===`agent/channels/ash.ts`&&!e.force&&await pathExists(i)){l.push(i);continue}await writeTextFile(i,renderWebAppTemplate(a,u),{force:!0}),c.push(i)}return{kind:`web`,action:a?`overwritten`:`created`,filesWritten:c,filesSkipped:l,packageJsonUpdated:o}}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let a=e.connectPackageVersion??`0.0.6-alpha.1`;assertStampedVersion(`connectPackageVersion`,a);let o=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,a),s=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);return await writeTextFile(t,buildSlackTemplate(s),{force:e.force}),{kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:o,slackConnectorSlug:s}}async function listAuthoredChannels(n){let r=join(n,`agent/channels`),i;try{i=await readdir(r,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let a=[];for(let n of i){if(n.isFile()){let t=getSupportedModuleBaseName(n.name);t!==null&&a.push(t);continue}if(n.isDirectory())try{(await readdir(join(r,n.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&a.push(n.name)}catch{}}return a.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,listAuthoredChannels,normalizeSlackConnectorSlug};
7
+ `}function renderWebAppTemplate(e,t){return e.replaceAll(`__ASH_INIT_APP_NAME__`,t)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,ash:n?.ash??WEB_DEFAULT_VERCEL_SERVICES.ash}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}function assertStampedVersion(e,t){if(t.startsWith(`__`))throw Error(`Channel scaffold received unstamped version token (${e}=${t}). Build @vercel/ash-scaffold before using its dist entrypoint.`)}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),s=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await hasPackageDependency(t,`next`))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let c=await patchWebPackageJson(t,resolveWebPackageVersions(e.webPackageVersions)),l=[],u=[],d=basename(resolve(e.projectRoot)),f=join(e.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(f)===`written`?l.push(f):u.push(f);let p=join(e.projectRoot,PNPM_WORKSPACE_PATH);await ensurePnpmWorkspacePolicy(p)===`written`?l.push(p):u.push(p);for(let[t,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(e.projectRoot,t);if(t===`agent/channels/ash.ts`&&!e.force&&await pathExists(a)){u.push(a);continue}await writeTextFile(a,renderWebAppTemplate(i,d),{force:!0}),l.push(a)}return{kind:`web`,action:s?`overwritten`:`created`,filesWritten:l,filesSkipped:u,packageJsonUpdated:c}}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let a=e.connectPackageVersion??`0.0.6-alpha.1`;assertStampedVersion(`connectPackageVersion`,a);let o=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,a),s=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);return await writeTextFile(t,buildSlackTemplate(s),{force:e.force}),{kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:o,slackConnectorSlug:s}}async function listAuthoredChannels(n){let r=join(n,`agent/channels`),i;try{i=await readdir(r,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let a=[];for(let n of i){if(n.isFile()){let t=getSupportedModuleBaseName(n.name);t!==null&&a.push(t);continue}if(n.isDirectory())try{(await readdir(join(r,n.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&a.push(n.name)}catch{}}return a.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,listAuthoredChannels,normalizeSlackConnectorSlug};
@@ -0,0 +1,11 @@
1
+ import{pathExists,writeTextFile}from"./files.js";import{readFile,writeFile}from"node:fs/promises";const PNPM_WORKSPACE_PATH=`pnpm-workspace.yaml`,PNPM_WORKSPACE_CONTENT=`minimumReleaseAgeExclude:
2
+ - experimental-ash
3
+ allowBuilds:
4
+ sharp: false
5
+ `,ASH_RELEASE_AGE_EXCLUSION=` - experimental-ash`,SHARP_BUILD_POLICY=` sharp: false`;function findYamlBlockEnd(e,t){let n=t+1;for(;n<e.length;){let t=e[n]??``;if(t.length>0&&!t.startsWith(` `)&&!t.startsWith(` `))break;n+=1}return n}function withSharpBuildPolicy(e){let t=e.endsWith(`
6
+ `)?e:`${e}\n`,n=t.split(`
7
+ `),r=n.findIndex(e=>e===`allowBuilds:`);if(r<0)return`${t.trim().length===0?``:`${t}\n`}allowBuilds:\n${SHARP_BUILD_POLICY}\n`;let i=findYamlBlockEnd(n,r);if(n.slice(r+1,i).some(e=>/^\s+sharp:/.test(e)))return e;let a=i;for(;a>r+1&&n[a-1]===``;)--a;return n.splice(a,0,SHARP_BUILD_POLICY),n.join(`
8
+ `)}function withExperimentalAshReleaseAgeExclusion(e){let t=e.endsWith(`
9
+ `)?e:`${e}\n`,n=t.split(`
10
+ `),r=n.findIndex(e=>e===`minimumReleaseAgeExclude:`);if(r<0)return`${t.trim().length===0?``:`${t}\n`}minimumReleaseAgeExclude:\n${ASH_RELEASE_AGE_EXCLUSION}\n`;let i=findYamlBlockEnd(n,r);if(n.slice(r+1,i).some(e=>e.trim()===`- experimental-ash`))return e;let a=i;for(;a>r+1&&n[a-1]===``;)--a;return n.splice(a,0,ASH_RELEASE_AGE_EXCLUSION),n.join(`
11
+ `)}async function ensurePnpmWorkspacePolicy(i){if(!await pathExists(i))return await writeTextFile(i,PNPM_WORKSPACE_CONTENT,{force:!0}),`written`;let o=await readFile(i,`utf8`),s=withExperimentalAshReleaseAgeExclusion(withSharpBuildPolicy(o));return s===o?`skipped`:(await writeFile(i,s,`utf8`),`written`)}export{PNPM_WORKSPACE_CONTENT,PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy};
@@ -1 +1 @@
1
- import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"./module-files.js";import"./files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{join}from"node:path";import{stat}from"node:fs/promises";new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),WEB_APP_TEMPLATE_FILES[`agent/channels/ash.ts`];async function isAshProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{isAshProject};
1
+ import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"./module-files.js";import"./files.js";import"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{join}from"node:path";import{stat}from"node:fs/promises";new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),WEB_APP_TEMPLATE_FILES[`agent/channels/ash.ts`];async function isAshProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{isAshProject};
@@ -16,10 +16,10 @@ function exampleProductionAuth(): AuthFn<Request> {
16
16
 
17
17
  export default ashChannel({
18
18
  auth: [
19
- // Lets the Ash TUI and your Vercel deployments reach the deployed agent.
20
- vercelOidc(),
21
19
  // Open on localhost for \`ash dev\` and the REPL; ignored in production.
22
20
  localDev(),
21
+ // Lets the Ash TUI and your Vercel deployments reach the deployed agent.
22
+ vercelOidc(),
23
23
  // Your end-user auth — replace the placeholder above.
24
24
  exampleProductionAuth(),
25
25
  ],
@@ -198,11 +198,12 @@ export declare function routeAuth(request: Request, auth: AuthFn<Request> | read
198
198
  */
199
199
  export declare function none<TEvent = unknown>(): AuthFn<TEvent>;
200
200
  /**
201
- * Returns an {@link AuthFn} that authenticates requests **only when the
202
- * inbound request was addressed to a loopback hostname**.
201
+ * Returns an {@link AuthFn} that authenticates requests during local
202
+ * development — either when the inbound request was addressed to a
203
+ * loopback hostname, or when the process is running under `vercel dev`.
203
204
  *
204
- * The check is on the request URL's hostname, not the host process. A
205
- * hostname is treated as loopback when it is one of:
205
+ * The primary check is on the request URL's hostname, not the host
206
+ * process. A hostname is treated as loopback when it is one of:
206
207
  *
207
208
  * - `localhost` or any `*.localhost` subdomain (per RFC 6761, browsers
208
209
  * and DNS resolvers route the whole `.localhost` TLD to loopback);
@@ -216,13 +217,16 @@ export declare function none<TEvent = unknown>(): AuthFn<TEvent>;
216
217
  * `[localDev(), vercelOidc()]` the canonical "open on localhost,
217
218
  * Vercel OIDC in prod" pattern.
218
219
  *
219
- * This is intentionally based on the request's destination hostname
220
- * rather than `process.env.VERCEL`. Sniffing the host process is
221
- * unsafe: a deployment outside Vercel (Fly, Railway, raw container,
222
- * etc.) would leave `VERCEL` unset and accept every request from the
223
- * public internet. Inspecting the request hostname is correct across
224
- * `ash dev`, `vercel dev`, smoke tests, Vitest workers, and any
225
- * deployment targetVercel or otherwise.
220
+ * The hostname check is intentionally not based on bare
221
+ * `process.env.VERCEL`: sniffing only that flag is unsafe because a
222
+ * deployment outside Vercel (Fly, Railway, raw container, etc.) leaves
223
+ * `VERCEL` unset and would accept every request from the public
224
+ * internet. The one process-level exception is `vercel dev`, detected
225
+ * by `VERCEL=1` **and** `VERCEL_ENV=development` together. That pair is
226
+ * only ever set by the local `vercel dev` server preview and
227
+ * production deployments report `VERCEL_ENV=preview`/`production` — so
228
+ * it opens the local dev server (which may serve over a non-loopback
229
+ * host) without ever opening a real deployment.
226
230
  *
227
231
  * Caveat: this helper assumes a sane edge in front of public origins.
228
232
  * If you publish an origin that trusts an attacker-controlled `Host`
@@ -256,6 +260,13 @@ export interface VerifyVercelOidcOptions {
256
260
  * accepted regardless of `subjects`. This guarantees the deployment's
257
261
  * own runtime callers (subagent, internal fetches, etc.) authenticate
258
262
  * without authors having to enumerate them.
263
+ * - Vercel OIDC tokens with an `external_sub` claim authenticate as
264
+ * `principalType: "user"` when they match the current
265
+ * `VERCEL_PROJECT_ID` (if set) and current `VERCEL_TARGET_ENV` /
266
+ * `VERCEL_ENV` (if set). Their `external_sub` becomes the Ash subject,
267
+ * `external_iss` or `connector_id` becomes the Ash issuer when present,
268
+ * and string-valued OIDC profile claims such as `name`, `picture`, and
269
+ * `email` are exposed as auth attributes.
259
270
  * - Tokens from other Vercel projects are accepted **only** when their
260
271
  * `sub` matches one of the supplied {@link VerifyVercelOidcOptions.subjects}
261
272
  * patterns.
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{decodeJwt}from"#compiled/jose/index.js";import{authenticateHttpBasicStrategy}from"#runtime/governance/auth/http-basic.js";import{authenticateJwtEcdsaStrategy}from"#runtime/governance/auth/jwt-ecdsa.js";import{authenticateJwtHmacStrategy}from"#runtime/governance/auth/jwt-hmac.js";import{authenticateOidcStrategy}from"#runtime/governance/auth/oidc.js";import{createRuntimeSessionAuthContext}from"#runtime/governance/auth/types.js";import{createRuntimeIpAllowList,isRuntimeIpAllowed}from"#runtime/governance/network/ip-allow-list.js";const vercelOidcLog=createLogger(`auth.vercel-oidc`);function verifyHttpBasic(e,t){if(e===null)return{ok:!1};let r=authenticateHttpBasicStrategy({authorization:e,strategy:{kind:`http-basic`,password:t.password,username:t.username}});return r.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(r.principal)}:{ok:!1}}async function verifyJwtHmac(e,t){if(e===null||e.length===0)return{ok:!1};let n=await authenticateJwtHmacStrategy({strategy:{algorithm:t.algorithm,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,issuer:t.issuer,kind:`jwt-hmac`,secret:t.secret,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},token:e});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function verifyJwtEcdsa(e,t){if(e===null||e.length===0)return{ok:!1};let n=await authenticateJwtEcdsaStrategy({strategy:{algorithm:t.algorithm,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,issuer:t.issuer,kind:`jwt-ecdsa`,publicKey:t.publicKey,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},token:e});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function verifyOidc(e,t){let n=await runOidcVerification(e,{...t,acceptCurrentVercelProject:!1});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function runOidcVerification(e,t){return e===null||e.length===0?{kind:`not-authenticated`}:await authenticateOidcStrategy({strategy:{acceptCurrentVercelProject:t.acceptCurrentVercelProject,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,discoveryUrl:t.discoveryUrl??`${t.issuer.replace(/\/$/,``)}/.well-known/openid-configuration`,issuer:t.issuer,kind:`oidc`,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},token:e})}function extractBearerToken(e){if(e===null)return null;let t=/^Bearer\s+(.+)$/i.exec(e)?.[1]?.trim();return t===void 0||t.length===0?null:t}function createIpAllowList(e){return createRuntimeIpAllowList(e)}function isIpAllowed(e,t){return e===null?!1:isRuntimeIpAllowed(e,t)}function createUnauthorizedResponse(e={}){let t=e.status??401,n=e.code??(t===403?`forbidden`:`unauthorized`),r=e.message??(t===403?`Forbidden.`:`Authorization is required for this route.`),i=e.challenges??[],a=new Headers({"cache-control":`no-store`});for(let e of i)a.append(`www-authenticate`,formatChallenge(e));return Response.json({code:n,error:r,ok:!1},{headers:a,status:t})}function formatChallenge(e){if(e.parameters===void 0||Object.keys(e.parameters).length===0)return e.scheme;let t=Object.entries(e.parameters).map(([e,t])=>`${e}="${escapeChallengeValue(t)}"`).join(`, `);return`${e.scheme} ${t}`}function escapeChallengeValue(e){return e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`)}async function routeAuth(e,t){let n=Array.isArray(t)?t:[t];for(let t of n){let n=await t(e);if(n)return n}return createUnauthorizedResponse({challenges:[{scheme:`Bearer`}]})}function none(){return()=>ANONYMOUS_SESSION_AUTH_CONTEXT}function localDev(){return e=>isLoopbackRequest(e)?LOCAL_DEV_SESSION_AUTH_CONTEXT:null}const LOOPBACK_HOSTNAMES=new Set([`localhost`,`[::1]`]),LOOPBACK_IPV4_PREFIX=/^127\./;function isLoopbackRequest(e){let t;try{t=new URL(e.url).hostname}catch{return!1}return!!(LOOPBACK_HOSTNAMES.has(t)||LOOPBACK_IPV4_PREFIX.test(t)||t.endsWith(`.localhost`))}const ANONYMOUS_SESSION_AUTH_CONTEXT={attributes:{},authenticator:`none`,principalId:`anonymous`,principalType:`anonymous`},LOCAL_DEV_SESSION_AUTH_CONTEXT={attributes:{},authenticator:`local-dev`,principalId:`local-dev`,principalType:`local-dev`};async function verifyVercelOidc(e,t={}){if(e===null||e.length===0)return vercelOidcLog.debug(`Rejected request without a bearer token.`),{ok:!1};let n=decodeUnverifiedJwtClaims(e);if(n===null)return vercelOidcLog.debug(`Rejected token that failed to decode as a JWT.`),{ok:!1};if(!n.issuer.startsWith(`https://oidc.vercel.com/`))return vercelOidcLog.debug(`Rejected token whose issuer is not a Vercel OIDC issuer.`,{issuer:n.issuer}),{ok:!1};if(n.audiences.length===0)return vercelOidcLog.debug(`Rejected token with no audience claim.`,{issuer:n.issuer}),{ok:!1};let r=await runOidcVerification(e,{acceptCurrentVercelProject:!0,audiences:n.audiences,issuer:n.issuer,subjects:t.subjects??[]});return r.kind===`authenticated`?(vercelOidcLog.debug(`Accepted Vercel OIDC token.`,{issuer:n.issuer,principalType:r.principal.principalType,subject:r.principal.subject}),{ok:!0,sessionAuth:createRuntimeSessionAuthContext(r.principal)}):(vercelOidcLog.debug(`Rejected Vercel OIDC token after verification.`,{audiences:n.audiences,issuer:n.issuer,reason:r.kind,subjectsConfigured:(t.subjects??[]).length>0,...r.kind===`misconfigured`?{detail:r.message}:{}}),{ok:!1})}function vercelSubject(e){assertVercelSubjectSegment(`teamSlug`,e.teamSlug),assertVercelSubjectSegment(`projectName`,e.projectName);let t=e.environment??`production`;if(t!==`production`&&t!==`preview`&&t!==`development`&&t!==`*`)throw Error(`vercelSubject: invalid environment ${JSON.stringify(t)}; expected "production", "preview", "development", or "*".`);return`owner:${e.teamSlug}:project:${e.projectName}:environment:${t}`}function assertVercelSubjectSegment(e,t){if(t.length===0)throw Error(`vercelSubject: ${e} must be a non-empty string.`);if(t.includes(`*`)||t.includes(`:`))throw Error(`vercelSubject: ${e} ${JSON.stringify(t)} may not contain ${t.includes(`:`)?`':'`:`'*'`}. Hand-write the subject string when wildcards are intentional.`)}function vercelOidc(e={}){return async t=>{let n=await verifyVercelOidc(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null}}function httpBasic(e){return t=>{let n=verifyHttpBasic(t.headers.get(`authorization`),e);return n.ok?n.sessionAuth:null}}function jwtHmac(e){return async t=>{let n=await verifyJwtHmac(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null}}function jwtEcdsa(e){return async t=>{let n=await verifyJwtEcdsa(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null}}function oidc(e){return async t=>{let n=await verifyOidc(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null}}function decodeUnverifiedJwtClaims(e){let n;try{n=decodeJwt(e)}catch{return null}return typeof n.iss!=`string`||n.iss.length===0?null:{audiences:typeof n.aud==`string`?[n.aud]:Array.isArray(n.aud)?n.aud.filter(e=>typeof e==`string`):[],issuer:n.iss}}export{createIpAllowList,createUnauthorizedResponse,extractBearerToken,httpBasic,isIpAllowed,jwtEcdsa,jwtHmac,localDev,none,oidc,routeAuth,vercelOidc,vercelSubject,verifyHttpBasic,verifyJwtEcdsa,verifyJwtHmac,verifyOidc,verifyVercelOidc};
1
+ import{createLogger}from"#internal/logging.js";import{decodeJwt}from"#compiled/jose/index.js";import{authenticateHttpBasicStrategy}from"#runtime/governance/auth/http-basic.js";import{authenticateJwtEcdsaStrategy}from"#runtime/governance/auth/jwt-ecdsa.js";import{authenticateJwtHmacStrategy}from"#runtime/governance/auth/jwt-hmac.js";import{authenticateOidcStrategy}from"#runtime/governance/auth/oidc.js";import{createRuntimeSessionAuthContext}from"#runtime/governance/auth/types.js";import{createRuntimeIpAllowList,isRuntimeIpAllowed}from"#runtime/governance/network/ip-allow-list.js";const vercelOidcLog=createLogger(`auth.vercel-oidc`);function verifyHttpBasic(e,t){if(e===null)return{ok:!1};let r=authenticateHttpBasicStrategy({authorization:e,strategy:{kind:`http-basic`,password:t.password,username:t.username}});return r.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(r.principal)}:{ok:!1}}async function verifyJwtHmac(e,t){if(e===null||e.length===0)return{ok:!1};let n=await authenticateJwtHmacStrategy({strategy:{algorithm:t.algorithm,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,issuer:t.issuer,kind:`jwt-hmac`,secret:t.secret,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},token:e});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function verifyJwtEcdsa(e,t){if(e===null||e.length===0)return{ok:!1};let n=await authenticateJwtEcdsaStrategy({strategy:{algorithm:t.algorithm,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,issuer:t.issuer,kind:`jwt-ecdsa`,publicKey:t.publicKey,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},token:e});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function verifyOidc(e,t){let n=await runOidcVerification(e,{...t,acceptCurrentVercelProject:!1});return n.kind===`authenticated`?{ok:!0,sessionAuth:createRuntimeSessionAuthContext(n.principal)}:{ok:!1}}async function runOidcVerification(e,t){return e===null||e.length===0?{kind:`not-authenticated`}:await authenticateOidcStrategy({strategy:{acceptCurrentVercelProject:t.acceptCurrentVercelProject,audiences:[...t.audiences],clockSkewSeconds:t.clockSkewSeconds??30,discoveryUrl:t.discoveryUrl??`${t.issuer.replace(/\/$/,``)}/.well-known/openid-configuration`,issuer:t.issuer,kind:`oidc`,...t.claims===void 0?{}:{claims:t.claims},...t.subjects===void 0?{}:{subjects:t.subjects}},token:e})}function extractBearerToken(e){if(e===null)return null;let t=/^Bearer\s+(.+)$/i.exec(e)?.[1]?.trim();return t===void 0||t.length===0?null:t}function createIpAllowList(e){return createRuntimeIpAllowList(e)}function isIpAllowed(e,t){return e===null?!1:isRuntimeIpAllowed(e,t)}function createUnauthorizedResponse(e={}){let t=e.status??401,n=e.code??(t===403?`forbidden`:`unauthorized`),r=e.message??(t===403?`Forbidden.`:`Authorization is required for this route.`),i=e.challenges??[],a=new Headers({"cache-control":`no-store`});for(let e of i)a.append(`www-authenticate`,formatChallenge(e));return Response.json({code:n,error:r,ok:!1},{headers:a,status:t})}function formatChallenge(e){if(e.parameters===void 0||Object.keys(e.parameters).length===0)return e.scheme;let t=Object.entries(e.parameters).map(([e,t])=>`${e}="${escapeChallengeValue(t)}"`).join(`, `);return`${e.scheme} ${t}`}function escapeChallengeValue(e){return e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`)}async function routeAuth(e,t){let n=Array.isArray(t)?t:[t];for(let t of n){let n=await t(e);if(n)return n}return createUnauthorizedResponse({challenges:[{scheme:`Bearer`}]})}function none(){return()=>ANONYMOUS_SESSION_AUTH_CONTEXT}function localDev(){return e=>process.env.VERCEL&&process.env.VERCEL_ENV===`development`||isLoopbackRequest(e)?LOCAL_DEV_SESSION_AUTH_CONTEXT:null}const LOOPBACK_HOSTNAMES=new Set([`localhost`,`[::1]`]),LOOPBACK_IPV4_PREFIX=/^127\./;function isLoopbackRequest(e){let t;try{t=new URL(e.url).hostname}catch{return!1}return!!(LOOPBACK_HOSTNAMES.has(t)||LOOPBACK_IPV4_PREFIX.test(t)||t.endsWith(`.localhost`))}const ANONYMOUS_SESSION_AUTH_CONTEXT={attributes:{},authenticator:`none`,principalId:`anonymous`,principalType:`anonymous`},LOCAL_DEV_SESSION_AUTH_CONTEXT={attributes:{},authenticator:`local-dev`,principalId:`local-dev`,principalType:`local-dev`};async function verifyVercelOidc(e,t={}){if(e===null||e.length===0)return{ok:!1};let n=decodeUnverifiedJwtClaims(e);if(n===null)return vercelOidcLog.debug(`Rejected token that failed to decode as a JWT.`),{ok:!1};if(!n.issuer.startsWith(`https://oidc.vercel.com/`))return vercelOidcLog.debug(`Rejected token whose issuer is not a Vercel OIDC issuer.`,{issuer:n.issuer}),{ok:!1};if(n.audiences.length===0)return vercelOidcLog.debug(`Rejected token with no audience claim.`,{issuer:n.issuer}),{ok:!1};let r=await runOidcVerification(e,{acceptCurrentVercelProject:!0,audiences:n.audiences,issuer:n.issuer,subjects:t.subjects??[]});return r.kind===`authenticated`?(vercelOidcLog.debug(`Accepted Vercel OIDC token.`,{issuer:n.issuer,principalType:r.principal.principalType,subject:r.principal.subject}),{ok:!0,sessionAuth:createRuntimeSessionAuthContext(r.principal)}):(vercelOidcLog.debug(`Rejected Vercel OIDC token after verification.`,{audiences:n.audiences,issuer:n.issuer,reason:r.kind,subjectsConfigured:(t.subjects??[]).length>0,...r.kind===`misconfigured`?{detail:r.message}:{}}),{ok:!1})}function vercelSubject(e){assertVercelSubjectSegment(`teamSlug`,e.teamSlug),assertVercelSubjectSegment(`projectName`,e.projectName);let t=e.environment??`production`;if(t!==`production`&&t!==`preview`&&t!==`development`&&t!==`*`)throw Error(`vercelSubject: invalid environment ${JSON.stringify(t)}; expected "production", "preview", "development", or "*".`);return`owner:${e.teamSlug}:project:${e.projectName}:environment:${t}`}function assertVercelSubjectSegment(e,t){if(t.length===0)throw Error(`vercelSubject: ${e} must be a non-empty string.`);if(t.includes(`*`)||t.includes(`:`))throw Error(`vercelSubject: ${e} ${JSON.stringify(t)} may not contain ${t.includes(`:`)?`':'`:`'*'`}. Hand-write the subject string when wildcards are intentional.`)}function vercelOidc(e={}){return async t=>{let n=await verifyVercelOidc(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null}}function httpBasic(e){return t=>{let n=verifyHttpBasic(t.headers.get(`authorization`),e);return n.ok?n.sessionAuth:null}}function jwtHmac(e){return async t=>{let n=await verifyJwtHmac(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null}}function jwtEcdsa(e){return async t=>{let n=await verifyJwtEcdsa(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null}}function oidc(e){return async t=>{let n=await verifyOidc(extractBearerToken(t.headers.get(`authorization`)),e);return n.ok?n.sessionAuth:null}}function decodeUnverifiedJwtClaims(e){let n;try{n=decodeJwt(e)}catch{return null}return typeof n.iss!=`string`||n.iss.length===0?null:{audiences:typeof n.aud==`string`?[n.aud]:Array.isArray(n.aud)?n.aud.filter(e=>typeof e==`string`):[],issuer:n.iss}}export{createIpAllowList,createUnauthorizedResponse,extractBearerToken,httpBasic,isIpAllowed,jwtEcdsa,jwtHmac,localDev,none,oidc,routeAuth,vercelOidc,vercelSubject,verifyHttpBasic,verifyJwtEcdsa,verifyJwtHmac,verifyOidc,verifyVercelOidc};
@@ -6,8 +6,9 @@
6
6
  *
7
7
  * @example
8
8
  * ```ts
9
- * import { defineState, getSession } from "experimental-ash/context";
9
+ * import { defineState } from "experimental-ash/context";
10
10
  * ```
11
11
  */
12
- export { getSession, type Session, type SessionAuth, type SessionAuthContext, type SessionParent, type SessionTurn, } from "#context/accessors.js";
12
+ export type { Session, SessionAuth, SessionAuthContext, SessionParent, SessionTurn, } from "#context/accessors.js";
13
13
  export { defineState, type StateHandle } from "#public/definitions/state.js";
14
+ export type { AshCallbackContext } from "#public/definitions/callback-context.js";
@@ -1 +1 @@
1
- import{getSession}from"#context/accessors.js";import{defineState}from"#public/definitions/state.js";export{defineState,getSession};
1
+ import{defineState}from"#public/definitions/state.js";export{defineState};
@@ -0,0 +1,22 @@
1
+ import type { SkillHandle } from "#execution/skills/types.js";
2
+ import type { SandboxSession } from "#shared/sandbox-session.js";
3
+ import type { SessionAuth, SessionParent, SessionTurn } from "#context/keys.js";
4
+ export type { SessionAuth, SessionParent, SessionTurn };
5
+ /**
6
+ * Shared runtime context available to all authored callbacks that run
7
+ * inside the ALS-scoped harness step (tools, hooks, channel events).
8
+ *
9
+ * Non-ALS callbacks (schedule `run`, sandbox `bootstrap`/`onSession`,
10
+ * instrumentation `setup`) do not receive this context — they get
11
+ * domain-specific arguments instead.
12
+ */
13
+ export interface AshCallbackContext {
14
+ readonly session: {
15
+ readonly id: string;
16
+ readonly auth: SessionAuth;
17
+ readonly turn: SessionTurn;
18
+ readonly parent?: SessionParent;
19
+ };
20
+ getSandbox(): Promise<SandboxSession>;
21
+ getSkill(identifier: string): SkillHandle;
22
+ }
@@ -0,0 +1 @@
1
+ export{};
@@ -1,16 +1,14 @@
1
1
  import type { ModelMessage } from "ai";
2
- import type { ContextAccessor } from "../../context/key.js";
3
2
  import type { HandleMessageStreamEvent } from "../../protocol/message.js";
3
+ import type { AshCallbackContext } from "./callback-context.js";
4
4
  import type { NamedSkillDefinition } from "./skill.js";
5
5
  /**
6
6
  * Context passed to every hook handler.
7
7
  *
8
- * Hook code reads and writes durable state through {@link ash}, the same
9
- * `ContextAccessor` used by tools, channels, and providers. The hooks
10
- * surface deliberately introduces no parallel state-patch channel — one
11
- * `AshContext`, one set of keys, one place to look when debugging.
8
+ * Extends {@link AshCallbackContext} with agent and channel metadata.
9
+ * `ctx` is always the last argument.
12
10
  */
13
- export interface HookContext {
11
+ export interface HookContext extends AshCallbackContext {
14
12
  readonly agent: {
15
13
  readonly name: string;
16
14
  readonly nodeId?: string;
@@ -19,27 +17,6 @@ export interface HookContext {
19
17
  readonly kind?: string;
20
18
  readonly continuationToken?: string;
21
19
  };
22
- readonly session: {
23
- readonly sessionId: string;
24
- };
25
- /**
26
- * Read/write access to durable and virtual context keys. Same accessor
27
- * tools, providers, and channels use.
28
- */
29
- readonly ash: ContextAccessor;
30
- }
31
- /**
32
- * Input passed to every lifecycle hook handler
33
- * (`lifecycle.session`, `lifecycle.turn`).
34
- */
35
- export interface LifecycleHookInput {
36
- readonly session: {
37
- readonly sessionId: string;
38
- };
39
- readonly turn: {
40
- readonly sequence: number;
41
- readonly turnId: string;
42
- };
43
20
  }
44
21
  /**
45
22
  * Result returned by a lifecycle hook (`lifecycle.session` or
@@ -67,25 +44,14 @@ export interface LifecycleHookResult {
67
44
  * Lifecycle hook signature shared by `lifecycle.session` and
68
45
  * `lifecycle.turn`.
69
46
  *
70
- * `lifecycle.session` runs once per durable session, before the first
71
- * model turn. The runtime sets `ash.sessionPrepared` before the chain
72
- * runs, so a thrown hook leaves the flag set and the chain does not
73
- * retry on the next turn — the throw fails the session terminally
74
- * (`session.failed`).
75
- *
76
- * `lifecycle.turn` runs once per fresh delivery (tool-loop continuations
77
- * and runtime-action resumes do not re-fire it). A thrown hook fails the
78
- * current turn (`turn.failed`) but the session keeps living.
47
+ * `ctx` is the sole argument session identity, auth, turn info,
48
+ * agent metadata, and channel metadata are all on `ctx`.
79
49
  */
80
- export type LifecycleHook = (input: LifecycleHookInput, ctx: HookContext) => void | LifecycleHookResult | Promise<void | LifecycleHookResult>;
50
+ export type LifecycleHook = (ctx: HookContext) => void | LifecycleHookResult | Promise<void | LifecycleHookResult>;
81
51
  /**
82
52
  * Side-effect-only handler for one accepted runtime stream event.
83
53
  *
84
- * Stream-event hooks run after Ash has accepted the event and written
85
- * it to the durable stream; their return values are ignored. Errors
86
- * propagate through the emit composer and fail the current turn
87
- * (`turn.failed`). Authors who want belt-and-suspenders semantics
88
- * wrap their body in `try`/`catch`.
54
+ * The typed event is the first argument, `ctx` is the last.
89
55
  */
90
56
  export type StreamEventHook<TEvent> = (event: TEvent, ctx: HookContext) => void | Promise<void>;
91
57
  /**
@@ -115,9 +81,6 @@ export type StreamEventHooks = {
115
81
  * stream-event subscribers (under `events:`), or both. The structural
116
82
  * split makes the contract explicit: `lifecycle:` can mutate the next
117
83
  * model call's context; `events:` is observe-only.
118
- *
119
- * Filenames carry stable identity for diagnostics and ordering; they
120
- * have no runtime semantics beyond identity.
121
84
  */
122
85
  export interface HookDefinition {
123
86
  readonly lifecycle?: LifecycleHooks;
@@ -127,8 +90,5 @@ export interface HookDefinition {
127
90
  * Identity-with-types helper. Authors export
128
91
  * `defineHook({ lifecycle: { session, turn }, events: { "session.started": … } })`
129
92
  * and receive a typed {@link HookDefinition}.
130
- *
131
- * `defineHook` runs no side effects — it exists purely so authors get
132
- * type checking and editor inference on the lifecycle and event keys.
133
93
  */
134
- export declare function defineHook(definition: HookDefinition): HookDefinition;
94
+ export declare function defineHook<T extends HookDefinition>(definition: T): T;
@@ -1,6 +1,6 @@
1
1
  import type { Optional } from "#shared/optional.js";
2
2
  import type { SandboxDefinition as SharedSandboxDefinition } from "#shared/sandbox-definition.js";
3
- export type { SandboxCommandResult, SandboxProcess, SandboxReadBinaryFileOptions, SandboxReadFileOptions, SandboxReadTextFileOptions, SandboxRunCommandOptions, SandboxRunOptions, SandboxSession, SandboxSpawnOptions, SandboxWriteBinaryFileOptions, SandboxWriteFileOptions, SandboxWriteTextFileOptions, } from "#shared/sandbox-session.js";
3
+ export type { SandboxCommandResult, SandboxProcess, SandboxReadBinaryFileOptions, SandboxReadFileOptions, SandboxReadTextFileOptions, SandboxRunOptions, SandboxSession, SandboxSpawnOptions, SandboxWriteBinaryFileOptions, SandboxWriteFileOptions, SandboxWriteTextFileOptions, } from "#shared/sandbox-session.js";
4
4
  export type { SandboxBootstrapUseFn, SandboxSessionUseFn, SandboxBootstrapContext, SandboxSessionContext, } from "#shared/sandbox-definition.js";
5
5
  export type SandboxDefinition<BO = Record<string, never>, SO = Record<string, never>> = Optional<SharedSandboxDefinition<BO, SO>, "backend">;
6
6
  /**
@@ -1,19 +1,12 @@
1
1
  import type { StandardJSONSchemaV1 } from "#compiled/@standard-schema/spec/index.js";
2
2
  import type { ModelMessage } from "ai";
3
- import type { AshContext } from "#context/container.js";
4
- import type { HarnessSession } from "#harness/types.js";
5
- import type { PublicToolDefinitionWithExecuteFn } from "#shared/tool-definition.js";
3
+ import type { PublicToolDefinition } from "#shared/tool-definition.js";
4
+ import type { AshCallbackContext } from "#public/definitions/callback-context.js";
6
5
  /**
7
- * Input passed to a tool's {@link ToolDefinition.onCompact} hook.
8
- *
9
- * Hooks run inside the same `AshContext` scope as the surrounding harness
10
- * step, so they may read or mutate context state via {@link ctx}. The
11
- * {@link session} is the harness session as it stands immediately after
12
- * message-history compaction.
6
+ * Domain input passed to a tool's {@link ToolDefinition.onCompact} hook.
13
7
  */
14
- export interface CompactionHookInput {
15
- readonly ctx: AshContext;
16
- readonly session: HarnessSession;
8
+ export interface CompactionInput {
9
+ readonly history: readonly ModelMessage[];
17
10
  readonly signal: AbortSignal;
18
11
  }
19
12
  /**
@@ -85,6 +78,11 @@ export interface RetentionSummary {
85
78
  * pure — no external I/O.
86
79
  */
87
80
  export type ToolRetentionPolicy<TOutput = unknown> = "auto" | "keep" | ((output: TOutput) => RetentionSummary);
81
+ /**
82
+ * Authored tool context. Passed as the last argument to
83
+ * {@link ToolDefinition.execute}.
84
+ */
85
+ export type ToolContext = AshCallbackContext;
88
86
  /**
89
87
  * Public tool definition authored in `agent/tools/*.ts`.
90
88
  *
@@ -95,7 +93,8 @@ export type ToolRetentionPolicy<TOutput = unknown> = "auto" | "keep" | ((output:
95
93
  * `get-weather`. Authored definitions do not carry a `name` field —
96
94
  * identity is path-derived.
97
95
  */
98
- export type ToolDefinition<TInput = unknown, TOutput = unknown> = PublicToolDefinitionWithExecuteFn<TInput, TOutput> & {
96
+ export type ToolDefinition<TInput = unknown, TOutput = unknown> = PublicToolDefinition<TInput> & {
97
+ execute(input: TInput, ctx: ToolContext): Promise<TOutput> | TOutput;
99
98
  /**
100
99
  * Optional per-tool approval gate. When set, the function's return value
101
100
  * determines whether user approval is required before executing this tool.
@@ -133,7 +132,7 @@ export type ToolDefinition<TInput = unknown, TOutput = unknown> = PublicToolDefi
133
132
  * Return `void` (or `{}`) for "no change". Errors fail the compaction
134
133
  * step — do not swallow them.
135
134
  */
136
- onCompact?(input: CompactionHookInput): CompactionHookResult | Promise<CompactionHookResult> | void | Promise<void>;
135
+ onCompact?(input: CompactionInput, ctx: ToolContext): CompactionHookResult | Promise<CompactionHookResult> | void | Promise<void>;
137
136
  };
138
137
  /**
139
138
  * Defines a tool configuration.
@@ -141,7 +140,7 @@ export type ToolDefinition<TInput = unknown, TOutput = unknown> = PublicToolDefi
141
140
  export declare function defineTool<TSchema extends StandardJSONSchemaV1<unknown, unknown>, TOutput>(definition: {
142
141
  description: ToolDefinition<unknown, unknown>["description"];
143
142
  inputSchema: TSchema;
144
- execute: ToolDefinition<StandardJSONSchemaV1.InferOutput<TSchema>, TOutput>["execute"];
143
+ execute(input: StandardJSONSchemaV1.InferOutput<TSchema>, ctx: ToolContext): Promise<TOutput> | TOutput;
145
144
  needsApproval?: ToolDefinition<unknown, unknown>["needsApproval"];
146
145
  retentionPolicy?: ToolDefinition<unknown, TOutput>["retentionPolicy"];
147
146
  toModelOutput?: ToolDefinition<unknown, TOutput>["toModelOutput"];
@@ -6,4 +6,4 @@
6
6
  * authoring shape and {@link HookContext} for the runtime context every
7
7
  * handler receives.
8
8
  */
9
- export { type HookContext, type HookDefinition, type LifecycleHook, type LifecycleHookInput, type LifecycleHookResult, type LifecycleHooks, type StreamEventHook, type StreamEventHooks, defineHook, } from "../definitions/hook.js";
9
+ export { type HookContext, type HookDefinition, type LifecycleHook, type LifecycleHookResult, type LifecycleHooks, type StreamEventHook, type StreamEventHooks, defineHook, } from "../definitions/hook.js";
@@ -2,8 +2,7 @@
2
2
  * Sandbox authoring helpers for `agent/sandbox.ts` (or
3
3
  * `agent/sandbox/sandbox.ts` when paired with a `workspace/` folder).
4
4
  */
5
- export { getSandbox } from "#context/accessors.js";
6
- export { defineSandbox, type SandboxBootstrapContext, type SandboxBootstrapUseFn, type SandboxCommandResult, type SandboxDefinition, type SandboxProcess, type SandboxReadBinaryFileOptions, type SandboxReadFileOptions, type SandboxReadTextFileOptions, type SandboxRunCommandOptions, type SandboxRunOptions, type SandboxSession, type SandboxSpawnOptions, type SandboxSessionContext, type SandboxSessionUseFn, type SandboxWriteBinaryFileOptions, type SandboxWriteFileOptions, type SandboxWriteTextFileOptions, } from "#public/definitions/sandbox.js";
5
+ export { defineSandbox, type SandboxBootstrapContext, type SandboxBootstrapUseFn, type SandboxCommandResult, type SandboxDefinition, type SandboxProcess, type SandboxReadBinaryFileOptions, type SandboxReadFileOptions, type SandboxReadTextFileOptions, type SandboxRunOptions, type SandboxSession, type SandboxSpawnOptions, type SandboxSessionContext, type SandboxSessionUseFn, type SandboxWriteBinaryFileOptions, type SandboxWriteFileOptions, type SandboxWriteTextFileOptions, } from "#public/definitions/sandbox.js";
7
6
  export type { SandboxBackend, SandboxBackendCreateInput, SandboxBackendHandle, SandboxBackendPrewarmInput, SandboxBackendRuntimeContext, SandboxBackendSessionState, SandboxSeedFile, } from "#public/definitions/sandbox-backend.js";
8
7
  export { SandboxTemplateNotProvisionedError } from "#public/definitions/sandbox-backend.js";
9
8
  export { defaultBackend } from "#public/sandbox/backends/default.js";
@@ -1 +1 @@
1
- import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{getSandbox}from"#context/accessors.js";import{localBackend}from"#public/sandbox/backends/local.js";import{vercelBackend}from"#public/sandbox/backends/vercel.js";import{defineSandbox}from"#public/definitions/sandbox.js";import{defaultBackend}from"#public/sandbox/backends/default.js";export{SandboxTemplateNotProvisionedError,defaultBackend,defineSandbox,getSandbox,localBackend,vercelBackend};
1
+ import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{localBackend}from"#public/sandbox/backends/local.js";import{vercelBackend}from"#public/sandbox/backends/vercel.js";import{defineSandbox}from"#public/definitions/sandbox.js";import{defaultBackend}from"#public/sandbox/backends/default.js";export{SandboxTemplateNotProvisionedError,defaultBackend,defineSandbox,localBackend,vercelBackend};
@@ -6,9 +6,9 @@ import type { Sandbox as SdkSandbox, SandboxUpdateParams } from "#compiled/@verc
6
6
  * session-create). Skipped on resume (`Sandbox.get`) since no create
7
7
  * happens there.
8
8
  *
9
- * Framework-injected fields (`name`, `persistent`, `signal`) are
10
- * excluded — the framework owns those and overrides any author-supplied
11
- * values.
9
+ * Framework-injected fields (`name`, `onResume`, `persistent`, `signal`)
10
+ * are excluded — the framework owns those and overrides any
11
+ * author-supplied values.
12
12
  *
13
13
  * `source` is honored only on the template create at prewarm time, so
14
14
  * an author-supplied snapshot, git revision, or tarball becomes the
@@ -20,7 +20,7 @@ import type { Sandbox as SdkSandbox, SandboxUpdateParams } from "#compiled/@verc
20
20
  * snapshot, force a template rebuild (e.g. by changing the sandbox
21
21
  * definition so its template key changes).
22
22
  */
23
- export type VercelSandboxCreateOptions = Omit<NonNullable<Parameters<typeof SdkSandbox.create>[0]>, "name" | "persistent" | "signal">;
23
+ export type VercelSandboxCreateOptions = Omit<NonNullable<Parameters<typeof SdkSandbox.create>[0]>, "name" | "onResume" | "persistent" | "signal">;
24
24
  /**
25
25
  * Options accepted by the Vercel backend's `bootstrap({ use })` hook.
26
26
  * Aliases the Vercel SDK's `SandboxUpdateParams` because bootstrap
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * Skill authoring helpers and runtime accessors.
3
3
  */
4
- export { getSkill } from "#context/accessors.js";
5
4
  export type { SkillFile, SkillHandle } from "#execution/skills/types.js";
6
5
  export { defineSkill, type NamedSkillDefinition, type SkillDefinition, type SkillFileContent, type SkillPackageDefinition, } from "#public/definitions/skill.js";
@@ -1 +1 @@
1
- import{defineSkill}from"#public/definitions/skill.js";import{getSkill}from"#context/accessors.js";export{defineSkill,getSkill};
1
+ import{defineSkill}from"#public/definitions/skill.js";export{defineSkill};
@@ -1 +1 @@
1
- import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{BASH_TOOL_DEFINITION}from"#runtime/framework-tools/bash.js";import{GLOB_TOOL_DEFINITION}from"#runtime/framework-tools/glob.js";import{GREP_TOOL_DEFINITION}from"#runtime/framework-tools/grep.js";import{READ_FILE_TOOL_DEFINITION}from"#runtime/framework-tools/read-file.js";import{SKILL_TOOL_DEFINITION}from"#runtime/framework-tools/skill.js";import{TODO_TOOL_DEFINITION}from"#runtime/framework-tools/todo.js";import{WEB_FETCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-fetch.js";import{WRITE_FILE_TOOL_DEFINITION}from"#runtime/framework-tools/write-file.js";import{toPublicToolDefinition}from"#public/tools/internal.js";const bash=toPublicToolDefinition(BASH_TOOL_DEFINITION),glob=toPublicToolDefinition(GLOB_TOOL_DEFINITION),grep=toPublicToolDefinition(GREP_TOOL_DEFINITION),readFile=toPublicToolDefinition(READ_FILE_TOOL_DEFINITION),writeFile=toPublicToolDefinition(WRITE_FILE_TOOL_DEFINITION),webFetch=toPublicToolDefinition(WEB_FETCH_TOOL_DEFINITION),todo=toPublicToolDefinition(TODO_TOOL_DEFINITION),loadSkill=toPublicToolDefinition(SKILL_TOOL_DEFINITION),webSearch={description:WEB_SEARCH_TOOL_DEFINITION.description,inputSchema:{},execute(){throw Error(`web_search is provider-managed and has no local execute. Override with defineTool() to provide a custom implementation.`)}};export{bash,glob,grep,loadSkill,readFile,todo,webFetch,webSearch,writeFile};
1
+ import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{BASH_TOOL_DEFINITION}from"#runtime/framework-tools/bash.js";import{GLOB_TOOL_DEFINITION}from"#runtime/framework-tools/glob.js";import{GREP_TOOL_DEFINITION}from"#runtime/framework-tools/grep.js";import{READ_FILE_TOOL_DEFINITION}from"#runtime/framework-tools/read-file.js";import{SKILL_TOOL_DEFINITION}from"#runtime/framework-tools/skill.js";import{TODO_TOOL_DEFINITION}from"#runtime/framework-tools/todo.js";import{WEB_FETCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-fetch.js";import{WRITE_FILE_TOOL_DEFINITION}from"#runtime/framework-tools/write-file.js";import{toPublicToolDefinition}from"#public/tools/internal.js";const bash=toPublicToolDefinition(BASH_TOOL_DEFINITION),glob=toPublicToolDefinition(GLOB_TOOL_DEFINITION),grep=toPublicToolDefinition(GREP_TOOL_DEFINITION),readFile=toPublicToolDefinition(READ_FILE_TOOL_DEFINITION),writeFile=toPublicToolDefinition(WRITE_FILE_TOOL_DEFINITION),webFetch=toPublicToolDefinition(WEB_FETCH_TOOL_DEFINITION),todo=toPublicToolDefinition(TODO_TOOL_DEFINITION),loadSkill=toPublicToolDefinition(SKILL_TOOL_DEFINITION),webSearch={description:WEB_SEARCH_TOOL_DEFINITION.description,inputSchema:{},execute(e){throw Error(`web_search is provider-managed and has no local execute. Override with defineTool() to provide a custom implementation.`)}};export{bash,glob,grep,loadSkill,readFile,todo,webFetch,webSearch,writeFile};
@@ -1 +1 @@
1
- import{BASH_INPUT_SCHEMA}from"#runtime/framework-tools/bash.js";import{executeBashOnSandbox}from"#execution/sandbox/bash-tool.js";function defineBashTool(t={}){return{description:t.description??`Execute a shell command in the workspace sandbox.`,async execute(e){return executeBashOnSandbox(e)},inputSchema:BASH_INPUT_SCHEMA}}export{defineBashTool};
1
+ import{BASH_INPUT_SCHEMA}from"#runtime/framework-tools/bash.js";import{executeBashOnSandbox}from"#execution/sandbox/bash-tool.js";function defineBashTool(t={}){return{description:t.description??`Execute a shell command in the workspace sandbox.`,async execute(e,t){return executeBashOnSandbox(await t.getSandbox(),e)},inputSchema:BASH_INPUT_SCHEMA}}export{defineBashTool};
@@ -1 +1 @@
1
- import{GLOB_INPUT_SCHEMA}from"#runtime/framework-tools/glob.js";import{executeGlobOnSandbox}from"#execution/sandbox/glob-tool.js";function defineGlobTool(t={}){return{description:t.description??`Search for files by glob pattern in the workspace sandbox.`,async execute(e){return executeGlobOnSandbox(e)},inputSchema:GLOB_INPUT_SCHEMA}}export{defineGlobTool};
1
+ import{GLOB_INPUT_SCHEMA}from"#runtime/framework-tools/glob.js";import{executeGlobOnSandbox}from"#execution/sandbox/glob-tool.js";function defineGlobTool(t={}){return{description:t.description??`Search for files by glob pattern in the workspace sandbox.`,async execute(e,t){return executeGlobOnSandbox(await t.getSandbox(),e)},inputSchema:GLOB_INPUT_SCHEMA}}export{defineGlobTool};
@@ -1 +1 @@
1
- import{GREP_INPUT_SCHEMA}from"#runtime/framework-tools/grep.js";import{executeGrepOnSandbox}from"#execution/sandbox/grep-tool.js";function defineGrepTool(t={}){return{description:t.description??`Search file contents by pattern in the workspace sandbox.`,async execute(e){return executeGrepOnSandbox(e)},inputSchema:GREP_INPUT_SCHEMA}}export{defineGrepTool};
1
+ import{GREP_INPUT_SCHEMA}from"#runtime/framework-tools/grep.js";import{executeGrepOnSandbox}from"#execution/sandbox/grep-tool.js";function defineGrepTool(t={}){return{description:t.description??`Search file contents by pattern in the workspace sandbox.`,async execute(e,t){return executeGrepOnSandbox(await t.getSandbox(),e)},inputSchema:GREP_INPUT_SCHEMA}}export{defineGrepTool};
@@ -1 +1 @@
1
- import{clearReadFileState}from"#runtime/framework-tools/file-state.js";import{READ_FILE_INPUT_SCHEMA}from"#runtime/framework-tools/read-file.js";import{executeReadFileOnSandbox}from"#execution/sandbox/read-file-tool.js";function defineReadFileTool(n={}){let r=n.onCompact??(({ctx:t})=>(clearReadFileState(t),{}));return{description:n.description??`Read a file from the workspace sandbox.`,async execute(e){return executeReadFileOnSandbox(e)},inputSchema:READ_FILE_INPUT_SCHEMA,onCompact:r}}export{defineReadFileTool};
1
+ import{clearReadFileState}from"#runtime/framework-tools/file-state.js";import{READ_FILE_INPUT_SCHEMA}from"#runtime/framework-tools/read-file.js";import{executeReadFileOnSandbox}from"#execution/sandbox/read-file-tool.js";function defineReadFileTool(n={}){let r=n.onCompact??(()=>(clearReadFileState(),{}));return{description:n.description??`Read a file from the workspace sandbox.`,async execute(e,t){return executeReadFileOnSandbox(await t.getSandbox(),e)},inputSchema:READ_FILE_INPUT_SCHEMA,onCompact:r}}export{defineReadFileTool};
@@ -1 +1 @@
1
- import{WRITE_FILE_INPUT_SCHEMA}from"#runtime/framework-tools/write-file.js";import{executeWriteFileOnSandbox}from"#execution/sandbox/write-file-tool.js";function defineWriteFileTool(t={}){return{description:t.description??`Write a file to the workspace sandbox.`,async execute(e){return executeWriteFileOnSandbox(e)},inputSchema:WRITE_FILE_INPUT_SCHEMA}}export{defineWriteFileTool};
1
+ import{WRITE_FILE_INPUT_SCHEMA}from"#runtime/framework-tools/write-file.js";import{executeWriteFileOnSandbox}from"#execution/sandbox/write-file-tool.js";function defineWriteFileTool(t={}){return{description:t.description??`Write a file to the workspace sandbox.`,async execute(e,t){return executeWriteFileOnSandbox(await t.getSandbox(),e)},inputSchema:WRITE_FILE_INPUT_SCHEMA}}export{defineWriteFileTool};
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Tool authoring helpers for `agent/tools/*.ts` files.
3
3
  */
4
- export { type CompactionHookInput, type CompactionHookResult, type DisabledToolSentinel, defineTool, disableTool, isDisabledToolSentinel, type NeedsApprovalContext, type RetentionSummary, type ToolDefinition, type ToolModelOutput, type ToolRetentionPolicy, } from "#public/definitions/tool.js";
4
+ export { type CompactionInput, type CompactionHookResult, type DisabledToolSentinel, defineTool, disableTool, isDisabledToolSentinel, type NeedsApprovalContext, type RetentionSummary, type ToolDefinition, type ToolContext, type ToolModelOutput, type ToolRetentionPolicy, } from "#public/definitions/tool.js";
5
+ export { type AshCallbackContext } from "#public/definitions/callback-context.js";
5
6
  export { toolResultFrom, type MatchedConnectionResult, type MatchedToolResult, type ToolResultFromFn, } from "#public/tool-result-narrowing.js";
6
7
  export { type DefineBashToolInput, defineBashTool } from "#public/tools/define-bash-tool.js";
7
8
  export { type DefineGlobToolInput, defineGlobTool } from "#public/tools/define-glob-tool.js";
@@ -4,5 +4,9 @@ import type { ToolDefinition } from "#public/definitions/tool.js";
4
4
  * Converter that strips internal identity fields from a framework
5
5
  * {@link ResolvedToolDefinition} so it can be re-exported as a public
6
6
  * {@link ToolDefinition}.
7
+ *
8
+ * Framework tools have the internal `(input) => output` signature.
9
+ * The public {@link ToolDefinition.execute} expects `(input, ctx)`.
10
+ * This wrapper bridges the gap — `ctx` is trailing and omitted.
7
11
  */
8
12
  export declare function toPublicToolDefinition(definition: ResolvedToolDefinition): ToolDefinition;
@@ -1 +1 @@
1
- function toPublicToolDefinition(e){if(!e.execute)throw Error(`Tool "${e.name}" is client-side and cannot be re-exported publicly.`);let t=e.execute,n=e.inputSchema,r={description:e.description,execute:t,inputSchema:n??{}};return e.needsApproval!==void 0&&(r.needsApproval=e.needsApproval),e.onCompact!==void 0&&(r.onCompact=e.onCompact),r}export{toPublicToolDefinition};
1
+ function toPublicToolDefinition(e){if(!e.execute)throw Error(`Tool "${e.name}" is client-side and cannot be re-exported publicly.`);let t=e.execute,n=e.inputSchema,r={description:e.description,execute:e=>t(e),inputSchema:n??{}};return e.needsApproval!==void 0&&(r.needsApproval=e.needsApproval),e.onCompact!==void 0&&(r.onCompact=e.onCompact),r}export{toPublicToolDefinition};
@@ -1 +1 @@
1
- import{executeBashOnSandbox}from"#execution/sandbox/bash-tool.js";const BASH_INPUT_SCHEMA={additionalProperties:!1,properties:{command:{description:`The shell command to execute.`,type:`string`}},required:[`command`],type:`object`};async function executeBash(e){return executeBashOnSandbox(e)}const BASH_TOOL_DEFINITION={description:`Execute a shell command in the shared workspace environment.`,execute:executeBash,inputSchema:BASH_INPUT_SCHEMA,logicalPath:`ash:framework/bash`,name:`bash`,sourceId:`ash:bash-tool`,sourceKind:`module`};export{BASH_INPUT_SCHEMA,BASH_TOOL_DEFINITION};
1
+ import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeBashOnSandbox}from"#execution/sandbox/bash-tool.js";const BASH_INPUT_SCHEMA={additionalProperties:!1,properties:{command:{description:`The shell command to execute.`,type:`string`}},required:[`command`],type:`object`};async function executeBash(e){return executeBashOnSandbox(await requireSandboxSession(),e)}const BASH_TOOL_DEFINITION={description:`Execute a shell command in the shared workspace environment.`,execute:executeBash,inputSchema:BASH_INPUT_SCHEMA,logicalPath:`ash:framework/bash`,name:`bash`,sourceId:`ash:bash-tool`,sourceKind:`module`};export{BASH_INPUT_SCHEMA,BASH_TOOL_DEFINITION};
@@ -1 +1 @@
1
- import{requestCodeModeInterrupt}from"../../node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/host-interrupt.js";import"../../node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/index.js";import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,isCodeModeToolExecutionOptions,readCodeModeConnectionAuthContext,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{qualifiedConnectionToolName}from"#runtime/framework-tools/connection-tools.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";const logger=createLogger(`framework.connection-search`),ConnectionRegistryKey=new ContextKey(`ash.connectionRegistry`),DiscoveredConnectionToolsKey=new ContextKey(`ash.discoveredConnectionTools`),CONNECTION_SEARCH_TOOL_DEFINITION={description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. linear__list_issues) in your next response.`,execute:async(e,t)=>executeConnectionSearch(e,{codeMode:isCodeModeToolExecutionOptions(t),codeModeInterrupt:readCodeModeConnectionAuthContext(t)}),inputSchema:{additionalProperties:!1,properties:{connection:{description:`Optional: limit search to a specific connection name.`,type:`string`},limit:{description:`Max results to return. Default 10.`,type:`number`},keywords:{description:`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`,type:`string`}},required:[`keywords`],type:`object`},logicalPath:`ash:framework/connection-search`,name:`connection_search`,onCompact({ctx:e}){return e.set(DiscoveredConnectionToolsKey,{byConnection:{}}),{}},sourceId:`ash:connection-search-tool`,sourceKind:`module`};async function executeConnectionSearch(t,r){let i=loadContext(),c=i.get(ConnectionRegistryKey);if(c===void 0)return[];await completePendingAuthorizations(c);let l=t.limit??10,u=tokenize(t.keywords),d=[],f=[],p=t.connection!==void 0&&t.connection!==``?c.getConnections().filter(e=>e.connectionName===t.connection):c.getConnections(),m={...i.get(DiscoveredConnectionToolsKey)?.byConnection},h=[];for(let n of p){if(r?.codeModeInterrupt?.resolution.status===`failed`&&r.codeModeInterrupt.payload.connectionName===n.connectionName){let{resolution:e}=r.codeModeInterrupt;f.push({connection:n.connectionName,description:n.description,error:e.reason,needsAuthorization:e.retryable});continue}let i;try{i=await c.getClient(n.connectionName).getToolMetadata()}catch(i){if(isConnectionAuthorizationRequiredError(i)){let i=resolveInteractiveAuth(c,n.connectionName);if(i&&r?.codeMode===!0&&requestCodeModeInterrupt({args:toCodeModeConnectionAuthArgs(t),connectionName:n.connectionName,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:`connection_search`}),i){let e=getHookUrl(n.connectionName);if(e){let t=resolveConnectionPrincipal(n.connectionName,i);try{let{challenge:r,state:a}=await i.startAuthorization({callbackUrl:e,connection:{url:n.url??``},principal:t});h.push({name:n.connectionName,challenge:r,hookUrl:e,state:a})}catch(e){logger.warn(`startAuthorization failed`,{connection:n.connectionName,error:e instanceof Error?e:Error(String(e))})}}}f.push({connection:n.connectionName,description:n.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(i)){logger.warn(`connection authorization failed`,{connection:n.connectionName,reason:i.reason,retryable:i.retryable,error:i}),f.push({connection:n.connectionName,description:n.description,error:`Authorization failed for ${n.connectionName}: ${i.message}`});continue}let o=i instanceof Error?i.message:`unknown error`;logger.warn(`failed to load connection tools`,{connection:n.connectionName,error:i instanceof Error?i:Error(o)}),f.push({connection:n.connectionName,description:n.description,error:`Failed to load tools for "${n.connectionName}": ${o}`});continue}m[n.connectionName]=i;for(let e of i){let t=scoreMatch(u,e);t>0&&d.push({item:{connection:n.connectionName,description:e.description,inputSchema:e.inputSchema,qualifiedName:qualifiedConnectionToolName(n.connectionName,e.name),tool:e.name},score:t})}}if(i.set(DiscoveredConnectionToolsKey,{byConnection:m}),h.length>0)return requestAuthorization(h);d.sort((e,t)=>t.score-e.score);let g=d.slice(0,l).map(e=>e.item);return g.length>0?[...g,...f]:p.map(e=>f.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}async function completePendingAuthorizations(e){let t=loadContext();for(let n of e.getConnections()){let r=getAuthorizationResult(n.connectionName);if(!r)continue;let a=resolveInteractiveAuth(e,n.connectionName);if(!a)continue;let o=resolveConnectionPrincipal(n.connectionName,a),s=await a.completeAuthorization({callbackUrl:r.hookUrl,connection:{url:n.url??``},principal:o,request:r.callback,state:r.state});writeCachedToken(t,n.connectionName,principalKey(o),s)}}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n?.authorization&&supportsInteractiveAuthorization(n.authorization))return n.authorization}export{CONNECTION_SEARCH_TOOL_DEFINITION,ConnectionRegistryKey,DiscoveredConnectionToolsKey,executeConnectionSearch,scoreMatch,tokenize};
1
+ import{requestCodeModeInterrupt}from"../../node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/host-interrupt.js";import"../../node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/index.js";import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,isCodeModeToolExecutionOptions,readCodeModeConnectionAuthContext,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{qualifiedConnectionToolName}from"#runtime/framework-tools/connection-tools.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";const logger=createLogger(`framework.connection-search`),ConnectionRegistryKey=new ContextKey(`ash.connectionRegistry`),DiscoveredConnectionToolsKey=new ContextKey(`ash.discoveredConnectionTools`),CONNECTION_SEARCH_TOOL_DEFINITION={description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. linear__list_issues) in your next response.`,execute:async(e,t)=>executeConnectionSearch(e,{codeMode:isCodeModeToolExecutionOptions(t),codeModeInterrupt:readCodeModeConnectionAuthContext(t)}),inputSchema:{additionalProperties:!1,properties:{connection:{description:`Optional: limit search to a specific connection name.`,type:`string`},limit:{description:`Max results to return. Default 10.`,type:`number`},keywords:{description:`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`,type:`string`}},required:[`keywords`],type:`object`},logicalPath:`ash:framework/connection-search`,name:`connection_search`,onCompact(){return loadContext().set(DiscoveredConnectionToolsKey,{byConnection:{}}),{}},sourceId:`ash:connection-search-tool`,sourceKind:`module`};async function executeConnectionSearch(t,r){let i=loadContext(),c=i.get(ConnectionRegistryKey);if(c===void 0)return[];await completePendingAuthorizations(c);let l=t.limit??10,u=tokenize(t.keywords),d=[],f=[],p=t.connection!==void 0&&t.connection!==``?c.getConnections().filter(e=>e.connectionName===t.connection):c.getConnections(),m={...i.get(DiscoveredConnectionToolsKey)?.byConnection},h=[];for(let n of p){if(r?.codeModeInterrupt?.resolution.status===`failed`&&r.codeModeInterrupt.payload.connectionName===n.connectionName){let{resolution:e}=r.codeModeInterrupt;f.push({connection:n.connectionName,description:n.description,error:e.reason,needsAuthorization:e.retryable});continue}let i;try{i=await c.getClient(n.connectionName).getToolMetadata()}catch(i){if(isConnectionAuthorizationRequiredError(i)){let i=resolveInteractiveAuth(c,n.connectionName);if(i&&r?.codeMode===!0&&requestCodeModeInterrupt({args:toCodeModeConnectionAuthArgs(t),connectionName:n.connectionName,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:`connection_search`}),i){let e=getHookUrl(n.connectionName);if(e){let t=resolveConnectionPrincipal(n.connectionName,i);try{let{challenge:r,state:a}=await i.startAuthorization({callbackUrl:e,connection:{url:n.url??``},principal:t});h.push({name:n.connectionName,challenge:r,hookUrl:e,state:a})}catch(e){logger.warn(`startAuthorization failed`,{connection:n.connectionName,error:e instanceof Error?e:Error(String(e))})}}}f.push({connection:n.connectionName,description:n.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(i)){logger.warn(`connection authorization failed`,{connection:n.connectionName,reason:i.reason,retryable:i.retryable,error:i}),f.push({connection:n.connectionName,description:n.description,error:`Authorization failed for ${n.connectionName}: ${i.message}`});continue}let o=i instanceof Error?i.message:`unknown error`;logger.warn(`failed to load connection tools`,{connection:n.connectionName,error:i instanceof Error?i:Error(o)}),f.push({connection:n.connectionName,description:n.description,error:`Failed to load tools for "${n.connectionName}": ${o}`});continue}m[n.connectionName]=i;for(let e of i){let t=scoreMatch(u,e);t>0&&d.push({item:{connection:n.connectionName,description:e.description,inputSchema:e.inputSchema,qualifiedName:qualifiedConnectionToolName(n.connectionName,e.name),tool:e.name},score:t})}}if(i.set(DiscoveredConnectionToolsKey,{byConnection:m}),h.length>0)return requestAuthorization(h);d.sort((e,t)=>t.score-e.score);let g=d.slice(0,l).map(e=>e.item);return g.length>0?[...g,...f]:p.map(e=>f.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}async function completePendingAuthorizations(e){let t=loadContext();for(let n of e.getConnections()){let r=getAuthorizationResult(n.connectionName);if(!r)continue;let a=resolveInteractiveAuth(e,n.connectionName);if(!a)continue;let o=resolveConnectionPrincipal(n.connectionName,a),s=await a.completeAuthorization({callbackUrl:r.hookUrl,connection:{url:n.url??``},principal:o,request:r.callback,state:r.state});writeCachedToken(t,n.connectionName,principalKey(o),s)}}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n?.authorization&&supportsInteractiveAuthorization(n.authorization))return n.authorization}export{CONNECTION_SEARCH_TOOL_DEFINITION,ConnectionRegistryKey,DiscoveredConnectionToolsKey,executeConnectionSearch,scoreMatch,tokenize};
@@ -1 +1 @@
1
- import{requestCodeModeInterrupt}from"../../node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/host-interrupt.js";import"../../node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/index.js";import{isObject}from"#shared/guards.js";import{loadContext}from"#context/container.js";import"#runtime/framework-tools/connection-search.js";import{jsonSchema}from"ai";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";function qualifiedConnectionToolName(e,t){return`${e}__${t}`}async function resolveConnectionToolsFromState(e,n,r){let i={};for(let[a,o]of Object.entries(n.byConnection)){let n=r.authMode===`code-mode`&&r.codeModeMetadataOnlyConnectionNames?.has(a)===!0,s=n?void 0:await e.getClient(a).getTools(),c=e.getConnectionApproval(a);for(let l of o){let o=qualifiedConnectionToolName(a,l.name);if(r.existingToolNames?.has(o))continue;let u=s?.[l.name]??(n?createMetadataOnlyTool(l,o):void 0);if(u===void 0)continue;let d=!n&&c!==void 0?async e=>c({approvedTools:r.approvedTools??new Set,toolInput:isObject(e)?e:void 0,toolName:o}):void 0,f=e.getConnections().find(e=>e.connectionName===a),p=u.execute,m=r.authMode===`code-mode`?wrapCodeModeConnectionToolExecute({connectionDefinition:f,connectionName:a,originalExecute:p,toolName:l.name}):wrapConnectionToolExecute({connectionDefinition:f,connectionName:a,originalExecute:p,toolName:l.name});i[o]={...u,execute:m,needsApproval:d}}}return i}function createMetadataOnlyTool(e,t){return{description:e.description,execute:async()=>{throw Error(`Connection tool "${t}" cannot execute without authorization.`)},inputSchema:jsonSchema(e.inputSchema)}}function wrapCodeModeConnectionToolExecute(t){let{connectionDefinition:r,connectionName:a,originalExecute:o,toolName:m}=t;if(o===void 0)return;let h=r?.authorization&&supportsInteractiveAuthorization(r.authorization)?r.authorization:void 0,g=r?.url??``;return async(t,r)=>{if(h){let e=getAuthorizationResult(a);if(e){let t=loadContext(),r=resolveConnectionPrincipal(a,h),i=await h.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:g},principal:r,request:e.callback,state:e.state});writeCachedToken(t,a,principalKey(r),i)}}try{return await o(t,r)}catch(n){if(!isConnectionAuthorizationRequiredError(n)||!h)throw n;requestCodeModeInterrupt({args:toCodeModeConnectionAuthArgs(t),connectionName:a,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:m})}}}function wrapConnectionToolExecute(e){let{connectionDefinition:t,connectionName:r,originalExecute:s}=e;if(s===void 0)return;let c=t?.authorization&&supportsInteractiveAuthorization(t.authorization)?t.authorization:void 0,m=t?.url??``;return async(e,t)=>{if(c){let e=getAuthorizationResult(r);if(e){let t=loadContext(),i=resolveConnectionPrincipal(r,c),a=await c.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:m},principal:i,request:e.callback,state:e.state});writeCachedToken(t,r,principalKey(i),a)}}try{return await s(e,t)}catch(e){if(!isConnectionAuthorizationRequiredError(e)||!c)throw e;let t=getHookUrl(r);if(!t)throw e;let n=resolveConnectionPrincipal(r,c),{challenge:i,state:s}=await c.startAuthorization({callbackUrl:t,connection:{url:m},principal:n});return requestAuthorization([{name:r,challenge:i,hookUrl:t,state:s}])}}}export{qualifiedConnectionToolName,resolveConnectionToolsFromState,wrapCodeModeConnectionToolExecute,wrapConnectionToolExecute};
1
+ import{requestCodeModeInterrupt}from"../../node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/host-interrupt.js";import"../../node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.10_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/index.js";import{isObject}from"#shared/guards.js";import{loadContext}from"#context/container.js";import"#runtime/framework-tools/connection-search.js";import{jsonSchema}from"ai";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";function qualifiedConnectionToolName(e,t){return`${e}__${t}`}async function resolveConnectionToolsFromState(e,n,r){let i={};for(let[a,o]of Object.entries(n.byConnection)){let n=r.authMode===`code-mode`&&r.codeModeMetadataOnlyConnectionNames?.has(a)===!0,s=n?void 0:await e.getClient(a).getTools(),c=e.getConnectionApproval(a);for(let l of o){let o=qualifiedConnectionToolName(a,l.name);if(r.existingToolNames?.has(o))continue;let u=s?.[l.name]??(n?createMetadataOnlyTool(l,o):void 0);if(u===void 0)continue;let d=!n&&c!==void 0?async e=>c({approvedTools:r.approvedTools??new Set,toolInput:isObject(e)?e:void 0,toolName:o}):void 0,f=e.getConnections().find(e=>e.connectionName===a),p=u.execute,m=r.authMode===`code-mode`?wrapCodeModeConnectionToolExecute({connectionDefinition:f,connectionName:a,originalExecute:p,toolName:l.name}):wrapConnectionToolExecute({connectionDefinition:f,connectionName:a,originalExecute:p,toolName:l.name});i[o]={...u,execute:m,needsApproval:d}}}return i}function createMetadataOnlyTool(e,t){return{description:e.description,execute:async()=>{throw Error(`Connection tool "${t}" cannot execute without authorization.`)},inputSchema:jsonSchema(e.inputSchema)}}function wrapCodeModeConnectionToolExecute(t){let{connectionDefinition:r,connectionName:a,originalExecute:o,toolName:m}=t;if(o===void 0)return;let h=r?.authorization&&supportsInteractiveAuthorization(r.authorization)?r.authorization:void 0,g=r?.url??``;return async(t,r)=>{if(h){let e=getAuthorizationResult(a);if(e){let t=loadContext(),r=resolveConnectionPrincipal(a,h),i=await h.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:g},principal:r,request:e.callback,state:e.state});writeCachedToken(t,a,principalKey(r),i)}}try{return await o(t,r)}catch(n){if(!isConnectionAuthorizationRequiredError(n)||!h)throw n;requestCodeModeInterrupt({args:toCodeModeConnectionAuthArgs(t),connectionName:a,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:m})}}}function wrapConnectionToolExecute(e){let{connectionDefinition:t,connectionName:r,originalExecute:s}=e;if(s===void 0)return;let c=t?.authorization&&supportsInteractiveAuthorization(t.authorization)?t.authorization:void 0,m=t?.url??``;return async(e,t)=>{if(c){let e=getAuthorizationResult(r);if(e){let t=loadContext(),i=resolveConnectionPrincipal(r,c),a=await c.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:m},principal:i,request:e.callback,state:e.state});writeCachedToken(t,r,principalKey(i),a)}}try{return await s(e,t)}catch(e){if(!isConnectionAuthorizationRequiredError(e)||!c)throw e;let t=getHookUrl(r);if(!t)throw e;let n=resolveConnectionPrincipal(r,c),{challenge:i,state:s}=await c.startAuthorization({callbackUrl:t,connection:{url:m},principal:n});return requestAuthorization([{name:r,challenge:i,hookUrl:t,state:s}])}}}export{qualifiedConnectionToolName,resolveConnectionToolsFromState,wrapCodeModeConnectionToolExecute,wrapConnectionToolExecute};