eve 0.13.6 → 0.13.8
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 +16 -0
- package/dist/src/cli/banner.d.ts +2 -4
- package/dist/src/cli/banner.js +1 -1
- package/dist/src/cli/commands/agent-prompt/build-and-verify.md +7 -4
- package/dist/src/cli/dev/tui/agent-header.js +1 -1
- package/dist/src/context/build-dynamic-tools.js +1 -1
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +1 -0
- package/dist/src/execution/create-session-step.d.ts +0 -17
- package/dist/src/execution/create-session-step.js +1 -1
- package/dist/src/execution/eve-workflow-attributes.d.ts +1 -1
- package/dist/src/execution/sandbox/prewarm.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-runtime.d.ts +10 -2
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/connection.js +1 -1
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/runtime/attributes/emit.d.ts +2 -43
- package/dist/src/runtime/attributes/emit.js +1 -1
- package/dist/src/runtime/attributes/normalize.d.ts +17 -0
- package/dist/src/runtime/attributes/normalize.js +1 -0
- package/dist/src/runtime/connections/principal.js +1 -1
- package/dist/src/runtime/resolve-agent-graph.js +1 -1
- package/dist/src/runtime/sessions/compiled-agent-cache.js +1 -1
- package/dist/src/setup/primitives/run-vercel.d.ts +8 -0
- package/dist/src/setup/primitives/run-vercel.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +6 -2
- package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
- package/dist/src/setup/scaffold/create/web-template.js +0 -17
- package/docs/README.md +2 -2
- package/docs/channels/linear.mdx +1 -1
- package/docs/connections/mcp.mdx +61 -0
- package/docs/connections/meta.json +4 -0
- package/docs/connections/openapi.mdx +68 -0
- package/docs/{connections.mdx → connections/overview.mdx} +28 -63
- package/docs/getting-started.mdx +0 -6
- package/docs/introduction.mdx +2 -8
- package/docs/reference/typescript-api.md +20 -19
- package/docs/tutorial/connect-a-warehouse.mdx +3 -3
- package/docs/tutorial/ship-it.mdx +1 -1
- package/package.json +1 -1
|
@@ -1,47 +1,6 @@
|
|
|
1
1
|
import "#internal/workflow/builtins.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Local mirror of `ATTRIBUTE_VALUE_MAX_BYTES` from `@workflow/world`
|
|
6
|
-
* (source of truth: `packages/world/src/attributes.ts` in the workflow
|
|
7
|
-
* repo). The value is duplicated rather than imported because
|
|
8
|
-
* `@workflow/core` — the only workflow surface bundled into the
|
|
9
|
-
* workflow body — does not re-export it, and pulling `@workflow/world`
|
|
10
|
-
* into the body bundle would drag in the full zod attribute validator
|
|
11
|
-
* (the same reason `workflow-core-shim.ts` skips runtime validation in
|
|
12
|
-
* its `experimental_setAttributes`).
|
|
13
|
-
*
|
|
14
|
-
* Strings emitted through {@link setEveAttributes} are truncated to this
|
|
15
|
-
* byte count before they reach the runtime so the validator never
|
|
16
|
-
* rejects a tag for length alone.
|
|
17
|
-
*
|
|
18
|
-
* Drift is conservative-by-construction: if workflow LOWERS the limit,
|
|
19
|
-
* over-long values are rejected and `setEveAttributes` swallows the
|
|
20
|
-
* failure (warn-once-per-process) — dashboards see a missing tag, never
|
|
21
|
-
* a broken agent; if workflow RAISES it, titles are merely shorter than
|
|
22
|
-
* necessary. `emit.drift.test.ts` asserts equality against the real
|
|
23
|
-
* `@workflow/world` export (a devDependency) so CI fails loudly the day
|
|
24
|
-
* the constants diverge.
|
|
25
|
-
*/
|
|
26
|
-
export declare const EVE_ATTRIBUTE_VALUE_MAX_BYTES = 256;
|
|
27
|
-
/**
|
|
28
|
-
* Attribute value the caller wants to write. `undefined` values are
|
|
29
|
-
* stripped before the runtime call; numbers are stringified; strings
|
|
30
|
-
* are truncated to {@link EVE_ATTRIBUTE_VALUE_MAX_BYTES}.
|
|
31
|
-
*/
|
|
32
|
-
export type EveAttributeValue = string | number | undefined;
|
|
33
|
-
/**
|
|
34
|
-
* Truncates a string so its UTF-8 byte length is at most `maxBytes`
|
|
35
|
-
* without splitting a multi-byte character.
|
|
36
|
-
*
|
|
37
|
-
* The workflow runtime measures attribute values in UTF-8 bytes, not
|
|
38
|
-
* code units, so `value.slice(0, maxBytes)` is not safe — a JS string
|
|
39
|
-
* with two-byte characters (e.g. emoji surrogate pairs) can serialize
|
|
40
|
-
* to twice as many bytes as code units. We re-encode the truncated
|
|
41
|
-
* candidate after each drop and shrink one code unit at a time when
|
|
42
|
-
* the candidate's last character straddles the byte budget.
|
|
43
|
-
*/
|
|
44
|
-
export declare function truncateForTag(value: string, maxBytes?: number): string;
|
|
2
|
+
import { type EveAttributeValue } from "#runtime/attributes/normalize.js";
|
|
3
|
+
export { EVE_ATTRIBUTE_VALUE_MAX_BYTES, type EveAttributeValue, truncateForTag, } from "#runtime/attributes/normalize.js";
|
|
45
4
|
/**
|
|
46
5
|
* Writes a batch of eve-owned attributes to the active workflow run.
|
|
47
6
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"#internal/workflow/builtins.js";
|
|
1
|
+
import{EVE_ATTRIBUTE_VALUE_MAX_BYTES,normalizeEveAttributes,truncateForTag}from"#runtime/attributes/normalize.js";import"#internal/workflow/builtins.js";let WARNED_ABOUT_TAG_FAILURE=!1;async function setEveAttributes(e){let t=normalizeEveAttributes(e);if(Object.keys(t).length!==0)try{let{experimental_setAttributes:e}=await import(`#compiled/@workflow/core/index.js`);await e(t,{allowReservedAttributes:!0})}catch(e){WARNED_ABOUT_TAG_FAILURE||(WARNED_ABOUT_TAG_FAILURE=!0,console.warn(`[eve] setEveAttributes failed; suppressing further warnings this process.`,{keys:Object.keys(t),error:e.message}))}}export{EVE_ATTRIBUTE_VALUE_MAX_BYTES,setEveAttributes,truncateForTag};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum UTF-8 byte size for one Workflow run attribute value.
|
|
3
|
+
*
|
|
4
|
+
* Mirrored from `ATTRIBUTE_VALUE_MAX_BYTES` in `@workflow/world` so the
|
|
5
|
+
* normalization helper stays independent of the full world package.
|
|
6
|
+
* `emit.drift.test.ts` guards the mirror against upstream changes.
|
|
7
|
+
*/
|
|
8
|
+
export declare const EVE_ATTRIBUTE_VALUE_MAX_BYTES = 256;
|
|
9
|
+
/** Attribute value accepted by eve's internal attribute builders. */
|
|
10
|
+
export type EveAttributeValue = string | number | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Truncates a string to Workflow's UTF-8 byte budget without splitting
|
|
13
|
+
* a surrogate pair.
|
|
14
|
+
*/
|
|
15
|
+
export declare function truncateForTag(value: string, maxBytes?: number): string;
|
|
16
|
+
/** Normalizes sparse eve attributes into Workflow's string-only shape. */
|
|
17
|
+
export declare function normalizeEveAttributes(attrs: Record<string, EveAttributeValue>): Record<string, string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const EVE_ATTRIBUTE_VALUE_MAX_BYTES=256;function truncateForTag(e,t=256){if(t<=0)return``;let n=new TextEncoder;if(n.encode(e).length<=t)return e;let r=e.length;for(;r>0;){let i=e.charCodeAt(r-1);if(i>=55296&&i<=56319){--r;continue}let a=e.slice(0,r);if(n.encode(a).length<=t)return a;--r}return``}function normalizeEveAttributes(e){let t={};for(let[n,r]of Object.entries(e))r!==void 0&&(t[n]=truncateForTag(typeof r==`number`?String(r):r));return t}export{EVE_ATTRIBUTE_VALUE_MAX_BYTES,normalizeEveAttributes,truncateForTag};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AuthKey}from"#context/keys.js";import{contextStorage}from"#context/container.js";import{ConnectionAuthorizationFailedError}from"#public/connections/errors.js";function principalKey(e){return e.type===`app`?`app`:`user:${e.issuer}:${e.id}`}function resolveConnectionPrincipal(
|
|
1
|
+
import{AuthKey}from"#context/keys.js";import{contextStorage}from"#context/container.js";import{ConnectionAuthorizationFailedError}from"#public/connections/errors.js";function principalKey(e){return e.type===`app`?`app`:`user:${e.issuer}:${e.id}`}function resolveConnectionPrincipal(r,i,a=contextStorage.getStore()){if(i.principalType===`app`)return{type:`app`};let o=a?.get(AuthKey);if(o==null||o.principalType!==`user`)throw new ConnectionAuthorizationFailedError(r,{message:buildUserPrincipalRequiredMessage(r,a,o?.principalType),reason:`principal_required`,retryable:!1});return{attributes:o.attributes,id:o.principalId,issuer:o.issuer??o.authenticator,type:`user`}}function buildUserPrincipalRequiredMessage(e,t,n){let r;return r=t===void 0?`it was invoked outside an eve context, so no authenticated user can be resolved.`:n===void 0?`the active session has no authenticated user.`:`the active session is scoped to "${n}", not an authenticated user.`,`Connection "${e}" is user-scoped, but ${r} User-scoped connections require route auth that resolves an authenticated user. If this connection should use credentials shared by the agent instead, configure it as an app-scoped connection.`}export{principalKey,resolveConnectionPrincipal};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{expectObjectRecord}from"#internal/authored-module.js";import{ROOT_COMPILED_AGENT_NODE_ID}from"#compiler/manifest.js";import{LOAD_SKILL_TOOL_NAME}from"#runtime/skills/fragment-context.js";import{
|
|
1
|
+
import{expectObjectRecord}from"#internal/authored-module.js";import{ROOT_COMPILED_AGENT_NODE_ID}from"#compiler/manifest.js";import{LOAD_SKILL_TOOL_NAME}from"#runtime/skills/fragment-context.js";import{CODE_MODE_TOOL_NAME,WORKFLOW_TOOL_NAME}from"#shared/code-mode.js";import{createRuntimeToolRegistry}from"#runtime/tools/registry.js";import{createRuntimeSubagentRegistry}from"#runtime/subagents/registry.js";import{ROOT_RUNTIME_AGENT_NODE_ID}from"#runtime/graph.js";import{getAllFrameworkChannelNames,getFrameworkChannelDefinitions}from"#runtime/framework-channels/index.js";import{getAllFrameworkToolNames,getFrameworkToolDefinitions}from"#runtime/framework-tools/index.js";import{createConnectionSearchResolver}from"#runtime/framework-tools/connection-search-dynamic.js";import{resolveAgent}from"#runtime/resolve-agent.js";import{loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{createResolvedRuntimeTurnAgent}from"#runtime/agent/bootstrap.js";import{createRuntimeHookRegistry}from"#runtime/hooks/registry.js";import{createRuntimeSandboxRegistry}from"#runtime/sandbox/registry.js";var ResolveRuntimeAgentGraphError=class extends Error{logicalPath;nodeId;sourceId;constructor(e,t={}){super(e),this.name=`ResolveRuntimeAgentGraphError`,t.logicalPath!==void 0&&(this.logicalPath=t.logicalPath),t.nodeId!==void 0&&(this.nodeId=t.nodeId),t.sourceId!==void 0&&(this.sourceId=t.sourceId)}};async function resolveRuntimeAgentGraph(e){let n=new Map,r=createChildNodeIdsByParentNodeId(e.manifest),i=new Map(e.manifest.subagents.map(e=>[e.nodeId,e]));return{nodesByNodeId:n,root:await resolveRuntimeAgentNode({childNodeIdsByParentNodeId:r,manifest:e.manifest,moduleMap:e.moduleMap,nodeId:ROOT_COMPILED_AGENT_NODE_ID,nodesByNodeId:n,subagentNodesById:i})}}async function resolveRuntimeAgentNode(e){let t=toRuntimeNodeId(e.nodeId);if(e.nodesByNodeId.has(t))throw new ResolveRuntimeAgentGraphError(`Found multiple runtime agent nodes for node id "${t}".`,{nodeId:t,sourceId:e.sourceId});let a=await resolveAgent({manifest:e.manifest,moduleMap:e.moduleMap,nodeId:e.nodeId}),o=a.connections.length>0,s=getFrameworkToolDefinitions({hasConnections:o}),c=new Set(s.map(e=>e.name)),l=getAllFrameworkToolNames(),u=new Set(a.tools.map(e=>e.name));for(let n of a.disabledFrameworkTools)if(!l.has(n))throw new ResolveRuntimeAgentGraphError(`agent/tools/${n}.ts exports disableTool() but "${n}" is not a framework tool. Rename the file to one of: ${[...l].sort().join(`, `)}.`,{nodeId:t,sourceId:e.sourceId});let d=new Set(a.disabledFrameworkTools),f=await createRuntimeToolRegistry({tools:[...s.filter(e=>!u.has(e.name)&&!d.has(e.name)),...a.tools]},{reservedToolNames:[CODE_MODE_TOOL_NAME,WORKFLOW_TOOL_NAME,...c.has(LOAD_SKILL_TOOL_NAME)||u.has(LOAD_SKILL_TOOL_NAME)?[]:[LOAD_SKILL_TOOL_NAME]]}),p=new Set(a.channels.map(e=>e.name)),m=getAllFrameworkChannelNames();for(let n of a.disabledFrameworkChannels)if(!m.has(n))throw new ResolveRuntimeAgentGraphError(`agent/channels/${n}.ts exports disableRoute() but "${n}" is not a framework channel. Rename the file to one of: ${[...m].sort().join(`, `)}.`,{nodeId:t,sourceId:e.sourceId});let h=new Set(a.disabledFrameworkChannels),g=[...getFrameworkChannelDefinitions().filter(e=>!p.has(e.name)&&!h.has(e.name)),...a.channels],_=createRuntimeSandboxRegistry({authoredSandbox:a.sandbox,workspaceResourceRoot:a.workspaceResourceRoot}),v=createRuntimeSubagentRegistry({reservedToolNames:[LOAD_SKILL_TOOL_NAME,...f.preparedTools.map(e=>e.name)],subagents:await resolveRuntimeSubagents({childNodeIdsByParentNodeId:e.childNodeIdsByParentNodeId,manifest:e.manifest,moduleMap:e.moduleMap,nodesByNodeId:e.nodesByNodeId,parentNodeId:e.nodeId,subagentNodesById:e.subagentNodesById})}),y=o?{...a,dynamicToolResolvers:[...a.dynamicToolResolvers,createConnectionSearchResolver()]}:a,b={agent:y,channels:g,hookRegistry:createRuntimeHookRegistry(y.hooks),nodeId:t,sandboxRegistry:_,sourceId:e.sourceId,subagentRegistry:v,toolRegistry:f,turnAgent:createResolvedRuntimeTurnAgent({agent:y,nodeId:t,tools:[...f.preparedTools,...v.preparedTools]})};return e.nodesByNodeId.set(t,b),b}async function resolveRuntimeSubagents(e){let t=[],n=e.childNodeIdsByParentNodeId.get(e.parentNodeId)??[];for(let r of n){let n=e.subagentNodesById.get(r);if(n===void 0)throw new ResolveRuntimeAgentGraphError(`Missing compiled subagent node "${r}" while resolving runtime subagents.`,{nodeId:toRuntimeNodeId(e.parentNodeId),sourceId:r});t.push(await resolveRuntimeSubagent({childNodeIdsByParentNodeId:e.childNodeIdsByParentNodeId,moduleMap:e.moduleMap,nodesByNodeId:e.nodesByNodeId,sourceRef:n,subagentNodesById:e.subagentNodesById}))}for(let n of e.manifest.remoteAgents)t.push(await resolveRuntimeRemoteAgent({moduleMap:e.moduleMap,nodeScopeId:e.parentNodeId,sourceRef:n}));return t}async function resolveRuntimeSubagent(e){let t={description:e.sourceRef.description,kind:`subagent`,logicalPath:e.sourceRef.logicalPath,name:e.sourceRef.name,nodeId:toRuntimeNodeId(e.sourceRef.nodeId),sourceId:e.sourceRef.sourceId,sourceKind:`module`};return await resolveRuntimeAgentNode({childNodeIdsByParentNodeId:e.childNodeIdsByParentNodeId,manifest:e.sourceRef.agent,moduleMap:e.moduleMap,nodeId:e.sourceRef.nodeId,nodesByNodeId:e.nodesByNodeId,sourceId:e.sourceRef.sourceId,subagentNodesById:e.subagentNodesById}),t}async function resolveRuntimeRemoteAgent(t){let n=expectObjectRecord(await loadResolvedModuleExport({definition:t.sourceRef,kindLabel:`remote agent`,moduleMap:t.moduleMap,nodeId:t.nodeScopeId}),`Expected remote agent source "${t.sourceRef.logicalPath}" to export an object.`),r={description:t.sourceRef.description,kind:`remote`,logicalPath:t.sourceRef.logicalPath,name:t.sourceRef.name,nodeId:toRuntimeNodeId(t.sourceRef.nodeId),outputSchema:t.sourceRef.outputSchema,path:t.sourceRef.path,sourceId:t.sourceRef.sourceId,sourceKind:`module`,url:t.sourceRef.url};typeof n.auth==`function`&&(r.auth=n.auth);let i=resolveRemoteAgentHeaders(n.headers);return i!==void 0&&(r.headers=i),r}function resolveRemoteAgentHeaders(e){if(e===void 0)return;if(typeof e==`function`)return e;if(typeof e!=`object`||!e||Array.isArray(e))return;let t={};for(let[n,r]of Object.entries(e))typeof r==`string`&&(t[n]=r);return t}function createChildNodeIdsByParentNodeId(e){let t=new Map;for(let n of e.subagentEdges){let e=t.get(n.parentNodeId);if(e===void 0){t.set(n.parentNodeId,[n.childNodeId]);continue}e.push(n.childNodeId)}return t}function toRuntimeNodeId(e){return e===ROOT_COMPILED_AGENT_NODE_ID?ROOT_RUNTIME_AGENT_NODE_ID:e}export{resolveRuntimeAgentGraph};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolvePackageSourceFilePath}from"#internal/application/package.js";import{
|
|
1
|
+
import{resolvePackageSourceFilePath}from"#internal/application/package.js";import{getRuntimeCompiledArtifactsCacheKey}from"#runtime/compiled-artifacts-source.js";import{getResolvedRuntimeAgentNode}from"#runtime/graph.js";import{loadCompiledManifest}from"#runtime/loaders/manifest.js";import{resolveRuntimeAgentGraph}from"#runtime/resolve-agent-graph.js";import{pathToFileURL}from"node:url";import{loadCompiledModuleMap}from"#runtime/loaders/module-map.js";import{getActiveRuntimeSession}from"#runtime/sessions/runtime-session.js";import{resolveRuntimeCompiledArtifactsVersionedCacheKey}from"#runtime/cache-key.js";import{createRuntimeAdapterRegistry}from"#runtime/channels/registry.js";const isCacheDisabled=process.env.EVE_DISABLE_AGENT_CACHE===`1`;function isDevelopmentRuntimeSnapshotRoot(e){return e.replaceAll(`\\`,`/`).includes(`/.eve/dev-runtime/snapshots/`)}function normalizeCompiledArtifactsSource(t){return t.kind!==`disk`||t.moduleMapLoaderPath!==void 0||!isDevelopmentRuntimeSnapshotRoot(t.appRoot)?t:{...t,moduleMapLoaderPath:resolvePackageSourceFilePath(`src/internal/authored-module-map-loader.ts`)}}async function loadFullBundle(e){let t=normalizeCompiledArtifactsSource(e),[n,a]=await Promise.all([loadCompiledManifest({compiledArtifactsSource:t}),loadRuntimeCompiledModuleMap(t)]),o=await resolveRuntimeAgentGraph({manifest:n,moduleMap:a}),s=o.root;return{adapterRegistry:createRuntimeAdapterRegistry({channels:collectResolvedChannels(o)}),compiledArtifactsSource:t,graph:o,hookRegistry:s.hookRegistry,moduleMap:a,resolvedAgent:s.agent,subagentRegistry:s.subagentRegistry,toolRegistry:s.toolRegistry,turnAgent:s.turnAgent}}async function loadRuntimeCompiledModuleMap(e){return e.kind===`disk`&&e.moduleMapLoaderPath!==void 0?await loadAuthoredSourceCompiledModuleMap(e):await loadCompiledModuleMap({compiledArtifactsSource:e})}async function loadAuthoredSourceCompiledModuleMap(e){if(e.moduleMapLoaderPath===void 0)throw Error(`Authored-source module map loading requires "moduleMapLoaderPath" in the compiled artifacts source.`);return await(await import(pathToFileURL(e.moduleMapLoaderPath).href)).loadCompiledModuleMapFromAuthoredSource({compiledArtifactsSource:e})}async function getOrLoadFullBundle(e){let n=normalizeCompiledArtifactsSource(e);if(isCacheDisabled)return loadFullBundle(n);let r=getActiveRuntimeSession(),i=getRuntimeCompiledArtifactsCacheKey(n),a=await resolveRuntimeCompiledArtifactsVersionedCacheKey(n),o=r.bundleCacheKeyBySourceKey.get(i);o!==void 0&&o!==a&&r.bundleCache.delete(o),r.bundleCacheKeyBySourceKey.set(i,a);let c=r.bundleCache.get(a);if(c!==void 0)return c;let l=loadFullBundle(n).catch(e=>{throw r.bundleCache.delete(a),r.bundleCacheKeyBySourceKey.get(i)===a&&r.bundleCacheKeyBySourceKey.delete(i),e});return r.bundleCache.set(a,l),l}async function getCompiledRuntimeAgentBundle(e){let t=await getOrLoadFullBundle(e.compiledArtifactsSource);if(e.nodeId===void 0)return t;let r=getResolvedRuntimeAgentNode(t.graph,e.nodeId);return{adapterRegistry:t.adapterRegistry,compiledArtifactsSource:t.compiledArtifactsSource,graph:{nodesByNodeId:t.graph.nodesByNodeId,root:r},hookRegistry:r.hookRegistry,moduleMap:t.moduleMap,nodeId:e.nodeId,resolvedAgent:r.agent,subagentRegistry:r.subagentRegistry,toolRegistry:r.toolRegistry,turnAgent:r.turnAgent}}function clearCompiledRuntimeAgentBundleCache(){let e=getActiveRuntimeSession();e.bundleCache.clear(),e.bundleCacheKeyBySourceKey.clear()}function collectResolvedChannels(e){let t=new Map;for(let n of e.nodesByNodeId.values())for(let e of n.channels)t.set(`${e.sourceId}:${e.name}`,e);return[...t.values()]}export{clearCompiledRuntimeAgentBundleCache,getCompiledRuntimeAgentBundle};
|
|
@@ -17,6 +17,13 @@ export interface RunVercelOptions {
|
|
|
17
17
|
*/
|
|
18
18
|
timeoutMs?: number;
|
|
19
19
|
}
|
|
20
|
+
interface VercelCliInvocation {
|
|
21
|
+
command: string;
|
|
22
|
+
commandArgs: string[];
|
|
23
|
+
shell?: boolean;
|
|
24
|
+
}
|
|
25
|
+
type Platform = NodeJS.Platform;
|
|
26
|
+
export declare function resolveVercelInvocation(cwd: string, args?: string[], platform?: Platform): VercelCliInvocation;
|
|
20
27
|
/**
|
|
21
28
|
* Runs a Vercel CLI command with the Connect feature flag enabled.
|
|
22
29
|
*
|
|
@@ -73,3 +80,4 @@ export type VercelCaptureResult = {
|
|
|
73
80
|
* streamed to it and the failure summary is appended after a non-zero exit.
|
|
74
81
|
*/
|
|
75
82
|
export declare function captureVercel(args: string[], options: RunVercelOptions): Promise<VercelCaptureResult>;
|
|
83
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{withoutCodingAgentMarkers}from"./coding-agent-env.js";import{createProcessOutputBuffer}from"./process-output.js";import{armProcessAbort}from"./process-abort.js";import{accessSync,constants,existsSync,statSync}from"node:fs";import{dirname,join,resolve}from"node:path";import{spawn}from"node:child_process";const CONNECT_FEATURE_FLAG_ENV={FF_CONNECT_ENABLED:`1`},VERCEL_NOT_FOUND_MESSAGE=`Vercel CLI not found. Install with: npm i -g vercel@latest`;function buildSpawnEnv(t){return{...withoutCodingAgentMarkers(process.env),...CONNECT_FEATURE_FLAG_ENV,...t}}function commandArgs(e,t){return!t||e.includes(`--non-interactive`)?e:[...e,`--non-interactive`]}function existingDir(e){let t=resolve(e);for(;!existsSync(t);){let e=dirname(t);if(e===t)break;t=e}return t}function armDeadline(e,t,n){if(t===void 0)return()=>{};let r=setTimeout(()=>{n(),e.kill(`SIGTERM`);let t=setTimeout(()=>e.kill(`SIGKILL`),5e3);t.unref(),e.once(`close`,()=>clearTimeout(t))},t);return r.unref(),()=>clearTimeout(r)}function timeoutMessage(e,t){return`vercel ${e.join(` `)} timed out after ${Math.round(t/1e3)}s and was aborted.`}function abortMessage(e){return`vercel ${e.join(` `)} was aborted.`}function isAbortError(e,t){return t?.aborted===!0||e.name===`AbortError`||e.code===`ABORT_ERR`}function ancestorDirectories(e){let t=[],n=resolve(e);for(;;){t.push(n);let e=dirname(n);if(e===n)return t;n=e}}function findExecutable(e){try{if(accessSync(e,constants.F_OK|constants.X_OK),statSync(e).isFile())return e}catch{return}}function findLocalVercel(e){for(let
|
|
1
|
+
import{withoutCodingAgentMarkers}from"./coding-agent-env.js";import{createProcessOutputBuffer}from"./process-output.js";import{armProcessAbort}from"./process-abort.js";import{accessSync,constants,existsSync,statSync}from"node:fs";import{dirname,join,resolve}from"node:path";import{spawn}from"node:child_process";const CONNECT_FEATURE_FLAG_ENV={FF_CONNECT_ENABLED:`1`},VERCEL_NOT_FOUND_MESSAGE=`Vercel CLI not found. Install with: npm i -g vercel@latest`;function buildSpawnEnv(t){return{...withoutCodingAgentMarkers(process.env),...CONNECT_FEATURE_FLAG_ENV,...t}}function commandArgs(e,t){return!t||e.includes(`--non-interactive`)?e:[...e,`--non-interactive`]}function existingDir(e){let t=resolve(e);for(;!existsSync(t);){let e=dirname(t);if(e===t)break;t=e}return t}function armDeadline(e,t,n){if(t===void 0)return()=>{};let r=setTimeout(()=>{n(),e.kill(`SIGTERM`);let t=setTimeout(()=>e.kill(`SIGKILL`),5e3);t.unref(),e.once(`close`,()=>clearTimeout(t))},t);return r.unref(),()=>clearTimeout(r)}function timeoutMessage(e,t){return`vercel ${e.join(` `)} timed out after ${Math.round(t/1e3)}s and was aborted.`}function abortMessage(e){return`vercel ${e.join(` `)} was aborted.`}function isAbortError(e,t){return t?.aborted===!0||e.name===`AbortError`||e.code===`ABORT_ERR`}function ancestorDirectories(e){let t=[],n=resolve(e);for(;;){t.push(n);let e=dirname(n);if(e===n)return t;n=e}}function findExecutable(e){try{if(accessSync(e,constants.F_OK|constants.X_OK),statSync(e).isFile())return e}catch{return}}function vercelExecutableNames(e){return e===`win32`?[`vercel.cmd`,`vercel.exe`]:[`vercel`]}function findLocalVercel(e,t){for(let n of ancestorDirectories(e))for(let e of vercelExecutableNames(t)){let t=findExecutable(join(n,`node_modules`,`.bin`,e));if(t!==void 0)return t}}function resolveVercelInvocation(e,t=[],n=process.platform){let r=findLocalVercel(e,n);return n===`win32`?{command:r??`vercel`,commandArgs:t,shell:!0}:r===void 0?{command:`vercel`,commandArgs:t}:{command:r,commandArgs:t}}function stdioForRun(e){return e.onOutput?[e.nonInteractive?`ignore`:`inherit`,`pipe`,`pipe`]:e.nonInteractive?[`ignore`,`pipe`,`pipe`]:`inherit`}async function runVercel(e,r){return r.signal?.aborted===!0?!1:new Promise(i=>{let a=existingDir(r.cwd),o=resolveVercelInvocation(a,commandArgs(e,r.nonInteractive)),s=r.onOutput&&createProcessOutputBuffer(r.onOutput),c=spawn(o.command,o.commandArgs,{cwd:a,stdio:stdioForRun(r),env:buildSpawnEnv(r.extraEnv??{}),shell:o.shell,signal:r.signal}),l=armProcessAbort(c,r.signal);c.stdout?.on(`data`,e=>s?.write(`stdout`,e)),c.stderr?.on(`data`,e=>s?.write(`stderr`,e));let u=!1;function settle(e){u||(u=!0,s?.flush(),i(e))}function reportFailure(e){r.onOutput?r.onOutput({stream:`stderr`,text:e}):process.stderr.write(`\n${e}\n`)}let d=armDeadline(c,r.timeoutMs,()=>{reportFailure(timeoutMessage(e,r.timeoutMs??0)),settle(!1)});c.on(`error`,t=>{isAbortError(t,r.signal)||(t.code===`ENOENT`?(l(),d(),reportFailure(VERCEL_NOT_FOUND_MESSAGE),settle(!1)):(l(),d(),reportFailure(`vercel ${e.join(` `)} failed: ${t.message}`),settle(!1)))}),c.on(`close`,t=>{if(l(),d(),r.signal?.aborted===!0){settle(!1);return}!u&&t!==0&&t!==null&&(s?.flush(),reportFailure(`vercel ${e.join(` `)} exited with code ${t}.`)),settle(t===0)})})}async function runVercelCaptureStdout(e,r){return r.signal?.aborted===!0?{ok:!1,stdout:``}:new Promise(i=>{let a=existingDir(r.cwd),o=resolveVercelInvocation(a,commandArgs(e,r.nonInteractive)),s=r.onOutput&&createProcessOutputBuffer(r.onOutput),c=spawn(o.command,o.commandArgs,{cwd:a,stdio:[r.nonInteractive?`ignore`:`inherit`,`pipe`,r.onOutput?`pipe`:`inherit`],env:buildSpawnEnv(r.extraEnv??{}),shell:o.shell,signal:r.signal}),l=armProcessAbort(c,r.signal),u=[];c.stdout?.on(`data`,e=>u.push(e.toString(`utf8`))),c.stderr?.on(`data`,e=>s?.write(`stderr`,e));let d=!1;function settle(e){d||(d=!0,s?.flush(),i({ok:e,stdout:u.join(``)}))}function reportFailure(e){r.onOutput?r.onOutput({stream:`stderr`,text:e}):process.stderr.write(`\n${e}\n`)}let f=armDeadline(c,r.timeoutMs,()=>{reportFailure(timeoutMessage(e,r.timeoutMs??0)),settle(!1)});c.on(`error`,t=>{isAbortError(t,r.signal)||(l(),f(),reportFailure(t.code===`ENOENT`?VERCEL_NOT_FOUND_MESSAGE:`vercel ${e.join(` `)} failed: ${t.message}`),settle(!1))}),c.on(`close`,t=>{if(l(),f(),r.signal?.aborted===!0){settle(!1);return}!d&&t!==0&&t!==null&&reportFailure(`vercel ${e.join(` `)} exited with code ${t}.`),settle(t===0)})})}async function captureVercel(e,r){return r.signal?.aborted===!0?{ok:!1,failure:{errno:`ABORT_ERR`,stdout:``,stderr:``,message:abortMessage(e)}}:new Promise(i=>{let a=existingDir(r.cwd),o=resolveVercelInvocation(a,commandArgs(e,r.nonInteractive)),s=r.onOutput&&createProcessOutputBuffer(r.onOutput),c=spawn(o.command,o.commandArgs,{cwd:a,stdio:[`ignore`,`pipe`,`pipe`],env:buildSpawnEnv(r.extraEnv??{}),shell:o.shell,signal:r.signal}),l=armProcessAbort(c,r.signal),u=[],d=[];c.stdout?.on(`data`,e=>u.push(e.toString(`utf8`))),c.stderr?.on(`data`,e=>{d.push(e.toString(`utf8`)),s?.write(`stderr`,e)});let f=!1;function fail(e,t=!0){f||(f=!0,s?.flush(),t&&r.onOutput?.({stream:`stderr`,text:e.message}),i({ok:!1,failure:e}))}function succeed(){f||(f=!0,s?.flush(),i({ok:!0,stdout:u.join(``)}))}let p=armDeadline(c,r.timeoutMs,()=>{fail({code:null,stdout:u.join(``),stderr:d.join(``),message:timeoutMessage(e,r.timeoutMs??0)})});c.on(`error`,t=>{isAbortError(t,r.signal)||(l(),p(),fail({errno:t.code,stdout:u.join(``),stderr:d.join(``),message:t.code===`ENOENT`?VERCEL_NOT_FOUND_MESSAGE:`vercel ${e.join(` `)} failed: ${t.message}`}))}),c.on(`close`,t=>{if(l(),p(),r.signal?.aborted===!0){fail({errno:`ABORT_ERR`,stdout:u.join(``),stderr:d.join(``),message:abortMessage(e)},!1);return}if(t!==0&&t!==null){fail({code:t,stdout:u.join(``),stderr:d.join(``),message:`vercel ${e.join(` `)} exited with code ${t}.`});return}succeed()})})}export{captureVercel,resolveVercelInvocation,runVercel,runVercelCaptureStdout};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`7.0.0-beta.178`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.13.
|
|
1
|
+
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`7.0.0-beta.178`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.13.8`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -75,7 +75,11 @@ dist
|
|
|
75
75
|
dist
|
|
76
76
|
`,"AGENTS.md":`# eve Agent App
|
|
77
77
|
|
|
78
|
-
This project uses the eve framework. Before writing code,
|
|
78
|
+
This project uses the eve framework. Before writing code, read the relevant guide
|
|
79
|
+
from the installed eve package docs. In most installs, those docs are at
|
|
80
|
+
\`node_modules/eve/docs/\`. In workspaces or local package installs, resolve the
|
|
81
|
+
installed \`eve\` package location first and read its \`docs/\` directory. If
|
|
82
|
+
package docs are unavailable, use https://eve.dev/docs as a fallback.
|
|
79
83
|
`,"CLAUDE.md":`@AGENTS.md
|
|
80
84
|
`};function templateFiles(e,t){return{"agent/agent.ts":e?`import { defineAgent } from "eve";
|
|
81
85
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const WEB_APP_TEMPLATE_FILES: {
|
|
2
2
|
readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n auth: [\n // Open on localhost for `eve dev` and the REPL; ignored in production.\n localDev(),\n // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n vercelOidc(),\n // This placeholder will not allow browser requests in production.\n // Replace it with your app\'s auth provider, like Auth.js or Clerk,\n // or use none() for a public demo.\n placeholderAuth(),\n ],\n});\n';
|
|
3
|
-
readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon } from "lucide-react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n} from "@/components/ai-elements/conversation";\nimport {\n PromptInput,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n} from "@/components/ai-elements/prompt-input";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\
|
|
3
|
+
readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon } from "lucide-react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n} from "@/components/ai-elements/conversation";\nimport {\n PromptInput,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n} from "@/components/ai-elements/prompt-input";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\n\ntype AgentStatus = ReturnType<typeof useEveAgent>["status"];\n\nexport function AgentChat() {\n const agent = useEveAgent();\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isEmpty = agent.data.messages.length === 0;\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if (!text || isBusy) return;\n\n await agent.send({ message: text });\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea placeholder="Send a message…" />\n <PromptInputSubmit onStop={agent.stop} status={agent.status} />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {isEmpty ? null : (\n <header className="flex h-14 shrink-0 items-center justify-center gap-3 pl-4 pr-2">\n <span className="flex min-w-0 items-center gap-2">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n <StatusDot status={agent.status} />\n </span>\n </header>\n )}\n\n {agent.error ? (\n <div className="mx-auto w-full max-w-3xl shrink-0 px-4 pt-2 sm:px-6">\n <div className="flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm">\n <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />\n <div>\n <p className="font-medium">Request failed</p>\n <p className="mt-0.5 text-muted-foreground">{agent.error.message}</p>\n </div>\n </div>\n </div>\n ) : null}\n\n {isEmpty ? null : (\n <Conversation className="min-h-0 flex-1">\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 py-6 sm:px-6">\n {agent.data.messages.map((message, index) => (\n <AgentMessage\n canRespond={!isBusy}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n onInputResponses={(inputResponses) => agent.send({ inputResponses })}\n />\n ))}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n )}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n isEmpty\n ? "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]"\n : "max-w-3xl shrink-0 pb-6",\n )}\n >\n {isEmpty ? (\n <div className="flex flex-col items-center gap-3 text-center">\n <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>\n </div>\n ) : null}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction StatusDot({ status }: { readonly status: AgentStatus }) {\n const isLive = status === "submitted" || status === "streaming";\n const tone =\n status === "error"\n ? "bg-destructive"\n : isLive\n ? "bg-emerald-500"\n : status === "ready"\n ? "bg-muted-foreground"\n : "bg-muted-foreground/50";\n\n return (\n <span className="relative flex size-1">\n {isLive ? (\n <span\n className={cn(\n "absolute inline-flex size-full animate-ping rounded-full opacity-75",\n tone,\n )}\n />\n ) : null}\n <span className={cn("relative inline-flex size-1 rounded-full transition-colors", tone)} />\n </span>\n );\n}\n';
|
|
4
4
|
readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type { EveDynamicToolPart, EveMessage, EveMessagePart } from "eve/react";\nimport { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";\nimport {\n Tool,\n ToolContent,\n ToolHeader,\n ToolInput,\n ToolOutput,\n} from "@/components/ai-elements/tool";\nimport { Button } from "@/components/ui/button";\n\nexport type AgentInputResponse = {\n readonly optionId?: string;\n readonly requestId: string;\n readonly text?: string;\n};\n\nexport function AgentMessage({\n canRespond,\n isStreaming,\n message,\n onInputResponses,\n}: {\n readonly canRespond: boolean;\n readonly isStreaming: boolean;\n readonly message: EveMessage;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n const lastTextIndex = message.parts.reduce(\n (last, part, index) => (part.type === "text" ? index : last),\n -1,\n );\n\n return (\n <Message\n data-optimistic={message.metadata?.optimistic ? "true" : undefined}\n from={message.role}\n >\n <MessageContent>\n {message.parts.map((part, index) => (\n <AgentMessagePart\n canRespond={canRespond}\n key={partKey(part, index)}\n onInputResponses={onInputResponses}\n part={part}\n showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}\n />\n ))}\n </MessageContent>\n </Message>\n );\n}\n\nfunction AgentMessagePart({\n canRespond,\n onInputResponses,\n part,\n showCaret,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveMessagePart;\n readonly showCaret: boolean;\n}) {\n switch (part.type) {\n case "step-start":\n return null;\n case "text":\n return (\n <MessageResponse caret="block" isAnimating={showCaret}>\n {part.text}\n </MessageResponse>\n );\n case "reasoning":\n return (\n <Reasoning defaultOpen isStreaming={part.state === "streaming"}>\n <ReasoningTrigger />\n <ReasoningContent>{part.text}</ReasoningContent>\n </Reasoning>\n );\n case "dynamic-tool":\n return (\n <Tool\n defaultOpen={part.state === "approval-requested" || part.state === "approval-responded"}\n >\n <ToolHeader\n state={part.state}\n title={part.toolName}\n toolName={part.toolName}\n type="dynamic-tool"\n />\n <ToolContent>\n <ToolInput input={part.input} />\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n <ToolOutput errorText={part.errorText} output={part.output} />\n </ToolContent>\n </Tool>\n );\n }\n}\n\nfunction InputRequestActions({\n canRespond,\n onInputResponses,\n part,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveDynamicToolPart;\n}) {\n const inputRequest = part.toolMetadata?.eve?.inputRequest;\n if (!inputRequest) {\n return null;\n }\n\n const inputResponse = part.toolMetadata?.eve?.inputResponse;\n const selectedOption = inputRequest.options?.find(\n (option) => option.id === inputResponse?.optionId,\n );\n\n return (\n <div className="space-y-3 rounded-md border border-yellow-500/30 bg-yellow-500/5 p-3">\n <p className="text-muted-foreground text-sm">{inputRequest.prompt}</p>\n {inputResponse ? (\n <p className="font-medium text-sm">\n Responded: {selectedOption?.label ?? inputResponse.text ?? inputResponse.optionId}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond}\n key={option.id}\n onClick={() => {\n void onInputResponses([\n {\n optionId: option.id,\n requestId: inputRequest.requestId,\n },\n ]);\n }}\n size="sm"\n type="button"\n variant={option.style === "danger" ? "destructive" : "default"}\n >\n {option.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction partKey(part: EveMessagePart, index: number): string {\n switch (part.type) {\n case "dynamic-tool":\n return part.toolCallId;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
|
|
5
5
|
readonly "app/globals.css": '@import "tailwindcss";\n@source "../node_modules/streamdown/dist/*.js";\n\n@theme inline {\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;\n --font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;\n}\n\n:root {\n color-scheme: light;\n /* Soft neutral page with white elevated surfaces so cards/composer pop. */\n --background: oklch(0.971 0 0);\n --foreground: oklch(0.16 0 0);\n --card: oklch(1 0 0);\n --card-foreground: oklch(0.16 0 0);\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.16 0 0);\n --primary: oklch(0.19 0 0);\n --primary-foreground: oklch(0.985 0 0);\n --secondary: oklch(0.94 0 0);\n --secondary-foreground: oklch(0.19 0 0);\n --muted: oklch(0.94 0 0);\n --muted-foreground: oklch(0.6 0 0);\n --accent: oklch(0.94 0 0);\n --accent-foreground: oklch(0.19 0 0);\n --destructive: oklch(0.577 0.245 27.325);\n --border: oklch(0.916 0 0);\n --input: oklch(0.916 0 0);\n --ring: oklch(0.708 0 0);\n --radius: 0.625rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n color-scheme: dark;\n --background: oklch(0.145 0 0);\n --foreground: oklch(0.985 0 0);\n --card: oklch(0.205 0 0);\n --card-foreground: oklch(0.985 0 0);\n --popover: oklch(0.205 0 0);\n --popover-foreground: oklch(0.985 0 0);\n --primary: oklch(0.922 0 0);\n --primary-foreground: oklch(0.205 0 0);\n --secondary: oklch(0.269 0 0);\n --secondary-foreground: oklch(0.985 0 0);\n --muted: oklch(0.269 0 0);\n --muted-foreground: oklch(0.708 0 0);\n --accent: oklch(0.269 0 0);\n --accent-foreground: oklch(0.985 0 0);\n --destructive: oklch(0.704 0.191 22.216);\n --border: oklch(1 0 0 / 10%);\n --input: oklch(1 0 0 / 15%);\n --ring: oklch(0.556 0 0);\n }\n}\n\n* {\n border-color: var(--border);\n}\n\nhtml {\n height: 100%;\n}\n\nbody {\n min-height: 100%;\n margin: 0;\n background: var(--background);\n font-family: var(--font-sans);\n}\n\nbutton,\ninput,\ntextarea {\n font: inherit;\n}\n';
|
|
6
6
|
readonly "app/layout.tsx": 'import type { Metadata } from "next";\nimport { Geist, Geist_Mono } from "next/font/google";\nimport type { ReactNode } from "react";\nimport { TooltipProvider } from "@/components/ui/tooltip";\nimport { cn } from "@/lib/utils";\nimport "./globals.css";\n\nconst sans = Geist({\n variable: "--font-sans",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nconst mono = Geist_Mono({\n variable: "--font-mono",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nexport const metadata: Metadata = {\n title: "__EVE_INIT_APP_NAME__",\n description: "A Next.js starter for eve agents with AI Elements.",\n};\n\nexport default function RootLayout({ children }: { readonly children: ReactNode }) {\n return (\n <html className={cn(sans.variable, mono.variable)} lang="en">\n <body>\n <TooltipProvider>{children}</TooltipProvider>\n </body>\n </html>\n );\n}\n';
|
|
@@ -32,7 +32,6 @@ import { cn } from "@/lib/utils";
|
|
|
32
32
|
import { AgentMessage } from "./agent-message";
|
|
33
33
|
|
|
34
34
|
const AGENT_NAME = "__EVE_INIT_APP_NAME__";
|
|
35
|
-
const BETA_TERMS_HREF = "https://vercel.com/docs/release-phases/public-beta-agreement";
|
|
36
35
|
|
|
37
36
|
type AgentStatus = ReturnType<typeof useEveAgent>["status"];
|
|
38
37
|
|
|
@@ -63,14 +62,6 @@ export function AgentChat() {
|
|
|
63
62
|
<span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>
|
|
64
63
|
<StatusDot status={agent.status} />
|
|
65
64
|
</span>
|
|
66
|
-
<a
|
|
67
|
-
className="rounded-full border border-amber-500/30 px-2 py-0.5 font-medium text-amber-700 text-xs transition-colors hover:bg-amber-500/10 dark:text-amber-300"
|
|
68
|
-
href={BETA_TERMS_HREF}
|
|
69
|
-
rel="noreferrer"
|
|
70
|
-
target="_blank"
|
|
71
|
-
>
|
|
72
|
-
Public preview
|
|
73
|
-
</a>
|
|
74
65
|
</header>
|
|
75
66
|
)}
|
|
76
67
|
|
|
@@ -116,14 +107,6 @@ export function AgentChat() {
|
|
|
116
107
|
{isEmpty ? (
|
|
117
108
|
<div className="flex flex-col items-center gap-3 text-center">
|
|
118
109
|
<h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>
|
|
119
|
-
<a
|
|
120
|
-
className="rounded-full border border-amber-500/30 px-2 py-0.5 font-medium text-amber-700 text-xs transition-colors hover:bg-amber-500/10 dark:text-amber-300"
|
|
121
|
-
href={BETA_TERMS_HREF}
|
|
122
|
-
rel="noreferrer"
|
|
123
|
-
target="_blank"
|
|
124
|
-
>
|
|
125
|
-
Public preview
|
|
126
|
-
</a>
|
|
127
110
|
</div>
|
|
128
111
|
) : null}
|
|
129
112
|
<div className="w-full">{composer}</div>
|
package/docs/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Important naming note:
|
|
|
12
12
|
|
|
13
13
|
## Legal and safeguards
|
|
14
14
|
|
|
15
|
-
eve is
|
|
15
|
+
eve is in preview; the framework, APIs, documentation, and behavior may change before general availability.
|
|
16
16
|
|
|
17
17
|
As the deployer, it is your responsibility to ensure your agent complies with applicable laws.
|
|
18
18
|
|
|
@@ -41,7 +41,7 @@ Read in this order:
|
|
|
41
41
|
6. [Context Control](./concepts/context-control.md)
|
|
42
42
|
7. [Skills](./skills.mdx)
|
|
43
43
|
8. [Tools](./tools/overview.mdx)
|
|
44
|
-
9. [Connections](./connections.mdx)
|
|
44
|
+
9. [Connections](./connections/overview.mdx)
|
|
45
45
|
10. [Sandboxes](./sandbox.mdx)
|
|
46
46
|
11. [Channels](./channels/overview.mdx)
|
|
47
47
|
12. [Session Context](./reference/typescript-api.md)
|
package/docs/channels/linear.mdx
CHANGED
|
@@ -158,4 +158,4 @@ For issue or comment targets, the channel calls Linear's proactive Agent Session
|
|
|
158
158
|
## What to read next
|
|
159
159
|
|
|
160
160
|
- [Channels overview](./overview): the channel contract and every built-in channel
|
|
161
|
-
- [
|
|
161
|
+
- [MCP connections](../connections/mcp): use the Linear MCP connection when the agent needs to inspect or edit Linear data from another channel
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "MCP Connections"
|
|
3
|
+
description: "Connect an eve agent to a remote MCP server and control which tools the model can discover."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
MCP connections point eve at an MCP server you do not author. The server publishes its tools and schemas, and eve exposes matched tools to the model through `connection_search`.
|
|
7
|
+
|
|
8
|
+
Use MCP when the service already has an MCP server, when the server needs to own tool schemas dynamically, or when one connection should expose a family of related remote tools.
|
|
9
|
+
|
|
10
|
+
## Define an MCP connection
|
|
11
|
+
|
|
12
|
+
`defineMcpClientConnection` needs a `url` and a `description`:
|
|
13
|
+
|
|
14
|
+
```ts title="agent/connections/linear.ts"
|
|
15
|
+
import { defineMcpClientConnection } from "eve/connections";
|
|
16
|
+
|
|
17
|
+
export default defineMcpClientConnection({
|
|
18
|
+
url: "https://mcp.linear.app/sse",
|
|
19
|
+
description: "Linear workspace: issues, projects, cycles, and comments.",
|
|
20
|
+
auth: {
|
|
21
|
+
getToken: async () => ({ token: process.env.LINEAR_API_TOKEN! }),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The `url` must speak Streamable HTTP or SSE. Write the `description` for the model, not for yourself. It shows up in `connection_search`, and the model uses it to decide which connection to query.
|
|
27
|
+
|
|
28
|
+
The file path provides the connection name. `agent/connections/linear.ts` registers as `linear`, and remote tools appear as `linear__<tool>` after discovery.
|
|
29
|
+
|
|
30
|
+
## Tool filters
|
|
31
|
+
|
|
32
|
+
To narrow which remote tools the model sees, set exactly one of `tools.allow` or `tools.block`. Filtered-out tools do not appear in `connection_search`:
|
|
33
|
+
|
|
34
|
+
```ts title="agent/connections/linear.ts"
|
|
35
|
+
import { defineMcpClientConnection } from "eve/connections";
|
|
36
|
+
|
|
37
|
+
export default defineMcpClientConnection({
|
|
38
|
+
url: "https://mcp.linear.app/sse",
|
|
39
|
+
description: "Linear: read-only.",
|
|
40
|
+
auth: { getToken: async () => ({ token: process.env.LINEAR_API_TOKEN! }) },
|
|
41
|
+
tools: { allow: ["search_issues", "get_issue"] },
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use `allow` for the smallest safe surface, especially on MCP servers that expose write tools alongside read tools. Use `block` only when the server has a broad stable surface and you need to hide a few tools.
|
|
46
|
+
|
|
47
|
+
## Auth, headers, and approval
|
|
48
|
+
|
|
49
|
+
MCP connections support the shared connection options:
|
|
50
|
+
|
|
51
|
+
- `auth` for static tokens, Vercel Connect, or self-hosted interactive OAuth.
|
|
52
|
+
- `headers` for API-key schemes or extra server configuration.
|
|
53
|
+
- `approval` for human-in-the-loop gates before connection tools run.
|
|
54
|
+
|
|
55
|
+
See [Connections](/docs/connections) for the shared auth, headers, and approval shapes.
|
|
56
|
+
|
|
57
|
+
## What to read next
|
|
58
|
+
|
|
59
|
+
- [OpenAPI connections](./openapi): generate tools from OpenAPI operations.
|
|
60
|
+
- [Auth & route protection](../guides/auth-and-route-protection): the full interactive-OAuth flow with Vercel Connect.
|
|
61
|
+
- [Security model](../concepts/security-model): how connection credentials stay out of the model's reach.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "OpenAPI Connections"
|
|
3
|
+
description: "Turn an OpenAPI 3.x document into eve connection tools, one tool per operation."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
OpenAPI connections turn any OpenAPI 3.x document into connection tools, one per operation. Use OpenAPI when a service already publishes an HTTP API contract and you want eve to derive the model-facing tools from that contract.
|
|
7
|
+
|
|
8
|
+
## Define an OpenAPI connection
|
|
9
|
+
|
|
10
|
+
`defineOpenAPIConnection` takes an HTTPS URL that eve fetches at runtime, or an inline parsed object:
|
|
11
|
+
|
|
12
|
+
```ts title="agent/connections/petstore.ts"
|
|
13
|
+
import { defineOpenAPIConnection } from "eve/connections";
|
|
14
|
+
|
|
15
|
+
export default defineOpenAPIConnection({
|
|
16
|
+
spec: "https://petstore3.swagger.io/api/v3/openapi.json",
|
|
17
|
+
description: "Pet store inventory and orders.",
|
|
18
|
+
auth: { getToken: async () => ({ token: process.env.PETSTORE_TOKEN! }) },
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each operation becomes `<connection>__<operationId>` (e.g. `petstore__getInventory`). When an operation has no `operationId`, eve derives a deterministic `<method>_<sanitized-path>` name instead.
|
|
23
|
+
|
|
24
|
+
The file path provides the connection name. `agent/connections/petstore.ts` registers as `petstore`, so operation tools are qualified under `petstore__`.
|
|
25
|
+
|
|
26
|
+
## OpenAPI fields
|
|
27
|
+
|
|
28
|
+
OpenAPI connections use the shared connection fields from [Connections](/docs/connections), plus two OpenAPI-specific fields:
|
|
29
|
+
|
|
30
|
+
| Field | Purpose |
|
|
31
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `baseUrl` | Base URL operation paths resolve against. Optional; defaults to the document's first usable `servers` entry. |
|
|
33
|
+
| `operations` | Filter keyed on `operationId` (`allow` or `block`). Mirrors `tools` on MCP connections, but names operations. |
|
|
34
|
+
|
|
35
|
+
Use `baseUrl` when the spec's `servers` list is absent, points at the wrong environment, or needs to be pinned for this agent.
|
|
36
|
+
|
|
37
|
+
## Operation filters
|
|
38
|
+
|
|
39
|
+
To narrow which generated tools the model sees, set exactly one of `operations.allow` or `operations.block`:
|
|
40
|
+
|
|
41
|
+
```ts title="agent/connections/petstore.ts"
|
|
42
|
+
import { defineOpenAPIConnection } from "eve/connections";
|
|
43
|
+
|
|
44
|
+
export default defineOpenAPIConnection({
|
|
45
|
+
spec: "https://petstore3.swagger.io/api/v3/openapi.json",
|
|
46
|
+
description: "Pet store inventory and orders.",
|
|
47
|
+
auth: { getToken: async () => ({ token: process.env.PETSTORE_TOKEN! }) },
|
|
48
|
+
operations: { allow: ["getInventory", "placeOrder"] },
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Filters match `operationId`. If an operation does not declare one, use the deterministic name eve derives from the method and path.
|
|
53
|
+
|
|
54
|
+
## Auth, headers, and approval
|
|
55
|
+
|
|
56
|
+
OpenAPI connections support the shared connection options:
|
|
57
|
+
|
|
58
|
+
- `auth` for static tokens, Vercel Connect, or self-hosted interactive OAuth.
|
|
59
|
+
- `headers` for API-key schemes or extra server configuration.
|
|
60
|
+
- `approval` for human-in-the-loop gates before generated operation tools run.
|
|
61
|
+
|
|
62
|
+
See [Connections](/docs/connections) for the shared auth, headers, and approval shapes.
|
|
63
|
+
|
|
64
|
+
## What to read next
|
|
65
|
+
|
|
66
|
+
- [MCP connections](./mcp): connect to remote MCP servers.
|
|
67
|
+
- [Auth & route protection](../guides/auth-and-route-protection): the full interactive-OAuth flow with Vercel Connect.
|
|
68
|
+
- [Security model](../concepts/security-model): how connection credentials stay out of the model's reach.
|