eve 0.76.0 → 0.76.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/src/channel/channel-address.d.ts +3 -1
- package/dist/src/channel/channel-address.js +1 -1
- package/dist/src/channel/session-stranded-error.d.ts +25 -0
- package/dist/src/channel/session-stranded-error.js +1 -0
- package/dist/src/channel/session.d.ts +13 -6
- package/dist/src/channel/types.d.ts +29 -1
- package/dist/src/chunks/{use-eve-agent-QmriPA-w.js → use-eve-agent-BtKWTEpm.js} +31 -14
- package/dist/src/chunks/{use-eve-agent-D2V9UhLt.js → use-eve-agent-jI6EsYAu.js} +31 -14
- package/dist/src/cli/commands/registry-project.d.ts +10 -3
- package/dist/src/cli/commands/registry-project.js +2 -2
- package/dist/src/cli/commands/registry.js +1 -1
- package/dist/src/cli/dev/tui/runner.js +3 -3
- package/dist/src/cli/dev/tui/stranded-session.d.ts +9 -0
- package/dist/src/cli/dev/tui/stranded-session.js +1 -0
- package/dist/src/client/client-error.d.ts +16 -0
- package/dist/src/client/client-error.js +1 -1
- package/dist/src/client/client.js +1 -1
- package/dist/src/client/index.d.ts +3 -2
- package/dist/src/client/index.js +1 -1
- package/dist/src/client/open-stream.d.ts +5 -3
- package/dist/src/client/open-stream.js +1 -1
- package/dist/src/client/session-controls.js +1 -1
- package/dist/src/client/session.js +1 -1
- package/dist/src/client/transcript-reducer.d.ts +46 -0
- package/dist/src/client/transcript-reducer.js +2 -0
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiler/extension-compatibility.d.ts +20 -20
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/normalize-agent-config.js +1 -1
- package/dist/src/context/accessors.d.ts +1 -1
- package/dist/src/context/build-callback-context.js +1 -1
- package/dist/src/context/dynamic-resolve-context.js +1 -1
- package/dist/src/context/keys.d.ts +5 -0
- package/dist/src/context/keys.js +1 -1
- package/dist/src/context/providers/session.d.ts +1 -0
- package/dist/src/context/providers/session.js +1 -1
- package/dist/src/context/session-context.d.ts +8 -2
- package/dist/src/dynamic/definition.d.ts +7 -1
- package/dist/src/evals/types.d.ts +2 -1
- package/dist/src/eve-channel/index.js +1 -1
- package/dist/src/eve-channel/request.js +2 -2
- package/dist/src/eve-channel/support.d.ts +2 -0
- package/dist/src/eve-channel/support.js +1 -1
- package/dist/src/execution/agent-sessions/steps.js +1 -1
- package/dist/src/execution/dynamic-workflow/tool.js +1 -1
- package/dist/src/execution/eve-workflow-attributes.d.ts +15 -0
- package/dist/src/execution/eve-workflow-attributes.js +1 -1
- package/dist/src/execution/install-workflow-world.d.ts +7 -0
- package/dist/src/execution/install-workflow-world.js +1 -0
- package/dist/src/execution/legacy-session/authorization.js +1 -1
- package/dist/src/execution/legacy-session/inbox.js +1 -1
- package/dist/src/execution/legacy-session/turn-workflow.js +1 -1
- package/dist/src/execution/session/entry.js +1 -1
- package/dist/src/execution/session/handoff-steps.d.ts +11 -0
- package/dist/src/execution/session/handoff-steps.js +1 -1
- package/dist/src/execution/session/program.js +1 -1
- package/dist/src/execution/session/timeout-steps.js +1 -1
- package/dist/src/execution/session-event-stream.d.ts +6 -0
- package/dist/src/execution/session-event-stream.js +1 -1
- package/dist/src/execution/session-inbox/address.d.ts +4 -0
- package/dist/src/execution/session-inbox/address.js +1 -1
- package/dist/src/execution/session-inbox/end-stranded-session.d.ts +22 -0
- package/dist/src/execution/session-inbox/end-stranded-session.js +1 -0
- package/dist/src/execution/session-inbox/hook-release.d.ts +4 -0
- package/dist/src/execution/session-inbox/hook-release.js +1 -0
- package/dist/src/execution/session-inbox/owner.d.ts +48 -0
- package/dist/src/execution/session-inbox/owner.js +1 -0
- package/dist/src/execution/session-inbox/resume.d.ts +12 -0
- package/dist/src/execution/session-inbox/resume.js +1 -1
- package/dist/src/execution/session-inbox/runnable-owner.d.ts +13 -0
- package/dist/src/execution/session-inbox/runnable-owner.js +1 -0
- package/dist/src/execution/session-inbox/stranded-replay-guard.d.ts +16 -0
- package/dist/src/execution/session-inbox/stranded-replay-guard.js +1 -0
- package/dist/src/execution/tools/agent-router-workflow.js +1 -1
- package/dist/src/execution/workflow-runtime.d.ts +5 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/harness/compaction/step.js +1 -1
- package/dist/src/harness/input-request-resolution.js +1 -1
- package/dist/src/harness/model-call/call.d.ts +1 -1
- package/dist/src/harness/model-call/call.js +1 -1
- package/dist/src/harness/model-call/errors.d.ts +5 -6
- package/dist/src/harness/model-call/errors.js +1 -1
- package/dist/src/harness/model-call/recovery.d.ts +4 -3
- package/dist/src/harness/model-call/request.d.ts +2 -1
- package/dist/src/harness/model-call/request.js +1 -1
- package/dist/src/harness/model-call/tools.js +1 -1
- package/dist/src/harness/model-profile.d.ts +10 -3
- package/dist/src/harness/model-profile.js +1 -1
- package/dist/src/harness/prompt-cache.d.ts +6 -3
- package/dist/src/harness/prompt-cache.js +1 -1
- package/dist/src/harness/provider-safety.d.ts +2 -1
- package/dist/src/harness/provider-safety.js +1 -1
- package/dist/src/harness/provider-tools.d.ts +1 -1
- package/dist/src/harness/provider-tools.js +1 -1
- package/dist/src/harness/session-machine/transitions.d.ts +3 -1
- package/dist/src/harness/session-machine/transitions.js +1 -1
- package/dist/src/harness/step/context.js +1 -1
- package/dist/src/harness/step-hooks.js +1 -1
- package/dist/src/internal/application/compiled-artifacts.js +4 -4
- package/dist/src/internal/application/package.d.ts +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/application/stamped-package-version.d.ts +5 -0
- package/dist/src/internal/application/stamped-package-version.js +1 -0
- package/dist/src/internal/authored-definition/core.d.ts +3 -0
- package/dist/src/internal/authored-definition/core.js +1 -1
- package/dist/src/internal/model-auth/available-models.js +1 -1
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
- package/dist/src/internal/workflow/development-run-unavailable-error.d.ts +15 -0
- package/dist/src/internal/workflow/development-run-unavailable-error.js +1 -0
- package/dist/src/internal/workflow/development-world-server.js +1 -1
- package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.d.ts +0 -4
- package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.js +1 -1
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +2 -2
- package/dist/src/internal/workflow-bundle/workflow-builders.js +1 -1
- package/dist/src/protocol/message.d.ts +14 -0
- package/dist/src/protocol/message.js +1 -1
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/channels/index.js +1 -1
- package/dist/src/public/context/index.d.ts +1 -1
- package/dist/src/public/definitions/callback-context.d.ts +1 -1
- package/dist/src/public/definitions/channel.d.ts +1 -0
- package/dist/src/public/definitions/channel.js +1 -1
- package/dist/src/public/models/openai/chatgpt/model.js +1 -1
- package/dist/src/public/models/openai/chatgpt/transport.js +1 -1
- package/dist/src/public/tanstack/index.d.ts +40 -0
- package/dist/src/public/tanstack/index.js +1 -0
- package/dist/src/runtime/agent/resolve-model.js +1 -1
- package/dist/src/runtime/resolve-agent.js +1 -1
- package/dist/src/setup/flows/model-login.js +1 -1
- package/dist/src/setup/integrations/web/framework.d.ts +8 -0
- package/dist/src/setup/integrations/web/framework.js +1 -0
- package/dist/src/setup/integrations/web/hosting.d.ts +43 -0
- package/dist/src/setup/integrations/web/hosting.js +15 -0
- package/dist/src/setup/integrations/web/setup.d.ts +6 -10
- package/dist/src/setup/integrations/web/setup.js +23 -17
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/scaffold/create/web-template.d.ts +6 -2
- package/dist/src/setup/scaffold/create/web-template.js +39 -2
- package/dist/src/setup/scaffold/update/channels.js +1 -1
- package/dist/src/setup/vercel-project-framework.js +1 -1
- package/dist/src/shared/agent-definition.d.ts +25 -0
- package/dist/src/shared/agent-definition.js +1 -1
- package/dist/src/shared/chatgpt-model.d.ts +8 -0
- package/dist/src/shared/chatgpt-model.js +1 -1
- package/dist/src/subagents/callback-route.js +1 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tools/provided/ask-question-workflow.js +1 -1
- package/dist/src/tools/provided/sleep-workflow.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/agent-config.md +32 -1
- package/docs/channels/custom.mdx +26 -0
- package/docs/channels/eve.mdx +8 -5
- package/docs/concepts/built-in-tools.md +17 -0
- package/docs/concepts/execution-model-and-durability.mdx +100 -0
- package/docs/concepts/project-structure.mdx +2 -2
- package/docs/concepts/sessions-runs-and-streaming.md +36 -34
- package/docs/guides/client/messages.mdx +10 -1
- package/docs/guides/deployment/overview.md +2 -2
- package/docs/guides/dev-tui.md +2 -0
- package/docs/guides/frontend/meta.json +9 -1
- package/docs/guides/frontend/nextjs.mdx +1 -1
- package/docs/guides/frontend/overview.mdx +9 -8
- package/docs/guides/frontend/tanstack.mdx +201 -0
- package/docs/guides/session-context.md +1 -0
- package/docs/install-integrations.mdx +1 -1
- package/docs/instructions.mdx +1 -1
- package/docs/reference/typescript-api.md +1 -1
- package/docs/tools/human-in-the-loop.md +2 -0
- package/docs/tutorial/ship-it.mdx +1 -1
- package/package.json +7 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveConnectionDefinition}from"#runtime/resolve-connection.js";import{createResolvedModuleSourceRef}from"#runtime/resolve-helpers.js";import{resolveChannelDefinition}from"#runtime/resolve-channel.js";import{resolveDynamicConnectionDefinition}from"#runtime/resolve-dynamic-connection.js";import{resolveHookDefinition}from"#runtime/resolve-hook.js";import{resolveSandboxDefinition}from"#runtime/resolve-sandbox.js";import{resolveDynamicInstructionsDefinition}from"#runtime/resolve-dynamic-instructions.js";import{resolveDynamicSkillDefinition}from"#runtime/resolve-dynamic-skill.js";import{resolveDynamicToolDefinition}from"#runtime/resolve-dynamic-tool.js";import{resolveToolDefinition}from"#runtime/resolve-tool.js";import{resolveMemoryDefinition}from"#runtime/resolve-memory.js";async function resolveAgent(e){let t=e.manifest.skills.map(t=>({...t,owner:requireCompiledSourceOwner(e.manifest,t),metadata:t.metadata===void 0?void 0:{...t.metadata}})),n=await Promise.all(e.manifest.channelRoutes.effective.map(t=>resolveChannelDefinition(t,e.moduleMap,e.nodeId))),r=await Promise.all(e.manifest.tools.map(t=>resolveToolDefinition(t,e.moduleMap,e.nodeId,requireBindingOwner(e.manifest,t.sourceId)))),i=await Promise.all(e.manifest.dynamicConnections.map(t=>resolveDynamicConnectionDefinition(t,e.moduleMap,e.nodeId))),a=await Promise.all((e.manifest.dynamicInstructions??[]).map(t=>resolveDynamicInstructionsDefinition(t,e.moduleMap,e.nodeId))),o=await Promise.all((e.manifest.dynamicSkills??[]).map(t=>resolveDynamicSkillDefinition(t,e.moduleMap,e.nodeId))),s=await Promise.all(e.manifest.dynamicTools.map(t=>resolveDynamicToolDefinition(t,e.moduleMap,e.nodeId))),c=await Promise.all(e.manifest.hooks.map(t=>resolveHookDefinition(t,e.moduleMap,e.nodeId))),l=await Promise.all(e.manifest.connections.map(t=>resolveConnectionDefinition(t,e.moduleMap,e.nodeId))),u=await Promise.all(e.manifest.memories.map(t=>resolveMemoryDefinition(t,e.moduleMap,e.nodeId))),d=await resolveSandboxDefinition(e.manifest.sandbox,e.moduleMap,e.nodeId),f=e.manifest.instructions.map(t=>createResolvedInstructionsDefinition(e.manifest,t)),p=e.manifest.workspaceResourceRoot,m={channels:n,connections:l,dynamicConnectionResolvers:i,dynamicInstructionsResolvers:a,dynamicSkillResolvers:o,dynamicToolResolvers:s,hooks:c,instructions:f,metadata:{agentRoot:e.manifest.agentRoot,appRoot:e.manifest.appRoot,diagnosticsSummary:e.manifest.diagnosticsSummary},memories:u,sandbox:d,workspaceResourceRoot:p,skills:t,tools:r,workspaceSpec:{rootEntries:[...p.rootEntries]}};return`config`in e.manifest?{...m,config:createResolvedAgentConfig(e.manifest)}:m}function requireBindingOwner(e,t){let n=e.bindings[t];if(n===void 0)throw Error(`Compiled source "${t}" has no binding.`);return n.owner}function createResolvedInstructionsDefinition(e,t){return{content:t.content,name:t.name,owner:requireCompiledSourceOwner(e,t),logicalPath:t.logicalPath,role:t.role,sourceId:t.sourceId,sourceKind:t.sourceKind}}function requireCompiledSourceOwner(e,t){let n=e.bindings[t.sourceId]?.owner??t.owner;if(n===void 0)throw Error(`Compiled source "${t.sourceId}" has no owner.`);return n}function createResolvedAgentConfig(e){let t={name:e.config.name};if(e.config.defaultTools!==void 0&&(t.defaultTools=e.config.defaultTools),e.config.description!==void 0&&(t.description=e.config.description),e.config.compaction!==void 0){let n={};e.config.compaction.model!==void 0&&(n.model=e.config.compaction.model.source===void 0?{contextWindowTokens:e.config.compaction.model.contextWindowTokens,id:e.config.compaction.model.id,maxOutputTokens:e.config.compaction.model.maxOutputTokens,providerOptions:e.config.compaction.model.providerOptions}:{contextWindowTokens:e.config.compaction.model.contextWindowTokens,id:e.config.compaction.model.id,maxOutputTokens:e.config.compaction.model.maxOutputTokens,providerOptions:e.config.compaction.model.providerOptions,source:{exportName:e.config.compaction.model.source.exportName,sourceKind:`module`,logicalPath:e.config.compaction.model.source.logicalPath,sourceId:e.config.compaction.model.source.sourceId}}),e.config.compaction.thresholdPercent!==void 0&&(n.thresholdPercent=e.config.compaction.thresholdPercent),t.compaction=n}if(e.config.experimental!==void 0&&(t.experimental={workflow:e.config.experimental.workflow===void 0?void 0:{modelCallsPerStep:e.config.experimental.workflow.modelCallsPerStep,retention:e.config.experimental.workflow.retention,world:e.config.experimental.workflow.world}}),e.config.reasoning!==void 0&&(t.reasoning=e.config.reasoning),e.config.source!==void 0&&(t.source=createResolvedModuleSourceRef(e.config.source)),e.config.tool!==void 0&&(t.tool=e.config.tool),e.config.limits!==void 0&&(t.limits={maxInputTokensPerSession:e.config.limits.maxInputTokensPerSession,maxOutputTokensPerSession:e.config.limits.maxOutputTokensPerSession,maxTokenCostUsdPerSession:e.config.limits.maxTokenCostUsdPerSession,sessionTimeoutMs:e.config.limits.sessionTimeoutMs}),e.config.dynamicModel!==void 0)return{...t,dynamicModel:{...createResolvedModuleSourceRef(e.config.dynamicModel),eventNames:[...e.config.dynamicModel.eventNames]}};let n=e.config.model;return{...t,model:n.source===void 0?{id:n.id,contextWindowTokens:n.contextWindowTokens,maxOutputTokens:n.maxOutputTokens,providerOptions:n.providerOptions}:{contextWindowTokens:n.contextWindowTokens,id:n.id,maxOutputTokens:n.maxOutputTokens,providerOptions:n.providerOptions,source:{exportName:n.source.exportName,sourceKind:`module`,logicalPath:n.source.logicalPath,sourceId:n.source.sourceId}}}}export{resolveAgent};
|
|
1
|
+
import{resolveConnectionDefinition}from"#runtime/resolve-connection.js";import{createResolvedModuleSourceRef}from"#runtime/resolve-helpers.js";import{resolveChannelDefinition}from"#runtime/resolve-channel.js";import{resolveDynamicConnectionDefinition}from"#runtime/resolve-dynamic-connection.js";import{resolveHookDefinition}from"#runtime/resolve-hook.js";import{resolveSandboxDefinition}from"#runtime/resolve-sandbox.js";import{resolveDynamicInstructionsDefinition}from"#runtime/resolve-dynamic-instructions.js";import{resolveDynamicSkillDefinition}from"#runtime/resolve-dynamic-skill.js";import{resolveDynamicToolDefinition}from"#runtime/resolve-dynamic-tool.js";import{resolveToolDefinition}from"#runtime/resolve-tool.js";import{resolveMemoryDefinition}from"#runtime/resolve-memory.js";async function resolveAgent(e){let t=e.manifest.skills.map(t=>({...t,owner:requireCompiledSourceOwner(e.manifest,t),metadata:t.metadata===void 0?void 0:{...t.metadata}})),n=await Promise.all(e.manifest.channelRoutes.effective.map(t=>resolveChannelDefinition(t,e.moduleMap,e.nodeId))),r=await Promise.all(e.manifest.tools.map(t=>resolveToolDefinition(t,e.moduleMap,e.nodeId,requireBindingOwner(e.manifest,t.sourceId)))),i=await Promise.all(e.manifest.dynamicConnections.map(t=>resolveDynamicConnectionDefinition(t,e.moduleMap,e.nodeId))),a=await Promise.all((e.manifest.dynamicInstructions??[]).map(t=>resolveDynamicInstructionsDefinition(t,e.moduleMap,e.nodeId))),o=await Promise.all((e.manifest.dynamicSkills??[]).map(t=>resolveDynamicSkillDefinition(t,e.moduleMap,e.nodeId))),s=await Promise.all(e.manifest.dynamicTools.map(t=>resolveDynamicToolDefinition(t,e.moduleMap,e.nodeId))),c=await Promise.all(e.manifest.hooks.map(t=>resolveHookDefinition(t,e.moduleMap,e.nodeId))),l=await Promise.all(e.manifest.connections.map(t=>resolveConnectionDefinition(t,e.moduleMap,e.nodeId))),u=await Promise.all(e.manifest.memories.map(t=>resolveMemoryDefinition(t,e.moduleMap,e.nodeId))),d=await resolveSandboxDefinition(e.manifest.sandbox,e.moduleMap,e.nodeId),f=e.manifest.instructions.map(t=>createResolvedInstructionsDefinition(e.manifest,t)),p=e.manifest.workspaceResourceRoot,m={channels:n,connections:l,dynamicConnectionResolvers:i,dynamicInstructionsResolvers:a,dynamicSkillResolvers:o,dynamicToolResolvers:s,hooks:c,instructions:f,metadata:{agentRoot:e.manifest.agentRoot,appRoot:e.manifest.appRoot,diagnosticsSummary:e.manifest.diagnosticsSummary},memories:u,sandbox:d,workspaceResourceRoot:p,skills:t,tools:r,workspaceSpec:{rootEntries:[...p.rootEntries]}};return`config`in e.manifest?{...m,config:createResolvedAgentConfig(e.manifest)}:m}function requireBindingOwner(e,t){let n=e.bindings[t];if(n===void 0)throw Error(`Compiled source "${t}" has no binding.`);return n.owner}function createResolvedInstructionsDefinition(e,t){return{content:t.content,name:t.name,owner:requireCompiledSourceOwner(e,t),logicalPath:t.logicalPath,role:t.role,sourceId:t.sourceId,sourceKind:t.sourceKind}}function requireCompiledSourceOwner(e,t){let n=e.bindings[t.sourceId]?.owner??t.owner;if(n===void 0)throw Error(`Compiled source "${t.sourceId}" has no owner.`);return n}function createResolvedAgentConfig(e){let t={name:e.config.name};if(e.config.defaultTools!==void 0&&(t.defaultTools=e.config.defaultTools),e.config.description!==void 0&&(t.description=e.config.description),e.config.compaction!==void 0){let n={};e.config.compaction.model!==void 0&&(n.model=e.config.compaction.model.source===void 0?{contextWindowTokens:e.config.compaction.model.contextWindowTokens,id:e.config.compaction.model.id,maxOutputTokens:e.config.compaction.model.maxOutputTokens,providerOptions:e.config.compaction.model.providerOptions}:{contextWindowTokens:e.config.compaction.model.contextWindowTokens,id:e.config.compaction.model.id,maxOutputTokens:e.config.compaction.model.maxOutputTokens,providerOptions:e.config.compaction.model.providerOptions,source:{exportName:e.config.compaction.model.source.exportName,sourceKind:`module`,logicalPath:e.config.compaction.model.source.logicalPath,sourceId:e.config.compaction.model.source.sourceId}}),e.config.compaction.thresholdPercent!==void 0&&(n.thresholdPercent=e.config.compaction.thresholdPercent),t.compaction=n}if(e.config.experimental!==void 0&&(t.experimental={workflow:e.config.experimental.workflow===void 0?void 0:{modelCallsPerStep:e.config.experimental.workflow.modelCallsPerStep,retention:e.config.experimental.workflow.retention,world:e.config.experimental.workflow.world}}),e.config.reasoning!==void 0&&(t.reasoning=e.config.reasoning),e.config.source!==void 0&&(t.source=createResolvedModuleSourceRef(e.config.source)),e.config.tool!==void 0&&(t.tool=e.config.tool),e.config.limits!==void 0&&(t.limits={maxInputTokensPerSession:e.config.limits.maxInputTokensPerSession,maxOutputTokensPerSession:e.config.limits.maxOutputTokensPerSession,maxTokenCostUsdPerSession:e.config.limits.maxTokenCostUsdPerSession,sessionTimeoutMs:e.config.limits.sessionTimeoutMs}),e.config.dynamicModel!==void 0)return{...t,dynamicModel:{...createResolvedModuleSourceRef(e.config.dynamicModel),eventNames:[...e.config.dynamicModel.eventNames]}};let n=e.config.model;return{...t,model:n.source===void 0?{id:n.id,contextWindowTokens:n.contextWindowTokens,maxOutputTokens:n.maxOutputTokens,providerOptions:n.providerOptions}:{contextWindowTokens:n.contextWindowTokens,id:n.id,maxOutputTokens:n.maxOutputTokens,promptCache:n.promptCache,providerOptions:n.providerOptions,source:{exportName:n.source.exportName,sourceKind:`module`,logicalPath:n.source.logicalPath,sourceId:n.source.sourceId}}}}export{resolveAgent};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fetchGatewayCatalog}from"../boxes/select-model.js";import{measureLoginStage,withLoginProgress}from"./model-login-progress.js";import{CONNECTION_OPTIONS}from"./model-login-options.js";import{authenticateModelConnection,reuseModelConnection}from"./model-login-connection.js";import{changeValidatedAgentModel,readAuthoredModelSelection}from"./model-source-change.js";import{MODEL_CONNECTION_ENV}from"#internal/model-auth/transport.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{inspectApplication}from"#services/inspect-application.js";import{WizardCancelledError}from"#setup/step.js";import{MODEL_HELPERS,parseModelHelper}from"#shared/model-helper.js";import{providerSettingsMatch,readProviderKeySourceSync,readProviderSelection,readProviderTeamSync,resolveAvailableProviders,writeProviderSelection}from"#setup/provider-settings.js";import{resolveModelEndpointStatus}from"#internal/resolve-model-endpoint-status.js";import{classifyModelRouting}from"#internal/classify-model-routing.js";import{availableHelperModels}from"#internal/model-auth/available-models.js";import{isModelConnection,readDefaultConnection,writeDefaultConnection}from"#internal/model-auth/store.js";function environmentConnection(e){if(e.AI_GATEWAY_API_KEY?.trim())return`ai-gateway-key`;if(e.VERCEL_OIDC_TOKEN?.trim())return`ai-gateway-project`;if(e.OPENAI_API_KEY?.trim())return`openai`;if(e.ANTHROPIC_API_KEY?.trim())return`anthropic`}async function readLoginModel(e){let t=await readAuthoredModelSelection(e);if(t!==void 0)return{selection:t,external:parseModelHelper(t)!==void 0};let n=(await inspectApplication(e)).compiledState?.manifest.config.model;return{selection:n?.routing.kind===`gateway`&&n.source===void 0?n.id:void 0,external:n?.routing.kind===`external`}}function modelSelection(e,t){let n=e===`chatgpt`||e===`openai`||e===`anthropic`?e:void 0,r=t.selection===void 0?void 0:parseModelHelper(t.selection),i=n?r?.helper===n:t.selection!==void 0&&!t.external,a=n?MODEL_HELPERS[n].defaultModel:DEFAULT_AGENT_MODEL_ID,o=r?.id??t.selection;return{helper:n,compatible:i,defaultId:a,needsModels:!i||o===a}}async function applyConnection(e,t,n,r){let{appRoot:i,prompter:a,signal:o}=e,{selected:s,team:c}=t,{helper:l,compatible:u,defaultId:d,needsModels:f}=modelSelection(s,n),p;if(f){let e=t.availableModels??r??(l?await withLoginProgress(a,`Loading available models…`,()=>availableHelperModels(l,o)):[]),i=d;if(!e.includes(
|
|
1
|
+
import{fetchGatewayCatalog}from"../boxes/select-model.js";import{measureLoginStage,withLoginProgress}from"./model-login-progress.js";import{CONNECTION_OPTIONS}from"./model-login-options.js";import{authenticateModelConnection,reuseModelConnection}from"./model-login-connection.js";import{changeValidatedAgentModel,readAuthoredModelSelection}from"./model-source-change.js";import{MODEL_CONNECTION_ENV}from"#internal/model-auth/transport.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{inspectApplication}from"#services/inspect-application.js";import{WizardCancelledError}from"#setup/step.js";import{MODEL_HELPERS,parseModelHelper}from"#shared/model-helper.js";import{providerSettingsMatch,readProviderKeySourceSync,readProviderSelection,readProviderTeamSync,resolveAvailableProviders,writeProviderSelection}from"#setup/provider-settings.js";import{resolveModelEndpointStatus}from"#internal/resolve-model-endpoint-status.js";import{classifyModelRouting}from"#internal/classify-model-routing.js";import{parseChatGptFastModelId}from"#shared/chatgpt-model.js";import{availableHelperModels}from"#internal/model-auth/available-models.js";import{isModelConnection,readDefaultConnection,writeDefaultConnection}from"#internal/model-auth/store.js";function environmentConnection(e){if(e.AI_GATEWAY_API_KEY?.trim())return`ai-gateway-key`;if(e.VERCEL_OIDC_TOKEN?.trim())return`ai-gateway-project`;if(e.OPENAI_API_KEY?.trim())return`openai`;if(e.ANTHROPIC_API_KEY?.trim())return`anthropic`}async function readLoginModel(e){let t=await readAuthoredModelSelection(e);if(t!==void 0)return{selection:t,external:parseModelHelper(t)!==void 0};let n=(await inspectApplication(e)).compiledState?.manifest.config.model;return{selection:n?.routing.kind===`gateway`&&n.source===void 0?n.id:void 0,external:n?.routing.kind===`external`}}function modelSelection(e,t){let n=e===`chatgpt`||e===`openai`||e===`anthropic`?e:void 0,r=t.selection===void 0?void 0:parseModelHelper(t.selection),i=n?r?.helper===n:t.selection!==void 0&&!t.external,a=n?MODEL_HELPERS[n].defaultModel:DEFAULT_AGENT_MODEL_ID,o=r?.id??t.selection;return{helper:n,compatible:i,defaultId:a,needsModels:!i||o===a}}async function applyConnection(e,t,n,r){let{appRoot:i,prompter:a,signal:o}=e,{selected:s,team:c}=t,{helper:l,compatible:u,defaultId:d,needsModels:f}=modelSelection(s,n),p;if(f){let e=t.availableModels??r??(l?await withLoginProgress(a,`Loading available models…`,()=>availableHelperModels(l,o)):[]),i=d,s=l===`chatgpt`?parseChatGptFastModelId(i).slug:i;if(!e.includes(s)){if(e.length===0)throw Error(`No models are available for this connection. Choose another connection with /login.`);i=await a.select({message:`Choose an available model`,search:!0,options:e.map(e=>({value:e,label:e}))})}let c=l?MODEL_HELPERS[l].prefix+i:i;(!u||c!==n.selection)&&(p=c)}let m=s===`openai`||s===`anthropic`||s===`ai-gateway-key`?e.automatic?readProviderKeySourceSync(i)??(environmentConnection(process.env)===s?`environment`:`secret`):`secret`:void 0,h={selected:s,...c,keySource:m},g=!await providerSettingsMatch(i,h),save=async()=>{if(o?.throwIfAborted(),p!==void 0){let t=await changeValidatedAgentModel({appRoot:e.agentRoot??i,slug:p});if(t.kind===`rejected`)throw Error(t.message)}g&&await writeProviderSelection(i,s,c,m),process.env[MODEL_CONNECTION_ENV]=s,m?process.env.EVE_MODEL_KEY_SOURCE=m:delete process.env.EVE_MODEL_KEY_SOURCE,c?(process.env.EVE_MODEL_TEAM=c.teamId,process.env.EVE_MODEL_TEAM_NAME=c.teamName):(delete process.env.EVE_MODEL_TEAM,delete process.env.EVE_MODEL_TEAM_NAME)};p===void 0?await save():await withLoginProgress(a,`Loading selected model…`,()=>measureLoginStage(`activation`,()=>e.withConnectionUpdate?e.withConnectionUpdate(save):save())),e.automatic||await writeDefaultConnection(s);let _=p??n.selection,v=l&&_?parseModelHelper(_)?.id:_,y;if(v){let e=l?{kind:`external`,provider:MODEL_HELPERS[l].provider}:classifyModelRouting(v);y={id:v,routing:e,endpoint:resolveModelEndpointStatus(e,{apiKey:s===`ai-gateway-key`,oidc:s===`ai-gateway-project`,account:s===`vercel`||s===`vercel-cli`,team:c?.teamName},{state:`ready`})}}return{kind:`ready`,reload:p!==void 0&&e.withConnectionUpdate===void 0,...y&&{model:y}}}async function runModelLogin(e){let{appRoot:t,prompter:n,signal:r}=e,i,getModel=()=>i??=measureLoginStage(`source_inspection`,()=>readLoginModel(e.agentRoot??t)),connect=async(i,a)=>{let o=new AbortController,s=r?AbortSignal.any([r,o.signal]):o.signal,c=getModel().then(async e=>{let t=modelSelection(i,e);return{model:e,models:!t.helper&&t.needsModels?(await measureLoginStage(`gateway_catalog`,()=>fetchGatewayCatalog(s))).filter(e=>e.type===`language`).map(e=>e.id):void 0}}).then(e=>({value:e}),e=>({error:e}));try{let r=readProviderTeamSync(t),o=await measureLoginStage(`authentication`,async()=>a?await withLoginProgress(n,`Checking model access…`,()=>reuseModelConnection(i,r,s)):await authenticateModelConnection({selected:i,team:r,prompter:n,signal:s}));if(!o)return;let l=await withLoginProgress(n,`Loading available models…`,()=>c);if(`error`in l)throw l.error;return await applyConnection({...e,automatic:a,signal:s},o,l.value.model,l.value.models)}finally{o.abort()}};try{if(e.automatic){let{model:e,selected:a}=await withLoginProgress(n,`Reading saved connection…`,async()=>{let[e,n,r,i]=await Promise.all([getModel(),readProviderSelection(t),resolveAvailableProviders(t),readDefaultConnection()]),a=e.selection===void 0?void 0:parseModelHelper(e.selection)?.helper;return{model:e,selected:n??a??environmentConnection(process.env)??(r.includes(`ai-gateway-project`)?`ai-gateway-project`:void 0)??i??`vercel-cli`}});if(e.external&&e.selection===void 0)return{kind:`ready`,reload:!1};try{let e=await connect(a,!0);if(e)return e}catch(e){i=void 0,r?.throwIfAborted(),n.log.warning(e instanceof Error?e.message:`Could not connect automatically. Retry /login.`)}}let a=e.selected;for(;;){let t=a??await n.select({message:e.connectionMessage??`Choose a connection`,search:!0,options:[...CONNECTION_OPTIONS]});if(a=void 0,!isModelConnection(t))throw Error(`Choose a model connection.`);try{let e=await connect(t,!1);if(e)return e}catch(e){if(i=void 0,e instanceof WizardCancelledError)return{kind:`cancelled`};r?.throwIfAborted(),n.log.warning(e instanceof Error?e.message:`Connection failed. Retry /login.`)}}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}export{environmentConnection,runModelLogin};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { pathExists } from "#setup/scaffold/files.js";
|
|
2
|
+
/** Frontend frameworks the Web Chat installer can generate. */
|
|
3
|
+
export type WebChatFramework = "next" | "tanstack";
|
|
4
|
+
export declare const WEB_CHAT_FRAMEWORK_LABELS: Readonly<Record<WebChatFramework, string>>;
|
|
5
|
+
/** Asked by `eve add channel/web` before it installs the frontend files. */
|
|
6
|
+
export declare const WEB_FRAMEWORK_QUESTION: import("#setup/ask.js").Question<WebChatFramework>;
|
|
7
|
+
/** The framework of the app already in `webRoot`, or `undefined` when it has none. */
|
|
8
|
+
export declare function detectWebChatFramework(webRoot: string, exists?: typeof pathExists): Promise<WebChatFramework | undefined>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{join}from"node:path";import{pathExists}from"#setup/scaffold/files.js";import{select}from"#setup/ask.js";const WEB_CHAT_FRAMEWORK_LABELS={next:`Next.js`,tanstack:`TanStack Start`},WEB_FRAMEWORK_QUESTION=select({key:`web-framework`,message:`Which framework should Web Chat use?`,options:[{id:`next`,label:WEB_CHAT_FRAMEWORK_LABELS.next,hint:`(Recommended) Supports Sign in with Vercel.`,value:`next`},{id:`tanstack`,label:WEB_CHAT_FRAMEWORK_LABELS.tanstack,hint:`A Vite app built with TanStack Router.`,value:`tanstack`}],recommended:`next`,required:!0}),HOST_CONFIG_FILES={next:[`next.config.ts`,`next.config.mjs`,`next.config.js`],tanstack:[`vite.config.ts`,`vite.config.mjs`,`vite.config.js`]};async function hasAny(e,t,n){for(let r of t)if(await n(join(e,r)))return!0;return!1}async function detectWebChatFramework(e,t=pathExists){let[n,r]=await Promise.all([hasAny(e,HOST_CONFIG_FILES.next,t),hasAny(e,HOST_CONFIG_FILES.tanstack,t)]);if(n&&r)throw Error(`${e} contains both a Next.js and a Vite config. Remove the app you no longer use, then retry.`);return n?`next`:r?`tanstack`:void 0}export{WEB_CHAT_FRAMEWORK_LABELS,WEB_FRAMEWORK_QUESTION,detectWebChatFramework};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { resolveEveProjectContext } from "#internal/project-context.js";
|
|
2
|
+
import { detectPackageManager, type PackageManagerKind } from "#setup/package-manager.js";
|
|
3
|
+
import { pathExists, writeTextFile } from "#setup/scaffold/files.js";
|
|
4
|
+
export interface WebChatHostingDeps {
|
|
5
|
+
detectPackageManager: typeof detectPackageManager;
|
|
6
|
+
pathExists: typeof pathExists;
|
|
7
|
+
readTextFile(path: string): Promise<string>;
|
|
8
|
+
/** Defaults to a forced `rm`. */
|
|
9
|
+
removeFile?(path: string): Promise<void>;
|
|
10
|
+
resolveEveProjectContext: typeof resolveEveProjectContext;
|
|
11
|
+
writeTextFile: typeof writeTextFile;
|
|
12
|
+
}
|
|
13
|
+
export declare const defaultWebChatHostingDeps: WebChatHostingDeps;
|
|
14
|
+
export declare function peerServiceVercelConfig(framework: "nextjs" | "tanstack-start", buildCommand: string): string;
|
|
15
|
+
export declare function runScriptCommand(packageManager: PackageManagerKind, script: string): string;
|
|
16
|
+
export interface WebChatProject {
|
|
17
|
+
environmentRoot: string;
|
|
18
|
+
agentAppRoot: string;
|
|
19
|
+
/** Set for a workspace member so the chat app targets it by name. */
|
|
20
|
+
agentName?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function resolveWebChatProject(appRoot: string, deps: WebChatHostingDeps): Promise<WebChatProject>;
|
|
23
|
+
export interface WebChatHostingInput {
|
|
24
|
+
project: WebChatProject;
|
|
25
|
+
webRoot: string;
|
|
26
|
+
force?: boolean;
|
|
27
|
+
/** Whether to write the default channel when it is absent (or forced). */
|
|
28
|
+
writeChannel: boolean;
|
|
29
|
+
/** The web app's framework config, e.g. `next.config.ts`. */
|
|
30
|
+
hostConfig: {
|
|
31
|
+
path: string;
|
|
32
|
+
source: string;
|
|
33
|
+
owned: readonly string[];
|
|
34
|
+
};
|
|
35
|
+
vercelServices: boolean;
|
|
36
|
+
/** The installer's `vercel.ts`, followed by any it wrote in earlier releases. */
|
|
37
|
+
vercelConfigs: readonly [string, ...string[]];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Checks every file Web Chat hosting would overwrite or remove, then returns
|
|
41
|
+
* the writer, so an authored file fails setup before anything is written.
|
|
42
|
+
*/
|
|
43
|
+
export declare function prepareWebChatHosting(input: WebChatHostingInput, deps: WebChatHostingDeps): Promise<() => Promise<void>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import{readFile,rm}from"node:fs/promises";import{join}from"node:path";import{resolveEveProjectContext}from"#internal/project-context.js";import{detectPackageManager}from"#setup/package-manager.js";import{pathExists,writeTextFile}from"#setup/scaffold/files.js";import{WEB_CHANNEL_TEMPLATES}from"#setup/scaffold/create/web-template.js";const removeFile=e=>rm(e,{force:!0}),defaultWebChatHostingDeps={detectPackageManager,pathExists,readTextFile:e=>readFile(e,`utf8`),removeFile,resolveEveProjectContext,writeTextFile},PEER_SERVICE_SCRIPTS={dev:`eve dev`,"dev:eve":`eve dev`,"dev:all":`vercel dev --local`};function peerServiceVercelConfig(e,t){return`import { withEve } from "eve/vercel";
|
|
2
|
+
|
|
3
|
+
export default await withEve({
|
|
4
|
+
services: {
|
|
5
|
+
web: {
|
|
6
|
+
framework: "${e}",
|
|
7
|
+
root: "apps/web",
|
|
8
|
+
buildCommand: "${t}",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
routes: [
|
|
12
|
+
{ src: "^(.*)$", destination: { type: "service", service: "web" } },
|
|
13
|
+
],
|
|
14
|
+
});
|
|
15
|
+
`}function runScriptCommand(e,t){switch(e){case`npm`:return`npm run ${t}`;case`pnpm`:return`pnpm ${t}`;case`yarn`:return`yarn ${t}`;case`bun`:return`bun run ${t}`}}async function resolveWebChatProject(e,t){let n=await t.resolveEveProjectContext(e);switch(n.kind){case`workspace`:throw Error(`Web Chat setup requires a selected workspace agent.`);case`workspace-member`:return{environmentRoot:n.environmentRoot,agentAppRoot:n.member.appRoot,agentName:n.member.name};case`standalone`:return{environmentRoot:n.environmentRoot,agentAppRoot:n.appRoot}}}async function readFileOwnership(e,t,n){if(!await n.pathExists(e))return`absent`;try{return t.includes(await n.readTextFile(e))?`installer`:`authored`}catch(e){if(e.code===`ENOENT`)return`absent`;throw e}}function assertNotAuthored(e,t){if(t===`authored`)throw Error(`Could not configure Web Chat because ${e} contains authored configuration. Preserve it and compose the eve integration manually.`)}async function updatePeerServiceScripts(e,t,n){let r=join(e,`package.json`),i=JSON.parse(await t.readTextFile(r)),a={...i.scripts};n(a),JSON.stringify(a)!==JSON.stringify({...i.scripts})&&await t.writeTextFile(r,`${JSON.stringify({...i,scripts:a},null,2)}\n`,{force:!0})}async function prepareWebChatHosting(e,t){let{project:n,webRoot:r,hostConfig:i}=e,a=join(n.environmentRoot,`vercel.ts`),o=join(n.environmentRoot,`vercel.json`);assertNotAuthored(i.path,await readFileOwnership(i.path,i.owned,t));let s=await readFileOwnership(a,e.vercelConfigs,t);if(e.vercelServices&&assertNotAuthored(a,s),e.vercelServices&&await t.pathExists(o))throw Error(`Could not configure Vercel services because ${o} already exists. Preserve it and compose eve/vercel manually.`);return async()=>{let o=join(n.agentAppRoot,`agent`,`channels`,`eve.ts`);e.writeChannel&&(e.force||!await t.pathExists(o))&&await t.writeTextFile(o,WEB_CHANNEL_TEMPLATES.default,{force:e.force}),await t.writeTextFile(join(r,`app`,`eve-agent.ts`),`/** Named workspace agent selected by the Web Chat installer. */\nexport const WEB_CHAT_AGENT: string | undefined = ${n.agentName===void 0?`undefined`:JSON.stringify(n.agentName)};\n`,{force:!0}),await t.writeTextFile(i.path,i.source,{force:!0}),e.vercelServices?(await t.writeTextFile(a,e.vercelConfigs[0],{force:!0}),await updatePeerServiceScripts(n.environmentRoot,t,e=>{for(let[t,n]of Object.entries(PEER_SERVICE_SCRIPTS))e[t]??=n})):s===`installer`&&(await(t.removeFile??removeFile)(a),await updatePeerServiceScripts(n.environmentRoot,t,e=>{for(let t of[`dev:eve`,`dev:all`])e[t]===PEER_SERVICE_SCRIPTS[t]&&delete e[t]}))}}export{defaultWebChatHostingDeps,peerServiceVercelConfig,prepareWebChatHosting,resolveWebChatProject,runScriptCommand};
|
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
import { resolveEveProjectContext } from "#internal/project-context.js";
|
|
2
1
|
import type { RegistrySetupCompletion } from "#setup/registry-setup-protocol.js";
|
|
3
2
|
import type { VercelProjectReference } from "#setup/project-resolution.js";
|
|
3
|
+
import type { PackageManagerKind } from "#setup/package-manager.js";
|
|
4
4
|
import { installScaffoldDependencies } from "../shared/scaffold.js";
|
|
5
5
|
import { prepareWebAuthScaffold } from "./auth-scaffold.js";
|
|
6
|
+
import { type WebChatFramework } from "./framework.js";
|
|
7
|
+
import { type WebChatHostingDeps } from "./hosting.js";
|
|
6
8
|
import { provisionWebChatAuth } from "./provision-auth.js";
|
|
7
|
-
import { detectPackageManager, type PackageManagerKind } from "#setup/package-manager.js";
|
|
8
|
-
import { pathExists, writeTextFile } from "#setup/scaffold/files.js";
|
|
9
9
|
import { type SetupApplyContext, type SetupPrepareContext } from "../types.js";
|
|
10
|
-
export interface WebSetupDeps {
|
|
11
|
-
detectPackageManager: typeof detectPackageManager;
|
|
12
|
-
pathExists: typeof pathExists;
|
|
13
|
-
readTextFile(path: string): Promise<string>;
|
|
14
|
-
resolveEveProjectContext: typeof resolveEveProjectContext;
|
|
15
|
-
writeTextFile: typeof writeTextFile;
|
|
10
|
+
export interface WebSetupDeps extends WebChatHostingDeps {
|
|
16
11
|
prepareWebAuthScaffold: typeof prepareWebAuthScaffold;
|
|
17
12
|
provisionWebChatAuth: typeof provisionWebChatAuth;
|
|
18
13
|
installScaffoldDependencies: typeof installScaffoldDependencies;
|
|
19
14
|
}
|
|
20
15
|
interface WebSetupPlan {
|
|
21
|
-
|
|
16
|
+
framework: WebChatFramework;
|
|
17
|
+
hosting: WebChatFramework | "vercel";
|
|
22
18
|
packageManager: PackageManagerKind;
|
|
23
19
|
authProject?: VercelProjectReference;
|
|
24
20
|
rootWebChat?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{defineSetupIntegration}from"../types.js";import{installScaffoldDependencies}from"../shared/scaffold.js";import{prepareWebAuthScaffold}from"./auth-scaffold.js";import{WEB_AUTHENTICATION_QUESTION}from"./auth-options.js";import{
|
|
1
|
+
import{defineSetupIntegration}from"../types.js";import{installScaffoldDependencies}from"../shared/scaffold.js";import{prepareWebAuthScaffold}from"./auth-scaffold.js";import{WEB_AUTHENTICATION_QUESTION}from"./auth-options.js";import{WEB_CHAT_FRAMEWORK_LABELS,detectWebChatFramework}from"./framework.js";import{defaultWebChatHostingDeps,peerServiceVercelConfig,prepareWebChatHosting,resolveWebChatProject,runScriptCommand}from"./hosting.js";import{provisionWebChatAuth}from"./provision-auth.js";import{join}from"node:path";import{select}from"#setup/ask.js";import{WEB_APP_TANSTACK_TEMPLATE_FILES}from"#setup/scaffold/create/web-template.js";const NEXT_HOSTED_CONFIG=`import type { NextConfig } from "next";
|
|
2
2
|
import { withEve } from "eve/next";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
|
|
@@ -11,24 +11,30 @@ export default withEve(nextConfig, { eveRoot });
|
|
|
11
11
|
const nextConfig: NextConfig = {};
|
|
12
12
|
|
|
13
13
|
export default nextConfig;
|
|
14
|
-
`,
|
|
15
|
-
|
|
16
|
-
export default await withEve({
|
|
17
|
-
services: {
|
|
18
|
-
web: {
|
|
19
|
-
framework: "nextjs",
|
|
20
|
-
root: "apps/web",
|
|
21
|
-
buildCommand: "node ../../node_modules/next/dist/bin/next build",
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
routes: [
|
|
25
|
-
{ src: "^(.*)$", destination: { type: "service", service: "web" } },
|
|
26
|
-
],
|
|
27
|
-
});
|
|
28
|
-
`,defaultDeps={detectPackageManager,pathExists,readTextFile:e=>readFile(e,`utf8`),resolveEveProjectContext,writeTextFile,prepareWebAuthScaffold,provisionWebChatAuth,installScaffoldDependencies};async function prepareWebSetup(e,t=defaultDeps){let n=await t.resolveEveProjectContext(e.appRoot);if(n.kind===`workspace`)throw Error(`Web Chat setup requires a selected workspace agent.`);let r=await t.pathExists(join(n.environmentRoot,`app`,`eve-agent.ts`))&&!await t.pathExists(join(n.environmentRoot,`apps`,`web`,`app`,`eve-agent.ts`)),i=r?`next`:await e.asker.ask(select({key:`web-hosting`,message:`How should Web Chat and your agents be deployed?`,options:[{id:`vercel`,label:`Vercel services`,hint:`(Recommended) Web Chat and agents deploy as separate services.`,value:`vercel`},{id:`next`,label:`Next.js`,hint:`One Next.js app serves Web Chat and routes agent requests.`,value:`next`}],recommended:`vercel`,required:!0})),a=await e.asker.ask(WEB_AUTHENTICATION_QUESTION)===`vercel`?await e.resolveVercelProject(`Web Chat sign-in`):void 0,o={hosting:i,packageManager:(await t.detectPackageManager(n.environmentRoot)).kind};return a!==void 0&&(o.authProject=a),r&&(o.rootWebChat=!0),o}function runScriptCommand(e,t){switch(e){case`npm`:return`npm run ${t}`;case`pnpm`:return`pnpm ${t}`;case`yarn`:return`yarn ${t}`;case`bun`:return`bun run ${t}`}}async function configurePeerServiceScripts(e,t){let n=join(e,`package.json`),r=JSON.parse(await t.readTextFile(n)),i={...r.scripts};i.dev??=`eve dev`,i[`dev:eve`]??=`eve dev`,i[`dev:all`]??=`vercel dev --local`,await t.writeTextFile(n,`${JSON.stringify({...r,scripts:i},null,2)}\n`,{force:!0})}async function assertInstallerOwned(e,t){try{let n=await readFile(e,`utf8`);if(t.includes(n))return}catch(e){if(e.code===`ENOENT`)return;throw e}throw Error(`Could not configure Web Chat because ${e} contains authored configuration. Preserve it and compose the eve integration manually.`)}async function applyWebSetup(e,t,n=defaultDeps){let r=await n.resolveEveProjectContext(t.appRoot);if(r.kind===`workspace`)throw Error(`Web Chat setup requires a selected workspace agent.`);let i=r.kind===`workspace-member`?r.member.appRoot:r.appRoot,a=join(i,`agent`,`channels`,`eve.ts`),o=e.rootWebChat?r.environmentRoot:join(r.environmentRoot,`apps`,`web`),s=e.authProject===void 0?void 0:await n.prepareWebAuthScaffold({environmentRoot:r.environmentRoot,agentAppRoot:i,webRoot:o,force:t.force});s===void 0&&(t.force||!await n.pathExists(a))&&await n.writeTextFile(a,WEB_CHANNEL_TEMPLATES.default,{force:t.force});let c=r.kind===`workspace-member`?r.member.name:void 0;await n.writeTextFile(join(o,`app`,`eve-agent.ts`),`/** Named workspace agent selected by the Web Chat installer. */\nexport const WEB_CHAT_AGENT: string | undefined = ${c===void 0?`undefined`:JSON.stringify(c)};\n`,{force:!0});let l=join(o,`next.config.ts`),u=`import type { NextConfig } from "next";
|
|
14
|
+
`,REGISTRY_NEXT_CONFIG=`import type { NextConfig } from "next";
|
|
29
15
|
import { withEve } from "eve/next";
|
|
30
16
|
|
|
31
17
|
const nextConfig: NextConfig = {};
|
|
32
18
|
|
|
33
19
|
export default withEve(nextConfig);
|
|
34
|
-
|
|
20
|
+
`,NEXT_PEER_SERVICE_VERCEL_CONFIG=peerServiceVercelConfig(`nextjs`,`node ../../node_modules/next/dist/bin/next build`),LEGACY_NEXT_PEER_SERVICE_VERCEL_CONFIG=NEXT_PEER_SERVICE_VERCEL_CONFIG.replace(/ web: \{[^}]+\},/,` web: { framework: "nextjs", root: "apps/web" },`),TANSTACK_REGISTRY_VITE_CONFIG=WEB_APP_TANSTACK_TEMPLATE_FILES[`vite.config.ts`],TANSTACK_HOSTED_VITE_CONFIG=`import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
21
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
22
|
+
import viteReact from "@vitejs/plugin-react";
|
|
23
|
+
import { eveTanStack } from "eve/tanstack";
|
|
24
|
+
import { nitro } from "nitro/vite";
|
|
25
|
+
import { fileURLToPath } from "node:url";
|
|
26
|
+
import { defineConfig } from "vite";
|
|
27
|
+
|
|
28
|
+
const eveRoot = fileURLToPath(new URL("../..", import.meta.url));
|
|
29
|
+
|
|
30
|
+
export default defineConfig({
|
|
31
|
+
resolve: { tsconfigPaths: true },
|
|
32
|
+
plugins: [
|
|
33
|
+
eveTanStack({ eveRoot }),
|
|
34
|
+
tailwindcss(),
|
|
35
|
+
tanstackStart({ srcDirectory: "app" }),
|
|
36
|
+
viteReact(),
|
|
37
|
+
nitro(),
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
`,TANSTACK_PEER_SERVICE_VERCEL_CONFIG=peerServiceVercelConfig(`tanstack-start`,`node ../../node_modules/vite/bin/vite.js build`),INSTALLER_VERCEL_CONFIGS=[NEXT_PEER_SERVICE_VERCEL_CONFIG,LEGACY_NEXT_PEER_SERVICE_VERCEL_CONFIG,TANSTACK_PEER_SERVICE_VERCEL_CONFIG],defaultWebSetupDeps={...defaultWebChatHostingDeps,prepareWebAuthScaffold,provisionWebChatAuth,installScaffoldDependencies};function hostingQuestion(e){let t=WEB_CHAT_FRAMEWORK_LABELS[e];return select({key:`web-hosting`,message:`How should Web Chat and your agents be deployed?`,options:[{id:`vercel`,label:`Vercel services`,hint:`(Recommended) Web Chat and agents deploy as separate services.`,value:`vercel`},{id:e,label:t,hint:`One ${t} app serves Web Chat and routes agent requests.`,value:e}],recommended:`vercel`,required:!0})}async function prepareWebSetup(e,t=defaultWebSetupDeps){let n=await resolveWebChatProject(e.appRoot,t),r=await detectWebChatFramework(join(n.environmentRoot,`apps`,`web`),t.pathExists)??`next`,i=r===`next`&&await t.pathExists(join(n.environmentRoot,`app`,`eve-agent.ts`))&&!await t.pathExists(join(n.environmentRoot,`apps`,`web`,`app`,`eve-agent.ts`)),a=i?`next`:r===`tanstack`&&n.agentName!==void 0?`vercel`:await e.asker.ask(hostingQuestion(r)),o=(r===`next`?await e.asker.ask(WEB_AUTHENTICATION_QUESTION):`custom`)===`vercel`?await e.resolveVercelProject(`Web Chat sign-in`):void 0,s={framework:r,hosting:a,packageManager:(await t.detectPackageManager(n.environmentRoot)).kind};return o!==void 0&&(s.authProject=o),i&&(s.rootWebChat=!0),s}function hostConfig(e,t){let n=e.hosting===`vercel`;if(e.framework===`tanstack`)return{path:join(t,`vite.config.ts`),source:n?TANSTACK_REGISTRY_VITE_CONFIG:TANSTACK_HOSTED_VITE_CONFIG,owned:[TANSTACK_REGISTRY_VITE_CONFIG,TANSTACK_HOSTED_VITE_CONFIG]};let r=e.rootWebChat?REGISTRY_NEXT_CONFIG:NEXT_HOSTED_CONFIG;return{path:join(t,`next.config.ts`),source:n?PEER_SERVICE_NEXT_CONFIG:r,owned:[REGISTRY_NEXT_CONFIG,NEXT_HOSTED_CONFIG,PEER_SERVICE_NEXT_CONFIG]}}async function applyWebSetup(e,t,n=defaultWebSetupDeps){let r=await resolveWebChatProject(t.appRoot,n),i=e.rootWebChat?r.environmentRoot:join(r.environmentRoot,`apps`,`web`),a=e.authProject===void 0?void 0:await n.prepareWebAuthScaffold({environmentRoot:r.environmentRoot,agentAppRoot:r.agentAppRoot,webRoot:i,force:t.force}),o=e.hosting===`vercel`,s=e.framework===`tanstack`?TANSTACK_PEER_SERVICE_VERCEL_CONFIG:NEXT_PEER_SERVICE_VERCEL_CONFIG;await(await prepareWebChatHosting({project:r,webRoot:i,force:t.force,writeChannel:a===void 0,hostConfig:hostConfig(e,i),vercelServices:o,vercelConfigs:[s,...INSTALLER_VERCEL_CONFIGS.filter(e=>e!==s)]},n))();let c=o?`dev:all`:e.rootWebChat?`dev`:`dev:web`;e.authProject!==void 0&&a!==void 0&&(await n.provisionWebChatAuth(e.authProject,t.signal),t.signal?.throwIfAborted(),await a(),await n.installScaffoldDependencies({changed:!0,log:t.presenter.log,projectPath:r.environmentRoot,signal:t.signal}),t.presenter.log.success(`Configured Sign in with Vercel for this project's team`),t.presenter.nextSteps(["Local setup is complete. Run `eve deploy` to publish these changes. Production and preview credentials are configured.",`Local development continues to use localDev() without signing in.`])),t.presenter.log.success(`Configured channel: web`);let l={facts:[{label:``,value:`Start locally with \`${runScriptCommand(e.packageManager,c)}\`.`}]};return e.authProject!==void 0&&(l.deploymentRequired=!0),l}const WEB_SETUP=defineSetupIntegration({kind:`web`,label:`Web Chat`,hint:`Browser-based chat interface`,prepare:prepareWebSetup,apply:applyWebSetup});export{WEB_SETUP,applyWebSetup,prepareWebSetup};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{DEFAULT_CONNECT_PACKAGE_VERSION,resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_CHANNEL_TEMPLATES}from"./web-template.js";import{AGENT_INSTRUCTIONS_TEMPLATE}from"./instructions-template.js";import{SCAFFOLDED_AGENT_PATHS}from"./agent-paths.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.128`,DEFAULT_ZOD_PACKAGE_VERSION=`4.5.4`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.76.
|
|
1
|
+
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{DEFAULT_CONNECT_PACKAGE_VERSION,resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_CHANNEL_TEMPLATES}from"./web-template.js";import{AGENT_INSTRUCTIONS_TEMPLATE}from"./instructions-template.js";import{SCAFFOLDED_AGENT_PATHS}from"./agent-paths.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.128`,DEFAULT_ZOD_PACKAGE_VERSION=`4.5.4`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.76.3`,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,t){return{[SCAFFOLDED_AGENT_PATHS.config]:renderAgentTemplate(e,t),[SCAFFOLDED_AGENT_PATHS.channel]:WEB_CHANNEL_TEMPLATES.default,[SCAFFOLDED_AGENT_PATHS.instructions]:AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,t){let n=parseChatGptModelSelection(e);return n===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(n)}),\n${reasoningTemplateLine(t)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,t){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(t.model)!==void 0?renderAgentTemplate(t.model,t.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t.reasoning)).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__",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export declare const WEB_APP_TEMPLATE_FILES: {
|
|
2
|
-
readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport type { UserContent } from "ai";\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon, BrainIcon, PlusIcon, SquareIcon } from "lucide-react";\nimport { useState } from "react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n ConversationTopFade,\n} from "@/components/ai-elements/conversation";\nimport { Message, MessageContent } from "@/components/ai-elements/message";\nimport {\n PromptInput,\n PromptInputButton,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n usePromptInputAttachments,\n} from "@/components/ai-elements/prompt-input";\nimport { Shimmer } from "@/components/ai-elements/shimmer";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\nimport { WEB_CHAT_AGENT } from "@/app/eve-agent";\n\nconst DEFAULT_AGENT_NAME = "__EVE_INIT_APP_NAME__";\nconst AGENT_NAME = WEB_CHAT_AGENT ?? DEFAULT_AGENT_NAME;\n\nexport function AgentChat({\n sessionId,\n sessionless = false,\n}: {\n readonly sessionId?: string;\n readonly sessionless?: boolean;\n}) {\n const [cancellationError, setCancellationError] = useState<string>();\n const [hasInputText, setHasInputText] = useState(false);\n const agent = useEveAgent({\n agent: WEB_CHAT_AGENT,\n initialSession:\n sessionId === undefined\n ? undefined\n : {\n sessionId,\n streamIndex: 0,\n },\n resume: sessionId !== undefined,\n onSessionChange(session) {\n if (sessionId === undefined && session !== undefined) {\n // Next patches window.history to navigate, which would detach the active stream.\n History.prototype.replaceState.call(\n window.history,\n window.history.state,\n "",\n `/s/${encodeURIComponent(session.sessionId)}`,\n );\n }\n },\n });\n\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isResuming = agent.status === "resuming";\n const isEmpty = agent.data.messages.length === 0;\n const lastMessage = agent.data.messages.at(-1);\n const isPendingAssistantShell =\n lastMessage?.role === "assistant" &&\n lastMessage.parts.every((part) => part.type === "step-start");\n const showPendingThinking =\n isBusy &&\n (agent.status === "submitted" || lastMessage?.role !== "assistant" || isPendingAssistantShell);\n const turnFailure = isBusy || isResuming ? undefined : getLatestTurnFailure(agent.events);\n const errorMessage = cancellationError ?? agent.error?.message ?? turnFailure;\n const hasConversationContent = sessionless || !isEmpty || errorMessage !== undefined;\n const showConversationLayout = isResuming || hasConversationContent;\n const activeSessionId = sessionId ?? agent.session?.sessionId;\n // Answers are accepted while work runs; the server\'s settlement closes each request.\n const canRespond = (requestId: string) =>\n !isResuming && agent.data.inputs[requestId]?.status === "open";\n // A tool call can ask several questions at once, and its message part holds only the latest.\n const questionsFor = (callId: string) =>\n Object.values(agent.data.inputs).filter(\n (input) => input.request.kind === "question" && input.request.action.callId === callId,\n );\n\n const requestCancellation = () => {\n setCancellationError(undefined);\n void agent.cancel().catch((error: unknown) => {\n setCancellationError(toErrorMessage(error));\n });\n };\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if ((text.length === 0 && message.files.length === 0) || isResuming) return;\n\n setHasInputText(false);\n setCancellationError(undefined);\n const options = isBusy ? { turnPolicy: "steer" as const } : undefined;\n\n if (message.files.length === 0) {\n await agent.send(text, options);\n return;\n }\n\n const parts: UserContent = [];\n if (text.length > 0) {\n parts.push({ text, type: "text" });\n }\n for (const file of message.files) {\n parts.push({\n data: file.url,\n filename: file.filename,\n mediaType: file.mediaType,\n type: "file",\n });\n }\n\n await agent.send(parts, options);\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea\n disabled={isResuming}\n onChange={(event) => setHasInputText(event.currentTarget.value.trim().length > 0)}\n placeholder="Send a message…"\n />\n <ComposerAction\n hasInputText={hasInputText}\n isBusy={isBusy}\n isResuming={isResuming}\n onCancel={requestCancellation}\n />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {showConversationLayout ? (\n <ChatHeader canStartNewChat={activeSessionId !== undefined} />\n ) : null}\n\n {showConversationLayout ? (\n <Conversation\n className="min-h-0 flex-1"\n initial={sessionId === undefined ? undefined : false}\n resize={activeSessionId === undefined ? "smooth" : "instant"}\n scrollRestorationKey={\n isEmpty || activeSessionId === undefined\n ? undefined\n : `eve:web-chat-scroll:${activeSessionId}`\n }\n >\n <ConversationTopFade className="top-14" />\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 pt-20 pb-36 sm:px-6">\n {agent.data.messages.map((message, index) =>\n showPendingThinking &&\n isPendingAssistantShell &&\n message.id === lastMessage.id ? null : (\n <AgentMessage\n canRespond={canRespond}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n questionsFor={questionsFor}\n onInputResponses={(inputResponses) => {\n setCancellationError(undefined);\n return agent.respond(inputResponses);\n }}\n />\n ),\n )}\n {showPendingThinking ? <PendingThinking /> : null}\n {errorMessage ? <ErrorMessage message={errorMessage} /> : null}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n ) : null}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n showConversationLayout\n ? "fixed bottom-0 left-1/2 z-20 max-w-3xl -translate-x-1/2 bg-gradient-to-t from-background via-background to-transparent pt-4 pb-6"\n : "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]",\n )}\n >\n {showConversationLayout ? null : (\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 )}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction ComposerAction({\n hasInputText,\n isBusy,\n isResuming,\n onCancel,\n}: {\n readonly hasInputText: boolean;\n readonly isBusy: boolean;\n readonly isResuming: boolean;\n readonly onCancel: () => void;\n}) {\n const attachments = usePromptInputAttachments();\n const canSubmit = hasInputText || attachments.files.length > 0;\n\n if (!isBusy || canSubmit) {\n return <PromptInputSubmit disabled={isResuming} />;\n }\n\n return (\n <PromptInputButton\n aria-label="Stop"\n className="absolute right-2.5 bottom-2.5"\n onClick={onCancel}\n variant="outline"\n >\n <SquareIcon className="size-3 fill-current" />\n </PromptInputButton>\n );\n}\n\nfunction ErrorMessage({ message }: { readonly message: string }) {\n return (\n <Message className="max-w-full" from="assistant">\n <MessageContent>\n <div\n className="flex w-full items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm"\n role="alert"\n >\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">{message}</p>\n </div>\n </div>\n </MessageContent>\n </Message>\n );\n}\n\nfunction ChatHeader({ canStartNewChat }: { readonly canStartNewChat: boolean }) {\n return (\n <header className="pointer-events-none fixed top-0 right-0 left-0 z-20 h-14">\n <div className="relative mx-auto flex h-full w-full max-w-3xl items-center justify-center bg-background px-24">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n {canStartNewChat ? (\n <Button\n aria-label="Start a new chat"\n className="pointer-events-auto fixed top-3 right-6 pr-4"\n onClick={() => window.location.assign("/s")}\n size="sm"\n type="button"\n variant="ghost"\n >\n <PlusIcon className="size-4" />\n <span className="hidden font-normal text-sm sm:inline">New chat</span>\n </Button>\n ) : null}\n </div>\n </header>\n );\n}\n\nfunction PendingThinking() {\n return (\n <Message aria-live="polite" from="assistant">\n <MessageContent>\n <div className="mb-4 flex w-full items-center gap-2 text-muted-foreground text-sm">\n <BrainIcon className="size-4" />\n <Shimmer duration={1}>Thinking</Shimmer>\n </div>\n </MessageContent>\n </Message>\n );\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : "Unable to cancel the response.";\n}\n\nfunction getLatestTurnFailure(\n events: ReturnType<typeof useEveAgent>["events"],\n): string | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n\n if (event.type === "turn.failed") {\n return event.data.code === "MODEL_CALL_FAILED"\n ? "The model is temporarily unavailable. Please try again."\n : event.data.message;\n }\n\n if (event.type === "turn.completed" || event.type === "turn.cancelled") {\n return undefined;\n }\n\n if (event.type === "message.received") {\n return undefined;\n }\n }\n\n return undefined;\n}\n';
|
|
2
|
+
readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport type { UserContent } from "ai";\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon, BrainIcon, PlusIcon, SquareIcon } from "lucide-react";\nimport { useState } from "react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n ConversationTopFade,\n} from "@/components/ai-elements/conversation";\nimport { Message, MessageContent } from "@/components/ai-elements/message";\nimport {\n PromptInput,\n PromptInputButton,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n usePromptInputAttachments,\n} from "@/components/ai-elements/prompt-input";\nimport { Shimmer } from "@/components/ai-elements/shimmer";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\nimport { WEB_CHAT_AGENT } from "@/app/eve-agent";\n\nconst DEFAULT_AGENT_NAME = "__EVE_INIT_APP_NAME__";\nconst AGENT_NAME = WEB_CHAT_AGENT ?? DEFAULT_AGENT_NAME;\n\nexport function AgentChat({\n sessionId,\n sessionless = false,\n}: {\n readonly sessionId?: string;\n readonly sessionless?: boolean;\n}) {\n const [cancellationError, setCancellationError] = useState<string>();\n const [hasInputText, setHasInputText] = useState(false);\n const agent = useEveAgent({\n agent: WEB_CHAT_AGENT,\n initialSession:\n sessionId === undefined\n ? undefined\n : {\n sessionId,\n streamIndex: 0,\n },\n resume: sessionId !== undefined,\n onSessionChange(session) {\n if (sessionId === undefined && session !== undefined) {\n // Framework routers (Next.js, TanStack Router) patch window.history; navigating\n // through them would remount this route and detach the active stream.\n History.prototype.replaceState.call(\n window.history,\n window.history.state,\n "",\n `/s/${encodeURIComponent(session.sessionId)}`,\n );\n }\n },\n });\n\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isResuming = agent.status === "resuming";\n const isEmpty = agent.data.messages.length === 0;\n const lastMessage = agent.data.messages.at(-1);\n const isPendingAssistantShell =\n lastMessage?.role === "assistant" &&\n lastMessage.parts.every((part) => part.type === "step-start");\n const showPendingThinking =\n isBusy &&\n (agent.status === "submitted" || lastMessage?.role !== "assistant" || isPendingAssistantShell);\n const turnFailure = isBusy || isResuming ? undefined : getLatestTurnFailure(agent.events);\n const errorMessage = cancellationError ?? agent.error?.message ?? turnFailure;\n const hasConversationContent = sessionless || !isEmpty || errorMessage !== undefined;\n const showConversationLayout = isResuming || hasConversationContent;\n const activeSessionId = sessionId ?? agent.session?.sessionId;\n // Answers are accepted while work runs; the server\'s settlement closes each request.\n const canRespond = (requestId: string) =>\n !isResuming && agent.data.inputs[requestId]?.status === "open";\n // A tool call can ask several questions at once, and its message part holds only the latest.\n const questionsFor = (callId: string) =>\n Object.values(agent.data.inputs).filter(\n (input) => input.request.kind === "question" && input.request.action.callId === callId,\n );\n\n const requestCancellation = () => {\n setCancellationError(undefined);\n void agent.cancel().catch((error: unknown) => {\n setCancellationError(toErrorMessage(error));\n });\n };\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if ((text.length === 0 && message.files.length === 0) || isResuming) return;\n\n setHasInputText(false);\n setCancellationError(undefined);\n const options = isBusy ? { turnPolicy: "steer" as const } : undefined;\n\n if (message.files.length === 0) {\n await agent.send(text, options);\n return;\n }\n\n const parts: UserContent = [];\n if (text.length > 0) {\n parts.push({ text, type: "text" });\n }\n for (const file of message.files) {\n parts.push({\n data: file.url,\n filename: file.filename,\n mediaType: file.mediaType,\n type: "file",\n });\n }\n\n await agent.send(parts, options);\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea\n disabled={isResuming}\n onChange={(event) => setHasInputText(event.currentTarget.value.trim().length > 0)}\n placeholder="Send a message…"\n />\n <ComposerAction\n hasInputText={hasInputText}\n isBusy={isBusy}\n isResuming={isResuming}\n onCancel={requestCancellation}\n />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {showConversationLayout ? (\n <ChatHeader canStartNewChat={activeSessionId !== undefined} />\n ) : null}\n\n {showConversationLayout ? (\n <Conversation\n className="min-h-0 flex-1"\n initial={sessionId === undefined ? undefined : false}\n resize={activeSessionId === undefined ? "smooth" : "instant"}\n scrollRestorationKey={\n isEmpty || activeSessionId === undefined\n ? undefined\n : `eve:web-chat-scroll:${activeSessionId}`\n }\n >\n <ConversationTopFade className="top-14" />\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 pt-20 pb-36 sm:px-6">\n {agent.data.messages.map((message, index) =>\n showPendingThinking &&\n isPendingAssistantShell &&\n message.id === lastMessage.id ? null : (\n <AgentMessage\n canRespond={canRespond}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n questionsFor={questionsFor}\n onInputResponses={(inputResponses) => {\n setCancellationError(undefined);\n return agent.respond(inputResponses);\n }}\n />\n ),\n )}\n {showPendingThinking ? <PendingThinking /> : null}\n {errorMessage ? <ErrorMessage message={errorMessage} /> : null}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n ) : null}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n showConversationLayout\n ? "fixed bottom-0 left-1/2 z-20 max-w-3xl -translate-x-1/2 bg-gradient-to-t from-background via-background to-transparent pt-4 pb-6"\n : "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]",\n )}\n >\n {showConversationLayout ? null : (\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 )}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction ComposerAction({\n hasInputText,\n isBusy,\n isResuming,\n onCancel,\n}: {\n readonly hasInputText: boolean;\n readonly isBusy: boolean;\n readonly isResuming: boolean;\n readonly onCancel: () => void;\n}) {\n const attachments = usePromptInputAttachments();\n const canSubmit = hasInputText || attachments.files.length > 0;\n\n if (!isBusy || canSubmit) {\n return <PromptInputSubmit disabled={isResuming} />;\n }\n\n return (\n <PromptInputButton\n aria-label="Stop"\n className="absolute right-2.5 bottom-2.5"\n onClick={onCancel}\n variant="outline"\n >\n <SquareIcon className="size-3 fill-current" />\n </PromptInputButton>\n );\n}\n\nfunction ErrorMessage({ message }: { readonly message: string }) {\n return (\n <Message className="max-w-full" from="assistant">\n <MessageContent>\n <div\n className="flex w-full items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm"\n role="alert"\n >\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">{message}</p>\n </div>\n </div>\n </MessageContent>\n </Message>\n );\n}\n\nfunction ChatHeader({ canStartNewChat }: { readonly canStartNewChat: boolean }) {\n return (\n <header className="pointer-events-none fixed top-0 right-0 left-0 z-20 h-14">\n <div className="relative mx-auto flex h-full w-full max-w-3xl items-center justify-center bg-background px-24">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n {canStartNewChat ? (\n <Button\n aria-label="Start a new chat"\n className="pointer-events-auto fixed top-3 right-6 pr-4"\n onClick={() => window.location.assign("/s")}\n size="sm"\n type="button"\n variant="ghost"\n >\n <PlusIcon className="size-4" />\n <span className="hidden font-normal text-sm sm:inline">New chat</span>\n </Button>\n ) : null}\n </div>\n </header>\n );\n}\n\nfunction PendingThinking() {\n return (\n <Message aria-live="polite" from="assistant">\n <MessageContent>\n <div className="mb-4 flex w-full items-center gap-2 text-muted-foreground text-sm">\n <BrainIcon className="size-4" />\n <Shimmer duration={1}>Thinking</Shimmer>\n </div>\n </MessageContent>\n </Message>\n );\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : "Unable to cancel the response.";\n}\n\nfunction getLatestTurnFailure(\n events: ReturnType<typeof useEveAgent>["events"],\n): string | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n\n if (event.type === "turn.failed") {\n return event.data.code === "MODEL_CALL_FAILED"\n ? "The model is temporarily unavailable. Please try again."\n : event.data.message;\n }\n\n if (event.type === "turn.completed" || event.type === "turn.cancelled") {\n return undefined;\n }\n\n if (event.type === "message.received") {\n return undefined;\n }\n }\n\n return undefined;\n}\n';
|
|
3
3
|
readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type {\n ConversationInput,\n EveAuthorizationPart,\n EveDynamicToolPart,\n EveMessage,\n EveMessageInputRequest,\n EveMessagePart,\n} from "eve/react";\nimport type { FormEvent } from "react";\nimport {\n CheckCircleIcon,\n ExternalLinkIcon,\n FileIcon,\n ImageIcon,\n KeyRoundIcon,\n XCircleIcon,\n} from "lucide-react";\nimport { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";\nimport {\n BashToolContent,\n Tool,\n ToolContent,\n ToolHeader,\n ToolInput,\n ToolOutput,\n} from "@/components/ai-elements/tool";\nimport { Button } from "@/components/ui/button";\nimport {\n Questionnaire,\n QuestionnaireActions,\n QuestionnaireChoice,\n QuestionnaireChoiceDescription,\n QuestionnaireChoices,\n QuestionnaireError,\n QuestionnaireInput,\n QuestionnaireItem,\n QuestionnaireSubmit,\n QuestionnaireTitle,\n} from "@/components/ui/questionnaire";\nimport { cn } from "@/lib/utils";\n\nexport type AgentInputResponse = {\n readonly optionId?: string;\n readonly requestId: string;\n readonly text?: string;\n};\n\ntype EveFilePart = Extract<EveMessagePart, { type: "file" }>;\n\nexport function AgentMessage({\n canRespond,\n isStreaming,\n message,\n onInputResponses,\n questionsFor,\n}: {\n readonly canRespond: (requestId: string) => boolean;\n readonly isStreaming: boolean;\n readonly message: EveMessage;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly questionsFor: (callId: string) => readonly ConversationInput[];\n}) {\n const lastTextIndex = message.parts.reduce(\n (last, part, index) => (part.type === "text" ? index : last),\n -1,\n );\n const hasAssistantText =\n message.role === "assistant" &&\n message.parts.some((part) => part.type === "text" && part.text.length > 0);\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 hasAssistantText && part.type === "reasoning" ? null : (\n <AgentMessagePart\n canRespond={canRespond}\n key={partKey(part, index)}\n onInputResponses={onInputResponses}\n part={part}\n questionsFor={questionsFor}\n showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}\n />\n ),\n )}\n </MessageContent>\n </Message>\n );\n}\n\nfunction AgentMessagePart({\n canRespond,\n onInputResponses,\n part,\n questionsFor,\n showCaret,\n}: {\n readonly canRespond: (requestId: string) => boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveMessagePart;\n readonly questionsFor: (callId: string) => readonly ConversationInput[];\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 "file":\n return <AttachmentPart part={part} />;\n case "authorization":\n return <AuthorizationPrompt part={part} />;\n case "dynamic-tool": {\n const questions = questionsFor(part.toolCallId);\n if (questions.length > 0) {\n return (\n <div className="space-y-4">\n {questions.map(({ request, response }) => (\n <QuestionRequest\n canRespond={canRespond(request.requestId)}\n inputRequest={request}\n inputResponse={response}\n key={request.requestId}\n onInputResponses={onInputResponses}\n />\n ))}\n </div>\n );\n }\n\n return (\n <>\n <Tool>\n <ToolHeader\n state={part.state}\n title={part.toolMetadata?.eve?.label ?? part.toolName}\n toolName={part.toolName}\n type="dynamic-tool"\n />\n <ToolContent>\n {part.toolName === "bash" ? (\n <BashToolContent\n errorText={part.errorText}\n input={part.input}\n output={part.output}\n />\n ) : (\n <>\n <ToolInput input={part.input} />\n <ToolOutput errorText={part.errorText} output={part.output} />\n </>\n )}\n </ToolContent>\n </Tool>\n {/* Kept outside the collapsed card so a pending approval is always visible. */}\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n </>\n );\n }\n }\n}\n\nfunction QuestionRequest({\n canRespond,\n inputRequest,\n inputResponse,\n onInputResponses,\n}: {\n readonly canRespond: boolean;\n readonly inputRequest: EveMessageInputRequest;\n readonly inputResponse?: AgentInputResponse;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n const options = inputRequest.options ?? [];\n const acceptsFreeform = inputRequest.allowFreeform === true || options.length === 0;\n const disabled = !canRespond || inputResponse !== undefined;\n\n const submitResponse = (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const answer = new FormData(event.currentTarget).get(inputRequest.requestId);\n if (typeof answer !== "string") {\n return;\n }\n\n const option = options.find(({ id }) => id === answer);\n void onInputResponses([\n option === undefined\n ? { requestId: inputRequest.requestId, text: answer.trim() }\n : { optionId: option.id, requestId: inputRequest.requestId },\n ]);\n };\n\n return (\n <Questionnaire\n className="gap-4 rounded-xl border bg-card p-4"\n // Validation reads disabled answers as missing, so locking a submitted form in place would\n // flag it invalid. Remounting restores the answer from the response instead.\n key={disabled ? "locked" : "open"}\n onSubmit={submitResponse}\n shortcuts="numbers"\n >\n <QuestionnaireItem name={inputRequest.requestId} required>\n <QuestionnaireTitle className="text-sm font-medium">\n {inputRequest.prompt}\n </QuestionnaireTitle>\n <QuestionnaireChoices>\n {options.map((option) => (\n <QuestionnaireChoice\n className={cn(inputResponse?.optionId === option.id && "data-disabled:opacity-100")}\n defaultChecked={inputResponse?.optionId === option.id}\n disabled={disabled}\n key={option.id}\n value={option.id}\n >\n {option.label}\n {option.description ? (\n <QuestionnaireChoiceDescription>\n {option.description}\n </QuestionnaireChoiceDescription>\n ) : null}\n </QuestionnaireChoice>\n ))}\n {acceptsFreeform ? (\n <QuestionnaireInput\n aria-label="Answer"\n className={cn(inputResponse?.text !== undefined && "disabled:opacity-100")}\n defaultValue={inputResponse?.text}\n disabled={disabled}\n placeholder="Type your answer…"\n />\n ) : null}\n </QuestionnaireChoices>\n <QuestionnaireError />\n </QuestionnaireItem>\n {disabled ? null : (\n <QuestionnaireActions>\n <QuestionnaireSubmit size="sm">Answer</QuestionnaireSubmit>\n </QuestionnaireActions>\n )}\n </Questionnaire>\n );\n}\n\nfunction AttachmentPart({ part }: { readonly part: EveFilePart }) {\n const label = part.filename ?? "Attachment";\n const detail = [part.mediaType, formatBytes(part.size)].filter(Boolean).join(" - ");\n const isImage = part.mediaType.startsWith("image/") && part.url !== undefined;\n const Icon = isImage ? ImageIcon : FileIcon;\n const body = (\n <span className="flex max-w-sm items-center gap-3 rounded-md border bg-background/60 p-2 text-sm">\n {isImage ? (\n <img alt={label} className="size-12 shrink-0 rounded-sm object-cover" src={part.url} />\n ) : (\n <span className="flex size-10 shrink-0 items-center justify-center rounded-sm bg-muted text-muted-foreground">\n <Icon className="size-4" />\n </span>\n )}\n <span className="min-w-0 flex-1">\n <span className="block truncate font-medium">{label}</span>\n {detail ? <span className="block truncate text-muted-foreground">{detail}</span> : null}\n </span>\n {part.url ? <ExternalLinkIcon className="size-4 shrink-0 text-muted-foreground" /> : null}\n </span>\n );\n\n return part.url ? (\n <a href={part.url} rel="noreferrer" target="_blank">\n {body}\n </a>\n ) : (\n body\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 formatBytes(size: number | undefined): string | undefined {\n if (size === undefined) {\n return undefined;\n }\n if (size < 1024) {\n return `${size} B`;\n }\n if (size < 1024 * 1024) {\n return `${(size / 1024).toFixed(1)} KB`;\n }\n return `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction InputRequestActions({\n canRespond,\n onInputResponses,\n part,\n}: {\n readonly canRespond: (requestId: string) => 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 // An approval can settle before its batch resolves, without an input response.\n const settledApproval =\n part.approval?.approved === undefined\n ? undefined\n : part.approval.approved\n ? "Approved"\n : "Denied";\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 || settledApproval ? (\n <p className="font-medium text-sm">\n Responded:{" "}\n {selectedOption?.label ??\n inputResponse?.text ??\n inputResponse?.optionId ??\n settledApproval}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond(inputRequest.requestId)}\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 part.attemptId === undefined\n ? `authorization:${part.turnId}:${part.stepIndex}:${part.name}`\n : `authorization:${part.attemptId}`;\n case "dynamic-tool":\n return part.toolCallId;\n case "reasoning":\n case "text":\n return `${part.type}:${part.id ?? index}`;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
|
|
4
4
|
readonly "app/apple-icon.tsx": 'import { ImageResponse } from "next/og";\n\nexport const size = {\n width: 180,\n height: 180,\n};\n\nexport const contentType = "image/png";\n\nexport default function AppleIcon() {\n return new ImageResponse(\n <svg fill="none" viewBox="0 0 102 102" xmlns="http://www.w3.org/2000/svg">\n <path d="M0 0h102v102H0z" fill="#000" />\n <path\n d="M49.28 66.94 75.03 34.96h-6.89L47.91 60.11l-5.49 6.83h6.86ZM0 34.96h42.4v5.11H0zm0 13.32h27.66v5.11H0zm0 13.54h27.66v5.11H0zm69.63-26.86H102v5.11H69.63zm4.71 13.32H102v5.11H74.34zm0 13.54H102v5.11H74.34z"\n fill="#fff"\n />\n </svg>,\n size,\n );\n}\n';
|
|
5
5
|
readonly "app/eve-agent.ts": '/** Named workspace agent selected by the Web Chat installer. */\nexport const WEB_CHAT_AGENT: string | undefined = undefined;\n';
|
|
6
|
-
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 -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\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
|
+
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}\n\n/* Not inline: font utilities must read the variables so each host\'s font loader can override them. */\n@theme {\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 -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\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';
|
|
7
7
|
readonly "app/icon.svg": '<svg width="102" height="102" viewBox="0 0 102 102" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path fill="#000" d="M0 0h102v102H0z" />\n <path\n fill="#fff"\n d="M49.28 66.94 75.03 34.96h-6.89L47.91 60.11l-5.49 6.83h6.86ZM0 34.96h42.4v5.11H0zm0 13.32h27.66v5.11H0zm0 13.54h27.66v5.11H0zm69.63-26.86H102v5.11H69.63zm4.71 13.32H102v5.11H74.34zm0 13.54H102v5.11H74.34z"\n />\n</svg>\n';
|
|
8
8
|
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';
|
|
9
9
|
readonly "app/page.tsx": 'import { AgentChat } from "@/app/_components/agent-chat";\n\nexport default function Page() {\n return <AgentChat />;\n}\n';
|
|
@@ -57,6 +57,10 @@ export declare const WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES: {
|
|
|
57
57
|
readonly "lib/auth-client.ts": '"use client";\n\nimport { createAuthClient } from "better-auth/react";\n\nexport const authClient = createAuthClient();\n';
|
|
58
58
|
readonly "lib/auth.ts": 'import { betterAuth } from "better-auth";\n\nconst SESSION_MAX_AGE_SECONDS = 8 * 60 * 60;\nconst DEVELOPMENT_ALLOWED_HOSTS = ["localhost:*", "127.0.0.1:*"];\n\nfunction getAllowedHosts(): string[] {\n if (process.env.NODE_ENV === "development") {\n return DEVELOPMENT_ALLOWED_HOSTS;\n }\n const deploymentHosts = [\n process.env.VERCEL_URL,\n process.env.VERCEL_BRANCH_URL,\n process.env.VERCEL_PROJECT_PRODUCTION_URL,\n ].filter((host): host is string => Boolean(host));\n if (deploymentHosts.length === 0) {\n throw new Error("No trusted deployment hosts are configured");\n }\n return Array.from(new Set(deploymentHosts));\n}\n\nfunction requireEnvironmentVariable(name: string): string {\n const value = process.env[name];\n if (value) return value;\n if (process.env.NODE_ENV === "development") return `development-${name}`;\n throw new Error(`Missing required environment variable: ${name}`);\n}\n\nexport const auth = betterAuth({\n baseURL: {\n allowedHosts: getAllowedHosts(),\n protocol: process.env.NODE_ENV === "development" ? "auto" : "https",\n },\n secret: requireEnvironmentVariable("BETTER_AUTH_SECRET"),\n session: {\n expiresIn: SESSION_MAX_AGE_SECONDS,\n disableSessionRefresh: true,\n cookieCache: {\n enabled: true,\n maxAge: SESSION_MAX_AGE_SECONDS,\n refreshCache: false,\n strategy: "jwe",\n },\n },\n socialProviders: {\n vercel: {\n clientId: requireEnvironmentVariable("VERCEL_APP_CLIENT_ID"),\n clientSecret: requireEnvironmentVariable("VERCEL_APP_CLIENT_SECRET"),\n scope: ["openid", "email", "profile"],\n },\n },\n});\n';
|
|
59
59
|
};
|
|
60
|
+
export declare const WEB_APP_TANSTACK_TEMPLATE_FILES: {
|
|
61
|
+
readonly "tsconfig.json": '{\n "$schema": "https://json.schemastore.org/tsconfig",\n "compilerOptions": {\n "target": "ES2022",\n "lib": ["dom", "dom.iterable", "esnext"],\n "skipLibCheck": true,\n "strict": true,\n "noEmit": true,\n "esModuleInterop": true,\n "module": "esnext",\n "moduleResolution": "Bundler",\n "resolveJsonModule": true,\n "isolatedModules": true,\n "jsx": "react-jsx",\n "types": ["vite/client"],\n "paths": {\n "@/*": ["./*"]\n }\n },\n "include": ["**/*.ts", "**/*.tsx"],\n "exclude": ["node_modules", ".output", ".vercel"]\n}\n';
|
|
62
|
+
readonly "vite.config.ts": 'import { tanstackStart } from "@tanstack/react-start/plugin/vite";\nimport tailwindcss from "@tailwindcss/vite";\nimport viteReact from "@vitejs/plugin-react";\nimport { nitro } from "nitro/vite";\nimport { defineConfig } from "vite";\n\nexport default defineConfig({\n resolve: { tsconfigPaths: true },\n plugins: [tailwindcss(), tanstackStart({ srcDirectory: "app" }), viteReact(), nitro()],\n});\n';
|
|
63
|
+
};
|
|
60
64
|
export declare const WEB_APP_TEMPLATE_PACKAGE_JSON: {
|
|
61
65
|
readonly scripts: {
|
|
62
66
|
readonly build: "next build";
|
|
@@ -49,7 +49,8 @@ export function AgentChat({
|
|
|
49
49
|
resume: sessionId !== undefined,
|
|
50
50
|
onSessionChange(session) {
|
|
51
51
|
if (sessionId === undefined && session !== undefined) {
|
|
52
|
-
//
|
|
52
|
+
// Framework routers (Next.js, TanStack Router) patch window.history; navigating
|
|
53
|
+
// through them would remount this route and detach the active stream.
|
|
53
54
|
History.prototype.replaceState.call(
|
|
54
55
|
window.history,
|
|
55
56
|
window.history.state,
|
|
@@ -841,6 +842,10 @@ export const WEB_CHAT_AGENT: string | undefined = undefined;
|
|
|
841
842
|
--radius-md: calc(var(--radius) - 2px);
|
|
842
843
|
--radius-lg: var(--radius);
|
|
843
844
|
--radius-xl: calc(var(--radius) + 4px);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/* Not inline: font utilities must read the variables so each host's font loader can override them. */
|
|
848
|
+
@theme {
|
|
844
849
|
--font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
|
|
845
850
|
--font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;
|
|
846
851
|
}
|
|
@@ -6115,4 +6120,36 @@ export const auth = betterAuth({
|
|
|
6115
6120
|
},
|
|
6116
6121
|
},
|
|
6117
6122
|
});
|
|
6118
|
-
`},
|
|
6123
|
+
`},WEB_APP_TANSTACK_TEMPLATE_FILES={"tsconfig.json":`{
|
|
6124
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
6125
|
+
"compilerOptions": {
|
|
6126
|
+
"target": "ES2022",
|
|
6127
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
6128
|
+
"skipLibCheck": true,
|
|
6129
|
+
"strict": true,
|
|
6130
|
+
"noEmit": true,
|
|
6131
|
+
"esModuleInterop": true,
|
|
6132
|
+
"module": "esnext",
|
|
6133
|
+
"moduleResolution": "Bundler",
|
|
6134
|
+
"resolveJsonModule": true,
|
|
6135
|
+
"isolatedModules": true,
|
|
6136
|
+
"jsx": "react-jsx",
|
|
6137
|
+
"types": ["vite/client"],
|
|
6138
|
+
"paths": {
|
|
6139
|
+
"@/*": ["./*"]
|
|
6140
|
+
}
|
|
6141
|
+
},
|
|
6142
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
6143
|
+
"exclude": ["node_modules", ".output", ".vercel"]
|
|
6144
|
+
}
|
|
6145
|
+
`,"vite.config.ts":`import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
6146
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
6147
|
+
import viteReact from "@vitejs/plugin-react";
|
|
6148
|
+
import { nitro } from "nitro/vite";
|
|
6149
|
+
import { defineConfig } from "vite";
|
|
6150
|
+
|
|
6151
|
+
export default defineConfig({
|
|
6152
|
+
resolve: { tsconfigPaths: true },
|
|
6153
|
+
plugins: [tailwindcss(), tanstackStart({ srcDirectory: "app" }), viteReact(), nitro()],
|
|
6154
|
+
});
|
|
6155
|
+
`},WEB_APP_TEMPLATE_PACKAGE_JSON={scripts:{build:`next build`,"build:eve":`eve build`,dev:`next dev`,"dev:eve":`eve dev`,start:`next start`,"start:eve":`eve start`,typecheck:`tsc --noEmit -p tsconfig.json`},dependencies:{"@radix-ui/react-use-controllable-state":`1.2.2`,"@shadcn/react":`0.3.1`,"@shikijs/core":`3.23.0`,"@shikijs/engine-javascript":`3.23.0`,"@shikijs/engine-oniguruma":`3.23.0`,"@streamdown/cjk":`1.0.3`,"@streamdown/code":`1.1.1`,"@streamdown/math":`1.0.2`,"@streamdown/mermaid":`1.0.2`,"@tailwindcss/postcss":`4.3.0`,"class-variance-authority":`0.7.1`,clsx:`2.1.1`,cmdk:`1.1.1`,"lucide-react":`1.16.0`,motion:`12.40.0`,nanoid:`5.1.11`,next:`16.3.6`,"radix-ui":`1.4.3`,react:`19.2.6`,"react-dom":`19.2.6`,shiki:`3.23.0`,streamdown:`2.5.0`,"tailwind-merge":`3.6.0`,tailwindcss:`4.3.0`,"use-stick-to-bottom":`1.1.4`,zod:`4.5.4`},devDependencies:{"@types/node":`26`,"@types/react":`19.2.15`,"@types/react-dom":`19.2.3`,typescript:`6.0.3`}};export{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TANSTACK_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON,WEB_CHANNEL_TEMPLATES};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{appendEnv}from"../../append-env.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON,WEB_CHANNEL_TEMPLATES}from"../create/web-template.js";import{resolveEvePackageContract}from"../create/project.js";import{resolveWebPackageVersions}from"./web-options.js";import{readFile,readdir,unlink,writeFile}from"node:fs/promises";import{basename,join,resolve}from"node:path";const SLACK_CHANNEL_DEFAULT_ROUTE=`/eve/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,NEXT_PACKAGE_NAME=`next`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`],WEB_CHANNEL_PATH=`agent/channels/eve.ts`,WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH);function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readPackageJsonObject(e){if(!await pathExists(e))return;let t=JSON.parse(await readFile(e,`utf8`));return isJsonObject(t)?t:void 0}async function readDependencyVersion(e,t){let n=await readPackageJsonObject(e);if(n===void 0||!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){let n=await readPackageJsonObject(e);return n!==void 0&&packageJsonHasDependency(n,t)}const VERCEL_HOST_FRAMEWORK_PRESETS={"@sveltejs/kit":`sveltekit`,[NEXT_PACKAGE_NAME]:`nextjs`,nuxt:`nuxtjs`,nuxt3:`nuxtjs`,"nuxt-edge":`nuxtjs`,"nuxt-nightly":`nuxtjs`};async function resolveVercelHostFrameworkPreset(e){if(await pathExists(join(e,`vercel.ts`)))return`services`;let t=await readPackageJsonObject(join(e,`package.json`));if(t!==void 0){for(let[e,n]of Object.entries(VERCEL_HOST_FRAMEWORK_PRESETS))if(packageJsonHasDependency(t,e))return n}}async function hasVercelHostFramework(e){return await resolveVercelHostFrameworkPreset(e)!==void 0}async function isNextJsProject(e){return hasPackageDependency(join(e,`package.json`),NEXT_PACKAGE_NAME)}async function ensurePackageDependency(e,t,n){return!await pathExists(e)||await readDependencyVersion(e,t)===n?[]:(await patchPackageJson(e,{dependencies:{[t]:n}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t,n,r,i,a=!0,o){let s=join(e,`package.json`);if(!await pathExists(s))return{mutations:[]};let c=resolveEvePackageContract(r.evePackage),l=pinnedNodeEngineMajor(c.nodeEngine),u={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:resolveVersionToken(`aiPackageVersion`,r.aiPackageVersion),eve:formatEveDependencySpecifier(c.version),next:resolveVersionToken(`nextPackageVersion`,r.nextPackageVersion),react:resolveVersionToken(`reactPackageVersion`,r.reactPackageVersion),"react-dom":resolveVersionToken(`reactDomPackageVersion`,r.reactDomPackageVersion),streamdown:resolveVersionToken(`streamdownPackageVersion`,r.streamdownPackageVersion),zod:resolveVersionToken(`zodPackageVersion`,r.zodPackageVersion),...o===`sign-in-with-vercel`?{"better-auth":resolveVersionToken(`betterAuthPackageVersion`,r.betterAuthPackageVersion)}:{}},d={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":l,"@types/react":resolveVersionToken(`typesReactPackageVersion`,r.typesReactPackageVersion),"@types/react-dom":resolveVersionToken(`typesReactDomPackageVersion`,r.typesReactDomPackageVersion),typescript:`6.0.3`},f=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts,p=isPackageManagerWorkspaceMember(t,n),m={scripts:f};a&&(m.dependencies=u,m.devDependencies=d),p||(m.nodeEngineRequirement=c.nodeEngine);let h=await patchPackageJson(s,m),g=(await patchWorkspaceRootPackageJson(t,n,{nodeEngineRequirement:c.nodeEngine,onWorkspaceRootMutation:i})).nodeEngineOverride??h.nodeEngineOverride;return{mutations:[{path:s,dependencies:a?Object.keys(u):[],devDependencies:a?Object.keys(d):[],scripts:Object.keys(f)}],nodeEngineOverride:g}}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 buildSlackConnectTemplate(e){if(!e.startsWith(`slack/`)||e.length===6)throw Error(`Invalid Slack connector UID "${e}".`);return`import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
1
|
+
import{appendEnv}from"../../append-env.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_SIGN_IN_WITH_VERCEL_TEMPLATE_FILES,WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON,WEB_CHANNEL_TEMPLATES}from"../create/web-template.js";import{resolveEvePackageContract}from"../create/project.js";import{resolveWebPackageVersions}from"./web-options.js";import{readFile,readdir,unlink,writeFile}from"node:fs/promises";import{basename,join,resolve}from"node:path";const SLACK_CHANNEL_DEFAULT_ROUTE=`/eve/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,NEXT_PACKAGE_NAME=`next`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`],WEB_CHANNEL_PATH=`agent/channels/eve.ts`,WEB_NEXT_CONFIG_PATH=`next.config.ts`,WEB_COMPETING_NEXT_CONFIG_PATHS=[`next.config.js`,`next.config.mjs`,WEB_NEXT_CONFIG_PATH,`next.config.mts`].filter(e=>e!==WEB_NEXT_CONFIG_PATH);function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readPackageJsonObject(e){if(!await pathExists(e))return;let t=JSON.parse(await readFile(e,`utf8`));return isJsonObject(t)?t:void 0}async function readDependencyVersion(e,t){let n=await readPackageJsonObject(e);if(n===void 0||!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){let n=await readPackageJsonObject(e);return n!==void 0&&packageJsonHasDependency(n,t)}const VERCEL_HOST_FRAMEWORK_PRESETS={"@sveltejs/kit":`sveltekit`,"@tanstack/react-start":`tanstack-start`,"@tanstack/solid-start":`tanstack-start`,[NEXT_PACKAGE_NAME]:`nextjs`,nuxt:`nuxtjs`,nuxt3:`nuxtjs`,"nuxt-edge":`nuxtjs`,"nuxt-nightly":`nuxtjs`};async function resolveVercelHostFrameworkPreset(e){if(await pathExists(join(e,`vercel.ts`)))return`services`;let t=await readPackageJsonObject(join(e,`package.json`));if(t!==void 0){for(let[e,n]of Object.entries(VERCEL_HOST_FRAMEWORK_PRESETS))if(packageJsonHasDependency(t,e))return n}}async function hasVercelHostFramework(e){return await resolveVercelHostFrameworkPreset(e)!==void 0}async function isNextJsProject(e){return hasPackageDependency(join(e,`package.json`),NEXT_PACKAGE_NAME)}async function ensurePackageDependency(e,t,n){return!await pathExists(e)||await readDependencyVersion(e,t)===n?[]:(await patchPackageJson(e,{dependencies:{[t]:n}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t,n,r,i,a=!0,o){let s=join(e,`package.json`);if(!await pathExists(s))return{mutations:[]};let c=resolveEvePackageContract(r.evePackage),l=pinnedNodeEngineMajor(c.nodeEngine),u={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:resolveVersionToken(`aiPackageVersion`,r.aiPackageVersion),eve:formatEveDependencySpecifier(c.version),next:resolveVersionToken(`nextPackageVersion`,r.nextPackageVersion),react:resolveVersionToken(`reactPackageVersion`,r.reactPackageVersion),"react-dom":resolveVersionToken(`reactDomPackageVersion`,r.reactDomPackageVersion),streamdown:resolveVersionToken(`streamdownPackageVersion`,r.streamdownPackageVersion),zod:resolveVersionToken(`zodPackageVersion`,r.zodPackageVersion),...o===`sign-in-with-vercel`?{"better-auth":resolveVersionToken(`betterAuthPackageVersion`,r.betterAuthPackageVersion)}:{}},d={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":l,"@types/react":resolveVersionToken(`typesReactPackageVersion`,r.typesReactPackageVersion),"@types/react-dom":resolveVersionToken(`typesReactDomPackageVersion`,r.typesReactDomPackageVersion),typescript:`6.0.3`},f=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts,p=isPackageManagerWorkspaceMember(t,n),m={scripts:f};a&&(m.dependencies=u,m.devDependencies=d),p||(m.nodeEngineRequirement=c.nodeEngine);let h=await patchPackageJson(s,m),g=(await patchWorkspaceRootPackageJson(t,n,{nodeEngineRequirement:c.nodeEngine,onWorkspaceRootMutation:i})).nodeEngineOverride??h.nodeEngineOverride;return{mutations:[{path:s,dependencies:a?Object.keys(u):[],devDependencies:a?Object.keys(d):[],scripts:Object.keys(f)}],nodeEngineOverride:g}}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 buildSlackConnectTemplate(e){if(!e.startsWith(`slack/`)||e.length===6)throw Error(`Invalid Slack connector UID "${e}".`);return`import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
2
2
|
import { slackChannel } from "eve/channels/slack";
|
|
3
3
|
|
|
4
4
|
export default slackChannel({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readProjectLink}from"./project-resolution.js";import{resolveVercelHostFrameworkPreset}from"./scaffold/update/channels.js";import"./scaffold/index.js";import{isForbiddenApiFailure,normalizeVercelApiResult}from"./vercel-api-failure.js";import{VERCEL_PROJECT_REQUEST_TIMEOUT_MS,parseVercelJson,requireVercelTeamAccess}from"./vercel-project-api.js";import*as fs from"node:fs/promises";import{z}from"#compiled/zod/index.js";import{extname,join}from"node:path";import{captureVercel}from"#setup/primitives/index.js";import"#setup/cli/index.js";const VercelProjectFrameworkSchema=z.object({framework:z.string().min(1).nullish()}),EVE_FRAMEWORK_INTEGRATIONS={nextjs:{label:`Next.js`,importSpecifier:`eve/next`},nuxt:{label:`Nuxt`,importSpecifier:`eve/nuxt`},nuxtjs:{label:`Nuxt`,importSpecifier:`eve/nuxt`},sveltekit:{label:`SvelteKit`,importSpecifier:`eve/sveltekit`}},FRAMEWORK_INTEGRATION_SOURCE_EXTENSIONS=new Set([`.cjs`,`.cts`,`.js`,`.json`,`.jsx`,`.mjs`,`.mts`,`.svelte`,`.ts`,`.tsx`,`.vue`]),FRAMEWORK_INTEGRATION_IGNORED_DIRECTORIES=new Set([`.eve`,`.git`,`.next`,`.nuxt`,`.output`,`.svelte-kit`,`.turbo`,`.vercel`,`build`,`coverage`,`dist`,`node_modules`]);function parseProjectFramework(e,t){let n=VercelProjectFrameworkSchema.safeParse(parseVercelJson(e,t));if(!n.success)throw Error(`Could not read Vercel project framework from ${t}.`);return n.data.framework??void 0}async function fetchProjectFramework(e,t,n,r){let i=normalizeVercelApiResult(await captureVercel([`api`,`/v9/projects/${n}`,`--scope`,t,`--raw`],{cwd:e,signal:r.signal,timeoutMs:VERCEL_PROJECT_REQUEST_TIMEOUT_MS}));if(i.ok)return parseProjectFramework(i.stdout,`project ${n}`);throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not inspect Vercel project "${n}". ${i.failure.message}`)}async function setProjectFramework(e,t,n,r,i,a){let o=normalizeVercelApiResult(await captureVercel([`api`,`/v9/projects/${n}`,`--scope`,t,`--method`,`PATCH`,`--raw-field`,`framework=${r}`,`--raw`],{cwd:e,onOutput:i,signal:a.signal,timeoutMs:VERCEL_PROJECT_REQUEST_TIMEOUT_MS}));if(!o.ok)throw isForbiddenApiFailure(o.failure)&&requireVercelTeamAccess(o.failure),Error(`Could not set Vercel project "${n}" framework to ${r}. ${o.failure.message}`)}async function confirmDetectedFramework(e,t,n){return await e.select({message:`Vercel detected ${n.label}. Is this project using ${n.importSpecifier}?`,options:[{value:!0,label:`Yes, keep ${t}`,hint:`Use this for a host app that integrates eve through ${n.importSpecifier}.`},{value:!1,label:`No, switch to eve`,hint:`Use this for a standalone eve agent.`}]})?`keep`:`switch-to-eve`}function sourceMentionsPackageSpecifier(e,t){return e.includes(`"${t}"`)||e.includes(`'${t}'`)||e.includes(`\`${t}\``)}async function directoryContainsPackageSpecifier(e,t){let n;try{n=await fs.readdir(e,{encoding:`utf8`,withFileTypes:!0})}catch{return!1}for(let r of n){let n=join(e,r.name);if(r.isDirectory()){if(FRAMEWORK_INTEGRATION_IGNORED_DIRECTORIES.has(r.name))continue;if(await directoryContainsPackageSpecifier(n,t))return!0;continue}if(r.isFile()&&FRAMEWORK_INTEGRATION_SOURCE_EXTENSIONS.has(extname(r.name)))try{if(sourceMentionsPackageSpecifier(await fs.readFile(n,`utf8`),t))return!0}catch{continue}}return!1}async function detectFrameworkIntegrationImport(e,t){return directoryContainsPackageSpecifier(e,t)}async function resolveDetectedFrameworkAction(e,t,n,r){let i=EVE_FRAMEWORK_INTEGRATIONS[n];return i===void 0?`switch-to-eve`:await(r.detectFrameworkIntegrationImport??detectFrameworkIntegrationImport)(t,i.importSpecifier)?`keep`:r.headless?`switch-to-eve`:confirmDetectedFramework(e,n,i)}function frameworkPresetLabel(e){return EVE_FRAMEWORK_INTEGRATIONS[e]?.label??e}function describeError(e){return e instanceof Error?e.message:String(e)}async function syncHostFrameworkPreset(e,t,n,r){let i=await resolveVercelHostFrameworkPreset(t);if(i===void 0)return;let a=await readProjectLink(t);if(a===void 0)return;let o=frameworkPresetLabel(i),s;try{s=await fetchProjectFramework(t,a.orgId,a.projectId,r)}catch(t){if(r.signal?.aborted)throw t;e.log.warning(`Could not check this Vercel project's Framework Preset (${describeError(t)}). If it is not already ${o}, set it under Project Settings → Build and Deployment → Framework Settings before deploying.`);return}if(s===i)return;let c=s===void 0?void 0:frameworkPresetLabel(s),l=c===void 0?`had no Framework Preset set`:`was set to "${c}"`;try{await setProjectFramework(t,a.orgId,a.projectId,i,n,r)}catch(t){if(r.signal?.aborted)throw t;e.log.warning(`${describeError(t)} Set this Vercel project's Framework Preset to ${o} (Project Settings → Build and Deployment → Framework Settings) before deploying, or the deployment will not build your ${o} app.`);return}e.log.info(`This project uses ${o}, but its Vercel Framework Preset ${l}; switched it to ${o} so the deployment builds your app.`)}async function ensureCreatedProjectFramework(e,t,n,r,i,a){let o=await fetchProjectFramework(t,n,r,a);if(await resolveVercelHostFrameworkPreset(t)===`services`){o!==`services`&&await setProjectFramework(t,n,r,`services`,i,a);return}o!==`eve`&&(o===void 0?`switch-to-eve`:await resolveDetectedFrameworkAction(e,t,o,a))===`switch-to-eve`&&await setProjectFramework(t,n,r,`eve`,i,a)}export{ensureCreatedProjectFramework,syncHostFrameworkPreset};
|
|
1
|
+
import{readProjectLink}from"./project-resolution.js";import{resolveVercelHostFrameworkPreset}from"./scaffold/update/channels.js";import"./scaffold/index.js";import{isForbiddenApiFailure,normalizeVercelApiResult}from"./vercel-api-failure.js";import{VERCEL_PROJECT_REQUEST_TIMEOUT_MS,parseVercelJson,requireVercelTeamAccess}from"./vercel-project-api.js";import*as fs from"node:fs/promises";import{z}from"#compiled/zod/index.js";import{extname,join}from"node:path";import{captureVercel}from"#setup/primitives/index.js";import"#setup/cli/index.js";const VercelProjectFrameworkSchema=z.object({framework:z.string().min(1).nullish()}),EVE_FRAMEWORK_INTEGRATIONS={nextjs:{label:`Next.js`,importSpecifier:`eve/next`},nuxt:{label:`Nuxt`,importSpecifier:`eve/nuxt`},nuxtjs:{label:`Nuxt`,importSpecifier:`eve/nuxt`},sveltekit:{label:`SvelteKit`,importSpecifier:`eve/sveltekit`},"tanstack-start":{label:`TanStack Start`,importSpecifier:`eve/tanstack`}},FRAMEWORK_INTEGRATION_SOURCE_EXTENSIONS=new Set([`.cjs`,`.cts`,`.js`,`.json`,`.jsx`,`.mjs`,`.mts`,`.svelte`,`.ts`,`.tsx`,`.vue`]),FRAMEWORK_INTEGRATION_IGNORED_DIRECTORIES=new Set([`.eve`,`.git`,`.next`,`.nuxt`,`.output`,`.svelte-kit`,`.tanstack`,`.turbo`,`.vercel`,`build`,`coverage`,`dist`,`node_modules`]);function parseProjectFramework(e,t){let n=VercelProjectFrameworkSchema.safeParse(parseVercelJson(e,t));if(!n.success)throw Error(`Could not read Vercel project framework from ${t}.`);return n.data.framework??void 0}async function fetchProjectFramework(e,t,n,r){let i=normalizeVercelApiResult(await captureVercel([`api`,`/v9/projects/${n}`,`--scope`,t,`--raw`],{cwd:e,signal:r.signal,timeoutMs:VERCEL_PROJECT_REQUEST_TIMEOUT_MS}));if(i.ok)return parseProjectFramework(i.stdout,`project ${n}`);throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not inspect Vercel project "${n}". ${i.failure.message}`)}async function setProjectFramework(e,t,n,r,i,a){let o=normalizeVercelApiResult(await captureVercel([`api`,`/v9/projects/${n}`,`--scope`,t,`--method`,`PATCH`,`--raw-field`,`framework=${r}`,`--raw`],{cwd:e,onOutput:i,signal:a.signal,timeoutMs:VERCEL_PROJECT_REQUEST_TIMEOUT_MS}));if(!o.ok)throw isForbiddenApiFailure(o.failure)&&requireVercelTeamAccess(o.failure),Error(`Could not set Vercel project "${n}" framework to ${r}. ${o.failure.message}`)}async function confirmDetectedFramework(e,t,n){return await e.select({message:`Vercel detected ${n.label}. Is this project using ${n.importSpecifier}?`,options:[{value:!0,label:`Yes, keep ${t}`,hint:`Use this for a host app that integrates eve through ${n.importSpecifier}.`},{value:!1,label:`No, switch to eve`,hint:`Use this for a standalone eve agent.`}]})?`keep`:`switch-to-eve`}function sourceMentionsPackageSpecifier(e,t){return e.includes(`"${t}"`)||e.includes(`'${t}'`)||e.includes(`\`${t}\``)}async function directoryContainsPackageSpecifier(e,t){let n;try{n=await fs.readdir(e,{encoding:`utf8`,withFileTypes:!0})}catch{return!1}for(let r of n){let n=join(e,r.name);if(r.isDirectory()){if(FRAMEWORK_INTEGRATION_IGNORED_DIRECTORIES.has(r.name))continue;if(await directoryContainsPackageSpecifier(n,t))return!0;continue}if(r.isFile()&&FRAMEWORK_INTEGRATION_SOURCE_EXTENSIONS.has(extname(r.name)))try{if(sourceMentionsPackageSpecifier(await fs.readFile(n,`utf8`),t))return!0}catch{continue}}return!1}async function detectFrameworkIntegrationImport(e,t){return directoryContainsPackageSpecifier(e,t)}async function resolveDetectedFrameworkAction(e,t,n,r){let i=EVE_FRAMEWORK_INTEGRATIONS[n];return i===void 0?`switch-to-eve`:await(r.detectFrameworkIntegrationImport??detectFrameworkIntegrationImport)(t,i.importSpecifier)?`keep`:r.headless?`switch-to-eve`:confirmDetectedFramework(e,n,i)}function frameworkPresetLabel(e){return EVE_FRAMEWORK_INTEGRATIONS[e]?.label??e}function describeError(e){return e instanceof Error?e.message:String(e)}async function syncHostFrameworkPreset(e,t,n,r){let i=await resolveVercelHostFrameworkPreset(t);if(i===void 0)return;let a=await readProjectLink(t);if(a===void 0)return;let o=frameworkPresetLabel(i),s;try{s=await fetchProjectFramework(t,a.orgId,a.projectId,r)}catch(t){if(r.signal?.aborted)throw t;e.log.warning(`Could not check this Vercel project's Framework Preset (${describeError(t)}). If it is not already ${o}, set it under Project Settings → Build and Deployment → Framework Settings before deploying.`);return}if(s===i)return;let c=s===void 0?void 0:frameworkPresetLabel(s),l=c===void 0?`had no Framework Preset set`:`was set to "${c}"`;try{await setProjectFramework(t,a.orgId,a.projectId,i,n,r)}catch(t){if(r.signal?.aborted)throw t;e.log.warning(`${describeError(t)} Set this Vercel project's Framework Preset to ${o} (Project Settings → Build and Deployment → Framework Settings) before deploying, or the deployment will not build your ${o} app.`);return}e.log.info(`This project uses ${o}, but its Vercel Framework Preset ${l}; switched it to ${o} so the deployment builds your app.`)}async function ensureCreatedProjectFramework(e,t,n,r,i,a){let o=await fetchProjectFramework(t,n,r,a);if(await resolveVercelHostFrameworkPreset(t)===`services`){o!==`services`&&await setProjectFramework(t,n,r,`services`,i,a);return}o!==`eve`&&(o===void 0?`switch-to-eve`:await resolveDetectedFrameworkAction(e,t,o,a))===`switch-to-eve`&&await setProjectFramework(t,n,r,`eve`,i,a)}export{ensureCreatedProjectFramework,syncHostFrameworkPreset};
|
|
@@ -8,7 +8,31 @@ import { type DynamicResolveContext, type DynamicSentinel } from "#dynamic/defin
|
|
|
8
8
|
*/
|
|
9
9
|
export interface AgentModelOptionsDefinition {
|
|
10
10
|
readonly providerOptions?: Record<string, JsonObject>;
|
|
11
|
+
/** Prompt-cache breakpoints eve places for this model. */
|
|
12
|
+
readonly promptCache?: AgentPromptCacheDefinition;
|
|
11
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Prompt-cache breakpoints eve places for a model it calls directly. AI Gateway models cache
|
|
16
|
+
* through `providerOptions.gateway.caching` instead and reject this option.
|
|
17
|
+
*/
|
|
18
|
+
export interface AgentPromptCacheDefinition {
|
|
19
|
+
/**
|
|
20
|
+
* Places Anthropic cache breakpoints on every call. eve already does this for Anthropic models
|
|
21
|
+
* it recognizes by provider or model id; set it for one it can't, such as a Bedrock application
|
|
22
|
+
* inference profile, or to change the cache lifetime.
|
|
23
|
+
*/
|
|
24
|
+
readonly anthropic?: AgentAnthropicPromptCacheDefinition;
|
|
25
|
+
}
|
|
26
|
+
export interface AgentAnthropicPromptCacheDefinition {
|
|
27
|
+
/**
|
|
28
|
+
* How long each breakpoint's cache entry lives: `"5m"` (the default) or `"1h"`. A 1-hour write
|
|
29
|
+
* costs more than a 5-minute one, so `"1h"` pays off when turns often land between five minutes
|
|
30
|
+
* and an hour apart. Only some Claude models accept a 1-hour TTL.
|
|
31
|
+
*/
|
|
32
|
+
readonly ttl?: AnthropicPromptCacheTtl;
|
|
33
|
+
}
|
|
34
|
+
export type AnthropicPromptCacheTtl = "5m" | "1h";
|
|
35
|
+
export declare const ANTHROPIC_PROMPT_CACHE_TTLS: readonly AnthropicPromptCacheTtl[];
|
|
12
36
|
/**
|
|
13
37
|
* Provider-agnostic reasoning effort forwarded to the AI SDK model call.
|
|
14
38
|
*/
|
|
@@ -45,6 +69,7 @@ export type InternalAgentModelDefinition = {
|
|
|
45
69
|
maxOutputTokens?: number;
|
|
46
70
|
source?: ModuleSourceRef;
|
|
47
71
|
providerOptions?: Record<string, JsonObject>;
|
|
72
|
+
promptCache?: AgentPromptCacheDefinition;
|
|
48
73
|
};
|
|
49
74
|
/**
|
|
50
75
|
* A concrete model handle: an AI Gateway model id string or an AI SDK
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isDynamicSentinel}from"#dynamic/definition.js";function isDynamicModelDefinition(e){return isDynamicSentinel(e)}const AGENT_WORKFLOW_RETENTION_VALUES=[0,`default`];export{AGENT_WORKFLOW_RETENTION_VALUES,isDynamicModelDefinition};
|
|
1
|
+
import{isDynamicSentinel}from"#dynamic/definition.js";const ANTHROPIC_PROMPT_CACHE_TTLS=[`5m`,`1h`];function isDynamicModelDefinition(e){return isDynamicSentinel(e)}const AGENT_WORKFLOW_RETENTION_VALUES=[0,`default`];export{AGENT_WORKFLOW_RETENTION_VALUES,ANTHROPIC_PROMPT_CACHE_TTLS,isDynamicModelDefinition};
|
|
@@ -6,3 +6,11 @@ export declare function parseChatGptModelSelection(selection: string): string |
|
|
|
6
6
|
export declare function isChatGptModelRouting(routing: ModelRouting | null | undefined): boolean;
|
|
7
7
|
/** Normalizes the model argument accepted by the public `chatgpt()` helper. */
|
|
8
8
|
export declare function normalizeChatGptModelId(model: string): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Codex has no `-fast` model slugs: it serves Fast mode as the `priority`
|
|
11
|
+
* service tier on the base model and rejects `gpt-6-luna-fast` outright.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseChatGptFastModelId(modelId: string): {
|
|
14
|
+
slug: string;
|
|
15
|
+
fast: boolean;
|
|
16
|
+
};
|