eve 0.15.4 → 0.16.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 (124) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/src/chunks/{use-eve-agent-8X2UMr8q.js → use-eve-agent-BEOUv37s.js} +70 -0
  3. package/dist/src/chunks/{use-eve-agent-9ZNiSFMb.js → use-eve-agent-C25KOe9i.js} +70 -0
  4. package/dist/src/cli/commands/init.js +1 -1
  5. package/dist/src/cli/dev/tui/agent-header.js +1 -1
  6. package/dist/src/cli/dev/tui/blocks.js +8 -8
  7. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  8. package/dist/src/cli/dev/tui/prompt-commands.d.ts +1 -1
  9. package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
  10. package/dist/src/cli/dev/tui/runner.d.ts +10 -3
  11. package/dist/src/cli/dev/tui/runner.js +1 -1
  12. package/dist/src/cli/dev/tui/setup-commands.d.ts +9 -1
  13. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  14. package/dist/src/cli/dev/tui/setup-flow.d.ts +8 -1
  15. package/dist/src/cli/dev/tui/setup-issues.d.ts +2 -14
  16. package/dist/src/cli/dev/tui/setup-issues.js +1 -1
  17. package/dist/src/cli/dev/tui/setup-panel.d.ts +14 -8
  18. package/dist/src/cli/dev/tui/setup-panel.js +3 -3
  19. package/dist/src/cli/dev/tui/terminal-renderer.js +6 -6
  20. package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
  21. package/dist/src/cli/dev/tui/tui.d.ts +2 -3
  22. package/dist/src/cli/dev/tui/tui.js +1 -1
  23. package/dist/src/cli/dev/ui-options.d.ts +21 -0
  24. package/dist/src/cli/dev/ui-options.js +1 -0
  25. package/dist/src/cli/run.d.ts +6 -42
  26. package/dist/src/cli/run.js +2 -2
  27. package/dist/src/client/authorization-message-parts.d.ts +4 -0
  28. package/dist/src/client/authorization-message-parts.js +1 -0
  29. package/dist/src/client/index.d.ts +2 -2
  30. package/dist/src/client/message-reducer-types.d.ts +40 -1
  31. package/dist/src/client/message-reducer.d.ts +3 -5
  32. package/dist/src/client/message-reducer.js +1 -1
  33. package/dist/src/compiled/.vendor-stamp.json +1 -1
  34. package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +71 -1
  35. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  36. package/dist/src/harness/emission.d.ts +3 -3
  37. package/dist/src/harness/emission.js +1 -1
  38. package/dist/src/harness/stream-actions.d.ts +17 -0
  39. package/dist/src/harness/stream-actions.js +1 -0
  40. package/dist/src/internal/application/package.js +1 -1
  41. package/dist/src/internal/nitro/host/configure-nitro-routes.js +2 -2
  42. package/dist/src/internal/nitro/host/dev-authored-source-watcher.d.ts +1 -0
  43. package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
  44. package/dist/src/internal/nitro/host/start-development-server.d.ts +8 -0
  45. package/dist/src/internal/nitro/host/start-development-server.js +2 -2
  46. package/dist/src/internal/nitro/host.d.ts +1 -1
  47. package/dist/src/internal/nitro/host.js +1 -1
  48. package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
  49. package/dist/src/internal/nitro/routes/dev-runtime-artifacts.d.ts +0 -3
  50. package/dist/src/internal/nitro/routes/dev-runtime-artifacts.js +1 -1
  51. package/dist/src/internal/nitro/routes/info.js +1 -1
  52. package/dist/src/internal/vercel/project-link.d.ts +10 -0
  53. package/dist/src/internal/vercel/project-link.js +1 -0
  54. package/dist/src/protocol/message.d.ts +14 -10
  55. package/dist/src/public/channels/auth.d.ts +26 -13
  56. package/dist/src/public/channels/auth.js +1 -1
  57. package/dist/src/react/index.d.ts +1 -1
  58. package/dist/src/runtime/agent/mock-model-adapter.d.ts +3 -2
  59. package/dist/src/runtime/agent/mock-model-adapter.js +1 -1
  60. package/dist/src/runtime/connections/principal.d.ts +2 -0
  61. package/dist/src/runtime/connections/principal.js +1 -1
  62. package/dist/src/runtime/connections/types.d.ts +4 -1
  63. package/dist/src/runtime/framework-channels/index.d.ts +1 -6
  64. package/dist/src/runtime/framework-channels/index.js +1 -1
  65. package/dist/src/runtime/governance/auth/oidc.js +1 -1
  66. package/dist/src/runtime/governance/auth/types.d.ts +14 -10
  67. package/dist/src/runtime/governance/auth/vercel-oidc-project.d.ts +12 -0
  68. package/dist/src/runtime/governance/auth/vercel-oidc-project.js +1 -0
  69. package/dist/src/services/dev-client/client-options.d.ts +5 -0
  70. package/dist/src/services/dev-client/client-options.js +1 -1
  71. package/dist/src/services/dev-client/request-headers.d.ts +8 -0
  72. package/dist/src/services/dev-client/request-headers.js +1 -1
  73. package/dist/src/services/dev-client/runtime-artifacts.d.ts +1 -0
  74. package/dist/src/services/dev-client/runtime-artifacts.js +1 -1
  75. package/dist/src/services/dev-client.d.ts +8 -0
  76. package/dist/src/services/dev-client.js +1 -1
  77. package/dist/src/setup/boxes/add-connections.d.ts +10 -8
  78. package/dist/src/setup/boxes/add-connections.js +1 -1
  79. package/dist/src/setup/boxes/resolve-provisioning.d.ts +1 -0
  80. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  81. package/dist/src/setup/cli/channel-setup-prompter.d.ts +9 -2
  82. package/dist/src/setup/cli/channel-setup-prompter.js +1 -1
  83. package/dist/src/setup/cli/index.d.ts +1 -1
  84. package/dist/src/setup/cli/prompt-ui.d.ts +2 -0
  85. package/dist/src/setup/cli/select-state.js +1 -1
  86. package/dist/src/setup/connection-connector.d.ts +26 -52
  87. package/dist/src/setup/connection-connector.js +2 -1
  88. package/dist/src/setup/flows/channels.js +1 -1
  89. package/dist/src/setup/flows/connections.d.ts +36 -0
  90. package/dist/src/setup/flows/connections.js +3 -0
  91. package/dist/src/setup/flows/link.d.ts +1 -0
  92. package/dist/src/setup/flows/link.js +1 -1
  93. package/dist/src/setup/project-resolution.d.ts +2 -8
  94. package/dist/src/setup/project-resolution.js +1 -1
  95. package/dist/src/setup/prompter.d.ts +2 -0
  96. package/dist/src/setup/scaffold/connections/catalog.d.ts +10 -5
  97. package/dist/src/setup/scaffold/connections/catalog.js +1 -1
  98. package/dist/src/setup/scaffold/create/project.js +11 -11
  99. package/dist/src/setup/scaffold/create/web-template.d.ts +2 -2
  100. package/dist/src/setup/scaffold/create/web-template.js +107 -3
  101. package/dist/src/setup/scaffold/index.d.ts +1 -1
  102. package/dist/src/setup/scaffold/index.js +1 -1
  103. package/dist/src/setup/scaffold/update/connections.d.ts +6 -0
  104. package/dist/src/setup/scaffold/update/connections.js +3 -3
  105. package/dist/src/svelte/index.d.ts +1 -1
  106. package/dist/src/svelte/index.js +1 -1
  107. package/dist/src/svelte/use-eve-agent.js +1 -1
  108. package/dist/src/vue/index.d.ts +1 -1
  109. package/dist/src/vue/index.js +1 -1
  110. package/dist/src/vue/use-eve-agent.js +1 -1
  111. package/docs/channels/eve.mdx +3 -3
  112. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  113. package/docs/guides/auth-and-route-protection.md +5 -5
  114. package/docs/guides/dev-tui.md +11 -4
  115. package/docs/guides/frontend/nextjs.mdx +2 -2
  116. package/docs/guides/frontend/nuxt.mdx +2 -2
  117. package/docs/guides/frontend/overview.mdx +34 -4
  118. package/docs/guides/frontend/sveltekit.mdx +2 -2
  119. package/docs/reference/cli.md +3 -1
  120. package/docs/tutorial/ship-it.mdx +1 -1
  121. package/docs/tutorial/team-playbooks.mdx +1 -1
  122. package/package.json +2 -2
  123. package/dist/src/internal/nitro/host/dev-rebuild-registry.d.ts +0 -5
  124. package/dist/src/internal/nitro/host/dev-rebuild-registry.js +0 -1
@@ -1,9 +1,15 @@
1
- import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.0`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.15.4`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.0`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.16.0`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
2
2
 
3
3
  export default defineAgent({
4
4
  model: "__EVE_INIT_MODEL__",
5
5
  });
6
- `;function packageJsonTemplate(e){return`{
6
+ `;function packageManagerAiPinTemplateSuffix(e){switch(e){case`bun`:case`npm`:return`,
7
+ "overrides": {
8
+ "ai": "__EVE_INIT_AI_SDK_VERSION__"
9
+ }`;case`yarn`:return`,
10
+ "resolutions": {
11
+ "ai": "__EVE_INIT_AI_SDK_VERSION__"
12
+ }`;case`pnpm`:return``;default:return e}}function packageJsonTemplate(e){return`{
7
13
  "name": "__EVE_INIT_APP_NAME__",
8
14
  "version": "0.0.0",
9
15
  "type": "module",
@@ -26,15 +32,9 @@ export default defineAgent({
26
32
  "devDependencies": {
27
33
  "@types/node": "__EVE_INIT_TYPES_NODE_VERSION__",
28
34
  "typescript": "__EVE_INIT_TYPESCRIPT_VERSION__"
29
- }${e?ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX:``}
35
+ }${e.includeRootOnlyFields?`${packageManagerAiPinTemplateSuffix(e.packageManager)}${ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX}`:``}
30
36
  }
31
37
  `}const ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX=`,
32
- "overrides": {
33
- "ai": "__EVE_INIT_AI_SDK_VERSION__"
34
- },
35
- "resolutions": {
36
- "ai": "__EVE_INIT_AI_SDK_VERSION__"
37
- },
38
38
  "engines": {
39
39
  "node": "__EVE_INIT_NODE_ENGINE__"
40
40
  }
@@ -81,7 +81,7 @@ from the installed eve package docs. In most installs, those docs are at
81
81
  installed \`eve\` package location first and read its \`docs/\` directory. If
82
82
  package docs are unavailable, use https://eve.dev/docs as a fallback.
83
83
  `,"CLAUDE.md":`@AGENTS.md
84
- `};function templateFiles(e,t){return{"agent/agent.ts":e?`import { defineAgent } from "eve";
84
+ `};function templateFiles(e){return{"agent/agent.ts":e.byokProvider?`import { defineAgent } from "eve";
85
85
 
86
86
  export default defineAgent({
87
87
  model: "__EVE_INIT_MODEL__",
@@ -95,4 +95,4 @@ export default defineAgent({
95
95
  },
96
96
  },
97
97
  });
98
- `:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(t)}}async function assertCanCreateInPlace(e,n){if(!await pathExists(e))return;let r=(await readdir(e)).filter(e=>!ALLOWED_CREATE_IN_PLACE_ENTRIES.has(e));if(r.length>0&&!n){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(o){let s=resolve(o.targetDirectory??process.cwd(),o.projectName),c=o.projectName===`.`,l=o.overwriteExisting??!1,u=o.byokProvider??!1,d=o.packageManager??`pnpm`,f=resolveEvePackageContract(o.evePackage),p=pinnedNodeEngineMajor(f.nodeEngine),m=resolve(o.workspaceProbeDirectory??s),h=isPackageManagerWorkspaceMember(d,m);if(c)await assertCanCreateInPlace(s,l);else if(await pathExists(s))throw Error(`Cannot create project because "${s}" already exists.`);let g={appName:basename(s),model:o.model,eveVersion:f.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,o.aiPackageVersion??`^7.0.0`),connectPackageVersion:resolveVersionToken(`connectPackageVersion`,o.connectPackageVersion??`0.2.2`),zodPackageVersion:resolveVersionToken(`zodPackageVersion`,o.zodPackageVersion??`4.4.3`),typescriptPackageVersion:resolveVersionToken(`typescriptPackageVersion`,o.typescriptPackageVersion??`7.0.1-rc`),nodeTypesVersion:p,nodeEngine:p};await mkdir(s,{recursive:!0});for(let[e,r]of Object.entries(templateFiles(u,!h))){let i=`${s}/${e}`,a=await pathExists(i);await writeTextFile(i,renderTemplate(r,g),{force:c&&l}),a&&await o.onOverwriteFile?.(i)}return await applyPackageManagerWorkspaceConfiguration({packageManager:d,projectRoot:s,workspaceProbeRoot:m,onWorkspaceRootMutation:o.onWorkspaceRootMutation}),await patchWorkspaceRootPackageJson(d,m,{aiPackageVersion:g.aiPackageVersion,nodeEngineRequirement:f.nodeEngine,onWorkspaceRootMutation:o.onWorkspaceRootMutation}),s}async function isEveProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{CURRENT_DIRECTORY_PROJECT_NAME,DEFAULT_AI_PACKAGE_VERSION,DEFAULT_CONNECT_PACKAGE_VERSION,DEFAULT_EVE_PACKAGE_CONTRACT,DEFAULT_ZOD_PACKAGE_VERSION,agentTemplateFiles,byokProviderEnvVar,formatEveDependencySpecifier,isEveProject,modelProviderSlug,resolveEvePackageContract,scaffoldBaseProject};
98
+ `:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate({includeRootOnlyFields:e.includeRootOnlyPackageJsonFields,packageManager:e.packageManager})}}async function assertCanCreateInPlace(e,n){if(!await pathExists(e))return;let r=(await readdir(e)).filter(e=>!ALLOWED_CREATE_IN_PLACE_ENTRIES.has(e));if(r.length>0&&!n){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(o){let s=resolve(o.targetDirectory??process.cwd(),o.projectName),c=o.projectName===`.`,l=o.overwriteExisting??!1,u=o.byokProvider??!1,d=o.packageManager??`pnpm`,f=resolveEvePackageContract(o.evePackage),p=pinnedNodeEngineMajor(f.nodeEngine),m=resolve(o.workspaceProbeDirectory??s),h=isPackageManagerWorkspaceMember(d,m);if(c)await assertCanCreateInPlace(s,l);else if(await pathExists(s))throw Error(`Cannot create project because "${s}" already exists.`);let g={appName:basename(s),model:o.model,eveVersion:f.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,o.aiPackageVersion??`^7.0.0`),connectPackageVersion:resolveVersionToken(`connectPackageVersion`,o.connectPackageVersion??`0.2.2`),zodPackageVersion:resolveVersionToken(`zodPackageVersion`,o.zodPackageVersion??`4.4.3`),typescriptPackageVersion:resolveVersionToken(`typescriptPackageVersion`,o.typescriptPackageVersion??`7.0.1-rc`),nodeTypesVersion:p,nodeEngine:p};await mkdir(s,{recursive:!0});for(let[e,r]of Object.entries(templateFiles({byokProvider:u,includeRootOnlyPackageJsonFields:!h,packageManager:d}))){let i=`${s}/${e}`,a=await pathExists(i);await writeTextFile(i,renderTemplate(r,g),{force:c&&l}),a&&await o.onOverwriteFile?.(i)}return await applyPackageManagerWorkspaceConfiguration({packageManager:d,projectRoot:s,workspaceProbeRoot:m,onWorkspaceRootMutation:o.onWorkspaceRootMutation}),await patchWorkspaceRootPackageJson(d,m,{aiPackageVersion:g.aiPackageVersion,nodeEngineRequirement:f.nodeEngine,onWorkspaceRootMutation:o.onWorkspaceRootMutation}),s}async function isEveProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{CURRENT_DIRECTORY_PROJECT_NAME,DEFAULT_AI_PACKAGE_VERSION,DEFAULT_CONNECT_PACKAGE_VERSION,DEFAULT_EVE_PACKAGE_CONTRACT,DEFAULT_ZOD_PACKAGE_VERSION,agentTemplateFiles,byokProviderEnvVar,formatEveDependencySpecifier,isEveProject,modelProviderSlug,resolveEvePackageContract,scaffoldBaseProject};
@@ -1,7 +1,7 @@
1
1
  export declare const WEB_APP_TEMPLATE_FILES: {
2
- readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n auth: [\n // Open on localhost for `eve dev` and the REPL; ignored in production.\n localDev(),\n // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n vercelOidc(),\n // This placeholder will not allow browser requests in production.\n // Replace it with your app\'s auth provider, like Auth.js or Clerk,\n // or use none() for a public demo.\n placeholderAuth(),\n ],\n});\n';
2
+ readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n auth: [\n // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n vercelOidc(),\n // Open on localhost for `eve dev` and the REPL; ignored in production.\n localDev(),\n // This placeholder will not allow browser requests in production.\n // Replace it with your app\'s auth provider, like Auth.js or Clerk,\n // or use none() for a public demo.\n placeholderAuth(),\n ],\n});\n';
3
3
  readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon } from "lucide-react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n} from "@/components/ai-elements/conversation";\nimport {\n PromptInput,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n} from "@/components/ai-elements/prompt-input";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\n\ntype AgentStatus = ReturnType<typeof useEveAgent>["status"];\n\nexport function AgentChat() {\n const agent = useEveAgent();\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isEmpty = agent.data.messages.length === 0;\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if (!text || isBusy) return;\n\n await agent.send({ message: text });\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea placeholder="Send a message…" />\n <PromptInputSubmit onStop={agent.stop} status={agent.status} />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {isEmpty ? null : (\n <header className="flex h-14 shrink-0 items-center justify-center gap-3 pl-4 pr-2">\n <span className="flex min-w-0 items-center gap-2">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n <StatusDot status={agent.status} />\n </span>\n </header>\n )}\n\n {agent.error ? (\n <div className="mx-auto w-full max-w-3xl shrink-0 px-4 pt-2 sm:px-6">\n <div className="flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm">\n <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />\n <div>\n <p className="font-medium">Request failed</p>\n <p className="mt-0.5 text-muted-foreground">{agent.error.message}</p>\n </div>\n </div>\n </div>\n ) : null}\n\n {isEmpty ? null : (\n <Conversation className="min-h-0 flex-1">\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 py-6 sm:px-6">\n {agent.data.messages.map((message, index) => (\n <AgentMessage\n canRespond={!isBusy}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n onInputResponses={(inputResponses) => agent.send({ inputResponses })}\n />\n ))}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n )}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n isEmpty\n ? "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]"\n : "max-w-3xl shrink-0 pb-6",\n )}\n >\n {isEmpty ? (\n <div className="flex flex-col items-center gap-3 text-center">\n <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>\n </div>\n ) : null}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction StatusDot({ status }: { readonly status: AgentStatus }) {\n const isLive = status === "submitted" || status === "streaming";\n const tone =\n status === "error"\n ? "bg-destructive"\n : isLive\n ? "bg-emerald-500"\n : status === "ready"\n ? "bg-muted-foreground"\n : "bg-muted-foreground/50";\n\n return (\n <span className="relative flex size-1">\n {isLive ? (\n <span\n className={cn(\n "absolute inline-flex size-full animate-ping rounded-full opacity-75",\n tone,\n )}\n />\n ) : null}\n <span className={cn("relative inline-flex size-1 rounded-full transition-colors", tone)} />\n </span>\n );\n}\n';
4
- readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type { EveDynamicToolPart, EveMessage, EveMessagePart } from "eve/react";\nimport { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";\nimport {\n Tool,\n ToolContent,\n ToolHeader,\n ToolInput,\n ToolOutput,\n} from "@/components/ai-elements/tool";\nimport { Button } from "@/components/ui/button";\n\nexport type AgentInputResponse = {\n readonly optionId?: string;\n readonly requestId: string;\n readonly text?: string;\n};\n\nexport function AgentMessage({\n canRespond,\n isStreaming,\n message,\n onInputResponses,\n}: {\n readonly canRespond: boolean;\n readonly isStreaming: boolean;\n readonly message: EveMessage;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n const lastTextIndex = message.parts.reduce(\n (last, part, index) => (part.type === "text" ? index : last),\n -1,\n );\n\n return (\n <Message\n data-optimistic={message.metadata?.optimistic ? "true" : undefined}\n from={message.role}\n >\n <MessageContent>\n {message.parts.map((part, index) => (\n <AgentMessagePart\n canRespond={canRespond}\n key={partKey(part, index)}\n onInputResponses={onInputResponses}\n part={part}\n showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}\n />\n ))}\n </MessageContent>\n </Message>\n );\n}\n\nfunction AgentMessagePart({\n canRespond,\n onInputResponses,\n part,\n showCaret,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveMessagePart;\n readonly showCaret: boolean;\n}) {\n switch (part.type) {\n case "step-start":\n return null;\n case "text":\n return (\n <MessageResponse caret="block" isAnimating={showCaret}>\n {part.text}\n </MessageResponse>\n );\n case "reasoning":\n return (\n <Reasoning defaultOpen isStreaming={part.state === "streaming"}>\n <ReasoningTrigger />\n <ReasoningContent>{part.text}</ReasoningContent>\n </Reasoning>\n );\n case "dynamic-tool":\n return (\n <Tool\n defaultOpen={part.state === "approval-requested" || part.state === "approval-responded"}\n >\n <ToolHeader\n state={part.state}\n title={part.toolName}\n toolName={part.toolName}\n type="dynamic-tool"\n />\n <ToolContent>\n <ToolInput input={part.input} />\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n <ToolOutput errorText={part.errorText} output={part.output} />\n </ToolContent>\n </Tool>\n );\n }\n}\n\nfunction InputRequestActions({\n canRespond,\n onInputResponses,\n part,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveDynamicToolPart;\n}) {\n const inputRequest = part.toolMetadata?.eve?.inputRequest;\n if (!inputRequest) {\n return null;\n }\n\n const inputResponse = part.toolMetadata?.eve?.inputResponse;\n const selectedOption = inputRequest.options?.find(\n (option) => option.id === inputResponse?.optionId,\n );\n\n return (\n <div className="space-y-3 rounded-md border border-yellow-500/30 bg-yellow-500/5 p-3">\n <p className="text-muted-foreground text-sm">{inputRequest.prompt}</p>\n {inputResponse ? (\n <p className="font-medium text-sm">\n Responded: {selectedOption?.label ?? inputResponse.text ?? inputResponse.optionId}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond}\n key={option.id}\n onClick={() => {\n void onInputResponses([\n {\n optionId: option.id,\n requestId: inputRequest.requestId,\n },\n ]);\n }}\n size="sm"\n type="button"\n variant={option.style === "danger" ? "destructive" : "default"}\n >\n {option.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction partKey(part: EveMessagePart, index: number): string {\n switch (part.type) {\n case "dynamic-tool":\n return part.toolCallId;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
4
+ readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type {\n EveAuthorizationPart,\n EveDynamicToolPart,\n EveMessage,\n EveMessagePart,\n} from "eve/react";\nimport { CheckCircleIcon, ExternalLinkIcon, KeyRoundIcon, XCircleIcon } from "lucide-react";\nimport { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";\nimport {\n Tool,\n ToolContent,\n ToolHeader,\n ToolInput,\n ToolOutput,\n} from "@/components/ai-elements/tool";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\n\nexport type AgentInputResponse = {\n readonly optionId?: string;\n readonly requestId: string;\n readonly text?: string;\n};\n\nexport function AgentMessage({\n canRespond,\n isStreaming,\n message,\n onInputResponses,\n}: {\n readonly canRespond: boolean;\n readonly isStreaming: boolean;\n readonly message: EveMessage;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n const lastTextIndex = message.parts.reduce(\n (last, part, index) => (part.type === "text" ? index : last),\n -1,\n );\n\n return (\n <Message\n data-optimistic={message.metadata?.optimistic ? "true" : undefined}\n from={message.role}\n >\n <MessageContent>\n {message.parts.map((part, index) => (\n <AgentMessagePart\n canRespond={canRespond}\n key={partKey(part, index)}\n onInputResponses={onInputResponses}\n part={part}\n showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}\n />\n ))}\n </MessageContent>\n </Message>\n );\n}\n\nfunction AgentMessagePart({\n canRespond,\n onInputResponses,\n part,\n showCaret,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveMessagePart;\n readonly showCaret: boolean;\n}) {\n switch (part.type) {\n case "step-start":\n return null;\n case "text":\n return (\n <MessageResponse caret="block" isAnimating={showCaret}>\n {part.text}\n </MessageResponse>\n );\n case "reasoning":\n return (\n <Reasoning defaultOpen isStreaming={part.state === "streaming"}>\n <ReasoningTrigger />\n <ReasoningContent>{part.text}</ReasoningContent>\n </Reasoning>\n );\n case "authorization":\n return <AuthorizationPrompt part={part} />;\n case "dynamic-tool":\n return (\n <Tool\n defaultOpen={part.state === "approval-requested" || part.state === "approval-responded"}\n >\n <ToolHeader\n state={part.state}\n title={part.toolName}\n toolName={part.toolName}\n type="dynamic-tool"\n />\n <ToolContent>\n <ToolInput input={part.input} />\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n <ToolOutput errorText={part.errorText} output={part.output} />\n </ToolContent>\n </Tool>\n );\n }\n}\n\nfunction AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {\n const isAuthorized = part.state === "completed" && part.outcome === "authorized";\n const isCompleted = part.state === "completed";\n const Icon = isAuthorized ? CheckCircleIcon : isCompleted ? XCircleIcon : KeyRoundIcon;\n const instructions = part.authorization?.instructions;\n const shouldShowInstructions = instructions !== undefined && instructions !== part.description;\n\n return (\n <div\n className={cn(\n "space-y-3 rounded-md border p-3",\n isAuthorized\n ? "border-emerald-500/30 bg-emerald-500/5"\n : isCompleted\n ? "border-destructive/30 bg-destructive/5"\n : "border-blue-500/30 bg-blue-500/5",\n )}\n >\n <div className="flex items-start gap-3">\n <span\n className={cn(\n "mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full",\n isAuthorized\n ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"\n : isCompleted\n ? "bg-destructive/10 text-destructive"\n : "bg-blue-500/10 text-blue-700 dark:text-blue-300",\n )}\n >\n <Icon className="size-4" />\n </span>\n <div className="min-w-0 flex-1 space-y-2">\n <p className="font-medium text-sm">{authorizationTitle(part)}</p>\n <p className="text-muted-foreground text-sm">{authorizationDescription(part)}</p>\n {shouldShowInstructions ? (\n <p className="text-muted-foreground text-sm">{instructions}</p>\n ) : null}\n {part.state === "required" && part.authorization?.userCode ? (\n <div className="flex flex-wrap items-center gap-2 text-sm">\n <span className="text-muted-foreground">Code</span>\n <code className="rounded-md bg-background px-2 py-1 font-mono">\n {part.authorization.userCode}\n </code>\n </div>\n ) : null}\n {part.state === "required" && part.authorization?.url ? (\n <Button asChild size="sm">\n <a href={part.authorization.url} rel="noreferrer" target="_blank">\n <ExternalLinkIcon className="size-4" />\n Sign in with {part.displayName}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n );\n}\n\nfunction authorizationTitle(part: EveAuthorizationPart): string {\n if (part.state === "required") {\n return `Connect ${part.displayName}`;\n }\n if (part.outcome === "authorized") {\n return `${part.displayName} connected`;\n }\n return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}`;\n}\n\nfunction authorizationDescription(part: EveAuthorizationPart): string {\n if (part.state === "required") {\n return part.description;\n }\n if (part.outcome === "authorized") {\n return `${part.displayName} connected.`;\n }\n const tail = part.reason !== undefined ? ` (${part.reason})` : "";\n return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}${tail}.`;\n}\n\nfunction formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["outcome"]>): string {\n switch (outcome) {\n case "authorized":\n return "authorized";\n case "declined":\n return "declined";\n case "failed":\n return "failed";\n case "timed-out":\n return "timed out";\n }\n}\n\nfunction InputRequestActions({\n canRespond,\n onInputResponses,\n part,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveDynamicToolPart;\n}) {\n const inputRequest = part.toolMetadata?.eve?.inputRequest;\n if (!inputRequest) {\n return null;\n }\n\n const inputResponse = part.toolMetadata?.eve?.inputResponse;\n const selectedOption = inputRequest.options?.find(\n (option) => option.id === inputResponse?.optionId,\n );\n\n return (\n <div className="space-y-3 rounded-md border border-yellow-500/30 bg-yellow-500/5 p-3">\n <p className="text-muted-foreground text-sm">{inputRequest.prompt}</p>\n {inputResponse ? (\n <p className="font-medium text-sm">\n Responded: {selectedOption?.label ?? inputResponse.text ?? inputResponse.optionId}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond}\n key={option.id}\n onClick={() => {\n void onInputResponses([\n {\n optionId: option.id,\n requestId: inputRequest.requestId,\n },\n ]);\n }}\n size="sm"\n type="button"\n variant={option.style === "danger" ? "destructive" : "default"}\n >\n {option.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction partKey(part: EveMessagePart, index: number): string {\n switch (part.type) {\n case "authorization":\n return `authorization:${part.turnId}:${part.stepIndex}:${part.name}`;\n case "dynamic-tool":\n return part.toolCallId;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
5
5
  readonly "app/globals.css": '@import "tailwindcss";\n@source "../node_modules/streamdown/dist/*.js";\n\n@theme inline {\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;\n --font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;\n}\n\n:root {\n color-scheme: light;\n /* Soft neutral page with white elevated surfaces so cards/composer pop. */\n --background: oklch(0.971 0 0);\n --foreground: oklch(0.16 0 0);\n --card: oklch(1 0 0);\n --card-foreground: oklch(0.16 0 0);\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.16 0 0);\n --primary: oklch(0.19 0 0);\n --primary-foreground: oklch(0.985 0 0);\n --secondary: oklch(0.94 0 0);\n --secondary-foreground: oklch(0.19 0 0);\n --muted: oklch(0.94 0 0);\n --muted-foreground: oklch(0.6 0 0);\n --accent: oklch(0.94 0 0);\n --accent-foreground: oklch(0.19 0 0);\n --destructive: oklch(0.577 0.245 27.325);\n --border: oklch(0.916 0 0);\n --input: oklch(0.916 0 0);\n --ring: oklch(0.708 0 0);\n --radius: 0.625rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n color-scheme: dark;\n --background: oklch(0.145 0 0);\n --foreground: oklch(0.985 0 0);\n --card: oklch(0.205 0 0);\n --card-foreground: oklch(0.985 0 0);\n --popover: oklch(0.205 0 0);\n --popover-foreground: oklch(0.985 0 0);\n --primary: oklch(0.922 0 0);\n --primary-foreground: oklch(0.205 0 0);\n --secondary: oklch(0.269 0 0);\n --secondary-foreground: oklch(0.985 0 0);\n --muted: oklch(0.269 0 0);\n --muted-foreground: oklch(0.708 0 0);\n --accent: oklch(0.269 0 0);\n --accent-foreground: oklch(0.985 0 0);\n --destructive: oklch(0.704 0.191 22.216);\n --border: oklch(1 0 0 / 10%);\n --input: oklch(1 0 0 / 15%);\n --ring: oklch(0.556 0 0);\n }\n}\n\n* {\n border-color: var(--border);\n}\n\nhtml {\n height: 100%;\n}\n\nbody {\n min-height: 100%;\n margin: 0;\n background: var(--background);\n font-family: var(--font-sans);\n}\n\nbutton,\ninput,\ntextarea {\n font: inherit;\n}\n';
6
6
  readonly "app/layout.tsx": 'import type { Metadata } from "next";\nimport { Geist, Geist_Mono } from "next/font/google";\nimport type { ReactNode } from "react";\nimport { TooltipProvider } from "@/components/ui/tooltip";\nimport { cn } from "@/lib/utils";\nimport "./globals.css";\n\nconst sans = Geist({\n variable: "--font-sans",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nconst mono = Geist_Mono({\n variable: "--font-mono",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nexport const metadata: Metadata = {\n title: "__EVE_INIT_APP_NAME__",\n description: "A Next.js starter for eve agents with AI Elements.",\n};\n\nexport default function RootLayout({ children }: { readonly children: ReactNode }) {\n return (\n <html className={cn(sans.variable, mono.variable)} lang="en">\n <body>\n <TooltipProvider>{children}</TooltipProvider>\n </body>\n </html>\n );\n}\n';
7
7
  readonly "app/page.tsx": 'import { AgentChat } from "@/app/_components/agent-chat";\n\nexport default function Page() {\n return <AgentChat />;\n}\n';
@@ -3,10 +3,10 @@ import { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";
3
3
 
4
4
  export default eveChannel({
5
5
  auth: [
6
- // Open on localhost for \`eve dev\` and the REPL; ignored in production.
7
- localDev(),
8
6
  // Lets the eve TUI and your Vercel deployments reach the deployed agent.
9
7
  vercelOidc(),
8
+ // Open on localhost for \`eve dev\` and the REPL; ignored in production.
9
+ localDev(),
10
10
  // This placeholder will not allow browser requests in production.
11
11
  // Replace it with your app's auth provider, like Auth.js or Clerk,
12
12
  // or use none() for a public demo.
@@ -142,7 +142,13 @@ function StatusDot({ status }: { readonly status: AgentStatus }) {
142
142
  }
143
143
  `,"app/_components/agent-message.tsx":`"use client";
144
144
 
145
- import type { EveDynamicToolPart, EveMessage, EveMessagePart } from "eve/react";
145
+ import type {
146
+ EveAuthorizationPart,
147
+ EveDynamicToolPart,
148
+ EveMessage,
149
+ EveMessagePart,
150
+ } from "eve/react";
151
+ import { CheckCircleIcon, ExternalLinkIcon, KeyRoundIcon, XCircleIcon } from "lucide-react";
146
152
  import { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";
147
153
  import { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";
148
154
  import {
@@ -153,6 +159,7 @@ import {
153
159
  ToolOutput,
154
160
  } from "@/components/ai-elements/tool";
155
161
  import { Button } from "@/components/ui/button";
162
+ import { cn } from "@/lib/utils";
156
163
 
157
164
  export type AgentInputResponse = {
158
165
  readonly optionId?: string;
@@ -223,6 +230,8 @@ function AgentMessagePart({
223
230
  <ReasoningContent>{part.text}</ReasoningContent>
224
231
  </Reasoning>
225
232
  );
233
+ case "authorization":
234
+ return <AuthorizationPrompt part={part} />;
226
235
  case "dynamic-tool":
227
236
  return (
228
237
  <Tool
@@ -248,6 +257,99 @@ function AgentMessagePart({
248
257
  }
249
258
  }
250
259
 
260
+ function AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {
261
+ const isAuthorized = part.state === "completed" && part.outcome === "authorized";
262
+ const isCompleted = part.state === "completed";
263
+ const Icon = isAuthorized ? CheckCircleIcon : isCompleted ? XCircleIcon : KeyRoundIcon;
264
+ const instructions = part.authorization?.instructions;
265
+ const shouldShowInstructions = instructions !== undefined && instructions !== part.description;
266
+
267
+ return (
268
+ <div
269
+ className={cn(
270
+ "space-y-3 rounded-md border p-3",
271
+ isAuthorized
272
+ ? "border-emerald-500/30 bg-emerald-500/5"
273
+ : isCompleted
274
+ ? "border-destructive/30 bg-destructive/5"
275
+ : "border-blue-500/30 bg-blue-500/5",
276
+ )}
277
+ >
278
+ <div className="flex items-start gap-3">
279
+ <span
280
+ className={cn(
281
+ "mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full",
282
+ isAuthorized
283
+ ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
284
+ : isCompleted
285
+ ? "bg-destructive/10 text-destructive"
286
+ : "bg-blue-500/10 text-blue-700 dark:text-blue-300",
287
+ )}
288
+ >
289
+ <Icon className="size-4" />
290
+ </span>
291
+ <div className="min-w-0 flex-1 space-y-2">
292
+ <p className="font-medium text-sm">{authorizationTitle(part)}</p>
293
+ <p className="text-muted-foreground text-sm">{authorizationDescription(part)}</p>
294
+ {shouldShowInstructions ? (
295
+ <p className="text-muted-foreground text-sm">{instructions}</p>
296
+ ) : null}
297
+ {part.state === "required" && part.authorization?.userCode ? (
298
+ <div className="flex flex-wrap items-center gap-2 text-sm">
299
+ <span className="text-muted-foreground">Code</span>
300
+ <code className="rounded-md bg-background px-2 py-1 font-mono">
301
+ {part.authorization.userCode}
302
+ </code>
303
+ </div>
304
+ ) : null}
305
+ {part.state === "required" && part.authorization?.url ? (
306
+ <Button asChild size="sm">
307
+ <a href={part.authorization.url} rel="noreferrer" target="_blank">
308
+ <ExternalLinkIcon className="size-4" />
309
+ Sign in with {part.displayName}
310
+ </a>
311
+ </Button>
312
+ ) : null}
313
+ </div>
314
+ </div>
315
+ </div>
316
+ );
317
+ }
318
+
319
+ function authorizationTitle(part: EveAuthorizationPart): string {
320
+ if (part.state === "required") {
321
+ return \`Connect \${part.displayName}\`;
322
+ }
323
+ if (part.outcome === "authorized") {
324
+ return \`\${part.displayName} connected\`;
325
+ }
326
+ return \`\${part.displayName} authorization \${formatAuthorizationOutcome(part.outcome)}\`;
327
+ }
328
+
329
+ function authorizationDescription(part: EveAuthorizationPart): string {
330
+ if (part.state === "required") {
331
+ return part.description;
332
+ }
333
+ if (part.outcome === "authorized") {
334
+ return \`\${part.displayName} connected.\`;
335
+ }
336
+ const tail = part.reason !== undefined ? \` (\${part.reason})\` : "";
337
+ return \`\${part.displayName} authorization \${formatAuthorizationOutcome(part.outcome)}\${tail}.\`;
338
+ }
339
+
340
+ function formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["outcome"]>): string {
341
+ switch (outcome) {
342
+ case "authorized":
343
+ return "authorized";
344
+ case "declined":
345
+ return "declined";
346
+ case "failed":
347
+ return "failed";
348
+ case "timed-out":
349
+ return "timed out";
350
+ }
351
+ }
352
+
251
353
  function InputRequestActions({
252
354
  canRespond,
253
355
  onInputResponses,
@@ -303,6 +405,8 @@ function InputRequestActions({
303
405
 
304
406
  function partKey(part: EveMessagePart, index: number): string {
305
407
  switch (part.type) {
408
+ case "authorization":
409
+ return \`authorization:\${part.turnId}:\${part.stepIndex}:\${part.name}\`;
306
410
  case "dynamic-tool":
307
411
  return part.toolCallId;
308
412
  default:
@@ -1,7 +1,7 @@
1
1
  export { byokProviderEnvVar, CURRENT_DIRECTORY_PROJECT_NAME, isEveProject, scaffoldBaseProject, type EvePackageContract, type ScaffoldBaseProjectOptions, } from "./create/project.js";
2
2
  export { DEFAULT_SLACK_CONNECTOR_SLUG, SLACK_CHANNEL_DEFAULT_ROUTE, deriveSlackConnectorSlug, ensureChannel, hasVercelHostFramework, isNextJsProject, listAuthoredChannels, normalizeSlackConnectorSlug, type ChannelKind, type ChannelMutationResult, type EnsureChannelOptions, type SlackConnectorSlug, type WebPackageVersions, } from "./update/channels.js";
3
3
  export { SCAFFOLDABLE_CHANNELS, type ScaffoldableChannel } from "./channels-catalog.js";
4
- export { ensureConnection, listAuthoredConnections, type ConnectionInput, type ConnectionMutationAction, type ConnectionMutationResult, type EnsureConnectionOptions, } from "./update/connections.js";
4
+ export { ensureConnection, ensureConnectionDependencies, listAuthoredConnections, type ConnectionInput, type ConnectionMutationAction, type ConnectionMutationResult, type EnsureConnectionOptions, type EnsureConnectionDependenciesOptions, } from "./update/connections.js";
5
5
  export { catalogSlugs, CONNECTION_CATALOG, CUSTOM_CONNECTION_SLUG, effectiveProtocols, endpointForProtocol, getCatalogEntry, isValidConnectionSlug, SUPPORTED_PROTOCOLS, type ConnectionAuthSpec, type ConnectionCatalogEntry, type ConnectionProtocol, type CustomConnectionInput, type EnvHeader, type McpEndpoint, type OpenApiEndpoint, } from "./connections/catalog.js";
6
6
  export { WriteFileExistsError } from "./files.js";
7
7
  export { HumanActionRequiredError, type HumanAction } from "../human-action.js";
@@ -1 +1 @@
1
- import{HumanActionRequiredError}from"../human-action.js";import{SCAFFOLDABLE_CHANNELS}from"./channels-catalog.js";import{CONNECTION_CATALOG,CUSTOM_CONNECTION_SLUG,SUPPORTED_PROTOCOLS,catalogSlugs,effectiveProtocols,endpointForProtocol,getCatalogEntry,isValidConnectionSlug}from"./connections/catalog.js";import{WriteFileExistsError}from"./files.js";import{CURRENT_DIRECTORY_PROJECT_NAME,byokProviderEnvVar,isEveProject,scaffoldBaseProject}from"./create/project.js";import{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,hasVercelHostFramework,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug}from"./update/channels.js";import{ensureConnection,listAuthoredConnections}from"./update/connections.js";export{CONNECTION_CATALOG,CURRENT_DIRECTORY_PROJECT_NAME,CUSTOM_CONNECTION_SLUG,DEFAULT_SLACK_CONNECTOR_SLUG,HumanActionRequiredError,SCAFFOLDABLE_CHANNELS,SLACK_CHANNEL_DEFAULT_ROUTE,SUPPORTED_PROTOCOLS,WriteFileExistsError,byokProviderEnvVar,catalogSlugs,deriveSlackConnectorSlug,effectiveProtocols,endpointForProtocol,ensureChannel,ensureConnection,getCatalogEntry,hasVercelHostFramework,isEveProject,isNextJsProject,isValidConnectionSlug,listAuthoredChannels,listAuthoredConnections,normalizeSlackConnectorSlug,scaffoldBaseProject};
1
+ import{CONNECTION_CATALOG,CUSTOM_CONNECTION_SLUG,SUPPORTED_PROTOCOLS,catalogSlugs,effectiveProtocols,endpointForProtocol,getCatalogEntry,isValidConnectionSlug}from"./connections/catalog.js";import{HumanActionRequiredError}from"../human-action.js";import{SCAFFOLDABLE_CHANNELS}from"./channels-catalog.js";import{WriteFileExistsError}from"./files.js";import{CURRENT_DIRECTORY_PROJECT_NAME,byokProviderEnvVar,isEveProject,scaffoldBaseProject}from"./create/project.js";import{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,hasVercelHostFramework,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug}from"./update/channels.js";import{ensureConnection,ensureConnectionDependencies,listAuthoredConnections}from"./update/connections.js";export{CONNECTION_CATALOG,CURRENT_DIRECTORY_PROJECT_NAME,CUSTOM_CONNECTION_SLUG,DEFAULT_SLACK_CONNECTOR_SLUG,HumanActionRequiredError,SCAFFOLDABLE_CHANNELS,SLACK_CHANNEL_DEFAULT_ROUTE,SUPPORTED_PROTOCOLS,WriteFileExistsError,byokProviderEnvVar,catalogSlugs,deriveSlackConnectorSlug,effectiveProtocols,endpointForProtocol,ensureChannel,ensureConnection,ensureConnectionDependencies,getCatalogEntry,hasVercelHostFramework,isEveProject,isNextJsProject,isValidConnectionSlug,listAuthoredChannels,listAuthoredConnections,normalizeSlackConnectorSlug,scaffoldBaseProject};
@@ -28,6 +28,12 @@ export interface EnsureConnectionOptions {
28
28
  force?: boolean;
29
29
  connectPackageVersion?: string;
30
30
  }
31
+ export interface EnsureConnectionDependenciesOptions {
32
+ projectRoot: string;
33
+ connectPackageVersion?: string;
34
+ }
35
+ /** Adds the package dependency required by a Connect-auth connection. */
36
+ export declare function ensureConnectionDependencies(options: EnsureConnectionDependenciesOptions): Promise<PackageJsonMutation[]>;
31
37
  /**
32
38
  * Scaffolds `agent/connections/<slug>.ts` from a catalog entry or custom
33
39
  * input, patching `package.json` for Connect-auth connections and seeding
@@ -1,4 +1,4 @@
1
- import{endpointForProtocol}from"../connections/catalog.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"./package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(`
1
+ import{endpointForProtocol}from"../connections/catalog.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"./package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(`
2
2
  `)}\n }),\n`;case`none`:return``}}function renderMcpTemplate(e,t,n){return`${n.kind===`connect`?`import { connect } from "@vercel/connect/eve";
3
3
  import { defineMcpClientConnection } from "eve/connections";
4
4
  `:`import { defineMcpClientConnection } from "eve/connections";
@@ -15,7 +15,7 @@ export default defineOpenAPIConnection({
15
15
  spec: "${e.spec}",
16
16
  ${i} description: "${t}",
17
17
  ${authBlock(n)}});
18
- `}function renderTemplate(e,t,n,r){return e===`mcp`?renderMcpTemplate(t,n,r):renderOpenApiTemplate(t,n,r)}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function ensureConnectDependency(e,n){if(!await pathExists(e))return[];let i=JSON.parse(await readFile(e,`utf8`));return(isJsonObject(i)&&isJsonObject(i.dependencies)?i.dependencies[CONNECT_PACKAGE_NAME]:void 0)===n?[]:(await patchPackageJson(e,{dependencies:{[CONNECT_PACKAGE_NAME]:n}}),[{path:e,dependencies:[CONNECT_PACKAGE_NAME],devDependencies:[],scripts:[]}])}function envKeyPresent(e,t){return RegExp(`^\\s*(?:export\\s+)?${t}\\s*=`,`m`).test(e)}async function seedEnvPlaceholders(e,n){if(n.length===0)return[];let r=``;await pathExists(e)&&(r=await readFile(e,`utf8`));let i=n.filter(e=>!envKeyPresent(r,e));if(i.length===0)return[];let a=`${r.length>0&&!r.endsWith(`
18
+ `}function renderTemplate(e,t,n,r){return e===`mcp`?renderMcpTemplate(t,n,r):renderOpenApiTemplate(t,n,r)}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function ensureConnectDependency(e,n){if(!await pathExists(e))return[];let i=JSON.parse(await readFile(e,`utf8`));return(isJsonObject(i)&&isJsonObject(i.dependencies)?i.dependencies[CONNECT_PACKAGE_NAME]:void 0)===n?[]:(await patchPackageJson(e,{dependencies:{[CONNECT_PACKAGE_NAME]:n}}),[{path:e,dependencies:[CONNECT_PACKAGE_NAME],devDependencies:[],scripts:[]}])}async function ensureConnectionDependencies(e){let t=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??DEFAULT_CONNECT_PACKAGE_VERSION);return ensureConnectDependency(join(e.projectRoot,`package.json`),t)}function envKeyPresent(e,t){return RegExp(`^\\s*(?:export\\s+)?${t}\\s*=`,`m`).test(e)}async function seedEnvPlaceholders(e,n){if(n.length===0)return[];let r=``;await pathExists(e)&&(r=await readFile(e,`utf8`));let i=n.filter(e=>!envKeyPresent(r,e));if(i.length===0)return[];let a=`${r.length>0&&!r.endsWith(`
19
19
  `)?`
20
20
  `:``}${i.map(e=>`${e}=`).join(`
21
- `)}\n`;return await writeFile(e,r+a,`utf8`),i}async function ensureConnection(r){let a=r.slug??r.entry.slug,o=resolveAuth(r.entry),s=endpointForProtocol(r.entry,r.protocol);if(s===null)throw Error(`Connection "${a}" is missing a ${r.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${r.protocol}".`);let c=join(r.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${a}.ts`),l=envKeysForAuth(o),u=await pathExists(c);if(!r.force&&u)return{slug:a,protocol:r.protocol,action:`skipped`,filePath:c,filesWritten:[],filesSkipped:[c],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:l};let d=[];if(o.kind===`connect`){let e=resolveVersionToken(`connectPackageVersion`,r.connectPackageVersion??`0.2.2`);d.push(...await ensureConnectDependency(join(r.projectRoot,`package.json`),e))}await writeTextFile(c,renderTemplate(r.protocol,s,r.entry.description,o),{force:!0});let f=await seedEnvPlaceholders(join(r.projectRoot,`.env.local`),l),p={slug:a,protocol:r.protocol,action:u?`overwritten`:`created`,filePath:c,filesWritten:[c],filesSkipped:[],packageJsonUpdated:d,envKeysAdded:f,envKeysRequired:l};return u&&(p.filesOverwritten=[c]),p}async function listAuthoredConnections(e){let t=join(e,USER_AUTHORED_CONNECTION_DIR),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{ensureConnection,listAuthoredConnections};
21
+ `)}\n`;return await writeFile(e,r+a,`utf8`),i}async function ensureConnection(r){let a=r.slug??r.entry.slug,o=resolveAuth(r.entry),s=endpointForProtocol(r.entry,r.protocol);if(s===null)throw Error(`Connection "${a}" is missing a ${r.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${r.protocol}".`);let c=join(r.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${a}.ts`),l=envKeysForAuth(o),u=await pathExists(c);if(!r.force&&u)return{slug:a,protocol:r.protocol,action:`skipped`,filePath:c,filesWritten:[],filesSkipped:[c],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:l};let d=[];if(o.kind===`connect`){let e=resolveVersionToken(`connectPackageVersion`,r.connectPackageVersion??DEFAULT_CONNECT_PACKAGE_VERSION);d.push(...await ensureConnectDependency(join(r.projectRoot,`package.json`),e))}await writeTextFile(c,renderTemplate(r.protocol,s,r.entry.description,o),{force:!0});let f=await seedEnvPlaceholders(join(r.projectRoot,`.env.local`),l),p={slug:a,protocol:r.protocol,action:u?`overwritten`:`created`,filePath:c,filesWritten:[c],filesSkipped:[],packageJsonUpdated:d,envKeysAdded:f,envKeysRequired:l};return u&&(p.filesOverwritten=[c]),p}async function listAuthoredConnections(e){let t=join(e,USER_AUTHORED_CONNECTION_DIR),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{ensureConnection,ensureConnectionDependencies,listAuthoredConnections};
@@ -1,3 +1,3 @@
1
1
  export { useEveAgent, type PrepareSend, type UseEveAgentOptions, type UseEveAgentReturn, type UseEveAgentSnapshot, type UseEveAgentStatus, } from "#svelte/use-eve-agent.js";
2
2
  export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespondedEvent, type ClientMessageFailedEvent, type ClientMessageSubmittedEvent, } from "#client/reducer.js";
3
- export { defaultMessageReducer, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
3
+ export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-8X2UMr8q.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BEOUv37s.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-8X2UMr8q.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-BEOUv37s.js";
2
2
 
3
3
  export { useEveAgent };
@@ -1,3 +1,3 @@
1
1
  export { useEveAgent, type PrepareSend, type UseEveAgentOptions, type UseEveAgentReturn, type UseEveAgentSnapshot, type UseEveAgentStatus, } from "#vue/use-eve-agent.js";
2
2
  export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespondedEvent, type ClientMessageFailedEvent, type ClientMessageSubmittedEvent, } from "#client/reducer.js";
3
- export { defaultMessageReducer, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
3
+ export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-9ZNiSFMb.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-C25KOe9i.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-9ZNiSFMb.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-C25KOe9i.js";
2
2
 
3
3
  export { useEveAgent };
@@ -12,7 +12,7 @@ import { eveChannel } from "eve/channels/eve";
12
12
  import { localDev, vercelOidc } from "eve/channels/auth";
13
13
 
14
14
  export default eveChannel({
15
- auth: [localDev(), vercelOidc()],
15
+ auth: [vercelOidc(), localDev()],
16
16
  });
17
17
  ```
18
18
 
@@ -54,7 +54,7 @@ The `auth` option decides who can call these routes. The built-in helpers cover
54
54
 
55
55
  Neither admits browser users or external clients in production. For a public app, wire the channel to your own auth (Clerk, Auth.js, your own OIDC/JWT verification, an API-key verifier, or any custom `AuthFn`). Vercel OIDC is optional; use it only when Vercel-issued deployment tokens are part of your trust model.
56
56
 
57
- `eve init` scaffolds an `agent/channels/eve.ts` with a production placeholder so you replace it before going live. The generated channel allows Vercel OIDC and localhost, and includes `placeholderAuth()`, which returns a setup-focused 401 in production until you swap it for real auth. Delete the file and eve falls back to `[localDev(), vercelOidc()]`, which still does not admit browser users in production.
57
+ `eve init` scaffolds an `agent/channels/eve.ts` with a production placeholder so you replace it before going live. The generated channel checks Vercel OIDC before falling back to localhost access, and includes `placeholderAuth()`, which returns a setup-focused 401 in production until you swap it for real auth. Delete the file and eve falls back to `[vercelOidc(), localDev()]`, which still does not admit browser users in production.
58
58
 
59
59
  For the full auth model and helper list, see [Auth & route protection](../guides/auth-and-route-protection).
60
60
 
@@ -67,7 +67,7 @@ import { eveChannel, defaultEveAuth } from "eve/channels/eve";
67
67
  import { localDev, vercelOidc } from "eve/channels/auth";
68
68
 
69
69
  export default eveChannel({
70
- auth: [localDev(), vercelOidc()],
70
+ auth: [vercelOidc(), localDev()],
71
71
  onMessage(ctx, message) {
72
72
  const callerId = ctx.eve.caller?.principalId ?? "anonymous";
73
73
  return {
@@ -108,7 +108,7 @@ Start with the [TypeScript SDK](../guides/client/overview) guide. It covers basi
108
108
  curl http://127.0.0.1:3000/eve/v1/info
109
109
  ```
110
110
 
111
- The route uses the same default auth chain as the eve channel (`[localDev(), vercelOidc()]`). Locally it answers anonymously; a deployed Vercel target requires a valid OIDC bearer, with a same-project bypass for in-deployment callers. See [auth & route protection](../guides/auth-and-route-protection).
111
+ The route uses the same default auth chain as the eve channel (`[vercelOidc(), localDev()]`). A local Vercel OIDC bearer takes precedence; other local requests fall back to development access. A deployed Vercel target requires a valid OIDC bearer, with a same-project bypass for in-deployment callers. See [auth & route protection](../guides/auth-and-route-protection).
112
112
 
113
113
  ## Dispatch order
114
114
 
@@ -27,7 +27,7 @@ import { eveChannel } from "eve/channels/eve";
27
27
  import { localDev, vercelOidc } from "eve/channels/auth";
28
28
 
29
29
  export default eveChannel({
30
- auth: [localDev(), vercelOidc()],
30
+ auth: [vercelOidc(), localDev()],
31
31
  });
32
32
  ```
33
33
 
@@ -62,11 +62,11 @@ function appSession(): AuthFn<Request> {
62
62
  }
63
63
 
64
64
  export default eveChannel({
65
- auth: [appSession(), localDev(), vercelOidc()],
65
+ auth: [appSession(), vercelOidc(), localDev()],
66
66
  });
67
67
  ```
68
68
 
69
- Put your own providers ahead of the catch-all helpers. Any entry that doesn't recognize the caller returns `null`, and the walk moves on. On non-Vercel hosts, omit `vercelOidc()` unless you specifically want to accept Vercel-issued tokens.
69
+ Put your own providers ahead of the catch-all helpers. `localDev()` is the final fallback: put `vercelOidc()` before it so a local Vercel OIDC bearer can resolve a user instead of being shadowed by the synthetic local principal. Any entry that doesn't recognize the caller returns `null`, and the walk moves on. On non-Vercel hosts, omit `vercelOidc()` unless you specifically want to accept Vercel-issued tokens.
70
70
 
71
71
  To reject with a precise status instead of skipping, throw:
72
72
 
@@ -197,11 +197,11 @@ import { eveChannel } from "eve/channels/eve";
197
197
  import { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";
198
198
 
199
199
  export default eveChannel({
200
- auth: [localDev(), vercelOidc(), placeholderAuth()],
200
+ auth: [vercelOidc(), localDev(), placeholderAuth()],
201
201
  });
202
202
  ```
203
203
 
204
- In production, `placeholderAuth()` returns a structured `401` so a generated web chat app can say "auth isn't configured yet" instead of throwing an internal error. Replace it before a browser caller submits a production request: swap in your app's `AuthFn` or one of the shipped helpers. Delete the authored channel file entirely and eve falls back to the framework default `[localDev(), vercelOidc()]`, which also rejects production browser traffic.
204
+ In production, `placeholderAuth()` returns a structured `401` so a generated web chat app can say "auth isn't configured yet" instead of throwing an internal error. Replace it before a browser caller submits a production request: swap in your app's `AuthFn` or one of the shipped helpers. Delete the authored channel file entirely and eve falls back to the framework default `[vercelOidc(), localDev()]`, which also rejects production browser traffic.
205
205
 
206
206
  You do not have to keep `vercelOidc()` in the final policy. For a self-hosted app, an app-embedded frontend, or any deployment that uses a non-Vercel identity system, use `httpBasic()`, `jwtHmac()`, `jwtEcdsa()`, generic `oidc()`, or a custom `AuthFn` that maps your verified user/session/API key into a `SessionAuthContext`.
207
207
 
@@ -16,7 +16,7 @@ On startup the TUI prints a brand line with your agent's name, plus a rotating t
16
16
  Use /channels to add more ways to reach your agent.
17
17
  ```
18
18
 
19
- If agent discovery reported problems, an error and warning count renders between the two lines. Instructions, tools, skills, and subagents are one `eve info` away, and `/help` lists every command. The TUI also runs a startup check. When the runtime confirms that no model provider is configured, it opens the `/model` provider picker before the first prompt. Unknown or linked-but-incomplete credential states remain visible as an attention line, with each command's outcome hanging under it on a `⎿` connector.
19
+ If agent discovery reported problems, an error and warning count renders between the two lines. Instructions, tools, skills, and subagents are one `eve info` away, and `/help` lists every command. The TUI also runs a startup check. A fresh `eve init` starts local `eve dev` with `/model` prefilled. That input starts onboarding: the flow installs the Vercel CLI if needed, asks you to log in if needed, then opens `/model` before the first prompt. Other `eve dev` sessions show missing setup as an attention line, with each command's outcome hanging under it on a `⎿` connector.
20
20
 
21
21
  ## Reading the transcript
22
22
 
@@ -30,12 +30,13 @@ Errors render compactly with docs links highlighted. A code bug escaping your ag
30
30
 
31
31
  ## Slash commands
32
32
 
33
- Each command echoes as an invocation line, asks through a bordered panel that takes the input area's place (one question at a time, separate from the chat transcript), and finishes with a one-line `⎿` result. Loading states stay on the ephemeral status line instead of piling into the transcript; model, channel, and the Vercel CLI commands (`/vc:install`, `/vc:login`) use the same green square pulse as the build phase, while `/deploy` keeps a spinner. Setup menus render the selected option with a filled arrow and an inverse label padded by one space on each side. Text prompts use a blinking block cursor over the character at the caret. The selected label is blue normally and yellow for warning rows.
33
+ Each command echoes as an invocation line, asks through a bordered panel that takes the input area's place (one question at a time, separate from the chat transcript), and finishes with a one-line `⎿` result. Loading states stay on the ephemeral status line instead of piling into the transcript; model, channel, connection, and the Vercel CLI commands (`/vc:install`, `/vc:login`) use the same green square pulse as the build phase, while `/deploy` keeps a spinner. A connection setup waiting for browser action changes that pulse and the word "browser" to yellow. Setup menus render the selected option with a filled arrow and an inverse label padded by one space on each side. Text prompts use a blinking block cursor over the character at the caret. The selected label is blue normally and yellow for warning rows.
34
34
 
35
35
  | Command | Does |
36
36
  | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
37
37
  | `/model` | Opens a configure menu that loops until Done (or Esc). See [Configure the model and provider](#configure-the-model-and-provider). |
38
38
  | `/channels` | Shows the agent's channel list and adds the one you pick. See [Add a channel](#add-a-channel). |
39
+ | `/connect` | Shows the Vercel Connect MCP catalog and configures the server you pick. See [Add a connection](#add-a-connection). |
39
40
  | `/deploy` | Ships the agent to Vercel production, linking the directory first when it is unlinked. |
40
41
  | `/vc:install` | Installs the Vercel CLI. Available locally and on a remote session. |
41
42
  | `/vc:login` | Logs in to Vercel locally. On a remote session, resolves the deployment's project, refreshes its OIDC token, and confirms any required Trusted Sources rule. |
@@ -44,7 +45,7 @@ Each command echoes as an invocation line, asks through a bordered panel that ta
44
45
  | `/exit` | Quits the TUI. |
45
46
  | `/help` | Lists the commands available for the current local or remote session. |
46
47
 
47
- `/model`, `/channels`, and `/deploy` manage the project and are available only when `eve dev` runs the server locally, not when connected to a remote server with `--url`.
48
+ `/model`, `/channels`, `/connect`, and `/deploy` manage the local agent or its linked project. They are available only when `eve dev` runs the server locally, not when connected to a remote server with `--url`.
48
49
 
49
50
  ### Configure the model and provider
50
51
 
@@ -58,6 +59,12 @@ The provider row demands attention (a bold yellow "Configure model access" with
58
59
 
59
60
  `/channels` shows the agent's channel list. Already-registered channels render as checked, focusable rows with an "Already installed" hint. Picking one adds it (including the Slack Connect provisioning), then installs the dependencies the scaffold added so the dev server can load the new channels right away. After each addition the list repaints with the channel checked, until Done (or Esc) leaves the flow.
60
61
 
62
+ ### Add a connection
63
+
64
+ `/connect` shows a searchable list of MCP servers available through Vercel Connect. Already-authored connections remain checked. Logged-out users are directed to `/vc:login`. When the directory is not linked, selecting a server opens the same team and project flow used by `/model`, including creating a project or linking an existing one.
65
+
66
+ For a selected server, eve first tries to attach the provider's canonical connector. If that fails, choose an existing connector from a searchable list or create one with a specific name. The fallback stays scoped to the team selected by the linked project. A connector created by the current attempt is removed if attachment or connection-file patching fails. Successful setup writes `agent/connections/<name>.ts`, records the attached connector UID, installs the new dependency so the dev server can load it, then returns to the main prompt.
67
+
61
68
  ## Keyboard shortcuts
62
69
 
63
70
  Chat and freeform `ask_question` inputs behave like a shell line editor.
@@ -83,7 +90,7 @@ When the agent needs something from you, the TUI asks inline.
83
90
 
84
91
  - Tool approvals are a `y` or `n`.
85
92
  - Option questions let you pick with `↑` / `↓` and `Enter`, or you can compose a multi-line freeform answer.
86
- - If a tool needs an authorized [connection](../connections), the URL shows up right in the transcript, and the turn picks back up once you finish the flow.
93
+ - If a tool needs an authorized [connection](../connections), the URL shows up right in the transcript, and the turn picks back up once you finish the flow. The same local `eve dev` server owns the callback route, so keep that command running until the browser returns. `eve dev --url` connects to an already-running server and does not start a local callback host.
87
94
 
88
95
  ## Control what logs show
89
96
 
@@ -61,13 +61,13 @@ const agent = useEveAgent({
61
61
  });
62
62
  ```
63
63
 
64
- The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [localDev(), vercelOidc()] })`: `localDev()` opens the routes on localhost, `vercelOidc()` admits Vercel OIDC callers in production, and everything else gets a `401`. To run your app's own auth policy, add `agent/channels/eve.ts`:
64
+ The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [vercelOidc(), localDev()] })`: `vercelOidc()` gets the first chance to resolve a Vercel caller, `localDev()` opens the remaining localhost requests, and everything else gets a `401`. To run your app's own auth policy, add `agent/channels/eve.ts`:
65
65
 
66
66
  ```ts title="agent/channels/eve.ts"
67
67
  import { eveChannel } from "eve/channels/eve";
68
68
  import { localDev, vercelOidc } from "eve/channels/auth";
69
69
 
70
- export default eveChannel({ auth: [localDev(), vercelOidc()] });
70
+ export default eveChannel({ auth: [vercelOidc(), localDev()] });
71
71
  ```
72
72
 
73
73
  For a public demo, use `none()` (also from `eve/channels/auth`) to skip authentication. See [Channels](../../channels/overview) and [Auth & route protection](../auth-and-route-protection).
@@ -60,13 +60,13 @@ async function handleSubmit() {
60
60
  </template>
61
61
  ```
62
62
 
63
- The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [localDev(), vercelOidc()] })`: `localDev()` opens the routes on localhost, `vercelOidc()` admits Vercel OIDC callers in production, and everything else gets a `401`. To run your own auth policy, add `agent/channels/eve.ts`:
63
+ The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [vercelOidc(), localDev()] })`: `vercelOidc()` gets the first chance to resolve a Vercel caller, `localDev()` opens the remaining localhost requests, and everything else gets a `401`. To run your own auth policy, add `agent/channels/eve.ts`:
64
64
 
65
65
  ```ts title="agent/channels/eve.ts"
66
66
  import { eveChannel } from "eve/channels/eve";
67
67
  import { localDev, vercelOidc } from "eve/channels/auth";
68
68
 
69
- export default eveChannel({ auth: [localDev(), vercelOidc()] });
69
+ export default eveChannel({ auth: [vercelOidc(), localDev()] });
70
70
  ```
71
71
 
72
72
  For a public demo, use `none()` (also from `eve/channels/auth`) to skip authentication. See [Channels](../../channels/overview) and [Auth & route protection](../auth-and-route-protection).